improved virtual buttons for touch devices (Android)
[rocksndiamonds.git] / src / libgame / sdl.c
index ba3b6ac41eb601a9623acaa9d2b65b49da8037ac..7221c7afeede8e9655e005b63d98bb8c4e1b51e0 100644 (file)
@@ -37,6 +37,9 @@ static boolean limit_screen_updates = FALSE;
 /* functions from SGE library */
 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
 
+/* functions to draw overlay graphics for touch device input */
+static void DrawTouchInputOverlay();
+
 void SDLLimitScreenUpdates(boolean enable)
 {
   limit_screen_updates = enable;
@@ -129,14 +132,42 @@ static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
     SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
   }
 
+  int xoff = video.screen_xoffset;
+  int yoff = video.screen_yoffset;
+  SDL_Rect dst_rect_screen = { xoff, yoff, video.width, video.height };
   SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
   SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
-#if defined(HAS_SCREEN_KEYBOARD)
-  SDL_Rect src_rect_up = { 0, video.height / 2, video.width, video.height / 2 };
-  SDL_Rect dst_rect_up = { 0, 0,                video.width, video.height / 2 };
 
-  if (video.shifted_up)
+  if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
+      video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
+    dst_rect2 = &dst_rect_screen;
+  else
+    dst_rect1 = &dst_rect_screen;
+
+#if defined(HAS_SCREEN_KEYBOARD)
+  if (video.shifted_up || video.shifted_up_delay)
   {
+    int time_current = SDL_GetTicks();
+    int pos = video.shifted_up_pos;
+    int pos_last = video.shifted_up_pos_last;
+
+    if (!DelayReachedExt(&video.shifted_up_delay, video.shifted_up_delay_value,
+                        time_current))
+    {
+      int delay = time_current - video.shifted_up_delay;
+      int delay_value = video.shifted_up_delay_value;
+
+      pos = pos_last + (pos - pos_last) * delay / delay_value;
+    }
+    else
+    {
+      video.shifted_up_pos_last = pos;
+      video.shifted_up_delay = 0;
+    }
+
+    SDL_Rect src_rect_up = { 0,    pos,  video.width, video.height - pos };
+    SDL_Rect dst_rect_up = { xoff, yoff, video.width, video.height - pos };
+
     if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
        video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
     {
@@ -173,6 +204,9 @@ static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
     SDL_SetRenderTarget(sdl_renderer, NULL);
     SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
   }
+
+  // draw overlay graphics for touch device input, if needed
+  DrawTouchInputOverlay();
 #endif
 
   // global synchronization point of the game to align video frame delay
@@ -534,14 +568,18 @@ static boolean SDLCreateScreen(boolean fullscreen)
 #endif
 #endif
 
+  SDLSetScreenSizeAndOffsets(video.width, video.height);
+
   int width  = video.width;
   int height = video.height;
+  int screen_width  = video.screen_width;
+  int screen_height = video.screen_height;
   int surface_flags = (fullscreen ? surface_flags_fullscreen :
                       surface_flags_window);
 
   // default window size is unscaled
-  video.window_width  = video.width;
-  video.window_height = video.height;
+  video.window_width  = screen_width;
+  video.window_height = screen_height;
 
 #if defined(TARGET_SDL2)
 
@@ -550,8 +588,8 @@ static boolean SDLCreateScreen(boolean fullscreen)
 
   float window_scaling_factor = (float)setup.window_scaling_percent / 100;
 
-  video.window_width  = window_scaling_factor * width;
-  video.window_height = window_scaling_factor * height;
+  video.window_width  = window_scaling_factor * screen_width;
+  video.window_height = window_scaling_factor * screen_height;
 
   if (sdl_texture_stream)
   {
@@ -595,7 +633,7 @@ static boolean SDLCreateScreen(boolean fullscreen)
 
     if (sdl_renderer != NULL)
     {
-      SDL_RenderSetLogicalSize(sdl_renderer, width, height);
+      SDL_RenderSetLogicalSize(sdl_renderer, screen_width, screen_height);
       // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
       SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
 
@@ -781,8 +819,8 @@ void SDLSetWindowScaling(int window_scaling_percent)
     return;
 
   float window_scaling_factor = (float)window_scaling_percent / 100;
-  int new_window_width  = (int)(window_scaling_factor * video.width);
-  int new_window_height = (int)(window_scaling_factor * video.height);
+  int new_window_width  = (int)(window_scaling_factor * video.screen_width);
+  int new_window_height = (int)(window_scaling_factor * video.screen_height);
 
   SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
 
@@ -854,6 +892,67 @@ void SDLSetWindowFullscreen(boolean fullscreen)
     video.fullscreen_initial = FALSE;
   }
 }
+
+void SDLSetDisplaySize()
+{
+  SDL_Rect display_bounds;
+
+  SDL_GetDisplayBounds(0, &display_bounds);
+
+  video.display_width  = display_bounds.w;
+  video.display_height = display_bounds.h;
+
+#if 0
+  Error(ERR_DEBUG, "SDL real screen size: %d x %d",
+       video.display_width, video.display_height);
+#endif
+}
+
+void SDLSetScreenSizeAndOffsets(int width, int height)
+{
+  // set default video screen size and offsets
+  video.screen_width = width;
+  video.screen_height = height;
+  video.screen_xoffset = 0;
+  video.screen_yoffset = 0;
+
+#if defined(USE_COMPLETE_DISPLAY)
+  float ratio_video   = (float) width / height;
+  float ratio_display = (float) video.display_width / video.display_height;
+
+  if (ratio_video != ratio_display)
+  {
+    // adjust drawable screen size to cover the whole device display
+
+    if (ratio_video < ratio_display)
+      video.screen_width  *= ratio_display / ratio_video;
+    else
+      video.screen_height *= ratio_video / ratio_display;
+
+    video.screen_xoffset = (video.screen_width  - width)  / 2;
+    video.screen_yoffset = (video.screen_height - height) / 2;
+
+#if 0
+    Error(ERR_DEBUG, "Changing screen from %dx%d to %dx%d (%.2f to %.2f)",
+         width, height,
+         video.screen_width, video.screen_height,
+         ratio_video, ratio_display);
+#endif
+  }
+#endif
+}
+
+void SDLSetScreenSizeForRenderer(int width, int height)
+{
+  SDL_RenderSetLogicalSize(sdl_renderer, width, height);
+}
+
+void SDLSetScreenProperties()
+{
+  SDLSetScreenSizeAndOffsets(video.width, video.height);
+  SDLSetScreenSizeForRenderer(video.screen_width, video.screen_height);
+}
+
 #endif
 
 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
@@ -2585,3 +2684,98 @@ boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
 
   return TRUE;
 }
