added empty borders between different virtual buttons
[rocksndiamonds.git] / src / libgame / sdl.c
index 5586628b40dabf1385a5ae09472828f079e5501b..0fd3aa62eb6a319d7523357ce0419e4b2d0f455f 100644 (file)
@@ -17,6 +17,8 @@
 
 #define ENABLE_UNUSED_CODE     0       /* currently unused functions */
 
+#define DEBUG_JOYSTICKS                0
+
 
 /* ========================================================================= */
 /* video functions                                                           */
@@ -60,6 +62,10 @@ static void FinalizeScreen(int draw_target)
   // copy global animations to render target buffer, if defined (above border)
   if (gfx.draw_global_anim_function != NULL)
     gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
+
+  // copy tile selection cursor to render target buffer, if defined (above all)
+  if (gfx.draw_tile_cursor_function != NULL)
+    gfx.draw_tile_cursor_function(draw_target);
 }
 
 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
@@ -532,11 +538,7 @@ inline static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen)
   SDLSetWindowIcon(program.icon_filename);
 
   /* set window and icon title */
-#if defined(TARGET_SDL2)
-  SDL_SetWindowTitle(sdl_window, program.window_title);
-#else
-  SDL_WM_SetCaption(program.window_title, program.window_title);
-#endif
+  SDLSetWindowTitle();
 }
 
 inline static void SDLInitVideoBuffer_DrawBuffer()
@@ -830,6 +832,9 @@ boolean SDLSetVideoMode(boolean fullscreen)
 void SDLSetWindowTitle()
 {
 #if defined(TARGET_SDL2)
+  if (sdl_window == NULL)
+    return;
+
   SDL_SetWindowTitle(sdl_window, program.window_title);
 #else
   SDL_WM_SetCaption(program.window_title, program.window_title);
@@ -2535,7 +2540,7 @@ void SDLCloseAudio(void)
 /* event functions                                                           */
 /* ========================================================================= */
 
-void SDLNextEvent(Event *event)
+void SDLWaitEvent(Event *event)
 {
   SDL_WaitEvent(event);
 }
@@ -2601,9 +2606,24 @@ static int sdl_js_axis[MAX_PLAYERS][2];
 static int sdl_js_button[MAX_PLAYERS][2];
 static boolean sdl_is_controller[MAX_PLAYERS];
 
-static boolean SDLOpenJoystick(int nr)
+void SDLClearJoystickState()
+{
+  int i, j;
+
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    for (j = 0; j < 2; j++)
+    {
+      sdl_js_axis_raw[i][j] = -1;
+      sdl_js_axis[i][j] = 0;
+      sdl_js_button[i][j] = 0;
+    }
+  }
+}
+
+boolean SDLOpenJoystick(int nr)
 {
-  if (nr < 0 || nr > MAX_PLAYERS)
+  if (nr < 0 || nr >= MAX_PLAYERS)
     return FALSE;
 
 #if defined(TARGET_SDL2)
@@ -2612,7 +2632,7 @@ static boolean SDLOpenJoystick(int nr)
   sdl_is_controller[nr] = FALSE;
 #endif
 
-#if 1
+#if DEBUG_JOYSTICKS
   Error(ERR_DEBUG, "opening joystick %d (%s)",
        nr, (sdl_is_controller[nr] ? "game controller" : "joystick"));
 #endif
@@ -2629,12 +2649,12 @@ static boolean SDLOpenJoystick(int nr)
   return (sdl_joystick[nr] != NULL);
 }
 
