/* 
 NSX11GraphicsContext.m
 
 X11 Backend Graphics Context class.  Conceptually, instances of 
 this subclass encapsulate a connection to an X display (X server).
 
 Copyright (C) 1998 Free Software Foundation, Inc.
 
 Author:  Felipe A. Rodriguez <far@pcmagic.net>
 Date:    November 1998
 
 Author:	H. N. Schaller <hns@computer.org>
 Date:		Jan 2006 - completely reworked

 Useful Manuals:
	http://tronche.com/gui/x/xlib											Xlib - basic X11 calls
    http://freetype.sourceforge.net/freetype2/docs/reference/ft2-toc.html	libFreetype2 - API
	http://netmirror.org/mirror/xfree86.org/4.4.0/doc/HTML/Xft.3.html		Xft - freetype glue
	http://netmirror.org/mirror/xfree86.org/4.4.0/doc/HTML/Xrandr.3.html	XResize - rotate extension
	http://netmirror.org/mirror/xfree86.org/4.4.0/doc/HTML/Xrender.3.html	XRender - antialiased, alpha, subpixel rendering
 
 This file is part of the mySTEP Library and is provided
 under the terms of the GNU Library General Public License.
 */ 

/* Notes when dealing with X11:
  - if we need to check that an object is not rotated, check that ctm transformStruct.m12 and .m21 both are 0.0
  - we can't handle any rotation for text drawing (yet)
  - we can't rotatate windows by angles not multiples of 90 degrees
  - note that X11 coordinates are flipped. This is taken into account by the _screen2X11 CTM.
  - But: for NSSizes you have to use -height because of that
  - finally, drawing into a window is relative to the origin
*/

#import "NSX11GraphicsContext.h"

// load full headers (to expand @class forward references)

#import "NSAppKitPrivate.h"
#import "NSApplication.h"
#import "NSAttributedString.h"
#import "NSBezierPath.h"
#import "NSColor.h"
#import "NSCursor.h"
#import "NSFont.h"
#import "NSGraphics.h"
#import "NSGraphicsContext.h"
#import "NSImage.h"
#import "NSScreen.h"
#import "NSWindow.h"
#import "NSPasteboard.h"

#if 1	// all windows are borderless, i.e. the frontend draws the title bar and manages windows directly
#define WINDOW_MANAGER_TITLE_HEIGHT 0
#else
#define WINDOW_MANAGER_TITLE_HEIGHT 23	// number of pixels added by window manager - the content view is moved down by that amount
#endif

#if __linux__	// this is needed for Sharp Zaurus (only) to detect the hinge status
#include <sys/ioctl.h>
#define SCRCTL_GET_ROTATION 0x413c
#endif

typedef struct
{ // WindowMaker window manager support
    CARD32 flags;
    CARD32 window_style;
    CARD32 window_level;
    CARD32 reserved;
    Pixmap miniaturize_pixmap;			// pixmap for miniaturize button 
    Pixmap close_pixmap;				// pixmap for close button 
    Pixmap miniaturize_mask;			// miniaturize pixmap mask 
    Pixmap close_mask;					// close pixmap mask 
    CARD32 extra_flags;
} GSAttributes;

#define GSWindowStyleAttr 					(1<<0)
#define GSWindowLevelAttr 					(1<<1)
#define GSMiniaturizePixmapAttr				(1<<3)
#define GSClosePixmapAttr					(1<<4)
#define GSMiniaturizeMaskAttr				(1<<5)
#define GSCloseMaskAttr						(1<<6)
#define GSExtraFlagsAttr       				(1<<7)

#define GSDocumentEditedFlag				(1<<0)			// extra flags
#define GSWindowWillResizeNotificationsFlag (1<<1)
#define GSWindowWillMoveNotificationsFlag 	(1<<2)
#define GSNoApplicationIconFlag				(1<<5)

#define WMFHideOtherApplications			10
#define WMFHideApplication					12

//
// Class variables
//

static Display *_display;		// we can currently manage only one Display - but several Screens

static Atom _stateAtom;
static Atom _protocolsAtom;
static Atom _deleteWindowAtom;
static Atom _windowDecorAtom;

static NSArray *_XRunloopModes;	// runloop modes to handle X11 events

#if 1	// OLD
Window __xKeyWindowNeedsFocus = None;			// xWindow waiting to be focusd
extern Window __xAppTileWindow;
#endif

#if 1	// DEPRECATED - only used in XRView to modify dragging operations - should be handled through the backend interface
unsigned int __modFlags = 0;		// current global modifier flags - updated every keyDown/keyUp event
#endif

static NSMapTable *__WindowNumToNSWindow = NULL;	// map Window to NSWindow

//
//  Private functions
//

static unsigned int xKeyModifierFlags(unsigned int state);
static unsigned short xKeyCode(XEvent *xEvent, KeySym keysym, unsigned int *eventModFlags);
extern void xHandleSelectionRequest(XSelectionRequestEvent *xe);

@implementation _NSX11GraphicsContext

- (id) _initWithAttributes:(NSDictionary *) attributes;
{
	NSWindow *window;
	Window win;
	unsigned long valuemask = 0;
	XSetWindowAttributes winattrs;
	XSizeHints size_hints;
	XWMHints *wm_hints;
	GSAttributes attrs;
	NSRect frame;
	int styleMask;
	NSBackingStoreType backingType;
#if 0
	NSLog(@"_NSX11GraphicsContext _initWithAttributes:%@", attributes);
#endif
	window=[attributes objectForKey:NSGraphicsContextDestinationAttributeName];
	frame=[window frame];	// window frame in screen coordinates
#if 0
	NSLog(@"window frame=%@", NSStringFromRect(frame));
#endif
	styleMask=[window styleMask];
	backingType=[window backingType];
	_nsscreen=[window screen];
	if(![window isKindOfClass:[NSWindow class]])
		{ [self release]; return nil; }	// must provide a NSWindow
	_screen=(Screen *) (_nsscreen->_backendPrivate1);
	// check that there isn't a non-rectangular rotation!
	_windowRect=frame;
	frame.origin=[(NSAffineTransform *) (_nsscreen->_screen2X11) transformPoint:frame.origin];
	frame.size=[(NSAffineTransform *) (_nsscreen->_screen2X11) transformSize:frame.size];
#if 0
	NSLog(@"transformed window frame=%@", NSStringFromRect(frame));
#endif
	if(NSWidth(frame) < 1.0 || NSHeight(frame) > -1.0)
		frame.size = (NSSize){48,-49};			// X doesn't like 0 siz
	if(!(wm_hints = XAllocWMHints())) 
		[NSException raise:NSMallocException format:@"XAllocWMHints() failed"];
//	winattrs.border_pixel=BlackPixelOfScreen(_screen);		
//	winattrs.background_pixel=[[NSColor windowBackgroundColor] _pixelForScreen:_screen];	// default pixel value for background
//	valuemask |= CWBackPixel | CWBorderPixel;
#if (WINDOW_MANAGER_TITLE_HEIGHT==0)	// always hide from window manager
	winattrs.override_redirect = True;
	valuemask |= CWOverrideRedirect;
#else
	// FIXME: should be for all windows
	if((styleMask&GSAllWindowMask) == NSBorderlessWindowMask)
		{ // set X override if borderless
		valuemask |= CWOverrideRedirect;
		winattrs.override_redirect = True;
		valuemask |= CWSaveUnder;
		winattrs.save_under = True;
		}
	else
		frame.origin.y -= WINDOW_MANAGER_TITLE_HEIGHT;   // if window manager moves window down by that amount!
#endif
#if 1
	frame.size.height=-frame.size.height;
	NSLog(@"XCreateWindow(%@)", NSStringFromRect(frame));
	frame.size.height=-frame.size.height;
#endif
	win=XCreateWindow(_display,
					  RootWindowOfScreen(_screen),			// create an X window on the screen defined by RootWindow
					  size_hints.x=(int) (frame.origin.x),
					  size_hints.y=(int) (frame.origin.y+frame.size.height),
					  (int) NSWidth(frame),
					  (int) -NSHeight(frame),					// screen2X11 has flipped coordinates
					  0,
					  CopyFromParent,
					  CopyFromParent,
					  CopyFromParent,
					  valuemask,
					  &winattrs);
	if(!win)
		NSLog(@"did not create Window");
	if(!__WindowNumToNSWindow)
		__WindowNumToNSWindow=NSCreateMapTable(NSIntMapKeyCallBacks,
											   NSNonRetainedObjectMapValueCallBacks, 20);
	NSMapInsert(__WindowNumToNSWindow, (void *) win, window);		// X11 Window to NSWindow
#if 0
	NSLog(@"NSWindow number=%lu", win);
	NSLog(@"Window list: %@", NSAllMapTableValues(__WindowNumToNSWindow));
#endif
	self=[self _initWithGraphicsPort:(void *) win];
	if(backingType ==  NSBackingStoreBuffered && 1 /* not disabled by -NSNotDubleBuffered or similar */)
		{
		// allocate pixmap for background buffer
		}
#if OLD
	_frontWindow=(void *) XCreateWindow(_display,
							RootWindowOfScreen(_screen),			// create an X window on the screen defined by RootWindow
							size_hints.x=(int) (frame.origin.x),
							size_hints.y=(int) (frame.origin.y+frame.size.height),
							(int) NSWidth(frame),
							(int) -NSHeight(frame),					// screen2X11 has flipped coordinates
							0,
							CopyFromParent,
							CopyFromParent,
							CopyFromParent,
							valuemask,
							&winattrs);
	// everything that follows should be done by _initWithGraphicsPort:
	if(!_frontWindow)
		NSLog(@"did not create Window");
	// if double buffered, allocate background pixmap
	_graphicsPort=_frontWindow;
	_windowNum=(int) _window;	// we should get a system-wide unique integer from the window-list/window-level-manager, e.g. a slot #
#endif
	size_hints.flags = PPosition | USPosition;		
	XSetNormalHints(_display, _window, &size_hints);	// tell window manager...
#if OLD
	[self saveGraphicsState];	// initialize graphics state with transformations, GC etc. - don't use anything which depends on graphics state before here!
	// Set the X event mask 
	XSelectInput(_display, _window,
				 ExposureMask | KeyPressMask | 
				 KeyReleaseMask | ButtonPressMask | 
				 ButtonReleaseMask | ButtonMotionMask | 
				 StructureNotifyMask | PointerMotionMask | 
				 EnterWindowMask | LeaveWindowMask | 
				 FocusChangeMask | PropertyChangeMask | 
				 ColormapChangeMask | KeymapStateMask | 
				 VisibilityChangeMask);
#endif
	wm_hints->initial_state = NormalState;			// set window manager hints
	wm_hints->input = True;								
	wm_hints->flags = StateHint | InputHint;		// WindowMaker ignores the
	XSetWMHints(_display, _window, wm_hints);		// frame origin unless it's also specified as a hint 
	/*
	 *
	 */
	if(styleMask&NSUnscaledWindowMask)
		{ // set 1:1 transform (here or in NSWindow???)
		}
	if((styleMask & NSClosableWindowMask))			// if window has close, button inform WM 
		XSetWMProtocols(_display, _window, &_deleteWindowAtom, 1);
	attrs.window_level = [window level];
	attrs.flags = GSWindowStyleAttr|GSWindowLevelAttr;
	attrs.window_style = (styleMask & GSAllWindowMask);		// set WindowMaker WM
	XChangeProperty(_display, _window, _windowDecorAtom, _windowDecorAtom,		// window style hints
					32, PropModeReplace, (unsigned char *)&attrs,
					sizeof(GSAttributes)/sizeof(CARD32));
	XFree(wm_hints);
	return self;
}

- (id) _initWithGraphicsPort:(void *) port;
{ // port should be the X11 Window *
#if 0
	NSLog(@"_NSX11GraphicsContext _initWithGraphicsPort:%@", attributes);
#endif
#if FIXME
	// get NSScreen/screen from port (Window *)
	_nsscreen=[window screen];
	_screen=(Screen *) (_nsscreen->_backendPrivate1);
	// read window size
	_windowRect=frame;
	// get size hints
#endif
	_graphicsPort=port;	// _window is a typed alias for _graphicsPort
	_frontWindow=(Window) port;	// default is unbuffered
	_windowNum=(int) _window;	// we should get a system-wide unique integer (slot #) from the window list/level manager
	[self saveGraphicsState];	// initialize graphics state with transformations, GC etc. - don't use anything which depends on graphics state before here!
	XSelectInput(_display, _window,
				 ExposureMask | KeyPressMask | 
				 KeyReleaseMask | ButtonPressMask | 
				 ButtonReleaseMask | ButtonMotionMask | 
				 StructureNotifyMask | PointerMotionMask | 
				 EnterWindowMask | LeaveWindowMask | 
				 FocusChangeMask | PropertyChangeMask | 
				 ColormapChangeMask | KeymapStateMask | 
				 VisibilityChangeMask);
	// query server for extensions
	return self;
}

- (void) dealloc
{
	if(_window)
		{
		NSMapRemove(__WindowNumToNSWindow, (void *) _windowNum);	// Remove X11 Window to NSWindows mapping
		XDestroyWindow(_display, _window);							// Destroy the X Window
		}
	if(_isDoubleBuffered)
/* XFreePixmap(_display, _backWindow) */;
	// here we could check if we were the last window and XDestroyWindow(_display, xAppRootWindow); XCloseDisplay(_display);
	[super dealloc];
}

- (BOOL) isDrawingToScreen	{ return YES; }

// NSBackend interface

- (void) _setColor:(NSColor *) color;
{
	unsigned long pixel=[color _pixelForScreen:_screen];
#if 0
	NSLog(@"_setColor -> pixel=%08x", pixel);
#endif
	XSetBackground(_display, _state->_gc, pixel);
	XSetForeground(_display, _state->_gc, pixel);
}

- (void) _setFillColor:(NSColor *) color;
{
	unsigned long pixel=[color _pixelForScreen:_screen];
#if 0
	NSLog(@"_setColor -> pixel=%08x", pixel);
#endif
	XSetBackground(_display, _state->_gc, pixel);
	// FIXME: X11 uses the foreground color for filling!
	XSetForeground(_display, _state->_gc, pixel);
}

- (void) _setStrokeColor:(NSColor *) color;
{
	unsigned long pixel=[color _pixelForScreen:_screen];
#if 0
	NSLog(@"_setColor -> pixel=%08x", pixel);
#endif
	XSetForeground(_display, _state->_gc, pixel);
}

