// -*-C++-*- 

/*  src/platform/Xq.cpp  */

/*
 * Author: Philogelos A. <Philogelos@yahoo.com>
 * Maintainer: Philogelos A.
 * Keywords: C++, library, containers
 *
 * Copyright (C) 1999 Philogelos A.
 *
 * This file is part of Quercus Robusta.
 *
 * Quercus Robusta is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this software; see the file COPYING.LIB.  If not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 */



/* $Id$ */

#if !defined(_INLINE)
static char cvsid[] = "@(#)$Id$";
static char debugFileId[] = __FILE__;
#endif

#include "platform/Xq.hpp"

#if defined( USE_XQ )

#include "exceptions/OSError.hpp"
#include "String.hpp"
#include "Debug.hpp"
#include "OGuard.hpp"
#include "LinkManager.hpp"

#include "platform/threads/ThreadsSystem.hpp"
#include "platform/threads/ThreadFactory.hpp"
#include "platform/threads/Mutex.hpp"
#include "platform/threads/RecMutex.hpp"
#include "platform/Platform.hpp"

#include "Backtrace.hpp"

#include <X11/Xproto.h>
#include <X11/cursorfont.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <sched.h>

#if defined( USE_XPM )
#include <X11/xpm.h>
#endif

extern "C" void *memset( void *s, int c, size_t n );

#define __USE_BSD
#define __EXTENSIONS__
/* XXX why on the earth M_PI in protected by some conditionals in math.h? */
#include <math.h>

#include <sys/time.h>
#include <sys/types.h>

#if !defined( XQ_USE_X11_MUTEX ) && !defined( XQ_USE_XINITTHREADS )
#warning "One ought to define XQ_USE_X11_MUTEX or XQ_USE_XINITTHREADS"
#endif

#if defined( USE_THREADS ) && defined( XQ_USE_X11_MUTEX )
#include "platform/threads/MGuard.hpp"
 
#define LOCK_X11_DISPLAY( display )  \
   MGuard _x11Serial( display -> getX11Serialiser(), \
      ( Xq::Display * ) display )

#define LOCK_X11 LOCK_X11_DISPLAY( getDisplay() ) 

#else

#define LOCK_X11 do {} while( false )
#define LOCK_X11_DISPLAY( display ) do {} while( false )

#endif

Xq::XqObject::XqObject( Xq::Display *aDisplay )
{
  preC_( aDisplay != ( Xq::Display * ) NULL );
  setDisplay( aDisplay );
}

Xq::XqObject::~XqObject()
{
  FDONE( display );
}

void Xq::XqObject::setDisplay( Display *aDisplay )
{
  FMOVE( display, aDisplay );
}

Xq::Display *Xq::XqObject::getDisplay() const
{
  return display;
}

String Xq::XqObject::toString() const
{
  return Object::toString() + 
	" on display: " + getDisplay() -> toString();
}

String Xq::XqObject::getClassName() const
{
  return "XqObject";
}


Xq::XIDObject::XIDObject( Display *aDisplay, XID anId ) :
  Xq::XqObject( aDisplay )
{
  id = anId;
}

Xq::XIDObject::~XIDObject()
{}

void Xq::XIDObject::setId( XID anId )
{
  id = anId;
}

XID Xq::XIDObject::getId() const
{
  return id;
}

String Xq::XIDObject::toString() const
{
  return XqObject::toString() +
	String( " id: " ) + String( getId(), 16 );
}

String Xq::XIDObject::getClassName() const
{
  return "XIDObject";
}

Xq::XqEventSource::XqEventSource()
{}

static int ( *oldXIOErrorHandler )( struct _XDisplay *aDisplay ) = NULL;
static int ( *oldXErrorHandler )( struct _XDisplay *aDisplay, 
								  XErrorEvent *anEvent ) = NULL;

static int XqXIOErrorHandler( struct _XDisplay *aDisplay )
{
  Backtrace trace;
  static const char *fatalXError = "Fatal X protocol error";

  trace.fill();
  DLOG( fatalXError );
  DLOGOBJECT( "Backtrace %s", &trace );

  if( oldXIOErrorHandler != NULL )
	{
	  oldXIOErrorHandler( aDisplay );
	}
  Debug::getLogger() -> exception( new Xq::XFatalException( aDisplay ) );
  Debug::getLogger() -> panic( fatalXError );
  return 0;
}

static int XqXErrorHandler( struct _XDisplay *aDisplay, 
							XErrorEvent *anEvent )
{
  Backtrace trace;
  static const char *xErrorMsg = "X protocol error";

  trace.fill();
  DLOG( xErrorMsg );
  DLOGOBJECT( "Backtrace %s", &trace );

  if( oldXErrorHandler != NULL )
	{
	  oldXErrorHandler( aDisplay, anEvent );
	}

  Debug::getLogger() -> exception( new Xq::XException( aDisplay, anEvent ) );
  Debug::getLogger() -> panic( xErrorMsg );
  return 0;
}

Xq::Display::Display( struct _XDisplay *aDisplay )
{
#if defined( USE_THREADS ) && defined( XQ_USE_X11_MUTEX )
  FINIT( x11Serializer, THR_SYSTEM -> recMutex() );
  setProtector( x11Serializer );
#endif

  LOCK_X11_DISPLAY( this );

  x11Data = aDisplay;
  FINIT( rootWindow, 
		 new Xq::Window( this, RootWindow( getData(), screen() ) ) );

  oldXErrorHandler = ::XSetErrorHandler( XqXErrorHandler );
  oldXIOErrorHandler = ::XSetIOErrorHandler( XqXIOErrorHandler );

  doneWithEvents = false;
}

Xq::Display::~Display()
{
#if defined( USE_THREADS ) && defined( XQ_USE_X11_MUTEX )
  FDONE( x11Serializer );
#endif
  FDONE( rootWindow );
  close();
}

void Xq::Display::close()
{
  LOCK_X11_DISPLAY( this );
  ::XCloseDisplay( getData() );
  doneWithEvents = true;
}

int Xq::Display::connectionNumber() const
{
  return ConnectionNumber( getData() );
}

int Xq::Display::screen() const
{
  return DefaultScreen( getData() );
}

Xq::Window *Xq::Display::getRootWindow() const
{
  return rootWindow;
}

Xq::Coord Xq::Display::getWidth() const
{
  LOCK_X11_DISPLAY( this );
  return DisplayWidth( getData(), screen() );
}

Xq::Coord Xq::Display::getHeight() const
{
  LOCK_X11_DISPLAY( this );
  return DisplayHeight( getData(), screen() );
}

XID Xq::Display::defaultColormap() const
{
  LOCK_X11_DISPLAY( this );
  return DefaultColormap( getData(), screen() );
}

int Xq::Display::blackPixel() const
{
  LOCK_X11_DISPLAY( this );
  return BlackPixel( getData(), screen() );
}

int Xq::Display::whitePixel() const
{
  LOCK_X11_DISPLAY( this );
  return WhitePixel( getData(), screen() );
}

int Xq::Display::defaultDepth() const
{
  LOCK_X11_DISPLAY( this );
  return DefaultDepth( getData(), screen() );
}

Visual *Xq::Display::defaultVisual( int aScreen ) const
{
  LOCK_X11_DISPLAY( this );
  return DefaultVisual( getData(), aScreen );
}

Xq::Display *Xq::Display::open()
{
  return open( NULL );
}

Xq::Display *Xq::Display::open( const char *aName )
{
  struct _XDisplay *data;

  data = ::XOpenDisplay( aName );
  if( data == ( struct _XDisplay * ) NULL )
	{
	  DLOG
		( "Cannot XOpenDisplay( \"%s\" )\n",
		  XDisplayName( aName ) );
	  throw new XException( "Cannot XOpenDisplay()" );
	}
  return new Xq::Display( data );
}

void Xq::Display::setData( struct _XDisplay *aDisplay )
{
  x11Data = aDisplay;
}

struct _XDisplay *Xq::Display::getData() const
{
  return x11Data;
}

#if defined( USE_THREADS ) && defined( XQ_USE_X11_MUTEX )
Mutex *Xq::Display::getX11Serialiser() const
{
  return x11Serializer;
}
#endif

Aq::AqEvent *Xq::Display::getNextTypedElement()
{
  XEvent event;

  LOCK_X11_DISPLAY( this );
  ::XNextEvent( getData(), &event );
  return XqEvent::create( this, &event );
}

String Xq::Display::getClassName() const
{
  return "Display";
}

Xq::Drawable::Drawable( Display *aDisplay, XID anId ) :
  Xq::XIDObject( aDisplay, anId ),
  Xq::XqObject( aDisplay ),
  Aq::Drawable( X11WindowSystem::x11WindowSystem )
{}

