added creating engine snapshots when using mouse click events
[rocksndiamonds.git] / src / screens.c
index b4f2e5fc8d46003189d0d2abf4c0435be3226116..6838dc9540ab8b3ec1e695091e7690c12a32eee8 100644 (file)
@@ -4,7 +4,7 @@
 // (c) 1995-2014 by Artsoft Entertainment
 //                         Holger Schemel
 //                 info@artsoft.org
-//                 http://www.artsoft.org/
+//                 https://www.artsoft.org/
 // ----------------------------------------------------------------------------
 // screens.c
 // ============================================================================
 // (should also match corresponding entries in src/conf_gfx.c)
 #define SETUP_MODE_MAIN                        0
 #define SETUP_MODE_GAME                        1
-#define SETUP_MODE_EDITOR              2
-#define SETUP_MODE_GRAPHICS            3
-#define SETUP_MODE_SOUND               4
-#define SETUP_MODE_ARTWORK             5
-#define SETUP_MODE_INPUT               6
-#define SETUP_MODE_TOUCH               7
-#define SETUP_MODE_SHORTCUTS           8
-#define SETUP_MODE_SHORTCUTS_1         9
-#define SETUP_MODE_SHORTCUTS_2         10
-#define SETUP_MODE_SHORTCUTS_3         11
-#define SETUP_MODE_SHORTCUTS_4         12
-#define SETUP_MODE_SHORTCUTS_5         13
+#define SETUP_MODE_ENGINES             2
+#define SETUP_MODE_EDITOR              3
+#define SETUP_MODE_GRAPHICS            4
+#define SETUP_MODE_SOUND               5
+#define SETUP_MODE_ARTWORK             6
+#define SETUP_MODE_INPUT               7
+#define SETUP_MODE_TOUCH               8
+#define SETUP_MODE_SHORTCUTS           9
+#define SETUP_MODE_SHORTCUTS_1         10
+#define SETUP_MODE_SHORTCUTS_2         11
+#define SETUP_MODE_SHORTCUTS_3         12
+#define SETUP_MODE_SHORTCUTS_4         13
+#define SETUP_MODE_SHORTCUTS_5         14
 
 // sub-screens on the setup screen (generic)
-#define SETUP_MODE_CHOOSE_ARTWORK      14
-#define SETUP_MODE_CHOOSE_OTHER                15
+#define SETUP_MODE_CHOOSE_ARTWORK      15
+#define SETUP_MODE_CHOOSE_OTHER                16
 
 // sub-screens on the setup screen (specific)
-#define SETUP_MODE_CHOOSE_GAME_SPEED   16
-#define SETUP_MODE_CHOOSE_SCROLL_DELAY 17
-#define SETUP_MODE_CHOOSE_SNAPSHOT_MODE        18
-#define SETUP_MODE_CHOOSE_WINDOW_SIZE  19
-#define SETUP_MODE_CHOOSE_SCALING_TYPE 20
-#define SETUP_MODE_CHOOSE_RENDERING    21
-#define SETUP_MODE_CHOOSE_VSYNC                22
-#define SETUP_MODE_CHOOSE_GRAPHICS     23
-#define SETUP_MODE_CHOOSE_SOUNDS       24
-#define SETUP_MODE_CHOOSE_MUSIC                25
-#define SETUP_MODE_CHOOSE_VOLUME_SIMPLE        26
-#define SETUP_MODE_CHOOSE_VOLUME_LOOPS 27
-#define SETUP_MODE_CHOOSE_VOLUME_MUSIC 28
-#define SETUP_MODE_CHOOSE_TOUCH_CONTROL        29
-#define SETUP_MODE_CHOOSE_MOVE_DISTANCE        30
-#define SETUP_MODE_CHOOSE_DROP_DISTANCE        31
-#define SETUP_MODE_CHOOSE_TRANSPARENCY 32
-#define SETUP_MODE_CHOOSE_GRID_XSIZE_0 33
-#define SETUP_MODE_CHOOSE_GRID_YSIZE_0 34
-#define SETUP_MODE_CHOOSE_GRID_XSIZE_1 35
-#define SETUP_MODE_CHOOSE_GRID_YSIZE_1 36
-#define SETUP_MODE_CONFIG_VIRT_BUTTONS 37
-
-#define MAX_SETUP_MODES                        38
+#define SETUP_MODE_CHOOSE_GAME_SPEED   17
+#define SETUP_MODE_CHOOSE_SCROLL_DELAY 18
+#define SETUP_MODE_CHOOSE_SNAPSHOT_MODE        19
+#define SETUP_MODE_CHOOSE_WINDOW_SIZE  20
+#define SETUP_MODE_CHOOSE_SCALING_TYPE 21
+#define SETUP_MODE_CHOOSE_RENDERING    22
+#define SETUP_MODE_CHOOSE_VSYNC                23
+#define SETUP_MODE_CHOOSE_GRAPHICS     24
+#define SETUP_MODE_CHOOSE_SOUNDS       25
+#define SETUP_MODE_CHOOSE_MUSIC                26
+#define SETUP_MODE_CHOOSE_VOLUME_SIMPLE        27
+#define SETUP_MODE_CHOOSE_VOLUME_LOOPS 28
+#define SETUP_MODE_CHOOSE_VOLUME_MUSIC 29
+#define SETUP_MODE_CHOOSE_TOUCH_CONTROL        30
+#define SETUP_MODE_CHOOSE_MOVE_DISTANCE        31
+#define SETUP_MODE_CHOOSE_DROP_DISTANCE        32
+#define SETUP_MODE_CHOOSE_TRANSPARENCY 33
+#define SETUP_MODE_CHOOSE_GRID_XSIZE_0 34
+#define SETUP_MODE_CHOOSE_GRID_YSIZE_0 35
+#define SETUP_MODE_CHOOSE_GRID_XSIZE_1 36
+#define SETUP_MODE_CHOOSE_GRID_YSIZE_1 37
+#define SETUP_MODE_CONFIG_VIRT_BUTTONS 38
+
+#define MAX_SETUP_MODES                        39
 
 #define MAX_MENU_MODES                 MAX(MAX_INFO_MODES, MAX_SETUP_MODES)
 
 // setup screen titles
 #define STR_SETUP_MAIN                 "Setup"
 #define STR_SETUP_GAME                 "Game & Menu"
+#define STR_SETUP_ENGINES              "Game Engines"
 #define STR_SETUP_EDITOR               "Editor"
 #define STR_SETUP_GRAPHICS             "Graphics"
 #define STR_SETUP_SOUND                        "Sound & Music"
 #define SCREEN_CTRL_ID_NEXT_PLAYER     6
 #define SCREEN_CTRL_ID_INSERT_SOLUTION 7
 #define SCREEN_CTRL_ID_PLAY_SOLUTION   8
-#define SCREEN_CTRL_ID_TOUCH_PREV_PAGE 9
-#define SCREEN_CTRL_ID_TOUCH_NEXT_PAGE 10
-#define SCREEN_CTRL_ID_TOUCH_PREV_PAGE2        11
-#define SCREEN_CTRL_ID_TOUCH_NEXT_PAGE2        12
-#define SCREEN_CTRL_ID_SCROLL_UP       13
-#define SCREEN_CTRL_ID_SCROLL_DOWN     14
-#define SCREEN_CTRL_ID_SCROLL_VERTICAL 15
-#define SCREEN_CTRL_ID_NETWORK_SERVER  16
-
-#define NUM_SCREEN_GADGETS             17
-
-#define NUM_SCREEN_MENUBUTTONS         13
+#define SCREEN_CTRL_ID_SWITCH_ECS_AGA  9
+#define SCREEN_CTRL_ID_TOUCH_PREV_PAGE 10
+#define SCREEN_CTRL_ID_TOUCH_NEXT_PAGE 11
+#define SCREEN_CTRL_ID_TOUCH_PREV_PAGE2        12
+#define SCREEN_CTRL_ID_TOUCH_NEXT_PAGE2        13
+#define SCREEN_CTRL_ID_SCROLL_UP       14
+#define SCREEN_CTRL_ID_SCROLL_DOWN     15
+#define SCREEN_CTRL_ID_SCROLL_VERTICAL 16
+#define SCREEN_CTRL_ID_NETWORK_SERVER  17
+
+#define NUM_SCREEN_GADGETS             18
+
+#define NUM_SCREEN_MENUBUTTONS         14
 #define NUM_SCREEN_SCROLLBUTTONS       2
 #define NUM_SCREEN_SCROLLBARS          1
 #define NUM_SCREEN_TEXTINPUT           1
@@ -238,6 +241,7 @@ static void CustomizeKeyboard(int);
 static void ConfigureJoystick(int);
 static void ConfigureVirtualButtons(void);
 static void execSetupGame(void);
+static void execSetupEngines(void);
 static void execSetupGraphics(void);
 static void execSetupSound(void);
 static void execSetupTouch(void);
@@ -924,10 +928,10 @@ static int getTitleSound(struct TitleControlInfo *tci)
   int sound_local = base + nr;
 
 #if 0
-  printf("::: %d, %d, %d: %d ['%s'], %d ['%s']\n",
-        nr, initial, is_image,
-        sound_global, getSoundListEntry(sound_global)->filename,
-        sound_local, getSoundListEntry(sound_local)->filename);
+  Debug("screens:getTitleSound", "%d, %d, %d: %d ['%s'], %d ['%s']",
+       nr, initial, is_image,
+       sound_global, getSoundListEntry(sound_global)->filename,
+       sound_local, getSoundListEntry(sound_local)->filename);
 #endif
 
   if (!strEqual(getSoundListEntry(sound_local)->filename, UNDEFINED_FILENAME))
@@ -954,10 +958,10 @@ static int getTitleMusic(struct TitleControlInfo *tci)
   int music_local = base + nr;
 
 #if 0
-  printf("::: %d, %d, %d: %d ['%s'], %d ['%s']\n",
-        nr, initial, is_image,
-        music_global, getMusicListEntry(music_global)->filename,
-        music_local, getMusicListEntry(music_local)->filename);
+  Debug("screens:getTitleMusic", "%d, %d, %d: %d ['%s'], %d ['%s']",
+       nr, initial, is_image,
+       music_global, getMusicListEntry(music_global)->filename,
+       music_local, getMusicListEntry(music_local)->filename);
 #endif
 
   if (!strEqual(getMusicListEntry(music_local)->filename, UNDEFINED_FILENAME))
@@ -1322,10 +1326,11 @@ static boolean insideTextPosRect(struct TextPosInfo *rect, int x, int y)
   int rect_y = ALIGNED_TEXT_YPOS(rect);
 
 #if 0
-  printf("::: insideTextPosRect: (%d, %d), (%d, %d) [%d, %d] (%d, %d) => %d\n",
-        x, y, rect_x, rect_y, rect->x, rect->y, rect->width, rect->height,
-        (x >= rect_x && x < rect_x + rect->width &&
-         y >= rect_y && y < rect_y + rect->height));
+  Debug("screens:insideTextPosRect",
+       "(%d, %d), (%d, %d) [%d, %d] (%d, %d) => %d",
+       x, y, rect_x, rect_y, rect->x, rect->y, rect->width, rect->height,
+       (x >= rect_x && x < rect_x + rect->width &&
+        y >= rect_y && y < rect_y + rect->height));
 #endif
 
   return (x >= rect_x && x < rect_x + rect->width &&
@@ -1576,10 +1581,6 @@ void DrawMainMenu(void)
     return;
   }
 
-  // needed if last screen was the setup screen and fullscreen state changed
-  // (moved to "execSetupGraphics()" to change fullscreen state directly)
-  // ToggleFullscreenOrChangeWindowScalingIfNeeded();
-
   // leveldir_current may be invalid (level group, parent link)
   if (!validLevelSeries(leveldir_current))
     leveldir_current = getFirstValidTreeInfoEntry(leveldir_last_valid);
