📄 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_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 + -