Logo Search packages:      
Sourcecode: ecos version File versions  Download package

client.c

/*
 * Copyright (c) 1999, 2000 Greg Haerr <greg@censoft.com>
 * Copyright (c) 1999, 2000 Alex Holden <alex@linuxhacker.org>
 * Copyright (c) 1991 David I. Bell
 * Copyright (c) 2000 Vidar Hokstad
 * Copyright (c) 2000 Morten Rolland <mortenro@screenmedia.no>
 *
 * Permission is granted to use, distribute, or modify this source,
 * provided that this copyright notice remains intact.
 *
 * Client routines to do graphics with windows and graphics contexts.
 *
 * Rewritten heavily for speed by Greg Haerr
 *
 * Whenever you add a new API entry point, please comment it in the same way
 * as the rest of the functions in this file. Also add your functions to the
 * appropriate section(s) in doc/nano-X/nano-X-sections.txt and regenerate the
 * documentation by running make in the doc/nano-X/ directory. If you do not
 * have the necessary tools (gtk-doc and the docbook-tools) to rebuild the
 * documentation, just skip that step and we will do it for you.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stddef.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
#if HAVE_SHAREDMEM_SUPPORT
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#endif
#include <sys/time.h>
#include <sys/socket.h>
#if ELKS
#include <linuxmt/na.h>
#include <linuxmt/time.h>
#elif __ECOS
#include <netinet/in.h>
#include <sys/select.h>
#include <cyg/kernel/kapi.h>
#define _NO_SVR_MAPPING
#define getpid()  ((int)cyg_thread_self())
#else
#include <sys/un.h>
#if hpux
#include <sys/time.h>
#else
#ifdef __GLIBC__
#include <sys/select.h>
#endif
#endif
#endif
#include "nano-X.h"
#include "serv.h"
#include "nxproto.h"

#define GR_CLOSE_FIX    1     /* dirty hack attempts to fix GrClose hang bug*/

#define SHM_BLOCK_SIZE  4096

#ifndef __ECOS
/* exported global data */
int      nxSocket = -1; /* The network socket descriptor */
#if HAVE_SHAREDMEM_SUPPORT
char *         nxSharedMem = 0;     /* Address of shared memory segment*/
static int nxSharedMemSize;   /* Size in bytes of shared mem segment*/
#endif

static int regfdmax = -1;     /* GrRegisterInput globals*/
static fd_set regfdset;

/* readable error strings*/
char *nxErrorStrings[] = {
      GR_ERROR_STRINGS
};

static EVENT_LIST *     evlist;

/*
 * The following is the user defined function for handling errors.
 * If this is not set, then the default action is to close the connection
 * to the server, describe the error, and then exit.  This error function
 * will only be called when the client asks for events.
 */
static GR_FNCALLBACKEVENT ErrorFunc = GrDefaultErrorHandler;

#else /* __ECOS*/
/*
 * eCos uses a thread data pointer to store all statics in...
 */
int ecos_nanox_client_data_index = CYGNUM_KERNEL_THREADS_DATA_MAX;

/* readable error strings*/
char *nxErrorStrings[] = {
      GR_ERROR_STRINGS
};
#endif

/*
 * Queue an event in FIFO for later retrieval.
 */
static void
QueueEvent(GR_EVENT *ep)
{
      EVENT_LIST *      elp;
      EVENT_LIST *      prevelp;
        ACCESS_PER_THREAD_DATA()

      elp = malloc(sizeof(EVENT_LIST));
      if (elp) {
            elp->event = *ep;
            elp->next = NULL;

            /* add as last entry on list*/
            if (!evlist) {
                  evlist = elp;
                  return;
            }
            prevelp = evlist;
            while (prevelp->next)
                  prevelp = prevelp->next;
            prevelp->next = elp;
      }
}

/*
 * Retrieve first event in FIFO event queue.
 */
static void
GetNextQueuedEvent(GR_EVENT *ep)
{
        ACCESS_PER_THREAD_DATA()
      *ep = evlist->event;
      evlist = evlist->next;
}

/*
 * Read n bytes of data from the server into block *b.  Make sure the data
 * you are about to read are actually of the correct type - e.g. make a
 * check for the first block of data you read as a response to a command
 * with the Typed version of this function. Returns 0 on success or -1 on
 * failure.
 */
static int GrReadBlock(void *b, int n)
{
      int i = 0;
      char *v;
        ACCESS_PER_THREAD_DATA()

      v = (char *) b;

      nxFlushReq(0L,0);
      while(v < ((char *) b + n)) {
            i = read(nxSocket, v, ((char *) b + n - v));
            if ( i <= 0 ) {
                  if ( i == 0 ) {
                        /* We should maybe produce an event here,
                         * if possible.
                         */
                        EPRINTF("nxclient: lost connection to Nano-X "
                              "server\n");
                        exit(1);
                  }
                  if ( errno == EINTR || errno == EAGAIN )
                        continue;

                  EPRINTF("nxclient: bad readblock %d\n", i);
                  return -1;
            }
            v += i;
      }

      return 0;
}

/*
 * Read a byte of data from the server.
 */
static int GrReadByte()
{
      unsigned char c;

      if(GrReadBlock(&c, 1) == -1)
            return -1;
      else return (int) c;
}

/*
 * Check if this is a CLIENT_DATA event, in which case we need to read the
 * data for the event into a buffer and set the event data pointer to the
 * address of it (or NULL if the malloc() fails). We also don't try to read
 * any data if datalen is 0.
 */
static void GrCheckForClientData(GR_EVENT *evp)
{
      GR_EVENT_CLIENT_DATA *event;

      if(evp->type == GR_EVENT_TYPE_CLIENT_DATA) {
            event = (GR_EVENT_CLIENT_DATA *)evp;
            if(!event->datalen) {
                  event->data = NULL;
                  return;
            }
            if(!(event->data = malloc(event->datalen))) return;
            GrReadBlock(event->data, event->datalen);
      }
}

/*
 * Check if the data we are about to read is of the correct type. This
 * must be done in order to avoid reading an event as part of the response
 * from the server to a command that requires a reply.
 */
static int GrCheckBlockType(short packettype)
{
      short       b;
      GR_EVENT    event;

      while (GrReadBlock(&b,sizeof(b)) != -1) {
            if (b == packettype)
                  return b;

            if (b == GrNumGetNextEvent) {
                  /*EPRINTF("nxclient %d: Storing event (expected %d)\n",
                        getpid(), packettype);*/

#if 0
                  /* We only need to handle one event, since the next
                   * event won't arrive until the next GrPrepareSelect()
                   * has been called, and by then we have already
                   * handled this event in GrServiceSelect(). If
                   * GrPrepareSelect() is never called, then we should
                   * never get here either, so that is cool too.
                   */
                  GrReadBlock(&storedevent_data,
                            sizeof(storedevent_data));
                  GrCheckForClientData(&storedevent_data);
                  storedevent = 1;
#endif
                  /* read event and queue it for later processing*/
                  GrReadBlock(&event, sizeof(event));
                  QueueEvent(&event);
            } else {
                  EPRINTF("nxclient %d: Wrong packet type %d "
                        "(expected %d)\n", getpid(),b, packettype);
            }
      }
      EPRINTF("nxclient %d: Corrupted packet\n", getpid());
      return -1;
}

/*
 * Actually read a response from the server, much like the GrReadBlock but
 * make sure the response is of the right kind, e.g. store the event that
 * may have sneaked into the stream.
 */
static int GrTypedReadBlock(void *b, int n, int type)
{
      int r;
   
      r = GrCheckBlockType(type);
      if (r != type)
            return -1;
      return GrReadBlock(b,n);
}

/*
 * Check if the passed event is an error event, and call the error handler if
 * there is one. After calling the handler (if it returns), the event type is
 * set to a non-event so that we don't return an error event through the
 * GetEvent() mechanism. This solution is simpler than creating a client-side
 * event queue.
 */
static void
CheckErrorEvent(GR_EVENT *ep)
{
        ACCESS_PER_THREAD_DATA()

      if (ep->type == GR_EVENT_TYPE_ERROR) {
            if (ErrorFunc) {
                  /* call error handler*/
                  ErrorFunc(ep);

                  /* then convert to null event*/
                  ep->type = GR_EVENT_TYPE_NONE;
            }
      }
}

/**
 * GrFlush:
 *
 * Flush the message buffer of any messages it may contain.
 */
void 
GrFlush(void)
{
      nxFlushReq(0L,1);
}

/**
 * GrOpen:
 * @Returns: the fd of the connection to the server or -1 on failure
 *
 * Open a connection to the graphics server.
 */
int 
GrOpen(void)
{
      size_t            size;
      nxOpenReq   req;
      int         tries;
      int         ret = 0;
#if ELKS
      struct sockaddr_na name;
#define ADDR_FAM AF_NANO
#elif defined(__ECOS)
      struct sockaddr_in name;
#define ADDR_FAM AF_INET
#else
      struct sockaddr_un name;
#define ADDR_FAM AF_UNIX
#endif
      ACCESS_PER_THREAD_DATA()
      
      if (nxSocket == -1) {
            if((nxSocket = socket(ADDR_FAM, SOCK_STREAM, 0)) == -1) {
                  nxSocket = -1;
                  return -1;
            }
        } else {
            return nxSocket;
        }

#if ELKS
      name.sun_family = AF_NANO;
      name.sun_no = GR_NUMB_SOCKET;
      size = sizeof(struct sockaddr_na);
#elif defined(__ECOS)
      name.sin_family = AF_INET;
      name.sin_port = htons(6600);                    /* Nano-X server port*/
      name.sin_addr.s_addr = inet_addr("127.0.0.1");  /* Loopback address*/
      size = sizeof(struct sockaddr_in);
#else
      name.sun_family = AF_UNIX;
      strcpy(name.sun_path, GR_NAMED_SOCKET);
      size = (offsetof(struct sockaddr_un, sun_path) +
            strlen(name.sun_path) + 1);
#endif

      /*
       * Try to open the connection for up to a second,
       * waiting 1/10 second between attempts.
       */
      for (tries=1; tries<=10; ++tries) {
            struct timespec req;

            ret = connect(nxSocket, (struct sockaddr *) &name, size);
            if (ret >= 0)
                  break;
            req.tv_sec = 0;
            req.tv_nsec = 100000000L;
            nanosleep(&req, NULL);
            EPRINTF("nxclient: retry connect attempt %d\n", tries);
      }
      if (ret == -1) {
            close(nxSocket);
            nxSocket = -1;
            return -1;
      }

      /*
       * By Performing the 'GrOpen' without allocating a buffer, just
       * shuffeling the struct over the wire, we can postpone the
       * allocation of the client size command buffer, which will never be
       * allocated if the first command after GrOpen() is
       * GrReqShmCmds() which allocates a replacement shared memory
       * segment.
       * So: Calling GrReqShmCmds() right after GrOpen will prevent the
       * traditional command queue buffer from being allocated from
       * the process heap - and only the shared memory segment is
       * allocated.
       */
      req.reqType = GrNumOpen;
      req.hilength = 0;
      req.length = sizeof(req);
      /* associate the process ID with the client*/
      req.pid = getpid();

      nxWriteSocket((char *)&req,sizeof(req));
      return nxSocket;
}

#if GR_CLOSE_FIX
static void
mySignalhandler(int sig)
{
      if (sig == SIGALRM) {
            printf("Oops! nxFlushReq() timed out, cowardly chickening "
                                                "out!\n");
            exit(127);
      }
}
#endif

/**
 * GrClose:
 *
 * Close the graphics device, flushing any waiting messages.
 */
/* Vladimir Cotfas: hang in GrFlush() --> nxFlushReq(0L,1); */
void 
GrClose(void)
{
      ACCESS_PER_THREAD_DATA()
#if GR_CLOSE_FIX
      /* allow 1 second to flush*/
      void * oldSignalHandler = signal(SIGALRM, mySignalhandler);
      alarm(1);
#endif
      AllocReq(Close);
      GrFlush();
#if GR_CLOSE_FIX
      alarm(0);
      signal(SIGALRM, oldSignalHandler);
#endif
      close(nxSocket);
      nxSocket = -1;
}

/**
 * GrDefaultErrorHandler:
 * @ep: the error event structure
 * 
 * The default error handler which is called when the server reports an error
 * event and the client hasn't set up a handler of it's own.
 *
 * Generates a human readable error message on stderr describing what error
 * occurred and what function it occured in, then exits.
 */
void 
GrDefaultErrorHandler(GR_EVENT *ep)
{
      if (ep->type == GR_EVENT_TYPE_ERROR) {
            EPRINTF("nxclient %d: Error (%s) ", getpid(), ep->error.name);
            EPRINTF(nxErrorStrings[ep->error.code], ep->error.id);
            GrClose();
            exit(1);
      }
}

/**
 * GrSetErrorHandler:
 * @fncb: the function to call to handle error events
 * @Returns: the address of the previous error handler
 *
 * Sets an error handling routine that will be called on any errors from
 * the server (assuming the client has asked to receive them). If zero is
 * used as the argument, errors will be returned as regular events instead.
 */
GR_FNCALLBACKEVENT
GrSetErrorHandler(GR_FNCALLBACKEVENT fncb)
{
      ACCESS_PER_THREAD_DATA()
      GR_FNCALLBACKEVENT orig = ErrorFunc;

      ErrorFunc = fncb;
      return orig;
}