@@ -2424,6 +2425,9 @@ static void HandleMenuScreen(int mx, int my, int dx, int dy, int button,
   static int num_page_entries_all_last[NUM_SPECIAL_GFX_ARGS][MAX_MENU_MODES];
   static int choice_stores[NUM_SPECIAL_GFX_ARGS][MAX_MENU_MODES];
   static int first_entry_stores[NUM_SPECIAL_GFX_ARGS][MAX_MENU_MODES];
+  boolean has_scrollbar = screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->mapped;
+  int mx_scrollbar = screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x;
+  int mx_right_border = (has_scrollbar ? mx_scrollbar : SX + SXSIZE);
   int *num_page_entries_last = num_page_entries_all_last[game_status];
   int *choice_store = choice_stores[game_status];
   int *first_entry_store = first_entry_stores[game_status];
@@ -2645,7 +2649,7 @@ static void HandleMenuScreen(int mx, int my, int dx, int dy, int button,
 
   if (!anyScrollbarGadgetActive() &&
       IN_VIS_MENU(x, y) &&
-      mx < screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x &&
+      mx < mx_right_border &&
       y >= 0 && y < num_page_entries)
   {
     if (button)
@@ -3677,7 +3681,9 @@ static void DrawInfoScreen_Version(void)
   }
 
   DrawTextF(xstart1, ystart, font_head, "Platform");
-  DrawTextF(xstart2, ystart, font_text, PLATFORM_STRING);
+  DrawTextF(xstart2, ystart, font_text, "%s (%s)",
+           PLATFORM_STRING,
+           PLATFORM_XX_BIT_STRING);
   ystart += ystep_line;
 
   DrawTextF(xstart1, ystart, font_head, "Target");
@@ -3686,6 +3692,10 @@ static void DrawInfoScreen_Version(void)
 
   DrawTextF(xstart1, ystart, font_head, "Source date");
   DrawTextF(xstart2, ystart, font_text, getSourceDateString());
+  ystart += ystep_line;
+
+  DrawTextF(xstart1, ystart, font_head, "Commit hash");
+  DrawTextF(xstart2, ystart, font_text, getSourceHashString());
   ystart += ystep_para;
 
   DrawTextF(xstart1, ystart, font_head, "Library");
@@ -3755,16 +3765,26 @@ static void DrawInfoScreen_Version(void)
   DrawTextF(xstart3, ystart, font_head, "Used");
   ystart += ystep_head;
 
-  driver_name = getStringCopyNStatic(SDL_GetVideoDriver(0), driver_name_len);
+  driver_name =
+    getStringCopyNStatic(SDLGetRendererName(), driver_name_len);
+
+  DrawTextF(xstart1, ystart, font_text, "Render Driver");
+  DrawTextF(xstart2, ystart, font_text, "%s", setup.system.sdl_renderdriver);
+  DrawTextF(xstart3, ystart, font_text, "%s", driver_name);
+  ystart += ystep_line;
+
+  driver_name =
+    getStringCopyNStatic(SDL_GetCurrentVideoDriver(), driver_name_len);
 
-  DrawTextF(xstart1, ystart, font_text, "SDL_VideoDriver");
+  DrawTextF(xstart1, ystart, font_text, "Video Driver");
   DrawTextF(xstart2, ystart, font_text, "%s", setup.system.sdl_videodriver);
   DrawTextF(xstart3, ystart, font_text, "%s", driver_name);
   ystart += ystep_line;
 
-  driver_name = getStringCopyNStatic(SDL_GetAudioDriver(0), driver_name_len);
+  driver_name =
+    getStringCopyNStatic(SDL_GetCurrentAudioDriver(), driver_name_len);
 
-  DrawTextF(xstart1, ystart, font_text, "SDL_AudioDriver");
+  DrawTextF(xstart1, ystart, font_text, "Audio Driver");
   DrawTextF(xstart2, ystart, font_text, "%s", setup.system.sdl_audiodriver);
   DrawTextF(xstart3, ystart, font_text, "%s", driver_name);
 
@@ -4199,6 +4219,9 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
                             TreeInfo **ti_ptr)
 {
   TreeInfo *ti = *ti_ptr;
+  boolean has_scrollbar = screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->mapped;
+  int mx_scrollbar = screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x;
+  int mx_right_border = (has_scrollbar ? mx_scrollbar : SX + SXSIZE);
   int x = 0;
   int y = ti->cl_cursor;
   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
@@ -4393,7 +4416,7 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
 
   if (!anyScrollbarGadgetActive() &&
       IN_VIS_MENU(x, y) &&
-      mx < screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x &&
+      mx < mx_right_border &&
       y >= 0 && y < num_page_entries)
   {
     if (button)
@@ -5016,6 +5039,13 @@ static void execSetupChooseSnapshotMode(void)
   DrawSetupScreen();
 }
 
+static void execSetupEngines(void)
+{
+  setup_mode = SETUP_MODE_ENGINES;
+
+  DrawSetupScreen();
+}
+
 static void execSetupEditor(void)
 {
   setup_mode = SETUP_MODE_EDITOR;
@@ -5273,13 +5303,21 @@ static void execSetupGraphics(void)
   if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE)
     execSetupGraphics_setWindowSizes(FALSE);
 
+  // update "setup.vsync_mode" from list selection
+  // (in this case, vsync mode was changed on setup screen)
+  if (setup_mode == SETUP_MODE_CHOOSE_VSYNC)
+    execSetupGraphics_setVsyncModes(FALSE);
+
   // update list selection from "setup.window_scaling_percent"
   // (window scaling may have changed by resizing the window)
   execSetupGraphics_setWindowSizes(TRUE);
 
+  // update list selection from "setup.vsync_mode"
+  // (vsync_mode may have changed by re-creating the renderer)
+  execSetupGraphics_setVsyncModes(TRUE);
+
   execSetupGraphics_setScalingTypes();
   execSetupGraphics_setRenderingModes();
-  execSetupGraphics_setVsyncModes(FALSE);
 
   setup_mode = SETUP_MODE_GRAPHICS;
 
@@ -5290,7 +5328,7 @@ static void execSetupGraphics(void)
     ModifyGameSpeedIfNeeded();
 
   // window scaling may have changed at this point
-  ToggleFullscreenOrChangeWindowScalingIfNeeded();
+  ChangeWindowScalingIfNeeded();
 
   // window scaling quality may have changed at this point
   if (!strEqual(setup.window_scaling_quality, video.window_scaling_quality))
@@ -5299,8 +5337,23 @@ static void execSetupGraphics(void)
   // screen rendering mode may have changed at this point
   SDLSetScreenRenderingMode(setup.screen_rendering_mode);
 
+  int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
+  int video_vsync_mode = video.vsync_mode;
+
   // screen vsync mode may have changed at this point
-  SDLSetScreenVsyncMode(setup.vsync_mode);
+  ChangeVsyncModeIfNeeded();
+
+  // check if setting vsync mode to selected value failed
+  if (setup_vsync_mode != video_vsync_mode &&
+      setup_vsync_mode != video.vsync_mode)
+  {
+    // changing vsync mode to selected value failed -- reset displayed value
+    execSetupGraphics_setVsyncModes(TRUE);
+
+    Request("Setting VSync failed!", REQ_CONFIRM);
+
+    DrawSetupScreen();
+  }
 }
 
 static void execSetupChooseWindowSize(void)
@@ -5903,10 +5956,10 @@ static void execSetupTouch(void)
 static void execSetupArtwork(void)
 {
 #if 0
-  printf("::: '%s', '%s', '%s'\n",
-        artwork.gfx_current->subdir,
-        artwork.gfx_current->fullpath,
-        artwork.gfx_current->basepath);
+  Debug("screens:execSetupArtwork", "'%s', '%s', '%s'",
+       artwork.gfx_current->subdir,
+       artwork.gfx_current->fullpath,
+       artwork.gfx_current->basepath);
 #endif
 
   setup.graphics_set = artwork.gfx_current->identifier;
@@ -6193,6 +6246,7 @@ static struct
   { &setup.touch.grid_ysize[1],                &grid_size_text[1][1]           },
 
   { &setup.internal.menu_game,         execSetupGame                   },
+  { &setup.internal.menu_engines,      execSetupEngines                },
   { &setup.internal.menu_editor,       execSetupEditor                 },
   { &setup.internal.menu_graphics,     execSetupGraphics               },
   { &setup.internal.menu_sound,                execSetupSound                  },
@@ -6218,6 +6272,7 @@ void setHideRelatedSetupEntries(void)
 static struct TokenInfo setup_info_main[] =
 {
   { TYPE_ENTER_MENU,   execSetupGame,          STR_SETUP_GAME          },
+  { TYPE_ENTER_MENU,   execSetupEngines,       STR_SETUP_ENGINES       },
   { TYPE_ENTER_MENU,   execSetupEditor,        STR_SETUP_EDITOR        },
   { TYPE_ENTER_MENU,   execSetupGraphics,      STR_SETUP_GRAPHICS      },
   { TYPE_ENTER_MENU,   execSetupSound,         STR_SETUP_SOUND         },
@@ -6264,6 +6319,21 @@ static struct TokenInfo setup_info_game[] =
   { 0,                 NULL,                   NULL                    }
 };
 
+static struct TokenInfo setup_info_engines[] =
+{
+  { TYPE_HEADLINE,     NULL,                   "Emerald Mine"          },
+  { TYPE_SWITCH,       &setup.forced_scroll_delay, "Scroll Delay:"     },
+  { TYPE_ECS_AGA,      &setup.prefer_aga_graphics, "Amiga Graphics Chipset:" },
+  { TYPE_SWITCH,       &setup.prefer_lowpass_sounds,"Low-Pass Filter Sounds:" },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_HEADLINE,     NULL,                   "Supaplex"              },
+  { TYPE_SWITCH,       &setup.sp_show_border_elements, "Border Elements:" },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_LEAVE_MENU,   execSetupMain,          "Back"                  },
+
+  { 0,                 NULL,                   NULL                    }
+};
+
 static struct TokenInfo setup_info_editor[] =
 {
 #if 0
@@ -6320,8 +6390,6 @@ static struct TokenInfo setup_info_graphics[] =
   { TYPE_SWITCH,       &setup.quick_doors,     "Quick Menu Doors:"     },
   { TYPE_SWITCH,       &setup.show_titlescreen,"Show Title Screens:"   },
   { TYPE_SWITCH,       &setup.toons,           "Show Menu Animations:" },
-  { TYPE_ECS_AGA,      &setup.prefer_aga_graphics,"EMC graphics preference:" },
-  { TYPE_SWITCH, &setup.sp_show_border_elements,"Supaplex Border Elements:" },
   { TYPE_SWITCH,       &setup.small_game_graphics, "Small Game Graphics:" },
   { TYPE_EMPTY,                NULL,                   ""                      },
   { TYPE_LEAVE_MENU,   execSetupMain,          "Back"                  },
@@ -6809,7 +6877,7 @@ static void changeSetupValue(int screen_pos, int setup_info_pos_raw, int dx)
 
   // fullscreen state may have changed at this point
   if (si->value == &setup.fullscreen)
-    ToggleFullscreenOrChangeWindowScalingIfNeeded();
+    ToggleFullscreenIfNeeded();
 
   // network mode may have changed at this point
   if (si->value == &setup.network_mode)
@@ -6892,6 +6960,11 @@ static void DrawSetupScreen_Generic(void)
     setup_info = setup_info_game;
     title_string = STR_SETUP_GAME;
   }
