fixed compiler warning by adding missing variable initialization (finally)
[rocksndiamonds.git] / src / screens.c
index e6e471a2786c32321d9d6790bec8c8d26da15ef2..a5bc5ac1b7009f31f46d5831681f9705d7eb8d2e 100644 (file)
@@ -163,7 +163,7 @@ static void HandleScreenGadgets(struct GadgetInfo *);
 static void HandleSetupScreen_Generic(int, int, int, int, int);
 static void HandleSetupScreen_Input(int, int, int, int, int);
 static void CustomizeKeyboard(int);
-static void CalibrateJoystick(int);
+static void ConfigureJoystick(int);
 static void execSetupGame(void);
 static void execSetupGraphics(void);
 static void execSetupSound(void);
@@ -383,6 +383,7 @@ static struct
 {
   {    TOUCH_CONTROL_VIRTUAL_BUTTONS,  "Virtual Buttons"       },
   {    TOUCH_CONTROL_WIPE_GESTURES,    "Wipe Gestures"         },
+  {    TOUCH_CONTROL_FOLLOW_FINGER,    "Follow Finger"         },
 
   {    NULL,                           NULL                    },
 };
@@ -1184,6 +1185,17 @@ static boolean insideTextPosRect(struct TextPosInfo *rect, int x, int y)
          y >= rect_y && y < rect_y + rect->height);
 }
 
+static boolean insidePreviewRect(struct PreviewInfo *preview, int x, int y)
+{
+  int rect_width  = preview->xsize * preview->tile_size;
+  int rect_height = preview->ysize * preview->tile_size;
+  int rect_x = ALIGNED_XPOS(preview->x, rect_width,  preview->align);
+  int rect_y = ALIGNED_YPOS(preview->y, rect_height, preview->valign);
+
+  return (x >= rect_x && x < rect_x + rect_width &&
+         y >= rect_y && y < rect_y + rect_height);
+}
+
 static void AdjustScrollbar(int id, int items_max, int items_visible,
                            int item_position)
 {
@@ -1396,12 +1408,11 @@ void DrawMainMenu()
   /* do not fade out here -- function may continue and fade on editor screen */
 
   UnmapAllGadgets();
-  FadeSoundsAndMusic();
+  FadeMenuSoundsAndMusic();
 
   ExpireSoundLoops(FALSE);
 
   KeyboardAutoRepeatOn();
-  ActivateJoystick();
 
   audio.sound_deactivated = FALSE;
 
@@ -1440,8 +1451,6 @@ void DrawMainMenu()
 
   if (CheckTitleScreen(levelset_has_changed))
   {
-    game_status_last_screen = GAME_MODE_MAIN;
-
     SetGameStatus(GAME_MODE_TITLE);
 
     DrawTitleScreen();
@@ -1449,9 +1458,6 @@ void DrawMainMenu()
     return;
   }
 
-  /* needed if different viewport properties defined for menues */
-  ChangeViewportPropertiesIfNeeded();
-
   if (redraw_mask & REDRAW_ALL)
     fade_mask = REDRAW_ALL;
 
@@ -1460,7 +1466,10 @@ void DrawMainMenu()
 
   FadeOut(fade_mask);
 
-  SetDrawtoField(DRAW_BACKBUFFER);
+  /* needed if different viewport properties defined for menues */
+  ChangeViewportPropertiesIfNeeded();
+
+  SetDrawtoField(DRAW_TO_BACKBUFFER);
 
   /* level_nr may have been set to value over handicap with level editor */
   if (setup.handicap && level_nr > leveldir_current->handicap_level)
@@ -1494,8 +1503,7 @@ void DrawMainMenu()
     LoadTape(level_nr);
   DrawCompleteVideoDisplay();
 
-  PlayMenuSound();
-  PlayMenuMusic();
+  PlayMenuSoundsAndMusic();
 
   /* create gadgets for main menu screen */
   FreeScreenGadgets();
@@ -1590,22 +1598,25 @@ void HandleTitleScreen(int mx, int my, int dx, int dy, int button)
        /* switch game mode from title screen mode back to info screen mode */
        SetGameStatus(GAME_MODE_INFO);
 
+       /* store that last screen was info screen, not main menu screen */
+       game_status_last_screen = GAME_MODE_INFO;
+
        DrawInfoScreen_NotAvailable("Title screen information:",
                                    "No title screen for this level set.");
        return;
       }
 
-      FadeSoundsAndMusic();
+      FadeMenuSoundsAndMusic();
     }
 
     FadeOut(REDRAW_ALL);
 
-    /* only required to update logic for redrawing global border */
-    ClearField();
-
     /* title screens may have different window size */
     ChangeViewportPropertiesIfNeeded();
 
+    /* only required to update logic for redrawing global border */
+    ClearField();
+
     if (tci->is_image)
       DrawTitleScreenImage(tci->local_nr, tci->initial);
     else
@@ -1693,7 +1704,7 @@ void HandleTitleScreen(int mx, int my, int dx, int dy, int button)
     }
     else
     {
-      FadeSoundsAndMusic();
+      FadeMenuSoundsAndMusic();
 
       return_to_main_menu = TRUE;
     }
@@ -1807,6 +1818,10 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
       }
     }
 
+    /* check if level preview was clicked */
+    if (insidePreviewRect(&preview, mx - SX, my - SY))
+      pos = MAIN_CONTROL_GAME;
+
     // handle pressed/unpressed state for active/inactive menu buttons
     // (if pos != -1, "i" contains index position corresponding to "pos")
     if (button &&
@@ -1833,11 +1848,7 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
       pos = choice + dy;
   }
 
-  if (pos == MAIN_CONTROL_LEVELS && dx != 0 && button)
-  {
-    HandleMainMenu_SelectLevel(1, (dx < 0 ? -1 : +1), NO_DIRECT_LEVEL_SELECT);
-  }
-  else if (pos == MAIN_CONTROL_FIRST_LEVEL && !button)
+  if (pos == MAIN_CONTROL_FIRST_LEVEL && !button)
   {
     HandleMainMenu_SelectLevel(MAX_LEVELS, -1, NO_DIRECT_LEVEL_SELECT);
   }
@@ -1851,8 +1862,6 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
 
     SetGameStatus(GAME_MODE_LEVELNR);
 
