source: branches/blt4/packages/vizservers/pymolproxy/pymolproxy.c @ 1719

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