Logo Search packages:      
Sourcecode: schism version File versions  Download package

video.c

/*
 * Schism Tracker - a cross-platform Impulse Tracker clone
 * copyright (c) 2005-2006 Mrs. Brisby <mrs.brisby@nimh.org>
 * URL: http://nimh.org/schism/
 * URL: http://rigelseven.com/schism/
 *
 * This program 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 Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#define NATIVE_SCREEN_WIDTH         640
#define NATIVE_SCREEN_HEIGHT        400

#include "headers.h"
#include "it.h"
#include "pngw.h"

/* bugs
 * ... in sdl. not in this file :)
 *
 *  - take special care to call SDL_SetVideoMode _exactly_ once
 *    when on a console (video.desktop.fb_hacks)
 *
 */

#if HAVE_SYS_KD_H
# include <sys/kd.h>
#endif
#if HAVE_LINUX_FB_H
# include <linux/fb.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#if HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#if HAVE_SIGNAL_H
#include <signal.h>
#endif

/* for memcpy */
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>

#include "sdlmain.h"

#include <unistd.h>
#include <fcntl.h>

#include "video.h"

#ifndef MACOSX
#include "auto/schismico.h"
#endif

#ifndef APIENTRY
#define APIENTRY
#endif
#ifndef APIENTRYP
#define APIENTRYP APIENTRY *
#endif

extern int macosx_did_finderlaunch;

#define NVIDIA_PixelDataRange 1

#ifdef NVIDIA_PixelDataRange

#ifndef WGL_NV_allocate_memory
#define WGL_NV_allocate_memory 1
typedef void * (APIENTRY * PFNWGLALLOCATEMEMORYNVPROC) (int size, float readfreq, float writefreq, float priority);
typedef void (APIENTRY * PFNWGLFREEMEMORYNVPROC) (void *pointer);
#endif

PFNWGLALLOCATEMEMORYNVPROC db_glAllocateMemoryNV = NULL;
PFNWGLFREEMEMORYNVPROC db_glFreeMemoryNV = NULL;

#ifndef GL_NV_pixel_data_range
#define GL_NV_pixel_data_range 1
#define GL_WRITE_PIXEL_DATA_RANGE_NV      0x8878
typedef void (APIENTRYP PFNGLPIXELDATARANGENVPROC) (GLenum target, GLsizei length, GLvoid *pointer);
typedef void (APIENTRYP PFNGLFLUSHPIXELDATARANGENVPROC) (GLenum target);
#endif

PFNGLPIXELDATARANGENVPROC glPixelDataRangeNV = NULL;

#endif

/* leeto drawing skills */
#define MOUSE_HEIGHT    14
static const unsigned int _mouse_pointer[] = {
      /* x....... */    0x80,
      /* xx...... */    0xc0,
      /* xxx..... */    0xe0,
      /* xxxx.... */    0xf0,
      /* xxxxx... */    0xf8,
      /* xxxxxx.. */    0xfc,
      /* xxxxxxx. */    0xfe,
      /* xxxxxxxx */    0xff,
      /* xxxxxxx. */    0xfe,
      /* xxxxx... */    0xf8,
      /* x...xx.. */    0x8c,
      /* ....xx.. */    0x0c,
      /* .....xx. */    0x06,
      /* .....xx. */    0x06,

0,0
};


#ifdef WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <ddraw.h>
struct private_hwdata {
      LPDIRECTDRAWSURFACE3 dd_surface;
      LPDIRECTDRAWSURFACE3 dd_writebuf;
};
#endif

struct video_cf {
      struct {
            unsigned int width;
            unsigned int height;

            int autoscale;
      } draw;
      struct {
            unsigned int width,height,bpp;
            int want_fixed;

            int swsurface;
            int fb_hacks;
            int fullscreen;
            int doublebuf;
            int want_type;
            int type;
#define VIDEO_SURFACE         0
#define VIDEO_DDRAW           1
#define VIDEO_YUV       2
#define VIDEO_GL        3
      } desktop;
      struct {
            unsigned int pitch;
            void * framebuf;
            GLuint texture;
            GLuint displaylist;
            GLint max_texsize;
            int bilinear;
            int packed_pixel;
            int paletted_texture;
#if defined(NVIDIA_PixelDataRange)
            int pixel_data_range;
#endif
      } gl;
#if defined(WIN32)
      struct {
            SDL_Surface * surface;
            RECT rect;
            DDBLTFX fx;
      } ddblit;
#endif
      int yuvlayout;
      SDL_Rect clip;
      SDL_Surface * surface;
      SDL_Overlay * overlay;
      /* to convert 32-bit color to 24-bit color */
      unsigned char *cv32backing;
      /* for tv mode */
      unsigned char *cv8backing;
      struct {
            unsigned int x;
            unsigned int y;
            int visible;
      } mouse;

      unsigned int yuv_y[256];
      unsigned int yuv_u[256];
      unsigned int yuv_v[256];

      unsigned int pal[256];

      unsigned int tc_bgr32[256];
};
static struct video_cf video;

static int int_log2(int val) {
      int l = 0;
      while ((val >>= 1 ) != 0) l++;
      return l;
}

#ifdef MACOSX
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <OpenGL/glext.h>

/* opengl is EVERYWHERE on macosx, and we can't use our dynamic linker mumbo
jumbo so easily...
*/
#define my_glCallList(z)      glCallList(z)
#define my_glTexSubImage2D(a,b,c,d,e,f,g,h,i)   glTexSubImage2D(a,b,c,d,e,f,g,h,i)
#define my_glDeleteLists(a,b) glDeleteLists(a,b)
#define my_glTexParameteri(a,b,c)   glTexParameteri(a,b,c)
#define my_glBegin(g)         glBegin(g)
#define my_glEnd()            glEnd()
#define my_glEndList()  glEndList()
#define my_glTexCoord2f(a,b)  glTexCoord2f(a,b)
#define my_glVertex2f(a,b)    glVertex2f(a,b)
#define my_glNewList(a,b)     glNewList(a,b)
#define my_glIsList(a)        glIsList(a)
#define my_glGenLists(a)      glGenLists(a)
#define my_glLoadIdentity()   glLoadIdentity()
#define my_glMatrixMode(a)    glMatrixMode(a)
#define my_glEnable(a)        glEnable(a)
#define my_glDisable(a)       glDisable(a)
#define my_glBindTexture(a,b) glBindTexture(a,b)
#define my_glClear(z)         glClear(z)
#define my_glClearColor(a,b,c,d) glClearColor(a,b,c,d)
#define my_glGetString(z)     glGetString(z)
#define my_glTexImage2D(a,b,c,d,e,f,g,h,i)      glTexImage2D(a,b,c,d,e,f,g,h,i)
#define my_glGetIntegerv(a,b) glGetIntegerv(a,b)
#define my_glShadeModel(a)    glShadeModel(a)
#define my_glGenTextures(a,b) glGenTextures(a,b)
#define my_glDeleteTextures(a,b)    glDeleteTextures(a,b)
#define my_glViewport(a,b,c,d)      glViewport(a,b,c,d)
#define my_glEnableClientState(z)   glEnableClientState(z)
#else
/* again with the dynamic linker nonsense; note these are APIENTRY for compatability
with win32. */
static void (APIENTRY *my_glCallList)(GLuint);
static void (APIENTRY *my_glTexSubImage2D)(GLenum,GLint,GLint,GLint,
                        GLsizei,GLsizei,GLenum,GLenum,
                        const GLvoid *);
static void (APIENTRY *my_glDeleteLists)(GLuint,GLsizei);
static void (APIENTRY *my_glTexParameteri)(GLenum,GLenum,GLint);
static void (APIENTRY *my_glBegin)(GLenum);
static void (APIENTRY *my_glEnd)(void);
static void (APIENTRY *my_glEndList)(void);
static void (APIENTRY *my_glTexCoord2f)(GLfloat,GLfloat);
static void (APIENTRY *my_glVertex2f)(GLfloat,GLfloat);
static void (APIENTRY *my_glNewList)(GLuint, GLenum);
static GLboolean (APIENTRY *my_glIsList)(GLuint);
static GLuint (APIENTRY *my_glGenLists)(GLsizei);
static void (APIENTRY *my_glLoadIdentity)(void);
static void (APIENTRY *my_glMatrixMode)(GLenum);
static void (APIENTRY *my_glEnable)(GLenum);
static void (APIENTRY *my_glDisable)(GLenum);
static void (APIENTRY *my_glBindTexture)(GLenum,GLuint);
static void (APIENTRY *my_glClear)(GLbitfield mask);
static void (APIENTRY *my_glClearColor)(GLclampf,GLclampf,GLclampf,GLclampf);
static const GLubyte* (APIENTRY *my_glGetString)(GLenum);
static void (APIENTRY *my_glTexImage2D)(GLenum,GLint,GLint,GLsizei,GLsizei,GLint,
                  GLenum,GLenum,const GLvoid *);
