view src/video/gem/SDL_gemvideo.c @ 2106:2714a8976e37

GEM has a window manager, of course
author Patrice Mandin <patmandin@gmail.com>
date Sat, 26 May 2007 19:46:04 +0000
parents adf732f1f016
children 757dfb38f574
line wrap: on
line source

/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2006 Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    Sam Lantinga
    slouken@libsdl.org
*/
#include "SDL_config.h"

/*
	GEM video driver

	Patrice Mandin
	and work from
	Olivier Landemarre, Johan Klockars, Xavier Joubert, Claude Attard
*/

/* Mint includes */
#include <gem.h>
#include <gemx.h>
#include <mint/osbind.h>
#include <mint/cookie.h>

#include "SDL_endian.h"
#include "SDL_video.h"
#include "SDL_mouse.h"
#include "../SDL_sysvideo.h"
#include "../SDL_pixels_c.h"
#include "../../events/SDL_events_c.h"
#include "../SDL_cursor_c.h"

#include "../ataricommon/SDL_ataric2p_s.h"
#include "../ataricommon/SDL_atarieddi_s.h"
#include "../ataricommon/SDL_atarimxalloc_c.h"
#include "../ataricommon/SDL_atarigl_c.h"

#include "SDL_gemvideo.h"
#include "SDL_gemevents_c.h"
#include "SDL_gemmouse_c.h"
#include "SDL_gemwm_c.h"
#include "../ataricommon/SDL_xbiosevents_c.h"
#include "../ataricommon/SDL_ataridevmouse_c.h"

/* Defines */

/*#define DEBUG_VIDEO_GEM	1*/

#define GEM_VID_DRIVER_NAME "gem"

#undef MIN
#define MIN(a,b) (((a)<(b)) ? (a) : (b))
#undef MAX
#define MAX(a,b) (((a)>(b)) ? (a) : (b))

/* Variables */

static unsigned char vdi_index[256] = {
    0, 2, 3, 6, 4, 7, 5, 8,
    9, 10, 11, 14, 12, 15, 13, 255
};

static const unsigned char empty_name[] = "";

/* Initialization/Query functions */
static int GEM_VideoInit(_THIS, SDL_PixelFormat * vformat);
static SDL_Rect **GEM_ListModes(_THIS, SDL_PixelFormat * format,
                                Uint32 flags);
static SDL_Surface *GEM_SetVideoMode(_THIS, SDL_Surface * current, int width,
                                     int height, int bpp, Uint32 flags);
static int GEM_SetColors(_THIS, int firstcolor, int ncolors,
                         SDL_Color * colors);
static void GEM_VideoQuit(_THIS);

/* Hardware surface functions */
static int GEM_AllocHWSurface(_THIS, SDL_Surface * surface);
static int GEM_LockHWSurface(_THIS, SDL_Surface * surface);
static int GEM_FlipHWSurface(_THIS, SDL_Surface * surface);
static void GEM_UnlockHWSurface(_THIS, SDL_Surface * surface);
static void GEM_FreeHWSurface(_THIS, SDL_Surface * surface);
static void GEM_UpdateRects(_THIS, int numrects, SDL_Rect * rects);
#if 0
static int GEM_ToggleFullScreen(_THIS, int on);
#endif

/* Internal functions */
static void GEM_FreeBuffers(_THIS);
static void GEM_ClearScreen(_THIS);
static void GEM_ClearRect(_THIS, short *rect);
static void GEM_SetNewPalette(_THIS, Uint16 newpal[256][3]);
static void GEM_LockScreen(_THIS);
static void GEM_UnlockScreen(_THIS);
static void refresh_window(_THIS, int winhandle, short *rect);

#if SDL_VIDEO_OPENGL
/* OpenGL functions */
static void GEM_GL_SwapBuffers(_THIS);
#endif

/* GEM driver bootstrap functions */

static int
GEM_Available(void)
{
    /* Test if AES available */
    if (appl_init() == -1)
        return 0;

    appl_exit();
    return 1;
}

static void
GEM_DeleteDevice(SDL_VideoDevice * device)
{
    SDL_free(device->hidden);
    SDL_free(device);
}

