xglx.c   [plain text]


/*
 * Copyright © 2004 David Reveman
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without
 * fee, provided that the above copyright notice appear in all copies
 * and that both that copyright notice and this permission notice
 * appear in supporting documentation, and that the name of
 * David Reveman not be used in advertising or publicity pertaining to
 * distribution of the software without specific, written prior permission.
 * David Reveman makes no representations about the suitability of this
 * software for any purpose. It is provided "as is" without express or
 * implied warranty.
 *
 * DAVID REVEMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
 * NO EVENT SHALL DAVID REVEMAN BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Author: David Reveman <davidr@novell.com>
 */

#include "xglx.h"

#include <X11/extensions/Xrandr.h>
#include <X11/cursorfont.h>

#include <glitz-glx.h>

#ifdef GLXEXT
#include "xglglxext.h"
#endif

#include "inputstr.h"
#include "cursorstr.h"
#include "mipointer.h"

#ifdef RANDR
#include "randrstr.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <math.h>

#ifdef XKB
#include <X11/extensions/XKB.h>
#include <xkbsrv.h>
#include <X11/extensions/XKBconfig.h>

extern Bool
XkbQueryExtension (Display *dpy,
		   int     *opcodeReturn,
		   int     *eventBaseReturn,
		   int     *errorBaseReturn,
		   int     *majorRtrn,
		   int     *minorRtrn);

extern XkbDescPtr
XkbGetKeyboard (Display      *dpy,
		unsigned int which,
		unsigned int deviceSpec);

extern Status
XkbGetControls (Display	    *dpy,
		unsigned long which,
		XkbDescPtr    desc);

#ifndef XKB_BASE_DIRECTORY
#define	XKB_BASE_DIRECTORY	"/usr/lib/X11/xkb/"
#endif
#ifndef XKB_CONFIG_FILE
#define	XKB_CONFIG_FILE		"X0-config.keyboard"
#endif
#ifndef XKB_DFLT_RULES_FILE
#define	XKB_DFLT_RULES_FILE	"xorg"
#endif
#ifndef XKB_DFLT_KB_LAYOUT
#define	XKB_DFLT_KB_LAYOUT	"us"
#endif
#ifndef XKB_DFLT_KB_MODEL
#define	XKB_DFLT_KB_MODEL	"pc101"
#endif
#ifndef XKB_DFLT_KB_VARIANT
#define	XKB_DFLT_KB_VARIANT	NULL
#endif
#ifndef XKB_DFLT_KB_OPTIONS
#define	XKB_DFLT_KB_OPTIONS	NULL
#endif

#endif

#define XGLX_DEFAULT_SCREEN_WIDTH  800
#define XGLX_DEFAULT_SCREEN_HEIGHT 600

typedef struct _xglxScreen {
    Window	       win, root;
    Colormap	       colormap;
    Bool	       fullscreen;
    CloseScreenProcPtr CloseScreen;
} xglxScreenRec, *xglxScreenPtr;

int xglxScreenGeneration = -1;
int xglxScreenPrivateIndex;

#define XGLX_GET_SCREEN_PRIV(pScreen)				         \
    ((xglxScreenPtr) (pScreen)->devPrivates[xglxScreenPrivateIndex].ptr)

#define XGLX_SET_SCREEN_PRIV(pScreen, v)			       \
    ((pScreen)->devPrivates[xglxScreenPrivateIndex].ptr = (pointer) v)

#define XGLX_SCREEN_PRIV(pScreen)			       \
    xglxScreenPtr pScreenPriv = XGLX_GET_SCREEN_PRIV (pScreen)

typedef struct _xglxCursor {
    Cursor cursor;
} xglxCursorRec, *xglxCursorPtr;

#define XGLX_GET_CURSOR_PRIV(pCursor, pScreen)		   \
    ((xglxCursorPtr) (pCursor)->devPriv[(pScreen)->myNum])

#define XGLX_SET_CURSOR_PRIV(pCursor, pScreen, v)	 \
    ((pCursor)->devPriv[(pScreen)->myNum] = (pointer) v)

#define XGLX_CURSOR_PRIV(pCursor, pScreen)			        \
    xglxCursorPtr pCursorPriv = XGLX_GET_CURSOR_PRIV (pCursor, pScreen)

static char	 *xDisplayName = 0;
static Display	 *xdisplay     = 0;
static int	 xscreen;
static CARD32	 lastEventTime = 0;
static ScreenPtr currentScreen = 0;
static Bool	 softCursor    = FALSE;
static Bool	 fullscreen    = TRUE;

static Bool randrExtension = FALSE;
static int  randrEvent, randrError;

static glitz_drawable_format_t *xglxScreenFormat = 0;

static Bool
xglxAllocatePrivates (ScreenPtr pScreen)
{
    xglxScreenPtr pScreenPriv;

    if (xglxScreenGeneration != serverGeneration)
    {
	xglxScreenPrivateIndex = AllocateScreenPrivateIndex ();
	if (xglxScreenPrivateIndex < 0)
	    return FALSE;

	xglxScreenGeneration = serverGeneration;
    }

    pScreenPriv = xalloc (sizeof (xglxScreenRec));
    if (!pScreenPriv)
	return FALSE;

    XGLX_SET_SCREEN_PRIV (pScreen, pScreenPriv);

    return TRUE;
}

#ifdef RANDR

#define DEFAULT_REFRESH_RATE 50