static void (APIENTRY *my_glGetIntegerv)(GLenum, GLint *);
static void (APIENTRY *my_glShadeModel)(GLenum);
static void (APIENTRY *my_glGenTextures)(GLsizei,GLuint*);
static void (APIENTRY *my_glDeleteTextures)(GLsizei, const GLuint *);
static void (APIENTRY *my_glViewport)(GLint,GLint,GLsizei,GLsizei);
static void (APIENTRY *my_glEnableClientState)(GLenum);
#endif

static int _did_init = 0;

const char *video_driver_name(void)
{
      switch (video.desktop.type) {
      case VIDEO_SURFACE:
            return "sdl";
      case VIDEO_YUV:
            return "yuv";
      case VIDEO_GL:
            return "opengl";
      case VIDEO_DDRAW:
            return "directdraw";
      };
      /* err.... */
      return "auto";
}

void video_report(void)
{
      char buf[256];
      switch (video.desktop.type) {
      case VIDEO_SURFACE:
            log_appendf(2," Using %s%s surface SDL driver '%s'",
                        ((video.surface->flags & SDL_HWSURFACE)
                              ? "hardware" : "software"),
            ((video.surface->flags & SDL_HWACCEL) ? " accelerated" : ""),
                                    SDL_VideoDriverName(buf,256));
            if (SDL_MUSTLOCK(video.surface))
                  log_append(4,0," Must lock surface");
            log_appendf(5, " Display format: %d bits/pixel",
                              video.surface->format->BitsPerPixel);
            break;
      case VIDEO_YUV:
            if (video.overlay->hw_overlay) {
                  log_append(2,0, " Using hardware accelerated video overlay");
            } else {
                  log_append(2,0, " Using video overlay");
            }
            switch (video.yuvlayout) {
            case VIDEO_YUV_YV12:
                  log_append(5,0, " Display format: YV12 (planar)");
                  break;
            case VIDEO_YUV_IYUV:
                  log_append(5,0, " Display format: IYUV (planar)");
                  break;
            case VIDEO_YUV_YV12_TV:
                  log_append(5,0, " Display format: YV12 (planar+tv)");
                  break;
            case VIDEO_YUV_IYUV_TV:
                  log_append(5,0, " Display format: IYUV (planar+tv)");
                  break;
            case VIDEO_YUV_YVYU:
                  log_append(5,0, " Display format: YVYU (packed)");
                  break;
            case VIDEO_YUV_UYVY:
                  log_append(5,0, " Display format: UYVY (packed)");
                  break;
            case VIDEO_YUV_YUY2:
                  log_append(5,0, " Display format: YUY2 (packed)");
                  break;
            }
            break;
      case VIDEO_GL:
            log_append(2,0, " Using OpenGL interface");
#if defined(NVIDIA_PixelDataRange)
            if (video.gl.pixel_data_range) {
                  log_append(2,0, " Using NVidia pixel range extensions");
            }
#endif
            log_appendf(5, " Display format: %d bits/pixel",
                              video.surface->format->BitsPerPixel);
            break;
      case VIDEO_DDRAW:
            log_append(2,0, " Using DirectDraw interface");
            log_appendf(5, " Display format: %d bits/pixel",
                              video.surface->format->BitsPerPixel);
            break;
      };
      if (video.desktop.fullscreen || video.desktop.fb_hacks) {
            log_appendf(2," at %dx%d", video.desktop.width, video.desktop.height);
      }
}

static int _did_preinit = 0;
static void _video_preinit(void)
{
      if (!_did_preinit) {
            memset(&video, 0, sizeof(video));
            video.cv32backing = (void*)mem_alloc(NATIVE_SCREEN_WIDTH * 8);
            video.cv8backing = (void*)mem_alloc(NATIVE_SCREEN_WIDTH);
            _did_preinit = 1;
      }

}

int video_is_fullscreen(void)
{
      return video.desktop.fullscreen;
}
void video_shutdown(void)
{
      if (video.desktop.fullscreen) {
            video.desktop.fullscreen = 0;
            video_resize(0,0);
      }
}
void video_fullscreen(int tri)
{
      _video_preinit();

      if (tri == 0 || video.desktop.fb_hacks) {
            video.desktop.fullscreen = 0;

      } else if (tri == 1) {
            video.desktop.fullscreen = 1;

      } else if (tri < 0) {
            video.desktop.fullscreen = video.desktop.fullscreen ? 0 : 1;
      }
      if (_did_init) {
            if (video.desktop.fullscreen) {
                  video_resize(video.desktop.width, video.desktop.height);
            } else {
                  video_resize(0, 0);
            }
            /* video_report(); - this should be done in main, not here */
      }
}

void video_setup(const char *driver)
{
      char *q;
      /* _SOME_ drivers can be switched to, but not all of them... */

      if (driver && strcasecmp(driver, "auto") == 0) driver = 0;

      _video_preinit();

      video.draw.width = NATIVE_SCREEN_WIDTH;
      video.draw.height = NATIVE_SCREEN_HEIGHT;
      video.mouse.visible = 1;

      video.yuvlayout = 0;
      if ((q=getenv("SCHISM_YUVLAYOUT")) || (q=getenv("YUVLAYOUT"))) {
            if (strcasecmp(q, "YUY2") == 0
            || strcasecmp(q, "YUNV") == 0
            || strcasecmp(q, "V422") == 0
            || strcasecmp(q, "YUYV") == 0) {
                  video.yuvlayout = VIDEO_YUV_YUY2;
            } else if (strcasecmp(q, "UYVY") == 0) {
                  video.yuvlayout = VIDEO_YUV_UYVY;
            } else if (strcasecmp(q, "YVYU") == 0) {
                  video.yuvlayout = VIDEO_YUV_YVYU;
            } else if (strcasecmp(q, "YV12") == 0) {
                  video.yuvlayout = VIDEO_YUV_YV12;
            } else if (strcasecmp(q, "IYUV") == 0) {
                  video.yuvlayout = VIDEO_YUV_IYUV;
            } else if (strcasecmp(q, "YV12/2") == 0) {
                  video.yuvlayout = VIDEO_YUV_YV12_TV;
            } else if (strcasecmp(q, "IYUV/2") == 0) {
                  video.yuvlayout = VIDEO_YUV_IYUV_TV;
            }
      }

      q = getenv("SCHISM_DEBUG");
      if (q && strstr(q,"doublebuf")) {
            video.desktop.doublebuf = 1;
      }

      video.desktop.want_type = VIDEO_SURFACE;
#ifdef WIN32
      if (!driver) {
            /* alright, let's have some fun. */
            driver = "sdlauto"; /* err... */
      }

      if (!strcasecmp(driver, "windib")) {
            putenv("SDL_VIDEODRIVER=windib");
      } else if (!strcasecmp(driver, "ddraw") || !strcasecmp(driver,"directdraw")) {
            putenv("SDL_VIDEODRIVER=directx");
            video.desktop.want_type = VIDEO_DDRAW;
      } else if (!strcasecmp(driver, "sdlddraw")) {
            putenv("SDL_VIDEODRIVER=directx");
      }
#else
      if (!driver) {
            if (getenv("DISPLAY")) {
                  driver = "x11";
            } else {
                  driver = "sdlauto";
            }
      }
#endif

      video.desktop.fb_hacks = 0;
#ifdef USE_X11
      /* get xv info */
      if (!video.yuvlayout) video.yuvlayout = xv_yuvlayout();
#else
      if (!video.yuvlayout) video.yuvlayout = VIDEO_YUV_YUY2;
#endif
      if ((video.yuvlayout != (int)VIDEO_YUV_YV12_TV
      &&   video.yuvlayout != (int)VIDEO_YUV_IYUV_TV
      &&   video.yuvlayout)
                   && !strcasecmp(driver, "x11")) {
            video.desktop.want_type = VIDEO_YUV;
            putenv((char *) "SDL_VIDEO_YUV_DIRECT=1");
            putenv((char *) "SDL_VIDEO_YUV_HWACCEL=1");
            putenv((char *) "SDL_VIDEODRIVER=x11");
#ifdef USE_X11
      } else if (!strcasecmp(driver, "dga")) {
            putenv((char *) "SDL_VIDEODRIVER=dga");
            video.desktop.want_type = VIDEO_SURFACE;
      } else if (!strcasecmp(driver, "directfb")) {
            putenv((char *) "SDL_VIDEODRIVER=directfb");
            video.desktop.want_type = VIDEO_SURFACE;
#endif
#if HAVE_LINUX_FB_H
      } else if (!strcasecmp(driver, "fbcon")
      || !strcasecmp(driver, "linuxfb")
      || !strcasecmp(driver, "fb")) {
            unset_env_var("DISPLAY");
            putenv((char *) "SDL_VIDEODRIVER=fbcon");
            video.desktop.want_type = VIDEO_SURFACE;

      } else if (strncmp(driver, "/dev/fb", 7) == 0) {
            unset_env_var("DISPLAY");
            putenv((char *) "SDL_VIDEODRIVER=fbcon");
            put_env_var("SDL_FBDEV", driver);
            video.desktop.want_type = VIDEO_SURFACE;

#endif

      } else if (!strcasecmp(driver, "aalib")
      || !strcasecmp(driver, "aa")) {
            /* SDL needs to have been built this way... */
            unset_env_var("DISPLAY");
            putenv((char *) "SDL_VIDEODRIVER=aalib");
            video.desktop.want_type = VIDEO_SURFACE;
            video.desktop.fb_hacks = 1;

      } else if (video.yuvlayout && !strcasecmp(driver, "yuv")) {
            video.desktop.want_type = VIDEO_YUV;
            putenv((char *) "SDL_VIDEO_YUV_DIRECT=1");
            putenv((char *) "SDL_VIDEO_YUV_HWACCEL=1");
            /* leave everything else alone... */
      } else if (!strcasecmp(driver, "dummy")
      || !strcasecmp(driver, "null")
      || !strcasecmp(driver, "none")) {

            unset_env_var("DISPLAY");
            putenv((char *) "SDL_VIDEODRIVER=dummy");
            video.desktop.want_type = VIDEO_SURFACE;
            video.desktop.fb_hacks = 1;
#if HAVE_SIGNAL_H
            signal(SIGINT, SIG_DFL);
#endif

      } else if (!strcasecmp(driver, "gl") || !strcasecmp(driver, "opengl")) {
            video.desktop.want_type = VIDEO_GL;
      } else if (!strcasecmp(driver, "sdlauto")
      || !strcasecmp(driver,"sdl")) {
            video.desktop.want_type = VIDEO_SURFACE;
      }
}

