/*
 * Copyright (c) 1999-2001 Greg Haerr <greg@censoft.com>
 * Copyright (c) 1999 Alex Holden <alex@linuxhacker.org>
 * Copyright (c) 2000 Vidar Hokstad
 * Copyright (c) 2000 Morten Rolland <mortenro@screenmedia.no>
 * Portions Copyright (c) 1991 David I. Bell
 *
 * Permission is granted to use, distribute, or modify this source,
 * provided that this copyright notice remains intact.
 *
 * Completely rewritten for speed by Greg Haerr
 *
 * This is the server side of the network interface, which accepts
 * connections from clients, receives functions from them, and dispatches
 * events to them.
 */
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#if HAVE_SHAREDMEM_SUPPORT
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#endif
#if ELKS
#include <linuxmt/na.h>
#elif __ECOS
#include <netinet/in.h>

#else
#include <sys/un.h>
#endif
#include "serv.h"
#include "nxproto.h"

/* fix bad MIPS sys headers...*/
#ifndef SOCK_STREAM
#define SOCK_STREAM	2	/* <asm/socket.h>*/
#endif

extern	int		un_sock;
extern	GR_CLIENT	*root_client;
extern	int		current_fd;

static int GsWriteType(int,short);

/*
 * Wrapper functions called after full packet read
 */
static void 
GrOpenWrapper(void *r)
{
	nxOpenReq *req = r;

	/* store process id of client*/
	curclient->processid = req->pid;

	GrOpen();
}

static void
GrCloseWrapper(void *r)
{
	GrClose();
}

static void
GrGetScreenInfoWrapper(void *r)
{
	GR_SCREEN_INFO si;

	GrGetScreenInfo(&si);
	GsWriteType(current_fd,GrNumGetScreenInfo);
	GsWrite(current_fd, &si, sizeof(si));
}

static void
GrGetSysColorWrapper(void *r)
{
	nxGetSysColorReq *req = r;
	GR_COLOR color = GrGetSysColor(req->index);

	GsWriteType(current_fd, GrNumGetSysColor);
	GsWrite(current_fd, &color, sizeof(color));
}

static void
GrNewWindowWrapper(void *r)
{
	nxNewWindowReq *req = r;
	GR_WINDOW_ID	wid;

	wid = GrNewWindow(req->parentid, req->x, req->y, req->width,
		req->height, req->bordersize, req->backgroundcolor,
		req->bordercolor);

	GsWriteType(current_fd,GrNumNewWindow);
	GsWrite(current_fd, &wid, sizeof(wid));
}


static void
GrNewPixmapWrapper(void *r)
{
	nxNewPixmapReq *req = r;
	GR_WINDOW_ID	wid;

        /* FIXME: Add support for passing info about shared memory
	 * segment
	 */
	wid = GrNewPixmap(req->width, req->height, 0);

	GsWriteType(current_fd,GrNumNewPixmap);
	GsWrite(current_fd, &wid, sizeof(wid));
}

static void
GrNewInputWindowWrapper(void *r)
{
	nxNewInputWindowReq *req = r;
	GR_WINDOW_ID         wid;

	wid = GrNewInputWindow(req->parentid, req->x, req->y, req->width,
		req->height);
	GsWriteType(current_fd,GrNumNewInputWindow);
	GsWrite(current_fd, &wid, sizeof(wid));
}

static void
GrDestroyWindowWrapper(void *r)
{
	nxDestroyWindowReq *req = r;

	GrDestroyWindow(req->windowid);
}

static void
GrNewGCWrapper(void *r)
{
	GR_GC_ID gc = GrNewGC();

	GsWriteType(current_fd,GrNumNewGC);
	GsWrite(current_fd, &gc, sizeof(gc));
}

static void
GrCopyGCWrapper(void *r)
{
	nxCopyGCReq *req = r;
	GR_GC_ID     gcnew;

	gcnew = GrCopyGC(req->gcid);
	GsWriteType(current_fd,GrNumCopyGC);
	GsWrite(current_fd, &gcnew, sizeof(gcnew));
}

static void
GrGetGCInfoWrapper(void *r)
{
	nxGetGCInfoReq *req = r;
	GR_GC_INFO      gcinfo;

	GrGetGCInfo(req->gcid, &gcinfo);
	GsWriteType(current_fd,GrNumGetGCInfo);
	GsWrite(current_fd, &gcinfo, sizeof(gcinfo));
}

static void
GrDestroyGCWrapper(void *r)
{
	nxDestroyGCReq *req = r;

	GrDestroyGC(req->gcid);
}

static void
GrNewRegionWrapper(void *r)
{
	GR_REGION_ID region = GrNewRegion();

	GsWriteType(current_fd, GrNumNewRegion);
	GsWrite(current_fd, &region, sizeof(region));
}

static void
GrDestroyRegionWrapper(void *r)
{
	nxDestroyRegionReq *req = r;

	GrDestroyRegion(req->regionid);
}

static void
GrIntersectRegionWrapper(void *r)
{
	nxIntersectRegionReq *req = r;

	GrIntersectRegion(req->regionid, req->srcregionid1,
		req->srcregionid2);
}

static void
GrUnionRectWithRegionWrapper(void *r)
{
	nxUnionRectWithRegionReq *req = r;

	GrUnionRectWithRegion(req->regionid, &(req->rect));
}

static void
GrUnionRegionWrapper(void *r)
{
	nxUnionRegionReq *req = r;

	GrUnionRegion(req->regionid, req->srcregionid1, req->srcregionid2);
}

static void
GrSubtractRegionWrapper(void *r)
{
	nxSubtractRegionReq *req = r;

	GrSubtractRegion(req->regionid, req->srcregionid1, req->srcregionid2);
}

static void
GrXorRegionWrapper(void *r)
{
	nxXorRegionReq *req = r;

	GrXorRegion(req->regionid, req->srcregionid1, req->srcregionid2);
}

static void
GrPointInRegionWrapper(void *r)
{
	nxPointInRegionReq *req = r;
	GR_BOOL ret_value = GrPointInRegion(req->regionid, req->x, req->y);
		
	GsWriteType(current_fd, GrNumPointInRegion);
	GsWrite(current_fd, &ret_value, sizeof(ret_value));
}

static void
GrRectInRegionWrapper(void *r)
{
	nxRectInRegionReq *req = r;
	unsigned short ret_value;
	
	ret_value = (unsigned short)GrRectInRegion(req->regionid,
		req->x, req->y, req->w, req->h);
		
	GsWriteType(current_fd, GrNumRectInRegion);
	GsWrite(current_fd, &ret_value, sizeof(ret_value));
}

static void
GrEmptyRegionWrapper(void *r)
{
	nxEmptyRegionReq *req = r;
	GR_BOOL		  ret_value;
	
	ret_value = GrEmptyRegion(req->regionid);
	GsWriteType(current_fd, GrNumEmptyRegion);
	GsWrite(current_fd, &ret_value, sizeof(ret_value));
}

static void
GrEqualRegionWrapper(void *r)
{
	nxEqualRegionReq *req = r;
	GR_BOOL		  ret_value;
	
	ret_value = GrEqualRegion(req->region1, req->region2);
	GsWriteType(current_fd, GrNumEqualRegion);
	GsWrite(current_fd, &ret_value, sizeof(ret_value));
}