- (void) _setCTM:(NSAffineTransform *) atm;
{ // translate window base coordinates to window-relative X11 coordinates
	[_state->_ctm release];
	_state->_ctm=[(NSAffineTransform *) (_nsscreen->_screen2X11) copy];	// make these coordinates window-relative
	[_state->_ctm translateXBy:0.0 yBy:HeightOfScreen((Screen *) _screen)-_windowRect.size.height];		// use window relative coordinates for drawing
	[_state->_ctm prependTransform:atm];
#if 0
	NSLog(@"_setCTM -> %@", _state->_ctm);
#endif
}

- (void) _concatCTM:(NSAffineTransform *) atm;
{
	[_state->_ctm prependTransform:atm];
#if 0
	NSLog(@"_concatCTM -> %@", _state->_ctm);
#endif
}

#define Point2Pixel(X) ((int)(X))	// multiply with userSpaceScale factor of current NSScreen! + convert 0 to 1!

static int _cs[]=
{ // translate cap style
	CapButt,	// NSButtLineCapStyle
	CapRound,	// NSRoundLineCapStyle
	CapNotLast,	// NSSquareLineCapStyle
	CapNotLast	// ?
};

static int _js[]=
{ // translate join style
	JoinMiter,	// NSMiterLineJoinStyle
	JoinRound,	// NSRoundLineJoinStyle
	JoinBevel,	// NSBevelLineJoinStyle
	JoinBevel	// ?
};

typedef struct _PointsForPathState
{
	NSBezierPath *path;
	unsigned element;	// current element being expanded
	unsigned elements;	// number of elements in path
	XPoint *points;		// points array
	XPoint lastpoint;
	int npoints;		// number of entries in array
	unsigned capacity;	// how many elements are allocated
} PointsForPathState;

static inline void addPoint(PointsForPathState *state, NSPoint point)
{
	XPoint pnt;
	if(state->npoints >= state->capacity)
		state->points=(XPoint *) objc_realloc(state->points, sizeof(state->points[0])*(state->capacity=2*state->capacity+5));	// make more room
	pnt.x=point.x;		// convert to integer
	pnt.y=point.y;
	if(state->npoints == 0 || pnt.x != state->lastpoint.x || pnt.y != state->lastpoint.y)
		{ // first or really different
		state->lastpoint=pnt;
		state->points[state->npoints++]=pnt;	// store point
		}
#if 0
	else
		NSLog(@"addPoint duplicate ignored:(%d, %d)", pnt.x, pnt.y);
#endif
#if 0
	NSLog(@"addPoint:(%d, %d)", (int) point.x, (int) point.y);
#endif
}

// CHECKME if this is really triggered by rectangle primitives and e.g. NSSegmentedCell

- (BOOL) _rectForPath:(PointsForPathState *) state rect:(XRectangle *) rect;
{ // check if points[] describe a simple Rectangle (clockwise orientation)
	if(state->npoints != 5)
		return NO;
	if((state->points[0].x == state->points[1].x) &&
	   (state->points[1].y == state->points[2].y) &&
	   (state->points[2].x == state->points[3].x) &&
	   (state->points[3].y == state->points[4].y))
		{
		rect->x=state->points[0].x;
		rect->y=state->points[0].y;
		if(state->points[3].x < state->points[0].x || state->points[1].y < state->points[0].y)
			return NO;
		rect->width=state->points[3].x-state->points[0].x;
		rect->height=state->points[1].y-state->points[0].y;
		return YES;
		}
	return NO;
}

- (BOOL) _pointsForPath:(PointsForPathState *) state;
{ // process next part - return YES if anything found
	NSPoint points[3];
	NSPoint first, current, next;
	if(state->element == 0)
		state->elements=[state->path elementCount];	// initialize
	if(state->element >= state->elements)
		{
		if(state->points)
			objc_free(state->points);	// release buffer
		return NO;	// all done
		}
	state->npoints=0;
	while(state->element < state->elements)
		{
		// (re)alloc points array as needed
		switch([state->path elementAtIndex:state->element associatedPoints:points])
			{
			case NSMoveToBezierPathElement:
				current=first=[_state->_ctm transformPoint:points[0]];
				addPoint(state, current);
				break;
			case NSLineToBezierPathElement:
				next=[_state->_ctm transformPoint:points[0]];
				addPoint(state, next);
				current=next;
				break;
			case NSCurveToBezierPathElement:
				{

				// should better create path by algorithm like the following:
				
				// http://www.niksula.cs.hut.fi/~hkankaan/Homepages/bezierfast.html
				// or http://www.antigrain.com/research/adaptive_bezier/
				
				// but: there might even be a better algorithm that resembles Bresenham or CORDIC that
				//
				// - works with integer values
				// - moves one pixel per step either in x or y direction
				// - is not based on a predefined number of steps
				// - uses screen resolution as the smoothness limit
				//
				NSPoint p0=current;
				NSPoint p1=[_state->_ctm transformPoint:points[0]];
				NSPoint p2=[_state->_ctm transformPoint:points[1]];
				NSPoint p3=[_state->_ctm transformPoint:points[2]];
				float t;
#if 0
				NSLog(@"pointsForPath: curved element");
#endif
				// FIXME: we should adjust the step size to the size of the path
				for(t=0.1; t<=0.9; t+=0.1)
					{ // very simple and slow approximation
					float t1=(1.0-t);
					float t12=t1*t1;
					float t13=t1*t12;
					float t2=t*t;
					float t3=t*t2;
					NSPoint pnt;
					pnt.x=p0.x*t13+3.0*(p1.x*t*t12+p2.x*t2*t1)+p3.x*t3;
					pnt.y=p0.y*t13+3.0*(p1.y*t*t12+p2.y*t2*t1)+p3.y*t3;
					addPoint(state, pnt);
					}
				addPoint(state, next=p3);	// move to final point (if not already there)
				current=next;
				break;
				}
			case NSClosePathBezierPathElement:
				addPoint(state, first);
				break;
			}
		state->element++;
		}	
	return YES;
}

- (void) _stroke:(NSBezierPath *) path;
{
	PointsForPathState state={ path };
	float *pattern=NULL;	// FIXME: who is owner of this data? and who takes care not to overflow?
	int count;
	float phase;
#if 0
	NSLog(@"_stroke");
#endif
	[path getLineDash:pattern count:&count phase:&phase];		
	XSetLineAttributes(_display, _state->_gc,
					   Point2Pixel([path lineWidth]),
					   count == 0 ? LineSolid : LineOnOffDash,
					   _cs[[path lineCapStyle]&0x03],
					   _js[[path lineJoinStyle]&0x03]
					   );
	if(count)
		{
		char dash_list[count];	// FIXME: this can overflow stack! => security risk by bad PDF files
		int i;
		for(i = 0; i < count; i++)
			dash_list[i] = (char) pattern[i];		
		XSetDashes(_display, _state->_gc, phase, dash_list, count);
		}
	while([self _pointsForPath:&state])
		XDrawLines(_display, _window, _state->_gc, state.points, state.npoints, CoordModeOrigin);
}

- (void) _fill:(NSBezierPath *) path;
{
	PointsForPathState state={ path };
#if 0
	NSLog(@"_fill");
#endif
	// FIXME: we have to temporarily swap background & foreground colors since X11 uses the FG color to fill!
	XSetFillStyle(_display, _state->_gc, FillSolid);
	XSetFillRule(_display, _state->_gc, [path windingRule] == NSNonZeroWindingRule?WindingRule:EvenOddRule);
	while([self _pointsForPath:&state])
		{
		XRectangle rect;
		if([self _rectForPath:&state rect:&rect])
			XFillRectangles(_display, _window, _state->_gc, &rect, 1);
		else
			XFillPolygon(_display, _window, _state->_gc, state.points, state.npoints, Complex, CoordModeOrigin);
		}
}

- (Region) _regionFromPath:(NSBezierPath *) path
{ // get region from path
	PointsForPathState state={ path };
	Region region=NULL;
	while([self _pointsForPath:&state])
		{
		if(!region)
			region=XPolygonRegion(state.points, state.npoints, [path windingRule] == NSNonZeroWindingRule?WindingRule:EvenOddRule);
		else
			; // else  FIXME: build the Union or intersection of both (depending on winding rule)
		}
	return region;
}

- (void) _setClip:(NSBezierPath *) path;
{
#if 0
	NSLog(@"_setClip");
#endif
	if(_state->_clip)
		XDestroyRegion(_state->_clip);	// delete previous
	_state->_clip=[self _regionFromPath:path];
	// check for Rect region
	XSetRegion(_display, _state->_gc, _state->_clip);
#if 0
	{
		XRectangle box;
		XClipBox(_state->_clip, &box);
		NSLog(@"_setClip box=((%d,%d),(%d,%d))", box.x, box.y, box.width, box.height);
	}
#endif
}

- (void) _addClip:(NSBezierPath *) path;
{
	Region r;
#if 0
	NSLog(@"_addClip");
#endif
	r=[self _regionFromPath:path];
	if(_state->_clip)
		{
#if 0
		{
			XRectangle box;
			XClipBox(r, &box);
			NSLog(@"_addClip box=((%d,%d),(%d,%d))", box.x, box.y, box.width, box.height);
			XClipBox(_state->_clip, &box);
			NSLog(@"      to box=((%d,%d),(%d,%d))", box.x, box.y, box.width, box.height);
		}
#endif
		XIntersectRegion(_state->_clip, r, _state->_clip);
		XDestroyRegion(r);	// no longer needed
		}
	else
		_state->_clip=r;	// first call
	XSetRegion(_display, _state->_gc, _state->_clip);
#if 0
	{
		XRectangle box;
		XClipBox(_state->_clip, &box);
		NSLog(@"         box=((%d,%d),(%d,%d))", box.x, box.y, box.width, box.height);
	}
#endif
}

- (NSRect) _clipBox;
{
	// could use XClipBox(_state->_clip, rect clip) -- to determine where to really draw/fill
	return NSZeroRect;
}

- (void) _setShadow:(NSShadow *) shadow;
{
	NIMP; 
}

- (void) _setShape:(NSBezierPath *) path;
{ // set window shape - the filled path defines the non-transparent area (needs Xext)
	Region region=[self _regionFromPath:path];
#if 0
	NSLog(@"_setShape: %@", self);
#endif
#if 0
	{ // check the result...
		Bool bounding_shaped, clip_shaped;
		int x_bounding, y_bounding, x_clip, y_clip;
		unsigned int w_bounding, h_bounding, w_clip, h_clip;
		XShapeQueryExtents(_display, _window, 
						   &bounding_shaped, 
						   &x_bounding, &y_bounding,
						   &w_bounding, &h_bounding,
						   &clip_shaped, 
						   &x_clip, &y_clip, &w_clip, &h_clip);
		NSLog(@"before %@%@ b:(%d, %d, %u, %u) clip:(%d, %d, %u, %u)",
			  bounding_shaped?@"bounding shaped ":@"", 
			  clip_shaped?@"bounding shaped ":@"", 
			  x_bounding, y_bounding, w_bounding, h_bounding,
			  x_clip, y_clip, w_clip, h_clip);
	}
#endif
	XShapeCombineRegion(_display, _window,
						ShapeClip,
						0, 0,
						region,
						ShapeSet);
	XShapeCombineRegion(_display, _window,
						ShapeBounding,
						0, 0,
						region,
						ShapeSet);
	XDestroyRegion(region);
	// ...inking also needs an overlaid InputOnly window to receive events at all pixels
#if 0
	{ // check the result...
		Bool bounding_shaped, clip_shaped;
		int x_bounding, y_bounding, x_clip, y_clip;
		unsigned int w_bounding, h_bounding, w_clip, h_clip;
		XShapeQueryExtents(_display, _window, 
						   &bounding_shaped, 
						   &x_bounding, &y_bounding,
						   &w_bounding, &h_bounding,
						   &clip_shaped, 
						   &x_clip, &y_clip, &w_clip, &h_clip);
		NSLog(@"after %@%@ b:(%d, %d, %u, %u) clip:(%d, %d, %u, %u)",
			  bounding_shaped?@"bounding shaped ":@"", 
			  clip_shaped?@"clip shaped ":@"", 
			  x_bounding, y_bounding, w_bounding, h_bounding,
			  x_clip, y_clip, w_clip, h_clip);
	}
#endif
}

- (void) _setFont:(NSFont *) font;
{
	// FIXME: scale font size with current CTM!!!
	//     -> we need to cache several fonts!
	_state->_font=*[font _font];
	XSetFont(_display, _state->_gc, _state->_font.fid);	// set font-ID
}

#define ASCENDER _state->_font.ascent
#define DESCENDER _state->_font.descent
// FIXME: XFontStruct would know ascender/descender/width for each individual character code
#define WIDTH (16)
#define HEIGHT (ASCENDER+DESCENDER)
#define LINE_SPACING (HEIGHT+5)

- (void) _beginText; { return; }	// do nothing
- (void) _endText; { return; }

- (void) _setTextPosition:(NSPoint) pos;
{ // PDF: x y Td
	_cursor=[_state->_ctm transformPoint:pos];
	_indent=_cursor.x;
#if 0
	NSLog(@"_setTextPosition %@ -> %@", NSStringFromPoint(pos), NSStringFromPoint(_cursor));
#endif
}

- (void) _setLeading:(float) lead;
{ // PDF: x TL
}

// FIXME: this does not properly handle rotated coords and newline

- (void) _newLine;
{ // PDF: T*
//	NSSize characterSize=NSMakeSize(WIDTH, HEIGHT);		// should be determined for each character!
	_cursor.y+=LINE_SPACING;	// go to next line
	_cursor.x=_indent;
#if 0
	NSLog(@"_newLine -> %@", NSStringFromPoint(_cursor));
#endif
}

// we need a command to set x-pos (only)