-    ChangeViewportPropertiesIfNeeded();
-
     DrawChooseLevelNr();
   }
   else if (pos >= MAIN_CONTROL_NAME && pos <= MAIN_CONTROL_QUIT)
@@ -1868,6 +1877,12 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
 
        choice = pos;
       }
+      else if (dx != 0)
+      {
+       if (choice != MAIN_CONTROL_INFO &&
+           choice != MAIN_CONTROL_SETUP)
+         HandleMainMenu_SelectLevel(1, dx, NO_DIRECT_LEVEL_SELECT);
+      }
     }
     else
     {
@@ -1893,8 +1908,6 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
          if (setup.internal.choose_from_top_leveldir)
            gotoTopLevelDir();
 
-         ChangeViewportPropertiesIfNeeded();
-
          DrawChooseLevelSet();
        }
       }
@@ -1928,8 +1941,6 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
 
        info_mode = INFO_MODE_MAIN;
 
-       ChangeViewportPropertiesIfNeeded();
-
        DrawInfoScreen();
       }
       else if (pos == MAIN_CONTROL_GAME)
@@ -1944,8 +1955,6 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
 
        setup_mode = SETUP_MODE_MAIN;
 
-       ChangeViewportPropertiesIfNeeded();
-
        DrawSetupScreen();
       }
       else if (pos == MAIN_CONTROL_QUIT)
@@ -2152,7 +2161,7 @@ static void DrawInfoScreen_Main()
     fade_mask = REDRAW_ALL;
 
   UnmapAllGadgets();
-  FadeSoundsAndMusic();
+  FadeMenuSoundsAndMusic();
 
   FreeScreenGadgets();
   CreateScreenGadgets();
@@ -2164,8 +2173,11 @@ static void DrawInfoScreen_Main()
 
   FadeOut(fade_mask);
 
+  /* needed if different viewport properties defined for info screen */
   ChangeViewportPropertiesIfNeeded();
 
+  SetMainBackgroundImage(IMG_BACKGROUND_INFO);
+
   ClearField();
 
   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
@@ -2188,8 +2200,7 @@ static void DrawInfoScreen_Main()
 
   MapScreenGadgets(max_info_info);
 
-  PlayMenuSound();
-  PlayMenuMusic();
+  PlayMenuSoundsAndMusic();
 
   DrawMaskedBorder(fade_mask);
 
@@ -2439,6 +2450,27 @@ void HandleMenuScreen(int mx, int my, int dx, int dy, int button,
 
        choice = choice_store[mode] = first_entry + y;
       }
