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

Last change on this file since 1565 was 1565, checked in by gah, 13 years ago

add transparent option to background for pymolproxy

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