- (NSSize) _string:(NSString *) string withAttributes:(NSDictionary *) attr draw:(BOOL) draw;
{ // draw string fragment with constant attributes and return size-  PDF: (string) Tj
	id attrib;
	unsigned length=[string length];
	static unichar *buf;
	static unsigned buflen;	// how much is allocated
	float baseline=0.0;
	float width;
#if 0
	if(draw)
		NSLog(@"NSString: _string:%@ withAttributes:%@ at {%f,%f}", string, attr, _cursor.x, _cursor.y);
#endif
	attrib=[attr objectForKey:NSFontAttributeName];
	if(!attrib)
		attrib=[NSFont systemFontOfSize:[NSFont systemFontSize]];
	[self _setFont:attrib];
	if(draw)
		{ // set attributes that don't influence size
		if((attrib=[attr objectForKey:NSForegroundColorAttributeName]))
			[self _setColor:attrib];
		else
			[self _setColor:[NSColor blackColor]];
		if((attrib=[attr objectForKey:NSStrokeColorAttributeName]))
			[self _setStrokeColor:attrib];
		if((attrib=[attr objectForKey:NSBackgroundColorAttributeName]))
			; // [self _setColor:attrib];
		if((attrib=[attr objectForKey:NSBaselineOffsetAttributeName]))
			baseline=[attrib floatValue];
		if((attrib=[attr objectForKey:NSSuperscriptAttributeName]))
			baseline+=3.0*[attrib intValue];
		// set any other attributes
		}
	if(buflen < sizeof(buf[0])*length)
		buf=(unichar *) objc_realloc(buf, buflen+=sizeof(buf[0])*(length+20));	// increase buffer size
	[string getCharacters:buf];
	if(NSHostByteOrder() == NS_LittleEndian)
		{ // swap all bytes
		int i;
		for(i=0; i<length; i++)
			buf[i]=NSSwapShort(buf[i]);
		}
	if(draw)
		{
#if 0
		{
			XRectangle box;
			XClipBox(_state->_clip, &box);
			NSLog(@"draw string %@ at (%d,%d) box=((%d,%d),(%d,%d))", string, (int)_cursor.x, (int)(_cursor.y-baseline+ASCENDER+1), box.x, box.y, box.width, box.height);
		}
#endif
		XDrawString16(_display, _window,
					  _state->_gc, 
					  _cursor.x, (int)(_cursor.y-baseline+ASCENDER+1),	// X11 defines y as the character baseline
					  (XChar2b *) buf,
					  length);		// Unicode drawing
		}
	width=XTextWidth16(&_state->_font, (XChar2b *) buf, length);
	if(draw)
		_cursor.x+=width;	// advance cursor accordingly
	return NSMakeSize(width, HEIGHT);	// return size of character box
}

- (void) _setFraction:(float) fraction;
{
	if(fraction > 1.0)
		_fraction=1.0;
	else if(fraction < 0.0)
		_fraction=0.0;
	else
		_fraction=fraction;	// save compositing fraction - fixme: convert to 0..256-integer
}

static void XIntersect(XRectangle *result, XRectangle *with)
{
	if(with->x > result->x+result->width)
		result->width=0;	// second box is completely to the right
	else if(with->x > result->x)
		result->width-=(with->x-result->x), result->x=with->x;	// new left border
	if(with->x+with->width < result->x)
		result->width=0;	// second box is completely to the left
	else if(with->x+with->width < result->x+result->width)
		result->width=with->x+with->width-result->x;	// new right border
	if(with->y > result->y+result->height)
		result->height=0;
	else if(with->y > result->y)
		result->height-=(with->y-result->y), result->y=with->y;
	if(with->y+with->height < result->y)
		result->height=0;	// empty
	else if(with->y+with->height < result->y+result->height)
		result->height=with->y+with->height-result->y;
}

struct RGBA8
{ // 8 bit per channel RGBA
	unsigned char R, G, B, A;
};

inline static struct RGBA8 getPixel(int x, int y,
							int pixelsWide,
							int pixelsHigh,
							/*
							 int bitsPerSample,
							 int samplesPerPixel,
							 int bitsPerPixel,
							 */
							int bytesPerRow,
							BOOL isPlanar,
							BOOL hasAlpha, 
							unsigned char *data[5])
{ // extract RGBA8 value of given pixel from bitmap
	int offset;
	struct RGBA8 src;
	if(x < 0 || y < 0 || x >= pixelsWide || y >= pixelsHigh)
		{ // transparent
		src.R=0;
		src.G=0;
		src.B=0;
		src.A=0;
		}
	else if(isPlanar)
		{ // planar
		offset=x+bytesPerRow*y;
		src.R=data[0][offset];
		src.G=data[1][offset];
		src.B=data[2][offset];
		if(hasAlpha)
			src.A=data[3][offset];
		else
			src.A=255;	// opaque
		}
	else
		{ // meshed
		offset=(hasAlpha?4:3)*x + bytesPerRow*y;
		src.R=data[0][offset+0];	// compiler should be able to optimize constant expression data[0]
		src.G=data[0][offset+1];
		src.B=data[0][offset+2];
		if(hasAlpha)
			src.A=data[0][offset+3];
		else
			src.A=255;	// opaque
		}
	return src;
}

inline static struct RGBA8 XGetRGBA8(XImage *img, int x, int y)
{ // get RGBA8
	unsigned int pixel=XGetPixel(img, x, y);
	struct RGBA8 dest;
	switch(img->depth)
		{
		case 24:
			{
				dest.R=(pixel>>0);
				dest.G=(pixel>>8);
				dest.B=(pixel>>16);
				break;
			}
		case 16:
			{ // scale 0..31 to 0..255
				// a better? algorithm could be val8bit=(val5bit<<3)+(val5bit>>2), i.e. fill the less significant bits with a copy of the more significant
				unsigned char tab5[]={ (0*255)/31,(1*255)/31,(2*255)/31,(3*255)/31,(4*255)/31,(5*255)/31,(6*255)/31,(7*255)/31,
					(8*255)/31,(9*255)/31,(10*255)/31,(11*255)/31,(12*255)/31,(13*255)/31,(14*255)/31,(15*255)/31,
					(16*255)/31,(17*255)/31,(18*255)/31,(19*255)/31,(20*255)/31,(21*255)/31,(22*255)/31,(23*255)/31,
					(24*255)/31,(25*255)/31,(26*255)/31,(27*255)/31,(28*255)/31,(29*255)/31,(30*255)/31,(31*255)/31 };
				unsigned char tab6[]={ (0*255)/63,(1*255)/63,(2*255)/63,(3*255)/63,(4*255)/63,(5*255)/63,(6*255)/63,(7*255)/63,
					(8*255)/63,(9*255)/63,(10*255)/63,(11*255)/63,(12*255)/63,(13*255)/63,(14*255)/63,(15*255)/63,
					(16*255)/63,(17*255)/63,(18*255)/63,(19*255)/63,(20*255)/63,(21*255)/63,(22*255)/63,(23*255)/63,
					(24*255)/63,(25*255)/63,(26*255)/63,(27*255)/63,(28*255)/63,(29*255)/63,(30*255)/63,(31*255)/63,
					(32*255)/63,(33*255)/63,(34*255)/63,(35*255)/63,(36*255)/63,(37*255)/63,(38*255)/63,(39*255)/63,
					(40*255)/63,(41*255)/63,(42*255)/63,(43*255)/63,(44*255)/63,(45*255)/63,(46*255)/63,(47*255)/63,
					(48*255)/63,(49*255)/63,(50*255)/63,(51*255)/63,(52*255)/63,(53*255)/63,(54*255)/63,(55*255)/63,
					(56*255)/63,(57*255)/63,(58*255)/63,(59*255)/63,(60*255)/63,(61*255)/63,(62*255)/63,(63*255)/63 };
				dest.R=tab5[(pixel>>11)&0x1f];	// highest 5 bit
				dest.G=tab6[(pixel>>5)&0x3f];	// middle 6 bit
				dest.B=tab5[pixel&0x1f];		// lowest 5 bit
			}
		}
	dest.A=255;
	return dest;
}

/* idea for sort of core-image extension
 *
 * struct filter { struct RGBA8 (*filter)(float x, float y); struct filter *input; other paramters }; describes a generic filter node
 * 
 * now, build a chain of filter modules, i.e.
 * 0. scanline RGBA to output image
 * 1. composite with a second image (i.e. the image fetched from screen)
 * 2. rotate&scale coordinates
 * 3. sample/interpolate
 * 4. fetch as RGBA from given bitmap
 * could add color space transforms etc.
 *
 */

- (BOOL) _draw:(NSImageRep *) rep;
{ // composite into unit square using current CTM, current compositingOp & fraction etc.

/* here we know:
- source bitmap: rep
- source rect: defined indirectly by clipping path
- clipping path (we only need to scan-line and interpolate visible pixels): _state->_clip
- compositing operation: _compositingOperation
- compositing fraction: _fraction
- interpolation algorithm: _imageInterpolation
- CTM (scales, rotates and translates): _state->_ctm
-- how do we know if we should really rotate or not? we don't need to know.
*/
	static NSRect unitSquare={{ 0.0, 0.0 }, { 1.0, 1.0 }};
	NSString *csp;
	int bytesPerRow;
	BOOL hasAlpha;
	BOOL isPlanar;
	float width, height;	// source image width&height
	unsigned char *imagePlanes[5];
	NSPoint origin;
	XRectangle box;			// relevant subarea to draw to
	NSRect scanRect;		// dest on screen in X11 coords
	BOOL isFlipped;
	NSAffineTransform *atm;	// projection from X11 window-relative to bitmap coordinates
	NSAffineTransformStruct atms;
	XRectangle xScanRect;	// on X11 where XImage is coming from
	XImage *img;
	int x, y;				// current position within XImage
	NSPoint pnt;			// current pixel in bitmap
	unsigned int fract=256.0*_fraction+0.5;
	/*
	 * check if we can draw
	 */
	if(!rep)	// could check for NSBitmapImageRep
		{
		NSLog(@"_draw: nil representation!");
		// raise exception
		return NO;
		}
	csp=[rep colorSpaceName];
	if(![csp isEqualToString:NSCalibratedRGBColorSpace] && ![csp isEqualToString:NSDeviceRGBColorSpace])
		{
		NSLog(@"_draw: colorSpace %@ not supported!", csp);
		// raise exception?
		return NO;
		}
	/*
	 * locate where to draw in X11 coordinates
	 */
	isFlipped=[self isFlipped];
	origin=[_state->_ctm transformPoint:NSZeroPoint];	// determine real drawing origin in X11 coordinates
	scanRect=[_state->_ctm _boundingRectForTransformedRect:unitSquare];	// get bounding box for transformed unit square
#if 0
	NSLog(@"scan rect=%@", NSStringFromRect(scanRect));
#endif
	xScanRect.width=scanRect.size.width;
	xScanRect.height=scanRect.size.height;
	xScanRect.x=scanRect.origin.x;
	xScanRect.y=scanRect.origin.y;	// X11 specifies upper left corner
#if 0
	NSLog(@"  scan box=((%d,%d),(%d,%d))", xScanRect.x, xScanRect.y, xScanRect.width, xScanRect.height);
#endif
	/*
	 * clip to visible area (by clipping box, window and screen
	 */
	XClipBox(_state->_clip, &box);
#if 0
	NSLog(@"  clip box=((%d,%d),(%d,%d))", box.x, box.y, box.width, box.height);
#endif
	XIntersect(&xScanRect, &box);
#if 0
	NSLog(@"  intersected scan box=((%d,%d),(%d,%d))", xScanRect.x, xScanRect.y, xScanRect.width, xScanRect.height);
#endif
	if(xScanRect.width == 0 || xScanRect.height == 0)
		return YES;	// empty
	// shrink by screen (if window is partially offscreen)
	/*
	 * calculate reverse projection from XImage pixel coordinate to bitmap coordinate
	 */
	atm=[NSAffineTransform transform];
	[atm translateXBy:-origin.x yBy:-origin.y];		// we will scan through XImage which is thought to be relative to the drawing origin
	[atm prependTransform:_state->_ctm];
	[atm invert];				// get reverse mapping (XImage coordinates to unit square)
	width=[rep pixelsWide];
	height=[rep pixelsHigh];
	if(isFlipped)
		[atm scaleXBy:width yBy:height];	// and directly map to pixel coordinates
	else
		[atm scaleXBy:width yBy:-height];	// and directly map to flipped pixel coordinates
	atms=[atm transformStruct];	// extract raw coordinate transform
	/*
	 * get current screen image for compositing
	 */
	hasAlpha=[rep hasAlpha];
	if(hasAlpha || atms.m11 != 1.0 || atms.m22 != 1.0 || atms.m12 != 0.0 || atms.m21 != 0.0 ||
	   (_compositingOperation != NSCompositeClear && _compositingOperation != NSCompositeCopy))
		{ // must really fetch current image from our context
		  //		NS_DURING
		{
			img=XGetImage(_display, _window,
						  xScanRect.x, xScanRect.y, xScanRect.width, xScanRect.height,
						  0x00ffffff, ZPixmap);
		}
		//		NS_HANDLER
		//			NSLog(@"_composite: could not fetch current screen contents due to %@", [localException reason]);
		//			img=nil;	// ignore for now
		//		NS_ENDHANDLER
		}
	else
		{ // we can simply create a new rectangular image and don't use anything existing
		int screen_number=XScreenNumberOfScreen(_screen);
		img=XCreateImage(_display, DefaultVisual(_display, screen_number), DefaultDepth(_display, screen_number),
						 ZPixmap, 0, NULL,
						 xScanRect.width, xScanRect.height,
						 8, 0);
		if(img && !(img->data = objc_malloc(img->bytes_per_line*img->height)))
			{ // we failed to allocate a data area
			XDestroyImage(img);
			img=NULL;
			}
		}
	if(!img)
		{
		NSLog(@"could not XGetImage or XCreateImage");
		return NO;
		}
#if 0
	{
		int redshift;
		int greenshift;
		int blueshift;
		NSLog(@"width=%d height=%d", img->width, img->height);
		NSLog(@"xoffset=%d", img->xoffset);
		NSLog(@"format=%d", img->format);
		NSLog(@"byte_order=%d", img->byte_order);
		NSLog(@"bitmap_unit=%d", img->bitmap_unit);
		NSLog(@"bitmap_bit_order=%d", img->bitmap_bit_order);
		NSLog(@"bitmap_pad=%d", img->bitmap_pad);
		NSLog(@"depth=%d", img->depth);
		NSLog(@"bytes_per_line=%d", img->bytes_per_line);
		NSLog(@"bits_per_pixel=%d", img->bits_per_pixel);
		for(redshift=0; ((1<<redshift)&img->red_mask) == 0; redshift++);
		for(greenshift=0; ((1<<greenshift)&img->green_mask) == 0; greenshift++);
		for(blueshift=0; ((1<<blueshift)&img->blue_mask) == 0; blueshift++);
		NSLog(@"red_mask=%lu", img->red_mask);
		NSLog(@"green_mask=%lu", img->green_mask);
		NSLog(@"blue_mask=%lu", img->blue_mask);
		NSLog(@"redshift=%d", redshift);
		NSLog(@"greenshift=%d", greenshift);
		NSLog(@"blueshift=%d", blueshift);
	}
#endif
#if 0
	[[NSColor redColor] set];	// will set _gc
	XFillRectangle(_display, _window, _state->_gc, xScanRect.x, xScanRect.y, xScanRect.width, xScanRect.height);
#endif
	/*
	 * get direct access to the bitmap
	 */
	isPlanar=[(NSBitmapImageRep *) rep isPlanar];
	bytesPerRow=[(NSBitmapImageRep *) rep bytesPerRow];
	[(NSBitmapImageRep *) rep getBitmapDataPlanes:imagePlanes];
	/*
	 * draw by scanning lines
	 */
	for(y=0; y<img->height; y++)
		{
		struct RGBA8 src={0,0,0,255}, dest={0,0,0,255};
		// FIXME: we must adjust x&y if we have clipped to the window, i.e. x&y are not aligned with the dest origin
		pnt.x=/*atms.m11*(0)+*/ atms.m12*(y)+atms.tX;	// first point of this scan line
		pnt.y=/*atms.m21*(0)+*/ atms.m22*(y)+atms.tY;
		for(x=0; x<img->width; x++, pnt.x+=atms.m11, pnt.y+=atms.m21)
			{
			unsigned short F, G;
			if(_compositingOperation != NSCompositeClear)
				{ // get smoothed RGBA from bitmap
				if(_compositingOperation != NSCompositeCopy)
					dest=XGetRGBA8(img, x, y);	// get current image value
				// we should pipeline this through core-image like filter modules
				switch(_imageInterpolation)
					{
					case NSImageInterpolationDefault:	// default is same as low
					case NSImageInterpolationLow:
						// FIXME: here we should inter/extrapolate several source points
					case NSImageInterpolationHigh:
						// FIXME: here we should inter/extrapolate more source points
					case NSImageInterpolationNone:
						{
						src=getPixel((int) pnt.x, (int) pnt.y, width, height,
									 /*
									  int bitsPerSample,
									  int samplesPerPixel,
									  int bitsPerPixel,
									  */
									 bytesPerRow,
									 isPlanar, hasAlpha,
									 imagePlanes);
						if(fract != 256)
							{ // dim source image
							src.R=(fract*src.R)>>8;
							src.G=(fract*src.G)>>8;
							src.B=(fract*src.B)>>8;
							src.A=(fract*src.A)>>8;
							}
						}
					}
				}
			// FIXME: speed optimization: handle by table of functions, i.e. CompositeClear(struct RGBA *dest, struct RGBA *src, float fraction) { dest->r=0; dest->g=(255*src->g+(255-src->a)*dest->g)>>8; ... }
			switch(_compositingOperation)
				{ // based on http://www.cs.wisc.edu/~schenney/courses/cs559-s2001/lectures/lecture-8-online.ppt
				case NSCompositeClear:				F=0, G=0; break;
				default:
				case NSCompositeCopy:				F=255, G=0; break;
				case NSCompositeHighlight:			// deprecated and mapped to NSCompositeSourceOver
				case NSCompositeSourceOver:			F=255, G=255-src.A; break;
				case NSCompositeSourceIn:			F=dest.A, G=0; break;
				case NSCompositeSourceOut:			F=255-dest.A, G=0; break;
				case NSCompositeSourceAtop:			F=dest.A, G=255-src.A; break;
				case NSCompositeDestinationOver:	F=255-dest.A, G=255; break;
				case NSCompositeDestinationIn:		F=0, G=src.A; break;
				case NSCompositeDestinationOut:		F=0, G=255-src.A; break;
				case NSCompositeDestinationAtop:	F=255-dest.A, G=src.A; break;
				case NSCompositePlusDarker:			F=255-25, G=255-25; break;		// FIXME: should not influence alpha of result!
				case NSCompositePlusLighter:		F=255+25, G=255+25; break;
				case NSCompositeXOR:				F=255-dest.A, G=255-src.A; break;
				}
			// FIXME: (255*255>>8) => 254???
			// FIXME: using Highlight etc. must be limited to pixel value 0/255
			// we must divide by 255 and not 256 - or adjust F&G scaling
			if(G == 0)
				{ // calculation is done with 'int' precision; stores only 8 bit
				dest.R=(F*src.R)>>8;
				dest.G=(F*src.G)>>8;
				dest.B=(F*src.B)>>8;
				dest.A=(F*src.A)>>8;
				}
			else if(F == 0)
				{
				dest.R=(G*dest.R)>>8;
				dest.G=(G*dest.G)>>8;
				dest.B=(G*dest.B)>>8;
				dest.A=(G*dest.A)>>8;
				}
			else
				{
				dest.R=(F*src.R+G*dest.R)>>8;
				dest.G=(F*src.G+G*dest.G)>>8;
				dest.B=(F*src.B+G*dest.B)>>8;
				dest.A=(F*src.A+G*dest.A)>>8;
				}
			if(img->depth==24)
				XPutPixel(img, x, y, (dest.R<<16)+(dest.G<<8)+(dest.B<<0));
			else if(img->depth==16)
				// FIXME: map to limited range
				XPutPixel(img, x, y, ((dest.R<<8)&0xf800)+((dest.G<<3)&0x07e0)+((dest.B>>3)&0x1f));	// 5/6/5 bit
			}
		}
	/*
	 * draw to screen
	 */
	XPutImage(_display, _window, _state->_gc, img, 0, 0, xScanRect.x, xScanRect.y, xScanRect.width, xScanRect.height);
	XDestroyImage(img);
#if 0
	[[NSColor redColor] set];	// will change _gc
	XDrawRectangle(_display, _window, _state->_gc, xScanRect.x, xScanRect.y, xScanRect.width, xScanRect.height);
#endif
	return YES;
}