static void
GrOffsetRegionWrapper(void *r)
{
	nxOffsetRegionReq *req = r;
	
	GrOffsetRegion(req->region, req->dx, req->dy);
}

static void
GrSetGCRegionWrapper(void *r)
{
	nxSetGCRegionReq *req = r;

	GrSetGCRegion(req->gcid, req->regionid);
}

static void
GrSetGCClipOriginWrapper(void *r)
{
	nxSetGCClipOriginReq *req = r;
	GrSetGCClipOrigin(req->gcid, req->xoff, req->yoff);
}

static void
GrGetRegionBoxWrapper(void *r)
{
	nxRectInRegionReq *req = r;
	GR_BOOL		   ret_value;
	GR_RECT		   ret_rect;

	ret_value = GrGetRegionBox(req->regionid, &ret_rect);
		
	GsWriteType(current_fd, GrNumGetRegionBox);
	GsWrite(current_fd, &ret_rect, sizeof(ret_rect));
	GsWriteType(current_fd, GrNumGetRegionBox);
	GsWrite(current_fd, &ret_value, sizeof(ret_value));
}

static void
GrNewPolygonRegionWrapper(void *r)
{
	GR_REGION_ID region;
	nxNewPolygonRegionReq *req = r;
	int count;
	
	/* FIXME: unportable method, depends on sizeof(int) in GR_POINT*/
	count = GetReqVarLen(req) / sizeof(GR_POINT);	
	region = GrNewPolygonRegion(req->mode, count,
		(GR_POINT *)GetReqData(req));
	
	GsWriteType(current_fd, GrNumNewPolygonRegion);
	GsWrite(current_fd, &region, sizeof(region));
}

static void
GrMapWindowWrapper(void *r)
{
	nxMapWindowReq *req = r;

	GrMapWindow(req->windowid);
}

static void
GrUnmapWindowWrapper(void *r)
{
	nxUnmapWindowReq *req = r;

	GrUnmapWindow(req->windowid);
}

static void
GrRaiseWindowWrapper(void *r)
{
	nxRaiseWindowReq *req = r;

	GrRaiseWindow(req->windowid);
}

static void
GrLowerWindowWrapper(void *r)
{
	nxLowerWindowReq *req = r;

	GrLowerWindow(req->windowid);
}

static void
GrMoveWindowWrapper(void *r)
{
	nxMoveWindowReq *req = r;

	GrMoveWindow(req->windowid, req->x, req->y);
}

static void
GrResizeWindowWrapper(void *r)
{
	nxResizeWindowReq *req = r;

	GrResizeWindow(req->windowid, req->width, req->height);
}

static void
GrReparentWindowWrapper(void *r)
{
	nxReparentWindowReq *req = r;

	GrReparentWindow(req->windowid, req->parentid, req->x, req->y);
}

static void
GrGetWindowInfoWrapper(void *r)
{
	nxGetWindowInfoReq *req = r;
	GR_WINDOW_INFO      wi;

	GrGetWindowInfo(req->windowid, &wi);
	GsWriteType(current_fd,GrNumGetWindowInfo);
	GsWrite(current_fd, &wi, sizeof(wi));
}

static void
GrGetFontInfoWrapper(void *r)
{
	nxGetFontInfoReq *req = r;
	GR_FONT_INFO      fi;

	GrGetFontInfo(req->fontid, &fi);
	GsWriteType(current_fd,GrNumGetFontInfo);
	GsWrite(current_fd, &fi, sizeof(fi));
}

static void
GrGetFocusWrapper(void *r)
{
	GR_WINDOW_ID wid = GrGetFocus();

	GsWriteType(current_fd, GrNumGetFocus);
	GsWrite(current_fd, &wid, sizeof(wid));
}

static void
GrSetFocusWrapper(void *r)
{
	nxSetFocusReq *req = r;

	GrSetFocus(req->windowid);
}

static void
GrSetWindowCursorWrapper(void *r)
{
	nxSetWindowCursorReq *req = r;

	GrSetWindowCursor(req->windowid, req->cursorid);
}

static void
GrClearAreaWrapper(void *r)
{
	nxClearAreaReq *req = r;

	GrClearArea(req->windowid, req->x, req->y, req->width,
		req->height, req->exposeflag);
}

static void
GrSelectEventsWrapper(void *r)
{
	nxSelectEventsReq *req = r;

	GrSelectEvents(req->windowid, req->eventmask);
}

static void
GrGetNextEventWrapper(void *r)
{
#if 1
	/* tell main loop to call Finish routine on event*/
	curclient->waiting_for_event = TRUE;
#else
	GR_EVENT evt;

	/* first check if any event ready*/
	GrCheckNextEvent(&evt);
	if(evt.type == GR_EVENT_TYPE_NONE) {
		/* tell main loop to call Finish routine on event*/
		curclient->waiting_for_event = TRUE;
		return;
	}

	GsWriteType(current_fd, GrNumGetNextEvent);
	GsWrite(current_fd, &evt, sizeof(evt));
	if(evt.type == GR_EVENT_TYPE_CLIENT_DATA) {
		GsWrite(fd, evt.data, evt.datalen);
		free(evt.data);
	}
#endif
}

/* Complete the GrGetNextEvent call from client.
 * The client is still waiting on a read at this point.
 */
void
GrGetNextEventWrapperFinish(int fd)
{
	GR_EVENT evt;
	GR_EVENT_CLIENT_DATA *cde;

	/* get the event and pass it to client*/
	/* this will never be GR_EVENT_TYPE_NONE*/
	GrCheckNextEvent(&evt);

	GsWriteType(fd,GrNumGetNextEvent);
	GsWrite(fd, &evt, sizeof(evt));
	if(evt.type == GR_EVENT_TYPE_CLIENT_DATA) {
		cde = (GR_EVENT_CLIENT_DATA *)&evt;
		if(cde->data) {
			GsWrite(fd, cde->data, cde->datalen);
			free(cde->data);
		} cde->datalen = 0;
	}
}

static void
GrCheckNextEventWrapper(void *r)
{
	GR_EVENT evt;
	GR_EVENT_CLIENT_DATA *cde;

	GrCheckNextEvent(&evt);

	GsWriteType(current_fd,GrNumGetNextEvent);
	GsWrite(current_fd, &evt, sizeof(evt));
	if(evt.type == GR_EVENT_TYPE_CLIENT_DATA) {
		cde = (GR_EVENT_CLIENT_DATA *)&evt;
		if(cde->data) {
			GsWrite(current_fd, cde->data, cde->datalen);
			free(cde->data);
		} cde->datalen = 0;
	}
}

static void
GrPeekEventWrapper(void *r)
{
	GR_EVENT evt;
	GR_CHAR	 ret;
	GR_EVENT_CLIENT_DATA *cde;

	ret = GrPeekEvent(&evt);

	GsWriteType(current_fd,GrNumPeekEvent);
	GsWrite(current_fd, &evt, sizeof(evt));
	if(evt.type == GR_EVENT_TYPE_CLIENT_DATA) {
		cde = (GR_EVENT_CLIENT_DATA *)&evt;
		if(cde->data) {
			GsWrite(current_fd, cde->data, cde->datalen);
			free(cde->data);
		} cde->datalen = 0;
	}
	GsWrite(current_fd, &ret, sizeof(GR_CHAR));
}

