⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 client.c

📁 开放源码实时操作系统源码.
💻 C
📖 第 1 页 / 共 5 页
字号:
/*
 * 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_len = sizeof(name);
	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.
 */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -