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

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