static void
GrLineWrapper(void *r)
{
	nxLineReq *req = r;

	GrLine(req->drawid, req->gcid, req->x1, req->y1, req->x2, req->y2);
}

static void
GrPointWrapper(void *r)
{
	nxPointReq *req = r;

	GrPoint(req->drawid, req->gcid, req->x, req->y);

}

static void
GrPointsWrapper(void *r)
{
	nxPointsReq *req = r;
	int        count;

	count = GetReqVarLen(req) / sizeof(GR_POINT);
	GrPoints(req->drawid, req->gcid, count, (GR_POINT *)GetReqData(req));
}

static void
GrRectWrapper(void *r)
{
	nxRectReq *req = r;

	GrRect(req->drawid, req->gcid, req->x, req->y, req->width, req->height);
}

static void
GrFillRectWrapper(void *r)
{
	nxFillRectReq *req = r;

	GrFillRect(req->drawid, req->gcid, req->x, req->y, req->width,
		req->height);
}

static void
GrPolyWrapper(void *r)
{
	nxPolyReq *req = r;
	int        count;

	count = GetReqVarLen(req) / sizeof(GR_POINT);
	GrPoly(req->drawid, req->gcid, count, (GR_POINT *)GetReqData(req));
}

/* FIXME: fails with pointtable size > 64k if sizeof(int) == 2*/
static void
GrFillPolyWrapper(void *r)
{
	nxPolyReq *req = r;
	int        count;

	count = GetReqVarLen(req) / sizeof(GR_POINT);
	GrFillPoly(req->drawid, req->gcid, count, (GR_POINT *)GetReqData(req));
}

static void
GrEllipseWrapper(void *r)
{
	nxEllipseReq *req = r;

	GrEllipse(req->drawid, req->gcid, req->x, req->y, req->rx, req->ry);
}

static void
GrFillEllipseWrapper(void *r)
{
	nxFillEllipseReq *req = r;

	GrFillEllipse(req->drawid, req->gcid, req->x, req->y, req->rx, req->ry);
}

static void
GrArcWrapper(void *r)
{
	nxArcReq *req = r;

	GrArc(req->drawid, req->gcid, req->x, req->y, req->rx, req->ry,
		req->ax, req->ay, req->bx, req->by, req->type);
}

static void
GrArcAngleWrapper(void *r)
{
	nxArcAngleReq *req = r;

	GrArcAngle(req->drawid, req->gcid, req->x, req->y, req->rx, req->ry,
		req->angle1, req->angle2, req->type);
}

static void
GrSetGCForegroundWrapper(void *r)
{
	nxSetGCForegroundReq *req = r;

	GrSetGCForeground(req->gcid, req->color);
}

static void
GrSetGCBackgroundWrapper(void *r)
{
	nxSetGCBackgroundReq *req = r;

	GrSetGCBackground(req->gcid, req->color);
}

static void
GrSetGCUseBackgroundWrapper(void *r)
{
	nxSetGCUseBackgroundReq *req = r;

	GrSetGCUseBackground(req->gcid, req->flag);
}

static void
GrSetGCModeWrapper(void *r)
{
	nxSetGCModeReq *req = r;

	GrSetGCMode(req->gcid, req->mode);
}

static void
GrCreateFontWrapper(void *r)
{
	nxCreateFontReq *req = r;
	GR_FONT_ID 	fontid;

	if(req->lf_used)
		fontid = GrCreateFont(NULL, 0, &req->lf);
	else
		fontid = GrCreateFont(req->lf.lfFaceName, req->height, NULL);

	GsWriteType(current_fd,GrNumCreateFont);
	GsWrite(current_fd, &fontid, sizeof(fontid));
}

static void
GrSetFontSizeWrapper(void *r)
{
	nxSetFontSizeReq *req = r;

 	GrSetFontSize(req->fontid, req->fontsize);
}

static void
GrSetFontRotationWrapper(void *r)
{
	nxSetFontRotationReq *req = r;

 	GrSetFontRotation(req->fontid, req->tenthdegrees);
}

static void
GrSetFontAttrWrapper(void *r)
{
	nxSetFontAttrReq *req = r;

 	GrSetFontAttr(req->fontid, req->setflags, req->clrflags);
}

static void
GrDestroyFontWrapper(void *r)
{
	nxDestroyFontReq *req = r;

	GrDestroyFont(req->fontid);
}

static void
GrSetGCFontWrapper(void *r)
{
	nxSetGCFontReq *req = r;

	GrSetGCFont(req->gcid, req->fontid);
}

static void
GrGetGCTextSizeWrapper(void *r)
{
	nxGetGCTextSizeReq *req = r;
	GR_SIZE             retwidth, retheight, retbase;

	GrGetGCTextSize(req->gcid, GetReqData(req), GetReqVarLen(req),
		req->flags, &retwidth, &retheight, &retbase);

 	GsWriteType(current_fd,GrNumGetGCTextSize);
	GsWrite(current_fd, &retwidth, sizeof(retwidth));
	GsWrite(current_fd, &retheight, sizeof(retheight));
	GsWrite(current_fd, &retbase, sizeof(retbase));
}

/* FIXME: fails with size > 64k if sizeof(int) == 2*/
static void
GsReadAreaWrapper(void *r)
{
	nxReadAreaReq *req = r;
	int            size;
	GR_PIXELVAL *   area;

	/* FIXME: optimize for smaller pixelvals*/
	size = req->width * req->height * sizeof(GR_PIXELVAL);

	if(!(area = malloc(size))) {
		/*GsPutCh(current_fd, GrRetENoMem);*/ /* FIXME*/
		/*return;*/
	}

	GrReadArea(req->drawid, req->x, req->y, req->width, req->height, area);
	GsWriteType(current_fd,GrNumReadArea);
	GsWrite(current_fd, area, size);
	free(area);
}

/* FIXME: fails with size > 64k if sizeof(int) == 2*/
static void
GrAreaWrapper(void *r)
{
	nxAreaReq *req = r;

	GrArea(req->drawid, req->gcid, req->x, req->y, req->width, req->height,
		GetReqData(req), req->pixtype);
}

/* FIXME: fails with bitmapsize > 64k if sizeof(int) == 2*/
static void
GrBitmapWrapper(void *r)
{
	nxBitmapReq *req = r;

	GrBitmap(req->drawid, req->gcid, req->x, req->y, req->width,
		req->height, GetReqData(req));
}

static void
GrDrawImageBitsWrapper(void *r)
{
	nxDrawImageBitsReq *req = r;
	char *		    addr;
	int		    imagesize;
	GR_IMAGE_HDR	    hdr;

	hdr.width = req->width;
	hdr.height = req->height;
	hdr.planes = req->planes;
	hdr.bpp = req->bpp;
	hdr.pitch = req->pitch;
	hdr.bytesperpixel = req->bytesperpixel;
	hdr.compression = req->compression;
	hdr.palsize = req->palsize;
	hdr.transcolor = req->transcolor;
	addr = GetReqData(req);
	hdr.imagebits = (MWUCHAR *)addr;
	imagesize = hdr.pitch * hdr.height;
	hdr.palette = (MWPALENTRY *)(addr + imagesize);
	GrDrawImageBits(req->drawid, req->gcid, req->x, req->y, &hdr);
}