+  else if (setup_mode == SETUP_MODE_ENGINES)
+  {
+    setup_info = setup_info_engines;
+    title_string = STR_SETUP_ENGINES;
+  }
   else if (setup_mode == SETUP_MODE_EDITOR)
   {
     setup_info = setup_info_editor;
@@ -7537,9 +7610,9 @@ static boolean ConfigureJoystickMapButtonsAndAxes(SDL_Joystick *joystick)
 
 #if DEBUG_JOYSTICKS
   // print info about the joystick we are watching
-  Error(ERR_DEBUG, "watching joystick %d: (%s)\n",
+  Debug("joystick", "watching joystick %d: (%s)",
        SDL_JoystickInstanceID(joystick), name);
-  Error(ERR_DEBUG, "joystick has %d axes, %d hats, %d balls, and %d buttons\n",
+  Debug("joystick", "joystick has %d axes, %d hats, %d balls, and %d buttons",
        SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick),
        SDL_JoystickNumBalls(joystick), SDL_JoystickNumButtons(joystick));
 #endif
@@ -7796,7 +7869,7 @@ static boolean ConfigureJoystickMapButtonsAndAxes(SDL_Joystick *joystick)
   if (success)
   {
 #if DEBUG_JOYSTICKS
-    Error(ERR_DEBUG, "New game controller mapping:\n\n%s\n\n", mapping);
+    Debug("joystick", "New game controller mapping:\n\n%s\n\n", mapping);
 #endif
 
     // activate mapping for this game
@@ -7955,6 +8028,8 @@ static boolean ConfigureVirtualButtonsMain(void)
 
   overlay.grid_button_highlight = grid_button[step_nr];
 
+  UnmapAllGadgets();
+
   FadeSetEnterMenu();
   FadeOut(REDRAW_FIELD);
 
@@ -8369,7 +8444,7 @@ void HandleGameActions(void)
   GameActions();       // main game loop
 
   if (tape.auto_play && !tape.playing)
-    AutoPlayTape();    // continue automatically playing next tape
+    AutoPlayTapes();   // continue automatically playing next tape
 }
 
 
@@ -8379,6 +8454,7 @@ static struct
 {
   int gfx_unpressed, gfx_pressed;
   struct MenuPosInfo *pos;
+  boolean *check_value;
   int gadget_id;
   int screen_mask;
   unsigned int event_mask;
@@ -8388,7 +8464,7 @@ static struct
 {
   {
     IMG_MENU_BUTTON_PREV_LEVEL, IMG_MENU_BUTTON_PREV_LEVEL_ACTIVE,
-    &menu.main.button.prev_level,
+    &menu.main.button.prev_level, NULL,
     SCREEN_CTRL_ID_PREV_LEVEL,
     SCREEN_MASK_MAIN,
     GD_EVENT_PRESSED | GD_EVENT_REPEATED,
@@ -8396,7 +8472,7 @@ static struct
   },
   {
     IMG_MENU_BUTTON_NEXT_LEVEL, IMG_MENU_BUTTON_NEXT_LEVEL_ACTIVE,
-    &menu.main.button.next_level,
+    &menu.main.button.next_level, NULL,
     SCREEN_CTRL_ID_NEXT_LEVEL,
     SCREEN_MASK_MAIN,
     GD_EVENT_PRESSED | GD_EVENT_REPEATED,
@@ -8404,7 +8480,7 @@ static struct
   },
   {
     IMG_MENU_BUTTON_FIRST_LEVEL, IMG_MENU_BUTTON_FIRST_LEVEL_ACTIVE,
-    &menu.main.button.first_level,
+    &menu.main.button.first_level, NULL,
     SCREEN_CTRL_ID_FIRST_LEVEL,
     SCREEN_MASK_MAIN,
     GD_EVENT_RELEASED,
@@ -8412,7 +8488,7 @@ static struct
   },
   {
     IMG_MENU_BUTTON_LAST_LEVEL, IMG_MENU_BUTTON_LAST_LEVEL_ACTIVE,
-    &menu.main.button.last_level,
+    &menu.main.button.last_level, NULL,
     SCREEN_CTRL_ID_LAST_LEVEL,
     SCREEN_MASK_MAIN,
     GD_EVENT_RELEASED,
@@ -8420,7 +8496,7 @@ static struct
   },
   {
     IMG_MENU_BUTTON_LEVEL_NUMBER, IMG_MENU_BUTTON_LEVEL_NUMBER_ACTIVE,
-    &menu.main.button.level_number,
+    &menu.main.button.level_number, NULL,
     SCREEN_CTRL_ID_LEVEL_NUMBER,
     SCREEN_MASK_MAIN,
     GD_EVENT_RELEASED,
@@ -8428,7 +8504,7 @@ static struct
   },
   {
     IMG_MENU_BUTTON_LEFT, IMG_MENU_BUTTON_LEFT_ACTIVE,
-    &menu.setup.button.prev_player,
+    &menu.setup.button.prev_player, NULL,
     SCREEN_CTRL_ID_PREV_PLAYER,
     SCREEN_MASK_INPUT,
     GD_EVENT_PRESSED | GD_EVENT_REPEATED,
@@ -8436,7 +8512,7 @@ static struct
   },
   {
     IMG_MENU_BUTTON_RIGHT, IMG_MENU_BUTTON_RIGHT_ACTIVE,
-    &menu.setup.button.next_player,
+    &menu.setup.button.next_player, NULL,
     SCREEN_CTRL_ID_NEXT_PLAYER,
     SCREEN_MASK_INPUT,
     GD_EVENT_PRESSED | GD_EVENT_REPEATED,
@@ -8444,7 +8520,7 @@ static struct
   },
   {
     IMG_MENU_BUTTON_INSERT_SOLUTION, IMG_MENU_BUTTON_INSERT_SOLUTION_ACTIVE,
-    &menu.main.button.insert_solution,
+    &menu.main.button.insert_solution, NULL,
     SCREEN_CTRL_ID_INSERT_SOLUTION,
     SCREEN_MASK_MAIN_HAS_SOLUTION,
     GD_EVENT_RELEASED,
@@ -8452,15 +8528,23 @@ static struct
   },
   {
     IMG_MENU_BUTTON_PLAY_SOLUTION, IMG_MENU_BUTTON_PLAY_SOLUTION_ACTIVE,
-    &menu.main.button.play_solution,
+    &menu.main.button.play_solution, NULL,
     SCREEN_CTRL_ID_PLAY_SOLUTION,
     SCREEN_MASK_MAIN_HAS_SOLUTION,
     GD_EVENT_RELEASED,
     FALSE, "play solution tape"
   },
+  {
+    IMG_MENU_BUTTON_SWITCH_ECS_AGA, IMG_MENU_BUTTON_SWITCH_ECS_AGA_ACTIVE,
+    &menu.main.button.switch_ecs_aga, &setup.prefer_aga_graphics,
+    SCREEN_CTRL_ID_SWITCH_ECS_AGA,
+    SCREEN_MASK_MAIN,
+    GD_EVENT_RELEASED | GD_EVENT_OFF_BORDERS,
+    FALSE, "switch ECS/AGA chipset"
+  },
   {
     IMG_MENU_BUTTON_TOUCH_BACK, IMG_MENU_BUTTON_TOUCH_BACK,
-    &menu.setup.button.touch_back,
+    &menu.setup.button.touch_back, NULL,
     SCREEN_CTRL_ID_TOUCH_PREV_PAGE,
     SCREEN_MASK_TOUCH,
     GD_EVENT_RELEASED,
@@ -8468,7 +8552,7 @@ static struct
   },
   {
     IMG_MENU_BUTTON_TOUCH_NEXT, IMG_MENU_BUTTON_TOUCH_NEXT,
-    &menu.setup.button.touch_next,
+    &menu.setup.button.touch_next, NULL,
     SCREEN_CTRL_ID_TOUCH_NEXT_PAGE,
     SCREEN_MASK_TOUCH,
     GD_EVENT_RELEASED,
@@ -8476,7 +8560,7 @@ static struct
   },
   {
     IMG_MENU_BUTTON_TOUCH_BACK2, IMG_MENU_BUTTON_TOUCH_BACK2,
-    &menu.setup.button.touch_back2,
+    &menu.setup.button.touch_back2, NULL,
     SCREEN_CTRL_ID_TOUCH_PREV_PAGE2,
     SCREEN_MASK_TOUCH2,
     GD_EVENT_RELEASED,
@@ -8484,7 +8568,7 @@ static struct
   },
   {
     IMG_MENU_BUTTON_TOUCH_NEXT2, IMG_MENU_BUTTON_TOUCH_NEXT2,
-    &menu.setup.button.touch_next2,
+    &menu.setup.button.touch_next2, NULL,
     SCREEN_CTRL_ID_TOUCH_NEXT_PAGE2,
     SCREEN_MASK_TOUCH2,
     GD_EVENT_RELEASED,
@@ -8564,11 +8648,15 @@ static void CreateScreenMenubuttons(void)
   {
     struct MenuPosInfo *pos = menubutton_info[i].pos;
     boolean is_touch_button = menubutton_info[i].is_touch_button;
+    boolean is_check_button = menubutton_info[i].check_value != NULL;
     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
     int gfx_unpressed, gfx_pressed;
     int x, y, width, height;
     int gd_x1, gd_x2, gd_y1, gd_y2;
+    int gd_x1a, gd_x2a, gd_y1a, gd_y2a;
     int id = menubutton_info[i].gadget_id;
+    int type = GD_TYPE_NORMAL_BUTTON;
+    boolean checked = FALSE;
 
     event_mask = menubutton_info[i].event_mask;
 
@@ -8586,6 +8674,10 @@ static void CreateScreenMenubuttons(void)
     gd_y1 = graphic_info[gfx_unpressed].src_y;
     gd_x2 = graphic_info[gfx_pressed].src_x;
     gd_y2 = graphic_info[gfx_pressed].src_y;
+    gd_x1a = gd_x1;
+    gd_y1a = gd_y1;
+    gd_x2a = gd_x2;
+    gd_y2a = gd_y2;
 
     if (is_touch_button)
     {
@@ -8593,6 +8685,17 @@ static void CreateScreenMenubuttons(void)
       gd_y2 += graphic_info[gfx_pressed].pressed_yoffset;
     }
 
+    if (is_check_button)
+    {
+      gd_x1a += graphic_info[gfx_unpressed].active_xoffset;
+      gd_y1a += graphic_info[gfx_unpressed].active_yoffset;
+      gd_x2a += graphic_info[gfx_pressed].active_xoffset;
+      gd_y2a += graphic_info[gfx_pressed].active_yoffset;
+
+      type = GD_TYPE_CHECK_BUTTON;
+      checked = *menubutton_info[i].check_value;
+    }
+
     gi = CreateGadget(GDI_CUSTOM_ID, id,
                      GDI_CUSTOM_TYPE_ID, i,
                      GDI_IMAGE_ID, gfx_unpressed,
@@ -8601,10 +8704,13 @@ static void CreateScreenMenubuttons(void)
                      GDI_Y, y,
                      GDI_WIDTH, width,
                      GDI_HEIGHT, height,
-                     GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
+                     GDI_TYPE, type,
                      GDI_STATE, GD_BUTTON_UNPRESSED,
+                     GDI_CHECKED, checked,
                      GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
                      GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
+                      GDI_ALT_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1a, gd_y1a,
+                      GDI_ALT_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2a, gd_y2a,
                      GDI_DIRECT_DRAW, FALSE,
                      GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
                      GDI_EVENT_MASK, event_mask,
@@ -8612,7 +8718,7 @@ static void CreateScreenMenubuttons(void)
                      GDI_END);
 
     if (gi == NULL)
-      Error(ERR_EXIT, "cannot create gadget");
+      Fail("cannot create gadget");
 
     screen_gadget[id] = gi;
   }
@@ -8680,7 +8786,7 @@ static void CreateScreenScrollbuttons(void)
                      GDI_END);
 
     if (gi == NULL)
-      Error(ERR_EXIT, "cannot create gadget");
+      Fail("cannot create gadget");
 
     screen_gadget[id] = gi;
   }
@@ -8761,7 +8867,7 @@ static void CreateScreenScrollbars(void)
                      GDI_END);
 
     if (gi == NULL)
-      Error(ERR_EXIT, "cannot create gadget");
+      Fail("cannot create gadget");
 
     screen_gadget[id] = gi;
   }
@@ -8807,7 +8913,7 @@ static void CreateScreenTextInputGadgets(void)
                      GDI_END);
 
     if (gi == NULL)
-      Error(ERR_EXIT, "cannot create gadget");
+      Fail("cannot create gadget");
 
     screen_gadget[id] = gi;
   }
@@ -8934,6 +9040,11 @@ static void HandleScreenGadgets(struct GadgetInfo *gi)
       PlaySolutionTape();
       break;
 
+    case SCREEN_CTRL_ID_SWITCH_ECS_AGA:
+      setup.prefer_aga_graphics = !setup.prefer_aga_graphics;
+      DrawMainMenu();
+      break;
+
     case SCREEN_CTRL_ID_TOUCH_PREV_PAGE:
     case SCREEN_CTRL_ID_TOUCH_NEXT_PAGE:
     case SCREEN_CTRL_ID_TOUCH_PREV_PAGE2: