source: pymolproxy/trunk/pymolproxy.c @ 4589

Last change on this file since 4589 was 3644, checked in by gah, 12 years ago
File size: 61.2 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 ppm-formatted image data.
13 *
14 *  Copyright (c) 2004-2012  HUBzero Foundation, LLC
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 numFrames;           /* # of frames sent to client. */
90    size_t numBytes;            /* # of bytes for all frames. */
91    size_t numCommands;         /* # 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 = 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
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 numWritten;                 /* 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 *numBytesPtr is set to
338 *      BUFFER_CONTINUE.
339 */
340static char *
341NextLine(ReadBuffer *readPtr, int *numBytesPtr)
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                *numBytesPtr = i - readPtr->mark;
364                readPtr->mark = i;
365#if READTRACE
366                Debug("Leaving NextLine(%.*s)\n", *numBytesPtr, 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            *numBytesPtr = 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            *numBytesPtr = 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    *numBytesPtr = 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 numBytes)
401{
402#if READTRACE
403    Debug("Entering ReadFollowingData(%d)\n", numBytes);
404#endif
405    while (numBytes > 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 >  numBytes) ? numBytes : bytesLeft;
415            memcpy(out, readPtr->bytes + readPtr->mark, size);
416            readPtr->mark += size;
417            numBytes -= size;
418            out += size;
419        }
420        if (numBytes == 0) {
421            /* Received requested # bytes. */
422#if READTRACE
423            Debug("Leaving ReadFollowingData(%d)\n", numBytes);
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", numBytes);
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 numBytes;
466        char *line;
467
468        line = NextLine(&proxyPtr->server, &numBytes);
469        if (line != NULL) {
470#if EXPECTTRACE
471            Debug("pymol says (read %d bytes):%.*s", numBytes, numBytes, line);
472#endif
473            if ((c == line[0]) && (strncmp(line, match, length) == 0)) {
474                if (maxSize < numBytes) {
475                    numBytes = maxSize;
476                }
477                memcpy(out, line, numBytes);
478                clear_error(proxyPtr);
479#if EXPECTTRACE
480                Debug("Leaving Expect: got (%.*s)\n", numBytes, out);
481#endif
482                return TCL_OK;
483            }
484            continue;
485        }
486        if (numBytes == 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.numFrames);
551    Tcl_DStringAppend(&ds, buf, -1);
552    sprintf(buf, "frame_bytes=\"%lu\" ", (unsigned long int)stats.numBytes);
553    Tcl_DStringAppend(&ds, buf, -1);
554    sprintf(buf, "num_commands=\"%lu\" ", (unsigned long int)stats.numCommands);
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#ifndef STATSDIR
586#define STATSDIR        "/var/tmp/visservers"
587#endif
588#define STATSFILE       STATSDIR "/" "data.xml"
589        if (access(STATSDIR, X_OK) != 0) {
590            mkdir(STATSDIR, 0770);
591        }
592        length = Tcl_DStringLength(&ds);
593        f = open(STATSFILE, O_APPEND | O_CREAT | O_WRONLY, 0600);
594        result = FALSE;
595        if (f < 0) {
596            goto error;
597        }
598        if (write(f, Tcl_DStringValue(&ds), length) != length) {
599            goto error;
600        }
601        result = TRUE;
602 error:
603        if (f >= 0) {
604            close(f);
605        }
606        Tcl_DStringFree(&ds);
607        return result;
608    }
609}
610#endif
611
612static void
613DoExit(int code)
614{
615#if KEEPSTATS
616    WriteStats("pymolproxy", code);
617#endif
618   
619    exit(code);
620}
621
622static int
623ExecuteCommand(Tcl_Interp *interp, const char *cmd)
624{
625    struct timeval tv;
626    double start, finish;
627    int result;
628
629    gettimeofday(&tv, NULL);
630    start = CVT2SECS(tv);
631
632    Debug("command from client is (%s)", cmd);
633    result = Tcl_Eval(interp, cmd);
634
635    gettimeofday(&tv, NULL);
636    finish = CVT2SECS(tv);
637
638    stats.cmdTime += finish - start;
639    stats.numCommands++;
640    return result;
641}
642
643static Image *
644NewImage(PymolProxy *proxyPtr, size_t dataLength)
645{
646    Image *imgPtr;
647
648    imgPtr = malloc(sizeof(Image) + dataLength);
649    if (imgPtr == NULL) {
650        ERROR("can't allocate image of %lu bytes",
651              (unsigned long)(sizeof(Image) + dataLength));
652        abort();
653    }
654    imgPtr->prevPtr = imgPtr->nextPtr = NULL;
655    imgPtr->bytesLeft = dataLength;
656    if (proxyPtr->headPtr != NULL) {
657        proxyPtr->headPtr->prevPtr = imgPtr;
658    }
659    imgPtr->nextPtr = proxyPtr->headPtr;
660    if (proxyPtr->tailPtr == NULL) {
661        proxyPtr->tailPtr = imgPtr;
662    }
663    proxyPtr->headPtr = imgPtr;
664    imgPtr->numWritten = 0;
665    return imgPtr;
666}
667
668INLINE static void
669FreeImage(Image *imgPtr)
670{
671    assert(imgPtr != NULL);
672    free(imgPtr);
673}
674
675static void
676WriteImage(PymolProxy *proxyPtr, int fd)
677{
678    Image *imgPtr, *prevPtr;
679
680    if (proxyPtr->tailPtr == NULL) {
681        ERROR("Should not be here: no image available to write");
682        return;
683    }
684    for (imgPtr = proxyPtr->tailPtr; imgPtr != NULL; imgPtr = prevPtr) {
685        ssize_t bytesLeft;
686
687        assert(imgPtr->nextPtr == NULL);
688        prevPtr = imgPtr->prevPtr;
689#if WRITETRACE
690        Debug("WriteImage: want to write %d bytes.", imgPtr->bytesLeft);
691#endif
692        for (bytesLeft = imgPtr->bytesLeft; bytesLeft > 0; /*empty*/) {
693            ssize_t numWritten;
694#if WRITETRACE
695            Debug("WriteImage: try to write %d bytes.", bytesLeft);
696#endif
697            numWritten = write(fd, imgPtr->data + imgPtr->numWritten, bytesLeft);
698#if WRITETRACE
699            Debug("WriteImage: wrote %d bytes.", numWritten);
700#endif
701            if (numWritten < 0) {
702                ERROR("Error writing fd=%d, %s", fd, strerror(errno));
703                return;
704            }
705            bytesLeft -= numWritten;
706            if (bytesLeft > 0) {
707                /* Wrote a short buffer, means we would block. */
708                imgPtr->numWritten += numWritten;
709                imgPtr->bytesLeft = bytesLeft;
710                return;
711            }
712            imgPtr->numWritten += numWritten;
713        }
714        /* Check if image is on the head.  */
715        proxyPtr->tailPtr = prevPtr;
716        if (prevPtr != NULL) {
717            prevPtr->nextPtr = NULL;
718        }
719        FreeImage(imgPtr);
720    }
721    proxyPtr->headPtr = NULL;
722}
723
724static int
725Pymol(PymolProxy *proxyPtr, const char *format, ...)
726{
727    va_list ap;
728    char buffer[BUFSIZ];
729    char expect[BUFSIZ];
730    int result;
731    ssize_t numWritten;
732    size_t length;
733    char *p;
734
735    if (proxyPtr->error) {
736        return TCL_ERROR;
737    }
738   
739    va_start(ap, format);
740    result = vsnprintf(buffer, BUFSIZ-1, format, ap);
741    va_end(ap);
742   
743    Debug("to-pymol>(%s) code=%d", buffer, result);
744    script("%s\n", buffer);
745   
746   
747    /* Write the command out to the server. */
748    length = strlen(buffer);
749    numWritten = write(proxyPtr->sin, buffer, length);
750    if (numWritten != length) {
751        ERROR("short write to pymol (wrote=%d, should have been %d): %s",
752              numWritten, length, strerror(errno));
753    }
754    for (p = buffer; *p != '\0'; p++) {
755        if (*p == '\n') {
756            *p = '\0';
757            break;
758        }
759    }
760    sprintf(expect, "PyMOL>%s", buffer);
761    /* Now wait for the "PyMOL>" prompt. */
762    if (Expect(proxyPtr, expect, buffer, BUFSIZ) != TCL_OK) {
763        proxyPtr->error = 1;
764        proxyPtr->status = TCL_ERROR;
765        return TCL_ERROR;
766    }
767    return  proxyPtr->status;
768}
769
770static void
771SetViewport(PymolProxy *proxyPtr)
772{
773    if (proxyPtr->flags & VIEWPORT_PENDING) {
774        Pymol(proxyPtr, "viewport %d,%d\n", proxyPtr->width, proxyPtr->height);
775        proxyPtr->flags &= ~VIEWPORT_PENDING;
776    }
777}
778
779static void
780SetZoom(PymolProxy *proxyPtr)
781{
782    if (proxyPtr->flags & ZOOM_PENDING) {
783        Pymol(proxyPtr,"move z,%f\n", proxyPtr->zoom);
784        proxyPtr->flags &= ~ZOOM_PENDING;
785    }
786}
787
788static void
789SetPan(PymolProxy *proxyPtr)
790{
791    if (proxyPtr->flags & PAN_PENDING) {
792        Pymol(proxyPtr,"move x,%f\nmove y,%f\n", proxyPtr->xPan,proxyPtr->yPan);
793        proxyPtr->flags &= ~PAN_PENDING;
794    }
795}
796
797static void
798SetRotation(PymolProxy *proxyPtr)
799{
800    if (proxyPtr->flags & ROTATE_PENDING) {
801        /* Every pymol command line generates a new rendering. Execute all
802         * three turns as a single command line. */
803        Pymol(proxyPtr,"turn x,%f\nturn y,%f\nturn z,%f\n",
804              proxyPtr->xAngle, proxyPtr->yAngle, proxyPtr->zAngle);
805        proxyPtr->xAngle = proxyPtr->yAngle = proxyPtr->zAngle = 0.0f;
806        proxyPtr->flags &= ~ROTATE_PENDING;
807    }
808}
809
810static void
811SetSphereScale(PymolProxy *proxyPtr)
812{
813    if (proxyPtr->flags & ATOM_SCALE_PENDING) {
814        Pymol(proxyPtr, "set sphere_scale,%f,all\n", proxyPtr->sphereScale);
815        proxyPtr->flags &= ~ATOM_SCALE_PENDING;
816    }
817}
818
819static void
820SetStickRadius(PymolProxy *proxyPtr)
821{
822    if (proxyPtr->flags & STICK_RADIUS_PENDING) {
823        Pymol(proxyPtr, "set stick_radius,%f,all\n", proxyPtr->stickRadius);
824        proxyPtr->flags &= ~STICK_RADIUS_PENDING;
825    }
826}
827
828static void
829UpdateSettings(PymolProxy *proxyPtr)
830{
831    /* Handle all the pending setting changes now. */
832    if (proxyPtr->flags & VIEWPORT_PENDING) {
833        SetViewport(proxyPtr);
834    }
835    if (proxyPtr->flags & ROTATE_PENDING) {
836        SetRotation(proxyPtr);
837    }
838    if (proxyPtr->flags & PAN_PENDING) {
839        SetPan(proxyPtr);
840    }
841    if (proxyPtr->flags & ZOOM_PENDING) {
842        SetZoom(proxyPtr);
843    }
844    if (proxyPtr->flags & ATOM_SCALE_PENDING) {
845        SetSphereScale(proxyPtr);
846    }
847    if (proxyPtr->flags & STICK_RADIUS_PENDING) {
848        SetStickRadius(proxyPtr);
849    }
850}
851
852static int
853CartoonCmd(ClientData clientData, Tcl_Interp *interp, int argc,
854           const char *argv[])
855{
856    int bool, defer, push, i;
857    PymolProxy *proxyPtr = clientData;
858    const char *model;
859
860    clear_error(proxyPtr);
861    defer = push = FALSE;
862    model = "all";
863    bool = FALSE;
864    for(i = 1; i < argc; i++) {
865        if (strcmp(argv[i],"-defer") == 0) {
866            defer = TRUE;
867        } else if (strcmp(argv[i],"-push") == 0) {
868            push = TRUE;
869        } else if (strcmp(argv[i],"-model") == 0) {
870            if (++i < argc) {
871                model = argv[i];
872            }
873        } else {
874            if (Tcl_GetBoolean(interp, argv[i], &bool) != TCL_OK) {
875                return TCL_ERROR;
876            }
877        }
878    }
879    proxyPtr->flags |= INVALIDATE_CACHE; /* cartoon */
880    if (!defer || push) {
881        proxyPtr->flags |= UPDATE_PENDING;
882    }
883    if (push) {
884        proxyPtr->flags |= FORCE_UPDATE;
885    }
886    if (bool) {
887        Pymol(proxyPtr, "show cartoon,%s\n", model);
888    } else {
889        Pymol(proxyPtr, "hide cartoon,%s\n", model);
890    }
891    return proxyPtr->status;
892}
893
894static int
895CartoonTraceCmd(ClientData clientData, Tcl_Interp *interp, int argc,
896                const char *argv[])
897{
898    int bool, defer, push, i;
899    PymolProxy *proxyPtr = clientData;
900    const char *model;
901
902    clear_error(proxyPtr);
903    defer = push = FALSE;
904    bool = FALSE;
905    model = "all";
906    for(i = 1; i < argc; i++) {
907        if (strcmp(argv[i],"-defer") == 0) {
908            defer = TRUE;
909        } else if (strcmp(argv[i],"-push") == 0) {
910            push = TRUE;
911        } else if (strcmp(argv[i],"-model") == 0) {
912            if (++i < argc) {
913                model = argv[i];
914            }
915        } else {
916            if (Tcl_GetBoolean(interp, argv[i], &bool) != TCL_OK) {
917                return TCL_ERROR;
918            }
919        }
920    }
921    proxyPtr->flags |= INVALIDATE_CACHE; /* cartoon_trace  */
922    if (!defer || push) {
923        proxyPtr->flags |= UPDATE_PENDING;
924    }
925    if (push) {
926        proxyPtr->flags |= FORCE_UPDATE;
927    }
928    Pymol(proxyPtr, "set cartoon_trace,%d,%s\n", bool, model);
929    return proxyPtr->status;
930}
931
932static int
933DisableCmd(ClientData clientData, Tcl_Interp *interp, int argc,
934           const char *argv[])
935{
936    PymolProxy *proxyPtr = clientData;
937    const char *model = "all";
938    int i, defer = 0, push = 0;
939
940    clear_error(proxyPtr);
941
942    for(i = 1; i < argc; i++) {
943
944        if (strcmp(argv[i], "-defer") == 0 )
945            defer = 1;
946        else if (strcmp(argv[i], "-push") == 0 )
947            push = 1;
948        else
949            model = argv[i];
950       
951    }
952
953    proxyPtr->flags |= INVALIDATE_CACHE;  /* disable */
954    if (!defer || push) {
955        proxyPtr->flags |= UPDATE_PENDING;
956    }
957    if (push) {
958        proxyPtr->flags |= FORCE_UPDATE;
959    }
960
961    Pymol( proxyPtr, "disable %s\n", model);
962
963    return proxyPtr->status;
964}
965
966
967static int
968EnableCmd(ClientData clientData, Tcl_Interp *interp, int argc,
969          const char *argv[])
970{
971    PymolProxy *proxyPtr = clientData;
972    const char *model;
973    int i, defer, push;
974
975    clear_error(proxyPtr);
976    push = defer = FALSE;
977    model = "all";
978    for(i = 1; i < argc; i++) {
979        if (strcmp(argv[i],"-defer") == 0) {
980            defer = TRUE;
981        } else if (strcmp(argv[i], "-push") == 0) {
982            push = TRUE;
983        } else {
984            model = argv[i];
985        }
986    }
987    proxyPtr->flags |= INVALIDATE_CACHE; /* enable */
988    if (!defer || push) {
989        proxyPtr->flags |= UPDATE_PENDING;
990    }
991    if (push) {
992        proxyPtr->flags |= FORCE_UPDATE;
993    }
994    Pymol( proxyPtr, "enable %s\n", model);
995    return proxyPtr->status;
996}
997
998static int
999FrameCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1000         const char *argv[])
1001{
1002    PymolProxy *proxyPtr = clientData;
1003    int i, push, defer, frame;
1004
1005    clear_error(proxyPtr);
1006    frame = 0;
1007    push = defer = TRUE;
1008    for(i = 1; i < argc; i++) {
1009        if (strcmp(argv[i],"-defer") == 0) {
1010            defer = TRUE;
1011        } else if (strcmp(argv[i],"-push") == 0) {
1012            push = TRUE;
1013        } else {
1014            frame = atoi(argv[i]);
1015        }
1016    }
1017    if (!defer || push) {
1018        proxyPtr->flags |= UPDATE_PENDING;
1019    }
1020    if (push) {
1021        proxyPtr->flags |= FORCE_UPDATE;
1022    }
1023    proxyPtr->frame = frame;
1024
1025    /* Does not invalidate cache? */
1026
1027    Pymol(proxyPtr,"frame %d\n", frame);
1028    return proxyPtr->status;
1029}
1030
1031
1032static int
1033LabelCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1034         const char *argv[])
1035{
1036    PymolProxy *proxyPtr = clientData;
1037    int i, push, defer, state, size;
1038    const char *model;
1039
1040    clear_error(proxyPtr);
1041    model = "all";
1042    size = 14;
1043    state = TRUE;
1044    push = defer = FALSE;
1045    for(i = 1; i < argc; i++) {
1046        if (strcmp(argv[i],"-defer") == 0) {
1047            defer = TRUE;
1048        } else if (strcmp(argv[i],"-push") == 0) {
1049            push = TRUE;
1050        } else if (strcmp(argv[i],"-model") == 0) {
1051            if (++i < argc) {
1052                model = argv[i];
1053            }
1054        } else if (strcmp(argv[i],"-size") == 0) {
1055            if (++i < argc) {
1056                size = atoi(argv[i]);
1057            }
1058        } else if (Tcl_GetBoolean(interp, argv[i], &state) != TCL_OK) {
1059            return TCL_ERROR;
1060        }
1061    }
1062    proxyPtr->flags |= INVALIDATE_CACHE;  /* label */
1063    if (!defer || push) {
1064        proxyPtr->flags |= UPDATE_PENDING;
1065    }
1066    if (push) {
1067        proxyPtr->flags |= FORCE_UPDATE;
1068    }
1069    Pymol(proxyPtr,
1070          "set label_color,white,%s\n"
1071          "set label_size,%d,%s\n",
1072          model, size, model);
1073    if (state) {
1074        Pymol(proxyPtr, "label %s,\"%%s%%s\" %% (ID,name)\n", model);
1075    } else {
1076        Pymol(proxyPtr, "label %s\n", model);
1077    }
1078    return proxyPtr->status;
1079}
1080
1081/*
1082 * LoadPDBCmd --
1083 *
1084 *      Load a PDB file into pymol.  There is no good way to load PDB data
1085 *      into pymol without using a file.  This causes problems in that we
1086 *      don't know when pymol is done with the file.  So there's always
1087 *      a bit of mess left around. We use the same file for every file
1088 *      so there's a chance that pymol is still using a file while we're
1089 *      overwriting it. 
1090 *
1091 *      The command expects the number of bytes in the pdb data, the
1092 *      name, and the state.
1093 */
1094static int
1095LoadPDBCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1096           const char *argv[])
1097{
1098    const char *data, *name;
1099    char *allocated;
1100    PymolProxy *proxyPtr = clientData;
1101    int state, defer, push;
1102    int numBytes;
1103    int i, j;
1104
1105    if (proxyPtr == NULL){
1106        return TCL_ERROR;
1107    }
1108    clear_error(proxyPtr);
1109    defer = push = FALSE;
1110    for(i = j = 1; i < argc; i++) {
1111        if (strcmp(argv[i],"-defer") == 0) {
1112            defer = TRUE;
1113        } else if (strcmp(argv[i],"-push") == 0) {
1114            push = TRUE;
1115        } else {
1116            if (j < i) {
1117                argv[j] = argv[i];
1118            }
1119            j++;
1120        }
1121    }
1122    argc = j;
1123    if (argc < 4) {
1124        Tcl_AppendResult(interp, "wrong # arguments: should be \"", argv[0],
1125                         " <data>|follows <model> <state> ?<numBytes>?\"",
1126                         (char *)NULL);
1127        return TCL_ERROR;
1128    }
1129    data = argv[1];
1130    name = argv[2];
1131    if (Tcl_GetInt(interp, argv[3], &state) != TCL_OK) {
1132        return TCL_ERROR;
1133    }
1134    numBytes = -1;
1135    if (strcmp(data, "follows") == 0) {
1136        if (argc != 5) {
1137            Tcl_AppendResult(interp, "wrong # arguments: should be \"", argv[0],
1138                         " follows <model> <state> <numBytes>\"", (char *)NULL);
1139            return TCL_ERROR;
1140        }
1141        if (Tcl_GetInt(interp, argv[4], &numBytes) != TCL_OK) {
1142            return TCL_ERROR;
1143        }
1144        if (numBytes < 0) {
1145            Tcl_AppendResult(interp, "bad value for # bytes \"", argv[4],
1146                         "\"", (char *)NULL);
1147            return TCL_ERROR;
1148        }
1149    }
1150    if (!defer || push) {
1151        proxyPtr->flags |= UPDATE_PENDING;
1152    }
1153    if (push) {
1154        proxyPtr->flags |= FORCE_UPDATE;
1155    }
1156
1157    /* Does not invalidate cache? */
1158
1159    allocated = NULL;
1160    if (numBytes >= 0) {
1161        allocated = malloc(sizeof(char) * numBytes);
1162        if (allocated == NULL) {
1163            Tcl_AppendResult(interp, "can't allocate buffer for pdbdata.",
1164                             (char *)NULL);
1165            return TCL_ERROR;
1166        }
1167        if (ReadFollowingData(&proxyPtr->client, allocated, numBytes)
1168            != BUFFER_OK) {
1169            Tcl_AppendResult(interp, "can't read pdbdata from client.",
1170                             (char *)NULL);
1171            free(allocated);
1172            return TCL_ERROR;
1173        }
1174        data = allocated;
1175    } else {
1176        numBytes = strlen(data);
1177    }
1178    {
1179        int f;
1180        ssize_t numWritten;
1181        char fileName[200];
1182
1183        strcpy(fileName, "/tmp/pdb.XXXXXX");
1184        proxyPtr->status = TCL_ERROR;
1185        f = mkstemp(fileName);
1186        if (f < 0) {
1187            Tcl_AppendResult(interp, "can't create temporary file \"",
1188                fileName, "\":", Tcl_PosixError(interp), (char *)NULL);
1189            goto error;
1190        }
1191        numWritten = write(f, data, numBytes);
1192        if (numBytes != numWritten) {
1193            Tcl_AppendResult(interp, "can't write PDB data to \"",
1194                             fileName, "\": ", Tcl_PosixError(interp),
1195                             (char *)NULL);
1196            close(f);
1197            goto error;
1198        }
1199        close(f);
1200        Pymol(proxyPtr, "loadandremovepdbfile %s,%s,%d\n", fileName, name, state);
1201        proxyPtr->status = TCL_OK;
1202    }
1203 error:
1204    if (allocated != NULL) {
1205        free(allocated);
1206    }
1207    return proxyPtr->status;
1208}
1209
1210static int
1211OrthoscopicCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1212              const char *argv[])
1213{
1214    int bool, defer, push, i;
1215    PymolProxy *proxyPtr = clientData;
1216
1217    clear_error(proxyPtr);
1218    defer = push = FALSE;
1219    bool = FALSE;
1220    for(i = 1; i < argc; i++) {
1221        if (strcmp(argv[i],"-defer") == 0) {
1222            defer = TRUE;
1223        } else if (strcmp(argv[i],"-push") == 0) {
1224            push = TRUE;
1225        } else {
1226            if (Tcl_GetBoolean(interp, argv[i], &bool) != TCL_OK) {
1227                return TCL_ERROR;
1228            }
1229        }
1230    }
1231    proxyPtr->flags |= INVALIDATE_CACHE; /* orthoscopic */
1232    if (!defer || push) {
1233        proxyPtr->flags |= UPDATE_PENDING;
1234    }
1235    if (push) {
1236        proxyPtr->flags |= FORCE_UPDATE;
1237    }
1238    Pymol(proxyPtr, "set orthoscopic=%d\n", bool);
1239    return proxyPtr->status;
1240}
1241
1242/*
1243 * PanCmd --
1244 *
1245 *      Issue "move" commands for changes in the x and y coordinates of the
1246 *      camera.  The problem here is that there is no event compression.
1247 *      Consecutive "pan" commands are not compressed into a single
1248 *      directive.  The means that the pymol server will render scenes that
1249 *      are not used by the client.
1250 *
1251 *      Need to 1) defer the "move" commands until we find the next command
1252 *      isn't a "pan". 2) Track the x and y coordinates as they are
1253 *      compressed.
1254 */
1255static int
1256PanCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1257        const char *argv[])
1258{
1259    PymolProxy *proxyPtr = clientData;
1260    double x, y;
1261    int i;
1262    int defer, push;
1263
1264    clear_error(proxyPtr);
1265    defer = push = FALSE;
1266    for (i = 1; i < argc; i++) {
1267        if (strcmp(argv[i],"-defer") == 0) {
1268            defer = 1;
1269        } else if (strcmp(argv[i],"-push") == 0) {
1270            push = 1;
1271        } else {
1272            break;
1273        }
1274    }
1275    if ((Tcl_GetDouble(interp, argv[i], &x) != TCL_OK) ||
1276        (Tcl_GetDouble(interp, argv[i+1], &y) != TCL_OK)) {
1277        return TCL_ERROR;
1278    }
1279    proxyPtr->flags |= INVALIDATE_CACHE; /* pan */
1280    if (!defer || push) {
1281        proxyPtr->flags |= UPDATE_PENDING;
1282    }
1283    if (push) {
1284        proxyPtr->flags |= FORCE_UPDATE;
1285    }
1286    if ((x != 0.0f) || (y != 0.0f)) {
1287        proxyPtr->xPan = x * 0.05;
1288        proxyPtr->yPan = -y * 0.05;
1289        proxyPtr->flags |= PAN_PENDING;
1290    }
1291    return proxyPtr->status;
1292}
1293
1294static int
1295PngCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
1296{
1297    char buffer[BUFSIZ];
1298    int numBytes=0;
1299    PymolProxy *proxyPtr = clientData;
1300    size_t length;
1301    Image *imgPtr;
1302
1303    clear_error(proxyPtr);
1304
1305    /* Force pymol to update the current scene. */
1306    Pymol(proxyPtr, "refresh\n");
1307    Pymol(proxyPtr, "png -\n");
1308
1309    if (Expect(proxyPtr, "png image follows: ", buffer, BUFSIZ-1) != TCL_OK) {
1310        return TCL_ERROR;
1311    }
1312    if (sscanf(buffer, "png image follows: %d\n", &numBytes) != 1) {
1313        Tcl_AppendResult(interp, "can't get # bytes from \"", buffer, "\"",
1314                         (char *)NULL);
1315        return TCL_ERROR;
1316    }
1317    sprintf(buffer, "nv>image %d %d %d %d\n", numBytes, proxyPtr->cacheId,
1318            proxyPtr->frame, proxyPtr->rockOffset);
1319    length = strlen(buffer);
1320    imgPtr = NewImage(proxyPtr, numBytes + length);
1321    strcpy(imgPtr->data, buffer);
1322    if (ReadFollowingData(&proxyPtr->server, imgPtr->data + length, numBytes)
1323        != BUFFER_OK) {
1324        ERROR("can't read %d bytes for \"image follows\" buffer: %s", numBytes,
1325              strerror(errno));
1326        return  TCL_ERROR;
1327    }
1328    if (Expect(proxyPtr, " ScenePNG", buffer, BUFSIZ-1) != TCL_OK) {
1329        return TCL_ERROR;
1330    }
1331    stats.numFrames++;
1332    stats.numBytes += numBytes;
1333    return proxyPtr->status;
1334}
1335
1336
1337static int
1338PpmCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
1339{
1340    char buffer[BUFSIZ];
1341    int numBytes=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 -,format=1\n");
1351
1352    if (Expect(proxyPtr, "ppm image follows: ", buffer, BUFSIZ-1) != TCL_OK) {
1353        return TCL_ERROR;
1354    }
1355    if (sscanf(buffer, "ppm image follows: %d\n", &numBytes) != 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", numBytes, proxyPtr->cacheId,
1361            proxyPtr->frame, proxyPtr->rockOffset);
1362    length = strlen(buffer);
1363    imgPtr = NewImage(proxyPtr, numBytes + length);
1364    strcpy(imgPtr->data, buffer);
1365    if (ReadFollowingData(&proxyPtr->server, imgPtr->data + length, numBytes)
1366        != BUFFER_OK) {
1367        ERROR("can't read %d bytes for \"image follows\" buffer: %s", numBytes,
1368              strerror(errno));
1369        return  TCL_ERROR;
1370    }
1371#ifdef notdef
1372    if (Expect(proxyPtr, " ScenePNG", buffer, BUFSIZ-1) != TCL_OK) {
1373        return TCL_ERROR;
1374    }
1375#endif
1376    stats.numFrames++;
1377    stats.numBytes += numBytes;
1378    return proxyPtr->status;
1379}
1380
1381
1382static int
1383PrintCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1384         const char *argv[])
1385{
1386    char buffer[800];
1387    int numBytes=0;
1388    PymolProxy *proxyPtr = clientData;
1389    size_t length;
1390    Image *imgPtr;
1391    int width, height;
1392    const char *token, *bgcolor;
1393
1394    clear_error(proxyPtr);
1395
1396    if (argc != 5) {
1397        Tcl_AppendResult(interp, "wrong # arguments: should be \"",
1398                         argv[0], " token width height color\"", (char *)NULL);
1399        return TCL_ERROR;
1400    }
1401    token = argv[1];
1402    if (Tcl_GetInt(interp, argv[2], &width) != TCL_OK) {
1403        return TCL_ERROR;
1404    }
1405    if (Tcl_GetInt(interp, argv[3], &height) != TCL_OK) {
1406        return TCL_ERROR;
1407    }
1408    bgcolor = argv[4];
1409    /* Force pymol to update the current scene. */
1410    if (strcmp(bgcolor, "none") == 0) {
1411        Pymol(proxyPtr, "set ray_opaque_background,off\n");
1412        Pymol(proxyPtr, "refresh\n", bgcolor);
1413    } else {
1414        Pymol(proxyPtr, "set ray_opaque_background,on\n");
1415        Pymol(proxyPtr, "bg_color %s\nrefresh\n", bgcolor);
1416    }
1417    Pymol(proxyPtr, "ray %d,%d\n", width, height);
1418    if (Expect(proxyPtr, " Ray:", buffer, BUFSIZ-1) != TCL_OK) {
1419        return TCL_ERROR;
1420    }
1421
1422    Pymol(proxyPtr, "png -,dpi=300\nbg_color black\n");
1423    if (Expect(proxyPtr, "png image follows: ", buffer, BUFSIZ-1) != TCL_OK) {
1424        return TCL_ERROR;
1425    }
1426
1427    if (sscanf(buffer, "png image follows: %d\n", &numBytes) != 1) {
1428        Tcl_AppendResult(interp, "can't get # bytes from \"", buffer, "\"",
1429                         (char *)NULL);
1430        return TCL_ERROR;
1431    }
1432    sprintf(buffer, "nv>image %d print \"%s\" %d\n", numBytes, token,
1433            proxyPtr->rockOffset);
1434    Debug("header is png is (%s)\n", buffer);
1435    length = strlen(buffer);
1436    imgPtr = NewImage(proxyPtr, numBytes + length);
1437    strcpy(imgPtr->data, buffer);
1438    if (ReadFollowingData(&proxyPtr->server, imgPtr->data + length, numBytes)
1439        != BUFFER_OK) {
1440        ERROR("can't read %d bytes for \"image follows\" buffer: %s", numBytes,
1441              strerror(errno));
1442        return  TCL_ERROR;
1443    }
1444    if (Expect(proxyPtr, " ScenePNG", buffer, BUFSIZ-1) != TCL_OK) {
1445        return TCL_ERROR;
1446    }
1447
1448    stats.numFrames++;
1449    stats.numBytes += numBytes;
1450    return proxyPtr->status;
1451}
1452
1453static int
1454RawCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
1455{
1456    PymolProxy *proxyPtr = clientData;
1457    int arg, defer = 0, push = 0;
1458    const char *cmd;
1459    clear_error(proxyPtr);
1460
1461    cmd = NULL;
1462    for(arg = 1; arg < argc; arg++) {
1463        if (strcmp(argv[arg], "-defer") == 0)
1464            defer = 1;
1465        else if (strcmp(argv[arg], "-push") == 0)
1466            push = 1;
1467        else {
1468            cmd = argv[arg];
1469        }
1470    }
1471
1472    proxyPtr->flags |= INVALIDATE_CACHE; /* raw */
1473    if (!defer || push) {
1474        proxyPtr->flags |= UPDATE_PENDING;
1475    }
1476    if (push) {
1477        proxyPtr->flags |= FORCE_UPDATE;
1478    }
1479    Pymol(proxyPtr,"%s\n", cmd);
1480    return proxyPtr->status;
1481}
1482
1483static int
1484ResetCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1485         const char *argv[])
1486{
1487    PymolProxy *proxyPtr = clientData;
1488    int arg, push = 0, defer = 0;
1489
1490    clear_error(proxyPtr);
1491
1492    for(arg = 1; arg < argc; arg++) {
1493        if ( strcmp(argv[arg],"-defer") == 0 )
1494            defer = 1;
1495        else if (strcmp(argv[arg],"-push") == 0 )
1496            push = 1;
1497    }
1498               
1499    proxyPtr->flags |= INVALIDATE_CACHE; /* reset */
1500    if (!defer || push) {
1501        proxyPtr->flags |= UPDATE_PENDING;
1502    }
1503    if (push) {
1504        proxyPtr->flags |= FORCE_UPDATE;
1505    }
1506    Pymol(proxyPtr,
1507          "reset\n"
1508          "zoom complete=1\n");
1509    return proxyPtr->status;
1510}
1511
1512static int
1513RockCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1514        const char *argv[])
1515{
1516    PymolProxy *proxyPtr = clientData;
1517    float y = 0.0;
1518    int arg, push = 0, defer = 0;
1519
1520    clear_error(proxyPtr);
1521
1522    for(arg = 1; arg < argc; arg++) {
1523        if ( strcmp(argv[arg],"-defer") == 0 )
1524            defer = 1;
1525        else if (strcmp(argv[arg],"-push") == 0 )
1526            push = 1;
1527        else
1528            y = atof( argv[arg] );
1529    }
1530               
1531    /* Does not invalidate cache. */
1532
1533    if (!defer || push) {
1534        proxyPtr->flags |= UPDATE_PENDING;
1535    }
1536    if (push) {
1537        proxyPtr->flags |= FORCE_UPDATE;
1538    }
1539    Pymol(proxyPtr,"turn y, %f\n", y - proxyPtr->rockOffset);
1540    proxyPtr->rockOffset = y;
1541    return proxyPtr->status;
1542}
1543
1544static int
1545RepresentationCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1546                  const char *argv[])
1547{
1548    PymolProxy *proxyPtr = clientData;
1549    const char *model;
1550    const char *rep;
1551    int defer, push, i;
1552
1553    clear_error(proxyPtr);
1554    defer = push = FALSE;
1555    model = "all";
1556    rep = NULL;
1557    for (i = 1; i < argc; i++) {
1558        if (strcmp(argv[i],"-defer") == 0 ) {
1559            defer = TRUE;
1560        } else if (strcmp(argv[i],"-push") == 0) {
1561            push = TRUE;
1562        } else if (strcmp(argv[i],"-model") == 0) {
1563            if (++i < argc) {
1564                model = argv[i];
1565            }
1566        } else {
1567            rep = argv[i];
1568        }
1569    }
1570    if (rep == NULL) {
1571        Tcl_AppendResult(interp, "missing representation argument",
1572                         (char *)NULL);
1573        return TCL_ERROR;
1574    }
1575
1576    proxyPtr->flags |= INVALIDATE_CACHE; /* representation */
1577    if (!defer || push) {
1578        proxyPtr->flags |= UPDATE_PENDING;
1579    }
1580    if (push) {
1581        proxyPtr->flags |= FORCE_UPDATE;
1582    }
1583    if (strcmp(rep, "ballnstick") == 0) { /* Ball 'n Stick */
1584        Pymol(proxyPtr,
1585              "set stick_color,white,%s\n"
1586              "show sticks,%s\n"
1587              "show spheres,%s\n"
1588              "hide lines,%s\n"
1589              "hide cartoon,%s\n",
1590              model, model, model, model, model);
1591    } else if (strcmp(rep, "spheres") == 0) { /* spheres */   
1592        Pymol(proxyPtr,
1593              "hide sticks,%s\n"
1594              "show spheres,%s\n"
1595              "hide lines,%s\n"
1596              "hide cartoon,%s\n"
1597              "set sphere_quality,2,%s\n"
1598              "set ambient,.2,%s\n",
1599              model, model, model, model, model, model);
1600    } else if (strcmp(rep, "none") == 0) { /* nothing */   
1601        Pymol(proxyPtr,
1602              "hide sticks,%s\n",
1603              "hide spheres,%s\n"
1604              "hide lines,%s\n"
1605              "hide cartoon,%s\n",
1606              model, model, model, model);
1607    } else if (strcmp(rep, "sticks") == 0) { /* sticks */   
1608        Pymol(proxyPtr,
1609              "set stick_color,white,%s\n"
1610              "show sticks,%s\n"
1611              "hide spheres,%s\n"
1612              "hide lines,%s\n"
1613              "hide cartoon,%s\n",
1614              model, model, model, model, model);
1615    } else if (strcmp(rep, "lines") == 0) { /* lines */   
1616        Pymol(proxyPtr,
1617              "hide sticks,%s\n"
1618              "hide spheres,%s\n"
1619              "show lines,%s\n"
1620              "hide cartoon,%s\n",
1621              model, model, model, model);
1622    } else if (strcmp(rep, "cartoon") == 0) { /* cartoon */   
1623        Pymol(proxyPtr,
1624              "hide sticks,%s\n"
1625              "hide spheres,%s\n"
1626              "hide lines,%s\n"
1627              "show cartoon,%s\n",
1628              model, model, model, model);
1629    }
1630    return proxyPtr->status;
1631}
1632
1633/*
1634 * RotateCmd --
1635 *
1636 *      Issue "turn" commands for changes in the angle of the camera.  The
1637 *      problem here is that there is no event compression.  Consecutive
1638 *      "rotate" commands are not compressed into a single directive.  The
1639 *      means that the pymol server will render many scene that are not used
1640 *      by the client.
1641 *
1642 *      Need to 1) defer the "turn" commands until we find the next command
1643 *      isn't a "rotate". 2) Track the rotation angles as they are compressed.
1644 */
1645static int
1646RotateCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1647          const char *argv[])
1648{
1649    float xAngle, yAngle, zAngle;
1650    PymolProxy *proxyPtr = clientData;
1651    int defer = 0, push = 0, arg, varg = 1;
1652
1653    clear_error(proxyPtr);
1654
1655    xAngle = yAngle = zAngle = 0.0f;
1656    for(arg = 1; arg < argc; arg++) {
1657        if (strcmp(argv[arg],"-defer") == 0) {
1658            defer = 1;
1659        } else if (strcmp(argv[arg],"-push") == 0) {
1660            push = 1;
1661        } else  if (varg == 1) {
1662            double value;
1663            if (Tcl_GetDouble(interp, argv[arg], &value) != TCL_OK) {
1664                return TCL_ERROR;
1665            }
1666            xAngle = (float)value;
1667            varg++;
1668        } else if (varg == 2) {
1669            double value;
1670            if (Tcl_GetDouble(interp, argv[arg], &value) != TCL_OK) {
1671                return TCL_ERROR;
1672            }
1673            yAngle = (float)value;
1674            varg++;
1675        } else if (varg == 3) {
1676            double value;
1677            if (Tcl_GetDouble(interp, argv[arg], &value) != TCL_OK) {
1678                return TCL_ERROR;
1679            }
1680            zAngle = (float)value;
1681            varg++;
1682        }
1683    }
1684    proxyPtr->flags |= INVALIDATE_CACHE; /* rotate */
1685    if (!defer || push) {
1686        proxyPtr->flags |= UPDATE_PENDING;
1687    }
1688    if (push) {
1689        proxyPtr->flags |= FORCE_UPDATE;
1690    }
1691    if ((xAngle != 0.0f) || (yAngle != 0.0f) || (zAngle != 0.0f)) {
1692        proxyPtr->xAngle += xAngle;
1693        proxyPtr->yAngle += yAngle;
1694        proxyPtr->zAngle += zAngle;
1695        proxyPtr->flags |= ROTATE_PENDING;
1696    }
1697    return proxyPtr->status;
1698}
1699
1700static int
1701ScreenCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1702          const char *argv[])
1703{
1704    PymolProxy *proxyPtr = clientData;
1705    int width = -1, height = -1;
1706    int defer, push, i, varg;
1707
1708    clear_error(proxyPtr);
1709    defer = push = FALSE;
1710    varg = 1;
1711    for(i = 1; i < argc; i++) {
1712        if ( strcmp(argv[i],"-defer") == 0 )
1713            defer = 1;
1714        else if ( strcmp(argv[i], "-push") == 0 )
1715            push = 1;
1716        else if (varg == 1) {
1717            width = atoi(argv[i]);
1718            height = width;
1719            varg++;
1720        }
1721        else if (varg == 2) {
1722            height = atoi(argv[i]);
1723            varg++;
1724        }
1725    }
1726    if ((width < 0) || (height < 0)) {
1727        return TCL_ERROR;
1728    }
1729    proxyPtr->flags |= INVALIDATE_CACHE; /* viewport */
1730    if (!defer || push) {
1731        proxyPtr->flags |= UPDATE_PENDING;
1732    }
1733    if (push) {
1734        proxyPtr->flags |= FORCE_UPDATE;
1735    }
1736    proxyPtr->width = width;
1737    proxyPtr->height = height;
1738    proxyPtr->flags |= VIEWPORT_PENDING;
1739
1740    //usleep(205000); // .2s delay for pymol to update its geometry *HACK ALERT*
1741       
1742    return proxyPtr->status;
1743}
1744
1745static int
1746SphereScaleCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1747           const char *argv[])
1748{
1749    int defer = 0, push = 0, i;
1750    double scale;
1751    const char *model = "all";
1752    PymolProxy *proxyPtr = clientData;
1753
1754    clear_error(proxyPtr);
1755    scale = 0.25f;
1756    for(i = 1; i < argc; i++) {
1757        if ( strcmp(argv[i],"-defer") == 0 ) {
1758            defer = 1;
1759        } else if (strcmp(argv[i],"-push") == 0) {
1760            push = 1;
1761        } else if (strcmp(argv[i],"-model") == 0) {
1762            if (++i < argc) {
1763                model = argv[i];
1764            }
1765        } else {
1766            if (Tcl_GetDouble(interp, argv[i], &scale) != TCL_OK) {
1767                return TCL_ERROR;
1768            }
1769        }
1770    }
1771    proxyPtr->flags |= INVALIDATE_CACHE;  /* sphere_scale */
1772    if (!defer || push) {
1773        proxyPtr->flags |= UPDATE_PENDING;
1774    }
1775    if (push) {
1776        proxyPtr->flags |= FORCE_UPDATE;
1777    }
1778
1779    if (strcmp(model, "all") == 0) {
1780        proxyPtr->flags |= ATOM_SCALE_PENDING;
1781        proxyPtr->sphereScale = scale;
1782    } else {
1783        Pymol(proxyPtr, "set sphere_scale,%f,%s\n", scale, model);
1784    }
1785    return proxyPtr->status;
1786}
1787
1788static int
1789StickRadiusCmd(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        } else {
1808            if (Tcl_GetDouble(interp, argv[i], &scale) != TCL_OK) {
1809                return TCL_ERROR;
1810            }
1811        }
1812    }
1813    proxyPtr->flags |= INVALIDATE_CACHE;  /* stick_radius */
1814    if (!defer || push) {
1815        proxyPtr->flags |= UPDATE_PENDING;
1816    }
1817    if (push) {
1818        proxyPtr->flags |= FORCE_UPDATE;
1819    }
1820
1821    if (strcmp(model, "all") == 0) {
1822        proxyPtr->flags |= STICK_RADIUS_PENDING;
1823        proxyPtr->stickRadius = scale;
1824    } else {
1825        Pymol(proxyPtr, "set stick_radius,%f,%s\n", scale, model);
1826    }
1827    return proxyPtr->status;
1828}
1829
1830static int
1831TransparencyCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1832                const char *argv[])
1833{
1834    PymolProxy *proxyPtr = clientData;
1835    const char *model;
1836    float transparency;
1837    int defer, push;
1838    int i;
1839
1840    clear_error(proxyPtr);
1841    model = "all";
1842    defer = push = FALSE;
1843    transparency = 0.0f;
1844    for(i = 1; i < argc; i++) {
1845        if ( strcmp(argv[i],"-defer") == 0 ) {
1846            defer = 1;
1847        } else if (strcmp(argv[i],"-push") == 0) {
1848            push = 1;
1849        } else if (strcmp(argv[i],"-model") == 0) {
1850            if (++i < argc) {
1851                model = argv[i];
1852            }
1853        } else {
1854            transparency = atof(argv[i]);
1855        }
1856    }
1857    proxyPtr->flags |= INVALIDATE_CACHE; /* transparency */
1858    if (!defer || push) {
1859        proxyPtr->flags |= UPDATE_PENDING;
1860    }
1861    if (push) {
1862        proxyPtr->flags |= FORCE_UPDATE;
1863    }
1864    Pymol(proxyPtr,
1865          "set sphere_transparency,%g,%s\n"
1866          "set stick_transparency,%g,%s\n"
1867          "set cartoon_transparency,%g,%s\n",
1868          transparency, model, transparency, model,
1869          transparency, model);
1870    return proxyPtr->status;
1871}
1872
1873static int
1874VMouseCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1875          const char *argv[])
1876{
1877    PymolProxy *proxyPtr = clientData;
1878    int i, defer = 0, push = 0, varg = 1;
1879    int arg1 = 0, arg2 = 0, arg3 = 0, arg4 = 0, arg5 = 0;
1880
1881    clear_error(proxyPtr);
1882
1883    for(i = 1; i < argc; i++) {
1884        if (strcmp(argv[i], "-defer") == 0)
1885            defer = 1;
1886        else if (strcmp(argv[i], "-push") == 0)
1887            push = 1;
1888        else if (varg == 1) {
1889            arg1 = atoi(argv[i]);
1890            varg++;
1891        }
1892        else if (varg == 2) {
1893            arg2 = atoi(argv[i]);
1894            varg++;
1895        }
1896        else if (varg == 3) {
1897            arg3 = atoi(argv[i]);
1898            varg++;
1899        }
1900        else if (varg == 4) {
1901            arg4 = atoi(argv[i]);
1902            varg++;
1903        }
1904        else if (varg == 5) {
1905            arg5 = atoi(argv[i]);
1906            varg++;
1907        }
1908    }
1909
1910    proxyPtr->flags |= INVALIDATE_CACHE; /* vmouse */
1911    if (!defer || push) {
1912        proxyPtr->flags |= UPDATE_PENDING;
1913    }
1914    if (push) {
1915        proxyPtr->flags |= FORCE_UPDATE;
1916    }
1917    Pymol(proxyPtr, "vmouse %d,%d,%d,%d,%d\n", arg1, arg2, arg3, arg4, arg5);
1918    return proxyPtr->status;
1919}
1920
1921
1922/*
1923 * ZoomCmd --
1924 *
1925 *      Issue "move" commands for changes in the z-coordinate of the camera.
1926 *      The problem here is that there is no event compression.  Consecutive
1927 *      "zoom" commands are not compressed into a single directive.  The means
1928 *      that the pymol server will render scenes that are not used by the
1929 *      client.
1930 *
1931 *      Need to 1) defer the "move" commands until we find the next command
1932 *      isn't a "zoom". 2) Track the z-coordinate as they are compressed.
1933 */
1934static int
1935ZoomCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
1936{
1937    double factor = 0.0;
1938    PymolProxy *proxyPtr = clientData;
1939    int defer = 0, push = 0, i, varg = 1;
1940
1941    clear_error(proxyPtr);
1942
1943    for(i = 1; i < argc; i++) {
1944        if (strcmp(argv[i],"-defer") == 0)
1945            defer = 1;
1946        else if (strcmp(argv[i],"-push") == 0)
1947            push = 1;
1948        else if (varg == 1) {
1949            double value;
1950            if (Tcl_GetDouble(interp, argv[i], &value) != TCL_OK) {
1951                return TCL_ERROR;
1952            }
1953            factor = (float)value;
1954            varg++;
1955        }
1956    }
1957    proxyPtr->flags |= INVALIDATE_CACHE; /* Zoom */
1958    if (!defer || push) {
1959        proxyPtr->flags |= UPDATE_PENDING;
1960    }
1961    if (push) {
1962        proxyPtr->flags |= FORCE_UPDATE;
1963    }
1964    if (factor != 0.0) {
1965        proxyPtr->zoom = factor;
1966        proxyPtr->flags |= ZOOM_PENDING;
1967    }
1968    return proxyPtr->status;
1969}
1970
1971       
1972static void
1973ChildHandler(int sigNum)
1974{
1975    ERROR("pymol (%d) died unexpectedly", stats.child);
1976    exit(1);
1977}
1978
1979static int
1980ProxyInit(int cin, int cout, char *const *argv)
1981{
1982    char stderrFile[200];
1983    int status, result = 0;
1984    int sin[2];
1985    int sout[2];
1986    Tcl_Interp *interp;
1987    pid_t child, parent;
1988    PymolProxy proxy;
1989    struct timeval end;
1990
1991    /* Create three pipes for communication with the external application. One
1992     * each for the applications's: stdin, stdout, and stderr  */
1993
1994    if (pipe(sin) == -1)
1995        return -1;
1996
1997    if (pipe(sout) == -1) {
1998        close(sin[0]);
1999        close(sin[1]);
2000        return -1;
2001    }
2002
2003    parent = getpid();
2004    sprintf(stderrFile, "/tmp/pymol%d.stderr", parent);
2005
2006    /* Fork the new process.  Connect I/O to the new socket.  */
2007
2008    child = fork();
2009    if (child < 0) {
2010        ERROR("can't fork process: %s\n", strerror(errno));
2011        return -3;
2012    }
2013
2014    interp = Tcl_CreateInterp();
2015    Tcl_MakeSafe(interp);
2016
2017    if (child == 0) {
2018        int f;
2019        char path[200];
2020
2021        /* Child process */
2022       
2023        /*
2024         * Create a new process group, so we can later kill this process and
2025         * all its children without affecting the process that created this
2026         * one.
2027         */
2028        setpgid(child, 0);
2029       
2030        /* Redirect stdin, stdout, and stderr to pipes before execing */
2031
2032        dup2(sin[0],  0);               // stdin
2033        dup2(sout[1], 1);               // stdout
2034        f = open(stderrFile, O_WRONLY | O_CREAT | O_TRUNC, 0600);
2035        if (f < 0) {
2036            ERROR("can't open server error file `%s': %s", path,
2037                  strerror(errno));
2038            exit(1);
2039        }
2040        dup2(f, 2);                     /* Redirect stderr to a file */
2041       
2042        /* Close all other descriptors  */       
2043        for (f = 3; f < FD_SETSIZE; f++) {
2044            close(f);
2045        }
2046        INFO("attempting to start \"%s\"", argv[0]);
2047       
2048        execvp(argv[0], argv);
2049        ERROR("can't exec `%s': %s", argv[0], strerror(errno));
2050        exit(-1);
2051    } else {
2052        signal(SIGCHLD, ChildHandler);
2053    }
2054    stats.child = child;
2055
2056    Debug("Started %s DISPLAY=%s\n", argv[0], getenv("DISPLAY"));
2057
2058    /* close opposite end of pipe, these now belong to the child process  */
2059    close(sin[0]);
2060    close(sout[1]);
2061
2062    memset(&proxy, 0, sizeof(PymolProxy));
2063    proxy.sin  = sin[1];
2064    proxy.sout = sout[0];
2065    proxy.cin  = cin;
2066    proxy.cout = cout;
2067    proxy.flags = CAN_UPDATE;
2068    proxy.frame = 1;
2069    proxy.interp = interp;
2070
2071    Tcl_CreateCommand(interp, "cartoon",       CartoonCmd,       &proxy, NULL);
2072    Tcl_CreateCommand(interp, "cartoontrace",  CartoonTraceCmd,  &proxy, NULL);
2073    Tcl_CreateCommand(interp, "disable",       DisableCmd,       &proxy, NULL);
2074    Tcl_CreateCommand(interp, "enable",        EnableCmd,        &proxy, NULL);
2075    Tcl_CreateCommand(interp, "frame",         FrameCmd,         &proxy, NULL);
2076    Tcl_CreateCommand(interp, "label",         LabelCmd,         &proxy, NULL);
2077    Tcl_CreateCommand(interp, "loadpdb",       LoadPDBCmd,       &proxy, NULL);
2078    Tcl_CreateCommand(interp, "orthoscopic",   OrthoscopicCmd,   &proxy, NULL);
2079    Tcl_CreateCommand(interp, "pan",           PanCmd,           &proxy, NULL);
2080    Tcl_CreateCommand(interp, "png",           PngCmd,           &proxy, NULL);
2081    Tcl_CreateCommand(interp, "ppm",           PpmCmd,           &proxy, NULL);
2082    Tcl_CreateCommand(interp, "print",         PrintCmd,         &proxy, NULL);
2083    Tcl_CreateCommand(interp, "raw",           RawCmd,           &proxy, NULL);
2084    Tcl_CreateCommand(interp, "representation",RepresentationCmd,&proxy, NULL);
2085    Tcl_CreateCommand(interp, "reset",         ResetCmd,         &proxy, NULL);
2086    Tcl_CreateCommand(interp, "rock",          RockCmd,          &proxy, NULL);
2087    Tcl_CreateCommand(interp, "rotate",        RotateCmd,        &proxy, NULL);
2088    Tcl_CreateCommand(interp, "screen",        ScreenCmd,        &proxy, NULL);
2089    Tcl_CreateCommand(interp, "spherescale",   SphereScaleCmd,   &proxy, NULL);
2090    Tcl_CreateCommand(interp, "stickradius",   StickRadiusCmd,   &proxy, NULL);
2091    Tcl_CreateCommand(interp, "transparency",  TransparencyCmd,  &proxy, NULL);
2092    Tcl_CreateCommand(interp, "viewport",      ScreenCmd,        &proxy, NULL);
2093    Tcl_CreateCommand(interp, "vmouse",        VMouseCmd,        &proxy, NULL);
2094    Tcl_CreateCommand(interp, "zoom",          ZoomCmd,          &proxy, NULL);
2095
2096
2097    gettimeofday(&end, NULL);
2098    stats.start = end;
2099
2100    // Main Proxy Loop
2101    //  accept tcl commands from socket
2102    //  translate them into pyMol commands, and issue them to child proccess
2103    //  send images back
2104
2105    PollForEvents(&proxy);
2106
2107    unlink(stderrFile);
2108    close(proxy.cout);
2109    close(proxy.sout);
2110    close(proxy.cin);
2111    close(proxy.sin);
2112
2113    if (waitpid(child, &result, 0) < 0) {
2114        ERROR("error waiting on pymol server to exit: %s", strerror(errno));
2115    }
2116    INFO("attempting to signal (SIGTERM) pymol server.");
2117    kill(-child, SIGTERM);              // Kill process group
2118    alarm(5);
2119   
2120    if (waitpid(child, &result, 0) < 0) {
2121        ERROR("error waiting on pymol server to exit after SIGTERM: %s",
2122              strerror(errno));
2123    }
2124    status = -1;
2125    while ((status == -1) && (errno == EINTR)) {
2126        ERROR("Attempting to signal (SIGKILL) pymol server.");
2127        kill(-child, SIGKILL);  // Kill process group
2128        alarm(10);
2129        status = waitpid(child, &result, 0);
2130        alarm(0);
2131    }
2132    INFO("pymol server process ended (result=%d)", result);
2133
2134    Tcl_DeleteInterp(interp);
2135   
2136    if (WIFEXITED(result)) {
2137        result = WEXITSTATUS(result);
2138    }
2139    DoExit(result);
2140    return 0;
2141}
2142
2143
2144static void
2145PollForEvents(PymolProxy *proxyPtr)
2146{
2147    Tcl_DString clientCmds;
2148    struct pollfd pollFd[3];
2149    int flags;
2150
2151    if (write(proxyPtr->cout, "PyMol 1.0\n", 10) != 10) {
2152        ERROR("short write of signature");
2153    }
2154
2155    flags = fcntl(proxyPtr->cin, F_GETFL);
2156    fcntl(proxyPtr->cin, F_SETFL, flags|O_NONBLOCK);
2157
2158#ifdef notdef
2159    flags = fcntl(proxyPtr->sout, F_GETFL);
2160    fcntl(proxyPtr->sout, F_SETFL, flags|O_NONBLOCK);
2161
2162    flags = fcntl(proxyPtr->cout, F_GETFL);
2163    fcntl(proxyPtr->cout, F_SETFL, flags|O_NONBLOCK);
2164#endif
2165
2166    /* Read file descriptors. */
2167    pollFd[0].fd = proxyPtr->cout;      /* Client standard output  */
2168    pollFd[1].fd = proxyPtr->sout;      /* Server standard error.  */
2169    pollFd[0].events = pollFd[1].events = POLLIN;
2170
2171    /* Write file descriptors. */
2172    pollFd[2].fd = proxyPtr->cin;       /* Client standard input. */
2173    pollFd[2].events = POLLOUT;
2174
2175    InitBuffer(&proxyPtr->client, "client", proxyPtr->cout);
2176    InitBuffer(&proxyPtr->server, "server", proxyPtr->sout);
2177
2178    Tcl_DStringInit(&clientCmds);
2179    for (;;) {
2180        int timeout, nChannels;
2181
2182        nChannels =  (proxyPtr->headPtr != NULL) ? 3 : 2;
2183#define PENDING_TIMEOUT         10  /* milliseconds. */
2184        timeout = (proxyPtr->flags & UPDATE_PENDING) ? PENDING_TIMEOUT : -1;
2185        nChannels = poll(pollFd, nChannels, timeout);
2186        if (nChannels < 0) {
2187            ERROR("POLL ERROR: %s", strerror(errno));
2188            continue;           /* or exit? */
2189        }
2190
2191        /*
2192         * The next two sections are to drain any leftover data in
2193         * the pymol server process' stdout or stderr.  We don't want the
2194         * the pymol server to block writing to stderr or stdout.
2195         */
2196        if (pollFd[1].revents & POLLIN) { /* Server stdout */
2197            int numBytes;
2198            char *line;
2199           
2200            Debug("Reading pymol stdout\n");
2201            /* Don't care what's in the server output buffer. */
2202            FlushBuffer(&proxyPtr->server);
2203            line = NextLine(&proxyPtr->server, &numBytes);
2204            if (line != NULL) {
2205                Debug("STDOUT>%.*s", numBytes, line);
2206                INFO("Garbage found from pymol server: %.%s", numBytes, line);
2207            } else if (numBytes == BUFFER_CONTINUE) {
2208                Debug("No data found on pymol stdout\n");
2209                continue;
2210            } else {
2211                ERROR("can't read pymol stdout (numBytes=%d): %s\n", numBytes,
2212                      strerror(errno));
2213                goto error;             /* Get out on EOF or error. */
2214            }
2215        }
2216
2217        /* We have some descriptors ready. */
2218        if (pollFd[0].revents & POLLIN) { /* Client stdout */
2219            Debug("Reading client stdout\n");
2220            for (;;) {
2221                int numBytes;
2222                char *line;
2223               
2224                line = NextLine(&proxyPtr->client, &numBytes);
2225                if (line != NULL) {
2226                    const char *cmd;
2227
2228                    Tcl_DStringAppend(&clientCmds, line, numBytes);
2229                    cmd = Tcl_DStringValue(&clientCmds);
2230                    if (Tcl_CommandComplete(cmd)) {
2231                        int result;
2232
2233                        /* May execute more than one command. */
2234                        result = ExecuteCommand(proxyPtr->interp, cmd);
2235                        if (result != TCL_OK) {
2236                            Tcl_Obj *objPtr;
2237
2238                            objPtr = Tcl_GetObjResult(proxyPtr->interp);
2239                            ERROR(Tcl_GetString(objPtr));
2240                            goto error;
2241                        }
2242                        Tcl_DStringSetLength(&clientCmds, 0);
2243                    }
2244                    continue;
2245                }
2246                if (numBytes == BUFFER_CONTINUE) {
2247                    break;
2248                }
2249                ERROR("can't read client stdout (numBytes=%d): %s\n", numBytes,
2250                      strerror(errno));
2251                goto error;             /* Get out on EOF or error. */
2252            }
2253            Debug("done with client stdout\n");
2254        }
2255        /*
2256         * Write the currently queued image if there is one.
2257         *
2258         * We want to transmit the current image back to the client.  But if
2259         * the client's busy (e.g. sending us another command), there's a
2260         * chance we'll deadlock.  Therefore, the file descriptor is
2261         * non-blocking and we write only what we can.  Must be careful not to
2262         * refresh the image until we're done.
2263         */
2264
2265        /* Handle all the pending setting changes now. */
2266        UpdateSettings(proxyPtr);
2267
2268        /* Write the current image buffer. */
2269        if (proxyPtr->headPtr == NULL) {
2270            /* We might want to refresh the image if we're not currently
2271             * transmitting an image back to the client. The image will be
2272             * refreshed after the image has been completely transmitted. */
2273            if ((nChannels == 0) || (proxyPtr->flags & FORCE_UPDATE)) {
2274                if (proxyPtr->flags & UPDATE_PENDING) {
2275                    Tcl_Eval(proxyPtr->interp, "ppm");
2276                    proxyPtr->flags &= ~UPDATE_PENDING;
2277                }
2278                proxyPtr->flags &= ~FORCE_UPDATE;
2279                continue;
2280            }
2281        }
2282        if ((proxyPtr->headPtr != NULL) && (pollFd[2].revents & POLLOUT)) {
2283            WriteImage(proxyPtr, pollFd[2].fd);
2284        }
2285    }
2286 error:
2287    Tcl_DStringFree(&clientCmds);
2288    return;
2289}
2290
2291int
2292main(int argc, char *argv[])
2293{
2294    flog = stderr;
2295    if (debug) {
2296        char fileName[200];
2297        sprintf(fileName, "/tmp/pymolproxy%d.log", getpid());
2298        sprintf(fileName, "/tmp/pymolproxy.log");
2299        flog = fopen(fileName, "w");
2300    }   
2301    scriptFile = NULL;
2302    if (savescript) {
2303        char fileName[200];
2304        sprintf(fileName, "/tmp/pymolproxy.py");
2305        scriptFile = fopen(fileName, "w");
2306    }   
2307    ProxyInit(fileno(stdout), fileno(stdin), argv + 1);
2308    return 0;
2309}
2310
Note: See TracBrowser for help on using the repository browser.