- (void) _copyBits:(void *) srcGstate fromRect:(NSRect) srcRect toPoint:(NSPoint) destPoint;
{ // copy srcRect using CTM from (_NSX11GraphicsState *) srcGstate to destPoint transformed by current CTM
	srcRect.origin=[((_NSX11GraphicsState *) srcGstate)->_ctm transformPoint:srcRect.origin];
	srcRect.size=[((_NSX11GraphicsState *) srcGstate)->_ctm transformSize:srcRect.size];
	destPoint=[_state->_ctm transformPoint:destPoint];
#if 1
	NSLog(@"_copyBits");
#endif
	XCopyArea(_display,
			  (Window) (((_NSGraphicsState *) srcGstate)->_context->_graphicsPort),	// source window
			  _window, _state->_gc,
			  srcRect.origin.x, srcRect.origin.y,
			  srcRect.size.width, -srcRect.size.height,
			  destPoint.x, destPoint.y);
}

- (void) _setCursor:(NSCursor *) cursor;
{
#if 0
	NSLog(@"_setCursor:%@", cursor);
#endif
	XDefineCursor(_display, _window, [cursor _cursor]);
}

- (int) _windowNumber; { return _windowNum; }

// FIXME: NSWindow frontend should identify the otherWin from the global window list

- (void) _orderWindow:(NSWindowOrderingMode) place relativeTo:(int) otherWin;
{
	XWindowChanges values;
	NSLog(@"_orderWindow:%d relativeTo:%d", place, otherWin);
	if([[NSWindow _windowForNumber:_windowNum] isMiniaturized])	// FIXME: used as special trick not to really map the window during init
		return;
	switch(place)
		{
		case NSWindowOut:
			XUnmapWindow(_display, _window);
			break;
		case NSWindowAbove:
			XMapWindow(_display, _window);	// if not yet
			values.sibling=otherWin;		// 0 will order front
			values.stack_mode=Above;
			XConfigureWindow(_display, _window, CWStackMode, &values);
			break;
		case NSWindowBelow:
			XMapWindow(_display, _window);	// if not yet
			values.sibling=otherWin;		// 0 will order back
			values.stack_mode=Below;
			XConfigureWindow(_display, _window, CWStackMode, &values);
			break;
		}
	// save (new) level so that we can order other windows accordingly
	// maybe we should use a window property to store the level?
}

- (void) _miniaturize;
{
	NSLog(@"_miniaturize");
//	Status XIconifyWindow(_display, _window, _screen_number)
}

- (void) _setOrigin:(NSPoint) point;
{ // note: it is the task of NSWindow to call this only if setFrame really changes the origin
	XSizeHints size_hints;		// also specified as a hint
#if 0
	NSLog(@"_setOrigin:%@", NSStringFromPoint(point));
#endif
	point=[(NSAffineTransform *)(_nsscreen->_backendPrivate2) transformPoint:point];
	// FIXME: we must cope for the window manager frame
	XMoveWindow(_display, _window,
				size_hints.x=(int)point.x,
				size_hints.y=(int)(point.y-_windowRect.size.height-WINDOW_MANAGER_TITLE_HEIGHT));
	size_hints.flags = PPosition | USPosition;		
	XSetNormalHints(_display, _window, &size_hints);
}

- (void) _setOriginAndSize:(NSRect) frame;
{ // note: it is the task of NSWindow to call this only if setFrame really changes the size
	XSizeHints size_hints;		// also specified as a hint
#if 0
	NSLog(@"_setOriginAndSize:%@", NSStringFromRect(frame));
#endif
	_windowRect=frame;
	frame.origin=[(NSAffineTransform *)(_nsscreen->_backendPrivate2) transformPoint:frame.origin];
	frame.size=[(NSAffineTransform *)(_nsscreen->_backendPrivate2) transformSize:frame.size];
	if(NSWidth(frame) < 1.0 || NSHeight(frame) > -1.0)
		frame.size = (NSSize){48,-49};			// X doesn't like 0 siz
	XMoveResizeWindow(_display, _window, 
					  size_hints.x=(int)frame.origin.x,
					  size_hints.y=(int)(frame.origin.y+frame.size.height-WINDOW_MANAGER_TITLE_HEIGHT),
					  frame.size.width,
					  -frame.size.height);
	size_hints.flags = PPosition | USPosition;		
	XSetNormalHints(_display, _window, &size_hints);
}

- (void) _setTitle:(NSString *) string;
{ // note: it is the task of NSWindow to call this only if setTitle really changes the title
	XTextProperty windowName;
	const char *newTitle = [string cString];	// UTF8String??
	XStringListToTextProperty((char**) &newTitle, 1, &windowName);
	XSetWMName(_display, _window, &windowName);
	XSetWMIconName(_display, _window, &windowName);
}

- (void) _setLevel:(int) level;
{ // note: it is the task of NSWindow to call this only if setLevel really changes the level
#if 1
	NSLog(@"setLevel of window %d", level);
#endif
	/*
	attrs.window_level = [window level];
	attrs.flags = GSWindowStyleAttr|GSWindowLevelAttr;
	attrs.window_style = (styleMask & GSAllWindowMask);
	XChangeProperty(_display, _window, _windowDecorAtom, _windowDecorAtom,		// window style hints
					32, PropModeReplace, (unsigned char *)&attrs,
					sizeof(GSAttributes)/sizeof(CARD32));
*/
}

- (void) _makeKeyWindow;
{
	XSetInputFocus(_display, _window, RevertToNone, CurrentTime);
}

- (BOOL) _isKeyWindow;
{
	Window focus_return;
	int revert_to_return;
	XGetInputFocus(_display, &focus_return, &revert_to_return);
	return focus_return == _window;	// check if we are the key window
}

- (NSRect) _frame;
{ // get current frame as on screen (might have been moved by window manager)
	int x, y;
	unsigned width, height;
	NSAffineTransform *ictm=[_nsscreen _X112screen];
	XGetGeometry(_display, _window, NULL, &x, &y, &width, &height, NULL, NULL);
	return (NSRect){[ictm transformPoint:NSMakePoint(x, y)], [ictm transformSize:NSMakeSize(width, -height)]};	// translate to screen coordinates!
}

- (void) _setDocumentEdited:(BOOL)flag					// mark doc as edited
{ 
	GSAttributes attrs;
    memset(&attrs, 0, sizeof(GSAttributes));
	attrs.extra_flags = (flag) ? GSDocumentEditedFlag : 0;
	attrs.flags = GSExtraFlagsAttr;						// set WindowMaker WM window style hints
	XChangeProperty(_display, _window,
					_windowDecorAtom, _windowDecorAtom, 
					32, PropModeReplace, (unsigned char *)&attrs, 
					sizeof(GSAttributes)/sizeof(CARD32));
}

- (void) _beginPage:(NSString *) title;
{ // can we (mis-)use that as setTitle???
	return;
}

- (void) _endPage; { return; }

- (_NSGraphicsState *) _copyGraphicsState:(_NSGraphicsState *) state;
{ 
	XGCValues values;
	_NSX11GraphicsState *new=(_NSX11GraphicsState *) objc_malloc(sizeof(*new));
	new->_gc=XCreateGC(_display, _window, 0l, &values);	// create a fresh GC without values
	if(state)
		{ // copy
		new->_ctm=[((_NSX11GraphicsState *) state)->_ctm copyWithZone:NSDefaultMallocZone()];
		XCopyGC(_display, ((_NSX11GraphicsState *) state)->_gc, 
				GCFunction |
				GCPlaneMask |
				GCForeground |
				GCBackground |
				GCLineWidth	|
				GCLineStyle	|
				GCCapStyle |
				GCJoinStyle	|
				GCFillStyle	|
				// GCFillRule
				// GCTile
				// GCStipple
				// GCTileStipXOrigin
				// GCTileStipYOrigin
				GCFont |
				GCSubwindowMode	|
				GCGraphicsExposures	|
				GCClipXOrigin |
				GCClipYOrigin |
				GCClipMask
				// GCDashOffset
				// GCDashList
				// GCArcMode
				, new->_gc);	// copy from existing
		if(((_NSX11GraphicsState *) state)->_clip)
			{
			new->_clip=XCreateRegion();	// create new region
			XUnionRegion(((_NSX11GraphicsState *) state)->_clip, ((_NSX11GraphicsState *) state)->_clip, new->_clip);	// copy clipping region
#if 0
			{
				XRectangle box;
				XClipBox(_state->_clip, &box);
				NSLog(@"copy clip box=((%d,%d),(%d,%d))", box.x, box.y, box.width, box.height);
			}
#endif
			}
		else
			new->_clip=NULL;	// not clipped
		}
	else
		{ // alloc
		new->_ctm=nil;		// no initial screen transformation (set by first lockFocus)
		new->_clip=NULL;	// not clipped
		}
	return (_NSGraphicsState *) new;
}

