source: trunk/packages/vizservers/pymolproxy/pymolproxy.c @ 1999

Last change on this file since 1999 was 1999, checked in by gah, 14 years ago

Fixed race condition reading pdb file.

File size: 57.7 KB
Line 
1
2/*
3 * ----------------------------------------------------------------------
4 * proxypymol.c
5 *
6 *      This module creates the Tcl interface to the pymol server.  It acts as
7 *      a go-between establishing communication between a molvisviewer widget
8 *      and the pymol server. The communication protocol from the molvisviewer
9 *      widget is the Tcl language.  Commands are then relayed to the pymol
10 *      server.  Responses from the pymol server are translated into Tcl
11 *      commands and send to the molvisviewer widget. For example, resulting
12 *      image rendered offscreen is returned as BMP-formatted image data.
13 *
14 *  Copyright (c) 2004-2006  Purdue Research Foundation
15 *
16 *  See the file "license.terms" for information on usage and
17 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
18 * ======================================================================
19 */
20
21/*
22 * Notes:   The proxy should not maintain any state information from the
23 *          client, other that what it needs for event (rotate, pan, zoom,
24 *          atom scale, bond thickness, etc.) compression.  This is because
25 *          the connection is periodically broken (timeout, error, etc.).
26 *          It's the responsibility of the client (molvisviewer) to restore
27 *          the settings of the previous view.  The proxy is just a relay
28 *          between the client and the pymol server.
29 */
30/*
31 *   +--------------+        +------------+         +--------+
32 *   |              | output |            |  input  |        |
33 *   |              |------->|            |-------->|        |
34 *   | molvisviewer |        | pymolproxy |  output | pymol  |
35 *   |    client    |        |            |<--------| server |
36 *   |              | input  |            |  errors |        |
37 *   |              |<-------|            |<--------|        |
38 *   +--------------+        +------------+         +--------+
39 *
40 * The communication between the pymol server and proxy is completely
41 * synchronous.  The proxy relays/translates commands from the client to the
42 * server and then wait for the responses.  It also watches the server's
43 * stdout and stderr changes so that extraneous writes don't block the
44 * the server. (I don't know exactly what those responses are)
45 *
46 * The communication between the client and the proxy is different.  The
47 * client commands are read when they become available on the socket.  Proxy
48 * writes to the client (image output) are non-blocking so that we don't
49 * deadlock with the client.  The client may be busy sending the next command
50 * when we want to write the resulting image from the last command.
51 *
52 */
53
54#include <assert.h>
55#include <ctype.h>
56#include <errno.h>
57#include <fcntl.h>
58#include <getopt.h>
59#include <poll.h>
60#include <stdio.h>
61#include <stdlib.h>
62#include <string.h>
63#include <sys/stat.h>
64#include <sys/time.h>
65#include <sys/times.h>
66#include <sys/types.h>
67#include <sys/wait.h>
68#include <time.h>
69#include <unistd.h>
70#include <tcl.h>
71
72#undef INLINE
73#ifdef __GNUC__
74#  define INLINE __inline__
75#else
76#  define INLINE
77#endif
78
79#define FALSE 0
80#define TRUE  1
81
82#define IO_TIMEOUT (30000)
83#define KEEPSTATS       1
84#define CVT2SECS(x)  ((double)(x).tv_sec) + ((double)(x).tv_usec * 1.0e-6)
85
86typedef struct {
87    pid_t child;                /* Child process. */
88    size_t nFrames;             /* # of frames sent to client. */
89    size_t nBytes;              /* # of bytes for all frames. */
90    size_t nCommands;           /* # of commands executed */
91    double cmdTime;             /* Elapsed time spend executing commands. */
92    struct timeval start;       /* Start of elapsed time. */
93} Stats;
94
95static Stats stats;
96
97static FILE *flog;
98static int debug = TRUE;
99static FILE *scriptFile;
100static int savescript = FALSE;
101
102typedef struct Image {
103    struct Image *nextPtr;      /* Next image in chain of images. The list is
104                                 * ordered by the most recently received image
105                                 * from the pymol server to the least. */
106    struct Image *prevPtr;      /* Previous image in chain of images. The list
107                                 * is ordered by the most recently received
108                                 * image from the pymol server to the
109                                 * least. */
110    ssize_t nWritten;           /* Number of bytes of image data already
111                                 * delivered.*/
112    size_t bytesLeft;           /* Number of bytes of image data left to
113                                 * delivered to the client. */
114    char data[1];               /* Start of image data. We allocate the size
115                                 * of the Image structure plus the size of the
116                                 * image data. */
117} Image;
118
119#define BUFFER_SIZE             4096
120
121typedef struct {
122    char bytes[BUFFER_SIZE];
123    int fill;
124    int mark;
125    int fd;
126} ReadBuffer;
127
128#define BUFFER_OK                0
129#define BUFFER_ERROR            -1
130#define BUFFER_CONTINUE         -2
131#define BUFFER_SHORT_READ       -3
132
133#define FORCE_UPDATE            (1<<0)
134#define CAN_UPDATE              (1<<1)
135#define INVALIDATE_CACHE        (1<<3)
136#define ATOM_SCALE_PENDING      (1<<4)
137#define STICK_RADIUS_PENDING    (1<<5)
138#define ROTATE_PENDING          (1<<6)
139#define PAN_PENDING             (1<<7)
140#define ZOOM_PENDING            (1<<8)
141#define UPDATE_PENDING          (1<<9)
142#define VIEWPORT_PENDING        (1<<10)
143
144typedef struct {
145    Tcl_Interp *interp;
146    unsigned int flags;                 /* Various flags. */
147    Image *headPtr, *tailPtr;           /* List of images to be delivered to
148                                         * the client.  The most recent images
149                                         * are in the front of the list. */
150
151    int sin, sout, serr;                /* Server file descriptors. */
152    int cin, cout;                      /* Client file descriptors. */
153    ReadBuffer client;                  /* Read buffer for client input. */
154    ReadBuffer server;                  /* Read buffer for server output. */
155    int frame;
156    int rockOffset;
157    int cacheId;
158    int error;
159    int status;
160    int width, height;                  /* Size of viewport. */
161    float xAngle, yAngle, zAngle;       /* Euler angles of pending
162                                         * rotation.  */
163    float sphereScale;                  /* Atom scale of pending re-scale. */
164    float stickRadius;                  /* Bond thickness of pending
165                                         * re-scale. */
166    float zoom;
167    float xPan, yPan;
168} PymolProxy;
169
170
171static void PollForEvents(PymolProxy *proxyPtr);
172static void
173trace TCL_VARARGS_DEF(const char *, arg1)
174{
175    if (debug) {
176        const char *format;
177        va_list args;
178
179        format = TCL_VARARGS_START(const char *, arg1, args);
180        fprintf(flog, "pymolproxy: ");
181        vfprintf(flog, format, args);
182        fprintf(flog, "\n");
183        fflush(flog);
184    }
185}
186
187static void
188script TCL_VARARGS_DEF(const char *, arg1)
189{
190    if (savescript) {
191        const char *format;
192        va_list args;
193
194        format = TCL_VARARGS_START(const char *, arg1, args);
195        vfprintf(scriptFile, format, args);
196        fflush(scriptFile);
197    }
198}
199
200static void
201InitBuffer(ReadBuffer *readPtr, int f)
202{
203    readPtr->fd = f;
204    readPtr->fill = 0;
205    readPtr->mark = 0;
206}
207
208static void
209FlushBuffer(ReadBuffer *readPtr)
210{
211    readPtr->fill = 0;
212    readPtr->mark = 0;
213}
214
215static int
216FillBuffer(ReadBuffer *readPtr)
217{
218    size_t bytesLeft;
219    ssize_t nRead;
220
221#ifdef notdef
222    trace("Entering FillBuffer (mark=%d, fill=%d)\n", readPtr->mark,
223          readPtr->fill);
224#endif
225    if (readPtr->mark >= readPtr->fill) {
226        readPtr->mark = readPtr->fill = 0;
227    }
228    if (readPtr->mark > 0) {
229        size_t i, j;
230
231        for (i = 0, j = readPtr->mark; j < readPtr->fill; i++, j++) {
232            readPtr->bytes[i] = readPtr->bytes[j];
233        }
234        readPtr->mark = 0;
235        readPtr->fill = i;
236    }
237    bytesLeft = BUFFER_SIZE - readPtr->fill - 1;
238    nRead = read(readPtr->fd, readPtr->bytes + readPtr->fill, bytesLeft);
239    if (nRead == 0) {
240        return BUFFER_ERROR;
241    }
242    if (nRead <= 0) {
243        if (errno != EAGAIN) {
244#ifdef notdef
245            trace("in FillBuffer: read failed %d: %s", errno, strerror(errno));
246            trace("Leaving FillBuffer FAIL(read %d bytes) mark=%d, fill=%d\n",
247                  nRead, readPtr->mark, readPtr->fill);
248#endif
249            return BUFFER_ERROR;
250        }
251        return BUFFER_SHORT_READ;
252    }
253    readPtr->fill += nRead;
254#ifdef notdef
255    trace("Leaving FillBuffer (read %d bytes) mark=%d, fill=%d\n",
256          nRead, readPtr->mark, readPtr->fill);
257#endif
258    return (nRead == bytesLeft) ? BUFFER_OK : BUFFER_SHORT_READ;
259}
260
261static char *
262GetLine(ReadBuffer *readPtr, int *nBytesPtr)
263{
264    int i;
265    int status;
266
267#ifdef notdef
268    trace("Entering GetLine (mark=%d, fill=%d)\n",readPtr->mark, readPtr->fill);
269#endif
270    status = BUFFER_OK;
271    for (;;) {
272        /* Look for the next newline (the next full line). */
273#ifdef notdef
274        trace("in GetLine: mark=%d fill=%d\n", readPtr->mark, readPtr->fill);
275#endif
276        for (i = readPtr->mark; i < readPtr->fill; i++) {
277            if (readPtr->bytes[i] == '\n') {
278                char *p;
279               
280                /* Save the start of the line. */
281                p = readPtr->bytes + readPtr->mark;
282                i++;
283                *nBytesPtr = i - readPtr->mark;
284                readPtr->mark = i;
285#ifdef notdef
286                trace("Leaving GetLine(%.*s)\n", *nBytesPtr, p);
287#endif
288                return p;
289            }
290        }
291        /* Couldn't find a newline, so it may be that we need to read some
292         * more. Check first that last read wasn't a short read. */
293        if (status == BUFFER_SHORT_READ) {
294            break;
295        }
296        status = FillBuffer(readPtr);
297        if (status == BUFFER_ERROR) {
298            *nBytesPtr = BUFFER_ERROR;
299            return NULL;        /* EOF or error on read. */
300        }
301    }
302#ifdef notdef
303    trace("Leaving GetLine failed to read line\n");
304#endif
305    *nBytesPtr = BUFFER_CONTINUE;
306    return NULL;
307}
308
309static int
310GetBytes(ReadBuffer *readPtr, char *out, int nBytes)
311{
312#ifdef notdef
313    trace("Entering GetBytes(%d)\n", nBytes);
314#endif
315    while (nBytes > 0) {
316        int bytesLeft;
317        int status;
318
319        bytesLeft = readPtr->fill - readPtr->mark;
320        if (bytesLeft > 0) {
321            int size;
322
323            size = (bytesLeft >  nBytes) ? nBytes : bytesLeft;
324            memcpy(out, readPtr->bytes + readPtr->mark, size);
325            readPtr->mark += size;
326            nBytes -= size;
327            out += size;
328        }
329        if (nBytes == 0) {
330            /* Received requested # bytes. */
331#ifdef notdef
332            trace("Leaving GetBytes(%d)\n", nBytes);
333#endif
334            return BUFFER_OK;
335        }
336        /* Didn't get enough bytes, need to read some more. */
337        status = FillBuffer(readPtr);
338        if (status == BUFFER_ERROR) {
339            return BUFFER_ERROR;
340        }
341#ifdef notdef
342        trace("in GetBytes: mark=%d fill=%d\n", readPtr->mark, readPtr->fill);
343#endif
344    }
345#ifdef notdef
346    trace("Leaving GetBytes(%d)\n", nBytes);
347#endif
348    return BUFFER_OK;
349}
350
351INLINE static void
352clear_error(PymolProxy *proxyPtr)
353{
354    proxyPtr->error = 0;
355    proxyPtr->status = TCL_OK;
356}
357
358static int
359CreateTmpDir(Tcl_Interp *interp)
360{
361    const char script[] = {
362        "set path \"/tmp/pymol[pid]\"\n"
363        "if { [file exists $path] } {\n"
364        "    file delete -force $path\n"
365        "}\n"
366        "file mkdir $path\n"
367    };
368    return Tcl_GlobalEval(interp, script);
369}
370
371static int
372DestroyTmpDir()
373{
374    char cmd[BUFSIZ];
375
376    sprintf(cmd, "/bin/rm -rf /tmp/pymol%d", getpid());
377    system(cmd);
378}
379
380static int
381Expect(PymolProxy *proxyPtr, char *match, char *out, int maxSize)
382{
383    char c;
384    size_t length;
385
386    if (proxyPtr->status != TCL_OK) {
387        return proxyPtr->status;
388    }
389#ifndef notdef
390    trace("Entering Expect(want=\"%s\", maxSize=%d)\n", match, maxSize);
391#endif
392    c = match[0];
393    length = strlen(match);
394    for (;;) {
395        int nBytes;
396        char *line;
397
398        line = GetLine(&proxyPtr->server, &nBytes);
399        if (line != NULL) {
400#ifndef notdef
401            trace("pymol says (read %d bytes):%.*s", nBytes, nBytes, line);
402#endif
403            if ((c == line[0]) && (strncmp(line, match, length) == 0)) {
404                if (maxSize < nBytes) {
405                    nBytes = maxSize;
406                }
407                memcpy(out, line, nBytes);
408                clear_error(proxyPtr);
409#ifndef notdef
410                trace("Leaving Expect: got (%.*s)\n", nBytes, out);
411#endif
412                return BUFFER_OK;
413            }
414            continue;
415        }
416        if (nBytes != BUFFER_CONTINUE) {
417            return BUFFER_ERROR;
418        }
419    }
420    trace("Leaving Expect: failed to find (%s)\n", match);
421    proxyPtr->error = 2;
422    proxyPtr->status = TCL_ERROR;
423    return BUFFER_ERROR;
424}
425
426#if KEEPSTATS
427
428static int
429WriteStats(const char *who, int code)
430{
431    double start, finish;
432    pid_t pid;
433    char buf[BUFSIZ];
434    Tcl_DString ds;
435
436    {
437        struct timeval tv;
438
439        /* Get ending time.  */
440        gettimeofday(&tv, NULL);
441        finish = CVT2SECS(tv);
442        tv = stats.start;
443        start = CVT2SECS(tv);
444    }
445    /*
446     * Session information:
447     *   1. Start date of session in seconds.
448     *   2. Process ID
449     *   3. Number of frames returned.
450     *   4. Number of bytes total returned (in frames).
451     *   5. Total elapsed time of all commands.
452     *   6. Total elapsed time of session.
453     *   7. Exit code of pymol server.
454     *   8. User time. 
455     *   9. System time.
456     *  10. Maximum resident size.
457     */
458    pid = getpid();
459    Tcl_DStringInit(&ds);
460   
461    sprintf(buf, "<session server=\"%s\" ", who);
462    Tcl_DStringAppend(&ds, buf, -1);
463
464    strcpy(buf, ctime(&stats.start.tv_sec));
465
466    buf[strlen(buf) - 1] = '\0';
467    Tcl_DStringAppend(&ds, "date=\"", -1);
468    Tcl_DStringAppend(&ds, buf, -1);
469    Tcl_DStringAppend(&ds, "\" ", -1);
470
471    sprintf(buf, "date_secs=\"%ld\" ", stats.start.tv_sec);
472    Tcl_DStringAppend(&ds, buf, -1);
473
474    sprintf(buf, "pid=\"%d\" ", pid);
475    Tcl_DStringAppend(&ds, buf, -1);
476    sprintf(buf, "num_frames=\"%lu\" ", (unsigned long int)stats.nFrames);
477    Tcl_DStringAppend(&ds, buf, -1);
478    sprintf(buf, "frame_bytes=\"%lu\" ", (unsigned long int)stats.nBytes);
479    Tcl_DStringAppend(&ds, buf, -1);
480    sprintf(buf, "num_commands=\"%lu\" ", (unsigned long int)stats.nCommands);
481    Tcl_DStringAppend(&ds, buf, -1);
482    sprintf(buf, "cmd_time=\"%g\" ", stats.cmdTime);
483    Tcl_DStringAppend(&ds, buf, -1);
484    sprintf(buf, "session_time=\"%g\" ", finish - start);
485    Tcl_DStringAppend(&ds, buf, -1);
486    sprintf(buf, "status=\"%d\" ", code);
487    Tcl_DStringAppend(&ds, buf, -1);
488    {
489        long clocksPerSec = sysconf(_SC_CLK_TCK);
490        double clockRes = 1.0 / clocksPerSec;
491        struct tms tms;
492
493        memset(&tms, 0, sizeof(tms));
494        times(&tms);
495        sprintf(buf, "utime=\"%g\" ", tms.tms_utime * clockRes);
496        Tcl_DStringAppend(&ds, buf, -1);
497        sprintf(buf, "stime=\"%g\" ", tms.tms_stime * clockRes);
498        Tcl_DStringAppend(&ds, buf, -1);
499        sprintf(buf, "cutime=\"%g\" ", tms.tms_cutime * clockRes);
500        Tcl_DStringAppend(&ds, buf, -1);
501        sprintf(buf, "cstime=\"%g\" ", tms.tms_cstime * clockRes);
502        Tcl_DStringAppend(&ds, buf, -1);
503    }
504    Tcl_DStringAppend(&ds, "/>\n", -1);
505
506    {
507        int f;
508        ssize_t length;
509        int result;
510
511#define STATSDIR        "/var/tmp/visservers"
512#define STATSFILE       STATSDIR "/" "data.xml"
513        if (access(STATSDIR, X_OK) != 0) {
514            mkdir(STATSDIR, 0770);
515        }
516        length = Tcl_DStringLength(&ds);
517        f = open(STATSFILE, O_APPEND | O_CREAT | O_WRONLY, 0600);
518        result = FALSE;
519        if (f < 0) {
520            goto error;
521        }
522        if (write(f, Tcl_DStringValue(&ds), length) != length) {
523            goto error;
524        }
525        result = TRUE;
526 error:
527        if (f >= 0) {
528            close(f);
529        }
530        Tcl_DStringFree(&ds);
531        return result;
532    }
533}
534#endif
535
536static void
537DoExit(int code)
538{
539#if KEEPSTATS
540    WriteStats("pymolproxy", code);
541#endif
542   
543    exit(code);
544}
545
546static int
547ExecuteCommand(Tcl_Interp *interp, const char *cmd)
548{
549    struct timeval tv;
550    double start, finish;
551    int result;
552
553    gettimeofday(&tv, NULL);
554    start = CVT2SECS(tv);
555
556    trace("command from client is (%s)", cmd);
557    result = Tcl_Eval(interp, cmd);
558
559    gettimeofday(&tv, NULL);
560    finish = CVT2SECS(tv);
561
562    stats.cmdTime += finish - start;
563    stats.nCommands++;
564    return result;
565}
566
567static Image *
568NewImage(PymolProxy *proxyPtr, size_t dataLength)
569{
570    Image *imgPtr;
571
572    imgPtr = malloc(sizeof(Image) + dataLength);
573    if (imgPtr == NULL) {
574        fprintf(stderr, "can't allocate image of %lu bytes",
575                (unsigned long)(sizeof(Image) + dataLength));
576        abort();
577    }
578    imgPtr->prevPtr = imgPtr->nextPtr = NULL;
579    imgPtr->bytesLeft = dataLength;
580    if (proxyPtr->headPtr != NULL) {
581        proxyPtr->headPtr->prevPtr = imgPtr;
582    }
583    imgPtr->nextPtr = proxyPtr->headPtr;
584    if (proxyPtr->tailPtr == NULL) {
585        proxyPtr->tailPtr = imgPtr;
586    }
587    proxyPtr->headPtr = imgPtr;
588    imgPtr->nWritten = 0;
589    return imgPtr;
590}
591
592INLINE static void
593FreeImage(Image *imgPtr)
594{
595    assert(imgPtr != NULL);
596    free(imgPtr);
597}
598
599static void
600WriteImage(PymolProxy *proxyPtr, int fd)
601{
602    Image *imgPtr, *prevPtr;
603
604    if (proxyPtr->tailPtr == NULL) {
605        trace("Should not be here: no image available to write");
606        return;
607    }
608    for (imgPtr = proxyPtr->tailPtr; imgPtr != NULL; imgPtr = prevPtr) {
609        ssize_t bytesLeft;
610
611        assert(imgPtr->nextPtr == NULL);
612        prevPtr = imgPtr->prevPtr;
613#ifdef notdef
614        trace("WriteImage: want to write %d bytes.", imgPtr->bytesLeft);
615#endif
616        for (bytesLeft = imgPtr->bytesLeft; bytesLeft > 0; /*empty*/) {
617            ssize_t nWritten;
618#ifdef notdef
619            trace("WriteImage: try to write %d bytes.", bytesLeft);
620#endif
621            nWritten = write(fd, imgPtr->data + imgPtr->nWritten, bytesLeft);
622#ifdef notdef
623            trace("WriteImage: wrote %d bytes.", nWritten);
624#endif
625            if (nWritten < 0) {
626                trace("Error writing fd(%d), %d/%s.", fd, errno,
627                      strerror(errno));
628                return;
629            }
630            bytesLeft -= nWritten;
631            if (bytesLeft > 0) {
632                /* Wrote a short buffer, means we would block. */
633                imgPtr->nWritten += nWritten;
634                imgPtr->bytesLeft = bytesLeft;
635                return;
636            }
637            imgPtr->nWritten += nWritten;
638        }
639        /* Check if image is on the head.  */
640        proxyPtr->tailPtr = prevPtr;
641        if (prevPtr != NULL) {
642            prevPtr->nextPtr = NULL;
643        }
644        FreeImage(imgPtr);
645    }
646    proxyPtr->headPtr = NULL;
647}
648
649static int
650Pymol(PymolProxy *proxyPtr, const char *format, ...)
651{
652    va_list ap;
653    char buffer[BUFSIZ];
654    char expect[BUFSIZ];
655    int result;
656    ssize_t nWritten;
657    size_t length;
658    char *p;
659
660    if (proxyPtr->error) {
661        return TCL_ERROR;
662    }
663   
664    va_start(ap, format);
665    result = vsnprintf(buffer, BUFSIZ-1, format, ap);
666    va_end(ap);
667   
668    trace("to-pymol>(%s) code=%d", buffer, result);
669    script("%s\n", buffer);
670   
671   
672    /* Write the command out to the server. */
673    length = strlen(buffer);
674    nWritten = write(proxyPtr->sin, buffer, length);
675    if (nWritten != length) {
676        trace("short write to pymol (wrote=%d, should have been %d)",
677              nWritten, length);
678    }
679    for (p = buffer; *p != '\0'; p++) {
680        if (*p == '\n') {
681            *p = '\0';
682            break;
683        }
684    }
685    sprintf(expect, "PyMOL>%s", buffer);
686    /* Now wait for the "PyMOL>" prompt. */
687    result = Expect(proxyPtr, expect, buffer, BUFSIZ);
688    if (result == BUFFER_ERROR) {
689        trace("timeout reading data (buffer=%s)", buffer);
690        proxyPtr->error = 1;
691        proxyPtr->status = TCL_ERROR;
692        return proxyPtr->status;
693    }
694    return  proxyPtr->status;
695}
696
697static void
698SetViewport(PymolProxy *proxyPtr)
699{
700    if (proxyPtr->flags & VIEWPORT_PENDING) {
701        Pymol(proxyPtr, "viewport %d,%d\n", proxyPtr->width, proxyPtr->height);
702        proxyPtr->flags &= ~VIEWPORT_PENDING;
703    }
704}
705
706static void
707SetZoom(PymolProxy *proxyPtr)
708{
709    if (proxyPtr->flags & ZOOM_PENDING) {
710        Pymol(proxyPtr,"move z,%f\n", proxyPtr->zoom);
711        proxyPtr->flags &= ~ZOOM_PENDING;
712    }
713}
714
715static void
716SetPan(PymolProxy *proxyPtr)
717{
718    if (proxyPtr->flags & PAN_PENDING) {
719        Pymol(proxyPtr,"move x,%f\nmove y,%f\n", proxyPtr->xPan,proxyPtr->yPan);
720        proxyPtr->flags &= ~PAN_PENDING;
721    }
722}
723
724static void
725SetRotation(PymolProxy *proxyPtr)
726{
727    if (proxyPtr->flags & ROTATE_PENDING) {
728        /* Every pymol command line generates a new rendering. Execute all
729         * three turns as a single command line. */
730        Pymol(proxyPtr,"turn x,%f\nturn y,%f\nturn z,%f\n",
731              proxyPtr->xAngle, proxyPtr->yAngle, proxyPtr->zAngle);
732        proxyPtr->xAngle = proxyPtr->yAngle = proxyPtr->zAngle = 0.0f;
733        proxyPtr->flags &= ~ROTATE_PENDING;
734    }
735}
736
737static void
738SetSphereScale(PymolProxy *proxyPtr)
739{
740    if (proxyPtr->flags & ATOM_SCALE_PENDING) {
741        Pymol(proxyPtr, "set sphere_scale,%f,all\n", proxyPtr->sphereScale);
742        proxyPtr->flags &= ~ATOM_SCALE_PENDING;
743    }
744}
745
746static void
747SetStickRadius(PymolProxy *proxyPtr)
748{
749    if (proxyPtr->flags & STICK_RADIUS_PENDING) {
750        Pymol(proxyPtr, "set stick_radius,%f,all\n", proxyPtr->stickRadius);
751        proxyPtr->flags &= ~STICK_RADIUS_PENDING;
752    }
753}
754
755static void
756UpdateSettings(PymolProxy *proxyPtr)
757{
758    /* Handle all the pending setting changes now. */
759    if (proxyPtr->flags & VIEWPORT_PENDING) {
760        SetViewport(proxyPtr);
761    }
762    if (proxyPtr->flags & ROTATE_PENDING) {
763        SetRotation(proxyPtr);
764    }
765    if (proxyPtr->flags & PAN_PENDING) {
766        SetPan(proxyPtr);
767    }
768    if (proxyPtr->flags & ZOOM_PENDING) {
769        SetZoom(proxyPtr);
770    }
771    if (proxyPtr->flags & ATOM_SCALE_PENDING) {
772        SetSphereScale(proxyPtr);
773    }
774    if (proxyPtr->flags & STICK_RADIUS_PENDING) {
775        SetStickRadius(proxyPtr);
776    }
777}
778
779
780static int
781BmpCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
782{
783    char buffer[BUFSIZ];
784    unsigned int nBytes=0;
785    PymolProxy *proxyPtr = clientData;
786    Image *imgPtr;
787    size_t length;
788    clear_error(proxyPtr);
789
790    if (proxyPtr->flags & INVALIDATE_CACHE)
791        proxyPtr->cacheId++;
792
793    proxyPtr->flags &= ~(UPDATE_PENDING|FORCE_UPDATE|INVALIDATE_CACHE);
794
795    /* Force pymol to update the current scene. */
796    UpdateSettings(proxyPtr);
797
798    Pymol(proxyPtr, "refresh\n");
799    Pymol(proxyPtr, "bmp -\n");
800    if (Expect(proxyPtr, "bmp image follows: ", buffer, BUFSIZ) != BUFFER_OK) {
801        trace("can't find image follows line (%s)", buffer);
802    }
803    if (sscanf(buffer, "bmp image follows: %d\n", &nBytes) != 1) {
804        Tcl_AppendResult(interp, "can't get # bytes from \"", buffer, "\"",
805                         (char *)NULL);
806        return TCL_ERROR;
807    }
808    sprintf(buffer, "nv>image %d %d %d %d\n", nBytes, proxyPtr->cacheId,
809            proxyPtr->frame, proxyPtr->rockOffset);
810
811    length = strlen(buffer);
812    imgPtr = NewImage(proxyPtr, nBytes + length);
813    strcpy(imgPtr->data, buffer);
814    if (GetBytes(&proxyPtr->server, imgPtr->data + length, nBytes)!=BUFFER_OK){
815        trace("can't read %d bytes for \"image follows\" buffer", nBytes);
816        return  TCL_ERROR;
817    }
818    stats.nFrames++;
819    stats.nBytes += nBytes;
820    return proxyPtr->status;
821}
822
823static int
824CartoonCmd(ClientData clientData, Tcl_Interp *interp, int argc,
825           const char *argv[])
826{
827    int bool, defer, push, i;
828    PymolProxy *proxyPtr = clientData;
829    const char *model;
830
831    clear_error(proxyPtr);
832    defer = push = FALSE;
833    model = "all";
834    bool = FALSE;
835    for(i = 1; i < argc; i++) {
836        if (strcmp(argv[i],"-defer") == 0) {
837            defer = TRUE;
838        } else if (strcmp(argv[i],"-push") == 0) {
839            push = TRUE;
840        } else if (strcmp(argv[i],"-model") == 0) {
841            if (++i < argc) {
842                model = argv[i];
843            }
844        } else {
845            if (Tcl_GetBoolean(interp, argv[i], &bool) != TCL_OK) {
846                return TCL_ERROR;
847            }
848        }
849    }
850    proxyPtr->flags |= INVALIDATE_CACHE; /* cartoon */
851    if (!defer || push) {
852        proxyPtr->flags |= UPDATE_PENDING;
853    }
854    if (push) {
855        proxyPtr->flags |= FORCE_UPDATE;
856    }
857    if (bool) {
858        Pymol(proxyPtr, "show cartoon,%s\n", model);
859    } else {
860        Pymol(proxyPtr, "hide cartoon,%s\n", model);
861    }
862    return proxyPtr->status;
863}
864
865static int
866CartoonTraceCmd(ClientData clientData, Tcl_Interp *interp, int argc,
867                const char *argv[])
868{
869    int bool, defer, push, i;
870    PymolProxy *proxyPtr = clientData;
871    const char *model;
872
873    clear_error(proxyPtr);
874    defer = push = FALSE;
875    bool = FALSE;
876    model = "all";
877    for(i = 1; i < argc; i++) {
878        if (strcmp(argv[i],"-defer") == 0) {
879            defer = TRUE;
880        } else if (strcmp(argv[i],"-push") == 0) {
881            push = TRUE;
882        } else if (strcmp(argv[i],"-model") == 0) {
883            if (++i < argc) {
884                model = argv[i];
885            }
886        } else {
887            if (Tcl_GetBoolean(interp, argv[i], &bool) != TCL_OK) {
888                return TCL_ERROR;
889            }
890        }
891    }
892    proxyPtr->flags |= INVALIDATE_CACHE; /* cartoon_trace  */
893    if (!defer || push) {
894        proxyPtr->flags |= UPDATE_PENDING;
895    }
896    if (push) {
897        proxyPtr->flags |= FORCE_UPDATE;
898    }
899    Pymol(proxyPtr, "set cartoon_trace,%d,%s\n", bool, model);
900    return proxyPtr->status;
901}
902
903static int
904DisableCmd(ClientData clientData, Tcl_Interp *interp, int argc,
905           const char *argv[])
906{
907    PymolProxy *proxyPtr = clientData;
908    const char *model = "all";
909    int i, defer = 0, push = 0;
910
911    clear_error(proxyPtr);
912
913    for(i = 1; i < argc; i++) {
914
915        if (strcmp(argv[i], "-defer") == 0 )
916            defer = 1;
917        else if (strcmp(argv[i], "-push") == 0 )
918            push = 1;
919        else
920            model = argv[i];
921       
922    }
923
924    proxyPtr->flags |= INVALIDATE_CACHE;  /* disable */
925    if (!defer || push) {
926        proxyPtr->flags |= UPDATE_PENDING;
927    }
928    if (push) {
929        proxyPtr->flags |= FORCE_UPDATE;
930    }
931
932    Pymol( proxyPtr, "disable %s\n", model);
933
934    return proxyPtr->status;
935}
936
937
938static int
939EnableCmd(ClientData clientData, Tcl_Interp *interp, int argc,
940          const char *argv[])
941{
942    PymolProxy *proxyPtr = clientData;
943    const char *model;
944    int i, defer, push;
945
946    clear_error(proxyPtr);
947    push = defer = FALSE;
948    model = "all";
949    for(i = 1; i < argc; i++) {
950        if (strcmp(argv[i],"-defer") == 0) {
951            defer = TRUE;
952        } else if (strcmp(argv[i], "-push") == 0) {
953            push = TRUE;
954        } else {
955            model = argv[i];
956        }
957    }
958    proxyPtr->flags |= INVALIDATE_CACHE; /* enable */
959    if (!defer || push) {
960        proxyPtr->flags |= UPDATE_PENDING;
961    }
962    if (push) {
963        proxyPtr->flags |= FORCE_UPDATE;
964    }
965    Pymol( proxyPtr, "enable %s\n", model);
966    return proxyPtr->status;
967}
968
969static int
970FrameCmd(ClientData clientData, Tcl_Interp *interp, int argc,
971         const char *argv[])
972{
973    PymolProxy *proxyPtr = clientData;
974    int i, push, defer, frame;
975
976    clear_error(proxyPtr);
977    frame = 0;
978    push = defer = TRUE;
979    for(i = 1; i < argc; i++) {
980        if (strcmp(argv[i],"-defer") == 0) {
981            defer = TRUE;
982        } else if (strcmp(argv[i],"-push") == 0) {
983            push = TRUE;
984        } else {
985            frame = atoi(argv[i]);
986        }
987    }
988    if (!defer || push) {
989        proxyPtr->flags |= UPDATE_PENDING;
990    }
991    if (push) {
992        proxyPtr->flags |= FORCE_UPDATE;
993    }
994    proxyPtr->frame = frame;
995
996    /* Does not invalidate cache? */
997
998    Pymol(proxyPtr,"frame %d\n", frame);
999    return proxyPtr->status;
1000}
1001
1002
1003static int
1004LabelCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1005         const char *argv[])
1006{
1007    PymolProxy *proxyPtr = clientData;
1008    int i, push, defer, state, size;
1009    const char *model;
1010
1011    clear_error(proxyPtr);
1012    model = "all";
1013    size = 14;
1014    state = TRUE;
1015    push = defer = FALSE;
1016    for(i = 1; i < argc; i++) {
1017        if (strcmp(argv[i],"-defer") == 0) {
1018            defer = TRUE;
1019        } else if (strcmp(argv[i],"-push") == 0) {
1020            push = TRUE;
1021        } else if (strcmp(argv[i],"-model") == 0) {
1022            if (++i < argc) {
1023                model = argv[i];
1024            }
1025        } else if (strcmp(argv[i],"-size") == 0) {
1026            if (++i < argc) {
1027                size = atoi(argv[i]);
1028            }
1029        } else if (Tcl_GetBoolean(interp, argv[i], &state) != TCL_OK) {
1030            return TCL_ERROR;
1031        }
1032    }
1033    proxyPtr->flags |= INVALIDATE_CACHE;  /* label */
1034    if (!defer || push) {
1035        proxyPtr->flags |= UPDATE_PENDING;
1036    }
1037    if (push) {
1038        proxyPtr->flags |= FORCE_UPDATE;
1039    }
1040    Pymol(proxyPtr,
1041          "set label_color,white,%s\n"
1042          "set label_size,%d,%s\n",
1043          model, size, model);
1044    if (state) {
1045        Pymol(proxyPtr, "label %s,\"%%s%%s\" %% (ID,name)\n", model);
1046    } else {
1047        Pymol(proxyPtr, "label %s\n", model);
1048    }
1049    return proxyPtr->status;
1050}
1051
1052/*
1053 * LoadPDBCmd --
1054 *
1055 *      Load a PDB file into pymol.  There is no good way to load PDB data
1056 *      into pymol without using a file.  This causes problems in that we
1057 *      don't know when pymol is done with the file.  So there's always
1058 *      a bit of mess left around. We use the same file for every file
1059 *      so there's a chance that pymol is still using a file while we're
1060 *      overwriting it. 
1061 *
1062 *      The command expects the number of bytes in the pdb data, the
1063 *      name, and the state.
1064 */
1065static int
1066LoadPDBCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1067           const char *argv[])
1068{
1069    const char *data, *name;
1070    char *allocated;
1071    PymolProxy *proxyPtr = clientData;
1072    int state, defer, push;
1073    int nBytes;
1074    int i, j;
1075
1076    if (proxyPtr == NULL){
1077        return TCL_ERROR;
1078    }
1079    clear_error(proxyPtr);
1080    defer = push = FALSE;
1081    for(i = j = 1; i < argc; i++) {
1082        if (strcmp(argv[i],"-defer") == 0) {
1083            defer = TRUE;
1084        } else if (strcmp(argv[i],"-push") == 0) {
1085            push = TRUE;
1086        } else {
1087            if (j < i) {
1088                argv[j] = argv[i];
1089            }
1090            j++;
1091        }
1092    }
1093    argc = j;
1094    if (argc < 4) {
1095        Tcl_AppendResult(interp, "wrong # arguments: should be \"", argv[0],
1096                         " <data>|follows <model> <state> ?<nBytes>?\"",
1097                         (char *)NULL);
1098        return TCL_ERROR;
1099    }
1100    data = argv[1];
1101    name = argv[2];
1102    if (Tcl_GetInt(interp, argv[3], &state) != TCL_OK) {
1103        return TCL_ERROR;
1104    }
1105    nBytes = -1;
1106    if (strcmp(data, "follows") == 0) {
1107        if (argc != 5) {
1108            Tcl_AppendResult(interp, "wrong # arguments: should be \"", argv[0],
1109                         " follows <model> <state> <nBytes>\"", (char *)NULL);
1110            return TCL_ERROR;
1111        }
1112        if (Tcl_GetInt(interp, argv[4], &nBytes) != TCL_OK) {
1113            return TCL_ERROR;
1114        }
1115        if (nBytes < 0) {
1116            Tcl_AppendResult(interp, "bad value for # bytes \"", argv[4],
1117                         "\"", (char *)NULL);
1118            return TCL_ERROR;
1119        }
1120    }
1121    if (!defer || push) {
1122        proxyPtr->flags |= UPDATE_PENDING;
1123    }
1124    if (push) {
1125        proxyPtr->flags |= FORCE_UPDATE;
1126    }
1127
1128    /* Does not invalidate cache? */
1129
1130    allocated = NULL;
1131    if (nBytes >= 0) {
1132        allocated = malloc(sizeof(char) * nBytes);
1133        if (allocated == NULL) {
1134            Tcl_AppendResult(interp, "can't allocate buffer for pdbdata.",
1135                             (char *)NULL);
1136            return TCL_ERROR;
1137        }
1138        if (GetBytes(&proxyPtr->client, allocated, nBytes) != BUFFER_OK) {
1139            Tcl_AppendResult(interp, "can't read pdbdata from client.",
1140                             (char *)NULL);
1141            free(allocated);
1142            return TCL_ERROR;
1143        }
1144        data = allocated;
1145    } else {
1146        nBytes = strlen(data);
1147    }
1148    {
1149        char fileName[200];
1150        FILE *f;
1151        ssize_t nWritten;
1152        static unsigned long count = 0;
1153
1154        proxyPtr->status = TCL_ERROR;
1155        sprintf(fileName, "/tmp/pymol%d/%ld.pdb", getpid(), count);
1156        count++;
1157        f = fopen(fileName, "w");
1158        if (f == NULL) {
1159            Tcl_AppendResult(interp, "can't create temporary file \"",
1160                             fileName, "\": ", Tcl_PosixError(interp),
1161                             (char *)NULL);
1162            goto error;
1163        }
1164        nWritten = fwrite(data, sizeof(char), nBytes, f);
1165        if (nBytes != nWritten) {
1166            Tcl_AppendResult(interp, "can't write PDB data to \"",
1167                             fileName, "\": ", Tcl_PosixError(interp),
1168                             (char *)NULL);
1169            fclose(f);
1170            goto error;
1171        }
1172        fclose(f);
1173        Pymol(proxyPtr, "load %s,%s,%d\n", fileName, name, state);
1174        proxyPtr->status = TCL_OK;
1175    }
1176 error:
1177    if (allocated != NULL) {
1178        free(allocated);
1179    }
1180    return proxyPtr->status;
1181}
1182
1183static int
1184OrthoscopicCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1185              const char *argv[])
1186{
1187    int bool, defer, push, i;
1188    PymolProxy *proxyPtr = clientData;
1189
1190    clear_error(proxyPtr);
1191    defer = push = FALSE;
1192    bool = FALSE;
1193    for(i = 1; i < argc; i++) {
1194        if (strcmp(argv[i],"-defer") == 0) {
1195            defer = TRUE;
1196        } else if (strcmp(argv[i],"-push") == 0) {
1197            push = TRUE;
1198        } else {
1199            if (Tcl_GetBoolean(interp, argv[i], &bool) != TCL_OK) {
1200                return TCL_ERROR;
1201            }
1202        }
1203    }
1204    proxyPtr->flags |= INVALIDATE_CACHE; /* orthoscopic */
1205    if (!defer || push) {
1206        proxyPtr->flags |= UPDATE_PENDING;
1207    }
1208    if (push) {
1209        proxyPtr->flags |= FORCE_UPDATE;
1210    }
1211    Pymol(proxyPtr, "set orthoscopic=%d\n", bool);
1212    return proxyPtr->status;
1213}
1214
1215/*
1216 * PanCmd --
1217 *
1218 *      Issue "move" commands for changes in the x and y coordinates of the
1219 *      camera.  The problem here is that there is no event compression.
1220 *      Consecutive "pan" commands are not compressed into a single
1221 *      directive.  The means that the pymol server will render scenes that
1222 *      are not used by the client.
1223 *
1224 *      Need to 1) defer the "move" commands until we find the next command
1225 *      isn't a "pan". 2) Track the x and y coordinates as they are
1226 *      compressed.
1227 */
1228static int
1229PanCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1230        const char *argv[])
1231{
1232    PymolProxy *proxyPtr = clientData;
1233    double x, y;
1234    int i;
1235    int defer, push;
1236
1237    clear_error(proxyPtr);
1238    defer = push = FALSE;
1239    for (i = 1; i < argc; i++) {
1240        if (strcmp(argv[i],"-defer") == 0) {
1241            defer = 1;
1242        } else if (strcmp(argv[i],"-push") == 0) {
1243            push = 1;
1244        } else {
1245            break;
1246        }
1247    }
1248    if ((Tcl_GetDouble(interp, argv[i], &x) != TCL_OK) ||
1249        (Tcl_GetDouble(interp, argv[i+1], &y) != TCL_OK)) {
1250        return TCL_ERROR;
1251    }
1252    proxyPtr->flags |= INVALIDATE_CACHE; /* pan */
1253    if (!defer || push) {
1254        proxyPtr->flags |= UPDATE_PENDING;
1255    }
1256    if (push) {
1257        proxyPtr->flags |= FORCE_UPDATE;
1258    }
1259    if ((x != 0.0f) || (y != 0.0f)) {
1260        proxyPtr->xPan = x * 0.05;
1261        proxyPtr->yPan = -y * 0.05;
1262        proxyPtr->flags |= PAN_PENDING;
1263    }
1264    return proxyPtr->status;
1265}
1266
1267static int
1268PngCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
1269{
1270    char buffer[800];
1271    int nBytes=0;
1272    PymolProxy *proxyPtr = clientData;
1273    size_t length;
1274    Image *imgPtr;
1275
1276    clear_error(proxyPtr);
1277
1278    /* Force pymol to update the current scene. */
1279    Pymol(proxyPtr, "refresh\n");
1280    Pymol(proxyPtr, "png -\n");
1281
1282    Expect(proxyPtr, "png image follows: ", buffer, 800);
1283
1284    if (sscanf(buffer, "png image follows: %d\n", &nBytes) != 1) {
1285        Tcl_AppendResult(interp, "can't get # bytes from \"", buffer, "\"",
1286                         (char *)NULL);
1287        return TCL_ERROR;
1288    }
1289    sprintf(buffer, "nv>image %d %d %d %d\n", nBytes, proxyPtr->cacheId,
1290            proxyPtr->frame, proxyPtr->rockOffset);
1291    length = strlen(buffer);
1292    imgPtr = NewImage(proxyPtr, nBytes + length);
1293    strcpy(imgPtr->data, buffer);
1294    if (GetBytes(&proxyPtr->server, imgPtr->data + length, nBytes)!=BUFFER_OK){
1295        trace("can't read %d bytes for \"image follows\" buffer", nBytes);
1296        return  TCL_ERROR;
1297    }
1298    Expect(proxyPtr, " ScenePNG", buffer, 800);
1299
1300    stats.nFrames++;
1301    stats.nBytes += nBytes;
1302    return proxyPtr->status;
1303}
1304
1305
1306static int
1307PrintCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1308         const char *argv[])
1309{
1310    char buffer[800];
1311    int nBytes=0;
1312    PymolProxy *proxyPtr = clientData;
1313    size_t length;
1314    Image *imgPtr;
1315    int width, height;
1316    const char *token, *bgcolor;
1317
1318    clear_error(proxyPtr);
1319
1320    if (argc != 5) {
1321        Tcl_AppendResult(interp, "wrong # arguments: should be \"",
1322                         argv[0], " token width height color\"", (char *)NULL);
1323        return TCL_ERROR;
1324    }
1325    token = argv[1];
1326    if (Tcl_GetInt(interp, argv[2], &width) != TCL_OK) {
1327        return TCL_ERROR;
1328    }
1329    if (Tcl_GetInt(interp, argv[3], &height) != TCL_OK) {
1330        return TCL_ERROR;
1331    }
1332    bgcolor = argv[4];
1333    /* Force pymol to update the current scene. */
1334    if (strcmp(bgcolor, "none") == 0) {
1335        Pymol(proxyPtr, "set ray_opaque_background,off\n");
1336        Pymol(proxyPtr, "refresh\n", bgcolor);
1337    } else {
1338        Pymol(proxyPtr, "set ray_opaque_background,on\n");
1339        Pymol(proxyPtr, "bg_color %s\nrefresh\n", bgcolor);
1340    }
1341    Pymol(proxyPtr, "ray %d,%d\n", width, height);
1342    Expect(proxyPtr, " Ray:", buffer, 800);
1343
1344    Pymol(proxyPtr, "png -,dpi=300\nbg_color black\n");
1345    Expect(proxyPtr, "png image follows: ", buffer, 800);
1346
1347    if (sscanf(buffer, "png image follows: %d\n", &nBytes) != 1) {
1348        Tcl_AppendResult(interp, "can't get # bytes from \"", buffer, "\"",
1349                         (char *)NULL);
1350        return TCL_ERROR;
1351    }
1352    sprintf(buffer, "nv>image %d print \"%s\" %d\n", nBytes, token,
1353            proxyPtr->rockOffset);
1354    trace("header is png is (%s)\n", buffer);
1355    length = strlen(buffer);
1356    imgPtr = NewImage(proxyPtr, nBytes + length);
1357    strcpy(imgPtr->data, buffer);
1358    if (GetBytes(&proxyPtr->server, imgPtr->data + length, nBytes)!=BUFFER_OK){
1359        trace("can't read %d bytes for \"image follows\" buffer", nBytes);
1360        return  TCL_ERROR;
1361    }
1362    Expect(proxyPtr, " ScenePNG", buffer, 800);
1363
1364    stats.nFrames++;
1365    stats.nBytes += nBytes;
1366    return proxyPtr->status;
1367}
1368
1369static int
1370RawCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
1371{
1372    PymolProxy *proxyPtr = clientData;
1373    int arg, defer = 0, push = 0;
1374    const char *cmd;
1375    clear_error(proxyPtr);
1376
1377    cmd = NULL;
1378    for(arg = 1; arg < argc; arg++) {
1379        if (strcmp(argv[arg], "-defer") == 0)
1380            defer = 1;
1381        else if (strcmp(argv[arg], "-push") == 0)
1382            push = 1;
1383        else {
1384            cmd = argv[arg];
1385        }
1386    }
1387
1388    proxyPtr->flags |= INVALIDATE_CACHE; /* raw */
1389    if (!defer || push) {
1390        proxyPtr->flags |= UPDATE_PENDING;
1391    }
1392    if (push) {
1393        proxyPtr->flags |= FORCE_UPDATE;
1394    }
1395    Pymol(proxyPtr,"%s\n", cmd);
1396    return proxyPtr->status;
1397}
1398
1399static int
1400ResetCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1401         const char *argv[])
1402{
1403    PymolProxy *proxyPtr = clientData;
1404    int arg, push = 0, defer = 0;
1405
1406    clear_error(proxyPtr);
1407
1408    for(arg = 1; arg < argc; arg++) {
1409        if ( strcmp(argv[arg],"-defer") == 0 )
1410            defer = 1;
1411        else if (strcmp(argv[arg],"-push") == 0 )
1412            push = 1;
1413    }
1414               
1415    proxyPtr->flags |= INVALIDATE_CACHE; /* reset */
1416    if (!defer || push) {
1417        proxyPtr->flags |= UPDATE_PENDING;
1418    }
1419    if (push) {
1420        proxyPtr->flags |= FORCE_UPDATE;
1421    }
1422    Pymol(proxyPtr,
1423          "reset\n"
1424          "zoom complete=1\n");
1425    return proxyPtr->status;
1426}
1427
1428static int
1429RockCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1430        const char *argv[])
1431{
1432    PymolProxy *proxyPtr = clientData;
1433    float y = 0.0;
1434    int arg, push = 0, defer = 0;
1435
1436    clear_error(proxyPtr);
1437
1438    for(arg = 1; arg < argc; arg++) {
1439        if ( strcmp(argv[arg],"-defer") == 0 )
1440            defer = 1;
1441        else if (strcmp(argv[arg],"-push") == 0 )
1442            push = 1;
1443        else
1444            y = atof( argv[arg] );
1445    }
1446               
1447    /* Does not invalidate cache. */
1448
1449    if (!defer || push) {
1450        proxyPtr->flags |= UPDATE_PENDING;
1451    }
1452    if (push) {
1453        proxyPtr->flags |= FORCE_UPDATE;
1454    }
1455    Pymol(proxyPtr,"turn y, %f\n", y - proxyPtr->rockOffset);
1456    proxyPtr->rockOffset = y;
1457    return proxyPtr->status;
1458}
1459
1460static int
1461RepresentationCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1462                  const char *argv[])
1463{
1464    PymolProxy *proxyPtr = clientData;
1465    const char *model;
1466    const char *rep;
1467    int defer, push, i;
1468
1469    clear_error(proxyPtr);
1470    defer = push = FALSE;
1471    model = "all";
1472    rep = NULL;
1473    for (i = 1; i < argc; i++) {
1474        if (strcmp(argv[i],"-defer") == 0 ) {
1475            defer = TRUE;
1476        } else if (strcmp(argv[i],"-push") == 0) {
1477            push = TRUE;
1478        } else if (strcmp(argv[i],"-model") == 0) {
1479            if (++i < argc) {
1480                model = argv[i];
1481            }
1482        } else {
1483            rep = argv[i];
1484        }
1485    }
1486    if (rep == NULL) {
1487        Tcl_AppendResult(interp, "missing representation argument",
1488                         (char *)NULL);
1489        return TCL_ERROR;
1490    }
1491
1492    proxyPtr->flags |= INVALIDATE_CACHE; /* representation */
1493    if (!defer || push) {
1494        proxyPtr->flags |= UPDATE_PENDING;
1495    }
1496    if (push) {
1497        proxyPtr->flags |= FORCE_UPDATE;
1498    }
1499    if (strcmp(rep, "ballnstick") == 0) { /* Ball 'n Stick */
1500        Pymol(proxyPtr,
1501              "set stick_color,white,%s\n"
1502              "show sticks,%s\n"
1503              "show spheres,%s\n"
1504              "hide lines,%s\n"
1505              "hide cartoon,%s\n",
1506              model, model, model, model, model);
1507    } else if (strcmp(rep, "spheres") == 0) { /* spheres */   
1508        Pymol(proxyPtr,
1509              "hide sticks,%s\n"
1510              "show spheres,%s\n"
1511              "hide lines,%s\n"
1512              "hide cartoon,%s\n"
1513              "set sphere_quality,2,%s\n"
1514              "set ambient,.2,%s\n",
1515              model, model, model, model, model, model);
1516    } else if (strcmp(rep, "none") == 0) { /* nothing */   
1517        Pymol(proxyPtr,
1518              "hide sticks,%s\n",
1519              "hide spheres,%s\n"
1520              "hide lines,%s\n"
1521              "hide cartoon,%s\n",
1522              model, model, model, model);
1523    } else if (strcmp(rep, "sticks") == 0) { /* sticks */   
1524        Pymol(proxyPtr,
1525              "set stick_color,white,%s\n"
1526              "show sticks,%s\n"
1527              "hide spheres,%s\n"
1528              "hide lines,%s\n"
1529              "hide cartoon,%s\n",
1530              model, model, model, model, model);
1531    } else if (strcmp(rep, "lines") == 0) { /* lines */   
1532        Pymol(proxyPtr,
1533              "hide sticks,%s\n"
1534              "hide spheres,%s\n"
1535              "show lines,%s\n"
1536              "hide cartoon,%s\n",
1537              model, model, model, model);
1538    } else if (strcmp(rep, "cartoon") == 0) { /* cartoon */   
1539        Pymol(proxyPtr,
1540              "hide sticks,%s\n"
1541              "hide spheres,%s\n"
1542              "hide lines,%s\n"
1543              "show cartoon,%s\n",
1544              model, model, model, model);
1545    }
1546    return proxyPtr->status;
1547}
1548
1549/*
1550 * RotateCmd --
1551 *
1552 *      Issue "turn" commands for changes in the angle of the camera.  The
1553 *      problem here is that there is no event compression.  Consecutive
1554 *      "rotate" commands are not compressed into a single directive.  The
1555 *      means that the pymol server will render many scene that are not used
1556 *      by the client.
1557 *
1558 *      Need to 1) defer the "turn" commands until we find the next command
1559 *      isn't a "rotate". 2) Track the rotation angles as they are compressed.
1560 */
1561static int
1562RotateCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1563          const char *argv[])
1564{
1565    float xAngle, yAngle, zAngle;
1566    PymolProxy *proxyPtr = clientData;
1567    int defer = 0, push = 0, arg, varg = 1;
1568
1569    clear_error(proxyPtr);
1570
1571    xAngle = yAngle = zAngle = 0.0f;
1572    for(arg = 1; arg < argc; arg++) {
1573        if (strcmp(argv[arg],"-defer") == 0) {
1574            defer = 1;
1575        } else if (strcmp(argv[arg],"-push") == 0) {
1576            push = 1;
1577        } else  if (varg == 1) {
1578            double value;
1579            if (Tcl_GetDouble(interp, argv[arg], &value) != TCL_OK) {
1580                return TCL_ERROR;
1581            }
1582            xAngle = (float)value;
1583            varg++;
1584        } else if (varg == 2) {
1585            double value;
1586            if (Tcl_GetDouble(interp, argv[arg], &value) != TCL_OK) {
1587                return TCL_ERROR;
1588            }
1589            yAngle = (float)value;
1590            varg++;
1591        } else if (varg == 3) {
1592            double value;
1593            if (Tcl_GetDouble(interp, argv[arg], &value) != TCL_OK) {
1594                return TCL_ERROR;
1595            }
1596            zAngle = (float)value;
1597            varg++;
1598        }
1599    }
1600    proxyPtr->flags |= INVALIDATE_CACHE; /* rotate */
1601    if (!defer || push) {
1602        proxyPtr->flags |= UPDATE_PENDING;
1603    }
1604    if (push) {
1605        proxyPtr->flags |= FORCE_UPDATE;
1606    }
1607    if ((xAngle != 0.0f) || (yAngle != 0.0f) || (zAngle != 0.0f)) {
1608        proxyPtr->xAngle += xAngle;
1609        proxyPtr->yAngle += yAngle;
1610        proxyPtr->zAngle += zAngle;
1611        proxyPtr->flags |= ROTATE_PENDING;
1612    }
1613    return proxyPtr->status;
1614}
1615
1616static int
1617ScreenCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1618          const char *argv[])
1619{
1620    PymolProxy *proxyPtr = clientData;
1621    int width = -1, height = -1;
1622    int defer, push, i, varg;
1623
1624    clear_error(proxyPtr);
1625    defer = push = FALSE;
1626    varg = 1;
1627    for(i = 1; i < argc; i++) {
1628        if ( strcmp(argv[i],"-defer") == 0 )
1629            defer = 1;
1630        else if ( strcmp(argv[i], "-push") == 0 )
1631            push = 1;
1632        else if (varg == 1) {
1633            width = atoi(argv[i]);
1634            height = width;
1635            varg++;
1636        }
1637        else if (varg == 2) {
1638            height = atoi(argv[i]);
1639            varg++;
1640        }
1641    }
1642    if ((width < 0) || (height < 0)) {
1643        return TCL_ERROR;
1644    }
1645    proxyPtr->flags |= INVALIDATE_CACHE; /* viewport */
1646    if (!defer || push) {
1647        proxyPtr->flags |= UPDATE_PENDING;
1648    }
1649    if (push) {
1650        proxyPtr->flags |= FORCE_UPDATE;
1651    }
1652    proxyPtr->width = width;
1653    proxyPtr->height = height;
1654    proxyPtr->flags |= VIEWPORT_PENDING;
1655
1656    //usleep(205000); // .2s delay for pymol to update its geometry *HACK ALERT*
1657       
1658    return proxyPtr->status;
1659}
1660
1661static int
1662SphereScaleCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1663           const char *argv[])
1664{
1665    int defer = 0, push = 0, i;
1666    double scale;
1667    const char *model = "all";
1668    PymolProxy *proxyPtr = clientData;
1669
1670    clear_error(proxyPtr);
1671    scale = 0.25f;
1672    for(i = 1; i < argc; i++) {
1673        if ( strcmp(argv[i],"-defer") == 0 ) {
1674            defer = 1;
1675        } else if (strcmp(argv[i],"-push") == 0) {
1676            push = 1;
1677        } else if (strcmp(argv[i],"-model") == 0) {
1678            if (++i < argc) {
1679                model = argv[i];
1680            }
1681        } else {
1682            if (Tcl_GetDouble(interp, argv[i], &scale) != TCL_OK) {
1683                return TCL_ERROR;
1684            }
1685        }
1686    }
1687    proxyPtr->flags |= INVALIDATE_CACHE;  /* sphere_scale */
1688    if (!defer || push) {
1689        proxyPtr->flags |= UPDATE_PENDING;
1690    }
1691    if (push) {
1692        proxyPtr->flags |= FORCE_UPDATE;
1693    }
1694
1695    if (strcmp(model, "all") == 0) {
1696        proxyPtr->flags |= ATOM_SCALE_PENDING;
1697        proxyPtr->sphereScale = scale;
1698    } else {
1699        Pymol(proxyPtr, "set sphere_scale,%f,%s\n", scale, model);
1700    }
1701    return proxyPtr->status;
1702}
1703
1704static int
1705StickRadiusCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1706                 const char *argv[])
1707{
1708    int defer = 0, push = 0, i;
1709    double scale;
1710    const char *model = "all";
1711    PymolProxy *proxyPtr = clientData;
1712
1713    clear_error(proxyPtr);
1714    scale = 0.25f;
1715    for(i = 1; i < argc; i++) {
1716        if ( strcmp(argv[i],"-defer") == 0 ) {
1717            defer = 1;
1718        } else if (strcmp(argv[i],"-push") == 0) {
1719            push = 1;
1720        } else if (strcmp(argv[i],"-model") == 0) {
1721            if (++i < argc)
1722                model = argv[i];
1723        } else {
1724            if (Tcl_GetDouble(interp, argv[i], &scale) != TCL_OK) {
1725                return TCL_ERROR;
1726            }
1727        }
1728    }
1729    proxyPtr->flags |= INVALIDATE_CACHE;  /* stick_radius */
1730    if (!defer || push) {
1731        proxyPtr->flags |= UPDATE_PENDING;
1732    }
1733    if (push) {
1734        proxyPtr->flags |= FORCE_UPDATE;
1735    }
1736
1737    if (strcmp(model, "all") == 0) {
1738        proxyPtr->flags |= STICK_RADIUS_PENDING;
1739        proxyPtr->stickRadius = scale;
1740    } else {
1741        Pymol(proxyPtr, "set stick_radius,%f,%s\n", scale, model);
1742    }
1743    return proxyPtr->status;
1744}
1745
1746static int
1747TransparencyCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1748                const char *argv[])
1749{
1750    PymolProxy *proxyPtr = clientData;
1751    const char *model;
1752    float transparency;
1753    int defer, push;
1754    int i;
1755
1756    clear_error(proxyPtr);
1757    model = "all";
1758    defer = push = FALSE;
1759    transparency = 0.0f;
1760    for(i = 1; i < argc; i++) {
1761        if ( strcmp(argv[i],"-defer") == 0 ) {
1762            defer = 1;
1763        } else if (strcmp(argv[i],"-push") == 0) {
1764            push = 1;
1765        } else if (strcmp(argv[i],"-model") == 0) {
1766            if (++i < argc) {
1767                model = argv[i];
1768            }
1769        } else {
1770            transparency = atof(argv[i]);
1771        }
1772    }
1773    proxyPtr->flags |= INVALIDATE_CACHE; /* transparency */
1774    if (!defer || push) {
1775        proxyPtr->flags |= UPDATE_PENDING;
1776    }
1777    if (push) {
1778        proxyPtr->flags |= FORCE_UPDATE;
1779    }
1780    Pymol(proxyPtr,
1781          "set sphere_transparency,%g,%s\n"
1782          "set stick_transparency,%g,%s\n"
1783          "set cartoon_transparency,%g,%s\n",
1784          transparency, model, transparency, model,
1785          transparency, model);
1786    return proxyPtr->status;
1787}
1788
1789static int
1790VMouseCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1791          const char *argv[])
1792{
1793    PymolProxy *proxyPtr = clientData;
1794    int i, defer = 0, push = 0, varg = 1;
1795    int arg1 = 0, arg2 = 0, arg3 = 0, arg4 = 0, arg5 = 0;
1796
1797    clear_error(proxyPtr);
1798
1799    for(i = 1; i < argc; i++) {
1800        if (strcmp(argv[i], "-defer") == 0)
1801            defer = 1;
1802        else if (strcmp(argv[i], "-push") == 0)
1803            push = 1;
1804        else if (varg == 1) {
1805            arg1 = atoi(argv[i]);
1806            varg++;
1807        }
1808        else if (varg == 2) {
1809            arg2 = atoi(argv[i]);
1810            varg++;
1811        }
1812        else if (varg == 3) {
1813            arg3 = atoi(argv[i]);
1814            varg++;
1815        }
1816        else if (varg == 4) {
1817            arg4 = atoi(argv[i]);
1818            varg++;
1819        }
1820        else if (varg == 5) {
1821            arg5 = atoi(argv[i]);
1822            varg++;
1823        }
1824    }
1825
1826    proxyPtr->flags |= INVALIDATE_CACHE; /* vmouse */
1827    if (!defer || push) {
1828        proxyPtr->flags |= UPDATE_PENDING;
1829    }
1830    if (push) {
1831        proxyPtr->flags |= FORCE_UPDATE;
1832    }
1833    Pymol(proxyPtr, "vmouse %d,%d,%d,%d,%d\n", arg1, arg2, arg3, arg4, arg5);
1834    return proxyPtr->status;
1835}
1836
1837
1838/*
1839 * ZoomCmd --
1840 *
1841 *      Issue "move" commands for changes in the z-coordinate of the camera.
1842 *      The problem here is that there is no event compression.  Consecutive
1843 *      "zoom" commands are not compressed into a single directive.  The means
1844 *      that the pymol server will render scenes that are not used by the
1845 *      client.
1846 *
1847 *      Need to 1) defer the "move" commands until we find the next command
1848 *      isn't a "zoom". 2) Track the z-coordinate as they are compressed.
1849 */
1850static int
1851ZoomCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
1852{
1853    double factor = 0.0;
1854    PymolProxy *proxyPtr = clientData;
1855    int defer = 0, push = 0, i, varg = 1;
1856
1857    clear_error(proxyPtr);
1858
1859    for(i = 1; i < argc; i++) {
1860        if (strcmp(argv[i],"-defer") == 0)
1861            defer = 1;
1862        else if (strcmp(argv[i],"-push") == 0)
1863            push = 1;
1864        else if (varg == 1) {
1865            double value;
1866            if (Tcl_GetDouble(interp, argv[i], &value) != TCL_OK) {
1867                return TCL_ERROR;
1868            }
1869            factor = (float)value;
1870            varg++;
1871        }
1872    }
1873    proxyPtr->flags |= INVALIDATE_CACHE; /* Zoom */
1874    if (!defer || push) {
1875        proxyPtr->flags |= UPDATE_PENDING;
1876    }
1877    if (push) {
1878        proxyPtr->flags |= FORCE_UPDATE;
1879    }
1880    if (factor != 0.0) {
1881        proxyPtr->zoom = factor;
1882        proxyPtr->flags |= ZOOM_PENDING;
1883    }
1884    return proxyPtr->status;
1885}
1886
1887       
1888static int
1889ProxyInit(int cin, int cout, char *const *argv)
1890{
1891    int status, result = 0;
1892    int sin[2];
1893    int sout[2];
1894    int serr[2];
1895    Tcl_Interp *interp;
1896    int child;
1897    PymolProxy proxy;
1898    struct timeval end;
1899
1900    /* Create three pipes for communication with the external application. One
1901     * each for the applications's: stdin, stdout, and stderr  */
1902
1903    if (pipe(sin) == -1)
1904        return -1;
1905
1906    if (pipe(sout) == -1) {
1907        close(sin[0]);
1908        close(sin[1]);
1909        return -1;
1910    }
1911
1912    if (pipe(serr) == -1) {
1913        close(sin[0]);
1914        close(sin[1]);
1915        close(sout[0]);
1916        close(sout[1]);
1917        return -1;
1918    }
1919
1920    /* Fork the new process.  Connect i/o to the new socket.  */
1921
1922    child = fork();
1923    if (child < 0) {
1924        fprintf(stderr, "can't fork process: %s\n", strerror(errno));
1925        return -3;
1926    }
1927    if (child == 0) {
1928        int f;
1929
1930        /* Child process */
1931       
1932        /*
1933         * Create a new process group, so we can later kill this process and
1934         * all its children without affecting the process that created this
1935         * one.
1936         */
1937        setpgid(child, 0);
1938       
1939        /* Redirect stdin, stdout, and stderr to pipes before execing */
1940
1941        dup2(sin[0], 0);  // stdin
1942        dup2(sout[1],1);  // stdout
1943        dup2(serr[1],2);  // stderr
1944       
1945        /* Close all other descriptors  */       
1946        for (f = 3; f < FD_SETSIZE; f++) {
1947            close(f);
1948        }
1949       
1950        execvp(argv[0], argv);
1951        trace("Failed to start pymol `%s'", argv[0]);
1952        exit(-1);
1953    }
1954    stats.child = child;
1955
1956    /* close opposite end of pipe, these now belong to the child process  */
1957    close(sin[0]);
1958    close(sout[1]);
1959    close(serr[1]);
1960
1961    signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE (e.g. nanoscale terminates)*/
1962
1963    memset(&proxy, 0, sizeof(PymolProxy));
1964    proxy.sin  = sin[1];
1965    proxy.sout = sout[0];
1966    proxy.serr = serr[0];
1967    proxy.cin  = cin;
1968    proxy.cout = cout;
1969    proxy.flags = CAN_UPDATE;
1970    proxy.frame = 1;
1971    interp = Tcl_CreateInterp();
1972    if (CreateTmpDir(interp) != TCL_OK) {
1973        trace(Tcl_GetStringResult(interp));
1974    }
1975    Tcl_MakeSafe(interp);
1976    proxy.interp = interp;
1977
1978    Tcl_CreateCommand(interp, "bmp",           BmpCmd,           &proxy, NULL);
1979    Tcl_CreateCommand(interp, "cartoon",       CartoonCmd,       &proxy, NULL);
1980    Tcl_CreateCommand(interp, "cartoontrace",  CartoonTraceCmd,  &proxy, NULL);
1981    Tcl_CreateCommand(interp, "disable",       DisableCmd,       &proxy, NULL);
1982    Tcl_CreateCommand(interp, "enable",        EnableCmd,        &proxy, NULL);
1983    Tcl_CreateCommand(interp, "frame",         FrameCmd,         &proxy, NULL);
1984    Tcl_CreateCommand(interp, "label",         LabelCmd,         &proxy, NULL);
1985    Tcl_CreateCommand(interp, "loadpdb",       LoadPDBCmd,       &proxy, NULL);
1986    Tcl_CreateCommand(interp, "orthoscopic",   OrthoscopicCmd,   &proxy, NULL);
1987    Tcl_CreateCommand(interp, "pan",           PanCmd,           &proxy, NULL);
1988    Tcl_CreateCommand(interp, "png",           PngCmd,           &proxy, NULL);
1989    Tcl_CreateCommand(interp, "print",         PrintCmd,         &proxy, NULL);
1990    Tcl_CreateCommand(interp, "raw",           RawCmd,           &proxy, NULL);
1991    Tcl_CreateCommand(interp, "representation",RepresentationCmd,&proxy, NULL);
1992    Tcl_CreateCommand(interp, "reset",         ResetCmd,         &proxy, NULL);
1993    Tcl_CreateCommand(interp, "rock",          RockCmd,          &proxy, NULL);
1994    Tcl_CreateCommand(interp, "rotate",        RotateCmd,        &proxy, NULL);
1995    Tcl_CreateCommand(interp, "screen",        ScreenCmd,        &proxy, NULL);
1996    Tcl_CreateCommand(interp, "spherescale",   SphereScaleCmd,   &proxy, NULL);
1997    Tcl_CreateCommand(interp, "stickradius",   StickRadiusCmd,   &proxy, NULL);
1998    Tcl_CreateCommand(interp, "transparency",  TransparencyCmd,  &proxy, NULL);
1999    Tcl_CreateCommand(interp, "viewport",      ScreenCmd,        &proxy, NULL);
2000    Tcl_CreateCommand(interp, "vmouse",        VMouseCmd,        &proxy, NULL);
2001    Tcl_CreateCommand(interp, "zoom",          ZoomCmd,          &proxy, NULL);
2002
2003
2004    gettimeofday(&end, NULL);
2005    stats.start = end;
2006
2007    // Main Proxy Loop
2008    //  accept tcl commands from socket
2009    //  translate them into pyMol commands, and issue them to child proccess
2010    //  send images back
2011    PollForEvents(&proxy);
2012
2013    close(proxy.cout);
2014    close(proxy.sout);
2015    close(proxy.serr);
2016    close(proxy.cin);
2017    close(proxy.sin);
2018
2019    status = waitpid(child, &result, WNOHANG);
2020    if (status == -1) {
2021        trace("error waiting on pymol server to exit: %s", strerror(errno));
2022    } else if (status == 0) {
2023        trace("attempting to signal (SIGTERM) pymol server.");
2024        kill(-child, SIGTERM);          // Kill process group
2025        alarm(5);
2026        status = waitpid(child, &result, 0);
2027        alarm(0);
2028       
2029        while ((status == -1) && (errno == EINTR)) {
2030            trace("Attempting to signal (SIGKILL) pymol server.");
2031            kill(-child, SIGKILL);      // Kill process group
2032            alarm(10);
2033            status = waitpid(child, &result, 0);
2034            alarm(0);
2035        }
2036    }
2037   
2038    trace("pymol server process ended (result=%d)", result);
2039    DestroyTmpDir();
2040
2041    Tcl_DeleteInterp(interp);
2042   
2043    if (WIFEXITED(result)) {
2044        result = WEXITSTATUS(result);
2045    }
2046    DoExit(result);
2047    return 0;
2048}
2049
2050#ifdef STANDALONE
2051
2052int
2053main(int argc, char *argv[])
2054{
2055    flog = stderr;
2056    if (debug) {
2057        char fileName[200];
2058        sprintf(fileName, "/tmp/pymolproxy%d.log", getpid());
2059        sprintf(fileName, "/tmp/pymolproxy.log");
2060        flog = fopen(fileName, "w");
2061    }   
2062    scriptFile = NULL;
2063    if (savescript) {
2064        char fileName[200];
2065        sprintf(fileName, "/tmp/pymolproxy.py");
2066        scriptFile = fopen(fileName, "w");
2067    }   
2068    ProxyInit(fileno(stdout), fileno(stdin), argv + 1);
2069    return 0;
2070}
2071
2072#endif
2073
2074
2075static void
2076PollForEvents(PymolProxy *proxyPtr)
2077{
2078    Tcl_DString clientCmds;
2079    struct pollfd pollResults[4];
2080    int flags;
2081
2082    flags = fcntl(proxyPtr->cin, F_GETFL);
2083    fcntl(proxyPtr->cin, F_SETFL, flags|O_NONBLOCK);
2084
2085    pollResults[0].fd = proxyPtr->cout;
2086    pollResults[1].fd = proxyPtr->sout;
2087    pollResults[2].fd = proxyPtr->serr;
2088    pollResults[0].events = pollResults[1].events =
2089        pollResults[2].events = POLLIN;
2090
2091    pollResults[3].fd = proxyPtr->cin;
2092    pollResults[3].events = POLLOUT;
2093
2094    InitBuffer(&proxyPtr->client, proxyPtr->cout);
2095    InitBuffer(&proxyPtr->server, proxyPtr->sout);
2096
2097    Tcl_DStringInit(&clientCmds);
2098    for (;;) {
2099        int timeout, nChannels;
2100
2101        nChannels =  (proxyPtr->headPtr != NULL) ? 4 : 3;
2102
2103#define PENDING_TIMEOUT         10  /* milliseconds. */
2104        timeout = (proxyPtr->flags & UPDATE_PENDING) ? PENDING_TIMEOUT : -1;
2105        nChannels = poll(pollResults, nChannels, timeout);
2106        if (nChannels < 0) {
2107            trace("POLL ERROR: %s", strerror(errno));
2108            continue;           /* or exit? */
2109        }
2110
2111        /*
2112         * The next two sections are to drain any leftover data in
2113         * the pymol server process' stdout or stderr.  We don't want the
2114         * the pymol server to block writing to stderr or stdout.
2115         */
2116        if (pollResults[1].revents & POLLIN) {
2117            int nBytes;
2118            char *line;
2119           
2120            trace("Reading pymol stdout\n");
2121            /* Don't care what's in the server output buffer. */
2122            FlushBuffer(&proxyPtr->server);
2123            line = GetLine(&proxyPtr->server, &nBytes);
2124            if (line != NULL) {
2125                trace("STDOUT>%.*s", nBytes, line);
2126                trace("Done with pymol stdout\n");
2127            } else if (nBytes == BUFFER_CONTINUE) {
2128                trace("Done with pymol stdout\n");
2129            } else {
2130                trace("Failed reading pymol stdout (nBytes=%d)\n", nBytes);
2131                goto error;     /* Get out on EOF or error. */
2132            }
2133        }
2134
2135        if (pollResults[2].revents & POLLIN) {
2136            ssize_t nRead;
2137            char buf[BUFSIZ];
2138           
2139            trace("Reading pymol stderr\n");
2140            /* pyMol Stderr Connection: pymol standard error output */
2141           
2142            nRead = read(pollResults[2].fd, buf, BUFSIZ-1);
2143            if (nRead <= 0) {
2144                trace("unexpected read error from server (stderr): %s",
2145                      strerror(errno));
2146                if (errno != EINTR) {
2147                    trace("lost connection (stderr) to pymol server.");
2148                    return;
2149                }
2150            }
2151            buf[nRead] = '\0';
2152            trace("stderr>%s", buf);
2153            trace("Done reading pymol stderr\n");
2154        }
2155
2156        /* We have some descriptors ready. */
2157        if (pollResults[0].revents & POLLIN) {
2158            trace("Reading client stdout\n");
2159            for (;;) {
2160                int nBytes;
2161                char *line;
2162               
2163                line = GetLine(&proxyPtr->client, &nBytes);
2164                if (line != NULL) {
2165                    const char *cmd;
2166
2167                    Tcl_DStringAppend(&clientCmds, line, nBytes);
2168                    cmd = Tcl_DStringValue(&clientCmds);
2169                    if (Tcl_CommandComplete(cmd)) {
2170                        /* May execute more than one command. */
2171                        ExecuteCommand(proxyPtr->interp, cmd);
2172                        Tcl_DStringSetLength(&clientCmds, 0);
2173                    }
2174                    continue;
2175                }
2176                if (nBytes == BUFFER_CONTINUE) {
2177                    break;
2178                }
2179                trace("Failed reading client stdout (nBytes=%d)\n", nBytes);
2180                goto error;             /* Get out on EOF or error. */
2181            }
2182            trace("done with client stdout\n");
2183        }
2184        /*
2185         * Write the currently queued image if there is one.
2186         *
2187         * We want to transmit the current image back to the client.  But if
2188         * the client's busy (e.g. sending us another command), there's a
2189         * chance we'll deadlock.  Therefore, the file descriptor is
2190         * non-blocking and we write only what we can.  Must be careful not to
2191         * refresh the image until we're done.
2192         */
2193
2194        /* Handle all the pending setting changes now. */
2195        UpdateSettings(proxyPtr);
2196
2197        /* Write the current image buffer. */
2198        if (proxyPtr->headPtr == NULL) {
2199            /* We might want to refresh the image if we're not currently
2200             * transmitting an image back to the client. The image will be
2201             * refreshed after the image has been completely transmitted. */
2202            if ((nChannels == 0) || (proxyPtr->flags & FORCE_UPDATE)) {
2203                if (proxyPtr->flags & UPDATE_PENDING) {
2204                    Tcl_Eval(proxyPtr->interp, "bmp");
2205                    proxyPtr->flags &= ~UPDATE_PENDING;
2206                }
2207                proxyPtr->flags &= ~FORCE_UPDATE;
2208                continue;
2209            }
2210        }
2211        if ((proxyPtr->headPtr != NULL) &&
2212            (pollResults[3].revents & POLLOUT)) {
2213            WriteImage(proxyPtr, pollResults[3].fd);
2214        }
2215    }
2216 error:
2217    Tcl_DStringFree(&clientCmds);
2218    return;
2219}
2220
Note: See TracBrowser for help on using the repository browser.