static SDL_VideoDevice *
GEM_CreateDevice(int devindex)
{
    SDL_VideoDevice *device;
    int vectors_mask;
    unsigned long dummy;

    /* Initialize all variables that we clean on shutdown */
    device = (SDL_VideoDevice *) SDL_malloc(sizeof(SDL_VideoDevice));
    if (device) {
        SDL_memset(device, 0, (sizeof *device));
        device->hidden = (struct SDL_PrivateVideoData *)
            SDL_malloc((sizeof *device->hidden));
        device->gl_data = (struct SDL_PrivateGLData *)
            SDL_malloc((sizeof *device->gl_data));
    }
    if ((device == NULL) || (device->hidden == NULL)) {
        SDL_OutOfMemory();
        if (device) {
            SDL_free(device);
        }
        return (0);
    }
    SDL_memset(device->hidden, 0, (sizeof *device->hidden));
    SDL_memset(device->gl_data, 0, sizeof(*device->gl_data));

    /* Set the function pointers */
    device->VideoInit = GEM_VideoInit;
    device->ListModes = GEM_ListModes;
    device->SetVideoMode = GEM_SetVideoMode;
    device->SetColors = GEM_SetColors;
    device->UpdateRects = NULL /*GEM_UpdateRects */ ;
    device->VideoQuit = GEM_VideoQuit;
    device->AllocHWSurface = GEM_AllocHWSurface;
    device->LockHWSurface = GEM_LockHWSurface;
    device->UnlockHWSurface = GEM_UnlockHWSurface;
    device->FlipHWSurface = GEM_FlipHWSurface;
    device->FreeHWSurface = GEM_FreeHWSurface;
    device->ToggleFullScreen = NULL /*GEM_ToggleFullScreen */ ;

    /* Window manager */
    device->SetCaption = GEM_SetCaption;
    device->SetIcon = GEM_SetIcon;
    device->IconifyWindow = GEM_IconifyWindow;
    device->GrabInput = GEM_GrabInput;

    /* Events */
    device->InitOSKeymap = GEM_InitOSKeymap;
    device->PumpEvents = GEM_PumpEvents;

    /* Mouse */
    device->FreeWMCursor = GEM_FreeWMCursor;
    device->CreateWMCursor = GEM_CreateWMCursor;
    device->ShowWMCursor = GEM_ShowWMCursor;
    device->WarpWMCursor = NULL /*GEM_WarpWMCursor */ ;
    device->CheckMouseMode = GEM_CheckMouseMode;

#if SDL_VIDEO_OPENGL
    /* OpenGL functions */
    device->GL_LoadLibrary = SDL_AtariGL_LoadLibrary;
    device->GL_GetProcAddress = SDL_AtariGL_GetProcAddress;
    device->GL_GetAttribute = SDL_AtariGL_GetAttribute;
    device->GL_MakeCurrent = SDL_AtariGL_MakeCurrent;
    device->GL_SwapBuffers = GEM_GL_SwapBuffers;
#endif

    device->hidden->use_dev_mouse =
        (SDL_AtariDevMouse_Open() != 0) ? SDL_TRUE : SDL_FALSE;

    vectors_mask = ATARI_XBIOS_JOYSTICKEVENTS;  /* XBIOS joystick events */
    if (!(device->hidden->use_dev_mouse)) {
        vectors_mask |= ATARI_XBIOS_MOUSEEVENTS;        /* XBIOS mouse events */
    }
    /*if (Getcookie(C_MiNT, &dummy) == C_FOUND) {
       vectors_mask = 0;
       } */

    SDL_AtariXbios_InstallVectors(vectors_mask);

    device->free = GEM_DeleteDevice;

    return device;
}

VideoBootStrap GEM_bootstrap = {
    GEM_VID_DRIVER_NAME, "Atari GEM video driver",
    GEM_Available, GEM_CreateDevice
};

static void
VDI_ReadExtInfo(_THIS, short *work_out)
{
    unsigned long EdDI_version;
    unsigned long cookie_EdDI;
    Uint32 num_colours;
    Uint16 clut_type, num_bits;

    /* Read EdDI informations */
    if (Getcookie(C_EdDI, &cookie_EdDI) == C_NOTFOUND) {
        return;
    }

    EdDI_version = Atari_get_EdDI_version((void *) cookie_EdDI);

    vq_scrninfo(VDI_handle, work_out);

    VDI_format = work_out[0];
    clut_type = work_out[1];
    num_bits = work_out[2];
    num_colours = *((Uint32 *) & work_out[3]);

    /* With EdDI>=1.1, we can have screen pitch, address and format
     * so we can directly write to screen without using vro_cpyfm
     */
    if (EdDI_version >= EDDI_11) {
        VDI_pitch = work_out[5];
        VDI_screen = (void *) *((unsigned long *) &work_out[6]);
    }

    switch (clut_type) {
    case VDI_CLUT_HARDWARE:
        {
            int i;
            Uint16 *tmp_p;

            tmp_p = (Uint16 *) & work_out[16];

            for (i = 0; i < 256; i++) {
                vdi_index[*tmp_p++] = i;
            }
        }
        break;
    case VDI_CLUT_SOFTWARE:
        {
            int component;      /* red, green, blue, alpha, overlay */
            int num_bit;
            unsigned short *tmp_p;

            /* We can build masks with info here */
            tmp_p = (unsigned short *) &work_out[16];
            for (component = 0; component < 5; component++) {
                for (num_bit = 0; num_bit < 16; num_bit++) {
                    unsigned short valeur;

                    valeur = *tmp_p++;

                    if (valeur == 0xffff) {
                        continue;
                    }

                    switch (component) {
                    case 0:
                        VDI_redmask |= 1 << valeur;
                        break;
                    case 1:
                        VDI_greenmask |= 1 << valeur;
                        break;
                    case 2:
                        VDI_bluemask |= 1 << valeur;
                        break;
                    case 3:
                        VDI_alphamask |= 1 << valeur;
                        break;
                    }
                }
            }
        }

        /* Remove lower green bits for Intel endian screen */
        if ((VDI_greenmask == ((7 << 13) | 3))
            || (VDI_greenmask == ((7 << 13) | 7))) {
            VDI_greenmask &= ~(7 << 13);
        }
        break;
    case VDI_CLUT_NONE:
        break;
    }
}

int
GEM_VideoInit(_THIS, SDL_PixelFormat * vformat)
{
    int i, menubar_size;
    short work_in[12], work_out[272], dummy;

    /* Open AES (Application Environment Services) */
    if (appl_init() == -1) {
        fprintf(stderr, "Can not open AES\n");
        return 1;
    }

    /* Read version and features */
    GEM_version = aes_global[0];
    if (GEM_version >= 0x0410) {
        short ap_gout[4], errorcode;

        GEM_wfeatures = 0;
        errorcode =
            appl_getinfo(AES_WINDOW, &ap_gout[0], &ap_gout[1], &ap_gout[2],
                         &ap_gout[3]);

        if (errorcode == 0) {
            GEM_wfeatures = ap_gout[0];
        }
    }

    /* Ask VDI physical workstation handle opened by AES */
    VDI_handle = graf_handle(&dummy, &dummy, &dummy, &dummy);
    if (VDI_handle < 1) {
        fprintf(stderr, "Wrong VDI handle %d returned by AES\n", VDI_handle);
        return 1;
    }

    /* Open virtual VDI workstation */
    work_in[0] = Getrez() + 2;
    for (i = 1; i < 10; i++)
        work_in[i] = 1;
    work_in[10] = 2;

    v_opnvwk(work_in, &VDI_handle, work_out);
    if (VDI_handle == 0) {
        fprintf(stderr, "Can not open VDI virtual workstation\n");
        return 1;
    }

    /* Read fullscreen size */
    VDI_w = work_out[0] + 1;
    VDI_h = work_out[1] + 1;

    /* Read desktop size and position */
    if (!wind_get
        (DESKTOP_HANDLE, WF_WORKXYWH, &GEM_desk_x, &GEM_desk_y, &GEM_desk_w,
         &GEM_desk_h)) {
        fprintf(stderr, "Can not read desktop properties\n");
        return 1;
    }

    /* Read bit depth */
    vq_extnd(VDI_handle, 1, work_out);
    VDI_bpp = work_out[4];
    VDI_oldnumcolors = 0;

    switch (VDI_bpp) {
    case 8:
        VDI_pixelsize = 1;
        break;
    case 15:
    case 16:
        VDI_pixelsize = 2;
        break;
    case 24:
        VDI_pixelsize = 3;
        break;
    case 32:
        VDI_pixelsize = 4;
        break;
    default:
        fprintf(stderr, "%d bits colour depth not supported\n", VDI_bpp);
        return 1;
    }

    /* Setup hardware -> VDI palette mapping */
    for (i = 16; i < 255; i++) {
        vdi_index[i] = i;
    }
    vdi_index[255] = 1;

    /* Save current palette */
    if (VDI_bpp > 8) {
        VDI_oldnumcolors = 1 << 8;
    } else {
        VDI_oldnumcolors = 1 << VDI_bpp;
    }

    for (i = 0; i < VDI_oldnumcolors; i++) {
        short rgb[3];

        vq_color(VDI_handle, i, 0, rgb);

        VDI_oldpalette[i][0] = rgb[0];
        VDI_oldpalette[i][1] = rgb[1];
        VDI_oldpalette[i][2] = rgb[2];
    }
    VDI_setpalette = GEM_SetNewPalette;
    SDL_memcpy(VDI_curpalette, VDI_oldpalette, sizeof(VDI_curpalette));

    /* Setup screen info */
    GEM_title_name = empty_name;
    GEM_icon_name = empty_name;

    GEM_handle = -1;
    GEM_locked = SDL_FALSE;
    GEM_win_fulled = SDL_FALSE;
    GEM_fullscreen = SDL_FALSE;
    GEM_lock_redraw = SDL_TRUE; /* Prevent redraw till buffers are setup */

    VDI_screen = NULL;
    VDI_pitch = VDI_w * VDI_pixelsize;
    VDI_format = ((VDI_bpp <= 8) ? VDI_FORMAT_INTER : VDI_FORMAT_PACK);
    VDI_redmask = VDI_greenmask = VDI_bluemask = VDI_alphamask = 0;
    VDI_ReadExtInfo(this, work_out);

#ifdef DEBUG_VIDEO_GEM
    printf("sdl:video:gem: screen: address=0x%08x, pitch=%d\n", VDI_screen,
           VDI_pitch);
    printf("sdl:video:gem: format=%d\n", VDI_format);
    printf("sdl:video:gem: masks: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
           VDI_alphamask, VDI_redmask, VDI_greenmask, VDI_bluemask);
#endif

    /* Setup destination mfdb */
    VDI_dst_mfdb.fd_addr = NULL;

    /* Determine the current screen size */
    this->info.current_w = VDI_w;
    this->info.current_h = VDI_h;

    /* Determine the screen depth */
    /* we change this during the SDL_SetVideoMode implementation... */
    vformat->BitsPerPixel = VDI_bpp;

    /* Set mouse cursor to arrow */
    graf_mouse(ARROW, NULL);

    /* Init chunky to planar routine */
    SDL_Atari_C2pConvert = SDL_Atari_C2pConvert8;

    /* Setup VDI fill functions */
    vsf_color(VDI_handle, 0);
    vsf_interior(VDI_handle, 1);
    vsf_perimeter(VDI_handle, 0);

    /* Menu bar save buffer */
    menubar_size = GEM_desk_w * GEM_desk_y * VDI_pixelsize;
    GEM_menubar = Atari_SysMalloc(menubar_size, MX_PREFTTRAM);

    /* Fill video modes list */
    SDL_modelist[0] = SDL_malloc(sizeof(SDL_Rect));
    SDL_modelist[0]->x = 0;
    SDL_modelist[0]->y = 0;
    SDL_modelist[0]->w = VDI_w;
    SDL_modelist[0]->h = VDI_h;

    SDL_modelist[1] = NULL;

#if SDL_VIDEO_OPENGL
    SDL_AtariGL_InitPointers(this);
#endif

    this->info.wm_available = 1;

    /* We're done! */
    return (0);
}

SDL_Rect **
GEM_ListModes(_THIS, SDL_PixelFormat * format, Uint32 flags)
{
    if (format->BitsPerPixel != VDI_bpp) {
        return ((SDL_Rect **) NULL);
    }

    if (flags & SDL_FULLSCREEN) {
        return (SDL_modelist);
    }

    return ((SDL_Rect **) - 1);
}

static void
GEM_FreeBuffers(_THIS)
{
    /* Release buffer */
    if (GEM_buffer2) {
        Mfree(GEM_buffer2);
        GEM_buffer2 = NULL;
    }

    if (GEM_buffer1) {
        Mfree(GEM_buffer1);
        GEM_buffer1 = NULL;
    }
}

static void
GEM_ClearRect(_THIS, short *rect)
{
    short oldrgb[3], rgb[3] = { 0, 0, 0 };

    vq_color(VDI_handle, vdi_index[0], 0, oldrgb);
    vs_color(VDI_handle, vdi_index[0], rgb);

    vsf_color(VDI_handle, 0);
    vsf_interior(VDI_handle, 1);
    vsf_perimeter(VDI_handle, 0);
    v_bar(VDI_handle, rect);

    vs_color(VDI_handle, vdi_index[0], oldrgb);
}

static void
GEM_ClearScreen(_THIS)
{
    short pxy[4];

    v_hide_c(VDI_handle);

    pxy[0] = pxy[1] = 0;
    pxy[2] = VDI_w - 1;
    pxy[3] = VDI_h - 1;
    GEM_ClearRect(this, pxy);

    v_show_c(VDI_handle, 1);
}

static void
GEM_SetNewPalette(_THIS, Uint16 newpal[256][3])
{
    int i;
    short rgb[3];

    if (VDI_oldnumcolors == 0)
        return;

    for (i = 0; i < VDI_oldnumcolors; i++) {
        rgb[0] = newpal[i][0];
        rgb[1] = newpal[i][1];
        rgb[2] = newpal[i][2];

        vs_color(VDI_handle, i, rgb);
    }
}

static void
GEM_LockScreen(_THIS)
{
    if (!GEM_locked) {
        /* Lock AES */
        wind_update(BEG_UPDATE);
        wind_update(BEG_MCTRL);
        /* Reserve memory space, used to be sure of compatibility */
        form_dial(FMD_START, 0, 0, 0, 0, 0, 0, VDI_w, VDI_h);

        /* Save menu bar */
        if (GEM_menubar) {
            MFDB mfdb_src;
            short blitcoords[8];

            mfdb_src.fd_addr = GEM_menubar;
            mfdb_src.fd_w = GEM_desk_w;
            mfdb_src.fd_h = GEM_desk_y;
            mfdb_src.fd_wdwidth = GEM_desk_w >> 4;
            mfdb_src.fd_nplanes = VDI_bpp;
            mfdb_src.fd_stand =
                mfdb_src.fd_r1 = mfdb_src.fd_r2 = mfdb_src.fd_r3 = 0;

            blitcoords[0] = blitcoords[4] = 0;
            blitcoords[1] = blitcoords[5] = 0;
            blitcoords[2] = blitcoords[6] = GEM_desk_w - 1;
            blitcoords[3] = blitcoords[7] = GEM_desk_y - 1;

            vro_cpyfm(VDI_handle, S_ONLY, blitcoords, &VDI_dst_mfdb,
                      &mfdb_src);
        }

        GEM_locked = SDL_TRUE;
    }
}

static void
GEM_UnlockScreen(_THIS)
{
    if (GEM_locked) {
        /* Restore menu bar */
        if (GEM_menubar) {
            MFDB mfdb_src;
            short blitcoords[8];

            mfdb_src.fd_addr = GEM_menubar;
            mfdb_src.fd_w = GEM_desk_w;
            mfdb_src.fd_h = GEM_desk_y;
            mfdb_src.fd_wdwidth = GEM_desk_w >> 4;
            mfdb_src.fd_nplanes = VDI_bpp;
            mfdb_src.fd_stand =
                mfdb_src.fd_r1 = mfdb_src.fd_r2 = mfdb_src.fd_r3 = 0;

            blitcoords[0] = blitcoords[4] = 0;
            blitcoords[1] = blitcoords[5] = 0;
            blitcoords[2] = blitcoords[6] = GEM_desk_w - 1;
            blitcoords[3] = blitcoords[7] = GEM_desk_y - 1;

            vro_cpyfm(VDI_handle, S_ONLY, blitcoords, &mfdb_src,
                      &VDI_dst_mfdb);
        }

        /* Restore screen memory, and send REDRAW to all apps */
        form_dial(FMD_FINISH, 0, 0, 0, 0, 0, 0, VDI_w, VDI_h);
        /* Unlock AES */
        wind_update(END_MCTRL);
        wind_update(END_UPDATE);

        GEM_locked = SDL_FALSE;
    }
}

SDL_Surface *
GEM_SetVideoMode(_THIS, SDL_Surface * current,
                 int width, int height, int bpp, Uint32 flags)
{
    int maxwidth, maxheight;
    Uint32 modeflags, screensize;
    SDL_bool use_shadow1, use_shadow2;

        /*--- Verify if asked mode can be used ---*/
    if (flags & SDL_FULLSCREEN) {
        maxwidth = VDI_w;
        maxheight = VDI_h;
    } else {
        /* Windowed mode */
        maxwidth = GEM_desk_w;
        maxheight = GEM_desk_h;
    }

    /* width must be multiple of 16, for vro_cpyfm() and c2p_convert() */
    if ((width & 15) != 0) {
        width = (width | 15) + 1;
    }

    if ((maxwidth < width) || (maxheight < height) || (VDI_bpp != bpp)) {
        SDL_SetError("Couldn't find requested mode in list");
        return (NULL);
    }

        /*--- Allocate the new pixel format for the screen ---*/
    if (!SDL_ReallocFormat
        (current, VDI_bpp, VDI_redmask, VDI_greenmask, VDI_bluemask,
         VDI_alphamask)) {
        SDL_SetError("Couldn't allocate new pixel format for requested mode");
        return (NULL);
    }

    screensize = width * height * VDI_pixelsize;

#ifdef DEBUG_VIDEO_GEM
    printf("sdl:video:gem: setvideomode(): %dx%dx%d = %d\n", width, height,
           bpp, screensize);
#endif

        /*--- Allocate shadow buffers if needed, and conversion operations ---*/
    GEM_FreeBuffers(this);

    GEM_bufops = 0;
    use_shadow1 = use_shadow2 = SDL_FALSE;
    if (VDI_screen && (flags & SDL_FULLSCREEN)) {
        if (VDI_format == VDI_FORMAT_INTER) {
            use_shadow1 = SDL_TRUE;
            GEM_bufops = B2S_C2P_1TOS;
        }
    } else {
        use_shadow1 = SDL_TRUE;
        if (VDI_format == VDI_FORMAT_PACK) {
            GEM_bufops = B2S_VROCPYFM_1TOS;
        } else {
            use_shadow2 = SDL_TRUE;
            GEM_bufops = B2S_C2P_1TO2 | B2S_VROCPYFM_2TOS;
        }
    }

    if (use_shadow1) {
        GEM_buffer1 = Atari_SysMalloc(screensize, MX_PREFTTRAM);
        if (GEM_buffer1 == NULL) {
            SDL_SetError("Can not allocate %d KB for frame buffer",
                         screensize >> 10);
            return NULL;
        }
        SDL_memset(GEM_buffer1, 0, screensize);
#ifdef DEBUG_VIDEO_GEM
        printf("sdl:video:gem: setvideomode(): allocated buffer 1\n");
#endif
    }

    if (use_shadow2) {
        GEM_buffer2 = Atari_SysMalloc(screensize, MX_PREFTTRAM);
        if (GEM_buffer2 == NULL) {
            SDL_SetError("Can not allocate %d KB for shadow buffer",
                         screensize >> 10);
            return NULL;
        }
        SDL_memset(GEM_buffer2, 0, screensize);
#ifdef DEBUG_VIDEO_GEM
        printf("sdl:video:gem: setvideomode(): allocated buffer 2\n");
#endif
    }

        /*--- Initialize screen ---*/
    modeflags = SDL_PREALLOC;
    if (VDI_bpp == 8) {
        modeflags |= SDL_HWPALETTE;
    }

    if (flags & SDL_FULLSCREEN) {
        GEM_LockScreen(this);

        GEM_ClearScreen(this);

        modeflags |= SDL_FULLSCREEN;
        if (VDI_screen && (VDI_format == VDI_FORMAT_PACK) && !use_shadow1) {
            modeflags |= SDL_HWSURFACE;
        } else {
            modeflags |= SDL_SWSURFACE;
        }

        GEM_fullscreen = SDL_TRUE;
    } else {
        int old_win_type;
        short x2, y2, w2, h2;

        GEM_UnlockScreen(this);

        /* Set window gadgets */
        old_win_type = GEM_win_type;
        if (!(flags & SDL_NOFRAME)) {
            GEM_win_type = NAME | MOVER | CLOSER | SMALLER;
            if (flags & SDL_RESIZABLE) {
                GEM_win_type |= FULLER | SIZER;
                modeflags |= SDL_RESIZABLE;
            }
        } else {
            GEM_win_type = 0;
            modeflags |= SDL_NOFRAME;
        }
        modeflags |= SDL_SWSURFACE;

        /* Recreate window ? only for different widget or non-created window */
        if ((old_win_type != GEM_win_type) || (GEM_handle < 0)) {
            /* Calculate window size */
            if (!wind_calc
                (WC_BORDER, GEM_win_type, 0, 0, width, height, &x2, &y2,
                 &w2, &h2)) {
                GEM_FreeBuffers(this);
                SDL_SetError("Can not calculate window attributes");
                return NULL;
            }

            /* Center window */
            x2 = GEM_desk_x + ((GEM_desk_w - w2) >> 1);
            y2 = GEM_desk_y + ((GEM_desk_h - h2) >> 1);

            /* Destroy existing window */
            if (GEM_handle >= 0) {
                wind_close(GEM_handle);
                wind_delete(GEM_handle);
            }

            /* Create window */
            GEM_handle = wind_create(GEM_win_type, x2, y2, w2, h2);
            if (GEM_handle < 0) {
                GEM_FreeBuffers(this);
                SDL_SetError("Can not create window");
                return NULL;
            }
#ifdef DEBUG_VIDEO_GEM
            printf("sdl:video:gem: handle=%d\n", GEM_handle);
#endif

            /* Setup window name */
            wind_set(GEM_handle, WF_NAME,
                     (short) (((unsigned long) GEM_title_name) >> 16),
                     (short) (((unsigned long) GEM_title_name) & 0xffff),
                     0, 0);
            GEM_refresh_name = SDL_FALSE;

            /* Open the window */
            wind_open(GEM_handle, x2, y2, w2, h2);
        } else {
            /* Resize window if needed, to fit asked video mode */
            if (modeflags & SDL_RESIZABLE) {
                wind_get(GEM_handle, WF_WORKXYWH, &x2, &y2, &w2, &h2);
                if ((w2 & 15) != 0) {
                    w2 = (w2 | 15) + 1;
                }
                if ((w2 != width) || (h2 != height)) {
                    if (wind_calc
                        (WC_BORDER, GEM_win_type, x2, y2, width,
                         height, &x2, &y2, &w2, &h2)) {
                        wind_set(GEM_handle, WF_CURRXYWH, x2, y2, w2, h2);
                    }
                }
            }
        }

        GEM_fullscreen = SDL_FALSE;
    }

    /* Set up the new mode framebuffer */
    current->w = width;
    current->h = height;
    if (use_shadow1) {
        current->pixels = GEM_buffer1;
        current->pitch = width * VDI_pixelsize;
    } else {
        current->pixels = VDI_screen;
        current->pitch = VDI_pitch;
    }

#if SDL_VIDEO_OPENGL
    if (flags & SDL_INTERNALOPENGL) {
        if (!SDL_AtariGL_Init(this, current)) {
            GEM_FreeBuffers(this);
            SDL_SetError("Can not create OpenGL context");
            return NULL;
        }

        modeflags |= SDL_INTERNALOPENGL;
    }
#endif

    current->flags = modeflags;

#ifdef DEBUG_VIDEO_GEM
    printf("sdl:video:gem: surface: %dx%d\n", current->w, current->h);
#endif

    this->UpdateRects = GEM_UpdateRects;
    GEM_lock_redraw = SDL_FALSE;        /* Enable redraw */

    /* We're done */
    return (current);
}

static int
GEM_AllocHWSurface(_THIS, SDL_Surface * surface)
{
    return -1;
}

static void
GEM_FreeHWSurface(_THIS, SDL_Surface * surface)
{
    return;
}

static int
GEM_LockHWSurface(_THIS, SDL_Surface * surface)
{
    return (0);
}

static void
GEM_UnlockHWSurface(_THIS, SDL_Surface * surface)
{
    return;
}

static void
GEM_UpdateRectsFullscreen(_THIS, int numrects, SDL_Rect * rects)
{
    SDL_Surface *surface;
    int i, surf_width;

    surface = this->screen;
    /* Need to be a multiple of 16 pixels */
    surf_width = surface->w;
    if ((surf_width & 15) != 0) {
        surf_width = (surf_width | 15) + 1;
    }

    if (GEM_bufops & (B2S_C2P_1TO2 | B2S_C2P_1TOS)) {
        void *destscr;
        int destpitch;

        if (GEM_bufops & B2S_C2P_1TOS) {
            destscr = VDI_screen;
            destpitch = VDI_pitch;
        } else {
            destscr = GEM_buffer2;
            destpitch = surface->pitch;
        }

        for (i = 0; i < numrects; i++) {
            void *source, *destination;
            int x1, x2;

            x1 = rects[i].x & ~15;
            x2 = rects[i].x + rects[i].w;
            if (x2 & 15) {
                x2 = (x2 | 15) + 1;
            }

            source = surface->pixels;
            source += surface->pitch * rects[i].y;
            source += x1;

            destination = destscr;
            destination += destpitch * rects[i].y;
            destination += x1;

            SDL_Atari_C2pConvert(source, destination,
                                 x2 - x1, rects[i].h,
                                 SDL_FALSE, surface->pitch, destpitch);
        }
    }

    if (GEM_bufops & (B2S_VROCPYFM_1TOS | B2S_VROCPYFM_2TOS)) {
        MFDB mfdb_src;
        short blitcoords[8];

        mfdb_src.fd_addr = surface->pixels;
        mfdb_src.fd_w = surf_width;
        mfdb_src.fd_h = surface->h;
        mfdb_src.fd_wdwidth = (surface->pitch / VDI_pixelsize) >> 4;
        mfdb_src.fd_nplanes = surface->format->BitsPerPixel;
        mfdb_src.fd_stand =
            mfdb_src.fd_r1 = mfdb_src.fd_r2 = mfdb_src.fd_r3 = 0;
        if (GEM_bufops & B2S_VROCPYFM_2TOS) {
            mfdb_src.fd_addr = GEM_buffer2;
        }

        for (i = 0; i < numrects; ++i) {
            blitcoords[0] = blitcoords[4] = rects[i].x;
            blitcoords[1] = blitcoords[5] = rects[i].y;
            blitcoords[2] = blitcoords[6] = rects[i].x + rects[i].w - 1;
            blitcoords[3] = blitcoords[7] = rects[i].y + rects[i].h - 1;

            vro_cpyfm(VDI_handle, S_ONLY, blitcoords, &mfdb_src,
                      &VDI_dst_mfdb);
        }
    }
}

static void
GEM_UpdateRectsWindowed(_THIS, int numrects, SDL_Rect * rects)
{
    short pxy[4], wind_pxy[4];
    int i;

    if (wind_get
        (GEM_handle, WF_WORKXYWH, &wind_pxy[0], &wind_pxy[1], &wind_pxy[2],
         &wind_pxy[3]) == 0) {
        return;
    }

    for (i = 0; i < numrects; ++i) {
        pxy[0] = wind_pxy[0] + rects[i].x;
        pxy[1] = wind_pxy[1] + rects[i].y;
        pxy[2] = rects[i].w;
        pxy[3] = rects[i].h;

        GEM_wind_redraw(this, GEM_handle, pxy);
    }
}

static void
GEM_UpdateRects(_THIS, int numrects, SDL_Rect * rects)
{
    SDL_Surface *surface;

    if (GEM_lock_redraw) {
        return;
    }

    surface = this->screen;

    if (surface->flags & SDL_FULLSCREEN) {
        GEM_UpdateRectsFullscreen(this, numrects, rects);
    } else {
        GEM_UpdateRectsWindowed(this, numrects, rects);
    }
}

static int
GEM_FlipHWSurfaceFullscreen(_THIS, SDL_Surface * surface)
{
    int surf_width;

    /* Need to be a multiple of 16 pixels */
    surf_width = surface->w;
    if ((surf_width & 15) != 0) {
        surf_width = (surf_width | 15) + 1;
    }

    if (GEM_bufops & (B2S_C2P_1TO2 | B2S_C2P_1TOS)) {
        void *destscr;
        int destpitch;

        if (GEM_bufops & B2S_C2P_1TOS) {
            destscr = VDI_screen;
            destpitch = VDI_pitch;
        } else {
            destscr = GEM_buffer2;
            destpitch = surface->pitch;
        }

        SDL_Atari_C2pConvert(surface->pixels, destscr,
                             surf_width, surface->h,
                             SDL_FALSE, surface->pitch, destpitch);
    }

    if (GEM_bufops & (B2S_VROCPYFM_1TOS | B2S_VROCPYFM_2TOS)) {
        MFDB mfdb_src;
        short blitcoords[8];

        mfdb_src.fd_w = surf_width;
        mfdb_src.fd_h = surface->h;
        mfdb_src.fd_wdwidth = mfdb_src.fd_w >> 4;
        mfdb_src.fd_nplanes = surface->format->BitsPerPixel;
        mfdb_src.fd_stand =
            mfdb_src.fd_r1 = mfdb_src.fd_r2 = mfdb_src.fd_r3 = 0;
        if (GEM_bufops & B2S_VROCPYFM_1TOS) {
            mfdb_src.fd_addr = surface->pixels;
        } else {
            mfdb_src.fd_addr = GEM_buffer2;
        }

        blitcoords[0] = blitcoords[4] = 0;
        blitcoords[1] = blitcoords[5] = 0;
        blitcoords[2] = blitcoords[6] = surface->w - 1;
        blitcoords[3] = blitcoords[7] = surface->h - 1;

        vro_cpyfm(VDI_handle, S_ONLY, blitcoords, &mfdb_src, &VDI_dst_mfdb);
    }

    return (0);
}

static int
GEM_FlipHWSurfaceWindowed(_THIS, SDL_Surface * surface)
{
    short pxy[8];

    /* Update the whole window */
    wind_get(GEM_handle, WF_WORKXYWH, &pxy[0], &pxy[1], &pxy[2], &pxy[3]);

    GEM_wind_redraw(this, GEM_handle, pxy);

    return (0);
}

static int
GEM_FlipHWSurface(_THIS, SDL_Surface * surface)
{
    if (GEM_lock_redraw) {
        return (0);
    }

    if (surface->flags & SDL_FULLSCREEN) {
        return GEM_FlipHWSurfaceFullscreen(this, surface);
    } else {
        return GEM_FlipHWSurfaceWindowed(this, surface);
    }
}

static int
GEM_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color * colors)
{
    int i;
    SDL_Surface *surface;

#ifdef DEBUG_VIDEO_GEM
    printf("sdl:video:gem: setcolors()\n");
#endif

    /* Do not change palette in True Colour */
    surface = this->screen;
    if (surface->format->BitsPerPixel > 8) {
        return 1;
    }

    for (i = 0; i < ncolors; i++) {
        int r, g, b;
        short rgb[3];

        r = colors[i].r;
        g = colors[i].g;
        b = colors[i].b;

        rgb[0] = VDI_curpalette[i][0] = (1000 * r) / 255;
        rgb[1] = VDI_curpalette[i][1] = (1000 * g) / 255;
        rgb[2] = VDI_curpalette[i][2] = (1000 * b) / 255;

        vs_color(VDI_handle, vdi_index[firstcolor + i], rgb);
    }

    return (1);
}

#if 0
static int
GEM_ToggleFullScreen(_THIS, int on)
{
    if (on) {
        GEM_LockScreen(this);
    } else {
        GEM_UnlockScreen(this);
    }

    return (1);
}
#endif

/* Note:  If we are terminated, this could be called in the middle of
   another SDL video routine -- notably UpdateRects.
*/
void
GEM_VideoQuit(_THIS)
{
    SDL_AtariXbios_RestoreVectors();
    if (GEM_usedevmouse) {
        SDL_AtariDevMouse_Close();
    }

    GEM_FreeBuffers(this);

#if SDL_VIDEO_OPENGL
    if (gl_active) {
        SDL_AtariGL_Quit(this, SDL_TRUE);
    }
#endif

    /* Destroy window */
    if (GEM_handle >= 0) {
        wind_close(GEM_handle);
        wind_delete(GEM_handle);
        GEM_handle = -1;
    }

    GEM_UnlockScreen(this);
    if (GEM_menubar) {
        Mfree(GEM_menubar);
        GEM_menubar = NULL;
    }

    appl_exit();

    GEM_SetNewPalette(this, VDI_oldpalette);

    /* Close VDI workstation */
    if (VDI_handle) {
        v_clsvwk(VDI_handle);
    }

    /* Free mode list */
    if (SDL_modelist[0]) {
        SDL_free(SDL_modelist[0]);
        SDL_modelist[0] = NULL;
    }

    this->screen->pixels = NULL;
}

void
GEM_wind_redraw(_THIS, int winhandle, short *inside)
{
    short todo[4];

    /* Tell AES we are going to update */
    wind_update(BEG_UPDATE);

    v_hide_c(VDI_handle);

    /* Browse the rectangle list to redraw */
    if (wind_get
        (winhandle, WF_FIRSTXYWH, &todo[0], &todo[1], &todo[2],
         &todo[3]) != 0) {

        while (todo[2] && todo[3]) {

            if (rc_intersect((GRECT *) inside, (GRECT *) todo)) {
                todo[2] += todo[0] - 1;
                todo[3] += todo[1] - 1;
                refresh_window(this, winhandle, todo);
            }

            if (wind_get
                (winhandle, WF_NEXTXYWH, &todo[0], &todo[1], &todo[2],
                 &todo[3]) == 0) {
                break;
            }
        }

    }

    /* Update finished */
    wind_update(END_UPDATE);

    v_show_c(VDI_handle, 1);
}

