/********************************************************
 *
 *       Video renderers
 *       Copyright 2000 Eugene Kuznetsov (divx@euro.ru)
 *
 ********************************************************/

#include <default.h>
#include <fourcc.h>
#include <Locker.h>
#include <except.h>
#include <renderer.h>
#include <RegAccess.h>
#include <playerwidget.h>
#include <cpuinfo.h>
#include <utils.h>
#include <mmx.h>
#include <string.h>

#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>

#ifndef WITHOUT_X

#if defined(__FreeBSD__) || defined(__NetBSD__)
#include <machine/param.h>
#include <sys/types.h>
#endif
#include <errno.h>

#include <sys/ipc.h>
#include <sys/shm.h>

#include <X11/Xlib.h>
#include <X11/Xlocale.h>
#include <X11/Xutil.h>
#include <X11/extensions/XInput.h> //XGetExtensionVersion
#include <X11/extensions/XShm.h>

#ifdef HAVE_XV
#include <X11/extensions/Xv.h>
#include <X11/extensions/Xvlib.h>
#endif

#ifdef HAVE_LIBXXF86DGA
#include <X11/extensions/xf86dga.h>
#endif

#ifdef HAVE_LIBXXF86VM
#include <X11/extensions/xf86vmode.h>
#endif

#ifdef HAVE_XFT
#include <X11/Xft/Xft.h>
//#include <X11/extensions/Xrender.h>
#endif

#include <VideoDPMS.h>

/*
#ifdef USE_QT
#include <qfont.h>
#include <qfontmetrics.h>
#endif
*/

#ifdef USE_SDL
/*
#ifdef USE_OPENGL
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glx.h>
#endif
*/
#include <SDL.h>
#include <SDL_video.h>
#include <SDL_syswm.h>
#include <SDL_mouse.h>

#ifndef SDL_DISABLE
#define SDL_DISABLE 0
#endif

#ifndef SDL_ENABLE
#define SDL_ENABLE 1
#endif

#endif

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <iostream>

#define __MODULE__ "VideoRenderer"

//#undef HAVE_XFT

using namespace std;

bool VideoRenderer::allow_sw_yuv=false;
bool VideoRenderer::direct_resizes=false;

/*
 * Solaris (maybe other operating systems, too) does not have setenv(),
 * and unsetenv() in libc, provide our own implementation.
 */
#if !HAVE_SETENV
static int setenv(const char *name, const char *value, int overwrite)
{
    if (getenv(name) != NULL && !overwrite)
	return 0;

    char *env = new char[strlen(name) + strlen(value) + 2];
    strcpy(env, name);
    strcat(env, "=");
    strcat(env, value);
    return putenv(env);
}
#endif


#if !HAVE_UNSETENV
static void unsetenv(const char *name)
{
    extern char **environ;
    char **ep;
    int len = strlen(name);
    for (ep = environ; *ep; ep++)
    {
	if (strncmp(*ep, name, len) == 0 && (*ep)[len] == '=')
	{
	    while ((ep[0] = ep[1]) != NULL)
		ep++;
	    break;
	}
    }
}
#endif

int GetPhysicalDepth(Display* dpy)
{
    if (!dpy)
	return 0;

    int screen = DefaultScreen(dpy);
    int planes = DefaultDepth(dpy, screen);
    int n, pixmap_bits = 0;
    XPixmapFormatValues *pf = XListPixmapFormats(dpy, &n);

    for (int i = 0; i < n; i++)
    {
	//cout << i << " depth: " << pf[i].depth << "  bpp: " << pf[i].bits_per_pixel << endl;
	if(pf[i].depth == planes)
	{
	    pixmap_bits = pf[i].bits_per_pixel;
            break;
	}
    }
    XFree(pf);

    if (pixmap_bits == 16 && DefaultVisual(dpy, screen)->red_mask == 0x7c00)
	pixmap_bits = 15;

    return pixmap_bits;
}

/* Direct copy from SDL */
/* Ack!  XPending() actually performs a blocking read if no events available */
int NonblockXPending(Display *display)
{
    /* Flush the display connection and look to see if events are queued */
    XFlush(display);
    if ( XEventsQueued(display, QueuedAlready) ) {
	return(1);
    }

    /* More drastic measures are required -- see if X is ready to talk */
    {
	static struct timeval zero_time;	/* static == 0 */
	int x11_fd;
	fd_set fdset;

	x11_fd = ConnectionNumber(display);
	FD_ZERO(&fdset);
	FD_SET(x11_fd, &fdset);
	if ( select(x11_fd+1, &fdset, NULL, NULL, &zero_time) == 1 ) {
	    return(XPending(display));
	}
    }

    /* Oh well, nothing is ready .. */
    return 0;
}

// use our internal class for our renderers;
class VideoRendererWithLock : public VideoRenderer
{
protected:
    int m_w, m_h;
    int pic_w, pic_h;
    bool subtitles;
    PthreadMutex m_Mutex;
    int64_t m_lLastDrawStamp;
    vector<VideoMode> modes;
public:
    VideoRendererWithLock(int width, int height, bool _subtitles = false)
	:m_w(width), m_h(height), pic_w(width), pic_h(height), subtitles(_subtitles)
    {
    }
    virtual ~VideoRendererWithLock() {}
    virtual int Lock()
    {
        //cout << "lock" << endl;
	return m_Mutex.Lock();
    }
    virtual int TryLock()
    {
	//cout << "trylock" << endl;
	return m_Mutex.TryLock();
    }
    virtual int Unlock()
    {
        //cout << "unlock" << endl;
	return m_Mutex.Unlock();
    }
    virtual const vector<VideoMode>& GetVideoModes() const
    {
        return modes;
    }
    virtual int GetSize(int& width, int& height) const
    {
	width = pic_w;
	height = pic_h;
	return 0;
    }
};

 /*fold00*/
/*
inline static void qSafeXDestroyImage( XImage *x )
{
    if ( x->data ) {
	free( x->data );
	x->data = 0;
    }
    XDestroyImage( x );
}

class ShmRenderer : public VideoRenderer
{
    bool	       xshminit;
    XShmSegmentInfo xshminfo;
    XImage	      *xshmimg;
    Pixmap	       xshmpm;
    GC 			gc;
    pthread_mutex_t mutex;
    int xpos;
    int screen;

public:
    ShmRenderer() : xshminit(0), xshmimg(0), xshmpm(0)
    {
    	pthread_mutex_init(&mutex, 0);
    }
    void Alloc()
    {
        static int major, minor;
	static Bool pixmaps_ok;
//        int dd	 = dev->x11Depth();
//        dpy 	 = dev->x11Display();
//        vis	 = (Visual*)dev->x11Visual();
	printf("Creating SHM renderer, width %d, height %d\n", pic_w, pic_h);
	printf("Server vendor %s, release %d\n", ServerVendor(_dpy), VendorRelease(_dpy));

	try
	{
    	    XGCValues xcg;
	    xcg.graphics_exposures=false;
	    gc=XCreateGC(_dpy, _vis, GCGraphicsExposures, &xcg);
	    if ( !XShmQueryVersion(_dpy, &major, &minor, &pixmaps_ok) )
		throw FATAL("MIT SHM extension not supported");

	    bool ok;
	    int _pic_w=((pic_w+m_w-1)/m_w)*m_w;
	    int _pic_h=((pic_h+m_h-1)/m_h)*m_h;
    	    xshminfo.shmid = shmget( IPC_PRIVATE,
    			     _pic_w*_pic_h*4,
    			     IPC_CREAT | 0777 );
	    ok = xshminfo.shmid != -1;
	    if(!ok)
		throw FATAL("Can't get shared memory segment");

	    xshminfo.shmaddr = (char*)shmat( xshminfo.shmid, 0, 0 );
	    ok = xshminfo.shmaddr != 0;
	    xshminfo.readOnly = FALSE;

    	    if ( !ok )
		throw FATAL("Can't attach shared memory segment");

	    ok = XShmAttach( _dpy, &xshminfo );
	    if ( !ok )
		throw FATAL("XShmAttach failed");

	    int dd=DefaultDepth(_dpy, DefaultScreen(dpy));
    	    xshmimg = XShmCreateImage( _dpy, _vis, dd, ZPixmap, xshminfo.shmaddr, &xshminfo, _pic_w, _pic_h );

	    if ( !xshmimg )
	        throw FATAL("Can't create shared image");

    	    printf("Video device: %d bpp ( physical %d ), color masks %X,%X,%X\n",
		dd, GetPhysicalDepth(_dpy), _vis->red_mask, _vis->green_mask, _vis->blue_mask);
	}
	catch(...)
	{
	    xshmimg = 0;
	    if ( xshminfo.shmaddr )
		shmdt( xshminfo.shmaddr );
	    if ( xshminfo.shmid != -1 )
		shmctl( xshminfo.shmid, IPC_RMID, 0 );
	    throw;
	}
    }
    int Create(Display *_dpy, Visual *_vis, int x, int y, int _xpos=0, int sub=0)
    {
	dpy=_dpy;
	vis=_vis;
	m_w=x;
        m_h=y;
	pic_w=x;
	pic_h=y;
        width=x;
	height=y;
	xpos=_xpos;
	Alloc();
    }
    int Free()
    {
	cerr<<"Free()"<<endl;
        if ( xshmimg == 0 )
	    return 0;
	XSync(dpy, false);
        if ( xshmpm ) {
	    XFreePixmap( dpy, xshmpm );
	    xshmpm = 0;
        }
        XShmDetach( dpy, &xshminfo ); xshmimg->data = 0;
        qSafeXDestroyImage( xshmimg ); xshmimg = 0;
	shmdt( xshminfo.shmaddr );
        shmctl( xshminfo.shmid, IPC_RMID, 0 );
	XFreeGC(_dpy, gc);

	xshminfo.shmaddr=0;
	xshminfo.shmid=0;
    }
    int Resize(int& new_w, int& new_h)
    {
	if(new_w<0)return -1;
	if(new_h<0)return -1;
	new_w&=(~7);
	new_h&=(~7);
	int xratio=(new_w+m_w-1)/m_w;
	int yratio=(new_h+m_h-1)/m_h;
	int old_xratio=(pic_w+m_w-1)/m_w;
	int old_yratio=(pic_h+m_h-1)/m_h;
	cerr<<"New size: "<<new_w<<" "<<new_h<<endl;
	pic_w=new_w;
	pic_h=new_h;
	if((xratio!=old_xratio)||(yratio!=old_yratio))
	{
	    pthread_mutex_lock(&mutex);
	    Free();
	    Alloc();
	    pthread_mutex_unlock(&mutex);
	}
    }
    virtual int Draw(const CImage* data)
    {
        char* outpic=xshmimg->data;
	if(outpic==0)
	    return 0;
	if(data==0)
	    return 0;

	int bit_depth=GetPhysicalDepth(_dpy);

	if(pthread_mutex_trylock(&mutex)==EBUSY)
	    return -1;
//	pthread_mutex_lock(&mutex);

	sync();
	const char* src;

	if((pic_h==m_h)	&& (pic_w==m_w))
	    memcpy(outpic, data->data(), data->bytes());
	else
	    zoom((short*)outpic, (short*)data->data(), pic_w, pic_h, m_w, m_h, bit_depth, m_w*((pic_w+m_w-1)/m_w));

	XShmPutImage(_dpy, _vis, gc,
		xshmimg, 0, 0, xpos, 0, pic_w, pic_h, true);

	pthread_mutex_unlock(&mutex);

	return 0;
    }
    virtual int sync()
    {
        XSync(_dpy, false);
	return 0;
    }
    virtual ~ShmRenderer()
    {
	Free();
	pthread_mutex_destroy(&mutex);
    }
    virtual int toggle_fullscreen(bool=false);
    virtual int drawSubtitles(const char*);

};
int ShmRenderer::ToggleFullscreen(bool maximize=false)
{
    return 0;
}

int ShmRenderer::DrawSubtitles(const char* sub)
{
    return 0;
}


VideoRenderer* CreateRenderer()
{
    return new ShmRenderer;
}
*/
 /*FOLD00*/

 /*fold00*/
#ifdef	HAVE_XV
class XvYUVRenderer : public VideoRendererWithLock
{
    Display* dpy;
    Window win;
    XFontStruct* font;
    int	screen;
    XShmSegmentInfo xshminfo[2];
    XvImage* xvimage[2];
    unsigned int xv_format;
    unsigned int xv_port;
    const char* title;
    GC gc;
    Pixmap xshmpm;
    XSizeHints hint;
    XVisualInfo vinfo;
    XEvent xev;
    XWindowAttributes attribs;

public:
    XvYUVRenderer(PlayerWidget* pw, Display* _dpy,
		  int width, int height, unsigned long format, bool subtitle = false)
	: VideoRendererWithLock(width, height, subtitle),
	dpy(_dpy), font(0), xv_format(0), xv_port(0), xshmpm(0)
    {
	//g_pw=pw;

	cout << XInitThreads() << endl;

	title = "Xv rulez";
	screen = DefaultScreen(dpy);

	hint.x = 0;
	hint.y = 0;
	hint.width = m_w;
	hint.height = m_h;
	hint.flags = /*PPosition | */PSize;

	XGetWindowAttributes(dpy, DefaultRootWindow(dpy), &attribs);
	int depth = GetPhysicalDepth(dpy);

	XMatchVisualInfo(dpy, screen, depth, TrueColor, &vinfo);
	Colormap cmap = XCreateColormap(dpy, RootWindow(dpy, screen),
					vinfo.visual, AllocNone);
	XSetWindowAttributes xswa;
	unsigned long xswamask;

	xswa.background_pixel = 0;
	xswa.border_pixel     = 1;
	xswa.colormap         = cmap;
	xswamask = CWBackPixel | CWBorderPixel |CWColormap;

	XLockDisplay(dpy);
	win = XCreateWindow(dpy, RootWindow(dpy, screen),
			    hint.x, hint.y, hint.width, hint.height, 4,
			    depth, CopyFromParent, vinfo.visual, xswamask, &xswa);

	XSelectInput(dpy, win, StructureNotifyMask);
	XSetStandardProperties(dpy, win, title, title, None, NULL, 0, &hint);

	//if ( fullscreen ) vo_x11_decoration( mydisplay,mywindow,0 );

	/* Map window. */
	XMapWindow(dpy, win);

	/* Wait for map. */
	do
	{
	    XNextEvent(dpy, &xev);
	    //cout << "T: " << xev.xany.type << " w: " << xev.xany.window << endl;

	}
	while (xev.type != MapNotify || xev.xmap.event != win);

	XSelectInput(dpy, win, NoEventMask);

	XFlush(dpy);
	XSync(dpy, False);


	unsigned int ver,rel,req,ev,err;
	if (Success != XvQueryExtension(dpy, &ver,&rel,&req,&ev,&err))
	    throw FATAL("Xv: XvQueryExtension failed");

	/* check for Xvideo support */
	unsigned int adaptors;
	XvAdaptorInfo *ai;
	if (Success != XvQueryAdaptors(dpy, DefaultRootWindow(dpy), &adaptors,&ai))
	    throw FATAL("Xv: XvQueryAdaptors failed");

	/* check adaptors */
	for (unsigned i = 0; i < adaptors; i++)
	{
	    if (!xv_port && (ai[i].type & XvInputMask) && (ai[i].type & XvImageMask))
		xv_port = ai[i].base_id;
	}

	XvFreeAdaptorInfo(ai);

	/* check image formats */
	if (!xv_port)
	    throw FATAL("Xv: XvQueryAdaptors no port");

	unsigned int formats;
	XvImageFormatValues* fo = XvListImageFormats(dpy, xv_port, (int*)&formats);
	for(unsigned i = 0; i < formats; i++)
	{
	    fprintf(stderr, "Xvideo image format: 0x%x (%4.4s) %s\n", fo[i].id,
		    (char*)&fo[i].id, (fo[i].format == XvPacked) ? "packed" : "planar");

	    if (fo[i].id == (signed) format)
		xv_format = fo[i].id;
	}
	if (!xv_format) /* no matching image format not */
	    throw FATAL("Xv: XvQueryAdaptors no port");

	fprintf(stderr,"using Xvideo port %d for hw scaling\n", xv_port);

	//allocate_xvimage(0);
	/* catch window resizes */
	XSelectInput(dpy, win,
		     EnterWindowMask | LeaveWindowMask
		     | PointerMotionMask
		     | StructureNotifyMask | KeyPressMask);
	//XSelectInput(mydisplay, mywindow, StructureNotifyMask);

	// resize:
	XMoveResizeWindow(dpy, win, 0, 0, m_w, m_h);
	XUnlockDisplay(dpy);
	Alloc();
    }

    virtual ~XvYUVRenderer()
    {
	cout <<"XvYUV: Destroy" << endl;
	Lock();
	Free();
    }

    void Alloc()
    {
	printf("Creating Xv renderer, width %d, height %d\n", pic_w, pic_h);
	printf("Server vendor %s, release %d\n",
	       ServerVendor(dpy), VendorRelease(dpy));
	try
	{
	    bool ok = true;
	    int width=((pic_w+m_w-1)/m_w)*m_w;
	    int height=((pic_h+m_h-1)/m_h)*m_h;
	    if (! ok)
		throw FATAL("XShmAttach failed");

	    XGCValues xgcv;
	    xgcv.graphics_exposures = false;
	    XLockDisplay(dpy);
	    gc = XCreateGC(dpy, win, 0L, &xgcv);
	    //int dd = DefaultDepth(dpy, DefaultScreen(dpy));
	    xvimage[0] = XvShmCreateImage(dpy, xv_port, xv_format, 0,
					  width, height, &xshminfo[0]);

	    if (! xvimage[0])
		throw FATAL("Can't create shared image");

	    xshminfo[0].shmid    = shmget(IPC_PRIVATE, xvimage[0]->data_size, IPC_CREAT | 0777);
	    xshminfo[0].shmaddr  = (char *) shmat(xshminfo[0].shmid, 0, 0);
	    xshminfo[0].readOnly = False;

	    xvimage[0]->data = xshminfo[0].shmaddr;
	    XShmAttach(dpy, &xshminfo[0]);
	    XSync(dpy, False);
	    XUnlockDisplay(dpy);
	    shmctl(xshminfo[0].shmid, IPC_RMID, 0);

	    /* so we can do grayscale while testing... */
	    memset(xvimage[0]->data, 128, xvimage[0]->data_size);

	    //printf("Video device: %d bpp ( physical %d ), color masks %lX,%lX,%lX\n",
	    //       dd, GetPhysicalDepth(dpy),
	    //       vis->red_mask, vis->green_mask, vis->blue_mask);
	    //Alloc();
	} catch(...)
	{
	    throw;
	}
    }

    int Free()
    {
	//cout <<"XvYUV: Free()" << endl;

	if (xvimage[0] == 0)
	    return 0;
	XLockDisplay(dpy);
	XFreeGC(dpy, gc);
	XSync(dpy, False);
	XShmDetach(dpy, &xshminfo[0]);
        XUnlockDisplay(dpy);

	XFree(xvimage[0]);
	xvimage[0] = 0;
	shmdt( xshminfo[0].shmaddr );
	shmctl( xshminfo[0].shmid, IPC_RMID, 0 );
        return 0;
    }

    int Resize(int& new_w, int& new_h)
    {
	if (new_w < 0 || new_h < 0)
	    return -1;

	new_w&=(~7);
	new_h&=(~7);
	int xratio=(new_w+m_w-1)/m_w;
	int yratio=(new_h+m_h-1)/m_h;
	int old_xratio=(pic_w+m_w-1)/m_w;
	int old_yratio=(pic_h+m_h-1)/m_h;
	cerr<<"New size: "<<new_w<<" "<<new_h<<endl;
	pic_w=new_w;
	pic_h=new_h;
	if (xratio != old_xratio || yratio != old_yratio)
	{
	    Lock();
	    Free();
	    Alloc();
	    Unlock();
	}
        return 0;
    }

    virtual int Draw(const CImage* data)
    {
	char* outpic = xvimage[0]->data;

	if (outpic==0 || data==0)
	    return 0;

	//int bit_depth=GetPhysicalDepth(dpy);
	if (TryLock()==EBUSY)
	    return -1;

	memcpy(outpic, data->Data(), data->Bytes());
	//cout << "b " << data->bytes();
	XLockDisplay(dpy);
	XvShmPutImage(dpy, xv_port, win, gc, xvimage[0],
		      0, 0, m_w, m_h, 0, 0, pic_w, pic_h, True);
	XFlush(dpy);
        XUnlockDisplay(dpy);

	Unlock();
	return 0;

	int ret=0;
	XEvent         xevent;
	char           buf[100];
	KeySym         keySym;
	XComposeStatus cstat;

	///XNextEvent( dpy, &xevent );
	cout << "T: " << xevent.xany.type << " w: " << xevent.xany.window << endl;
	//cout << XPending( dpy ) << endl;
	//while ( NonblockXPending( dpy ) )
	while ( XPending( dpy ) )
	{
            cout << "event " << endl;
	    XNextEvent( dpy, &xevent );
	    switch( xevent.type )
	    {
	    case EnterNotify:
		cout << "Enter" << endl;
                break;
	    case LeaveNotify:
		cout << "Enter" << endl;
                break;
	    case Expose:
		//ret|=VO_EVENT_EXPOSE;
		break;
	    case ConfigureNotify:
		{
		int width=xevent.xconfigure.width;
		int height=xevent.xconfigure.height;
		//ret|=VO_EVENT_RESIZE;
		cout << "Resize " << width << "  " << height << endl;
		}
		break;
	    case KeyPress:
		XLookupString( &xevent.xkey,buf,sizeof(buf),&keySym,&cstat );
		//vo_x11_putkey( ( (keySym&0xff00) != 0?( (keySym&0x00ff) + 256 ):( keySym ) ) );
		//ret|=VO_EVENT_KEYPRESS;
		break;
	    }
	}

	return 0;
    }
    virtual int Sync()
    {
	//XFlush(dpy);
	return 0;
    }
    virtual int ToggleFullscreen(bool=false)
    {
        return 0;
    }
    virtual int DrawSubtitles(const char*)
    {
	return 0;
    }
    int Refresh()
    {
        return 0;
    }
};

VideoRenderer* CreateXvYUVRenderer(PlayerWidget* pw, Display* dpy,
				   int width, int height,
				   fourcc_t yuv_fmt, bool sub)
{
    return new XvYUVRenderer(pw, dpy, width, height, yuv_fmt, sub);
}

#endif	/* HAVE_XV */
 /*FOLD00*/

#ifdef USE_SDL
#undef __MODULE__
#define __MODULE__ "Fullscreen renderer"
static PlayerWidget* g_pw = 0;
static PthreadMutex evmutex;
/**
 *
 * This function processes events received by SDL window.
 *
 */
static const char event_names[][30]=
{
    "SDL_NOEVENT",
    "SDL_ACTIVEEVENT",
    "SDL_KEYDOWN",
    "SDL_KEYUP",
    "SDL_MOUSEMOTION",
    "SDL_MOUSEBUTTONDOWN",
    "SDL_MOUSEBUTTONUP",
    "SDL_JOYAXISMOTION",
    "SDL_JOYBALLMOTION",
    "SDL_JOYHATMOTION",
    "SDL_JOYBUTTONDOWN",
    "SDL_JOYBUTTONUP",
    "SDL_QUIT",
    "SDL_SYSWMEVENT",
    "SDL_EVENT_RESERVEDA",
    "SDL_EVENT_RESERVEDB",
    "SDL_VIDEORESIZE",
    "SDL_EVENT_RESERVED1",
    "SDL_EVENT_RESERVED2",
    "SDL_EVENT_RESERVED3",
    "SDL_EVENT_RESERVED4",
    "SDL_EVENT_RESERVED5",
    "SDL_EVENT_RESERVED6",
    "SDL_EVENT_RESERVED7"
};

// emulate key autorepeat as for some reason SDL
// does not support key autorepeat for EventFilter usage
// (and we do not recieve Pushed SdlEvents here)
//
// this implementation is quite broken, but its definitely
// better then nothing.
static SDL_Event keyrepev;
static pthread_t keyrepth;
static bool sdlKbdOn = true;
static int EventFilter(const SDL_Event* ev);
static int SDL_mouse_x, SDL_mouse_y;
static bool SDL_fullscreen = false;

static Uint32 MouseTimer(Uint32 interval)
{
    SDL_ShowCursor(SDL_DISABLE);
    return 0;
}

static void* KeyRepeatThread(void *arg)
{
    //
    // BIG WARNING HERE - autorepeated keys are not in sync with SDL
    // so this might be some source of threading problems
    // player has to be able to handle such situation
    // I believe aviplayer is now thread safe
    //

    int j = 10; // initial wait
    while (keyrepev.type == SDL_USEREVENT)
    {
	int i = j;
	while (--i && keyrepev.type == SDL_USEREVENT)
	{
	    avi_usleep(10000);
	}
	evmutex.Lock();
	if (!i && keyrepev.type == SDL_USEREVENT)
	{
	    switch (keyrepev.key.keysym.sym)
	    {
	    case SDLK_c:
	    case SDLK_p:
	    case SDLK_SPACE:
		g_pw->PW_pause_func();
		break;
	    default:
		g_pw->PW_key_func(keyrepev.key.keysym.sym, keyrepev.key.keysym.mod);
		break;
	    }
	    //cout << "Event repeated" << endl;
	}
	evmutex.Unlock();
	//else cout << "Event canceled" << endl;
        j = 3;
    }
    return NULL;
}

static int EventFilter(const SDL_Event* ev)
{
    if (!g_pw)
	return 1;

    evmutex.Lock();

    if (ev->type == SDL_KEYUP)
    {
	//cout << "Key " << (int) ev->key.keysym.sym << " mod: "
	//    << ev->key.keysym.mod << endl;
	if (keyrepev.type); // disable autorepeat
	{
	    keyrepev.type = 0; // disable autorepeat
	    evmutex.Unlock();
	    pthread_join(keyrepth, NULL);
	    evmutex.Lock();
	}
    }
    else if (sdlKbdOn && ev->type == SDL_KEYDOWN && (keyrepev.type == 0))
    {
        bool autorep = false;
	switch (ev->key.keysym.sym)
	{
	case SDLK_RETURN:
            autorep = true;
	    if (!(ev->key.keysym.mod & (KMOD_ALT | KMOD_META)))
		break;
	    autorep = false;
	    // fall through - alt + enter
	case SDLK_ESCAPE:
	case SDLK_f:
	    g_pw->PW_fullscreen();
	    break;
	case SDLK_m:
	    g_pw->PW_maximize_func();
	    break;
	case SDLK_x:
	    g_pw->PW_stop_func();
	    break;
	case SDLK_v:
	    g_pw->PW_play_func();
	    break;
	case SDLK_q:
	    g_pw->PW_quit_func();
	    break;
	case SDLK_p:
	case SDLK_c:
	case SDLK_SPACE:
            autorep = true;
	    g_pw->PW_pause_func();
	    break;
	default:
            autorep = true;
	    break;
	}

	// passing all keys
	g_pw->PW_key_func(ev->key.keysym.sym, ev->key.keysym.mod);
	// start autorepeat thread
	if (autorep && !keyrepev.type)
	{
	    memcpy(&keyrepev, ev, sizeof(keyrepev));
	    keyrepev.type = SDL_USEREVENT;
	    //keyrepev.user.code = 1;
	    pthread_create(&keyrepth, 0, KeyRepeatThread, 0);
	}
    }
    else if (ev->type == SDL_VIDEORESIZE)
    {
	g_pw->PW_resize(ev->resize.w, ev->resize.h);
    }
    else if (ev->type == SDL_MOUSEBUTTONDOWN)
    {
	switch (ev->button.button)
	{
	case SDL_BUTTON_RIGHT:
	    SDL_ShowCursor(SDL_ENABLE);
	    if (SDL_fullscreen)
		g_pw->PW_fullscreen();
	    g_pw->PW_menu_slot();
	    break;
	case SDL_BUTTON_MIDDLE:
	    g_pw->PW_middle_button();
	    break;
	case SDL_BUTTON_LEFT:
	    g_pw->PW_pause_func();
	    break;
	}
    }
#ifdef SDL_VIDEOEXPOSE
    else if (ev->type == SDL_VIDEOEXPOSE)
    {
        // we are not receiving this event !!
	cout << "SDL_EXPOSE arrived" << endl;
    }
#endif
    else if (ev->type == SDL_ACTIVEEVENT)
    {
	//cout << "ACTIVE " << (int) ev->active.gain <<"  "<<(int) ev->active.state << endl;
	if (ev->active.state == SDL_APPINPUTFOCUS)
	{
	    sdlKbdOn = (ev->active.gain) ? true : false;
	    g_pw->PW_refresh();
	}
    }
    else if (ev->type == SDL_MOUSEMOTION)
    {
        // safer for reading mouse position
	SDL_mouse_x = ev->motion.x;
	SDL_mouse_y = ev->motion.y;

        SDL_SetTimer(1000, MouseTimer); // 1sec delay before mouse off
	SDL_ShowCursor(SDL_ENABLE);

	//cout << "MOUSE MOVE " << ev->motion.x << "  " << ev->motion.y << endl;
    }
    else if (ev->type == SDL_QUIT)
    {
	g_pw->PW_quit_func();
    }

    evmutex.Unlock();
    return 1; // event has been filtered
}

class FullscreenRenderer: public VideoRendererWithLock
{
protected:
    Display* dpy;
    Visual* vis;
    SDL_Surface* screen;
    int dim_w, dim_h;
    int max_w, max_h;
    GC xgc;
    GC wmxgc;
    int fs;
    int m_sub;
    int zoom_divider;
    bool dirty;
    char *convbuf;
    int dga;
    int mouse_x, mouse_y;
    Window wnd;
    XFontStruct* font;
    int fm_height;
    const CImage* image;
    int bit_depth;
    int bpp;
    VideoDPMS* dpmsSafe;
    char* old_text;
    bool resizeEnabled;
    SDL_SysWMinfo info;
    subtitle_line_t* m_pSubline;
    const subtitle_line_t* m_pSublineRef;
#ifdef HAVE_XFT
    XftDraw *xftdraw;
    XftFont *xftfont;
    XftColor *xftcolor;
#endif

    // private implementation - called with lock
    virtual int doResize(int& new_w, int& new_h)
    {
        // allow original size
        if (new_w != m_w)
	    new_w = new_w & ~7;
	if (new_h != m_h)
	    // some resizing algorithms requires this for now
	    new_h = new_h & ~7;

	if (new_w == pic_w && new_h == pic_h)
	    return -1;

	if (!resizeEnabled)
	{
	    //printf("Disabled %dx%d  %dx%d\n", new_w, new_h, pic_w, pic_h);
	    new_w = m_w;
	    new_h = m_h;
	}

	if (new_h < m_h / 4 || new_w < m_w / 4)
	    return -1;

	pic_w = new_w;
	pic_h = new_h;
	dim_w = pic_w;
	dim_h = pic_h + (m_sub?fm_height*SUBTITLE_SPACE:0);
	//cout << "New image size " << pic_w << "   " << dim_h << "   " << m_sub << "  " << endl;

	//if (screen)
	//    SDL_FreeSurface(screen);

        // 0 is intentional -> use current screen depth and color model
	screen = SDL_SetVideoMode(dim_w, dim_h, 0 * bit_depth, fs);
	return 0;
    }

public:
    static const int SUBTITLE_SPACE = 4;
    static int s_iTrickNvidia;

    FullscreenRenderer(PlayerWidget* pw, Display* xdpy,
		       int width, int height, bool subt = false)
	:VideoRendererWithLock(width, height, subt),
	dpy(xdpy), screen(0), max_w(0), max_h(0), xgc(0), wmxgc(0), convbuf(0),
	font(0), image(0), dpmsSafe(0), old_text(0), resizeEnabled(true)
    {
	g_pw = pw;
	sdlKbdOn = true;
	m_pSubline = 0;
        m_pSublineRef = 0;

#ifdef HAVE_XFT
	xftdraw = 0;
	xftfont = 0;
	xftcolor = 0;
#endif
	char *s;
	int found = False;

        s = setlocale(LC_CTYPE, "");
	if (!s)
	    cerr << "Warning: Locale not supported by C library" << endl;
	else
	{
	    if (!XSupportsLocale())
	    {
		cerr << "Warning: Locale non supported by Xlib" << endl;
                setlocale(LC_CTYPE, "C");
	    }
	    //cout << "Locale OK" << endl;
	}

	if (s_iTrickNvidia == -1)
	{
	    int lines;
	    //XExtensionVersion* ext = XGetExtensionVersion(dpy, "NVIDIA-GLX");
	    int n = 0;
	    char **extlist = XListExtensions(dpy, &n);

	    //printf ("number of extensions:    %d\n", n);
	    s_iTrickNvidia = 0;
	    if (extlist) {
		int i;
		int opcode, event, error;

		for (i = 0; i < n; i++)
		    if (strcmp("NVIDIA-GLX", extlist[i]) == 0)
		    {
                        s_iTrickNvidia = 2;
			cout << "Detected nVidia GLX driver - clearing "
			    << s_iTrickNvidia << " lowest lines..." << endl;
		    }
		/* do not free, Xlib can depend on contents being unaltered */
		/* XFreeExtensionList (extlist); */
	    }

	    if (getenv("AVIPLAY_NVIDIA_ENABLE"))
	    {
		s_iTrickNvidia = 2;
		cout << "nVidia - line clearing hack - forced On" << endl;
	    }
	    if (getenv("AVIPLAY_NVIDIA_DISABLE"))
	    {
		s_iTrickNvidia = 0;
                cout << "nVidia - line clearing hack - forced Off" << endl;
	    }
	}

	//fs = SDL_RESIZABLE | SDL_SWSURFACE | SDL_HWACCEL | SDL_ANYFORMAT;
	fs = SDL_RESIZABLE | SDL_HWSURFACE | SDL_HWACCEL | SDL_ANYFORMAT;
	//| SDL_ASYNCBLIT;// ;// | SDL_DOUBLEBUF ;
	// Async blit makes this a bit faster - however we prefer
	// to know when the image is drawn
	// - Async uses XFlush, Sync calls XSync
	// using double buffering would be great
        // but for some reason it doesn't work for YUV

	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTTHREAD
                     | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) < 0)
	    throw FATAL("Failed to init SDL");

	//char vname[100];
	//SDL_VideoDriverName(vname, sizeof(vname));
	//cout << "Video driver " << vname << endl;

	dga = 0;
	if (getenv("SDL_VIDEODRIVER") != NULL
	    && strcmp(getenv ("SDL_VIDEODRIVER"), "dga") == 0) {
	    dga = 1;
	    char size[10];
	    double ratio_x;
	    double ratio_y;
	    double ratio;
	    if (getenv("AVIPLAY_DGA_WIDTH") == NULL)
		ratio_x = 640.0;
	    else {
		strncpy (size, getenv("AVIPLAY_DGA_WIDTH"), 9);
		ratio_x = atof (size);
	    }
	    if (getenv("AVIPLAY_DGA_HEIGHT") == NULL)
		ratio_y = 480.0;
	    else {
		strncpy(size, getenv("AVIPLAY_DGA_HEIGHT"), 9);
		ratio_y = atof (size);
	    }
	    ratio_x = ratio_x / m_w;
	    ratio_y = ratio_y / m_h;
	    ratio = (ratio_x < ratio_y) ? ratio_x : ratio_y;
	    pic_w = (int) rint(ratio * m_w);
	    pic_h = (int) rint(ratio * m_h);
	    convbuf = (char *) malloc(pic_w * pic_h * (GetPhysicalDepth(dpy) + 7) / 8);
	}
	else
	{
	    pic_w = width;
	    pic_h = height;
	}

	dirty = false;
	dim_w = pic_w;
	dim_h = pic_h;

	/*int newbpp = SDL_VideoModeOK(dim_w, dim_h, GetPhysicalDepth(_dpy), fs);
	  if (!newbpp)
	    throw FATAL("Failed to set up video mode");
	 */

	bit_depth = GetPhysicalDepth(dpy);
	bpp = (bit_depth + 7) / 8;

        screen = SDL_SetVideoMode(dim_w, dim_h, bit_depth, fs);

	if (!screen)
	    throw FATAL("Failed to set up video mode");
	try
	{
	    SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
	    //SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
	    SDL_SetEventFilter(EventFilter);
	    SDL_WM_SetCaption("AviPlayer: M/F max/fullscreen, [/] adjust a-v sync", 0);
	    SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, 100/*SDL_DEFAULT_REPEAT_INTERVAL*/);

	    SDL_VERSION(&info.version);
	    SDL_GetWMInfo(&info);

	    //dpy = info.info.x11.display;
            m_sub = 0;
	    //::wnd=info.info.x11.wmwindow;
	    XGCValues gcv;
	    gcv.graphics_exposures = False;
	    xgc = XCreateGC(dpy, info.info.x11.window,
			    GCGraphicsExposures, &gcv);
	    wmxgc = XCreateGC(dpy, info.info.x11.wmwindow,
			      GCGraphicsExposures, &gcv);
 	    if (dga == 0) {
 	    } else
		SDL_ShowCursor(SDL_DISABLE);

	    dpmsSafe = new VideoDPMS(dpy);
	    keyrepev.type = 0;

#ifdef HAVE_LIBXXF86VM
	    XF86VidModeModeInfo** modesinfo;
	    int lines;
	    XF86VidModeGetAllModeLines(dpy, DefaultScreen(dpy), &lines, &modesinfo);

	    for (int i = 0; modesinfo && modesinfo[i] && i < lines; i++)
	    {
		//cout << "Modes  " << modesinfo[i]->hdisplay << " x "  << modesinfo[i]->vdisplay << endl;
		VideoMode vm;
		vm.width = modesinfo[i]->hdisplay;
		vm.height = modesinfo[i]->vdisplay;
                vm.freq = modesinfo[i]->dotclock * 1000 /
		    modesinfo[i]->htotal / (double) modesinfo[i]->vtotal;
		// calculate refresh frequency
		//cout << "dt " << modesinfo[i]->dotclock
		//    << "   ht " << modesinfo[i]->htotal
		//    << "   vt " << modesinfo[i]->vtotal << endl;
		char b[100];
		sprintf(b, "%d x %d  %dHz", vm.width, vm.height, (int)(vm.freq + 0.5));
                vm.name = b;
                modes.push_back(vm);
	    }
#endif
	    //cout << "SDL surface w: " << pic_w << "  h: " << pic_h << endl;
	}
	catch(...)
	{
	    SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_EVENTTHREAD);
	    throw;
	}
    }

    virtual ~FullscreenRenderer()
    {
        SDL_SetTimer(0, NULL);
	if (screen && (screen->flags & SDL_FULLSCREEN))
	    SDL_WM_ToggleFullScreen(screen);

	Lock();
	SDL_ShowCursor(SDL_ENABLE);

	if (image)
	{
	    image->Release();
            image = 0;
	}
	g_pw = 0;
	SDL_SetEventFilter(0);

	if (keyrepev.type == SDL_USEREVENT)
	{
	    keyrepev.type = 0;
            pthread_join(keyrepth, NULL);
	}

        freeFont();
	if (m_pSubline)
            subtitle_line_free(m_pSubline);
        if (xgc)
	    XFreeGC(dpy, xgc);
        if (wmxgc)
	    XFreeGC(dpy, wmxgc);
	if (convbuf)
	    free(convbuf);
	SDL_QuitSubSystem(SDL_INIT_VIDEO);
	delete dpmsSafe;
    }
    virtual void freeFont()
    {
#ifdef HAVE_XFT
	//if (xftdraw) ///  Who Knows why this results in X Error ??
	//    XftDrawDestroy(xftdraw);
	if (xftfont)
	    XftFontClose(dpy, xftfont);
        if (xftcolor)
	    delete xftcolor;
#endif
	if (font)
	    XFreeFont(dpy, font);
    }
    virtual CImage* GetData(int idx)
    {
	if (pic_w != m_w || pic_h != m_h || idx > 0)
	    return 0;
	BitmapInfo bi;
        bi.biWidth = m_w;
	bi.biHeight = -m_h;
        bi.biPlanes = 1;
	bi.SetBits(bit_depth);
	//cout << "GETDATA " << endl;
        //bi.Print();

	// for direct rendering bitdepth must match!
        resizeEnabled = false; // not allowed for x11 resizing
	CImage* ci = new CImage(&bi, (uint8_t*) screen->pixels, false);
        ci->SetUserData(screen->pixels);
	//cout << "HWADDR " << (void*) screen->pixels << endl;
	return ci;
    }
    virtual int Resize(int& new_w, int& new_h)
    {
	Lock();
	SDL_GetWMInfo(&info);
	info.info.x11.lock_func();
	int r = doResize(new_w, new_h);
	info.info.x11.unlock_func();
	Unlock();
	if (r == 0)
	    Refresh();
	return r;
    }
    virtual int Refresh()
    {
	if (screen && image && to_float(longcount(), m_lLastDrawStamp) > 0.1)
	{
	    // because of scaling we need to call Draw
	    Draw(image);
	    if (m_pSubline)
		DrawSubtitles(m_pSubline);
	    return Sync();
	}
        return -1;
    }
    virtual int Draw(const CImage* data)
    {
	if (!screen || !data)
	    return -1;
	if (TryLock() == EBUSY)
	    return -1;

        m_lLastDrawStamp = longcount();
	data->AddRef();
	if (image)
	    image->Release();
	image = data;

	//cout << "DRAW TO " << pic_w << " " << pic_h << endl;
	if (dga == 1) {
	    SDL_LockSurface(screen);
	    memory_lock();
	    if ((pic_w == m_w) && (pic_h == m_h))
		memcpy(screen->pixels, data->Data(), data->Bytes());
	    else
	    {
		// the reason for convbuf is that writing to VideoRAM is slower
		// compared to main memory. If you don't believe me try it
		// yourself and see how performance drops. Strange however:
		// doing more results in more fluent drawing than doing less
		// :-)
		zoom ((uint16_t*)convbuf, (const uint16_t*)data->Data(), pic_w, pic_h, m_w, m_h, bit_depth);
		memcpy (screen->pixels, convbuf, pic_w * pic_h * bpp);
	    }
	    memory_unlock();
	    SDL_UnlockSurface(screen);
	}
	else if ((pic_w == m_w) && (pic_h == m_h))
	{
	    //cout << "DATA " << (void*) data << endl;
	    if (!data->GetUserData())
	    {
		SDL_LockSurface(screen);
		memory_lock();
		//cout << "NONDIRECT DRAW " << endl;
		//data->GetFmt()->Print();
		if (data->Direction())
		{
		    // currently only in original SIZE
		    //printf("ss %.4s\n", &data->GetFmt()->biCompression);
		    uint8_t* out = (uint8_t*) screen->pixels;
		    const uint8_t* in = data->Data();
		    const uint8_t* inbegin = in;
		    int bpl = data->Bpl();
		    if (bpl > 0)
		    {
			in += data->Bytes() - bpl;
			while (in >= inbegin)
			{
			    memcpy(out, in, bpl);
			    in -= bpl;
			    out += bpl;
			}
		    }
		}
		else
		{
		    //printf("MEM %p   %p  %d\n",
		    //       screen->pixels, data->Data(), data->Bytes());
		    memcpy(screen->pixels, data->Data(), data->Bytes());
		}

		memory_unlock();
		SDL_UnlockSurface(screen);
	    }
	    //else cout << "Direct"  << endl;
	}
	else
	{
            // FIXME write zoom with Upside-Down flipping
	    SDL_LockSurface(screen);
	    memory_lock();
	    zoom((uint16_t*)screen->pixels, (const uint16_t*)data->Data(), pic_w, pic_h, m_w, m_h, bit_depth);
	    memory_unlock();
	    SDL_UnlockSurface(screen);
	}

	Unlock();

	dirty = true;
	return 0;
    }
    virtual int Sync()
    {
	if (dirty)
	{
	    dirty = false;
	    if (TryLock() == EBUSY)
		return -1;
	    if (screen)
	    {
		SDL_GetWMInfo(&info);
		info.info.x11.lock_func();
		// no surface lock and no memory locks here !
		SDL_UpdateRect(screen, 0, 0, pic_w, pic_h);
		info.info.x11.unlock_func();
	    }
	    Unlock();
	}
	return 0;
    }
    virtual int ToggleFullscreen(bool maximize = false)
    {
	Lock();
	SDL_GetWMInfo(&info);
	info.info.x11.lock_func();

	fs = screen->flags;
	if (!(fs & SDL_FULLSCREEN))
	{
	    StoreMousePosition();
	    if (maximize && resizeEnabled)
	    {
		int w = max_w = pic_w; // set some defaults
		int h = max_h = pic_h;
		GetModeLine(w, h);
		double ratio_x = (double) w / pic_w;
		double ratio_y = (double) h / pic_h;
		double ratio = (ratio_x < ratio_y) ? ratio_x : ratio_y;
		w = (int) rint(ratio * pic_w);
		h = (int) rint(ratio * pic_h);
		if (pic_w != w || pic_h != h)
		    //XMoveWindow(dpy, info.info.x11.wmwindow, 0, 0);
		    // FIXME - needs to flush XQueue here to be able to move
		    // this window before Toggle occures
		    if (doResize(w, h) < 0)
			max_w = max_h = 0;
	    }
	}

	SDL_WM_ToggleFullScreen(screen);
	fs = screen->flags;
	SDL_fullscreen = (fs & SDL_FULLSCREEN);

        //cout << "MAX " << max_w << "   " << max_h << "  " << fs << endl;
	if (!(fs & SDL_FULLSCREEN))
	{
	    if (SDL_ShowCursor(SDL_QUERY) == SDL_DISABLE)
	    {
		RestoreMousePosition();
		if (max_w && max_h)
		    doResize(max_w, max_h);
		max_w = max_h = 0;
	    }
	}
	SDL_ShowCursor((fs & SDL_FULLSCREEN) ? SDL_DISABLE : SDL_ENABLE);

	info.info.x11.unlock_func();
	Unlock();
	Refresh();
	return (fs & SDL_FULLSCREEN);
    }
    virtual int DrawSubtitles(const subtitle_line_t* sl)
    {
	if (subtitle_line_equals(sl, m_pSubline))
	    return 0;

	if (TryLock() == EBUSY)
	    return -1;

	SDL_GetWMInfo(&info);
	info.info.x11.lock_func(); // locking is necessary!!!
	Display* d = info.info.x11.display;
	// here is usage of two different GC better
	GC lxgc = (fs & SDL_FULLSCREEN) ? wmxgc : xgc;
        // but here correct info is in window all the time (don't ask me why)
	Window win = info.info.x11.window;

#ifndef HAVE_XFT
 	//XSetForeground(info.info.x11.display, lxgc, 0xd7dced);
#endif
	XSetForeground(d, lxgc, 0x0);
	XFillRectangle(d, win, lxgc, 0, dim_h - m_sub, dim_w, m_sub);
	XSetForeground(d, lxgc, 0xFFFFFFFF);

	if (m_pSubline)
	    subtitle_line_free(m_pSubline);
	m_pSubline = (sl) ? subtitle_line_copy(sl) : NULL;

	for (int i = 0; m_pSubline && i < m_pSubline->lines; i++)
	{
	    char *sub = m_pSubline->line[i];

	    if (sub == NULL)
	    {
		cout << "??? Sub NULL line " << i << endl;
                break;
	    }
	    //cout << "Line " << i << "  " << sub << endl;
	    //XDrawString16(dpy, win, gc, dim_w/2-6*strlen(sub)/2,
	    //              dim_h-65+20*line,
	    //              (XChar2b*)(short*)QString::fromLocal8Bit(sub).unicode(), strlen(sub));
	    int slen = strlen(sub);
            int draw_h = pic_h + (i + 1) * fm_height;
#ifdef HAVE_XFT
	    if (slen && xftfont)
	    {
		wchar_t u_sub[slen + 1];
		int c = mbstowcs(u_sub, sub, slen);

		XGlyphInfo extents;
		XftTextExtents32(d, xftfont, (XftChar32 *)u_sub, c, &extents);
		int fm_width_sub = extents.xOff;
		//cout << "Width " << fm_width_sub << endl;

		XftDrawString32(xftdraw, xftcolor, xftfont,
				(dim_w - fm_width_sub) / 2,
				draw_h, (XftChar32 *)u_sub, c);
	    }
#endif
	    if (slen && font)
	    {
		int fm_width_sub = XTextWidth(font, sub, slen);
		//printf("Width %d, height %d   %s\n", fm_width_sub, fm_height, sub);
		XDrawString(d, win, lxgc, (dim_w - fm_width_sub) / 2,
			    draw_h, sub, slen);
	    }
	    //XFlush(d);
	}
	info.info.x11.unlock_func();
	Unlock();
	return 0;
    }
    virtual int SetCaption(const char* title, const char* icon)
    {
	SDL_WM_SetCaption(title, icon);
        return 0;
    }
    virtual int SetFont(const char* lf)
    {
        freeFont();
	//XSetWindowAttributes attr;
	//XWindowAttributes src;
	//XGetWindowAttributes(dpy, info.info.x11.window, &src);
	//attr.event_mask=VisibilityChangeMask;
	//XChangeWindowAttributes(dpy, info.info.x11.wmwindow, CWEventMask, &attr);
	//XChangeWindowAttributes(dpy, info.info.x11.window, CWEventMask, &attr);
	//::wnd=info.info.x11.wmwindow;

	//const char* lf;
	//GetProperties(SUBTITLE_FONT, &lf, 0);

	cout << "Loading subfont: \"" << lf << "\"" << endl;
	fm_height = 20;

	//font = XLoadQueryFont(dpy, lf);

#ifdef HAVE_XFT
	//cout << "Display " << (void*)dpy << "   " << (void*)info.info.x11.display << endl;
	Display *d = dpy;//info.info.x11.display;
	xftcolor = new XftColor;
	xftcolor->color.red = 0xd700;
	xftcolor->color.green = 0xdc00;
	xftcolor->color.blue = 0xed00;
	xftcolor->color.alpha = 0xffff;
	xftcolor->pixel = 0xd7dced;

	cout << "Using antialiased Xft renderer: "
	    << ((XftDefaultHasRender(d) == 1) ? "yes" : "no") << endl;

#if 0
	xftfont = XftFontOpen (d, DefaultScreen(d),
			       XFT_FAMILY, XftTypeString, "Arial",
			       //XFT_ENCODING, XftTypeString, "iso10646",
			       XFT_ENCODING, XftTypeString, "iso8859-2",
			       XFT_SIZE, XftTypeDouble, 20.0,
			       0);
	if (!xftfont)
	    cout << "No such FONT " << endl;
#endif

	xftfont = XftFontOpenXlfd(d, info.info.x11.window, lf);
	//xftfont = XftFontOpenName(d, info.info.x11.window, "verdana:pixelsize=30");
	if (!xftfont)
	{
	    cerr << "Failed to open font" << endl;
	    font = XLoadQueryFont(dpy, lf);
	    if (!font)
	    {
		cerr << "Failed to open font" << endl;
	    }
	}
	else
	{
	    fm_height = xftfont->height;
	    //cout << "HEIGHT  " << fm_height << endl;
	    xftdraw = XftDrawCreate(d, info.info.x11.window,
				    DefaultVisual(d, DefaultScreen(d)),
				    DefaultColormap(d, DefaultScreen(d)));
	}

#else
	font = XLoadQueryFont(dpy, lf);
	if (!font)
	{
	    cerr << "Failed to open font" << endl;
	}
#endif
	if (font)
	{
	    XSetFont(dpy, xgc, font->fid);
	    XSetFont(dpy, wmxgc, font->fid);
	    fm_height = font->max_bounds.ascent + font->max_bounds.descent;
	    //printf("Max bounds: %d, %d\n",
	    //       _font->max_bounds.ascent, _font->max_bounds.descent);
	}

	if (subtitles)
	{
	    m_sub = fm_height * SUBTITLE_SPACE;
	    dim_h = pic_h + m_sub;

	    //SDL_FreeSurface(screen);
	    screen = SDL_SetVideoMode(dim_w, dim_h, bit_depth, fs);
	    //		printf("Max bounds: %d, %d\n",
	    //		    _font->max_bounds.ascent, _font->max_bounds.descent);
	    /*	    else
	     {
	     XGetGCValues(dpy, xgc, GCFont, &gcv);
	     _font=XQueryFont
	     */
	}
        return 0;
    }

    virtual void GetModeLine(int& w, int& h)
    {
	// method to detect actuall screen resolution in pixels
	// e.g. when user switches resolution with Ctrl Alt '+'
	if (SDL_GetWMInfo(&info) > 0)
	{
	    // called with lock alread info.info.x11.lock_func();
	    int unused;
#ifdef HAVE_LIBXXF86VM
	    XF86VidModeModeLine vidmode;
	    XF86VidModeGetModeLine(info.info.x11.display,
				   DefaultScreen(info.info.x11.display),
				   &unused, &vidmode);
	    w = vidmode.hdisplay;
	    h = vidmode.vdisplay;
#else
	    // this will return just size of the whole screen
	    // and I don't know other way then XF86VidMode
	    // to read current screen size
	    w = DisplayWidth(info.info.x11.display,
			     DefaultScreen(info.info.x11.display));
	    h = DisplayHeight(info.info.x11.display,
			      DefaultScreen(info.info.x11.display));
#endif // HAVE_LIBXXF86VM
	    // info.info.x11.unlock_func();
	    //cout << "w " << w << "  h " << h << endl;
	}
    }
    virtual void StoreMousePosition()
    {
	    // save original mouse position
#define RESTOREMOUSEPOS
#ifdef RESTOREMOUSEPOS

#if 0
        // dangerous - causes deadlocks - using MouseMotion event instead
	int root_x, root_y;
	int x, y;
	unsigned int buttons;
	Window root, child;
	// this should lock SDL X queue
	// as suggested by Sam Lantinga <slouken@devolution.com>
	if (SDL_GetWMInfo(&info) > 0)
	{
	    info.info.x11.lock_func();
	    XQueryPointer(info.info.x11.display, info.info.x11.window,
			  &root, &child,
			  &root_x, &root_y, &mouse_x, &mouse_y, &buttons);
	    info.info.x11.unlock_func();
	    //cout << "Saving mouse position: rx: " << root_x << " ry: "
	    //    <<  root_y << " x: " << mouse_x << " y: " << mouse_y << endl;
	}
#endif // 0
        // using mouse motion event
        mouse_x = SDL_mouse_x;
        mouse_y = SDL_mouse_y;
#endif // RESTOREMOUSEPOS
    }
    virtual void RestoreMousePosition()
    {
        // recover original mouse position
        SDL_WarpMouse(mouse_x, mouse_y); // this seems to be doing same thing
/*
#ifdef RESTOREMOUSEPOS
	SDL_SysWMinfo info;
	SDL_VERSION(&info.version);
	if (SDL_GetWMInfo(&info) > 0)
	{
#ifdef unix
	    info.info.x11.lock_func();
	    XWarpPointer(info.info.x11.display, None, info.info.x11.window,
			 0, 0, 0, 0, mouse_x, mouse_y);
	    info.info.x11.unlock_func();
	    //cout << "Recovering  x: " << mouse_x << "  y: " << mouse_y << endl;
	}
#else
#error Need to implement these functions for other systems
#endif // unix
#endif // RESTOREMOUSEPOS
*/
    }
};

class EnvKeeper
{
    char* str;
    static const char* sdl_var;
public:
    EnvKeeper();
    ~EnvKeeper();
};

const char* EnvKeeper::sdl_var = "SDL_VIDEO_X11_NODIRECTCOLOR";
int FullscreenRenderer::s_iTrickNvidia = -1;

EnvKeeper::EnvKeeper()
{
    char* tmp=getenv(sdl_var);
    if(!tmp)
	str = 0;
    else
    {
	str = new char[strlen(tmp)+1];
	strcpy(str, tmp);
    }
    setenv(sdl_var, "1", true);
}

EnvKeeper::~EnvKeeper()
{
    if (!str)
	unsetenv(sdl_var);
    delete str;
}

static EnvKeeper keeper;


class YUVRenderer: public FullscreenRenderer
{
protected:
    SDL_Overlay* m_ov;
    fourcc_t m_fmt;
    std::vector<SDL_Overlay*> ovlist;

    virtual int doResize(int& new_w, int& new_h)
    {
	//cout << "Resize " << new_w << "  " << new_h << "  " << pic_w << "  " << pic_h << endl;
	if (new_w < m_w/4 || new_h < m_h/4)
	    return -1;
#if 1
	pic_w = new_w;
	pic_h = new_h;
	int ndim_w = pic_w;
        int ndim_h = pic_h + m_sub;
#else
	// better way - user would probably expect this behavior
	// as we do not need to rescale image again for subtitles
	// however it would be harder to preserver Ratio in this case
        // FIMXE later....
	int ndim_w = new_w;
	int ndim_h = new_h;
	pic_w = dim_w;
        pic_h = dim_h - m_sub;
#endif
	if (ndim_w == dim_w && ndim_h == dim_h)
	    return -1;

	dim_w = ndim_w;
        dim_h = ndim_h;
	screen = SDL_SetVideoMode(dim_w, dim_h, bit_depth, fs);

        return 0;
    }
public:
    YUVRenderer(PlayerWidget* pw, Display* _dpy,
		int _width, int _height, fourcc_t yuvm_fmt, bool _subtitles = false)
    : FullscreenRenderer(pw, _dpy, _width, _height, _subtitles)
    {
	printf("Creating YUV overlay %dx%d, fourcc 0x%x (%.4s)\n",
	       _width, _height, yuvm_fmt, (char*)&yuvm_fmt);

	m_ov = SDL_CreateYUVOverlay(_width, _height, yuvm_fmt, screen);

#define _SDL_VER SDL_VERSIONNUM(SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL)

	if ((!m_ov)
#if _SDL_VER>1104
    // SDL <=1.1.5 does not have hwm_overlay flag
    // since sw YUY2 was disabled, it is safe not to check it
	    || (
#if _SDL_VER>1106
		(!allow_sw_yuv)&&
#endif
		(!m_ov->hw_overlay))
#endif
	   )
	{
	    const char* errmsg = (!m_ov) ? "Failed to create overlay" :
		"No hardware acceleration detected!";

	    if (m_ov)
		SDL_FreeYUVOverlay(m_ov);

	    throw FATAL(errmsg);
	}
	m_fmt = yuvm_fmt;
	ovlist.push_back(m_ov);
	//cout << "--- MEMORY ADDR " << (void*) m_ov->pixels[0] << "  " << m_ov->hwm_overlay << endl;
    }
    ~YUVRenderer()
    {
        Lock();
	m_ov = 0;
	while (ovlist.size() > 0)
	{
	    SDL_FreeYUVOverlay(ovlist.back());
            ovlist.pop_back();
	}
	Unlock();
    }
    virtual CImage* GetData(int idx)
    {
	while ((unsigned)idx >= ovlist.size())
	{
	    SDL_Overlay* o = SDL_CreateYUVOverlay(m_w, m_h, m_fmt, screen);
	    if (!o)
		return 0;

	    ovlist.push_back(o);
	}
	BitmapInfo bi;
        bi.biWidth = m_w;
	bi.biHeight = m_h;
        bi.biPlanes = 1;
	bi.SetSpace(m_fmt);
	//bi.Print();
	CImage* ci = new CImage(&bi, (unsigned char**) ovlist[idx]->pixels, false);
	ci->SetUserData(&(*ovlist[idx]));
	return ci;
    }
    virtual int Draw(const CImage* data)
    {
	if (!data)
	    return -1;

	if (TryLock() == EBUSY)
	    return -1;

	//cout << "DRAW " << data->GetUserData() << endl;
	m_lLastDrawStamp = longcount();

	data->AddRef();
	if (image)
	    image->Release();
	image = data;

	switch(m_fmt)
	{
#if _SDL_VER > 1104
	case fccYV12:
	case fccIYUV:
	case fccI420:
	    //cout << "YV12" << endl;
	    if (!image->GetUserData())
	    {
		SDL_LockYUVOverlay(m_ov);
		memory_lock();
		memcpy(m_ov->pixels[0], data->Data(), data->Pixels());
		memcpy(m_ov->pixels[2], data->Data() + data->Pixels(),
		       data->Pixels() / 4);
		memcpy(m_ov->pixels[1], data->Data() + data->Pixels() * 5 / 4,
		       data->Pixels() / 4);
		memory_unlock();
		SDL_UnlockYUVOverlay(m_ov);
	    }
	    break;
#else
	case fccYV12:
	case fccIYUV:
	case fccI420:
#endif
	case fccYUY2:
	case fccUYVY:
	case fccYVYU:
	    //cout << "YUY2" << endl;
	    if (!image->GetUserData())
	    {
		//printf("FMT %.4s\n", (char*) &m_fmt);
		SDL_LockYUVOverlay(m_ov);
		memory_lock();
                if (data->Data())
		    memcpy(m_ov->pixels[0], data->Data(), 2 * data->Pixels());
		else
		    printf("!!!MEMOV %p   %p  %d\n",
			   m_ov->pixels[0], data->Data(), 2 * data->Pixels());
		memory_unlock();
		SDL_UnlockYUVOverlay(m_ov);
	    }
	    //else cout << "no memcpy" << endl;
	    break;
	}
	Unlock();

	return 0;
    }
    virtual int Sync()
    {
        //cout << "SYNC" << endl;
	if (TryLock() == EBUSY)
	    return -1;

	SDL_Rect rect;
	rect.x = rect.y = 0;
	rect.w = pic_w;
	rect.h = pic_h;

	// as the following operation might lock itself in XServer
	// (when someone is resizing windows
	// we can't hold any memory lock here!
	SDL_Overlay* o = (SDL_Overlay*) image->GetUserData();
	if (!o)
	    o = m_ov;

        // no surface lock as SDL documentation recomends!
	SDL_GetWMInfo(&info);
	info.info.x11.lock_func();

	if (s_iTrickNvidia > 0)
	{
	    GC lxgc = (fs & SDL_FULLSCREEN) ? wmxgc : xgc;
	    XSetForeground(info.info.x11.display, lxgc, 0x0);
	    XFillRectangle(info.info.x11.display, info.info.x11.window,
			   lxgc, 0, pic_h - s_iTrickNvidia,
			   dim_w, s_iTrickNvidia);
	}
	SDL_DisplayYUVOverlay(o, &rect);
	info.info.x11.unlock_func();
	Unlock();
	return 0;
    }
};

VideoRenderer* CreateFullscreenRenderer(PlayerWidget* pw, Display* dpy,
					int width, int height, bool sub)
{
    return new FullscreenRenderer(pw, dpy, width, height, sub);
}

VideoRenderer* CreateYUVRenderer(PlayerWidget* pw, Display* dpy,
				 int width, int height,
				 fourcc_t yuv_fmt, bool sub)
{
    // return new XvYUVRenderer(pw, dpy, width, height, yuv_fmt, sub);
    return new YUVRenderer(pw, dpy, width, height, yuv_fmt, sub);
}

#else

VideoRenderer* CreateFullscreenRenderer(PlayerWidget* pw, Display* dpy,
					int width, int height, bool sub)
{
    cout << "library compiled without SDL support - sorry on rendering" << endl;
    return 0;
}

VideoRenderer* CreateYUVRenderer(PlayerWidget* pw, Display* dpy,
				 int width, int height,
				 fourcc_t yuv_fmt, bool sub)
{
    cout << "library compiled without SDL support - sorry on rendering" << endl;
    return 0;
}