+
+static void DrawTouchInputOverlay()
+{
+#if defined(USE_TOUCH_INPUT_OVERLAY)
+  static SDL_Texture *texture = NULL;
+  static boolean initialized = FALSE;
+  static boolean deactivated = TRUE;
+  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 = 0;
+
+  if (!overlay.active && deactivated)
+    return;
+
+  if (overlay.active)
+  {
+    if (alpha < alpha_max)
+      alpha = MIN(alpha + alpha_step, alpha_max);
+
+    deactivated = FALSE;
+  }
+  else
+  {
+    alpha = MAX(0, alpha - alpha_step);
+
+    if (alpha == 0)
+      deactivated = TRUE;
+  }
+
+  if (!initialized)
+  {
+    char *basename = "overlay/VirtualButtons.png";
+    char *filename = getCustomImageFilename(basename);
+
+    if (filename == NULL)
+      Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
+
+    SDL_Surface *surface;
+
+    if ((surface = IMG_Load(filename)) == NULL)
+      Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
+
+    width  = surface->w;
+    height = surface->h;
+
+    /* set black pixel to transparent if no alpha channel / transparent color */
+    if (!SDLHasAlpha(surface) &&
+       !SDLHasColorKey(surface))
+      SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
+                     SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
+
+    if ((texture = SDLCreateTextureFromSurface(surface)) == NULL)
+      Error(ERR_EXIT, "SDLCreateTextureFromSurface() failed");
+
+    SDL_FreeSurface(surface);
+
+    SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
+    SDL_SetTextureAlphaMod(texture, alpha_max);
+
+    initialized = TRUE;
+  }
+
+  if (alpha != alpha_last)
+    SDL_SetTextureAlphaMod(texture, alpha);
+
+  alpha_last = alpha;
+
+  float ratio_overlay = (float) width / height;
+  float ratio_screen = (float) video.screen_width / video.screen_height;
+  int width_scaled, height_scaled;
+  int xpos, ypos;
+
+  if (ratio_overlay > ratio_screen)
+  {
+    width_scaled = video.screen_width;
+    height_scaled = video.screen_height * ratio_screen / ratio_overlay;
+    xpos = 0;
+    ypos = video.screen_height - height_scaled;
+  }
+  else
+  {
+    width_scaled = video.screen_width * ratio_overlay / ratio_screen;
+    height_scaled = video.screen_height;
+    xpos = (video.screen_width - width_scaled) / 2;
+    ypos = 0;
+  }
+
+  SDL_Rect src_rect = { 0, 0, width, height };
+  SDL_Rect dst_rect = { xpos, ypos, width_scaled, height_scaled };
+
+  SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
+#endif
+}