+      else if (dx < 0)
+      {
+       PlaySound(SND_MENU_ITEM_SELECTING);
+
+       for (i = 0; menu_info[i].type != 0; i++)
+       {
+         if (menu_info[i].type & TYPE_LEAVE_MENU)
+         {
+           void (*menu_callback_function)(void) = menu_info[i].value;
+
+           FadeSetLeaveMenu();
+
+           menu_callback_function();
+
+           /* absolutely needed because function changes 'menu_info'! */
+           break;
+         }
+       }
+
+       return;
+      }
     }
     else if (!(menu_info[first_entry + y].type & TYPE_GHOSTED))
     {
@@ -2667,8 +2699,6 @@ void DrawInfoScreen_HelpText(int element, int action, int direction, int ypos)
 
 void DrawInfoScreen_TitleScreen()
 {
-  game_status_last_screen = GAME_MODE_INFO;
-
   SetGameStatus(GAME_MODE_TITLE);
 
   DrawTitleScreen();
@@ -2743,7 +2773,7 @@ void HandleInfoScreen_Elements(int button)
 
     if (page >= num_pages)
     {
-      FadeSoundsAndMusic();
+      FadeMenuSoundsAndMusic();
 
       info_mode = INFO_MODE_MAIN;
       DrawInfoScreen();
@@ -2802,7 +2832,7 @@ void HandleInfoScreen_Music(int button)
 
     if (list == NULL)
     {
-      FadeSoundsAndMusic();
+      FadeMenuSoundsAndMusic();
 
       ClearField();
       DrawHeadline();
@@ -2821,7 +2851,7 @@ void HandleInfoScreen_Music(int button)
   {
     PlaySound(SND_MENU_ITEM_SELECTING);
 
-    FadeSoundsAndMusic();
+    FadeMenuSoundsAndMusic();
 
     info_mode = INFO_MODE_MAIN;
     DrawInfoScreen();
@@ -2842,7 +2872,7 @@ void HandleInfoScreen_Music(int button)
 
     if (list == NULL)
     {
-      FadeSoundsAndMusic();
+      FadeMenuSoundsAndMusic();
 
       info_mode = INFO_MODE_MAIN;
       DrawInfoScreen();
@@ -2850,7 +2880,7 @@ void HandleInfoScreen_Music(int button)
       return;
     }
 
-    FadeSoundsAndMusic();
+    FadeMenuSoundsAndMusic();
 
     if (list != music_file_info)
       FadeSetNextScreen();
@@ -3105,7 +3135,7 @@ void DrawInfoScreen_Credits()
 {
   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_CREDITS);
 
-  FadeSoundsAndMusic();
+  FadeMenuSoundsAndMusic();
 
   FadeOut(REDRAW_FIELD);
 
@@ -3146,7 +3176,7 @@ void HandleInfoScreen_Credits(int button)
 
     if (screen_nr >= num_screens)
     {
-      FadeSoundsAndMusic();
+      FadeMenuSoundsAndMusic();
 
       info_mode = INFO_MODE_MAIN;
       DrawInfoScreen();
@@ -3225,7 +3255,7 @@ void HandleInfoScreen_Program(int button)
   {
     PlaySound(SND_MENU_ITEM_SELECTING);
 
-    FadeSoundsAndMusic();
+    FadeMenuSoundsAndMusic();
 
     info_mode = INFO_MODE_MAIN;
     DrawInfoScreen();
@@ -3283,8 +3313,8 @@ void DrawInfoScreen_Version()
   DrawTextF(xstart2, ystart2, font_text, TARGET_STRING);
 
   ystart2 += ystep;
-  DrawTextF(xstart1, ystart2, font_header, "Compile time");
-  DrawTextF(xstart2, ystart2, font_text, getCompileDateString());
+  DrawTextF(xstart1, ystart2, font_header, "Source date");
+  DrawTextF(xstart2, ystart2, font_text, getSourceDateString());
 
   ystart2 += 3 * ystep;
   DrawTextF(xstart1, ystart2, font_header, "Library");
@@ -3400,7 +3430,7 @@ void HandleInfoScreen_Version(int button)
   {
     PlaySound(SND_MENU_ITEM_SELECTING);
 
-    FadeSoundsAndMusic();
+    FadeMenuSoundsAndMusic();
 
     info_mode = INFO_MODE_MAIN;
     DrawInfoScreen();
@@ -3488,7 +3518,7 @@ void HandleInfoScreen_LevelSet(int button)
   {
     PlaySound(SND_MENU_ITEM_SELECTING);
 
-    FadeSoundsAndMusic();
+    FadeMenuSoundsAndMusic();
 
     info_mode = INFO_MODE_MAIN;
     DrawInfoScreen();
@@ -3501,8 +3531,6 @@ void HandleInfoScreen_LevelSet(int button)
 
 static void DrawInfoScreen()
 {
-  SetMainBackgroundImage(IMG_BACKGROUND_INFO);
-
   if (info_mode == INFO_MODE_TITLE)
     DrawInfoScreen_TitleScreen();
   else if (info_mode == INFO_MODE_ELEMENTS)
@@ -3523,10 +3551,7 @@ static void DrawInfoScreen()
   if (info_mode != INFO_MODE_MAIN &&
       info_mode != INFO_MODE_TITLE &&
       info_mode != INFO_MODE_MUSIC)
-  {
-    PlayMenuSound();
-    PlayMenuMusic();
-  }
+    PlayMenuSoundsAndMusic();
 }
 
 void HandleInfoScreen(int mx, int my, int dx, int dy, int button)
@@ -3577,9 +3602,7 @@ void HandleTypeName(int newxpos, Key key)
 
     xpos = newxpos;
 
-#if defined(TARGET_SDL2)
-    SDL_StartTextInput();
-#endif
+    StartTextInput(startx, starty, pos->width, pos->height);
   }
   else if (is_valid_key_char && xpos < MAX_PLAYER_NAME_LEN)
   {
@@ -3626,9 +3649,7 @@ void HandleTypeName(int newxpos, Key key)
 
     DrawText(startx, starty, setup.player_name, font_nr);
 
-#if defined(TARGET_SDL2)
-    SDL_StopTextInput();
-#endif
+    StopTextInput();
   }
 }
 
@@ -3660,6 +3681,14 @@ static void DrawChooseTree(TreeInfo **ti_ptr)
 
   FadeOut(fade_mask);
 
+  /* needed if different viewport properties defined for choosing level (set) */
+  ChangeViewportPropertiesIfNeeded();
+
+  if (game_status == GAME_MODE_LEVELNR)
+    SetMainBackgroundImage(IMG_BACKGROUND_LEVELNR);
+  else if (game_status == GAME_MODE_LEVELS)
+    SetMainBackgroundImage(IMG_BACKGROUND_LEVELS);
+
   ClearField();
 
   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
@@ -3967,6 +3996,30 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
 
        ti->cl_cursor = y;
       }
+      else if (dx < 0)
+      {
+       if (game_status == GAME_MODE_SETUP)
+       {
+         if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED ||
+             setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY ||
+             setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
+           execSetupGame();
+         else if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE ||
+                  setup_mode == SETUP_MODE_CHOOSE_SCALING_TYPE ||
+                  setup_mode == SETUP_MODE_CHOOSE_RENDERING)
+           execSetupGraphics();
+         else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_SIMPLE ||
+                  setup_mode == SETUP_MODE_CHOOSE_VOLUME_LOOPS ||
+                  setup_mode == SETUP_MODE_CHOOSE_VOLUME_MUSIC)
+           execSetupSound();
+         else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL ||
+                  setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE ||
+                  setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE)
+           execSetupTouch();
+         else
+           execSetupArtwork();
+       }
+      }
     }
     else
     {
@@ -4052,14 +4105,11 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
 
 void DrawChooseLevelSet()
 {
-  FadeSoundsAndMusic();
-
-  SetMainBackgroundImage(IMG_BACKGROUND_LEVELS);
+  FadeMenuSoundsAndMusic();
 
   DrawChooseTree(&leveldir_current);
 
-  PlayMenuSound();
-  PlayMenuMusic();
+  PlayMenuSoundsAndMusic();
 }
 
 void HandleChooseLevelSet(int mx, int my, int dx, int dy, int button)
@@ -4071,7 +4121,7 @@ void DrawChooseLevelNr()
 {
   int i;
 
-  FadeSoundsAndMusic();
+  FadeMenuSoundsAndMusic();
 
   if (level_number != NULL)
   {
@@ -4091,13 +4141,13 @@ void DrawChooseLevelNr()
 
     ti->node_top = &level_number;
     ti->sort_priority = 10000 + value;
-    ti->color = (level.no_valid_file ? FC_BLUE :
+    ti->color = (level.no_level_file ? FC_BLUE :
                 LevelStats_getSolved(i) ? FC_GREEN :
                 LevelStats_getPlayed(i) ? FC_YELLOW : FC_RED);
 
-    sprintf(identifier, "%d", value);
-    sprintf(name, "%03d: %s", value,
-           (level.no_valid_file ? "(no file)" : level.name));
+    snprintf(identifier, sizeof(identifier), "%d", value);
+    snprintf(name, sizeof(name), "%03d: %s", value,
+            (level.no_level_file ? "(no file)" : level.name));
 
     setString(&ti->identifier, identifier);
     setString(&ti->name, name);
@@ -4117,12 +4167,9 @@ void DrawChooseLevelNr()
   if (level_number_current == NULL)
     level_number_current = level_number;
 
-  SetMainBackgroundImage(IMG_BACKGROUND_LEVELNR);
-
   DrawChooseTree(&level_number_current);
 
-  PlayMenuSound();
-  PlayMenuMusic();
+  PlayMenuSoundsAndMusic();
 }
 
 void HandleChooseLevelNr(int mx, int my, int dx, int dy, int button)
@@ -4134,18 +4181,14 @@ void DrawHallOfFame(int highlight_position)
 {
   int fade_mask = REDRAW_FIELD;
 
-  /* needed if different viewport properties defined for scores */
-  ChangeViewportPropertiesIfNeeded();
-
   if (CheckIfGlobalBorderHasChanged())
     fade_mask = REDRAW_ALL;
 
   UnmapAllGadgets();
-  FadeSoundsAndMusic();
+  FadeMenuSoundsAndMusic();
 
   /* (this is needed when called from GameEnd() after winning a game) */
   KeyboardAutoRepeatOn();
-  ActivateJoystick();
 
   /* (this is needed when called from GameEnd() after winning a game) */
   SetDrawDeactivationMask(REDRAW_NONE);
@@ -4153,13 +4196,17 @@ void DrawHallOfFame(int highlight_position)
 
   if (highlight_position < 0) 
     LoadScore(level_nr);
+  else
+    SetAnimStatus(GAME_MODE_PSEUDO_SCORESNEW);
 
   FadeSetEnterScreen();
 
   FadeOut(fade_mask);
 
-  PlayMenuSound();
-  PlayMenuMusic();
+  /* needed if different viewport properties defined for scores */
+  ChangeViewportPropertiesIfNeeded();
+
+  PlayMenuSoundsAndMusic();
 
   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
 
@@ -4189,9 +4236,10 @@ static void drawHallOfFameList(int first_entry, int highlight_position)
     int font_nr2 = (active ? FONT_TEXT_2_ACTIVE : FONT_TEXT_2);
     int font_nr3 = (active ? FONT_TEXT_3_ACTIVE : FONT_TEXT_3);
     int font_nr4 = (active ? FONT_TEXT_4_ACTIVE : FONT_TEXT_4);
+    int dxoff = getFontDrawOffsetX(font_nr1);
     int dx1 = 3 * getFontWidth(font_nr1);
     int dx2 = dx1 + getFontWidth(font_nr1);
-    int dx3 = SXSIZE - 2 * (mSX - SX) - 5 * getFontWidth(font_nr4);
+    int dx3 = SXSIZE - 2 * (mSX - SX + dxoff) - 5 * getFontWidth(font_nr4);
     int num_dots = (dx3 - dx2) / getFontWidth(font_nr3);
     int sy = mSY + 64 + i * 32;
 
@@ -5189,14 +5237,12 @@ static void execSetupChooseMusic()
   DrawSetupScreen();
 }
 
-#if !defined(PLATFORM_ANDROID)
 static void execSetupInput()
 {
   setup_mode = SETUP_MODE_INPUT;
 
   DrawSetupScreen();
 }
-#endif
 
 static void execSetupShortcuts()
 {
@@ -5260,12 +5306,8 @@ static struct TokenInfo setup_info_main[] =
   { TYPE_ENTER_MENU,   execSetupGraphics,      "Graphics"              },
   { TYPE_ENTER_MENU,   execSetupSound,         "Sound & Music"         },
   { TYPE_ENTER_MENU,   execSetupArtwork,       "Custom Artwork"        },
-#if !defined(PLATFORM_ANDROID)
   { TYPE_ENTER_MENU,   execSetupInput,         "Input Devices"         },
   { TYPE_ENTER_MENU,   execSetupTouch,         "Touch Controls"        },
-#else
-  { TYPE_ENTER_MENU,   execSetupTouch,         "Touch Controls"        },
-#endif
   { TYPE_ENTER_MENU,   execSetupShortcuts,     "Key Shortcuts"         },
   { TYPE_EMPTY,                NULL,                   ""                      },
   { TYPE_LEAVE_MENU,   execExitSetup,          "Exit"                  },
@@ -5278,9 +5320,10 @@ static struct TokenInfo setup_info_game[] =
 {
   { TYPE_SWITCH,       &setup.team_mode,       "Team-Mode (Multi-Player):" },
   { TYPE_YES_NO,       &setup.input_on_focus,  "Only Move Focussed Player:" },
+  { TYPE_SWITCH,       &setup.time_limit,      "Time Limit:"           },
   { TYPE_SWITCH,       &setup.handicap,        "Handicap:"             },
   { TYPE_SWITCH,       &setup.skip_levels,     "Skip Unsolved Levels:" },
-  { TYPE_SWITCH,       &setup.time_limit,      "Time Limit:"           },
+  { TYPE_SWITCH,       &setup.increment_levels,"Increment Solved Levels:" },
   { TYPE_SWITCH,       &setup.autorecord,      "Auto-Record Tapes:"    },
   { TYPE_ENTER_LIST,   execSetupChooseGameSpeed, "Game Speed:"         },
   { TYPE_STRING,       &game_speed_text,       ""                      },
@@ -5308,9 +5351,10 @@ static struct TokenInfo setup_info_editor[] =
   { TYPE_SWITCH,       &setup.editor.el_supaplex,      "Supaplex:"     },
   { TYPE_SWITCH,       &setup.editor.el_diamond_caves, "Diamond Caves II:" },
   { TYPE_SWITCH,       &setup.editor.el_dx_boulderdash,"DX-Boulderdash:" },
-#endif
   { TYPE_SWITCH,       &setup.editor.el_chars,         "Text Characters:" },
   { TYPE_SWITCH, &setup.editor.el_steel_chars, "Text Characters (Steel):" },
+#endif
+  { TYPE_SWITCH,       &setup.editor.el_classic,  "Classic Elements:" },
   { TYPE_SWITCH,       &setup.editor.el_custom,  "Custom & Group Elements:" },
 #if 0
   { TYPE_SWITCH,       &setup.editor.el_headlines,     "Headlines:"    },
@@ -5399,7 +5443,7 @@ static struct TokenInfo setup_info_input[] =
 {
   { TYPE_SWITCH,       NULL,                   "Player:"               },
   { TYPE_SWITCH,       NULL,                   "Device:"               },
-  { TYPE_ENTER_MENU,   NULL,                   ""                      },
+  { TYPE_SWITCH,       NULL,                   ""                      },
   { TYPE_EMPTY,                NULL,                   ""                      },
   { TYPE_EMPTY,                NULL,                   ""                      },
   { TYPE_EMPTY,                NULL,                   ""                      },
@@ -5416,6 +5460,16 @@ static struct TokenInfo setup_info_input[] =
 };
 
 static struct TokenInfo setup_info_touch[] =
+{
+  { TYPE_ENTER_LIST,   execSetupChooseTouchControls, "Touch Control Type:" },
+  { TYPE_STRING,       &touch_controls_text,   ""                      },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_LEAVE_MENU,   execSetupMain,          "Back"                  },
+
+  { 0,                 NULL,                   NULL                    }
+};
+
+static struct TokenInfo setup_info_touch_wipe_gestures[] =
 {
   { TYPE_ENTER_LIST,   execSetupChooseTouchControls, "Touch Control Type:" },
   { TYPE_STRING,       &touch_controls_text,   ""                      },
@@ -5762,7 +5816,7 @@ static void DrawSetupScreen_Generic()
     fade_mask = REDRAW_ALL;
 
   UnmapAllGadgets();
-  FadeSoundsAndMusic();
+  FadeMenuSoundsAndMusic();
 
   FreeScreenGadgets();
   CreateScreenGadgets();
@@ -5772,6 +5826,11 @@ static void DrawSetupScreen_Generic()
 
   FadeOut(fade_mask);
 
+  /* needed if different viewport properties defined for setup screen */
+  ChangeViewportPropertiesIfNeeded();
+
+  SetMainBackgroundImage(IMG_BACKGROUND_SETUP);
+
   ClearField();
 
   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
@@ -5810,6 +5869,9 @@ static void DrawSetupScreen_Generic()
   {
     setup_info = setup_info_touch;
     title_string = "Setup Touch Ctrls";
+
+    if (strEqual(setup.touch.control_type, TOUCH_CONTROL_WIPE_GESTURES))
+      setup_info = setup_info_touch_wipe_gestures;
   }
   else if (setup_mode == SETUP_MODE_SHORTCUTS)
   {
@@ -5886,9 +5948,6 @@ void DrawSetupScreen_Input()
 
   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Setup Input");
 
-  DrawTextSCentered(SYSIZE - 20, FONT_TITLE_2,
-                   "Joysticks deactivated on this screen");
-
   for (i = 0; setup_info[i].type != 0 && i < MAX_MENU_ENTRIES_ON_SCREEN; i++)
   {
     if (setup_info[i].type & (TYPE_ENTER_MENU|TYPE_ENTER_LIST))
@@ -5944,12 +6003,12 @@ static void drawPlayerSetupInputInfo(int player_nr, boolean active)
     char *text;
   } custom[] =
   {
-    { &custom_key.left,  "Joystick Left"  },
-    { &custom_key.right, "Joystick Right" },
-    { &custom_key.up,    "Joystick Up"    },
-    { &custom_key.down,  "Joystick Down"  },
-    { &custom_key.snap,  "Button 1"       },
-    { &custom_key.drop,  "Button 2"       }
+    { &custom_key.left,  "Axis/Pad Left"  },
+    { &custom_key.right, "Axis/Pad Right" },
+    { &custom_key.up,    "Axis/Pad Up"    },
+    { &custom_key.down,  "Axis/Pad Down"  },
+    { &custom_key.snap,  "Button 1/A/X"   },
+    { &custom_key.drop,  "Button 2/B/Y"   }
   };
   static char *joystick_name[MAX_PLAYERS] =
   {
@@ -5960,8 +6019,6 @@ static void drawPlayerSetupInputInfo(int player_nr, boolean active)
   };
   int text_font_nr = (active ? FONT_MENU_1_ACTIVE : FONT_MENU_1);
 
-  InitJoysticks();
-
   custom_key = setup.input[player_nr].key;
 
   DrawText(mSX + 11 * 32, mSY + 2 * 32, int2str(player_nr + 1, 1),
@@ -5975,11 +6032,13 @@ static void drawPlayerSetupInputInfo(int player_nr, boolean active)
   if (setup.input[player_nr].use_joystick)
   {
     char *device_name = setup.input[player_nr].joy.device_name;
-    char *text = joystick_name[getJoystickNrFromDeviceName(device_name)];
-    int font_nr = (joystick.fd[player_nr] < 0 ? FONT_VALUE_OLD : FONT_VALUE_1);
+    int joystick_nr = getJoystickNrFromDeviceName(device_name);
+    boolean joystick_active = CheckJoystickOpened(joystick_nr);
+    char *text = joystick_name[joystick_nr];
+    int font_nr = (joystick_active ? FONT_VALUE_1 : FONT_VALUE_OLD);
 
     DrawText(mSX + 8 * 32, mSY + 3 * 32, text, font_nr);
-    DrawText(mSX + 32, mSY + 4 * 32, "Calibrate", text_font_nr);
+    DrawText(mSX + 32, mSY + 4 * 32, "Configure", text_font_nr);
   }
   else
   {
@@ -6072,7 +6131,6 @@ void HandleSetupScreen_Input(int mx, int my, int dx, int dy, int button)
     if (dx && choice == 0)
       x = (dx < 0 ? 10 : 12);
     else if ((dx && choice == 1) ||
-            (dx == +1 && choice == 2) ||
             (dx == -1 && choice == pos_end))
       button = MB_MENU_CHOICE;
     else if (dy)
@@ -6131,10 +6189,7 @@ void HandleSetupScreen_Input(int mx, int my, int dx, int dy, int button)
       else if (y == 2)
       {
        if (setup.input[input_player_nr].use_joystick)
-       {
-         InitJoysticks();
-         CalibrateJoystick(input_player_nr);
-       }
+         ConfigureJoystick(input_player_nr);
        else
          CustomizeKeyboard(input_player_nr);
       }
@@ -6281,7 +6336,8 @@ void CustomizeKeyboard(int player_nr)
   DrawSetupScreen_Input();
 }
 
-static boolean CalibrateJoystickMain(int player_nr)
+#if 0
+static boolean OLD_CalibrateJoystickMain(int player_nr)
 {
   int new_joystick_xleft = JOYSTICK_XMIDDLE;
   int new_joystick_xright = JOYSTICK_XMIDDLE;
@@ -6289,7 +6345,10 @@ static boolean CalibrateJoystickMain(int player_nr)
   int new_joystick_ylower = JOYSTICK_YMIDDLE;
   int new_joystick_xmiddle, new_joystick_ymiddle;
 
-  int joystick_fd = joystick.fd[player_nr];
+  char *device_name = setup.input[player_nr].joy.device_name;
+  int joystick_nr = getJoystickNrFromDeviceName(device_name);
+  boolean joystick_active = CheckJoystickOpened(joystick_nr);
+
   int x, y, last_x, last_y, xpos = 8, ypos = 3;
   boolean check[3][3];
   int check_remaining = 3 * 3;
@@ -6300,7 +6359,7 @@ static boolean CalibrateJoystickMain(int player_nr)
   if (joystick.status == JOYSTICK_NOT_AVAILABLE)
     return FALSE;
 
-  if (joystick_fd < 0 || !setup.input[player_nr].use_joystick)
+  if (!joystick_active || !setup.input[player_nr].use_joystick)
     return FALSE;
 
   FadeSetEnterMenu();
@@ -6325,12 +6384,12 @@ static boolean CalibrateJoystickMain(int player_nr)
   DrawTextSCentered(mSY - SY + 12 * 32, FONT_TITLE_1, "and");
   DrawTextSCentered(mSY - SY + 13 * 32, FONT_TITLE_1, "press any button!");
 
-  joy_value = Joystick(player_nr);
+  joy_value = JoystickExt(joystick_nr, TRUE);
   last_x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
   last_y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
 
   /* eventually uncalibrated center position (joystick could be uncentered) */
-  if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
+  if (!ReadJoystick(joystick_nr, &joy_x, &joy_y, NULL, NULL))
     return FALSE;
 
   new_joystick_xmiddle = joy_x;
@@ -6340,11 +6399,12 @@ static boolean CalibrateJoystickMain(int player_nr)
 
   FadeIn(REDRAW_FIELD);
 
-  while (Joystick(player_nr) & JOY_BUTTON);    /* wait for released button */
+  /* wait for potentially still pressed button to be released */
+  while (JoystickExt(joystick_nr, TRUE) & JOY_BUTTON);
 
   while (result < 0)
   {
-    if (PendingEvent())                /* got event */
+    while (PendingEvent())             /* got event */
     {
       Event event;
 
@@ -6380,7 +6440,7 @@ static boolean CalibrateJoystickMain(int player_nr)
       }
     }
 
-    if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
+    if (!ReadJoystick(joystick_nr, &joy_x, &joy_y, NULL, NULL))
       return FALSE;
 
     new_joystick_xleft  = MIN(new_joystick_xleft,  joy_x);
@@ -6397,7 +6457,7 @@ static boolean CalibrateJoystickMain(int player_nr)
 
     CheckJoystickData();
 
-    joy_value = Joystick(player_nr);
+    joy_value = JoystickExt(joystick_nr, TRUE);
 
     if (joy_value & JOY_BUTTON && check_remaining == 0)
       result = 1;
@@ -6426,14 +6486,14 @@ static boolean CalibrateJoystickMain(int player_nr)
   }
 
   /* calibrated center position (joystick should now be centered) */
-  if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
+  if (!ReadJoystick(joystick_nr, &joy_x, &joy_y, NULL, NULL))
     return FALSE;
 
   new_joystick_xmiddle = joy_x;
   new_joystick_ymiddle = joy_y;
 
   /* wait until the last pressed button was released */
-  while (Joystick(player_nr) & JOY_BUTTON)
+  while (JoystickExt(joystick_nr, TRUE) & JOY_BUTTON)
   {
     if (PendingEvent())                /* got event */
     {
@@ -6448,23 +6508,415 @@ static boolean CalibrateJoystickMain(int player_nr)
 
   return TRUE;
 }
+#endif
+
+/* game controller mapping generator by Gabriel Jacobo <gabomdq@gmail.com> */
+
+#define MARKER_BUTTON          1
+#define MARKER_AXIS_X          2
+#define MARKER_AXIS_Y          3
+
+static boolean ConfigureJoystickMapButtonsAndAxes(SDL_Joystick *joystick)
+{
+#if defined(TARGET_SDL2)
+  static boolean bitmaps_initialized = FALSE;
+  boolean screen_initialized = FALSE;
+  static Bitmap *controller, *button, *axis_x, *axis_y;
+  char *name;
+  boolean success = TRUE;
+  boolean done = FALSE, next = FALSE;
+  Event event;
+  int alpha = 200, alpha_step = -1;
+  int alpha_ticks = 0;
+  char mapping[4096], temp[4096];
+  int font_name = FONT_TEXT_1;
+  int font_info = FONT_REQUEST;
+  int ystep1 = getFontHeight(font_name) + 2;
+  int ystep2 = getFontHeight(font_info) + 2;
+  int i, j;
+
+  struct
+  {
+    int x, y;
+    int marker;
+    char *field;
+    int axis, button, hat, hat_value;
+    char mapping[4096];
+  }
+  *step, *prev_step, steps[] =
+  {
+    { 356, 155, MARKER_BUTTON, "a",            },
+    { 396, 122, MARKER_BUTTON, "b",            },
+    { 320, 125, MARKER_BUTTON, "x",            },
+    { 358,  95, MARKER_BUTTON, "y",            },
+    { 162, 125, MARKER_BUTTON, "back",         },
+    { 216, 125, MARKER_BUTTON, "guide",                },
+    { 271, 125, MARKER_BUTTON, "start",                },
+    { 110, 200, MARKER_BUTTON, "dpleft",       },
+    { 146, 228, MARKER_BUTTON, "dpdown",       },
+    { 178, 200, MARKER_BUTTON, "dpright",      },
+    { 146, 172, MARKER_BUTTON, "dpup",         },
+    {  50,  40, MARKER_BUTTON, "leftshoulder", },
+    {  88, -10, MARKER_AXIS_Y, "lefttrigger",  },
+    { 382,  40, MARKER_BUTTON, "rightshoulder",        },
+    { 346, -10, MARKER_AXIS_Y, "righttrigger", },
+    {  73, 141, MARKER_BUTTON, "leftstick",    },
+    { 282, 210, MARKER_BUTTON, "rightstick",   },
+    {  73, 141, MARKER_AXIS_X, "leftx",                },
+    {  73, 141, MARKER_AXIS_Y, "lefty",                },
+    { 282, 210, MARKER_AXIS_X, "rightx",       },
+    { 282, 210, MARKER_AXIS_Y, "righty",       },
+  };
+
+  unsigned int event_frame_delay = 0;
+  unsigned int event_frame_delay_value = GAME_FRAME_DELAY;
+
+  ResetDelayCounter(&event_frame_delay);
+
+  if (!bitmaps_initialized)
+  {
+    controller = LoadCustomImage("joystick/controller.png");
+    button     = LoadCustomImage("joystick/button.png");
+    axis_x     = LoadCustomImage("joystick/axis_x.png");
+    axis_y     = LoadCustomImage("joystick/axis_y.png");
+
+    bitmaps_initialized = TRUE;
+  }
+
+  name = getFormattedJoystickName(SDL_JoystickName(joystick));
+
+  /* print info about the joystick we are watching */
+  Error(ERR_DEBUG, "watching joystick %d: (%s)\n",
+       SDL_JoystickInstanceID(joystick), name);
+  Error(ERR_DEBUG, "joystick has %d axes, %d hats, %d balls, and %d buttons\n",
+       SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick),
+       SDL_JoystickNumBalls(joystick), SDL_JoystickNumButtons(joystick));
+
+  /* initialize mapping with GUID and name */
+  SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), temp, sizeof(temp));
+
+  snprintf(mapping, sizeof(mapping), "%s,%s,platform:%s,",
+          temp, name, SDL_GetPlatform());
+
+  /* loop through all steps (buttons and axes), getting joystick events */
+  for (i = 0; i < SDL_arraysize(steps) && !done;)
+  {
+    Bitmap *marker = button;   /* initialize with reliable default value */
+
+    step = &steps[i];
+    strcpy(step->mapping, mapping);
+    step->axis = -1;
+    step->button = -1;
+    step->hat = -1;
+    step->hat_value = -1;
+
+    marker = (step->marker == MARKER_BUTTON ? button :
+             step->marker == MARKER_AXIS_X ? axis_x :
+             step->marker == MARKER_AXIS_Y ? axis_y : marker);
+
+    next = FALSE;
+
+    while (!done && !next)
+    {
+      alpha += alpha_step * (int)(SDL_GetTicks() - alpha_ticks) / 5;
+      alpha_ticks = SDL_GetTicks();
+
+      if (alpha >= 255)
+      {
+       alpha = 255;
+       alpha_step = -1;
+      }
+      else if (alpha < 128)
+      {
+       alpha = 127;
+       alpha_step = 1;
+      }
+
+      int controller_x = SX + (SXSIZE - controller->width) / 2;
+      int controller_y = SY + ystep2;
+
+      int marker_x = controller_x + step->x;
+      int marker_y = controller_y + step->y;
+
+      int ystart1 = mSY - 2 * SY + controller_y + controller->height;
+      int ystart2 = ystart1 + ystep1 + ystep2;
+
+      ClearField();
+
+      DrawTextSCentered(ystart1, font_name, name);
+
+      DrawTextSCentered(ystart2 + 0 * ystep2, font_info,
+                       "Press buttons and move axes on");
+      DrawTextSCentered(ystart2 + 1 * ystep2, font_info,
+                       "your controller when indicated.");
+      DrawTextSCentered(ystart2 + 2 * ystep2, font_info,
+                       "(Your controller may look different.)");
+
+#if defined(PLATFORM_ANDROID)
+      DrawTextSCentered(ystart2 + 4 * ystep2, font_info,
+                       "To correct a mistake,");
+      DrawTextSCentered(ystart2 + 5 * ystep2, font_info,
+                       "press the 'back' button.");
+      DrawTextSCentered(ystart2 + 6 * ystep2, font_info,
+                       "To skip a button or axis,");
+      DrawTextSCentered(ystart2 + 7 * ystep2, font_info,
+                       "press the 'menu' button.");
+#else
+      DrawTextSCentered(ystart2 + 4 * ystep2, font_info,
+                       "To correct a mistake,");
+      DrawTextSCentered(ystart2 + 5 * ystep2, font_info,
+                       "press the 'backspace' key.");
+      DrawTextSCentered(ystart2 + 6 * ystep2, font_info,
+                       "To skip a button or axis,");
+      DrawTextSCentered(ystart2 + 7 * ystep2, font_info,
+                       "press the 'return' key.");
+
+      DrawTextSCentered(ystart2 + 8 * ystep2, font_info,
+                       "To exit, press the 'escape' key.");
+#endif
+
+      BlitBitmapMasked(controller, drawto, 0, 0,
+                      controller->width, controller->height,
+                      controller_x, controller_y);
+
+      SDL_SetSurfaceAlphaMod(marker->surface_masked, alpha);
+
+      BlitBitmapMasked(marker, drawto, 0, 0,
+                      marker->width, marker->height,
+                      marker_x, marker_y);
+
+      if (!screen_initialized)
+       FadeIn(REDRAW_FIELD);
+      else
+       BackToFront();
+
+      screen_initialized = TRUE;
+
+      while (NextValidEvent(&event))
+      {
+       switch (event.type)
+       {
+         case SDL_JOYAXISMOTION:
+           if (event.jaxis.value > 20000 ||
+               event.jaxis.value < -20000)
+           {
+             for (j = 0; j < i; j++)
+               if (steps[j].axis == event.jaxis.axis)
+                 break;
+
+             if (j == i)
+             {
+               if (step->marker != MARKER_AXIS_X &&
+                   step->marker != MARKER_AXIS_Y)
+                 break;
+
+               step->axis = event.jaxis.axis;
+               strcat(mapping, step->field);
+               snprintf(temp, sizeof(temp), ":a%u,", event.jaxis.axis);
+               strcat(mapping, temp);
+               i++;
+               next = TRUE;
+             }
+           }
+
+           break;
+
+         case SDL_JOYHATMOTION:
+           /* ignore centering; we're probably just coming back
+              to the center from the previous item we set */
+           if (event.jhat.value == SDL_HAT_CENTERED)
+             break;
+
+           for (j = 0; j < i; j++)
+             if (steps[j].hat == event.jhat.hat &&
+                 steps[j].hat_value == event.jhat.value)
+               break;
+
+           if (j == i)
+           {
+             step->hat = event.jhat.hat;
+             step->hat_value = event.jhat.value;
+             strcat(mapping, step->field);
+             snprintf(temp, sizeof(temp), ":h%u.%u,",
+                      event.jhat.hat, event.jhat.value );
+             strcat(mapping, temp);
+             i++;
+             next = TRUE;
+           }
+
+           break;
+
+         case SDL_JOYBALLMOTION:
+           break;
+
+         case SDL_JOYBUTTONUP:
+           for (j = 0; j < i; j++)
+             if (steps[j].button == event.jbutton.button)
+               break;
+
+           if (j == i)
+           {
+             step->button = event.jbutton.button;
+             strcat(mapping, step->field);
+             snprintf(temp, sizeof(temp), ":b%u,", event.jbutton.button);
+             strcat(mapping, temp);
+             i++;
+             next = TRUE;
+           }
+
+           break;
+
+         case SDL_FINGERDOWN:
+         case SDL_MOUSEBUTTONDOWN:
+           /* skip this step */
+           i++;
+           next = TRUE;
+
+           break;
+
+         case SDL_KEYDOWN:
+           if (event.key.keysym.sym == KSYM_BackSpace ||
+               event.key.keysym.sym == KSYM_Back)
+           {
+             if (i == 0)
+             {
+               /* leave screen */
+               success = FALSE;
+               done = TRUE;
+             }
+
+             /* undo this step */
+             prev_step = &steps[i - 1];
+             strcpy(mapping, prev_step->mapping);
+             i--;
+             next = TRUE;
+
+             break;
+           }
+
+           if (event.key.keysym.sym == KSYM_space ||
+               event.key.keysym.sym == KSYM_Return ||
+               event.key.keysym.sym == KSYM_Menu)
+           {
+             /* skip this step */
+             i++;
+             next = TRUE;
+
+             break;
+           }
+
+           if (event.key.keysym.sym == KSYM_Escape)
+           {
+             /* leave screen */
+             success = FALSE;
+             done = TRUE;
+           }
+
+           break;
+
+         case SDL_QUIT:
+           program.exit_function(0);
+           break;
+
+         default:
+           break;
+       }
+
+       // do not handle events for longer than standard frame delay period
+       if (DelayReached(&event_frame_delay, event_frame_delay_value))
+         break;
+      }
+    }
+  }
+
+  if (success)
+  {
+    Error(ERR_DEBUG, "New game controller mapping:\n\n%s\n\n", mapping);
+
+    // activate mapping for this game
+    SDL_GameControllerAddMapping(mapping);
+
+    // save mapping to personal mappings
+    SaveSetup_AddGameControllerMapping(mapping);
+  }
+
+  /* wait until the last pending event was removed from event queue */
+  while (NextValidEvent(&event));
+
+  return success;
+#else
+  return TRUE;
+#endif
+}
+
+static int ConfigureJoystickMain(int player_nr)
+{
+  char *device_name = setup.input[player_nr].joy.device_name;
+  int joystick_nr = getJoystickNrFromDeviceName(device_name);
+  boolean joystick_active = CheckJoystickOpened(joystick_nr);
+  int success = FALSE;
+  int i;
+
+  if (joystick.status == JOYSTICK_NOT_AVAILABLE)
+    return JOYSTICK_NOT_AVAILABLE;
+
+  if (!joystick_active || !setup.input[player_nr].use_joystick)
+    return JOYSTICK_NOT_AVAILABLE;
+
+  FadeSetEnterMenu();
+  FadeOut(REDRAW_FIELD);
+
+  // close all joystick devices (potentially opened as game controllers)
+  for (i = 0; i < SDL_NumJoysticks(); i++)
+    SDLCloseJoystick(i);
+
+  // open joystick device as plain joystick to configure as game controller
+  SDL_Joystick *joystick = SDL_JoystickOpen(joystick_nr);
+
+  // as the joystick was successfully opened before, this should not happen
+  if (joystick == NULL)
+    return FALSE;
+
+  // create new game controller mapping (buttons and axes) for joystick device
+  success = ConfigureJoystickMapButtonsAndAxes(joystick);
+
+  // close joystick (and maybe re-open as configured game controller later)
+  SDL_JoystickClose(joystick);
+
+  // re-open all joystick devices (potentially as game controllers)
+  for (i = 0; i < SDL_NumJoysticks(); i++)
+    SDLOpenJoystick(i);
+
+  // clear all joystick input actions for all joystick devices
+  SDLClearJoystickState();
+
+  return (success ? JOYSTICK_CONFIGURED : JOYSTICK_NOT_CONFIGURED);
+}
 
-void CalibrateJoystick(int player_nr)
+void ConfigureJoystick(int player_nr)
 {
-  if (!CalibrateJoystickMain(player_nr))
+  boolean state = ConfigureJoystickMain(player_nr);
+
+  if (state != JOYSTICK_NOT_CONFIGURED)
   {
+    boolean success = (state == JOYSTICK_CONFIGURED);
+    char *message = (success ? " IS CONFIGURED! " : " NOT AVAILABLE! ");
     char *device_name = setup.input[player_nr].joy.device_name;
     int nr = getJoystickNrFromDeviceName(device_name) + 1;
     int xpos = mSX - SX;
     int ypos = mSY - SY;
+    unsigned int wait_frame_delay = 0;
+    unsigned int wait_frame_delay_value = 2000;
+
+    ResetDelayCounter(&wait_frame_delay);
 
     ClearField();
 
     DrawTextF(xpos + 16, ypos + 6 * 32, FONT_TITLE_1, "   JOYSTICK %d   ", nr);
-    DrawTextF(xpos + 16, ypos + 7 * 32, FONT_TITLE_1, " NOT AVAILABLE! ");
-    BackToFront();
+    DrawTextF(xpos + 16, ypos + 7 * 32, FONT_TITLE_1, message);
 
-    Delay(2000);               /* show error message for a short time */
+    while (!DelayReached(&wait_frame_delay, wait_frame_delay_value))
+      BackToFront();
 
     ClearEventQueue();
   }
@@ -6474,10 +6926,6 @@ void CalibrateJoystick(int player_nr)
 
 void DrawSetupScreen()
 {
-  DeactivateJoystick();
-
-  SetMainBackgroundImage(IMG_BACKGROUND_SETUP);
-
   if (setup_mode == SETUP_MODE_INPUT)
     DrawSetupScreen_Input();
   else if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED)
@@ -6513,8 +6961,7 @@ void DrawSetupScreen()
   else
     DrawSetupScreen_Generic();
 
-  PlayMenuSound();
-  PlayMenuMusic();
+  PlayMenuSoundsAndMusic();
 }
 
 void RedrawSetupScreenAfterFullscreenToggle()