/**
 * GrDelay:
 * @msecs: number of milliseconds to delay
 *
 * This function suspends execution of the program for the specified
 * number of milliseconds.
 */
void
GrDelay(GR_TIMEOUT msecs)
{
      struct timeval timeval;

      timeval.tv_sec = msecs / 1000;
      timeval.tv_usec = msecs % 1000;
      select(0, NULL, NULL, NULL, &timeval);
}

/**
 * GrGetScreenInfo:
 * @sip: pointer to a GR_SCREEN_INFO structure
 *
 * Fills in the specified GR_SCREEN_INFO structure.
 */
void 
GrGetScreenInfo(GR_SCREEN_INFO *sip)
{
      AllocReq(GetScreenInfo);
      GrTypedReadBlock(sip, sizeof(GR_SCREEN_INFO),GrNumGetScreenInfo);
}

/**
 * GrGetSysColor:
 * @index: an index into the server's colour look up table
 * @Returns: the colour found at the specified index
 *
 * Returns the colour at the specified index into the server's colour look
 * up table. The colours in the table are those with names like
 * "GR_COLOR_DESKTOP", "GR_COLOR_ACTIVECAPTION", "GR_COLOR_APPWINDOW", etc.
 * as listed in nano-X.h
 */
GR_COLOR
GrGetSysColor(int index)
{
      nxGetSysColorReq *req;
      GR_COLOR color;

      req = AllocReq(GetSysColor);
      req->index = index;
      if(GrTypedReadBlock(&color, sizeof(color),GrNumGetSysColor) == -1)
            return 0;
      return color;
}

/**
 * GrGetFontInfo:
 * @fontno: the font ID number
 * @fip: pointer to a GR_FONT_INFO structure
 *
 * Fills in the specified GR_FONT_INFO structure with information regarding
 * the specified font.
 */
void 
GrGetFontInfo(GR_FONT_ID fontno, GR_FONT_INFO *fip)
{
      nxGetFontInfoReq *req;

      req = AllocReq(GetFontInfo);
      req->fontid = fontno;
      GrTypedReadBlock(fip, sizeof(GR_FONT_INFO),GrNumGetFontInfo);
}

/**
 * GrGetGCInfo:
 * @gc: a graphics context
 * @gcip: pointer to a GR_GC_INFO structure
 *
 * Fills in the specified GR_GC_INFO structure with information regarding the
 * specified graphics context.
 */
void GrGetGCInfo(GR_GC_ID gc, GR_GC_INFO *gcip)
{
      nxGetGCInfoReq *req;

      req = AllocReq(GetGCInfo);
      req->gcid = gc;
      GrTypedReadBlock(gcip, sizeof(GR_GC_INFO),GrNumGetGCInfo);
}

/**
 * GrGetGCTextSize:
 * @gc: the graphics context
 * @str: pointer to a text string
 * @count: the length of the string
 * @flags: text rendering flags (GR_TF*)
 * @retwidth: pointer to the variable the width will be returned in
 * @retheight: pointer to the variable the height will be returned in
 * @retbase: pointer to the variable the baseline height will be returned in
 *
 * Calculates the dimensions of the specified text string using the current font
 * and flags in the specified graphics context. The count argument can be -1
 * if the string is null terminated.
 */
void GrGetGCTextSize(GR_GC_ID gc, void *str, int count, int flags,
      GR_SIZE *retwidth, GR_SIZE *retheight, GR_SIZE *retbase)
{
      nxGetGCTextSizeReq *req;
      int size;

      if(count == -1 && (flags&MWTF_PACKMASK) == MWTF_ASCII)
            count = strlen((char *)str);

      size = nxCalcStringBytes(str, count, flags);

      req = AllocReqExtra(GetGCTextSize, size);
      req->gcid = gc;
      req->flags = flags;
      memcpy(GetReqData(req), str, size);
      GrTypedReadBlock(retwidth, sizeof(*retwidth),GrNumGetGCTextSize);
      GrReadBlock(retheight, sizeof(*retheight));
      GrReadBlock(retbase, sizeof(*retbase));
}

/**
 * GrRegisterInput:
 * @fd: the file descriptor to monitor
 *
 * Register an extra file descriptor to monitor in the main select() call.
 * An event will be returned when the fd has data waiting to be read if that
 * event has been selected for.
 */
void 
GrRegisterInput(int fd)
{
      ACCESS_PER_THREAD_DATA()

      if (fd < 0)
            return;
      FD_SET(fd, &regfdset);
      if (fd > regfdmax) regfdmax = fd + 1;
}

/**
 * GrUnregisterInput:
 * @fd: the file descriptor to stop monitoring
 *
 * Stop monitoring a file descriptor (previously registered with
 * GrRegisterInput()) in the main select() call.
 */
void
GrUnregisterInput(int fd)
{
      int i, max;
      ACCESS_PER_THREAD_DATA()

      /* unregister all inputs if fd is -1 */
      if (fd == -1) {
            FD_ZERO(&regfdset);
            regfdmax = -1;
            return;
      }

      FD_CLR(fd, &regfdset);
      /* recalculate the max file descriptor */
      for (i = 0, max = regfdmax, regfdmax = -1; i < max; i++)
            if (FD_ISSET(i, &regfdset))
                  regfdmax = i + 1;
}

/**
 * GrPrepareSelect:
 * @maxfd: pointer to a variable which the highest in use fd will be written to
 * @rfdset: pointer to the file descriptor set structure to use
 *
 * Prepare for a GrServiceSelect function by asking the server to send the next
 * event but not waiting around for it to arrive and initialising the
 * specified fd_set structure with the client/server socket descriptor and any
 * previously registered external file descriptors. Also compares the current
 * contents of maxfd, the client/server socket descriptor, and the previously
 * registered external file descriptors, and returns the highest of them in
 * maxfd.
 */
void
GrPrepareSelect(int *maxfd,void *rfdset)
{
      fd_set *rfds = rfdset;
      int fd;

      ACCESS_PER_THREAD_DATA()

      AllocReq(GetNextEvent);
      GrFlush();

      FD_SET(nxSocket, rfds);
      if(nxSocket > *maxfd)
            *maxfd = nxSocket;

      /* handle registered input file descriptors*/
      for (fd = 0; fd < regfdmax; fd++) {
            if (FD_ISSET(fd, &regfdset)) {
                  FD_SET(fd, rfds);
                  if (fd > *maxfd) *maxfd = fd;
            }
      }
}

/**
 * GrServiceSelect:
 * @rfdset: pointer to the file descriptor set to monitor
 * @fncb: pointer to the function to call when an event needs handling
 *
 * Used by GrMainLoop() to call the specified callback function when an
 * event arrives or there is data waiting on an external fd specified by
 * GrRegisterInput().
 */
void
GrServiceSelect(void *rfdset, GR_FNCALLBACKEVENT fncb)
{
      fd_set *    rfds = rfdset;
      int         fd;
      GR_EVENT    ev;

      ACCESS_PER_THREAD_DATA()

        /* Clean out any event that might have arrived while waiting
       * for other data, for instance by doing Nano-X requests
       * between GrPrepareSelect() and GrServiceSelect(), or when
       * an event is generated in Nano-X at the same time as the
       * client wakes up for some reason and calls Nano-X functions.
       */
      if (evlist) {
            /*DPRINTF("nxclient: Handling queued event\n");*/
            GetNextQueuedEvent(&ev);
            CheckErrorEvent(&ev);
            fncb(&ev);
      }
      else {
            if(FD_ISSET(nxSocket, rfds)) {
                  GrTypedReadBlock(&ev, sizeof(ev),GrNumGetNextEvent);
                  GrCheckForClientData(&ev);
                  CheckErrorEvent(&ev);
                  fncb(&ev);
            }
      }

      /* check for input on registered file descriptors */
      for (fd = 0; fd < regfdmax; fd++) {
            if (FD_ISSET(fd, &regfdset) && FD_ISSET(fd, rfds)) {
                  ev.type = GR_EVENT_TYPE_FDINPUT;
                  ev.fdinput.fd = fd;
                  fncb(&ev);
            }
      }
}

/**
 * GrMainLoop:
 * @fncb:
 *
 * A convenience function which calls the specified callback function whenever
 * an event arrives or there is data to be read on a file descriptor previously
 * specified by GrRegisterInput(). Currently never returns.
 */
void
GrMainLoop(GR_FNCALLBACKEVENT fncb)
{
      fd_set      rfds;
      int   setsize = 0;

      for(;;) {
            FD_ZERO(&rfds);
            GrPrepareSelect(&setsize, &rfds);
            if(select(setsize+1, &rfds, NULL, NULL, NULL) > 0)
                  GrServiceSelect(&rfds, fncb);
      }
}

/**
 * GrGetNextEvent:
 * @ep: pointer to the GR_EVENT structure to return the event in
 *
 * Gets the next event from the event queue and places it in the specified
 * GR_EVENT structure. If the queue is currently empty, we sleep until the
 * next event arrives from the server or input is read on a file descriptor
 * previously specified by GrRegisterInput().
 */
void 
GrGetNextEvent(GR_EVENT *ep)
{
      GrGetNextEventTimeout(ep, 0L);
}

/**
 * GrGetNextEventTimeout:
 * @ep: pointer to the GR_EVENT structure to return the event in
 * @timeout: the number of milliseconds to wait before timing out
 *
 * Gets the next event from the event queue and places it in the specified
 * GR_EVENT structure. If the queue is currently empty, we sleep until the
 * next event arrives from the server, input is read on a file descriptor
 * previously specified by GrRegisterInput(), or a timeout occurs. Note
 * that a value of 0 for the timeout parameter doesn't mean "timeout after 0
 * milliseconds" but is in fact a magic number meaning "never time out".
 */
void
GrGetNextEventTimeout(GR_EVENT *ep, GR_TIMEOUT timeout)
{
      fd_set            rfds;
      int         setsize = 0;
      int         e;
      struct timeval    to;
      ACCESS_PER_THREAD_DATA()

      if (evlist) {
            /*DPRINTF("nxclient %d: Returning queued event\n",getpid());*/
            GetNextQueuedEvent(ep);
            CheckErrorEvent(ep);
            return;
      }

      FD_ZERO(&rfds);
      /*
       * This will cause a GrGetNextEvent to be sent down the wire.
       * If we timeout before the server responds, and then
       * call this procedure again, and the server has more than
       * one event waiting for this process, then more than one
       * event will be written on the socket by the server.  At
       * that point, a single stored event won't work, and the
       * client needs an event queue.
       */
      GrPrepareSelect(&setsize, &rfds);
      if (timeout) {
            to.tv_sec = timeout / 1000;
            to.tv_usec = (timeout % 1000) * 1000;
      }

      if((e = select(setsize+1, &rfds, NULL, NULL, timeout ? &to : NULL))>0) {
            int fd;

            if(FD_ISSET(nxSocket, &rfds)) {
                  /*
                   * This will never be GR_EVENT_NONE with the current
                   * implementation.
                   */
                    GrTypedReadBlock(ep, sizeof(*ep),GrNumGetNextEvent);
                  GrCheckForClientData(ep);
                  CheckErrorEvent(ep);
                  return;
            }

            /* check for input on registered file descriptors */
            for (fd = 0; fd < regfdmax; fd++) {
                  if (FD_ISSET(fd, &regfdset) && FD_ISSET(fd, &rfds)) {
                        ep->type = GR_EVENT_TYPE_FDINPUT;
                        ep->fdinput.fd = fd;
                        break;
                  }
            }
      }
      else if (e == 0) {
            /*
             * Timeout has occured. We currently return a timeout event
             * regardless of whether the client has selected for it.
             */
            ep->type = GR_EVENT_TYPE_TIMEOUT;
      } else {
            if(errno == EINTR) {
                  ep->type = GR_EVENT_TYPE_NONE;
            } else {
                  EPRINTF("nxclient: select failed\n");
                  GrClose();
                  exit(1);
            }
      }
}

/**
 * GrCheckNextEvent:
 * @ep: pointer to the GR_EVENT structure to return the event in
 *
 * Gets the next event from the event queue if there is one, or returns
 * immediately with an event type of GR_EVENT_TYPE_NONE if it is empty.
 */
void 
GrCheckNextEvent(GR_EVENT *ep)
{
      ACCESS_PER_THREAD_DATA()
      if (evlist) {
            /*DPRINTF("nxclient %d: Returning queued event\n",getpid());*/
            GetNextQueuedEvent(ep);
            CheckErrorEvent(ep);
            return;
      }

      AllocReq(CheckNextEvent);
      GrTypedReadBlock(ep, sizeof(*ep),GrNumGetNextEvent);
      GrCheckForClientData(ep);
      CheckErrorEvent(ep);
}

/**
 * GrPeekEvent:
 * @ep: pointer to the GR_EVENT structure to return the event in
 * @Returns: 1 if an event was returned, or 0 if the queue was empty
 *
 * Fills in the specified event structure with a copy of the next event on the
 * queue, without actually removing it from the queue. An event type of
 * GR_EVENT_TYPE_NONE is given if the queue is empty.
 */
int 
GrPeekEvent(GR_EVENT *ep)
{
      int ret;
      ACCESS_PER_THREAD_DATA()

      if (evlist) {
            *ep = evlist->event;
            CheckErrorEvent(ep);
            return 1;
      }

      AllocReq(PeekEvent);
      GrTypedReadBlock(ep, sizeof(*ep),GrNumPeekEvent);
      GrCheckForClientData(ep);
      ret = GrReadByte();
      CheckErrorEvent(ep);
      return ret;
}