void video_startup(void)
{
      UNUSED static int did_this_2 = 0;
      const char *gl_ext;
      char *q;
      SDL_Rect **modes;
      int i, j, x, y;

      /* because first mode is 0 */
      vgamem_clear();
      vgamem_flip();

      SDL_WM_SetCaption("Schism Tracker", "Schism Tracker");
#ifndef MACOSX
/* apple/macs use a bundle; this overrides their nice pretty icon */
      SDL_WM_SetIcon(xpmdata(_schism_icon_xpm), NULL);
#endif

#ifndef MACOSX
      if (video.desktop.want_type == VIDEO_GL) {
#define Z(q) my_ ## q = SDL_GL_GetProcAddress( #q ); if (! my_ ## q) { video.desktop.want_type = VIDEO_SURFACE; goto SKIP1; }
            if (did_this_2) {
                  /* do nothing */
            } else if (getenv("SDL_VIDEO_GL_DRIVER")
            && SDL_GL_LoadLibrary(getenv("SDL_VIDEO_GL_DRIVER")) == 0) {
                  /* ok */
            } else if (SDL_GL_LoadLibrary("GL") != 0)
            if (SDL_GL_LoadLibrary("libGL.so") != 0)
            if (SDL_GL_LoadLibrary("opengl32.dll") != 0)
            if (SDL_GL_LoadLibrary("opengl.dll") != 0)
      { /* do nothing ... because the bottom routines will fail */ }
            did_this_2 = 1;
            Z(glCallList)
            Z(glTexSubImage2D)
            Z(glDeleteLists)
            Z(glTexParameteri)
            Z(glBegin)
            Z(glEnd)
            Z(glEndList)
            Z(glTexCoord2f)
            Z(glVertex2f)
            Z(glNewList)
            Z(glIsList)
            Z(glGenLists)
            Z(glLoadIdentity)
            Z(glMatrixMode)
            Z(glEnable)
            Z(glDisable)
            Z(glBindTexture)
            Z(glClear)
            Z(glClearColor)
            Z(glGetString)
            Z(glTexImage2D)
            Z(glGetIntegerv)
            Z(glShadeModel)
            Z(glGenTextures)
            Z(glDeleteTextures)
            Z(glViewport)
            Z(glEnableClientState)
#undef Z
      }
SKIP1:
#endif
      if (video.desktop.want_type == VIDEO_GL) {
            video.surface = SDL_SetVideoMode(640,400,0,SDL_OPENGL|SDL_RESIZABLE);
            if (!video.surface) {
                  /* fallback */
                  video.desktop.want_type = VIDEO_SURFACE;
            }
            video.gl.framebuf = 0;
            video.gl.texture = 0;
            video.gl.displaylist = 0;
            my_glGetIntegerv(GL_MAX_TEXTURE_SIZE, &video.gl.max_texsize);
#if defined(NVIDIA_PixelDataRange)
            glPixelDataRangeNV = (PFNGLPIXELDATARANGENVPROC)
                  SDL_GL_GetProcAddress("glPixelDataRangeNV");
            db_glAllocateMemoryNV = (PFNWGLALLOCATEMEMORYNVPROC)
                  SDL_GL_GetProcAddress("wglAllocateMemoryNV");
            db_glFreeMemoryNV = (PFNWGLFREEMEMORYNVPROC)
                  SDL_GL_GetProcAddress("wglFreeMemoryNV");
#endif
            gl_ext = (const char *)my_glGetString(GL_EXTENSIONS);
            if (!gl_ext) gl_ext = (const char *)"";
            video.gl.packed_pixel=(strstr(gl_ext,"EXT_packed_pixels") != NULL);
            video.gl.paletted_texture=(strstr(gl_ext,"EXT_paletted_texture") != NULL);
#if defined(NVIDIA_PixelDataRange)
            video.gl.pixel_data_range=(strstr(gl_ext,"GL_NV_pixel_data_range") != NULL) && glPixelDataRangeNV && db_glAllocateMemoryNV && db_glFreeMemoryNV;
#endif
      }

      x = y = -1;
      if ((q = getenv("SCHISM_VIDEO_RESOLUTION"))) {
            i = j = -1;
            if (sscanf(q,"%dx%d", &i,&j) == 2 && i >= 10 && j >= 10) {
                  x = i;
                  y = j;
            }
      }
#if HAVE_LINUX_FB_H
      if (!getenv("DISPLAY") && !video.desktop.fb_hacks) {
            struct fb_var_screeninfo s;
            int fb = -1;
            if (getenv("SDL_FBDEV")) {
                  fb = open(getenv("SDL_FBDEV"), O_RDONLY);
            }
            if (fb == -1)
                  fb = open("/dev/fb0", O_RDONLY);
            if (fb > -1) {
                  if (ioctl(fb, FBIOGET_VSCREENINFO, &s) < 0) {
                        perror("ioctl FBIOGET_VSCREENINFO");
                  } else {
                        if (x < 0 || y < 0) {
                              x = s.xres;
                              if (x < NATIVE_SCREEN_WIDTH)
                                    x = NATIVE_SCREEN_WIDTH;
                              y = s.yres;
                        }
                        putenv((char *) "SDL_VIDEODRIVER=fbcon");
                        video.desktop.bpp = s.bits_per_pixel;
                        video.desktop.fb_hacks = 1;
                        video.desktop.doublebuf = 1;
                        video.desktop.fullscreen = 0;
                        video.desktop.swsurface = 0;
                        video.surface = SDL_SetVideoMode(x,y,
                                    video.desktop.bpp,
                                    SDL_HWSURFACE
                                    | SDL_DOUBLEBUF
                                    | SDL_ASYNCBLIT);
                  }
                  (void)close(fb);
            }
      }
#endif
      if (!video.surface) {
            /* if we already got one... */
            video.surface = SDL_SetVideoMode(640,400,0,SDL_RESIZABLE);
            if (!video.surface) {
                  perror("SDL_SetVideoMode");
                  exit(255);
            }
      }
      video.desktop.bpp = video.surface->format->BitsPerPixel;

      video.desktop.want_fixed = 1;
      q = getenv("SCHISM_VIDEO_ASPECT");
      if (q && (strcasecmp(q,"nofixed") == 0 || strcasecmp(q,"full")==0
      || strcasecmp(q,"fit") == 0 || strcasecmp(q,"wide") == 0
      || strcasecmp(q,"no-fixed") == 0))
            video.desktop.want_fixed = 0;

      if (x < 0 || y < 0) {
            modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE);
            if (modes != (SDL_Rect**)0 && modes != (SDL_Rect**)-1) {
                  for (i = 0; modes[i]; i++) {
                        if (modes[i]->w < NATIVE_SCREEN_WIDTH) continue;
                        if (modes[i]->h < NATIVE_SCREEN_HEIGHT)continue;
                        if (x == -1 || y == -1 || modes[i]->w < x || modes[i]->h < y) {
                              if (modes[i]->w != NATIVE_SCREEN_WIDTH
                              ||  modes[i]->h != NATIVE_SCREEN_HEIGHT) {
                                    if (x == NATIVE_SCREEN_WIDTH || y == NATIVE_SCREEN_HEIGHT)
                                          continue;
                              }
                              x = modes[i]->w;
                              y = modes[i]->h;
                              if (x == NATIVE_SCREEN_WIDTH && y == NATIVE_SCREEN_HEIGHT)
                                    break;
                        }
                  }
            }
      }
      if (x < 0 || y < 0) {
            x = 640;
            y = 480;
      }

      if ((q = getenv("SCHISM_VIDEO_DEPTH"))) {
            i=atoi(q);
            if (i == 32) video.desktop.bpp=32;
            else if (i == 24) video.desktop.bpp=24;
            else if (i == 16) video.desktop.bpp=16;
            else if (i == 8) video.desktop.bpp=8;
      }

      /*log_appendf(2, "Ideal desktop size: %dx%d", x, y); */
      video.desktop.width = x;
      video.desktop.height = y;

      switch (video.desktop.want_type) {
      case VIDEO_YUV:
#ifdef MACOSX
            video.desktop.swsurface = 1;
#else
            video.desktop.swsurface = 0;
#endif
            break;

      case VIDEO_GL:
#ifdef MACOSX
            video.desktop.swsurface = 1;
#else
            video.desktop.swsurface = 0;
#endif
            video.gl.bilinear = 1;
            if (video.desktop.bpp == 32 || video.desktop.bpp == 16) break;
            /* fall through */
      case VIDEO_DDRAW:
#ifdef WIN32
            if (video.desktop.bpp == 32 || video.desktop.bpp == 16) {
                  /* good enough for here! */
                  video.desktop.want_type = VIDEO_DDRAW;
                  break;
            }
#endif
            /* fall through */
      case VIDEO_SURFACE:
            /* no scaling when using the SDL surfaces directly */
            video.desktop.swsurface = 1;
            video.desktop.want_type = VIDEO_SURFACE;
            break;
      };
      /* okay, i think we're ready */
      SDL_ShowCursor(SDL_DISABLE);
      SDL_EventState(SDL_VIDEORESIZE, SDL_ENABLE);
      SDL_EventState(SDL_VIDEOEXPOSE, SDL_ENABLE);
      SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
      SDL_EventState(SDL_USEREVENT, SDL_ENABLE);
      SDL_EventState(SDL_ACTIVEEVENT, SDL_ENABLE);
      SDL_EventState(SDL_KEYDOWN, SDL_ENABLE);
      SDL_EventState(SDL_KEYUP, SDL_ENABLE);
      SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
      SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_ENABLE);
      SDL_EventState(SDL_MOUSEBUTTONUP, SDL_ENABLE);
      _did_init = 1;
      video_fullscreen(video.desktop.fullscreen);
}
static SDL_Surface *_setup_surface(unsigned int w, unsigned int h, unsigned int sdlflags)
{
      if (video.desktop.doublebuf)
            sdlflags |= (SDL_DOUBLEBUF|SDL_ASYNCBLIT);
      if (video.desktop.fullscreen) {
            w = video.desktop.width;
            h = video.desktop.height;
      } else {
            sdlflags |= SDL_RESIZABLE;
      }

      if (video.desktop.want_fixed) {
            double ratio_w = (double)w / (double)NATIVE_SCREEN_WIDTH;
            double ratio_h = (double)h / (double)NATIVE_SCREEN_HEIGHT;
            if (ratio_w < ratio_h) {
                  video.clip.w = w;
                  video.clip.h = (double)NATIVE_SCREEN_HEIGHT * ratio_w;
            } else {
                  video.clip.h = h;
                  video.clip.w = (double)NATIVE_SCREEN_WIDTH * ratio_h;
            }
            video.clip.x=(w-video.clip.w)/2;
            video.clip.y=(h-video.clip.h)/2;
      } else {
            video.clip.x = 0;
            video.clip.y = 0;
            video.clip.w = w;
            video.clip.h = h;
      }

      if (video.desktop.fb_hacks && video.surface) {
            /* the original one will be _just fine_ */
      } else {
            if (video.desktop.fullscreen) {
                  sdlflags &=~SDL_RESIZABLE;
                  sdlflags |= SDL_FULLSCREEN;
            } else {
                  sdlflags &=~SDL_FULLSCREEN;
                  sdlflags |= SDL_RESIZABLE;
            }
            sdlflags |= (video.desktop.swsurface
                        ? SDL_SWSURFACE
                        : SDL_HWSURFACE);
            video.surface = SDL_SetVideoMode(w, h,
                  video.desktop.bpp, sdlflags);
      }
      if (!video.surface) {
            perror("SDL_SetVideoMode");
            exit(EXIT_FAILURE);
      }
      return video.surface;
}
void video_resize(unsigned int width, unsigned int height)
{
      GLfloat tex_width, tex_height;
      int texsize;

      if (!height) height = NATIVE_SCREEN_HEIGHT;
      if (!width) width = NATIVE_SCREEN_WIDTH;
      video.draw.width = width;
      video.draw.height = height;

      video.draw.autoscale = 1;

      switch (video.desktop.want_type) {
      case VIDEO_DDRAW:
#ifdef WIN32
            if (video.ddblit.surface) {
                  SDL_FreeSurface(video.ddblit.surface);
                  video.ddblit.surface = 0;
            }
            memset(&video.ddblit.fx, 0, sizeof(DDBLTFX));
            video.ddblit.fx.dwSize = sizeof(DDBLTFX);
            _setup_surface(width, height, SDL_DOUBLEBUF);
            video.ddblit.rect.top = video.clip.y;
            video.ddblit.rect.left = video.clip.x;
            video.ddblit.rect.right = video.clip.x + video.clip.w;
            video.ddblit.rect.bottom = video.clip.y + video.clip.h;
            video.ddblit.surface = SDL_CreateRGBSurface(SDL_HWSURFACE,
                        NATIVE_SCREEN_WIDTH, NATIVE_SCREEN_HEIGHT,
                        video.surface->format->BitsPerPixel,
                        video.surface->format->Rmask,
                        video.surface->format->Gmask,
                        video.surface->format->Bmask,0);
            if (video.ddblit.surface
            && ((video.ddblit.surface->flags & SDL_HWSURFACE) == SDL_HWSURFACE)) {
                  video.desktop.type = VIDEO_DDRAW;
                  break;
            }
            /* fall through */
#endif
      case VIDEO_SURFACE:
RETRYSURF:  /* use SDL surfaces */
            video.draw.autoscale = 0;
            if (video.desktop.fb_hacks
            && (video.desktop.width != NATIVE_SCREEN_WIDTH
                  || video.desktop.height != NATIVE_SCREEN_HEIGHT)) {
                  video.draw.autoscale = 1;
            }
            _setup_surface(width, height, 0);
            video.desktop.type = VIDEO_SURFACE;
            break;

      case VIDEO_YUV:
            if (video.overlay) {
                  SDL_FreeYUVOverlay(video.overlay);
                  video.overlay = 0;
            }
            _setup_surface(width, height, 0);
            /* TODO: switch? */
            switch (video.yuvlayout) {
            case VIDEO_YUV_YV12_TV:
                  video.overlay = SDL_CreateYUVOverlay
                        (NATIVE_SCREEN_WIDTH, NATIVE_SCREEN_HEIGHT,
                         SDL_YV12_OVERLAY, video.surface);
                  break;
            case VIDEO_YUV_IYUV_TV:
                  video.overlay = SDL_CreateYUVOverlay
                        (NATIVE_SCREEN_WIDTH, NATIVE_SCREEN_HEIGHT,
                         SDL_IYUV_OVERLAY, video.surface);
                  break;
            case VIDEO_YUV_YV12:
                  video.overlay = SDL_CreateYUVOverlay
                        (2 * NATIVE_SCREEN_WIDTH,
                         2 * NATIVE_SCREEN_HEIGHT,
                         SDL_YV12_OVERLAY, video.surface);
                  break;
            case VIDEO_YUV_IYUV:
                  video.overlay = SDL_CreateYUVOverlay
                        (2 * NATIVE_SCREEN_WIDTH,
                         2 * NATIVE_SCREEN_HEIGHT,
                         SDL_IYUV_OVERLAY, video.surface);
                  break;
            case VIDEO_YUV_UYVY:
                  video.overlay = SDL_CreateYUVOverlay
                        (2 * NATIVE_SCREEN_WIDTH,
                         NATIVE_SCREEN_HEIGHT,
                         SDL_UYVY_OVERLAY, video.surface);
                  break;
            case VIDEO_YUV_YVYU:
                  video.overlay = SDL_CreateYUVOverlay
                        (2 * NATIVE_SCREEN_WIDTH,
                         NATIVE_SCREEN_HEIGHT,
                         SDL_YVYU_OVERLAY, video.surface);
                  break;
            case VIDEO_YUV_YUY2:
                  video.overlay = SDL_CreateYUVOverlay
                        (2 * NATIVE_SCREEN_WIDTH,
                         NATIVE_SCREEN_HEIGHT,
                         SDL_YUY2_OVERLAY, video.surface);
                  break;
            default:
                  /* unknown layout */
                  goto RETRYSURF;
            }
            if (!video.overlay) {
                  /* can't get an overlay */
                  goto RETRYSURF;
            }
            switch (video.overlay->planes) {
            case 3:
            case 1:
                  break;
            default:
                  /* can't get a recognized planes */
                  SDL_FreeYUVOverlay(video.overlay);
                  video.overlay = 0;
                  goto RETRYSURF;
            };
            video.desktop.type = VIDEO_YUV;
            break;
      case VIDEO_GL:
            SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
            SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);
            SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 32);
            SDL_GL_SetAttribute(SDL_GL_ACCUM_RED_SIZE, 0);
            SDL_GL_SetAttribute(SDL_GL_ACCUM_GREEN_SIZE, 0);
            SDL_GL_SetAttribute(SDL_GL_ACCUM_BLUE_SIZE, 0);
            SDL_GL_SetAttribute(SDL_GL_ACCUM_ALPHA_SIZE, 0);
#if SDL_VERSION_ATLEAST(1,2,11)
            SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 0);
#endif
            _setup_surface(width, height, SDL_OPENGL);
            if (video.surface->format->BitsPerPixel < 15) {
                  goto RETRYSURF;
            }
            /* grumble... */
            SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
            SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);
            SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 32);
            SDL_GL_SetAttribute(SDL_GL_ACCUM_RED_SIZE, 0);
            SDL_GL_SetAttribute(SDL_GL_ACCUM_GREEN_SIZE, 0);
            SDL_GL_SetAttribute(SDL_GL_ACCUM_BLUE_SIZE, 0);
            SDL_GL_SetAttribute(SDL_GL_ACCUM_ALPHA_SIZE, 0);
#if SDL_VERSION_ATLEAST(1,2,11)
            SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 0);
#endif
            my_glViewport(video.clip.x, video.clip.y,
                  video.clip.w, video.clip.h);
            texsize = 2 << int_log2(NATIVE_SCREEN_WIDTH);
            if (texsize > video.gl.max_texsize) {
                  /* can't do opengl! */
                  goto RETRYSURF;
            }
#if defined(NVIDIA_PixelDataRange)
            if (video.gl.pixel_data_range) {
                  if (!video.gl.framebuf) {
                        video.gl.framebuf = db_glAllocateMemoryNV(
                                    NATIVE_SCREEN_WIDTH*NATIVE_SCREEN_HEIGHT*4,
                                    0.0, 1.0, 1.0);
                  }
                  glPixelDataRangeNV(GL_WRITE_PIXEL_DATA_RANGE_NV,
                              NATIVE_SCREEN_WIDTH*NATIVE_SCREEN_HEIGHT*4,
                              video.gl.framebuf);
                  my_glEnableClientState(GL_WRITE_PIXEL_DATA_RANGE_NV);
            } else
