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

Last change on this file since 1340 was 1340, checked in by gah, 16 years ago

first pass at HQ hardcopy.

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