/**
 * GrPeekWaitEvent:
 * @ep: pointer to the GR_EVENT structure to return the event in
 *
 * Wait until an event is available for a client, and then peek at it.
 */
void
GrPeekWaitEvent(GR_EVENT *ep)
{
      EVENT_LIST *      elp;
      ACCESS_PER_THREAD_DATA()

      if (evlist) {
            *ep = evlist->event;
            CheckErrorEvent(ep);
            return;
      }

      /* no events, wait for next event*/
      GrGetNextEvent(ep);

      /* add event back on head of list*/
      elp = malloc(sizeof(EVENT_LIST));
      if (elp) {
            elp->event = *ep;
            elp->next = evlist;
      }

      /* peek at it*/
      GrPeekEvent(ep);
}

/**
 * GrSelectEvents:
 * @wid: the ID of the window to set the event mask of
 * @eventmask: a bit field specifying the desired event mask
 *
 * Select the event types which should be returned for the specified window.
 */
void 
GrSelectEvents(GR_WINDOW_ID wid, GR_EVENT_MASK eventmask)
{
      
      nxSelectEventsReq *req;

      req = AllocReq(SelectEvents);
      req->windowid = wid;
      req->eventmask = eventmask;
}

/**
 * GrNewWindow:
 * @parent: the ID of the parent window
 * @x: the X coordinate of the new window relative to the parent window
 * @y: the Y coordinate of the new window relative to the parent window
 * @width: the width of the new window
 * @height: the height of the new window
 * @bordersize: the width of the window border
 * @background: the colour of the window background
 * @bordercolor: the colour of the window border
 * @Returns: the ID of the newly created window
 *
 * Create a new window with the specified parent and window attributes.
 */
GR_WINDOW_ID
GrNewWindow(GR_WINDOW_ID parent, GR_COORD x, GR_COORD y, GR_SIZE width,
      GR_SIZE height, GR_SIZE bordersize, GR_COLOR background,
      GR_COLOR bordercolor)
{
      nxNewWindowReq *req;
      GR_WINDOW_ID      wid;

      req = AllocReq(NewWindow);
      req->parentid = parent;
      req->x = x;
      req->y = y;
      req->width = width;
      req->height = height;
      req->backgroundcolor = background;
      req->bordercolor = bordercolor;
      req->bordersize = bordersize;
      if(GrTypedReadBlock(&wid, sizeof(wid),GrNumNewWindow) == -1)
            return 0;
      return wid;
}
   
   
/**
 * GrNewPixmap:
 * @width: the width of the pixmap
 * @height: the height of the pixmap
 * @addr: currently unused in client/server mode
 * @Returns: the ID of the newly created pixmap
 *
 * Create a new server side pixmap (an offscreen drawing area which can be
 * copied into a window using a GrCopyArea call) of the specified width and
 * height.
 */
/* FIXME: Add support for shared memory... */
GR_WINDOW_ID
GrNewPixmap(GR_SIZE width, GR_SIZE height, void *addr)
{
      nxNewPixmapReq *req;
      GR_WINDOW_ID      wid;

      req = AllocReq(NewPixmap);
      req->width = width;
      req->height = height;
      if(GrTypedReadBlock(&wid, sizeof(wid), GrNumNewPixmap) == -1)
            return 0;
      return wid;
}

/**
 * GrNewInputWindow:
 * @parent: the ID of the window to use as the parent of the new window
 * @x: the X coordinate of the new window relative to the parent window
 * @y: the Y coordinate of the new window relative to the parent window
 * @width: the width of the new window
 * @height: the height of the new window
 * @Returns: the ID of the newly created window
 *
 * Create a new input-only window with the specified dimensions which is a
 * child of the specified parent window.
 */
GR_WINDOW_ID
GrNewInputWindow(GR_WINDOW_ID parent, GR_COORD x, GR_COORD y, GR_SIZE width,
      GR_SIZE height)
{
      nxNewInputWindowReq *req;
      GR_WINDOW_ID           wid;

      req = AllocReq(NewInputWindow);
      req->parentid = parent;
      req->x = x;
      req->y = y;
      req->width = width;
      req->height = height;
      if(GrTypedReadBlock(&wid, sizeof(wid), GrNumNewInputWindow) == -1)
            return 0;
      return wid;
}

/**
 * GrDestroyWindow:
 * @wid: the ID of the window to destroy
 *
 * Recursively unmaps and frees the data structures associated with the
 * specified window and all of its children.
 */
void 
GrDestroyWindow(GR_WINDOW_ID wid)
{
      nxDestroyWindowReq *req;

      req = AllocReq(DestroyWindow);
      req->windowid = wid;
}

/**
 * GrGetWindowInfo:
 * @wid: the ID of the window to retrieve information about
 * @infoptr: pointer to a GR_WINDOW_INFO structure to return the information in
 *
 * Fills in a GR_WINDOW_INFO structure with information regarding the window
 * with the specified window ID.
 */
void 
GrGetWindowInfo(GR_WINDOW_ID wid, GR_WINDOW_INFO *infoptr)
{
      nxGetWindowInfoReq *req;

      req = AllocReq(GetWindowInfo);
      req->windowid = wid;
      GrTypedReadBlock(infoptr, sizeof(GR_WINDOW_INFO), GrNumGetWindowInfo);
}

/**
 * GrNewGC:
 * @Returns: the ID of the newly created graphics context or 0 on error
 *
 * Creates a new graphics context structure and returns the ID used to refer
 * to it. The structure is initialised with a set of default parameters.
 */
GR_GC_ID 
GrNewGC(void)
{
      GR_GC_ID    gc;

      AllocReq(NewGC);
      if(GrTypedReadBlock(&gc, sizeof(gc),GrNumNewGC) == -1)
            return 0;
      return gc;
}

/**
 * GrCopyGC:
 * @gc: the already existing graphics context to copy the parameters from
 * @Returns: the ID of the newly created graphics context or 0 on error
 *
 * Creates a new graphics context structure and fills it in with the values
 * from the specified already existing graphics context.
 */
GR_GC_ID 
GrCopyGC(GR_GC_ID gc)
{
      nxCopyGCReq *req;
      GR_GC_ID     newgc;

      req = AllocReq(CopyGC);
      req->gcid = gc;
      if(GrTypedReadBlock(&newgc, sizeof(newgc),GrNumCopyGC) == -1)
            return 0;
      return newgc;
}

/**
 * GrDestroyGC:
 * @gc: the ID of the graphics context structure to destroy
 *
 * Destroys the graphics context structure with the specified ID.
 */
void
GrDestroyGC(GR_GC_ID gc)
{
      nxDestroyGCReq *req;

      req = AllocReq(DestroyGC);
      req->gcid = gc;
}

/**
 * GrNewRegion:
 * @Returns: the ID of the newly created region
 *
 * Creates a new region structure and returns the ID used to refer to it.
 * The structure is initialised with a set of default parameters.
 */
GR_REGION_ID 
GrNewRegion(void)
{
      GR_REGION_ID    region;

      AllocReq(NewRegion);
      if(GrTypedReadBlock(&region, sizeof(region),GrNumNewRegion) == -1)
            return 0;
      return region;
}

/**
 * GrDestroyRegion:
 * @region: the ID of the region structure to destroy
 *
 * Destroys the region structure with the specified ID.
 */
void
GrDestroyRegion(GR_REGION_ID region)
{
      nxDestroyRegionReq *req;

      req = AllocReq(DestroyRegion);
      req->regionid = region;
}

/**
 * GrUnionRectWithRegion:
 * @region: the ID of the region to modify
 * @rect: a pointer to the rectangle to add to the region
 *
 * Makes a union of the specified region and the specified rectangle and
 * places the result back in the source region.
 */
void
GrUnionRectWithRegion(GR_REGION_ID region, GR_RECT *rect)
{
      nxUnionRectWithRegionReq *req;

      req = AllocReq(UnionRectWithRegion);
      if(rect)
            memcpy(&req->rect, rect, sizeof(*rect));
      req->regionid = region;
}

/**
 * GrUnionRegion:
 * @dst_rgn: the ID of the destination region
 * @src_rgn1: the ID of the first source region
 * @src_rgn2: the ID of the second source region
 *
 * Makes a union of the specified source regions and places the result in the
 * specified destination region.
 */
void
GrUnionRegion(GR_REGION_ID dst_rgn, GR_REGION_ID src_rgn1,
      GR_REGION_ID src_rgn2)
{
      nxUnionRegionReq *req;

      req = AllocReq(UnionRegion);
      req->regionid = dst_rgn;
      req->srcregionid1 = src_rgn1;
      req->srcregionid2 = src_rgn2;
}

/**
 * GrSubtractRegion:
 * @dst_rgn: the ID of the destination region
 * @src_rgn1: the ID of the first source region
 * @src_rgn2: the ID of the second source region
 *
 * Subtracts the second source region from the first source region and places
 * the result in the specified destination region.
 */
void
GrSubtractRegion(GR_REGION_ID dst_rgn, GR_REGION_ID src_rgn1,
      GR_REGION_ID src_rgn2)
{
      nxSubtractRegionReq *req;

      req = AllocReq(SubtractRegion);
      req->regionid = dst_rgn;
      req->srcregionid1 = src_rgn1;
      req->srcregionid2 = src_rgn2;
}

/**
 * GrXorRegion:
 * @dst_rgn: the ID of the destination region
 * @src_rgn1: the ID of the first source region
 * @src_rgn2: the ID of the second source region
 *
 * Performs a logical exclusive OR operation on the specified source regions
 * and places the result in the destination region. The destination region
 * will contain only the parts of the source regions which do not overlap.
 */
void
GrXorRegion(GR_REGION_ID dst_rgn, GR_REGION_ID src_rgn1,
      GR_REGION_ID src_rgn2)
{
      nxXorRegionReq *req;

      req = AllocReq(XorRegion);
      req->regionid = dst_rgn;
      req->srcregionid1 = src_rgn1;
      req->srcregionid2 = src_rgn2;
}

/**
 * GrIntersectRegion:
 * @dst_rgn: the ID of the destination region
 * @src_rgn1: the ID of the first source region
 * @src_rgn2: the ID of the second source region
 *
 * Calculates the intersection of the two specified source regions and places
 * the result in the specified destination region. The destination region
 * will contain only the parts of the source regions which overlap each other.
 */
void
GrIntersectRegion(GR_REGION_ID dst_rgn, GR_REGION_ID src_rgn1,
      GR_REGION_ID src_rgn2)
{
      nxIntersectRegionReq *req;

      req = AllocReq(IntersectRegion);
      req->regionid = dst_rgn;
      req->srcregionid1 = src_rgn1;
      req->srcregionid2 = src_rgn2;
}

/**
 * GrSetGCRegion:
 * @gc: the ID of the graphics context to set the clip mask of
 * @region: the ID of the region to use as the clip mask
 *
 * Sets the clip mask of the specified graphics context to the specified
 * region. Subsequent drawing operations using this graphics context will not
 * draw outside the specified region. The region ID can be set to 0 to remove
 * the clipping region from the specified graphics context.
 */
void
GrSetGCRegion(GR_GC_ID gc, GR_REGION_ID region)
{
      nxSetGCRegionReq *req;
      
      req = AllocReq(SetGCRegion);
      req->gcid = gc;
      req->regionid = region;
}

/**
 * GrSetGCClipOrigin:
 * @gc: the ID of the graphics context with user clip region
 * @xoff: new X offset of user clip region
 * @xoff: new Y offset of user clip region
 *
 * Sets the X,Y origin of the user clip region in the specified
 * graphics context.
 */
void 
GrSetGCClipOrigin(GR_GC_ID gc, int x, int y)
{
      nxSetGCClipOriginReq *req;

      req = AllocReq(SetGCClipOrigin);
      req->gcid = gc;
      req->xoff = x;
      req->yoff = y;
}

/**
 * GrPointInRegion:
 * @region: the ID of the region to examine
 * @x: the X coordinate of the point to test for
 * @y: the Y coordinate of the point to test for
 * @Returns: True if the point is within the region, or False otherwise
 *
 * Tests whether the specified point is within the specified region, and
 * then returns either True or False depending on the result.
 */
GR_BOOL
GrPointInRegion(GR_REGION_ID region, GR_COORD x, GR_COORD y)
{
      nxPointInRegionReq *req;
      GR_BOOL             ret_value;

      req = AllocReq(PointInRegion);
      req->regionid = region;
      req->x = x;
      req->y = y;
      if(GrTypedReadBlock(&ret_value, sizeof(ret_value),
            GrNumPointInRegion) == -1)
            return GR_FALSE;
      return ret_value;
}

/**
 * GrRectInRegion:
 * @region: the ID of the region to examine
 * @x: the X coordinates of the rectangle to test
 * @y: the Y coordinates of the rectangle to test
 * @w: the width of the rectangle to test
 * @h: the height of the rectangle to test
 * @Returns: GR_RECT_PARTIN, GR_RECT_ALLIN, or GR_RECT_OUT
 *
 * Tests whether the specified rectangle is contained within the specified
 * region. Returns GR_RECT_OUT if it is not inside it at all, GR_RECT_ALLIN
 * if it is completely contained within the region, or GR_RECT_PARTIN if
 * it is partially contained within the region.
 */