static void
refresh_window(_THIS, int winhandle, short *rect)
{
    MFDB mfdb_src;
    short pxy[8], wind_pxy[8];
    SDL_Surface *surface;
    int iconified;

    /* Is window iconified ? */
    iconified = 0;
/*	if (GEM_wfeatures & (1<<WF_ICONIFY))*/
    {
        if (wind_get
            (winhandle, WF_ICONIFY, &wind_pxy[0], &wind_pxy[1], &wind_pxy[2],
             &wind_pxy[3]) != 0) {
            iconified = wind_pxy[0];
        }
    }

    if (wind_get
        (winhandle, WF_WORKXYWH, &wind_pxy[0], &wind_pxy[1], &wind_pxy[2],
         &wind_pxy[3]) == 0) {
        return;
    }

    if (iconified && GEM_icon) {
        short icon_rect[4], dst_rect[4];
        short iconx, icony;

        surface = GEM_icon;

        GEM_ClearRect(this, rect);

        /* Calculate centered icon(x,y,w,h) relative to window */
        iconx = (wind_pxy[2] - surface->w) >> 1;
        icony = (wind_pxy[3] - surface->h) >> 1;

        icon_rect[0] = iconx;
        icon_rect[1] = icony;
        icon_rect[2] = surface->w;
        icon_rect[3] = surface->h;

        /* Calculate redraw rectangle(x,y,w,h) relative to window */
        dst_rect[0] = rect[0] - wind_pxy[0];
        dst_rect[1] = rect[1] - wind_pxy[1];
        dst_rect[2] = rect[2] - rect[0] + 1;
        dst_rect[3] = rect[3] - rect[1] + 1;

        /* Does the icon rectangle must be redrawn ? */
        if (!rc_intersect((GRECT *) icon_rect, (GRECT *) dst_rect)) {
            return;
        }
#if DEBUG_VIDEO_GEM
        printf("sdl:video:gem:  clip(0,0,%d,%d) to (%d,%d,%d,%d)\n",
               surface->w - 1, surface->h - 1, dst_rect[0], dst_rect[1],
               dst_rect[2], dst_rect[3]);
        printf("sdl:video:gem:  icon(%d,%d,%d,%d)\n", icon_rect[0],
               icon_rect[1], icon_rect[2], icon_rect[3]);
        printf("sdl:video:gem: refresh_window(): draw icon\n");
#endif

        /* Calculate icon(x1,y1,x2,y2) relative to screen */
        icon_rect[0] += wind_pxy[0];
        icon_rect[1] += wind_pxy[1];
        icon_rect[2] += icon_rect[0] - 1;
        icon_rect[3] += icon_rect[1] - 1;

        /* Calculate intersection rectangle to redraw */
        pxy[4] = pxy[0] = MAX(icon_rect[0], rect[0]);
        pxy[5] = pxy[1] = MAX(icon_rect[1], rect[1]);
        pxy[6] = pxy[2] = MIN(icon_rect[2], rect[2]);
        pxy[7] = pxy[3] = MIN(icon_rect[3], rect[3]);

        /* Calculate icon source image pos relative to window */
        pxy[0] -= wind_pxy[0] + iconx;
        pxy[1] -= wind_pxy[1] + icony;
        pxy[2] -= wind_pxy[0] + iconx;
        pxy[3] -= wind_pxy[1] + icony;

    } else {
        surface = this->screen;

#if DEBUG_VIDEO_GEM
        printf("sdl:video:gem: refresh_window(): draw frame buffer\n");
#endif

        /* Redraw all window content */
        pxy[0] = rect[0] - wind_pxy[0];
        pxy[1] = rect[1] - wind_pxy[1];
        pxy[2] = rect[2] - wind_pxy[0];
        pxy[3] = rect[3] - wind_pxy[1];

        pxy[4] = rect[0];
        pxy[5] = rect[1];
        pxy[6] = rect[2];
        pxy[7] = rect[3];
    }

    if (GEM_bufops & B2S_C2P_1TO2) {
        void *src, *dest;
        int x1, x2;

        x1 = (rect[0] - wind_pxy[0]) & ~15;
        x2 = rect[2] - wind_pxy[0];
        if (x2 & 15) {
            x2 = (x2 | 15) + 1;
        }

        src = surface->pixels;
        src += surface->pitch * (rect[1] - wind_pxy[1]);
        src += x1;

        dest = GEM_buffer2;
        dest += surface->pitch * (rect[1] - wind_pxy[1]);
        dest += x1;

        SDL_Atari_C2pConvert(src, dest,
                             x2 - x1, rect[3] - rect[1] + 1,
                             SDL_FALSE, surface->pitch, surface->pitch);
    }

    mfdb_src.fd_addr = surface->pixels;
    {
        int width;

        /* Need to be a multiple of 16 pixels */
        width = surface->w;
        if ((width & 15) != 0) {
            width = (width | 15) + 1;
        }
        mfdb_src.fd_w = width;
    }
    mfdb_src.fd_h = surface->h;
    mfdb_src.fd_nplanes = surface->format->BitsPerPixel;
    mfdb_src.fd_wdwidth = mfdb_src.fd_w >> 4;
    mfdb_src.fd_stand = mfdb_src.fd_r1 = mfdb_src.fd_r2 = mfdb_src.fd_r3 = 0;

    if (GEM_bufops & B2S_VROCPYFM_2TOS) {
        mfdb_src.fd_addr = GEM_buffer2;
    }
#if DEBUG_VIDEO_GEM
    printf("sdl:video:gem: redraw %dx%d: (%d,%d,%d,%d) to (%d,%d,%d,%d)\n",
           surface->w, surface->h,
           pxy[0], pxy[1], pxy[2], pxy[3], pxy[4], pxy[5], pxy[6], pxy[7]);
#endif

    vro_cpyfm(VDI_handle, S_ONLY, pxy, &mfdb_src, &VDI_dst_mfdb);
}

#if SDL_VIDEO_OPENGL

static void
GEM_GL_SwapBuffers(_THIS)
{
    SDL_AtariGL_SwapBuffers(this);
    GEM_FlipHWSurface(this, this->screen);
}

#endif
/* vi: set ts=4 sw=4 expandtab: */