void Xq::Drawable::copyTo( Xq::Drawable *aDest, Xq::GC *aGC, 
						   Xq::Coord aSrcX, Xq::Coord aSrcY, 
						   int aWidth, int aHeight,
						   Xq::Coord aDstX, Xq::Coord aDstY )
{
  preC_( aDest != ( Xq::Drawable * ) NULL );
  preC_( aGC != ( Xq::GC * ) NULL );
  preC_( aSrcX >= 0 );
  preC_( aSrcY >= 0 );
  preC_( aDstX >= 0 );
  preC_( aDstY >= 0 );
  preC_( aWidth >= 0 );
  preC_( aHeight >= 0 );
  
  LOCK_X11;
  ::XCopyArea( getDisplay() -> getData(),
			   getId(), aDest -> getId(), aGC -> getData(), 
			   aSrcX, aSrcY, aWidth, aHeight, aDstX, aDstY );
}

void Xq::Drawable::drawPoint( Xq::GC *aGC, Xq::Coord aX, Xq::Coord aY )
{
  preC_( aGC != NULL );

  LOCK_X11;
  ::XDrawPoint( getDisplay() -> getData(),
				getId(), aGC -> getData(), aX, aY );
}

void Xq::Drawable::drawLine( Xq::GC *aGC, 
					   Xq::Coord aStartX, Xq::Coord aStartY,
					   Xq::Coord anEndX, Xq::Coord anEndY )
{
  preC_( aGC != NULL );

  LOCK_X11;
  ::XDrawLine( getDisplay() -> getData(),
			   getId(), aGC -> getData(), aStartX, aStartY, anEndX, anEndY );
}

void Xq::Drawable::fillRectangle( Xq::GC *aGC, Xq::Coord aX, Xq::Coord aY, 
								  int aWidth, int aHeight )
{
  preC_( aGC != NULL );

  LOCK_X11;
  ::XFillRectangle( getDisplay() -> getData(),
					getId(), aGC -> getData(),
					aX, aY, aWidth, aHeight );
}

void Xq::Drawable::drawString( Xq::GC *aGC, Xq::Coord aX, Xq::Coord aY, 
							   String aString )
{
  char *string;

  string = aString.getChars();
  drawCString( aGC, aX, aY, string );
  delete []string;
}

void Xq::Drawable::drawCString( Xq::GC *aGC, Xq::Coord aX, Xq::Coord aY, 
								char *aString )
{
  preC_( aGC != NULL );

  LOCK_X11;
  ::XDrawString( getDisplay() -> getData(), getId(), aGC -> getData(),
				 aX, aY, aString, PLAF -> strlen( aString ) );
}

Xq::Window::Window( Display *aDisplay, XID anId ) :
  Xq::Drawable( aDisplay, anId ),
  Xq::XIDObject( aDisplay, anId ),
  Xq::XqObject( aDisplay ),
  Aq::Window( X11WindowSystem::x11WindowSystem )
{}

Xq::Window::~Window()
{
  LOCK_X11;
  ::XDestroyWindow( getDisplay() -> getData(), getId() );
}

void Xq::Window::map()
{
  LOCK_X11;
  ::XMapWindow( getDisplay() -> getData(), getId() );
}

void Xq::Window::unmap()
{
  LOCK_X11;
  ::XUnmapWindow( getDisplay() -> getData(), getId() );
}

void Xq::Window::clear()
{
  LOCK_X11;
  ::XClearWindow( getDisplay() -> getData(), getId() );
}

void Xq::Window::selectInput( long aMask )
{
  LOCK_X11;
  ::XSelectInput( getDisplay() -> getData(), getId(), aMask );
}

void Xq::Window::setSize( int aWidth, int aHeight )
{
  preC_( aWidth > 0 );
  preC_( aHeight > 0 );

  LOCK_X11;
  ::XResizeWindow( getDisplay() -> getData(), getId(), aWidth, aHeight );
}

void Xq::Window::setSizeHints( int aMinWidth, int aMinHeight,
							   int aMaxWidth, int aMaxHeight,
							   int aBaseWidth, int aBaseHeight,
							   int aWidthInc, int aHeightInc )
{
  XSizeHints hints;

  if( aMinWidth > 0 )
	{
	  test_( aMinHeight > 0 );

	  hints.min_width = aMinWidth;
	  hints.min_height = aMinHeight;
	  hints.flags |= PMinSize;
	}
  if( aMaxWidth > 0 )
	{
	  test_( aMaxHeight > 0 );

	  hints.max_width = aMaxWidth;
	  hints.max_height = aMaxHeight;
	  hints.flags |= PMaxSize;
	}
  if( aBaseWidth > 0 )
	{
	  test_( aBaseHeight > 0 );

	  hints.base_width = aBaseWidth;
	  hints.base_height = aBaseHeight;
	  hints.flags |= PBaseSize;
	}
  if( aWidthInc > 0 )
	{
	  test_( aHeightInc > 0 );

	  hints.width_inc = aWidthInc;
	  hints.height_inc = aHeightInc;
	  hints.flags |= PResizeInc;
	}
  ::XSetWMNormalHints( getDisplay() -> getData(), 
					   getId(), &hints );
}

void Xq::Window::setName( String aName )
{
  char *name;

  name = aName.getChars();
  LOCK_X11;
  ::XStoreName( getDisplay() -> getData(), 
				getId(), name );
  delete []name;
}

void Xq::Window::setClass( String aName, String aClass )
{
  char *appName;
  char *appClass;
  XClassHint hints;

  appName = aName.getChars();
  appClass = aClass.getChars();

  hints.res_name = appName;
  hints.res_class = appClass;
  LOCK_X11;
  XSetClassHint( getDisplay() -> getData(),
				 getId(), &hints );
  delete []appName;
  delete []appClass;
}

Aq::EventSource *Xq::Window::getEventSource() const
{
  return getXEventSource();
}

Xq::WindowEventSource *Xq::Window::getXEventSource() const
{
  return new Xq::WindowEventSource( this );
}

Xq::Window *Xq::Window::createSimple( Xq::Display *aDisplay,
									  Xq::Window *aParent,
									  int aX, int aY, 
									  int aWidth, int aHeight, 
									  int aBorderWidth,
									  int aForePixel, int aBackPixel )
{
  preC_( aDisplay != ( Xq::Display * ) NULL );
  preC_( aParent != ( Xq::Window * ) NULL );

  LOCK_X11_DISPLAY( aDisplay );
  return new Xq::Window
	( aDisplay, 
	  ::XCreateSimpleWindow( aDisplay -> getData(), aParent -> getId(),
							 aX, aY, aWidth, aHeight, aBorderWidth,
							 aForePixel, aBackPixel ) );
}

void Xq::Window::setCursor( Xq::Cursor *aCursor )
{
  preC_( aCursor != NULL );
  
  OGuard _cursor( aCursor, this );
  LOCK_X11;
  ::XDefineCursor( getDisplay() -> getData(), getId(), aCursor -> getId() );
}

void Xq::Window::unsetCursor()
{
  LOCK_X11;
  ::XUndefineCursor( getDisplay() -> getData(), getId() );
}

String Xq::Window::getClassName() const
{
  return "Window";
}

Xq::WindowEventSource::WindowEventSource( const Xq::Window *aWindow ) :
  window( aWindow )
{
  preC_( window != ( Xq::Window * ) NULL );
  OREG( window );
}

Xq::WindowEventSource::~WindowEventSource()
{
  FDONE( window );
}

Xq::Window *Xq::WindowEventSource::getWindow() const
{
  return ( Xq::Window * ) window;
}

Aq::AqEvent *Xq::WindowEventSource::getNextTypedElement()
{
  /* busy loop! (somewhat relieved by ::select() ) XXX */
  while( true )
	{
	  XEvent event;

#if defined( XQ_XCHECKEVENT_HACK )

	  fd_set checkXConnection;
	  fd_set writeXConnection;
	  fd_set errorXConnection;
	  int result;
	  	  
	  FD_ZERO( &checkXConnection );
	  FD_ZERO( &writeXConnection );
	  FD_ZERO( &errorXConnection );

	  FD_SET( getWindow() -> getDisplay() -> connectionNumber(), 
			  &checkXConnection );
	  FD_SET( getWindow() -> getDisplay() -> connectionNumber(), 
			  &writeXConnection );
	  FD_SET( getWindow() -> getDisplay() -> connectionNumber(), 
			  &errorXConnection );
	  
	  /* magic, more magic! */
	  result = ::select
		( getWindow() -> getDisplay() -> connectionNumber() + 1,
		  &checkXConnection, &writeXConnection, &errorXConnection, NULL );
	  //	  DLOG( "::select()" );
	  THR_SYSTEM -> sleep( 0, XQ_XCHECKEVENT_HACK_TIMEOUT );
	  if( result == -1 )
		{
		  throw new Errno
			( "Xq::WindowEventSource::getNextTypedElement(): select()", null );
		}
#endif	  
	  LOCK_X11_DISPLAY( getWindow() -> getDisplay() );
	  if( ::XCheckWindowEvent
		  ( getWindow() -> getDisplay() -> getData(), 
			getWindow() -> getId(),
			ExposureMask | KeyPressMask | ButtonPressMask | 
			StructureNotifyMask |
			KeyReleaseMask | ButtonReleaseMask | EnterWindowMask | 
			LeaveWindowMask |  PointerMotionMask | 
			/* PointerMotionHintMask | */ 
			Button1MotionMask | Button2MotionMask |Button3MotionMask | 
			Button4MotionMask | Button5MotionMask | ButtonMotionMask | 
			KeymapStateMask | ExposureMask |
			VisibilityChangeMask | ResizeRedirectMask | 
			SubstructureNotifyMask |
			SubstructureRedirectMask | FocusChangeMask | 
			PropertyChangeMask |
			ColormapChangeMask | OwnerGrabButtonMask, &event ) )
		{
#if defined( XQ_XCHECKEVENT_HACK )
		  sched_yield();
#endif	  
		  return XqEvent::create( getWindow() -> getDisplay(), &event,
								  getWindow() );
		}
#if defined( XQ_XCHECKEVENT_HACK )
	  sched_yield();
#endif	  
	}
}

String Xq::WindowEventSource::getClassName() const
{
  return "WindowEventSource";
}

Xq::GC::GC( Xq::Display *aDisplay, struct _XGC *aGC ) :
  XqObject( aDisplay ),
  Aq::GraphicContext( X11WindowSystem::x11WindowSystem )
{
  x11Data = aGC;
}

Xq::GC::~GC()
{
  LOCK_X11;
  ::XFreeGC( getDisplay() -> getData(), x11Data );
}

struct _XGC *Xq::GC::getData() const
{
  return x11Data;
}

void Xq::GC::setData( struct _XGC *aGC )
{
  if( x11Data != ( struct _XGC * ) NULL )
	{
	  LOCK_X11;
	  ::XFreeGC( getDisplay() -> getData(), x11Data );
	}
  x11Data = aGC;
}

void Xq::GC::setBackground( Xq::Color *aColor )
{
  preC_( aColor != ( Xq::Color * ) NULL );
  OGuard _color( aColor, this );
  LOCK_X11;
  ::XSetBackground( getDisplay() -> getData(), 
					getData(), aColor -> getData() -> pixel );
}

void Xq::GC::setForeground( Xq::Color *aColor )
{
  preC_( aColor != ( Xq::Color * ) NULL );
  OGuard _color( aColor, this );
  LOCK_X11;
  ::XSetForeground( getDisplay() -> getData(), 
					getData(), aColor -> getData() -> pixel );
}

void Xq::GC::setFont( Xq::Font *aFont )
{
  preC_( aFont != ( Xq::Font * ) NULL );

  OGuard _font( aFont, this );
  LOCK_X11;
  ::XSetFont( getDisplay() -> getData(), getData(), 
			  aFont -> getData() -> fid );

}

void Xq::GC::setClipMask( Xq::Pixmap *aPixmap )
{
  preC_( aPixmap != NULL );

  OGuard _pixmap( aPixmap, this );

  LOCK_X11;
  ::XSetClipMask( getDisplay() -> getData(), 
				  getData(), aPixmap -> getId() );
}

Xq::Color *Xq::GC::getBackground() const
{
  XGCValues returnValues;

  LOCK_X11;
  ::XGetGCValues( getDisplay() -> getData(), 
				  getData(), GCBackground, &returnValues );
  return Xq::Color::create( getDisplay(), returnValues.background );
}

Xq::Color *Xq::GC::getForeground() const
{
  XGCValues returnValues;

  LOCK_X11;
  ::XGetGCValues( getDisplay() -> getData(), 
				  getData(), GCForeground, &returnValues );
  return Xq::Color::create( getDisplay(), returnValues.foreground );
}

void Xq::GC::copyTo( Xq::GC *aGC ) const
{
  preC_( aGC != NULL );

  LOCK_X11;
  ::XCopyGC( getDisplay() -> getData(), getData(),
			 GCFunction | GCPlaneMask |
			 GCForeground | GCBackground | GCLineWidth | GCLineStyle |
			 GCCapStyle | GCJoinStyle | GCFillStyle | GCFillRule | GCTile |
			 GCStipple | GCTileStipXOrigin | GCTileStipYOrigin | GCFont |
			 GCSubwindowMode | GCGraphicsExposures | GCClipXOrigin | 
			 GCClipYOrigin | GCDashOffset | GCArcMode,
			 aGC -> getData() );
}

void Xq::GC::setGraphicsExposures( boolean aFlag )
{
  LOCK_X11;
  ::XSetGraphicsExposures( getDisplay() -> getData(),
						   getData(), aFlag );
}

Xq::GC *Xq::GC::create( Xq::Display *aDisplay, Xq::Drawable *aDrawable )
{
  preC_( aDisplay != ( Xq::Display * ) NULL );
  preC_( aDrawable != ( Xq::Drawable * ) NULL );

  struct _XGC *data;

  unsigned int valueMask;
  XGCValues values;

  valueMask = GCPlaneMask;
  values.plane_mask = AllPlanes;

  LOCK_X11_DISPLAY( aDisplay );
  data = ::XCreateGC( aDisplay -> getData(), 
					  aDrawable -> getId(), valueMask, &values );
  //  DLOG( "XCreateGC: %p", data );
  return new Xq::GC( aDisplay, data );
}


Xq::Font::Font( Display *aDisplay, XFontStruct *aData ) :
  Xq::XqObject( aDisplay )
{
  x11Data = aData;
}

Xq::Font::~Font()
{
  LOCK_X11;
  XFreeFont( getDisplay() -> getData(), getData() );
}

XFontStruct *Xq::Font::getData() const
{
  return x11Data;
}

void Xq::Font::setData( XFontStruct *aData )
{
  if( x11Data != ( XFontStruct * ) NULL )
	{
	  LOCK_X11;
	  XFreeFont( getDisplay() -> getData(), x11Data );
	}
  x11Data = aData;
}  

Aq::Size Xq::Font::getWidth() const
{
  return getData() -> max_bounds.width;
}

Aq::Size Xq::Font::getHeight() const
{
  return getData() -> max_bounds.ascent + getData() -> max_bounds.descent;
}

Aq::Size Xq::Font::getAscent() const
{
  return getData() -> max_bounds.ascent;
}

Xq::Font *Xq::Font::createFixed( Display *aDisplay )
{
  return create( aDisplay, "fixed" );
}

Xq::Font *Xq::Font::create( Display *aDisplay, String aName )
{
  preC_( aDisplay != ( Xq::Display * ) NULL );

  XFontStruct *data;
  char *name;

  name = aName.getChars();
  LOCK_X11_DISPLAY( aDisplay );
  data = XLoadQueryFont( aDisplay -> getData(), name );
  delete []name;
  if( data == ( XFontStruct * ) NULL )
	{
	  throw new XException( "Cannot XLoadQueryFont()" );
	}
  return new Xq::Font( aDisplay, data );
}

Xq::Pixmap::Pixmap( Display *aDisplay, XID anId, 
					int aWidth, int aHeight, 
					int aXHot, int aYHot ) :
  Xq::Drawable( aDisplay, anId ),
  Xq::XIDObject( aDisplay, anId ),
  Xq::XqObject( aDisplay )
{
  preC_( aWidth >= 0 );
  preC_( aHeight >= 0 );

  width = aWidth;
  height = aHeight;
  xHot = aXHot;
  yHot = aYHot;
}

Xq::Pixmap::~Pixmap()
{
  LOCK_X11;
  ::XFreePixmap( getDisplay() -> getData(), getId() );
}

Xq::Coord Xq::Pixmap::getWidth() const
{
  return width;
}

Xq::Coord Xq::Pixmap::getHeight() const
{
  return height;
}

int Xq::Pixmap::getXHot() const
{
  return xHot;
}

int Xq::Pixmap::getYHot() const
{
  return yHot;
}

Xq::Pixmap *Xq::Pixmap::create( Xq::Drawable *aDrawable, 
							int aWidth, 
							int aHeight, 
							int aDepth )
{
  preC_( aDrawable != ( Xq::Drawable * ) NULL );
  preC_( aWidth >= 0 );
  preC_( aHeight >= 0 );
  preC_( aDepth >= 0 );

  XID id_;

  LOCK_X11_DISPLAY( aDrawable -> getDisplay() );
  id_ = ::XCreatePixmap( aDrawable -> getDisplay() -> getData(), 
						 aDrawable -> getId(), 
						 ( unsigned ) aWidth, ( unsigned ) aHeight, aDepth );
  return new Xq::Pixmap( aDrawable -> getDisplay(), id_,
						 aWidth, aHeight, 0, 0 );
}

Xq::Pixmap *Xq::Pixmap::create( Xq::Drawable *aDrawable, 
								String aFileName )
{
  preC_( aDrawable != ( Xq::Drawable * ) NULL );

  XID id_;
  char *fileName;
  unsigned width_;
  unsigned height_;
  int xHot_;
  int yHot_;

  fileName = aFileName.getChars();
  LOCK_X11_DISPLAY( aDrawable -> getDisplay() );
  ::XReadBitmapFile( aDrawable -> getDisplay() -> getData(), 
					 aDrawable -> getId(), fileName,
					 &width_, &height_, &id_, &xHot_, &yHot_ );
  delete []fileName;
  return new Xq::Pixmap( aDrawable -> getDisplay(), id_,
						 width_, height_, xHot_, yHot_ );
}

#if defined( USE_XPM )
Xq::Pixmap *Xq::Pixmap::createFromXPM( Xq::Drawable *aDrawable, 
									   String aFileName,
									   Xq::Pixmap **aMask )
{
  preC_( aDrawable != NULL );

  XID id_;
  XID shapeId;
  XpmAttributes attrib;
  char *fileName;
  int returnCode;
		
  attrib.visual = aDrawable -> getDisplay() -> defaultVisual
	( aDrawable -> getDisplay() -> screen() );
  attrib.colormap = aDrawable -> getDisplay() -> defaultColormap();
  attrib.depth = aDrawable -> getDisplay() -> defaultDepth();
  //  attrib.closeness = 65000;
  //  attrib.exactColors = 0;

  attrib.valuemask = XpmVisual | XpmColormap | XpmDepth 
	/* | XpmCloseness | XpmExactColors */;
  fileName = aFileName.getChars();

  LOCK_X11_DISPLAY( aDrawable -> getDisplay() );

  returnCode = ::XpmReadFileToPixmap
	( aDrawable -> getDisplay() -> getData(),
	  aDrawable -> getId(),
	  fileName, &id_, &shapeId, &attrib );
  handleXPMError( returnCode, "Xq::Pixmap::createFromXPM()" );

  delete []fileName;
  test_( attrib.valuemask & XpmSize );
  if( aMask != NULL )
	{
	  if( shapeId != None )
		{
		  *aMask = new Xq::Pixmap( aDrawable -> getDisplay(), shapeId, 
								   attrib.width, attrib.height,
								   attrib.x_hotspot, attrib.y_hotspot );
		}
	  else
		{
		  *aMask = NULL;
		}
	}
  return new Xq::Pixmap( aDrawable -> getDisplay(), id_, 
						 attrib.width, attrib.height,
						 attrib.x_hotspot, attrib.y_hotspot );
}

void Xq::Pixmap::writeToXPMFile( String aFileName )
{
  char *fileName;

  fileName = aFileName.getChars();
  LOCK_X11;
  handleXPMError
	( ::XpmWriteFileFromPixmap( getDisplay() -> getData(),
								fileName,
								getId(),
								None,
								NULL ),
	  "Xq::Pixmap::writeToXPMFile()" );
  delete []fileName;
}

void Xq::Pixmap::handleXPMError( int aCode, String aMessage )
{
  if( aCode != XpmSuccess )
	{
	  const char *errorMsg;

	  switch( aCode )
		{
		case XpmColorError:
		  errorMsg = "XpmColorError";
		  break;
		case XpmOpenFailed:
		  errorMsg = "XpmOpenFailed";
		  break;
		case XpmFileInvalid:
		  errorMsg = "XpmFileInvalid";
		  break;
		case XpmNoMemory:
		  errorMsg = "XpmNoMemory";
		  break;
		case XpmColorFailed:
		  errorMsg = "XpmColorFailed";
		  break;
		default:
		  errorMsg = "Xpm Unknown error";
		  break;
		}
	  throw new XException( aMessage + errorMsg );
	}
}

#endif

Xq::Pixmap *Xq::Pixmap::create( Xq::Drawable *aDrawable, 
								char *aData,
								int aWidth, int aHeight )
{
  preC_( aDrawable != ( Xq::Drawable * ) NULL );
  preC_( aData != NULL );
  preC_( aWidth >= 0 );
  preC_( aHeight >= 0 );

  XID id_;

  LOCK_X11_DISPLAY( aDrawable -> getDisplay() );
  id_ = ::XCreateBitmapFromData
	( aDrawable -> getDisplay() -> getData(), aDrawable -> getId(),
 	  aData,
	  aWidth, aHeight );
  return new Xq::Pixmap( aDrawable -> getDisplay(), id_,
						 aWidth, aHeight, 0, 0 );
}

void Xq::Pixmap::writeToXBMFile( String aFileName )
{
  char *fileName;

  fileName = aFileName.getChars();
  LOCK_X11;
  ::XWriteBitmapFile( getDisplay() -> getData(),
					  fileName, getId(), getWidth(), getHeight(), 
					  getXHot(), getYHot() );
  delete []fileName;
}

Xq::Color::Color( Display *aDisplay, XColor *aColor ) :
  Xq::XqObject( aDisplay )
{
  setData( aColor );
}

Xq::Color::~Color()
{}

XColor *Xq::Color::getData() const
{
  return ( XColor * ) &x11Data;
}

void Xq::Color::setData( XColor *aData )
{
  PLAF -> memcpy
	( &x11Data, aData, sizeof( XColor ) );
}

Xq::Color *Xq::Color::create( Display *aDisplay, String aName )
{
  preC_( aDisplay != ( Xq::Display * ) NULL );

  XColor data;
  char *name;
  XID defColormap;

  name = aName.getChars();
  
  defColormap = aDisplay -> defaultColormap();
  LOCK_X11_DISPLAY( aDisplay );
  if( ::XParseColor( aDisplay -> getData(), defColormap, name, &data ) == 0 )
	{
	  delete []name;
	  throw new XException
		( String( "Cannot XParseColor( " ) + aName + " )" );
	}
  delete []name;
  if( ::XAllocColor( aDisplay -> getData(), defColormap, &data ) == 0 )
	{
	  throw new XException
		( String( "Cannot XAllocColor( " ) + aName + " )" );
	}
  return new Xq::Color( aDisplay, &data );
}

Xq::Color *Xq::Color::create( Display *aDisplay, unsigned long aPixel )
{
  XColor data;

  data.pixel = aPixel;
  return new Xq::Color( aDisplay, &data );
}

Xq::Cursor::Cursor( Display *aDisplay, XID anId ) :
  Xq::XIDObject( aDisplay, anId ),
  Xq::XqObject( aDisplay )
{
}

Xq::Cursor::~Cursor()
{
  LOCK_X11;
  ::XFreeCursor( getDisplay() -> getData(), getId() );
}

void Xq::Cursor::recolor( Xq::Color *fg, Xq::Color *bg )
{
  preC_( bg != NULL );
  preC_( fg != NULL );

  LOCK_X11;
  ::XRecolorCursor( getDisplay() -> getData(),
					getId(), fg -> getData(), bg -> getData() );
  
}

Xq::Cursor *Xq::Cursor::createStd( Xq::Display *aDisplay,
								   int shape )
{
  preC_( aDisplay != NULL );

  LOCK_X11_DISPLAY( aDisplay );
  return new Xq::Cursor( aDisplay,
						 ::XCreateFontCursor( aDisplay -> getData(),
											  shape ) );
}

Xq::Cursor *Xq::Cursor::create( Xq::Display *aDisplay,
								   Xq::Pixmap *aSource, Xq::Pixmap *aMask,
								   Xq::Color *foreground, 
								   Xq::Color *background,
								   Xq::Coord xHot, Xq::Coord yHot )
{
  preC_( aDisplay != NULL );
  preC_( aSource != NULL );
  preC_( aMask != NULL );
  preC_( foreground != NULL );
  preC_( background != NULL );

  OGuard _source( aSource, aSource );
  OGuard _mask( aMask, aMask );
  OGuard _fore( foreground, foreground );
  OGuard _back( background, background );

  LOCK_X11_DISPLAY( aDisplay );
  return new Xq::Cursor
	( aDisplay,
	  ::XCreatePixmapCursor( aDisplay -> getData(), 
							 aSource -> getId(), aMask -> getId(),
							 foreground -> getData(), background -> getData(), 
							 xHot, yHot ) );
}


Xq::XException::XException( struct _XDisplay *aDisplay, 
							XErrorEvent *anEvent ) :
  OSError( "Error in X protocol", null ),
  Exception( "Error in X protocol", null )
{
  preC_( anEvent != ( XErrorEvent * ) NULL );
  event.type = anEvent -> type;
  event.display = anEvent -> display;
  event.serial = anEvent -> serial;
  event.error_code = anEvent -> error_code;
  event.request_code = anEvent -> request_code;
  event.minor_code = anEvent -> minor_code;
  event.resourceid = anEvent -> resourceid;

  test_( event.display == aDisplay );
  fromEvent = true;
}

Xq::XException::XException( String aMessage ) :
  OSError( aMessage, null ),
  Exception( aMessage, null )
{
  fromEvent = false;
}

Xq::XException::~XException()
{}

String  Xq::XException::getClassName() const
{
  return "XException";
}

String  Xq::XException::getDescription() const
{
  if( fromEvent )
	{
	  String requestName;
	  char errorTextBuffer[ 1024 ]; /* XXX param */

	  requestName = "Unknown request";
	  for( int i = 0 ; requestNameMap[ i ].xRequestId != -1 ; ++i )
		{
		  if( requestNameMap[ i ].xRequestId == event.request_code )
			{
			  requestName = requestNameMap[ i ].xRequestName;
			  break;
			}
		}
	  requestName = String( "%s (%i)" ).sprintf
		( 1, requestName.getChars(), event.request_code );

	  /* XXX x11Serializer _must_ be recursive: 
		 this can be called from within Xlib! */
	  // LOCK_X11_DISPLAY();
	  ::XGetErrorText
		( event.display, 
		  event.error_code, errorTextBuffer, 
		  ( sizeof errorTextBuffer ) / ( sizeof errorTextBuffer[ 0 ] ) );
	  return OSError::getDescription() + 
		String( " :\n XError: serial: " ) + String( event.serial, 10 ) +
		String( " error code: " ) + String( event.error_code ) +
		String( " error text: " ) + String( errorTextBuffer ) +
		String( " request: " ) + requestName +
		String( " minor code: " ) + String( event.minor_code, 10 ) +
		String( " resource: " ) + String( event.resourceid, 0x10 );
	}
  else
	{
	  return OSError::getDescription() + 
		" :\nError in call to Xlib";
	}
}

Xq::XException::MapEntry Xq::XException::requestNameMap[] = 
{
  /* generated from X11/Xproto.h */
  { X_CreateWindow,			"X_CreateWindow" },
  { X_ChangeWindowAttributes,			"X_ChangeWindowAttributes" },
  { X_GetWindowAttributes,			"X_GetWindowAttributes" },
  { X_DestroyWindow,			"X_DestroyWindow" },
  { X_DestroySubwindows,			"X_DestroySubwindows" },
  { X_ChangeSaveSet,			"X_ChangeSaveSet" },
  { X_ReparentWindow,			"X_ReparentWindow" },
  { X_MapWindow,			"X_MapWindow" },
  { X_MapSubwindows,			"X_MapSubwindows" },
  { X_UnmapWindow,			"X_UnmapWindow" },
  { X_UnmapSubwindows,			"X_UnmapSubwindows" },
  { X_ConfigureWindow,			"X_ConfigureWindow" },
  { X_CirculateWindow,			"X_CirculateWindow" },
  { X_GetGeometry,			"X_GetGeometry" },
  { X_QueryTree,			"X_QueryTree" },
  { X_InternAtom,			"X_InternAtom" },
  { X_GetAtomName,			"X_GetAtomName" },
  { X_ChangeProperty,			"X_ChangeProperty" },
  { X_DeleteProperty,			"X_DeleteProperty" },
  { X_GetProperty,			"X_GetProperty" },
  { X_ListProperties,			"X_ListProperties" },
  { X_SetSelectionOwner,			"X_SetSelectionOwner" },
  { X_GetSelectionOwner,			"X_GetSelectionOwner" },
  { X_ConvertSelection,			"X_ConvertSelection" },
  { X_SendEvent,			"X_SendEvent" },
  { X_GrabPointer,			"X_GrabPointer" },
  { X_UngrabPointer,			"X_UngrabPointer" },
  { X_GrabButton,			"X_GrabButton" },
  { X_UngrabButton,			"X_UngrabButton" },
  { X_ChangeActivePointerGrab,			"X_ChangeActivePointerGrab" },
  { X_GrabKeyboard,			"X_GrabKeyboard" },
  { X_UngrabKeyboard,			"X_UngrabKeyboard" },
  { X_GrabKey,			"X_GrabKey" },
  { X_UngrabKey,			"X_UngrabKey" },
  { X_AllowEvents,			"X_AllowEvents" },
  { X_GrabServer,			"X_GrabServer" },
  { X_UngrabServer,			"X_UngrabServer" },
  { X_QueryPointer,			"X_QueryPointer" },
  { X_GetMotionEvents,			"X_GetMotionEvents" },
  { X_TranslateCoords,			"X_TranslateCoords" },
  { X_WarpPointer,			"X_WarpPointer" },
  { X_SetInputFocus,			"X_SetInputFocus" },
  { X_GetInputFocus,			"X_GetInputFocus" },
  { X_QueryKeymap,			"X_QueryKeymap" },
  { X_OpenFont,			"X_OpenFont" },
  { X_CloseFont,			"X_CloseFont" },
  { X_QueryFont,			"X_QueryFont" },
  { X_QueryTextExtents,			"X_QueryTextExtents" },
  { X_ListFonts,			"X_ListFonts" },
  { X_ListFontsWithInfo,			"X_ListFontsWithInfo" },
  { X_SetFontPath,			"X_SetFontPath" },
  { X_GetFontPath,			"X_GetFontPath" },
  { X_CreatePixmap,			"X_CreatePixmap" },
  { X_FreePixmap,			"X_FreePixmap" },
  { X_CreateGC,			"X_CreateGC" },
  { X_ChangeGC,			"X_ChangeGC" },
  { X_CopyGC,			"X_CopyGC" },
  { X_SetDashes,			"X_SetDashes" },
  { X_SetClipRectangles,			"X_SetClipRectangles" },
  { X_FreeGC,			"X_FreeGC" },
  { X_ClearArea,			"X_ClearArea" },
  { X_CopyArea,			"X_CopyArea" },
  { X_CopyPlane,			"X_CopyPlane" },
  { X_PolyPoint,			"X_PolyPoint" },
  { X_PolyLine,			"X_PolyLine" },
  { X_PolySegment,			"X_PolySegment" },
  { X_PolyRectangle,			"X_PolyRectangle" },
  { X_PolyArc,			"X_PolyArc" },
  { X_FillPoly,			"X_FillPoly" },
  { X_PolyFillRectangle,			"X_PolyFillRectangle" },
  { X_PolyFillArc,			"X_PolyFillArc" },
  { X_PutImage,			"X_PutImage" },
  { X_GetImage,			"X_GetImage" },
  { X_PolyText8,			"X_PolyText8" },
  { X_PolyText16,			"X_PolyText16" },
  { X_ImageText8,			"X_ImageText8" },
  { X_ImageText16,			"X_ImageText16" },
  { X_CreateColormap,			"X_CreateColormap" },
  { X_FreeColormap,			"X_FreeColormap" },
  { X_CopyColormapAndFree,			"X_CopyColormapAndFree" },
  { X_InstallColormap,			"X_InstallColormap" },
  { X_UninstallColormap,			"X_UninstallColormap" },
  { X_ListInstalledColormaps,			"X_ListInstalledColormaps" },
  { X_AllocColor,			"X_AllocColor" },
  { X_AllocNamedColor,			"X_AllocNamedColor" },
  { X_AllocColorCells,			"X_AllocColorCells" },
  { X_AllocColorPlanes,			"X_AllocColorPlanes" },
  { X_FreeColors,			"X_FreeColors" },
  { X_StoreColors,			"X_StoreColors" },
  { X_StoreNamedColor,			"X_StoreNamedColor" },
  { X_QueryColors,			"X_QueryColors" },
  { X_LookupColor,			"X_LookupColor" },
  { X_CreateCursor,			"X_CreateCursor" },
  { X_CreateGlyphCursor,			"X_CreateGlyphCursor" },
  { X_FreeCursor,			"X_FreeCursor" },
  { X_RecolorCursor,			"X_RecolorCursor" },
  { X_QueryBestSize,			"X_QueryBestSize" },
  { X_QueryExtension,			"X_QueryExtension" },
  { X_ListExtensions,			"X_ListExtensions" },
  { X_ChangeKeyboardMapping,			"X_ChangeKeyboardMapping" },
  { X_GetKeyboardMapping,			"X_GetKeyboardMapping" },
  { X_ChangeKeyboardControl,			"X_ChangeKeyboardControl" },
  { X_GetKeyboardControl,			"X_GetKeyboardControl" },
  { X_Bell,			"X_Bell" },
  { X_ChangePointerControl,			"X_ChangePointerControl" },
  { X_GetPointerControl,			"X_GetPointerControl" },
  { X_SetScreenSaver,			"X_SetScreenSaver" },
  { X_GetScreenSaver,			"X_GetScreenSaver" },
  { X_ChangeHosts,			"X_ChangeHosts" },
  { X_ListHosts,			"X_ListHosts" },
  { X_SetAccessControl,			"X_SetAccessControl" },
  { X_SetCloseDownMode,			"X_SetCloseDownMode" },
  { X_KillClient,			"X_KillClient" },
  { X_RotateProperties,			"X_RotateProperties" },
  { X_ForceScreenSaver,			"X_ForceScreenSaver" },
  { X_SetPointerMapping,			"X_SetPointerMapping" },
  { X_GetPointerMapping,			"X_GetPointerMapping" },
  { X_SetModifierMapping,			"X_SetModifierMapping" },
  { X_GetModifierMapping,			"X_GetModifierMapping" },
  { X_NoOperation,			"X_NoOperation" },
  { -1, "" }
};

Xq::XFatalException::XFatalException( struct _XDisplay * ) :
  Errno( "Fatal X protocol error", null ),
  Exception( "Fatal X protocol error", null )
{}

Xq::XFatalException::~XFatalException()
{}

String  Xq::XFatalException::getClassName() const
{
  return "XFatalException";
}

String  Xq::XFatalException::getDescription() const
{
  return Errno::getDescription() +
	":\n fatal X protocol error";
}

Xq::XqEvent::XqEvent( Xq::Display *aDisplay, 
					  XEvent *aData ) :
  Xq::XqObject( aDisplay )
{
  PLAF -> memcpy
	( &event, aData, sizeof( XEvent ) );
}

Xq::XqEvent::~XqEvent()
{
  FCDONE( window );
}

XEvent *Xq::XqEvent::getData() const
{
  return ( XEvent * ) &event;
}

Xq::Window *Xq::XqEvent::getWindow() const
{
  return window;
}

void Xq::XqEvent::setWindow( Xq::Window *aWindow )
{
  FMOVE( window, aWindow );
}

Xq::XqEvent *Xq::XqEvent::create( Xq::Display *aDisplay, 
							  XEvent *aData,
							  Xq::Window *aWindow )
{
  preC_( aData != ( XEvent * ) NULL );

  Xq::XqEvent *result;

  result = NULL;
  switch( aData -> type )
	{
	case KeyPress:
	case KeyRelease:
	  result = new Xq::KeyEvent( aDisplay, aData );
	  break;
	case ButtonPress:
	case ButtonRelease:
	  result = new Xq::ButtonEvent( aDisplay, aData );
	  break;
	case MotionNotify:
	case EnterNotify:
	case LeaveNotify:
	  result = new Xq::MouseMotionEvent( aDisplay, aData );
	  break;
	case FocusIn:
	case FocusOut:
	  result = new Xq::FocusEvent( aDisplay, aData );
	  break;
	case Expose:
	case GraphicsExpose:
	case NoExpose:
	case VisibilityNotify:
	case MappingNotify:
	case CreateNotify:
	case UnmapNotify:
	case MapNotify:
	case CirculateNotify:
	  result = new Xq::WindowEvent( aDisplay, aData );
	  break;
	case DestroyNotify:
	  result = new Xq::DestroyEvent( aDisplay, aData );
	  break;
	case ConfigureNotify:
	case ResizeRequest:
	  result = new Xq::ResizeEvent( aDisplay, aData );
	  break;
	case SelectionClear:
	case SelectionRequest:
	case SelectionNotify:
	  result = new Xq::SelectionEvent( aDisplay, aData );
	  break;
	case ColormapNotify:
	case ClientMessage:

	case CirculateRequest:
	case PropertyNotify:
	case ConfigureRequest:
	case GravityNotify:
	case MapRequest:
	case ReparentNotify:
	case KeymapNotify:
	  result = new Xq::XqEvent( aDisplay, aData );
	  break;
	case LASTEvent:
	  test( false, "XEvent.type == LASTEvent ?!" );
	default:
	  test( false, "Unknown XEvent.type" );
	}
  result -> setWindow( aWindow );
  return result;
}

String Xq::XqEvent::toString() const
{
  return Xq::XqObject::toString() +
	String( " of type: %i serial: %lu SendEvent?: %s window: " ).
	sprintf( 1, event.type, event.xany.serial, 
			 ( event.xany.send_event ) ? "yes" : "no" ) +
	( ( window != ( Xq::Window * ) NULL ) ? window -> toString() : 
	  String( "unknown" ) );
}

String Xq::XqEvent::getClassName() const
{
  return "XqEvent";
}

Xq::KeyEvent::KeyEvent( Xq::Display *aDisplay, XEvent *aData ) :
  Xq::XqObject( aDisplay ),
  Xq::XqEvent( aDisplay, aData )
{
  preC_( aData != ( XEvent * ) NULL );
}

Xq::KeyEvent::~KeyEvent()
{}

unsigned int Xq::KeyEvent::getKeycode() const
{
  return event.xkey.keycode;
}

KeySym Xq::KeyEvent::getKeySym() const
{
  return keycodeToKeysym( getDisplay(), getKeycode() );
}

KeySym Xq::KeyEvent::keycodeToKeysym( Xq::Display *aDisplay,
									  unsigned int aKeycode )
{
  preC_( aDisplay != NULL );

  LOCK_X11_DISPLAY( aDisplay );
  return ::XKeycodeToKeysym( aDisplay -> getData(),
							 aKeycode, 0 );
}

String Xq::KeyEvent::keysymToString( Xq::Display *aDisplay,
									 ::KeySym aKeySym )
{
  preC_( aDisplay != NULL );

  LOCK_X11_DISPLAY( aDisplay );
  return ::XKeysymToString( aKeySym );
}

String Xq::KeyEvent::getClassName() const
{
  return "KeyEvent";
}

Xq::ButtonEvent::ButtonEvent( Xq::Display *aDisplay, XEvent *aData ) :
  Xq::XqObject( aDisplay ),
  Xq::XqEvent( aDisplay, aData )
{
  preC_( aData != ( XEvent * ) NULL );
}

Xq::ButtonEvent::~ButtonEvent()
{}

String Xq::ButtonEvent::getClassName() const
{
  return "ButtonEvent";
}

Xq::MouseMotionEvent::MouseMotionEvent( Xq::Display *aDisplay, XEvent *aData ) :
  Xq::XqObject( aDisplay ),
  Xq::XqEvent( aDisplay, aData )
{
  preC_( aData != ( XEvent * ) NULL );
}

Xq::MouseMotionEvent::~MouseMotionEvent()
{}

String Xq::MouseMotionEvent::getClassName() const
{
  return "MouseMotionEvent";
}

Xq::FocusEvent::FocusEvent( Xq::Display *aDisplay, XEvent *aData ) :
  Xq::XqObject( aDisplay ),
  Xq::XqEvent( aDisplay, aData )
{
  preC_( aData != ( XEvent * ) NULL );
}

Xq::FocusEvent::~FocusEvent()
{}

String Xq::FocusEvent::getClassName() const
{
  return "FocusEvent";
}

Xq::WindowEvent::WindowEvent( Xq::Display *aDisplay, XEvent *aData ) :
  Xq::XqObject( aDisplay ),
  Xq::XqEvent( aDisplay, aData )
{
  preC_( aData != ( XEvent * ) NULL );
}

Xq::WindowEvent::~WindowEvent()
{}

String Xq::WindowEvent::getClassName() const
{
  return "WindowEvent";
}

Xq::DestroyEvent::DestroyEvent( Xq::Display *aDisplay, XEvent *aData ) :
  Xq::XqObject( aDisplay ),
  Xq::XqEvent( aDisplay, aData )
{
  preC_( aData != ( XEvent * ) NULL );
}

Xq::DestroyEvent::~DestroyEvent()
{}

String Xq::DestroyEvent::getClassName() const
{
  return "DestroyEvent";
}

Xq::ResizeEvent::ResizeEvent( Xq::Display *aDisplay, XEvent *aData ) :
  Xq::XqObject( aDisplay ),
  Xq::XqEvent( aDisplay, aData )
{
  preC_( aData != ( XEvent * ) NULL );
}

Xq::ResizeEvent::~ResizeEvent()
{}

String Xq::ResizeEvent::getClassName() const
{
  return "ResizeEvent";
}

Xq::SelectionEvent::SelectionEvent( Xq::Display *aDisplay, XEvent *aData ) :
  Xq::XqObject( aDisplay ),
  Xq::XqEvent( aDisplay, aData )
{
  preC_( aData != ( XEvent * ) NULL );
}

Xq::SelectionEvent::~SelectionEvent()
{}

String Xq::SelectionEvent::getClassName() const
{
  return "SelectionEvent";
}

Xq::Applet::Applet( Xq::Drawable *aDrawable, 
					Xq::XqEventSource *aSource,
					Xq::GC *aGC ) :
  Xq::XqObject( aDrawable -> getDisplay() )
{
  preC_( aDrawable != NULL );
  preC_( aSource != NULL );
  preC_( aGC != NULL );

  FNNINIT( drawable, aDrawable );
  FNNINIT( graphics, aGC );

  aSource -> addPassive( this );
}

Xq::Applet::~Applet()
{
  FDONE( graphics );
  FDONE( drawable );
}

void Xq::Applet::onWindowEvent( Aq::WindowEvent *anEvent )
{
  preC_( anEvent != NULL );
  
  activate();
}

void Xq::Applet::activate()
{
  paint( drawable, graphics );
}

void Xq::Applet::deactivate()
{
}

Xq::Drawable *Xq::Applet::getDrawable() const
{
  return drawable;
}

HandlerResult Xq::Applet::fire( Condition *aCondition, 
								ObservationContext * )
{
  TCAST( aCondition, Xq::XqEvent ) -> dispatch( this );
  return 0;
}

Xq::AppletThread::AppletThread( Xq::Applet *anApplet, int aDelay )
{
  preC_( anApplet != ( Xq::Applet * ) NULL );
  preC_( aDelay >= 0 );

  FNNINIT( applet, anApplet );
  delay = aDelay;
  thread = NULL;
}

Xq::AppletThread::~AppletThread()
{
  FCDONE( thread );
  FDONE( applet );
}

void Xq::AppletThread::start()
{
  FINIT( thread, THR_FACTORY -> createFromRunner( this ) );
}

void *Xq::AppletThread::run()
{
  while( true )
	{
	  // DLOG( "Xq::AppletThread::run()" );
	  {
		OGuard _lock( applet -> getDrawable(), this );
		applet -> deactivate();
		step();
		applet -> activate();
	  }
 	  THR_SYSTEM -> sleep( 0, delay );
	}
  return ( void * ) NULL;
}

Aq::Painter *Xq::X11WindowSystem::createPainter( Aq::GraphicContext *aContext, 
												 Aq::Drawable *aDrawable ) const
{
  return new Xq::XPainter
	( TCAST( aContext, Xq::GC ), TCAST( aDrawable, Xq::Drawable ) );
}

Xq::X11WindowSystem *Xq::X11WindowSystem::x11WindowSystem = 
     TCAST( ( new Xq::X11WindowSystem() ) -> dontManage(), 
			Xq::X11WindowSystem );

Xq::XPainter::XPainter( Xq::GC *aGC, Xq::Drawable *aDrawable ) :
  Aq::Painter( aGC, aDrawable )
{
  FINIT( clearGC, Xq::GC::create( aDrawable -> getDisplay(), aDrawable ) );
  
  aGC -> copyTo( clearGC );
  clearGC -> setForeground( aGC -> getBackground() );

  arena = aDrawable;
}

Xq::XPainter::~XPainter()
{
  FDONE( clearGC );
}

void Xq::XPainter::setDrawable( Aq::Drawable *aDrawable )
{
  arena = TCAST( aDrawable, Xq::Drawable );
}

void Xq::XPainter::init()
{
//   getDrawable() -> lock( this );
//   getContext() -> lock( this );
}

void Xq::XPainter::done()
{
//   getContext() -> unlock( this );
//   getDrawable() -> unlock( this );
}

void Xq::XPainter::drawLine( Aq::CoordX aStartX, Aq::CoordY aStartY, 
							 Aq::CoordX anEndX, Aq::CoordY anEndY ) const
{
  getXDrawable() -> drawLine( getGC(), aStartX, aStartY, anEndX, anEndY );
}

void Xq::XPainter::drawPoint( Aq::CoordX aX, Aq::CoordY aY ) const
{
  getXDrawable() -> drawPoint( getGC(), aX, aY );
}

void Xq::XPainter::drawCString( Aq::CoordX aX, Aq::CoordY aY, 
									char *aString ) const
{
  preC_( aString != NULL );

  getXDrawable() -> drawCString( getGC(), aX, aY, aString );
}

void Xq::XPainter::fillRectangle( Aq::CoordX aX, Aq::CoordY aY,
								  Aq::Size aWidth, Aq::Size aHeight ) const
{
  getXDrawable() -> fillRectangle( getGC(), aX, aY, aWidth, aHeight );
}

void Xq::XPainter::clearRectangle( Aq::CoordX aX, Aq::CoordY aY,
								   Aq::Size aWidth, Aq::Size aHeight ) const
{
  getXDrawable() -> fillRectangle( getClearGC(), aX, aY, aWidth, aHeight );
}

void Xq::XPainter::copyFrom( Aq::CoordX aSourceX, Aq::CoordY aSourceY, 
							 Aq::Size aWidth, Aq::Size aHeight,
							 Aq::Drawable *aDrawable,
							 Aq::CoordX aDestX, Aq::CoordY aDestY ) const
{
  preC_( aDrawable != NULL );
  preC_( getDrawable() -> isCompatible( aDrawable ) );

  Xq::Drawable *destination;

  destination = TCAST( aDrawable, Xq::Drawable );
  OGuard _destination( destination, this );

  destination -> copyTo
	( getXDrawable(), getGC(), aSourceX, aSourceY, aWidth, aHeight, 
	  aDestX, aDestY );
}


Xq::Drawable *Xq::XPainter::getXDrawable() const
{
  return arena;
}

Xq::GC *Xq::XPainter::getGC() const
{
  return TCAST( getContext(), Xq::GC );
}

Xq::GC *Xq::XPainter::getClearGC() const
{
  return clearGC;
}

Xq::XPainter *Xq::XPainter::create( GC *aGC, Drawable *aDrawable, 
									Xq::Color *aFore )
{
  preC_( aDrawable != NULL );
  preC_( aGC != NULL );
  preC_( aFore != NULL );

  OGuard _port( aDrawable, aDrawable );
  OGuard _gc( aGC, aGC );
  OGuard _fore( aFore, aFore );

  Xq::GC *newGC;
  
  newGC = Xq::GC::create( aDrawable -> getDisplay(), aDrawable );
  OGuard _newGC( newGC, newGC );

  aGC -> copyTo( newGC );
  newGC -> setForeground( aFore );
  return new Xq::XPainter( newGC, aDrawable );
}

#if defined( TESTING )

#include "observe/EchoCondition.hpp"
#include "observe/ObservationContext.hpp"
#include "observe/EchoObserver.hpp"

#include <unistd.h>
#include <string.h>

class TestApplet : public Xq::Applet
{
public: TestApplet( Xq::Drawable *aDrawable, 
					Xq::XqEventSource *aSource,
					Xq::GC *aGC ) :
  Xq::XqObject( aDrawable -> getDisplay() ),
  Xq::Applet( aDrawable, aSource, aGC  )
	{
	  Xq::Color *red;
	  Xq::Color *black;
	  x = y = 10;

	  red = Xq::Color::create( getDisplay(), "red" );
	  OGuard _red( red, this );
	  graphics -> setForeground( red );

	  FINIT( clearGraphics, Xq::GC::create( getDisplay(), aDrawable ) );

	  black = Xq::Color::create( getDisplay(), "black" );
	  OGuard _black( black, this );

	  clearGraphics -> setForeground( black );
	  clearGraphics -> setBackground( black );
	};

public: virtual ~TestApplet()
	{
	  FDONE( clearGraphics );
	};

public: virtual void deactivate()
	{
	  LOCK_X11_DISPLAY( drawable -> getDisplay() );
	  ::XFillRectangle( drawable -> getDisplay() -> getData(), 
						drawable -> getId(), clearGraphics -> getData(),
						x, y, 6, 6 );
	};

public: virtual void paint( Xq::Drawable *aDrawable, Xq::GC *aGraphics )
	{
	  LOCK_X11_DISPLAY( aDrawable -> getDisplay() );
	  ::XDrawRectangle( aDrawable -> getDisplay() -> getData(),
						aDrawable -> getId(),
						aGraphics -> getData(), x, y, 5, 5 );
	  // DLOG( "TestApplet::paint()" );
	};

public: int x;
public: int y;

protected: Xq::GC *clearGraphics;
};

class TestAppletThread : public Xq::AppletThread
{
public: TestAppletThread( TestApplet *anApplet, int aDelay, int aDx, int aDy ) :
  Xq::AppletThread( anApplet, aDelay )
	{
	  this -> dx = aDx;
	  this -> dy = aDy;
	};

public: void step()
	{
	  TestApplet *app;

	  if( rand() % 20 == 0 )
		{
		  dx += ( rand() % 3 ) - 1;
		  dy += ( rand() % 3 ) - 1;
		}

	  app = TCAST( applet, TestApplet );
	  if( ( app -> x + dx > 200 ) ||
		  ( app -> x + dx < 0 ) )
		{
		  dx = -dx;
		}
	  if( ( app -> y + dy > 200 ) ||
		  ( app -> y + dy < 0 ) )
		{
		  dy = -dy;
		}
	  app -> x += dx;
	  app -> y += dy;
	};
protected: int dx;
protected: int dy;
};

class TestCursor : public Runnable,
				   public virtual Object
{
public: TestCursor( Xq::Window *aWindow )
	{
	  preC_( aWindow != NULL );
	  
	  FNNINIT( window, aWindow );

	  for( int r = 0 ; r < 32 ; ++r )
		{
		  Xq::Pixmap *pix;

		  pix = Xq::Pixmap::create
			( window, String( "tmp/cursor-%2.2i.xbm" ).sprintf( 1, r ) );
		  FINIT( cursor[ r ], Xq::Cursor::create
				 ( window -> getDisplay(),
				   pix, pix,
				   Xq::Color::create( window -> getDisplay(), "green" ), 
				   Xq::Color::create( window -> getDisplay(), "red" ), 
				   32, 32 ) );
		}
	};

public: virtual ~TestCursor()
	{
	  for( int r = 0 ; r < 32 ; ++r )
		{
		  FDONE( cursor[ r ] );
		}
	  FDONE( window );
	}

public: virtual void *run()
	{
	  for( int i = 0 ; ; ++i )
		{
		  int r;

		  if( ( i / 32 ) % 2 == 0 )
			{
			  r = i % 32;
			}
		  else
			{
			  r = ( 63 - ( i % 64 ) ) % 32;
			}
		  window -> setCursor( cursor[ r ] );
		  THR_SYSTEM -> sleep( 0, 100000000 );
		}
	  return NULL;
	}

protected: Xq::Window *window;
protected: Xq::Cursor *cursor[ 32 ];
};

class TestFrameFeeder : public Runnable,
					public virtual Object
{
public: TestFrameFeeder( Xq::Drawable *aSource,
					 Xq::Drawable *aTarget,
					 Xq::GC *aGC,
					 Index aDelay )
	{
	  preC_( aSource != NULL );
	  preC_( aTarget != NULL );
	  preC_( aGC != NULL );
	  preC_( aDelay > 0 );
	  
	  FNNINIT( source, aSource );
	  FNNINIT( target, aTarget );
	  FNNINIT( gc, aGC );

	  delay = aDelay;
	};

public: virtual ~TestFrameFeeder()
	{
	  FDONE( gc );
	  FDONE( target );
	  FDONE( source );
	}

public: virtual void *run()
	{
	  while( true )
		{
		  {
			OGuard _lock( source, this );
			source -> copyTo( target, gc, 0, 0, 200, 200, 0, 0 );
		  }
		  THR_SYSTEM -> sleep( 0, delay );
		}
	  return NULL;
	}

protected: Xq::Drawable *source;
protected: Xq::Drawable *target;
protected: Xq::GC *gc;
protected: Index delay;
};

boolean Xq::tester( int )
{
  Xq::Display *display;
  Xq::Window *win;
  EchoObserver *echo;
  Xq::XqEventSource *eventSource;
  Xq::Pixmap *pix;
  Xq::GC *gc;

#if defined( XQ_USE_XINITTHREADS )
  test_( ::XInitThreads() != 0 );
#endif

  display = Xq::Display::open();
  display -> dontManage();

  DLOG( "typeid: %s", typeid( *new Object() ).name() );
  win = Xq::Window::createSimple
	( display, display -> getRootWindow(),
	  0, 0,
	  200, 200, 
	  3,
	  display -> whitePixel(), display -> blackPixel() );
  OGuard _win( win, win );
  win -> map();
  win -> selectInput
	( ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask |
	  KeyReleaseMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
	  PointerMotionMask | /* PointerMotionHintMask | */ Button1MotionMask |
	  Button2MotionMask |Button3MotionMask | Button4MotionMask | 
	  Button5MotionMask | ButtonMotionMask | KeymapStateMask | ExposureMask |
	  VisibilityChangeMask | ResizeRedirectMask | SubstructureNotifyMask |
	  SubstructureRedirectMask | FocusChangeMask | PropertyChangeMask |
	  ColormapChangeMask | OwnerGrabButtonMask );

  echo = new EchoObserver();
  OGuard _echo( echo, echo );

  eventSource = win -> getXEventSource();
  OGuard _eventSource( eventSource, eventSource );

  pix = Xq::Pixmap::create( win, 200, 200, display -> defaultDepth() );
  OGuard _pix( pix, pix );

  pix -> setProtector( THR_SYSTEM -> recMutex() );

  gc = Xq::GC::create( pix -> getDisplay(), pix );
  OGuard _gc( gc, gc );

  gc -> setBackground( Xq::Color::create( display, "black" ) );
  gc -> setForeground( Xq::Color::create( display, "black" ) );

  {
	LOCK_X11_DISPLAY( display );
	::XFillRectangle( display -> getData(), pix -> getId(), 
					  gc -> getData(), 
					  0, 0, 200, 200 );
  }

  char bitmapData[ 64 * 8 ];
  for( int r = 0 ; r < 32 ; ++r )
	{
	  PLAF -> memset( bitmapData, 0, sizeof bitmapData );
	  for( int a = 0 ; a < 100 ; ++a )
		{
		  double angle;
		  int x;
		  int y;

		  angle = 2 * M_PI / 100 * a;
		  x = 32 + ( int ) ( r * cos( angle ) );
		  y = 32 - ( int ) ( r * sin( angle ) );
		  bitmapData[ y * 8 + ( x / 8 ) ] |= ( 1 << ( x % 8 ) );
		  if( r > 2 )
			{
			  x = 32 + ( int ) ( ( r - 2 ) * cos( angle ) );
			  y = 32 - ( int ) ( ( r - 2 ) * sin( angle ) );
			  bitmapData[ y * 8 + ( x / 8 ) ] |= ( 1 << ( x % 8 ) );
			}

		  if( a < 96 )
			{
			  for( int petal = 0 ; petal < 4 ; ++petal )
				{
				  angle = 2 * M_PI / 100 * a + 2 * M_PI / 32 * r + 
					M_PI / 2 * petal;
				  x = 32 + ( int ) ( a / 3 * cos( angle ) );
				  y = 32 - ( int ) ( a / 3 * sin( angle ) );
				  bitmapData[ y * 8 + ( x / 8 ) ] |= ( 1 << ( x % 8 ) );
				}
			}
		}
	  {
		  Xq::Pixmap *cursor;

		  cursor = Xq::Pixmap::create( win, bitmapData, 64, 64 );
		  OGuard _cursor( cursor, cursor );
		  cursor -> writeToXBMFile( String( "tmp/cursor-%2.2i.xbm" ).
									sprintf( 1, r ) );
	  }
	}


  //  eventSource -> addPassive( echo );

  THR_FACTORY -> createFromRunner( eventSource );
  THR_FACTORY -> createFromRunner( new TestCursor( win ) );
  THR_FACTORY -> createFromRunner( new TestFrameFeeder( pix, win, gc, 100000000 ) );

  for( int i = 0 ; i < 6 ; ++i )
	{
	  TestApplet *applet;
	  TestAppletThread *thread;

	  applet = new TestApplet( pix, eventSource, gc );

	  OGuard _applet( applet, applet );

	  thread = new TestAppletThread
		( applet, 10000000 * ( rand() % 10 + 1 ), 
		  rand() % 10, rand() % 10 );
	  //	  OGuard _thread( thread, thread );
	  thread -> start();
	}

//   TestApplet *applet1;
//   applet1 = new TestApplet( win, eventSource );
//   OGuard _applet1( applet1, applet1 );

//   TestApplet *applet2;
//   applet2 = new TestApplet( win, eventSource );
//   OGuard _applet2( applet2, applet2 );

//   TestAppletThread *thread1;
//   thread1 = new TestAppletThread( applet1, 100000000, 1, 2 );
//   OGuard _thread1( thread1, thread1 );
//   thread1 -> start();

//   TestAppletThread *thread2;
//   thread2 = new TestAppletThread( applet2, 100000000, 3, 1 );
//   OGuard _thread2( thread2, thread2 );
//   thread2 -> start();

  for( ; ; )
	{
	  THR_SYSTEM -> sleep( 1, 0 );
#if defined( XQ_USE_X11_MUTEX )
	  //	  DLOGOBJECT
	  //		( "Display mutex: %s", display -> getX11Serialiser() );
#endif
	}
  return true;
}
#endif

#if defined(_INLINE)
#include "../src/Debug.ipp"
#endif

/* USE_XQ */
#endif

/* $Log$ */
