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

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