Example of cooperative rendering into one window by two processes
This commit is contained in:
+11
-1
@@ -8,7 +8,9 @@ INCDIR = $(TOP)/include
|
||||
|
||||
LIB_DEP = $(TOP)/$(LIB_DIR)/$(GL_LIB_NAME) $(TOP)/$(LIB_DIR)/$(GLU_LIB_NAME)
|
||||
|
||||
PROGS = glthreads \
|
||||
PROGS = \
|
||||
corender \
|
||||
glthreads \
|
||||
glxdemo \
|
||||
glxgears \
|
||||
glxgears_fbconfig \
|
||||
@@ -83,3 +85,11 @@ xuserotfont.o: xuserotfont.c xuserotfont.h
|
||||
xrotfontdemo.o: xrotfontdemo.c xuserotfont.h
|
||||
$(CC) -c -I. -I$(INCDIR) $(X11_INCLUDES) $(CFLAGS) xrotfontdemo.c
|
||||
|
||||
corender: corender.o ipc.o
|
||||
$(CC) $(CFLAGS) corender.o ipc.o $(APP_LIB_DEPS) -o $@
|
||||
|
||||
corender.o: corender.c ipc.h
|
||||
$(CC) -c -I. -I$(INCDIR) $(X11_INCLUDES) $(CFLAGS) corender.c
|
||||
|
||||
ipc.o: ipc.c ipc.h
|
||||
$(CC) -c -I. -I$(INCDIR) $(X11_INCLUDES) $(CFLAGS) ipc.c
|
||||
@@ -0,0 +1,396 @@
|
||||
/**
|
||||
* Example of cooperative rendering into one window by two processes.
|
||||
* The first instance of the program creates the GLX window.
|
||||
* The second instance of the program gets the window ID from the first
|
||||
* and draws into it.
|
||||
* Socket IPC is used for synchronization.
|
||||
*
|
||||
* Usage:
|
||||
* 1. run 'corender &'
|
||||
* 2. run 'corender 2' (any arg will do)
|
||||
*
|
||||
* Brian Paul
|
||||
* 11 Oct 2007
|
||||
*/
|
||||
|
||||
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glx.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <X11/keysym.h>
|
||||
#include <unistd.h>
|
||||
#include "ipc.h"
|
||||
|
||||
|
||||
static int MyID = 0; /* 0 or 1 */
|
||||
static int WindowID = 0;
|
||||
static GLXContext Context = 0;
|
||||
static int Width = 700, Height = 350;
|
||||
static int Rot = 0;
|
||||
static int Sock = 0;
|
||||
|
||||
static GLfloat Red[4] = {1.0, 0.2, 0.2, 1.0};
|
||||
static GLfloat Blue[4] = {0.2, 0.2, 1.0, 1.0};
|
||||
|
||||
static int Sync = 1; /** synchronized rendering? */
|
||||
|
||||
|
||||
static void
|
||||
setup_ipc(void)
|
||||
{
|
||||
int k, port = 10001;
|
||||
|
||||
if (MyID == 0) {
|
||||
/* I'm the first one, wait for connection from second */
|
||||
k = CreatePort(&port);
|
||||
assert(k != -1);
|
||||
|
||||
printf("Waiting for connection from another 'corender'\n");
|
||||
Sock = AcceptConnection(k);
|
||||
|
||||
printf("Got connection, sending windowID\n");
|
||||
|
||||
/* send windowID */
|
||||
SendData(Sock, &WindowID, sizeof(WindowID));
|
||||
}
|
||||
else {
|
||||
/* I'm the second one, connect to first */
|
||||
char hostname[1000];
|
||||
|
||||
MyHostName(hostname, 1000);
|
||||
Sock = Connect(hostname, port);
|
||||
assert(Sock != -1);
|
||||
|
||||
/* get windowID */
|
||||
ReceiveData(Sock, &WindowID, sizeof(WindowID));
|
||||
printf("Contacted first 'corender', getting WindowID\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** from GLUT */
|
||||
static void
|
||||
doughnut(GLfloat r, GLfloat R, GLint nsides, GLint rings)
|
||||
{
|
||||
int i, j;
|
||||
GLfloat theta, phi, theta1;
|
||||
GLfloat cosTheta, sinTheta;
|
||||
GLfloat cosTheta1, sinTheta1;
|
||||
GLfloat ringDelta, sideDelta;
|
||||
|
||||
ringDelta = 2.0 * M_PI / rings;
|
||||
sideDelta = 2.0 * M_PI / nsides;
|
||||
|
||||
theta = 0.0;
|
||||
cosTheta = 1.0;
|
||||
sinTheta = 0.0;
|
||||
for (i = rings - 1; i >= 0; i--) {
|
||||
theta1 = theta + ringDelta;
|
||||
cosTheta1 = cos(theta1);
|
||||
sinTheta1 = sin(theta1);
|
||||
glBegin(GL_QUAD_STRIP);
|
||||
phi = 0.0;
|
||||
for (j = nsides; j >= 0; j--) {
|
||||
GLfloat cosPhi, sinPhi, dist;
|
||||
|
||||
phi += sideDelta;
|
||||
cosPhi = cos(phi);
|
||||
sinPhi = sin(phi);
|
||||
dist = R + r * cosPhi;
|
||||
|
||||
glNormal3f(cosTheta1 * cosPhi, -sinTheta1 * cosPhi, sinPhi);
|
||||
glVertex3f(cosTheta1 * dist, -sinTheta1 * dist, r * sinPhi);
|
||||
glNormal3f(cosTheta * cosPhi, -sinTheta * cosPhi, sinPhi);
|
||||
glVertex3f(cosTheta * dist, -sinTheta * dist, r * sinPhi);
|
||||
}
|
||||
glEnd();
|
||||
theta = theta1;
|
||||
cosTheta = cosTheta1;
|
||||
sinTheta = sinTheta1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
redraw(Display *dpy)
|
||||
{
|
||||
int dbg = 0;
|
||||
|
||||
glXMakeCurrent(dpy, WindowID, Context);
|
||||
glEnable(GL_LIGHTING);
|
||||
glEnable(GL_LIGHT0);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glClearColor(0.5, 0.5, 0.5, 0.0);
|
||||
|
||||
if (MyID == 0) {
|
||||
/* First process */
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(-1, 0, 0);
|
||||
glRotatef(Rot, 1, 0, 0);
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Red);
|
||||
doughnut(0.5, 2.0, 20, 30);
|
||||
glPopMatrix();
|
||||
|
||||
glFinish();
|
||||
if (!Sync) {
|
||||
usleep(1000*10);
|
||||
}
|
||||
|
||||
/* signal second process to render */
|
||||
if (Sync) {
|
||||
int code = 1;
|
||||
if (dbg) printf("0: send signal\n");
|
||||
SendData(Sock, &code, sizeof(code));
|
||||
SendData(Sock, &Rot, sizeof(Rot));
|
||||
}
|
||||
|
||||
/* wait for second process to finish rendering */
|
||||
if (Sync) {
|
||||
int code = 0;
|
||||
if (dbg) printf("0: wait signal\n");
|
||||
ReceiveData(Sock, &code, sizeof(code));
|
||||
if (dbg) printf("0: got signal\n");
|
||||
assert(code == 2);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
/* Second process */
|
||||
|
||||
/* wait for first process's signal for me to render */
|
||||
if (Sync) {
|
||||
int code = 0;
|
||||
if (dbg) printf("1: wait signal\n");
|
||||
ReceiveData(Sock, &code, sizeof(code));
|
||||
ReceiveData(Sock, &Rot, sizeof(Rot));
|
||||
|
||||
if (dbg) printf("1: got signal\n");
|
||||
assert(code == 1);
|
||||
}
|
||||
|
||||
/* XXX this clear should not be here, but for some reason, it
|
||||
* makes things _mostly_ work correctly w/ NVIDIA's driver.
|
||||
* There's only occasional glitches.
|
||||
* Without this glClear(), depth buffer for the second process
|
||||
* is pretty much broken.
|
||||
*/
|
||||
//glClear(GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(1, 0, 0);
|
||||
glRotatef(Rot + 90 , 1, 0, 0);
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Blue);
|
||||
doughnut(0.5, 2.0, 20, 30);
|
||||
glPopMatrix();
|
||||
glFinish();
|
||||
|
||||
glXSwapBuffers(dpy, WindowID);
|
||||
usleep(1000*10);
|
||||
|
||||
/* signal first process that I'm done rendering */
|
||||
if (Sync) {
|
||||
int code = 2;
|
||||
if (dbg) printf("1: send signal\n");
|
||||
SendData(Sock, &code, sizeof(code));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
resize(Display *dpy, int width, int height)
|
||||
{
|
||||
float ar = (float) width / height;
|
||||
|
||||
glXMakeCurrent(dpy, WindowID, Context);
|
||||
|
||||
glViewport(0, 0, width, height);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glFrustum(-ar, ar, 1.0, -1.0, 5.0, 200.0);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glTranslatef(0, 0, -15);
|
||||
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
set_window_title(Display *dpy, Window win, const char *title)
|
||||
{
|
||||
XSizeHints sizehints;
|
||||
sizehints.flags = 0;
|
||||
XSetStandardProperties(dpy, win, title, title,
|
||||
None, (char **)NULL, 0, &sizehints);
|
||||
}
|
||||
|
||||
|
||||
static Window
|
||||
make_gl_window(Display *dpy, XVisualInfo *visinfo, int width, int height)
|
||||
{
|
||||
int scrnum;
|
||||
XSetWindowAttributes attr;
|
||||
unsigned long mask;
|
||||
Window root;
|
||||
Window win;
|
||||
int x = 0, y = 0;
|
||||
char *name = NULL;
|
||||
|
||||
scrnum = DefaultScreen( dpy );
|
||||
root = RootWindow( dpy, scrnum );
|
||||
|
||||
/* window attributes */
|
||||
attr.background_pixel = 0;
|
||||
attr.border_pixel = 0;
|
||||
attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone);
|
||||
attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
|
||||
mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
|
||||
|
||||
win = XCreateWindow( dpy, root, x, y, width, height,
|
||||
0, visinfo->depth, InputOutput,
|
||||
visinfo->visual, mask, &attr );
|
||||
|
||||
/* set hints and properties */
|
||||
{
|
||||
XSizeHints sizehints;
|
||||
sizehints.x = x;
|
||||
sizehints.y = y;
|
||||
sizehints.width = width;
|
||||
sizehints.height = height;
|
||||
sizehints.flags = USSize | USPosition;
|
||||
XSetNormalHints(dpy, win, &sizehints);
|
||||
XSetStandardProperties(dpy, win, name, name,
|
||||
None, (char **)NULL, 0, &sizehints);
|
||||
}
|
||||
|
||||
return win;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
set_event_mask(Display *dpy, Window win)
|
||||
{
|
||||
XSetWindowAttributes attr;
|
||||
attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
|
||||
XChangeWindowAttributes(dpy, win, CWEventMask, &attr);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
event_loop(Display *dpy)
|
||||
{
|
||||
while (1) {
|
||||
while (XPending(dpy) > 0) {
|
||||
XEvent event;
|
||||
XNextEvent(dpy, &event);
|
||||
|
||||
switch (event.type) {
|
||||
case Expose:
|
||||
redraw(dpy);
|
||||
break;
|
||||
case ConfigureNotify:
|
||||
resize(dpy, event.xconfigure.width, event.xconfigure.height);
|
||||
break;
|
||||
case KeyPress:
|
||||
{
|
||||
char buffer[10];
|
||||
int r, code;
|
||||
code = XLookupKeysym(&event.xkey, 0);
|
||||
if (code == XK_Left) {
|
||||
}
|
||||
else {
|
||||
r = XLookupString(&event.xkey, buffer, sizeof(buffer),
|
||||
NULL, NULL);
|
||||
if (buffer[0] == 27) {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
/* nothing */
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
if (MyID == 0 || !Sync)
|
||||
Rot += 1;
|
||||
redraw(dpy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static XVisualInfo *
|
||||
choose_visual(Display *dpy)
|
||||
{
|
||||
int attribs[] = { GLX_RGBA,
|
||||
GLX_RED_SIZE, 1,
|
||||
GLX_GREEN_SIZE, 1,
|
||||
GLX_BLUE_SIZE, 1,
|
||||
GLX_DOUBLEBUFFER,
|
||||
GLX_DEPTH_SIZE, 1,
|
||||
None };
|
||||
int scrnum = DefaultScreen( dpy );
|
||||
return glXChooseVisual(dpy, scrnum, attribs);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
parse_opts(int argc, char *argv[])
|
||||
{
|
||||
if (argc > 1) {
|
||||
MyID = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main( int argc, char *argv[] )
|
||||
{
|
||||
Display *dpy;
|
||||
XVisualInfo *visinfo;
|
||||
|
||||
parse_opts(argc, argv);
|
||||
|
||||
dpy = XOpenDisplay(NULL);
|
||||
|
||||
visinfo = choose_visual(dpy);
|
||||
|
||||
Context = glXCreateContext( dpy, visinfo, NULL, True );
|
||||
if (!Context) {
|
||||
printf("Error: glXCreateContext failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (MyID == 0) {
|
||||
WindowID = make_gl_window(dpy, visinfo, Width, Height);
|
||||
set_window_title(dpy, WindowID, "corender");
|
||||
XMapWindow(dpy, WindowID);
|
||||
/*printf("WindowID 0x%x\n", (int) WindowID);*/
|
||||
}
|
||||
|
||||
/* do ipc hand-shake here */
|
||||
setup_ipc();
|
||||
assert(Sock);
|
||||
assert(WindowID);
|
||||
|
||||
if (MyID == 1) {
|
||||
set_event_mask(dpy, WindowID);
|
||||
}
|
||||
|
||||
resize(dpy, Width, Height);
|
||||
|
||||
event_loop(dpy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
/* Copyright (c) 2003 Tungsten Graphics, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files ("the
|
||||
* Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions: The above copyright notice, the Tungsten
|
||||
* Graphics splash screen, and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software. THE SOFTWARE
|
||||
* IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
* SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Simple IPC API
|
||||
* Brian Paul
|
||||
*/
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include "ipc.h"
|
||||
|
||||
#if defined(IRIX) || defined(irix)
|
||||
typedef int socklen_t;
|
||||
#endif
|
||||
|
||||
#define NO_DELAY 1
|
||||
|
||||
#define DEFAULT_MASTER_PORT 7011
|
||||
|
||||
|
||||
/*
|
||||
* Return my hostname in <nameOut>.
|
||||
* Return 1 for success, 0 for error.
|
||||
*/
|
||||
int
|
||||
MyHostName(char *nameOut, int maxNameLength)
|
||||
{
|
||||
int k = gethostname(nameOut, maxNameLength);
|
||||
return k==0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a socket attached to a port. Later, we can call AcceptConnection
|
||||
* on the socket returned from this function.
|
||||
* Return the new socket number or -1 if error.
|
||||
*/
|
||||
int
|
||||
CreatePort(int *port)
|
||||
{
|
||||
char hostname[1000];
|
||||
struct sockaddr_in servaddr;
|
||||
struct hostent *hp;
|
||||
int so_reuseaddr = 1;
|
||||
int tcp_nodelay = 1;
|
||||
int sock, k;
|
||||
|
||||
/* create socket */
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
assert(sock > 2);
|
||||
|
||||
/* get my host name */
|
||||
k = gethostname(hostname, 1000);
|
||||
assert(k == 0);
|
||||
|
||||
/* get hostent info */
|
||||
hp = gethostbyname(hostname);
|
||||
assert(hp);
|
||||
|
||||
/* initialize the servaddr struct */
|
||||
memset(&servaddr, 0, sizeof(servaddr) );
|
||||
servaddr.sin_family = AF_INET;
|
||||
servaddr.sin_port = htons((unsigned short) (*port));
|
||||
memcpy((char *) &servaddr.sin_addr, hp->h_addr,
|
||||
sizeof(servaddr.sin_addr));
|
||||
|
||||
/* deallocate when we exit */
|
||||
k = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
|
||||
(char *) &so_reuseaddr, sizeof(so_reuseaddr));
|
||||
assert(k==0);
|
||||
|
||||
/* send packets immediately */
|
||||
#if NO_DELAY
|
||||
k = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
|
||||
(char *) &tcp_nodelay, sizeof(tcp_nodelay));
|
||||
assert(k==0);
|
||||
#endif
|
||||
|
||||
if (*port == 0)
|
||||
*port = DEFAULT_MASTER_PORT;
|
||||
|
||||
k = 1;
|
||||
while (k && (*port < 65534)) {
|
||||
/* bind our address to the socket */
|
||||
servaddr.sin_port = htons((unsigned short) (*port));
|
||||
k = bind(sock, (struct sockaddr *) &servaddr, sizeof(servaddr));
|
||||
if (k)
|
||||
*port = *port + 1;
|
||||
}
|
||||
|
||||
#if 0
|
||||
printf("###### Real Port: %d\n", *port);
|
||||
#endif
|
||||
|
||||
/* listen for connections */
|
||||
k = listen(sock, 100);
|
||||
assert(k == 0);
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Accept a connection on the named socket.
|
||||
* Return a new socket for the new connection, or -1 if error.
|
||||
*/
|
||||
int
|
||||
AcceptConnection(int socket)
|
||||
{
|
||||
struct sockaddr addr;
|
||||
socklen_t addrLen;
|
||||
int newSock;
|
||||
|
||||
addrLen = sizeof(addr);
|
||||
newSock = accept(socket, &addr, &addrLen);
|
||||
if (newSock == 1)
|
||||
return -1;
|
||||
else
|
||||
return newSock;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Contact the server running on the given host on the named port.
|
||||
* Return socket number or -1 if error.
|
||||
*/
|
||||
int
|
||||
Connect(const char *hostname, int port)
|
||||
{
|
||||
struct sockaddr_in servaddr;
|
||||
struct hostent *hp;
|
||||
int sock, k;
|
||||
int tcp_nodelay = 1;
|
||||
|
||||
assert(port);
|
||||
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
assert(sock >= 0);
|
||||
|
||||
hp = gethostbyname(hostname);
|
||||
assert(hp);
|
||||
|
||||
memset(&servaddr, 0, sizeof(servaddr));
|
||||
servaddr.sin_family = AF_INET;
|
||||
servaddr.sin_port = htons((unsigned short) port);
|
||||
memcpy((char *) &servaddr.sin_addr, hp->h_addr, sizeof(servaddr.sin_addr));
|
||||
|
||||
k = connect(sock, (struct sockaddr *) &servaddr, sizeof(servaddr));
|
||||
if (k != 0) {
|
||||
perror("Connect:");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if NO_DELAY
|
||||
/* send packets immediately */
|
||||
k = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
|
||||
(char *) &tcp_nodelay, sizeof(tcp_nodelay));
|
||||
assert(k==0);
|
||||
#endif
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CloseSocket(int socket)
|
||||
{
|
||||
close(socket);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
SendData(int socket, const void *data, int bytes)
|
||||
{
|
||||
int sent = 0;
|
||||
int b;
|
||||
|
||||
while (sent < bytes) {
|
||||
b = write(socket, (char *) data + sent, bytes - sent);
|
||||
if (b <= 0)
|
||||
return -1; /* something broke */
|
||||
sent += b;
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ReceiveData(int socket, void *data, int bytes)
|
||||
{
|
||||
int received = 0, b;
|
||||
|
||||
while (received < bytes) {
|
||||
b = read(socket, (char *) data + received, bytes - received);
|
||||
if (b <= 0)
|
||||
return -1;
|
||||
received += b;
|
||||
}
|
||||
return received;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
SendString(int socket, const char *str)
|
||||
{
|
||||
const int len = strlen(str);
|
||||
int sent, b;
|
||||
|
||||
/* first, send a 4-byte length indicator */
|
||||
b = write(socket, &len, sizeof(len));
|
||||
if (b <= 0)
|
||||
return -1;
|
||||
|
||||
sent = SendData(socket, str, len);
|
||||
assert(sent == len);
|
||||
return sent;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ReceiveString(int socket, char *str, int maxLen)
|
||||
{
|
||||
int len, received, b;
|
||||
|
||||
/* first, read 4 bytes to see how long of string to receive */
|
||||
b = read(socket, &len, sizeof(len));
|
||||
if (b <= 0)
|
||||
return -1;
|
||||
|
||||
assert(len <= maxLen); /* XXX fix someday */
|
||||
assert(len >= 0);
|
||||
received = ReceiveData(socket, str, len);
|
||||
assert(received != -1);
|
||||
assert(received == len);
|
||||
str[len] = 0;
|
||||
return received;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef IPC_H
|
||||
#define IPC_H
|
||||
|
||||
|
||||
extern int MyHostName(char *nameOut, int maxNameLength);
|
||||
extern int CreatePort(int *port);
|
||||
extern int AcceptConnection(int socket);
|
||||
extern int Connect(const char *hostname, int port);
|
||||
extern void CloseSocket(int socket);
|
||||
extern int SendData(int socket, const void *data, int bytes);
|
||||
extern int ReceiveData(int socket, void *data, int bytes);
|
||||
extern int SendString(int socket, const char *str);
|
||||
extern int ReceiveString(int socket, char *str, int maxLen);
|
||||
|
||||
|
||||
#endif /* IPC_H */
|
||||
Reference in New Issue
Block a user