int
GrRectInRegion(GR_REGION_ID region, GR_COORD x, GR_COORD y, GR_COORD w,
      GR_COORD h)
{
      nxRectInRegionReq *req;
      unsigned short       ret_value;
      
      req = AllocReq(RectInRegion);
      req->regionid = region;
      req->x = x;
      req->y = y;
      req->w = w;
      req->h = h;
      if(GrTypedReadBlock(&ret_value, sizeof(ret_value),
            GrNumRectInRegion) == -1)
            return 0;
      return (int)ret_value;
}

/**
 * GrEmptyRegion:
 * @region: the ID of the region to examine
 * @Returns: GR_TRUE if the region is empty, or GR_FALSE if it is not
 *
 * Determines whether the specified region is empty, and returns GR_TRUE
 * if it is, or GR_FALSE otherwise.
 */
GR_BOOL
GrEmptyRegion(GR_REGION_ID region)
{
      nxEmptyRegionReq *req;
      GR_BOOL       ret_value;
      
      req = AllocReq(EmptyRegion);
      req->regionid = region;
      if(GrTypedReadBlock(&ret_value, sizeof(ret_value),
            GrNumEmptyRegion) == -1)
            return GR_FALSE;
      return ret_value;
}

/**
 * GrEqualRegion:
 * @rgn1: the ID of the first region to examine
 * @rgn2: the ID of the second region to examine
 * @Returns: GR_TRUE if the regions are equal, or GR_FALSE otherwise
 *
 * Determines whether the specified regions are identical, and returns GR_TRUE
 * if it is, or GR_FALSE otherwise.
 */
GR_BOOL
GrEqualRegion(GR_REGION_ID rgn1, GR_REGION_ID rgn2)
{
      nxEqualRegionReq *req;
      GR_BOOL       ret_value;
      
      req = AllocReq(EqualRegion);
      req->region1 = rgn1;
      req->region2 = rgn2;
      if(GrTypedReadBlock(&ret_value, sizeof(ret_value),
            GrNumEqualRegion) == -1)
            return GR_FALSE;
      return ret_value;
}

/**
 * GrOffsetRegion:
 * @region: the ID of the region to offset
 * @dx: the distance to offset the region by in the X axis
 * @dy: the distance to offset the region by in the Y axis
 *
 * Offsets the specified region by the specified distance.
 */
void
GrOffsetRegion(GR_REGION_ID region, GR_SIZE dx, GR_SIZE dy)
{
      nxOffsetRegionReq *req;
      
      req = AllocReq(OffsetRegion);
      req->region = region;
      req->dx = dx;
      req->dy = dy;
}

/**
 * GrGetRegionBox:
 * @region: the ID of the region to get the bounding box of
 * @rect: pointer to a rectangle structure
 * @Returns: the region type
 *
 * Fills in the specified rectangle structure with a bounding box that would
 * completely enclose the specified region, and also returns the type of the
 * specified region. 
 */
int
GrGetRegionBox(GR_REGION_ID region, GR_RECT *rect)
{
      nxGetRegionBoxReq *req;
      unsigned short       ret_value;
      
      if (!rect)
            return GR_FALSE;
      req = AllocReq(GetRegionBox);
      req->regionid = region;
      if(GrTypedReadBlock(rect, sizeof(*rect), GrNumGetRegionBox) == -1)
            return GR_FALSE;
      if(GrTypedReadBlock(&ret_value, sizeof(ret_value),
            GrNumGetRegionBox) == -1)
            return GR_FALSE;
      return ret_value;
}

/**
 * GrNewPolygonRegion:
 * @mode: the polygon mode to use (GR_POLY_EVENODD or GR_POLY_WINDING)
 * @count: the number of points in the polygon
 * @points: pointer to an array of point structures describing the polygon
 * @Returns: the ID of the newly allocated region structure, or 0 on error
 *
 * Creates a new region structure, fills it with the region described by the
 * specified polygon, and returns the ID used to refer to it.
 */
GR_REGION_ID 
GrNewPolygonRegion(int mode, GR_COUNT count, GR_POINT *points)
{
      nxNewPolygonRegionReq   *req;
      long              size;
      GR_REGION_ID            region;

      if(count == 0)
            return GrNewRegion();
      
      if(points == NULL)
            return 0;

      size = (long)count * sizeof(GR_POINT);
      req = AllocReqExtra(NewPolygonRegion, size);
      req->mode = mode;
      /* FIXME: unportable method, depends on sizeof(int) in GR_POINT*/
      memcpy(GetReqData(req), points, size);

      if(GrTypedReadBlock(&region, sizeof(region),
            GrNumNewPolygonRegion) == -1)
            return 0;
      return region;
}

/**
 * GrMapWindow:
 * @wid: the ID of the window to map
 *
 * Recursively maps (makes visible) the specified window and all of the
 * child windows which have a sufficient map count. The border and background
 * of the window are painted, and an exposure event is generated for the
 * window and every child which becomes visible.
 */
void 
GrMapWindow(GR_WINDOW_ID wid)
{
      nxMapWindowReq *req;

      req = AllocReq(MapWindow);
      req->windowid = wid;
}

/**
 * GrUnmapWindow:
 * @wid: the ID of the window to unmap
 *
 * Recursively unmaps (makes invisible) the specified window and all of the
 * child windows.
 */
void 
GrUnmapWindow(GR_WINDOW_ID wid)
{
      nxUnmapWindowReq *req;

      req = AllocReq(UnmapWindow);
      req->windowid = wid;
}

/**
 * GrRaiseWindow:
 * @wid: the ID of the window to raise
 *
 * Places the specified window at the top of its parents drawing stack, above
 * all of its sibling windows.
 */
void 
GrRaiseWindow(GR_WINDOW_ID wid)
{
      nxRaiseWindowReq *req;

      req = AllocReq(RaiseWindow);
      req->windowid = wid;
}

/**
 * GrLowerWindow:
 * @wid: the ID of the window to lower
 *
 * Places the specified window at the bottom of its parents drawing stack,
 * below all of its sibling windows.
 */
void 
GrLowerWindow(GR_WINDOW_ID wid)
{
      nxLowerWindowReq *req;

      req = AllocReq(LowerWindow);
      req->windowid = wid;
}

/**
 * GrMoveWindow:
 * @wid: the ID of the window to move
 * @x: the X coordinate to move the window to relative to its parent.
 * @y: the Y coordinate to move the window to relative to its parent.
 * 
 * Moves the specified window to the specified position relative to its
 * parent window.
 */
void 
GrMoveWindow(GR_WINDOW_ID wid, GR_COORD x, GR_COORD y)
{
      nxMoveWindowReq *req;

      req = AllocReq(MoveWindow);
      req->windowid = wid;
      req->x = x;
      req->y = y;
}

/**
 * GrResizeWindow:
 * @wid: the ID of the window to resize
 * @width: the width to resize the window to
 * @height: the height to resize the window to
 *
 * Resizes the specified window to be the specified width and height.
 */
void 
GrResizeWindow(GR_WINDOW_ID wid, GR_SIZE width, GR_SIZE height)
{
      nxResizeWindowReq *req;

      req = AllocReq(ResizeWindow);
      req->windowid = wid;
      req->width = width;
      req->height = height;
}

/**
 * GrReparentWindow:
 * @wid: the ID of the window to reparent
 * @pwid: the ID of the new parent window
 * @x: the X coordinate to place the window at relative to the new parent
 * @y: the Y coordinate to place the window at relative to the new parent
 *
 * Changes the parent window of the specified window to the specified parent
 * window and places it at the specified coordinates relative to the new
 * parent.
 */
void 
GrReparentWindow(GR_WINDOW_ID wid, GR_WINDOW_ID pwid, GR_COORD x, GR_COORD y)
{
      nxReparentWindowReq *req;

      req = AllocReq(ReparentWindow);
      req->windowid = wid;
      req->parentid = pwid;
      req->x = x;
      req->y = y;
}

/**
 * GrClearArea:
 * @wid: window ID
 * @exposeflag: a flag indicating whether to also generate an exposure event
 *
 * Clears the specified window by to its background color or pixmap.
 * If exposeflag is non zero, an exposure event is generated for
 * the window after it has been cleared.
 */
void
GrClearArea(GR_WINDOW_ID wid, GR_COORD x, GR_COORD y, GR_SIZE width,
      GR_SIZE height, GR_BOOL exposeflag)
{
      nxClearAreaReq *req;

      req = AllocReq(ClearArea);
      req->windowid = wid;
      req->x = x;
      req->y = y;
      req->width = width;
      req->height = height;
      req->exposeflag = exposeflag;
}

/**
 * GrGetFocus:
 * @Returns: the ID of the window which currently has the keyboard focus
 *
 * Returns the ID of the window which currently has the keyboard focus.
 */
GR_WINDOW_ID
GrGetFocus(void)
{
      GR_WINDOW_ID    wid;

      AllocReq(GetFocus);
      if(GrTypedReadBlock(&wid, sizeof(wid), GrNumGetFocus) == -1)
            return 0;
      return wid;
}

/**
 * GrSetFocus:
 * @wid: the ID of the window to set the focus to
 *
 * Sets the keyboard focus to the specified window.
 */
void 
GrSetFocus(GR_WINDOW_ID wid)
{
      nxSetFocusReq *req;

      req = AllocReq(SetFocus);
      req->windowid = wid;
}

/**
 * GrSetWindowCursor:
 * @wid: the ID of the window to set the cursor for
 * @cid: the cursor ID
 *
 * Specify a cursor for a window.
 * This cursor will only be used within that window, and by default
 * for its new children.  If the cursor is currently within this
 * window, it will be changed to the new one immediately.
 * If the new cursor ID is 0, revert to the root window cursor.
 */
void
GrSetWindowCursor(GR_WINDOW_ID wid, GR_CURSOR_ID cid)
{
      nxSetWindowCursorReq *req;

      req = AllocReq(SetWindowCursor);
      req->windowid = wid;
      req->cursorid = cid;
}

/**
 * GrNewCursor:
 * @width: the width of the pointer bitmap
 * @height: the height of the pointer bitmap
 * @hotx: the X coordinate within the bitmap used as the target of the pointer
 * @hoty: the Y coordinate within the bitmap used as the target of the pointer
 * @foreground: the colour to use for the foreground of the pointer
 * @background: the colour to use for the background of the pointer
 * @fgbitmap: pointer to bitmap data specifying the foreground of the pointer
 * @bgbitmap: pointer to bitmap data specifying the background of the pointer
 *
 * Creates a server-based cursor (mouse graphic) resource.
 */
GR_CURSOR_ID
GrNewCursor(GR_SIZE width, GR_SIZE height, GR_COORD hotx, GR_COORD hoty,
      GR_COLOR foreground, GR_COLOR background,
      GR_BITMAP *fgbitmap, GR_BITMAP *bgbitmap)
{
      nxNewCursorReq *req;
      int         bitmapsize;
      char *            data;
      GR_CURSOR_ID      cursorid;

      bitmapsize = GR_BITMAP_SIZE(width, height) * sizeof(GR_BITMAP);
      req = AllocReqExtra(NewCursor, bitmapsize*2);
      req->width = width;
      req->height = height;
      req->hotx = hotx;
      req->hoty = hoty;
      req->fgcolor = foreground;
      req->bgcolor = background;
      data = GetReqData(req);
      memcpy(data, fgbitmap, bitmapsize);
      memcpy(data+bitmapsize, bgbitmap, bitmapsize);

      if(GrTypedReadBlock(&cursorid, sizeof(cursorid), GrNumNewCursor) == -1)
            return 0;
      return cursorid;
}

/**
 * GrMoveCursor:
 * @x: the X coordinate to move the pointer to
 * @y: the Y coordinate to move the pointer to
 *
 * Moves the cursor (mouse pointer) to the specified coordinates.
 * The coordinates are relative to the root window, where (0,0) is the upper
 * left hand corner of the screen. The reference point used for the pointer
 * is that of the "hot spot". After moving the pointer, the graphic used for
 * the pointer will change to the graphic defined for use in the window which
 * it is over.
 */
void 
GrMoveCursor(GR_COORD x, GR_COORD y)
{
      nxMoveCursorReq *req;

      req = AllocReq(MoveCursor);
      req->x = x;
      req->y = y;
}

/**
 * GrSetGCForeground:
 * @gc: the ID of the graphics context to set the foreground colour of
 * @foreground: the colour to use as the new foreground colour
 *
 * Changes the foreground colour of the specified graphics context to the
 * specified colour.
 */
void 
GrSetGCForeground(GR_GC_ID gc, GR_COLOR foreground)
{
      nxSetGCForegroundReq *req;

      req = AllocReq(SetGCForeground);
      req->gcid = gc;
      req->color = foreground;
}

/**
 * GrSetGCBackground:
 * @gc: the ID of the graphics context to set the background colour of
 * @background: the colour to use as the new background colour
 *
 * Changes the background colour of the specified graphics context to the
 * specified colour.
 */
void 
GrSetGCBackground(GR_GC_ID gc, GR_COLOR background)
{
      nxSetGCBackgroundReq *req;

      req = AllocReq(SetGCBackground);
      req->gcid = gc;
      req->color = background;
}

/**
 * GrSetGCMode:
 * @gc: the ID of the graphics context to set the drawing mode of
 * @mode: the new drawing mode
 *
 * Changes the drawing mode (SET, XOR, OR, AND, etc.) of the specified
 * graphics context to the specified mode.
 */