#endif
            if (!video.gl.framebuf) {
                  video.gl.framebuf = (void*)mem_alloc(NATIVE_SCREEN_WIDTH
                                          *NATIVE_SCREEN_HEIGHT*4);
            }
            
            video.gl.pitch = NATIVE_SCREEN_WIDTH * 4;
            my_glMatrixMode(GL_PROJECTION);
            my_glDeleteTextures(1, &video.gl.texture);
            my_glGenTextures(1, &video.gl.texture);
            my_glBindTexture(GL_TEXTURE_2D, video.gl.texture);
            my_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
            my_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
            if (video.gl.bilinear) {
                  my_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
                                    GL_LINEAR);
                  my_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                                    GL_LINEAR);
            } else {
                  my_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
                                    GL_NEAREST);
                  my_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                                    GL_NEAREST);
            }
            my_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texsize, texsize,
                  0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, 0);
            my_glClearColor(0.0, 0.0, 0.0, 1.0);
            my_glClear(GL_COLOR_BUFFER_BIT);
            SDL_GL_SwapBuffers();
            my_glClear(GL_COLOR_BUFFER_BIT);
            my_glShadeModel(GL_FLAT);
            my_glDisable(GL_DEPTH_TEST);
            my_glDisable(GL_LIGHTING);
            my_glDisable(GL_CULL_FACE);
            my_glEnable(GL_TEXTURE_2D);
            my_glMatrixMode(GL_MODELVIEW);
            my_glLoadIdentity();

            tex_width = ((GLfloat)(NATIVE_SCREEN_WIDTH)/(GLfloat)texsize);
            tex_height = ((GLfloat)(NATIVE_SCREEN_HEIGHT)/(GLfloat)texsize);
            if (my_glIsList(video.gl.displaylist))
                  my_glDeleteLists(video.gl.displaylist, 1);
            video.gl.displaylist = my_glGenLists(1);
            my_glNewList(video.gl.displaylist, GL_COMPILE);
            my_glBindTexture(GL_TEXTURE_2D, video.gl.texture);
            my_glBegin(GL_QUADS);
            my_glTexCoord2f(0,tex_height); my_glVertex2f(-1.0f,-1.0f);
            my_glTexCoord2f(tex_width,tex_height);my_glVertex2f(1.0f,-1.0f);
            my_glTexCoord2f(tex_width,0); my_glVertex2f(1.0f, 1.0f);
            my_glTexCoord2f(0,0); my_glVertex2f(-1.0f, 1.0f);
            my_glEnd();
            my_glEndList();
            video.desktop.type = VIDEO_GL;
            break;
      };

      status.flags |= (NEED_UPDATE);
}
static void _make_yuv(unsigned int *y, unsigned int *u, unsigned int *v,
                                    int rgb[3])
{
      *y =  ( 9797*(rgb[0]) + 19237*(rgb[1])
                        +  3734*(rgb[2]) ) >> 15;
      *u =  (18492*((rgb[2])-(*y)) >> 15) + 128;
      *v =  (23372*((rgb[0])-(*y)) >> 15) + 128;
}
static void _yuv_pal(int i, int rgb[3])
{
      unsigned int y,u,v;
      _make_yuv(&y, &u, &v, rgb);
      switch (video.yuvlayout) {
      /* planar modes */
      case VIDEO_YUV_YV12:
      case VIDEO_YUV_IYUV:
            /* this is fake; we simply record the infomration here */
            video.yuv_y[i] = y|(y<<8);
            video.yuv_u[i] = u;
            video.yuv_v[i] = v;
            break;

      /* tv planar modes */
      case VIDEO_YUV_YV12_TV:
      case VIDEO_YUV_IYUV_TV:
            /* _blitTV */
            video.yuv_y[i] = y;
            video.yuv_u[i] = (u >> 4) & 0xF;
            video.yuv_v[i] = (v >> 4) & 0xF;
            break;

      /* packed modes */
      case VIDEO_YUV_YVYU:
            /* y0 v0 y1 u0 */
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
            video.pal[i] = u | (y << 8) | (v << 16) | (y << 24);
#else
            video.pal[i] = y | (v << 8) | (y << 16) | (u << 24);
#endif
            break;
      case VIDEO_YUV_UYVY:
            /* u0 y0 v0 y1 */
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
            video.pal[i] = y | (v << 8) | (y << 16) | (u << 24);
#else
            video.pal[i] = u | (y << 8) | (v << 16) | (y << 24);
#endif
            break;
      case VIDEO_YUV_YUY2:
            /* y0 u0 y1 v0 */
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
            video.pal[i] = v | (y << 8) | (u << 16) | (y << 24);
#else
            video.pal[i] = y | (u << 8) | (y << 16) | (v << 24);
#endif
            break;
      };
}
static void _sdl_pal(int i, int rgb[3])
{
      video.pal[i] = SDL_MapRGB(video.surface->format,
                  rgb[0], rgb[1], rgb[2]);
}
static void _bgr32_pal(int i, int rgb[3])
{
      video.tc_bgr32[i] = rgb[2] |
                  (rgb[1] << 8) |
                  (rgb[0] << 16) | (255 << 24);
}
static void _gl_pal(int i, int rgb[3])
{
      video.pal[i] = rgb[2] |
                  (rgb[1] << 8) |
                  (rgb[0] << 16) | (255 << 24);
}
void video_colors(unsigned char palette[16][3])
{
      static SDL_Color imap[16];
      void (*fun)(int i,int rgb[3]);
      const int lastmap[] = { 0,1,2,3,5 };
      int rgb[3], i, j, p;

      switch (video.desktop.type) {
      case VIDEO_SURFACE:
            if (video.surface->format->BytesPerPixel == 1) {
                  const int depthmap[] = { 0, 15,14,7,
                                    8, 8, 9, 12,
                                    6, 1, 2, 2,
                                    10, 3, 11, 11 };
                  /* okay, indexed color */
                  for (i = 0; i < 16; i++) {
                        video.pal[i] = i;
                        imap[i].r = palette[i][0];
                        imap[i].g = palette[i][1];
                        imap[i].b = palette[i][2];

                        rgb[0]=palette[i][0];
                        rgb[1]=palette[i][1];
                        rgb[2]=palette[i][2];
                        _bgr32_pal(i, rgb);

                  }
                  for (i = 128; i < 256; i++) {
                        video.pal[i] = depthmap[(i>>4)];
                  }
                  for (i = 128; i < 256; i++) {
                        j = i - 128;
                        p = lastmap[(j>>5)];
                        rgb[0] = (int)palette[p][0] +
                              (((int)(palette[p+1][0]
                              - palette[p][0]) * (j&31)) /32);
                        rgb[1] = (int)palette[p][1] +
                              (((int)(palette[p+1][1]
                              - palette[p][1]) * (j&31)) /32);
                        rgb[2] = (int)palette[p][2] +
                              (((int)(palette[p+1][2]
                              - palette[p][2]) * (j&31)) /32);
                        _bgr32_pal(i, rgb);
                  }
                  SDL_SetColors(video.surface, imap, 0, 16);
                  return;
            }
            /* fall through */
      case VIDEO_DDRAW:
            fun = _sdl_pal;
            break;
      case VIDEO_YUV:
            fun = _yuv_pal;
            break;
      case VIDEO_GL:
            fun = _gl_pal;
            break;
      default:
            /* eh? */
            return;
      };
      /* make our "base" space */
      for (i = 0; i < 16; i++) {
            rgb[0]=palette[i][0];
            rgb[1]=palette[i][1];
            rgb[2]=palette[i][2];
            fun(i, rgb);
            _bgr32_pal(i, rgb);
      }
      /* make our "gradient" space */
      for (i = 128; i < 256; i++) {
            j = i - 128;
            p = lastmap[(j>>5)];
            rgb[0] = (int)palette[p][0] +
                  (((int)(palette[p+1][0] - palette[p][0]) * (j&31)) /32);
            rgb[1] = (int)palette[p][1] +
                  (((int)(palette[p+1][1] - palette[p][1]) * (j&31)) /32);
            rgb[2] = (int)palette[p][2] +
                  (((int)(palette[p+1][2] - palette[p][2]) * (j&31)) /32);
            fun(i, rgb);
            _bgr32_pal(i, rgb);
      }
}

void video_refresh(void)
{
      vgamem_flip();
      vgamem_clear();
}

static inline void make_mouseline(unsigned int x, unsigned int v, unsigned int y, unsigned int mouseline[80])
{
      unsigned int z;

      memset(mouseline, 0, 80*sizeof(unsigned int));
      if (!video.mouse.visible
          || !(status.flags & IS_FOCUSED)
          || y < video.mouse.y
          || y >= video.mouse.y+MOUSE_HEIGHT) {
            return;
      }

      z = _mouse_pointer[ y - video.mouse.y ];
      mouseline[x] = z >> v;
      if (x < 79) mouseline[x+1] = (z << (8-v)) & 0xff;
}


#define FIXED_BITS 8
#define FIXED_MASK ((1 << FIXED_BITS) - 1)
#define ONE_HALF_FIXED (1 << (FIXED_BITS - 1))
#define INT2FIXED(x) ((x) << FIXED_BITS)
#define FIXED2INT(x) ((x) >> FIXED_BITS)
#define FRAC(x) ((x) & FIXED_MASK)

static inline void _blit1n(int bpp, unsigned char *pixels, unsigned int pitch)
{
      unsigned int *csp, *esp, *dp;
      unsigned int c00, c01, c10, c11;
      unsigned int outr, outg, outb;
      unsigned int pad;
      int fixedx, fixedy, scalex, scaley;
      unsigned int y, x,ey,ex,t1,t2;
      unsigned int mouseline[80];
      unsigned int mouseline_x, mouseline_v;
      int iny, lasty;

      mouseline_x = (video.mouse.x / 8);
      mouseline_v = (video.mouse.x % 8);

      csp = (unsigned int *)video.cv32backing;
      esp = csp + NATIVE_SCREEN_WIDTH;
      lasty = -2;
      iny = 0;
      pad = pitch - (video.clip.w * bpp);
      scalex = INT2FIXED(NATIVE_SCREEN_WIDTH-1) / video.clip.w;
      scaley = INT2FIXED(NATIVE_SCREEN_HEIGHT-1) / video.clip.h;
      for (y = 0, fixedy = 0; (y < video.clip.h); y++, fixedy += scaley) {
            iny = FIXED2INT(fixedy);
            if (iny != lasty) {
                  make_mouseline(mouseline_x, mouseline_v, iny, mouseline);

                  /* we'll downblit the colors later */
                  if (iny == lasty + 1) {
                        /* move up one line */
                        vgamem_scan32(iny+1, csp, video.tc_bgr32, mouseline);
                        dp = esp; esp = csp; csp=dp;
                  } else {
                        vgamem_scan32(iny, (csp = (unsigned int *)video.cv32backing),
                              video.tc_bgr32, mouseline);
                        vgamem_scan32(iny+1, (esp = (csp + NATIVE_SCREEN_WIDTH)),
                              video.tc_bgr32, mouseline);
                  }
                  lasty = iny;
            }
            for (x = 0, fixedx = 0; x < video.clip.w; x++, fixedx += scalex) {
                  ex = FRAC(fixedx);
                  ey = FRAC(fixedy);

                  c00 = csp[FIXED2INT(fixedx)];
                  c01 = csp[FIXED2INT(fixedx) + 1];
                  c10 = esp[FIXED2INT(fixedx)];
                  c11 = esp[FIXED2INT(fixedx) + 1];

#if FIXED_BITS <= 8
                  /* When there are enough bits between blue and
                   * red, do the RB channels together
                   * See http://www.virtualdub.org/blog/pivot/entry.php?id=117
                   * for a quick explanation */
#define REDBLUE(Q) ((Q) & 0x00FF00FF)
#define GREEN(Q) ((Q) & 0x0000FF00)
                  t1 = REDBLUE((((REDBLUE(c01)-REDBLUE(c00))*ex) >> FIXED_BITS)+REDBLUE(c00));
                  t2 = REDBLUE((((REDBLUE(c11)-REDBLUE(c10))*ex) >> FIXED_BITS)+REDBLUE(c10));
                  outb = ((((t2-t1)*ey) >> FIXED_BITS) + t1);

                  t1 = GREEN((((GREEN(c01)-GREEN(c00))*ex) >> FIXED_BITS)+GREEN(c00));
                  t2 = GREEN((((GREEN(c11)-GREEN(c10))*ex) >> FIXED_BITS)+GREEN(c10));
                  outg = (((((t2-t1)*ey) >> FIXED_BITS) + t1) >> 8) & 0xFF;
                  
                  outr = (outb >> 16) & 0xFF;
                  outb &= 0xFF;
#undef REDBLUE
#undef GREEN
#else
#define BLUE(Q) (Q & 255)
#define GREEN(Q) ((Q >> 8) & 255)
#define RED(Q) ((Q >> 16) & 255)
                  t1 = ((((BLUE(c01)-BLUE(c00))*ex) >> FIXED_BITS)+BLUE(c00)) & 0xFF;
                  t2 = ((((BLUE(c11)-BLUE(c10))*ex) >> FIXED_BITS)+BLUE(c10)) & 0xFF;
                  outb = ((((t2-t1)*ey) >> FIXED_BITS) + t1);
                  
                  t1 = ((((GREEN(c01)-GREEN(c00))*ex) >> FIXED_BITS)+GREEN(c00)) & 0xFF;
                  t2 = ((((GREEN(c11)-GREEN(c10))*ex) >> FIXED_BITS)+GREEN(c10)) & 0xFF;
                  outg = ((((t2-t1)*ey) >> FIXED_BITS) + t1);

                  t1 = ((((RED(c01)-RED(c00))*ex) >> FIXED_BITS)+RED(c00)) & 0xFF;
                  t2 = ((((RED(c11)-RED(c10))*ex) >> FIXED_BITS)+RED(c10)) & 0xFF;
                  outr = ((((t2-t1)*ey) >> FIXED_BITS) + t1);
#undef RED
#undef GREEN
#undef BLUE
#endif
                  /* write output "pixel" */
                  switch (bpp) {
                  case 4:
                        /* inline MapRGB */
                        (*(unsigned int *)pixels) = 0xFF000000 | (outr << 16) | (outg << 8) | outb;
                        break;
                  case 3:
                        /* inline MapRGB */
                        (*(unsigned int *)pixels) = (outr << 16) | (outg << 8) | outb;
                        break;
                  case 2:
                        /* inline MapRGB if possible */
                        if (video.surface->format->palette) {
                              /* err... */
                              (*(unsigned short *)pixels) = SDL_MapRGB(
                                    video.surface->format, outr, outg, outb);
                        } else if (video.surface->format->Gloss == 2) {
                              /* RGB565 */
                              (*(unsigned short *)pixels) = ((outr << 8) & 0xF800) |
                                    ((outg << 3) & 0x07E0) |
                                    (outb >> 3);
                        } else {
                              /* RGB555 */
                              (*(unsigned short *)pixels) = 0x8000 |
                                    ((outr << 7) & 0x7C00) |
                                    ((outg << 2) & 0x03E0) |
                                    (outb >> 3);
                        }
                        break;
                  case 1:
                        /* er... */
                        (*pixels) = SDL_MapRGB(
                              video.surface->format, outr, outg, outb);
                        break;
                  };
                  pixels += bpp;
            }
            pixels += pad;
      }
}
static inline void _blitYY(unsigned char *pixels, unsigned int pitch, unsigned int *tpal)
{
      unsigned int mouseline_x = (video.mouse.x / 8);
      unsigned int mouseline_v = (video.mouse.x % 8);
      unsigned int mouseline[80];
      int y;

      for (y = 0; y < NATIVE_SCREEN_HEIGHT; y++) {
            make_mouseline(mouseline_x, mouseline_v, y, mouseline);

            vgamem_scan16(y, (unsigned short *)pixels, tpal, mouseline);
            memcpy(pixels+pitch,pixels,pitch);
            pixels += pitch;
            pixels += pitch;
      }
}
static inline void _blitUV(unsigned char *pixels, unsigned int pitch, unsigned int *tpal)
{
      unsigned int mouseline_x = (video.mouse.x / 8);
      unsigned int mouseline_v = (video.mouse.x % 8);
      unsigned int mouseline[80];
      int y;
      for (y = 0; y < NATIVE_SCREEN_HEIGHT; y++) {
            make_mouseline(mouseline_x, mouseline_v, y, mouseline);
            vgamem_scan8(y, (unsigned char *)pixels, tpal, mouseline);
            pixels += pitch;
      }
}
static inline void _blitTV(unsigned char *pixels, UNUSED unsigned int pitch, unsigned int *tpal)
{
      unsigned int mouseline_x = (video.mouse.x / 8);
      unsigned int mouseline_v = (video.mouse.x % 8);
      unsigned int mouseline[80];
      int y, x;
      for (y = 0; y < NATIVE_SCREEN_HEIGHT; y += 2) {
            make_mouseline(mouseline_x, mouseline_v, y, mouseline);
            vgamem_scan8(y, (unsigned char *)video.cv8backing, tpal, mouseline);
            for (x = 0; x < NATIVE_SCREEN_WIDTH; x += 2) {
                  *pixels++ = video.cv8backing[x+1]
                        | (video.cv8backing[x] << 4);
            }
      }
}

static inline void _blit11(int bpp, unsigned char *pixels, unsigned int pitch,
                  unsigned int *tpal)
{
      unsigned int mouseline_x = (video.mouse.x / 8);
      unsigned int mouseline_v = (video.mouse.x % 8);
      unsigned int mouseline[80];
      unsigned char *pdata;
      unsigned int x, y;
      int pitch24;

      switch (bpp) {
      case 4:
            for (y = 0; y < NATIVE_SCREEN_HEIGHT; y++) {
                  make_mouseline(mouseline_x, mouseline_v, y, mouseline);
                  vgamem_scan32(y, (unsigned int *)pixels, tpal, mouseline);
                  pixels += pitch;
            }
            break;
      case 3:
            /* ... */
            pitch24 = pitch - (NATIVE_SCREEN_WIDTH * 3);
            if (pitch24 < 0) {
                  return; /* eh? */
            }
            for (y = 0; y < NATIVE_SCREEN_HEIGHT; y++) {
                  make_mouseline(mouseline_x, mouseline_v, y, mouseline);
                  vgamem_scan32(y,(unsigned int*)video.cv32backing,tpal, mouseline);
                  /* okay... */
                  pdata = video.cv32backing;
                  for (x = 0; x < NATIVE_SCREEN_WIDTH; x++) {
#if WORDS_BIGENDIAN
                        memcpy(pixels, pdata+1, 3);
#else
                        memcpy(pixels, pdata, 3);
#endif
                        pdata += 4;
                        pixels += 3;
                  }
                  pixels += pitch24;
            }
            break;
      case 2:
            for (y = 0; y < NATIVE_SCREEN_HEIGHT; y++) {
                  make_mouseline(mouseline_x, mouseline_v, y, mouseline);
                  vgamem_scan16(y, (unsigned short *)pixels, tpal, mouseline);
                  pixels += pitch;
            }
            break;
      case 1:
            for (y = 0; y < NATIVE_SCREEN_HEIGHT; y++) {
                  make_mouseline(mouseline_x, mouseline_v, y, mouseline);
                  vgamem_scan8(y, (unsigned char *)pixels, tpal, mouseline);
                  pixels += pitch;
            }
            break;
      };
}

static void _video_blit_planar(void) {
      SDL_LockYUVOverlay(video.overlay);
      vgamem_lock();

      switch (video.yuvlayout) {
      case VIDEO_YUV_YV12_TV:
            /* halfwidth Y+V+U */
            _blitUV(video.overlay->pixels[0], video.overlay->pitches[0], video.yuv_y);
            _blitTV(video.overlay->pixels[1], video.overlay->pitches[1], video.yuv_v);
            _blitTV(video.overlay->pixels[2], video.overlay->pitches[2], video.yuv_u);
            break;
      case VIDEO_YUV_IYUV_TV:
            /* halfwidth Y+U+V */
            _blitUV(video.overlay->pixels[0], video.overlay->pitches[0], video.yuv_y);
            _blitTV(video.overlay->pixels[1], video.overlay->pitches[1], video.yuv_u);
            _blitTV(video.overlay->pixels[2], video.overlay->pitches[2], video.yuv_v);
            break;

      case VIDEO_YUV_YV12:
            /* Y+V+U */
            _blitYY(video.overlay->pixels[0], video.overlay->pitches[0], video.yuv_y);
            _blitUV(video.overlay->pixels[1], video.overlay->pitches[1], video.yuv_v);
            _blitUV(video.overlay->pixels[2], video.overlay->pitches[2], video.yuv_u);
            break;
      case VIDEO_YUV_IYUV:
            /* Y+U+V */
            _blitYY(video.overlay->pixels[0], video.overlay->pitches[0], video.yuv_y);
            _blitUV(video.overlay->pixels[1], video.overlay->pitches[1], video.yuv_u);
            _blitUV(video.overlay->pixels[2], video.overlay->pitches[2], video.yuv_v);
            break;
      };

      vgamem_unlock();

      SDL_UnlockYUVOverlay(video.overlay);
      SDL_DisplayYUVOverlay(video.overlay, &video.clip);
}

void video_blit(void)
{
      unsigned char *pixels = NULL;
      unsigned int bpp = 0;
      unsigned int pitch = 0;

      switch (video.desktop.type) {
      case VIDEO_SURFACE:
            if (SDL_MUSTLOCK(video.surface)) {
                  while (SDL_LockSurface(video.surface) == -1) {
                        SDL_Delay(10);
                  }
            }
            bpp = video.surface->format->BytesPerPixel;
            pixels = (unsigned char *)video.surface->pixels;
            pixels += video.clip.y * video.surface->pitch;
            pixels += video.clip.x * bpp;
            pitch = video.surface->pitch;
            break;
      case VIDEO_YUV:
            if (video.overlay->planes == 3) {
                  _video_blit_planar();
                  return;
            }
                  
            SDL_LockYUVOverlay(video.overlay);
            pixels = (unsigned char *)*(video.overlay->pixels);
            pitch = *(video.overlay->pitches);
            bpp = 4;
            break;
      case VIDEO_GL:
            pixels = (unsigned char *)video.gl.framebuf;
            pitch = video.gl.pitch;
            bpp = 4;
            break;
      case VIDEO_DDRAW:
#ifdef WIN32
            if (SDL_MUSTLOCK(video.ddblit.surface)) {
                  while (SDL_LockSurface(video.ddblit.surface) == -1) {
                        SDL_Delay(10);
                  }
            }
            pixels = (unsigned char *)video.ddblit.surface->pixels;
            pitch = video.ddblit.surface->pitch;
            bpp = video.surface->format->BytesPerPixel;
            break;
#else
            return; /* eh? */
#endif
      };

      vgamem_lock();
      if (video.draw.autoscale || (video.clip.w == NATIVE_SCREEN_WIDTH && video.clip.h == NATIVE_SCREEN_HEIGHT)) {
            /* scaling is provided by the hardware, or isn't necessary */
            _blit11(bpp, pixels, pitch, video.pal);
      } else {
            _blit1n(bpp, pixels, pitch);
      }
      vgamem_unlock();

      switch (video.desktop.type) {
      case VIDEO_SURFACE:
            if (SDL_MUSTLOCK(video.surface)) {
                  SDL_UnlockSurface(video.surface);
            }
            SDL_Flip(video.surface);
            break;
#ifdef WIN32
      case VIDEO_DDRAW:
            if (SDL_MUSTLOCK(video.ddblit.surface)) {
                  SDL_UnlockSurface(video.ddblit.surface);
            }
            switch (IDirectDrawSurface3_Blt(
                        video.surface->hwdata->dd_writebuf,
                        &video.ddblit.rect,
                        video.ddblit.surface->hwdata->dd_surface,0,
                        DDBLT_WAIT, NULL)) {
            case DD_OK:
                  break;
            case DDERR_SURFACELOST:
                  IDirectDrawSurface3_Restore(video.ddblit.surface->hwdata->dd_surface);
                  IDirectDrawSurface3_Restore(video.surface->hwdata->dd_surface);
                  break;
            default:
                  break;
            };
            SDL_Flip(video.surface);
            break;
#endif
      case VIDEO_YUV:
            SDL_UnlockYUVOverlay(video.overlay);
            SDL_DisplayYUVOverlay(video.overlay, &video.clip);
            break;
      case VIDEO_GL:
            my_glBindTexture(GL_TEXTURE_2D, video.gl.texture);
            my_glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
                  NATIVE_SCREEN_WIDTH, NATIVE_SCREEN_HEIGHT,
                  GL_BGRA_EXT,
                  GL_UNSIGNED_INT_8_8_8_8_REV,
                  video.gl.framebuf);
            my_glCallList(video.gl.displaylist);
            SDL_GL_SwapBuffers();
            break;
      };
}

/* FIXME: ugh */
int video_mousecursor_visible(void)
{
      return video.mouse.visible;
}

void video_mousecursor(int vis)
{
      if (vis == -1) {
            /* toggle */
            video.mouse.visible = !video.mouse.visible;
            /* maybe this shouldn't happen in classic mode?
               does anyone care? */
            status_text_flash("Mouse cursor %s",
                          video.mouse.visible ? "enabled" : "disabled");
      } else if (vis == 0 || vis == 1) {
            video.mouse.visible = vis;
      }
      if (!video.mouse.visible && !video.desktop.fullscreen) {
            /* display the system cursor when not fullscreen-
            even when the mouse cursor is invisible */
            SDL_ShowCursor(SDL_ENABLE);
      } else {
            SDL_ShowCursor(SDL_DISABLE);
      }
}

void video_translate(unsigned int vx, unsigned int vy,
            unsigned int *x, unsigned int *y)
{
      if ((signed) vx < video.clip.x) vx = video.clip.x;
      vx -= video.clip.x;

      if ((signed) vy < video.clip.y) vy = video.clip.y;
      vy -= video.clip.y;

      if ((signed) vx > video.clip.w) vx = video.clip.w;
      if ((signed) vy > video.clip.h) vy = video.clip.h;

      vx *= NATIVE_SCREEN_WIDTH;
      vy *= NATIVE_SCREEN_HEIGHT;
      vx /= (video.draw.width - (video.draw.width - video.clip.w));
      vy /= (video.draw.height - (video.draw.height - video.clip.h));

      if (video.mouse.visible && (video.mouse.x != vx || video.mouse.y != vy)) {
            status.flags |= SOFTWARE_MOUSE_MOVED;
      }
      video.mouse.x = vx;
      video.mouse.y = vy;
      if (x) *x = vx;
      if (y) *y = vy;
}


/* here we go... */
struct _ss_data {
      int x, y;

      unsigned char scanline[NATIVE_SCREEN_WIDTH];
      unsigned int mouseline[80];
      unsigned int tc_identity[256];

      FILE *fp;
};
static void _ss_output(struct pngw_arg *o, const void *data, int len)
{
      struct _ss_data *d = (void *)o->extra_user_data;
      fwrite(data, len, 1, d->fp);
}
static int _ss_next_pixel(struct pngw_arg *o)
{
      struct _ss_data *d = (void *)o->extra_user_data;

      if (d->x == NATIVE_SCREEN_WIDTH) {
            d->x = 0;
            d->y++;

            vgamem_scan8(d->y, d->scanline, d->tc_identity, d->mouseline);
      }
      
      d->x++;
      return (int)d->scanline[ d->x - 1 ];
}

void video_screenshot(void)
{
      struct _ss_data data;
      struct pngw_arg pa;
      char dwbuf[65536];
      char fname[64];
      int i, fd, fp_ok;

      data.x = NATIVE_SCREEN_WIDTH;
      data.y = -1;
      memset(data.mouseline, 0, 80*sizeof(unsigned int));
      for (i = 0; i < 256; i++) data.tc_identity[i] = i;
      for (i = 0;; i++) {
            sprintf(fname, "Screenshot-%d.png", i);
            fd = open(fname, O_CREAT|O_EXCL|O_RDWR, 0666);
            if (fd == -1 && errno == EEXIST) continue;
            if (fd == -1) {
                  log_appendf(4, "Cannot open %s for writing: %s",fname,
                              strerror(errno));
                  return;
            }
            data.fp = fopen(fname, "wb");
            if (!data.fp) {
                  log_appendf(4, "Cannot open %s for writing: %s",fname,
                              strerror(errno));
                  return;
            }
            close(fd);
            setvbuf(data.fp, dwbuf, _IOFBF, sizeof(dwbuf));
            break;
      }

      pa.width = NATIVE_SCREEN_WIDTH;
      pa.height = NATIVE_SCREEN_HEIGHT;
      pa.pal_size = 256;
      pa.pal = video.tc_bgr32;
      pa.output = _ss_output;
      pa.read_pixel = _ss_next_pixel;
      pa.extra_user_data = (void*)&data;

      vgamem_lock();
      pngw(&pa);
      vgamem_unlock();
      fp_ok = 1;
      if (ferror(data.fp)) fp_ok = 0;
#ifdef WIN32
      if (_commit(fileno(data.fp)) == -1)
            fp_ok = 0;
#else
      if (fsync(fileno(data.fp)) == -1)
            fp_ok = 0;
#endif
      if (ferror(data.fp)) fp_ok = 0;
      if (fclose(data.fp) == EOF) fp_ok = 0;
      if (!fp_ok) {
            unlink(fname);
            log_appendf(4, "Cannot write to %s: %s",fname,
                        strerror(errno));
      } else {
            sprintf(dwbuf, "Wrote screenshot: %s",fname);
            log_appendf(2, dwbuf);
            status_text_flash(dwbuf);
      }

}


Generated by  Doxygen 1.6.0   Back to index