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

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