- (void) restoreGraphicsState;
{
	if(!_graphicsState)
		return;
	if(((_NSX11GraphicsState *) _graphicsState)->_ctm)
		[((_NSX11GraphicsState *) _graphicsState)->_ctm release];
	if(((_NSX11GraphicsState *) _graphicsState)->_clip)
		XDestroyRegion(((_NSX11GraphicsState *) _graphicsState)->_clip);
	if(((_NSX11GraphicsState *) _graphicsState)->_gc)
		XFreeGC(_display, ((_NSX11GraphicsState *) _graphicsState)->_gc);
	[super restoreGraphicsState];
#if 0
	{
		XRectangle box;
		if(_state && _state->_clip)
			{
			XClipBox(_state->_clip, &box);
			NSLog(@"clip     box=((%d,%d),(%d,%d))", box.x, box.y, box.width, box.height);
			}
		else
			NSLog(@"no clip");
	}
#endif
}

- (NSColor *) _readPixel:(NSPoint) location;
{
	XImage *img;
	struct RGBA8 pix;
	NSColor *c;
	location=[_state->_ctm transformPoint:location];
	img=XGetImage(_display, _window,
				  location.x, location.y, 1, 1,
				  0x00ffffff, ZPixmap);
	pix=XGetRGBA8(img, 0, 0);
	XDestroyImage(img);
	c=[NSColor colorWithDeviceRed:pix.R/255.0 green:pix.G/255.0 blue:pix.B/255.0 alpha:pix.A/255.0];	// convert pixel to NSColor
	return c;
}

-  (void) _initBitmap:(NSBitmapImageRep *) bitmap withFocusedViewRect:(NSRect) rect;
{
	XImage *img;
	rect.origin=[_state->_ctm transformPoint:rect.origin];
	rect.size=[_state->_ctm transformSize:rect.size];
	img=XGetImage(_display, _window,
				  rect.origin.x, rect.origin.y, rect.size.width, -rect.size.height,
				  0x00ffffff, ZPixmap);
	// copy pixels to bitmap
	XDestroyImage(img);
}

- (void) flushGraphics;
{
	if(_isDoubleBuffered)
		{
		// copy dirty area (if any) from back to front buffer and clear dirty area
		_dirty=(XRectangle){ 0, 0, 0, 0 };	// clear
		}
	XFlush(_display);
}

- (NSPoint) _mouseLocationOutsideOfEventStream;
{ // Return mouse location in receiver's base coords ignoring the event loop
	Window root, child;
	int root_x, root_y, window_x, window_y;
	unsigned int mask;	// modifier and mouse keys
	if(!XQueryPointer(_display, _window, &root, &child, &root_x, &root_y, &window_x, &window_y, &mask))
		return NSZeroPoint;
	return NSMakePoint(window_x, _windowRect.size.height-window_y);
}

- (int) _keyModfierFlags;
{
	return __modFlags;
}

@end /* XRGraphicsContext */

static unsigned short xKeyCode(XEvent *xEvent, KeySym keysym, unsigned int *eventModFlags)
{ // translate key code
	unsigned short keyCode = 0;
	
	switch(keysym)
		{
		case XK_Return:
		case XK_KP_Enter:
		case XK_Linefeed:
			return '\r';
		case XK_Tab:
			return '\t';
		case XK_space:
			return ' ';
		}
	if ((keysym >= XK_F1) && (keysym <= XK_F35)) 			// if a function
		{													// key was pressed
		*eventModFlags |= NSFunctionKeyMask; 
		
		switch(xEvent->xkey.keycode)	// FIXME: why not use keysym here??
			{
			case XK_F1:  keyCode = NSF1FunctionKey;  break;
			case XK_F2:  keyCode = NSF2FunctionKey;  break;
			case XK_F3:  keyCode = NSF3FunctionKey;  break;
			case XK_F4:  keyCode = NSF4FunctionKey;  break;
			case XK_F5:  keyCode = NSF5FunctionKey;  break;
			case XK_F6:  keyCode = NSF6FunctionKey;  break;
			case XK_F7:  keyCode = NSF7FunctionKey;  break;
			case XK_F8:  keyCode = NSF8FunctionKey;  break;
			case XK_F9:  keyCode = NSF9FunctionKey;  break;
			case XK_F10: keyCode = NSF10FunctionKey; break;
			case XK_F11: keyCode = NSF11FunctionKey; break;
			case XK_F12: keyCode = NSF12FunctionKey; break;
			case XK_F13: keyCode = NSF13FunctionKey; break;
			case XK_F14: keyCode = NSF14FunctionKey; break;
			case XK_F15: keyCode = NSF15FunctionKey; break;
			case XK_F16: keyCode = NSF16FunctionKey; break;
			case XK_F17: keyCode = NSF17FunctionKey; break;
			case XK_F18: keyCode = NSF18FunctionKey; break;
			case XK_F19: keyCode = NSF19FunctionKey; break;
			case XK_F20: keyCode = NSF20FunctionKey; break;
			case XK_F21: keyCode = NSF21FunctionKey; break;
			case XK_F22: keyCode = NSF22FunctionKey; break;
			case XK_F23: keyCode = NSF23FunctionKey; break;
			case XK_F24: keyCode = NSF24FunctionKey; break;
			case XK_F25: keyCode = NSF25FunctionKey; break;
			case XK_F26: keyCode = NSF26FunctionKey; break;
			case XK_F27: keyCode = NSF27FunctionKey; break;
			case XK_F28: keyCode = NSF28FunctionKey; break;
			case XK_F29: keyCode = NSF29FunctionKey; break;
			case XK_F30: keyCode = NSF30FunctionKey; break;
			case XK_F31: keyCode = NSF31FunctionKey; break;
			case XK_F32: keyCode = NSF32FunctionKey; break;
			case XK_F33: keyCode = NSF33FunctionKey; break;
			case XK_F34: keyCode = NSF34FunctionKey; break;
			case XK_F35: keyCode = NSF35FunctionKey; break;
			default:								 break;
			}	}
	else 
		{
		switch(keysym) 
			{
			case XK_BackSpace:  keyCode = NSBackspaceKey;			break;
			case XK_Delete: 	keyCode = NSDeleteFunctionKey;		break;
			case XK_Home:		keyCode = NSHomeFunctionKey;		break;
			case XK_Left:		keyCode = NSLeftArrowFunctionKey;	break;
			case XK_Up:  		keyCode = NSUpArrowFunctionKey;		break;
			case XK_Right:		keyCode = NSRightArrowFunctionKey;	break;
			case XK_Down:		keyCode = NSDownArrowFunctionKey;	break;
			case XK_Prior:		keyCode = NSPrevFunctionKey;		break;
			case XK_Next:  		keyCode = NSNextFunctionKey;		break;
			case XK_End:  		keyCode = NSEndFunctionKey;			break;
			case XK_Begin:  	keyCode = NSBeginFunctionKey;		break;
			case XK_Select:		keyCode = NSSelectFunctionKey;		break;
			case XK_Print:  	keyCode = NSPrintScreenFunctionKey;	break;
			case XK_Execute:  	keyCode = NSExecuteFunctionKey;		break;
			case XK_Insert:  	keyCode = NSInsertFunctionKey;		break;
			case XK_Undo: 		keyCode = NSUndoFunctionKey;		break;
			case XK_Redo:		keyCode = NSRedoFunctionKey;		break;
			case XK_Menu:		keyCode = NSMenuFunctionKey;		break;
			case XK_Find:  		keyCode = NSFindFunctionKey;		break;
			case XK_Help:		keyCode = NSHelpFunctionKey;		break;
			case XK_Break:  	keyCode = NSBreakFunctionKey;		break;
			case XK_Mode_switch:keyCode = NSModeSwitchFunctionKey;	break;
			case XK_Sys_Req:	keyCode = NSSysReqFunctionKey;		break;
			case XK_Scroll_Lock:keyCode = NSScrollLockFunctionKey;	break;
			case XK_Pause:  	keyCode = NSPauseFunctionKey;		break;
			case XK_Clear:		keyCode = NSClearDisplayFunctionKey;break;
				// NSPageUpFunctionKey
				// NSPageDownFunctionKey
				// NSResetFunctionKey
				// NSStopFunctionKey
				// NSUserFunctionKey
				// and others
			default:												break;
			}
		
		if(keyCode)
			*eventModFlags |= NSFunctionKeyMask;
		else
			{ // other keys to handle
			if ((keysym == XK_Shift_L) || (keysym == XK_Shift_R))
				*eventModFlags |= NSFunctionKeyMask | NSShiftKeyMask; 
			else if ((keysym == XK_Control_L) || (keysym == XK_Control_R))
				*eventModFlags |= NSFunctionKeyMask | NSControlKeyMask; 
			else if ((keysym == XK_Alt_R) || (keysym == XK_Meta_R))
				*eventModFlags |= NSAlternateKeyMask;
			else if ((keysym == XK_Alt_L) || (keysym == XK_Meta_L))
				*eventModFlags |= NSCommandKeyMask | NSAlternateKeyMask; 
			}
		}
	
	if ((keysym > XK_KP_Space) && (keysym < XK_KP_9)) 		// If the key press
		{													// originated from
		*eventModFlags |= NSNumericPadKeyMask;				// the key pad
		
		switch(keysym) 
			{
			case XK_KP_F1:        keyCode = NSF1FunctionKey;         break;
			case XK_KP_F2:        keyCode = NSF2FunctionKey;         break;
			case XK_KP_F3:        keyCode = NSF3FunctionKey;         break;
			case XK_KP_F4:        keyCode = NSF4FunctionKey;         break;
			case XK_KP_Home:      keyCode = NSHomeFunctionKey;       break;
			case XK_KP_Left:      keyCode = NSLeftArrowFunctionKey;  break;
			case XK_KP_Up:        keyCode = NSUpArrowFunctionKey;    break;
			case XK_KP_Right:     keyCode = NSRightArrowFunctionKey; break;
			case XK_KP_Down:      keyCode = NSDownArrowFunctionKey;  break;
			case XK_KP_Page_Up:   keyCode = NSPageUpFunctionKey;     break;
			case XK_KP_Page_Down: keyCode = NSPageDownFunctionKey;   break;
			case XK_KP_End:       keyCode = NSEndFunctionKey;        break;
			case XK_KP_Begin:     keyCode = NSBeginFunctionKey;      break;
			case XK_KP_Insert:    keyCode = NSInsertFunctionKey;     break;
			case XK_KP_Delete:    keyCode = NSDeleteFunctionKey;     break;
			default:												 break;
			}
		}
	
	if (((keysym > XK_KP_Space) && (keysym <= XK_KP_9)) ||
		((keysym > XK_space) && (keysym <= XK_asciitilde)))
		{
		// Not processed
		} 
	
	return keyCode;
}

// determine which modifier
// keys (Command, Control,
// Shift, etc..) were held down
// while the event occured.

static unsigned int	xKeyModifierFlags(unsigned int state)
{
	unsigned int flags = 0; 
	
	if (state & ControlMask)
		flags |= NSControlKeyMask;
	
	if (state & ShiftMask)
		flags |= NSShiftKeyMask;
	
	if (state & Mod1Mask)
		flags |= NSAlternateKeyMask;	// not recognized??
	
	if (state & Mod2Mask) 
		flags |= NSCommandKeyMask; 
	
	if (state & Mod3Mask) 
		flags |= NSAlphaShiftKeyMask;
	
	if (state & Mod4Mask) 
		flags |= NSHelpKeyMask; 
	
	if (state & Mod5Mask) 
		flags |= NSControlKeyMask; 
	// we don't handle the NSNumericPadKeyMask and NSFunctionKeyMask here
#if 0
	NSLog(@"state=%x flags=%x", state, flags);
#endif
	return flags;
}

