diff --git a/Makefile b/Makefile
index e4b04e2d9e..95f6cae0b5 100644
--- a/Makefile
+++ b/Makefile
@@ -67,7 +67,7 @@ ifeq ($(HAVE_PULSE), 1)
endif
ifeq ($(HAVE_SDL), 1)
- OBJ += gfx/gl.o input/sdl.o audio/sdl.o audio/buffer.o
+ OBJ += gfx/sdl.o gfx/gl.o input/sdl.o audio/sdl.o audio/buffer.o
DEFINES += $(SDL_CFLAGS)
LIBS += $(SDL_LIBS)
ifeq ($(OSX),1)
diff --git a/Makefile.win32 b/Makefile.win32
index 1bb41fde16..ad8daf0546 100644
--- a/Makefile.win32
+++ b/Makefile.win32
@@ -33,7 +33,7 @@ else
endif
ifeq ($(HAVE_SDL), 1)
- OBJ += gfx/gl.o input/sdl.o audio/sdl.o audio/buffer.o
+ OBJ += gfx/sdl.o gfx/gl.o input/sdl.o audio/sdl.o audio/buffer.o
LIBS += $(SDL_LIBS) -lopengl32
DEFINES += $(SDL_CFLAGS) -DHAVE_SDL
endif
diff --git a/config.def.h b/config.def.h
index bec4ecd141..aba9548a2b 100644
--- a/config.def.h
+++ b/config.def.h
@@ -44,6 +44,7 @@
///////////////// Drivers
#define VIDEO_GL 0
#define VIDEO_XVIDEO 11
+#define VIDEO_SDL 13
////////////////////////
#define AUDIO_RSOUND 1
#define AUDIO_OSS 2
diff --git a/driver.c b/driver.c
index ebb460a580..70e8807d15 100644
--- a/driver.c
+++ b/driver.c
@@ -61,6 +61,7 @@ static const audio_driver_t *audio_drivers[] = {
static const video_driver_t *video_drivers[] = {
#ifdef HAVE_SDL
&video_gl,
+ &video_sdl,
#endif
#ifdef HAVE_XVIDEO
&video_xvideo,
diff --git a/driver.h b/driver.h
index 02c3b6ed32..c9f2fb3612 100644
--- a/driver.h
+++ b/driver.h
@@ -111,7 +111,7 @@ typedef struct input_driver
typedef struct video_driver
{
- void* (*init)(video_info_t *video, const input_driver_t **input, void **input_data);
+ void* (*init)(const video_info_t *video, const input_driver_t **input, void **input_data);
// Should the video driver act as an input driver as well? :) The video init might preinitialize an input driver to override the settings in case the video driver relies on input driver for event handling, e.g.
bool (*frame)(void* data, const void* frame, unsigned width, unsigned height, unsigned pitch, const char *msg); // msg is for showing a message on the screen along with the video frame.
void (*set_nonblock_state)(void* data, bool toggle); // Should we care about syncing to vblank? Fast forwarding.
@@ -156,6 +156,7 @@ extern const audio_driver_t audio_xa;
extern const audio_driver_t audio_pulse;
extern const video_driver_t video_gl;
extern const video_driver_t video_xvideo;
+extern const video_driver_t video_sdl;
extern const input_driver_t input_sdl;
extern const input_driver_t input_x;
////////////////////////////////////////////////
diff --git a/gfx/gl.c b/gfx/gl.c
index 25b67e0578..dd50d4e63a 100644
--- a/gfx/gl.c
+++ b/gfx/gl.c
@@ -893,7 +893,7 @@ static void gl_set_nonblock_state(void *data, bool state)
}
}
-static void* gl_init(video_info_t *video, const input_driver_t **input, void **input_data)
+static void* gl_init(const video_info_t *video, const input_driver_t **input, void **input_data)
{
if (SDL_Init(SDL_INIT_VIDEO) < 0)
return NULL;
diff --git a/gfx/sdl.c b/gfx/sdl.c
new file mode 100644
index 0000000000..a7763c11d4
--- /dev/null
+++ b/gfx/sdl.c
@@ -0,0 +1,160 @@
+/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
+ * Copyright (C) 2010-2011 - Hans-Kristian Arntzen
+ *
+ * Some code herein may be based on code found in BSNES.
+ *
+ * SSNES is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with SSNES.
+ * If not, see .
+ */
+
+#include "SDL.h"
+#include "driver.h"
+#include
+#include
+#include
+#include "general.h"
+#include "input/ssnes_sdl_input.h"
+
+typedef struct sdl_video sdl_video_t;
+struct sdl_video
+{
+ SDL_Surface *screen, *buffer;
+ bool quitting;
+};
+
+static void sdl_gfx_free(void *data)
+{
+ sdl_video_t *vid = data;
+ if (!vid)
+ return;
+
+ if (vid->buffer)
+ SDL_FreeSurface(vid->screen);
+
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+
+ free(vid);
+}
+
+static void* sdl_gfx_init(const video_info_t *video, const input_driver_t **input, void **input_data)
+{
+ SDL_InitSubSystem(SDL_INIT_VIDEO);
+
+ sdl_video_t *vid = calloc(1, sizeof(*vid));
+ if (!vid)
+ return NULL;
+
+ const SDL_VideoInfo *video_info = SDL_GetVideoInfo();
+ assert(video_info);
+ unsigned full_x = video_info->current_w;
+ unsigned full_y = video_info->current_h;
+ SSNES_LOG("Detecting desktop resolution %ux%u.\n", full_x, full_y);
+
+ vid->screen = SDL_SetVideoMode(video->width, video->height, 15, SDL_HWSURFACE | (video->fullscreen ? SDL_FULLSCREEN : 0));
+ if (!vid->screen)
+ {
+ SSNES_ERR("Failed to init SDL surface.\n");
+ goto error;
+ }
+
+ SDL_ShowCursor(SDL_DISABLE);
+ vid->buffer = SDL_CreateRGBSurface(SDL_SWSURFACE, 256 * video->input_scale, 256 * video->input_scale, 15,
+ 0x7c00, 0x03e0, 0x001f, 0);
+ if (!vid->buffer)
+ {
+ SSNES_ERR("SDL_CreateRGBSurface failed: %s\n", SDL_GetError());
+ goto error;
+ }
+
+ sdl_input_t *sdl_input = input_sdl.init();
+ if (sdl_input)
+ {
+ sdl_input->quitting = &vid->quitting;
+ *input = &input_sdl;
+ *input_data = sdl_input;
+ }
+ else
+ *input = NULL;
+
+ return vid;
+
+error:
+ sdl_gfx_free(vid);
+ return NULL;
+}
+
+static bool sdl_gfx_frame(void *data, const void* frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
+{
+ (void)msg;
+ sdl_video_t *vid = data;
+
+ if (SDL_MUSTLOCK(vid->buffer))
+ SDL_LockSurface(vid->buffer);
+
+ for (unsigned y = 0; y < height; y++)
+ {
+ uint16_t *dest = (uint16_t*)vid->buffer->pixels + ((y * vid->buffer->pitch) >> 1);
+ const uint16_t *src = (const uint16_t*)frame + ((y * pitch) >> 1);
+ memcpy(dest, src, width * sizeof(uint16_t));
+ }
+
+ if (SDL_MUSTLOCK(vid->buffer))
+ SDL_UnlockSurface(vid->buffer);
+
+ SDL_Rect src = {
+ .x = 0,
+ .y = 0,
+ .w = width,
+ .h = height
+ };
+
+ SDL_Rect dest = {
+ .x = 0,
+ .y = 0,
+ .w = vid->screen->w,
+ .h = vid->screen->h
+ };
+
+ SDL_SoftStretch(vid->buffer, &src, vid->screen, &dest);
+ SDL_UpdateRect(vid->screen, dest.x, dest.y, dest.w, dest.h);
+
+ return true;
+}
+
+static void sdl_gfx_set_nonblock_state(void *data, bool state)
+{
+ (void)data; // Can SDL even do this?
+ (void)state;
+}
+
+static bool sdl_gfx_alive(void *data)
+{
+ sdl_video_t *vid = data;
+ return !vid->quitting;
+}
+
+static bool sdl_gfx_focus(void *data)
+{
+ (void)data;
+ return (SDL_GetAppState() & (SDL_APPINPUTFOCUS | SDL_APPACTIVE)) == (SDL_APPINPUTFOCUS | SDL_APPACTIVE);
+}
+
+
+const video_driver_t video_sdl = {
+ .init = sdl_gfx_init,
+ .frame = sdl_gfx_frame,
+ .alive = sdl_gfx_alive,
+ .set_nonblock_state = sdl_gfx_set_nonblock_state,
+ .focus = sdl_gfx_focus,
+ .free = sdl_gfx_free,
+ .ident = "sdl"
+};
+
diff --git a/gfx/xvideo.c b/gfx/xvideo.c
index d287e836cd..e0fe94c160 100644
--- a/gfx/xvideo.c
+++ b/gfx/xvideo.c
@@ -298,7 +298,7 @@ static void render32_uyvy(xv_t *xv, const void *input_, unsigned width, unsigned
}
-static void* xv_init(video_info_t *video, const input_driver_t **input, void **input_data)
+static void* xv_init(const video_info_t *video, const input_driver_t **input, void **input_data)
{
xv_t *xv = calloc(1, sizeof(*xv));
if (!xv)
diff --git a/settings.c b/settings.c
index 4d63bca723..58b963e29a 100644
--- a/settings.c
+++ b/settings.c
@@ -47,6 +47,9 @@ static void set_defaults(void)
case VIDEO_XVIDEO:
def_video = "xvideo";
break;
+ case VIDEO_SDL:
+ def_video = "sdl";
+ break;
default:
break;
}