-static void SDLCloseJoystick(int nr)
+void SDLCloseJoystick(int nr)
 {
-  if (nr < 0 || nr > MAX_PLAYERS)
+  if (nr < 0 || nr >= MAX_PLAYERS)
     return;
 
-#if 1
+#if DEBUG_JOYSTICKS
   Error(ERR_DEBUG, "closing joystick %d", nr);
 #endif
 
@@ -2648,18 +2668,11 @@ static void SDLCloseJoystick(int nr)
 #endif
 
   sdl_joystick[nr] = NULL;
-
-  sdl_js_axis_raw[nr][0] = -1;
-  sdl_js_axis_raw[nr][1] = -1;
-  sdl_js_axis[nr][0] = 0;
-  sdl_js_axis[nr][1] = 0;
-  sdl_js_button[nr][0] = 0;
-  sdl_js_button[nr][1] = 0;
 }
 
 boolean SDLCheckJoystickOpened(int nr)
 {
-  if (nr < 0 || nr > MAX_PLAYERS)
+  if (nr < 0 || nr >= MAX_PLAYERS)
     return FALSE;
 
 #if defined(TARGET_SDL2)
@@ -2680,6 +2693,9 @@ static void setJoystickAxis(int nr, int axis_id_raw, int axis_value)
   int axis_id = axis_id_raw % 2;
 #endif
 
+  if (nr < 0 || nr >= MAX_PLAYERS)
+    return;
+
   if (axis_id == -1)
     return;
 
@@ -2723,6 +2739,9 @@ static void setJoystickButton(int nr, int button_id_raw, int button_state)
   int button_id = button_id_raw % 2;
 #endif
 
+  if (nr < 0 || nr >= MAX_PLAYERS)
+    return;
+
   if (button_id == -1)
     return;
 
@@ -2734,8 +2753,24 @@ void HandleJoystickEvent(Event *event)
   switch(event->type)
   {
 #if defined(TARGET_SDL2)
+    case SDL_CONTROLLERDEVICEADDED:
+#if DEBUG_JOYSTICKS
+      Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added",
+           event->cdevice.which);
+#endif
+      InitJoysticks();
+      break;
+
+    case SDL_CONTROLLERDEVICEREMOVED:
+#if DEBUG_JOYSTICKS
+      Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed",
+           event->cdevice.which);
+#endif
+      InitJoysticks();
+      break;
+
     case SDL_CONTROLLERAXISMOTION:
-#if 1
+#if DEBUG_JOYSTICKS
       Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d",
            event->caxis.which, event->caxis.axis, event->caxis.value);
 #endif
@@ -2745,7 +2780,7 @@ void HandleJoystickEvent(Event *event)
       break;
 
     case SDL_CONTROLLERBUTTONDOWN:
-#if 1
+#if DEBUG_JOYSTICKS
       Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d",
            event->cbutton.which, event->cbutton.button);
 #endif
@@ -2755,7 +2790,7 @@ void HandleJoystickEvent(Event *event)
       break;
 
     case SDL_CONTROLLERBUTTONUP:
-#if 1
+#if DEBUG_JOYSTICKS
       Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d",
            event->cbutton.which, event->cbutton.button);
 #endif
@@ -2769,7 +2804,7 @@ void HandleJoystickEvent(Event *event)
       if (sdl_is_controller[event->jaxis.which])
        break;
 
-#if 1
+#if DEBUG_JOYSTICKS
       Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d",
            event->jaxis.which, event->jaxis.axis, event->jaxis.value);
 #endif
@@ -2783,7 +2818,7 @@ void HandleJoystickEvent(Event *event)
       if (sdl_is_controller[event->jaxis.which])
        break;
 
-#if 1
+#if DEBUG_JOYSTICKS
       Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d",
            event->jbutton.which, event->jbutton.button);
 #endif
@@ -2797,7 +2832,7 @@ void HandleJoystickEvent(Event *event)
       if (sdl_is_controller[event->jaxis.which])
        break;
 
-#if 1
+#if DEBUG_JOYSTICKS
       Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d",
            event->jbutton.which, event->jbutton.button);
 #endif
@@ -2817,7 +2852,10 @@ void SDLInitJoysticks()
   static boolean sdl_joystick_subsystem_initialized = FALSE;
   boolean print_warning = !sdl_joystick_subsystem_initialized;
 #if defined(TARGET_SDL2)