void 
GrSetGCMode(GR_GC_ID gc, int mode)
{
      nxSetGCModeReq *req;

      req = AllocReq(SetGCMode);
      req->gcid = gc;
      req->mode = mode;
}

/**
 * GrSetGCUseBackground:
 * @gc: the ID of the graphics context to change the "use background" flag of
 * @flag: flag specifying whether to use the background colour or not
 *
 * Sets the flag which chooses whether or not the background colour is used
 * when drawing bitmaps and text using the specified graphics context to the
 * specified value.
 */
void 
GrSetGCUseBackground(GR_GC_ID gc, GR_BOOL flag)
{
      nxSetGCUseBackgroundReq *req;

      req = AllocReq(SetGCUseBackground);
      req->gcid = gc;
      req->flag = flag;
}

/**
 * GrCreateFont:
 * @name: string containing the name of a built in font to look for
 * @height: the desired height of the font
 * @plogfont: pointer to a LOGFONT structure
 * @Returns: a font ID number which can be used to refer to the font
 *
 * Attempts to locate a font with the desired attributes and returns a font
 * ID number which can be used to refer to it. If the plogfont argument is
 * not NULL, the values in that structure will be used to choose a font.
 * Otherwise, if the height is non zero, the built in font with the closest
 * height to that specified will be used. If the height is zero, the built
 * in font with the specified name will be used. If the desired font is not
 * found, the first built in font will be returned as a last resort.
 */
GR_FONT_ID
GrCreateFont(GR_CHAR *name, GR_COORD height, GR_LOGFONT *plogfont)
{
      nxCreateFontReq *req;
      GR_FONT_ID  fontid;

      req = AllocReq(CreateFont);
      if (plogfont) {
            memcpy(&req->lf, plogfont, sizeof(*plogfont));
            req->height = 0;
            req->lf_used = 1;
      } else {
            if (name)
                  strcpy(req->lf.lfFaceName, name);
            else req->lf.lfFaceName[0] = '\0';
            req->height = height;
            req->lf_used = 0;
      }
  
      if(GrTypedReadBlock(&fontid, sizeof(fontid),GrNumCreateFont) == -1)
            return 0;
      return fontid;
}

/**
 * GrGetFontList:
 * @fonts: pointer used to return an array of font names.
 * @numfonts: pointer used to return the number of names found.
 *
 * Returns an array of strings containing the names of available fonts and an
 * integer that specifies the number of strings returned. 
 */
void 
GrGetFontList(GR_FONTLIST ***fonts, int *numfonts)
{
      nxGetFontListReq *req;
      char *tmpstr;
      GR_FONTLIST **flist;
      int num, len, i;

      req = AllocReq(GetFontList);

      GrTypedReadBlock(&num, sizeof(int), GrNumGetFontList);
      
      *numfonts = num;

      if(num == -1)
            return;

      flist = (GR_FONTLIST**)malloc(num * sizeof(GR_FONTLIST*));

      for(i = 0; i < num; i++) 
            flist[i] = (GR_FONTLIST*)malloc(sizeof(GR_FONTLIST*));

      for(i = 0; i < num; i++) {
            GrReadBlock(&len, sizeof(int));
            tmpstr = (char*)malloc(len * sizeof(char));
            GrReadBlock(tmpstr, len * sizeof(char));
            flist[i]->ttname = tmpstr;

            GrReadBlock(&len, sizeof(int));
            tmpstr = (char*)malloc(len * sizeof(char));
            GrReadBlock(tmpstr, len * sizeof(char));
            flist[i]->mwname = tmpstr;
            
      }

      *fonts = flist;
}

/**
 * GrFreeFontList:
 * @fonts: pointer to array returned by GrGetFontList
 * @numfonts: the number of font names in the array
 *
 * free's the specified array.
 */
void
GrFreeFontList(GR_FONTLIST ***fonts, int n)
{
      int i;
      MWFONTLIST *g, **list = *fonts;

      for (i = 0; i < n; i++) {
            g = list[i];
            if(g) {
                  if(g->mwname) 
                        free(g->mwname);
                  if(g->ttname) 
                        free(g->ttname);
                  free(g);
            }
      }
      free(list);
      *fonts = 0;
}

/**
 * GrSetFontSize:
 * @fontid: the ID number of the font to change the size of
 * @fontsize: the size to change the font to
 *
 * Changes the size of the specified font to the specified size.
 */
void
GrSetFontSize(GR_FONT_ID fontid, GR_COORD fontsize)
{
      nxSetFontSizeReq *req;

      req = AllocReq(SetFontSize);
      req->fontid = fontid;
      req->fontsize = fontsize;
}

/**
 * GrSetFontRotation:
 * @fontid: the ID number of the font to rotate
 * @tenthdegrees: the angle to set the rotation to in tenths of a degree
 *
 * Changes the rotation of the specified font to the specified angle.
 */
void
GrSetFontRotation(GR_FONT_ID fontid, int tenthdegrees)
{
      nxSetFontRotationReq *req;

      req = AllocReq(SetFontRotation);
      req->fontid = fontid;
      req->tenthdegrees = tenthdegrees;
}

/**
 * GrSetFontAttr:
 * @fontid: the ID of the font to set the attributes of
 * @setflags: mask specifying attribute flags to set
 * @clrflags: mask specifying attribute flags to clear
 *
 * Changes the attributes (GR_TFKERNING, GR_TFANTIALIAS, GR_TFUNDERLINE, etc.)
 * of the specified font according to the set and clear mask arguments.
 */
void
GrSetFontAttr(GR_FONT_ID fontid, int setflags, int clrflags)
{
      nxSetFontAttrReq *req;

      req = AllocReq(SetFontAttr);
      req->fontid = fontid;
      req->setflags = setflags;
      req->clrflags = clrflags;
}

/**
 * GrDestroyFont:
 * @fontid: the ID of the font to destroy
 *
 * Frees all resources associated with the specified font ID, and if the font
 * is a non built in type and this is the last ID referring to it, unloads the
 * font from memory.
 */
void
GrDestroyFont(GR_FONT_ID fontid)
{
      nxDestroyFontReq *req;

      req = AllocReq(DestroyFont);
      req->fontid = fontid;
}

/**
 * GrSetGCFont:
 * @gc: the ID of the graphics context to set the font of
 * @font: the ID of the font
 *
 * Sets the font to be used for text drawing in the specified graphics
 * context to the specified font ID.
 */
void
GrSetGCFont(GR_GC_ID gc, GR_FONT_ID font)
{
      nxSetGCFontReq *req;

      req = AllocReq(SetGCFont);
      req->gcid = gc;
      req->fontid = font;
}

/**
 * GrLine:
 * @id: the ID of the drawable to draw the line on
 * @gc: the ID of the graphics context to use when drawing the line
 * @x1: the X coordinate of the start of the line relative to the drawable
 * @y1: the Y coordinate of the start of the line relative to the drawable
 * @x2: the X coordinate of the end of the line relative to the drawable
 * @y2: the Y coordinate of the end of the line relative to the drawable
 *
 * Draws a line using the specified graphics context on the specified drawable
 * from (x1, y1) to (x2, y2), with coordinates given relative to the drawable.
 */
void 
GrLine(GR_DRAW_ID id, GR_GC_ID gc, GR_COORD x1, GR_COORD y1, GR_COORD x2,
      GR_COORD y2)
{
      nxLineReq *req;

      req = AllocReq(Line);
      req->drawid = id;
      req->gcid = gc;
      req->x1 = x1;
      req->y1 = y1;
      req->x2 = x2;
      req->y2 = y2;
}

/**
 * GrRect:
 * @id: the ID of the drawable to draw the rectangle on
 * @gc: the ID of the graphics context to use when drawing the rectangle
 * @x: the X coordinate of the rectangle relative to the drawable
 * @y: the Y coordinate of the rectangle relative to the drawable
 * @width: the width of the rectangle
 * @height: the height of the rectangle
 *
 * Draw the boundary of a rectangle of the specified dimensions and position
 * on the specified drawable using the specified graphics context.
 */
void 
GrRect(GR_DRAW_ID id, GR_GC_ID gc, GR_COORD x, GR_COORD y, GR_SIZE width,
      GR_SIZE height)
{
      nxRectReq *req;

      req = AllocReq(Rect);
      req->drawid = id;
      req->gcid = gc;
      req->x = x;
      req->y = y;
      req->width = width;
      req->height = height;
}

/**
 * GrFillRect:
 * @id: the ID of the drawable to draw the rectangle on
 * @gc: the ID of the graphics context to use when drawing the rectangle
 * @x: the X coordinate of the rectangle relative to the drawable
 * @y: the Y coordinate of the rectangle relative to the drawable
 * @width: the width of the rectangle
 * @height: the height of the rectangle
 *
 * Draw a filled rectangle of the specified dimensions and position on the
 * specified drawable using the specified graphics context.
 */
void 
GrFillRect(GR_DRAW_ID id, GR_GC_ID gc, GR_COORD x, GR_COORD y,
      GR_SIZE width, GR_SIZE height)
{
      nxFillRectReq *req;

      req = AllocReq(FillRect);
      req->drawid = id;
      req->gcid = gc;
      req->x = x;
      req->y = y;
      req->width = width;
      req->height = height;
}

/**
 * GrEllipse:
 * @id: the ID of the drawable to draw the ellipse on
 * @gc: the ID of the graphics context to use when drawing the ellipse
 * @x: the X coordinate to draw the ellipse at relative to the drawable
 * @y: the Y coordinate to draw the ellipse at relative to the drawable
 * @rx: the radius of the ellipse on the X axis
 * @ry: the radius of the ellipse on the Y axis
 *
 * Draws the boundary of ellipse at the specified position using the specified
 * dimensions and graphics context on the specified drawable.
 */
void 
GrEllipse(GR_DRAW_ID id, GR_GC_ID gc, GR_COORD x, GR_COORD y, GR_SIZE rx,
      GR_SIZE ry)
{
      nxEllipseReq *req;

      req = AllocReq(Ellipse);
      req->drawid = id;
      req->gcid = gc;
      req->x = x;
      req->y = y;
      req->rx = rx;
      req->ry = ry;
}

/**
 * GrFillEllipse:
 * @id: the ID of the drawable to draw the filled ellipse on
 * @gc: the ID of the graphics context to use when drawing the ellipse
 * @x: the X coordinate to draw the ellipse at relative to the drawable
 * @y: the Y coordinate to draw the ellipse at relative to the drawable
 * @rx: the radius of the ellipse on the X axis
 * @ry: the radius of the ellipse on the Y axis
 *
 * Draws a filled ellipse at the specified position using the specified
 * dimensions and graphics context on the specified drawable.
 */
void 
GrFillEllipse(GR_DRAW_ID id, GR_GC_ID gc, GR_COORD x, GR_COORD y,
      GR_SIZE rx, GR_SIZE ry)
{
      nxFillEllipseReq *req;

      req = AllocReq(FillEllipse);
      req->drawid = id;
      req->gcid = gc;
      req->x = x;
      req->y = y;
      req->rx = rx;
      req->ry = ry;
}

/**
 * GrArc:
 * @id: the ID of the drawable to draw the arc on
 * @gc: the graphics context to use when drawing the arc
 * @x: the X coordinate to draw the arc at relative to the drawable
 * @y: the Y coordinate to draw the arc at relative to the drawable
 * @rx: the radius of the arc on the X axis
 * @ry: the radius of the arc on the Y axis
 * @ax: the X coordinate of the start of the arc relative to the drawable
 * @ay: the Y coordinate of the start of the arc relative to the drawable
 * @bx: the X coordinate of the end of the arc relative to the drawable
 * @by: the Y coordinate of the end of the arc relative to the drawable
 * @type: the fill style to use when drawing the arc
 *
 * Draws an arc with the specified dimensions at the specified position
 * on the specified drawable using the specified graphics context.
 * The type specifies the fill type. Possible values include GR_ARC and
 * GR_PIE.
 */
void  
GrArc(GR_DRAW_ID id, GR_GC_ID gc, GR_COORD x, GR_COORD y,
      GR_SIZE rx, GR_SIZE ry, GR_COORD ax, GR_COORD ay,
      GR_COORD bx, GR_COORD by, int type)
{
      nxArcReq *req;

      req = AllocReq(Arc);
      req->drawid = id;
      req->gcid = gc;
      req->x = x;
      req->y = y;
      req->rx = rx;
      req->ry = ry;
      req->ax = ax;
      req->ay = ay;
      req->bx = bx;
      req->by = by;
      req->type = type;
}

/**
 * GrArcAngle:
 * @id: the ID of the drawable to draw the arc on
 * @gc: the graphics context to use when drawing the arc
 * @x: the X coordinate to draw the arc at relative to the drawable
 * @y: the Y coordinate to draw the arc at relative to the drawable
 * @rx: the radius of the arc on the X axis
 * @ry: the radius of the arc on the Y axis
 * @angle1: the angle of the start of the arc
 * @angle2: the angle of the end of the arc
 * @type: the fill style to use when drawing the arc
 *
 * Draws an arc with the specified dimensions at the specified position
 * on the specified drawable using the specified graphics context.
 * The type specifies the fill type. Possible values include GR_ARC and
 * GR_PIE. This function requires floating point support, and is slightly
 * slower than the GrArc() function which does not require floating point.
 */
