📄 client.c
字号:
/* * Copyright (c) 1999, 2000, 2002, 2003 Greg Haerr <greg@censoft.com> * Portions Copyright (c) 2002, 2003 by Koninklijke Philips Electronics N.V. * 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>#define ADDR_FAM AF_NANO#elif __ECOS#include <netinet/in.h>#include <arpa/inet.h>#include <sys/select.h>#include <cyg/kernel/kapi.h>#define ADDR_FAM AF_INET#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"#include "lock.h"#ifndef ADDR_FAM/** * Default to unix socket for client/server. * @internal */#define ADDR_FAM AF_UNIX#endif#define GR_CLOSE_FIX 1 /* dirty hack attempts to fix GrClose hang bug*//** * The granularity of the shared memory allocation. The requested size * will be rounded up to a multiple of this many bytes. * * @internal */#define SHM_BLOCK_SIZE 4096#ifndef __ECOS/* exported global data */int nxSocket = -1; /* The network socket descriptor */LOCK_DECLARE(nxGlobalLock); /* global lock for threads safety*/#if HAVE_SHAREDMEM_SUPPORTchar * nxSharedMem = 0; /* Address of shared memory segment*/static int nxSharedMemSize; /* Size in bytes of shared mem segment*/#endifstatic int regfdmax = -1; /* GrRegisterInput globals*/static fd_set regfdset;/** * Human-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;#endifstatic void QueueEvent(GR_EVENT *ep);static void GetNextQueuedEvent(GR_EVENT *ep);static void _GrGetNextEventTimeout(GR_EVENT *ep, GR_TIMEOUT timeout);static int _GrPeekEvent(GR_EVENT * ep);/** * 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. * * @param b Destination for read. * @param n Number of bytes to read. * @return 0 on success or -1 on failure. * @internal */static intReadBlock(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, errno %d\n", i, errno); return -1; } v += i; } return 0;}/** * Read a byte of data from the server. * * @return The unsigned character read from the server, or -1 on error. * * @internal */static intReadByte(void){ unsigned char c; if(ReadBlock(&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. * * @param evp Event to check * * @internal */static voidCheckForClientData(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; ReadBlock(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. * * @param packettype Expected type. * @return packettype on success, -1 on error. * * @internal */static intCheckBlockType(short packettype){ short b; GR_EVENT event; while (ReadBlock(&b,sizeof(b)) != -1) { if (b == packettype) return b; if (b == GrNumGetNextEvent) { /*DPRINTF("nxclient %d: Storing event (expected %d)\n", getpid(), packettype);*/ /* read event and queue it for later processing*/ ReadBlock(&event, sizeof(event)); CheckForClientData(&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 ReadBlock but * make sure the response is of the right kind, e.g. store the event that * may have sneaked into the stream. * * @param b Destination for read data. * @param n Number of bytes to read. * @param type The required packet type. * @return 0 on success or -1 on failure. * * @internal */static intTypedReadBlock(void *b, int n, int type){ int r; r = CheckBlockType(type); if (r != type) return -1; return ReadBlock(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. * * @param ep The event to check. * * @internal */static voidCheckErrorEvent(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; } }}/** * Open a connection to the graphics server. * * @return the fd of the connection to the server or -1 on failure * * @ingroup nanox_general */int GrOpen(void){ size_t size; nxOpenReq req; int tries; int ret = 0;#if ADDR_FAM == AF_NANO struct sockaddr_na name;#elif ADDR_FAM == AF_INET struct sockaddr_in name; struct hostent *he; /* allow specification of remote nano-X server address*/ char *sockaddr = getenv("NXDISPLAY"); if (!sockaddr) sockaddr = "127.0.0.1"; /* loopback address*/#elif ADDR_FAM == AF_UNIX struct sockaddr_un name; /* allow override of named UNIX socket (default /tmp/.nano-X)*/ char *sockname = getenv("NXDISPLAY"); if (!sockname) sockname = GR_NAMED_SOCKET;#else#error "ADDR_FAM not defined to AF_NANO, AF_INET or AF_UNIX"#endif ACCESS_PER_THREAD_DATA() /* check already open*/ if (nxSocket >= 0) return nxSocket; /* try to get socket*/ if ((nxSocket = socket(ADDR_FAM, SOCK_STREAM, 0)) == -1) return -1; /* initialize global critical section lock*/ LOCK_INIT(&nxGlobalLock);#if ADDR_FAM == AF_NANO name.sun_family = AF_NANO; name.sun_no = GR_ELKS_SOCKET; /* AF_NANO socket 79*/ size = sizeof(struct sockaddr_na);#elif ADDR_FAM == AF_INET name.sin_family = AF_INET; name.sin_port = htons(GR_NUM_SOCKET); /* AF_INET socket 6600*/ if (!(he = gethostbyname(sockaddr))) { EPRINTF("nxclient: Can't resolve address for server %s\n", sockaddr); close(nxSocket); nxSocket = -1; return -1; } name.sin_addr = *(struct in_addr *)he->h_addr_list[0]; size = sizeof(struct sockaddr_in);#elif ADDR_FAM == AF_UNIX name.sun_family = AF_UNIX; strcpy(name.sun_path, sockname); size = (offsetof(struct sockaddr_un, sun_path) + strlen(name.sun_path) + 1);#endif /* * Try to open the connection ten times, * waiting 0.1 or 2.0 seconds between attempts. */ for (tries=1; tries<=10; ++tries) { struct timespec req; ret = connect(nxSocket, (struct sockaddr *) &name, size); if (ret >= 0) break;#if ADDR_FAM == AF_INET req.tv_sec = 0; req.tv_nsec = 100000000L;#else req.tv_sec = 2; req.tv_nsec = 0;#endif nanosleep(&req, NULL); EPRINTF("nxclient: retry connect attempt %d\n", tries); } if (ret == -1) { close(nxSocket); nxSocket = -1; return -1; } setbuf(stdout, NULL); setbuf(stderr, NULL); /* * 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_FIXstatic voidmySignalhandler(int sig){ if (sig == SIGALRM) { EPRINTF("Oops! nxFlushReq() timed out, exiting\n"); exit(127); }}#endif/* Vladimir Cotfas: hang in GrFlush() --> nxFlushReq(0L,1); *//** * Close the graphics device. Flushes any waiting messages. * * @ingroup nanox_general */void GrClose(void){ ACCESS_PER_THREAD_DATA()#if GR_CLOSE_FIX /* allow 1 second to flush*/ void * oldSignalHandler = signal(SIGALRM, mySignalhandler); alarm(1);#endif LOCK(&nxGlobalLock); AllocReq(Close); GrFlush(); UNLOCK(&nxGlobalLock);#if GR_CLOSE_FIX alarm(0); signal(SIGALRM, oldSignalHandler);#endif close(nxSocket); nxSocket = -1; LOCK_FREE(&nxGlobalLock);}/** * Flush the message buffer of any messages it may contain. * * @ingroup nanox_general */void GrFlush(void){ nxFlushReq(0L,1); #ifdef NOTUSED GR_EVENT event; /* And stick any incoming events on the local queue */ while (_GrPeekEvent(&event)) { _GrGetNextEventTimeout(&event, 0L); QueueEvent(&event); }#endif}/** * The default error handler. This 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 describing what error * occurred and what function it occured in, then exits. * * @param ep The error event structure. * * @ingroup nanox_general */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); }}/** * 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. * * @param fncb the function to call to handle error events * @return the address of the previous error handler * * @ingroup nanox_general */GR_FNCALLBACKEVENTGrSetErrorHandler(GR_FNCALLBACKEVENT fncb){ GR_FNCALLBACKEVENT orig; ACCESS_PER_THREAD_DATA() LOCK(&nxGlobalLock); orig = ErrorFunc; ErrorFunc = fncb; UNLOCK(&nxGlobalLock); return orig;}/** * This function suspends execution of the program for the specified * number of milliseconds. * * @param msecs Number of milliseconds to delay. * * @ingroup nanox_timer
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -