added configurability of grid size for virtual buttons
[rocksndiamonds.git] / src / screens.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // screens.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "screens.h"
15 #include "events.h"
16 #include "game.h"
17 #include "tools.h"
18 #include "editor.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "anim.h"
22 #include "network.h"
23 #include "init.h"
24 #include "config.h"
25
26
27 #define DEBUG_JOYSTICKS         0
28
29
30 /* screens on the info screen */
31 #define INFO_MODE_MAIN                  0
32 #define INFO_MODE_TITLE                 1
33 #define INFO_MODE_ELEMENTS              2
34 #define INFO_MODE_MUSIC                 3
35 #define INFO_MODE_CREDITS               4
36 #define INFO_MODE_PROGRAM               5
37 #define INFO_MODE_VERSION               6
38 #define INFO_MODE_LEVELSET              7
39
40 #define MAX_INFO_MODES                  8
41
42 /* screens on the setup screen */
43 /* (must match GFX_SPECIAL_ARG_SETUP_* values as defined in src/main.h) */
44 /* (should also match corresponding entries in src/conf_gfx.c) */
45 #define SETUP_MODE_MAIN                 0
46 #define SETUP_MODE_GAME                 1
47 #define SETUP_MODE_EDITOR               2
48 #define SETUP_MODE_GRAPHICS             3
49 #define SETUP_MODE_SOUND                4
50 #define SETUP_MODE_ARTWORK              5
51 #define SETUP_MODE_INPUT                6
52 #define SETUP_MODE_TOUCH                7
53 #define SETUP_MODE_SHORTCUTS            8
54 #define SETUP_MODE_SHORTCUTS_1          9
55 #define SETUP_MODE_SHORTCUTS_2          10
56 #define SETUP_MODE_SHORTCUTS_3          11
57 #define SETUP_MODE_SHORTCUTS_4          12
58 #define SETUP_MODE_SHORTCUTS_5          13
59
60 /* sub-screens on the setup screen (generic) */
61 #define SETUP_MODE_CHOOSE_ARTWORK       14
62 #define SETUP_MODE_CHOOSE_OTHER         15
63
64 /* sub-screens on the setup screen (specific) */
65 #define SETUP_MODE_CHOOSE_GAME_SPEED    16
66 #define SETUP_MODE_CHOOSE_SCROLL_DELAY  17
67 #define SETUP_MODE_CHOOSE_SNAPSHOT_MODE 18
68 #define SETUP_MODE_CHOOSE_WINDOW_SIZE   19
69 #define SETUP_MODE_CHOOSE_SCALING_TYPE  20
70 #define SETUP_MODE_CHOOSE_RENDERING     21
71 #define SETUP_MODE_CHOOSE_GRAPHICS      22
72 #define SETUP_MODE_CHOOSE_SOUNDS        23
73 #define SETUP_MODE_CHOOSE_MUSIC         24
74 #define SETUP_MODE_CHOOSE_VOLUME_SIMPLE 25
75 #define SETUP_MODE_CHOOSE_VOLUME_LOOPS  26
76 #define SETUP_MODE_CHOOSE_VOLUME_MUSIC  27
77 #define SETUP_MODE_CHOOSE_TOUCH_CONTROL 28
78 #define SETUP_MODE_CHOOSE_MOVE_DISTANCE 29
79 #define SETUP_MODE_CHOOSE_DROP_DISTANCE 30
80 #define SETUP_MODE_CHOOSE_TRANSPARENCY  31
81 #define SETUP_MODE_CHOOSE_GRID_XSIZE_0  32
82 #define SETUP_MODE_CHOOSE_GRID_YSIZE_0  33
83 #define SETUP_MODE_CHOOSE_GRID_XSIZE_1  34
84 #define SETUP_MODE_CHOOSE_GRID_YSIZE_1  35
85
86 #define MAX_SETUP_MODES                 36
87
88 #define MAX_MENU_MODES                  MAX(MAX_INFO_MODES, MAX_SETUP_MODES)
89
90 /* setup screen titles */
91 #define STR_SETUP_MAIN                  "Setup"
92 #define STR_SETUP_GAME                  "Game & Menu"
93 #define STR_SETUP_EDITOR                "Editor"
94 #define STR_SETUP_GRAPHICS              "Graphics"
95 #define STR_SETUP_SOUND                 "Sound & Music"
96 #define STR_SETUP_ARTWORK               "Custom Artwork"
97 #define STR_SETUP_INPUT                 "Input Devices"
98 #define STR_SETUP_TOUCH                 "Touch Controls"
99 #define STR_SETUP_SHORTCUTS             "Key Shortcuts"
100 #define STR_SETUP_EXIT                  "Exit"
101 #define STR_SETUP_SAVE_AND_EXIT         "Save and Exit"
102
103 #define STR_SETUP_CHOOSE_GAME_SPEED     "Game Speed"
104 #define STR_SETUP_CHOOSE_SCROLL_DELAY   "Scroll Delay"
105 #define STR_SETUP_CHOOSE_SNAPSHOT_MODE  "Snapshot Mode"
106 #define STR_SETUP_CHOOSE_WINDOW_SIZE    "Window Scaling"
107 #define STR_SETUP_CHOOSE_SCALING_TYPE   "Anti-Aliasing"
108 #define STR_SETUP_CHOOSE_RENDERING      "Rendering Mode"
109 #define STR_SETUP_CHOOSE_VOLUME_SIMPLE  "Sound Volume"
110 #define STR_SETUP_CHOOSE_VOLUME_LOOPS   "Loops Volume"
111 #define STR_SETUP_CHOOSE_VOLUME_MUSIC   "Music Volume"
112 #define STR_SETUP_CHOOSE_TOUCH_CONTROL  "Control Type"
113 #define STR_SETUP_CHOOSE_MOVE_DISTANCE  "Move Distance"
114 #define STR_SETUP_CHOOSE_DROP_DISTANCE  "Drop Distance"
115 #define STR_SETUP_CHOOSE_TRANSPARENCY   "Transparency"
116 #define STR_SETUP_CHOOSE_GRID_XSIZE_0   "Horiz. Buttons"
117 #define STR_SETUP_CHOOSE_GRID_YSIZE_0   "Vert. Buttons"
118 #define STR_SETUP_CHOOSE_GRID_XSIZE_1   "Horiz. Buttons"
119 #define STR_SETUP_CHOOSE_GRID_YSIZE_1   "Vert. Buttons"
120
121 /* for input setup functions */
122 #define SETUPINPUT_SCREEN_POS_START     0
123 #define SETUPINPUT_SCREEN_POS_EMPTY1    3
124 #define SETUPINPUT_SCREEN_POS_EMPTY2    12
125 #define SETUPINPUT_SCREEN_POS_END       13
126
127 #define MENU_SETUP_FONT_TITLE           FONT_TEXT_1
128 #define MENU_SETUP_FONT_TEXT            FONT_TITLE_2
129
130 /* for various menu stuff  */
131 #define MENU_SCREEN_START_XPOS          1
132 #define MENU_SCREEN_START_YPOS          2
133 #define MENU_SCREEN_VALUE_XPOS          (SCR_FIELDX - 3)
134 #define MENU_SCREEN_MAX_XPOS            (SCR_FIELDX - 1)
135 #define MENU_TITLE1_YPOS                8
136 #define MENU_TITLE2_YPOS                46
137 #define MENU_INFO_FONT_TITLE            FONT_TEXT_1
138 #define MENU_INFO_FONT_HEAD             FONT_TEXT_2
139 #define MENU_INFO_FONT_TEXT             FONT_TEXT_3
140 #define MENU_INFO_FONT_FOOT             FONT_TEXT_4
141 #define MENU_INFO_SPACE_HEAD            (menu.headline2_spacing_info[info_mode])
142 #define MENU_SCREEN_INFO_SPACE_LEFT     (menu.left_spacing_info[info_mode])
143 #define MENU_SCREEN_INFO_SPACE_RIGHT    (menu.right_spacing_info[info_mode])
144 #define MENU_SCREEN_INFO_SPACE_TOP      (menu.top_spacing_info[info_mode])
145 #define MENU_SCREEN_INFO_SPACE_BOTTOM   (menu.bottom_spacing_info[info_mode])
146 #define MENU_SCREEN_INFO_YSTART1        MENU_SCREEN_INFO_SPACE_TOP
147 #define MENU_SCREEN_INFO_YSTART2        (MENU_SCREEN_INFO_YSTART1 +            \
148                                          getMenuTextStep(MENU_INFO_SPACE_HEAD, \
149                                                          MENU_INFO_FONT_TITLE))
150 #define MENU_SCREEN_INFO_YSTEP          (TILEY + 4)
151 #define MENU_SCREEN_INFO_YBOTTOM        (SYSIZE - MENU_SCREEN_INFO_SPACE_BOTTOM)
152 #define MENU_SCREEN_INFO_YSIZE          (MENU_SCREEN_INFO_YBOTTOM -     \
153                                          MENU_SCREEN_INFO_YSTART2 -     \
154                                          TILEY / 2)
155 #define MAX_INFO_ELEMENTS_ON_SCREEN     128
156 #define STD_INFO_ELEMENTS_ON_SCREEN     (MENU_SCREEN_INFO_YSIZE /       \
157                                          MENU_SCREEN_INFO_YSTEP)
158 #define NUM_INFO_ELEMENTS_FROM_CONF     \
159   (menu.list_size_info[GFX_SPECIAL_ARG_INFO_ELEMENTS] > 0 ?             \
160    menu.list_size_info[GFX_SPECIAL_ARG_INFO_ELEMENTS] :                 \
161    MAX_MENU_ENTRIES_ON_SCREEN)
162 #define NUM_INFO_ELEMENTS_ON_SCREEN     MIN(MIN(STD_INFO_ELEMENTS_ON_SCREEN, \
163                                                 MAX_INFO_ELEMENTS_ON_SCREEN), \
164                                             NUM_INFO_ELEMENTS_FROM_CONF)
165 #define MAX_MENU_ENTRIES_ON_SCREEN      (SCR_FIELDY - MENU_SCREEN_START_YPOS)
166 #define MAX_MENU_TEXT_LENGTH_BIG        13
167 #define MAX_MENU_TEXT_LENGTH_MEDIUM     (MAX_MENU_TEXT_LENGTH_BIG * 2)
168
169 /* buttons and scrollbars identifiers */
170 #define SCREEN_CTRL_ID_PREV_LEVEL       0
171 #define SCREEN_CTRL_ID_NEXT_LEVEL       1
172 #define SCREEN_CTRL_ID_PREV_PLAYER      2
173 #define SCREEN_CTRL_ID_NEXT_PLAYER      3
174 #define SCREEN_CTRL_ID_SCROLL_UP        4
175 #define SCREEN_CTRL_ID_SCROLL_DOWN      5
176 #define SCREEN_CTRL_ID_SCROLL_VERTICAL  6
177
178 #define NUM_SCREEN_GADGETS              7
179
180 #define NUM_SCREEN_MENUBUTTONS          4
181 #define NUM_SCREEN_SCROLLBUTTONS        2
182 #define NUM_SCREEN_SCROLLBARS           1
183
184 #define SCREEN_MASK_MAIN                (1 << 0)
185 #define SCREEN_MASK_INPUT               (1 << 1)
186
187 /* graphic position and size values for buttons and scrollbars */
188 #define SC_MENUBUTTON_XSIZE             TILEX
189 #define SC_MENUBUTTON_YSIZE             TILEY
190
191 #define SC_SCROLLBUTTON_XSIZE           TILEX
192 #define SC_SCROLLBUTTON_YSIZE           TILEY
193
194 #define SC_SCROLLBAR_XPOS               (SXSIZE - SC_SCROLLBUTTON_XSIZE)
195
196 #define SC_SCROLL_VERTICAL_XSIZE        SC_SCROLLBUTTON_XSIZE
197 #define SC_SCROLL_VERTICAL_YSIZE        ((MAX_MENU_ENTRIES_ON_SCREEN - 2) * \
198                                          SC_SCROLLBUTTON_YSIZE)
199
200 #define SC_SCROLL_UP_XPOS               SC_SCROLLBAR_XPOS
201 #define SC_SCROLL_UP_YPOS               (2 * SC_SCROLLBUTTON_YSIZE)
202
203 #define SC_SCROLL_VERTICAL_XPOS         SC_SCROLLBAR_XPOS
204 #define SC_SCROLL_VERTICAL_YPOS         (SC_SCROLL_UP_YPOS + \
205                                          SC_SCROLLBUTTON_YSIZE)
206
207 #define SC_SCROLL_DOWN_XPOS             SC_SCROLLBAR_XPOS
208 #define SC_SCROLL_DOWN_YPOS             (SC_SCROLL_VERTICAL_YPOS + \
209                                          SC_SCROLL_VERTICAL_YSIZE)
210
211 #define SC_BORDER_SIZE                  14
212
213
214 /* forward declarations of internal functions */
215 static void HandleScreenGadgets(struct GadgetInfo *);
216 static void HandleSetupScreen_Generic(int, int, int, int, int);
217 static void HandleSetupScreen_Input(int, int, int, int, int);
218 static void CustomizeKeyboard(int);
219 static void ConfigureJoystick(int);
220 static void ConfigureVirtualButtons();
221 static void execSetupGame(void);
222 static void execSetupGraphics(void);
223 static void execSetupSound(void);
224 static void execSetupTouch(void);
225 static void execSetupArtwork(void);
226 static void HandleChooseTree(int, int, int, int, int, TreeInfo **);
227
228 static void DrawChooseLevelSet(void);
229 static void DrawChooseLevelNr(void);
230 static void DrawInfoScreen(void);
231 static void DrawSetupScreen(void);
232
233 static void DrawInfoScreen_NotAvailable(char *, char *);
234 static void DrawInfoScreen_HelpAnim(int, int, boolean);
235 static void DrawInfoScreen_HelpText(int, int, int, int);
236 static void HandleInfoScreen_Main(int, int, int, int, int);
237 static void HandleInfoScreen_TitleScreen(int);
238 static void HandleInfoScreen_Elements(int);
239 static void HandleInfoScreen_Music(int);
240 static void HandleInfoScreen_Credits(int);
241 static void HandleInfoScreen_Program(int);
242 static void HandleInfoScreen_Version(int);
243
244 static void MapScreenMenuGadgets(int);
245 static void MapScreenGadgets(int);
246 static void MapScreenTreeGadgets(TreeInfo *);
247
248 static struct GadgetInfo *screen_gadget[NUM_SCREEN_GADGETS];
249
250 static int info_mode = INFO_MODE_MAIN;
251 static int setup_mode = SETUP_MODE_MAIN;
252
253 static TreeInfo *window_sizes = NULL;
254 static TreeInfo *window_size_current = NULL;
255
256 static TreeInfo *scaling_types = NULL;
257 static TreeInfo *scaling_type_current = NULL;
258
259 static TreeInfo *rendering_modes = NULL;
260 static TreeInfo *rendering_mode_current = NULL;
261
262 static TreeInfo *scroll_delays = NULL;
263 static TreeInfo *scroll_delay_current = NULL;
264
265 static TreeInfo *snapshot_modes = NULL;
266 static TreeInfo *snapshot_mode_current = NULL;
267
268 static TreeInfo *game_speeds = NULL;
269 static TreeInfo *game_speed_current = NULL;
270
271 static TreeInfo *volumes_simple = NULL;
272 static TreeInfo *volume_simple_current = NULL;
273
274 static TreeInfo *volumes_loops = NULL;
275 static TreeInfo *volume_loops_current = NULL;
276
277 static TreeInfo *volumes_music = NULL;
278 static TreeInfo *volume_music_current = NULL;
279
280 static TreeInfo *touch_controls = NULL;
281 static TreeInfo *touch_control_current = NULL;
282
283 static TreeInfo *move_distances = NULL;
284 static TreeInfo *move_distance_current = NULL;
285
286 static TreeInfo *drop_distances = NULL;
287 static TreeInfo *drop_distance_current = NULL;
288
289 static TreeInfo *transparencies = NULL;
290 static TreeInfo *transparency_current = NULL;
291
292 static TreeInfo *grid_sizes[2][2] = { { NULL, NULL }, { NULL, NULL } };
293 static TreeInfo *grid_size_current[2][2] = { { NULL, NULL }, { NULL, NULL } };
294
295 static TreeInfo *level_number = NULL;
296 static TreeInfo *level_number_current = NULL;
297
298 static struct
299 {
300   int value;
301   char *text;
302 } window_sizes_list[] =
303 {
304   {     50,     "50 %"                          },
305   {     80,     "80 %"                          },
306   {     90,     "90 %"                          },
307   {     100,    "100 % (Default)"               },
308   {     110,    "110 %"                         },
309   {     120,    "120 %"                         },
310   {     130,    "130 %"                         },
311   {     140,    "140 %"                         },
312   {     150,    "150 %"                         },
313   {     200,    "200 %"                         },
314   {     250,    "250 %"                         },
315   {     300,    "300 %"                         },
316
317   {     -1,     NULL                            },
318 };
319
320 static struct
321 {
322   char *value;
323   char *text;
324 } scaling_types_list[] =
325 {
326   {     SCALING_QUALITY_NEAREST, "Off"          },
327   {     SCALING_QUALITY_LINEAR,  "Linear"       },
328   {     SCALING_QUALITY_BEST,    "Anisotropic"  },
329
330   {     NULL,                    NULL           },
331 };
332
333 static struct
334 {
335   char *value;
336   char *text;
337 } rendering_modes_list[] =
338 {
339   {     STR_SPECIAL_RENDERING_OFF,      "Off (May show artifacts, fast)" },
340   {     STR_SPECIAL_RENDERING_BITMAP,   "Bitmap/Texture mode (slower)"   },
341 #if DEBUG
342   // this mode may work under certain conditions, but does not work on Windows
343   {     STR_SPECIAL_RENDERING_TARGET,   "Target Texture mode (slower)"   },
344 #endif
345   {     STR_SPECIAL_RENDERING_DOUBLE,   "Double Texture mode (slower)"   },
346
347   {     NULL,                            NULL                            },
348 };
349
350 static struct
351 {
352   int value;
353   char *text;
354 } game_speeds_list[] =
355 {
356 #if 1
357   {     30,     "Very Slow"                     },
358   {     25,     "Slow"                          },
359   {     20,     "Normal"                        },
360   {     15,     "Fast"                          },
361   {     10,     "Very Fast"                     },
362 #else
363   {     1000,   "1/1s (Extremely Slow)"         },
364   {     500,    "1/2s"                          },
365   {     200,    "1/5s"                          },
366   {     100,    "1/10s"                         },
367   {     50,     "1/20s"                         },
368   {     29,     "1/35s (Original Supaplex)"     },
369   {     25,     "1/40s"                         },
370   {     20,     "1/50s (Normal Speed)"          },
371   {     14,     "1/70s (Maximum Supaplex)"      },
372   {     10,     "1/100s"                        },
373   {     5,      "1/200s"                        },
374   {     2,      "1/500s"                        },
375   {     1,      "1/1000s (Extremely Fast)"      },
376 #endif
377
378   {     -1,     NULL                            },
379 };
380
381 static struct
382 {
383   int value;
384   char *text;
385 } scroll_delays_list[] =
386 {
387   {     0,      "0 Tiles (No Scroll Delay)"     },
388   {     1,      "1 Tile"                        },
389   {     2,      "2 Tiles"                       },
390   {     3,      "3 Tiles (Default)"             },
391   {     4,      "4 Tiles"                       },
392   {     5,      "5 Tiles"                       },
393   {     6,      "6 Tiles"                       },
394   {     7,      "7 Tiles"                       },
395   {     8,      "8 Tiles (Maximum Scroll Delay)"},
396
397   {     -1,     NULL                            },
398 };
399
400 static struct
401 {
402   char *value;
403   char *text;
404 } snapshot_modes_list[] =
405 {
406   {     STR_SNAPSHOT_MODE_OFF,                  "Off"           },
407   {     STR_SNAPSHOT_MODE_EVERY_STEP,           "Every Step"    },
408   {     STR_SNAPSHOT_MODE_EVERY_MOVE,           "Every Move"    },
409   {     STR_SNAPSHOT_MODE_EVERY_COLLECT,        "Every Collect" },
410
411   {     NULL,                                   NULL            },
412 };
413
414 static struct
415 {
416   int value;
417   char *text;
418 } volumes_list[] =
419 {
420   {     0,      "0 %"                           },
421   {     1,      "1 %"                           },
422   {     2,      "2 %"                           },
423   {     5,      "5 %"                           },
424   {     10,     "10 %"                          },
425   {     20,     "20 %"                          },
426   {     30,     "30 %"                          },
427   {     40,     "40 %"                          },
428   {     50,     "50 %"                          },
429   {     60,     "60 %"                          },
430   {     70,     "70 %"                          },
431   {     80,     "80 %"                          },
432   {     90,     "90 %"                          },
433   {     100,    "100 %"                         },
434
435   {     -1,     NULL                            },
436 };
437
438 static struct
439 {
440   char *value;
441   char *text;
442 } touch_controls_list[] =
443 {
444   {     TOUCH_CONTROL_OFF,              "Off"                   },
445   {     TOUCH_CONTROL_VIRTUAL_BUTTONS,  "Virtual Buttons"       },
446   {     TOUCH_CONTROL_WIPE_GESTURES,    "Wipe Gestures"         },
447   {     TOUCH_CONTROL_FOLLOW_FINGER,    "Follow Finger"         },
448
449   {     NULL,                           NULL                    },
450 };
451
452 static struct
453 {
454   int value;
455   char *text;
456 } distances_list[] =
457 {
458   {     1,      "1 %"                           },
459   {     2,      "2 %"                           },
460   {     3,      "3 %"                           },
461   {     4,      "4 %"                           },
462   {     5,      "5 %"                           },
463   {     10,     "10 %"                          },
464   {     15,     "15 %"                          },
465   {     20,     "20 %"                          },
466   {     25,     "25 %"                          },
467
468   {     -1,     NULL                            },
469 };
470
471 static struct
472 {
473   int value;
474   char *text;
475 } transparencies_list[] =
476 {
477   {     0,      "0 % (Opaque)"                  },
478   {     10,     "10 %"                          },
479   {     20,     "20 %"                          },
480   {     30,     "30 %"                          },
481   {     40,     "40 %"                          },
482   {     50,     "50 %"                          },
483   {     60,     "60 %"                          },
484   {     70,     "70 %"                          },
485   {     80,     "80 %"                          },
486   {     90,     "90 %"                          },
487   {     100,    "100 % (Invisible)"             },
488
489   {     -1,     NULL                            },
490 };
491
492 static struct
493 {
494   int value;
495   char *text;
496 } grid_sizes_list[] =
497 {
498   {     3,      "3"                             },
499   {     4,      "4"                             },
500   {     5,      "5"                             },
501   {     6,      "6"                             },
502   {     7,      "7"                             },
503   {     8,      "8"                             },
504   {     9,      "9"                             },
505   {     10,     "10"                            },
506   {     11,     "11"                            },
507   {     12,     "12"                            },
508   {     13,     "13"                            },
509   {     14,     "14"                            },
510   {     15,     "15"                            },
511   {     16,     "16"                            },
512   {     17,     "17"                            },
513   {     18,     "18"                            },
514   {     19,     "19"                            },
515   {     20,     "20"                            },
516   {     21,     "21"                            },
517   {     22,     "22"                            },
518   {     23,     "23"                            },
519   {     24,     "24"                            },
520   {     25,     "25"                            },
521   {     26,     "26"                            },
522   {     27,     "27"                            },
523   {     28,     "28"                            },
524   {     29,     "29"                            },
525   {     30,     "30"                            },
526   {     31,     "31"                            },
527   {     32,     "32"                            },
528
529   {     -1,     NULL                            },
530 };
531
532 #define DRAW_MODE(s)            ((s) >= GAME_MODE_MAIN &&               \
533                                  (s) <= GAME_MODE_SETUP ? (s) :         \
534                                  (s) == GAME_MODE_PSEUDO_TYPENAME ?     \
535                                  GAME_MODE_MAIN : GAME_MODE_DEFAULT)
536
537 /* (there are no draw offset definitions needed for INFO_MODE_TITLE) */
538 #define DRAW_MODE_INFO(i)       ((i) >= INFO_MODE_TITLE &&              \
539                                  (i) <= INFO_MODE_LEVELSET ? (i) :      \
540                                  INFO_MODE_MAIN)
541
542 #define DRAW_MODE_SETUP(i)      ((i) >= SETUP_MODE_MAIN &&              \
543                                  (i) <= SETUP_MODE_SHORTCUTS_5 ? (i) :  \
544                                  (i) >= SETUP_MODE_CHOOSE_GRAPHICS &&   \
545                                  (i) <= SETUP_MODE_CHOOSE_MUSIC ?       \
546                                  SETUP_MODE_CHOOSE_ARTWORK :            \
547                                  SETUP_MODE_CHOOSE_OTHER)
548
549 #define DRAW_XOFFSET_INFO(i)    (DRAW_MODE_INFO(i) == INFO_MODE_MAIN ?  \
550                                  menu.draw_xoffset[GAME_MODE_INFO] :    \
551                                  menu.draw_xoffset_info[DRAW_MODE_INFO(i)])
552 #define DRAW_YOFFSET_INFO(i)    (DRAW_MODE_INFO(i) == INFO_MODE_MAIN ?  \
553                                  menu.draw_yoffset[GAME_MODE_INFO] :    \
554                                  menu.draw_yoffset_info[DRAW_MODE_INFO(i)])
555 #define EXTRA_SPACING_INFO(i)   (DRAW_MODE_INFO(i) == INFO_MODE_MAIN ? \
556                                  menu.extra_spacing[GAME_MODE_INFO] :   \
557                                  menu.extra_spacing_info[DRAW_MODE_INFO(i)])
558
559 #define DRAW_XOFFSET_SETUP(i)   (DRAW_MODE_SETUP(i) == SETUP_MODE_MAIN ? \
560                                  menu.draw_xoffset[GAME_MODE_SETUP] :   \
561                                  menu.draw_xoffset_setup[DRAW_MODE_SETUP(i)])
562 #define DRAW_YOFFSET_SETUP(i)   (DRAW_MODE_SETUP(i) == SETUP_MODE_MAIN ? \
563                                  menu.draw_yoffset[GAME_MODE_SETUP] :   \
564                                  menu.draw_yoffset_setup[DRAW_MODE_SETUP(i)])
565 #define EXTRA_SPACING_SETUP(i)  (DRAW_MODE_SETUP(i) == SETUP_MODE_MAIN ? \
566                                  menu.extra_spacing[GAME_MODE_SETUP] :  \
567                                  menu.extra_spacing_setup[DRAW_MODE_SETUP(i)])
568
569 #define DRAW_XOFFSET(s)         ((s) == GAME_MODE_INFO ?                \
570                                  DRAW_XOFFSET_INFO(info_mode) :         \
571                                  (s) == GAME_MODE_SETUP ?               \
572                                  DRAW_XOFFSET_SETUP(setup_mode) :       \
573                                  menu.draw_xoffset[DRAW_MODE(s)])
574 #define DRAW_YOFFSET(s)         ((s) == GAME_MODE_INFO ?                \
575                                  DRAW_YOFFSET_INFO(info_mode) :         \
576                                  (s) == GAME_MODE_SETUP ?               \
577                                  DRAW_YOFFSET_SETUP(setup_mode) :       \
578                                  menu.draw_yoffset[DRAW_MODE(s)])
579 #define EXTRA_SPACING(s)        ((s) == GAME_MODE_INFO ?                \
580                                  EXTRA_SPACING_INFO(info_mode) :        \
581                                  (s) == GAME_MODE_SETUP ?               \
582                                  EXTRA_SPACING_SETUP(setup_mode) :      \
583                                  menu.extra_spacing[DRAW_MODE(s)])
584
585 #define mSX                     (SX + DRAW_XOFFSET(game_status))
586 #define mSY                     (SY + DRAW_YOFFSET(game_status))
587
588 #define NUM_MENU_ENTRIES_ON_SCREEN (menu.list_size[game_status] > 2 ?   \
589                                     menu.list_size[game_status] :       \
590                                     MAX_MENU_ENTRIES_ON_SCREEN)
591
592 #define IN_VIS_MENU(x, y)       IN_FIELD(x, y, SCR_FIELDX,              \
593                                          NUM_MENU_ENTRIES_ON_SCREEN)
594
595
596 /* title display and control definitions */
597
598 #define MAX_NUM_TITLE_SCREENS   (2 * MAX_NUM_TITLE_IMAGES +             \
599                                  2 * MAX_NUM_TITLE_MESSAGES)
600
601 #define NO_DIRECT_LEVEL_SELECT  (-1)
602
603
604 static int num_title_screens = 0;
605
606 struct TitleControlInfo
607 {
608   boolean is_image;
609   boolean initial;
610   boolean first;
611   int local_nr;
612   int sort_priority;
613 };
614
615 struct TitleControlInfo title_controls[MAX_NUM_TITLE_SCREENS];
616
617 /* main menu display and control definitions */
618
619 #define MAIN_CONTROL_NAME                       0
620 #define MAIN_CONTROL_LEVELS                     1
621 #define MAIN_CONTROL_SCORES                     2
622 #define MAIN_CONTROL_EDITOR                     3
623 #define MAIN_CONTROL_INFO                       4
624 #define MAIN_CONTROL_GAME                       5
625 #define MAIN_CONTROL_SETUP                      6
626 #define MAIN_CONTROL_QUIT                       7
627 #define MAIN_CONTROL_PREV_LEVEL                 8
628 #define MAIN_CONTROL_NEXT_LEVEL                 9
629 #define MAIN_CONTROL_FIRST_LEVEL                10
630 #define MAIN_CONTROL_LAST_LEVEL                 11
631 #define MAIN_CONTROL_LEVEL_NUMBER               12
632 #define MAIN_CONTROL_LEVEL_INFO_1               13
633 #define MAIN_CONTROL_LEVEL_INFO_2               14
634 #define MAIN_CONTROL_LEVEL_NAME                 15
635 #define MAIN_CONTROL_LEVEL_AUTHOR               16
636 #define MAIN_CONTROL_LEVEL_YEAR                 17
637 #define MAIN_CONTROL_LEVEL_IMPORTED_FROM        18
638 #define MAIN_CONTROL_LEVEL_IMPORTED_BY          19
639 #define MAIN_CONTROL_LEVEL_TESTED_BY            20
640 #define MAIN_CONTROL_TITLE_1                    21
641 #define MAIN_CONTROL_TITLE_2                    22
642 #define MAIN_CONTROL_TITLE_3                    23
643
644 static char str_main_text_name[10];
645 static char str_main_text_first_level[10];
646 static char str_main_text_last_level[10];
647 static char str_main_text_level_number[10];
648
649 static char *main_text_name                     = str_main_text_name;
650 static char *main_text_first_level              = str_main_text_first_level;
651 static char *main_text_last_level               = str_main_text_last_level;
652 static char *main_text_level_number             = str_main_text_level_number;
653 static char *main_text_levels                   = "Levelset";
654 static char *main_text_scores                   = "Hall Of Fame";
655 static char *main_text_editor                   = "Level Creator";
656 static char *main_text_info                     = "Info Screen";
657 static char *main_text_game                     = "Start Game";
658 static char *main_text_setup                    = "Setup";
659 static char *main_text_quit                     = "Quit";
660 static char *main_text_level_name               = level.name;
661 static char *main_text_level_author             = level.author;
662 static char *main_text_level_year               = NULL;
663 static char *main_text_level_imported_from      = NULL;
664 static char *main_text_level_imported_by        = NULL;
665 static char *main_text_level_tested_by          = NULL;
666 static char *main_text_title_1                  = NULL;
667 static char *main_text_title_2                  = NULL;
668 static char *main_text_title_3                  = NULL;
669
670 struct MainControlInfo
671 {
672   int nr;
673
674   struct MenuPosInfo *pos_button;
675   int button_graphic;
676
677   struct TextPosInfo *pos_text;
678   char **text;
679
680   struct TextPosInfo *pos_input;
681   char **input;
682 };
683
684 static struct MainControlInfo main_controls[] =
685 {
686   {
687     MAIN_CONTROL_NAME,
688     &menu.main.button.name,             IMG_MENU_BUTTON_NAME,
689     &menu.main.text.name,               &main_text_name,
690     &menu.main.input.name,              &setup.player_name,
691   },
692   {
693     MAIN_CONTROL_LEVELS,
694     &menu.main.button.levels,           IMG_MENU_BUTTON_LEVELS,
695     &menu.main.text.levels,             &main_text_levels,
696     NULL,                               NULL,
697   },
698   {
699     MAIN_CONTROL_SCORES,
700     &menu.main.button.scores,           IMG_MENU_BUTTON_SCORES,
701     &menu.main.text.scores,             &main_text_scores,
702     NULL,                               NULL,
703   },
704   {
705     MAIN_CONTROL_EDITOR,
706     &menu.main.button.editor,           IMG_MENU_BUTTON_EDITOR,
707     &menu.main.text.editor,             &main_text_editor,
708     NULL,                               NULL,
709   },
710   {
711     MAIN_CONTROL_INFO,
712     &menu.main.button.info,             IMG_MENU_BUTTON_INFO,
713     &menu.main.text.info,               &main_text_info,
714     NULL,                               NULL,
715   },
716   {
717     MAIN_CONTROL_GAME,
718     &menu.main.button.game,             IMG_MENU_BUTTON_GAME,
719     &menu.main.text.game,               &main_text_game,
720     NULL,                               NULL,
721   },
722   {
723     MAIN_CONTROL_SETUP,
724     &menu.main.button.setup,            IMG_MENU_BUTTON_SETUP,
725     &menu.main.text.setup,              &main_text_setup,
726     NULL,                               NULL,
727   },
728   {
729     MAIN_CONTROL_QUIT,
730     &menu.main.button.quit,             IMG_MENU_BUTTON_QUIT,
731     &menu.main.text.quit,               &main_text_quit,
732     NULL,                               NULL,
733   },
734 #if 0
735   /* (these two buttons are real gadgets) */
736   {
737     MAIN_CONTROL_PREV_LEVEL,
738     &menu.main.button.prev_level,       IMG_MENU_BUTTON_PREV_LEVEL,
739     NULL,                               NULL,
740     NULL,                               NULL,
741   },
742   {
743     MAIN_CONTROL_NEXT_LEVEL,
744     &menu.main.button.next_level,       IMG_MENU_BUTTON_NEXT_LEVEL,
745     NULL,                               NULL,
746     NULL,                               NULL,
747   },
748 #endif
749   {
750     MAIN_CONTROL_FIRST_LEVEL,
751     NULL,                               -1,
752     &menu.main.text.first_level,        &main_text_first_level,
753     NULL,                               NULL,
754   },
755   {
756     MAIN_CONTROL_LAST_LEVEL,
757     NULL,                               -1,
758     &menu.main.text.last_level,         &main_text_last_level,
759     NULL,                               NULL,
760   },
761   {
762     MAIN_CONTROL_LEVEL_NUMBER,
763     NULL,                               -1,
764     &menu.main.text.level_number,       &main_text_level_number,
765     NULL,                               NULL,
766   },
767   {
768     MAIN_CONTROL_LEVEL_INFO_1,
769     NULL,                               -1,
770     &menu.main.text.level_info_1,       NULL,
771     NULL,                               NULL,
772   },
773   {
774     MAIN_CONTROL_LEVEL_INFO_2,
775     NULL,                               -1,
776     &menu.main.text.level_info_2,       NULL,
777     NULL,                               NULL,
778   },
779   {
780     MAIN_CONTROL_LEVEL_NAME,
781     NULL,                               -1,
782     &menu.main.text.level_name,         &main_text_level_name,
783     NULL,                               NULL,
784   },
785   {
786     MAIN_CONTROL_LEVEL_AUTHOR,
787     NULL,                               -1,
788     &menu.main.text.level_author,       &main_text_level_author,
789     NULL,                               NULL,
790   },
791   {
792     MAIN_CONTROL_LEVEL_YEAR,
793     NULL,                               -1,
794     &menu.main.text.level_year,         &main_text_level_year,
795     NULL,                               NULL,
796   },
797   {
798     MAIN_CONTROL_LEVEL_IMPORTED_FROM,
799     NULL,                               -1,
800     &menu.main.text.level_imported_from, &main_text_level_imported_from,
801     NULL,                               NULL,
802   },
803   {
804     MAIN_CONTROL_LEVEL_IMPORTED_BY,
805     NULL,                               -1,
806     &menu.main.text.level_imported_by,  &main_text_level_imported_by,
807     NULL,                               NULL,
808   },
809   {
810     MAIN_CONTROL_LEVEL_TESTED_BY,
811     NULL,                               -1,
812     &menu.main.text.level_tested_by,    &main_text_level_tested_by,
813     NULL,                               NULL,
814   },
815   {
816     MAIN_CONTROL_TITLE_1,
817     NULL,                               -1,
818     &menu.main.text.title_1,            &main_text_title_1,
819     NULL,                               NULL,
820   },
821   {
822     MAIN_CONTROL_TITLE_2,
823     NULL,                               -1,
824     &menu.main.text.title_2,            &main_text_title_2,
825     NULL,                               NULL,
826   },
827   {
828     MAIN_CONTROL_TITLE_3,
829     NULL,                               -1,
830     &menu.main.text.title_3,            &main_text_title_3,
831     NULL,                               NULL,
832   },
833
834   {
835     -1,
836     NULL,                               -1,
837     NULL,                               NULL,
838     NULL,                               NULL,
839   }
840 };
841
842
843 static int getTitleScreenGraphic(int nr, boolean initial)
844 {
845   return (initial ? IMG_TITLESCREEN_INITIAL_1 : IMG_TITLESCREEN_1) + nr;
846 }
847
848 static struct TitleMessageInfo *getTitleMessageInfo(int nr, boolean initial)
849 {
850   return (initial ? &titlemessage_initial[nr] : &titlemessage[nr]);
851 }
852
853 #if 0
854 static int getTitleScreenGameMode(boolean initial)
855 {
856   return (initial ? GAME_MODE_TITLE_INITIAL : GAME_MODE_TITLE);
857 }
858 #endif
859
860 static int getTitleMessageGameMode(boolean initial)
861 {
862   return (initial ? GAME_MODE_TITLE_INITIAL : GAME_MODE_TITLE);
863 }
864
865 static int getTitleAnimMode(struct TitleControlInfo *tci)
866 {
867   int base = (tci->initial ? GAME_MODE_TITLE_INITIAL_1 : GAME_MODE_TITLE_1);
868
869   return base + tci->local_nr;
870 }
871
872 #if 0
873 static int getTitleScreenBackground(boolean initial)
874 {
875   return (initial ? IMG_BACKGROUND_TITLE_INITIAL : IMG_BACKGROUND_TITLE);
876 }
877 #endif
878
879 #if 0
880 static int getTitleMessageBackground(int nr, boolean initial)
881 {
882   return (initial ? IMG_BACKGROUND_TITLE_INITIAL : IMG_BACKGROUND_TITLE);
883 }
884 #endif
885
886 static int getTitleBackground(int nr, boolean initial, boolean is_image)
887 {
888   int base = (is_image ?
889               (initial ? IMG_BACKGROUND_TITLESCREEN_INITIAL_1 :
890                          IMG_BACKGROUND_TITLESCREEN_1) :
891               (initial ? IMG_BACKGROUND_TITLEMESSAGE_INITIAL_1 :
892                          IMG_BACKGROUND_TITLEMESSAGE_1));
893   int graphic_global = (initial ? IMG_BACKGROUND_TITLE_INITIAL :
894                                   IMG_BACKGROUND_TITLE);
895   int graphic_local = base + nr;
896
897   if (graphic_info[graphic_local].bitmap != NULL)
898     return graphic_local;
899
900   if (graphic_info[graphic_global].bitmap != NULL)
901     return graphic_global;
902
903   return IMG_UNDEFINED;
904 }
905
906 static int getTitleSound(struct TitleControlInfo *tci)
907 {
908   boolean is_image = tci->is_image;
909   int initial = tci->initial;
910   int nr = tci->local_nr;
911   int mode = (initial ? GAME_MODE_TITLE_INITIAL : GAME_MODE_TITLE);
912   int base = (is_image ?
913               (initial ? SND_BACKGROUND_TITLESCREEN_INITIAL_1 :
914                          SND_BACKGROUND_TITLESCREEN_1) :
915               (initial ? SND_BACKGROUND_TITLEMESSAGE_INITIAL_1 :
916                          SND_BACKGROUND_TITLEMESSAGE_1));
917   int sound_global = menu.sound[mode];
918   int sound_local = base + nr;
919
920 #if 0
921   printf("::: %d, %d, %d: %d ['%s'], %d ['%s']\n",
922          nr, initial, is_image,
923          sound_global, getSoundListEntry(sound_global)->filename,
924          sound_local, getSoundListEntry(sound_local)->filename);
925 #endif
926
927   if (!strEqual(getSoundListEntry(sound_local)->filename, UNDEFINED_FILENAME))
928     return sound_local;
929
930   if (!strEqual(getSoundListEntry(sound_global)->filename, UNDEFINED_FILENAME))
931     return sound_global;
932
933   return SND_UNDEFINED;
934 }
935
936 static int getTitleMusic(struct TitleControlInfo *tci)
937 {
938   boolean is_image = tci->is_image;
939   int initial = tci->initial;
940   int nr = tci->local_nr;
941   int mode = (initial ? GAME_MODE_TITLE_INITIAL : GAME_MODE_TITLE);
942   int base = (is_image ?
943               (initial ? MUS_BACKGROUND_TITLESCREEN_INITIAL_1 :
944                          MUS_BACKGROUND_TITLESCREEN_1) :
945               (initial ? MUS_BACKGROUND_TITLEMESSAGE_INITIAL_1 :
946                          MUS_BACKGROUND_TITLEMESSAGE_1));
947   int music_global = menu.music[mode];
948   int music_local = base + nr;
949
950 #if 0
951   printf("::: %d, %d, %d: %d ['%s'], %d ['%s']\n",
952          nr, initial, is_image,
953          music_global, getMusicListEntry(music_global)->filename,
954          music_local, getMusicListEntry(music_local)->filename);
955 #endif
956
957   if (!strEqual(getMusicListEntry(music_local)->filename, UNDEFINED_FILENAME))
958     return music_local;
959
960   if (!strEqual(getMusicListEntry(music_global)->filename, UNDEFINED_FILENAME))
961     return music_global;
962
963   return MUS_UNDEFINED;
964 }
965
966 static struct TitleFadingInfo getTitleFading(struct TitleControlInfo *tci)
967 {
968   boolean is_image = tci->is_image;
969   boolean initial = tci->initial;
970   boolean first = tci->first;
971   int nr = tci->local_nr;
972   struct TitleMessageInfo tmi;
973   struct TitleFadingInfo ti;
974
975   tmi = (is_image ? (initial ? (first ?
976                                 titlescreen_initial_first[nr] :
977                                 titlescreen_initial[nr])
978                              : (first ?
979                                 titlescreen_first[nr] :
980                                 titlescreen[nr]))
981                   : (initial ? (first ?
982                                 titlemessage_initial_first[nr] :
983                                 titlemessage_initial[nr])
984                              : (first ?
985                                 titlemessage_first[nr] :
986                                 titlemessage[nr])));
987
988   ti.fade_mode  = tmi.fade_mode;
989   ti.fade_delay = tmi.fade_delay;
990   ti.post_delay = tmi.post_delay;
991   ti.auto_delay = tmi.auto_delay;
992
993   return ti;
994 }
995
996 static int compareTitleControlInfo(const void *object1, const void *object2)
997 {
998   const struct TitleControlInfo *tci1 = (struct TitleControlInfo *)object1;
999   const struct TitleControlInfo *tci2 = (struct TitleControlInfo *)object2;
1000   int compare_result;
1001
1002   if (tci1->initial != tci2->initial)
1003     compare_result = (tci1->initial ? -1 : +1);
1004   else if (tci1->sort_priority != tci2->sort_priority)
1005     compare_result = tci1->sort_priority - tci2->sort_priority;
1006   else if (tci1->is_image != tci2->is_image)
1007     compare_result = (tci1->is_image ? -1 : +1);
1008   else
1009     compare_result = tci1->local_nr - tci2->local_nr;
1010
1011   return compare_result;
1012 }
1013
1014 static void InitializeTitleControlsExt_AddTitleInfo(boolean is_image,
1015                                                     boolean initial,
1016                                                     int nr, int sort_priority)
1017 {
1018   title_controls[num_title_screens].is_image = is_image;
1019   title_controls[num_title_screens].initial = initial;
1020   title_controls[num_title_screens].local_nr = nr;
1021   title_controls[num_title_screens].sort_priority = sort_priority;
1022
1023   title_controls[num_title_screens].first = FALSE;      /* will be set later */
1024
1025   num_title_screens++;
1026 }
1027
1028 static void InitializeTitleControls_CheckTitleInfo(boolean initial)
1029 {
1030   int i;
1031
1032   for (i = 0; i < MAX_NUM_TITLE_IMAGES; i++)
1033   {
1034     int graphic = getTitleScreenGraphic(i, initial);
1035     Bitmap *bitmap = graphic_info[graphic].bitmap;
1036     int sort_priority = graphic_info[graphic].sort_priority;
1037
1038     if (bitmap != NULL)
1039       InitializeTitleControlsExt_AddTitleInfo(TRUE, initial, i, sort_priority);
1040   }
1041
1042   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
1043   {
1044     struct TitleMessageInfo *tmi = getTitleMessageInfo(i, initial);
1045     char *filename = getLevelSetTitleMessageFilename(i, initial);
1046     int sort_priority = tmi->sort_priority;
1047
1048     if (filename != NULL)
1049       InitializeTitleControlsExt_AddTitleInfo(FALSE, initial, i, sort_priority);
1050   }
1051 }
1052
1053 static void InitializeTitleControls(boolean show_title_initial)
1054 {
1055   num_title_screens = 0;
1056
1057   /* 1st step: initialize title screens for game start (only when starting) */
1058   if (show_title_initial)
1059     InitializeTitleControls_CheckTitleInfo(TRUE);
1060
1061   /* 2nd step: initialize title screens for current level set */
1062   InitializeTitleControls_CheckTitleInfo(FALSE);
1063
1064   /* sort title screens according to sort_priority and title number */
1065   qsort(title_controls, num_title_screens, sizeof(struct TitleControlInfo),
1066         compareTitleControlInfo);
1067
1068   /* mark first title screen */
1069   title_controls[0].first = TRUE;
1070 }
1071
1072 static boolean visibleMenuPos(struct MenuPosInfo *pos)
1073 {
1074   return (pos != NULL && pos->x != -1 && pos->y != -1);
1075 }
1076
1077 static boolean visibleTextPos(struct TextPosInfo *pos)
1078 {
1079   return (pos != NULL && pos->x != -1 && pos->y != -1);
1080 }
1081
1082 static void InitializeMainControls()
1083 {
1084   boolean local_team_mode = (!options.network && setup.team_mode);
1085   int i;
1086
1087   /* set main control text values to dynamically determined values */
1088   sprintf(main_text_name,         "%s",   local_team_mode ? "Team:" : "Name:");
1089
1090   strcpy(main_text_first_level,  int2str(leveldir_current->first_level,
1091                                          menu.main.text.first_level.size));
1092   strcpy(main_text_last_level,   int2str(leveldir_current->last_level,
1093                                          menu.main.text.last_level.size));
1094   strcpy(main_text_level_number, int2str(level_nr,
1095                                          menu.main.text.level_number.size));
1096
1097   main_text_level_year          = leveldir_current->year;
1098   main_text_level_imported_from = leveldir_current->imported_from;
1099   main_text_level_imported_by   = leveldir_current->imported_by;
1100   main_text_level_tested_by     = leveldir_current->tested_by;
1101
1102   main_text_title_1 = getConfigProgramTitleString();
1103   main_text_title_2 = getConfigProgramCopyrightString();
1104   main_text_title_3 = getConfigProgramCompanyString();
1105
1106   /* set main control screen positions to dynamically determined values */
1107   for (i = 0; main_controls[i].nr != -1; i++)
1108   {
1109     struct MainControlInfo *mci = &main_controls[i];
1110     int nr                         = mci->nr;
1111     struct MenuPosInfo *pos_button = mci->pos_button;
1112     struct TextPosInfo *pos_text   = mci->pos_text;
1113     struct TextPosInfo *pos_input  = mci->pos_input;
1114     char *text                     = (mci->text  ? *mci->text  : NULL);
1115     char *input                    = (mci->input ? *mci->input : NULL);
1116     int button_graphic             = mci->button_graphic;
1117     int font_text                  = (pos_text  ? pos_text->font  : -1);
1118     int font_input                 = (pos_input ? pos_input->font : -1);
1119
1120     int font_text_width   = (font_text  != -1 ? getFontWidth(font_text)   : 0);
1121     int font_text_height  = (font_text  != -1 ? getFontHeight(font_text)  : 0);
1122     int font_input_width  = (font_input != -1 ? getFontWidth(font_input)  : 0);
1123     int font_input_height = (font_input != -1 ? getFontHeight(font_input) : 0);
1124     int text_chars  = (text  != NULL ? strlen(text)  : 0);
1125     int input_chars = (input != NULL ? strlen(input) : 0);
1126
1127     int button_width =
1128       (button_graphic != -1 ? graphic_info[button_graphic].width  : 0);
1129     int button_height =
1130       (button_graphic != -1 ? graphic_info[button_graphic].height : 0);
1131     int text_width   = font_text_width * text_chars;
1132     int text_height  = font_text_height;
1133     int input_width  = font_input_width * input_chars;
1134     int input_height = font_input_height;
1135
1136     if (nr == MAIN_CONTROL_NAME)
1137     {
1138       menu.main.input.name.width  = input_width;
1139       menu.main.input.name.height = input_height;
1140     }
1141
1142     if (pos_button != NULL)             /* (x/y may be -1/-1 here) */
1143     {
1144       pos_button->width  = button_width;
1145       pos_button->height = button_height;
1146     }
1147
1148     if (pos_text != NULL)               /* (x/y may be -1/-1 here) */
1149     {
1150       /* calculate size for non-clickable text -- needed for text alignment */
1151       boolean calculate_text_size = (pos_button == NULL && text != NULL);
1152
1153       if (pos_text->width == -1 || calculate_text_size)
1154         pos_text->width = text_width;
1155       if (pos_text->height == -1 || calculate_text_size)
1156         pos_text->height = text_height;
1157
1158       if (visibleMenuPos(pos_button))
1159       {
1160         if (pos_text->x == -1)
1161           pos_text->x = pos_button->x + pos_button->width;
1162         if (pos_text->y == -1)
1163           pos_text->y =
1164             pos_button->y + (pos_button->height - pos_text->height) / 2;
1165       }
1166     }
1167
1168     if (pos_input != NULL)              /* (x/y may be -1/-1 here) */
1169     {
1170       if (visibleTextPos(pos_text))
1171       {
1172         if (pos_input->x == -1)
1173           pos_input->x = pos_text->x + pos_text->width;
1174         if (pos_input->y == -1)
1175           pos_input->y = pos_text->y;
1176       }
1177
1178       if (pos_input->width == -1)
1179         pos_input->width = input_width;
1180       if (pos_input->height == -1)
1181         pos_input->height = input_height;
1182     }
1183   }
1184 }
1185
1186 static void DrawPressedGraphicThruMask(int dst_x, int dst_y,
1187                                        int graphic, boolean pressed)
1188 {
1189   struct GraphicInfo *g = &graphic_info[graphic];
1190   Bitmap *src_bitmap;
1191   int src_x, src_y;
1192   int xoffset = (pressed ? g->pressed_xoffset : 0);
1193   int yoffset = (pressed ? g->pressed_yoffset : 0);
1194
1195   getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1196
1197   BlitBitmapMasked(src_bitmap, drawto, src_x + xoffset, src_y + yoffset,
1198                    g->width, g->height, dst_x, dst_y);
1199 }
1200
1201 static void DrawCursorAndText_Main_Ext(int nr, boolean active_text,
1202                                        boolean active_input,
1203                                        boolean pressed_button)
1204 {
1205   int i;
1206
1207   for (i = 0; main_controls[i].nr != -1; i++)
1208   {
1209     struct MainControlInfo *mci = &main_controls[i];
1210
1211     if (mci->nr == nr || nr == -1)
1212     {
1213       struct MenuPosInfo *pos_button = mci->pos_button;
1214       struct TextPosInfo *pos_text   = mci->pos_text;
1215       struct TextPosInfo *pos_input  = mci->pos_input;
1216       char *text                     = (mci->text  ? *mci->text  : NULL);
1217       char *input                    = (mci->input ? *mci->input : NULL);
1218       int button_graphic             = mci->button_graphic;
1219       int font_text                  = (pos_text  ? pos_text->font  : -1);
1220       int font_input                 = (pos_input ? pos_input->font : -1);
1221
1222       if (active_text)
1223       {
1224         button_graphic = BUTTON_ACTIVE(button_graphic);
1225         font_text = FONT_ACTIVE(font_text);
1226       }
1227
1228       if (active_input)
1229       {
1230         font_input = FONT_ACTIVE(font_input);
1231       }
1232
1233       if (visibleMenuPos(pos_button))
1234       {
1235         struct MenuPosInfo *pos = pos_button;
1236         int x = mSX + pos->x;
1237         int y = mSY + pos->y;
1238
1239         DrawBackgroundForGraphic(x, y, pos->width, pos->height, button_graphic);
1240         DrawPressedGraphicThruMask(x, y, button_graphic, pressed_button);
1241       }
1242
1243       if (visibleTextPos(pos_text) && text != NULL)
1244       {
1245         struct TextPosInfo *pos = pos_text;
1246         int x = mSX + ALIGNED_TEXT_XPOS(pos);
1247         int y = mSY + ALIGNED_TEXT_YPOS(pos);
1248
1249 #if 1
1250         /* (check why/if this is needed) */
1251         DrawBackgroundForFont(x, y, pos->width, pos->height, font_text);
1252 #endif
1253         DrawText(x, y, text, font_text);
1254       }
1255
1256       if (visibleTextPos(pos_input) && input != NULL)
1257       {
1258         struct TextPosInfo *pos = pos_input;
1259         int x = mSX + ALIGNED_TEXT_XPOS(pos);
1260         int y = mSY + ALIGNED_TEXT_YPOS(pos);
1261
1262 #if 1
1263         /* (check why/if this is needed) */
1264         DrawBackgroundForFont(x, y, pos->width, pos->height, font_input);
1265 #endif
1266         DrawText(x, y, input, font_input);
1267       }
1268     }
1269   }
1270 }
1271
1272 static void DrawCursorAndText_Main(int nr, boolean active_text,
1273                                    boolean pressed_button)
1274 {
1275   DrawCursorAndText_Main_Ext(nr, active_text, FALSE, pressed_button);
1276 }
1277
1278 #if 0
1279 static void DrawCursorAndText_Main_Input(int nr, boolean active_text,
1280                                          boolean pressed_button)
1281 {
1282   DrawCursorAndText_Main_Ext(nr, active_text, TRUE, pressed_button);
1283 }
1284 #endif
1285
1286 static struct MainControlInfo *getMainControlInfo(int nr)
1287 {
1288   int i;
1289
1290   for (i = 0; main_controls[i].nr != -1; i++)
1291     if (main_controls[i].nr == nr)
1292       return &main_controls[i];
1293
1294   return NULL;
1295 }
1296
1297 static boolean insideMenuPosRect(struct MenuPosInfo *rect, int x, int y)
1298 {
1299   if (rect == NULL)
1300     return FALSE;
1301
1302   int rect_x = ALIGNED_TEXT_XPOS(rect);
1303   int rect_y = ALIGNED_TEXT_YPOS(rect);
1304
1305   return (x >= rect_x && x < rect_x + rect->width &&
1306           y >= rect_y && y < rect_y + rect->height);
1307 }
1308
1309 static boolean insideTextPosRect(struct TextPosInfo *rect, int x, int y)
1310 {
1311   if (rect == NULL)
1312     return FALSE;
1313
1314   int rect_x = ALIGNED_TEXT_XPOS(rect);
1315   int rect_y = ALIGNED_TEXT_YPOS(rect);
1316
1317 #if 0
1318   printf("::: insideTextPosRect: (%d, %d), (%d, %d) [%d, %d] (%d, %d) => %d\n",
1319          x, y, rect_x, rect_y, rect->x, rect->y, rect->width, rect->height,
1320          (x >= rect_x && x < rect_x + rect->width &&
1321           y >= rect_y && y < rect_y + rect->height));
1322 #endif
1323
1324   return (x >= rect_x && x < rect_x + rect->width &&
1325           y >= rect_y && y < rect_y + rect->height);
1326 }
1327
1328 static boolean insidePreviewRect(struct PreviewInfo *preview, int x, int y)
1329 {
1330   int rect_width  = preview->xsize * preview->tile_size;
1331   int rect_height = preview->ysize * preview->tile_size;
1332   int rect_x = ALIGNED_XPOS(preview->x, rect_width,  preview->align);
1333   int rect_y = ALIGNED_YPOS(preview->y, rect_height, preview->valign);
1334
1335   return (x >= rect_x && x < rect_x + rect_width &&
1336           y >= rect_y && y < rect_y + rect_height);
1337 }
1338
1339 static void AdjustScrollbar(int id, int items_max, int items_visible,
1340                             int item_position)
1341 {
1342   struct GadgetInfo *gi = screen_gadget[id];
1343
1344   if (item_position > items_max - items_visible)
1345     item_position = items_max - items_visible;
1346
1347   ModifyGadget(gi, GDI_SCROLLBAR_ITEMS_MAX, items_max,
1348                GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
1349                GDI_SCROLLBAR_ITEM_POSITION, item_position, GDI_END);
1350 }
1351
1352 static void AdjustChooseTreeScrollbar(int id, int first_entry, TreeInfo *ti)
1353 {
1354   AdjustScrollbar(id, numTreeInfoInGroup(ti), NUM_MENU_ENTRIES_ON_SCREEN,
1355                   first_entry);
1356 }
1357
1358 static void clearMenuListArea()
1359 {
1360   int scrollbar_xpos = mSX + SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset;
1361
1362   /* correct scrollbar position if placed outside menu (playfield) area */
1363   if (scrollbar_xpos > SX + SC_SCROLLBAR_XPOS)
1364     scrollbar_xpos = SX + SC_SCROLLBAR_XPOS;
1365
1366   /* clear menu list area, but not title or scrollbar */
1367   DrawBackground(mSX, mSY + MENU_SCREEN_START_YPOS * 32,
1368                  scrollbar_xpos - mSX, NUM_MENU_ENTRIES_ON_SCREEN * 32);
1369 }
1370
1371 static void drawCursorExt(int xpos, int ypos, boolean active, int graphic)
1372 {
1373   static int cursor_array[MAX_LEV_FIELDY];
1374   int x = mSX + TILEX * xpos;
1375   int y = mSY + TILEY * (MENU_SCREEN_START_YPOS + ypos);
1376
1377   if (xpos == 0)
1378   {
1379     if (graphic != -1)
1380       cursor_array[ypos] = graphic;
1381     else
1382       graphic = cursor_array[ypos];
1383   }
1384
1385   if (active)
1386     graphic = BUTTON_ACTIVE(graphic);
1387
1388   DrawBackgroundForGraphic(x, y, TILEX, TILEY, graphic);
1389   DrawFixedGraphicThruMaskExt(drawto, x, y, graphic, 0);
1390 }
1391
1392 static void initCursor(int ypos, int graphic)
1393 {
1394   drawCursorExt(0, ypos, FALSE, graphic);
1395 }
1396
1397 static void drawCursor(int ypos, boolean active)
1398 {
1399   drawCursorExt(0, ypos, active, -1);
1400 }
1401
1402 static void drawCursorXY(int xpos, int ypos, int graphic)
1403 {
1404   drawCursorExt(xpos, ypos, FALSE, graphic);
1405 }
1406
1407 static void drawChooseTreeCursor(int ypos, boolean active)
1408 {
1409   drawCursorExt(0, ypos, active, -1);
1410 }
1411
1412 void DrawHeadline()
1413 {
1414   DrawTextSCentered(MENU_TITLE1_YPOS, FONT_TITLE_1, main_text_title_1);
1415   DrawTextSCentered(MENU_TITLE2_YPOS, FONT_TITLE_2, main_text_title_2);
1416 }
1417
1418 void DrawTitleScreenImage(int nr, boolean initial)
1419 {
1420   int graphic = getTitleScreenGraphic(nr, initial);
1421   Bitmap *bitmap = graphic_info[graphic].bitmap;
1422   int width  = graphic_info[graphic].width;
1423   int height = graphic_info[graphic].height;
1424   int src_x = graphic_info[graphic].src_x;
1425   int src_y = graphic_info[graphic].src_y;
1426   int dst_x, dst_y;
1427
1428   if (bitmap == NULL)
1429     return;
1430
1431   if (width > WIN_XSIZE)
1432   {
1433     /* image width too large for window => center image horizontally */
1434     src_x = (width - WIN_XSIZE) / 2;
1435     width = WIN_XSIZE;
1436   }
1437
1438   if (height > WIN_YSIZE)
1439   {
1440     /* image height too large for window => center image vertically */
1441     src_y = (height - WIN_YSIZE) / 2;
1442     height = WIN_YSIZE;
1443   }
1444
1445   /* always display title screens centered */
1446   dst_x = (WIN_XSIZE - width) / 2;
1447   dst_y = (WIN_YSIZE - height) / 2;
1448
1449   SetDrawBackgroundMask(REDRAW_ALL);
1450   SetWindowBackgroundImage(getTitleBackground(nr, initial, TRUE));
1451
1452   ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
1453
1454   if (DrawingOnBackground(dst_x, dst_y))
1455     BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
1456   else
1457     BlitBitmap(bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
1458
1459   redraw_mask = REDRAW_ALL;
1460 }
1461
1462 void DrawTitleScreenMessage(int nr, boolean initial)
1463 {
1464   char *filename = getLevelSetTitleMessageFilename(nr, initial);
1465   struct TitleMessageInfo *tmi = getTitleMessageInfo(nr, initial);
1466
1467   if (filename == NULL)
1468     return;
1469
1470   /* force TITLE font on title message screen */
1471   SetFontStatus(getTitleMessageGameMode(initial));
1472
1473   /* if chars *and* width set to "-1", automatically determine width */
1474   if (tmi->chars == -1 && tmi->width == -1)
1475     tmi->width = viewport.window[game_status].width;
1476
1477   /* if lines *and* height set to "-1", automatically determine height */
1478   if (tmi->lines == -1 && tmi->height == -1)
1479     tmi->height = viewport.window[game_status].height;
1480
1481   /* if chars set to "-1", automatically determine by text and font width */
1482   if (tmi->chars == -1)
1483     tmi->chars = tmi->width / getFontWidth(tmi->font);
1484   else
1485     tmi->width = tmi->chars * getFontWidth(tmi->font);
1486
1487   /* if lines set to "-1", automatically determine by text and font height */
1488   if (tmi->lines == -1)
1489     tmi->lines = tmi->height / getFontHeight(tmi->font);
1490   else
1491     tmi->height = tmi->lines * getFontHeight(tmi->font);
1492
1493   /* if x set to "-1", automatically determine by width and alignment */
1494   if (tmi->x == -1)
1495     tmi->x = -1 * ALIGNED_XPOS(0, tmi->width, tmi->align);
1496
1497   /* if y set to "-1", automatically determine by height and alignment */
1498   if (tmi->y == -1)
1499     tmi->y = -1 * ALIGNED_YPOS(0, tmi->height, tmi->valign);
1500
1501   SetDrawBackgroundMask(REDRAW_ALL);
1502   SetWindowBackgroundImage(getTitleBackground(nr, initial, FALSE));
1503
1504   ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
1505
1506   DrawTextFile(ALIGNED_TEXT_XPOS(tmi), ALIGNED_TEXT_YPOS(tmi),
1507                filename, tmi->font, tmi->chars, -1, tmi->lines, 0, -1,
1508                tmi->autowrap, tmi->centered, tmi->parse_comments);
1509
1510   ResetFontStatus();
1511 }
1512
1513 void DrawTitleScreen()
1514 {
1515   KeyboardAutoRepeatOff();
1516
1517   HandleTitleScreen(0, 0, 0, 0, MB_MENU_INITIALIZE);
1518 }
1519
1520 boolean CheckTitleScreen(boolean levelset_has_changed)
1521 {
1522   static boolean show_title_initial = TRUE;
1523   boolean show_titlescreen = FALSE;
1524
1525   /* needed to be able to skip title screen, if no image or message defined */
1526   InitializeTitleControls(show_title_initial);
1527
1528   if (setup.show_titlescreen && (show_title_initial || levelset_has_changed))
1529     show_titlescreen = TRUE;
1530
1531   /* show initial title images and messages only once at program start */
1532   show_title_initial = FALSE;
1533
1534   return (show_titlescreen && num_title_screens > 0);
1535 }
1536
1537 void DrawMainMenu()
1538 {
1539   static LevelDirTree *leveldir_last_valid = NULL;
1540   boolean levelset_has_changed = FALSE;
1541   int fade_mask = REDRAW_FIELD;
1542
1543   LimitScreenUpdates(FALSE);
1544
1545   FadeSetLeaveScreen();
1546
1547   /* do not fade out here -- function may continue and fade on editor screen */
1548
1549   UnmapAllGadgets();
1550   FadeMenuSoundsAndMusic();
1551
1552   ExpireSoundLoops(FALSE);
1553
1554   KeyboardAutoRepeatOn();
1555
1556   audio.sound_deactivated = FALSE;
1557
1558   GetPlayerConfig();
1559
1560   /* needed if last screen was the playing screen, invoked from level editor */
1561   if (level_editor_test_game)
1562   {
1563     CloseDoor(DOOR_CLOSE_ALL);
1564
1565     SetGameStatus(GAME_MODE_EDITOR);
1566
1567     DrawLevelEd();
1568
1569     return;
1570   }
1571
1572   /* needed if last screen was the setup screen and fullscreen state changed */
1573   // (moved to "execSetupGraphics()" to change fullscreen state directly)
1574   // ToggleFullscreenOrChangeWindowScalingIfNeeded();
1575
1576   /* leveldir_current may be invalid (level group, parent link) */
1577   if (!validLevelSeries(leveldir_current))
1578     leveldir_current = getFirstValidTreeInfoEntry(leveldir_last_valid);
1579
1580   if (leveldir_current != leveldir_last_valid)
1581     levelset_has_changed = TRUE;
1582
1583   /* store valid level series information */
1584   leveldir_last_valid = leveldir_current;
1585
1586   init_last = init;                     /* switch to new busy animation */
1587
1588   /* needed if last screen (level choice) changed graphics, sounds or music */
1589   ReloadCustomArtwork(0);
1590
1591   if (CheckTitleScreen(levelset_has_changed))
1592   {
1593     SetGameStatus(GAME_MODE_TITLE);
1594
1595     DrawTitleScreen();
1596
1597     return;
1598   }
1599
1600   if (redraw_mask & REDRAW_ALL)
1601     fade_mask = REDRAW_ALL;
1602
1603   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
1604     fade_mask = REDRAW_ALL;
1605
1606   FadeOut(fade_mask);
1607
1608   /* needed if different viewport properties defined for menues */
1609   ChangeViewportPropertiesIfNeeded();
1610
1611   SetDrawtoField(DRAW_TO_BACKBUFFER);
1612
1613   /* level_nr may have been set to value over handicap with level editor */
1614   if (setup.handicap && level_nr > leveldir_current->handicap_level)
1615     level_nr = leveldir_current->handicap_level;
1616
1617   LoadLevel(level_nr);
1618   LoadScore(level_nr);
1619
1620   SaveLevelSetup_SeriesInfo();
1621
1622   // set this after "ChangeViewportPropertiesIfNeeded()" (which may reset it)
1623   SetDrawDeactivationMask(REDRAW_NONE);
1624   SetDrawBackgroundMask(REDRAW_FIELD);
1625
1626   SetMainBackgroundImage(IMG_BACKGROUND_MAIN);
1627
1628 #if 0
1629   if (fade_mask == REDRAW_ALL)
1630     RedrawGlobalBorder();
1631 #endif
1632
1633   ClearField();
1634
1635   InitializeMainControls();
1636
1637   DrawCursorAndText_Main(-1, FALSE, FALSE);
1638   DrawPreviewLevelInitial();
1639
1640   HandleMainMenu(0, 0, 0, 0, MB_MENU_INITIALIZE);
1641
1642   TapeStop();
1643   if (TAPE_IS_EMPTY(tape))
1644     LoadTape(level_nr);
1645   DrawCompleteVideoDisplay();
1646
1647   PlayMenuSoundsAndMusic();
1648
1649   /* create gadgets for main menu screen */
1650   FreeScreenGadgets();
1651   CreateScreenGadgets();
1652
1653   /* map gadgets for main menu screen */
1654   MapTapeButtons();
1655   MapScreenMenuGadgets(SCREEN_MASK_MAIN);
1656
1657   /* copy actual game door content to door double buffer for OpenDoor() */
1658   BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
1659   BlitBitmap(drawto, bitmap_db_door_2, VX, VY, VXSIZE, VYSIZE, 0, 0);
1660
1661   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
1662
1663   DrawMaskedBorder(fade_mask);
1664
1665   FadeIn(fade_mask);
1666   FadeSetEnterMenu();
1667
1668   /* update screen area with special editor door */
1669   redraw_mask |= REDRAW_ALL;
1670   BackToFront();
1671
1672   SetMouseCursor(CURSOR_DEFAULT);
1673
1674   OpenDoor(DOOR_CLOSE_1 | DOOR_OPEN_2);
1675 }
1676
1677 static void gotoTopLevelDir()
1678 {
1679   /* move upwards until inside (but not above) top level directory */
1680   while (leveldir_current->node_parent &&
1681          !strEqual(leveldir_current->node_parent->subdir, STRING_TOP_DIRECTORY))
1682   {
1683     /* write a "path" into level tree for easy navigation to last level */
1684     if (leveldir_current->node_parent->node_group->cl_first == -1)
1685     {
1686       int num_leveldirs = numTreeInfoInGroup(leveldir_current);
1687       int leveldir_pos = posTreeInfo(leveldir_current);
1688       int num_page_entries;
1689       int cl_first, cl_cursor;
1690
1691       if (num_leveldirs <= NUM_MENU_ENTRIES_ON_SCREEN)
1692         num_page_entries = num_leveldirs;
1693       else
1694         num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
1695
1696       cl_first = MAX(0, leveldir_pos - num_page_entries + 1);
1697       cl_cursor = leveldir_pos - cl_first;
1698
1699       leveldir_current->node_parent->node_group->cl_first = cl_first;
1700       leveldir_current->node_parent->node_group->cl_cursor = cl_cursor;
1701     }
1702
1703     leveldir_current = leveldir_current->node_parent;
1704   }
1705 }
1706
1707 void HandleTitleScreen(int mx, int my, int dx, int dy, int button)
1708 {
1709   static unsigned int title_delay = 0;
1710   static int title_screen_nr = 0;
1711   static int last_sound = -1, last_music = -1;
1712   boolean return_to_main_menu = FALSE;
1713   struct TitleControlInfo *tci;
1714   int sound, music;
1715
1716   if (button == MB_MENU_INITIALIZE)
1717   {
1718     title_delay = 0;
1719     title_screen_nr = 0;
1720     tci = &title_controls[title_screen_nr];
1721
1722     SetAnimStatus(getTitleAnimMode(tci));
1723
1724     last_sound = SND_UNDEFINED;
1725     last_music = MUS_UNDEFINED;
1726
1727     if (num_title_screens != 0)
1728     {
1729       FadeSetEnterScreen();
1730
1731       /* use individual title fading instead of global "enter screen" fading */
1732       fading = getTitleFading(tci);
1733     }
1734
1735     if (game_status_last_screen == GAME_MODE_INFO)
1736     {
1737       if (num_title_screens == 0)
1738       {
1739         /* switch game mode from title screen mode back to info screen mode */
1740         SetGameStatus(GAME_MODE_INFO);
1741
1742         /* store that last screen was info screen, not main menu screen */
1743         game_status_last_screen = GAME_MODE_INFO;
1744
1745         DrawInfoScreen_NotAvailable("Title screen information:",
1746                                     "No title screen for this level set.");
1747         return;
1748       }
1749
1750       FadeMenuSoundsAndMusic();
1751     }
1752
1753     FadeOut(REDRAW_ALL);
1754
1755     /* title screens may have different window size */
1756     ChangeViewportPropertiesIfNeeded();
1757
1758     /* only required to update logic for redrawing global border */
1759     ClearField();
1760
1761     if (tci->is_image)
1762       DrawTitleScreenImage(tci->local_nr, tci->initial);
1763     else
1764       DrawTitleScreenMessage(tci->local_nr, tci->initial);
1765
1766     sound = getTitleSound(tci);
1767     music = getTitleMusic(tci);
1768
1769     if (sound != last_sound)
1770       PlayMenuSoundExt(sound);
1771     if (music != last_music)
1772       PlayMenuMusicExt(music);
1773
1774     last_sound = sound;
1775     last_music = music;
1776
1777     SetMouseCursor(CURSOR_NONE);
1778
1779     FadeIn(REDRAW_ALL);
1780
1781     DelayReached(&title_delay, 0);      /* reset delay counter */
1782
1783     return;
1784   }
1785
1786   if (fading.auto_delay > 0 && DelayReached(&title_delay, fading.auto_delay))
1787     button = MB_MENU_CHOICE;
1788
1789   if (button == MB_MENU_LEAVE)
1790   {
1791     return_to_main_menu = TRUE;
1792   }
1793   else if (button == MB_MENU_CHOICE)
1794   {
1795     if (game_status_last_screen == GAME_MODE_INFO && num_title_screens == 0)
1796     {
1797       SetGameStatus(GAME_MODE_INFO);
1798
1799       info_mode = INFO_MODE_MAIN;
1800
1801       DrawInfoScreen();
1802
1803       return;
1804     }
1805
1806     title_screen_nr++;
1807
1808     if (title_screen_nr < num_title_screens)
1809     {
1810       tci = &title_controls[title_screen_nr];
1811
1812       SetAnimStatus(getTitleAnimMode(tci));
1813
1814       sound = getTitleSound(tci);
1815       music = getTitleMusic(tci);
1816
1817       if (last_sound != SND_UNDEFINED && sound != last_sound)
1818         FadeSound(last_sound);
1819       if (last_music != MUS_UNDEFINED && music != last_music)
1820         FadeMusic();
1821
1822       fading = getTitleFading(tci);
1823
1824       FadeOut(REDRAW_ALL);
1825
1826       if (tci->is_image)
1827         DrawTitleScreenImage(tci->local_nr, tci->initial);
1828       else
1829         DrawTitleScreenMessage(tci->local_nr, tci->initial);
1830
1831       sound = getTitleSound(tci);
1832       music = getTitleMusic(tci);
1833
1834       if (sound != last_sound)
1835         PlayMenuSoundExt(sound);
1836       if (music != last_music)
1837         PlayMenuMusicExt(music);
1838
1839       last_sound = sound;
1840       last_music = music;
1841
1842       FadeIn(REDRAW_ALL);
1843
1844       DelayReached(&title_delay, 0);    /* reset delay counter */
1845     }
1846     else
1847     {
1848       FadeMenuSoundsAndMusic();
1849
1850       return_to_main_menu = TRUE;
1851     }
1852   }
1853
1854   if (return_to_main_menu)
1855   {
1856     SetMouseCursor(CURSOR_DEFAULT);
1857
1858     /* force full menu screen redraw after displaying title screens */
1859     redraw_mask = REDRAW_ALL;
1860
1861     if (game_status_last_screen == GAME_MODE_INFO)
1862     {
1863       SetGameStatus(GAME_MODE_INFO);
1864
1865       info_mode = INFO_MODE_MAIN;
1866
1867       DrawInfoScreen();
1868     }
1869     else        /* default: return to main menu */
1870     {
1871       SetGameStatus(GAME_MODE_MAIN);
1872
1873       DrawMainMenu();
1874     }
1875   }
1876 }
1877
1878 void HandleMainMenu_SelectLevel(int step, int direction, int selected_level_nr)
1879 {
1880   int old_level_nr = level_nr;
1881   int new_level_nr;
1882
1883   if (selected_level_nr != NO_DIRECT_LEVEL_SELECT)
1884     new_level_nr = selected_level_nr;
1885   else
1886     new_level_nr = old_level_nr + step * direction;
1887
1888   if (new_level_nr < leveldir_current->first_level)
1889     new_level_nr = leveldir_current->first_level;
1890   if (new_level_nr > leveldir_current->last_level)
1891     new_level_nr = leveldir_current->last_level;
1892
1893   if (setup.handicap && new_level_nr > leveldir_current->handicap_level)
1894   {
1895     /* skipping levels is only allowed when trying to skip single level */
1896     if (setup.skip_levels && new_level_nr == old_level_nr + 1 &&
1897         Request("Level still unsolved! Skip despite handicap?", REQ_ASK))
1898     {
1899       leveldir_current->handicap_level++;
1900       SaveLevelSetup_SeriesInfo();
1901     }
1902
1903     new_level_nr = leveldir_current->handicap_level;
1904   }
1905
1906   if (new_level_nr != old_level_nr)
1907   {
1908     struct MainControlInfo *mci= getMainControlInfo(MAIN_CONTROL_LEVEL_NUMBER);
1909
1910     PlaySound(SND_MENU_ITEM_SELECTING);
1911
1912     level_nr = new_level_nr;
1913
1914     DrawText(mSX + mci->pos_text->x, mSY + mci->pos_text->y,
1915              int2str(level_nr, menu.main.text.level_number.size),
1916              mci->pos_text->font);
1917
1918     LoadLevel(level_nr);
1919     DrawPreviewLevelInitial();
1920
1921     TapeErase();
1922     LoadTape(level_nr);
1923     DrawCompleteVideoDisplay();
1924
1925     SaveLevelSetup_SeriesInfo();
1926
1927     /* needed because DrawPreviewLevelInitial() takes some time */
1928     BackToFront();
1929     /* SyncDisplay(); */
1930   }
1931 }
1932
1933 void HandleMainMenu(int mx, int my, int dx, int dy, int button)
1934 {
1935   static int choice = MAIN_CONTROL_GAME;
1936   static boolean button_pressed_last = FALSE;
1937   boolean button_pressed = FALSE;
1938   int pos = choice;
1939   int i;
1940
1941   if (button == MB_MENU_INITIALIZE)
1942   {
1943     DrawCursorAndText_Main(choice, TRUE, FALSE);
1944
1945     return;
1946   }
1947
1948   if (mx || my)         /* mouse input */
1949   {
1950     pos = -1;
1951
1952     for (i = 0; main_controls[i].nr != -1; i++)
1953     {
1954       if (insideMenuPosRect(main_controls[i].pos_button, mx - mSX, my - mSY) ||
1955           insideTextPosRect(main_controls[i].pos_text,   mx - mSX, my - mSY) ||
1956           insideTextPosRect(main_controls[i].pos_input,  mx - mSX, my - mSY))
1957       {
1958         pos = main_controls[i].nr;
1959
1960         break;
1961       }
1962     }
1963
1964     /* check if level preview was clicked */
1965     if (insidePreviewRect(&preview, mx - SX, my - SY))
1966       pos = MAIN_CONTROL_GAME;
1967
1968     // handle pressed/unpressed state for active/inactive menu buttons
1969     // (if pos != -1, "i" contains index position corresponding to "pos")
1970     if (button &&
1971         pos >= MAIN_CONTROL_NAME && pos <= MAIN_CONTROL_QUIT &&
1972         insideMenuPosRect(main_controls[i].pos_button, mx - mSX, my - mSY))
1973       button_pressed = TRUE;
1974
1975     if (button_pressed != button_pressed_last)
1976     {
1977       DrawCursorAndText_Main(choice, TRUE, button_pressed);
1978
1979       if (button_pressed)
1980         PlaySound(SND_MENU_BUTTON_PRESSING);
1981       else
1982         PlaySound(SND_MENU_BUTTON_RELEASING);
1983     }
1984   }
1985   else if (dx || dy)    /* keyboard input */
1986   {
1987     if (dx > 0 && (choice == MAIN_CONTROL_INFO ||
1988                    choice == MAIN_CONTROL_SETUP))
1989       button = MB_MENU_CHOICE;
1990     else if (dy)
1991       pos = choice + dy;
1992   }
1993
1994   if (pos == MAIN_CONTROL_FIRST_LEVEL && !button)
1995   {
1996     HandleMainMenu_SelectLevel(MAX_LEVELS, -1, NO_DIRECT_LEVEL_SELECT);
1997   }
1998   else if (pos == MAIN_CONTROL_LAST_LEVEL && !button)
1999   {
2000     HandleMainMenu_SelectLevel(MAX_LEVELS, +1, NO_DIRECT_LEVEL_SELECT);
2001   }
2002   else if (pos == MAIN_CONTROL_LEVEL_NUMBER && !button)
2003   {
2004     CloseDoor(DOOR_CLOSE_2);
2005
2006     SetGameStatus(GAME_MODE_LEVELNR);
2007
2008     DrawChooseLevelNr();
2009   }
2010   else if (pos >= MAIN_CONTROL_NAME && pos <= MAIN_CONTROL_QUIT)
2011   {
2012     if (button)
2013     {
2014       if (pos != choice)
2015       {
2016         PlaySound(SND_MENU_ITEM_ACTIVATING);
2017
2018         DrawCursorAndText_Main(choice, FALSE, FALSE);
2019         DrawCursorAndText_Main(pos, TRUE, button_pressed);
2020
2021         choice = pos;
2022       }
2023       else if (dx != 0)
2024       {
2025         if (choice != MAIN_CONTROL_INFO &&
2026             choice != MAIN_CONTROL_SETUP)
2027           HandleMainMenu_SelectLevel(1, dx, NO_DIRECT_LEVEL_SELECT);
2028       }
2029     }
2030     else
2031     {
2032       PlaySound(SND_MENU_ITEM_SELECTING);
2033
2034       if (pos == MAIN_CONTROL_NAME)
2035       {
2036         SetGameStatus(GAME_MODE_PSEUDO_TYPENAME);
2037
2038         HandleTypeName(strlen(setup.player_name), 0);
2039       }
2040       else if (pos == MAIN_CONTROL_LEVELS)
2041       {
2042         if (leveldir_first)
2043         {
2044           CloseDoor(DOOR_CLOSE_2);
2045
2046           SetGameStatus(GAME_MODE_LEVELS);
2047
2048           SaveLevelSetup_LastSeries();
2049           SaveLevelSetup_SeriesInfo();
2050
2051           if (setup.internal.choose_from_top_leveldir)
2052             gotoTopLevelDir();
2053
2054           DrawChooseLevelSet();
2055         }
2056       }
2057       else if (pos == MAIN_CONTROL_SCORES)
2058       {
2059         CloseDoor(DOOR_CLOSE_2);
2060
2061         SetGameStatus(GAME_MODE_SCORES);
2062
2063         DrawHallOfFame(-1);
2064       }
2065       else if (pos == MAIN_CONTROL_EDITOR)
2066       {
2067         if (leveldir_current->readonly &&
2068             !strEqual(setup.player_name, "Artsoft"))
2069           Request("This level is read only!", REQ_CONFIRM);
2070
2071         CloseDoor(DOOR_CLOSE_2);
2072
2073         SetGameStatus(GAME_MODE_EDITOR);
2074
2075         FadeSetEnterScreen();
2076
2077         DrawLevelEd();
2078       }
2079       else if (pos == MAIN_CONTROL_INFO)
2080       {
2081         CloseDoor(DOOR_CLOSE_2);
2082
2083         SetGameStatus(GAME_MODE_INFO);
2084
2085         info_mode = INFO_MODE_MAIN;
2086
2087         DrawInfoScreen();
2088       }
2089       else if (pos == MAIN_CONTROL_GAME)
2090       {
2091         StartGameActions(options.network, setup.autorecord, level.random_seed);
2092       }
2093       else if (pos == MAIN_CONTROL_SETUP)
2094       {
2095         CloseDoor(DOOR_CLOSE_2);
2096
2097         SetGameStatus(GAME_MODE_SETUP);
2098
2099         setup_mode = SETUP_MODE_MAIN;
2100
2101         DrawSetupScreen();
2102       }
2103       else if (pos == MAIN_CONTROL_QUIT)
2104       {
2105         SaveLevelSetup_LastSeries();
2106         SaveLevelSetup_SeriesInfo();
2107
2108         if (Request("Do you really want to quit?", REQ_ASK | REQ_STAY_CLOSED))
2109           SetGameStatus(GAME_MODE_QUIT);
2110       }
2111     }
2112   }
2113
2114   button_pressed_last = button_pressed;
2115 }
2116
2117
2118 /* ========================================================================= */
2119 /* info screen functions                                                     */
2120 /* ========================================================================= */
2121
2122 static struct TokenInfo *info_info;
2123 static int num_info_info;       /* number of info entries shown on screen */
2124 static int max_info_info;       /* total number of info entries in list */
2125
2126 static void execInfoTitleScreen()
2127 {
2128   info_mode = INFO_MODE_TITLE;
2129
2130   DrawInfoScreen();
2131 }
2132
2133 static void execInfoElements()
2134 {
2135   info_mode = INFO_MODE_ELEMENTS;
2136
2137   DrawInfoScreen();
2138 }
2139
2140 static void execInfoMusic()
2141 {
2142   info_mode = INFO_MODE_MUSIC;
2143
2144   DrawInfoScreen();
2145 }
2146
2147 static void execInfoCredits()
2148 {
2149   info_mode = INFO_MODE_CREDITS;
2150
2151   DrawInfoScreen();
2152 }
2153
2154 static void execInfoProgram()
2155 {
2156   info_mode = INFO_MODE_PROGRAM;
2157
2158   DrawInfoScreen();
2159 }
2160
2161 static void execInfoVersion()
2162 {
2163   info_mode = INFO_MODE_VERSION;
2164
2165   DrawInfoScreen();
2166 }
2167
2168 static void execInfoLevelSet()
2169 {
2170   info_mode = INFO_MODE_LEVELSET;
2171
2172   DrawInfoScreen();
2173 }
2174
2175 static void execExitInfo()
2176 {
2177   SetGameStatus(GAME_MODE_MAIN);
2178
2179   DrawMainMenu();
2180 }
2181
2182 static struct TokenInfo info_info_main[] =
2183 {
2184   { TYPE_ENTER_SCREEN,  execInfoTitleScreen,    "Title Screen"          },
2185   { TYPE_ENTER_SCREEN,  execInfoElements,       "Elements Info"         },
2186   { TYPE_ENTER_SCREEN,  execInfoMusic,          "Music Info"            },
2187   { TYPE_ENTER_SCREEN,  execInfoCredits,        "Credits"               },
2188   { TYPE_ENTER_SCREEN,  execInfoProgram,        "Program Info"          },
2189   { TYPE_ENTER_SCREEN,  execInfoVersion,        "Version Info"          },
2190   { TYPE_ENTER_SCREEN,  execInfoLevelSet,       "Level Set Info"        },
2191   { TYPE_EMPTY,         NULL,                   ""                      },
2192   { TYPE_LEAVE_MENU,    execExitInfo,           "Exit"                  },
2193
2194   { 0,                  NULL,                   NULL                    }
2195 };
2196
2197 static int getMenuTextFont(int type)
2198 {
2199   if (type & (TYPE_SWITCH       |
2200               TYPE_YES_NO       |
2201               TYPE_YES_NO_AUTO  |
2202               TYPE_STRING       |
2203               TYPE_ECS_AGA      |
2204               TYPE_KEYTEXT      |
2205               TYPE_ENTER_LIST))
2206     return FONT_MENU_2;
2207   else
2208     return FONT_MENU_1;
2209 }
2210
2211 static struct TokenInfo *setup_info;
2212 static struct TokenInfo setup_info_input[];
2213
2214 static struct TokenInfo *menu_info;
2215
2216 static void DrawCursorAndText_Menu_Ext(struct TokenInfo *token_info,
2217                                        int screen_pos, int menu_info_pos_raw,
2218                                        boolean active)
2219 {
2220   int pos = (menu_info_pos_raw < 0 ? screen_pos : menu_info_pos_raw);
2221   struct TokenInfo *ti = &token_info[pos];
2222   int xpos = MENU_SCREEN_START_XPOS;
2223   int ypos = MENU_SCREEN_START_YPOS + screen_pos;
2224   int font_nr = getMenuTextFont(ti->type);
2225
2226   if (setup_mode == SETUP_MODE_INPUT)
2227     font_nr = FONT_MENU_1;
2228
2229   if (active)
2230     font_nr = FONT_ACTIVE(font_nr);
2231
2232   DrawText(mSX + xpos * 32, mSY + ypos * 32, ti->text, font_nr);
2233
2234   if (ti->type & ~TYPE_SKIP_ENTRY)
2235     drawCursor(screen_pos, active);
2236 }
2237
2238 static void DrawCursorAndText_Menu(int screen_pos, int menu_info_pos_raw,
2239                                    boolean active)
2240 {
2241   DrawCursorAndText_Menu_Ext(menu_info, screen_pos, menu_info_pos_raw, active);
2242 }
2243
2244 static void DrawCursorAndText_Setup(int screen_pos, int menu_info_pos_raw,
2245                                     boolean active)
2246 {
2247   DrawCursorAndText_Menu_Ext(setup_info, screen_pos, menu_info_pos_raw, active);
2248 }
2249
2250 static char *window_size_text;
2251 static char *scaling_type_text;
2252
2253 static void drawSetupValue(int, int);
2254
2255 static void drawMenuInfoList(int first_entry, int num_page_entries,
2256                              int max_page_entries)
2257 {
2258   int i;
2259
2260   if (first_entry + num_page_entries > max_page_entries)
2261     first_entry = 0;
2262
2263   clearMenuListArea();
2264
2265   for (i = 0; i < num_page_entries; i++)
2266   {
2267     int menu_info_pos = first_entry + i;
2268     struct TokenInfo *si = &menu_info[menu_info_pos];
2269     void *value_ptr = si->value;
2270
2271     /* set some entries to "unchangeable" according to other variables */
2272     if ((value_ptr == &setup.sound_simple && !audio.sound_available) ||
2273         (value_ptr == &setup.sound_loops  && !audio.loops_available) ||
2274         (value_ptr == &setup.sound_music  && !audio.music_available) ||
2275         (value_ptr == &setup.fullscreen   && !video.fullscreen_available) ||
2276         (value_ptr == &window_size_text   && !video.window_scaling_available) ||
2277         (value_ptr == &scaling_type_text  && !video.window_scaling_available))
2278       si->type |= TYPE_GHOSTED;
2279
2280     if (si->type & (TYPE_ENTER_MENU|TYPE_ENTER_LIST))
2281       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
2282     else if (si->type & (TYPE_LEAVE_MENU|TYPE_LEAVE_LIST))
2283       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
2284     else if (si->type & ~TYPE_SKIP_ENTRY)
2285       initCursor(i, IMG_MENU_BUTTON);
2286
2287     DrawCursorAndText_Menu(i, menu_info_pos, FALSE);
2288
2289     if (si->type & TYPE_VALUE &&
2290         menu_info == setup_info)
2291       drawSetupValue(i, menu_info_pos);
2292   }
2293 }
2294
2295 static void DrawInfoScreen_Main()
2296 {
2297   int fade_mask = REDRAW_FIELD;
2298   int i;
2299
2300   if (redraw_mask & REDRAW_ALL)
2301     fade_mask = REDRAW_ALL;
2302
2303   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
2304     fade_mask = REDRAW_ALL;
2305
2306   UnmapAllGadgets();
2307   FadeMenuSoundsAndMusic();
2308
2309   FreeScreenGadgets();
2310   CreateScreenGadgets();
2311
2312   /* (needed after displaying title screens which disable auto repeat) */
2313   KeyboardAutoRepeatOn();
2314
2315   FadeSetLeaveScreen();
2316
2317   FadeOut(fade_mask);
2318
2319   /* needed if different viewport properties defined for info screen */
2320   ChangeViewportPropertiesIfNeeded();
2321
2322   SetMainBackgroundImage(IMG_BACKGROUND_INFO);
2323
2324   ClearField();
2325
2326   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
2327
2328   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Info Screen");
2329
2330   info_info = info_info_main;
2331
2332   // determine maximal number of info entries that can be displayed on screen
2333   num_info_info = 0;
2334   for (i = 0; info_info[i].type != 0 && i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
2335     num_info_info++;
2336
2337   // determine maximal number of info entries available for menu of info screen
2338   max_info_info = 0;
2339   for (i = 0; info_info[i].type != 0; i++)
2340     max_info_info++;
2341
2342   HandleInfoScreen_Main(0, 0, 0, 0, MB_MENU_INITIALIZE);
2343
2344   MapScreenGadgets(max_info_info);
2345
2346   PlayMenuSoundsAndMusic();
2347
2348   DrawMaskedBorder(fade_mask);
2349
2350   FadeIn(fade_mask);
2351 }
2352
2353 static void changeSetupValue(int, int, int);
2354
2355 void HandleMenuScreen(int mx, int my, int dx, int dy, int button,
2356                       int mode, int num_page_entries, int max_page_entries)
2357 {
2358   static int num_page_entries_all_last[NUM_SPECIAL_GFX_ARGS][MAX_MENU_MODES];
2359   static int choice_stores[NUM_SPECIAL_GFX_ARGS][MAX_MENU_MODES];
2360   static int first_entry_stores[NUM_SPECIAL_GFX_ARGS][MAX_MENU_MODES];
2361   int *num_page_entries_last = num_page_entries_all_last[game_status];
2362   int *choice_store = choice_stores[game_status];
2363   int *first_entry_store = first_entry_stores[game_status];
2364   int choice = choice_store[mode];              /* starts with 0 */
2365   int first_entry = first_entry_store[mode];    /* starts with 0 */
2366   int x = 0;
2367   int y = choice - first_entry;
2368   int y_old = y;
2369   boolean position_set_by_scrollbar = (dx == 999);
2370   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
2371   int i;
2372
2373   if (button == MB_MENU_INITIALIZE)
2374   {
2375     // check if number of menu page entries has changed (may happen by change
2376     // of custom artwork definition value for 'list_size' for this menu screen)
2377     // (in this case, the last menu position most probably has to be corrected)
2378     if (num_page_entries != num_page_entries_last[mode])
2379     {
2380       choice_store[mode] = first_entry_store[mode] = 0;
2381
2382       choice = first_entry = 0;
2383       y = y_old = 0;
2384
2385       num_page_entries_last[mode] = num_page_entries;
2386     }
2387
2388     /* advance to first valid menu entry */
2389     while (choice < num_page_entries &&
2390            menu_info[choice].type & TYPE_SKIP_ENTRY)
2391       choice++;
2392
2393     if (position_set_by_scrollbar)
2394       first_entry = first_entry_store[mode] = dy;
2395     else
2396       AdjustScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL, max_page_entries,
2397                       NUM_MENU_ENTRIES_ON_SCREEN, first_entry);
2398
2399     drawMenuInfoList(first_entry, num_page_entries, max_page_entries);
2400
2401     if (choice < first_entry)
2402     {
2403       choice = first_entry;
2404
2405       if (menu_info[choice].type & TYPE_SKIP_ENTRY)
2406         choice++;
2407     }
2408     else if (choice > first_entry + num_page_entries - 1)
2409     {
2410       choice = first_entry + num_page_entries - 1;
2411
2412       if (menu_info[choice].type & TYPE_SKIP_ENTRY)
2413         choice--;
2414     }
2415
2416     choice_store[mode] = choice;
2417
2418     DrawCursorAndText_Menu(choice - first_entry, choice, TRUE);
2419
2420     return;
2421   }
2422   else if (button == MB_MENU_LEAVE)
2423   {
2424     PlaySound(SND_MENU_ITEM_SELECTING);
2425
2426     for (i = 0; i < max_page_entries; i++)
2427     {
2428       if (menu_info[i].type & TYPE_LEAVE_MENU)
2429       {
2430         void (*menu_callback_function)(void) = menu_info[i].value;
2431
2432         FadeSetLeaveMenu();
2433
2434         menu_callback_function();
2435
2436         break;  /* absolutely needed because function changes 'menu_info'! */
2437       }
2438     }
2439
2440     return;
2441   }
2442
2443   if (mx || my)         /* mouse input */
2444   {
2445     x = (mx - mSX) / 32;
2446     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
2447   }
2448   else if (dx || dy)    /* keyboard or scrollbar/scrollbutton input */
2449   {
2450     /* move cursor instead of scrolling when already at start/end of list */
2451     if (dy == -1 * SCROLL_LINE && first_entry == 0)
2452       dy = -1;
2453     else if (dy == +1 * SCROLL_LINE &&
2454              first_entry + num_page_entries == max_page_entries)
2455       dy = 1;
2456
2457     /* handle scrolling screen one line or page */
2458     if (y + dy < 0 ||
2459         y + dy > num_page_entries - 1)
2460     {
2461       boolean redraw = FALSE;
2462
2463       if (ABS(dy) == SCROLL_PAGE)
2464         step = num_page_entries - 1;
2465
2466       if (dy < 0 && first_entry > 0)
2467       {
2468         /* scroll page/line up */
2469
2470         first_entry -= step;
2471         if (first_entry < 0)
2472           first_entry = 0;
2473
2474         redraw = TRUE;
2475       }
2476       else if (dy > 0 && first_entry + num_page_entries < max_page_entries)
2477       {
2478         /* scroll page/line down */
2479
2480         first_entry += step;
2481         if (first_entry + num_page_entries > max_page_entries)
2482           first_entry = MAX(0, max_page_entries - num_page_entries);
2483
2484         redraw = TRUE;
2485       }
2486
2487       if (redraw)
2488       {
2489         choice += first_entry - first_entry_store[mode];
2490
2491         if (choice < first_entry)
2492         {
2493           choice = first_entry;
2494
2495           if (menu_info[choice].type & TYPE_SKIP_ENTRY)
2496             choice++;
2497         }
2498         else if (choice > first_entry + num_page_entries - 1)
2499         {
2500           choice = first_entry + num_page_entries - 1;
2501
2502           if (menu_info[choice].type & TYPE_SKIP_ENTRY)
2503             choice--;
2504         }
2505         else if (menu_info[choice].type & TYPE_SKIP_ENTRY)
2506         {
2507           choice += SIGN(dy);
2508
2509           if (choice < first_entry ||
2510               choice > first_entry + num_page_entries - 1)
2511           first_entry += SIGN(dy);
2512         }
2513
2514         first_entry_store[mode] = first_entry;
2515         choice_store[mode] = choice;
2516
2517         drawMenuInfoList(first_entry, num_page_entries, max_page_entries);
2518
2519         DrawCursorAndText_Menu(choice - first_entry, choice, TRUE);
2520
2521         AdjustScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL, max_page_entries,
2522                         NUM_MENU_ENTRIES_ON_SCREEN, first_entry);
2523       }
2524
2525       return;
2526     }
2527
2528     if (dx)
2529     {
2530       int menu_navigation_type = (dx < 0 ? TYPE_LEAVE : TYPE_ENTER);
2531
2532       if (menu_info[choice].type & menu_navigation_type ||
2533           menu_info[choice].type & TYPE_BOOLEAN_STYLE ||
2534           menu_info[choice].type & TYPE_YES_NO_AUTO)
2535         button = MB_MENU_CHOICE;
2536     }
2537     else if (dy)
2538       y += dy;
2539
2540     /* jump to next non-empty menu entry (up or down) */
2541     while (first_entry + y > 0 &&
2542            first_entry + y < max_page_entries - 1 &&
2543            menu_info[first_entry + y].type & TYPE_SKIP_ENTRY)
2544       y += dy;
2545
2546     if (!IN_VIS_MENU(x, y))
2547     {
2548       choice += y - y_old;
2549
2550       if (choice < first_entry)
2551         first_entry = choice;
2552       else if (choice > first_entry + num_page_entries - 1)
2553         first_entry = choice - num_page_entries + 1;
2554
2555       if (first_entry >= 0 &&
2556           first_entry + num_page_entries <= max_page_entries)
2557       {
2558         first_entry_store[mode] = first_entry;
2559
2560         if (choice < first_entry)
2561           choice = first_entry;
2562         else if (choice > first_entry + num_page_entries - 1)
2563           choice = first_entry + num_page_entries - 1;
2564
2565         choice_store[mode] = choice;
2566
2567         drawMenuInfoList(first_entry, num_page_entries, max_page_entries);
2568
2569         DrawCursorAndText_Menu(choice - first_entry, choice, TRUE);
2570
2571         AdjustScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL, max_page_entries,
2572                         NUM_MENU_ENTRIES_ON_SCREEN, first_entry);
2573       }
2574
2575       return;
2576     }
2577   }
2578
2579   if (!anyScrollbarGadgetActive() &&
2580       IN_VIS_MENU(x, y) &&
2581       mx < screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x &&
2582       y >= 0 && y < num_page_entries)
2583   {
2584     if (button)
2585     {
2586       if (first_entry + y != choice &&
2587           menu_info[first_entry + y].type & ~TYPE_SKIP_ENTRY)
2588       {
2589         PlaySound(SND_MENU_ITEM_ACTIVATING);
2590
2591         DrawCursorAndText_Menu(choice - first_entry, choice, FALSE);
2592         DrawCursorAndText_Menu(y, first_entry + y, TRUE);
2593
2594         choice = choice_store[mode] = first_entry + y;
2595       }
2596       else if (dx < 0)
2597       {
2598         PlaySound(SND_MENU_ITEM_SELECTING);
2599
2600         for (i = 0; menu_info[i].type != 0; i++)
2601         {
2602           if (menu_info[i].type & TYPE_LEAVE_MENU)
2603           {
2604             void (*menu_callback_function)(void) = menu_info[i].value;
2605
2606             FadeSetLeaveMenu();
2607
2608             menu_callback_function();
2609
2610             /* absolutely needed because function changes 'menu_info'! */
2611             break;
2612           }
2613         }
2614
2615         return;
2616       }
2617     }
2618     else if (!(menu_info[first_entry + y].type & TYPE_GHOSTED))
2619     {
2620       PlaySound(SND_MENU_ITEM_SELECTING);
2621
2622       /* when selecting key headline, execute function for key value change */
2623       if (menu_info[first_entry + y].type & TYPE_KEYTEXT &&
2624           menu_info[first_entry + y + 1].type & TYPE_KEY)
2625         y++;
2626
2627       /* when selecting string value, execute function for list selection */
2628       if (menu_info[first_entry + y].type & TYPE_STRING && y > 0 &&
2629           menu_info[first_entry + y - 1].type & TYPE_ENTER_LIST)
2630         y--;
2631
2632       if (menu_info[first_entry + y].type & TYPE_ENTER_OR_LEAVE)
2633       {
2634         void (*menu_callback_function)(void) =
2635           menu_info[first_entry + y].value;
2636
2637         FadeSetFromType(menu_info[first_entry + y].type);
2638
2639         menu_callback_function();
2640       }
2641       else if (menu_info[first_entry + y].type & TYPE_VALUE &&
2642                menu_info == setup_info)
2643       {
2644         changeSetupValue(y, first_entry + y, dx);
2645       }
2646     }
2647   }
2648 }
2649
2650 void HandleInfoScreen_Main(int mx, int my, int dx, int dy, int button)
2651 {
2652   menu_info = info_info;
2653
2654   HandleMenuScreen(mx, my, dx, dy, button,
2655                    info_mode, num_info_info, max_info_info);
2656 }
2657
2658 static int getMenuFontSpacing(int spacing_height, int font_nr)
2659 {
2660   int font_spacing = getFontHeight(font_nr) + EXTRA_SPACING(game_status);
2661
2662   return (spacing_height < 0 ? ABS(spacing_height) * font_spacing :
2663           spacing_height);
2664 }
2665
2666 static int getMenuTextSpacing(int spacing_height, int font_nr)
2667 {
2668   return (getMenuFontSpacing(spacing_height, font_nr) +
2669           EXTRA_SPACING(game_status));
2670 }
2671
2672 static int getMenuTextStep(int spacing_height, int font_nr)
2673 {
2674   return getFontHeight(font_nr) + getMenuTextSpacing(spacing_height, font_nr);
2675 }
2676
2677 void DrawInfoScreen_NotAvailable(char *text_title, char *text_error)
2678 {
2679   int font_title = MENU_INFO_FONT_TITLE;
2680   int font_error = FONT_TEXT_2;
2681   int font_foot  = MENU_INFO_FONT_FOOT;
2682   int spacing_title = menu.headline1_spacing_info[info_mode];
2683   int ystep_title = getMenuTextStep(spacing_title, font_title);
2684   int ystart1 = mSY - SY + MENU_SCREEN_INFO_YSTART1;
2685   int ystart2 = ystart1 + ystep_title;
2686   int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
2687
2688   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO);
2689
2690   FadeOut(REDRAW_FIELD);
2691
2692   ClearField();
2693   DrawHeadline();
2694
2695   DrawTextSCentered(ystart1, font_title, text_title);
2696   DrawTextSCentered(ystart2, font_error, text_error);
2697
2698   DrawTextSCentered(ybottom, font_foot,
2699                     "Press any key or button for info menu");
2700
2701   FadeIn(REDRAW_FIELD);
2702 }
2703
2704 void DrawInfoScreen_HelpAnim(int start, int max_anims, boolean init)
2705 {
2706   static int infoscreen_step[MAX_INFO_ELEMENTS_ON_SCREEN];
2707   static int infoscreen_frame[MAX_INFO_ELEMENTS_ON_SCREEN];
2708   int font_title = MENU_INFO_FONT_TITLE;
2709   int font_foot  = MENU_INFO_FONT_FOOT;
2710   int xstart  = mSX + MENU_SCREEN_INFO_SPACE_LEFT;
2711   int ystart1 = mSY - SY + MENU_SCREEN_INFO_YSTART1;
2712   int ystart2 = mSY + MENU_SCREEN_INFO_YSTART2;
2713   int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
2714   int ystep = MENU_SCREEN_INFO_YSTEP;
2715   int element, action, direction;
2716   int graphic;
2717   int delay;
2718   int sync_frame;
2719   int i, j;
2720
2721   if (init)
2722   {
2723     for (i = 0; i < NUM_INFO_ELEMENTS_ON_SCREEN; i++)
2724       infoscreen_step[i] = infoscreen_frame[i] = 0;
2725
2726     ClearField();
2727     DrawHeadline();
2728
2729     DrawTextSCentered(ystart1, font_title, "The Game Elements:");
2730
2731     DrawTextSCentered(ybottom, font_foot,
2732                       "Press any key or button for next page");
2733
2734     FrameCounter = 0;
2735   }
2736
2737   i = j = 0;
2738   while (helpanim_info[j].element != HELPANIM_LIST_END)
2739   {
2740     if (i >= start + NUM_INFO_ELEMENTS_ON_SCREEN ||
2741         i >= max_anims)
2742       break;
2743     else if (i < start)
2744     {
2745       while (helpanim_info[j].element != HELPANIM_LIST_NEXT)
2746         j++;
2747
2748       j++;
2749       i++;
2750
2751       continue;
2752     }
2753
2754     j += infoscreen_step[i - start];
2755
2756     element = helpanim_info[j].element;
2757     action = helpanim_info[j].action;
2758     direction = helpanim_info[j].direction;
2759
2760     if (element < 0)
2761       element = EL_UNKNOWN;
2762
2763     if (action != -1 && direction != -1)
2764       graphic = el_act_dir2img(element, action, direction);
2765     else if (action != -1)
2766       graphic = el_act2img(element, action);
2767     else if (direction != -1)
2768       graphic = el_dir2img(element, direction);
2769     else
2770       graphic = el2img(element);
2771
2772     delay = helpanim_info[j++].delay;
2773
2774     if (delay == -1)
2775       delay = 1000000;
2776
2777     if (infoscreen_frame[i - start] == 0)
2778     {
2779       sync_frame = 0;
2780       infoscreen_frame[i - start] = delay - 1;
2781     }
2782     else
2783     {
2784       sync_frame = delay - infoscreen_frame[i - start];
2785       infoscreen_frame[i - start]--;
2786     }
2787
2788     if (helpanim_info[j].element == HELPANIM_LIST_NEXT)
2789     {
2790       if (!infoscreen_frame[i - start])
2791         infoscreen_step[i - start] = 0;
2792     }
2793     else
2794     {
2795       if (!infoscreen_frame[i - start])
2796         infoscreen_step[i - start]++;
2797       while (helpanim_info[j].element != HELPANIM_LIST_NEXT)
2798         j++;
2799     }
2800
2801     j++;
2802
2803     ClearRectangleOnBackground(drawto, xstart, ystart2 + (i - start) * ystep,
2804                                TILEX, TILEY);
2805     DrawFixedGraphicAnimationExt(drawto, xstart, ystart2 + (i - start) * ystep,
2806                                  graphic, sync_frame, USE_MASKING);
2807
2808     if (init)
2809       DrawInfoScreen_HelpText(element, action, direction, i - start);
2810
2811     i++;
2812   }
2813
2814   redraw_mask |= REDRAW_FIELD;
2815
2816   FrameCounter++;
2817 }
2818
2819 static char *getHelpText(int element, int action, int direction)
2820 {
2821   char token[MAX_LINE_LEN];
2822
2823   strcpy(token, element_info[element].token_name);
2824
2825   if (action != -1)
2826     strcat(token, element_action_info[action].suffix);
2827
2828   if (direction != -1)
2829     strcat(token, element_direction_info[MV_DIR_TO_BIT(direction)].suffix);
2830
2831   return getHashEntry(helptext_info, token);
2832 }
2833
2834 void DrawInfoScreen_HelpText(int element, int action, int direction, int ypos)
2835 {
2836   int font_nr = FONT_INFO_ELEMENTS;
2837   int font_width = getFontWidth(font_nr);
2838   int font_height = getFontHeight(font_nr);
2839   int yoffset = (TILEX - 2 * font_height) / 2;
2840   int xstart = mSX + MENU_SCREEN_INFO_SPACE_LEFT + TILEX + MINI_TILEX;
2841   int ystart = mSY + MENU_SCREEN_INFO_YSTART2 + yoffset;
2842   int ystep = TILEY + 4;
2843   int pad_left = xstart - SX;
2844   int pad_right = MENU_SCREEN_INFO_SPACE_RIGHT;
2845   int max_chars_per_line = (SXSIZE - pad_left - pad_right) / font_width;
2846   int max_lines_per_text = 2;    
2847   char *text = NULL;
2848
2849   if (action != -1 && direction != -1)          /* element.action.direction */
2850     text = getHelpText(element, action, direction);
2851
2852   if (text == NULL && action != -1)             /* element.action */
2853     text = getHelpText(element, action, -1);
2854
2855   if (text == NULL && direction != -1)          /* element.direction */
2856     text = getHelpText(element, -1, direction);
2857
2858   if (text == NULL)                             /* base element */
2859     text = getHelpText(element, -1, -1);
2860
2861   if (text == NULL)                             /* not found */
2862     text = "No description available";
2863
2864   if (strlen(text) <= max_chars_per_line)       /* only one line of text */
2865     ystart += getFontHeight(font_nr) / 2;
2866
2867   DrawTextBuffer(xstart, ystart + ypos * ystep, text, font_nr,
2868                  max_chars_per_line, -1, max_lines_per_text, 0, -1,
2869                  TRUE, FALSE, FALSE);
2870 }
2871
2872 void DrawInfoScreen_TitleScreen()
2873 {
2874   SetGameStatus(GAME_MODE_TITLE);
2875
2876   DrawTitleScreen();
2877 }
2878
2879 void HandleInfoScreen_TitleScreen(int button)
2880 {
2881   HandleTitleScreen(0, 0, 0, 0, button);
2882 }
2883
2884 void DrawInfoScreen_Elements()
2885 {
2886   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_ELEMENTS);
2887
2888   FadeOut(REDRAW_FIELD);
2889
2890   LoadHelpAnimInfo();
2891   LoadHelpTextInfo();
2892
2893   HandleInfoScreen_Elements(MB_MENU_INITIALIZE);
2894
2895   FadeIn(REDRAW_FIELD);
2896 }
2897
2898 void HandleInfoScreen_Elements(int button)
2899 {
2900   static unsigned int info_delay = 0;
2901   static int num_anims;
2902   static int num_pages;
2903   static int page;
2904   int anims_per_page = NUM_INFO_ELEMENTS_ON_SCREEN;
2905   int i;
2906
2907   if (button == MB_MENU_INITIALIZE)
2908   {
2909     boolean new_element = TRUE;
2910
2911     num_anims = 0;
2912
2913     for (i = 0; helpanim_info[i].element != HELPANIM_LIST_END; i++)
2914     {
2915       if (helpanim_info[i].element == HELPANIM_LIST_NEXT)
2916         new_element = TRUE;
2917       else if (new_element)
2918       {
2919         num_anims++;
2920         new_element = FALSE;
2921       }
2922     }
2923
2924     num_pages = (num_anims + anims_per_page - 1) / anims_per_page;
2925     page = 0;
2926   }
2927
2928   if (button == MB_MENU_LEAVE)
2929   {
2930     PlaySound(SND_MENU_ITEM_SELECTING);
2931
2932     info_mode = INFO_MODE_MAIN;
2933     DrawInfoScreen();
2934
2935     return;
2936   }
2937   else if (button == MB_MENU_CHOICE || button == MB_MENU_INITIALIZE)
2938   {
2939     if (button != MB_MENU_INITIALIZE)
2940     {
2941       PlaySound(SND_MENU_ITEM_SELECTING);
2942
2943       page++;
2944     }
2945
2946     if (page >= num_pages)
2947     {
2948       FadeMenuSoundsAndMusic();
2949
2950       info_mode = INFO_MODE_MAIN;
2951       DrawInfoScreen();
2952
2953       return;
2954     }
2955
2956     if (page > 0)
2957       FadeSetNextScreen();
2958
2959     if (button != MB_MENU_INITIALIZE)
2960       FadeOut(REDRAW_FIELD);
2961
2962     DrawInfoScreen_HelpAnim(page * anims_per_page, num_anims, TRUE);
2963
2964     if (button != MB_MENU_INITIALIZE)
2965       FadeIn(REDRAW_FIELD);
2966   }
2967   else
2968   {
2969     if (DelayReached(&info_delay, GameFrameDelay))
2970       if (page < num_pages)
2971         DrawInfoScreen_HelpAnim(page * anims_per_page, num_anims, FALSE);
2972
2973     PlayMenuSoundIfLoop();
2974   }
2975 }
2976
2977 void DrawInfoScreen_Music()
2978 {
2979   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_MUSIC);
2980
2981   FadeOut(REDRAW_FIELD);
2982
2983   ClearField();
2984   DrawHeadline();
2985
2986   LoadMusicInfo();
2987
2988   HandleInfoScreen_Music(MB_MENU_INITIALIZE);
2989
2990   FadeIn(REDRAW_FIELD);
2991 }
2992
2993 void HandleInfoScreen_Music(int button)
2994 {
2995   static struct MusicFileInfo *list = NULL;
2996   int font_title = MENU_INFO_FONT_TITLE;
2997   int font_head  = MENU_INFO_FONT_HEAD;
2998   int font_text  = MENU_INFO_FONT_TEXT;
2999   int font_foot  = MENU_INFO_FONT_FOOT;
3000   int spacing_title = menu.headline1_spacing_info[info_mode];
3001   int spacing_head  = menu.headline2_spacing_info[info_mode];
3002   int ystep_title = getMenuTextStep(spacing_title, font_title);
3003   int ystep_head  = getMenuTextStep(spacing_head,  font_head);
3004   int ystart  = mSY - SY + MENU_SCREEN_INFO_YSTART1;
3005   int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
3006
3007   if (button == MB_MENU_INITIALIZE)
3008   {
3009     list = music_file_info;
3010
3011     if (list == NULL)
3012     {
3013       FadeMenuSoundsAndMusic();
3014
3015       ClearField();
3016       DrawHeadline();
3017
3018       DrawTextSCentered(ystart, font_title,
3019                         "No music info for this level set.");
3020
3021       DrawTextSCentered(ybottom, font_foot,
3022                         "Press any key or button for info menu");
3023
3024       return;
3025     }
3026   }
3027
3028   if (button == MB_MENU_LEAVE)
3029   {
3030     PlaySound(SND_MENU_ITEM_SELECTING);
3031
3032     FadeMenuSoundsAndMusic();
3033
3034     info_mode = INFO_MODE_MAIN;
3035     DrawInfoScreen();
3036
3037     return;
3038   }
3039   else if (button == MB_MENU_CHOICE || button == MB_MENU_INITIALIZE)
3040   {
3041     if (button != MB_MENU_INITIALIZE)
3042     {
3043       PlaySound(SND_MENU_ITEM_SELECTING);
3044
3045       if (list != NULL)
3046         list = list->next;
3047     }
3048
3049     if (list == NULL)
3050     {
3051       FadeMenuSoundsAndMusic();
3052
3053       info_mode = INFO_MODE_MAIN;
3054       DrawInfoScreen();
3055
3056       return;
3057     }
3058
3059     FadeMenuSoundsAndMusic();
3060
3061     if (list != music_file_info)
3062       FadeSetNextScreen();
3063
3064     if (button != MB_MENU_INITIALIZE)
3065       FadeOut(REDRAW_FIELD);
3066
3067     ClearField();
3068     DrawHeadline();
3069
3070     if (list->is_sound)
3071     {
3072       int sound = list->music;
3073
3074       if (sound_info[sound].loop)
3075         PlaySoundLoop(sound);
3076       else
3077         PlaySound(sound);
3078
3079       DrawTextSCentered(ystart, font_title, "The Game Background Sounds:");
3080     }
3081     else
3082     {
3083       PlayMusic(list->music);
3084
3085       DrawTextSCentered(ystart, font_title, "The Game Background Music:");
3086     }
3087
3088     ystart += ystep_title;
3089
3090     if (!strEqual(list->title, UNKNOWN_NAME))
3091     {
3092       if (!strEqual(list->title_header, UNKNOWN_NAME))
3093       {
3094         DrawTextSCentered(ystart, font_head, list->title_header);
3095         ystart += ystep_head;
3096       }
3097
3098       DrawTextFCentered(ystart, font_text, "\"%s\"", list->title);
3099       ystart += ystep_head;
3100     }
3101
3102     if (!strEqual(list->artist, UNKNOWN_NAME))
3103     {
3104       if (!strEqual(list->artist_header, UNKNOWN_NAME))
3105         DrawTextSCentered(ystart, font_head, list->artist_header);
3106       else
3107         DrawTextSCentered(ystart, font_head, "by");
3108
3109       ystart += ystep_head;
3110
3111       DrawTextFCentered(ystart, font_text, "%s", list->artist);
3112       ystart += ystep_head;
3113     }
3114
3115     if (!strEqual(list->album, UNKNOWN_NAME))
3116     {
3117       if (!strEqual(list->album_header, UNKNOWN_NAME))
3118         DrawTextSCentered(ystart, font_head, list->album_header);
3119       else
3120         DrawTextSCentered(ystart, font_head, "from the album");
3121
3122       ystart += ystep_head;
3123
3124       DrawTextFCentered(ystart, font_text, "\"%s\"", list->album);
3125       ystart += ystep_head;
3126     }
3127
3128     if (!strEqual(list->year, UNKNOWN_NAME))
3129     {
3130       if (!strEqual(list->year_header, UNKNOWN_NAME))
3131         DrawTextSCentered(ystart, font_head, list->year_header);
3132       else
3133         DrawTextSCentered(ystart, font_head, "from the year");
3134
3135       ystart += ystep_head;
3136
3137       DrawTextFCentered(ystart, font_text, "%s", list->year);
3138       ystart += ystep_head;
3139     }
3140
3141     DrawTextSCentered(ybottom, FONT_TEXT_4,
3142                       "Press any key or button for next page");
3143
3144     if (button != MB_MENU_INITIALIZE)
3145       FadeIn(REDRAW_FIELD);
3146   }
3147
3148   if (list != NULL && list->is_sound && sound_info[list->music].loop)
3149     PlaySoundLoop(list->music);
3150 }
3151
3152 static void DrawInfoScreen_CreditsScreen(int screen_nr)
3153 {
3154   int font_title = MENU_INFO_FONT_TITLE;
3155   int font_head  = MENU_INFO_FONT_HEAD;
3156   int font_text  = MENU_INFO_FONT_TEXT;
3157   int font_foot  = MENU_INFO_FONT_FOOT;
3158   int spacing_title = menu.headline1_spacing_info[info_mode];
3159   int spacing_head  = menu.headline2_spacing_info[info_mode];
3160   int spacing_para  = menu.paragraph_spacing_info[info_mode];
3161   int spacing_line  = menu.line_spacing_info[info_mode];
3162   int ystep_title = getMenuTextStep(spacing_title, font_title);
3163   int ystep_head  = getMenuTextStep(spacing_head,  font_head);
3164   int ystep_para  = getMenuTextStep(spacing_para,  font_text);
3165   int ystep_line  = getMenuTextStep(spacing_line,  font_text);
3166   int ystart  = mSY - SY + MENU_SCREEN_INFO_YSTART1;
3167   int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
3168
3169   ClearField();
3170   DrawHeadline();
3171
3172   DrawTextSCentered(ystart, font_title, "Credits:");
3173   ystart += ystep_title;
3174
3175   if (screen_nr == 0)
3176   {
3177     DrawTextSCentered(ystart, font_head,
3178                       "Special thanks to");
3179     ystart += ystep_head;
3180     DrawTextSCentered(ystart, font_text,
3181                       "Peter Liepa");
3182     ystart += ystep_head;
3183     DrawTextSCentered(ystart, font_head,
3184                       "for creating");
3185     ystart += ystep_head;
3186     DrawTextSCentered(ystart, font_text,
3187                       "\"Boulder Dash\"");
3188     ystart += ystep_head;
3189     DrawTextSCentered(ystart, font_head,
3190                       "in the year");
3191     ystart += ystep_head;
3192     DrawTextSCentered(ystart, font_text,
3193                       "1984");
3194     ystart += ystep_head;
3195     DrawTextSCentered(ystart, font_head,
3196                       "published by");
3197     ystart += ystep_head;
3198     DrawTextSCentered(ystart, font_text,
3199                       "First Star Software");
3200   }
3201   else if (screen_nr == 1)
3202   {
3203     DrawTextSCentered(ystart, font_head,
3204                       "Special thanks to");
3205     ystart += ystep_head;
3206     DrawTextSCentered(ystart, font_text,
3207                       "Klaus Heinz & Volker Wertich");
3208     ystart += ystep_head;
3209     DrawTextSCentered(ystart, font_head,
3210                       "for creating");
3211     ystart += ystep_head;
3212     DrawTextSCentered(ystart, font_text,
3213                       "\"Emerald Mine\"");
3214     ystart += ystep_head;
3215     DrawTextSCentered(ystart, font_head,
3216                       "in the year");
3217     ystart += ystep_head;
3218     DrawTextSCentered(ystart, font_text,
3219                       "1987");
3220     ystart += ystep_head;
3221     DrawTextSCentered(ystart, font_head,
3222                       "published by");
3223     ystart += ystep_head;
3224     DrawTextSCentered(ystart, font_text,
3225                       "Kingsoft");
3226   }
3227   else if (screen_nr == 2)
3228   {
3229     DrawTextSCentered(ystart, font_head,
3230                       "Special thanks to");
3231     ystart += ystep_head;
3232     DrawTextSCentered(ystart, font_text,
3233                       "Michael Stopp & Philip Jespersen");
3234     ystart += ystep_head;
3235     DrawTextSCentered(ystart, font_head,
3236                       "for creating");
3237     ystart += ystep_head;
3238     DrawTextSCentered(ystart, font_text,
3239                       "\"Supaplex\"");
3240     ystart += ystep_head;
3241     DrawTextSCentered(ystart, font_head,
3242                       "in the year");
3243     ystart += ystep_head;
3244     DrawTextSCentered(ystart, font_text,
3245                       "1991");
3246     ystart += ystep_head;
3247     DrawTextSCentered(ystart, font_head,
3248                       "published by");
3249     ystart += ystep_head;
3250     DrawTextSCentered(ystart, font_text,
3251                       "Digital Integration");
3252   }
3253   else if (screen_nr == 3)
3254   {
3255     DrawTextSCentered(ystart, font_head,
3256                       "Special thanks to");
3257     ystart += ystep_head;
3258     DrawTextSCentered(ystart, font_text,
3259                       "Hiroyuki Imabayashi");
3260     ystart += ystep_head;
3261     DrawTextSCentered(ystart, font_head,
3262                       "for creating");
3263     ystart += ystep_head;
3264     DrawTextSCentered(ystart, font_text,
3265                       "\"Sokoban\"");
3266     ystart += ystep_head;
3267     DrawTextSCentered(ystart, font_head,
3268                       "in the year");
3269     ystart += ystep_head;
3270     DrawTextSCentered(ystart, font_text,
3271                       "1982");
3272     ystart += ystep_head;
3273     DrawTextSCentered(ystart, font_head,
3274                       "published by");
3275     ystart += ystep_head;
3276     DrawTextSCentered(ystart, font_text,
3277                       "Thinking Rabbit");
3278   }
3279   else if (screen_nr == 4)
3280   {
3281     DrawTextSCentered(ystart, font_head,
3282                       "Special thanks to");
3283     ystart += ystep_head;
3284     DrawTextSCentered(ystart, font_text,
3285                       "Alan Bond");
3286     ystart += ystep_head;
3287     DrawTextSCentered(ystart, font_head,
3288                       "and");
3289     ystart += ystep_head;
3290     DrawTextSCentered(ystart, font_text,
3291                       "J\xfcrgen Bonhagen");
3292     ystart += ystep_head;
3293     DrawTextSCentered(ystart, font_head,
3294                       "for the continuous creation");
3295     ystart += ystep_line;
3296     DrawTextSCentered(ystart, font_head,
3297                       "of outstanding level sets");
3298   }
3299   else if (screen_nr == 5)
3300   {
3301     DrawTextSCentered(ystart, font_head,
3302                       "Thanks to");
3303     ystart += ystep_head;
3304     DrawTextSCentered(ystart, font_text,
3305                       "Peter Elzner");
3306     ystart += ystep_head;
3307     DrawTextSCentered(ystart, font_head,
3308                       "for ideas and inspiration by");
3309     ystart += ystep_head;
3310     DrawTextSCentered(ystart, font_text,
3311                       "Diamond Caves");
3312     ystart += ystep_para;
3313
3314     DrawTextSCentered(ystart, font_head,
3315                       "Thanks to");
3316     ystart += ystep_head;
3317     DrawTextSCentered(ystart, font_text,
3318                       "Steffest");
3319     ystart += ystep_head;
3320     DrawTextSCentered(ystart, font_head,
3321                       "for ideas and inspiration by");
3322     ystart += ystep_head;
3323     DrawTextSCentered(ystart, font_text,
3324                       "DX-Boulderdash");
3325   }
3326   else if (screen_nr == 6)
3327   {
3328     DrawTextSCentered(ystart, font_head,
3329                       "Thanks to");
3330     ystart += ystep_head;
3331     DrawTextSCentered(ystart, font_text,
3332                       "David Tritscher");
3333     ystart += ystep_head;
3334     DrawTextSCentered(ystart, font_head,
3335                       "for the code base used for the");
3336     ystart += ystep_line;
3337     DrawTextSCentered(ystart, font_head,
3338                       "native Emerald Mine engine");
3339   }
3340   else if (screen_nr == 7)
3341   {
3342     DrawTextSCentered(ystart, font_head,
3343                       "Thanks to");
3344     ystart += ystep_head;
3345     DrawTextSCentered(ystart, font_text,
3346                       "Guido Schulz");
3347     ystart += ystep_head;
3348     DrawTextSCentered(ystart, font_head,
3349                       "for the initial DOS port");
3350     ystart += ystep_para;
3351
3352     DrawTextSCentered(ystart, font_head,
3353                       "Thanks to");
3354     ystart += ystep_head;
3355     DrawTextSCentered(ystart, font_text,
3356                       "Karl H\xf6rnell");
3357     ystart += ystep_head;
3358     DrawTextSCentered(ystart, font_head,
3359                       "for some additional toons");
3360   }
3361   else if (screen_nr == 8)
3362   {
3363     DrawTextSCentered(ystart, font_head,
3364                       "And not to forget:");
3365     ystart += ystep_head;
3366     DrawTextSCentered(ystart, font_head,
3367                       "Many thanks to");
3368     ystart += ystep_head;
3369     DrawTextSCentered(ystart, font_text,
3370                       "All those who contributed");
3371     ystart += ystep_line;
3372     DrawTextSCentered(ystart, font_text,
3373                       "levels to this game");
3374     ystart += ystep_line;
3375     DrawTextSCentered(ystart, font_text,
3376                       "since 1995");
3377   }
3378
3379   DrawTextSCentered(ybottom, font_foot,
3380                     "Press any key or button for next page");
3381 }
3382
3383 void DrawInfoScreen_Credits()
3384 {
3385   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_CREDITS);
3386
3387   FadeMenuSoundsAndMusic();
3388
3389   FadeOut(REDRAW_FIELD);
3390
3391   HandleInfoScreen_Credits(MB_MENU_INITIALIZE);
3392
3393   FadeIn(REDRAW_FIELD);
3394 }
3395
3396 void HandleInfoScreen_Credits(int button)
3397 {
3398   static int screen_nr = 0;
3399   int num_screens = 9;
3400
3401   if (button == MB_MENU_INITIALIZE)
3402   {
3403     screen_nr = 0;
3404
3405     // DrawInfoScreen_CreditsScreen(screen_nr);
3406   }
3407
3408   if (button == MB_MENU_LEAVE)
3409   {
3410     PlaySound(SND_MENU_ITEM_SELECTING);
3411
3412     info_mode = INFO_MODE_MAIN;
3413     DrawInfoScreen();
3414
3415     return;
3416   }
3417   else if (button == MB_MENU_CHOICE || button == MB_MENU_INITIALIZE)
3418   {
3419     if (button != MB_MENU_INITIALIZE)
3420     {
3421       PlaySound(SND_MENU_ITEM_SELECTING);
3422
3423       screen_nr++;
3424     }
3425
3426     if (screen_nr >= num_screens)
3427     {
3428       FadeMenuSoundsAndMusic();
3429
3430       info_mode = INFO_MODE_MAIN;
3431       DrawInfoScreen();
3432
3433       return;
3434     }
3435
3436     if (screen_nr > 0)
3437       FadeSetNextScreen();
3438
3439     if (button != MB_MENU_INITIALIZE)
3440       FadeOut(REDRAW_FIELD);
3441
3442     DrawInfoScreen_CreditsScreen(screen_nr);
3443
3444     if (button != MB_MENU_INITIALIZE)
3445       FadeIn(REDRAW_FIELD);
3446   }
3447   else
3448   {
3449     PlayMenuSoundIfLoop();
3450   }
3451 }
3452
3453 void DrawInfoScreen_Program()
3454 {
3455   int font_title = MENU_INFO_FONT_TITLE;
3456   int font_head  = MENU_INFO_FONT_HEAD;
3457   int font_text  = MENU_INFO_FONT_TEXT;
3458   int font_foot  = MENU_INFO_FONT_FOOT;
3459   int spacing_title = menu.headline1_spacing_info[info_mode];
3460   int spacing_head  = menu.headline2_spacing_info[info_mode];
3461   int spacing_para  = menu.paragraph_spacing_info[info_mode];
3462   int spacing_line  = menu.line_spacing_info[info_mode];
3463   int ystep_title = getMenuTextStep(spacing_title, font_title);
3464   int ystep_head  = getMenuTextStep(spacing_head,  font_head);
3465   int ystep_para  = getMenuTextStep(spacing_para,  font_text);
3466   int ystep_line  = getMenuTextStep(spacing_line,  font_text);
3467   int ystart  = mSY - SY + MENU_SCREEN_INFO_YSTART1;
3468   int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
3469
3470   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_PROGRAM);
3471
3472   FadeOut(REDRAW_FIELD);
3473
3474   ClearField();
3475   DrawHeadline();
3476
3477   DrawTextSCentered(ystart, font_title, "Program Information:");
3478   ystart += ystep_title;
3479
3480   DrawTextSCentered(ystart, font_head,
3481                     "This game is Freeware!");
3482   ystart += ystep_head;
3483   DrawTextSCentered(ystart, font_head,
3484                     "If you like it, send e-mail to:");
3485   ystart += ystep_head;
3486   DrawTextSCentered(ystart, font_text,
3487                     setup.internal.program_email);
3488   ystart += ystep_para;
3489
3490   DrawTextSCentered(ystart, font_head,
3491                     "More information and levels:");
3492   ystart += ystep_head;
3493   DrawTextSCentered(ystart, font_text,
3494                     setup.internal.program_website);
3495   ystart += ystep_para;
3496
3497   DrawTextSCentered(ystart, font_head,
3498                     "If you have created new levels,");
3499   ystart += ystep_line;
3500   DrawTextSCentered(ystart, font_head,
3501                     "send them to me to include them!");
3502   ystart += ystep_head;
3503   DrawTextSCentered(ystart, font_head,
3504                     ":-)");
3505
3506   DrawTextSCentered(ybottom, font_foot,
3507                     "Press any key or button for info menu");
3508
3509   FadeIn(REDRAW_FIELD);
3510 }
3511
3512 void HandleInfoScreen_Program(int button)
3513 {
3514   if (button == MB_MENU_LEAVE)
3515   {
3516     PlaySound(SND_MENU_ITEM_SELECTING);
3517
3518     info_mode = INFO_MODE_MAIN;
3519     DrawInfoScreen();
3520
3521     return;
3522   }
3523   else if (button == MB_MENU_CHOICE)
3524   {
3525     PlaySound(SND_MENU_ITEM_SELECTING);
3526
3527     FadeMenuSoundsAndMusic();
3528
3529     info_mode = INFO_MODE_MAIN;
3530     DrawInfoScreen();
3531   }
3532   else
3533   {
3534     PlayMenuSoundIfLoop();
3535   }
3536 }
3537
3538 void DrawInfoScreen_Version()
3539 {
3540   int font_title = MENU_INFO_FONT_TITLE;
3541   int font_head  = MENU_INFO_FONT_HEAD;
3542   int font_text  = MENU_INFO_FONT_TEXT;
3543   int font_foot  = MENU_INFO_FONT_FOOT;
3544   int spacing_title = menu.headline1_spacing_info[info_mode];
3545   int spacing_head  = menu.headline2_spacing_info[info_mode];
3546   int spacing_para  = menu.paragraph_spacing_info[info_mode];
3547   int spacing_line  = menu.line_spacing_info[info_mode];
3548   int xstep = getFontWidth(font_text);
3549   int ystep_title = getMenuTextStep(spacing_title, font_title);
3550   int ystep_head  = getMenuTextStep(spacing_head,  font_head);
3551   int ystep_para  = getMenuTextStep(spacing_para,  font_text);
3552   int ystep_line  = getMenuTextStep(spacing_line,  font_text);
3553   int ystart  = mSY - SY + MENU_SCREEN_INFO_YSTART1;
3554   int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
3555   int xstart1 = mSX - SX + 2 * xstep;
3556   int xstart2 = mSX - SX + 18 * xstep;
3557   int xstart3 = mSX - SX + 28 * xstep;
3558   SDL_version sdl_version_compiled;
3559   const SDL_version *sdl_version_linked;
3560   int driver_name_len = 10;
3561 #if defined(TARGET_SDL2)
3562   SDL_version sdl_version_linked_ext;
3563   const char *driver_name = NULL;
3564 #else
3565   char driver_name[driver_name_len];
3566 #endif
3567
3568   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_VERSION);
3569
3570   FadeOut(REDRAW_FIELD);
3571
3572   ClearField();
3573   DrawHeadline();
3574
3575   DrawTextSCentered(ystart, font_title, "Version Information:");
3576   ystart += ystep_title;
3577
3578   DrawTextF(xstart1, ystart, font_head, "Name");
3579   DrawTextF(xstart2, ystart, font_text, getProgramTitleString());
3580   ystart += ystep_line;
3581
3582   if (!strEqual(getProgramVersionString(), getProgramRealVersionString()))
3583   {
3584     DrawTextF(xstart1, ystart, font_head, "Version (fake)");
3585     DrawTextF(xstart2, ystart, font_text, getProgramVersionString());
3586     ystart += ystep_line;
3587
3588     DrawTextF(xstart1, ystart, font_head, "Version (real)");
3589     DrawTextF(xstart2, ystart, font_text, getProgramRealVersionString());
3590     ystart += ystep_line;
3591   }
3592   else
3593   {
3594     DrawTextF(xstart1, ystart, font_head, "Version");
3595     DrawTextF(xstart2, ystart, font_text, getProgramVersionString());
3596     ystart += ystep_line;
3597   }
3598
3599   DrawTextF(xstart1, ystart, font_head, "Platform");
3600   DrawTextF(xstart2, ystart, font_text, PLATFORM_STRING);
3601   ystart += ystep_line;
3602
3603   DrawTextF(xstart1, ystart, font_head, "Target");
3604   DrawTextF(xstart2, ystart, font_text, TARGET_STRING);
3605   ystart += ystep_line;
3606
3607   DrawTextF(xstart1, ystart, font_head, "Source date");
3608   DrawTextF(xstart2, ystart, font_text, getSourceDateString());
3609   ystart += ystep_para;
3610
3611   DrawTextF(xstart1, ystart, font_head, "Library");
3612   DrawTextF(xstart2, ystart, font_head, "compiled");
3613   DrawTextF(xstart3, ystart, font_head, "linked");
3614   ystart += ystep_head;
3615
3616   SDL_VERSION(&sdl_version_compiled);
3617 #if defined(TARGET_SDL2)
3618   SDL_GetVersion(&sdl_version_linked_ext);
3619   sdl_version_linked = &sdl_version_linked_ext;
3620 #else
3621   sdl_version_linked = SDL_Linked_Version();
3622 #endif
3623
3624   DrawTextF(xstart1, ystart, font_text, "SDL");
3625   DrawTextF(xstart2, ystart, font_text, "%d.%d.%d",
3626             sdl_version_compiled.major,
3627             sdl_version_compiled.minor,
3628             sdl_version_compiled.patch);
3629   DrawTextF(xstart3, ystart, font_text, "%d.%d.%d",
3630             sdl_version_linked->major,
3631             sdl_version_linked->minor,
3632             sdl_version_linked->patch);
3633   ystart += ystep_line;
3634
3635   SDL_IMAGE_VERSION(&sdl_version_compiled);
3636   sdl_version_linked = IMG_Linked_Version();
3637
3638   DrawTextF(xstart1, ystart, font_text, "SDL_image");
3639   DrawTextF(xstart2, ystart, font_text, "%d.%d.%d",
3640             sdl_version_compiled.major,
3641             sdl_version_compiled.minor,
3642             sdl_version_compiled.patch);
3643   DrawTextF(xstart3, ystart, font_text, "%d.%d.%d",
3644             sdl_version_linked->major,
3645             sdl_version_linked->minor,
3646             sdl_version_linked->patch);
3647   ystart += ystep_line;
3648
3649   SDL_MIXER_VERSION(&sdl_version_compiled);
3650   sdl_version_linked = Mix_Linked_Version();
3651
3652   DrawTextF(xstart1, ystart, font_text, "SDL_mixer");
3653   DrawTextF(xstart2, ystart, font_text, "%d.%d.%d",
3654             sdl_version_compiled.major,
3655             sdl_version_compiled.minor,
3656             sdl_version_compiled.patch);
3657   DrawTextF(xstart3, ystart, font_text, "%d.%d.%d",
3658             sdl_version_linked->major,
3659             sdl_version_linked->minor,
3660             sdl_version_linked->patch);
3661   ystart += ystep_line;
3662
3663   SDL_NET_VERSION(&sdl_version_compiled);
3664   sdl_version_linked = SDLNet_Linked_Version();
3665
3666   DrawTextF(xstart1, ystart, font_text, "SDL_net");
3667   DrawTextF(xstart2, ystart, font_text, "%d.%d.%d",
3668             sdl_version_compiled.major,
3669             sdl_version_compiled.minor,
3670             sdl_version_compiled.patch);
3671   DrawTextF(xstart3, ystart, font_text, "%d.%d.%d",
3672             sdl_version_linked->major,
3673             sdl_version_linked->minor,
3674             sdl_version_linked->patch);
3675   ystart += ystep_para;
3676
3677   DrawTextF(xstart1, ystart, font_head, "Driver");
3678   DrawTextF(xstart2, ystart, font_head, "Requested");
3679   DrawTextF(xstart3, ystart, font_head, "Used");
3680   ystart += ystep_head;
3681
3682 #if defined(TARGET_SDL2)
3683   driver_name = getStringCopyNStatic(SDL_GetVideoDriver(0), driver_name_len);
3684 #else
3685   SDL_VideoDriverName(driver_name, driver_name_len);
3686 #endif
3687
3688   DrawTextF(xstart1, ystart, font_text, "SDL_VideoDriver");
3689   DrawTextF(xstart2, ystart, font_text, "%s", setup.system.sdl_videodriver);
3690   DrawTextF(xstart3, ystart, font_text, "%s", driver_name);
3691   ystart += ystep_line;
3692
3693 #if defined(TARGET_SDL2)
3694   driver_name = getStringCopyNStatic(SDL_GetAudioDriver(0), driver_name_len);
3695 #else
3696   SDL_AudioDriverName(driver_name, driver_name_len);
3697 #endif
3698
3699   DrawTextF(xstart1, ystart, font_text, "SDL_AudioDriver");
3700   DrawTextF(xstart2, ystart, font_text, "%s", setup.system.sdl_audiodriver);
3701   DrawTextF(xstart3, ystart, font_text, "%s", driver_name);
3702
3703   DrawTextSCentered(ybottom, font_foot,
3704                     "Press any key or button for info menu");
3705
3706   FadeIn(REDRAW_FIELD);
3707 }
3708
3709 void HandleInfoScreen_Version(int button)
3710 {
3711   if (button == MB_MENU_LEAVE)
3712   {
3713     PlaySound(SND_MENU_ITEM_SELECTING);
3714
3715     info_mode = INFO_MODE_MAIN;
3716     DrawInfoScreen();
3717
3718     return;
3719   }
3720   else if (button == MB_MENU_CHOICE)
3721   {
3722     PlaySound(SND_MENU_ITEM_SELECTING);
3723
3724     FadeMenuSoundsAndMusic();
3725
3726     info_mode = INFO_MODE_MAIN;
3727     DrawInfoScreen();
3728   }
3729   else
3730   {
3731     PlayMenuSoundIfLoop();
3732   }
3733 }
3734
3735 void DrawInfoScreen_LevelSet()
3736 {
3737   struct TitleMessageInfo *tmi = &readme;
3738   char *filename = getLevelSetInfoFilename();
3739   char *title = "Level Set Information:";
3740   int ystart  = mSY - SY + MENU_SCREEN_INFO_YSTART1;
3741   int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM;
3742
3743   if (filename == NULL)
3744   {
3745     DrawInfoScreen_NotAvailable(title, "No information for this level set.");
3746
3747     return;
3748   }
3749
3750   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_LEVELSET);
3751
3752   FadeOut(REDRAW_FIELD);
3753
3754   ClearField();
3755   DrawHeadline();
3756
3757   DrawTextSCentered(ystart, FONT_TEXT_1, title);
3758
3759   /* if x position set to "-1", automatically determine by playfield width */
3760   if (tmi->x == -1)
3761     tmi->x = SXSIZE / 2;
3762
3763   /* if y position set to "-1", use static default value */
3764   if (tmi->y == -1)
3765     tmi->y = 150;
3766
3767   /* if width set to "-1", automatically determine by playfield width */
3768   if (tmi->width == -1)
3769     tmi->width = SXSIZE - 2 * TILEX;
3770
3771   /* if height set to "-1", automatically determine by playfield height */
3772   if (tmi->height == -1)
3773     tmi->height = MENU_SCREEN_INFO_YBOTTOM - tmi->y - 10;
3774
3775   /* if chars set to "-1", automatically determine by text and font width */
3776   if (tmi->chars == -1)
3777     tmi->chars = tmi->width / getFontWidth(tmi->font);
3778   else
3779     tmi->width = tmi->chars * getFontWidth(tmi->font);
3780
3781   /* if lines set to "-1", automatically determine by text and font height */
3782   if (tmi->lines == -1)
3783     tmi->lines = tmi->height / getFontHeight(tmi->font);
3784   else
3785     tmi->height = tmi->lines * getFontHeight(tmi->font);
3786
3787   DrawTextFile(mSX + ALIGNED_TEXT_XPOS(tmi), mSY + ALIGNED_TEXT_YPOS(tmi),
3788                filename, tmi->font, tmi->chars, -1, tmi->lines, 0, -1,
3789                tmi->autowrap, tmi->centered, tmi->parse_comments);
3790
3791   DrawTextSCentered(ybottom, FONT_TEXT_4,
3792                     "Press any key or button for info menu");
3793
3794   FadeIn(REDRAW_FIELD);
3795 }
3796
3797 void HandleInfoScreen_LevelSet(int button)
3798 {
3799   if (button == MB_MENU_LEAVE)
3800   {
3801     PlaySound(SND_MENU_ITEM_SELECTING);
3802
3803     info_mode = INFO_MODE_MAIN;
3804     DrawInfoScreen();
3805
3806     return;
3807   }
3808   else if (button == MB_MENU_CHOICE)
3809   {
3810     PlaySound(SND_MENU_ITEM_SELECTING);
3811
3812     FadeMenuSoundsAndMusic();
3813
3814     info_mode = INFO_MODE_MAIN;
3815     DrawInfoScreen();
3816   }
3817   else
3818   {
3819     PlayMenuSoundIfLoop();
3820   }
3821 }
3822
3823 static void DrawInfoScreen()
3824 {
3825   if (info_mode == INFO_MODE_TITLE)
3826     DrawInfoScreen_TitleScreen();
3827   else if (info_mode == INFO_MODE_ELEMENTS)
3828     DrawInfoScreen_Elements();
3829   else if (info_mode == INFO_MODE_MUSIC)
3830     DrawInfoScreen_Music();
3831   else if (info_mode == INFO_MODE_CREDITS)
3832     DrawInfoScreen_Credits();
3833   else if (info_mode == INFO_MODE_PROGRAM)
3834     DrawInfoScreen_Program();
3835   else if (info_mode == INFO_MODE_VERSION)
3836     DrawInfoScreen_Version();
3837   else if (info_mode == INFO_MODE_LEVELSET)
3838     DrawInfoScreen_LevelSet();
3839   else
3840     DrawInfoScreen_Main();
3841
3842   if (info_mode != INFO_MODE_MAIN &&
3843       info_mode != INFO_MODE_TITLE &&
3844       info_mode != INFO_MODE_MUSIC)
3845     PlayMenuSoundsAndMusic();
3846 }
3847
3848 void HandleInfoScreen(int mx, int my, int dx, int dy, int button)
3849 {
3850   if (info_mode == INFO_MODE_TITLE)
3851     HandleInfoScreen_TitleScreen(button);
3852   else if (info_mode == INFO_MODE_ELEMENTS)
3853     HandleInfoScreen_Elements(button);
3854   else if (info_mode == INFO_MODE_MUSIC)
3855     HandleInfoScreen_Music(button);
3856   else if (info_mode == INFO_MODE_CREDITS)
3857     HandleInfoScreen_Credits(button);
3858   else if (info_mode == INFO_MODE_PROGRAM)
3859     HandleInfoScreen_Program(button);
3860   else if (info_mode == INFO_MODE_VERSION)
3861     HandleInfoScreen_Version(button);
3862   else if (info_mode == INFO_MODE_LEVELSET)
3863     HandleInfoScreen_LevelSet(button);
3864   else
3865     HandleInfoScreen_Main(mx, my, dx, dy, button);
3866 }
3867
3868
3869 /* ========================================================================= */
3870 /* type name functions                                                       */
3871 /* ========================================================================= */
3872
3873 void HandleTypeName(int newxpos, Key key)
3874 {
3875   static char last_player_name[MAX_PLAYER_NAME_LEN + 1];
3876   struct MainControlInfo *mci = getMainControlInfo(MAIN_CONTROL_NAME);
3877   struct TextPosInfo *pos = mci->pos_input;
3878   int startx = mSX + ALIGNED_TEXT_XPOS(pos);
3879   int starty = mSY + ALIGNED_TEXT_YPOS(pos);
3880   static int xpos = 0;
3881   int font_nr = pos->font;
3882   int font_active_nr = FONT_ACTIVE(font_nr);
3883   int font_width = getFontWidth(font_active_nr);
3884   char key_char = getValidConfigValueChar(getCharFromKey(key));
3885   boolean is_valid_key_char = (key_char != 0 && (key_char != ' ' || xpos > 0));
3886   boolean is_active = TRUE;
3887
3888   DrawBackgroundForFont(startx,starty, pos->width, pos->height, font_active_nr);
3889
3890   if (newxpos)
3891   {
3892     strcpy(last_player_name, setup.player_name);
3893
3894     xpos = newxpos;
3895
3896     StartTextInput(startx, starty, pos->width, pos->height);
3897   }
3898   else if (is_valid_key_char && xpos < MAX_PLAYER_NAME_LEN)
3899   {
3900     setup.player_name[xpos] = key_char;
3901     setup.player_name[xpos + 1] = 0;
3902
3903     xpos++;
3904   }
3905   else if ((key == KSYM_Delete || key == KSYM_BackSpace) && xpos > 0)
3906   {
3907     xpos--;
3908
3909     setup.player_name[xpos] = 0;
3910   }
3911   else if (key == KSYM_Return && xpos > 0)
3912   {
3913     SaveSetup();
3914
3915     is_active = FALSE;
3916
3917     SetGameStatus(GAME_MODE_MAIN);
3918   }
3919   else if (key == KSYM_Escape)
3920   {
3921     strcpy(setup.player_name, last_player_name);
3922
3923     is_active = FALSE;
3924
3925     SetGameStatus(GAME_MODE_MAIN);
3926   }
3927
3928   if (is_active)
3929   {
3930     pos->width = (strlen(setup.player_name) + 1) * font_width;
3931     startx = mSX + ALIGNED_TEXT_XPOS(pos);
3932
3933     DrawText(startx, starty, setup.player_name, font_active_nr);
3934     DrawText(startx + xpos * font_width, starty, "_", font_active_nr);
3935   }
3936   else
3937   {
3938     pos->width = strlen(setup.player_name) * font_width;
3939     startx = mSX + ALIGNED_TEXT_XPOS(pos);
3940
3941     DrawText(startx, starty, setup.player_name, font_nr);
3942
3943     StopTextInput();
3944   }
3945 }
3946
3947
3948 /* ========================================================================= */
3949 /* tree menu functions                                                       */
3950 /* ========================================================================= */
3951
3952 static void DrawChooseTree(TreeInfo **ti_ptr)
3953 {
3954   int fade_mask = REDRAW_FIELD;
3955
3956   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3957     fade_mask = REDRAW_ALL;
3958
3959   if (strEqual((*ti_ptr)->subdir, STRING_TOP_DIRECTORY))
3960   {
3961     SetGameStatus(GAME_MODE_MAIN);
3962
3963     DrawMainMenu();
3964
3965     return;
3966   }
3967
3968   UnmapAllGadgets();
3969
3970   FreeScreenGadgets();
3971   CreateScreenGadgets();
3972
3973   FadeOut(fade_mask);
3974
3975   /* needed if different viewport properties defined for choosing level (set) */
3976   ChangeViewportPropertiesIfNeeded();
3977
3978   if (game_status == GAME_MODE_LEVELNR)
3979     SetMainBackgroundImage(IMG_BACKGROUND_LEVELNR);
3980   else if (game_status == GAME_MODE_LEVELS)
3981     SetMainBackgroundImage(IMG_BACKGROUND_LEVELS);
3982
3983   ClearField();
3984
3985   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3986
3987   HandleChooseTree(0, 0, 0, 0, MB_MENU_INITIALIZE, ti_ptr);
3988   MapScreenTreeGadgets(*ti_ptr);
3989
3990   DrawMaskedBorder(fade_mask);
3991
3992   FadeIn(fade_mask);
3993 }
3994
3995 static void drawChooseTreeList(int first_entry, int num_page_entries,
3996                                TreeInfo *ti)
3997 {
3998   int i;
3999   char *title_string = NULL;
4000   int yoffset_sets = MENU_TITLE1_YPOS;
4001   int yoffset_setup = 16;
4002   int yoffset = (ti->type == TREE_TYPE_LEVEL_DIR ||
4003                  ti->type == TREE_TYPE_LEVEL_NR ? yoffset_sets : yoffset_setup);
4004
4005   title_string = ti->infotext;
4006
4007   DrawTextSCentered(mSY - SY + yoffset, FONT_TITLE_1, title_string);
4008
4009   clearMenuListArea();
4010
4011   for (i = 0; i < num_page_entries; i++)
4012   {
4013     TreeInfo *node, *node_first;
4014     int entry_pos = first_entry + i;
4015     int xpos = MENU_SCREEN_START_XPOS;
4016     int ypos = MENU_SCREEN_START_YPOS + i;
4017     int startx = mSX + xpos * 32;
4018     int starty = mSY + ypos * 32;
4019     int font_nr = FONT_TEXT_1;
4020     int font_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
4021     int startx_text = startx + font_xoffset;
4022     int startx_scrollbar = mSX + SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset;
4023     int text_size = startx_scrollbar - startx_text;
4024     int max_buffer_len = text_size / getFontWidth(font_nr);
4025     char buffer[max_buffer_len + 1];
4026
4027     node_first = getTreeInfoFirstGroupEntry(ti);
4028     node = getTreeInfoFromPos(node_first, entry_pos);
4029
4030     strncpy(buffer, node->name, max_buffer_len);
4031     buffer[max_buffer_len] = '\0';
4032
4033     DrawText(startx, starty, buffer, font_nr + node->color);
4034
4035     if (node->parent_link)
4036       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
4037     else if (node->level_group)
4038       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
4039     else
4040       initCursor(i, IMG_MENU_BUTTON);
4041   }
4042
4043   redraw_mask |= REDRAW_FIELD;
4044 }
4045
4046 static void drawChooseTreeInfo(int entry_pos, TreeInfo *ti)
4047 {
4048   TreeInfo *node, *node_first;
4049   int x, last_redraw_mask = redraw_mask;
4050   int ypos = MENU_TITLE2_YPOS;
4051   int font_nr = FONT_TITLE_2;
4052
4053   if (ti->type == TREE_TYPE_LEVEL_NR)
4054     DrawTextFCentered(ypos, font_nr, leveldir_current->name);
4055
4056   if (ti->type != TREE_TYPE_LEVEL_DIR)
4057     return;
4058
4059   node_first = getTreeInfoFirstGroupEntry(ti);
4060   node = getTreeInfoFromPos(node_first, entry_pos);
4061
4062   DrawBackgroundForFont(SX, SY + ypos, SXSIZE, getFontHeight(font_nr), font_nr);
4063
4064   if (node->parent_link)
4065     DrawTextFCentered(ypos, font_nr, "leave \"%s\"",
4066                       node->node_parent->name);
4067   else if (node->level_group)
4068     DrawTextFCentered(ypos, font_nr, "enter \"%s\"",
4069                       node->name);
4070   else if (ti->type == TREE_TYPE_LEVEL_DIR)
4071     DrawTextFCentered(ypos, font_nr, "%3d %s (%s)",
4072                       node->levels, (node->levels > 1 ? "levels" : "level"),
4073                       node->class_desc);
4074
4075   /* let BackToFront() redraw only what is needed */
4076   redraw_mask = last_redraw_mask;
4077   for (x = 0; x < SCR_FIELDX; x++)
4078     MarkTileDirty(x, 1);
4079 }
4080
4081 static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
4082                              TreeInfo **ti_ptr)
4083 {
4084   TreeInfo *ti = *ti_ptr;
4085   int x = 0;
4086   int y = ti->cl_cursor;
4087   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
4088   int num_entries = numTreeInfoInGroup(ti);
4089   int num_page_entries;
4090   boolean position_set_by_scrollbar = (dx == 999);
4091
4092   if (num_entries <= NUM_MENU_ENTRIES_ON_SCREEN)
4093     num_page_entries = num_entries;
4094   else
4095     num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
4096
4097   if (button == MB_MENU_INITIALIZE)
4098   {
4099     int num_entries = numTreeInfoInGroup(ti);
4100     int entry_pos = posTreeInfo(ti);
4101
4102     if (ti->cl_first == -1)
4103     {
4104       /* only on initialization */
4105       ti->cl_first = MAX(0, entry_pos - num_page_entries + 1);
4106       ti->cl_cursor = entry_pos - ti->cl_first;
4107     }
4108     else if (ti->cl_cursor >= num_page_entries ||
4109              (num_entries > num_page_entries &&
4110               num_entries - ti->cl_first < num_page_entries))
4111     {
4112       /* only after change of list size (by custom graphic configuration) */
4113       ti->cl_first = MAX(0, entry_pos - num_page_entries + 1);
4114       ti->cl_cursor = entry_pos - ti->cl_first;
4115     }
4116
4117     if (position_set_by_scrollbar)
4118       ti->cl_first = dy;
4119     else
4120       AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
4121                                 ti->cl_first, ti);
4122
4123     drawChooseTreeList(ti->cl_first, num_page_entries, ti);
4124     drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
4125     drawChooseTreeCursor(ti->cl_cursor, TRUE);
4126
4127     return;
4128   }
4129   else if (button == MB_MENU_LEAVE)
4130   {
4131     FadeSetLeaveMenu();
4132
4133     PlaySound(SND_MENU_ITEM_SELECTING);
4134
4135     if (ti->node_parent)
4136     {
4137       *ti_ptr = ti->node_parent;
4138       DrawChooseTree(ti_ptr);
4139     }
4140     else if (game_status == GAME_MODE_SETUP)
4141     {
4142       if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED ||
4143           setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY ||
4144           setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
4145         execSetupGame();
4146       else if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE ||
4147                setup_mode == SETUP_MODE_CHOOSE_SCALING_TYPE ||
4148                setup_mode == SETUP_MODE_CHOOSE_RENDERING)
4149         execSetupGraphics();
4150       else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_SIMPLE ||
4151                setup_mode == SETUP_MODE_CHOOSE_VOLUME_LOOPS ||
4152                setup_mode == SETUP_MODE_CHOOSE_VOLUME_MUSIC)
4153         execSetupSound();
4154       else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL ||
4155                setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE ||
4156                setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE ||
4157                setup_mode == SETUP_MODE_CHOOSE_TRANSPARENCY ||
4158                setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_0 ||
4159                setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_0 ||
4160                setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_1 ||
4161                setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_1)
4162         execSetupTouch();
4163       else
4164         execSetupArtwork();
4165     }
4166     else
4167     {
4168       if (game_status == GAME_MODE_LEVELNR)
4169       {
4170         int new_level_nr = atoi(level_number_current->identifier);
4171
4172         HandleMainMenu_SelectLevel(0, 0, new_level_nr);
4173       }
4174
4175       SetGameStatus(GAME_MODE_MAIN);
4176
4177       DrawMainMenu();
4178     }
4179
4180     return;
4181   }
4182
4183   if (mx || my)         /* mouse input */
4184   {
4185     x = (mx - mSX) / 32;
4186     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
4187   }
4188   else if (dx || dy)    /* keyboard or scrollbar/scrollbutton input */
4189   {
4190     /* move cursor instead of scrolling when already at start/end of list */
4191     if (dy == -1 * SCROLL_LINE && ti->cl_first == 0)
4192       dy = -1;
4193     else if (dy == +1 * SCROLL_LINE &&
4194              ti->cl_first + num_page_entries == num_entries)
4195       dy = 1;
4196
4197     /* handle scrolling screen one line or page */
4198     if (ti->cl_cursor + dy < 0 ||
4199         ti->cl_cursor + dy > num_page_entries - 1)
4200     {
4201       boolean redraw = FALSE;
4202
4203       if (ABS(dy) == SCROLL_PAGE)
4204         step = num_page_entries - 1;
4205
4206       if (dy < 0 && ti->cl_first > 0)
4207       {
4208         /* scroll page/line up */
4209
4210         ti->cl_first -= step;
4211         if (ti->cl_first < 0)
4212           ti->cl_first = 0;
4213
4214         redraw = TRUE;
4215       }
4216       else if (dy > 0 && ti->cl_first + num_page_entries < num_entries)
4217       {
4218         /* scroll page/line down */
4219
4220         ti->cl_first += step;
4221         if (ti->cl_first + num_page_entries > num_entries)
4222           ti->cl_first = MAX(0, num_entries - num_page_entries);
4223
4224         redraw = TRUE;
4225       }
4226
4227       if (redraw)
4228       {
4229         drawChooseTreeList(ti->cl_first, num_page_entries, ti);
4230         drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
4231         drawChooseTreeCursor(ti->cl_cursor, TRUE);
4232
4233         AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
4234                                   ti->cl_first, ti);
4235       }
4236
4237       return;
4238     }
4239
4240     /* handle moving cursor one line */
4241     y = ti->cl_cursor + dy;
4242   }
4243
4244   if (dx == 1)
4245   {
4246     TreeInfo *node_first, *node_cursor;
4247     int entry_pos = ti->cl_first + y;
4248
4249     node_first = getTreeInfoFirstGroupEntry(ti);
4250     node_cursor = getTreeInfoFromPos(node_first, entry_pos);
4251
4252     if (node_cursor->node_group)
4253     {
4254       FadeSetEnterMenu();
4255
4256       PlaySound(SND_MENU_ITEM_SELECTING);
4257
4258       node_cursor->cl_first = ti->cl_first;
4259       node_cursor->cl_cursor = ti->cl_cursor;
4260       *ti_ptr = node_cursor->node_group;
4261       DrawChooseTree(ti_ptr);
4262
4263       return;
4264     }
4265   }
4266   else if (dx == -1 && ti->node_parent)
4267   {
4268     FadeSetLeaveMenu();
4269
4270     PlaySound(SND_MENU_ITEM_SELECTING);
4271
4272     *ti_ptr = ti->node_parent;
4273     DrawChooseTree(ti_ptr);
4274
4275     return;
4276   }
4277
4278   if (!anyScrollbarGadgetActive() &&
4279       IN_VIS_MENU(x, y) &&
4280       mx < screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x &&
4281       y >= 0 && y < num_page_entries)
4282   {
4283     if (button)
4284     {
4285       if (y != ti->cl_cursor)
4286       {
4287         PlaySound(SND_MENU_ITEM_ACTIVATING);
4288
4289         drawChooseTreeCursor(ti->cl_cursor, FALSE);
4290         drawChooseTreeCursor(y, TRUE);
4291         drawChooseTreeInfo(ti->cl_first + y, ti);
4292
4293         ti->cl_cursor = y;
4294       }
4295       else if (dx < 0)
4296       {
4297         if (game_status == GAME_MODE_SETUP)
4298         {
4299           if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED ||
4300               setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY ||
4301               setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
4302             execSetupGame();
4303           else if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE ||
4304                    setup_mode == SETUP_MODE_CHOOSE_SCALING_TYPE ||
4305                    setup_mode == SETUP_MODE_CHOOSE_RENDERING)
4306             execSetupGraphics();
4307           else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_SIMPLE ||
4308                    setup_mode == SETUP_MODE_CHOOSE_VOLUME_LOOPS ||
4309                    setup_mode == SETUP_MODE_CHOOSE_VOLUME_MUSIC)
4310             execSetupSound();
4311           else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL ||
4312                    setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE ||
4313                    setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE ||
4314                    setup_mode == SETUP_MODE_CHOOSE_TRANSPARENCY ||
4315                    setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_0 ||
4316                    setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_0 ||
4317                    setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_1 ||
4318                    setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_1)
4319             execSetupTouch();
4320           else
4321             execSetupArtwork();
4322         }
4323       }
4324     }
4325     else
4326     {
4327       TreeInfo *node_first, *node_cursor;
4328       int entry_pos = ti->cl_first + y;
4329
4330       PlaySound(SND_MENU_ITEM_SELECTING);
4331
4332       node_first = getTreeInfoFirstGroupEntry(ti);
4333       node_cursor = getTreeInfoFromPos(node_first, entry_pos);
4334
4335       if (node_cursor->node_group)
4336       {
4337         FadeSetEnterMenu();
4338
4339         node_cursor->cl_first = ti->cl_first;
4340         node_cursor->cl_cursor = ti->cl_cursor;
4341         *ti_ptr = node_cursor->node_group;
4342         DrawChooseTree(ti_ptr);
4343       }
4344       else if (node_cursor->parent_link)
4345       {
4346         FadeSetLeaveMenu();
4347
4348         *ti_ptr = node_cursor->node_parent;
4349         DrawChooseTree(ti_ptr);
4350       }
4351       else
4352       {
4353         FadeSetEnterMenu();
4354
4355         node_cursor->cl_first = ti->cl_first;
4356         node_cursor->cl_cursor = ti->cl_cursor;
4357         *ti_ptr = node_cursor;
4358
4359         if (ti->type == TREE_TYPE_LEVEL_DIR)
4360         {
4361           LoadLevelSetup_SeriesInfo();
4362
4363           SaveLevelSetup_LastSeries();
4364           SaveLevelSetup_SeriesInfo();
4365           TapeErase();
4366         }
4367
4368         if (game_status == GAME_MODE_SETUP)
4369         {
4370           if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED ||
4371               setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY ||
4372               setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
4373             execSetupGame();
4374           else if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE ||
4375                    setup_mode == SETUP_MODE_CHOOSE_SCALING_TYPE ||
4376                    setup_mode == SETUP_MODE_CHOOSE_RENDERING)
4377             execSetupGraphics();
4378           else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_SIMPLE ||
4379                    setup_mode == SETUP_MODE_CHOOSE_VOLUME_LOOPS ||
4380                    setup_mode == SETUP_MODE_CHOOSE_VOLUME_MUSIC)
4381             execSetupSound();
4382           else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL ||
4383                    setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE ||
4384                    setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE ||
4385                    setup_mode == SETUP_MODE_CHOOSE_TRANSPARENCY ||
4386                    setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_0 ||
4387                    setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_0 ||
4388                    setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_1 ||
4389                    setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_1)
4390             execSetupTouch();
4391           else
4392             execSetupArtwork();
4393         }
4394         else
4395         {
4396           if (game_status == GAME_MODE_LEVELNR)
4397           {
4398             int new_level_nr = atoi(level_number_current->identifier);
4399
4400             HandleMainMenu_SelectLevel(0, 0, new_level_nr);
4401           }
4402
4403           SetGameStatus(GAME_MODE_MAIN);
4404
4405           DrawMainMenu();
4406         }
4407       }
4408     }
4409   }
4410 }
4411
4412 void DrawChooseLevelSet()
4413 {
4414   FadeMenuSoundsAndMusic();
4415
4416   DrawChooseTree(&leveldir_current);
4417
4418   PlayMenuSoundsAndMusic();
4419 }
4420
4421 void HandleChooseLevelSet(int mx, int my, int dx, int dy, int button)
4422 {
4423   HandleChooseTree(mx, my, dx, dy, button, &leveldir_current);
4424 }
4425
4426 void DrawChooseLevelNr()
4427 {
4428   int i;
4429
4430   FadeMenuSoundsAndMusic();
4431
4432   if (level_number != NULL)
4433   {
4434     freeTreeInfo(level_number);
4435
4436     level_number = NULL;
4437   }
4438
4439   for (i = leveldir_current->first_level; i <= leveldir_current->last_level;i++)
4440   {
4441     TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_LEVEL_NR);
4442     char identifier[32], name[32];
4443     int value = i;
4444
4445     /* temporarily load level info to get level name */
4446     LoadLevelInfoOnly(i);
4447
4448     ti->node_top = &level_number;
4449     ti->sort_priority = 10000 + value;
4450     ti->color = (level.no_level_file ? FC_BLUE :
4451                  LevelStats_getSolved(i) ? FC_GREEN :
4452                  LevelStats_getPlayed(i) ? FC_YELLOW : FC_RED);
4453
4454     snprintf(identifier, sizeof(identifier), "%d", value);
4455     snprintf(name, sizeof(name), "%03d: %s", value,
4456              (level.no_level_file ? "(no file)" : level.name));
4457
4458     setString(&ti->identifier, identifier);
4459     setString(&ti->name, name);
4460     setString(&ti->name_sorting, name);
4461
4462     pushTreeInfo(&level_number, ti);
4463   }
4464
4465   /* sort level number values to start with lowest level number */
4466   sortTreeInfo(&level_number);
4467
4468   /* set current level number to current level number */
4469   level_number_current =
4470     getTreeInfoFromIdentifier(level_number, i_to_a(level_nr));
4471
4472   /* if that also fails, set current level number to first available level */
4473   if (level_number_current == NULL)
4474     level_number_current = level_number;
4475
4476   DrawChooseTree(&level_number_current);
4477
4478   PlayMenuSoundsAndMusic();
4479 }
4480
4481 void HandleChooseLevelNr(int mx, int my, int dx, int dy, int button)
4482 {
4483   HandleChooseTree(mx, my, dx, dy, button, &level_number_current);
4484 }
4485
4486 void DrawHallOfFame(int highlight_position)
4487 {
4488   int fade_mask = REDRAW_FIELD;
4489
4490   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
4491     fade_mask = REDRAW_ALL;
4492
4493   UnmapAllGadgets();
4494   FadeMenuSoundsAndMusic();
4495
4496   /* (this is needed when called from GameEnd() after winning a game) */
4497   KeyboardAutoRepeatOn();
4498
4499   /* (this is needed when called from GameEnd() after winning a game) */
4500   SetDrawDeactivationMask(REDRAW_NONE);
4501   SetDrawBackgroundMask(REDRAW_FIELD);
4502
4503   if (highlight_position < 0) 
4504     LoadScore(level_nr);
4505   else
4506     SetAnimStatus(GAME_MODE_PSEUDO_SCORESNEW);
4507
4508   FadeSetEnterScreen();
4509
4510   FadeOut(fade_mask);
4511
4512   /* needed if different viewport properties defined for scores */
4513   ChangeViewportPropertiesIfNeeded();
4514
4515   PlayMenuSoundsAndMusic();
4516
4517   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
4518
4519   HandleHallOfFame(highlight_position, 0, 0, 0, MB_MENU_INITIALIZE);
4520
4521   DrawMaskedBorder(fade_mask);
4522
4523   FadeIn(fade_mask);
4524 }
4525
4526 static void drawHallOfFameList(int first_entry, int highlight_position)
4527 {
4528   int i, j;
4529
4530   SetMainBackgroundImage(IMG_BACKGROUND_SCORES);
4531   ClearField();
4532
4533   DrawTextSCentered(MENU_TITLE1_YPOS, FONT_TITLE_1, "Hall Of Fame");
4534   DrawTextFCentered(MENU_TITLE2_YPOS, FONT_TITLE_2,
4535                     "HighScores of Level %d", level_nr);
4536
4537   for (i = 0; i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
4538   {
4539     int entry = first_entry + i;
4540     boolean active = (entry == highlight_position);
4541     int font_nr1 = (active ? FONT_TEXT_1_ACTIVE : FONT_TEXT_1);
4542     int font_nr2 = (active ? FONT_TEXT_2_ACTIVE : FONT_TEXT_2);
4543     int font_nr3 = (active ? FONT_TEXT_3_ACTIVE : FONT_TEXT_3);
4544     int font_nr4 = (active ? FONT_TEXT_4_ACTIVE : FONT_TEXT_4);
4545     int dxoff = getFontDrawOffsetX(font_nr1);
4546     int dx1 = 3 * getFontWidth(font_nr1);
4547     int dx2 = dx1 + getFontWidth(font_nr1);
4548     int dx3 = SXSIZE - 2 * (mSX - SX + dxoff) - 5 * getFontWidth(font_nr4);
4549     int num_dots = (dx3 - dx2) / getFontWidth(font_nr3);
4550     int sy = mSY + 64 + i * 32;
4551
4552     DrawText(mSX, sy, int2str(entry + 1, 3), font_nr1);
4553     DrawText(mSX + dx1, sy, ".", font_nr1);
4554
4555     for (j = 0; j < num_dots; j++)
4556       DrawText(mSX + dx2 + j * getFontWidth(font_nr3), sy, ".", font_nr3);
4557
4558     if (!strEqual(highscore[entry].Name, EMPTY_PLAYER_NAME))
4559       DrawText(mSX + dx2, sy, highscore[entry].Name, font_nr2);
4560
4561     DrawText(mSX + dx3, sy, int2str(highscore[entry].Score, 5), font_nr4);
4562   }
4563
4564   redraw_mask |= REDRAW_FIELD;
4565 }
4566
4567 void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
4568 {
4569   static int first_entry = 0;
4570   static int highlight_position = 0;
4571   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
4572
4573   if (button == MB_MENU_INITIALIZE)
4574   {
4575     first_entry = 0;
4576     highlight_position = mx;
4577     drawHallOfFameList(first_entry, highlight_position);
4578
4579     return;
4580   }
4581
4582   if (ABS(dy) == SCROLL_PAGE)           /* handle scrolling one page */
4583     step = NUM_MENU_ENTRIES_ON_SCREEN - 1;
4584
4585   if (dy < 0)
4586   {
4587     if (first_entry > 0)
4588     {
4589       first_entry -= step;
4590       if (first_entry < 0)
4591         first_entry = 0;
4592
4593       drawHallOfFameList(first_entry, highlight_position);
4594     }
4595   }
4596   else if (dy > 0)
4597   {
4598     if (first_entry + NUM_MENU_ENTRIES_ON_SCREEN < MAX_SCORE_ENTRIES)
4599     {
4600       first_entry += step;
4601       if (first_entry + NUM_MENU_ENTRIES_ON_SCREEN > MAX_SCORE_ENTRIES)
4602         first_entry = MAX(0, MAX_SCORE_ENTRIES - NUM_MENU_ENTRIES_ON_SCREEN);
4603
4604       drawHallOfFameList(first_entry, highlight_position);
4605     }
4606   }
4607   else if (button == MB_MENU_LEAVE)
4608   {
4609     PlaySound(SND_MENU_ITEM_SELECTING);
4610
4611     FadeSound(SND_BACKGROUND_SCORES);
4612
4613     SetGameStatus(GAME_MODE_MAIN);
4614
4615     DrawMainMenu();
4616   }
4617   else if (button == MB_MENU_CHOICE)
4618   {
4619     PlaySound(SND_MENU_ITEM_SELECTING);
4620
4621     FadeSound(SND_BACKGROUND_SCORES);
4622
4623     SetGameStatus(GAME_MODE_MAIN);
4624
4625     DrawMainMenu();
4626   }
4627
4628   if (game_status == GAME_MODE_SCORES)
4629     PlayMenuSoundIfLoop();
4630 }
4631
4632
4633 /* ========================================================================= */
4634 /* setup screen functions                                                    */
4635 /* ========================================================================= */
4636
4637 static struct TokenInfo *setup_info;
4638 static int num_setup_info;      /* number of setup entries shown on screen */
4639 static int max_setup_info;      /* total number of setup entries in list */
4640
4641 static char *window_size_text;
4642 static char *scaling_type_text;
4643 static char *rendering_mode_text;
4644 static char *scroll_delay_text;
4645 static char *snapshot_mode_text;
4646 static char *game_speed_text;
4647 static char *graphics_set_name;
4648 static char *sounds_set_name;
4649 static char *music_set_name;
4650 static char *volume_simple_text;
4651 static char *volume_loops_text;
4652 static char *volume_music_text;
4653 static char *touch_controls_text;
4654 static char *move_distance_text;
4655 static char *drop_distance_text;
4656 static char *transparency_text;
4657 static char *grid_size_text[2][2];
4658
4659 static void execSetupMain()
4660 {
4661   setup_mode = SETUP_MODE_MAIN;
4662
4663   DrawSetupScreen();
4664 }
4665
4666 static void execSetupGame_setGameSpeeds()
4667 {
4668   if (game_speeds == NULL)
4669   {
4670     int i;
4671
4672     for (i = 0; game_speeds_list[i].value != -1; i++)
4673     {
4674       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
4675       char identifier[32], name[32];
4676       int value = game_speeds_list[i].value;
4677       char *text = game_speeds_list[i].text;
4678
4679       ti->node_top = &game_speeds;
4680       ti->sort_priority = 10000 - value;
4681
4682       sprintf(identifier, "%d", value);
4683       sprintf(name, "%s", text);
4684
4685       setString(&ti->identifier, identifier);
4686       setString(&ti->name, name);
4687       setString(&ti->name_sorting, name);
4688       setString(&ti->infotext, STR_SETUP_CHOOSE_GAME_SPEED);
4689
4690       pushTreeInfo(&game_speeds, ti);
4691     }
4692
4693     /* sort game speed values to start with slowest game speed */
4694     sortTreeInfo(&game_speeds);
4695
4696     /* set current game speed to configured game speed value */
4697     game_speed_current =
4698       getTreeInfoFromIdentifier(game_speeds, i_to_a(setup.game_frame_delay));
4699
4700     /* if that fails, set current game speed to reliable default value */
4701     if (game_speed_current == NULL)
4702       game_speed_current =
4703         getTreeInfoFromIdentifier(game_speeds, i_to_a(GAME_FRAME_DELAY));
4704
4705     /* if that also fails, set current game speed to first available speed */
4706     if (game_speed_current == NULL)
4707       game_speed_current = game_speeds;
4708   }
4709
4710   setup.game_frame_delay = atoi(game_speed_current->identifier);
4711
4712   /* needed for displaying game speed text instead of identifier */
4713   game_speed_text = game_speed_current->name;
4714 }
4715
4716 static void execSetupGame_setScrollDelays()
4717 {
4718   if (scroll_delays == NULL)
4719   {
4720     int i;
4721
4722     for (i = 0; scroll_delays_list[i].value != -1; i++)
4723     {
4724       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
4725       char identifier[32], name[32];
4726       int value = scroll_delays_list[i].value;
4727       char *text = scroll_delays_list[i].text;
4728
4729       ti->node_top = &scroll_delays;
4730       ti->sort_priority = value;
4731
4732       sprintf(identifier, "%d", value);
4733       sprintf(name, "%s", text);
4734
4735       setString(&ti->identifier, identifier);
4736       setString(&ti->name, name);
4737       setString(&ti->name_sorting, name);
4738       setString(&ti->infotext, STR_SETUP_CHOOSE_SCROLL_DELAY);
4739
4740       pushTreeInfo(&scroll_delays, ti);
4741     }
4742
4743     /* sort scroll delay values to start with lowest scroll delay value */
4744     sortTreeInfo(&scroll_delays);
4745
4746     /* set current scroll delay value to configured scroll delay value */
4747     scroll_delay_current =
4748       getTreeInfoFromIdentifier(scroll_delays,i_to_a(setup.scroll_delay_value));
4749
4750     /* if that fails, set current scroll delay to reliable default value */
4751     if (scroll_delay_current == NULL)
4752       scroll_delay_current =
4753         getTreeInfoFromIdentifier(scroll_delays, i_to_a(STD_SCROLL_DELAY));
4754
4755     /* if that also fails, set current scroll delay to first available value */
4756     if (scroll_delay_current == NULL)
4757       scroll_delay_current = scroll_delays;
4758   }
4759
4760   setup.scroll_delay_value = atoi(scroll_delay_current->identifier);
4761
4762   /* needed for displaying scroll delay text instead of identifier */
4763   scroll_delay_text = scroll_delay_current->name;
4764 }
4765
4766 static void execSetupGame_setSnapshotModes()
4767 {
4768   if (snapshot_modes == NULL)
4769   {
4770     int i;
4771
4772     for (i = 0; snapshot_modes_list[i].value != NULL; i++)
4773     {
4774       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
4775       char identifier[32], name[32];
4776       char *value = snapshot_modes_list[i].value;
4777       char *text = snapshot_modes_list[i].text;
4778
4779       ti->node_top = &snapshot_modes;
4780       ti->sort_priority = i;
4781
4782       sprintf(identifier, "%s", value);
4783       sprintf(name, "%s", text);
4784
4785       setString(&ti->identifier, identifier);
4786       setString(&ti->name, name);
4787       setString(&ti->name_sorting, name);
4788       setString(&ti->infotext, STR_SETUP_CHOOSE_SNAPSHOT_MODE);
4789
4790       pushTreeInfo(&snapshot_modes, ti);
4791     }
4792
4793     /* sort snapshot mode values to start with lowest snapshot mode value */
4794     sortTreeInfo(&snapshot_modes);
4795
4796     /* set current snapshot mode value to configured snapshot mode value */
4797     snapshot_mode_current =
4798       getTreeInfoFromIdentifier(snapshot_modes, setup.engine_snapshot_mode);
4799
4800     /* if that fails, set current snapshot mode to reliable default value */
4801     if (snapshot_mode_current == NULL)
4802       snapshot_mode_current =
4803         getTreeInfoFromIdentifier(snapshot_modes, STR_SNAPSHOT_MODE_DEFAULT);
4804
4805     /* if that also fails, set current snapshot mode to first available value */
4806     if (snapshot_mode_current == NULL)
4807       snapshot_mode_current = snapshot_modes;
4808   }
4809
4810   setup.engine_snapshot_mode = snapshot_mode_current->identifier;
4811
4812   /* needed for displaying snapshot mode text instead of identifier */
4813   snapshot_mode_text = snapshot_mode_current->name;
4814 }
4815
4816 static void execSetupGame()
4817 {
4818   execSetupGame_setGameSpeeds();
4819   execSetupGame_setScrollDelays();
4820   execSetupGame_setSnapshotModes();
4821
4822   setup_mode = SETUP_MODE_GAME;
4823
4824   DrawSetupScreen();
4825 }
4826
4827 static void execSetupChooseGameSpeed()
4828 {
4829   setup_mode = SETUP_MODE_CHOOSE_GAME_SPEED;
4830
4831   DrawSetupScreen();
4832 }
4833
4834 static void execSetupChooseScrollDelay()
4835 {
4836   setup_mode = SETUP_MODE_CHOOSE_SCROLL_DELAY;
4837
4838   DrawSetupScreen();
4839 }
4840
4841 static void execSetupChooseSnapshotMode()
4842 {
4843   setup_mode = SETUP_MODE_CHOOSE_SNAPSHOT_MODE;
4844
4845   DrawSetupScreen();
4846 }
4847
4848 static void execSetupEditor()
4849 {
4850   setup_mode = SETUP_MODE_EDITOR;
4851
4852   DrawSetupScreen();
4853 }
4854
4855 static void execSetupGraphics_setWindowSizes(boolean update_list)
4856 {
4857   if (window_sizes != NULL && update_list)
4858   {
4859     freeTreeInfo(window_sizes);
4860
4861     window_sizes = NULL;
4862   }
4863
4864   if (window_sizes == NULL)
4865   {
4866     boolean current_window_size_found = FALSE;
4867     int i;
4868
4869     for (i = 0; window_sizes_list[i].value != -1; i++)
4870     {
4871       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
4872       char identifier[32], name[32];
4873       int value = window_sizes_list[i].value;
4874       char *text = window_sizes_list[i].text;
4875
4876       ti->node_top = &window_sizes;
4877       ti->sort_priority = value;
4878
4879       sprintf(identifier, "%d", value);
4880       sprintf(name, "%s", text);
4881
4882       setString(&ti->identifier, identifier);
4883       setString(&ti->name, name);
4884       setString(&ti->name_sorting, name);
4885       setString(&ti->infotext, STR_SETUP_CHOOSE_WINDOW_SIZE);
4886
4887       pushTreeInfo(&window_sizes, ti);
4888
4889       if (value == setup.window_scaling_percent)
4890         current_window_size_found = TRUE;
4891     }
4892
4893     if (!current_window_size_found)
4894     {
4895       // add entry for non-preset window scaling value
4896
4897       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
4898       char identifier[32], name[32];
4899       int value = setup.window_scaling_percent;
4900
4901       ti->node_top = &window_sizes;
4902       ti->sort_priority = value;
4903
4904       sprintf(identifier, "%d", value);
4905       sprintf(name, "%d %% (Current)", value);
4906
4907       setString(&ti->identifier, identifier);
4908       setString(&ti->name, name);
4909       setString(&ti->name_sorting, name);
4910       setString(&ti->infotext, STR_SETUP_CHOOSE_WINDOW_SIZE);
4911
4912       pushTreeInfo(&window_sizes, ti);
4913     }
4914
4915     /* sort window size values to start with lowest window size value */
4916     sortTreeInfo(&window_sizes);
4917
4918     /* set current window size value to configured window size value */
4919     window_size_current =
4920       getTreeInfoFromIdentifier(window_sizes,
4921                                 i_to_a(setup.window_scaling_percent));
4922
4923     /* if that fails, set current window size to reliable default value */
4924     if (window_size_current == NULL)
4925       window_size_current =
4926         getTreeInfoFromIdentifier(window_sizes,
4927                                   i_to_a(STD_WINDOW_SCALING_PERCENT));
4928
4929     /* if that also fails, set current window size to first available value */
4930     if (window_size_current == NULL)
4931       window_size_current = window_sizes;
4932   }
4933
4934   setup.window_scaling_percent = atoi(window_size_current->identifier);
4935
4936   /* needed for displaying window size text instead of identifier */
4937   window_size_text = window_size_current->name;
4938 }
4939
4940 static void execSetupGraphics_setScalingTypes()
4941 {
4942   if (scaling_types == NULL)
4943   {
4944     int i;
4945
4946     for (i = 0; scaling_types_list[i].value != NULL; i++)
4947     {
4948       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
4949       char identifier[32], name[32];
4950       char *value = scaling_types_list[i].value;
4951       char *text = scaling_types_list[i].text;
4952
4953       ti->node_top = &scaling_types;
4954       ti->sort_priority = i;
4955
4956       sprintf(identifier, "%s", value);
4957       sprintf(name, "%s", text);
4958
4959       setString(&ti->identifier, identifier);
4960       setString(&ti->name, name);
4961       setString(&ti->name_sorting, name);
4962       setString(&ti->infotext, STR_SETUP_CHOOSE_SCALING_TYPE);
4963
4964       pushTreeInfo(&scaling_types, ti);
4965     }
4966
4967     /* sort scaling type values to start with lowest scaling type value */
4968     sortTreeInfo(&scaling_types);
4969
4970     /* set current scaling type value to configured scaling type value */
4971     scaling_type_current =
4972       getTreeInfoFromIdentifier(scaling_types, setup.window_scaling_quality);
4973
4974     /* if that fails, set current scaling type to reliable default value */
4975     if (scaling_type_current == NULL)
4976       scaling_type_current =
4977         getTreeInfoFromIdentifier(scaling_types, SCALING_QUALITY_DEFAULT);
4978
4979     /* if that also fails, set current scaling type to first available value */
4980     if (scaling_type_current == NULL)
4981       scaling_type_current = scaling_types;
4982   }
4983
4984   setup.window_scaling_quality = scaling_type_current->identifier;
4985
4986   /* needed for displaying scaling type text instead of identifier */
4987   scaling_type_text = scaling_type_current->name;
4988 }
4989
4990 static void execSetupGraphics_setRenderingModes()
4991 {
4992   if (rendering_modes == NULL)
4993   {
4994     int i;
4995
4996     for (i = 0; rendering_modes_list[i].value != NULL; i++)
4997     {
4998       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
4999       char identifier[32], name[32];
5000       char *value = rendering_modes_list[i].value;
5001       char *text = rendering_modes_list[i].text;
5002
5003       ti->node_top = &rendering_modes;
5004       ti->sort_priority = i;
5005
5006       sprintf(identifier, "%s", value);
5007       sprintf(name, "%s", text);
5008
5009       setString(&ti->identifier, identifier);
5010       setString(&ti->name, name);
5011       setString(&ti->name_sorting, name);
5012       setString(&ti->infotext, STR_SETUP_CHOOSE_RENDERING);
5013
5014       pushTreeInfo(&rendering_modes, ti);
5015     }
5016
5017     /* sort rendering mode values to start with lowest rendering mode value */
5018     sortTreeInfo(&rendering_modes);
5019
5020     /* set current rendering mode value to configured rendering mode value */
5021     rendering_mode_current =
5022       getTreeInfoFromIdentifier(rendering_modes, setup.screen_rendering_mode);
5023
5024     /* if that fails, set current rendering mode to reliable default value */
5025     if (rendering_mode_current == NULL)
5026       rendering_mode_current =
5027         getTreeInfoFromIdentifier(rendering_modes,
5028                                   STR_SPECIAL_RENDERING_DEFAULT);
5029
5030     /* if that also fails, set current rendering mode to first available one */
5031     if (rendering_mode_current == NULL)
5032       rendering_mode_current = rendering_modes;
5033   }
5034
5035   setup.screen_rendering_mode = rendering_mode_current->identifier;
5036
5037   /* needed for displaying rendering mode text instead of identifier */
5038   rendering_mode_text = rendering_mode_current->name;
5039 }
5040
5041 static void execSetupGraphics()
5042 {
5043   // update "setup.window_scaling_percent" from list selection
5044   // (in this case, window scaling was changed on setup screen)
5045   if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE)
5046     execSetupGraphics_setWindowSizes(FALSE);
5047
5048   // update list selection from "setup.window_scaling_percent"
5049   // (window scaling may have changed by resizing the window)
5050   execSetupGraphics_setWindowSizes(TRUE);
5051
5052   execSetupGraphics_setScalingTypes();
5053   execSetupGraphics_setRenderingModes();
5054
5055   setup_mode = SETUP_MODE_GRAPHICS;
5056
5057   DrawSetupScreen();
5058
5059 #if defined(TARGET_SDL2)
5060   // window scaling may have changed at this point
5061   ToggleFullscreenOrChangeWindowScalingIfNeeded();
5062
5063   // window scaling quality may have changed at this point
5064   if (!strEqual(setup.window_scaling_quality, video.window_scaling_quality))
5065     SDLSetWindowScalingQuality(setup.window_scaling_quality);
5066
5067   // screen rendering mode may have changed at this point
5068   SDLSetScreenRenderingMode(setup.screen_rendering_mode);
5069 #endif
5070 }
5071
5072 static void execSetupChooseWindowSize()
5073 {
5074   setup_mode = SETUP_MODE_CHOOSE_WINDOW_SIZE;
5075
5076   DrawSetupScreen();
5077 }
5078
5079 static void execSetupChooseScalingType()
5080 {
5081   setup_mode = SETUP_MODE_CHOOSE_SCALING_TYPE;
5082
5083   DrawSetupScreen();
5084 }
5085
5086 static void execSetupChooseRenderingMode()
5087 {
5088   setup_mode = SETUP_MODE_CHOOSE_RENDERING;
5089
5090   DrawSetupScreen();
5091 }
5092
5093 static void execSetupChooseVolumeSimple()
5094 {
5095   setup_mode = SETUP_MODE_CHOOSE_VOLUME_SIMPLE;
5096
5097   DrawSetupScreen();
5098 }
5099
5100 static void execSetupChooseVolumeLoops()
5101 {
5102   setup_mode = SETUP_MODE_CHOOSE_VOLUME_LOOPS;
5103
5104   DrawSetupScreen();
5105 }
5106
5107 static void execSetupChooseVolumeMusic()
5108 {
5109   setup_mode = SETUP_MODE_CHOOSE_VOLUME_MUSIC;
5110
5111   DrawSetupScreen();
5112 }
5113
5114 static void execSetupSound()
5115 {
5116   if (volumes_simple == NULL)
5117   {
5118     boolean current_volume_simple_found = FALSE;
5119     int i;
5120
5121     for (i = 0; volumes_list[i].value != -1; i++)
5122     {
5123       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
5124       char identifier[32], name[32];
5125       int value = volumes_list[i].value;
5126       char *text = volumes_list[i].text;
5127
5128       ti->node_top = &volumes_simple;
5129       ti->sort_priority = value;
5130
5131       sprintf(identifier, "%d", value);
5132       sprintf(name, "%s", text);
5133
5134       setString(&ti->identifier, identifier);
5135       setString(&ti->name, name);
5136       setString(&ti->name_sorting, name);
5137       setString(&ti->infotext, STR_SETUP_CHOOSE_VOLUME_SIMPLE);
5138
5139       pushTreeInfo(&volumes_simple, ti);
5140
5141       if (value == setup.volume_simple)
5142         current_volume_simple_found = TRUE;
5143     }
5144
5145     if (!current_volume_simple_found)
5146     {
5147       // add entry for non-preset volume value
5148
5149       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
5150       char identifier[32], name[32];
5151       int value = setup.volume_simple;
5152
5153       ti->node_top = &volumes_simple;
5154       ti->sort_priority = value;
5155
5156       sprintf(identifier, "%d", value);
5157       sprintf(name, "%d %% (Current)", value);
5158
5159       setString(&ti->identifier, identifier);
5160       setString(&ti->name, name);
5161       setString(&ti->name_sorting, name);
5162       setString(&ti->infotext, STR_SETUP_CHOOSE_VOLUME_SIMPLE);
5163
5164       pushTreeInfo(&volumes_simple, ti);
5165     }
5166
5167     /* sort volume values to start with lowest volume value */
5168     sortTreeInfo(&volumes_simple);
5169
5170     /* set current volume value to configured volume value */
5171     volume_simple_current =
5172       getTreeInfoFromIdentifier(volumes_simple,i_to_a(setup.volume_simple));
5173
5174     /* if that fails, set current volume to reliable default value */
5175     if (volume_simple_current == NULL)
5176       volume_simple_current =
5177         getTreeInfoFromIdentifier(volumes_simple, i_to_a(100));
5178
5179     /* if that also fails, set current volume to first available value */
5180     if (volume_simple_current == NULL)
5181       volume_simple_current = volumes_simple;
5182   }
5183
5184   if (volumes_loops == NULL)
5185   {
5186     boolean current_volume_loops_found = FALSE;
5187     int i;
5188
5189     for (i = 0; volumes_list[i].value != -1; i++)
5190     {
5191       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
5192       char identifier[32], name[32];
5193       int value = volumes_list[i].value;
5194       char *text = volumes_list[i].text;
5195
5196       ti->node_top = &volumes_loops;
5197       ti->sort_priority = value;
5198
5199       sprintf(identifier, "%d", value);
5200       sprintf(name, "%s", text);
5201
5202       setString(&ti->identifier, identifier);
5203       setString(&ti->name, name);
5204       setString(&ti->name_sorting, name);
5205       setString(&ti->infotext, STR_SETUP_CHOOSE_VOLUME_LOOPS);
5206
5207       pushTreeInfo(&volumes_loops, ti);
5208
5209       if (value == setup.volume_loops)
5210         current_volume_loops_found = TRUE;
5211     }
5212
5213     if (!current_volume_loops_found)
5214     {
5215       // add entry for non-preset volume value
5216
5217       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
5218       char identifier[32], name[32];
5219       int value = setup.volume_loops;
5220
5221       ti->node_top = &volumes_loops;
5222       ti->sort_priority = value;
5223
5224       sprintf(identifier, "%d", value);
5225       sprintf(name, "%d %% (Current)", value);
5226
5227       setString(&ti->identifier, identifier);
5228       setString(&ti->name, name);
5229       setString(&ti->name_sorting, name);
5230       setString(&ti->infotext, STR_SETUP_CHOOSE_VOLUME_LOOPS);
5231
5232       pushTreeInfo(&volumes_loops, ti);
5233     }
5234
5235     /* sort volume values to start with lowest volume value */
5236     sortTreeInfo(&volumes_loops);
5237
5238     /* set current volume value to configured volume value */
5239     volume_loops_current =
5240       getTreeInfoFromIdentifier(volumes_loops,i_to_a(setup.volume_loops));
5241
5242     /* if that fails, set current volume to reliable default value */
5243     if (volume_loops_current == NULL)
5244       volume_loops_current =
5245         getTreeInfoFromIdentifier(volumes_loops, i_to_a(100));
5246
5247     /* if that also fails, set current volume to first available value */
5248     if (volume_loops_current == NULL)
5249       volume_loops_current = volumes_loops;
5250   }
5251
5252   if (volumes_music == NULL)
5253   {
5254     boolean current_volume_music_found = FALSE;
5255     int i;
5256
5257     for (i = 0; volumes_list[i].value != -1; i++)
5258     {
5259       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
5260       char identifier[32], name[32];
5261       int value = volumes_list[i].value;
5262       char *text = volumes_list[i].text;
5263
5264       ti->node_top = &volumes_music;
5265       ti->sort_priority = value;
5266
5267       sprintf(identifier, "%d", value);
5268       sprintf(name, "%s", text);
5269
5270       setString(&ti->identifier, identifier);
5271       setString(&ti->name, name);
5272       setString(&ti->name_sorting, name);
5273       setString(&ti->infotext, STR_SETUP_CHOOSE_VOLUME_MUSIC);
5274
5275       pushTreeInfo(&volumes_music, ti);
5276
5277       if (value == setup.volume_music)
5278         current_volume_music_found = TRUE;
5279     }
5280
5281     if (!current_volume_music_found)
5282     {
5283       // add entry for non-preset volume value
5284
5285       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
5286       char identifier[32], name[32];
5287       int value = setup.volume_music;
5288
5289       ti->node_top = &volumes_music;
5290       ti->sort_priority = value;
5291
5292       sprintf(identifier, "%d", value);
5293       sprintf(name, "%d %% (Current)", value);
5294
5295       setString(&ti->identifier, identifier);
5296       setString(&ti->name, name);
5297       setString(&ti->name_sorting, name);
5298       setString(&ti->infotext, STR_SETUP_CHOOSE_VOLUME_MUSIC);
5299
5300       pushTreeInfo(&volumes_music, ti);
5301     }
5302
5303     /* sort volume values to start with lowest volume value */
5304     sortTreeInfo(&volumes_music);
5305
5306     /* set current volume value to configured volume value */
5307     volume_music_current =
5308       getTreeInfoFromIdentifier(volumes_music,i_to_a(setup.volume_music));
5309
5310     /* if that fails, set current volume to reliable default value */
5311     if (volume_music_current == NULL)
5312       volume_music_current =
5313         getTreeInfoFromIdentifier(volumes_music, i_to_a(100));
5314
5315     /* if that also fails, set current volume to first available value */
5316     if (volume_music_current == NULL)
5317       volume_music_current = volumes_music;
5318   }
5319
5320   setup.volume_simple = atoi(volume_simple_current->identifier);
5321   setup.volume_loops  = atoi(volume_loops_current->identifier);
5322   setup.volume_music  = atoi(volume_music_current->identifier);
5323
5324   /* needed for displaying volume text instead of identifier */
5325   volume_simple_text = volume_simple_current->name;
5326   volume_loops_text = volume_loops_current->name;
5327   volume_music_text = volume_music_current->name;
5328
5329   setup_mode = SETUP_MODE_SOUND;
5330
5331   DrawSetupScreen();
5332 }
5333
5334 static void execSetupChooseTouchControls()
5335 {
5336   setup_mode = SETUP_MODE_CHOOSE_TOUCH_CONTROL;
5337
5338   DrawSetupScreen();
5339 }
5340
5341 static void execSetupChooseMoveDistance()
5342 {
5343   setup_mode = SETUP_MODE_CHOOSE_MOVE_DISTANCE;
5344
5345   DrawSetupScreen();
5346 }
5347
5348 static void execSetupChooseDropDistance()
5349 {
5350   setup_mode = SETUP_MODE_CHOOSE_DROP_DISTANCE;
5351
5352   DrawSetupScreen();
5353 }
5354
5355 static void execSetupChooseTransparency()
5356 {
5357   setup_mode = SETUP_MODE_CHOOSE_TRANSPARENCY;
5358
5359   DrawSetupScreen();
5360 }
5361
5362 static void execSetupChooseGridXSize_0()
5363 {
5364   setup_mode = SETUP_MODE_CHOOSE_GRID_XSIZE_0;
5365
5366   DrawSetupScreen();
5367 }
5368
5369 static void execSetupChooseGridYSize_0()
5370 {
5371   setup_mode = SETUP_MODE_CHOOSE_GRID_YSIZE_0;
5372
5373   DrawSetupScreen();
5374 }
5375
5376 static void execSetupChooseGridXSize_1()
5377 {
5378   setup_mode = SETUP_MODE_CHOOSE_GRID_XSIZE_1;
5379
5380   DrawSetupScreen();
5381 }
5382
5383 static void execSetupChooseGridYSize_1()
5384 {
5385   setup_mode = SETUP_MODE_CHOOSE_GRID_YSIZE_1;
5386
5387   DrawSetupScreen();
5388 }
5389
5390 static void execSetupConfigureVirtualButtons()
5391 {
5392   ConfigureVirtualButtons();
5393
5394   DrawSetupScreen();
5395 }
5396
5397 static void execSetupTouch()
5398 {
5399   int i, j, k;
5400
5401   if (touch_controls == NULL)
5402   {
5403     for (i = 0; touch_controls_list[i].value != NULL; i++)
5404     {
5405       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
5406       char identifier[32], name[32];
5407       char *value = touch_controls_list[i].value;
5408       char *text = touch_controls_list[i].text;
5409
5410       ti->node_top = &touch_controls;
5411       ti->sort_priority = i;
5412
5413       sprintf(identifier, "%s", value);
5414       sprintf(name, "%s", text);
5415
5416       setString(&ti->identifier, identifier);
5417       setString(&ti->name, name);
5418       setString(&ti->name_sorting, name);
5419       setString(&ti->infotext, STR_SETUP_CHOOSE_TOUCH_CONTROL);
5420
5421       pushTreeInfo(&touch_controls, ti);
5422     }
5423
5424     /* sort touch control values to start with lowest touch control value */
5425     sortTreeInfo(&touch_controls);
5426
5427     /* set current touch control value to configured touch control value */
5428     touch_control_current =
5429       getTreeInfoFromIdentifier(touch_controls, setup.touch.control_type);
5430
5431     /* if that fails, set current touch control to reliable default value */
5432     if (touch_control_current == NULL)
5433       touch_control_current =
5434         getTreeInfoFromIdentifier(touch_controls, TOUCH_CONTROL_DEFAULT);
5435
5436     /* if that also fails, set current touch control to first available value */
5437     if (touch_control_current == NULL)
5438       touch_control_current = touch_controls;
5439   }
5440
5441   if (move_distances == NULL)
5442   {
5443     for (i = 0; distances_list[i].value != -1; i++)
5444     {
5445       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
5446       char identifier[32], name[32];
5447       int value = distances_list[i].value;
5448       char *text = distances_list[i].text;
5449
5450       ti->node_top = &move_distances;
5451       ti->sort_priority = value;
5452
5453       sprintf(identifier, "%d", value);
5454       sprintf(name, "%s", text);
5455
5456       setString(&ti->identifier, identifier);
5457       setString(&ti->name, name);
5458       setString(&ti->name_sorting, name);
5459       setString(&ti->infotext, STR_SETUP_CHOOSE_MOVE_DISTANCE);
5460
5461       pushTreeInfo(&move_distances, ti);
5462     }
5463
5464     /* sort distance values to start with lowest distance value */
5465     sortTreeInfo(&move_distances);
5466
5467     /* set current distance value to configured distance value */
5468     move_distance_current =
5469       getTreeInfoFromIdentifier(move_distances,
5470                                 i_to_a(setup.touch.move_distance));
5471
5472     /* if that fails, set current distance to reliable default value */
5473     if (move_distance_current == NULL)
5474       move_distance_current =
5475         getTreeInfoFromIdentifier(move_distances,
5476                                   i_to_a(TOUCH_MOVE_DISTANCE_DEFAULT));
5477
5478     /* if that also fails, set current distance to first available value */
5479     if (move_distance_current == NULL)
5480       move_distance_current = move_distances;
5481   }
5482
5483   if (drop_distances == NULL)
5484   {
5485     for (i = 0; distances_list[i].value != -1; i++)
5486     {
5487       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
5488       char identifier[32], name[32];
5489       int value = distances_list[i].value;
5490       char *text = distances_list[i].text;
5491
5492       ti->node_top = &drop_distances;
5493       ti->sort_priority = value;
5494
5495       sprintf(identifier, "%d", value);
5496       sprintf(name, "%s", text);
5497
5498       setString(&ti->identifier, identifier);
5499       setString(&ti->name, name);
5500       setString(&ti->name_sorting, name);
5501       setString(&ti->infotext, STR_SETUP_CHOOSE_DROP_DISTANCE);
5502
5503       pushTreeInfo(&drop_distances, ti);
5504     }
5505
5506     /* sort distance values to start with lowest distance value */
5507     sortTreeInfo(&drop_distances);
5508
5509     /* set current distance value to configured distance value */
5510     drop_distance_current =
5511       getTreeInfoFromIdentifier(drop_distances,
5512                                 i_to_a(setup.touch.drop_distance));
5513
5514     /* if that fails, set current distance to reliable default value */
5515     if (drop_distance_current == NULL)
5516       drop_distance_current =
5517         getTreeInfoFromIdentifier(drop_distances,
5518                                   i_to_a(TOUCH_DROP_DISTANCE_DEFAULT));
5519
5520     /* if that also fails, set current distance to first available value */
5521     if (drop_distance_current == NULL)
5522       drop_distance_current = drop_distances;
5523   }
5524
5525   if (transparencies == NULL)
5526   {
5527     for (i = 0; transparencies_list[i].value != -1; i++)
5528     {
5529       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
5530       char identifier[32], name[32];
5531       int value = transparencies_list[i].value;
5532       char *text = transparencies_list[i].text;
5533
5534       ti->node_top = &transparencies;
5535       ti->sort_priority = value;
5536
5537       sprintf(identifier, "%d", value);
5538       sprintf(name, "%s", text);
5539
5540       setString(&ti->identifier, identifier);
5541       setString(&ti->name, name);
5542       setString(&ti->name_sorting, name);
5543       setString(&ti->infotext, STR_SETUP_CHOOSE_TRANSPARENCY);
5544
5545       pushTreeInfo(&transparencies, ti);
5546     }
5547
5548     /* sort transparency values to start with lowest transparency value */
5549     sortTreeInfo(&transparencies);
5550
5551     /* set current transparency value to configured transparency value */
5552     transparency_current =
5553       getTreeInfoFromIdentifier(transparencies,
5554                                 i_to_a(setup.touch.transparency));
5555
5556     /* if that fails, set current transparency to reliable default value */
5557     if (transparency_current == NULL)
5558       transparency_current =
5559         getTreeInfoFromIdentifier(transparencies,
5560                                   i_to_a(TOUCH_TRANSPARENCY_DEFAULT));
5561
5562     /* if that also fails, set current transparency to first available value */
5563     if (transparency_current == NULL)
5564       transparency_current = transparencies;
5565   }
5566
5567   for (i = 0; i < 2; i++)
5568   {
5569     for (j = 0; j < 2; j++)
5570     {
5571       if (grid_sizes[i][j] == NULL)
5572       {
5573         for (k = 0; grid_sizes_list[k].value != -1; k++)
5574         {
5575           TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
5576           char identifier[32], name[32];
5577           int value = grid_sizes_list[k].value;
5578           char *text = grid_sizes_list[k].text;
5579
5580           ti->node_top = &grid_sizes[i][j];
5581           ti->sort_priority = value;
5582
5583           sprintf(identifier, "%d", value);
5584           sprintf(name, "%s", text);
5585
5586           setString(&ti->identifier, identifier);
5587           setString(&ti->name, name);
5588           setString(&ti->name_sorting, name);
5589           setString(&ti->infotext,
5590                     (i == 0 ?
5591                      (j == 0 ?
5592                       STR_SETUP_CHOOSE_GRID_XSIZE_0 :
5593                       STR_SETUP_CHOOSE_GRID_YSIZE_0) :
5594                      (j == 0 ?
5595                       STR_SETUP_CHOOSE_GRID_XSIZE_1 :
5596                       STR_SETUP_CHOOSE_GRID_YSIZE_1)));
5597
5598           pushTreeInfo(&grid_sizes[i][j], ti);
5599         }
5600
5601         /* sort grid size values to start with lowest grid size value */
5602         sortTreeInfo(&grid_sizes[i][j]);
5603
5604         /* set current grid size value to configured grid size value */
5605         grid_size_current[i][j] =
5606           getTreeInfoFromIdentifier(grid_sizes[i][j],
5607                                     i_to_a(j == 0 ?
5608                                            setup.touch.grid_xsize[i] :
5609                                            setup.touch.grid_ysize[i]));
5610
5611         /* if that fails, set current grid size to reliable default value */
5612         if (grid_size_current[i][j] == NULL)
5613           grid_size_current[i][j] =
5614             getTreeInfoFromIdentifier(grid_sizes[i][j],
5615                                       i_to_a(j == 0 ?
5616                                              DEFAULT_GRID_XSIZE(i) :
5617                                              DEFAULT_GRID_YSIZE(i)));
5618
5619         /* if that also fails, set current grid size to first available value */
5620         if (grid_size_current[i][j] == NULL)
5621           grid_size_current[i][j] = grid_sizes[i][j];
5622       }
5623     }
5624   }
5625
5626   setup.touch.control_type = touch_control_current->identifier;
5627   setup.touch.move_distance = atoi(move_distance_current->identifier);
5628   setup.touch.drop_distance = atoi(drop_distance_current->identifier);
5629   setup.touch.transparency = atoi(transparency_current->identifier);
5630
5631   for (i = 0; i < 2; i++)
5632   {
5633     setup.touch.grid_xsize[i] = atoi(grid_size_current[i][0]->identifier);
5634     setup.touch.grid_ysize[i] = atoi(grid_size_current[i][1]->identifier);
5635
5636     if (i == GRID_ACTIVE_NR())
5637     {
5638       overlay.grid_xsize = setup.touch.grid_xsize[i];
5639       overlay.grid_ysize = setup.touch.grid_ysize[i];
5640     }
5641   }
5642
5643   /* needed for displaying value text instead of identifier */
5644   touch_controls_text = touch_control_current->name;
5645   move_distance_text = move_distance_current->name;
5646   drop_distance_text = drop_distance_current->name;
5647   transparency_text = transparency_current->name;
5648
5649   for (i = 0; i < 2; i++)
5650     for (j = 0; j < 2; j++)
5651       grid_size_text[i][j] = grid_size_current[i][j]->name;
5652
5653   setup_mode = SETUP_MODE_TOUCH;
5654
5655   DrawSetupScreen();
5656 }
5657
5658 static void execSetupArtwork()
5659 {
5660 #if 0
5661   printf("::: '%s', '%s', '%s'\n",
5662          artwork.gfx_current->subdir,
5663          artwork.gfx_current->fullpath,
5664          artwork.gfx_current->basepath);
5665 #endif
5666
5667   setup.graphics_set = artwork.gfx_current->identifier;
5668   setup.sounds_set = artwork.snd_current->identifier;
5669   setup.music_set = artwork.mus_current->identifier;
5670
5671   /* needed if last screen (setup choice) changed graphics, sounds or music */
5672   ReloadCustomArtwork(0);
5673
5674   /* needed for displaying artwork name instead of artwork identifier */
5675   graphics_set_name = artwork.gfx_current->name;
5676   sounds_set_name = artwork.snd_current->name;
5677   music_set_name = artwork.mus_current->name;
5678
5679   setup_mode = SETUP_MODE_ARTWORK;
5680
5681   DrawSetupScreen();
5682 }
5683
5684 static void execSetupChooseGraphics()
5685 {
5686   setup_mode = SETUP_MODE_CHOOSE_GRAPHICS;
5687
5688   DrawSetupScreen();
5689 }
5690
5691 static void execSetupChooseSounds()
5692 {
5693   setup_mode = SETUP_MODE_CHOOSE_SOUNDS;
5694
5695   DrawSetupScreen();
5696 }
5697
5698 static void execSetupChooseMusic()
5699 {
5700   setup_mode = SETUP_MODE_CHOOSE_MUSIC;
5701
5702   DrawSetupScreen();
5703 }
5704
5705 static void execSetupInput()
5706 {
5707   setup_mode = SETUP_MODE_INPUT;
5708
5709   DrawSetupScreen();
5710 }
5711
5712 static void execSetupShortcuts()
5713 {
5714   setup_mode = SETUP_MODE_SHORTCUTS;
5715
5716   DrawSetupScreen();
5717 }
5718
5719 static void execSetupShortcuts1()
5720 {
5721   setup_mode = SETUP_MODE_SHORTCUTS_1;
5722
5723   DrawSetupScreen();
5724 }
5725
5726 static void execSetupShortcuts2()
5727 {
5728   setup_mode = SETUP_MODE_SHORTCUTS_2;
5729
5730   DrawSetupScreen();
5731 }
5732
5733 static void execSetupShortcuts3()
5734 {
5735   setup_mode = SETUP_MODE_SHORTCUTS_3;
5736
5737   DrawSetupScreen();
5738 }
5739
5740 static void execSetupShortcuts4()
5741 {
5742   setup_mode = SETUP_MODE_SHORTCUTS_4;
5743
5744   DrawSetupScreen();
5745 }
5746
5747 static void execSetupShortcuts5()
5748 {
5749   setup_mode = SETUP_MODE_SHORTCUTS_5;
5750
5751   DrawSetupScreen();
5752 }
5753
5754 static void execExitSetup()
5755 {
5756   SetGameStatus(GAME_MODE_MAIN);
5757
5758   DrawMainMenu();
5759 }
5760
5761 static void execSaveAndExitSetup()
5762 {
5763   SaveSetup();
5764   execExitSetup();
5765 }
5766
5767 static struct
5768 {
5769   void *value;
5770   void *related_value;
5771 } hide_related_entry_list[] =
5772 {
5773   { &setup.game_frame_delay,            execSetupChooseGameSpeed        },
5774   { &setup.game_frame_delay,            &game_speed_text                },
5775
5776   { &setup.scroll_delay_value,          execSetupChooseScrollDelay      },
5777   { &setup.scroll_delay_value,          &scroll_delay_text              },
5778
5779   { &setup.engine_snapshot_mode,        execSetupChooseSnapshotMode     },
5780   { &setup.engine_snapshot_mode,        &snapshot_mode_text             },
5781
5782   { &setup.window_scaling_percent,      execSetupChooseWindowSize       },
5783   { &setup.window_scaling_percent,      &window_size_text               },
5784
5785   { &setup.window_scaling_quality,      execSetupChooseScalingType      },
5786   { &setup.window_scaling_quality,      &scaling_type_text              },
5787
5788   { &setup.screen_rendering_mode,       execSetupChooseRenderingMode    },
5789   { &setup.screen_rendering_mode,       &rendering_mode_text            },
5790
5791   { &setup.graphics_set,                execSetupChooseGraphics         },
5792   { &setup.graphics_set,                &graphics_set_name              },
5793
5794   { &setup.sounds_set,                  execSetupChooseSounds           },
5795   { &setup.sounds_set,                  &sounds_set_name                },
5796
5797   { &setup.music_set,                   execSetupChooseMusic            },
5798   { &setup.music_set,                   &music_set_name                 },
5799
5800   { &setup.volume_simple,               execSetupChooseVolumeSimple     },
5801   { &setup.volume_simple,               &volume_simple_text             },
5802
5803   { &setup.volume_loops,                execSetupChooseVolumeLoops      },
5804   { &setup.volume_loops,                &volume_loops_text              },
5805
5806   { &setup.volume_music,                execSetupChooseVolumeMusic      },
5807   { &setup.volume_music,                &volume_music_text              },
5808
5809   { &setup.touch.control_type,          execSetupChooseTouchControls    },
5810   { &setup.touch.control_type,          &touch_controls_text            },
5811
5812   { &setup.touch.move_distance,         execSetupChooseMoveDistance     },
5813   { &setup.touch.move_distance,         &move_distance_text             },
5814
5815   { &setup.touch.drop_distance,         execSetupChooseDropDistance     },
5816   { &setup.touch.drop_distance,         &drop_distance_text             },
5817
5818   { &setup.touch.transparency,          execSetupChooseTransparency     },
5819   { &setup.touch.transparency,          &transparency_text              },
5820
5821   { &setup.touch.grid_xsize[0],         execSetupChooseGridXSize_0      },
5822   { &setup.touch.grid_xsize[0],         &grid_size_text[0][0]           },
5823
5824   { &setup.touch.grid_ysize[0],         execSetupChooseGridYSize_0      },
5825   { &setup.touch.grid_ysize[0],         &grid_size_text[0][1]           },
5826
5827   { &setup.touch.grid_xsize[1],         execSetupChooseGridXSize_1      },
5828   { &setup.touch.grid_xsize[1],         &grid_size_text[1][0]           },
5829
5830   { &setup.touch.grid_ysize[1],         execSetupChooseGridYSize_1      },
5831   { &setup.touch.grid_ysize[1],         &grid_size_text[1][1]           },
5832
5833   { NULL,                               NULL                            }
5834 };
5835
5836 void setHideRelatedSetupEntries()
5837 {
5838   int i;
5839
5840   for (i = 0; hide_related_entry_list[i].value != NULL; i++)
5841     if (hideSetupEntry(hide_related_entry_list[i].value))
5842       setHideSetupEntry(hide_related_entry_list[i].related_value);
5843 }
5844
5845 static struct TokenInfo setup_info_main[] =
5846 {
5847   { TYPE_ENTER_MENU,    execSetupGame,          STR_SETUP_GAME          },
5848   { TYPE_ENTER_MENU,    execSetupEditor,        STR_SETUP_EDITOR        },
5849   { TYPE_ENTER_MENU,    execSetupGraphics,      STR_SETUP_GRAPHICS      },
5850   { TYPE_ENTER_MENU,    execSetupSound,         STR_SETUP_SOUND         },
5851   { TYPE_ENTER_MENU,    execSetupArtwork,       STR_SETUP_ARTWORK       },
5852   { TYPE_ENTER_MENU,    execSetupInput,         STR_SETUP_INPUT         },
5853   { TYPE_ENTER_MENU,    execSetupTouch,         STR_SETUP_TOUCH         },
5854   { TYPE_ENTER_MENU,    execSetupShortcuts,     STR_SETUP_SHORTCUTS     },
5855   { TYPE_EMPTY,         NULL,                   ""                      },
5856   { TYPE_LEAVE_MENU,    execExitSetup,          STR_SETUP_EXIT          },
5857   { TYPE_LEAVE_MENU,    execSaveAndExitSetup,   STR_SETUP_SAVE_AND_EXIT },
5858
5859   { 0,                  NULL,                   NULL                    }
5860 };
5861
5862 static struct TokenInfo setup_info_game[] =
5863 {
5864   { TYPE_SWITCH,        &setup.team_mode,       "Team-Mode (Multi-Player):" },
5865   { TYPE_YES_NO,        &setup.input_on_focus,  "Only Move Focussed Player:" },
5866   { TYPE_SWITCH,        &setup.time_limit,      "Time Limit:"           },
5867   { TYPE_SWITCH,        &setup.handicap,        "Handicap:"             },
5868   { TYPE_SWITCH,        &setup.skip_levels,     "Skip Unsolved Levels:" },
5869   { TYPE_SWITCH,        &setup.increment_levels,"Increment Solved Levels:" },
5870   { TYPE_SWITCH,        &setup.autorecord,      "Auto-Record Tapes:"    },
5871   { TYPE_ENTER_LIST,    execSetupChooseGameSpeed, "Game Speed:"         },
5872   { TYPE_STRING,        &game_speed_text,       ""                      },
5873 #if 1
5874   { TYPE_ENTER_LIST,    execSetupChooseScrollDelay, "Scroll Delay:"     },
5875   { TYPE_STRING,        &scroll_delay_text,     ""                      },
5876 #endif
5877   { TYPE_ENTER_LIST, execSetupChooseSnapshotMode,"Game Engine Snapshot Mode:" },
5878   { TYPE_STRING,        &snapshot_mode_text,    ""                      },
5879   { TYPE_SWITCH,        &setup.show_snapshot_buttons,"Show Snapshot Buttons:" },
5880   { TYPE_EMPTY,         NULL,                   ""                      },
5881   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
5882
5883   { 0,                  NULL,                   NULL                    }
5884 };
5885
5886 static struct TokenInfo setup_info_editor[] =
5887 {
5888 #if 0
5889   { TYPE_SWITCH,        &setup.editor.el_boulderdash,   "Boulder Dash:" },
5890   { TYPE_SWITCH,        &setup.editor.el_emerald_mine,  "Emerald Mine:" },
5891   { TYPE_SWITCH, &setup.editor.el_emerald_mine_club,    "Emerald Mine Club:" },
5892   { TYPE_SWITCH,        &setup.editor.el_more,          "Rocks'n'Diamonds:" },
5893   { TYPE_SWITCH,        &setup.editor.el_sokoban,       "Sokoban:"      },
5894   { TYPE_SWITCH,        &setup.editor.el_supaplex,      "Supaplex:"     },
5895   { TYPE_SWITCH,        &setup.editor.el_diamond_caves, "Diamond Caves II:" },
5896   { TYPE_SWITCH,        &setup.editor.el_dx_boulderdash,"DX-Boulderdash:" },
5897   { TYPE_SWITCH,        &setup.editor.el_chars,         "Text Characters:" },
5898   { TYPE_SWITCH, &setup.editor.el_steel_chars, "Text Characters (Steel):" },
5899 #endif
5900   { TYPE_SWITCH,        &setup.editor.el_classic,  "Classic Elements:" },
5901   { TYPE_SWITCH,        &setup.editor.el_custom,  "Custom & Group Elements:" },
5902 #if 0
5903   { TYPE_SWITCH,        &setup.editor.el_headlines,     "Headlines:"    },
5904 #endif
5905   { TYPE_SWITCH, &setup.editor.el_user_defined, "User defined element list:" },
5906   { TYPE_SWITCH,        &setup.editor.el_dynamic,  "Dynamic level elements:" },
5907   { TYPE_EMPTY,         NULL,                   ""                      },
5908 #if 0
5909   { TYPE_SWITCH,        &setup.editor.el_by_game,   "Show elements by game:" },
5910   { TYPE_SWITCH,        &setup.editor.el_by_type,   "Show elements by type:" },
5911   { TYPE_EMPTY,         NULL,                   ""                      },
5912 #endif
5913   { TYPE_SWITCH, &setup.editor.show_element_token,      "Show element token:" },
5914   { TYPE_EMPTY,         NULL,                   ""                      },
5915   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
5916
5917   { 0,                  NULL,                   NULL                    }
5918 };
5919
5920 static struct TokenInfo setup_info_graphics[] =
5921 {
5922 #if defined(TARGET_SDL2) && !defined(PLATFORM_ANDROID)
5923   { TYPE_SWITCH,        &setup.fullscreen,      "Fullscreen:"           },
5924   { TYPE_ENTER_LIST,    execSetupChooseWindowSize, "Window Scaling:"    },
5925   { TYPE_STRING,        &window_size_text,      ""                      },
5926   { TYPE_ENTER_LIST,    execSetupChooseScalingType, "Anti-Aliasing:"    },
5927   { TYPE_STRING,        &scaling_type_text,     ""                      },
5928   { TYPE_ENTER_LIST,    execSetupChooseRenderingMode, "Special Rendering:" },
5929   { TYPE_STRING,        &rendering_mode_text,   ""                      },
5930 #endif
5931 #if 0
5932   { TYPE_ENTER_LIST,    execSetupChooseScrollDelay, "Scroll Delay:"     },
5933   { TYPE_STRING,        &scroll_delay_text,     ""                      },
5934 #endif
5935   { TYPE_SWITCH,        &setup.fade_screens,    "Fade Screens:"         },
5936   { TYPE_SWITCH,        &setup.quick_switch,    "Quick Player Focus Switch:" },
5937   { TYPE_SWITCH,        &setup.quick_doors,     "Quick Menu Doors:"     },
5938   { TYPE_SWITCH,        &setup.show_titlescreen,"Show Title Screens:"   },
5939   { TYPE_SWITCH,        &setup.toons,           "Show Menu Animations:" },
5940   { TYPE_ECS_AGA,       &setup.prefer_aga_graphics,"EMC graphics preference:" },
5941   { TYPE_SWITCH, &setup.sp_show_border_elements,"Supaplex Border Elements:" },
5942   { TYPE_SWITCH,        &setup.small_game_graphics, "Small Game Graphics:" },
5943   { TYPE_EMPTY,         NULL,                   ""                      },
5944   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
5945
5946   { 0,                  NULL,                   NULL                    }
5947 };
5948
5949 static struct TokenInfo setup_info_sound[] =
5950 {
5951   { TYPE_SWITCH,        &setup.sound_simple,    "Sound Effects (Normal):"  },
5952   { TYPE_SWITCH,        &setup.sound_loops,     "Sound Effects (Looping):" },
5953   { TYPE_SWITCH,        &setup.sound_music,     "Music:"                },
5954   { TYPE_EMPTY,         NULL,                   ""                      },
5955   { TYPE_ENTER_LIST,    execSetupChooseVolumeSimple, "Sound Volume (Normal):" },
5956   { TYPE_STRING,        &volume_simple_text,    ""                      },
5957   { TYPE_ENTER_LIST,    execSetupChooseVolumeLoops, "Sound Volume (Looping):" },
5958   { TYPE_STRING,        &volume_loops_text,     ""                      },
5959   { TYPE_ENTER_LIST,    execSetupChooseVolumeMusic, "Music Volume:"     },
5960   { TYPE_STRING,        &volume_music_text,     ""                      },
5961   { TYPE_EMPTY,         NULL,                   ""                      },
5962   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
5963
5964   { 0,                  NULL,                   NULL                    }
5965 };
5966
5967 static struct TokenInfo setup_info_artwork[] =
5968 {
5969   { TYPE_ENTER_LIST,    execSetupChooseGraphics,"Custom Graphics:"      },
5970   { TYPE_STRING,        &graphics_set_name,     ""                      },
5971   { TYPE_ENTER_LIST,    execSetupChooseSounds,  "Custom Sounds:"        },
5972   { TYPE_STRING,        &sounds_set_name,       ""                      },
5973   { TYPE_ENTER_LIST,    execSetupChooseMusic,   "Custom Music:"         },
5974   { TYPE_STRING,        &music_set_name,        ""                      },
5975   { TYPE_EMPTY,         NULL,                   ""                      },
5976   { TYPE_YES_NO_AUTO,&setup.override_level_graphics,"Override Level Graphics:"},
5977   { TYPE_YES_NO_AUTO,&setup.override_level_sounds,  "Override Level Sounds:"  },
5978   { TYPE_YES_NO_AUTO,&setup.override_level_music,   "Override Level Music:"   },
5979   { TYPE_EMPTY,         NULL,                   ""                      },
5980   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
5981
5982   { 0,                  NULL,                   NULL                    }
5983 };
5984
5985 static struct TokenInfo setup_info_input[] =
5986 {
5987   { TYPE_SWITCH,        NULL,                   "Player:"               },
5988   { TYPE_SWITCH,        NULL,                   "Device:"               },
5989   { TYPE_SWITCH,        NULL,                   ""                      },
5990   { TYPE_SKIPPABLE,     NULL,                   ""                      },
5991   { TYPE_EMPTY,         NULL,                   ""                      },
5992   { TYPE_EMPTY,         NULL,                   ""                      },
5993   { TYPE_EMPTY,         NULL,                   ""                      },
5994   { TYPE_EMPTY,         NULL,                   ""                      },
5995   { TYPE_EMPTY,         NULL,                   ""                      },
5996   { TYPE_EMPTY,         NULL,                   ""                      },
5997   { TYPE_EMPTY,         NULL,                   ""                      },
5998   { TYPE_EMPTY,         NULL,                   ""                      },
5999   { TYPE_SKIPPABLE,     NULL,                   ""                      },
6000   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
6001
6002   { 0,                  NULL,                   NULL                    }
6003 };
6004
6005 static struct TokenInfo setup_info_touch[] =
6006 {
6007   { TYPE_ENTER_LIST,    execSetupChooseTouchControls, "Touch Control Type:" },
6008   { TYPE_STRING,        &touch_controls_text,   ""                      },
6009   { TYPE_EMPTY,         NULL,                   ""                      },
6010   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
6011
6012   { 0,                  NULL,                   NULL                    }
6013 };
6014
6015 static struct TokenInfo setup_info_touch_virtual_buttons[] =
6016 {
6017   { TYPE_ENTER_LIST,    execSetupChooseTouchControls, "Touch Control Type:" },
6018   { TYPE_STRING,        &touch_controls_text,   ""                      },
6019   { TYPE_EMPTY,         NULL,                   ""                      },
6020   { TYPE_ENTER_LIST,    execSetupChooseGridXSize_0, "Horizontal Buttons (Landscape):"   },
6021   { TYPE_STRING,        &grid_size_text[0][0],  ""                      },
6022   { TYPE_ENTER_LIST,    execSetupChooseGridYSize_0, "Vertical Buttons (Landscape):"     },
6023   { TYPE_STRING,        &grid_size_text[0][1],  ""                      },
6024   { TYPE_ENTER_LIST,    execSetupChooseTransparency, "Transparency:"    },
6025   { TYPE_STRING,        &transparency_text,     ""                      },
6026   { TYPE_EMPTY,         NULL,                   ""                      },
6027   { TYPE_ENTER_LIST,    execSetupConfigureVirtualButtons, "Configure Virtual Buttons" },
6028   { TYPE_EMPTY,         NULL,                   ""                      },
6029   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
6030
6031   { 0,                  NULL,                   NULL                    }
6032 };
6033
6034 static struct TokenInfo setup_info_touch_wipe_gestures[] =
6035 {
6036   { TYPE_ENTER_LIST,    execSetupChooseTouchControls, "Touch Control Type:" },
6037   { TYPE_STRING,        &touch_controls_text,   ""                      },
6038   { TYPE_EMPTY,         NULL,                   ""                      },
6039   { TYPE_ENTER_LIST,    execSetupChooseMoveDistance, "Move Trigger Distance:" },
6040   { TYPE_STRING,        &move_distance_text,    ""                      },
6041   { TYPE_ENTER_LIST,    execSetupChooseDropDistance, "Drop Trigger Distance:" },
6042   { TYPE_STRING,        &drop_distance_text,    ""                      },
6043   { TYPE_EMPTY,         NULL,                   ""                      },
6044   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
6045
6046   { 0,                  NULL,                   NULL                    }
6047 };
6048
6049 static struct TokenInfo setup_info_shortcuts[] =
6050 {
6051   { TYPE_ENTER_MENU,    execSetupShortcuts1,    "Various Keys"          },
6052   { TYPE_ENTER_MENU,    execSetupShortcuts2,    "Player Focus"          },
6053   { TYPE_ENTER_MENU,    execSetupShortcuts3,    "Tape Buttons"          },
6054   { TYPE_ENTER_MENU,    execSetupShortcuts4,    "Sound & Music"         },
6055   { TYPE_ENTER_MENU,    execSetupShortcuts5,    "TAS Snap Keys"         },
6056   { TYPE_EMPTY,         NULL,                   ""                      },
6057   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
6058
6059   { 0,                  NULL,                   NULL                    }
6060 };
6061
6062 static struct TokenInfo setup_info_shortcuts_1[] =
6063 {
6064   { TYPE_KEYTEXT,       NULL,           "Quick Save Game to Tape:",     },
6065   { TYPE_KEY,           &setup.shortcut.save_game, ""                   },
6066   { TYPE_KEYTEXT,       NULL,           "Quick Load Game from Tape:",   },
6067   { TYPE_KEY,           &setup.shortcut.load_game, ""                   },
6068   { TYPE_KEYTEXT,       NULL,           "Start Game & Toggle Pause:",   },
6069   { TYPE_KEY,           &setup.shortcut.toggle_pause, ""                },
6070   { TYPE_EMPTY,         NULL,                   ""                      },
6071   { TYPE_YES_NO,        &setup.ask_on_escape,   "Ask on 'Esc' Key:"     },
6072   { TYPE_YES_NO, &setup.ask_on_escape_editor,   "Ask on 'Esc' Key (Editor):" },
6073   { TYPE_EMPTY,         NULL,                   ""                      },
6074   { TYPE_LEAVE_MENU,    execSetupShortcuts,     "Back"                  },
6075
6076   { 0,                  NULL,                   NULL                    }
6077 };
6078
6079 static struct TokenInfo setup_info_shortcuts_2[] =
6080 {
6081   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 1:",       },
6082   { TYPE_KEY,           &setup.shortcut.focus_player[0], ""             },
6083   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 2:",       },
6084   { TYPE_KEY,           &setup.shortcut.focus_player[1], ""             },
6085   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 3:",       },
6086   { TYPE_KEY,           &setup.shortcut.focus_player[2], ""             },
6087   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 4:",       },
6088   { TYPE_KEY,           &setup.shortcut.focus_player[3], ""             },
6089   { TYPE_KEYTEXT,       NULL,           "Set Focus to All Players:",    },
6090   { TYPE_KEY,           &setup.shortcut.focus_player_all, ""            },
6091   { TYPE_EMPTY,         NULL,                   ""                      },
6092   { TYPE_LEAVE_MENU,    execSetupShortcuts,     "Back"                  },
6093
6094   { 0,                  NULL,                   NULL                    }
6095 };
6096
6097 static struct TokenInfo setup_info_shortcuts_3[] =
6098 {
6099   { TYPE_KEYTEXT,       NULL,                   "Eject Tape:",          },
6100   { TYPE_KEY,           &setup.shortcut.tape_eject, ""                  },
6101   { TYPE_KEYTEXT,       NULL,                   "Warp / Single Step:",  },
6102   { TYPE_KEY,           &setup.shortcut.tape_extra, ""                  },
6103   { TYPE_KEYTEXT,       NULL,                   "Stop Tape:",           },
6104   { TYPE_KEY,           &setup.shortcut.tape_stop, ""                   },
6105   { TYPE_KEYTEXT,       NULL,                   "Pause / Unpause Tape:",},
6106   { TYPE_KEY,           &setup.shortcut.tape_pause, ""                  },
6107   { TYPE_KEYTEXT,       NULL,                   "Record Tape:",         },
6108   { TYPE_KEY,           &setup.shortcut.tape_record, ""                 },
6109   { TYPE_KEYTEXT,       NULL,                   "Play Tape:",           },
6110   { TYPE_KEY,           &setup.shortcut.tape_play, ""                   },
6111   { TYPE_EMPTY,         NULL,                   ""                      },
6112   { TYPE_LEAVE_MENU,    execSetupShortcuts,     "Back"                  },
6113
6114   { 0,                  NULL,                   NULL                    }
6115 };
6116
6117 static struct TokenInfo setup_info_shortcuts_4[] =
6118 {
6119   { TYPE_KEYTEXT,       NULL,           "Toggle Sound Effects (Normal):", },
6120   { TYPE_KEY,           &setup.shortcut.sound_simple, ""                },
6121   { TYPE_KEYTEXT,       NULL,           "Toggle Sound Effects (Looping):", },
6122   { TYPE_KEY,           &setup.shortcut.sound_loops, ""                 },
6123   { TYPE_KEYTEXT,       NULL,           "Toggle Music:",                },
6124   { TYPE_KEY,           &setup.shortcut.sound_music, ""                 },
6125   { TYPE_EMPTY,         NULL,                   ""                      },
6126   { TYPE_LEAVE_MENU,    execSetupShortcuts,     "Back"                  },
6127
6128   { 0,                  NULL,                   NULL                    }
6129 };
6130
6131 static struct TokenInfo setup_info_shortcuts_5[] =
6132 {
6133   { TYPE_KEYTEXT,       NULL,                   "Snap Left:",           },
6134   { TYPE_KEY,           &setup.shortcut.snap_left, ""                   },
6135   { TYPE_KEYTEXT,       NULL,                   "Snap Right:",          },
6136   { TYPE_KEY,           &setup.shortcut.snap_right, ""                  },
6137   { TYPE_KEYTEXT,       NULL,                   "Snap Up:",             },
6138   { TYPE_KEY,           &setup.shortcut.snap_up, ""                     },
6139   { TYPE_KEYTEXT,       NULL,                   "Snap Down:",           },
6140   { TYPE_KEY,           &setup.shortcut.snap_down, ""                   },
6141   { TYPE_EMPTY,         NULL,                   ""                      },
6142   { TYPE_LEAVE_MENU,    execSetupShortcuts,     "Back"                  },
6143
6144   { 0,                  NULL,                   NULL                    }
6145 };
6146
6147 static Key getSetupKey()
6148 {
6149   Key key = KSYM_UNDEFINED;
6150   boolean got_key_event = FALSE;
6151
6152   while (!got_key_event)
6153   {
6154     Event event;
6155
6156     if (NextValidEvent(&event))
6157     {
6158       switch (event.type)
6159       {
6160         case EVENT_KEYPRESS:
6161           {
6162             key = GetEventKey((KeyEvent *)&event, TRUE);
6163
6164             /* press 'Escape' or 'Enter' to keep the existing key binding */
6165             if (key == KSYM_Escape || key == KSYM_Return)
6166               key = KSYM_UNDEFINED;     /* keep old value */
6167
6168             got_key_event = TRUE;
6169           }
6170           break;
6171
6172         case EVENT_KEYRELEASE:
6173           key_joystick_mapping = 0;
6174           break;
6175
6176         default:
6177           HandleOtherEvents(&event);
6178           break;
6179       }
6180     }
6181
6182     BackToFront();
6183   }
6184
6185   return key;
6186 }
6187
6188 static int getSetupValueFont(int type, void *value)
6189 {
6190   if (type & TYPE_GHOSTED)
6191     return FONT_OPTION_OFF;
6192   else if (type & TYPE_KEY)
6193     return (type & TYPE_QUERY ? FONT_INPUT_1_ACTIVE : FONT_VALUE_1);
6194   else if (type & TYPE_STRING)
6195     return FONT_VALUE_2;
6196   else if (type & TYPE_ECS_AGA)
6197     return FONT_VALUE_1;
6198   else if (type & TYPE_BOOLEAN_STYLE)
6199     return (*(boolean *)value ? FONT_OPTION_ON : FONT_OPTION_OFF);
6200   else if (type & TYPE_YES_NO_AUTO)
6201     return (*(int *)value == AUTO  ? FONT_OPTION_ON :
6202             *(int *)value == FALSE ? FONT_OPTION_OFF : FONT_OPTION_ON);
6203   else
6204     return FONT_VALUE_1;
6205 }
6206
6207 static int getSetupValueFontNarrow(int type, int font_nr)
6208 {
6209   return (font_nr == FONT_VALUE_1    ? FONT_VALUE_NARROW :
6210           font_nr == FONT_OPTION_ON  ? FONT_OPTION_ON_NARROW :
6211           font_nr == FONT_OPTION_OFF ? FONT_OPTION_OFF_NARROW :
6212           font_nr);
6213 }
6214
6215 static void drawSetupValue(int screen_pos, int setup_info_pos_raw)
6216 {
6217   int si_pos = (setup_info_pos_raw < 0 ? screen_pos : setup_info_pos_raw);
6218   struct TokenInfo *si = &setup_info[si_pos];
6219   boolean font_draw_xoffset_modified = FALSE;
6220   boolean scrollbar_needed = (num_setup_info < max_setup_info);
6221   int font_draw_xoffset_old = -1;
6222   int xoffset = (scrollbar_needed ? -1 : 0);
6223   int menu_screen_value_xpos = MENU_SCREEN_VALUE_XPOS + xoffset;
6224   int menu_screen_max_xpos = MENU_SCREEN_MAX_XPOS + xoffset;
6225   int xpos = menu_screen_value_xpos;
6226   int ypos = MENU_SCREEN_START_YPOS + screen_pos;
6227   int startx = mSX + xpos * 32;
6228   int starty = mSY + ypos * 32;
6229   int font_nr, font_nr_default, font_width_default;
6230   int type = si->type;
6231   void *value = si->value;
6232   char *value_string = getSetupValue(type, value);
6233   int i;
6234
6235   if (value_string == NULL)
6236     return;
6237
6238   if (type & TYPE_KEY)
6239   {
6240     xpos = MENU_SCREEN_START_XPOS;
6241
6242     if (type & TYPE_QUERY)
6243       value_string = "<press key>";
6244   }
6245   else if (type & TYPE_STRING)
6246   {
6247     int max_value_len = (SCR_FIELDX - 2) * 2;
6248
6249     xpos = MENU_SCREEN_START_XPOS;
6250
6251     if (strlen(value_string) > max_value_len)
6252       value_string[max_value_len] = '\0';
6253   }
6254   else if (type & TYPE_YES_NO_AUTO)
6255   {
6256     xpos = menu_screen_value_xpos - 1;
6257   }
6258
6259   startx = mSX + xpos * 32;
6260   starty = mSY + ypos * 32;
6261   font_nr_default = getSetupValueFont(type, value);
6262   font_width_default = getFontWidth(font_nr_default);
6263
6264   font_nr = font_nr_default;
6265
6266   // special check if right-side setup values moved left due to scrollbar
6267   if (scrollbar_needed && xpos > MENU_SCREEN_START_XPOS)
6268   {
6269     int max_menu_text_length = 26;      // maximum text length for classic menu
6270     int font_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
6271     int text_startx = mSX + MENU_SCREEN_START_XPOS * 32;
6272     int text_font_nr = getMenuTextFont(FONT_MENU_2);
6273     int text_font_xoffset = getFontBitmapInfo(text_font_nr)->draw_xoffset;
6274     int text_width = max_menu_text_length * getFontWidth(text_font_nr);
6275
6276     if (startx + font_xoffset < text_startx + text_width + text_font_xoffset)
6277     {
6278       xpos += 1;
6279       startx = mSX + xpos * 32;
6280
6281       font_nr = getSetupValueFontNarrow(type, font_nr);
6282     }
6283   }
6284
6285   /* downward compatibility correction for Juergen Bonhagen's menu settings */
6286   if (setup_mode != SETUP_MODE_INPUT)
6287   {
6288     int max_menu_text_length_big = (menu_screen_value_xpos -
6289                                     MENU_SCREEN_START_XPOS);
6290     int max_menu_text_length_medium = max_menu_text_length_big * 2;
6291     int check_font_nr = FONT_OPTION_ON; /* known font that needs correction */
6292     int font1_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
6293     int font2_xoffset = getFontBitmapInfo(check_font_nr)->draw_xoffset;
6294     int text_startx = mSX + MENU_SCREEN_START_XPOS * 32;
6295     int text_font_nr = getMenuTextFont(FONT_MENU_2);
6296     int text_font_xoffset = getFontBitmapInfo(text_font_nr)->draw_xoffset;
6297     int text_width = max_menu_text_length_medium * getFontWidth(text_font_nr);
6298     boolean correct_font_draw_xoffset = FALSE;
6299
6300     if (xpos == MENU_SCREEN_START_XPOS &&
6301         startx + font1_xoffset < text_startx + text_font_xoffset)
6302       correct_font_draw_xoffset = TRUE;
6303
6304     if (xpos == menu_screen_value_xpos &&
6305         startx + font2_xoffset < text_startx + text_width + text_font_xoffset)
6306       correct_font_draw_xoffset = TRUE;
6307
6308     /* check if setup value would overlap with setup text when printed */
6309     /* (this can happen for extreme/wrong values for font draw offset) */
6310     if (correct_font_draw_xoffset)
6311     {
6312       font_draw_xoffset_old = getFontBitmapInfo(font_nr)->draw_xoffset;
6313       font_draw_xoffset_modified = TRUE;
6314
6315       if (type & TYPE_KEY)
6316         getFontBitmapInfo(font_nr)->draw_xoffset += 2 * getFontWidth(font_nr);
6317       else if (!(type & TYPE_STRING))
6318         getFontBitmapInfo(font_nr)->draw_xoffset = text_font_xoffset + 20 -
6319           max_menu_text_length_medium * (16 - getFontWidth(text_font_nr));
6320     }
6321   }
6322
6323   for (i = 0; i <= menu_screen_max_xpos - xpos; i++)
6324     DrawText(startx + i * font_width_default, starty, " ", font_nr_default);
6325
6326   DrawText(startx, starty, value_string, font_nr);
6327
6328   if (font_draw_xoffset_modified)
6329     getFontBitmapInfo(font_nr)->draw_xoffset = font_draw_xoffset_old;
6330 }
6331
6332 static void changeSetupValue(int screen_pos, int setup_info_pos_raw, int dx)
6333 {
6334   int si_pos = (setup_info_pos_raw < 0 ? screen_pos : setup_info_pos_raw);
6335   struct TokenInfo *si = &setup_info[si_pos];
6336
6337   if (si->type & TYPE_BOOLEAN_STYLE)
6338   {
6339     *(boolean *)si->value ^= TRUE;
6340   }
6341   else if (si->type & TYPE_YES_NO_AUTO)
6342   {
6343     *(int *)si->value =
6344       (dx == -1 ?
6345        (*(int *)si->value == AUTO ? TRUE :
6346         *(int *)si->value == TRUE ? FALSE : AUTO) :
6347        (*(int *)si->value == TRUE ? AUTO :
6348         *(int *)si->value == AUTO ? FALSE : TRUE));
6349   }
6350   else if (si->type & TYPE_KEY)
6351   {
6352     Key key;
6353
6354     si->type |= TYPE_QUERY;
6355     drawSetupValue(screen_pos, setup_info_pos_raw);
6356     si->type &= ~TYPE_QUERY;
6357
6358     key = getSetupKey();
6359     if (key != KSYM_UNDEFINED)
6360       *(Key *)si->value = key;
6361   }
6362
6363   drawSetupValue(screen_pos, setup_info_pos_raw);
6364
6365   // fullscreen state may have changed at this point
6366   if (si->value == &setup.fullscreen)
6367     ToggleFullscreenOrChangeWindowScalingIfNeeded();
6368 }
6369
6370 static struct TokenInfo *getSetupInfoFinal(struct TokenInfo *setup_info_orig)
6371 {
6372   static struct TokenInfo *setup_info_final = NULL;
6373   int list_size = 0;
6374   int list_pos = 0;
6375   int i;
6376
6377   /* determine maximum list size of target list */
6378   while (setup_info_orig[list_size++].type != 0);
6379
6380   /* free, allocate and clear memory for target list */
6381   checked_free(setup_info_final);
6382   setup_info_final = checked_calloc(list_size * sizeof(struct TokenInfo));
6383
6384   /* copy setup info list without setup entries marked as hidden */
6385   for (i = 0; setup_info_orig[i].type != 0; i++)
6386   {
6387     /* skip setup entries configured to be hidden */
6388     if (hideSetupEntry(setup_info_orig[i].value))
6389       continue;
6390
6391     /* skip skippable setup entries if screen is lower than usual */
6392     if (SCR_FIELDY < SCR_FIELDY_DEFAULT &&
6393         setup_info_orig[i].type == TYPE_SKIPPABLE)
6394       continue;
6395
6396     setup_info_final[list_pos++] = setup_info_orig[i];
6397   }
6398
6399   return setup_info_final;
6400 }
6401
6402 static void DrawSetupScreen_Generic()
6403 {
6404   int fade_mask = REDRAW_FIELD;
6405   boolean redraw_all = FALSE;
6406   char *title_string = NULL;
6407   int i;
6408
6409   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
6410     fade_mask = REDRAW_ALL;
6411
6412   UnmapAllGadgets();
6413   FadeMenuSoundsAndMusic();
6414
6415   FreeScreenGadgets();
6416   CreateScreenGadgets();
6417
6418   if (redraw_mask & REDRAW_ALL)
6419     redraw_all = TRUE;
6420
6421   FadeOut(fade_mask);
6422
6423   /* needed if different viewport properties defined for setup screen */
6424   ChangeViewportPropertiesIfNeeded();
6425
6426   SetMainBackgroundImage(IMG_BACKGROUND_SETUP);
6427
6428   ClearField();
6429
6430   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
6431
6432   if (setup_mode == SETUP_MODE_MAIN)
6433   {
6434     setup_info = setup_info_main;
6435     title_string = STR_SETUP_MAIN;
6436   }
6437   else if (setup_mode == SETUP_MODE_GAME)
6438   {
6439     setup_info = setup_info_game;
6440     title_string = STR_SETUP_GAME;
6441   }
6442   else if (setup_mode == SETUP_MODE_EDITOR)
6443   {
6444     setup_info = setup_info_editor;
6445     title_string = STR_SETUP_EDITOR;
6446   }
6447   else if (setup_mode == SETUP_MODE_GRAPHICS)
6448   {
6449     setup_info = setup_info_graphics;
6450     title_string = STR_SETUP_GRAPHICS;
6451   }
6452   else if (setup_mode == SETUP_MODE_SOUND)
6453   {
6454     setup_info = setup_info_sound;
6455     title_string = STR_SETUP_SOUND;
6456   }
6457   else if (setup_mode == SETUP_MODE_ARTWORK)
6458   {
6459     setup_info = setup_info_artwork;
6460     title_string = STR_SETUP_ARTWORK;
6461   }
6462   else if (setup_mode == SETUP_MODE_TOUCH)
6463   {
6464     setup_info = setup_info_touch;
6465     title_string = STR_SETUP_TOUCH;
6466
6467     if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
6468       setup_info = setup_info_touch_virtual_buttons;
6469     else if (strEqual(setup.touch.control_type, TOUCH_CONTROL_WIPE_GESTURES))
6470       setup_info = setup_info_touch_wipe_gestures;
6471   }
6472   else if (setup_mode == SETUP_MODE_SHORTCUTS)
6473   {
6474     setup_info = setup_info_shortcuts;
6475     title_string = STR_SETUP_SHORTCUTS;
6476   }
6477   else if (setup_mode == SETUP_MODE_SHORTCUTS_1)
6478   {
6479     setup_info = setup_info_shortcuts_1;
6480     title_string = STR_SETUP_SHORTCUTS;
6481   }
6482   else if (setup_mode == SETUP_MODE_SHORTCUTS_2)
6483   {
6484     setup_info = setup_info_shortcuts_2;
6485     title_string = STR_SETUP_SHORTCUTS;
6486   }
6487   else if (setup_mode == SETUP_MODE_SHORTCUTS_3)
6488   {
6489     setup_info = setup_info_shortcuts_3;
6490     title_string = STR_SETUP_SHORTCUTS;
6491   }
6492   else if (setup_mode == SETUP_MODE_SHORTCUTS_4)
6493   {
6494     setup_info = setup_info_shortcuts_4;
6495     title_string = STR_SETUP_SHORTCUTS;
6496   }
6497   else if (setup_mode == SETUP_MODE_SHORTCUTS_5)
6498   {
6499     setup_info = setup_info_shortcuts_5;
6500     title_string = STR_SETUP_SHORTCUTS;
6501   }
6502
6503   /* use modified setup info without setup entries marked as hidden */
6504   setup_info = getSetupInfoFinal(setup_info);
6505
6506   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, title_string);
6507
6508   // determine maximal number of setup entries that can be displayed on screen
6509   num_setup_info = 0;
6510   for (i = 0; setup_info[i].type != 0 && i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
6511     num_setup_info++;
6512
6513   // determine maximal number of setup entries available for this setup screen
6514   max_setup_info = 0;
6515   for (i = 0; setup_info[i].type != 0; i++)
6516     max_setup_info++;
6517
6518   HandleSetupScreen_Generic(0, 0, 0, 0, MB_MENU_INITIALIZE);
6519
6520   MapScreenGadgets(max_setup_info);
6521
6522   if (redraw_all)
6523     redraw_mask = fade_mask = REDRAW_ALL;
6524
6525   DrawMaskedBorder(fade_mask);
6526
6527   FadeIn(fade_mask);
6528 }
6529
6530 void HandleSetupScreen_Generic(int mx, int my, int dx, int dy, int button)
6531 {
6532   menu_info = setup_info;
6533
6534   HandleMenuScreen(mx, my, dx, dy, button,
6535                    setup_mode, num_setup_info, max_setup_info);
6536 }
6537
6538 void DrawSetupScreen_Input()
6539 {
6540   int i;
6541
6542   FadeOut(REDRAW_FIELD);
6543
6544   ClearField();
6545
6546   setup_info = getSetupInfoFinal(setup_info_input);
6547
6548   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, STR_SETUP_INPUT);
6549
6550   for (i = 0; setup_info[i].type != 0; i++)
6551   {
6552     if (setup_info[i].type & (TYPE_ENTER_MENU|TYPE_ENTER_LIST))
6553       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
6554     else if (setup_info[i].type & (TYPE_LEAVE_MENU|TYPE_LEAVE_LIST))
6555       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
6556     else if (setup_info[i].type & ~TYPE_SKIP_ENTRY)
6557       initCursor(i, IMG_MENU_BUTTON);
6558
6559     DrawCursorAndText_Setup(i, -1, FALSE);
6560   }
6561
6562   /* create gadgets for setup input menu screen */
6563   FreeScreenGadgets();
6564   CreateScreenGadgets();
6565
6566   /* map gadgets for setup input menu screen */
6567   MapScreenMenuGadgets(SCREEN_MASK_INPUT);
6568
6569   HandleSetupScreen_Input(0, 0, 0, 0, MB_MENU_INITIALIZE);
6570
6571   FadeIn(REDRAW_FIELD);
6572 }
6573
6574 static void setJoystickDeviceToNr(char *device_name, int device_nr)
6575 {
6576   if (device_name == NULL)
6577     return;
6578
6579   if (device_nr < 0 || device_nr >= MAX_PLAYERS)
6580     device_nr = 0;
6581
6582   if (strlen(device_name) > 1)
6583   {
6584     char c1 = device_name[strlen(device_name) - 1];
6585     char c2 = device_name[strlen(device_name) - 2];
6586
6587     if (c1 >= '0' && c1 <= '9' && !(c2 >= '0' && c2 <= '9'))
6588       device_name[strlen(device_name) - 1] = '0' + (char)(device_nr % 10);
6589   }
6590   else
6591     strncpy(device_name, getDeviceNameFromJoystickNr(device_nr),
6592             strlen(device_name));
6593 }
6594
6595 static void drawPlayerSetupInputInfo(int player_nr, boolean active)
6596 {
6597   int i;
6598   static struct SetupKeyboardInfo custom_key;
6599   static struct
6600   {
6601     Key *key;
6602     char *text;
6603   } custom[] =
6604   {
6605     { &custom_key.left,  "Axis/Pad Left"  },
6606     { &custom_key.right, "Axis/Pad Right" },
6607     { &custom_key.up,    "Axis/Pad Up"    },
6608     { &custom_key.down,  "Axis/Pad Down"  },
6609     { &custom_key.snap,  "Button 1/A/X"   },
6610     { &custom_key.drop,  "Button 2/B/Y"   }
6611   };
6612   static char *joystick_name[MAX_PLAYERS] =
6613   {
6614     "Joystick1",
6615     "Joystick2",
6616     "Joystick3",
6617     "Joystick4"
6618   };
6619   int font_nr_menu = (active ? FONT_MENU_1_ACTIVE : FONT_MENU_1);
6620   int font_nr_info = FONT_MENU_1;
6621   int font_nr_name = FONT_VALUE_OLD;
6622   int font_nr_on   = FONT_VALUE_1;
6623   int font_nr_off  = FONT_VALUE_OLD;
6624   int pos = 4;
6625
6626   if (SCR_FIELDX < SCR_FIELDX_DEFAULT)
6627   {
6628     font_nr_info = FONT_MENU_2;
6629     font_nr_on   = FONT_VALUE_NARROW;
6630     font_nr_off  = FONT_VALUE_OLD_NARROW;
6631   }
6632
6633   custom_key = setup.input[player_nr].key;
6634
6635   DrawText(mSX + 11 * 32, mSY + 2 * 32, int2str(player_nr + 1, 1),
6636            FONT_INPUT_1_ACTIVE);
6637
6638   ClearRectangleOnBackground(drawto, mSX + 8 * TILEX, mSY + 2 * TILEY,
6639                              TILEX, TILEY);
6640   DrawFixedGraphicThruMaskExt(drawto, mSX + 8 * TILEX, mSY + 2 * TILEY,
6641                               PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0);
6642
6643   if (setup.input[player_nr].use_joystick)
6644   {
6645     char *device_name = setup.input[player_nr].joy.device_name;
6646     int joystick_nr = getJoystickNrFromDeviceName(device_name);
6647     boolean joystick_active = CheckJoystickOpened(joystick_nr);
6648     char *text = joystick_name[joystick_nr];
6649     int font_nr = (joystick_active ? font_nr_on : font_nr_off);
6650
6651     DrawText(mSX + 8 * 32, mSY + 3 * 32, text, font_nr);
6652     DrawText(mSX + 32, mSY + 4 * 32, "Configure", font_nr_menu);
6653   }
6654   else
6655   {
6656     DrawText(mSX + 8 * 32, mSY + 3 * 32, "Keyboard ", font_nr_on);
6657     DrawText(mSX + 1 * 32, mSY + 4 * 32, "Customize", font_nr_menu);
6658   }
6659
6660   if (SCR_FIELDY >= SCR_FIELDY_DEFAULT)
6661     DrawText(mSX + 32, mSY + 5 * 32, "Actual Settings:", font_nr_info);
6662   else
6663     pos = 3;
6664
6665   drawCursorXY(1, pos + 0, IMG_MENU_BUTTON_LEFT);
6666   drawCursorXY(1, pos + 1, IMG_MENU_BUTTON_RIGHT);
6667   drawCursorXY(1, pos + 2, IMG_MENU_BUTTON_UP);
6668   drawCursorXY(1, pos + 3, IMG_MENU_BUTTON_DOWN);
6669
6670   DrawText(mSX + 2 * 32, mSY + (pos + 2) * 32, ":", font_nr_name);
6671   DrawText(mSX + 2 * 32, mSY + (pos + 3) * 32, ":", font_nr_name);
6672   DrawText(mSX + 2 * 32, mSY + (pos + 4) * 32, ":", font_nr_name);
6673   DrawText(mSX + 2 * 32, mSY + (pos + 5) * 32, ":", font_nr_name);
6674   DrawText(mSX + 1 * 32, mSY + (pos + 6) * 32, "Snap Field:", font_nr_name);
6675   DrawText(mSX + 1 * 32, mSY + (pos + 8) * 32, "Drop Element:", font_nr_name);
6676
6677   for (i = 0; i < 6; i++)
6678   {
6679     int ypos = (pos + 2) + i + (i > 3 ? i - 3 : 0);
6680
6681     DrawText(mSX + 3 * 32, mSY + ypos * 32,
6682              "              ", font_nr_on);
6683     DrawText(mSX + 3 * 32, mSY + ypos * 32,
6684              (setup.input[player_nr].use_joystick ?
6685               custom[i].text :
6686               getKeyNameFromKey(*custom[i].key)), font_nr_on);
6687   }
6688 }
6689
6690 static int input_player_nr = 0;
6691
6692 void HandleSetupScreen_Input_Player(int step, int direction)
6693 {
6694   int old_player_nr = input_player_nr;
6695   int new_player_nr;
6696
6697   new_player_nr = old_player_nr + step * direction;
6698   if (new_player_nr < 0)
6699     new_player_nr = 0;
6700   if (new_player_nr > MAX_PLAYERS - 1)
6701     new_player_nr = MAX_PLAYERS - 1;
6702
6703   if (new_player_nr != old_player_nr)
6704   {
6705     input_player_nr = new_player_nr;
6706
6707     drawPlayerSetupInputInfo(input_player_nr, FALSE);
6708   }
6709 }
6710
6711 void HandleSetupScreen_Input(int mx, int my, int dx, int dy, int button)
6712 {
6713   static int choice = 0;
6714   int x = 0;
6715   int y = choice;
6716   int pos_start  = SETUPINPUT_SCREEN_POS_START;
6717   int pos_empty1 = SETUPINPUT_SCREEN_POS_EMPTY1;
6718   int pos_empty2 = SETUPINPUT_SCREEN_POS_EMPTY2;
6719   int pos_end    = SETUPINPUT_SCREEN_POS_END;
6720
6721   if (SCR_FIELDY < SCR_FIELDY_DEFAULT)
6722   {
6723     int i;
6724
6725     for (i = 0; setup_info_input[i].type != 0; i++)
6726     {
6727       /* adjust menu structure according to skipped setup entries */
6728       if (setup_info_input[i].type == TYPE_SKIPPABLE)
6729       {
6730         pos_empty2--;
6731         pos_end--;
6732       }
6733     }
6734   }
6735
6736   if (button == MB_MENU_INITIALIZE)
6737   {
6738     /* input setup menu may have changed size due to graphics configuration */
6739     if (choice >= pos_empty1)
6740       choice = pos_end;
6741
6742     drawPlayerSetupInputInfo(input_player_nr, (choice == 2));
6743
6744     DrawCursorAndText_Setup(choice, -1, TRUE);
6745
6746     return;
6747   }
6748   else if (button == MB_MENU_LEAVE)
6749   {
6750     setup_mode = SETUP_MODE_MAIN;
6751     DrawSetupScreen();
6752     InitJoysticks();
6753
6754     return;
6755   }
6756
6757   if (mx || my)         /* mouse input */
6758   {
6759     x = (mx - mSX) / 32;
6760     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
6761   }
6762   else if (dx || dy)    /* keyboard input */
6763   {
6764     if (dx && choice == 0)
6765       x = (dx < 0 ? 10 : 12);
6766     else if ((dx && choice == 1) ||
6767              (dx == -1 && choice == pos_end))
6768       button = MB_MENU_CHOICE;
6769     else if (dy)
6770       y = choice + dy;
6771
6772     if (y >= pos_empty1 && y <= pos_empty2)
6773       y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1);
6774   }
6775
6776   if (y == 0 && dx != 0 && button)
6777   {
6778     HandleSetupScreen_Input_Player(1, dx < 0 ? -1 : +1);
6779   }
6780   else if (IN_VIS_FIELD(x, y) &&        // (does not use "IN_VIS_MENU()" yet)
6781            y >= pos_start && y <= pos_end &&
6782            !(y >= pos_empty1 && y <= pos_empty2))
6783   {
6784     if (button)
6785     {
6786       if (y != choice)
6787       {
6788         DrawCursorAndText_Setup(choice, -1, FALSE);
6789         DrawCursorAndText_Setup(y, -1, TRUE);
6790
6791         drawPlayerSetupInputInfo(input_player_nr, (y == 2));
6792
6793         choice = y;
6794       }
6795     }
6796     else
6797     {
6798       if (y == 1)
6799       {
6800         char *device_name = setup.input[input_player_nr].joy.device_name;
6801
6802         if (!setup.input[input_player_nr].use_joystick)
6803         {
6804           int new_device_nr = (dx >= 0 ? 0 : MAX_PLAYERS - 1);
6805
6806           setJoystickDeviceToNr(device_name, new_device_nr);
6807           setup.input[input_player_nr].use_joystick = TRUE;
6808         }
6809         else
6810         {
6811           int device_nr = getJoystickNrFromDeviceName(device_name);
6812           int new_device_nr = device_nr + (dx >= 0 ? +1 : -1);
6813
6814           if (new_device_nr < 0 || new_device_nr >= MAX_PLAYERS)
6815             setup.input[input_player_nr].use_joystick = FALSE;
6816           else
6817             setJoystickDeviceToNr(device_name, new_device_nr);
6818         }
6819
6820         drawPlayerSetupInputInfo(input_player_nr, FALSE);
6821       }
6822       else if (y == 2)
6823       {
6824         if (setup.input[input_player_nr].use_joystick)
6825           ConfigureJoystick(input_player_nr);
6826         else
6827           CustomizeKeyboard(input_player_nr);
6828       }
6829       else if (y == pos_end)
6830       {
6831         InitJoysticks();
6832
6833         FadeSetLeaveMenu();
6834
6835         setup_mode = SETUP_MODE_MAIN;
6836         DrawSetupScreen();
6837       }
6838     }
6839   }
6840 }
6841
6842 static boolean CustomizeKeyboardMain(int player_nr)
6843 {
6844   int i;
6845   int step_nr;
6846   boolean finished = FALSE;
6847   static struct SetupKeyboardInfo custom_key;
6848   static struct
6849   {
6850     Key *key;
6851     char *text;
6852   } customize_step[] =
6853   {
6854     { &custom_key.left,  "Move Left"    },
6855     { &custom_key.right, "Move Right"   },
6856     { &custom_key.up,    "Move Up"      },
6857     { &custom_key.down,  "Move Down"    },
6858     { &custom_key.snap,  "Snap Field"   },
6859     { &custom_key.drop,  "Drop Element" }
6860   };
6861   int font_nr_old = FONT_VALUE_OLD;
6862   int font_nr_new = FONT_VALUE_1;
6863   boolean success = FALSE;
6864
6865   if (SCR_FIELDX < SCR_FIELDX_DEFAULT)
6866   {
6867     font_nr_old = FONT_VALUE_OLD_NARROW;
6868     font_nr_new = FONT_VALUE_NARROW;
6869   }
6870
6871   /* read existing key bindings from player setup */
6872   custom_key = setup.input[player_nr].key;
6873
6874   FadeSetEnterMenu();
6875   FadeOut(REDRAW_FIELD);
6876
6877   ClearField();
6878
6879   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Keyboard Input");
6880
6881   step_nr = 0;
6882   DrawText(mSX, mSY + (2 + 2 * step_nr) * 32,
6883            customize_step[step_nr].text, FONT_INPUT_1_ACTIVE);
6884   DrawText(mSX, mSY + (2 + 2 * step_nr + 1) * 32,
6885            "Key:", FONT_INPUT_1_ACTIVE);
6886   DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
6887            getKeyNameFromKey(*customize_step[step_nr].key), font_nr_old);
6888
6889   FadeIn(REDRAW_FIELD);
6890
6891   while (!finished)
6892   {
6893     Event event;
6894
6895     if (NextValidEvent(&event))
6896     {
6897       switch (event.type)
6898       {
6899         case EVENT_KEYPRESS:
6900           {
6901             Key key = GetEventKey((KeyEvent *)&event, FALSE);
6902
6903             /* press 'Escape' to abort and keep the old key bindings */
6904             if (key == KSYM_Escape)
6905             {
6906               FadeSkipNextFadeIn();
6907
6908               finished = TRUE;
6909
6910               break;
6911             }
6912
6913             /* press 'Enter' to keep the existing key binding */
6914             if (key == KSYM_Return)
6915               key = *customize_step[step_nr].key;
6916
6917             /* check if key already used */
6918             for (i = 0; i < step_nr; i++)
6919               if (*customize_step[i].key == key)
6920                 break;
6921             if (i < step_nr)
6922               break;
6923
6924             /* got new key binding */
6925             *customize_step[step_nr].key = key;
6926             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
6927                      "             ", font_nr_new);
6928             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
6929                      getKeyNameFromKey(key), font_nr_new);
6930             step_nr++;
6931
6932             /* un-highlight last query */
6933             DrawText(mSX, mSY + (2 + 2 * (step_nr - 1)) * 32,
6934                      customize_step[step_nr - 1].text, FONT_MENU_1);
6935             DrawText(mSX, mSY + (2 + 2 * (step_nr - 1) + 1) * 32,
6936                      "Key:", FONT_MENU_1);
6937
6938             /* all keys configured */
6939             if (step_nr == 6)
6940             {
6941               finished = TRUE;
6942               success = TRUE;
6943
6944               break;
6945             }
6946
6947             /* query next key binding */
6948             DrawText(mSX, mSY + (2 + 2 * step_nr) * 32,
6949                      customize_step[step_nr].text, FONT_INPUT_1_ACTIVE);
6950             DrawText(mSX, mSY + (2 + 2 * step_nr + 1) * 32,
6951                      "Key:", FONT_INPUT_1_ACTIVE);
6952             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
6953                      getKeyNameFromKey(*customize_step[step_nr].key),
6954                      font_nr_old);
6955           }
6956           break;
6957
6958         case EVENT_KEYRELEASE:
6959           key_joystick_mapping = 0;
6960           break;
6961
6962         default:
6963           HandleOtherEvents(&event);
6964           break;
6965       }
6966     }
6967
6968     BackToFront();
6969   }
6970
6971   /* write new key bindings back to player setup, if successfully finished */
6972   if (success)
6973     setup.input[player_nr].key = custom_key;
6974
6975   return success;
6976 }
6977
6978 void CustomizeKeyboard(int player_nr)
6979 {
6980   boolean success = CustomizeKeyboardMain(player_nr);
6981
6982   if (success)
6983   {
6984     int font_nr = FONT_TITLE_1;
6985     int font_height = getFontHeight(font_nr);
6986     int ypos1 = SYSIZE / 2 - font_height * 2;
6987     int ypos2 = SYSIZE / 2 - font_height * 1;
6988     unsigned int wait_frame_delay = 0;
6989     unsigned int wait_frame_delay_value = 2000;
6990
6991     ResetDelayCounter(&wait_frame_delay);
6992
6993     ClearField();
6994
6995     DrawTextSCentered(ypos1, font_nr, "Keyboard");
6996     DrawTextSCentered(ypos2, font_nr, "configured!");
6997
6998     while (!DelayReached(&wait_frame_delay, wait_frame_delay_value))
6999       BackToFront();
7000
7001     ClearEventQueue();
7002   }
7003
7004   DrawSetupScreen_Input();
7005 }
7006
7007 /* game controller mapping generator by Gabriel Jacobo <gabomdq@gmail.com> */
7008
7009 #define MARKER_BUTTON           1
7010 #define MARKER_AXIS_X           2
7011 #define MARKER_AXIS_Y           3
7012
7013 static boolean ConfigureJoystickMapButtonsAndAxes(SDL_Joystick *joystick)
7014 {
7015 #if defined(TARGET_SDL2)
7016   static boolean bitmaps_initialized = FALSE;
7017   boolean screen_initialized = FALSE;
7018   static Bitmap *controller, *button, *axis_x, *axis_y;
7019   char *name;
7020   boolean success = TRUE;
7021   boolean done = FALSE, next = FALSE;
7022   Event event;
7023   int alpha = 200, alpha_step = -1;
7024   int alpha_ticks = 0;
7025   char mapping[4096], temp[4096];
7026   int font_name = MENU_SETUP_FONT_TITLE;
7027   int font_info = MENU_SETUP_FONT_TEXT;
7028   int spacing_name = menu.line_spacing_setup[SETUP_MODE_INPUT];
7029   int spacing_line = menu.line_spacing_setup[SETUP_MODE_INPUT];
7030   int spacing_para = menu.paragraph_spacing_setup[SETUP_MODE_INPUT];
7031   int ystep_name = getMenuTextStep(spacing_name, font_name);
7032   int ystep_line = getMenuTextStep(spacing_line, font_info);
7033   int ystep_para = getMenuTextStep(spacing_para, font_info);
7034   int i, j;
7035
7036   struct
7037   {
7038     int x, y;
7039     int marker;
7040     char *field;
7041     int axis, button, hat, hat_value;
7042     char mapping[4096];
7043   }
7044   *step, *prev_step, steps[] =
7045   {
7046     { 356, 155, MARKER_BUTTON, "a",             },
7047     { 396, 122, MARKER_BUTTON, "b",             },
7048     { 320, 125, MARKER_BUTTON, "x",             },
7049     { 358,  95, MARKER_BUTTON, "y",             },
7050     { 162, 125, MARKER_BUTTON, "back",          },
7051     { 216, 125, MARKER_BUTTON, "guide",         },
7052     { 271, 125, MARKER_BUTTON, "start",         },
7053     { 110, 200, MARKER_BUTTON, "dpleft",        },
7054     { 146, 228, MARKER_BUTTON, "dpdown",        },
7055     { 178, 200, MARKER_BUTTON, "dpright",       },
7056     { 146, 172, MARKER_BUTTON, "dpup",          },
7057     {  50,  40, MARKER_BUTTON, "leftshoulder",  },
7058     {  88, -10, MARKER_AXIS_Y, "lefttrigger",   },
7059     { 382,  40, MARKER_BUTTON, "rightshoulder", },
7060     { 346, -10, MARKER_AXIS_Y, "righttrigger",  },
7061     {  73, 141, MARKER_BUTTON, "leftstick",     },
7062     { 282, 210, MARKER_BUTTON, "rightstick",    },
7063     {  73, 141, MARKER_AXIS_X, "leftx",         },
7064     {  73, 141, MARKER_AXIS_Y, "lefty",         },
7065     { 282, 210, MARKER_AXIS_X, "rightx",        },
7066     { 282, 210, MARKER_AXIS_Y, "righty",        },
7067   };
7068
7069   unsigned int event_frame_delay = 0;
7070   unsigned int event_frame_delay_value = GAME_FRAME_DELAY;
7071
7072   ResetDelayCounter(&event_frame_delay);
7073
7074   if (!bitmaps_initialized)
7075   {
7076     controller = LoadCustomImage("joystick/controller.png");
7077     button     = LoadCustomImage("joystick/button.png");
7078     axis_x     = LoadCustomImage("joystick/axis_x.png");
7079     axis_y     = LoadCustomImage("joystick/axis_y.png");
7080
7081     bitmaps_initialized = TRUE;
7082   }
7083
7084   name = getFormattedJoystickName(SDL_JoystickName(joystick));
7085
7086 #if DEBUG_JOYSTICKS
7087   /* print info about the joystick we are watching */
7088   Error(ERR_DEBUG, "watching joystick %d: (%s)\n",
7089         SDL_JoystickInstanceID(joystick), name);
7090   Error(ERR_DEBUG, "joystick has %d axes, %d hats, %d balls, and %d buttons\n",
7091         SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick),
7092         SDL_JoystickNumBalls(joystick), SDL_JoystickNumButtons(joystick));
7093 #endif
7094
7095   /* initialize mapping with GUID and name */
7096   SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), temp, sizeof(temp));
7097
7098   snprintf(mapping, sizeof(mapping), "%s,%s,platform:%s,",
7099            temp, name, SDL_GetPlatform());
7100
7101   /* loop through all steps (buttons and axes), getting joystick events */
7102   for (i = 0; i < SDL_arraysize(steps) && !done;)
7103   {
7104     Bitmap *marker = button;    /* initialize with reliable default value */
7105
7106     step = &steps[i];
7107     strcpy(step->mapping, mapping);
7108     step->axis = -1;
7109     step->button = -1;
7110     step->hat = -1;
7111     step->hat_value = -1;
7112
7113     marker = (step->marker == MARKER_BUTTON ? button :
7114               step->marker == MARKER_AXIS_X ? axis_x :
7115               step->marker == MARKER_AXIS_Y ? axis_y : marker);
7116
7117     next = FALSE;
7118
7119     while (!done && !next)
7120     {
7121       alpha += alpha_step * (int)(SDL_GetTicks() - alpha_ticks) / 5;
7122       alpha_ticks = SDL_GetTicks();
7123
7124       if (alpha >= 255)
7125       {
7126         alpha = 255;
7127         alpha_step = -1;
7128       }
7129       else if (alpha < 128)
7130       {
7131         alpha = 127;
7132         alpha_step = 1;
7133       }
7134
7135       int controller_x = SX + (SXSIZE - controller->width) / 2;
7136       int controller_y = SY + ystep_line;
7137
7138       int marker_x = controller_x + step->x;
7139       int marker_y = controller_y + step->y;
7140
7141       int ystart1 = mSY - 2 * SY + controller_y + controller->height;
7142       int ystart2 = ystart1 + ystep_name + ystep_line;
7143
7144       ClearField();
7145
7146       DrawTextSCentered(ystart1, font_name, name);
7147
7148       DrawTextSCentered(ystart2, font_info,
7149                         "Press buttons and move axes on");
7150       ystart2 += ystep_line;
7151       DrawTextSCentered(ystart2, font_info,
7152                         "your controller when indicated.");
7153       ystart2 += ystep_line;
7154       DrawTextSCentered(ystart2, font_info,
7155                         "(Your controller may look different.)");
7156       ystart2 += ystep_para;
7157
7158 #if defined(PLATFORM_ANDROID)
7159       DrawTextSCentered(ystart2, font_info,
7160                         "To correct a mistake,");
7161       ystart2 += ystep_line;
7162       DrawTextSCentered(ystart2, font_info,
7163                         "press the 'back' button.");
7164       ystart2 += ystep_line;
7165       DrawTextSCentered(ystart2, font_info,
7166                         "To skip a button or axis,");
7167       ystart2 += ystep_line;
7168       DrawTextSCentered(ystart2, font_info,
7169                         "press the 'menu' button.");
7170 #else
7171       DrawTextSCentered(ystart2, font_info,
7172                         "To correct a mistake,");
7173       ystart2 += ystep_line;
7174       DrawTextSCentered(ystart2, font_info,
7175                         "press the 'backspace' key.");
7176       ystart2 += ystep_line;
7177       DrawTextSCentered(ystart2, font_info,
7178                         "To skip a button or axis,");
7179       ystart2 += ystep_line;
7180       DrawTextSCentered(ystart2, font_info,
7181                         "press the 'return' key.");
7182       ystart2 += ystep_line;
7183       DrawTextSCentered(ystart2, font_info,
7184                         "To exit, press the 'escape' key.");
7185 #endif
7186
7187       BlitBitmapMasked(controller, drawto, 0, 0,
7188                        controller->width, controller->height,
7189                        controller_x, controller_y);
7190
7191       SDL_SetSurfaceBlendMode(marker->surface_masked, SDL_BLENDMODE_BLEND);
7192       SDL_SetSurfaceAlphaMod(marker->surface_masked, alpha);
7193
7194       BlitBitmapMasked(marker, drawto, 0, 0,
7195                        marker->width, marker->height,
7196                        marker_x, marker_y);
7197
7198       if (!screen_initialized)
7199         FadeIn(REDRAW_FIELD);
7200       else
7201         BackToFront();
7202
7203       screen_initialized = TRUE;
7204
7205       while (NextValidEvent(&event))
7206       {
7207         switch (event.type)
7208         {
7209           case SDL_JOYAXISMOTION:
7210             if (event.jaxis.value > 20000 ||
7211                 event.jaxis.value < -20000)
7212             {
7213               for (j = 0; j < i; j++)
7214                 if (steps[j].axis == event.jaxis.axis)
7215                   break;
7216
7217               if (j == i)
7218               {
7219                 if (step->marker != MARKER_AXIS_X &&
7220                     step->marker != MARKER_AXIS_Y)
7221                   break;
7222
7223                 step->axis = event.jaxis.axis;
7224                 strcat(mapping, step->field);
7225                 snprintf(temp, sizeof(temp), ":a%u,", event.jaxis.axis);
7226                 strcat(mapping, temp);
7227                 i++;
7228                 next = TRUE;
7229               }
7230             }
7231
7232             break;
7233
7234           case SDL_JOYHATMOTION:
7235             /* ignore centering; we're probably just coming back
7236                to the center from the previous item we set */
7237             if (event.jhat.value == SDL_HAT_CENTERED)
7238               break;
7239
7240             for (j = 0; j < i; j++)
7241               if (steps[j].hat == event.jhat.hat &&
7242                   steps[j].hat_value == event.jhat.value)
7243                 break;
7244
7245             if (j == i)
7246             {
7247               step->hat = event.jhat.hat;
7248               step->hat_value = event.jhat.value;
7249               strcat(mapping, step->field);
7250               snprintf(temp, sizeof(temp), ":h%u.%u,",
7251                        event.jhat.hat, event.jhat.value );
7252               strcat(mapping, temp);
7253               i++;
7254               next = TRUE;
7255             }
7256
7257             break;
7258
7259           case SDL_JOYBALLMOTION:
7260             break;
7261
7262           case SDL_JOYBUTTONUP:
7263             for (j = 0; j < i; j++)
7264               if (steps[j].button == event.jbutton.button)
7265                 break;
7266
7267             if (j == i)
7268             {
7269               step->button = event.jbutton.button;
7270               strcat(mapping, step->field);
7271               snprintf(temp, sizeof(temp), ":b%u,", event.jbutton.button);
7272               strcat(mapping, temp);
7273               i++;
7274               next = TRUE;
7275             }
7276
7277             break;
7278
7279           case SDL_FINGERDOWN:
7280           case SDL_MOUSEBUTTONDOWN:
7281             /* skip this step */
7282             i++;
7283             next = TRUE;
7284
7285             break;
7286
7287           case SDL_KEYDOWN:
7288             if (event.key.keysym.sym == KSYM_BackSpace ||
7289                 event.key.keysym.sym == KSYM_Back)
7290             {
7291               if (i == 0)
7292               {
7293                 /* leave screen */
7294                 success = FALSE;
7295                 done = TRUE;
7296
7297                 break;
7298               }
7299
7300               /* undo this step */
7301               prev_step = &steps[i - 1];
7302               strcpy(mapping, prev_step->mapping);
7303               i--;
7304               next = TRUE;
7305
7306               break;
7307             }
7308
7309             if (event.key.keysym.sym == KSYM_space ||
7310                 event.key.keysym.sym == KSYM_Return ||
7311                 event.key.keysym.sym == KSYM_Menu)
7312             {
7313               /* skip this step */
7314               i++;
7315               next = TRUE;
7316
7317               break;
7318             }
7319
7320             if (event.key.keysym.sym == KSYM_Escape)
7321             {
7322               /* leave screen */
7323               success = FALSE;
7324               done = TRUE;
7325             }
7326
7327             break;
7328
7329           case SDL_QUIT:
7330             program.exit_function(0);
7331             break;
7332
7333           default:
7334             break;
7335         }
7336
7337         // do not handle events for longer than standard frame delay period
7338         if (DelayReached(&event_frame_delay, event_frame_delay_value))
7339           break;
7340       }
7341     }
7342   }
7343
7344   if (success)
7345   {
7346 #if DEBUG_JOYSTICKS
7347     Error(ERR_DEBUG, "New game controller mapping:\n\n%s\n\n", mapping);
7348 #endif
7349
7350     // activate mapping for this game
7351     SDL_GameControllerAddMapping(mapping);
7352
7353     // save mapping to personal mappings
7354     SaveSetup_AddGameControllerMapping(mapping);
7355   }
7356
7357   /* wait until the last pending event was removed from event queue */
7358   while (NextValidEvent(&event));
7359
7360   return success;
7361 #else
7362   return TRUE;
7363 #endif
7364 }
7365
7366 static int ConfigureJoystickMain(int player_nr)
7367 {
7368   char *device_name = setup.input[player_nr].joy.device_name;
7369   int joystick_nr = getJoystickNrFromDeviceName(device_name);
7370   boolean joystick_active = CheckJoystickOpened(joystick_nr);
7371   int success = FALSE;
7372   int i;
7373
7374   if (joystick.status == JOYSTICK_NOT_AVAILABLE)
7375     return JOYSTICK_NOT_AVAILABLE;
7376
7377   if (!joystick_active || !setup.input[player_nr].use_joystick)
7378     return JOYSTICK_NOT_AVAILABLE;
7379
7380   FadeSetEnterMenu();
7381   FadeOut(REDRAW_FIELD);
7382
7383   // close all joystick devices (potentially opened as game controllers)
7384   for (i = 0; i < SDL_NumJoysticks(); i++)
7385     SDLCloseJoystick(i);
7386
7387   // open joystick device as plain joystick to configure as game controller
7388   SDL_Joystick *joystick = SDL_JoystickOpen(joystick_nr);
7389
7390   // as the joystick was successfully opened before, this should not happen
7391   if (joystick == NULL)
7392     return FALSE;
7393
7394   // create new game controller mapping (buttons and axes) for joystick device
7395   success = ConfigureJoystickMapButtonsAndAxes(joystick);
7396
7397   // close joystick (and maybe re-open as configured game controller later)
7398   SDL_JoystickClose(joystick);
7399
7400   // re-open all joystick devices (potentially as game controllers)
7401   for (i = 0; i < SDL_NumJoysticks(); i++)
7402     SDLOpenJoystick(i);
7403
7404   // clear all joystick input actions for all joystick devices
7405   SDLClearJoystickState();
7406
7407   return (success ? JOYSTICK_CONFIGURED : JOYSTICK_NOT_CONFIGURED);
7408 }
7409
7410 void ConfigureJoystick(int player_nr)
7411 {
7412   boolean state = ConfigureJoystickMain(player_nr);
7413
7414   if (state != JOYSTICK_NOT_CONFIGURED)
7415   {
7416     boolean success = (state == JOYSTICK_CONFIGURED);
7417     char message1[MAX_OUTPUT_LINESIZE + 1];
7418     char *message2 = (success ? "configured!" : "not available!");
7419     char *device_name = setup.input[player_nr].joy.device_name;
7420     int nr = getJoystickNrFromDeviceName(device_name) + 1;
7421     int font_nr = FONT_TITLE_1;
7422     int font_height = getFontHeight(font_nr);
7423     int ypos1 = SYSIZE / 2 - font_height * 2;
7424     int ypos2 = SYSIZE / 2 - font_height * 1;
7425     unsigned int wait_frame_delay = 0;
7426     unsigned int wait_frame_delay_value = 2000;
7427
7428     ResetDelayCounter(&wait_frame_delay);
7429
7430     ClearField();
7431
7432     sprintf(message1, "Joystick %d", nr);
7433
7434     DrawTextSCentered(ypos1, font_nr, message1);
7435     DrawTextSCentered(ypos2, font_nr, message2);
7436
7437     while (!DelayReached(&wait_frame_delay, wait_frame_delay_value))
7438       BackToFront();
7439
7440     ClearEventQueue();
7441   }
7442
7443   DrawSetupScreen_Input();
7444 }
7445
7446 boolean ConfigureVirtualButtonsMain()
7447 {
7448   static char *customize_step_text[] =
7449   {
7450     "Move Left",
7451     "Move Right",
7452     "Move Up",
7453     "Move Down",
7454     "Snap Field",
7455     "Drop Element"
7456   };
7457   char grid_button[] =
7458   {
7459     CHAR_GRID_BUTTON_LEFT,
7460     CHAR_GRID_BUTTON_RIGHT,
7461     CHAR_GRID_BUTTON_UP,
7462     CHAR_GRID_BUTTON_DOWN,
7463     CHAR_GRID_BUTTON_SNAP,
7464     CHAR_GRID_BUTTON_DROP
7465   };
7466   int font_nr = FONT_INPUT_1_ACTIVE;
7467   int font_height = getFontHeight(font_nr);
7468   int ypos1 = SYSIZE / 2 - font_height * 2;
7469   int ypos2 = SYSIZE / 2 - font_height * 1;
7470   boolean success = FALSE;
7471   boolean finished = FALSE;
7472   int step_nr = 0;
7473   char grid_button_draw = CHAR_GRID_BUTTON_NONE;
7474   char grid_button_old[MAX_GRID_XSIZE][MAX_GRID_YSIZE];
7475   char grid_button_tmp[MAX_GRID_XSIZE][MAX_GRID_YSIZE];
7476   boolean set_grid_button = FALSE;
7477   int nr = GRID_ACTIVE_NR();
7478   int x, y;
7479
7480   for (x = 0; x < MAX_GRID_XSIZE; x++)
7481     for (y = 0; y < MAX_GRID_YSIZE; y++)
7482       grid_button_old[x][y] = grid_button_tmp[x][y] = overlay.grid_button[x][y];
7483
7484   overlay.grid_button_highlight = grid_button[step_nr];
7485
7486   FadeSetEnterMenu();
7487   FadeOut(REDRAW_FIELD);
7488
7489   ClearField();
7490
7491   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Virtual Buttons");
7492   DrawTextSCentered(ypos1, font_nr, "Select tiles to");
7493   DrawTextSCentered(ypos2, font_nr, customize_step_text[step_nr]);
7494
7495   FadeIn(REDRAW_FIELD);
7496
7497   SetOverlayShowGrid(TRUE);
7498
7499   while (!finished)
7500   {
7501     Event event;
7502
7503     while (NextValidEvent(&event))
7504     {
7505       switch (event.type)
7506       {
7507         case EVENT_KEYPRESS:
7508           {
7509             Key key = GetEventKey((KeyEvent *)&event, FALSE);
7510
7511             /* press 'Escape' to abort and keep the old key bindings */
7512             if (key == KSYM_Escape)
7513             {
7514               for (x = 0; x < MAX_GRID_XSIZE; x++)
7515                 for (y = 0; y < MAX_GRID_YSIZE; y++)
7516                   overlay.grid_button[x][y] = grid_button_old[x][y];
7517
7518               FadeSkipNextFadeIn();
7519
7520               finished = TRUE;
7521
7522               break;
7523             }
7524
7525             /* press 'Enter' to keep the existing key binding */
7526             if (key == KSYM_Return ||
7527                 key == KSYM_Menu ||
7528                 key == KSYM_space)
7529             {
7530               step_nr++;
7531             }
7532             else if (key == KSYM_BackSpace ||
7533                      key == KSYM_Back)
7534             {
7535               if (step_nr == 0)
7536               {
7537                 FadeSkipNextFadeIn();
7538
7539                 finished = TRUE;
7540
7541                 break;
7542               }
7543
7544               step_nr--;
7545             }
7546             else
7547             {
7548               break;
7549             }
7550
7551             /* all virtual buttons configured */
7552             if (step_nr == 6)
7553             {
7554               finished = TRUE;
7555               success = TRUE;
7556
7557               break;
7558             }
7559
7560             for (x = 0; x < MAX_GRID_XSIZE; x++)
7561               for (y = 0; y < MAX_GRID_YSIZE; y++)
7562                 grid_button_tmp[x][y] = overlay.grid_button[x][y];
7563
7564             overlay.grid_button_highlight = grid_button[step_nr];
7565
7566             /* query next virtual button */
7567
7568             ClearField();
7569
7570             DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Virtual Buttons");
7571             DrawTextSCentered(ypos1, font_nr, "Select tiles to");
7572             DrawTextSCentered(ypos2, font_nr, customize_step_text[step_nr]);
7573           }
7574           break;
7575
7576         case EVENT_KEYRELEASE:
7577           key_joystick_mapping = 0;
7578           break;
7579
7580         case EVENT_BUTTONPRESS:
7581         case EVENT_BUTTONRELEASE:
7582           {
7583             ButtonEvent *button = (ButtonEvent *)&event;
7584
7585             button->x += video.screen_xoffset;
7586             button->y += video.screen_yoffset;
7587
7588             x = button->x * overlay.grid_xsize / video.screen_width;
7589             y = button->y * overlay.grid_ysize / video.screen_height;
7590
7591             if (button->type == EVENT_BUTTONPRESS)
7592             {
7593               button_status = button->button;
7594
7595               grid_button_draw =
7596                 (overlay.grid_button[x][y] != grid_button[step_nr] ?
7597                  grid_button[step_nr] : CHAR_GRID_BUTTON_NONE);
7598
7599               set_grid_button = TRUE;
7600             }
7601             else
7602             {
7603               button_status = MB_RELEASED;
7604             }
7605           }
7606           break;
7607
7608         case EVENT_MOTIONNOTIFY:
7609           {
7610             MotionEvent *motion = (MotionEvent *)&event;
7611
7612             motion->x += video.screen_xoffset;
7613             motion->y += video.screen_yoffset;
7614
7615             x = motion->x * overlay.grid_xsize / video.screen_width;
7616             y = motion->y * overlay.grid_ysize / video.screen_height;
7617
7618             set_grid_button = TRUE;
7619           }
7620           break;
7621
7622 #if defined(TARGET_SDL2)
7623         case SDL_WINDOWEVENT:
7624           HandleWindowEvent((WindowEvent *) &event);
7625
7626           // check if device has been rotated
7627           if (nr != GRID_ACTIVE_NR())
7628           {
7629             nr = GRID_ACTIVE_NR();
7630
7631             for (x = 0; x < MAX_GRID_XSIZE; x++)
7632               for (y = 0; y < MAX_GRID_YSIZE; y++)
7633                 grid_button_old[x][y] = grid_button_tmp[x][y] =
7634                   overlay.grid_button[x][y];
7635           }
7636
7637           break;
7638
7639         case SDL_APP_WILLENTERBACKGROUND:
7640         case SDL_APP_DIDENTERBACKGROUND:
7641         case SDL_APP_WILLENTERFOREGROUND:
7642         case SDL_APP_DIDENTERFOREGROUND:
7643           HandlePauseResumeEvent((PauseResumeEvent *) &event);
7644           break;
7645 #endif
7646
7647         default:
7648           HandleOtherEvents(&event);
7649           break;
7650       }
7651
7652       if (set_grid_button)
7653       {
7654         overlay.grid_button[x][y] =
7655           (grid_button_draw != CHAR_GRID_BUTTON_NONE ? grid_button_draw :
7656            grid_button_tmp[x][y] == grid_button[step_nr] ? CHAR_GRID_BUTTON_NONE :
7657            grid_button_tmp[x][y]);
7658
7659         set_grid_button = FALSE;
7660       }
7661     }
7662
7663     BackToFront();
7664   }
7665
7666   for (x = 0; x < MAX_GRID_XSIZE; x++)
7667     for (y = 0; y < MAX_GRID_YSIZE; y++)
7668       setup.touch.grid_button[nr][x][y] = overlay.grid_button[x][y];
7669
7670   overlay.grid_button_highlight = CHAR_GRID_BUTTON_NONE;
7671
7672   SetOverlayShowGrid(FALSE);
7673
7674   return success;
7675 }
7676
7677 void ConfigureVirtualButtons()
7678 {
7679   boolean success = ConfigureVirtualButtonsMain();
7680
7681   if (success)
7682   {
7683     int font_nr = FONT_TITLE_1;
7684     int font_height = getFontHeight(font_nr);
7685     int ypos1 = SYSIZE / 2 - font_height * 2;
7686     int ypos2 = SYSIZE / 2 - font_height * 1;
7687     unsigned int wait_frame_delay = 0;
7688     unsigned int wait_frame_delay_value = 2000;
7689
7690     ResetDelayCounter(&wait_frame_delay);
7691
7692     ClearField();
7693
7694     DrawTextSCentered(ypos1, font_nr, "Virtual buttons");
7695     DrawTextSCentered(ypos2, font_nr, "configured!");
7696
7697     while (!DelayReached(&wait_frame_delay, wait_frame_delay_value))
7698       BackToFront();
7699
7700     ClearEventQueue();
7701   }
7702 }
7703
7704 void DrawSetupScreen()
7705 {
7706   if (setup_mode == SETUP_MODE_INPUT)
7707     DrawSetupScreen_Input();
7708   else if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED)
7709     DrawChooseTree(&game_speed_current);
7710   else if (setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY)
7711     DrawChooseTree(&scroll_delay_current);
7712   else if (setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
7713     DrawChooseTree(&snapshot_mode_current);
7714   else if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE)
7715     DrawChooseTree(&window_size_current);
7716   else if (setup_mode == SETUP_MODE_CHOOSE_SCALING_TYPE)
7717     DrawChooseTree(&scaling_type_current);
7718   else if (setup_mode == SETUP_MODE_CHOOSE_RENDERING)
7719     DrawChooseTree(&rendering_mode_current);
7720   else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
7721     DrawChooseTree(&artwork.gfx_current);
7722   else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
7723     DrawChooseTree(&artwork.snd_current);
7724   else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
7725     DrawChooseTree(&artwork.mus_current);
7726   else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_SIMPLE)
7727     DrawChooseTree(&volume_simple_current);
7728   else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_LOOPS)
7729     DrawChooseTree(&volume_loops_current);
7730   else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_MUSIC)
7731     DrawChooseTree(&volume_music_current);
7732   else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL)
7733     DrawChooseTree(&touch_control_current);
7734   else if (setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE)
7735     DrawChooseTree(&move_distance_current);
7736   else if (setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE)
7737     DrawChooseTree(&drop_distance_current);
7738   else if (setup_mode == SETUP_MODE_CHOOSE_TRANSPARENCY)
7739     DrawChooseTree(&transparency_current);
7740   else if (setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_0)
7741     DrawChooseTree(&grid_size_current[0][0]);
7742   else if (setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_0)
7743     DrawChooseTree(&grid_size_current[0][1]);
7744   else if (setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_1)
7745     DrawChooseTree(&grid_size_current[1][0]);
7746   else if (setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_1)
7747     DrawChooseTree(&grid_size_current[1][1]);
7748   else
7749     DrawSetupScreen_Generic();
7750
7751   PlayMenuSoundsAndMusic();
7752 }
7753
7754 void RedrawSetupScreenAfterFullscreenToggle()
7755 {
7756   if (setup_mode == SETUP_MODE_GRAPHICS ||
7757       setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE)
7758   {
7759     // update list selection from "setup.window_scaling_percent"
7760     execSetupGraphics_setWindowSizes(TRUE);
7761
7762     DrawSetupScreen();
7763   }
7764 }
7765
7766 void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
7767 {
7768   if (setup_mode == SETUP_MODE_INPUT)
7769     HandleSetupScreen_Input(mx, my, dx, dy, button);
7770   else if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED)
7771     HandleChooseTree(mx, my, dx, dy, button, &game_speed_current);
7772   else if (setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY)
7773     HandleChooseTree(mx, my, dx, dy, button, &scroll_delay_current);
7774   else if (setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
7775     HandleChooseTree(mx, my, dx, dy, button, &snapshot_mode_current);
7776   else if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE)
7777     HandleChooseTree(mx, my, dx, dy, button, &window_size_current);
7778   else if (setup_mode == SETUP_MODE_CHOOSE_SCALING_TYPE)
7779     HandleChooseTree(mx, my, dx, dy, button, &scaling_type_current);
7780   else if (setup_mode == SETUP_MODE_CHOOSE_RENDERING)
7781     HandleChooseTree(mx, my, dx, dy, button, &rendering_mode_current);
7782   else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
7783     HandleChooseTree(mx, my, dx, dy, button, &artwork.gfx_current);
7784   else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
7785     HandleChooseTree(mx, my, dx, dy, button, &artwork.snd_current);
7786   else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
7787     HandleChooseTree(mx, my, dx, dy, button, &artwork.mus_current);
7788   else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_SIMPLE)
7789     HandleChooseTree(mx, my, dx, dy, button, &volume_simple_current);
7790   else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_LOOPS)
7791     HandleChooseTree(mx, my, dx, dy, button, &volume_loops_current);
7792   else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_MUSIC)
7793     HandleChooseTree(mx, my, dx, dy, button, &volume_music_current);
7794   else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL)
7795     HandleChooseTree(mx, my, dx, dy, button, &touch_control_current);
7796   else if (setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE)
7797     HandleChooseTree(mx, my, dx, dy, button, &move_distance_current);
7798   else if (setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE)
7799     HandleChooseTree(mx, my, dx, dy, button, &drop_distance_current);
7800   else if (setup_mode == SETUP_MODE_CHOOSE_TRANSPARENCY)
7801     HandleChooseTree(mx, my, dx, dy, button, &transparency_current);
7802   else if (setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_0)
7803     HandleChooseTree(mx, my, dx, dy, button, &grid_size_current[0][0]);
7804   else if (setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_0)
7805     HandleChooseTree(mx, my, dx, dy, button, &grid_size_current[0][1]);
7806   else if (setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_1)
7807     HandleChooseTree(mx, my, dx, dy, button, &grid_size_current[1][0]);
7808   else if (setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_1)
7809     HandleChooseTree(mx, my, dx, dy, button, &grid_size_current[1][1]);
7810   else
7811     HandleSetupScreen_Generic(mx, my, dx, dy, button);
7812 }
7813
7814 void HandleGameActions()
7815 {
7816   if (game.restart_game_message != NULL)
7817     RequestRestartGame(game.restart_game_message);
7818
7819   if (game_status != GAME_MODE_PLAYING)
7820     return;
7821
7822   GameActions();        /* main game loop */
7823
7824   if (tape.auto_play && !tape.playing)
7825     AutoPlayTape();     /* continue automatically playing next tape */
7826 }
7827
7828
7829 /* ---------- new screen button stuff -------------------------------------- */
7830
7831 static void getScreenMenuButtonPos(int *x, int *y, int gadget_id)
7832 {
7833   switch (gadget_id)
7834   {
7835     case SCREEN_CTRL_ID_PREV_LEVEL:
7836       *x = mSX + GDI_ACTIVE_POS(menu.main.button.prev_level.x);
7837       *y = mSY + GDI_ACTIVE_POS(menu.main.button.prev_level.y);
7838       break;
7839
7840     case SCREEN_CTRL_ID_NEXT_LEVEL:
7841       *x = mSX + GDI_ACTIVE_POS(menu.main.button.next_level.x);
7842       *y = mSY + GDI_ACTIVE_POS(menu.main.button.next_level.y);
7843       break;
7844
7845     case SCREEN_CTRL_ID_PREV_PLAYER:
7846       *x = mSX + TILEX * 10;
7847       *y = mSY + TILEY * MENU_SCREEN_START_YPOS;
7848       break;
7849
7850     case SCREEN_CTRL_ID_NEXT_PLAYER:
7851       *x = mSX + TILEX * 12;
7852       *y = mSY + TILEY * MENU_SCREEN_START_YPOS;
7853       break;
7854
7855     default:
7856       Error(ERR_EXIT, "unknown gadget ID %d", gadget_id);
7857   }
7858 }
7859
7860 static struct
7861 {
7862   int gfx_unpressed, gfx_pressed;
7863   void (*get_gadget_position)(int *, int *, int);
7864   int gadget_id;
7865   int screen_mask;
7866   char *infotext;
7867 } menubutton_info[NUM_SCREEN_MENUBUTTONS] =
7868 {
7869   {
7870     IMG_MENU_BUTTON_PREV_LEVEL, IMG_MENU_BUTTON_PREV_LEVEL_ACTIVE,
7871     getScreenMenuButtonPos,
7872     SCREEN_CTRL_ID_PREV_LEVEL,
7873     SCREEN_MASK_MAIN,
7874     "last level"
7875   },
7876   {
7877     IMG_MENU_BUTTON_NEXT_LEVEL, IMG_MENU_BUTTON_NEXT_LEVEL_ACTIVE,
7878     getScreenMenuButtonPos,
7879     SCREEN_CTRL_ID_NEXT_LEVEL,
7880     SCREEN_MASK_MAIN,
7881     "next level"
7882   },
7883   {
7884     IMG_MENU_BUTTON_LEFT, IMG_MENU_BUTTON_LEFT_ACTIVE,
7885     getScreenMenuButtonPos,
7886     SCREEN_CTRL_ID_PREV_PLAYER,
7887     SCREEN_MASK_INPUT,
7888     "last player"
7889   },
7890   {
7891     IMG_MENU_BUTTON_RIGHT, IMG_MENU_BUTTON_RIGHT_ACTIVE,
7892     getScreenMenuButtonPos,
7893     SCREEN_CTRL_ID_NEXT_PLAYER,
7894     SCREEN_MASK_INPUT,
7895     "next player"
7896   },
7897 };
7898
7899 static struct
7900 {
7901   int gfx_unpressed, gfx_pressed;
7902   int x, y;
7903   int gadget_id;
7904   char *infotext;
7905 } scrollbutton_info[NUM_SCREEN_SCROLLBUTTONS] =
7906 {
7907   {
7908     IMG_MENU_BUTTON_UP, IMG_MENU_BUTTON_UP_ACTIVE,
7909     -1, -1,     /* these values are not constant, but can change at runtime */
7910     SCREEN_CTRL_ID_SCROLL_UP,
7911     "scroll up"
7912   },
7913   {
7914     IMG_MENU_BUTTON_DOWN, IMG_MENU_BUTTON_DOWN_ACTIVE,
7915     -1, -1,     /* these values are not constant, but can change at runtime */
7916     SCREEN_CTRL_ID_SCROLL_DOWN,
7917     "scroll down"
7918   }
7919 };
7920
7921 static struct
7922 {
7923   int gfx_unpressed, gfx_pressed;
7924   int x, y;
7925   int width, height;
7926   int type;
7927   int gadget_id;
7928   char *infotext;
7929 } scrollbar_info[NUM_SCREEN_SCROLLBARS] =
7930 {
7931   {
7932     IMG_MENU_SCROLLBAR, IMG_MENU_SCROLLBAR_ACTIVE,
7933     -1, -1,     /* these values are not constant, but can change at runtime */
7934     -1, -1,     /* these values are not constant, but can change at runtime */
7935     GD_TYPE_SCROLLBAR_VERTICAL,
7936     SCREEN_CTRL_ID_SCROLL_VERTICAL,
7937     "scroll level series vertically"
7938   }
7939 };
7940
7941 static void CreateScreenMenubuttons()
7942 {
7943   struct GadgetInfo *gi;
7944   unsigned int event_mask;
7945   int i;
7946
7947   for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
7948   {
7949     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
7950     int gfx_unpressed, gfx_pressed;
7951     int x, y, width, height;
7952     int gd_x1, gd_x2, gd_y1, gd_y2;
7953     int id = menubutton_info[i].gadget_id;
7954
7955     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
7956
7957     menubutton_info[i].get_gadget_position(&x, &y, id);
7958
7959     width = SC_MENUBUTTON_XSIZE;
7960     height = SC_MENUBUTTON_YSIZE;
7961
7962     gfx_unpressed = menubutton_info[i].gfx_unpressed;
7963     gfx_pressed   = menubutton_info[i].gfx_pressed;
7964     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
7965     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
7966     gd_x1 = graphic_info[gfx_unpressed].src_x;
7967     gd_y1 = graphic_info[gfx_unpressed].src_y;
7968     gd_x2 = graphic_info[gfx_pressed].src_x;
7969     gd_y2 = graphic_info[gfx_pressed].src_y;
7970
7971     gi = CreateGadget(GDI_CUSTOM_ID, id,
7972                       GDI_CUSTOM_TYPE_ID, i,
7973                       GDI_INFO_TEXT, menubutton_info[i].infotext,
7974                       GDI_X, x,
7975                       GDI_Y, y,
7976                       GDI_WIDTH, width,
7977                       GDI_HEIGHT, height,
7978                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
7979                       GDI_STATE, GD_BUTTON_UNPRESSED,
7980                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
7981                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
7982                       GDI_DIRECT_DRAW, FALSE,
7983                       GDI_EVENT_MASK, event_mask,
7984                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
7985                       GDI_END);
7986
7987     if (gi == NULL)
7988       Error(ERR_EXIT, "cannot create gadget");
7989
7990     screen_gadget[id] = gi;
7991   }
7992 }
7993
7994 static void CreateScreenScrollbuttons()
7995 {
7996   struct GadgetInfo *gi;
7997   unsigned int event_mask;
7998   int i;
7999
8000   /* these values are not constant, but can change at runtime */
8001   scrollbutton_info[0].x = SC_SCROLL_UP_XPOS;
8002   scrollbutton_info[0].y = SC_SCROLL_UP_YPOS;
8003   scrollbutton_info[1].x = SC_SCROLL_DOWN_XPOS;
8004   scrollbutton_info[1].y = SC_SCROLL_DOWN_YPOS;
8005
8006   for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++)
8007   {
8008     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
8009     int gfx_unpressed, gfx_pressed;
8010     int x, y, width, height;
8011     int gd_x1, gd_x2, gd_y1, gd_y2;
8012     int id = scrollbutton_info[i].gadget_id;
8013
8014     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
8015
8016     x = mSX + scrollbutton_info[i].x + menu.scrollbar_xoffset;
8017     y = mSY + scrollbutton_info[i].y;
8018     width = SC_SCROLLBUTTON_XSIZE;
8019     height = SC_SCROLLBUTTON_YSIZE;
8020
8021     /* correct scrollbar position if placed outside menu (playfield) area */
8022     if (x > SX + SC_SCROLL_UP_XPOS)
8023       x = SX + SC_SCROLL_UP_XPOS;
8024
8025     if (id == SCREEN_CTRL_ID_SCROLL_DOWN)
8026       y = mSY + (SC_SCROLL_VERTICAL_YPOS +
8027                  (NUM_MENU_ENTRIES_ON_SCREEN - 2) * SC_SCROLLBUTTON_YSIZE);
8028
8029     gfx_unpressed = scrollbutton_info[i].gfx_unpressed;
8030     gfx_pressed   = scrollbutton_info[i].gfx_pressed;
8031     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
8032     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
8033     gd_x1 = graphic_info[gfx_unpressed].src_x;
8034     gd_y1 = graphic_info[gfx_unpressed].src_y;
8035     gd_x2 = graphic_info[gfx_pressed].src_x;
8036     gd_y2 = graphic_info[gfx_pressed].src_y;
8037
8038     gi = CreateGadget(GDI_CUSTOM_ID, id,
8039                       GDI_CUSTOM_TYPE_ID, i,
8040                       GDI_INFO_TEXT, scrollbutton_info[i].infotext,
8041                       GDI_X, x,
8042                       GDI_Y, y,
8043                       GDI_WIDTH, width,
8044                       GDI_HEIGHT, height,
8045                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
8046                       GDI_STATE, GD_BUTTON_UNPRESSED,
8047                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
8048                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
8049                       GDI_DIRECT_DRAW, FALSE,
8050                       GDI_EVENT_MASK, event_mask,
8051                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
8052                       GDI_END);
8053
8054     if (gi == NULL)
8055       Error(ERR_EXIT, "cannot create gadget");
8056
8057     screen_gadget[id] = gi;
8058   }
8059 }
8060
8061 static void CreateScreenScrollbars()
8062 {
8063   int i;
8064
8065   /* these values are not constant, but can change at runtime */
8066   scrollbar_info[0].x = SC_SCROLL_VERTICAL_XPOS;
8067   scrollbar_info[0].y = SC_SCROLL_VERTICAL_YPOS;
8068   scrollbar_info[0].width  = SC_SCROLL_VERTICAL_XSIZE;
8069   scrollbar_info[0].height = SC_SCROLL_VERTICAL_YSIZE;
8070
8071   for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++)
8072   {
8073     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
8074     int gfx_unpressed, gfx_pressed;
8075     int x, y, width, height;
8076     int gd_x1, gd_x2, gd_y1, gd_y2;
8077     struct GadgetInfo *gi;
8078     int items_max, items_visible, item_position;
8079     unsigned int event_mask;
8080     int num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
8081     int id = scrollbar_info[i].gadget_id;
8082
8083     event_mask = GD_EVENT_MOVING | GD_EVENT_OFF_BORDERS;
8084
8085     x = mSX + scrollbar_info[i].x + menu.scrollbar_xoffset;
8086     y = mSY + scrollbar_info[i].y;
8087     width  = scrollbar_info[i].width;
8088     height = scrollbar_info[i].height;
8089
8090     /* correct scrollbar position if placed outside menu (playfield) area */
8091     if (x > SX + SC_SCROLL_VERTICAL_XPOS)
8092       x = SX + SC_SCROLL_VERTICAL_XPOS;
8093
8094     if (id == SCREEN_CTRL_ID_SCROLL_VERTICAL)
8095       height = (NUM_MENU_ENTRIES_ON_SCREEN - 2) * SC_SCROLLBUTTON_YSIZE;
8096
8097     items_max = num_page_entries;
8098     items_visible = num_page_entries;
8099     item_position = 0;
8100
8101     gfx_unpressed = scrollbar_info[i].gfx_unpressed;
8102     gfx_pressed   = scrollbar_info[i].gfx_pressed;
8103     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
8104     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
8105     gd_x1 = graphic_info[gfx_unpressed].src_x;
8106     gd_y1 = graphic_info[gfx_unpressed].src_y;
8107     gd_x2 = graphic_info[gfx_pressed].src_x;
8108     gd_y2 = graphic_info[gfx_pressed].src_y;
8109
8110     gi = CreateGadget(GDI_CUSTOM_ID, id,
8111                       GDI_CUSTOM_TYPE_ID, i,
8112                       GDI_INFO_TEXT, scrollbar_info[i].infotext,
8113                       GDI_X, x,
8114                       GDI_Y, y,
8115                       GDI_WIDTH, width,
8116                       GDI_HEIGHT, height,
8117                       GDI_TYPE, scrollbar_info[i].type,
8118                       GDI_SCROLLBAR_ITEMS_MAX, items_max,
8119                       GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
8120                       GDI_SCROLLBAR_ITEM_POSITION, item_position,
8121                       GDI_WHEEL_AREA_X, SX,
8122                       GDI_WHEEL_AREA_Y, SY,
8123                       GDI_WHEEL_AREA_WIDTH, SXSIZE,
8124                       GDI_WHEEL_AREA_HEIGHT, SYSIZE,
8125                       GDI_STATE, GD_BUTTON_UNPRESSED,
8126                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
8127                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
8128                       GDI_BORDER_SIZE, SC_BORDER_SIZE, SC_BORDER_SIZE,
8129                       GDI_DIRECT_DRAW, FALSE,
8130                       GDI_EVENT_MASK, event_mask,
8131                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
8132                       GDI_END);
8133
8134     if (gi == NULL)
8135       Error(ERR_EXIT, "cannot create gadget");
8136
8137     screen_gadget[id] = gi;
8138   }
8139 }
8140
8141 void CreateScreenGadgets()
8142 {
8143   CreateScreenMenubuttons();
8144
8145   CreateScreenScrollbuttons();
8146   CreateScreenScrollbars();
8147 }
8148
8149 void FreeScreenGadgets()
8150 {
8151   int i;
8152
8153   for (i = 0; i < NUM_SCREEN_GADGETS; i++)
8154     FreeGadget(screen_gadget[i]);
8155 }
8156
8157 void MapScreenMenuGadgets(int screen_mask)
8158 {
8159   int i;
8160
8161   for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
8162     if (screen_mask & menubutton_info[i].screen_mask)
8163       MapGadget(screen_gadget[menubutton_info[i].gadget_id]);
8164 }
8165
8166 void MapScreenGadgets(int num_entries)
8167 {
8168   int i;
8169
8170   if (num_entries <= NUM_MENU_ENTRIES_ON_SCREEN)
8171     return;
8172
8173   for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++)
8174     MapGadget(screen_gadget[scrollbutton_info[i].gadget_id]);
8175
8176   for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++)
8177     MapGadget(screen_gadget[scrollbar_info[i].gadget_id]);
8178 }
8179
8180 void MapScreenTreeGadgets(TreeInfo *ti)
8181 {
8182   MapScreenGadgets(numTreeInfoInGroup(ti));
8183 }
8184
8185 static void HandleScreenGadgets(struct GadgetInfo *gi)
8186 {
8187   int id = gi->custom_id;
8188   int button = gi->event.button;
8189   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
8190
8191   switch (id)
8192   {
8193     case SCREEN_CTRL_ID_PREV_LEVEL:
8194       HandleMainMenu_SelectLevel(step, -1, NO_DIRECT_LEVEL_SELECT);
8195       break;
8196
8197     case SCREEN_CTRL_ID_NEXT_LEVEL:
8198       HandleMainMenu_SelectLevel(step, +1, NO_DIRECT_LEVEL_SELECT);
8199       break;
8200
8201     case SCREEN_CTRL_ID_PREV_PLAYER:
8202       HandleSetupScreen_Input_Player(step, -1);
8203       break;
8204
8205     case SCREEN_CTRL_ID_NEXT_PLAYER:
8206       HandleSetupScreen_Input_Player(step, +1);
8207       break;
8208
8209     case SCREEN_CTRL_ID_SCROLL_UP:
8210       if (game_status == GAME_MODE_LEVELS)
8211         HandleChooseLevelSet(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
8212       else if (game_status == GAME_MODE_LEVELNR)
8213         HandleChooseLevelNr(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
8214       else if (game_status == GAME_MODE_SETUP)
8215         HandleSetupScreen(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
8216       else if (game_status == GAME_MODE_INFO)
8217         HandleInfoScreen(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
8218       break;
8219
8220     case SCREEN_CTRL_ID_SCROLL_DOWN:
8221       if (game_status == GAME_MODE_LEVELS)
8222         HandleChooseLevelSet(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
8223       else if (game_status == GAME_MODE_LEVELNR)
8224         HandleChooseLevelNr(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
8225       else if (game_status == GAME_MODE_SETUP)
8226         HandleSetupScreen(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
8227       else if (game_status == GAME_MODE_INFO)
8228         HandleInfoScreen(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
8229       break;
8230
8231     case SCREEN_CTRL_ID_SCROLL_VERTICAL:
8232       if (game_status == GAME_MODE_LEVELS)
8233         HandleChooseLevelSet(0,0,999,gi->event.item_position,MB_MENU_INITIALIZE);
8234       else if (game_status == GAME_MODE_LEVELNR)
8235         HandleChooseLevelNr(0,0,999,gi->event.item_position,MB_MENU_INITIALIZE);
8236       else if (game_status == GAME_MODE_SETUP)
8237         HandleSetupScreen(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
8238       else if (game_status == GAME_MODE_INFO)
8239         HandleInfoScreen(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
8240       break;
8241
8242     default:
8243       break;
8244   }
8245 }