blob: 2f1648203cb156a325e3ccaf0265b3ef4f6ed31f [file] [log] [blame]
#include <SDL/SDL.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include "sdldriver.h"
#define SDL_NATIVE(obj) (VISUAL_CHECK_CAST ((obj), SDLNative))
typedef struct _SDLNative SDLNative;
static SDL_GLattr __lv_sdl_gl_attribute_map[] = {
[VISUAL_GL_ATTRIBUTE_NONE] = -1,
[VISUAL_GL_ATTRIBUTE_BUFFER_SIZE] = SDL_GL_BUFFER_SIZE,
[VISUAL_GL_ATTRIBUTE_LEVEL] = -1,
[VISUAL_GL_ATTRIBUTE_RGBA] = -1,
[VISUAL_GL_ATTRIBUTE_DOUBLEBUFFER] = SDL_GL_DOUBLEBUFFER,
[VISUAL_GL_ATTRIBUTE_STEREO] = SDL_GL_STEREO,
[VISUAL_GL_ATTRIBUTE_AUX_BUFFERS] = -1,
[VISUAL_GL_ATTRIBUTE_RED_SIZE] = SDL_GL_RED_SIZE,
[VISUAL_GL_ATTRIBUTE_GREEN_SIZE] = SDL_GL_GREEN_SIZE,
[VISUAL_GL_ATTRIBUTE_BLUE_SIZE] = SDL_GL_BLUE_SIZE,
[VISUAL_GL_ATTRIBUTE_ALPHA_SIZE] = SDL_GL_ALPHA_SIZE,
[VISUAL_GL_ATTRIBUTE_DEPTH_SIZE] = SDL_GL_DEPTH_SIZE,
[VISUAL_GL_ATTRIBUTE_STENCIL_SIZE] = SDL_GL_STENCIL_SIZE,
[VISUAL_GL_ATTRIBUTE_ACCUM_RED_SIZE] = SDL_GL_ACCUM_RED_SIZE,
[VISUAL_GL_ATTRIBUTE_ACCUM_GREEN_SIZE] = SDL_GL_ACCUM_GREEN_SIZE,
[VISUAL_GL_ATTRIBUTE_ACCUM_BLUE_SIZE] = SDL_GL_ACCUM_BLUE_SIZE,
[VISUAL_GL_ATTRIBUTE_ACCUM_ALPHA_SIZE] = SDL_GL_ACCUM_ALPHA_SIZE,
[VISUAL_GL_ATTRIBUTE_LAST] = -1
};
static int native_create (SADisplay *display, VisVideoDepth depth, VisVideoAttributeOptions *vidoptions,
int width, int height, int resizable);
static int native_close (SADisplay *display);
static int native_lock (SADisplay *display);
static int native_unlock (SADisplay *display);
static int native_fullscreen (SADisplay *display, int fullscreen, int autoscale);
static int native_getvideo (SADisplay *display, VisVideo *screen);
static int native_updaterect (SADisplay *display, VisRectangle *rect);
static int native_drainevents (SADisplay *display, VisEventQueue *eventqueue);
static int get_nearest_resolution (SADisplay *display, int *width, int *height);
static int sdl_initialized;
struct _SDLNative {
VisObject object;
SDL_Surface *screen;
VisVideoDepth requested_depth;
int oldx;
int oldy;
int oldwidth;
int oldheight;
int resizable;
int active;
};
SADisplayDriver *sdl_driver_new ()
{
SADisplayDriver *driver;
driver = visual_mem_new0 (SADisplayDriver, 1);
visual_object_initialize (VISUAL_OBJECT (driver), TRUE, NULL);
driver->create = native_create;
driver->close = native_close;
driver->lock = native_lock;
driver->unlock = native_unlock;
driver->fullscreen = native_fullscreen;
driver->getvideo = native_getvideo;
driver->updaterect = native_updaterect;
driver->drainevents = native_drainevents;
return driver;
}
static int native_create (SADisplay *display, VisVideoDepth depth, VisVideoAttributeOptions *vidoptions,
int width, int height, int resizable)
{
SDLNative *native = SDL_NATIVE (display->native);
const SDL_VideoInfo *videoinfo;
int videoflags = 0;
int bpp;
int i;
if (native == NULL) {
native = visual_mem_new0 (SDLNative, 1);
visual_object_initialize (VISUAL_OBJECT (native), TRUE, NULL);
}
if (native->screen != NULL) {
SDL_FreeSurface (native->screen);
}
videoflags |= resizable ? SDL_RESIZABLE : 0;
if (sdl_initialized == FALSE) {
if (SDL_Init (SDL_INIT_VIDEO) < 0) {
fprintf (stderr, "Unable to init SDL VIDEO: %s\n", SDL_GetError ());
exit (0);
}
sdl_initialized = TRUE;
}
native->resizable = resizable;
native->requested_depth = depth;
if (depth == VISUAL_VIDEO_DEPTH_GL) {
videoinfo = SDL_GetVideoInfo ();
if (!videoinfo) {
return -1;
}
videoflags |= SDL_OPENGL | SDL_GL_DOUBLEBUFFER | SDL_HWPALETTE;
if (videoinfo->hw_available)
videoflags |= SDL_HWSURFACE;
else
videoflags |= SDL_SWSURFACE;
if (videoinfo->blit_hw)
videoflags |= SDL_HWACCEL;
if (vidoptions != NULL) {
for (i = VISUAL_GL_ATTRIBUTE_NONE; i < VISUAL_GL_ATTRIBUTE_LAST; i++) {
if (vidoptions->gl_attributes[i].mutated == TRUE) {
SDL_GLattr sdl_attribute =
__lv_sdl_gl_attribute_map[
vidoptions->gl_attributes[i].attribute];
if (sdl_attribute < 0)
continue;
SDL_GL_SetAttribute (sdl_attribute, vidoptions->gl_attributes[i].value);
}
}
}
bpp = videoinfo->vfmt->BitsPerPixel;
native->screen = SDL_SetVideoMode (width, height, bpp, videoflags);
} else {
native->screen = SDL_SetVideoMode (width, height,
visual_video_depth_value_from_enum (depth),
videoflags);
}
SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
display->native = VISUAL_OBJECT (native);
return 0;
}
static int native_close (SADisplay *display)
{
return 0;
}
static int native_lock (SADisplay *display)
{
SDLNative *native = SDL_NATIVE (display->native);
SDL_Surface *screen = native->screen;
if (SDL_MUSTLOCK (screen) == SDL_TRUE)
SDL_LockSurface (screen);
return 0;
}
static int native_unlock (SADisplay *display)
{
SDLNative *native = SDL_NATIVE (display->native);
SDL_Surface *screen = native->screen;
if (SDL_MUSTLOCK (screen) == SDL_TRUE)
SDL_UnlockSurface (screen);
return 0;
}
static int native_fullscreen (SADisplay *display, int fullscreen, int autoscale)
{
SDLNative *native = SDL_NATIVE (display->native);
SDL_Surface *screen = native->screen;
if (fullscreen == TRUE) {
if (!(screen->flags & SDL_FULLSCREEN)) {
if (autoscale == TRUE) {
int width = display->screen->width;
int height = display->screen->height;
native->oldwidth = width;
native->oldheight = height;
get_nearest_resolution (display, &width, &height);
native_create (display, native->requested_depth, NULL, width, height, native->resizable);
}
SDL_ShowCursor (SDL_FALSE);
SDL_WM_ToggleFullScreen (screen);
}
} else {
if ((screen->flags & SDL_FULLSCREEN)) {
SDL_ShowCursor (SDL_TRUE);
SDL_WM_ToggleFullScreen (screen);
if (autoscale == TRUE)
native_create (display, native->requested_depth, NULL, native->oldwidth,
native->oldheight, native->resizable);
}
}
return 0;
}
static int native_getvideo (SADisplay *display, VisVideo *screen)
{
SDLNative *native = SDL_NATIVE (display->native);
SDL_Surface *sdlscreen = native->screen;
if (native->requested_depth == VISUAL_VIDEO_DEPTH_GL)
visual_video_set_depth (screen, VISUAL_VIDEO_DEPTH_GL);
else
visual_video_set_depth (screen, visual_video_depth_enum_from_value (sdlscreen->format->BitsPerPixel));
visual_video_set_dimension (screen, sdlscreen->w, sdlscreen->h);
visual_video_set_pitch (screen, sdlscreen->pitch);
visual_video_set_buffer (screen, sdlscreen->pixels);
return 0;
}
static int native_updaterect (SADisplay *display, VisRectangle *rect)
{
SDLNative *native = SDL_NATIVE (display->native);
SDL_Surface *sdlscreen = native->screen;
if (sdlscreen->format->BitsPerPixel == 8) {
SDL_Color colors[256];
VisPalette *pal = display->screen->pal;
visual_mem_set (colors, 0, sizeof (colors));
if (pal != NULL && pal->ncolors <= 256) {
int i;
for (i = 0; i < pal->ncolors; i++) {
colors[i].r = pal->colors[i].r;
colors[i].g = pal->colors[i].g;
colors[i].b = pal->colors[i].b;
}
SDL_SetColors (sdlscreen, colors, 0, 256);
}
}
if (native->requested_depth == VISUAL_VIDEO_DEPTH_GL)
SDL_GL_SwapBuffers ();
else
SDL_UpdateRect (sdlscreen, rect->x, rect->y, rect->width, rect->height);
return 0;
}
static int native_drainevents (SADisplay *display, VisEventQueue *eventqueue)
{
SDLNative *native = SDL_NATIVE (display->native);
SDL_Event event;
/* Visible or not */
if (((SDL_GetAppState () & SDL_APPACTIVE) == 0) && (native->active == TRUE)) {
native->active = FALSE;
visual_event_queue_add_visibility (eventqueue, FALSE);
} else if (((SDL_GetAppState () & SDL_APPACTIVE) != 0) && (native->active == FALSE)) {
native->active = TRUE;
visual_event_queue_add_visibility (eventqueue, TRUE);
}
/* Events */
while (SDL_PollEvent (&event)) {
switch (event.type) {
case SDL_KEYUP:
visual_event_queue_add_keyboard (eventqueue, event.key.keysym.sym, event.key.keysym.mod,
VISUAL_KEY_UP);
break;
case SDL_KEYDOWN:
visual_event_queue_add_keyboard (eventqueue, event.key.keysym.sym, event.key.keysym.mod,
VISUAL_KEY_DOWN);
break;
case SDL_VIDEORESIZE:
visual_event_queue_add_resize (eventqueue, display->screen, event.resize.w, event.resize.h);
native_create (display, display->screen->depth, NULL, event.resize.w, event.resize.h, native->resizable);
break;
case SDL_MOUSEMOTION:
visual_event_queue_add_mousemotion (eventqueue, event.motion.x, event.motion.y);
break;
case SDL_MOUSEBUTTONDOWN:
visual_event_queue_add_mousebutton (eventqueue, event.button.button, VISUAL_MOUSE_DOWN,
event.button.x, event.button.y);
break;
case SDL_MOUSEBUTTONUP:
visual_event_queue_add_mousebutton (eventqueue, event.button.button, VISUAL_MOUSE_UP,
event.button.x, event.button.y);
break;
case SDL_QUIT:
visual_event_queue_add_quit (eventqueue, FALSE);
break;
default:
break;
}
}
return 0;
}
static int get_nearest_resolution (SADisplay *display, int *width, int *height)
{
SDL_Rect **modelist;
int w, h;
int i;
modelist = SDL_ListModes (NULL, SDL_FULLSCREEN);
if (modelist == NULL)
return -1;
w = *width;
h = *height;
/* Window is bigger than highest resolution */
if (modelist[0]->w <= *width || modelist[0]->h <= *height) {
*width = modelist[0]->w;
*height = modelist[0]->h;
return 0;
}
for (i = 0; modelist[i]; i++) {
if (modelist[i]->w >= *width && modelist[i]->h >= *height) {
w = modelist[i]->w;
h = modelist[i]->h;
}
}
*width = w;
*height = h;
return 0;
}