void
GrArcAngle(GR_DRAW_ID id, GR_GC_ID gc, GR_COORD x, GR_COORD y,
      GR_SIZE rx, GR_SIZE ry, GR_COORD angle1, GR_COORD angle2, int type)
{
      nxArcAngleReq *req;

      req = AllocReq(ArcAngle);
      req->drawid = id;
      req->gcid = gc;
      req->x = x;
      req->y = y;
      req->rx = rx;
      req->ry = ry;
      req->angle1 = angle1;
      req->angle2 = angle2;
      req->type = type;
}

/**
 * GrBitmap:
 * @id: the ID of the drawable to draw the bitmap onto
 * @gc: the ID of the graphics context to use when drawing the bitmap
 * @x: the X coordinate to draw the bitmap at relative to the drawable
 * @y: the Y coordinate to draw the bitmap at relative to the drawable
 * @width: the width of the bitmap
 * @height: the height of the bitmap
 * @bitmaptable: pointer to the bitmap data
 *
 * Draws the monochrome bitmap data provided in the bitmaptable argument
 * at the specified position on the specified drawable using the specified
 * graphics context. Note that the bitmap data should be an array of aligned
 * 16 bit words. The usebackground flag in the graphics context specifies
 * whether to draw the background colour wherever a bit value is zero.
 */
void 
GrBitmap(GR_DRAW_ID id, GR_GC_ID gc, GR_COORD x, GR_COORD y, GR_SIZE width,
      GR_SIZE height, GR_BITMAP *bitmaptable)
{
      nxBitmapReq *req;
      long       bitmapsize;

      bitmapsize = (long)GR_BITMAP_SIZE(width, height) * sizeof(GR_BITMAP);
      req = AllocReqExtra(Bitmap, bitmapsize);
      req->drawid = id;
      req->gcid = gc;
      req->x = x;
      req->y = y;
      req->width = width;
      req->height = height;
      memcpy(GetReqData(req), bitmaptable, bitmapsize);
}

/**
 * GrDrawImageBits:
 * @id: the ID of the drawable to draw the image onto
 * @gc: the ID of the graphics context to use when drawing the image
 * @x: the X coordinate to draw the image at relative to the drawable
 * @y: the Y coordinate to draw the image at relative to the drawable
 * @pimage: pointer to the image structure
 *
 * Draws the image contained in the specified image structure onto the
 * specified drawable at the specified coordinates using the specified
 * graphics context.
 */
void
GrDrawImageBits(GR_DRAW_ID id, GR_GC_ID gc, GR_COORD x, GR_COORD y,
      GR_IMAGE_HDR *pimage)
{
      nxDrawImageBitsReq      *req;
      int               imagesize;
      int               palsize;
      char              *addr;

      imagesize = pimage->pitch * pimage->height;
      palsize = pimage->palsize * sizeof(MWPALENTRY);
      req = AllocReqExtra(DrawImageBits, imagesize + palsize);
      req->drawid = id;
      req->gcid = gc;
      req->x = x;
      req->y = y;
      /* fill MWIMAGEHDR items passed externally*/
      req->width = pimage->width;
      req->height = pimage->height;
      req->planes = pimage->planes;
      req->bpp = pimage->bpp;
      req->pitch = pimage->pitch;
      req->bytesperpixel = pimage->bytesperpixel;
      req->compression = pimage->compression;
      req->palsize = pimage->palsize;
      req->transcolor = pimage->transcolor;
      addr = GetReqData(req);
      memcpy(addr, pimage->imagebits, imagesize);
      memcpy(addr+imagesize, pimage->palette, palsize);
}

/**
 * GrDrawImageFromFile:
 * @id: the ID of the drawable to draw the image onto
 * @gc: the ID of the graphics context to use when drawing the image
 * @x: the X coordinate to draw the image at relative to the drawable
 * @y: the Y coordinate to draw the image at relative to the drawable
 * @width: the maximum image width
 * @height: the maximum image height
 * @path: string containing the filename of the image to load
 * @flags: flags specific to the particular image loader
 *
 * Loads the specified image file and draws it at the specified position
 * on the specified drawable using the specified graphics context. The
 * width and height values specify the size of the image to draw- if the
 * actual image is a different size, it will be scaled to fit. The image type
 * is automatically detected using the magic numbers in the image header (ie.
 * the filename extension is irrelevant). The currently supported image types
 * include GIF, JPEG, Windows BMP, PNG, XPM, and both ascii and binary
 * variants of PBM, PGM, and PPM. However the image types supported by a
 * particular server depend on which image types were enabled in the server
 * configuration at build time. 
 */
void
GrDrawImageFromFile(GR_DRAW_ID id, GR_GC_ID gc, GR_COORD x, GR_COORD y,
      GR_SIZE width, GR_SIZE height, char* path, int flags)
{
      nxDrawImageFromFileReq *req;

      req = AllocReqExtra(DrawImageFromFile, strlen(path)+1);
      req->drawid = id;
      req->gcid = gc;
      req->x = x;
      req->y = y;
      req->width = width;
      req->height = height;
      req->flags = flags;
      memcpy(GetReqData(req), path, strlen(path)+1);
}

/**
 * GrLoadImageFromFile:
 * @path: string containing the filename of the image to load
 * @flags: flags specific to the particular image loader
 * @Returns: ID of the image buffer the image was loaded into
 *
 * Loads the specified image file into a newly created server image buffer
 * and returns the ID of the buffer. The image type is automatically detected
 * using the magic numbers in the image header (ie. the filename extension is
 * irrelevant). The currently supported image types include GIF, JPEG, Windows
 * BMP, PNG, XPM, and both ascii and binary variants of PBM, PGM, and PPM.
 * However the image types supported by a particular server depend on which
 * image types were enabled in the server configuration at build time. 
 */
GR_IMAGE_ID
GrLoadImageFromFile(char *path, int flags)
{
      nxLoadImageFromFileReq *req;
      GR_IMAGE_ID       imageid;

      req = AllocReqExtra(LoadImageFromFile, strlen(path)+1);
      req->flags = flags;
      memcpy(GetReqData(req), path, strlen(path)+1);

      if(GrTypedReadBlock(&imageid, sizeof(imageid),
            GrNumLoadImageFromFile) == -1)
                  return 0;
      return imageid;
}

/**
 * GrDrawImageToFit:
 * @id: the ID of the drawable to draw the image onto
 * @gc: the ID of the graphics context to use when drawing the image
 * @x: the X coordinate to draw the image at relative to the drawable
 * @y: the Y coordinate to draw the image at relative to the drawable
 * @width: the maximum image width
 * @height: the maximum image height
 * @imageid: the ID of the image buffer containing the image to display
 *
 * Draws the image from the specified image buffer at the specified position
 * on the specified drawable using the specified graphics context. The
 * width and height values specify the size of the image to draw- if the
 * actual image is a different size, it will be scaled to fit.
 */ 
void
GrDrawImageToFit(GR_DRAW_ID id, GR_GC_ID gc, GR_COORD x, GR_COORD y,
      GR_SIZE width, GR_SIZE height, GR_IMAGE_ID imageid)
{
      nxDrawImageToFitReq *req;

      req = AllocReq(DrawImageToFit);
      req->drawid = id;
      req->gcid = gc;
      req->x = x;
      req->y = y;
      req->width = width;
      req->height = height;
      req->imageid = imageid;
}

/**
 * GrFreeImage:
 * @id: ID of the image buffer to free
 *
 * Destroys the specified image buffer and reclaims the memory used by it.
 */
void
GrFreeImage(GR_IMAGE_ID id)
{
      nxFreeImageReq *req;

      req = AllocReq(FreeImage);
      req->id = id;
}

/**
 * GrGetImageInfo:
 * @id: ID of an image buffer
 * @iip: pointer to a GR_IMAGE_INFO structure
 *
 * Fills in the specified image information structure with the details of the
 * specified image buffer.
 */
void
GrGetImageInfo(GR_IMAGE_ID id, GR_IMAGE_INFO *iip)
{
      nxGetImageInfoReq *req;

      req = AllocReq(GetImageInfo);
      req->id = id;
      GrTypedReadBlock(iip, sizeof(GR_IMAGE_INFO), GrNumGetImageInfo);
}

static int sendImageBuffer(void *buffer, int size) {

  int bufid;
  int bufsize = size;
  void *bufptr = buffer;

  nxImageBufferAllocReq *alloc;
  nxImageBufferSendReq *send;

  /* Step 1 - Allocate a buffer on the other side */

  alloc = AllocReq(ImageBufferAlloc);
  alloc->size = size;

  GrTypedReadBlock(&bufid, sizeof(bufid), GrNumImageBufferAlloc);
  
  if (bufid < 0) return(0);
  
  /* step 2 - Send the buffer across */

  while(bufsize > 0) {
    int chunk = (MAXREQUESTSZ - sizeof(nxImageBufferSendReq));
    if (chunk > bufsize) chunk=bufsize;
    
    send = AllocReqExtra(ImageBufferSend, chunk);
    send->buffer_id = bufid;
    send->size = chunk;

    memcpy(GetReqData(send), bufptr, chunk);
    bufptr += chunk;
    bufsize -= chunk;
  }

  return(bufid);
}

GR_IMAGE_ID GrLoadImageFromBuffer(void *buffer, int size, int flags) {
  
  int bufid;
  nxLoadImageFromBufferReq *req;
  GR_IMAGE_ID imageid;

  /* Step 1 - Send the buffer to the other side */
  bufid = sendImageBuffer(buffer, size);

  if (!bufid) return(0);
  
  /* Step 2 - Send the command to load the image */
  /* Note - This will free the buffer automagically */

  req = AllocReq(LoadImageFromBuffer);
  req->flags = flags;
  req->buffer = bufid;

  if(GrTypedReadBlock(&imageid, sizeof(imageid),
                  GrNumLoadImageFromBuffer) == -1)
    return(0);
  else
    return(imageid);
}

void GrDrawImageFromBuffer(GR_DRAW_ID id, GR_GC_ID gc, GR_COORD x, GR_COORD y,
                     GR_SIZE width, GR_SIZE height, 
                     void *buffer, int size, int flags) {

  int bufid;
  nxDrawImageFromBufferReq *req;
  
  /* Step 1 - Send the buffer to the other side */
  bufid = sendImageBuffer(buffer, size);

  if (!bufid) return;
  
  /* Step 2 - Send the command to load/draw the image */
  /* Note - This will free the buffer automagically */

  req = AllocReq(DrawImageFromBuffer);
  req->flags = flags;
  req->drawid = id;
  req->gcid = gc;
  req->x = x;
  req->y = y;
  req->width = width;
  req->height = height;
  req->flags = flags;
  req->buffer = bufid;
}


/*
 * Draw a rectangular area in the specified drawable using the specified
 * graphics context.  This differs from rectangle drawing in that the
 * color values for each pixel in the rectangle are specified.
 * The color table is indexed
 * row by row.  Values whose color matches the background color are only
 * written if the usebackground flag is set in the GC.
 *
 * The pixels are packed according to pixtype:
 *
 * pixtype        array of
 * MWPF_RGB       MWCOLORVAL (unsigned long)
 * MWPF_PIXELVAL  MWPIXELVAL (compile-time dependent)
 * MWPF_PALETTE         unsigned char
 * MWPF_TRUECOLOR0888   unsigned long
 * MWPF_TRUECOLOR888    packed struct {char r,char g,char b} (24 bits)
 * MWPF_TRUECOLOR565    unsigned short
 * MWPF_TRUECOLOR555    unsigned short
 * MWPF_TRUECOLOR332    unsigned char
 */
/**
 * GrArea:
 * @id: the ID of the drawable to draw the area onto
 * @gc: the ID of the graphics context to use when drawing the area
 * @x: the X coordinate to draw the area at relative to the drawable
 * @y: the Y coordinate to draw the area at relative to the drawable
 * @width: the width of the area
 * @height: the height of the area
 * @pixels: pointer to an array containing the pixel data
 * @pixtype: the format of the pixel data
 *
 * Draws the specified pixel array of the specified size and format onto the
 * specified drawable using the specified graphics context at the specified
 * position. Note that colour conversion is currently only performed when using
 * the GR_PF_RGB format, which is an unsigned long containing RGBX data.
 */
void 
GrArea(GR_DRAW_ID id, GR_GC_ID gc, GR_COORD x, GR_COORD y, GR_SIZE width,
      GR_SIZE height, void *pixels, int pixtype)
{
      nxAreaReq *req;
      long       size;
      long       chunk_y;
      int        pixsize;

      /* Calculate size of packed pixels*/
      switch(pixtype) {
      case MWPF_RGB:
            pixsize = sizeof(MWCOLORVAL);
            break;
      case MWPF_PIXELVAL:
            pixsize = sizeof(MWPIXELVAL);
            break;
      case MWPF_PALETTE:
      case MWPF_TRUECOLOR332:
            pixsize = sizeof(unsigned char);
            break;
      case MWPF_TRUECOLOR0888:
            pixsize = sizeof(unsigned long);
            break;
      case MWPF_TRUECOLOR888:
            pixsize = 3;
            break;
      case MWPF_TRUECOLOR565:
      case MWPF_TRUECOLOR555:
            pixsize = sizeof(unsigned short);
            break;
      default:
            return;
      }

      /* Break request into MAXREQUESTSZ size packets*/
      while(height > 0) {
            chunk_y = (MAXREQUESTSZ - sizeof(nxAreaReq)) /
                  ((long)width * pixsize);
            if(chunk_y > height)
                  chunk_y = height;
            size = chunk_y * ((long)width * pixsize);
            req = AllocReqExtra(Area, size);
            req->drawid = id;
            req->gcid = gc;
            req->x = x;
            req->y = y;
            req->width = width;
            req->height = chunk_y;
            req->pixtype = pixtype;
            memcpy(GetReqData(req), pixels, size);
            pixels = (void *)(((char *)pixels) + size);
            y += chunk_y;
            height -= chunk_y;
      }
}