static void
GrCopyAreaWrapper(void *r)
{
	nxCopyAreaReq *req = r;

	GrCopyArea(req->drawid, req->gcid, req->x, req->y, req->width, req->height,
		req->srcid, req->srcx, req->srcy, req->op);
}

static void
GrTextWrapper(void *r)
{
	nxTextReq *req = r;

	GrText(req->drawid, req->gcid, req->x, req->y, GetReqData(req),
		req->count, req->flags);
}

static void
GrNewCursorWrapper(void *r)
{
	nxNewCursorReq *req = r;
	int		varlen;
	char *          bitmap;
	GR_CURSOR_ID	cursorid;

	varlen = GetReqVarLen(req);
	bitmap = GetReqData(req);

	cursorid = GrNewCursor(req->width, req->height,
		req->hotx, req->hoty, req->fgcolor, req->bgcolor,
		(GR_BITMAP *)bitmap, (GR_BITMAP *)(bitmap+varlen/2));

	GsWriteType(current_fd, GrNumNewCursor);
	GsWrite(current_fd, &cursorid, sizeof(cursorid));
}

static void
GrMoveCursorWrapper(void *r)
{
	nxMoveCursorReq *req = r;

	GrMoveCursor(req->x, req->y);
}

static void
GrGetSystemPaletteWrapper(void *r)
{
	GR_PALETTE             pal;

	GrGetSystemPalette(&pal);
	GsWriteType(current_fd,GrNumGetSystemPalette);
	GsWrite(current_fd, &pal, sizeof(pal));
}

static void
GrSetSystemPaletteWrapper(void *r)
{
	nxSetSystemPaletteReq *req = r;
	GR_PALETTE	       pal;

	pal.count = req->count;
	memcpy(pal.palette, req->palette, sizeof(pal.palette));
	GrSetSystemPalette(req->first, &pal);
}

static void
GrFindColorWrapper(void *r)
{
	nxFindColorReq *req = r;
	GR_PIXELVAL	pixel;

	GrFindColor(req->color, &pixel);
	GsWriteType(current_fd,GrNumFindColor);
	GsWrite(current_fd, &pixel, sizeof(pixel));
}

static void
GrInjectEventWrapper(void *r)
{
	nxInjectEventReq *req = r;

	switch(req->event_type) {
	case GR_INJECT_EVENT_POINTER:
		GrInjectPointerEvent(req->event.pointer.x,
				     req->event.pointer.y,
				     req->event.pointer.button,
				     req->event.pointer.visible);
		break;

	case GR_INJECT_EVENT_KEYBOARD:
		GrInjectKeyboardEvent(req->event.keyboard.wid,
				      req->event.keyboard.keyvalue,
				      req->event.keyboard.modifier,
				      req->event.keyboard.scancode,
				      req->event.keyboard.pressed);
		break;
	}
}

static void
GrSetWMPropertiesWrapper(void *r)
{
	nxSetWMPropertiesReq *req = r;
	GR_WM_PROPERTIES *props = GetReqData(req);

	if(GetReqVarLen(req) - sizeof(GR_WM_PROPERTIES)) 
		props->title = (char *)props + sizeof(GR_WM_PROPERTIES);
	else
		props->title = NULL;
	GrSetWMProperties(req->windowid, props);
}

static void
GrGetWMPropertiesWrapper(void *r)
{
	nxGetWMPropertiesReq *req = r;
	UINT16 textlen;
	GR_WM_PROPERTIES props;
	
	GrGetWMProperties(req->windowid, &props);

	if(props.title)
		textlen = strlen(props.title) + 1;
	else textlen = 0;

	GsWriteType(current_fd,GrNumGetWMProperties);
	GsWrite(current_fd, &props, sizeof(props));
	GsWrite(current_fd, &textlen, sizeof(textlen));
	if(textlen)
		GsWrite(current_fd, props.title, textlen);
}

static void
GrCloseWindowWrapper(void *r)
{
	nxCloseWindowReq *req = r;

	GrCloseWindow(req->windowid);
}

static void
GrKillWindowWrapper(void *r)
{
	nxKillWindowReq *req = r;

	GrKillWindow(req->windowid);
}

static void
GrDrawImageFromFileWrapper(void *r)
{
	nxDrawImageFromFileReq *req = r;

	GrDrawImageFromFile(req->drawid, req->gcid, req->x, req->y, req->width,
		req->height, GetReqData(req), req->flags);
}

static void
GrLoadImageFromFileWrapper(void *r)
{
	nxLoadImageFromFileReq *req = r;
	GR_IMAGE_ID		id;

	id = GrLoadImageFromFile(GetReqData(req), req->flags);
	GsWriteType(current_fd, GrNumLoadImageFromFile);
	GsWrite(current_fd, &id, sizeof(id));
}

static void
GrDrawImageToFitWrapper(void *r)
{
	nxDrawImageToFitReq *req = r;

	GrDrawImageToFit(req->drawid, req->gcid, req->x, req->y, req->width,
		req->height, req->imageid);
}

static void
GrFreeImageWrapper(void *r)
{
	nxFreeImageReq *req = r;

	GrFreeImage(req->id);
}

static void
GrGetImageInfoWrapper(void *r)
{
	nxGetImageInfoReq *req = r;
	GR_IMAGE_INFO	   info;

	GrGetImageInfo(req->id, &info);
	GsWriteType(current_fd, GrNumGetImageInfo);
	GsWrite(current_fd, &info, sizeof(info));
}

static void
GrSetScreenSaverTimeoutWrapper(void *r)
{
	nxSetScreenSaverTimeoutReq *req = r;

	GrSetScreenSaverTimeout(req->timeout);
}

static void
GrSetSelectionOwnerWrapper(void *r)
{
	nxSetSelectionOwnerReq *req = r;

	GrSetSelectionOwner(req->wid, GetReqData(req));
}

static void
GrGetSelectionOwnerWrapper(void *r)
{
	GR_CHAR *typelist;
	GR_WINDOW_ID wid;
	unsigned short len;

	wid = GrGetSelectionOwner(&typelist);
	GsWriteType(current_fd, GrNumGetSelectionOwner);
	GsWrite(current_fd, &wid, sizeof(wid));

	if(wid) {
		len = strlen(typelist) + 1;
		GsWrite(current_fd, &len, sizeof(len));
		GsWrite(current_fd, typelist, len);
	}
}

static void
GrRequestClientDataWrapper(void *r)
{
	nxRequestClientDataReq *req = r;

	GrRequestClientData(req->wid, req->rid, req->serial, req->mimetype);
}

static void
GrSendClientDataWrapper(void *r)
{
	nxSendClientDataReq *req = r;

	GrSendClientData(req->wid, req->did, req->serial, req->len,
		GetReqVarLen(req), GetReqData(req));
}

static void
GrBellWrapper(void *r)
{
	GrBell();
}

static void
GrSetBackgroundPixmapWrapper(void *r)
{
	nxSetBackgroundPixmapReq *req = r;

	GrSetBackgroundPixmap(req->wid, req->pixmap, req->flags);
}

static void
GrDestroyCursorWrapper(void *r)
{
	nxDestroyCursorReq *req = r;

	GrDestroyCursor(req->cursorid);
}

