| /** |
| * 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" |
| |
| |
| #ifndef M_PI |
| #define M_PI 3.14159265358979323846 |
| #endif |
| |
| 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; |
| } |