static Bool
xglxRandRGetInfo (ScreenPtr pScreen,
		  Rotation  *rotations)
{
    RRScreenSizePtr pSize;

    *rotations = RR_Rotate_0;

    if (randrExtension)
    {
	XRRScreenConfiguration *xconfig;
	XRRScreenSize	       *sizes;
	int		       nSizes, currentSize = 0;
	short		       *rates, currentRate;
	int		       nRates, i, j;

	XGLX_SCREEN_PRIV (pScreen);

	xconfig	    = XRRGetScreenInfo (xdisplay, pScreenPriv->root);
	sizes	    = XRRConfigSizes (xconfig, &nSizes);
	currentRate = XRRConfigCurrentRate (xconfig);

	if (pScreenPriv->fullscreen)
	{
	    Rotation rotation;

	    currentSize = XRRConfigCurrentConfiguration (xconfig, &rotation);

	    for (i = 0; i < nSizes; i++)
	    {
		pSize = RRRegisterSize (pScreen,
					sizes[i].width,
					sizes[i].height,
					sizes[i].mwidth,
					sizes[i].mheight);

		rates = XRRConfigRates (xconfig, i, &nRates);

		for (j = 0; j < nRates; j++)
		{
		    RRRegisterRate (pScreen, pSize, rates[j]);

		    if (i == currentSize && rates[j] == currentRate)
			RRSetCurrentConfig (pScreen, RR_Rotate_0, currentRate,
					    pSize);
		}
	    }
	}
	else
	{
	    pSize = RRRegisterSize (pScreen,
				    pScreen->width,
				    pScreen->height,
				    pScreen->mmWidth,
				    pScreen->mmHeight);

	    for (i = 0; i < nSizes; i++)
	    {
		rates = XRRConfigRates (xconfig, i, &nRates);

		for (j = 0; j < nRates; j++)
		{
		    RRRegisterRate (pScreen, pSize, rates[j]);

		    if (rates[j] == currentRate)
			RRSetCurrentConfig (pScreen, RR_Rotate_0, currentRate,
					    pSize);
		}
	    }
	}

	XRRFreeScreenConfigInfo (xconfig);
    }
    else
    {
	pSize = RRRegisterSize (pScreen,
				pScreen->width,
				pScreen->height,
				pScreen->mmWidth,
				pScreen->mmHeight);

	RRRegisterRate (pScreen, pSize, DEFAULT_REFRESH_RATE);
	RRSetCurrentConfig (pScreen, RR_Rotate_0, DEFAULT_REFRESH_RATE, pSize);
    }

    return TRUE;
}

static Bool
xglxRandRSetConfig (ScreenPtr	    pScreen,
		    Rotation	    rotations,
		    int		    rate,
		    RRScreenSizePtr pSize)
{
    if (randrExtension)
    {
	XRRScreenConfiguration *xconfig;
	XRRScreenSize	       *sizes;
	int		       nSizes, currentSize;
	int		       i, size = -1;
	int		       status = RRSetConfigFailed;
	Rotation	       rotation;

	XGLX_SCREEN_PRIV (pScreen);

	xconfig	    = XRRGetScreenInfo (xdisplay, pScreenPriv->root);
	sizes	    = XRRConfigSizes (xconfig, &nSizes);
	currentSize = XRRConfigCurrentConfiguration (xconfig, &rotation);

	for (i = 0; i < nSizes; i++)
	{
	    if (pScreenPriv->fullscreen)
	    {
		if (sizes[i].width   == pSize->width   &&
		    sizes[i].height  == pSize->height  &&
		    sizes[i].mwidth  == pSize->mmWidth &&
		    sizes[i].mheight == pSize->mmHeight)
		{
		    size = i;
		    break;
		}
	    }
	    else
	    {
		short *rates;
		int   nRates, j;

		rates = XRRConfigRates (xconfig, i, &nRates);

		for (j = 0; j < nRates; j++)
		{
		    if (rates[j] == rate)
		    {
			size = i;
			if (i >= currentSize)
			    break;
		    }
		}
	    }
	}

	if (size >= 0)
	    status = XRRSetScreenConfigAndRate (xdisplay,
						xconfig,
						pScreenPriv->root,
						size,
						RR_Rotate_0,
						rate,
						CurrentTime);

	XRRFreeScreenConfigInfo (xconfig);

	if (status == RRSetConfigSuccess)
	{
	    PixmapPtr pPixmap;

	    pPixmap = (*pScreen->GetScreenPixmap) (pScreen);

	    if (pScreenPriv->fullscreen)
	    {
		XGL_PIXMAP_PRIV (pPixmap);

		xglSetRootClip (pScreen, FALSE);

		XResizeWindow (xdisplay, pScreenPriv->win,
			       pSize->width, pSize->height);

		glitz_drawable_update_size (pPixmapPriv->drawable,
					    pSize->width, pSize->height);

		pScreen->width    = pSize->width;
		pScreen->height   = pSize->height;
		pScreen->mmWidth  = pSize->mmWidth;
		pScreen->mmHeight = pSize->mmHeight;

		(*pScreen->ModifyPixmapHeader) (pPixmap,
						pScreen->width,
						pScreen->height,
						pPixmap->drawable.depth,
						pPixmap->drawable.bitsPerPixel,
						0, 0);

		xglSetRootClip (pScreen, TRUE);
	    }

	    return TRUE;
	}
    }

    return FALSE;
}

static Bool
xglxRandRInit (ScreenPtr pScreen)
{
    rrScrPrivPtr pScrPriv;

    if (!RRScreenInit (pScreen))
	return FALSE;

    pScrPriv = rrGetScrPriv (pScreen);
    pScrPriv->rrGetInfo   = xglxRandRGetInfo;
    pScrPriv->rrSetConfig = xglxRandRSetConfig;

    return TRUE;
}

#endif

static void
xglxConstrainCursor (ScreenPtr pScreen,
		     BoxPtr    pBox)
{
}

static void
xglxCursorLimits (ScreenPtr pScreen,
		  CursorPtr pCursor,
		  BoxPtr    pHotBox,
		  BoxPtr    pTopLeftBox)
{
    *pTopLeftBox = *pHotBox;
}

static Bool
xglxDisplayCursor (ScreenPtr pScreen,
		   CursorPtr pCursor)
{
    XGLX_SCREEN_PRIV (pScreen);
    XGLX_CURSOR_PRIV (pCursor, pScreen);

    XDefineCursor (xdisplay, pScreenPriv->win, pCursorPriv->cursor);

    return TRUE;
}

#ifdef ARGB_CURSOR

static Bool
xglxARGBCursorSupport (void);

static Cursor
xglxCreateARGBCursor (ScreenPtr pScreen,
		      CursorPtr pCursor);

#endif

static Bool
xglxRealizeCursor (ScreenPtr pScreen,
		   CursorPtr pCursor)
{
    xglxCursorPtr pCursorPriv;
    XImage	  *ximage;
    Pixmap	  source, mask;
    XColor	  fgColor, bgColor;
    XlibGC	  xgc;
    unsigned long valuemask;
    XGCValues	  values;

    XGLX_SCREEN_PRIV (pScreen);

    valuemask = GCForeground | GCBackground;

    values.foreground = 1L;
    values.background = 0L;

    pCursorPriv = xalloc (sizeof (xglxCursorRec));
    if (!pCursorPriv)
	return FALSE;

    XGLX_SET_CURSOR_PRIV (pCursor, pScreen, pCursorPriv);

#ifdef ARGB_CURSOR
    if (pCursor->bits->argb)
    {
	pCursorPriv->cursor = xglxCreateARGBCursor (pScreen, pCursor);
	if (pCursorPriv->cursor)
	    return TRUE;
    }
#endif

    source = XCreatePixmap (xdisplay,
			    pScreenPriv->win,
			    pCursor->bits->width,
			    pCursor->bits->height,
			    1);

    mask = XCreatePixmap (xdisplay,
			  pScreenPriv->win,
			  pCursor->bits->width,
			  pCursor->bits->height,
			  1);

    xgc = XCreateGC (xdisplay, source, valuemask, &values);

    ximage = XCreateImage (xdisplay,
			   DefaultVisual (xdisplay, xscreen),
			   1, XYBitmap, 0,
			   (char *) pCursor->bits->source,
			   pCursor->bits->width,
			   pCursor->bits->height,
			   BitmapPad (xdisplay), 0);

    XPutImage (xdisplay, source, xgc, ximage,
	       0, 0, 0, 0, pCursor->bits->width, pCursor->bits->height);

    XFree (ximage);

    ximage = XCreateImage (xdisplay,
			   DefaultVisual (xdisplay, xscreen),
			   1, XYBitmap, 0,
			   (char *) pCursor->bits->mask,
			   pCursor->bits->width,
			   pCursor->bits->height,
			   BitmapPad (xdisplay), 0);

    XPutImage (xdisplay, mask, xgc, ximage,
	       0, 0, 0, 0, pCursor->bits->width, pCursor->bits->height);

    XFree (ximage);
    XFreeGC (xdisplay, xgc);

    fgColor.red   = pCursor->foreRed;
    fgColor.green = pCursor->foreGreen;
    fgColor.blue  = pCursor->foreBlue;

    bgColor.red   = pCursor->backRed;
    bgColor.green = pCursor->backGreen;
    bgColor.blue  = pCursor->backBlue;

    pCursorPriv->cursor =
	XCreatePixmapCursor (xdisplay, source, mask, &fgColor, &bgColor,
			     pCursor->bits->xhot, pCursor->bits->yhot);

    XFreePixmap (xdisplay, mask);
    XFreePixmap (xdisplay, source);

    return TRUE;
}

static Bool
xglxUnrealizeCursor (ScreenPtr pScreen,
		     CursorPtr pCursor)
{
    XGLX_CURSOR_PRIV (pCursor, pScreen);

    XFreeCursor (xdisplay, pCursorPriv->cursor);
    xfree (pCursorPriv);

    return TRUE;
}

static void
xglxRecolorCursor (ScreenPtr pScreen,
		   CursorPtr pCursor,
		   Bool	     displayed)
{
    XColor fgColor, bgColor;

    XGLX_CURSOR_PRIV (pCursor, pScreen);

    fgColor.red   = pCursor->foreRed;
    fgColor.green = pCursor->foreGreen;
    fgColor.blue  = pCursor->foreBlue;

    bgColor.red   = pCursor->backRed;
    bgColor.green = pCursor->backGreen;
    bgColor.blue  = pCursor->backBlue;

    XRecolorCursor (xdisplay, pCursorPriv->cursor, &fgColor, &bgColor);
}

static Bool
xglxSetCursorPosition (ScreenPtr pScreen,
		       int	 x,
		       int	 y,
		       Bool	 generateEvent)
{
    XGLX_SCREEN_PRIV (pScreen);

    XWarpPointer (xdisplay, pScreenPriv->win, pScreenPriv->win,
		  0, 0, 0, 0, x, y);

    return TRUE;
}

static Bool
xglxCloseScreen (int	   index,
		 ScreenPtr pScreen)
{
    glitz_drawable_t *drawable;

    XGLX_SCREEN_PRIV (pScreen);

    drawable = XGL_GET_SCREEN_PRIV (pScreen)->drawable;
    if (drawable)
	glitz_drawable_destroy (drawable);

    xglClearVisualTypes ();

    if (pScreenPriv->win)
	XDestroyWindow (xdisplay, pScreenPriv->win);

    if (pScreenPriv->colormap)
	XFreeColormap (xdisplay, pScreenPriv->colormap);

    XGL_SCREEN_UNWRAP (CloseScreen);
    xfree (pScreenPriv);

    return (*pScreen->CloseScreen) (index, pScreen);
}

static Bool
xglxCursorOffScreen (ScreenPtr *ppScreen, int *x, int *y)
{
    return FALSE;
}

static void
xglxCrossScreen (ScreenPtr pScreen, Bool entering)
{
}

static void
xglxWarpCursor (ScreenPtr pScreen, int x, int y)
{
    miPointerWarpCursor (pScreen, x, y);
}

miPointerScreenFuncRec xglxPointerScreenFuncs = {
    xglxCursorOffScreen,
    xglxCrossScreen,
    xglxWarpCursor
};

static Bool
xglxScreenInit (int	  index,
		ScreenPtr pScreen,
		int	  argc,
		char	  **argv)
{
    XSetWindowAttributes    xswa;
    XWMHints		    *wmHints;
    XSizeHints		    *normalHints;
    XClassHint		    *classHint;
    xglxScreenPtr	    pScreenPriv;
    XVisualInfo		    *vinfo;
    XEvent		    xevent;
    glitz_drawable_format_t *format;
    glitz_drawable_t	    *drawable;

    format = xglxScreenFormat;

    if (!xglxAllocatePrivates (pScreen))
	return FALSE;

    currentScreen = pScreen;

    pScreenPriv = XGLX_GET_SCREEN_PRIV (pScreen);

    pScreenPriv->root	    = RootWindow (xdisplay, xscreen);
    pScreenPriv->fullscreen = fullscreen;

    vinfo = glitz_glx_get_visual_info_from_format (xdisplay, xscreen, format);
    if (!vinfo)
    {
	ErrorF ("[%d] no visual info from format\n", index);
	return FALSE;
    }

    pScreenPriv->colormap =
	XCreateColormap (xdisplay, pScreenPriv->root, vinfo->visual,
			 AllocNone);

    if (XRRQueryExtension (xdisplay, &randrEvent, &randrError))
	randrExtension = TRUE;

    if (fullscreen)
    {
	xglScreenInfo.width    = DisplayWidth (xdisplay, xscreen);
	xglScreenInfo.height   = DisplayHeight (xdisplay, xscreen);
	xglScreenInfo.widthMm  = DisplayWidthMM (xdisplay, xscreen);
	xglScreenInfo.heightMm = DisplayHeightMM (xdisplay, xscreen);

	if (randrExtension)
	{
	    XRRScreenConfiguration *xconfig;
	    Rotation		   rotation;
	    XRRScreenSize	   *sizes;
	    int			   nSizes, currentSize;

	    xconfig	= XRRGetScreenInfo (xdisplay, pScreenPriv->root);
	    currentSize = XRRConfigCurrentConfiguration (xconfig, &rotation);
	    sizes	= XRRConfigSizes (xconfig, &nSizes);

	    xglScreenInfo.width    = sizes[currentSize].width;
	    xglScreenInfo.height   = sizes[currentSize].height;
	    xglScreenInfo.widthMm  = sizes[currentSize].mwidth;
	    xglScreenInfo.heightMm = sizes[currentSize].mheight;

	    XRRFreeScreenConfigInfo (xconfig);
	}
    }
    else if (xglScreenInfo.width == 0 || xglScreenInfo.height == 0)
    {
	xglScreenInfo.width  = XGLX_DEFAULT_SCREEN_WIDTH;
	xglScreenInfo.height = XGLX_DEFAULT_SCREEN_HEIGHT;
    }

    xswa.colormap = pScreenPriv->colormap;

    pScreenPriv->win =
	XCreateWindow (xdisplay, pScreenPriv->root, 0, 0,
		       xglScreenInfo.width, xglScreenInfo.height, 0,
		       vinfo->depth, InputOutput, vinfo->visual,
		       CWColormap, &xswa);

    XFree (vinfo);

    normalHints = XAllocSizeHints ();
    normalHints->flags      = PMinSize | PMaxSize | PSize;
    normalHints->min_width  = xglScreenInfo.width;
    normalHints->min_height = xglScreenInfo.height;
    normalHints->max_width  = xglScreenInfo.width;
    normalHints->max_height = xglScreenInfo.height;

    if (fullscreen)
    {
	normalHints->x = 0;
	normalHints->y = 0;
	normalHints->flags |= PPosition;
    }

    classHint = XAllocClassHint ();
    classHint->res_name = "xglx";
    classHint->res_class = "Xglx";

    wmHints = XAllocWMHints ();
    wmHints->flags = InputHint;
    wmHints->input = TRUE;

    Xutf8SetWMProperties (xdisplay, pScreenPriv->win, "Xglx", "Xglx", 0, 0,
			  normalHints, wmHints, classHint);

    XFree (wmHints);
    XFree (classHint);
    XFree (normalHints);

    drawable = glitz_glx_create_drawable_for_window (xdisplay, xscreen,
						     format, pScreenPriv->win,
						     xglScreenInfo.width,
						     xglScreenInfo.height);
    if (!drawable)
    {
	ErrorF ("[%d] couldn't create glitz drawable for window\n", index);
	return FALSE;
    }

    XSelectInput (xdisplay, pScreenPriv->win,
		  ButtonPressMask | ButtonReleaseMask |
		  KeyPressMask | KeyReleaseMask | EnterWindowMask |
		  PointerMotionMask | ExposureMask);

    XMapWindow (xdisplay, pScreenPriv->win);

    if (fullscreen)
    {
	XClientMessageEvent xev;

	memset (&xev, 0, sizeof (xev));

	xev.type = ClientMessage;
	xev.message_type = XInternAtom (xdisplay, "_NET_WM_STATE", FALSE);
	xev.display = xdisplay;
	xev.window = pScreenPriv->win;
	xev.format = 32;
	xev.data.l[0] = 1;
	xev.data.l[1] =
	    XInternAtom (xdisplay, "_NET_WM_STATE_FULLSCREEN", FALSE);

	XSendEvent (xdisplay, pScreenPriv->root, FALSE,
		    SubstructureRedirectMask, (XEvent *) &xev);
    }

    xglScreenInfo.drawable = drawable;

    if (!xglScreenInit (pScreen))
	return FALSE;

#ifdef GLXEXT
    if (!xglInitVisualConfigs (pScreen))
	return FALSE;
#endif

    XGL_SCREEN_WRAP (CloseScreen, xglxCloseScreen);

#ifdef ARGB_CURSOR
    if (!xglxARGBCursorSupport ())
	softCursor = TRUE;
#endif

    if (softCursor)
    {
	static char data = 0;
	XColor	    black, dummy;
	Pixmap	    bitmap;
	Cursor	    cursor;

	if (!XAllocNamedColor (xdisplay, pScreenPriv->colormap,
			       "black", &black, &dummy))
	    return FALSE;

	bitmap = XCreateBitmapFromData (xdisplay, pScreenPriv->win, &data,
					1, 1);
	if (!bitmap)
	    return FALSE;

	cursor = XCreatePixmapCursor (xdisplay, bitmap, bitmap, &black, &black,
				      0, 0);
	if (!cursor)
	    return FALSE;

	XDefineCursor (xdisplay, pScreenPriv->win, cursor);

	XFreeCursor (xdisplay, cursor);
	XFreePixmap (xdisplay, bitmap);
	XFreeColors (xdisplay, pScreenPriv->colormap, &black.pixel, 1, 0);

	miDCInitialize (pScreen, &xglxPointerScreenFuncs);
    }
    else
    {
	pScreen->ConstrainCursor   = xglxConstrainCursor;
	pScreen->CursorLimits      = xglxCursorLimits;
	pScreen->DisplayCursor     = xglxDisplayCursor;
	pScreen->RealizeCursor     = xglxRealizeCursor;
	pScreen->UnrealizeCursor   = xglxUnrealizeCursor;
	pScreen->RecolorCursor     = xglxRecolorCursor;
	pScreen->SetCursorPosition = xglxSetCursorPosition;
    }

    if (!xglFinishScreenInit (pScreen))
	return FALSE;

#ifdef RANDR
    if (!xglxRandRInit (pScreen))
	return FALSE;
#endif

    while (XNextEvent (xdisplay, &xevent))
	if (xevent.type == Expose)
	    break;

    return TRUE;
}