static void
GrQueryTreeWrapper(void *r)
{
	nxQueryTreeReq *req = r;
	GR_WINDOW_ID	parentid;
	GR_WINDOW_ID *	children;
	GR_COUNT 	nchildren;

	GrQueryTree(req->windowid, &parentid, &children, &nchildren);

	GsWriteType(current_fd, GrNumQueryTree);
	GsWrite(current_fd, &parentid, sizeof(parentid));
	GsWrite(current_fd, &nchildren, sizeof(nchildren));
	if (nchildren) {
		GsWrite(current_fd, children, nchildren * sizeof(GR_WINDOW_ID));
		free(children);
	}
}

static void
GrCreateTimerWrapper(void *r)
{
    nxCreateTimerReq *req = r;
    GR_TIMER_ID  timerid;

    timerid = GrCreateTimer(req->wid, req->period);

    GsWriteType(current_fd, GrNumCreateTimer);
    GsWrite(current_fd, &timerid, sizeof (timerid));
}

typedef struct image_list {
 void *data;
 int id;
 int size;
 int offset;

 struct image_list *next;
} imagelist_t;

static imagelist_t *imageListHead = 0;
static imagelist_t *imageListTail = 0;
static int imageListId = 0;

static void freeImageBuffer(imagelist_t *buffer) {

 imagelist_t *prev = 0;
 imagelist_t *ptr = imageListHead;

 while(ptr) {
   if (ptr == buffer) {
     if (prev) 
	prev->next = buffer->next;
     else 
	imageListHead = buffer->next;
     
     if (!imageListHead) imageListTail = 0;
     
     free(buffer->data);
     free(buffer);
     return;
   }
   
   prev = ptr;
   ptr = ptr->next;
 }  
}

static void GrImageBufferAllocWrapper(void *r) {
 nxImageBufferAllocReq *req = r;
 
 /* Add a new buffer to the end of the list */

 if (!imageListTail) {
   imageListHead = imageListTail = (imagelist_t *) malloc(sizeof(imagelist_t));
 }
 else {
   imageListTail->next = (imagelist_t *) malloc(sizeof(imagelist_t));
   imageListTail = imageListTail->next;
 }

 imageListTail->id = ++imageListId;
 imageListTail->data = (void *) malloc(req->size);
 imageListTail->size = req->size;
 imageListTail->offset = 0;

 imageListTail->next = 0;

 GsWriteType(current_fd,GrNumImageBufferAlloc);
 GsWrite(current_fd, &imageListTail->id, sizeof(int));
}

static void GrImageBufferSendWrapper(void *r) {

 int csize = 0;

 imagelist_t *buffer = 0;
 nxImageBufferSendReq *req = r;

 for(buffer = imageListHead; buffer; buffer = buffer->next)
   if (buffer->id == req->buffer_id) break;

 if (!buffer) return;

 if (buffer->offset + req->size >= buffer->size) 
   csize = buffer->size - buffer->offset;
 else
   csize = req->size;

 if (!csize) return;

 memcpy((void *) (buffer->data + buffer->offset), 
	 GetReqData(req), csize);
 
 buffer->offset += csize;
}

void GrLoadImageFromBufferWrapper(void *r) {

 GR_IMAGE_ID		id;
 imagelist_t *buffer = 0;

 nxLoadImageFromBufferReq *req = r;
 
 for(buffer = imageListHead; buffer; buffer = buffer->next)
   if (buffer->id == req->buffer) break;
 
 if (!buffer) return;

 id = GrLoadImageFromBuffer(buffer->data, buffer->size, req->flags);

 GsWriteType(current_fd, GrNumLoadImageFromBuffer);
 GsWrite(current_fd, &id, sizeof(id));

 freeImageBuffer(buffer);
}

void GrDrawImageFromBufferWrapper(void *r) {

 imagelist_t *buffer = 0;

 nxDrawImageFromBufferReq *req = r;
 
 for(buffer = imageListHead; buffer; buffer = buffer->next)
   if (buffer->id == req->buffer) break;
 
 if (!buffer) return;

 GrDrawImageFromBuffer(req->drawid, req->gcid, req->x, req->y, req->width, 
			req->height, buffer->data, buffer->size, 
			req->flags);
 
 freeImageBuffer(buffer);
}

 

static void
GrDestroyTimerWrapper(void *r)
{
    nxDestroyTimerReq *req = r;
    
    GrDestroyTimer(req->timerid);
}

static void
GrSetPortraitModeWrapper(void *r)
{
    nxSetPortraitModeReq *req = r;
    
    GrSetPortraitMode(req->portraitmode);
}

/*
 * This function makes the Nano-X server set up a shared memory segment
 * that the client can use when feeding the Nano-X server with requests.
 * There is a corresponding GrShmCmdsFlush function that will make the
 * server execute the batched commands.
 */
#define SHMKEY_BASE 1000000
#define SHMKEY_MAX 256

void
GrReqShmCmdsWrapper(void *r)
{
#if HAVE_SHAREDMEM_SUPPORT
	nxReqShmCmdsReq *req = r;
	int 		key, shmid;
	char 		*tmp;

	if ( curclient->shm_cmds != 0 )
		goto bad;

	for ( key=SHMKEY_BASE; key < SHMKEY_BASE+SHMKEY_MAX; key++ ) {
		shmid = shmget(key,req->size,IPC_CREAT|IPC_EXCL|0666);
		if ( shmid == -1 ) {
			if ( errno != EEXIST )
				goto bad;
		} else {
			tmp = shmat(shmid,0,0);
			if ( tmp == (char *)-1 )
				goto bad;
			curclient->shm_cmds = tmp;
			curclient->shm_cmds_shmid = shmid;
			curclient->shm_cmds_size = req->size;
			goto finish;
		}
	}

 bad:
	key = 0;

 finish:
	DPRINTF("Shm: Request key granted=%d\n",key);
	GsWrite(current_fd, &key, sizeof(key));
#else
	/* return no shared memory support*/
	int key = 0;
	GsWrite(current_fd, &key, sizeof(key));
#endif /* HAVE_SHAREDMEM_SUPPORT*/
}

static void 
GrGetFontListWrapper(void *r)
{
	MWFONTLIST **list;
	int num;
	int i, ttlen, mwlen;

	GrGetFontList(&list, &num);

	GsWriteType(current_fd, GrNumGetFontList);

	/* the number of strings comming in */
	GsWrite(current_fd, &num, sizeof(int));

	if(num != -1) {
		for(i = 0; i < num; i++) {
			ttlen = strlen(list[i]->ttname) + 1;
			mwlen = strlen(list[i]->mwname) + 1;

			GsWrite(current_fd, &ttlen, sizeof(int));
			GsWrite(current_fd, list[i]->ttname, ttlen * sizeof(char));

			GsWrite(current_fd, &mwlen, sizeof(int));
			GsWrite(current_fd, list[i]->mwname, mwlen * sizeof(char));
		}
		
		GrFreeFontList(&list, num);
	}
}

void GrShmCmdsFlushWrapper(void *r);

/*
 * Handler functions, ordered by reqType
 */
