fixed ignoring clicks on global animations after executing event actions
[rocksndiamonds.git] / src / screens.c
index dc28e237a19db3810867bf82440545bb782c1c41..89b9d3d48c515478dc2894924fd96ef9de463004 100644 (file)
 #define SETUP_MODE_CHOOSE_TOUCH_CONTROL        28
 #define SETUP_MODE_CHOOSE_MOVE_DISTANCE        29
 #define SETUP_MODE_CHOOSE_DROP_DISTANCE        30
+#define SETUP_MODE_CHOOSE_TRANSPARENCY 31
+#define SETUP_MODE_CHOOSE_GRID_XSIZE_0 32
+#define SETUP_MODE_CHOOSE_GRID_YSIZE_0 33
+#define SETUP_MODE_CHOOSE_GRID_XSIZE_1 34
+#define SETUP_MODE_CHOOSE_GRID_YSIZE_1 35
+#define SETUP_MODE_CONFIG_VIRT_BUTTONS 36
 
-#define MAX_SETUP_MODES                        31
+#define MAX_SETUP_MODES                        37
 
 #define MAX_MENU_MODES                 MAX(MAX_INFO_MODES, MAX_SETUP_MODES)
 
 #define STR_SETUP_CHOOSE_TOUCH_CONTROL "Control Type"
 #define STR_SETUP_CHOOSE_MOVE_DISTANCE "Move Distance"
 #define STR_SETUP_CHOOSE_DROP_DISTANCE "Drop Distance"
+#define STR_SETUP_CHOOSE_TRANSPARENCY  "Transparency"
+#define STR_SETUP_CHOOSE_GRID_XSIZE_0  "Horiz. Buttons"
+#define STR_SETUP_CHOOSE_GRID_YSIZE_0  "Vert. Buttons"
+#define STR_SETUP_CHOOSE_GRID_XSIZE_1  "Horiz. Buttons"
+#define STR_SETUP_CHOOSE_GRID_YSIZE_1  "Vert. Buttons"
 
 /* for input setup functions */
 #define SETUPINPUT_SCREEN_POS_START    0
@@ -207,6 +218,7 @@ static void HandleSetupScreen_Generic(int, int, int, int, int);
 static void HandleSetupScreen_Input(int, int, int, int, int);
 static void CustomizeKeyboard(int);
 static void ConfigureJoystick(int);
+static void ConfigureVirtualButtons();
 static void execSetupGame(void);
 static void execSetupGraphics(void);
 static void execSetupSound(void);
@@ -275,6 +287,12 @@ static TreeInfo *move_distance_current = NULL;
 static TreeInfo *drop_distances = NULL;
 static TreeInfo *drop_distance_current = NULL;
 
+static TreeInfo *transparencies = NULL;
+static TreeInfo *transparency_current = NULL;
+
+static TreeInfo *grid_sizes[2][2] = { { NULL, NULL }, { NULL, NULL } };
+static TreeInfo *grid_size_current[2][2] = { { NULL, NULL }, { NULL, NULL } };
+
 static TreeInfo *level_number = NULL;
 static TreeInfo *level_number_current = NULL;
 
@@ -451,6 +469,67 @@ static struct
   {    -1,     NULL                            },
 };
 
+static struct
+{
+  int value;
+  char *text;
+} transparencies_list[] =
+{
+  {    0,      "0 % (Opaque)"                  },
+  {    10,     "10 %"                          },
+  {    20,     "20 %"                          },
+  {    30,     "30 %"                          },
+  {    40,     "40 %"                          },
+  {    50,     "50 %"                          },
+  {    60,     "60 %"                          },
+  {    70,     "70 %"                          },
+  {    80,     "80 %"                          },
+  {    90,     "90 %"                          },
+  {    100,    "100 % (Invisible)"             },
+
+  {    -1,     NULL                            },
+};
+
+static struct
+{
+  int value;
+  char *text;
+} grid_sizes_list[] =
+{
+  {    3,      "3"                             },
+  {    4,      "4"                             },
+  {    5,      "5"                             },
+  {    6,      "6"                             },
+  {    7,      "7"                             },
+  {    8,      "8"                             },
+  {    9,      "9"                             },
+  {    10,     "10"                            },
+  {    11,     "11"                            },
+  {    12,     "12"                            },
+  {    13,     "13"                            },
+  {    14,     "14"                            },
+  {    15,     "15"                            },
+  {    16,     "16"                            },
+  {    17,     "17"                            },
+  {    18,     "18"                            },
+  {    19,     "19"                            },
+  {    20,     "20"                            },
+  {    21,     "21"                            },
+  {    22,     "22"                            },
+  {    23,     "23"                            },
+  {    24,     "24"                            },
+  {    25,     "25"                            },
+  {    26,     "26"                            },
+  {    27,     "27"                            },
+  {    28,     "28"                            },
+  {    29,     "29"                            },
+  {    30,     "30"                            },
+  {    31,     "31"                            },
+  {    32,     "32"                            },
+
+  {    -1,     NULL                            },
+};
+
 #define DRAW_MODE(s)           ((s) >= GAME_MODE_MAIN &&               \
                                 (s) <= GAME_MODE_SETUP ? (s) :         \
                                 (s) == GAME_MODE_PSEUDO_TYPENAME ?     \
@@ -670,19 +749,19 @@ static struct MainControlInfo main_controls[] =
 #endif
   {
     MAIN_CONTROL_FIRST_LEVEL,
-    NULL,                              -1,
+    &menu.main.button.first_level,     IMG_MENU_BUTTON_FIRST_LEVEL,
     &menu.main.text.first_level,       &main_text_first_level,
     NULL,                              NULL,
   },
   {
     MAIN_CONTROL_LAST_LEVEL,
-    NULL,                              -1,
+    &menu.main.button.last_level,      IMG_MENU_BUTTON_LAST_LEVEL,
     &menu.main.text.last_level,                &main_text_last_level,
     NULL,                              NULL,
   },
   {
     MAIN_CONTROL_LEVEL_NUMBER,
-    NULL,                              -1,
+    &menu.main.button.level_number,    IMG_MENU_BUTTON_LEVEL_NUMBER,
     &menu.main.text.level_number,      &main_text_level_number,
     NULL,                              NULL,
   },
@@ -1069,8 +1148,8 @@ static void InitializeMainControls()
 
     if (pos_text != NULL)              /* (x/y may be -1/-1 here) */
     {
-      /* calculate size for non-clickable text -- needed for text alignment */
-      boolean calculate_text_size = (pos_button == NULL && text != NULL);
+      /* calculate text size -- needed for text alignment */
+      boolean calculate_text_size = (text != NULL);
 
       if (pos_text->width == -1 || calculate_text_size)
        pos_text->width = text_width;
@@ -1572,6 +1651,10 @@ void DrawMainMenu()
   FreeScreenGadgets();
   CreateScreenGadgets();
 
+  /* may be required if audio buttons shown on tape and changed in setup menu */
+  FreeGameButtons();
+  CreateGameButtons();
+
   /* map gadgets for main menu screen */
   MapTapeButtons();
   MapScreenMenuGadgets(SCREEN_MASK_MAIN);
@@ -4075,7 +4158,12 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
        execSetupSound();
       else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL ||
               setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE ||
-              setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE)
+              setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE ||
+              setup_mode == SETUP_MODE_CHOOSE_TRANSPARENCY ||
+              setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_0 ||
+              setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_0 ||
+              setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_1 ||
+              setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_1)
        execSetupTouch();
       else
        execSetupArtwork();
@@ -4227,7 +4315,12 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
            execSetupSound();
          else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL ||
                   setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE ||
-                  setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE)
+                  setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE ||
+                  setup_mode == SETUP_MODE_CHOOSE_TRANSPARENCY ||
+                  setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_0 ||
+                  setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_0 ||
+                  setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_1 ||
+                  setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_1)
            execSetupTouch();
          else
            execSetupArtwork();
@@ -4293,7 +4386,12 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
            execSetupSound();
          else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL ||
                   setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE ||
-                  setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE)
+                  setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE ||
+                  setup_mode == SETUP_MODE_CHOOSE_TRANSPARENCY ||
+                  setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_0 ||
+                  setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_0 ||
+                  setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_1 ||
+                  setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_1)
            execSetupTouch();
          else
            execSetupArtwork();
@@ -4560,6 +4658,8 @@ static char *volume_music_text;
 static char *touch_controls_text;
 static char *move_distance_text;
 static char *drop_distance_text;
+static char *transparency_text;
+static char *grid_size_text[2][2];
 
 static void execSetupMain()
 {
@@ -5257,12 +5357,58 @@ static void execSetupChooseDropDistance()
   DrawSetupScreen();
 }
 
