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