/**
 * GrCopyArea:
 * @id: the ID of the drawable to copy the area to
 * @gc: the ID of the graphics context to use when copying the area
 * @x: the X coordinate to copy the area to within the destination drawable
 * @y: the Y coordinate to copy the area to within the destination drawable
 * @width: the width of the area to copy
 * @height: the height of the area to copy
 * @srcid: the ID of the drawable to copy the area from
 * @srcx: the X coordinate to copy the area from within the source drawable
 * @srcy: the Y coordinate to copy the area from within the source drawable
 * @op: the ROP codes to pass to the blitter when performing the copy
 *
 * Copies the specified area of the specified size between the specified
 * drawables at the specified positions using the specified graphics context
 * and ROP codes. 0 is a sensible default ROP code in most cases.
 */
void
GrCopyArea(GR_DRAW_ID id, GR_GC_ID gc, GR_COORD x, GR_COORD y,
      GR_SIZE width, GR_SIZE height, GR_DRAW_ID srcid,
      GR_COORD srcx, GR_COORD srcy, int op)
{
      nxCopyAreaReq *req;

        req = AllocReq(CopyArea);
        req->drawid = id;
        req->gcid = gc;
        req->x = x;
        req->y = y;
        req->width = width;
        req->height = height;
        req->srcid = srcid;
        req->srcx = srcx;
        req->srcy = srcy;
        req->op = op;
}
   
   
/**
 * GrReadArea:
 * @id: the ID of the drawable to read an area from
 * @x: the X coordinate to read the area from relative to the drawable
 * @y: the Y coordinate to read the area from relative to the drawable
 * @width: the width of the area to read
 * @height: the height of the area to read
 * @pixels: pointer to an area of memory to place the pixel data in
 *
 * Reads the pixel data of the specified size from the specified position on
 * the specified drawable into the specified pixel array. If the drawable is
 * a window, the data returned will be the pixel values from the relevant
 * position on the screen regardless of whether the window is obscured by other
 * windows. If the window is unmapped, or partially or fully outside a window
 * boundary, black pixel values will be returned.
 */
void 
GrReadArea(GR_DRAW_ID id,GR_COORD x,GR_COORD y,GR_SIZE width,
      GR_SIZE height, GR_PIXELVAL *pixels)
{
      nxReadAreaReq *req;
      long           size;

      req = AllocReq(ReadArea);
      req->drawid = id;
      req->x = x;
      req->y = y;
      req->width = width;
      req->height = height;
      size = (long)width * height * sizeof(MWPIXELVAL);
      GrTypedReadBlock(pixels, size, GrNumReadArea);
}

/**
 * GrPoint:
 * @id: the ID of the drawable to draw a point on
 * @gc: the ID of the graphics context to use when drawing the point
 * @x: the X coordinate to draw the point at relative to the drawable
 * @y: the Y coordinate to draw the point at relative to the drawable
 *
 * Draws a point using the specified graphics context at the specified position
 * on the specified drawable.
 */
void 
GrPoint(GR_DRAW_ID id, GR_GC_ID gc, GR_COORD x, GR_COORD y)
{
      nxPointReq *req;

      req = AllocReq(Point);
      req->drawid = id;
      req->gcid = gc;
      req->x = x;
      req->y = y;

}

/**
 * GrPoints:
 * @id: the ID of the drawable to draw a point on
 * @gc: the ID of the graphics context to use when drawing the point
 * @count: the number of points in the point table
 * @pointtable: pointer to a GR_POINT array which lists the points to draw
 *
 * Draws a set of points using the specified graphics context at the positions
 * specified by the point table on the specified drawable.
 */
void
GrPoints(GR_DRAW_ID id, GR_GC_ID gc, GR_COUNT count, GR_POINT *pointtable)
{
      nxPointsReq *req;
      long  size;

      size = (long)count * sizeof(GR_POINT);
      req = AllocReqExtra(Points, size);
      req->drawid = id;
      req->gcid = gc;
      memcpy(GetReqData(req), pointtable, size);
}

/**
 * GrPoly:
 * @id: the ID of the drawable to draw the polygon onto
 * @gc: the ID of the graphics context to use when drawing the polygon
 * @count: the number of points in the point array
 * @pointtable: pointer to an array of points describing the polygon
 *
 * Draws an unfilled polygon on the specified drawable using the specified
 * graphics context. The polygon is specified by an array of point structures.
 * The polygon is not automatically closed- if a closed polygon is desired,
 * the last point must be the same as the first.
 */
void 
GrPoly(GR_DRAW_ID id, GR_GC_ID gc, GR_COUNT count, GR_POINT *pointtable)
{
      nxPolyReq *req;
      long       size;

      size = (long)count * sizeof(GR_POINT);
      req = AllocReqExtra(Poly, size);
      req->drawid = id;
      req->gcid = gc;
      memcpy(GetReqData(req), pointtable, size);
}

/**
 * GrFillPoly:
 * @id: the ID of the drawable to draw the polygon onto
 * @gc: the ID of the graphics context to use when drawing the polygon
 * @count: the number of points in the point array
 * @pointtable: pointer to an array of points describing the polygon
 *
 * Draws a filled polygon on the specified drawable using the specified
 * graphics context. The polygon is specified by an array of point structures.
 * The polygon is automatically closed- the last point need not be the same as
 * the first in order for the polygon to be closed.
 */
void 
GrFillPoly(GR_DRAW_ID id, GR_GC_ID gc, GR_COUNT count,GR_POINT *pointtable)
{
      nxFillPolyReq *req;
      long           size;

      size = (long)count * sizeof(GR_POINT);
      req = AllocReqExtra(FillPoly, size);
      req->drawid = id;
      req->gcid = gc;
      memcpy(GetReqData(req), pointtable, size);
}

/**
 * GrText:
 * @id: the ID of the drawable to draw the text string onto
 * @gc: the ID of the graphics context to use when drawing the text string
 * @x: the X coordinate to draw the string at relative to the drawable
 * @y: the Y coordinate to draw the string at relative to the drawable
 * @str: the text string to draw
 * @count: the number of characters (not bytes) in the string
 * @flags: flags specifying text encoding, alignment, etc.
 *
 * Draws the specified text string at the specified position on the specified
 * drawable using the specified graphics context and flags. The default flags
 * specify ASCII encoding and baseline alignment.
 */
void 
GrText(GR_DRAW_ID id, GR_GC_ID gc, GR_COORD x, GR_COORD y, void *str,
      GR_COUNT count, int flags)
{
      nxTextReq *req;
      int      size;

      if(count == -1 && (flags&MWTF_PACKMASK) == MWTF_ASCII)
            count = strlen((char *)str);

      size = nxCalcStringBytes(str, count, flags);

      req = AllocReqExtra(Text, size);
      req->drawid = id;
      req->gcid = gc;
      req->x = x;
      req->y = y;
      req->count = count;
      req->flags = flags;
      memcpy(GetReqData(req), str, size);
}


/**
 * GrGetSystemPalette:
 * @pal: pointer to a palette structure to fill in with the system palette
 *
 * Retrieves the system palette and places it in the specified palette
 * structure.
 */
void
GrGetSystemPalette(GR_PALETTE *pal)
{
      AllocReq(GetSystemPalette);
      GrTypedReadBlock(pal, sizeof(*pal), GrNumGetSystemPalette);
}

/**
 * GrSetSystemPalette:
 * @first: the first palette value to set
 * @pal: pointer to a palette structure containing the new values
 *
 * Sets the system palette to the values stored in the specified palette
 * structure. The values before the specified first value are not set.
 */
void
GrSetSystemPalette(GR_COUNT first, GR_PALETTE *pal)
{
      nxSetSystemPaletteReq *req;

      req = AllocReq(SetSystemPalette);
      req->first = first;
      req->count = pal->count;
      memcpy(req->palette, pal->palette, sizeof(GR_PALENTRY) * pal->count);
}

/**
 * GrFindColor:
 * @c: the colour value to find
 * @retpixel: pointer to the returned pixel value
 *
 * Calculates the pixel value to use to display the specified colour value.
 * The colour value is specified as a GR_COLOR, which is a 32 bit truecolour
 * value stored as RGBX. The pixel value size depends on the architecture.
 */
void
GrFindColor(GR_COLOR c, GR_PIXELVAL *retpixel)
{
      nxFindColorReq *req;

      req = AllocReq(FindColor);
      req->color = c;
      GrTypedReadBlock(retpixel, sizeof(*retpixel), GrNumFindColor);
}

/**
 * GrReqShmCmds:
 * @shmsize: the size of the shared memory area to allocate
 *
 * Requests a shared memory area of the specified size to use for transferring
 * command arguments. This is faster but less portable than the standard BSD
 * sockets method of communication (and of course will only work if the client
 * and server are on the same machine). Apart from the initial allocation of
 * the area using this call, the use of shared memory is completely
 * transparent. Additionally, if the allocation fails we silently and
 * automatically fall back on socket communication. It is safe to call this
 * function even if shared memory support is not compiled in; it will simply
 * do nothing.
 *
 * FIXME: how does the user decide what size of shared memory area to allocate?
 */
void
GrReqShmCmds(long shmsize)
{
#if HAVE_SHAREDMEM_SUPPORT
      nxReqShmCmdsReq   req;
      int key, shmid;

      if ( nxSharedMem != 0 )
            return;

      GrFlush();

      shmsize = (shmsize+SHM_BLOCK_SIZE-1) & ~(SHM_BLOCK_SIZE-1);

      req.reqType = GrNumReqShmCmds;
      req.hilength = 0;
      req.length = sizeof(req);
      req.size = shmsize;

      nxWriteSocket((char *)&req,sizeof(req));
      GrReadBlock(&key,sizeof(key));

      if ( !key ) {
            EPRINTF("nxclient: no shared memory support on server\n");
            return;
      }

      shmid = shmget(key,shmsize,0);
      if ( shmid == -1 ) {
            EPRINTF("nxclient: Can't shmget key %d: %m\n", key);
            return;
      }

      nxSharedMem = shmat(shmid,0,0);
      shmctl(shmid,IPC_RMID,0);     /* Prevent other from attaching */
      if ( nxSharedMem == (char *)-1 )
            return;

      nxSharedMemSize = shmsize;
      nxAssignReqbuffer(nxSharedMem, shmsize);
#endif /* HAVE_SHAREDMEM_SUPPORT*/
}

/**
 * GrInjectPointerEvent:
 * @x: the X coordinate of the pointer event relevant to the root window
 * @y: the Y coordinate of the pointer event relevant to the root window
 * @button: the pointer button status
 * @visible: whether to display the pointer after the event
 *
 * Sets the pointer invisible if the visible parameter is GR_FALSE, or visible
 * if it is GR_TRUE, then moves the pointer to the specified position and
 * generates a mouse event with the specified button status. Also performs
 * a GrFlush() so that the event takes effect immediately.
 */
void
GrInjectPointerEvent(GR_COORD x, GR_COORD y, int button, int visible)
{
      nxInjectEventReq *req;

      req = AllocReq(InjectEvent);
      req->event_type = GR_INJECT_EVENT_POINTER;
      req->event.pointer.visible = visible;
      req->event.pointer.x = x;
      req->event.pointer.y = y;
      req->event.pointer.button = button;

      GrFlush();
}

/**
 * GrInjectKeyboardEvent:
 * @wid: ID of the window to send the event to, or 0
 * @uch: 32 bit Unicode keystroke value to inject
 * @ch: 8 bit ascii keystroke value to inject
 * @modifier: modifiers (shift, ctrl, alt, etc.) to inject
 * @special: special keys to inject
 * @content: mask specifying which arguments are valid
 *
 * Sends a keyboard event to the specified window, or to the window with the
 * current keyboard focus if 0 is used as the ID. The other arguments
 * correspond directly to the fields of the same names in the keyboard event
 * structure.
 */
void
GrInjectKeyboardEvent(GR_WINDOW_ID wid, GR_KEY keyvalue,
      GR_KEYMOD modifier, GR_SCANCODE scancode, GR_BOOL pressed)
{
      nxInjectEventReq *req;

      req = AllocReq(InjectEvent);
      req->event_type = GR_INJECT_EVENT_KEYBOARD;
      req->event.keyboard.wid = wid;
      req->event.keyboard.keyvalue = keyvalue;
      req->event.keyboard.modifier = modifier;
      req->event.keyboard.scancode = scancode;
      req->event.keyboard.pressed = pressed;

      GrFlush();
}

/**
 * GrSetWMProperties:
 * @wid: the ID of the window to set the WM properties of
 * @props: pointer to a GR_WM_PROPERTIES structure
 *
 * Copies the provided GR_WM_PROPERTIES structure into the the GR_WM_PROPERTIES
 * structure of the specified window id.
 */