-  char *mappings_file = "gamecontrollerdb.txt";
+  char *mappings_file_base = getPath2(options.conf_directory,
+                                     GAMECONTROLLER_BASENAME);
+  char *mappings_file_user = getPath2(getUserGameDataDir(),
+                                     GAMECONTROLLER_BASENAME);
   int num_mappings;
 #endif
   int i;
@@ -2839,15 +2877,32 @@ void SDLInitJoysticks()
     }
 
 #if defined(TARGET_SDL2)
-    num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file);
+    num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base);
 
-    if (num_mappings != -1)
-      Error(ERR_INFO, "%d game controller mapping(s) added", num_mappings);
+    /* the included game controller base mappings should always be found */
+    if (num_mappings == -1)
+      Error(ERR_WARN, "no game controller base mappings found");
+#if DEBUG_JOYSTICKS
     else
-      Error(ERR_WARN, "no game controller mappings found");
+      Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings);
+#endif
+
+    num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user);
+
+#if DEBUG_JOYSTICKS
+    /* the personal game controller user mappings may or may not be found */
+    if (num_mappings == -1)
+      Error(ERR_WARN, "no game controller user mappings found");
+    else
+      Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings);
 
     Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks());
+#endif
+
+    checked_free(mappings_file_base);
+    checked_free(mappings_file_user);
 
+#if DEBUG_JOYSTICKS
     for (i = 0; i < SDL_NumJoysticks(); i++)
     {
       const char *name, *type;
@@ -2866,6 +2921,7 @@ void SDLInitJoysticks()
       Error(ERR_INFO, "- joystick %d (%s): '%s'",
            i, type, (name ? name : "(Unknown)"));
     }
+#endif
 #endif
   }
 
@@ -2884,8 +2940,8 @@ void SDLInitJoysticks()
       joystick_nr = -1;
     }
 
-    /* misuse joystick file descriptor variable to store joystick number */
-    joystick.fd[i] = joystick_nr;
+    /* store configured joystick number for each player */
+    joystick.nr[i] = joystick_nr;
   }
 
   /* now open all connected joysticks (regardless if configured or not) */
@@ -2900,6 +2956,8 @@ void SDLInitJoysticks()
     else if (print_warning)
       Error(ERR_WARN, "cannot open joystick %d", i);
   }
+
+  SDLClearJoystickState();
 }
 
 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
@@ -2920,22 +2978,141 @@ boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
   return TRUE;
 }
 
+
+/* ========================================================================= */
+/* touch input overlay functions                                             */
+/* ========================================================================= */
+
 #if defined(USE_TOUCH_INPUT_OVERLAY)