struct GrFunction {
	void		(*func)(void *);
	GR_FUNC_NAME 	name;
} GrFunctions[] = {
	/*   0 */ {GrOpenWrapper, "GrOpen"},
	/*   1 */ {GrCloseWrapper, "GrClose"},
	/*   2 */ {GrGetScreenInfoWrapper, "GrGetScreenInfo"},
	/*   3 */ {GrNewWindowWrapper, "GrNewWindow"},
	/*   4 */ {GrNewInputWindowWrapper, "GrNewInputWindow"},
	/*   5 */ {GrDestroyWindowWrapper, "GrDestroyWindow"},
	/*   6 */ {GrNewGCWrapper, "GrNewGC"},
	/*   7 */ {GrCopyGCWrapper, "GrCopyGC"},
	/*   8 */ {GrGetGCInfoWrapper, "GrGetGCInfo"},
	/*   9 */ {GrDestroyGCWrapper, "GrDestroyGC"},
	/*  10 */ {GrMapWindowWrapper, "GrMapWindow"},
	/*  11 */ {GrUnmapWindowWrapper, "GrUnmapWindow"},
	/*  12 */ {GrRaiseWindowWrapper, "GrRaiseWindow"},
	/*  13 */ {GrLowerWindowWrapper, "GrLowerWindow"},
	/*  14 */ {GrMoveWindowWrapper, "GrMoveWindow"},
	/*  15 */ {GrResizeWindowWrapper, "GrResizeWindow"},
	/*  16 */ {GrGetWindowInfoWrapper, "GrGetWindowInfo"},
	/*  17 */ {GrGetFontInfoWrapper, "GrGetFontInfo"},
	/*  18 */ {GrSetFocusWrapper, "GrSetFocus"},
	/*  19 */ {GrSetWindowCursorWrapper, "GrSetWindowCursor"},
	/*  20 */ {GrClearAreaWrapper, "GrClearAreaWrapper"},
	/*  21 */ {GrSelectEventsWrapper, "GrSelectEvents"},
	/*  22 */ {GrGetNextEventWrapper, "GrGetNextEvent"},
	/*  23 */ {GrCheckNextEventWrapper, "GrCheckNextEvent"},
	/*  24 */ {GrPeekEventWrapper, "GrPeekEvent"},
	/*  25 */ {GrLineWrapper, "GrLine"},
	/*  26 */ {GrPointWrapper, "GrPoint"},
	/*  27 */ {GrRectWrapper, "GrRect"},
	/*  28 */ {GrFillRectWrapper, "GrFillRect"},
	/*  29 */ {GrPolyWrapper, "GrPoly"},
	/*  30 */ {GrFillPolyWrapper, "GrFillPoly"},
	/*  31 */ {GrEllipseWrapper, "GrEllipse"},
	/*  32 */ {GrFillEllipseWrapper, "GrFillEllipse"},
	/*  33 */ {GrSetGCForegroundWrapper, "GrSetGCForeground"},
	/*  34 */ {GrSetGCBackgroundWrapper, "GrSetGCBackGround"},
	/*  35 */ {GrSetGCUseBackgroundWrapper, "GrSetGCUseBackGround"},
	/*  36 */ {GrSetGCModeWrapper, "GrSetGCMode"},
	/*  37 */ {GrSetGCFontWrapper, "GrSetGCFont"},
	/*  38 */ {GrGetGCTextSizeWrapper, "GrGetGCTextSize"},
	/*  39 */ {GsReadAreaWrapper, "GsReadArea"},
	/*  40 */ {GrAreaWrapper, "GrArea"},
	/*  41 */ {GrBitmapWrapper, "GrBitmap"},
	/*  42 */ {GrTextWrapper, "GrText"},
	/*  43 */ {GrNewCursorWrapper, "GrNewCursor"},
	/*  44 */ {GrMoveCursorWrapper, "GrMoveCursor"},
	/*  45 */ {GrGetSystemPaletteWrapper, "GrGetSystemPalette"},
	/*  46 */ {GrFindColorWrapper, "GrFindColor"},
	/*  47 */ {GrReparentWindowWrapper, "GrReparentWindow"},
	/*  48 */ {GrDrawImageFromFileWrapper, "GrDrawImageFromFile"},
	/*  49 */ {GrLoadImageFromFileWrapper, "GrLoadImageFromFile"},
	/*  50 */ {GrNewPixmapWrapper, "GrNewPixmap"},
	/*  51 */ {GrCopyAreaWrapper, "GrCopyArea"},
	/*  52 */ {GrSetFontSizeWrapper, "GrSetFontSize"},
	/*  53 */ {GrCreateFontWrapper, "GrCreateFont"},
	/*  54 */ {GrDestroyFontWrapper, "GrDestroyFont"},
	/*  55 */ {GrReqShmCmdsWrapper, "GrReqShmCmds"},
	/*  56 */ {GrShmCmdsFlushWrapper, "GrShmCmdsFlush"},
	/*  57 */ {GrSetFontRotationWrapper, "GrSetFontRotation"},
	/*  58 */ {GrSetFontAttrWrapper, "GrSetFontAttr"},
	/*  59 */ {GrSetSystemPaletteWrapper, "GrSetSystemPalette"},
	/*  60 */ {GrInjectEventWrapper, "GrInjectEvent"},
	/*  61 */ {GrNewRegionWrapper, "GrNewRegion"},
	/*  62 */ {GrDestroyRegionWrapper, "GrDestroyRegion"},
	/*  63 */ {GrUnionRectWithRegionWrapper, "GrUnionRectWithRegion"},
	/*  64 */ {GrUnionRegionWrapper, "GrUnionRegion"},
	/*  65 */ {GrIntersectRegionWrapper, "GrIntersectRegion"},
	/*  66 */ {GrSetGCRegionWrapper, "GrSetGCRegion"},
	/*  67 */ {GrSubtractRegionWrapper, "GrSubtractRegion"},
	/*  68 */ {GrXorRegionWrapper, "GrXorRegion"},
	/*  69 */ {GrPointInRegionWrapper, "GrPointInRegion"},
	/*  70 */ {GrRectInRegionWrapper, "GrRectInRegion"},	
	/*  71 */ {GrEmptyRegionWrapper, "GrEmptyRegion"},	
	/*  72 */ {GrEqualRegionWrapper, "GrEqualRegion"},	
	/*  73 */ {GrOffsetRegionWrapper, "GrOffsetRegion"},	
	/*  74 */ {GrGetRegionBoxWrapper, "GrGetRegionBox"},	
	/*  75 */ {GrNewPolygonRegionWrapper, "GrNewPolygonRegion"},	
	/*  76 */ {GrArcWrapper, "GrArc"},
	/*  77 */ {GrArcAngleWrapper, "GrArcAngle"},
	/*  78 */ {GrSetWMPropertiesWrapper, "GrSetWMProperties"},
	/*  79 */ {GrGetWMPropertiesWrapper, "GrGetWMProperties"},
	/*  80 */ {GrCloseWindowWrapper, "GrCloseWindow"},
	/*  81 */ {GrKillWindowWrapper, "GrKillWindow"},
	/*  82 */ {GrDrawImageToFitWrapper, "GrDrawImageToFit"},
	/*  83 */ {GrFreeImageWrapper, "GrFreeImage"},
	/*  84 */ {GrGetImageInfoWrapper, "GrGetImageInfo"},
	/*  85 */ {GrDrawImageBitsWrapper, "GrDrawImageBits"},
 	/*  86 */ {GrPointsWrapper, "GrPoints"},
 	/*  87 */ {GrGetFocusWrapper, "GrGetFocus"},
 	/*  88 */ {GrGetSysColorWrapper, "GrGetSysColor"},
	/*  89 */ {GrSetScreenSaverTimeoutWrapper, "GrSetScreenSaverTimeout"},
	/*  90 */ {GrSetSelectionOwnerWrapper, "GrSetSelectionOwner"},
	/*  91 */ {GrGetSelectionOwnerWrapper, "GrGetSelectionOwner"},
	/*  92 */ {GrRequestClientDataWrapper, "GrRequestClientData"},
	/*  93 */ {GrSendClientDataWrapper, "GrSendClientData"},
	/*  94 */ {GrBellWrapper, "GrBell"},
	/*  95 */ {GrSetBackgroundPixmapWrapper, "GrSetBackgroundPixmap"},
	/*  96 */ {GrDestroyCursorWrapper, "GrDestroyCursor"},
	/*  97 */ {GrQueryTreeWrapper, "GrQueryTree"},
	/*  98 */ {GrCreateTimerWrapper, "GrCreateTimer"},
	/*  99 */ {GrDestroyTimerWrapper, "GrDestroyTimer"},
	/* 100 */ {GrSetPortraitModeWrapper, "GrSetPortraitMode"},
	/* 101 */ {GrImageBufferAllocWrapper, "GrImageBufferAlloc"},
	/* 102 */ {GrImageBufferSendWrapper, "GrImageBufferSend"},
	/* 103 */ {GrLoadImageFromBufferWrapper, "GrLoadImageFromBuffer"},
	/* 104 */ {GrDrawImageFromBufferWrapper, "GrDrawImageFromBuffer"},
	/* 105 */ {GrGetFontListWrapper, "GrGetFontList"},
	/* 106 */ {GrSetGCClipOriginWrapper, "GrSetGCClipOrigin"},
};

void
GrShmCmdsFlushWrapper(void *r)
{
	nxShmCmdsFlushReq *req = r;
	unsigned char 	reply;
#if HAVE_SHAREDMEM_SUPPORT
	nxReq 		*pr;
	int 		length;
	unsigned char 	*do_req, *do_req_last;

	if ( current_shm_cmds == 0 || current_shm_cmds_size < req->size ) {
		/* No or short shm present serverside, bug or mischief */
		EPRINTF("nano-X: Ill behaved client assumes shm ok\n");
		if ( req->reply ) {
			reply = 0;
			GsWrite(current_fd, &reply, 1);
		}
		return;
	}

	do_req = current_shm_cmds;
	do_req_last = current_shm_cmds + req->size;

	while ( do_req < do_req_last ) {
		pr = (nxReq *)do_req;
		length = GetReqAlignedLen(pr);
		if ( pr->reqType < GrTotalNumCalls ) {
			GrFunctions[pr->reqType].func(pr);
		} else {
			EPRINTF("nano-X: Error bad shm function!\n");
		}
		do_req += length;
	}

	if ( req->reply ) {
		reply = 1;
		GsWrite(current_fd, &reply, 1);
	}
#else
	/* no shared memory support*/
	if ( req->reply ) {
		reply = 0;
		GsWrite(current_fd, &reply, 1);
	}
#endif /* HAVE_SHAREDMEM_SUPPORT*/
}

/*
 * This function is used to bind to the named socket which is used to
 * accept connections from the clients.
 */
int 
GsOpenSocket(void)
{
#if ELKS
	struct sockaddr_na sckt;
#ifndef SUN_LEN
#define SUN_LEN(ptr)	(sizeof(sckt))
#endif
#elif __ECOS
	struct sockaddr_in sckt;
#ifndef SUN_LEN
#define SUN_LEN(ptr)	(sizeof(sckt))
#endif
#else
	struct sockaddr_un sckt;
#ifndef SUN_LEN
#define SUN_LEN(ptr)	((size_t) (((struct sockaddr_un *) 0)->sun_path) \
		      		+ strlen ((ptr)->sun_path))
#endif
#endif /* ELKS */

#if ELKS
	if((un_sock = socket(AF_NANO, SOCK_STREAM, 0)) == -1)
		return -1;

	sckt.sun_family = AF_NANO;
	sckt.sun_no = GR_NUMB_SOCKET;
#elif __ECOS
	// Create the socket
	if((un_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	    return -1;

	// Bind to any/all local IP addresses
	memset( &sckt, '\0', sizeof(sckt) );
	sckt.sin_family = AF_INET;
	sckt.sin_len = sizeof(sckt);
	sckt.sin_port = htons(6600);
	sckt.sin_addr.s_addr = INADDR_ANY;
#else
	if (access(GR_NAMED_SOCKET, F_OK) == 0) {
		/* FIXME: should try connecting to see if server is active */
		if(unlink(GR_NAMED_SOCKET))
			return -1;
	}

	/* Create the socket: */
	if((un_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
		return -1;

	/* Bind a name to the socket: */
	sckt.sun_family = AF_UNIX;
	strncpy(sckt.sun_path, GR_NAMED_SOCKET, sizeof(sckt.sun_path));
#endif /* ELKS */
	if(bind(un_sock, (struct sockaddr *) &sckt, SUN_LEN(&sckt)) < 0)
		return -1;

	/* Start listening on the socket: */
	if(listen(un_sock, 5) == -1)
		return -1;
	return 1;
}

void
GsCloseSocket(void)
{
	if(un_sock != -1)
		close(un_sock);
	un_sock = -1;
	unlink(GR_NAMED_SOCKET);
}

/*
 * This function is used to accept a connnection from a client.
 */
void
GsAcceptClient(void)
{
	int i;
#if ELKS
	struct sockaddr_na sckt;
#elif __ECOS
	struct sockaddr_in sckt;
#else
	struct sockaddr_un sckt;
#endif
	size_t size = sizeof(sckt);

	if((i = accept(un_sock, (struct sockaddr *) &sckt, &size)) == -1) {
#ifdef __ECOS
            // All ports should do this
		EPRINTF("nano-X: Error accept failed (%s)\n", strerror(errno));
#else
		EPRINTF("nano-X: Error accept failed (%d)\n", errno);
#endif
		return;
	}
	GsAcceptClientFd(i);
}

/*
 * This function accepts a client ID, and searches through the
 * linked list of clients, returning a pointer to the relevant
 * structure or NULL if not found.
 */
GR_CLIENT *
GsFindClient(int fd)
{
	GR_CLIENT *client;

	client = root_client;

	while(client) {
		if(client->id == fd)
			return(client);
		client = client->next;
	}
	
	return 0;
}

/*
 * Destroy windows and eventclient structures used by client.
 * Called by GsDropClient after a client has exited to clean
 * up resources.
 */
void
GsDestroyClientResources(GR_CLIENT * client)
{
	GR_WINDOW     * wp, *nwp;
	GR_PIXMAP     * pp, *npp;
	GR_GC 	      * gp, *ngp;
	GR_REGION     * rp, *nrp;
	GR_FONT       * fp, *nfp;
	GR_IMAGE      * ip, *nip;
	GR_CURSOR     *	cp, *ncp;
	GR_EVENT_CLIENT *ecp, *necp;
	GR_EVENT_CLIENT *pecp = NULL;
	GR_EVENT_LIST	*evp;

printf("Destroy client %d resources\n", client->id);
	/* search window list, destroy windows owned by client*/
	for(wp=listwp; wp; wp=nwp) {
		nwp = wp->next;
		/*
		 * Remove eventclient structures for this client
		 */
		ecp = wp->eventclients;
		while (ecp) {
			necp = ecp->next;
			if (ecp->client == client) {
printf( "  Destroy window %d eventclient mask %08lx\n", wp->id, ecp->eventmask);
				if (ecp == wp->eventclients)
					wp->eventclients = ecp->next;
				else
					pecp->next = ecp->next;
				free(ecp);
			} else
				pecp = ecp;
			ecp = necp;
		}
		if (wp->owner == client) {
printf("  Destroy window %d\n", wp->id);
			GrDestroyWindow(wp->id);
		}
	}

	/* search pixmap list, destroy pixmaps owned by client*/
	for(pp=listpp; pp; pp=npp) {
		npp = pp->next;
		if (pp->owner == client) {
printf("  Destroy pixmap %d\n", pp->id);
			GrDestroyWindow(pp->id);
		}
	}

	/* free gc's owned by client*/
	for(gp=listgcp; gp; gp=ngp) {
		ngp = gp->next;
		if (gp->owner == client) {
printf("  Destroy gc %d\n", gp->id);
			GrDestroyGC(gp->id);
		}
	}

	/* free fonts owned by client*/
	for(fp=listfontp; fp; fp=nfp) {
		nfp = fp->next;
		if (fp->owner == client) {
printf("  Destroy font %d\n", fp->id);
			GrDestroyFont(fp->id);
		}
	}

	/* free regions owned by client*/
	for(rp=listregionp; rp; rp=nrp) {
		nrp = rp->next;
		if (rp->owner == client) {
printf("  Destroy region %d\n", rp->id);
			GrDestroyRegion(rp->id);
		}
	}

	/* free images owned by client*/
	for(ip=listimagep; ip; ip=nip) {
		nip = ip->next;
		if (ip->owner == client) {
printf("  Destroy image %d\n", ip->id);
			GrFreeImage(ip->id);
		}
	}

	/* free cursors owned by client*/
	for(cp=listcursorp; cp; cp=ncp) {
		ncp = cp->next;
		if (cp->owner == client) {
printf("  Destroy cursor %d\n", cp->id);
			GrDestroyCursor(cp->id);
		}
	}

	/* Free events associated with client*/
	evp = client->eventhead;
	while (evp) {
printf("  Destroy event %d\n", evp->event.type);
		client->eventhead = evp->next;
		evp->next = eventfree;
		eventfree = evp;
		evp = client->eventhead;
	}
}

/*
 * Display window, pixmap, gc, font, region lists
 */
void
GsPrintResources(void)
{
	GR_WINDOW *wp;
	GR_PIXMAP *pp;
	GR_GC *gp;
	GR_REGION *rp;
	GR_FONT *fp;
	GR_IMAGE *ip;

	/* window list*/
	printf("Window list:\n");
	for(wp=listwp; wp; wp=wp->next) {
		printf("%d(%d),", wp->id, wp->owner? wp->owner->id: 0);
	}
	printf("\nPixmap list:\n");
	for(pp=listpp; pp; pp=pp->next) {
		printf("%d(%d),", pp->id, pp->owner->id);
	}
	printf("\nGC list:\n");
	for(gp=listgcp; gp; gp=gp->next) {
		printf("%d(%d),", gp->id, gp->owner->id);
	}
	printf("\nFont list:\n");
	for(fp=listfontp; fp; fp=fp->next) {
		printf("%d(%d),", fp->id, fp->owner->id);
	}
	printf("\nRegion list:\n");
	for(rp=listregionp; rp; rp=rp->next) {
		printf("%d(%d),", rp->id, rp->owner->id);
	}
	printf("\nImage list:\n");
	for(ip=listimagep; ip; ip=ip->next) {
		printf("%d(%d),", ip->id, ip->owner->id);
	}
	printf("\n");
}

/*
 * This is used to drop a client when it is detected that the connection to it
 * has been lost.
 */
void
GsDropClient(int fd)
{
	GR_CLIENT *client;

	if((client = GsFindClient(fd))) { /* If it exists */
		close(fd);	/* Close the socket */

		GsDestroyClientResources(client);
		if(client == root_client)
			root_client = client->next;
		/* Link the prev to the next */
		if(client->prev) client->prev->next = client->next;

		/* Link the next to the prev */
		if(client->next) client->next->prev = client->prev;

#if HAVE_SHAREDMEM_SUPPORT
		if ( client->shm_cmds != 0 ) {
			/* Free shared memory */
			shmctl(client->shm_cmds_shmid,IPC_RMID,0);
			shmdt(client->shm_cmds);
		}
#endif
GsPrintResources();
		free(client);	/* Free the structure */

		clipwp = NULL;	/* reset clip window*/
		--connectcount;
	} else EPRINTF("nano-X: trying to drop non-existent client %d.\n", fd);
}

/*
 * This is a wrapper to read() which handles error conditions, and
 * returns 0 for both error conditions and no data.
 */
int
#if ELKS
GsRead(int fd, char *buf, int c)
#else
GsRead(int fd, void *buf, int c)
#endif
{
	int e, n;

	n = 0;

	while(n < c) {
		e = read(fd, (buf + n), (c - n));
		if(e <= 0) {
			if (e == 0)
				EPRINTF("nano-X: client closed socket: %d\n", fd);
			else EPRINTF("nano-X: GsRead failed %d %d: %d\r\n",
			       e, n, errno);
			GsClose(fd);
			return -1;
		}
		n += e;
	}

	return 0;
}

/*
 * This is a wrapper to write().
 */
int GsWrite(int fd, void *buf, int c)
{
	int e, n;

	n = 0;

	while(n < c) {
		e = write(fd, ((char *) buf + n), (c - n));
		if(e <= 0) {
			GsClose(fd);
			return -1;
		}
		n += e;
	}

	return 0;
}

int GsWriteType(int fd, short type)
{
	return GsWrite(fd,&type,sizeof(type));
}

/*
 * This function is used to parse and dispatch requests from the clients.
 * Note that the maximum request size is allocated from the stack
 * in this function.
 */
void
GsHandleClient(int fd)
{
	nxReq *	req;
	long	len;
	char	buf[MAXREQUESTSZ];

	current_fd = fd;
#if HAVE_SHAREDMEM_SUPPORT
	current_shm_cmds = curclient->shm_cmds;
	current_shm_cmds_size = curclient->shm_cmds_size;
#endif
	/* read request header*/
	if(GsRead(fd, buf, sizeof(nxReq)))
		return;

	len = GetReqAlignedLen((nxReq *)&buf[0]);
	if(len > sizeof(nxReq)) {
		if(len > MAXREQUESTSZ) {
			EPRINTF("nano-X: GsHandleClient request too large: %ld > %d\n",
				len, MAXREQUESTSZ);
			exit(1);
		}
		/* read additional request data*/
		if(GsRead(fd, &buf[sizeof(nxReq)], len-sizeof(nxReq)))
			return;
	}
	req = (nxReq *)&buf[0];

	if(req->reqType < GrTotalNumCalls) {
		curfunc = GrFunctions[req->reqType].name;
		/*DPRINTF("HandleClient %s\n", curfunc);*/
		GrFunctions[req->reqType].func(req);
	} else {
		EPRINTF("nano-X: GsHandleClient bad function\n");
	}
}