static void X11ErrorHandler(Display *display, XErrorEvent *error_event)
{
	static struct { char *name; int major; } requests[]={
		{ "CreateWindow", 1 }, 
		{ "ChangeWindowAttributes", 2 }, 
		{ "GetWindowAttributes", 3 },
		{ "DestroyWindow", 4 },
		{ "DestroySubwindows", 5 },
		{ "ChangeSaveSet", 6 },
		{ "ReparentWindow", 7 },
		{ "MapWindow", 8 },
		{ "MapSubwindows", 9 },
		{ "UnmapWindow", 10 },
		{ "UnmapSubwindows", 11 },
		{ "ConfigureWindow", 12 },
		{ "CirculateWindow", 13 },
		{ "GetGeometry", 14 },
		{ "QueryTree", 15 },
		{ "InternAtom", 16 },
		{ "GetAtomName", 17 },
		{ "ChangeProperty", 18 }, 
		{ "DeleteProperty", 19 },
		{ "GetProperty", 20 },
		{ "ListProperties", 21 },
		{ "SetSelectionOwner", 22 },    
		{ "GetSelectionOwner", 23 },
		{ "ConvertSelection", 24 }, 
		{ "SendEvent", 25 },
		{ "GrabPointer", 26 },
		{ "UngrabPointer", 27 },
		{ "GrabButton", 28 },
		{ "UngrabButton", 29 },
		{ "ChangeActivePointerGrab", 30 },
		{ "GrabKeyboard", 31 },
		{ "UngrabKeyboard", 32 }, 
		{ "GrabKey", 33 },
		{ "UngrabKey", 34 },
		{ "AllowEvents", 35 },       
		{ "GrabServer", 36 },    
		{ "UngrabServer", 37 }, 
		{ "QueryPointer", 38 }, 
		{ "GetMotionEvents", 39 },
		{ "TranslateCoords", 40 }, 
		{ "WarpPointer", 41 },       
		{ "SetInputFocus", 42 }, 
		{ "GetInputFocus", 43 }, 
		{ "QueryKeymap", 44 },      
		{ "OpenFont", 45 },
		{ "CloseFont", 46 },     
		{ "QueryFont", 47 },
		{ "QueryTextExtents", 48 },     
		{ "ListFonts", 49 },
		{ "ListFontsWithInfo", 50 }, 
		{ "SetFontPath", 51 },
		{ "GetFontPath", 52 },
		{ "CreatePixmap", 53 }, 
		{ "FreePixmap", 54 },    
		{ "CreateGC", 55 },
		{ "ChangeGC", 56 },   
		{ "CopyGC", 57 },
		{ "SetDashes", 58 },     
		{ "SetClipRectangles", 59 }, 
		{ "FreeGC", 60 },
		{ "ClearArea", 61 }, 
		{ "CopyArea", 62 },    
		{ "CopyPlane", 63 },     
		{ "PolyPoint", 64 },     
		{ "PolyLine", 65 },    
		{ "PolySegment", 66 },       
		{ "PolyRectangle", 67 }, 
		{ "PolyArc", 68 },
		{ "FillPoly", 69 },    
		{ "PolyFillRectangle", 70 }, 
		{ "PolyFillArc", 71 },       
		{ "PutImage", 72 },
		{ "GetImage", 73 },
		{ "PolyText8", 74 },     
		{ "PolyText16", 75 },      
		{ "ImageText8", 76 },      
		{ "ImageText16", 77 },       
		{ "CreateColormap", 78 }, 
		{ "FreeColormap", 79 }, 
		{ "CopyColormapAndFree", 80 }, 
		{ "InstallColormap", 81 }, 
		{ "UninstallColormap", 82 }, 
		{ "ListInstalledColormaps", 83 }, 
		{ "AllocColor", 84 },
		{ "AllocNamedColor", 85 }, 
		{ "AllocColorCells", 86 }, 
		{ "AllocColorPlanes", 87 }, 
		{ "FreeColors", 88 },     
		{ "StoreColors", 89 },       
		{ "StoreNamedColor", 90 }, 
		{ "QueryColors", 91 },  
		{ "LookupColor", 92 },
		{ "CreateCursor", 93 }, 
		{ "CreateGlyphCursor", 94 }, 
		{ "FreeCursor", 95 },    
		{ "RecolorCursor", 96 }, 
		{ "QueryBestSize", 97 }, 
		{ "QueryExtension", 98 }, 
		{ "ListExtensions", 99 }, 
		{ "ChangeKeyboardMapping", 100 },
		{ "GetKeyboardMapping", 101 },
		{ "ChangeKeyboardControl", 102 }, 
		{ "GetKeyboardControl", 103 }, 
		{ "Bell", 104 },
		{ "ChangePointerControl", 105 },
		{ "GetPointerControl", 106 },
		{ "SetScreenSaver", 107 }, 
		{ "GetScreenSaver", 108 }, 
		{ "ChangeHosts", 109 },    
		{ "ListHosts", 110 },
		{ "SetAccessControl", 111 }, 
		{ "SetCloseDownMode", 112 },
		{ "KillClient", 113 },
		{ "RotateProperties", 114 },
		{ "ForceScreenSaver", 115 },
		{ "SetPointerMapping", 116 },
		{ "GetPointerMapping", 117 },
		{ "SetModifierMapping", 118 },
		{ "GetModifierMapping", 119 },
		{ "NoOperation", 127 },
		{ NULL} };
	char string[1025];
	int i;
    XGetErrorText(display, error_event->error_code, string, sizeof(string)-1);
	string[sizeof(string)-1]=0;
    NSLog(@"X Error: %s", string);
    NSLog(@"  code: %u", error_event->error_code);
    NSLog(@"  display: %s", DisplayString(display));
	for(i=0; requests[i].name; i++)
		if(requests[i].major == error_event->request_code)
			break;
	if(requests[i].name)
		NSLog(@"  request: %s(%u).%u", requests[i].name, error_event->request_code, error_event->minor_code);
	else
		NSLog(@"  request: %u.%u", error_event->request_code, error_event->minor_code);
    NSLog(@"  resource: %lu", error_event->resourceid);
	if(error_event->request_code == 73)
		return;
#if 1
	*((long *) 1)=0;	// force SEGFAULT to ease debugging by writing a core dump
	abort();
#endif
	[NSException raise:NSGenericException format:@"X11 Internal Error"];
	
}  /* X11ErrorHandler */

@implementation NSGraphicsContext (NSBackendOverride)

+ (NSGraphicsContext *) graphicsContextWithGraphicsPort:(void *) port flipped:(BOOL) flipped;
{
	NSGraphicsContext *gc=[[[_NSX11GraphicsContext alloc] _initWithGraphicsPort:port] autorelease];
	gc->_isFlipped=flipped;
	return gc;
}

+ (NSGraphicsContext *) graphicsContextWithWindow:(NSWindow *) window
{
	return [_NSX11GraphicsContext graphicsContextWithAttributes:
		[NSDictionary dictionaryWithObject:window forKey:NSGraphicsContextDestinationAttributeName]];
}

@end

@implementation NSWindow (NSBackend)

+ (NSWindow *) _windowForNumber:(int) windowNum;
{
#if 0
	NSLog(@"windowNumber %d -> %@", windowNum, NSMapGet(__WindowNumToNSWindow, (void *) windowNum));
#endif
	return NSMapGet(__WindowNumToNSWindow, (void *) windowNum);
}

+ (NSArray *) _windowList;
{ // get all NSWindows of this application
#if COMPLEX
	int count;
	int context=getpid();	// filter only our windows!
	NSCountWindowsForContext(context, &count);
	if(count)
		{
		int list[count];	// get window numbers
		NSMutableArray *a=[NSMutableArray arrayWithCapacity:count];
		NSWindowList(context, count, list);
		for(i=0; i<count; i++)
			[a addObject:NSMapGet(__WindowNumToNSWindow, (void *) list[i]);	// translate to NSWindows
		return a;
		}
	return nil;
#endif
	return NSAllMapTableValues(__WindowNumToNSWindow);		// all windows we currently know by window number
}

@end

@implementation NSScreen (NSBackend)

#if OLD

+ (struct WindowList *) _getWindowList:(unsigned *) count;
{
	FILE *f;
	static struct WindowList *list=NULL;
	static unsigned int size=0;	// current size
	int n;
	if(!window_list_file)
		window_list_file=[WINDOW_LIST_FILE fileSystemRepresentation];	// convert
	f=fopen(window_list_file, "r");
	if(!f)
		{ // try to create
		[self _setWindowList:NULL withLength:0 append:NO];
		f=fopen(window_list_file, "r");
		if(!f)
			{ // failed to create
			*count=0;
			return NULL;
			}
		}
	*count=0;
	do
		{ // read next part
			n=100;
			if(size < (*count + n))
				{ // we need more space
				size+=n;
				if(list)
					list=objc_realloc(list, size*sizeof(*list));
				else
					list=objc_malloc(size*sizeof(*list));
				}
			n=fread(&list[*count], sizeof(*list), n, f);	// read next junk
			(*count)+=n;
		} while(n > 0);
	fclose(f);
	return list;
}

+ (void) _setWindowList:(struct WindowList *) list withLength:(unsigned) count append:(BOOL) flag;
{
	FILE *f;
#if 1
	NSLog(@"_setWindowList ... withLength:%u append:%d", count, flag);
#endif
	if(count > 20)
		NSLog(@"_setWindowList: unexpectedly large number of windows: %u", count), count=20;
	if(!window_list_file)
		window_list_file=[WINDOW_LIST_FILE fileSystemRepresentation];	// convert
																		// FIXME: here we should lock
	f=fopen(window_list_file, flag?"a":"w");	// append or overwrite
												// if file is new, we should create with a+w mode? Or we should have one file per user-id in his home directory
	if(f)
		{
		if(count)
			fwrite(list, sizeof(*list), count, f);
		fclose(f);
		}
	else
		NSLog(@"can't write file %@", WINDOW_LIST_FILE);
	// FIXME: unlock here
}

#endif

// FIXME: should be + method (screen independent)
// and get _display from the (Window) structure

- (int) _getLevelOfWindowNumber:(int) windowNum;
{
	Atom actual_type_return;
	int actual_format_return;
	unsigned long nitems_return;
	unsigned long bytes_after_return;
	unsigned char *prop_return;
	int level;
#if 1
	NSLog(@"getLevel of window %d", windowNum);
#endif
	if(!XGetWindowProperty(_display, (Window) windowNum, _windowDecorAtom, 0, 0, False, _windowDecorAtom, 
						   &actual_type_return, &actual_format_return, &nitems_return, &bytes_after_return, &prop_return))
		return 0;
	level=((GSAttributes *) prop_return)->window_level;
	XFree(prop_return);
#if 1
	NSLog(@"  = %d", level);
#endif
	return level;
}

+ (void) _X11EventNotification:(NSNotification *) n;
{
#if 0
	NSLog(@"X11 notification");
#endif
	[self _handleNewEvents];
#if 0
	NSLog(@"  X11 notification done");
#endif
	[[n object] waitForDataInBackgroundAndNotifyForModes:_XRunloopModes];
}

+ (void) initialize;	// called when looking at the first screen
{ // initialize backend
	static char *atomNames[] =
	{
		"WM_STATE",
		"WM_PROTOCOLS",
		"WM_DELETE_WINDOW",
		"_GNUSTEP_WM_ATTR"
	};
	Atom atoms[sizeof(atomNames)/sizeof(atomNames[0])];
	NSFileHandle *fh;
#if 1
	NSLog(@"NSScreen backend +initialize");
//	system("export;/usr/X11R6/bin/xeyes&");
#endif
#if 0
	XInitThreads();	// make us thread-safe
#endif
	if((_display=XOpenDisplay(NULL)) == NULL) 		// connect to X server based on DISPLAY variable
		[NSException raise:NSGenericException format:@"Unable to connect to X server"];
	XSetErrorHandler((XErrorHandler)X11ErrorHandler);
	fh=[[NSFileHandle alloc] initWithFileDescriptor:XConnectionNumber(_display)];
	[[NSNotificationCenter defaultCenter] addObserver:self
											 selector:@selector(_X11EventNotification:)
												 name:NSFileHandleDataAvailableNotification
											   object:fh];
	// CHECKME: do we really have to handle NSConnectionReplyMode? Well, this keeps the UI responsive if DO is multi-threaded but might als lead to strange synchronization issues (nested runloops)
	_XRunloopModes=[[NSArray arrayWithObjects:NSDefaultRunLoopMode,
		NSConnectionReplyMode,
		NSModalPanelRunLoopMode,
		NSEventTrackingRunLoopMode,
		nil] retain];
	[fh waitForDataInBackgroundAndNotifyForModes:_XRunloopModes];
    if(XInternAtoms(_display, atomNames, sizeof(atomNames)/sizeof(atomNames[0]), False, atoms) == 0)
		[NSException raise: NSGenericException format:@"XInternAtoms()"];
    _stateAtom = atoms[0];
    _protocolsAtom = atoms[1];
    _deleteWindowAtom = atoms[2];
    _windowDecorAtom = atoms[3];
}

+ (NSArray *) screens
{
	static NSMutableArray *screens;
	if(!screens)
		{ // create screens list
		int i;
		screens=[[NSMutableArray alloc] initWithCapacity:ScreenCount(_display)];
		for(i=0; i<ScreenCount(_display); i++)
			{
			NSScreen *s=[[self alloc] init];	// create screen object
			s->_backendPrivate1=XScreenOfDisplay(_display, i);	// Screen
			if(s->_backendPrivate1)
				{
				if(i == XDefaultScreen(_display))
					[screens insertObject:s atIndex:0];	// make first
				else
					[screens addObject:s];
				}
			[s release];	// retained by array
			}
#if 0
		NSLog(@"screens=%@", screens);
#endif
		}
	return screens;
}

- (NSDictionary *) deviceDescription;
{
	BOOL changed=NO;
	NSSize resolution, size;
	if(!_device)
		{
		size.width=WidthOfScreen((Screen *) _screen);			// in Pixels
		size.height=HeightOfScreen((Screen *) _screen);
		resolution.width=(25.4*size.width)/WidthMMOfScreen((Screen *) _screen);		// returns size in mm -> translate to DPI
		resolution.height=(25.4*size.height)/HeightMMOfScreen((Screen *) _screen);
		[(NSAffineTransform *) _screen2X11 release];
		_screen2X11=[[NSAffineTransform alloc] init];
		// apply userSpaceScaling here?
		[(NSAffineTransform *) _screen2X11 scaleXBy:1.0 yBy:-1.0];	// flip Y axis
		[(NSAffineTransform *) _screen2X11 translateXBy:0.5 yBy:-0.5-size.height];	// adjust for screen height and proper rounding
#if __APPLE__
		size.height-=22;	// subtract menu bar of X11 server from frame
#endif
#if 0
		NSLog(@"_screen2X11=%@", (NSAffineTransform *) _screen2X11);
#endif
#if __linux__
		if(XDisplayString(_display)[0] == ':' ||
		   strncmp(XDisplayString(_display), "localhost:", 10) == 0)
			{ // local server
			static int fd=-1;
			int r;
			if(fd < 0)
				fd=open("/dev/apm_bios", O_RDWR|O_NONBLOCK);
			if(fd < 0)
				NSLog(@"Failed to get hinge state from /dev/apm_bios");
			else
				{
				r=ioctl(fd, SCRCTL_GET_ROTATION);
#if 1
				NSLog(@"hinge state=%d", r);
#endif
				switch(r)
					{
					default:
						NSLog(@"unknown hinge state %d", r);
						break;
					case 3:	// Case Closed
						break;
					case 2:	// Case open portrait
						{
							float h=size.height;
							size.height=size.width;
							size.width=h;
							h=resolution.height;
							resolution.height=resolution.width;
							resolution.width=h;
							[(NSAffineTransform *) _screen2X11 rotateByDegrees:90.0];
							break;
						}
					case 0:	// Case open landscape
						break;
					}
				}
			// setup a timer to verify/update the deviceDescription every now and then
			}
		else
			size.height-=22;	// subtract menu bar of X11 server from frame
#endif
		_device=[[NSMutableDictionary alloc] initWithObjectsAndKeys:
			[NSNumber numberWithInt:PlanesOfScreen((Screen *) _screen)], NSDeviceBitsPerSample,
			@"DeviceRGBColorSpace", NSDeviceColorSpaceName,
			@"NO", NSDeviceIsPrinter,
			@"YES", NSDeviceIsScreen,
			[NSValue valueWithSize:resolution], NSDeviceResolution,
			[NSValue valueWithSize:size], NSDeviceSize,
			[NSNumber numberWithInt:XScreenNumberOfScreen(_screen)], @"NSScreenNumber",
			nil];
#if 0
		NSLog(@"deviceDescription=%@", _device);
		NSLog(@"  resolution=%@", NSStringFromSize(resolution));
		NSLog(@"  size=%@", NSStringFromSize(size));
#endif
		if(changed)
			{
			[[NSNotificationCenter defaultCenter] postNotificationName:NSApplicationDidChangeScreenParametersNotification
																object:NSApp];
			}
		}
	return _device;
}

- (NSWindowDepth) depth
{
	int BpS=PlanesOfScreen((Screen *) _screen);
	return WindowDepth(BpS, BpS/3, YES, NSRGBColorSpaceModel);
}

- (const NSWindowDepth *) supportedWindowDepths;
{
	/*
	int *XListDepths(display, screen_number, count_return)
	Display *display;
	int screen_number;
	int *count_return;
	 */
	NIMP; return NULL; 
}

- (NSAffineTransform *) _X112screen;
{
	// should be cached
	NSAffineTransform *ictm=[(NSAffineTransform *)_screen2X11 copy];
	[ictm invert];
	return [ictm autorelease];
}

- (BOOL) _hasWindowManager;
{ // there is a window manager so we should not add _NSWindowTitleView
	return YES;
}

- (int) _windowTitleHeight;
{ // amount added by window manager for window title
	return 20;
}

+ (int) _windowListForContext:(int) context size:(int) size list:(int *) list;	// list may be NULL, return # of entries copied
{ // get window numbers from front to back
	int i, j;
	Window *children;	// list of children
	unsigned int nchildren;
	// this mus be a) fast, b) front2back, c) allow for easy access to the window level, d) returns internal window numbers and not NSWindows e) for potentially ALL applications
	// where can we get that from? a) from the Xserver, b) from a local shared file (per NSScreen), c) from a property attached to the Sceen and/or the Windows (WM_HINTS?)
#if OLD
	///
	/// the task is to fill with up to size window number (front to back)
	/// and filtered by context (pid?) if that is != 0
	///
	/*
	 or do we use a shared file to store window levels and stacking order?
	 struct XLevel {
		 unsigned long nextToBack;
		 long context;	// pid()
		 long level;
		 Window window;
		 };
	 
	 XQueryTree approach must fail since it does not return appropriate stacking order for child windows with different parent!!!
	 
	 */
#endif
	// FIXME: should go through all screens
//	for(i=0; i<ScreenCount(_display); i++)
//		{

#if 1
	NSLog(@"XQueryTree");
#endif
	if(!XQueryTree(_display, RootWindowOfScreen(XScreenOfDisplay(_display, 0)), NULL, NULL, &children, &nchildren))
		return 0;	// failed
#if 1
	NSLog(@"  nchildren= %d", nchildren);
#endif
	for(i=nchildren-1, j=0; i>0; i--)
		{
		if(context != 0 && 0 /* not equal */)
			{
			// what is context? A client ID? A process ID?
			continue;	// skip since it is owned by a different application
			}
		if(list)
			{
			if(j >= size)
				break;	// done
			list[j++]=(int) children[i];	// get windows in front2back order (i.e. reverse) and translate to window numbers
			}
		}
	XFree(children);
	return i;
}

+ (void) _handleNewEvents;
{
	int count;
	while((count = XPending(_display)) > 0)		// while X events are pending
		{
		//	fprintf(stderr,"_NSX11GraphicsContext ((XPending count = %d): \n", count);
		while(count-- > 0)
			{											// loop and grab all of the
			static Window lastXWin=None;		// last window (cache key)
			static int windowNumber;			// number of lastXWin
			static unsigned int windowHeight;	// attributes of lastXWin
			static NSWindow *window=nil;		// associated NSWindow of lastXWin
			static NSEvent *lastMotionEvent=nil;
			static Time timeOfLastClick = 0;
			static int clickCount = 1;
			NSEventType type;
			Window thisXWin;				// window of this event
			XEvent xe;
			NSEvent *e = nil;	// resulting event
			XNextEvent(_display, &xe);
			switch(xe.type)
				{ // extract window from event
				case ButtonPress:
				case ButtonRelease:
					thisXWin=xe.xbutton.window;
					break;
				case MotionNotify:
					thisXWin=xe.xmotion.window;
					if(thisXWin != lastXWin)						
						lastMotionEvent=nil;	// window has changed - we need a new event
					break;
				case ReparentNotify:
					thisXWin=xe.xreparent.window;
					break;
				case Expose:
					thisXWin=xe.xexpose.window;
					break;
				case ClientMessage:
					thisXWin=xe.xclient.window;
					break;
				case ConfigureNotify:					// window has been resized
					thisXWin=xe.xconfigure.window;
					break;
				case FocusIn:
				case FocusOut:							// keyboard focus left
					thisXWin=xe.xfocus.window;
					break;
				case KeyPress:							// a key has been pressed
				case KeyRelease:						// a key has been released
					thisXWin=xe.xkey.window;
					break;
				case MapNotify:							// when a window changes
				case UnmapNotify:
					thisXWin=xe.xmap.window;
					break;
				case PropertyNotify:
					thisXWin=xe.xproperty.window;
					break;
				default:
					thisXWin=lastXWin;	// assume unchanged
				}
			if(thisXWin != lastXWin)						
				{ // update cached references to window and prepare for translation
				window=NSMapGet(__WindowNumToNSWindow, (void *) thisXWin);
				// FIXME: if a window is closed, it might be removed from this list but events might be pending!
				if(!window)
					{
					NSLog(@"*** event from unknown Window (%d). Ignored.", (long) thisXWin);
					NSLog(@"Window list: %@", NSAllMapTableValues(__WindowNumToNSWindow));
					windowNumber=-1;
					windowHeight=100;
					}
				else
					{
					windowNumber=[window windowNumber];
					windowHeight=((_NSX11GraphicsContext *)[window graphicsContext])->_windowRect.size.height;
					}
				lastXWin=thisXWin;
				}
			// we could post the raw X-event as an NSNotification so that we could build a window manager...
			switch(xe.type)
				{										// mouse button events
				case ButtonPress:
					{
						float pressure=0.0;
						NSDebugLog(@"ButtonPress: X11 time %u timeOfLastClick %u \n", 
								   xe.xbutton.time, timeOfLastClick);
						// hardwired test for a double click
						// default of 300 should be user set;
						// under NS the windowserver does this
						if(xe.xbutton.time < (unsigned long)(timeOfLastClick+300))
							clickCount++;
						else
							clickCount = 1;							// reset click cnt
						timeOfLastClick = xe.xbutton.time;
						switch (xe.xbutton.button)
							{
							case Button4:
								type = NSScrollWheel;
								pressure = (float)clickCount;
								break;								
							case Button5:
								type = NSScrollWheel;
								pressure = -(float)clickCount;
								break;
							case Button1:	type = NSLeftMouseDown;		break;
							case Button3:	type = NSRightMouseDown;	break;
							default:		type = NSOtherMouseDown;	break;
							}
						e = [NSEvent mouseEventWithType:type		// create NSEvent	
											   location:NSMakePoint(xe.xbutton.x, windowHeight-xe.xbutton.y)
										  modifierFlags:__modFlags
											  timestamp:(NSTimeInterval)xe.xbutton.time*0.001
										   windowNumber:windowNumber
												context:self
											eventNumber:xe.xbutton.serial
											 clickCount:clickCount
											   pressure:pressure];
						break;
					}					
				case ButtonRelease:
					{
						NSDebugLog(@"ButtonRelease");
						type = (xe.xbutton.button == Button1 ? NSLeftMouseUp : NSRightMouseUp);
						e = [NSEvent mouseEventWithType:type		// create NSEvent	
											   location:NSMakePoint(xe.xbutton.x, windowHeight-xe.xbutton.y)
										  modifierFlags:__modFlags
											  timestamp:(NSTimeInterval)xe.xbutton.time*0.001
										   windowNumber:windowNumber
												context:self
											eventNumber:xe.xbutton.serial
											 clickCount:clickCount
											   pressure:1.0];
						break;
					}
				case CirculateNotify:	// a change to the stacking order
					NSDebugLog(@"CirculateNotify\n");
					break;					
				case CirculateRequest:
					NSDebugLog(@"CirculateRequest");
					break;
				case ClientMessage:								// client events
					NSDebugLog(@"ClientMessage\n");
					if(xe.xclient.message_type == _protocolsAtom &&
					   xe.xclient.data.l[0] == _deleteWindowAtom) 
						{ // WM is asking us to close
						[window performClose:self];
						}									// to close window
#if DND
					else
						XRProcessXDND(_display, &xe);		// handle X DND
#endif
					break;
				case ColormapNotify:					// colormap attribute chg
					NSDebugLog(@"ColormapNotify\n");
					break;
				case ConfigureNotify:					// window has been moved or resized by window manager
					NSDebugLog(@"ConfigureNotify\n");
#if FIXME
					if(!xe.xconfigure.override_redirect || 
						xe.xconfigure.window == _wAppTileWindow)
						{
						NSRect f = (NSRect){{(float)xe.xconfigure.x,
							(float)xe.xconfigure.y},
							{(float)xe.xconfigure.width,
								(float)xe.xconfigure.height}};	// get frame rect
						if(!(w = XRWindowWithXWindow(xe.xconfigure.window)) && xe.xconfigure.window == _wAppTileWindow)
							w = XRWindowWithXWindow(__xAppTileWindow);
						if(xe.xconfigure.above == 0)
							f.origin = [w xFrame].origin;
						//					if(!xe.xconfigure.override_redirect && xe.xconfigure.send_event == 0)
						f.origin.y += WINDOW_MANAGER_TITLE_HEIGHT;		// adjust for title bar offset
						NSDebugLog(@"New frame %f %f %f %f\n", 
								   f.origin.x, f.origin.y,
								   f.size.width, f.size.height);
						// FIXME: shouldn't this be an NSNotification that a window can catch?
						[window _setFrame:f];
						}
					if(xe.xconfigure.window == lastXWin)
						{
						// xFrame = [w xFrame];
						xFrame = (NSRect){{(float)xe.xconfigure.x,
								(float)xe.xconfigure.y},
								{(float)xe.xconfigure.width,
									(float)xe.xconfigure.height}};
						}
					break;								
#endif
				case ConfigureRequest:					// same as ConfigureNotify but we get this event
					NSDebugLog(@"ConfigureRequest\n");	// before the change has 
					break;								// actually occurred 					
				case CreateNotify:						// a window has been
					NSDebugLog(@"CreateNotify\n");		// created
					break;
				case DestroyNotify:						// a window has been
					NSLog(@"DestroyNotify\n");			// Destroyed
					break;
				case EnterNotify:						// when the pointer
					NSDebugLog(@"EnterNotify\n");		// enters a window
					break;					
				case LeaveNotify:						// when the pointer 
					NSDebugLog(@"LeaveNotify\n");		// leaves a window
					break;
				case Expose:
					{
						if(/* window isDoubleBuffered */ 0)
							{ // copy from backing store
							}
						else
							{
							NSRect r=NSMakeRect(xe.xexpose.x, (float) ((windowHeight-xe.xexpose.y)-xe.xexpose.height), xe.xexpose.width, xe.xexpose.height);
#if 0
							NSLog(@"expose %@ %u,%u,%u,%u -> %@", window,
								  xe.xexpose.x, xe.xexpose.y, xe.xexpose.width, xe.xexpose.height,
								  NSStringFromRect(r));
#endif
							// shouldn't we post the event to the start of the queue to sync with runloop actions?
							[[NSNotificationCenter defaultCenter] postNotificationName:NSWindowDidExposeNotification
																				object:window
																			  userInfo:[NSDictionary dictionaryWithObject:[NSValue valueWithRect:r]
																												   forKey:@"NSExposedRect"]];
							}
						break;
					}
				case FocusIn:							
					{ // keyboard focus entered one of our windows - take this a a hint from the WindowManager to bring us to the front
					NSLog(@"FocusIn 1: %d\n", xe.xfocus.detail);
#if OLD
					// NotifyAncestor			0
					// NotifyVirtual			1
					// NotifyInferior			2
					// NotifyNonlinear			3
					// NotifyNonlinearVirtual	4
					// NotifyPointer			5
					// NotifyPointerRoot		6
					// NotifyDetailNone			7
					[NSApp activateIgnoringOtherApps:YES];	// user has clicked: bring our application windows and menus to front
					[window makeKey];
					if(xe.xfocus.detail == NotifyAncestor)
						{
						//				if (![[[NSApp mainMenu] _menuWindow] isVisible])
						//					[[NSApp mainMenu] display];
						}
					else if(xe.xfocus.detail == NotifyNonlinear
							&& __xKeyWindowNeedsFocus == None)
						{ // create fake mouse dn
						NSLog(@"FocusIn 2");
						// FIXME: shouldn't we better use data1 and data2 to specify that we are having focus-events??
						e = [NSEvent otherEventWithType:NSAppKitDefined
											   location:NSZeroPoint
										  modifierFlags:0
											  timestamp:(NSTimeInterval)0
										   windowNumber:windowNumber
												context:self
												subtype:xe.xfocus.serial
												  data1:0
												  data2:0];
						}
#endif
					break;
					}
				case FocusOut:
					{ // keyboard focus has left one of our windows
					NSDebugLog(@"FocusOut");
					e = [NSEvent otherEventWithType:NSSystemDefined
										   location:NSZeroPoint
									  modifierFlags:0
										  timestamp:(NSTimeInterval)0
									   windowNumber:windowNumber
											context:self
											subtype:xe.xfocus.serial
											  data1:0
											  data2:0];
#if FIXME
					if(xe.xfocus.detail == NotifyAncestor)	// what does this mean?
						{
						NSLog(@"FocusOut 1");
						[w xFrame];
						XFlush(_display);
						if([w xGrabMouse] == GrabSuccess)
							[w xReleaseMouse];
						else
							{
							NSWindow *k = [NSApp keyWindow];
							
							if((w == k && [k isVisible]) || !k)
								[[NSApp mainMenu] close];	// parent titlebar is moving the window
							}
						}
					else
						{
						Window xfw;
						int r;
						// check if focus is in one of our windows
						XGetInputFocus(_display, &xfw, &r);
						if(!(w = XRWindowWithXWindow(xfw)))
							{
							NSLog(@"FocusOut 3");
							//							[NSApp deactivate];
							}
						}
					if(__xKeyWindowNeedsFocus == xe.xfocus.window)
						__xKeyWindowNeedsFocus = None;
#endif
					break;
					}
				case GraphicsExpose:
					NSDebugLog(@"GraphicsExpose\n");
					break;
				case NoExpose:
					NSDebugLog(@"NoExpose\n");
					break;
				case GravityNotify:						// window is moved because
					NSDebugLog(@"GravityNotify\n");		// of a change in the size
					break;								// of its parent
				case KeyPress:							// a key has been pressed
				case KeyRelease:						// a key has been released
#if 0
					NSLog(@"Process key event");
#endif
					{
						NSEventType eventType=(xe.type == KeyPress)?NSKeyDown:NSKeyUp;
						char buf[256];
						XComposeStatus cs;
						KeySym ksym;
						NSString *keys = @"";
						unsigned short keyCode = 0;
						unsigned mflags, _modFlags;
						unsigned int count = XLookupString(&xe.xkey, buf, 256, &ksym, &cs);
						
						
						buf[MIN(count, 255)] = '\0'; // Terminate string properly
#if 0
						NSLog(@"xKeyEvent: xkey.state=%d", xe.xkey.state);
#endif						
						_modFlags = mflags = xKeyModifierFlags(xe.xkey.state);		// decode modifier flags
						if((keyCode = xKeyCode(&xe, ksym, &_modFlags)) != 0 || count != 0)
							keys = [NSString stringWithCString:buf];	// key has a code
						else
							{ // if we have neither a keyCode nor characters we have just changed a modifier Key
							if(eventType == NSKeyUp)
								_modFlags &= ~mflags;	// just reset flags by this key
							eventType=NSFlagsChanged;
							}
						__modFlags=_modFlags;	// if modified
						e= [NSEvent keyEventWithType:eventType
											location:(NSPoint){0,0}
									   modifierFlags:__modFlags
										   timestamp:(NSTimeInterval)xe.xkey.time*0.001
										windowNumber:windowNumber
											 context:self
										  characters:keys
						 charactersIgnoringModifiers:[keys lowercaseString]		// FIX ME?
										   isARepeat:NO	// any idea how to FIXME?
											 keyCode:keyCode];
#if 0
						NSLog(@"xKeyEvent: %@", e);
#endif
						break;
					}
					
				case KeymapNotify:						// reports the state of the
					NSDebugLog(@"KeymapNotify");		// keyboard when pointer or
					break;								// focus enters a window
					
				case MapNotify:							// when a window changes
					NSDebugLog(@"MapNotify");			// state from ummapped to
														// mapped or vice versa
					[window _setIsVisible:YES];
					break;								 
					
				case UnmapNotify:						// find the NSWindow and
					NSDebugLog(@"UnmapNotify\n");		// inform it that it is no
														// longer visible
					[window _setIsVisible:NO];
					break;
					
				case MapRequest:						// like MapNotify but
					NSDebugLog(@"MapRequest\n");		// occurs before the
					break;								// request is carried out
					
				case MappingNotify:						// keyboard or mouse   
					NSDebugLog(@"MappingNotify\n");		// mapping has been changed
					break;								// by another client
					
				case MotionNotify:
					{ // the mouse has moved
					NSDebugLog(@"MotionNotify");
					if(xe.xmotion.state & Button1Mask)		
						type = NSLeftMouseDragged;	
					else
						type = (xe.xmotion.state & (Button3Mask|Button2Mask)) 
							? NSRightMouseDragged : NSMouseMoved;
#if 0
					if(lastMotionEvent &&
					   [NSApp _eventIsQueued:lastMotionEvent])
						{
						NSLog(@"motion event still in queue: %@", lastMotionEvent);
						}
#endif
					if(lastMotionEvent &&
					   [NSApp _eventIsQueued:lastMotionEvent] &&	// must come first because event may already have been relesed/deallocated
					   [lastMotionEvent type] == type)
						{ // replace if last motion event is still unprocessed in queue
						typedef struct _NSEvent_t { @defs(NSEvent) } _NSEvent;
						_NSEvent *a = (_NSEvent *)lastMotionEvent;	// this allows to access iVars directly
#if 0
						NSLog(@"update motion event");
#endif
						a->location_point=NSMakePoint(xe.xmotion.x, windowHeight-xe.xmotion.y);
						a->modifier_flags=__modFlags;
						a->event_time=(NSTimeInterval)xe.xmotion.time*0.001;
						a->event_data.mouse.event_num=xe.xmotion.serial;
						break;
						}
					e = [NSEvent mouseEventWithType:type		// create NSEvent
										   location:NSMakePoint(xe.xmotion.x, windowHeight-xe.xmotion.y)
									  modifierFlags:__modFlags
										  timestamp:(NSTimeInterval)xe.xmotion.time*0.001
									   windowNumber:windowNumber
											context:self
										eventNumber:xe.xmotion.serial
										 clickCount:1
										   pressure:1.0];
					lastMotionEvent = e;
#if 0
					NSLog(@"MotionNotify e=%@", e);
#endif
					break;
					}
				case PropertyNotify:
					{ // a window property has changed or been deleted
					NSDebugLog(@"PropertyNotify");
					if(_stateAtom == xe.xproperty.atom)
						{
						Atom target;
						unsigned long number_items, bytes_remaining;
						unsigned char *data;
						int status, format;						
						status = XGetWindowProperty(_display,
													xe.xproperty.window, 
													xe.xproperty.atom, 
													0, 1, False, _stateAtom,
													&target, &format, 
													&number_items,&bytes_remaining,
													(unsigned char **)&data);
						if(status != Success || !data) 
							break;
						if(*data == IconicState)
							[window miniaturize:self];
						else if(*data == NormalState)
							[window deminiaturize:self];
						if(number_items > 0)
							XFree(data);
						}
#if 1	// debug
					if(_stateAtom == xe.xproperty.atom)
						{
						char *data = XGetAtomName(_display, xe.xproperty.atom);
						NSLog(@"PropertyNotify: Atom name is '%s' \n", data);
						XFree(data);
						}
#endif
					break;
					}
				case ReparentNotify:					// a client successfully
					NSDebugLog(@"ReparentNotify\n");	// reparents a window
#if FIXME
					if(__xAppTileWindow == xe.xreparent.window)
						{ // WM reparenting appicon
						_wAppTileWindow = xe.xreparent.parent;
					//	[window xSetFrameFromXContentRect: [window xFrame]];
						XSelectInput(_display, _wAppTileWindow, StructureNotifyMask);
						// FIXME: should this be an NSNotification?
						}
#endif
					break;
				case ResizeRequest:						// another client (or WM) attempts to change window size
					NSDebugLog(@"ResizeRequest"); 
					break;
				case SelectionNotify:
					NSLog(@"SelectionNotify");
					{
//						NSPasteboard *pb = [NSPasteboard generalPasteboard];
						
						// FIXME: should this be an NSNotification? Or where should we send this event to?
//						[pb _handleSelectionNotify:(XSelectionEvent *)&xe];
						
						e = [NSEvent otherEventWithType:NSFlagsChanged	
											   location:NSZeroPoint
										  modifierFlags:0
											  timestamp:(NSTimeInterval)xe.xbutton.time*0.001
										   windowNumber:windowNumber	// 0 ??
												context:self
												subtype:0
												  data1:0
												  data2:0];
						break;
					}					
				case SelectionClear:						// X selection events 
				case SelectionRequest:
					NSLog(@"SelectionRequest");
#if FIXME
					xHandleSelectionRequest((XSelectionRequestEvent *)&xe);
#endif
					break;
				case VisibilityNotify:						// window's visibility 
					NSDebugLog(@"VisibilityNotify");		// has changed
					break;
				default:									// should not get here
					NSLog(@"Received an untrapped event");
					break;
				} // end of event type switch
			if(e != nil)
				[NSApp postEvent:e atStart:NO];			// add event to app queue
			}
		}
}

@end

@implementation NSColor (NSBackend)

- (unsigned long) _pixelForScreen:(Screen *) scr;
{
	// FIXME: we must cache different pixel values for different screens!
	if(!_colorData)
		{
		NSColor *color;
		if(!(_colorData = objc_malloc(sizeof(XColor))))
			[NSException raise:NSMallocException format:@"Unable to malloc XColor backend structure"];
		if(_colorspaceName != NSDeviceRGBColorSpace)
			color=[self colorUsingColorSpaceName:NSDeviceRGBColorSpace];	// convert
		else
			color=self;
		if(color)
			{
			((XColor *) _colorData)->red = (unsigned short)(65535 * [color redComponent]);
			((XColor *) _colorData)->green = (unsigned short)(65535 * color->_rgb.green);
			((XColor *) _colorData)->blue = (unsigned short)(65535 * color->_rgb.blue);
			}
		if(!color || !scr || !XAllocColor(_display, XDefaultColormapOfScreen(scr), _colorData))
			NSLog(@"Unable to allocate color %@ for X11 Screen %08x", color, scr);
#if OLD
		else
			{ // Copy actual values back to the NSColor variables
			_rgb.red = ((float) (((XColor *) _colorData)->red)) / 65535;
			_rgb.green = ((float) (((XColor *) _colorData)->green)) / 65535;
			_rgb.blue = ((float) (((XColor *) _colorData)->blue)) / 65535;
			_color.rgb=YES;
			}
#endif
		}
	return ((XColor *) _colorData)->pixel;
}

- (void) dealloc;
{
	if(_colorData)
		objc_free(_colorData);
	// release other components - super is superclass not overwritten definition
	[super dealloc];
}

@end

/* notes:
- a font file contains multiple faces
- faces correspond to NSFont, so the value for backend_private 
- a font-file is therefore handled by NSFontDescriptor (i.e. usually the Font Family)
*/

@implementation NSFont (NSBackend)

// FIXME: we should overwrite all required methods and wrap libFreetype here
// tutorial: http://www.freetype.org/freetype2/docs/tutorial/step1.html

static FT_Library _freetypeLibrary;

- (FT_Face) _face;
{
	FT_Error error;
	FT_Face face;	// should be an Ivar!
	if(!_freetypeLibrary)
		{
		if((error=FT_Init_FreeType(&_freetypeLibrary)))
			{
			// ... an error occurred during library initialization ...
			}
		}
	// search for file matching the font name here
	error=FT_New_Face(_freetypeLibrary, [@"/Library/Fonts/arial.ttf" fileSystemRepresentation], 0, &face);
	if(error == FT_Err_Unknown_File_Format)
		{
//		... the font file could be opened and read, but it appears
//		... that its font format is unsupported
		}
	else if(error)
		{
//		... another error code means that the font file could not
//		... be opened or read, or simply that it is broken...
		}
	return face;
}

- (XFontStruct *) _font;
{
	if(!_fontStruct)
		{
		char *xFoundry = "*";
		char *xFamily = "helvetica"; 							// font family 
		char *xWeight = "medium"; 								// font weight (light, bold)
		char *xSlant = "r";										// font slant (roman, italic, oblique)
		char *xWidth = "*";										// width (normal, condensed, narrow)
		char *xStyle = "*";										// additional style (sans serif)
		char *xPixel = "*";
		char *xPoint = (char *) [[NSString stringWithFormat:@"%d", (int) (10.0*[self pointSize])] cString];	// variable size - resize by CTM and/or userspace scaling?
		char *xXDPI = "*";										// we could try to match screen resolution first and try again with *
		char *xYDPI = "*";
		char *xSpacing = "*";									// P proportional, M monospaced, C cell
		char *xAverage = "*";									// average width
		char *xRegistry = "*";
		char *xEncoding = "*";
		
		NSString *xf = nil;
		if([_fontName caseInsensitiveCompare:@"Helvetica-Bold"] == NSOrderedSame)
			{
			xWeight = "bold";
			}
		else if(([_fontName caseInsensitiveCompare: @"Courier"] == NSOrderedSame))
			{
			xFamily = "courier";
			}
		else if(([_fontName caseInsensitiveCompare: @"Ohlfs"] == NSOrderedSame))
			{
			xFoundry="misc";
			xFamily="fixed";
			xWidth="ohlfs";
			xRegistry="iso8859";
			xFamily="1";
			}
		else if(([_fontName caseInsensitiveCompare: @"Courier-Bold"] == NSOrderedSame))
			{
			xFamily = "courier";
			xWeight = "bold";
			}
		xf=[NSString stringWithFormat: @"-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s",
			xFoundry, xFamily, xWeight, xSlant, xWidth, xStyle,
			xPixel, xPoint, xXDPI, xYDPI, xSpacing, xAverage,
			xRegistry, xEncoding];
		if((_fontStruct = XLoadQueryFont(_display, [xf cString])) == NULL)		// Load X font
			{
			NSLog(@"font: %@ is not available", xf);
			NSLog(@"Trying 9x15 system fixed font instead");			
			if((_fontStruct = XLoadQueryFont(_display, "9x15")) == NULL)
				{
				if((_fontStruct = XLoadQueryFont(_display, "fixed")) == NULL)
					{
					NSLog(@"Unable to open any fixed font");
					return NULL;
					}
				}
			}
		else
			NSDebugLog(@"Loaded font: %@", xf);
#if OLD
		font->_xFontInfo = xFontStruct;
		if (XFONT != _xFontInfo->fid) 
			{
			XFONT = _xFontInfo->fid;
			XRFONT = _xFontInfo;
			__gs->xGC->dirty |= GCFont;
			_XFlushGCCache(_display, _state->_gc);
			ASCENDER = _xFontInfo->max_bounds.ascent;
			DESCENDER = _xFontInfo->max_bounds.descent;
			FONT = self;
			}
#endif
		}
	return _fontStruct;
}

- (void) dealloc;
{
	if(_fontStruct)
		XFreeFont(_display, _fontStruct);	// no longer needed
	// release other components - super is superclass not overwritten definition
	[super dealloc];
}

@end

@implementation NSFontDescriptor (NSBackend)

// manage font files here 
// we could cache an FT_Face matching the NSFontDescriptor in _backendPrivate

- (void) dealloc;
{
	if(_backendPrivate)
		; // release
	[_attributes release];
	[super dealloc];
}

@end

@implementation NSCursor (NSBackend)

- (Cursor) _cursor;
{
	if(!_backendPrivate)
		{
		if(_image)
			{
#if FIXME
			// we should lockFocus on a Pixmap and call _draw:bestReo
			NSBitmapImageRep *bestRep = [_image bestRepresentationForDevice:nil];	// get device description??
			Pixmap mask = (Pixmap)[bestRep xPixmapMask];
			Pixmap bits = (Pixmap)[bestRep xPixmapBitmap];
			_backendPrivate = XCreatePixmapCursor(_display, bits, mask, &fg, &bg, _hotSpot.x, _hotSpot.y);
#endif
			}
		if(!_backendPrivate)
			return None;	// did not initialize
		}
	return (Cursor) _backendPrivate;
}

- (void) dealloc;
{
	if(_backendPrivate)
		XFreeCursor(_display, (Cursor) _backendPrivate);	// no longer needed
	[_image release];
	[super dealloc];
}

@end

// EOF