#endif
/*
#ifdef USE_OPENGL
struct texts {
    int w;
    int h;
    int u_w;
    int u_h;
    GLuint *texture;
    int quad_1;
    int quad_2;
    int quad_3;
    int quad_4;
    };

class GLFullscreenRenderer: public VideoRenderer
{

    SDL_Surface *screen;
    int dim_w, dim_h;
    int fs;
    int m_sub;
    int zoom_divider;
    int dirty;
    Window wnd;
    pthread_mutex_t mutex;
    struct texts *textures;
    GLuint *textureids;
    GLubyte* pixels;
    int textcount_w;
    int textcount_h;
    int max_texture_size;
public:
    GLFullscreenRenderer():screen(0){}


    virtual int Create(QWidget* w, int width, int height, int xpos=0, int subtitles=0)
    {
    fs=SDL_RESIZABLE | SDL_OPENGL;

    char SDL_windowhack[256];
       dev=w;
       Display *dpy = dev->x11Display();
       ::dpy=dpy;
       unsigned long mask;

  int glxMajorVersion, glxMinorVersion;


  if(SDL_Init(SDL_INIT_VIDEO ) <0)
           throw FATAL("Failed to init SDL");
    m_w=width;

         m_h=height;
    width=1024;
    height=768;
    pic_w=width;
       pic_h=height;
       m_sub=subtitles;
       dirty=0;
       dim_w=pic_w;
       dim_h=pic_h+(subtitles?80:0);
       pthread_mutex_init(&mutex, 0);
//  screen = SDL_SetVideoMode(1024, 768, 0, SDL_OPENGL);    | SDL_INIT_EVENTTHREAD
        screen=SDL_SetVideoMode(dim_w, dim_h, GetPhysicalDepth(dev), fs);
       if(screen )
       {
//         SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
//         SDL_SetEventFilter(EventFilter);
           valid_filter=1;
           SDL_WM_SetCaption("Movie window", 0);

           SDL_SysWMinfo info;

           SDL_VERSION(&info.version);
           SDL_GetWMInfo(&info);
           Display *dpy=dev->x11Display();

      GLint maxsize=0;
      GLboolean b= false;
        SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
      glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
      cerr<<"Texturemaxsize="<<max_texture_size<<endl;
      glGetIntegerv(GL_RED_BITS, &maxsize);
      cerr<<"RED BITS="<<maxsize<<endl;
      glGetIntegerv(GL_BLUE_BITS, &maxsize);
      cerr<<"BLUE BITS="<<maxsize<<endl;
      glGetIntegerv(GL_GREEN_BITS, &maxsize);
      cerr<<"GREEN BITS="<<maxsize<<endl;
      glGetIntegerv(GL_ALPHA_BITS, &maxsize);
      cerr<<"ALPHA BITS="<<maxsize<<endl;
      glGetIntegerv(GL_MAX_TEXTURE_STACK_DEPTH, &maxsize);
      cerr<<"MAX_TEXTURE_STACK_DEPTH="<<maxsize<<endl;
      glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &maxsize);
      cerr<<"MAX_TEXTURE_UNITS="<<maxsize<<endl;
      glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &maxsize);
      cerr<<"MAX_VIEWPORT_DIMS="<<maxsize<<endl;
      glGetBooleanv(GL_DOUBLEBUFFER, &b);
      if (b)
        cerr<<"DOUBLBUFFER IS OK"<<endl;
      else
        cerr<<"NO DOUBELBUFFER"<<endl;
      glGetBooleanv(GL_RGBA_MODE, &b);
      if (b)
        cerr<<"RGBA IS TRUE"<<endl;
      else
        cerr<<"RGBA IS FALSE"<<endl;
      glXQueryVersion(::dpy, &glxMajorVersion, &glxMinorVersion);
        cerr<<"glX-Version "<<glxMajorVersion<<"."<<glxMinorVersion<<endl;
      if (glXIsDirect(::dpy, glXGetCurrentContext()))
        cerr<<"Congrats, you have Direct Rendering!"<<endl;
      else {
        cerr<<"Sorry, no Direct Rendering possible!"<<endl;
        screen = false;
        }

        glEnable( GL_TEXTURE_2D );
        glViewport(0,0,pic_w, pic_h);
        if (createTextures()>0) {
          cerr<<"Fatal Error : can't create Textures"<<endl;
          screen=false;
        }
        else {
          gluOrtho2D(0, m_w, m_h, 0);
           int error = glGetError( );
          if (error)
                cerr<<"Initerror :"<<error<<endl;

//          glShadeModel(GL_SMOOTH);                   // Enables Smooth Color Shading


          glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
          glEnable( GL_TEXTURE_2D );
          if (error)
                cerr<<"Initerror :"<<error<<endl;
        }

           return 0;
       }
       if(!screen)
           throw FATAL("Failed to set up video mode");
       throw FATAL("Failed to set up fullscreen mode");

    }

    GLenum createTextures()
    {

      int skip_w=0;
      int skip_h=0;
      textcount_w=0;
      textcount_h=0;
      // This look how many textures we need in width
      while(skip_w<(m_w-8)) {
        skip_w += getPowerOf2(m_w-skip_w);
        textcount_w++;
      }
      // This look how many textures we need in height
      while(skip_h<(m_h-8)) {
        skip_h += getPowerOf2(m_h-skip_h);
        textcount_h++;
      }
      // A Backround will be set, if not the texture will not shown later
      pixels = (GLubyte*) malloc( m_w * m_h * 4);
           memset( pixels, 128, m_w * m_h * 4 );
      if (!pixels) {
        return GL_OUT_OF_MEMORY;
      }

      textures = new texts[textcount_w * textcount_h];       // Here we stores all informatio n
of the textures
      textureids = new GLuint[textcount_w * textcount_h];    // The Textureids so we can
 create all textures at once
      glGenTextures( textcount_w * textcount_h, textureids); // And here their will created
      skip_h=0;                                              // Stores the Rows to skip
      int i=0;                                               // only needed for pointerarithmetic
      for(int h=0; h<textcount_h;h++) {
        skip_w=0;                                            // the pixels in row to skip
        int text_size_h = getPowerOf2(m_h-skip_h);
        for(int w=0; w<textcount_w;w++) {
            int text_size_w = getPowerOf2(m_w-skip_w);
            (textures+i)->w = text_size_w;                   // width and height of texture
            (textures+i)->h = text_size_h;
            cerr<<"Texturesize("<<i<<") :"<<text_size_w<<"; "<<text_size_h<<endl;
            (textures+i)->u_w = skip_w;                      // Pixels zu skip in row
            (textures+i)->u_h = skip_h;                      // Rows to skip
            (textures+i)->texture = (textureids+i);
            (textures+i)->quad_1 = skip_w;                   // is the vortex of the texture,
 so we can
            (textures+i)->quad_2 = skip_h;                   // draw faster (later)
            (textures+i)->quad_3 = (textures+i)->quad_1 + (textures+i)->w;
            (textures+i)->quad_4 = (textures+i)->quad_2 + (textures+i)->h;
            skip_w +=text_size_w;

            glBindTexture( GL_TEXTURE_2D, (GLuint) (textures+i)->texture);

            glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
//GL_DECAL

            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );

            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

            glTexImage2D( GL_TEXTURE_2D,  0,  GL_RGB, (textures+i)->w, (textures+i)->h, 0,
 GL_RGBA,  GL_UNSIGNED_BYTE, pixels );
            int error = glGetError( );
            if (error) return error;
            i++;
        }
        skip_h +=text_size_h;
      }

      // Simple state setup at the end.
      glClearColor( 0.0, 0.0, 0.0, 0.0 );

      return glGetError( );
    }

    int getPowerOf2(int value)
    {
        int i,e;
        for(i=1;((i<=value) && (i<=max_texture_size)) ; i*=2)
         e=i;
        return e;
    }

    int Resize(int& new_w, int& new_h)
    {
       if(new_w<0)return -1;
       if(new_h<0)return -1;
       if(new_h<m_h/4)
           return -1;
       if(new_w<m_w/4)
           return -1;
       pic_w=new_w;
       pic_h=new_h;
       dim_w=pic_w;
       dim_h=pic_h+(m_sub?fm_height*4:0);
       pthread_mutex_lock(&mutex);

  if(screen){ SDL_FreeSurface(screen); screen=0;}
       screen=SDL_SetVideoMode(dim_w, dim_h, 0, fs);
  if (screen)
    glViewport(0,0,pic_w, pic_h);


       pthread_mutex_unlock(&mutex);
    }






    virtual int Draw(const CImage* data)
    {
           if(!screen)return -1;
           if(pthread_mutex_trylock(&mutex)==EBUSY) return -1;
        // copy the picture and swap BGR to RGB
        memcpy(pixels, data->Data(), data->Bytes());
        for (int i=0;i<data->Bytes();i+=3)
        {
         GLubyte tmp;
          tmp = *(pixels+i);
          *(pixels+i)=* (pixels+i+2);
          *(pixels+i+2)=tmp;
        }
        // load picture in textures and draw them
        for(int i=0; i<(textcount_w*textcount_h); i++) {
           glBindTexture( GL_TEXTURE_2D, (GLuint)(textures+i)->texture);
           glPixelStorei( GL_UNPACK_SKIP_PIXELS, (textures+i)->u_w );
           glPixelStorei( GL_UNPACK_SKIP_ROWS, (textures+i)->u_h );
           glPixelStorei( GL_UNPACK_ROW_LENGTH, m_w );

           glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0,  (textures+i)->w, (textures+i)->h, GL_RGB,
 GL_UNSIGNED_BYTE, pixels );

           glBegin( GL_QUADS );
           glTexCoord2f( 0.0, 0.0 );
           glVertex2i((textures+i)->quad_1, (textures+i)->quad_2);
           glTexCoord2f( 0.0, 1.0 );
           glVertex2i( (textures+i)->quad_1, (textures+i)->quad_4);
           glTexCoord2f( 1.0, 1.0 );
           glVertex2i( (textures+i)->quad_3, (textures+i)->quad_4 );
           glTexCoord2f( 1.0, 0.0 );
           glVertex2i((textures+i)->quad_3, (textures+i)->quad_2 );
           glEnd( );
           int error = glGetError( );
           if (error)
                cerr<<"Drawerror :"<<error<<endl;
      }
      SDL_GL_SwapBuffers();
           pthread_mutex_unlock(&mutex);
      SDL_Event event;
      while(SDL_PollEvent(&event))
        EventFilter(&event);
           return 0;
    }

    virtual int sync()
    {
       if(dirty)
       {
           SDL_UpdateRect(screen, 0, 0, dim_w, dim_h-(m_sub?fm_height*4:0));
           dirty=0;
       }
       return 0;
    }
    virtual ~GLFullscreenRenderer()
    {
       pthread_mutex_lock(&mutex);

           free(pixels);
      SDL_SetEventFilter(0);
      SDL_QuitSubSystem(SDL_INIT_VIDEO);
      valid_filter=0;
      pthread_mutex_unlock(&mutex);
      pthread_mutex_destroy(&mutex);

    }
    virtual int toggle_fullscreen(bool maximize=false)
    {
       pthread_mutex_lock(&mutex);
       fs=fs^SDL_FULLSCREEN;
       if(screen){ SDL_FreeSurface(screen); screen=0;}
       screen=SDL_SetVideoMode(dim_w, dim_h, GetPhysicalDepth(dev), SDL_OPENGL | fs);
       if(!screen)
       {
           fs=fs^SDL_FULLSCREEN;
           screen=SDL_SetVideoMode(dim_w, dim_h, GetPhysicalDepth(dev), SDL_OPENGL |fs);
       }
  else
      glViewport(0, 0, pic_w, pic_h);

       if(fs&SDL_FULLSCREEN)
       {
               SDL_ShowCursor(0);
       }
       else
       {
               SDL_ShowCursor(1);
       }

#ifdef HAVE_LIBXXF86VM
       if(maximize && (fs&SDL_FULLSCREEN))
       {
           XF86VidModeModeLine mode;
           int unused;
           int w, h;
           double ratio_x, ratio_y;
           if ( XF86VidModeGetModeLine(dev->x11Display(), dev->x11Screen(), &unused, &mode) )
           {
               w = mode.hdisplay;
               h = mode.vdisplay-(m_sub?fm_height*4:0);
               ratio_x=double(w)/pic_w;
               ratio_y=double(h)/pic_h;
               double ratio=(ratio_x<ratio_y)?ratio_x:ratio_y;
               w=int(ratio*pic_w);
               h=int(ratio*pic_h);
               cerr<<mode.hdisplay<<" "<<mode.vdisplay<<" "<<w<<" "<<h<<endl;
               pthread_mutex_unlock(&mutex);
               Resize(w, h);

               return ((fs&SDL_FULLSCREEN)==0)?0:1;
           }
        }
#endif
       pthread_mutex_unlock(&mutex);


       return ((fs&SDL_FULLSCREEN)==0)?0:1;

    }
    virtual int drawSubtitles(const char* text)
    {
       static char* old_text=0;
       if(!m_sub)return 0;
       if(!text)return 0;
       if((!old_text) && (!text[0]))return 0;

       SDL_SysWMinfo info;
       XGCValues gcv;

       SDL_VERSION(&info.version);
       SDL_GetWMInfo(&info);
       Window win;
       if(fs & SDL_FULLSCREEN)
           win=info.info.x11.fswindow;
       else
           win=info.info.x11.window;
       Display *dpy=dev->x11Display();
       gcv.graphics_exposures = False;
       dpy=info.info.x11.display;
       win=info.info.x11.window;

       if(old_text && (!strcmp(text, old_text)))
           return 0;

       GC gc=XCreateGC(dpy, win, GCGraphicsExposures, &gcv);
       QFont font=RegAccess::ReadFont();
       XSetFont(dpy, gc, font.handle());
       XSetForeground(dpy, gc, 0x0);
       if(old_text && strcmp(text, old_text))
           XFillRectangle(dpy, win, gc, 0, dim_h-80, dim_w, 80);
       XSetForeground(dpy, gc, 0xFFFFFFFF);
       if(old_text)delete old_text;
       old_text=new char[strlen(text)+1];
       strcpy(old_text, text);
       char* sub=new char[strlen(text)+1];
       char* sub_orig=sub;
       strcpy(sub, text);
       char* run=sub;
       int line=0;
       while(*run!=0)
       {
           run=strchr(run, '|');
           if(run)*run=0;
//         XDrawString16(dpy, win, gc,
//             dim_w/2-6*strlen(sub)/2, dim_h-65+20*line,
//                 (XChar2b*)(short*)QString::fromLocal8Bit(sub).unicode(), strlen(sub));
           XDrawString(dpy, win, gc,
               dim_w/2-6*strlen(sub)/2, dim_h-65+20*line,
                   sub, strlen(sub));
           if(run==0)break;
           line++;
           run++;
           sub=run;
       }
       delete sub_orig;
       XFreeGC(dpy, gc);
       return 0;
    }
};

VideoRenderer* CreateGLFullscreenRenderer()
{
    return new GLFullscreenRenderer;
}
#endif //USE_OPENGL
*/
#ifdef HAVE_LIBXXF86DGA
#ifdef HAVE_LIBXXF86VM
/********************************************************

	DGA video renderer
	Copyright 2000 Heinrich Langos

*********************************************************/
/*
#define INT32 __my_int32
#define INT8 __my_int8
// damn qglobal.h defines INT32 and INT8 without saying anything about signs
// thanx to roots__ from #c for helping me out with this.

//#include "mmx_dga.cpp"
#include <X11/Xproto.h>
#include <X11/Xmd.h>
#include <X11/extensions/xf86dga.h>
#include <X11/extensions/xf86dgastr.h>

#define VIDMODE_SWITCHING_DELAY 100

#include <X11/extensions/xf86vmode.h>
#include <X11/extensions/xf86vmstr.h>

class DGARenderer: public VideoRenderer
  // vm_* videomode variables
  // vp_* vievport variables
{
  Display *dpy;
  int ppl;    //pixel per line (scan line, not frame line !! )
  char* base; //frame buffer base address

  int zoom_divider;


  int  vm_count;  //number of available videomodes
  XF86VidModeModeInfo **vm_modelines;  // the modes themself
  bool vm_switched; //only used to decide if we have to switch back on quit

  int vp_width,vp_height;  // the viewport size
  int vp_flip_offset;      // memory offset to the next frame in framebuffer
  int vp_current;          // currently used viewport (0..vp_frames-1)
  const static int vp_frames=2; // the number of viewports that we use.
  int bit_depth,byte_depth;  //self explaining ...
  char *conversionbuf;  // only used if we dont render directly into the framebuffer


  void set_vidmode(int nr)
  {
    printf("switching video mode to %dx%d/%d now\n",
	   vm_modelines[nr]->hdisplay,
	   vm_modelines[nr]->vdisplay,
	   vm_modelines[nr]->dotclock);

//    XF86VidModeSwitchToMode(dpy,XDefaultScreen(dpy),vm_modelines[nr]);
    avi_usleep(VIDMODE_SWITCHING_DELAY*1000);
  }

public:
  DGARenderer()
  {}
  int toggle_fullscreen()
  {}
  virtual int Create(QWidget* w, int x, int y, int zoom=1, int xpos=0, int sub=0)
  {
    zoom_divider=zoom;
    if(zoom!=1 && zoom!=2)throw FATAL("Unsupported zoom factor");
    vp_current=0;
    dev=(QPaintDevice*)w;
    dpy = dev->x11Display();
    m_w=x;
    m_h=y;
    width=x;
    height=y;
    Visual  *vis = (Visual*)dev->x11Visual();
    bool have_dga=false;
    int minor, major, bank, ram, flags;



    int i,bestmatch, diff_x=99999, diff_y=99999;
    XF86VidModeGetAllModeLines(dpy,XDefaultScreen(dpy),
			       &vm_count,&vm_modelines);
    for (i = 0; i < vm_count; i++) {
      if ((width/zoom_divider  <= vm_modelines[i]->hdisplay) &&
	  (height/zoom_divider <= vm_modelines[i]->vdisplay))
	if  ( ( (vm_modelines[i]->hdisplay - width/zoom_divider)  <= diff_x) &&
	      ( (vm_modelines[i]->vdisplay - height/zoom_divider) <= diff_y) ) {
	  bestmatch=i;
	  diff_x =vm_modelines[i]->hdisplay - width/zoom_divider ;
	  diff_y =vm_modelines[i]->vdisplay - height/zoom_divider ;
	}
    }
    i=bestmatch;
    if (i != 0 && i != vm_count) {
      set_vidmode(i);
      vm_switched = 1;
      vp_width = vm_modelines[i]->hdisplay;
      vp_height = vm_modelines[i]->vdisplay;
    } else {
      vm_switched = 0;
      vp_width = vm_modelines[0]->hdisplay;
      vp_height = vm_modelines[0]->vdisplay;
    }
//    XWarpPointer(dpy, None, RootWindow(dpy, DefaultScreen(dpy)),
//		 0, 0, 0, 0, 0,0);
    XF86VidModeSetViewPort(dpy,XDefaultScreen(dpy),0,0);
    if(XF86DGAQueryExtension(dpy, &major, &minor))
      {
	XF86DGAQueryDirectVideo(dpy, XDefaultScreen(dpy), &flags);
	if(flags & XF86DGADirectPresent)
	  {
	    have_dga=true;
	    XF86DGAGetVideo(dpy, DefaultScreen(dpy),  &base, &ppl, &bank, &ram);
	    if(!base)
	      return -1;

	    // This is quoted from xmms-1.0.1 dga.c
  	    // The things we must go through to get a proper depth...
	    //   If I find the idiot that thought making 32bit report 24bit
	    //   was a good idea, there may be one less `programmer' in this
	    //   world...
	    {
	      XImage *img;

	      img = XGetImage(dpy, RootWindow(dpy, DefaultScreen(dpy)),
			      0, 0, 1, 1, AllPlanes, ZPixmap);
	      bit_depth = img->bits_per_pixel;
	      XDestroyImage(img);
	    }
	    byte_depth = (bit_depth+7)/8;
	    // end quote :-)
	    XF86DGAGetViewPortSize(dpy, DefaultScreen(dpy), &vp_width, &vp_height);
	  }

      }

    if(have_dga) {


      //      printf("DGA: base:%x , ppl:%d, bank:%d, ram:%d \n",base, ppl, bank, ram );
      //      flip_offset=ppl*vp_height*byte_depth;

      vp_flip_offset=vp_height*ppl*byte_depth;
      XF86DGADirectVideo(dpy, DefaultScreen(dpy), XF86DGADirectGraphics);

      //			     XF86DGADirectMouse|
      //			     XF86DGADirectKeyb);

      XF86DGASetViewPort(dpy, DefaultScreen(dpy), 0, 0);

      //
h}%@h}% zero=0;
        for (int v=0;v<vp_flip_offset*vp_frames; v+=4)
      	  memcpy((void*)(base+v), &zero, 4);

      return 0;
    }else {
//      conversionbuf=(char *)malloc(x*y*byte_depth);
//      if (!conversionbuf)
//	return -1;
    }
  }

  virtual int Draw(const CImage* data)
  {
    //    printf("Drawing %d lines to base %x ppl: %d width: %d \n",height,base,ppl, width);
    bit_depth++;
    bit_depth/=8;
    int bpl=::width*bit_depth;
//    conversionbuf+=bpl*(height-1);
    sync();
//    for(int y=::height-1; y>0; y--)
//    {
//        memcpy(conversionbuf, data, bpl);
//        data+=bpl;
//        conversionbuf-=bpl;
//    }
    for(int i=0; i<height; i++)
	memcpy((void*)(base+i*ppl*byte_depth), data->at(i), data->bpl());

    return 0;
  };
  virtual int sync()
  {
    Display *dpy = dev->x11Display();
    XSync(dpy, false);
    return 0;
  };
  virtual ~DGARenderer(){
    Display *dpy = dev->x11Display();
    if (vm_switched)
       set_vidmode(0);
    XF86DGASetViewPort(dpy, DefaultScreen(dpy), 0, 0);
    XF86DGADirectVideo(dpy, DefaultScreen(dpy), 0);
//    free(conversionbuf);
  }
     virtual int drawSubtitles(const char*)
     {
        return 0;
     }
};

VideoRenderer* CreateDGARenderer()
{
h}%@h}%@rn new DGARenderer;
}
*/
#endif	/*HAVE_LIBXXF86VM*/
#endif  /*HAVE_LIBXXF86DGA*/

#endif  /*WITHOUT_X*/
/*
vim: tabstop=8
*/