void
GrSetWMProperties(GR_WINDOW_ID wid, GR_WM_PROPERTIES *props)
{
      nxSetWMPropertiesReq *req;
      char        *addr;
      int         s;

      if ((props->flags & GR_WM_FLAGS_TITLE) && props->title)
            s = strlen(props->title) + 1;
      else s = 0;

      req = AllocReqExtra(SetWMProperties, s + sizeof(GR_WM_PROPERTIES));
      req->windowid = wid;
      addr = GetReqData(req);
      memcpy(addr, props, sizeof(GR_WM_PROPERTIES));
      if (s)
            memcpy(addr + sizeof(GR_WM_PROPERTIES), props->title, s);
}

/**
 * GrGetWMProperties:
 * @wid: the ID of the window to retreive the WM properties of
 * @props: pointer to a GR_WM_PROPERTIES structure to fill in
 *
 * Reads the GR_WM_PROPERTIES structure for the window with the specified
 * id and fills in the provided structure with the information.
 * It is the callers responsibility to free the title member as it is allocated
 * dynamically. The title field will be set to NULL if the window has no title.
 */
void
GrGetWMProperties(GR_WINDOW_ID wid, GR_WM_PROPERTIES *props)
{
      nxGetWMPropertiesReq *req;
      UINT16 textlen;
      GR_CHAR c;

      req = AllocReq(GetWMProperties);
      req->windowid = wid;

      GrTypedReadBlock(props, sizeof(GR_WM_PROPERTIES), GrNumGetWMProperties);
      GrReadBlock(&textlen, sizeof(textlen));
      if(!textlen) {
            props->title = NULL;
            return;
      }
      if(!(props->title = malloc(textlen))) {
            /* Oh dear, we're out of memory but still have to purge the
               requested data (and throw it away) */
            while(textlen--) GrReadBlock(&c, 1);
      } else {
            GrReadBlock(props->title, textlen);
      }
}

/**
 * GrCloseWindow:
 * @wid: the ID of the window to send the CLOSE_REQ event to
 *
 * Sends a CLOSE_REQ event to the specified window if the client has selected
 * to receive CLOSE_REQ events on this window. Used to request an application
 * to shut down but not force it to do so immediately, so the application can
 * ask whether to save changed files before shutting down cleanly.
 */
void 
GrCloseWindow(GR_WINDOW_ID wid)
{
      nxCloseWindowReq *req;

      req = AllocReq(CloseWindow);
      req->windowid = wid;
}

/**
 * GrKillWindow:
 * @wid: the ID of the window to kill
 *
 * Forcibly disconnects the client which owns this window with the specified
 * ID number. Used to kill an application which has locked up and is not
 * responding to CLOSE_REQ events.
 */
void 
GrKillWindow(GR_WINDOW_ID wid)
{
      nxKillWindowReq *req;

      req = AllocReq(KillWindow);
      req->windowid = wid;
}

/**
 * GrSetScreenSaverTimeout:
 * @timeout: the number of seconds of inactivity before screen saver activates
 *
 * Sets the number of seconds of inactivity before a screen saver activate
 * event is sent to the root window ID. A value of 0 activates the
 * screen saver immediately, and a value of -1 disables the screen saver
 * function.
 */
void 
GrSetScreenSaverTimeout(GR_TIMEOUT timeout)
{
      nxSetScreenSaverTimeoutReq *req;

      req = AllocReq(SetScreenSaverTimeout);
      req->timeout = timeout;
}

/**
 * GrSetSelectionOwner:
 * @wid: the ID of the window to set the selection owner to
 * @typelist: list of mime types selection data can be supplied as
 *
 * Sets the current selection (otherwise known as the clipboard) ownership
 * to the specified window. Specifying an owner of 0 disowns the selection.
 * The typelist argument is a list of mime types (seperated by space
 * characters) which the window is able to supply the data as. At least one
 * type must be specified unless you are disowning the selection (typically
 * text/plain for plain ASCII text or text/uri-list for a filename).
 *
 * The window which owns the current selection must be prepared to handle
 * SELECTION_LOST events (received when another window takes ownership of the
 * selection) and CLIENT_DATA_REQ events (received when a client wishes to
 * retreive the selection data).
 */
void
GrSetSelectionOwner(GR_WINDOW_ID wid, GR_CHAR *typelist)
{
      nxSetSelectionOwnerReq *req;
      char *p;
      int len;

      if(wid) {
            len = strlen(typelist) + 1;
            req = AllocReqExtra(SetSelectionOwner, len);
            p = GetReqData(req);
            memcpy(p, typelist, len);
      } else {
            req = AllocReq(SetSelectionOwner);
      }

      req->wid = wid;
}

/**
 * GrGetSelectionOwner:
 * @typelist: pointer used to return the list of available mime types 
 * @Returns: the ID of the window which currently owns the selection, or 0
 *
 * Finds the window which currently owns the selection and returns its ID,
 * or 0 if no window currently owns the selection. A pointer to the list of
 * mime types the selection owner is capable of supplying is placed in the
 * pointer specified by the typelist argument. The typelist is null terminated,
 * and the fields are seperated by space characters. It is the callers
 * responsibility to free the typelist string, as it is allocated dynamically.
 * If the allocation fails, it will be set to a NULL pointer, so remember to
 * check the value of it before using it.
 */
GR_WINDOW_ID
GrGetSelectionOwner(GR_CHAR **typelist)
{
      nxGetSelectionOwnerReq *req;
      UINT16 textlen;
      GR_CHAR c;
      GR_WINDOW_ID wid;

      req = AllocReq(GetSelectionOwner);
      GrTypedReadBlock(&wid, sizeof(wid), GrNumGetSelectionOwner);
      if(wid) {
            GrReadBlock(&textlen, sizeof(textlen));
            if(!(*typelist = malloc(textlen))) {
                  /* Oh dear, we're out of memory but still have to
                  purge the requested data (and throw it away) */
                  while(textlen--) GrReadBlock(&c, 1);
            } else {
                  GrReadBlock(*typelist, textlen);
            }
      }

      return wid;
}

/**
 * GrRequestClientData:
 * @wid: the ID of the window requesting the data
 * @rid: the ID of the window to request the data from
 * @serial: the serial number of the request
 * @mimetype: the number of the desired mime type to request
 *
 * Sends a CLIENT_DATA_REQ event to the specified window. Used for requesting
 * both selection and "drag and drop" data. The mimetype argument specifies
 * the format of the data you would like to receive, as an index into the list
 * returned by GrGetSelectionOwner (the first type in the list is index 0).
 * The server makes no guarantees as to when, or even if, the client will
 * reply to the request. If the client does reply, the reply will take the
 * form of one or more CLIENT_DATA events. The request serial number is
 * typically a unique ID which the client can assign to a request in order for
 * it to be able to keep track of transfers (CLIENT_DATA events contain the
 * same number in the sid field). Remember to free the data field of the
 * CLIENT_DATA events as they are dynamically allocated. Also note that if
 * the allocation fails the data field will be set to NULL, so you should
 * check the value before using it.
 */
void
GrRequestClientData(GR_WINDOW_ID wid, GR_WINDOW_ID rid, GR_SERIALNO serial,
                                          GR_MIMETYPE mimetype)
{
      nxRequestClientDataReq *req;

      req = AllocReq(RequestClientData);
      req->wid = wid;
      req->rid = rid;
      req->serial = serial;
      req->mimetype = mimetype;
}

/**
 * GrSendClientData:
 * @wid: the ID of the window sending the data
 * @did: the ID of the destination window
 * @sid: the serial number of the request
 * @len: the number of bytes of data to transfer
 * @thislen: the number of bytes in this packet
 * @data: pointer to the data to transfer
 *
 * Used as the response to a CLIENT_DATA_REQ event. Sends the specified data
 * of the specified length to the specified window using the specified source
 * window ID and transfer serial number. Any fragmentation of the data into
 * multiple CLIENT_DATA events which is required is handled automatically.
 * The serial number should always be set to the value supplied by the
 * CLIENT_DATA_REQ event. The thislen parameter is used internally to split
 * the data up into packets. It should be set to the same value as the len
 * parameter.
 * 
 */
void
GrSendClientData(GR_WINDOW_ID wid, GR_WINDOW_ID did, GR_SERIALNO serial,
                  GR_LENGTH len, GR_LENGTH thislen, void *data)
{
      nxSendClientDataReq *req;
      char *p;
      GR_LENGTH l, pos = 0;

      while(pos < len) {
            l = MAXREQUESTSZ - sizeof(nxSendClientDataReq);
            if(l > (len - pos)) l = len - pos;
            req = AllocReqExtra(SendClientData, l);
            req->wid = wid;
            req->did = did;
            req->serial = serial;
            req->len = len;
            p = GetReqData(req);
            memcpy(p, data + pos, l);
            pos += l;
      }
}

/**
 * GrBell:
 *
 * Asks the server to ring the console bell on behalf of the client (intended
 * for terminal apps to be able to ring the bell on the server even if they
 * are running remotely).
 */
void
GrBell(void)
{
      AllocReq(Bell);
}

/**
 * GrSetBackgroundPixmap:
 * @wid: ID of the window to set the background of
 * @pixmap: ID of the pixmap to use as the background
 * @flags: flags specifying how to draw the pixmap onto the window
 *
 * Sets the background of the specified window to the specified pixmap.
 * The flags which specify how to draw the pixmap (in the top left of the
 * window, in the centre of the window, tiled, etc.) are those which start with
 * GR_BACKGROUND_ in nano-X.h. If the pixmap value is 0, the server will
 * disable the background pixmap and return to using a solid colour fill.
 */
void
GrSetBackgroundPixmap(GR_WINDOW_ID wid, GR_WINDOW_ID pixmap, int flags)
{
      nxSetBackgroundPixmapReq *req;

      req = AllocReq(SetBackgroundPixmap);
      req->wid = wid;
      req->pixmap = pixmap;
      req->flags = flags;
}

/**
 * GrDestroyCursor:
 * @id: ID of the cursor to destory
 *
 * Destroys the specified server-based cursor and
 * reclaims the memory used by it.
 */
void
GrDestroyCursor(GR_CURSOR_ID cid)
{
      nxDestroyCursorReq *req;

      req = AllocReq(DestroyCursor);
      req->cursorid = cid;
}

/**
 * GrQueryTree:
 * @wid: window ID for query
 * @parentid: returned parent ID
 * @children: returned children ID list
 * @nchildren: returned children count
 *
 * Return window parent and list of children.
 * Caller must free() children list after use.
 */
void
GrQueryTree(GR_WINDOW_ID wid, GR_WINDOW_ID *parentid, GR_WINDOW_ID **children,
      GR_COUNT *nchildren)
{
      nxQueryTreeReq *req;
      GR_COUNT    count;
      GR_WINDOW_ID      dummy;

      req = AllocReq(QueryTree);
      req->windowid = wid;

      GrTypedReadBlock(parentid, sizeof(*parentid), GrNumQueryTree);
      GrReadBlock(nchildren, sizeof(*nchildren));
      if (!*nchildren) {
            *children = NULL;
            return;
      }
      count = *nchildren;
      if(!(*children = malloc(count * sizeof(GR_WINDOW_ID)))) {
            /* We're out of memory but still have to purge the
               requested data (and throw it away) */
            while(count--)
                  GrReadBlock(&dummy, sizeof(GR_WINDOW_ID));
      } else {
            GrReadBlock(*children, count * sizeof(GR_WINDOW_ID));
      }
}

#if YOU_WANT_TO_IMPLEMENT_DRAG_AND_DROP
/**
 * GrRegisterDragAndDropWindow:
 * @wid: the ID of the window to use as the drag and drop source window
 * @iid: the ID of the pixmap to use as the drag and drop icon
 * @typelist: list of mime types the drag and drop data can be supplied as
 *
 * Enables the specified window to be used as a drag and drop source. The
 * specified pixmap will be used as the icon shown whilst dragging, and the
 * null terminated, newline seperated list of mime types which the data can
 * be supplied as is specified by the typelist argument. At least one type
 * (typically text/plain for plain ASCII or text/uri-list for a filename or
 * list of filenames) must be specified. When the icon is dropped,
 * the window which it is dropped on will receive a DROP event (providing it
 * has selected for DROP events), and if the client wishes to accept the data
 * and is able to handle one of the mime types in the type list, it should use
 * GrRequestClientData() to retrieve the data from the drag and drop source
 * window. Remember to free the typelist field of the DROP event as it is
 * dynamically allocated. It is possible for a client to select for DROP events
 * on the Root window if it is desired to allow dropping icons on the desktop.
 */
void
GrRegisterDragAndDropWindow(GR_WINDOW_ID wid, GR_WINDOW_ID iid,
                        GR_CHAR *typelist)
{
}
#endif


GR_TIMER_ID
GrCreateTimer (GR_WINDOW_ID wid, GR_TIMEOUT period)
{
    nxCreateTimerReq  *req;
    GR_TIMER_ID  timerid;

    req = AllocReq(CreateTimer);

    req->wid = wid;
    req->period = period;

    if (GrTypedReadBlock(&timerid, sizeof (timerid), GrNumCreateTimer) == -1)
        return 0;
    return timerid;
}

void
GrDestroyTimer (GR_TIMER_ID tid)
{
    nxDestroyTimerReq *req;
    
    req = AllocReq(DestroyTimer);
    req->timerid = tid;
}

void
GrSetPortraitMode(int portraitmode)
{
    nxSetPortraitModeReq *req;
    
    req = AllocReq(SetPortraitMode);
    req->portraitmode = portraitmode;
}

Generated by  Doxygen 1.6.0   Back to index