rnd-20020513-1-src
[rocksndiamonds.git] / src / libgame / sdl.c
index 18de01b3425b9c109224e28773cd38e506820910..42e6286bf78697605e42c52cef07c04b636c5c60 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2000 Artsoft Entertainment                      *
+* (c) 1994-2001 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -13,6 +13,7 @@
 
 #include "system.h"
 #include "sound.h"
+#include "joystick.h"
 #include "misc.h"
 
 
 /* video functions                                                           */
 /* ========================================================================= */
 
+/* functions from SGE library */
+inline void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
+
+#ifdef PLATFORM_WIN32
+#define FULLSCREEN_BUG
+#endif
+
+/* stuff needed to work around SDL/Windows fullscreen drawing bug */
+static int fullscreen_width;
+static int fullscreen_height;
+static int fullscreen_xoffset;
+static int fullscreen_yoffset;
+static int video_xoffset;
+static int video_yoffset;
+
 inline void SDLInitVideoDisplay(void)
 {
   /* initialize SDL video */
@@ -35,6 +51,38 @@ inline void SDLInitVideoDisplay(void)
 inline void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
                               boolean fullscreen)
 {
+#ifdef FULLSCREEN_BUG
+  int i;
+  static int screen_xy[][2] =
+  {
+    {  640, 480 },
+    {  800, 600 },
+    { 1024, 768 },
+    {   -1,  -1 }
+  };
+#endif
+
+  /* default: normal game window size */
+  fullscreen_width = video.width;
+  fullscreen_height = video.height;
+  fullscreen_xoffset = 0;
+  fullscreen_yoffset = 0;
+
+#ifdef FULLSCREEN_BUG
+  for (i=0; screen_xy[i][0] != -1; i++)
+  {
+    if (video.width <= screen_xy[i][0] && video.height <= screen_xy[i][1])
+    {
+      fullscreen_width = screen_xy[i][0];
+      fullscreen_height = screen_xy[i][1];
+      break;
+    }
+  }
+
+  fullscreen_xoffset = (fullscreen_width - video.width) / 2;
+  fullscreen_yoffset = (fullscreen_height - video.height) / 2;
+#endif
+
   /* open SDL video output device (window or fullscreen mode) */
   if (!SDLSetVideoMode(backbuffer, fullscreen))
     Error(ERR_EXIT, "setting video mode failed");
@@ -62,16 +110,21 @@ inline void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
 inline boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
 {
   boolean success = TRUE;
-  int surface_flags = SDL_HWSURFACE | (fullscreen ? SDL_FULLSCREEN : 0);
+  int surface_flags_fullscreen = SURFACE_FLAGS | SDL_FULLSCREEN;
+  int surface_flags_window = SURFACE_FLAGS;
+  SDL_Surface *new_surface = NULL;
+
+  if (*backbuffer == NULL)
+    *backbuffer = CreateBitmapStruct();
 
   if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
   {
-    /* switch display to fullscreen mode, if available */
-    DrawWindow *window_old = *backbuffer;
-    DrawWindow *window_new = CreateBitmapStruct();
+    video_xoffset = fullscreen_xoffset;
+    video_yoffset = fullscreen_yoffset;
 
-    if ((window_new->surface = SDL_SetVideoMode(video.width, video.height,
-                                               video.depth, surface_flags))
+    /* switch display to fullscreen mode, if available */
+    if ((new_surface = SDL_SetVideoMode(fullscreen_width, fullscreen_height,
+                                       video.depth, surface_flags_fullscreen))
        == NULL)
     {
       /* switching display to fullscreen mode failed */
@@ -83,23 +136,21 @@ inline boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
     }
     else
     {
-      if (window_old)
-       FreeBitmap(window_old);
-      *backbuffer = window_new;
+      (*backbuffer)->surface = new_surface;
 
       video.fullscreen_enabled = TRUE;
       success = TRUE;
     }
   }
 
-  if ((!fullscreen && video.fullscreen_enabled) || !*backbuffer)
+  if ((!fullscreen && video.fullscreen_enabled) || new_surface == NULL)
   {
-    /* switch display to window mode */
-    DrawWindow *window_old = *backbuffer;
-    DrawWindow *window_new = CreateBitmapStruct();
+    video_xoffset = 0;
+    video_yoffset = 0;
 
-    if ((window_new->surface = SDL_SetVideoMode(video.width, video.height,
-                                               video.depth, surface_flags))
+    /* switch display to window mode */
+    if ((new_surface = SDL_SetVideoMode(video.width, video.height,
+                                       video.depth, surface_flags_window))
        == NULL)
     {
       /* switching display to window mode failed -- should not happen */
@@ -109,9 +160,7 @@ inline boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
     }
     else
     {
-      if (window_old)
-       FreeBitmap(window_old);
-      *backbuffer = window_new;
+      (*backbuffer)->surface = new_surface;
 
       video.fullscreen_enabled = FALSE;
       success = TRUE;
@@ -129,11 +178,27 @@ inline void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
   SDL_Rect src_rect, dst_rect;
 
+#ifdef FULLSCREEN_BUG
+  if (src_bitmap == backbuffer)
+  {
+    src_x += video_xoffset;
+    src_y += video_yoffset;
+  }
+#endif
+
   src_rect.x = src_x;
   src_rect.y = src_y;
   src_rect.w = width;
   src_rect.h = height;
 
+#ifdef FULLSCREEN_BUG
+  if (dst_bitmap == backbuffer || dst_bitmap == window)
+  {
+    dst_x += video_xoffset;
+    dst_y += video_yoffset;
+  }
+#endif
+
   dst_rect.x = dst_x;
   dst_rect.y = dst_y;
   dst_rect.w = width;
@@ -157,6 +222,14 @@ inline void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y,
   unsigned int color_g = (color >>  8) && 0xff;
   unsigned int color_b = (color >>  0) && 0xff;
 
+#ifdef FULLSCREEN_BUG
+  if (dst_bitmap == backbuffer || dst_bitmap == window)
+  {
+    x += video_xoffset;
+    y += video_yoffset;
+  }
+#endif
+
   rect.x = x;
   rect.y = y;
   rect.w = width;
@@ -170,9 +243,10 @@ inline void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y,
     SDL_UpdateRect(backbuffer->surface, x, y, width, height);
 }
 
-inline void SDLDrawSimpleLine(SDL_Surface *surface, int from_x, int from_y,
+inline void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
                              int to_x, int to_y, unsigned int color)
 {
+  SDL_Surface *surface = dst_bitmap->surface;
   SDL_Rect rect;
   unsigned int color_r = (color >> 16) & 0xff;
   unsigned int color_g = (color >>  8) & 0xff;
@@ -189,14 +263,32 @@ inline void SDLDrawSimpleLine(SDL_Surface *surface, int from_x, int from_y,
   rect.w = (to_x - from_x + 1);
   rect.h = (to_y - from_y + 1);
 
+#ifdef FULLSCREEN_BUG
+  if (dst_bitmap == backbuffer || dst_bitmap == window)
+  {
+    rect.x += video_xoffset;
+    rect.y += video_yoffset;
+  }
+#endif
+
   SDL_FillRect(surface, &rect,
                SDL_MapRGB(surface->format, color_r, color_g, color_b));
 }
 
-inline void SDLDrawLine(SDL_Surface *surface, int from_x, int from_y,
+inline void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
                        int to_x, int to_y, Uint32 color)
 {
-  sge_Line(surface, from_x, from_y, to_x, to_y, color);
+#ifdef FULLSCREEN_BUG
+  if (dst_bitmap == backbuffer || dst_bitmap == window)
+  {
+    from_x += video_xoffset;
+    from_y += video_yoffset;
+    to_x += video_xoffset;
+    to_y += video_yoffset;
+  }
+#endif
+
+  sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
 }
 
 #if 0
@@ -229,6 +321,60 @@ inline void SDLDrawLines(SDL_Surface *surface, struct XY *points,
 }
 #endif
 
+inline Pixel SDLGetPixel(Bitmap *dst_bitmap, int x, int y)
+{
+  SDL_Surface *surface = dst_bitmap->surface;
+
+#ifdef FULLSCREEN_BUG
+  if (dst_bitmap == backbuffer || dst_bitmap == window)
+  {
+    x += video_xoffset;
+    y += video_yoffset;
+  }
+#endif
+
+  switch (surface->format->BytesPerPixel)
+  {
+    case 1:            /* assuming 8-bpp */
+    {
+      return *((Uint8 *)surface->pixels + y * surface->pitch + x);
+    }
+    break;
+
+    case 2:            /* probably 15-bpp or 16-bpp */
+    {
+      return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
+    }
+    break;
+
+  case 3:              /* slow 24-bpp mode; usually not used */
+    {
+      /* does this work? */
+      Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
+      Uint32 color = 0;
+      int shift;
+
+      shift = surface->format->Rshift;
+      color |= *(pix + shift / 8) >> shift;
+      shift = surface->format->Gshift;
+      color |= *(pix + shift / 8) >> shift;
+      shift = surface->format->Bshift;
+      color |= *(pix + shift / 8) >> shift;
+
+      return color;
+    }
+    break;
+
+  case 4:              /* probably 32-bpp */
+    {
+      return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
+    }
+    break;
+  }
+
+  return 0;
+}
+
 
 /* ========================================================================= */
 /* The following functions have been taken from the SGE library              */
@@ -683,21 +829,33 @@ Bitmap *SDLLoadImage(char *filename)
 
   /* load image to temporary surface */
   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
-    Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
+  {
+    SetError("IMG_Load(): %s", SDL_GetError());
+    return NULL;
+  }
 
   /* create native non-transparent surface for current image */
   if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
-    Error(ERR_EXIT, "SDL_DisplayFormat() failed: %s", SDL_GetError());
+  {
+    SetError("SDL_DisplayFormat(): %s", SDL_GetError());
+    return NULL;
+  }
 
   /* create native transparent surface for current image */
   SDL_SetColorKey(sdl_image_tmp, SDL_SRCCOLORKEY,
                  SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
   if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
-    Error(ERR_EXIT, "SDL_DisplayFormat() failed: %s", SDL_GetError());
+  {
+    SetError("SDL_DisplayFormat(): %s", SDL_GetError());
+    return NULL;
+  }
 
   /* free temporary surface */
   SDL_FreeSurface(sdl_image_tmp);
 
+  new_bitmap->width = new_bitmap->surface->w;
+  new_bitmap->height = new_bitmap->surface->h;
+
   return new_bitmap;
 }
 
@@ -715,7 +873,7 @@ inline void SDLOpenAudio(void)
   }
 
   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, AUDIO_S16,
-                   AUDIO_STEREO_CHANNELS,
+                   AUDIO_NUM_CHANNELS_STEREO,
                    DEFAULT_AUDIO_FRAGMENT_SIZE) < 0)
   {
     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
@@ -727,19 +885,12 @@ inline void SDLOpenAudio(void)
   audio.loops_available = TRUE;
   audio.sound_enabled = TRUE;
 
-  /* determine number of available channels */
-  audio.channels = Mix_AllocateChannels(MIX_CHANNELS);
-
-  if (!audio.mods_available)   /* reserve first channel for music loops */
-  {
-    if (Mix_ReserveChannels(1) == 1)
-      audio.music_channel = 0;
-    else
-      audio.music_available = FALSE;
-  }
+  /* set number of available mixer channels */
+  audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
+  audio.music_channel = MUSIC_CHANNEL;
+  audio.first_sound_channel = FIRST_SOUND_CHANNEL;
 
-  Mix_Volume(-1, SOUND_MAX_VOLUME);
-  Mix_VolumeMusic(SOUND_MAX_VOLUME);
+  Mixer_InitChannels();
 }
 
 inline void SDLCloseAudio(void)
@@ -748,6 +899,162 @@ inline void SDLCloseAudio(void)
   Mix_HaltChannel(-1);
 
   Mix_CloseAudio();
+  SDL_QuitSubSystem(SDL_INIT_AUDIO);
+}
+
+
+/* ========================================================================= */
+/* event functions                                                           */
+/* ========================================================================= */
+
+inline void SDLNextEvent(Event *event)
+{
+  SDL_WaitEvent(event);
+
+#ifdef FULLSCREEN_BUG
+  if (event->type == EVENT_BUTTONPRESS ||
+      event->type == EVENT_BUTTONRELEASE)
+  {
+    if (((ButtonEvent *)event)->x > video_xoffset)
+      ((ButtonEvent *)event)->x -= video_xoffset;
+    else
+      ((ButtonEvent *)event)->x = 0;
+    if (((ButtonEvent *)event)->y > video_yoffset)
+      ((ButtonEvent *)event)->y -= video_yoffset;
+    else
+      ((ButtonEvent *)event)->y = 0;
+  }
+  else if (event->type == EVENT_MOTIONNOTIFY)
+  {
+    if (((ButtonEvent *)event)->x > video_xoffset)
+      ((ButtonEvent *)event)->x -= video_xoffset;
+    else
+      ((ButtonEvent *)event)->x = 0;
+    if (((ButtonEvent *)event)->y > video_yoffset)
+      ((ButtonEvent *)event)->y -= video_yoffset;
+    else
+      ((ButtonEvent *)event)->y = 0;
+  }
+#endif
+}
+
+
+/* ========================================================================= */
+/* joystick functions                                                        */
+/* ========================================================================= */
+
+static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
+static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
+static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
+
+static boolean SDLOpenJoystick(int nr)
+{
+  if (nr < 0 || nr > MAX_PLAYERS)
+    return FALSE;
+
+  return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
+}
+
+static void SDLCloseJoystick(int nr)
+{
+  if (nr < 0 || nr > MAX_PLAYERS)
+    return;
+
+  SDL_JoystickClose(sdl_joystick[nr]);
+}
+
+static boolean SDLCheckJoystickOpened(int nr)
+{
+  if (nr < 0 || nr > MAX_PLAYERS)
+    return FALSE;
+
+  return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
+}
+
+void HandleJoystickEvent(Event *event)
+{
+  switch(event->type)
+  {
+    case SDL_JOYAXISMOTION:
+      if (event->jaxis.axis < 2)
+       sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
+      break;
+
+    case SDL_JOYBUTTONDOWN:
+      if (event->jbutton.button < 2)
+       sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
+      break;
+
+    case SDL_JOYBUTTONUP:
+      if (event->jbutton.button < 2)
+       sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
+      break;
+
+    default:
+      break;
+  }
+}
+
+void SDLInitJoysticks()
+{
+  static boolean sdl_joystick_subsystem_initialized = FALSE;
+  int i;
+
+  if (!sdl_joystick_subsystem_initialized)
+  {
+    sdl_joystick_subsystem_initialized = TRUE;
+
+    if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
+    {
+      Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
+      return;
+    }
+  }
+
+  for (i=0; i<MAX_PLAYERS; i++)
+  {
+    char *device_name = setup.input[i].joy.device_name;
+    int joystick_nr = getJoystickNrFromDeviceName(device_name);
+
+    if (joystick_nr >= SDL_NumJoysticks())
+      joystick_nr = -1;
+
+    /* misuse joystick file descriptor variable to store joystick number */
+    joystick.fd[i] = joystick_nr;
+
+    /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
+    if (SDLCheckJoystickOpened(joystick_nr))
+      SDLCloseJoystick(joystick_nr);
+
+    if (!setup.input[i].use_joystick)
+      continue;
+
+    if (!SDLOpenJoystick(joystick_nr))
+    {
+      Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
+      continue;
+    }
+
+    joystick.status = JOYSTICK_ACTIVATED;
+  }
+}
+
+boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
+{
+  if (nr < 0 || nr >= MAX_PLAYERS)
+    return FALSE;
+
+  if (x != NULL)
+    *x = sdl_js_axis[nr][0];
+  if (y != NULL)
+    *y = sdl_js_axis[nr][1];
+
+  if (b1 != NULL)
+    *b1 = sdl_js_button[nr][0];
+  if (b2 != NULL)
+    *b2 = sdl_js_button[nr][1];
+
+  return TRUE;
 }
 
 #endif /* TARGET_SDL */