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