void
xglxInitOutput (ScreenInfo *pScreenInfo,
		int	   argc,
		char	   **argv)
{
    glitz_drawable_format_t *format, templ;
    int			    i;
    unsigned long	    mask;
    unsigned long	    extraMask[] = {
	GLITZ_FORMAT_DOUBLEBUFFER_MASK | GLITZ_FORMAT_ALPHA_SIZE_MASK,
	GLITZ_FORMAT_DOUBLEBUFFER_MASK,
	GLITZ_FORMAT_ALPHA_SIZE_MASK,
	0
    };

    xglClearVisualTypes ();

    xglSetPixmapFormats (pScreenInfo);

    if (!xdisplay)
    {
	char *name = xDisplayName;

	if (!name)
	    name = xglxInitXorg ();

	xdisplay = XOpenDisplay (name);
	if (!xdisplay)
	    FatalError ("can't open display: %s\n", name ? name : "NULL");

	xscreen = DefaultScreen (xdisplay);

	if (!xDisplayName)
	    XDefineCursor (xdisplay, RootWindow (xdisplay, xscreen),
			   XCreateFontCursor (xdisplay, XC_watch));
    }

    templ.samples          = 1;
    templ.doublebuffer     = 1;
    templ.color.fourcc     = GLITZ_FOURCC_RGB;
    templ.color.alpha_size = 8;

    mask = GLITZ_FORMAT_SAMPLES_MASK | GLITZ_FORMAT_FOURCC_MASK;

    for (i = 0; i < sizeof (extraMask) / sizeof (extraMask[0]); i++)
    {
	format = glitz_glx_find_window_format (xdisplay, xscreen,
					       mask | extraMask[i],
					       &templ, 0);
	if (format)
	    break;
    }

    if (!format)
	FatalError ("no visual format found");

    xglScreenInfo.depth =
	format->color.red_size   +
	format->color.green_size +
	format->color.blue_size;

    xglSetVisualTypes (xglScreenInfo.depth,
		       (1 << TrueColor),
		       format->color.red_size,
		       format->color.green_size,
		       format->color.blue_size);

    xglxScreenFormat = format;

    AddScreen (xglxScreenInit, argc, argv);
}