+static void execSetupChooseTransparency()
+{
+  setup_mode = SETUP_MODE_CHOOSE_TRANSPARENCY;
+
+  DrawSetupScreen();
+}
+
+static void execSetupChooseGridXSize_0()
+{
+  setup_mode = SETUP_MODE_CHOOSE_GRID_XSIZE_0;
+
+  DrawSetupScreen();
+}
+
+static void execSetupChooseGridYSize_0()
+{
+  setup_mode = SETUP_MODE_CHOOSE_GRID_YSIZE_0;
+
+  DrawSetupScreen();
+}
+
+static void execSetupChooseGridXSize_1()
+{
+  setup_mode = SETUP_MODE_CHOOSE_GRID_XSIZE_1;
+
+  DrawSetupScreen();
+}
+
+static void execSetupChooseGridYSize_1()
+{
+  setup_mode = SETUP_MODE_CHOOSE_GRID_YSIZE_1;
+
+  DrawSetupScreen();
+}
+
+static void execSetupConfigureVirtualButtons()
+{
+  setup_mode = SETUP_MODE_CONFIG_VIRT_BUTTONS;
+
+  ConfigureVirtualButtons();
+
+  setup_mode = SETUP_MODE_TOUCH;
+
+  DrawSetupScreen();
+}
+
 static void execSetupTouch()
 {
+  int i, j, k;
+
   if (touch_controls == NULL)
   {
-    int i;
-
     for (i = 0; touch_controls_list[i].value != NULL; i++)
     {
       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
@@ -5303,8 +5449,6 @@ static void execSetupTouch()
 
   if (move_distances == NULL)
   {
-    int i;
-
     for (i = 0; distances_list[i].value != -1; i++)
     {
       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
@@ -5337,7 +5481,8 @@ static void execSetupTouch()
     /* if that fails, set current distance to reliable default value */
     if (move_distance_current == NULL)
       move_distance_current =
-       getTreeInfoFromIdentifier(move_distances, i_to_a(1));
+       getTreeInfoFromIdentifier(move_distances,
+                                 i_to_a(TOUCH_MOVE_DISTANCE_DEFAULT));
 
     /* if that also fails, set current distance to first available value */
     if (move_distance_current == NULL)
@@ -5346,8 +5491,6 @@ static void execSetupTouch()
 
   if (drop_distances == NULL)
   {
-    int i;
-
     for (i = 0; distances_list[i].value != -1; i++)
     {
       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
@@ -5380,21 +5523,141 @@ static void execSetupTouch()
     /* if that fails, set current distance to reliable default value */
     if (drop_distance_current == NULL)
       drop_distance_current =
-       getTreeInfoFromIdentifier(drop_distances, i_to_a(1));
+       getTreeInfoFromIdentifier(drop_distances,
+                                 i_to_a(TOUCH_DROP_DISTANCE_DEFAULT));
 
     /* if that also fails, set current distance to first available value */
     if (drop_distance_current == NULL)
       drop_distance_current = drop_distances;
   }
 
+  if (transparencies == NULL)
+  {
+    for (i = 0; transparencies_list[i].value != -1; i++)
+    {
+      TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
+      char identifier[32], name[32];
+      int value = transparencies_list[i].value;
+      char *text = transparencies_list[i].text;
+
+      ti->node_top = &transparencies;
+      ti->sort_priority = value;
+
+      sprintf(identifier, "%d", value);
+      sprintf(name, "%s", text);
+
+      setString(&ti->identifier, identifier);
+      setString(&ti->name, name);
+      setString(&ti->name_sorting, name);
+      setString(&ti->infotext, STR_SETUP_CHOOSE_TRANSPARENCY);
+
+      pushTreeInfo(&transparencies, ti);
+    }
+
+    /* sort transparency values to start with lowest transparency value */
+    sortTreeInfo(&transparencies);
+
+    /* set current transparency value to configured transparency value */
+    transparency_current =
+      getTreeInfoFromIdentifier(transparencies,
+                               i_to_a(setup.touch.transparency));
+
+    /* if that fails, set current transparency to reliable default value */
+    if (transparency_current == NULL)
+      transparency_current =
+       getTreeInfoFromIdentifier(transparencies,
+                                 i_to_a(TOUCH_TRANSPARENCY_DEFAULT));
+
+    /* if that also fails, set current transparency to first available value */
+    if (transparency_current == NULL)
+      transparency_current = transparencies;
+  }
+
+  for (i = 0; i < 2; i++)
+  {
+    for (j = 0; j < 2; j++)
+    {
+      if (grid_sizes[i][j] == NULL)
+      {
+       for (k = 0; grid_sizes_list[k].value != -1; k++)
+       {
+         TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
+         char identifier[32], name[32];
+         int value = grid_sizes_list[k].value;
+         char *text = grid_sizes_list[k].text;
+
+         ti->node_top = &grid_sizes[i][j];
+         ti->sort_priority = value;
+
+         sprintf(identifier, "%d", value);
+         sprintf(name, "%s", text);
+
+         setString(&ti->identifier, identifier);
+         setString(&ti->name, name);
+         setString(&ti->name_sorting, name);
+         setString(&ti->infotext,
+                   (i == 0 ?
+                    (j == 0 ?
+                     STR_SETUP_CHOOSE_GRID_XSIZE_0 :
+                     STR_SETUP_CHOOSE_GRID_YSIZE_0) :
+                    (j == 0 ?
+                     STR_SETUP_CHOOSE_GRID_XSIZE_1 :
+                     STR_SETUP_CHOOSE_GRID_YSIZE_1)));
+
+         pushTreeInfo(&grid_sizes[i][j], ti);
+       }
+
+       /* sort grid size values to start with lowest grid size value */
+       sortTreeInfo(&grid_sizes[i][j]);
+
+       /* set current grid size value to configured grid size value */
+       grid_size_current[i][j] =
+         getTreeInfoFromIdentifier(grid_sizes[i][j],
+                                   i_to_a(j == 0 ?
+                                          setup.touch.grid_xsize[i] :
+                                          setup.touch.grid_ysize[i]));
+
+       /* if that fails, set current grid size to reliable default value */
+       if (grid_size_current[i][j] == NULL)
+         grid_size_current[i][j] =
+           getTreeInfoFromIdentifier(grid_sizes[i][j],
+                                     i_to_a(j == 0 ?
+                                            DEFAULT_GRID_XSIZE(i) :
+                                            DEFAULT_GRID_YSIZE(i)));
+
+       /* if that also fails, set current grid size to first available value */
+       if (grid_size_current[i][j] == NULL)
+         grid_size_current[i][j] = grid_sizes[i][j];
+      }
+    }
+  }
+
   setup.touch.control_type = touch_control_current->identifier;
   setup.touch.move_distance = atoi(move_distance_current->identifier);
   setup.touch.drop_distance = atoi(drop_distance_current->identifier);
+  setup.touch.transparency = atoi(transparency_current->identifier);
 
-  /* needed for displaying volume text instead of identifier */
+  for (i = 0; i < 2; i++)
+  {
+    setup.touch.grid_xsize[i] = atoi(grid_size_current[i][0]->identifier);
+    setup.touch.grid_ysize[i] = atoi(grid_size_current[i][1]->identifier);
+
+    if (i == GRID_ACTIVE_NR())
+    {
+      overlay.grid_xsize = setup.touch.grid_xsize[i];
+      overlay.grid_ysize = setup.touch.grid_ysize[i];
+    }
+  }
+
+  /* needed for displaying value text instead of identifier */
   touch_controls_text = touch_control_current->name;
   move_distance_text = move_distance_current->name;
   drop_distance_text = drop_distance_current->name;
+  transparency_text = transparency_current->name;
+
+  for (i = 0; i < 2; i++)
+    for (j = 0; j < 2; j++)
+      grid_size_text[i][j] = grid_size_current[i][j]->name;
 
   setup_mode = SETUP_MODE_TOUCH;
 
@@ -5561,6 +5824,21 @@ static struct
   { &setup.touch.drop_distance,                execSetupChooseDropDistance     },
   { &setup.touch.drop_distance,                &drop_distance_text             },
 
+  { &setup.touch.transparency,         execSetupChooseTransparency     },
+  { &setup.touch.transparency,         &transparency_text              },
+
+  { &setup.touch.grid_xsize[0],                execSetupChooseGridXSize_0      },
+  { &setup.touch.grid_xsize[0],                &grid_size_text[0][0]           },
+
+  { &setup.touch.grid_ysize[0],                execSetupChooseGridYSize_0      },
+  { &setup.touch.grid_ysize[0],                &grid_size_text[0][1]           },
+
+  { &setup.touch.grid_xsize[1],                execSetupChooseGridXSize_1      },
+  { &setup.touch.grid_xsize[1],                &grid_size_text[1][0]           },
+
+  { &setup.touch.grid_ysize[1],                execSetupChooseGridYSize_1      },
+  { &setup.touch.grid_ysize[1],                &grid_size_text[1][1]           },
+
   { NULL,                              NULL                            }
 };
 
@@ -5743,6 +6021,54 @@ static struct TokenInfo setup_info_touch[] =
   { 0,                 NULL,                   NULL                    }
 };
 
+static struct TokenInfo setup_info_touch_virtual_buttons_0[] =
+{
+  { TYPE_ENTER_LIST,   execSetupChooseTouchControls, "Touch Control Type:" },
+  { TYPE_STRING,       &touch_controls_text,   ""                      },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_ENTER_LIST,   execSetupChooseGridXSize_0, "Horizontal Buttons (Landscape):"   },
+  { TYPE_STRING,       &grid_size_text[0][0],  ""                      },
+  { TYPE_ENTER_LIST,   execSetupChooseGridYSize_0, "Vertical Buttons (Landscape):"     },
+  { TYPE_STRING,       &grid_size_text[0][1],  ""                      },
+  { TYPE_ENTER_LIST,   execSetupChooseTransparency, "Button Transparency:" },
+  { TYPE_STRING,       &transparency_text,     ""                      },
+  { TYPE_SWITCH,       &setup.touch.draw_outlined, "Draw Buttons Outlined:" },
+  { TYPE_SWITCH,       &setup.touch.draw_pressed, "Highlight Pressed Buttons:" },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_ENTER_LIST,   execSetupConfigureVirtualButtons, "Configure Virtual Buttons" },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_LEAVE_MENU,   execSetupMain,          "Back"                  },
+
+  { 0,                 NULL,                   NULL                    }
+};
+
+static struct TokenInfo setup_info_touch_virtual_buttons_1[] =
+{
+  { TYPE_ENTER_LIST,   execSetupChooseTouchControls, "Touch Control Type:" },
+  { TYPE_STRING,       &touch_controls_text,   ""                      },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_ENTER_LIST,   execSetupChooseGridXSize_1, "Horizontal Buttons (Portrait):"    },
+  { TYPE_STRING,       &grid_size_text[1][0],  ""                      },
+  { TYPE_ENTER_LIST,   execSetupChooseGridYSize_1, "Vertical Buttons (Portrait):"      },
+  { TYPE_STRING,       &grid_size_text[1][1],  ""                      },
+  { TYPE_ENTER_LIST,   execSetupChooseTransparency, "Button Transparency:" },
+  { TYPE_STRING,       &transparency_text,     ""                      },
+  { TYPE_SWITCH,       &setup.touch.draw_outlined, "Draw Buttons Outlined:" },
+  { TYPE_SWITCH,       &setup.touch.draw_pressed, "Highlight Pressed Buttons:" },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_ENTER_LIST,   execSetupConfigureVirtualButtons, "Configure Virtual Buttons" },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_LEAVE_MENU,   execSetupMain,          "Back"                  },
+
+  { 0,                 NULL,                   NULL                    }
+};
+
+static struct TokenInfo *setup_info_touch_virtual_buttons[] =
+{
+  setup_info_touch_virtual_buttons_0,
+  setup_info_touch_virtual_buttons_1
+};
+
 static struct TokenInfo setup_info_touch_wipe_gestures[] =
 {
   { TYPE_ENTER_LIST,   execSetupChooseTouchControls, "Touch Control Type:" },
@@ -6176,7 +6502,9 @@ static void DrawSetupScreen_Generic()
     setup_info = setup_info_touch;
     title_string = STR_SETUP_TOUCH;
 
-    if (strEqual(setup.touch.control_type, TOUCH_CONTROL_WIPE_GESTURES))
+    if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
+      setup_info = setup_info_touch_virtual_buttons[GRID_ACTIVE_NR()];
+    else if (strEqual(setup.touch.control_type, TOUCH_CONTROL_WIPE_GESTURES))
       setup_info = setup_info_touch_wipe_gestures;
   }
   else if (setup_mode == SETUP_MODE_SHORTCUTS)
@@ -6570,7 +6898,7 @@ static boolean CustomizeKeyboardMain(int player_nr)
   };
   int font_nr_old = FONT_VALUE_OLD;
   int font_nr_new = FONT_VALUE_1;
-  int success = FALSE;
+  boolean success = FALSE;
 
   if (SCR_FIELDX < SCR_FIELDX_DEFAULT)
   {
@@ -6898,6 +7226,7 @@ static boolean ConfigureJoystickMapButtonsAndAxes(SDL_Joystick *joystick)
                       controller->width, controller->height,
                       controller_x, controller_y);
 
+      SDL_SetSurfaceBlendMode(marker->surface_masked, SDL_BLENDMODE_BLEND);
       SDL_SetSurfaceAlphaMod(marker->surface_masked, alpha);
 
       BlitBitmapMasked(marker, drawto, 0, 0,
@@ -7002,6 +7331,8 @@ static boolean ConfigureJoystickMapButtonsAndAxes(SDL_Joystick *joystick)
                /* leave screen */
                success = FALSE;
                done = TRUE;
+
+               break;
              }
 
              /* undo this step */
@@ -7150,6 +7481,270 @@ void ConfigureJoystick(int player_nr)
   DrawSetupScreen_Input();
 }
 
+boolean ConfigureVirtualButtonsMain()
+{
+  static char *customize_step_text[] =
+  {
+    "Move Left",
+    "Move Right",
+    "Move Up",
+    "Move Down",
+    "Snap Field",
+    "Drop Element"
+  };
+  char grid_button[] =
+  {
+    CHAR_GRID_BUTTON_LEFT,
+    CHAR_GRID_BUTTON_RIGHT,
+    CHAR_GRID_BUTTON_UP,
+    CHAR_GRID_BUTTON_DOWN,
+    CHAR_GRID_BUTTON_SNAP,
+    CHAR_GRID_BUTTON_DROP
+  };
+  int font_nr = FONT_INPUT_1_ACTIVE;
+  int font_height = getFontHeight(font_nr);
+  int ypos1 = SYSIZE / 2 - font_height * 2;
+  int ypos2 = SYSIZE / 2 - font_height * 1;
+  boolean success = FALSE;
+  boolean finished = FALSE;
+  int step_nr = 0;
+  char grid_button_draw = CHAR_GRID_BUTTON_NONE;
+  char grid_button_old[MAX_GRID_XSIZE][MAX_GRID_YSIZE];
+  char grid_button_tmp[MAX_GRID_XSIZE][MAX_GRID_YSIZE];
+  boolean set_grid_button = FALSE;
+  int nr = GRID_ACTIVE_NR();
+  int x, y;
+
+  for (x = 0; x < MAX_GRID_XSIZE; x++)
+    for (y = 0; y < MAX_GRID_YSIZE; y++)
+      grid_button_old[x][y] = grid_button_tmp[x][y] = overlay.grid_button[x][y];
+
+  overlay.grid_button_highlight = grid_button[step_nr];
+
+  FadeSetEnterMenu();
+  FadeOut(REDRAW_FIELD);
+
+  ClearField();
+
+  DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Virtual Buttons");
+  DrawTextSCentered(ypos1, font_nr, "Select tiles to");
+  DrawTextSCentered(ypos2, font_nr, customize_step_text[step_nr]);
+
+  FadeIn(REDRAW_FIELD);
+
+  SetOverlayShowGrid(TRUE);
+
+  while (!finished)
+  {
+    Event event;
+
+    while (NextValidEvent(&event))
+    {
+      switch (event.type)
+      {
+        case EVENT_KEYPRESS:
+         {
+           Key key = GetEventKey((KeyEvent *)&event, FALSE);
+
+           /* press 'Escape' to abort and keep the old key bindings */
+           if (key == KSYM_Escape)
+           {
+             for (x = 0; x < MAX_GRID_XSIZE; x++)
+               for (y = 0; y < MAX_GRID_YSIZE; y++)
+                 overlay.grid_button[x][y] = grid_button_old[x][y];
+
+             FadeSkipNextFadeIn();
+
+             finished = TRUE;
+
+             break;
+           }
+
+           /* press 'Enter' to keep the existing key binding */
+           if (key == KSYM_Return ||
+#if defined(TARGET_SDL2)
+               key == KSYM_Menu ||
+#endif
+               key == KSYM_space)
+           {
+             step_nr++;
+           }
+           else if (key == KSYM_BackSpace
+#if defined(TARGET_SDL2)
+                    ||
+                    key == KSYM_Back
+#endif
+                    )
+           {
+             if (step_nr == 0)
+             {
+               FadeSkipNextFadeIn();
+
+               finished = TRUE;
+
+               break;
+             }
+
+             step_nr--;
+           }
+           else
+           {
+             break;
+           }
+
+           /* all virtual buttons configured */
+           if (step_nr == 6)
+           {
+             finished = TRUE;
+             success = TRUE;
+
+             break;
+           }
+
+           for (x = 0; x < MAX_GRID_XSIZE; x++)
+             for (y = 0; y < MAX_GRID_YSIZE; y++)
+               grid_button_tmp[x][y] = overlay.grid_button[x][y];
+
+           overlay.grid_button_highlight = grid_button[step_nr];
+
+           /* query next virtual button */
+
+           ClearField();
+
+           DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Virtual Buttons");
+           DrawTextSCentered(ypos1, font_nr, "Select tiles to");
+           DrawTextSCentered(ypos2, font_nr, customize_step_text[step_nr]);
+         }
+         break;
+
+        case EVENT_KEYRELEASE:
+         key_joystick_mapping = 0;
+         break;
+
+       case EVENT_BUTTONPRESS:
+       case EVENT_BUTTONRELEASE:
+         {
+           ButtonEvent *button = (ButtonEvent *)&event;
+
+           button->x += video.screen_xoffset;
+           button->y += video.screen_yoffset;
+
+           x = button->x * overlay.grid_xsize / video.screen_width;
+           y = button->y * overlay.grid_ysize / video.screen_height;
+
+           if (button->type == EVENT_BUTTONPRESS)
+           {
+             button_status = button->button;
+
+             grid_button_draw =
+               (overlay.grid_button[x][y] != grid_button[step_nr] ?
+                grid_button[step_nr] : CHAR_GRID_BUTTON_NONE);
+
+             set_grid_button = TRUE;
+           }
+           else
+           {
+             button_status = MB_RELEASED;
+           }
+         }
+         break;
+
+       case EVENT_MOTIONNOTIFY:
+         {
+           MotionEvent *motion = (MotionEvent *)&event;
+
+           motion->x += video.screen_xoffset;
+           motion->y += video.screen_yoffset;
+
+           x = motion->x * overlay.grid_xsize / video.screen_width;
+           y = motion->y * overlay.grid_ysize / video.screen_height;
+
+           set_grid_button = TRUE;
+         }
+         break;
+
+#if defined(TARGET_SDL2)
+       case SDL_WINDOWEVENT:
+         HandleWindowEvent((WindowEvent *) &event);
+
+         // check if device has been rotated
+         if (nr != GRID_ACTIVE_NR())
+         {
+           nr = GRID_ACTIVE_NR();
+
+           for (x = 0; x < MAX_GRID_XSIZE; x++)
+             for (y = 0; y < MAX_GRID_YSIZE; y++)
+               grid_button_old[x][y] = grid_button_tmp[x][y] =
+                 overlay.grid_button[x][y];
+         }
+
+         break;
+
+       case SDL_APP_WILLENTERBACKGROUND:
+       case SDL_APP_DIDENTERBACKGROUND:
+       case SDL_APP_WILLENTERFOREGROUND:
+       case SDL_APP_DIDENTERFOREGROUND:
+         HandlePauseResumeEvent((PauseResumeEvent *) &event);
+         break;
+#endif
+
+        default:
+         HandleOtherEvents(&event);
+         break;
+      }
+
+      if (set_grid_button)
+      {
+       overlay.grid_button[x][y] =
+         (grid_button_draw != CHAR_GRID_BUTTON_NONE ? grid_button_draw :
+          grid_button_tmp[x][y] == grid_button[step_nr] ? CHAR_GRID_BUTTON_NONE :
+          grid_button_tmp[x][y]);
+
+       set_grid_button = FALSE;
+      }
+    }
+
+    BackToFront();
+  }
+
+  for (x = 0; x < MAX_GRID_XSIZE; x++)
+    for (y = 0; y < MAX_GRID_YSIZE; y++)
+      setup.touch.grid_button[nr][x][y] = overlay.grid_button[x][y];
+
+  overlay.grid_button_highlight = CHAR_GRID_BUTTON_NONE;
+
+  SetOverlayShowGrid(FALSE);
+
+  return success;
+}
+
+void ConfigureVirtualButtons()
+{
+  boolean success = ConfigureVirtualButtonsMain();
+
+  if (success)
+  {
+    int font_nr = FONT_TITLE_1;
+    int font_height = getFontHeight(font_nr);
+    int ypos1 = SYSIZE / 2 - font_height * 2;
+    int ypos2 = SYSIZE / 2 - font_height * 1;
+    unsigned int wait_frame_delay = 0;
+    unsigned int wait_frame_delay_value = 2000;
+
+    ResetDelayCounter(&wait_frame_delay);
+
+    ClearField();
+
+    DrawTextSCentered(ypos1, font_nr, "Virtual buttons");
+    DrawTextSCentered(ypos2, font_nr, "configured!");
+
+    while (!DelayReached(&wait_frame_delay, wait_frame_delay_value))
+      BackToFront();
+
+    ClearEventQueue();
+  }
+}
+
 void DrawSetupScreen()
 {
   if (setup_mode == SETUP_MODE_INPUT)
@@ -7184,6 +7779,16 @@ void DrawSetupScreen()
     DrawChooseTree(&move_distance_current);
   else if (setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE)
     DrawChooseTree(&drop_distance_current);
+  else if (setup_mode == SETUP_MODE_CHOOSE_TRANSPARENCY)
+    DrawChooseTree(&transparency_current);
+  else if (setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_0)
+    DrawChooseTree(&grid_size_current[0][0]);
+  else if (setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_0)
+    DrawChooseTree(&grid_size_current[0][1]);
+  else if (setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_1)
+    DrawChooseTree(&grid_size_current[1][0]);
+  else if (setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_1)
+    DrawChooseTree(&grid_size_current[1][1]);
   else
     DrawSetupScreen_Generic();
 
@@ -7202,6 +7807,24 @@ void RedrawSetupScreenAfterFullscreenToggle()
   }
 }
 