+static void DrawTouchInputOverlay_ShowGrid(int alpha)
+{
+  SDL_Rect rect;
+  int grid_xsize = overlay.grid_xsize;
+  int grid_ysize = overlay.grid_ysize;
+  int x, y;
+
+  SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
+  SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
+
+  for (x = 0; x < grid_xsize; x++)
+  {
+    rect.x = (x + 0) * video.screen_width / grid_xsize;
+    rect.w = (x + 1) * video.screen_width / grid_xsize - rect.x;
+
+    for (y = 0; y < grid_ysize; y++)
+    {
+      rect.y = (y + 0) * video.screen_height / grid_ysize;
+      rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
+
+      if (overlay.grid_button[x][y] == CHAR_GRID_BUTTON_NONE)
+       SDL_RenderDrawRect(sdl_renderer, &rect);
+    }
+  }
+
+  SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
+}
+
+static void DrawTouchInputOverlay_ShowGridButtons(int alpha)
+{
+  static int alpha_max = SDL_ALPHA_OPAQUE / 2;
+  static int alpha_step = 5;
+  static int alpha_direction = 0;
+  static int alpha_highlight = 0;
+  SDL_Rect rect;
+  int grid_xsize = overlay.grid_xsize;
+  int grid_ysize = overlay.grid_ysize;
+  int x, y;
+
+  if (alpha == alpha_max)
+  {
+    if (alpha_direction < 0)
+    {
+      alpha_highlight = MAX(0, alpha_highlight - alpha_step);
+
+      if (alpha_highlight == 0)
+       alpha_direction = 1;
+    }
+    else
+    {
+      alpha_highlight = MIN(alpha_highlight + alpha_step, alpha_max);
+
+      if (alpha_highlight == alpha_max)
+       alpha_direction = -1;
+    }
+  }
+  else
+  {
+    alpha_direction = 1;
+    alpha_highlight = alpha;
+  }
+
+  SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
+
+  for (x = 0; x < grid_xsize; x++)
+  {
+    for (y = 0; y < grid_ysize; y++)
+    {
+      rect.x = (x + 0) * video.screen_width  / grid_xsize;
+      rect.y = (y + 0) * video.screen_height / grid_ysize;
+      rect.w = (x + 1) * video.screen_width  / grid_xsize - rect.x;
+      rect.h = (y + 1) * video.screen_height / grid_ysize - rect.y;
+
+      if (x == 0 ||
+         overlay.grid_button[x - 1][y] != overlay.grid_button[x][y])
+      {
+       rect.x += 2;
+       rect.w -= 2;
+      }
+
+      if (x == grid_xsize - 1 ||
+         overlay.grid_button[x + 1][y] != overlay.grid_button[x][y])
+      {
+       rect.w -= 2;
+      }
+
+      if (y == 0 ||
+         overlay.grid_button[x][y - 1] != overlay.grid_button[x][y])
+      {
+       rect.y += 2;
+       rect.h -= 2;
+      }
+
+      if (y == grid_ysize - 1 ||
+         overlay.grid_button[x][y + 1] != overlay.grid_button[x][y])
+      {
+       rect.h -= 2;
+      }
+
+      if (overlay.grid_button[x][y] == overlay.grid_button_highlight)
+       SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha_highlight);
+      else
+       SDL_SetRenderDrawColor(sdl_renderer, 255, 255, 255, alpha);
+
+      if (overlay.grid_button[x][y] != CHAR_GRID_BUTTON_NONE)
+       SDL_RenderFillRect(sdl_renderer, &rect);
+    }
+  }
+
+  SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
+}
+
 static void DrawTouchInputOverlay()
 {
   static SDL_Texture *texture = NULL;
   static boolean initialized = FALSE;
   static boolean deactivated = TRUE;
+  static boolean show_grid = FALSE;
   static int width = 0, height = 0;
   static int alpha_max = SDL_ALPHA_OPAQUE / 2;
   static int alpha_step = 5;
-  static int alpha_last = 0;
+  static int alpha_last = -1;
   static int alpha = 0;
+  boolean active = (overlay.enabled && overlay.active);
 
-  if (!overlay.active && deactivated)
+  if (!active && deactivated)
     return;
 
-  if (overlay.active)
+  if (active)
   {
     if (alpha < alpha_max)
       alpha = MIN(alpha + alpha_step, alpha_max);
@@ -2950,6 +3127,21 @@ static void DrawTouchInputOverlay()
       deactivated = TRUE;
   }
 
+  if (overlay.show_grid)
+    show_grid = TRUE;
+  else if (deactivated)
+    show_grid = FALSE;
+
+  if (show_grid)
+    DrawTouchInputOverlay_ShowGrid(alpha);
+
+  DrawTouchInputOverlay_ShowGridButtons(alpha);
+
+  return;
+
+
+  // !!! VIRTUAL BUTTONS FROM IMAGE FILE NOT USED ANYMORE !!!
+
   if (!initialized)
   {
     char *basename = "overlay/VirtualButtons.png";
@@ -2978,7 +3170,6 @@ static void DrawTouchInputOverlay()
     SDL_FreeSurface(surface);
 
     SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
-    SDL_SetTextureAlphaMod(texture, alpha_max);
 
     initialized = TRUE;
   }