static Bool
xglxExposurePredicate (Display *xdisplay,
		       XEvent  *xevent,
		       char    *args)
{
    return (xevent->type == Expose);
}

static Bool
xglxNotExposurePredicate (Display *xdisplay,
			  XEvent  *xevent,
			  char	  *args)
{
    return (xevent->type != Expose);
}

static int
xglxWindowExposures (WindowPtr pWin,
		     pointer   pReg)
{
    ScreenPtr pScreen = pWin->drawable.pScreen;
    RegionRec ClipList;

    if (HasBorder (pWin))
    {
	REGION_INIT (pScreen, &ClipList, NullBox, 0);
	REGION_SUBTRACT (pScreen, &ClipList, &pWin->borderClip,
			 &pWin->winSize);
	REGION_INTERSECT (pScreen, &ClipList, &ClipList, (RegionPtr) pReg);
	(*pScreen->PaintWindowBorder) (pWin, &ClipList, PW_BORDER);
	REGION_UNINIT (pScreen, &ClipList);
    }

    REGION_INIT (pScreen, &ClipList, NullBox, 0);
    REGION_INTERSECT (pScreen, &ClipList, &pWin->clipList, (RegionPtr) pReg);
    (*pScreen->WindowExposures) (pWin, &ClipList, NullRegion);
    REGION_UNINIT (pScreen, &ClipList);

    return WT_WALKCHILDREN;
}

static void
xglxBlockHandler (pointer   blockData,
		  OSTimePtr pTimeout,
		  pointer   pReadMask)
{
    XEvent    X;
    RegionRec region;
    BoxRec    box;

    XGL_SCREEN_PRIV (currentScreen);

    while (XCheckIfEvent (xdisplay, &X, xglxExposurePredicate, NULL))
    {
	ScreenPtr pScreen = currentScreen;

	box.x1 = X.xexpose.x;
	box.y1 = X.xexpose.y;
	box.x2 = box.x1 + X.xexpose.width;
	box.y2 = box.y1 + X.xexpose.height;

	REGION_INIT (currentScreen, &region, &box, 1);

	WalkTree (pScreen, xglxWindowExposures, &region);

	REGION_UNINIT (pScreen, &region);
    }

    if (!xglSyncSurface (&pScreenPriv->pScreenPixmap->drawable))
	FatalError (XGL_SW_FAILURE_STRING);

    glitz_surface_flush (pScreenPriv->surface);
    glitz_drawable_flush (pScreenPriv->drawable);

    XFlush (xdisplay);
}

static void
xglxWakeupHandler (pointer blockData,
		   int     result,
		   pointer pReadMask)
{
    ScreenPtr pScreen = currentScreen;
    XEvent    X;
    xEvent    x;

    while (XCheckIfEvent (xdisplay, &X, xglxNotExposurePredicate, NULL))
    {
	switch (X.type) {
	case KeyPress:
	    x.u.u.type = KeyPress;
	    x.u.u.detail = X.xkey.keycode;
	    x.u.keyButtonPointer.time = lastEventTime = GetTimeInMillis ();
	    mieqEnqueue (&x);
	    break;
	case KeyRelease:
	    x.u.u.type = KeyRelease;
	    x.u.u.detail = X.xkey.keycode;
	    x.u.keyButtonPointer.time = lastEventTime = GetTimeInMillis ();
	    mieqEnqueue (&x);
	    break;
	case ButtonPress:
	    x.u.u.type = ButtonPress;
	    x.u.u.detail = X.xbutton.button;
	    x.u.keyButtonPointer.time = lastEventTime = GetTimeInMillis ();
	    mieqEnqueue (&x);
	    break;
	case ButtonRelease:
	    x.u.u.type = ButtonRelease;
	    x.u.u.detail = X.xbutton.button;
	    x.u.keyButtonPointer.time = lastEventTime = GetTimeInMillis ();
	    mieqEnqueue (&x);
	    break;
	case MotionNotify:
	    x.u.u.type = MotionNotify;
	    x.u.u.detail = 0;
	    x.u.keyButtonPointer.rootX = X.xmotion.x;
	    x.u.keyButtonPointer.rootY = X.xmotion.y;
	    x.u.keyButtonPointer.time = lastEventTime = GetTimeInMillis ();
	    miPointerAbsoluteCursor (X.xmotion.x, X.xmotion.y, lastEventTime);
	    mieqEnqueue (&x);
	    break;
	case EnterNotify:
	    if (X.xcrossing.detail != NotifyInferior) {
		if (pScreen) {
		    NewCurrentScreen (pScreen, X.xcrossing.x, X.xcrossing.y);
		    x.u.u.type = MotionNotify;
		    x.u.u.detail = 0;
		    x.u.keyButtonPointer.rootX = X.xcrossing.x;
		    x.u.keyButtonPointer.rootY = X.xcrossing.y;
		    x.u.keyButtonPointer.time = lastEventTime =
			GetTimeInMillis ();
		    mieqEnqueue (&x);
		}
	    }
	    break;
	default:
	    break;
	}
    }
}

static void
xglxBell (int	       volume,
	  DeviceIntPtr pDev,
	  pointer      ctrl,
	  int	       cls)
{
  XBell (xdisplay, volume);
}

static void
xglxKbdCtrl (DeviceIntPtr pDev,
	     KeybdCtrl    *ctrl)
{
    unsigned long    valueMask;
    XKeyboardControl values;
    int		     i;

    valueMask = KBKeyClickPercent | KBBellPercent | KBBellPitch |
	KBBellDuration | KBAutoRepeatMode;

    values.key_click_percent = ctrl->click;
    values.bell_percent	     = ctrl->bell;
    values.bell_pitch	     = ctrl->bell_pitch;
    values.bell_duration     = ctrl->bell_duration;
    values.auto_repeat_mode  = (ctrl->autoRepeat) ? AutoRepeatModeOn :
	AutoRepeatModeOff;

    XChangeKeyboardControl (xdisplay, valueMask, &values);

    valueMask = KBLed | KBLedMode;

    for (i = 1; i <= 32; i++)
    {
	values.led = i;
	values.led_mode = (ctrl->leds & (1 << (i - 1))) ? LedModeOn :
	    LedModeOff;

	XChangeKeyboardControl (xdisplay, valueMask, &values);
    }
}

static int
xglxKeybdProc (DeviceIntPtr pDevice,
	       int	    onoff)
{
    Bool      ret = FALSE;
    DevicePtr pDev = (DevicePtr) pDevice;

    if (!pDev)
	return BadImplementation;

    switch (onoff) {
    case DEVICE_INIT: {
      XModifierKeymap *xmodMap;
      KeySym	      *xkeyMap;
      int	      minKeyCode, maxKeyCode, mapWidth, i, j;
      KeySymsRec      xglxKeySyms;
      CARD8	      xglxModMap[256];
      XKeyboardState  values;

#ifdef _XSERVER64
      KeySym64	      *xkeyMap64;
      int	      len;
#endif

#ifdef XKB
      Bool	      xkbExtension = FALSE;
      int	      xkbOp, xkbEvent, xkbError, xkbMajor, xkbMinor;
#endif

      if (pDev != LookupKeyboardDevice ())
	  return !Success;

      xmodMap = XGetModifierMapping (xdisplay);

      XDisplayKeycodes (xdisplay, &minKeyCode, &maxKeyCode);

#ifdef _XSERVER64
      xkeyMap64 = XGetKeyboardMapping (xdisplay,
				       minKeyCode,
				       maxKeyCode - minKeyCode + 1,
				       &mapWidth);

      len = (maxKeyCode - minKeyCode + 1) * mapWidth;
      xkeyMap = (KeySym *) xalloc (len * sizeof (KeySym));
      for (i = 0; i < len; ++i)
	  xkeyMap[i] = xkeyMap64[i];

      XFree (xkeyMap64);
#else
      xkeyMap = XGetKeyboardMapping (xdisplay,
				     minKeyCode,
				     maxKeyCode - minKeyCode + 1,
				     &mapWidth);
#endif

      memset (xglxModMap, 0, 256);

      for (j = 0; j < 8; j++)
      {
	  for (i = 0; i < xmodMap->max_keypermod; i++)
	  {
	      CARD8 keyCode;

	      keyCode = xmodMap->modifiermap[j * xmodMap->max_keypermod + i];
	      if (keyCode)
		  xglxModMap[keyCode] |= 1 << j;
	  }
      }

      XFreeModifiermap (xmodMap);

      xglxKeySyms.minKeyCode = minKeyCode;
      xglxKeySyms.maxKeyCode = maxKeyCode;
      xglxKeySyms.mapWidth   = mapWidth;
      xglxKeySyms.map	     = xkeyMap;

#ifdef XKB
      if (!noXkbExtension)
	  xkbExtension = XkbQueryExtension (xdisplay,
					    &xkbOp, &xkbEvent, &xkbError,
					    &xkbMajor, &xkbMinor);

      if (xkbExtension)
      {
	  XkbDescPtr desc;
	  char	     *rules, *model, *layout, *variants, *options;

	  desc = XkbGetKeyboard (xdisplay,
				 XkbGBN_AllComponentsMask,
				 XkbUseCoreKbd);

	  if (desc && desc->geom)
	  {
	      XkbComponentNamesRec names;
	      FILE		   *file;

	      rules    = XKB_DFLT_RULES_FILE;
	      model    = XKB_DFLT_KB_MODEL;
	      layout   = XKB_DFLT_KB_LAYOUT;
	      variants = XKB_DFLT_KB_VARIANT;
	      options  = XKB_DFLT_KB_OPTIONS;

	      XkbGetControls (xdisplay, XkbAllControlsMask, desc);

	      memset (&names, 0, sizeof (XkbComponentNamesRec));

	      XkbSetRulesDflts (rules, model, layout, variants, options);

	      ret = XkbInitKeyboardDeviceStruct ((pointer) pDev,
						 &names,
						 &xglxKeySyms,
						 xglxModMap,
						 xglxBell,
						 xglxKbdCtrl);

	      if (ret)
		  XkbDDXChangeControls ((pointer) pDev, desc->ctrls,
					desc->ctrls);

	      XkbFreeKeyboard (desc, 0, False);
	  }
      }
#endif

      if (!ret)
      {
	  XGetKeyboardControl (xdisplay, &values);

	  memmove (defaultKeyboardControl.autoRepeats,
		   values.auto_repeats, sizeof (values.auto_repeats));

	  ret = InitKeyboardDeviceStruct (pDev,
					  &xglxKeySyms,
					  xglxModMap,
					  xglxBell,
					  xglxKbdCtrl);
      }

#ifdef _XSERVER64
      xfree (xkeyMap);
#else
      XFree (xkeyMap);
#endif

      if (!ret)
	  return BadImplementation;

    } break;
    case DEVICE_ON:
	pDev->on = TRUE;
	break;
    case DEVICE_OFF:
    case DEVICE_CLOSE:
	pDev->on = FALSE;
	break;
    }

    return Success;
}

Bool
xglxLegalModifier (unsigned int key,
		   DeviceIntPtr pDev)
{
    return TRUE;
}

void
xglxProcessInputEvents (void)
{
    mieqProcessInputEvents ();
    miPointerUpdate ();
}

void
xglxInitInput (int  argc,
	       char **argv)
{
    DeviceIntPtr pKeyboard, pPointer;

    pPointer  = AddInputDevice (xglMouseProc, TRUE);
    pKeyboard = AddInputDevice (xglxKeybdProc, TRUE);

    RegisterPointerDevice (pPointer);
    RegisterKeyboardDevice (pKeyboard);

    miRegisterPointerDevice (screenInfo.screens[0], pPointer);
    mieqInit (&pKeyboard->public, &pPointer->public);

    AddEnabledDevice (XConnectionNumber (xdisplay));

    RegisterBlockAndWakeupHandlers (xglxBlockHandler,
				    xglxWakeupHandler,
				    NULL);
}

void
xglxUseMsg (void)
{
    ErrorF ("-screen WIDTH[/WIDTHMM]xHEIGHT[/HEIGHTMM] "
	    "specify screen characteristics\n");
    ErrorF ("-fullscreen            run fullscreen\n");
    ErrorF ("-display string        display name of the real server\n");
    ErrorF ("-softcursor            force software cursor\n");

    if (!xDisplayName)
	xglxUseXorgMsg ();
}

int
xglxProcessArgument (int  argc,
		     char **argv,
		     int  i)
{
    static Bool checkDisplayName = FALSE;

    if (!checkDisplayName)
    {
	char *display = ":0";
	int  j;

	for (j = i; j < argc; j++)
	{
	    if (!strcmp (argv[j], "-display"))
	    {
		if (++j < argc)
		    xDisplayName = argv[j];

		break;
	    }
	    else if (argv[j][0] == ':')
	    {
		display = argv[j];
	    }
	}

	if (!xDisplayName)
	    xDisplayName = getenv ("DISPLAY");

	if (xDisplayName)
	{
	    int n;

	    n = strspn (xDisplayName, ":0123456789");
	    if (strncmp (xDisplayName, display, n) == 0)
		xDisplayName = 0;
	}

	if (xDisplayName)
	    fullscreen = FALSE;

	checkDisplayName = TRUE;
    }

    if (!strcmp (argv[i], "-screen"))
    {
	if ((i + 1) < argc)
	{
	    xglParseScreen (argv[i + 1]);
	}
	else
	    return 1;

	return 2;
    }
    else if (!strcmp (argv[i], "-fullscreen"))
    {
	fullscreen = TRUE;
	return 1;
    }
    else if (!strcmp (argv[i], "-display"))
    {
	if (++i < argc)
	    return 2;

	return 0;
    }
    else if (!strcmp (argv[i], "-softcursor"))
    {
	softCursor = TRUE;
	return 1;
    }
    else if (!xDisplayName)
    {
	return xglxProcessXorgArgument (argc, argv, i);
    }

    return 0;
}

void
xglxAbort (void)
{
    xglxAbortXorg ();
}

void
xglxGiveUp (void)
{
    AbortDDX ();
}

void
xglxOsVendorInit (void)
{
}

#ifdef ARGB_CURSOR

#include <X11/extensions/Xrender.h>

static Bool
xglxARGBCursorSupport (void)
{
    int renderMajor, renderMinor;

    if (!XRenderQueryVersion (xdisplay, &renderMajor, &renderMinor))
	renderMajor = renderMinor = -1;

    return (renderMajor > 0 || renderMinor > 4);
}

static Cursor
xglxCreateARGBCursor (ScreenPtr pScreen,
		      CursorPtr pCursor)
{
    Pixmap	      xpixmap;
    XlibGC	      xgc;
    XImage	      *ximage;
    XRenderPictFormat *xformat;
    Picture	      xpicture;
    Cursor	      cursor;

    XGLX_SCREEN_PRIV (pScreen);

    xpixmap = XCreatePixmap (xdisplay,
			     pScreenPriv->win,
			     pCursor->bits->width,
			     pCursor->bits->height,
			     32);

    xgc = XCreateGC (xdisplay, xpixmap, 0, NULL);

    ximage = XCreateImage (xdisplay,
			   DefaultVisual (xdisplay, xscreen),
			   32, ZPixmap, 0,
			   (char *) pCursor->bits->argb,
			   pCursor->bits->width,
			   pCursor->bits->height,
			   32, pCursor->bits->width * 4);

    XPutImage (xdisplay, xpixmap, xgc, ximage,
	       0, 0, 0, 0, pCursor->bits->width, pCursor->bits->height);

    XFree (ximage);
    XFreeGC (xdisplay, xgc);

    xformat = XRenderFindStandardFormat (xdisplay, PictStandardARGB32);
    xpicture = XRenderCreatePicture (xdisplay, xpixmap, xformat, 0, 0);

    cursor = XRenderCreateCursor (xdisplay, xpicture,
				  pCursor->bits->xhot,
				  pCursor->bits->yhot);

    XRenderFreePicture (xdisplay, xpicture);
    XFreePixmap (xdisplay, xpixmap);

    return cursor;
}

#endif