+void RedrawSetupScreenAfterScreenRotation(int nr)
+{
+  int x, y;
+
+  if (setup_mode == SETUP_MODE_TOUCH)
+  {
+    // update virtual button settings (depending on screen orientation)
+    DrawSetupScreen();
+  }
+  else if (setup_mode == SETUP_MODE_CONFIG_VIRT_BUTTONS)
+  {
+    // save already configured virtual buttons
+    for (x = 0; x < MAX_GRID_XSIZE; x++)
+      for (y = 0; y < MAX_GRID_YSIZE; y++)
+       setup.touch.grid_button[nr][x][y] = overlay.grid_button[x][y];
+  }
+}
+
 void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
 {
   if (setup_mode == SETUP_MODE_INPUT)
@@ -7236,6 +7859,16 @@ void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
     HandleChooseTree(mx, my, dx, dy, button, &move_distance_current);
   else if (setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE)
     HandleChooseTree(mx, my, dx, dy, button, &drop_distance_current);
+  else if (setup_mode == SETUP_MODE_CHOOSE_TRANSPARENCY)
+    HandleChooseTree(mx, my, dx, dy, button, &transparency_current);
+  else if (setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_0)
+    HandleChooseTree(mx, my, dx, dy, button, &grid_size_current[0][0]);
+  else if (setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_0)
+    HandleChooseTree(mx, my, dx, dy, button, &grid_size_current[0][1]);
+  else if (setup_mode == SETUP_MODE_CHOOSE_GRID_XSIZE_1)
+    HandleChooseTree(mx, my, dx, dy, button, &grid_size_current[1][0]);
+  else if (setup_mode == SETUP_MODE_CHOOSE_GRID_YSIZE_1)
+    HandleChooseTree(mx, my, dx, dy, button, &grid_size_current[1][1]);
   else
     HandleSetupScreen_Generic(mx, my, dx, dy, button);
 }
@@ -7399,6 +8032,7 @@ static void CreateScreenMenubuttons()
 
     gi = CreateGadget(GDI_CUSTOM_ID, id,
                      GDI_CUSTOM_TYPE_ID, i,
+                     GDI_IMAGE_ID, gfx_unpressed,
                      GDI_INFO_TEXT, menubutton_info[i].infotext,
                      GDI_X, x,
                      GDI_Y, y,
@@ -7466,6 +8100,7 @@ static void CreateScreenScrollbuttons()
 
     gi = CreateGadget(GDI_CUSTOM_ID, id,
                      GDI_CUSTOM_TYPE_ID, i,
+                     GDI_IMAGE_ID, gfx_unpressed,
                      GDI_INFO_TEXT, scrollbutton_info[i].infotext,
                      GDI_X, x,
                      GDI_Y, y,
@@ -7538,6 +8173,7 @@ static void CreateScreenScrollbars()
 
     gi = CreateGadget(GDI_CUSTOM_ID, id,
                      GDI_CUSTOM_TYPE_ID, i,
+                     GDI_IMAGE_ID, gfx_unpressed,
                      GDI_INFO_TEXT, scrollbar_info[i].infotext,
                      GDI_X, x,
                      GDI_Y, y,