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

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