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

Last change on this file since 4369 was 3644, checked in by gah, 11 years ago
File size: 61.2 KB
RevLine 
[914]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
[1334]11 *      commands and send to the molvisviewer widget. For example, resulting
[2615]12 *      image rendered offscreen is returned as ppm-formatted image data.
[914]13 *
[3177]14 *  Copyright (c) 2004-2012  HUBzero Foundation, LLC
[914]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
[1278]21/*
[1538]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/*
[1278]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
[1428]54#include <assert.h>
55#include <ctype.h>
56#include <errno.h>
57#include <fcntl.h>
58#include <getopt.h>
59#include <poll.h>
[691]60#include <stdio.h>
61#include <stdlib.h>
62#include <string.h>
[1164]63#include <sys/stat.h>
[1428]64#include <sys/time.h>
65#include <sys/times.h>
[691]66#include <sys/types.h>
67#include <sys/wait.h>
[1428]68#include <time.h>
[2381]69#include <syslog.h>
[1428]70#include <unistd.h>
[691]71#include <tcl.h>
72
[914]73#undef INLINE
74#ifdef __GNUC__
75#  define INLINE __inline__
76#else
77#  define INLINE
78#endif
79
[1161]80#define FALSE 0
81#define TRUE  1
82
[691]83#define IO_TIMEOUT (30000)
[1161]84#define KEEPSTATS       1
85#define CVT2SECS(x)  ((double)(x).tv_sec) + ((double)(x).tv_usec * 1.0e-6)
[691]86
[1152]87typedef struct {
[1161]88    pid_t child;                /* Child process. */
[3644]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 */
[1278]92    double cmdTime;             /* Elapsed time spend executing commands. */
[1152]93    struct timeval start;       /* Start of elapsed time. */
94} Stats;
95
96static Stats stats;
97
[2614]98#define READTRACE       1
[2452]99#define EXPECTTRACE     0
[2371]100#define WRITETRACE      0
101
[2664]102static int debug = FALSE;
[2373]103
[914]104static FILE *flog;
[1712]105static FILE *scriptFile;
[1830]106static int savescript = FALSE;
[914]107
[1334]108typedef struct Image {
[2419]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. */
[3644]117    ssize_t numWritten;                 /* Number of bytes of image data
[2419]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. */
[1334]124} Image;
[691]125
[1278]126#define BUFFER_SIZE             4096
127
[914]128typedef struct {
[2451]129    const char *ident;
[1278]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
[1337]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)
[1551]144#define STICK_RADIUS_PENDING    (1<<5)
[1337]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)
[1336]150
[1278]151typedef struct {
152    Tcl_Interp *interp;
[1808]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. */
[1336]157
[2441]158    int sin, sout;                      /* Server file descriptors. */
[1970]159    int cin, cout;                      /* Client file descriptors. */
[1551]160    ReadBuffer client;                  /* Read buffer for client input. */
161    ReadBuffer server;                  /* Read buffer for server output. */
[914]162    int frame;
[1336]163    int rockOffset;
164    int cacheId;
[914]165    int error;
166    int status;
[1551]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. */
[1336]173    float zoom;
174    float xPan, yPan;
[914]175} PymolProxy;
[691]176
[2381]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__)
[1999]181
[2381]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
[1278]220static void PollForEvents(PymolProxy *proxyPtr);
[1161]221static void
[2381]222Debug TCL_VARARGS_DEF(const char *, arg1)
[1161]223{
224    if (debug) {
[1999]225        const char *format;
[1161]226        va_list args;
227
[1999]228        format = TCL_VARARGS_START(const char *, arg1, args);
[1213]229        fprintf(flog, "pymolproxy: ");
[1161]230        vfprintf(flog, format, args);
231        fprintf(flog, "\n");
232        fflush(flog);
233    }
234}
235
[1278]236static void
[1999]237script TCL_VARARGS_DEF(const char *, arg1)
[1712]238{
239    if (savescript) {
[1999]240        const char *format;
[1712]241        va_list args;
242
[1999]243        format = TCL_VARARGS_START(const char *, arg1, args);
[1712]244        vfprintf(scriptFile, format, args);
245        fflush(scriptFile);
246    }
247}
248
249static void
[2451]250InitBuffer(ReadBuffer *readPtr, const char *ident, int f)
[1278]251{
[2451]252    readPtr->ident = ident;
[1278]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
[2451]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 */
[1278]277static int
278FillBuffer(ReadBuffer *readPtr)
279{
280    size_t bytesLeft;
281    ssize_t nRead;
282
[2371]283#if READTRACE
[2381]284    Debug("Entering FillBuffer (mark=%d, fill=%d)\n", readPtr->mark,
[1278]285          readPtr->fill);
286#endif
287    if (readPtr->mark >= readPtr->fill) {
[2451]288        INFO("tossing full buffer mark=%d, fill=%d", readPtr->mark,
289             readPtr->fill);
[1278]290        readPtr->mark = readPtr->fill = 0;
291    }
292    if (readPtr->mark > 0) {
293        size_t i, j;
294
[2451]295        /* Some data has been consumed. Move the unconsumed data to the front
296         * of the buffer. */
[1278]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) {
[2451]306        INFO("EOF found reading \"%s\" buffer", readPtr->ident);
307        return BUFFER_ERROR;            /* EOF, treat as an error. */
[1278]308    }
[2451]309    if (nRead < 0) {
[1278]310        if (errno != EAGAIN) {
[2371]311#if READTRACE
[2381]312            Debug("in FillBuffer: read failed %d: %s", errno, strerror(errno));
313            Debug("Leaving FillBuffer FAIL(read %d bytes) mark=%d, fill=%d\n",
[1278]314                  nRead, readPtr->mark, readPtr->fill);
315#endif
[2451]316            ERROR("error reading \"%s\" buffer: %s", readPtr->ident,
317                  strerror(errno));
[1278]318            return BUFFER_ERROR;
319        }
[2451]320        INFO("Short read for \"%s\" buffer", readPtr->ident);
321        return BUFFER_CONTINUE;
[1278]322    }
323    readPtr->fill += nRead;
[2371]324#if READTRACE
[2381]325    Debug("Leaving FillBuffer (read %d bytes) mark=%d, fill=%d\n",
[1278]326          nRead, readPtr->mark, readPtr->fill);
327#endif
[2451]328    return (nRead == bytesLeft) ? BUFFER_OK : BUFFER_CONTINUE;
[1278]329}
330
[2451]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
[3644]337 *      short read, NULL is returned and *numBytesPtr is set to
[2451]338 *      BUFFER_CONTINUE.
339 */
[1278]340static char *
[3644]341NextLine(ReadBuffer *readPtr, int *numBytesPtr)
[1278]342{
343    int i;
344    int status;
345
[2371]346#if READTRACE
[2451]347    Debug("Entering NextLine (mark=%d, fill=%d)\n",readPtr->mark, readPtr->fill);
[1278]348#endif
349    status = BUFFER_OK;
350    for (;;) {
[2371]351#if READTRACE
[2451]352        Debug("in NextLine: mark=%d fill=%d\n", readPtr->mark, readPtr->fill);
[1533]353#endif
[2451]354        /* Check for a newline in the current buffer (it's the next
355         * full line). */
[1278]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;
[2451]362                i++;                    /* Include the newline. */
[3644]363                *numBytesPtr = i - readPtr->mark;
[1278]364                readPtr->mark = i;
[2371]365#if READTRACE
[3644]366                Debug("Leaving NextLine(%.*s)\n", *numBytesPtr, p);
[1278]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. */
[2451]373        if (status == BUFFER_CONTINUE) {
[3644]374            *numBytesPtr = BUFFER_CONTINUE;  /* No complete line just yet. */
[2451]375            return NULL;
[1278]376        }
[2451]377        /* Try to add more data to the buffer. */
[1278]378        status = FillBuffer(readPtr);
379        if (status == BUFFER_ERROR) {
[3644]380            *numBytesPtr = BUFFER_ERROR;
[1278]381            return NULL;        /* EOF or error on read. */
382        }
[2451]383        /* BUFFER_OK or BUFFER_CONTINUE */
[1278]384    }
[2371]385#if READTRACE
[2451]386    Debug("Leaving NextLine failed to read line\n");
[1278]387#endif
[3644]388    *numBytesPtr = BUFFER_CONTINUE;
[1278]389    return NULL;
390}
391
[2451]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 */
[1278]399static int
[3644]400ReadFollowingData(ReadBuffer *readPtr, char *out, int numBytes)
[1278]401{
[2371]402#if READTRACE
[3644]403    Debug("Entering ReadFollowingData(%d)\n", numBytes);
[1278]404#endif
[3644]405    while (numBytes > 0) {
[1278]406        int bytesLeft;
407        int status;
408
409        bytesLeft = readPtr->fill - readPtr->mark;
410        if (bytesLeft > 0) {
411            int size;
412
[2451]413            /* Pull bytes out of the buffer, updating the mark. */
[3644]414            size = (bytesLeft >  numBytes) ? numBytes : bytesLeft;
[1278]415            memcpy(out, readPtr->bytes + readPtr->mark, size);
416            readPtr->mark += size;
[3644]417            numBytes -= size;
[1278]418            out += size;
419        }
[3644]420        if (numBytes == 0) {
[1278]421            /* Received requested # bytes. */
[2371]422#if READTRACE
[3644]423            Debug("Leaving ReadFollowingData(%d)\n", numBytes);
[1278]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        }
[2371]432#if READTRACE
[2451]433        Debug("in ReadFollowingData: after fill: mark=%d fill=%d status=%d\n",
434              readPtr->mark, readPtr->fill, status);
[1278]435#endif
436    }
[2371]437#if READTRACE
[3644]438    Debug("Leaving ReadFollowingData(%d)\n", numBytes);
[1278]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) {
[2451]457        return TCL_ERROR;
[1278]458    }
[2371]459#if EXPECTTRACE
[2381]460    Debug("Entering Expect(want=\"%s\", maxSize=%d)\n", match, maxSize);
[1533]461#endif
[1278]462    c = match[0];
463    length = strlen(match);
464    for (;;) {
[3644]465        int numBytes;
[1278]466        char *line;
467
[3644]468        line = NextLine(&proxyPtr->server, &numBytes);
[1278]469        if (line != NULL) {
[2371]470#if EXPECTTRACE
[3644]471            Debug("pymol says (read %d bytes):%.*s", numBytes, numBytes, line);
[1533]472#endif
[1278]473            if ((c == line[0]) && (strncmp(line, match, length) == 0)) {
[3644]474                if (maxSize < numBytes) {
475                    numBytes = maxSize;
[1278]476                }
[3644]477                memcpy(out, line, numBytes);
[1278]478                clear_error(proxyPtr);
[2371]479#if EXPECTTRACE
[3644]480                Debug("Leaving Expect: got (%.*s)\n", numBytes, out);
[1533]481#endif
[2451]482                return TCL_OK;
[1278]483            }
484            continue;
485        }
[3644]486        if (numBytes == BUFFER_ERROR) {
[2451]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;
[1278]491        }
492    }
[2451]493    Tcl_AppendResult(proxyPtr->interp, "failed to find match for \"", match,
494        "\"", (char *)NULL);
[1278]495    proxyPtr->error = 2;
496    proxyPtr->status = TCL_ERROR;
[2451]497    return TCL_ERROR;
[1278]498}
499
[1164]500#if KEEPSTATS
[1161]501
[1152]502static int
[1161]503WriteStats(const char *who, int code)
[1152]504{
505    double start, finish;
[1161]506    pid_t pid;
507    char buf[BUFSIZ];
508    Tcl_DString ds;
[1152]509
[1161]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);
[1164]534   
[1161]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);
[3644]550    sprintf(buf, "num_frames=\"%lu\" ", (unsigned long int)stats.numFrames);
[1161]551    Tcl_DStringAppend(&ds, buf, -1);
[3644]552    sprintf(buf, "frame_bytes=\"%lu\" ", (unsigned long int)stats.numBytes);
[1161]553    Tcl_DStringAppend(&ds, buf, -1);
[3644]554    sprintf(buf, "num_commands=\"%lu\" ", (unsigned long int)stats.numCommands);
[1161]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));
[1162]568        times(&tms);
[1161]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    {
[1164]581        int f;
582        ssize_t length;
[1161]583        int result;
584
[3412]585#ifndef STATSDIR
[1164]586#define STATSDIR        "/var/tmp/visservers"
[3412]587#endif
[1164]588#define STATSFILE       STATSDIR "/" "data.xml"
589        if (access(STATSDIR, X_OK) != 0) {
590            mkdir(STATSDIR, 0770);
591        }
[1161]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;
[1164]602 error:
[1161]603        if (f >= 0) {
604            close(f);
605        }
606        Tcl_DStringFree(&ds);
607        return result;
608    }
[1152]609}
[1161]610#endif
[1152]611
[1161]612static void
613DoExit(int code)
614{
[1164]615#if KEEPSTATS
[1161]616    WriteStats("pymolproxy", code);
617#endif
[1999]618   
[1161]619    exit(code);
620}
[1155]621
[1152]622static int
[1278]623ExecuteCommand(Tcl_Interp *interp, const char *cmd)
[1152]624{
625    struct timeval tv;
626    double start, finish;
[1161]627    int result;
[1152]628
629    gettimeofday(&tv, NULL);
[1161]630    start = CVT2SECS(tv);
631
[2381]632    Debug("command from client is (%s)", cmd);
[1278]633    result = Tcl_Eval(interp, cmd);
[1161]634
635    gettimeofday(&tv, NULL);
636    finish = CVT2SECS(tv);
637
638    stats.cmdTime += finish - start;
[3644]639    stats.numCommands++;
[1161]640    return result;
[1152]641}
642
[1334]643static Image *
644NewImage(PymolProxy *proxyPtr, size_t dataLength)
[691]645{
[1334]646    Image *imgPtr;
647
648    imgPtr = malloc(sizeof(Image) + dataLength);
649    if (imgPtr == NULL) {
[2381]650        ERROR("can't allocate image of %lu bytes",
651              (unsigned long)(sizeof(Image) + dataLength));
[1334]652        abort();
653    }
[1808]654    imgPtr->prevPtr = imgPtr->nextPtr = NULL;
[1336]655    imgPtr->bytesLeft = dataLength;
[1808]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;
[3644]664    imgPtr->numWritten = 0;
[1334]665    return imgPtr;
[691]666}
667
[914]668INLINE static void
[1334]669FreeImage(Image *imgPtr)
[691]670{
[1278]671    assert(imgPtr != NULL);
[1334]672    free(imgPtr);
[691]673}
674
[1213]675static void
[1278]676WriteImage(PymolProxy *proxyPtr, int fd)
677{
[1808]678    Image *imgPtr, *prevPtr;
[1278]679
[1808]680    if (proxyPtr->tailPtr == NULL) {
[2381]681        ERROR("Should not be here: no image available to write");
[1334]682        return;
683    }
[1808]684    for (imgPtr = proxyPtr->tailPtr; imgPtr != NULL; imgPtr = prevPtr) {
685        ssize_t bytesLeft;
686
687        assert(imgPtr->nextPtr == NULL);
688        prevPtr = imgPtr->prevPtr;
[2371]689#if WRITETRACE
[2381]690        Debug("WriteImage: want to write %d bytes.", imgPtr->bytesLeft);
[1533]691#endif
[1808]692        for (bytesLeft = imgPtr->bytesLeft; bytesLeft > 0; /*empty*/) {
[3644]693            ssize_t numWritten;
[2371]694#if WRITETRACE
[2381]695            Debug("WriteImage: try to write %d bytes.", bytesLeft);
[1533]696#endif
[3644]697            numWritten = write(fd, imgPtr->data + imgPtr->numWritten, bytesLeft);
[2371]698#if WRITETRACE
[3644]699            Debug("WriteImage: wrote %d bytes.", numWritten);
[1533]700#endif
[3644]701            if (numWritten < 0) {
[2427]702                ERROR("Error writing fd=%d, %s", fd, strerror(errno));
[1808]703                return;
704            }
[3644]705            bytesLeft -= numWritten;
[1808]706            if (bytesLeft > 0) {
707                /* Wrote a short buffer, means we would block. */
[3644]708                imgPtr->numWritten += numWritten;
[1808]709                imgPtr->bytesLeft = bytesLeft;
710                return;
711            }
[3644]712            imgPtr->numWritten += numWritten;
[1278]713        }
[1808]714        /* Check if image is on the head.  */
715        proxyPtr->tailPtr = prevPtr;
716        if (prevPtr != NULL) {
717            prevPtr->nextPtr = NULL;
[1334]718        }
719        FreeImage(imgPtr);
720    }
[1808]721    proxyPtr->headPtr = NULL;
[1278]722}
723
[914]724static int
[1533]725Pymol(PymolProxy *proxyPtr, const char *format, ...)
[691]726{
[1428]727    va_list ap;
728    char buffer[BUFSIZ];
729    char expect[BUFSIZ];
730    int result;
[3644]731    ssize_t numWritten;
[1428]732    size_t length;
733    char *p;
[691]734
[1278]735    if (proxyPtr->error) {
736        return TCL_ERROR;
737    }
[1428]738   
739    va_start(ap, format);
[1563]740    result = vsnprintf(buffer, BUFSIZ-1, format, ap);
[1428]741    va_end(ap);
742   
[2381]743    Debug("to-pymol>(%s) code=%d", buffer, result);
[1712]744    script("%s\n", buffer);
[1428]745   
[1712]746   
[1428]747    /* Write the command out to the server. */
748    length = strlen(buffer);
[3644]749    numWritten = write(proxyPtr->sin, buffer, length);
750    if (numWritten != length) {
[2427]751        ERROR("short write to pymol (wrote=%d, should have been %d): %s",
[3644]752              numWritten, length, strerror(errno));
[691]753    }
[1428]754    for (p = buffer; *p != '\0'; p++) {
[1808]755        if (*p == '\n') {
[1428]756            *p = '\0';
757            break;
[1278]758        }
759    }
[1428]760    sprintf(expect, "PyMOL>%s", buffer);
761    /* Now wait for the "PyMOL>" prompt. */
[2451]762    if (Expect(proxyPtr, expect, buffer, BUFSIZ) != TCL_OK) {
[1428]763        proxyPtr->error = 1;
764        proxyPtr->status = TCL_ERROR;
[2451]765        return TCL_ERROR;
[1428]766    }
767    return  proxyPtr->status;
[691]768}
769
[1278]770static void
[1337]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
[1336]780SetZoom(PymolProxy *proxyPtr)
[914]781{
[1336]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) {
[1533]792        Pymol(proxyPtr,"move x,%f\nmove y,%f\n", proxyPtr->xPan,proxyPtr->yPan);
[1336]793        proxyPtr->flags &= ~PAN_PENDING;
794    }
795}
796
797static void
798SetRotation(PymolProxy *proxyPtr)
799{
800    if (proxyPtr->flags & ROTATE_PENDING) {
[1278]801        /* Every pymol command line generates a new rendering. Execute all
802         * three turns as a single command line. */
[1533]803        Pymol(proxyPtr,"turn x,%f\nturn y,%f\nturn z,%f\n",
[1278]804              proxyPtr->xAngle, proxyPtr->yAngle, proxyPtr->zAngle);
805        proxyPtr->xAngle = proxyPtr->yAngle = proxyPtr->zAngle = 0.0f;
[1336]806        proxyPtr->flags &= ~ROTATE_PENDING;
[1213]807    }
[1278]808}
[691]809
[1278]810static void
[1551]811SetSphereScale(PymolProxy *proxyPtr)
[1278]812{
[1336]813    if (proxyPtr->flags & ATOM_SCALE_PENDING) {
[1551]814        Pymol(proxyPtr, "set sphere_scale,%f,all\n", proxyPtr->sphereScale);
[1336]815        proxyPtr->flags &= ~ATOM_SCALE_PENDING;
[691]816    }
817}
818
[1336]819static void
[1551]820SetStickRadius(PymolProxy *proxyPtr)
[1336]821{
[1551]822    if (proxyPtr->flags & STICK_RADIUS_PENDING) {
823        Pymol(proxyPtr, "set stick_radius,%f,all\n", proxyPtr->stickRadius);
824        proxyPtr->flags &= ~STICK_RADIUS_PENDING;
[1336]825    }
826}
827
[1998]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
[914]852static int
[1551]853CartoonCmd(ClientData clientData, Tcl_Interp *interp, int argc,
854           const char *argv[])
[1336]855{
[1551]856    int bool, defer, push, i;
[1336]857    PymolProxy *proxyPtr = clientData;
[1551]858    const char *model;
[1336]859
860    clear_error(proxyPtr);
[1551]861    defer = push = FALSE;
862    model = "all";
863    bool = FALSE;
[1533]864    for(i = 1; i < argc; i++) {
[1551]865        if (strcmp(argv[i],"-defer") == 0) {
866            defer = TRUE;
[1533]867        } else if (strcmp(argv[i],"-push") == 0) {
[1551]868            push = TRUE;
[1533]869        } else if (strcmp(argv[i],"-model") == 0) {
[1551]870            if (++i < argc) {
[1533]871                model = argv[i];
[1551]872            }
873        } else {
874            if (Tcl_GetBoolean(interp, argv[i], &bool) != TCL_OK) {
[1336]875                return TCL_ERROR;
876            }
877        }
878    }
[1712]879    proxyPtr->flags |= INVALIDATE_CACHE; /* cartoon */
[1336]880    if (!defer || push) {
881        proxyPtr->flags |= UPDATE_PENDING;
882    }
883    if (push) {
884        proxyPtr->flags |= FORCE_UPDATE;
885    }
[1551]886    if (bool) {
887        Pymol(proxyPtr, "show cartoon,%s\n", model);
[1336]888    } else {
[1551]889        Pymol(proxyPtr, "hide cartoon,%s\n", model);
[1336]890    }
891    return proxyPtr->status;
892}
893
894static int
[1551]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    }
[1712]921    proxyPtr->flags |= INVALIDATE_CACHE; /* cartoon_trace  */
[1551]922    if (!defer || push) {
923        proxyPtr->flags |= UPDATE_PENDING;
924    }
925    if (push) {
926        proxyPtr->flags |= FORCE_UPDATE;
927    }
[1557]928    Pymol(proxyPtr, "set cartoon_trace,%d,%s\n", bool, model);
[1551]929    return proxyPtr->status;
930}
931
932static int
[1213]933DisableCmd(ClientData clientData, Tcl_Interp *interp, int argc,
934           const char *argv[])
[691]935{
[1213]936    PymolProxy *proxyPtr = clientData;
[691]937    const char *model = "all";
[1533]938    int i, defer = 0, push = 0;
[691]939
[1213]940    clear_error(proxyPtr);
[691]941
[1533]942    for(i = 1; i < argc; i++) {
[691]943
[1533]944        if (strcmp(argv[i], "-defer") == 0 )
[914]945            defer = 1;
[1533]946        else if (strcmp(argv[i], "-push") == 0 )
[914]947            push = 1;
948        else
[1533]949            model = argv[i];
[914]950       
951    }
[691]952
[1712]953    proxyPtr->flags |= INVALIDATE_CACHE;  /* disable */
[1336]954    if (!defer || push) {
955        proxyPtr->flags |= UPDATE_PENDING;
956    }
957    if (push) {
958        proxyPtr->flags |= FORCE_UPDATE;
959    }
[691]960
[1278]961    Pymol( proxyPtr, "disable %s\n", model);
[691]962
[1213]963    return proxyPtr->status;
[691]964}
965
[1278]966
[914]967static int
[1213]968EnableCmd(ClientData clientData, Tcl_Interp *interp, int argc,
969          const char *argv[])
[691]970{
[1213]971    PymolProxy *proxyPtr = clientData;
[1534]972    const char *model;
973    int i, defer, push;
[691]974
[1213]975    clear_error(proxyPtr);
[1534]976    push = defer = FALSE;
977    model = "all";
[1533]978    for(i = 1; i < argc; i++) {
[1534]979        if (strcmp(argv[i],"-defer") == 0) {
980            defer = TRUE;
981        } else if (strcmp(argv[i], "-push") == 0) {
982            push = TRUE;
983        } else {
[1533]984            model = argv[i];
[1534]985        }
[914]986    }
[1712]987    proxyPtr->flags |= INVALIDATE_CACHE; /* enable */
[1336]988    if (!defer || push) {
989        proxyPtr->flags |= UPDATE_PENDING;
990    }
991    if (push) {
992        proxyPtr->flags |= FORCE_UPDATE;
993    }
[1278]994    Pymol( proxyPtr, "enable %s\n", model);
[1213]995    return proxyPtr->status;
[691]996}
997
[914]998static int
[1278]999FrameCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1000         const char *argv[])
[691]1001{
[1213]1002    PymolProxy *proxyPtr = clientData;
[1534]1003    int i, push, defer, frame;
[691]1004
[1213]1005    clear_error(proxyPtr);
[1534]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 {
[1533]1014            frame = atoi(argv[i]);
[1534]1015        }
[691]1016    }
[1336]1017    if (!defer || push) {
1018        proxyPtr->flags |= UPDATE_PENDING;
1019    }
1020    if (push) {
1021        proxyPtr->flags |= FORCE_UPDATE;
1022    }
[1278]1023    proxyPtr->frame = frame;
[691]1024
[1278]1025    /* Does not invalidate cache? */
[691]1026
[1278]1027    Pymol(proxyPtr,"frame %d\n", frame);
[1213]1028    return proxyPtr->status;
[691]1029}
1030
1031
[914]1032static int
[1213]1033LabelCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1034         const char *argv[])
[691]1035{
[1213]1036    PymolProxy *proxyPtr = clientData;
[1533]1037    int i, push, defer, state, size;
1038    const char *model;
[691]1039
[1213]1040    clear_error(proxyPtr);
[1533]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        }
[914]1061    }
[1712]1062    proxyPtr->flags |= INVALIDATE_CACHE;  /* label */
[1336]1063    if (!defer || push) {
1064        proxyPtr->flags |= UPDATE_PENDING;
1065    }
1066    if (push) {
1067        proxyPtr->flags |= FORCE_UPDATE;
1068    }
[1553]1069    Pymol(proxyPtr,
1070          "set label_color,white,%s\n"
1071          "set label_size,%d,%s\n",
[1533]1072          model, size, model);
[691]1073    if (state) {
[1533]1074        Pymol(proxyPtr, "label %s,\"%%s%%s\" %% (ID,name)\n", model);
1075    } else {
1076        Pymol(proxyPtr, "label %s\n", model);
[691]1077    }
[1213]1078    return proxyPtr->status;
[691]1079}
1080
[1808]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 */
[914]1094static int
[1213]1095LoadPDBCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1096           const char *argv[])
[1072]1097{
[1830]1098    const char *data, *name;
1099    char *allocated;
[1213]1100    PymolProxy *proxyPtr = clientData;
[1830]1101    int state, defer, push;
[3644]1102    int numBytes;
[1830]1103    int i, j;
[1808]1104
[1830]1105    if (proxyPtr == NULL){
[1213]1106        return TCL_ERROR;
[1830]1107    }
[1213]1108    clear_error(proxyPtr);
[1830]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++;
[1072]1120        }
1121    }
[1830]1122    argc = j;
1123    if (argc < 4) {
1124        Tcl_AppendResult(interp, "wrong # arguments: should be \"", argv[0],
[3644]1125                         " <data>|follows <model> <state> ?<numBytes>?\"",
[1830]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    }
[3644]1134    numBytes = -1;
[1830]1135    if (strcmp(data, "follows") == 0) {
1136        if (argc != 5) {
1137            Tcl_AppendResult(interp, "wrong # arguments: should be \"", argv[0],
[3644]1138                         " follows <model> <state> <numBytes>\"", (char *)NULL);
[1830]1139            return TCL_ERROR;
1140        }
[3644]1141        if (Tcl_GetInt(interp, argv[4], &numBytes) != TCL_OK) {
[1830]1142            return TCL_ERROR;
1143        }
[3644]1144        if (numBytes < 0) {
[1830]1145            Tcl_AppendResult(interp, "bad value for # bytes \"", argv[4],
1146                         "\"", (char *)NULL);
1147            return TCL_ERROR;
1148        }
1149    }
[1336]1150    if (!defer || push) {
1151        proxyPtr->flags |= UPDATE_PENDING;
1152    }
1153    if (push) {
1154        proxyPtr->flags |= FORCE_UPDATE;
1155    }
[1142]1156
[1278]1157    /* Does not invalidate cache? */
1158
[1830]1159    allocated = NULL;
[3644]1160    if (numBytes >= 0) {
1161        allocated = malloc(sizeof(char) * numBytes);
[1830]1162        if (allocated == NULL) {
1163            Tcl_AppendResult(interp, "can't allocate buffer for pdbdata.",
1164                             (char *)NULL);
1165            return TCL_ERROR;
1166        }
[3644]1167        if (ReadFollowingData(&proxyPtr->client, allocated, numBytes)
[2451]1168            != BUFFER_OK) {
[1830]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 {
[3644]1176        numBytes = strlen(data);
[1830]1177    }
[1142]1178    {
[2614]1179        int f;
[3644]1180        ssize_t numWritten;
[1142]1181        char fileName[200];
1182
[2614]1183        strcpy(fileName, "/tmp/pdb.XXXXXX");
[1830]1184        proxyPtr->status = TCL_ERROR;
[2614]1185        f = mkstemp(fileName);
1186        if (f < 0) {
1187            Tcl_AppendResult(interp, "can't create temporary file \"",
1188                fileName, "\":", Tcl_PosixError(interp), (char *)NULL);
[1830]1189            goto error;
[1811]1190        }
[3644]1191        numWritten = write(f, data, numBytes);
1192        if (numBytes != numWritten) {
[1830]1193            Tcl_AppendResult(interp, "can't write PDB data to \"",
1194                             fileName, "\": ", Tcl_PosixError(interp),
1195                             (char *)NULL);
[2614]1196            close(f);
[1830]1197            goto error;
[1142]1198        }
[2614]1199        close(f);
1200        Pymol(proxyPtr, "loadandremovepdbfile %s,%s,%d\n", fileName, name, state);
[1830]1201        proxyPtr->status = TCL_OK;
[1142]1202    }
[1830]1203 error:
1204    if (allocated != NULL) {
1205        free(allocated);
1206    }
[1213]1207    return proxyPtr->status;
[1072]1208}
1209
1210static int
[1278]1211OrthoscopicCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1212              const char *argv[])
[691]1213{
[1278]1214    int bool, defer, push, i;
[1213]1215    PymolProxy *proxyPtr = clientData;
[691]1216
[1213]1217    clear_error(proxyPtr);
[1278]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            }
[1111]1229        }
1230    }
[1712]1231    proxyPtr->flags |= INVALIDATE_CACHE; /* orthoscopic */
[1336]1232    if (!defer || push) {
1233        proxyPtr->flags |= UPDATE_PENDING;
1234    }
1235    if (push) {
1236        proxyPtr->flags |= FORCE_UPDATE;
1237    }
[1278]1238    Pymol(proxyPtr, "set orthoscopic=%d\n", bool);
[1215]1239    return proxyPtr->status;
1240}
[691]1241
[1278]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 */
[1215]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;
[691]1263
[1215]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    }
[1712]1279    proxyPtr->flags |= INVALIDATE_CACHE; /* pan */
[1336]1280    if (!defer || push) {
1281        proxyPtr->flags |= UPDATE_PENDING;
1282    }
1283    if (push) {
1284        proxyPtr->flags |= FORCE_UPDATE;
1285    }
[1278]1286    if ((x != 0.0f) || (y != 0.0f)) {
[1336]1287        proxyPtr->xPan = x * 0.05;
1288        proxyPtr->yPan = -y * 0.05;
1289        proxyPtr->flags |= PAN_PENDING;
[1278]1290    }
[1213]1291    return proxyPtr->status;
[691]1292}
1293
[914]1294static int
[1278]1295PngCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
[691]1296{
[2451]1297    char buffer[BUFSIZ];
[3644]1298    int numBytes=0;
[1278]1299    PymolProxy *proxyPtr = clientData;
[1334]1300    size_t length;
1301    Image *imgPtr;
1302
[1213]1303    clear_error(proxyPtr);
[691]1304
[1336]1305    /* Force pymol to update the current scene. */
[1538]1306    Pymol(proxyPtr, "refresh\n");
1307    Pymol(proxyPtr, "png -\n");
[691]1308
[2451]1309    if (Expect(proxyPtr, "png image follows: ", buffer, BUFSIZ-1) != TCL_OK) {
1310        return TCL_ERROR;
1311    }
[3644]1312    if (sscanf(buffer, "png image follows: %d\n", &numBytes) != 1) {
[1428]1313        Tcl_AppendResult(interp, "can't get # bytes from \"", buffer, "\"",
1314                         (char *)NULL);
1315        return TCL_ERROR;
1316    }
[3644]1317    sprintf(buffer, "nv>image %d %d %d %d\n", numBytes, proxyPtr->cacheId,
[1336]1318            proxyPtr->frame, proxyPtr->rockOffset);
[1334]1319    length = strlen(buffer);
[3644]1320    imgPtr = NewImage(proxyPtr, numBytes + length);
[1334]1321    strcpy(imgPtr->data, buffer);
[3644]1322    if (ReadFollowingData(&proxyPtr->server, imgPtr->data + length, numBytes)
[2451]1323        != BUFFER_OK) {
[3644]1324        ERROR("can't read %d bytes for \"image follows\" buffer: %s", numBytes,
[2427]1325              strerror(errno));
[1334]1326        return  TCL_ERROR;
1327    }
[2451]1328    if (Expect(proxyPtr, " ScenePNG", buffer, BUFSIZ-1) != TCL_OK) {
1329        return TCL_ERROR;
1330    }
[3644]1331    stats.numFrames++;
1332    stats.numBytes += numBytes;
[1213]1333    return proxyPtr->status;
[691]1334}
1335
[1340]1336
[914]1337static int
[2614]1338PpmCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
1339{
1340    char buffer[BUFSIZ];
[3644]1341    int numBytes=0;
[2614]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    }
[3644]1355    if (sscanf(buffer, "ppm image follows: %d\n", &numBytes) != 1) {
[2614]1356        Tcl_AppendResult(interp, "can't get # bytes from \"", buffer, "\"",
1357                         (char *)NULL);
1358        return TCL_ERROR;
1359    }
[3644]1360    sprintf(buffer, "nv>image %d %d %d %d\n", numBytes, proxyPtr->cacheId,
[2614]1361            proxyPtr->frame, proxyPtr->rockOffset);
1362    length = strlen(buffer);
[3644]1363    imgPtr = NewImage(proxyPtr, numBytes + length);
[2614]1364    strcpy(imgPtr->data, buffer);
[3644]1365    if (ReadFollowingData(&proxyPtr->server, imgPtr->data + length, numBytes)
[2614]1366        != BUFFER_OK) {
[3644]1367        ERROR("can't read %d bytes for \"image follows\" buffer: %s", numBytes,
[2614]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
[3644]1376    stats.numFrames++;
1377    stats.numBytes += numBytes;
[2614]1378    return proxyPtr->status;
1379}
1380
1381
1382static int
[1340]1383PrintCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1384         const char *argv[])
1385{
1386    char buffer[800];
[3644]1387    int numBytes=0;
[1340]1388    PymolProxy *proxyPtr = clientData;
1389    size_t length;
1390    Image *imgPtr;
1391    int width, height;
[1563]1392    const char *token, *bgcolor;
[1340]1393
1394    clear_error(proxyPtr);
1395
[1563]1396    if (argc != 5) {
[1340]1397        Tcl_AppendResult(interp, "wrong # arguments: should be \"",
[1563]1398                         argv[0], " token width height color\"", (char *)NULL);
[1340]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    }
[1563]1408    bgcolor = argv[4];
[1340]1409    /* Force pymol to update the current scene. */
[1565]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    }
[1563]1417    Pymol(proxyPtr, "ray %d,%d\n", width, height);
[2451]1418    if (Expect(proxyPtr, " Ray:", buffer, BUFSIZ-1) != TCL_OK) {
1419        return TCL_ERROR;
1420    }
[1563]1421
1422    Pymol(proxyPtr, "png -,dpi=300\nbg_color black\n");
[2451]1423    if (Expect(proxyPtr, "png image follows: ", buffer, BUFSIZ-1) != TCL_OK) {
1424        return TCL_ERROR;
1425    }
[1340]1426
[3644]1427    if (sscanf(buffer, "png image follows: %d\n", &numBytes) != 1) {
[1428]1428        Tcl_AppendResult(interp, "can't get # bytes from \"", buffer, "\"",
1429                         (char *)NULL);
1430        return TCL_ERROR;
[1340]1431    }
[3644]1432    sprintf(buffer, "nv>image %d print \"%s\" %d\n", numBytes, token,
[1341]1433            proxyPtr->rockOffset);
[2381]1434    Debug("header is png is (%s)\n", buffer);
[1340]1435    length = strlen(buffer);
[3644]1436    imgPtr = NewImage(proxyPtr, numBytes + length);
[1340]1437    strcpy(imgPtr->data, buffer);
[3644]1438    if (ReadFollowingData(&proxyPtr->server, imgPtr->data + length, numBytes)
[2451]1439        != BUFFER_OK) {
[3644]1440        ERROR("can't read %d bytes for \"image follows\" buffer: %s", numBytes,
[2427]1441              strerror(errno));
[1340]1442        return  TCL_ERROR;
1443    }
[2451]1444    if (Expect(proxyPtr, " ScenePNG", buffer, BUFSIZ-1) != TCL_OK) {
1445        return TCL_ERROR;
1446    }
[1428]1447
[3644]1448    stats.numFrames++;
1449    stats.numBytes += numBytes;
[1340]1450    return proxyPtr->status;
1451}
1452
1453static int
[1278]1454RawCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
[691]1455{
[1213]1456    PymolProxy *proxyPtr = clientData;
[1278]1457    int arg, defer = 0, push = 0;
1458    const char *cmd;
1459    clear_error(proxyPtr);
[691]1460
[1278]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
[1712]1472    proxyPtr->flags |= INVALIDATE_CACHE; /* raw */
[1336]1473    if (!defer || push) {
1474        proxyPtr->flags |= UPDATE_PENDING;
1475    }
1476    if (push) {
1477        proxyPtr->flags |= FORCE_UPDATE;
1478    }
[1278]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
[1213]1490    clear_error(proxyPtr);
[691]1491
[1278]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               
[1712]1499    proxyPtr->flags |= INVALIDATE_CACHE; /* reset */
[1336]1500    if (!defer || push) {
1501        proxyPtr->flags |= UPDATE_PENDING;
1502    }
1503    if (push) {
1504        proxyPtr->flags |= FORCE_UPDATE;
1505    }
[1553]1506    Pymol(proxyPtr,
1507          "reset\n"
1508          "zoom complete=1\n");
[1278]1509    return proxyPtr->status;
1510}
[691]1511
[1278]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;
[691]1519
[1278]1520    clear_error(proxyPtr);
[691]1521
[1278]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. */
[691]1532
[1336]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;
[1278]1541    return proxyPtr->status;
1542}
1543
[1551]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
[1712]1576    proxyPtr->flags |= INVALIDATE_CACHE; /* representation */
[1551]1577    if (!defer || push) {
1578        proxyPtr->flags |= UPDATE_PENDING;
1579    }
1580    if (push) {
1581        proxyPtr->flags |= FORCE_UPDATE;
1582    }
[1563]1583    if (strcmp(rep, "ballnstick") == 0) { /* Ball 'n Stick */
[1553]1584        Pymol(proxyPtr,
[1563]1585              "set stick_color,white,%s\n"
1586              "show sticks,%s\n"
1587              "show spheres,%s\n"
[1553]1588              "hide lines,%s\n"
[1563]1589              "hide cartoon,%s\n",
1590              model, model, model, model, model);
1591    } else if (strcmp(rep, "spheres") == 0) { /* spheres */   
[1553]1592        Pymol(proxyPtr,
1593              "hide sticks,%s\n"
[1563]1594              "show spheres,%s\n"
[1553]1595              "hide lines,%s\n"
[1563]1596              "hide cartoon,%s\n"
[1553]1597              "set sphere_quality,2,%s\n"
1598              "set ambient,.2,%s\n",
[1563]1599              model, model, model, model, model, model);
1600    } else if (strcmp(rep, "none") == 0) { /* nothing */   
[1553]1601        Pymol(proxyPtr,
[1563]1602              "hide sticks,%s\n",
[1553]1603              "hide spheres,%s\n"
1604              "hide lines,%s\n"
[1563]1605              "hide cartoon,%s\n",
1606              model, model, model, model);
1607    } else if (strcmp(rep, "sticks") == 0) { /* sticks */   
[1553]1608        Pymol(proxyPtr,
[1563]1609              "set stick_color,white,%s\n"
1610              "show sticks,%s\n"
[1553]1611              "hide spheres,%s\n"
1612              "hide lines,%s\n"
[1563]1613              "hide cartoon,%s\n",
1614              model, model, model, model, model);
1615    } else if (strcmp(rep, "lines") == 0) { /* lines */   
[1553]1616        Pymol(proxyPtr,
[1563]1617              "hide sticks,%s\n"
[1553]1618              "hide spheres,%s\n"
[1563]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,
[1553]1624              "hide sticks,%s\n"
[1563]1625              "hide spheres,%s\n"
1626              "hide lines,%s\n"
1627              "show cartoon,%s\n",
1628              model, model, model, model);
1629    }
[1551]1630    return proxyPtr->status;
1631}
1632
[1278]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    }
[1712]1684    proxyPtr->flags |= INVALIDATE_CACHE; /* rotate */
[1336]1685    if (!defer || push) {
1686        proxyPtr->flags |= UPDATE_PENDING;
1687    }
1688    if (push) {
1689        proxyPtr->flags |= FORCE_UPDATE;
1690    }
[1278]1691    if ((xAngle != 0.0f) || (yAngle != 0.0f) || (zAngle != 0.0f)) {
1692        proxyPtr->xAngle += xAngle;
1693        proxyPtr->yAngle += yAngle;
1694        proxyPtr->zAngle += zAngle;
[1336]1695        proxyPtr->flags |= ROTATE_PENDING;
[691]1696    }
[1213]1697    return proxyPtr->status;
[691]1698}
[1278]1699
1700static int
1701ScreenCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1702          const char *argv[])
1703{
1704    PymolProxy *proxyPtr = clientData;
[1538]1705    int width = -1, height = -1;
1706    int defer, push, i, varg;
[1278]1707
1708    clear_error(proxyPtr);
[1538]1709    defer = push = FALSE;
1710    varg = 1;
[1533]1711    for(i = 1; i < argc; i++) {
1712        if ( strcmp(argv[i],"-defer") == 0 )
[1278]1713            defer = 1;
[1533]1714        else if ( strcmp(argv[i], "-push") == 0 )
[1278]1715            push = 1;
1716        else if (varg == 1) {
[1533]1717            width = atoi(argv[i]);
[1278]1718            height = width;
1719            varg++;
1720        }
1721        else if (varg == 2) {
[1533]1722            height = atoi(argv[i]);
[1278]1723            varg++;
1724        }
1725    }
[1538]1726    if ((width < 0) || (height < 0)) {
1727        return TCL_ERROR;
1728    }
[1336]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    }
[1337]1736    proxyPtr->width = width;
1737    proxyPtr->height = height;
1738    proxyPtr->flags |= VIEWPORT_PENDING;
[1278]1739
1740    //usleep(205000); // .2s delay for pymol to update its geometry *HACK ALERT*
[914]1741       
[1278]1742    return proxyPtr->status;
1743}
1744
[1551]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;
[1533]1753
[1551]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    }
[1712]1771    proxyPtr->flags |= INVALIDATE_CACHE;  /* sphere_scale */
[1551]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
[1278]1788static int
[1551]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    }
[1712]1813    proxyPtr->flags |= INVALIDATE_CACHE;  /* stick_radius */
[1551]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
[1533]1831TransparencyCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1832                const char *argv[])
1833{
1834    PymolProxy *proxyPtr = clientData;
[1553]1835    const char *model;
[1533]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    }
[1712]1857    proxyPtr->flags |= INVALIDATE_CACHE; /* transparency */
[1533]1858    if (!defer || push) {
1859        proxyPtr->flags |= UPDATE_PENDING;
1860    }
1861    if (push) {
1862        proxyPtr->flags |= FORCE_UPDATE;
1863    }
[1553]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);
[1533]1870    return proxyPtr->status;
1871}
1872
1873static int
[1278]1874VMouseCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1875          const char *argv[])
1876{
1877    PymolProxy *proxyPtr = clientData;
[1533]1878    int i, defer = 0, push = 0, varg = 1;
[1278]1879    int arg1 = 0, arg2 = 0, arg3 = 0, arg4 = 0, arg5 = 0;
1880
1881    clear_error(proxyPtr);
1882
[1533]1883    for(i = 1; i < argc; i++) {
1884        if (strcmp(argv[i], "-defer") == 0)
[1278]1885            defer = 1;
[1533]1886        else if (strcmp(argv[i], "-push") == 0)
[1278]1887            push = 1;
1888        else if (varg == 1) {
[1533]1889            arg1 = atoi(argv[i]);
[1278]1890            varg++;
1891        }
1892        else if (varg == 2) {
[1533]1893            arg2 = atoi(argv[i]);
[1278]1894            varg++;
1895        }
1896        else if (varg == 3) {
[1533]1897            arg3 = atoi(argv[i]);
[1278]1898            varg++;
1899        }
1900        else if (varg == 4) {
[1533]1901            arg4 = atoi(argv[i]);
[1278]1902            varg++;
1903        }
1904        else if (varg == 5) {
[1533]1905            arg5 = atoi(argv[i]);
[1278]1906            varg++;
1907        }
1908    }
1909
[1336]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);
[1278]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;
[1533]1939    int defer = 0, push = 0, i, varg = 1;
[1278]1940
1941    clear_error(proxyPtr);
1942
[1533]1943    for(i = 1; i < argc; i++) {
1944        if (strcmp(argv[i],"-defer") == 0)
[1278]1945            defer = 1;
[1533]1946        else if (strcmp(argv[i],"-push") == 0)
[1278]1947            push = 1;
1948        else if (varg == 1) {
1949            double value;
[1533]1950            if (Tcl_GetDouble(interp, argv[i], &value) != TCL_OK) {
[1278]1951                return TCL_ERROR;
1952            }
1953            factor = (float)value;
1954            varg++;
1955        }
1956    }
[1336]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    }
[1278]1964    if (factor != 0.0) {
[1336]1965        proxyPtr->zoom = factor;
1966        proxyPtr->flags |= ZOOM_PENDING;
[1278]1967    }
1968    return proxyPtr->status;
1969}
1970
1971       
[2451]1972static void
1973ChildHandler(int sigNum)
1974{
1975    ERROR("pymol (%d) died unexpectedly", stats.child);
1976    exit(1);
1977}
1978
[914]1979static int
[1970]1980ProxyInit(int cin, int cout, char *const *argv)
[691]1981{
[2614]1982    char stderrFile[200];
[1278]1983    int status, result = 0;
[1970]1984    int sin[2];
1985    int sout[2];
[914]1986    Tcl_Interp *interp;
[2441]1987    pid_t child, parent;
[1213]1988    PymolProxy proxy;
[1278]1989    struct timeval end;
[691]1990
[914]1991    /* Create three pipes for communication with the external application. One
1992     * each for the applications's: stdin, stdout, and stderr  */
[691]1993
[1970]1994    if (pipe(sin) == -1)
[1213]1995        return -1;
[691]1996
[1970]1997    if (pipe(sout) == -1) {
1998        close(sin[0]);
1999        close(sin[1]);
[1213]2000        return -1;
[914]2001    }
[691]2002
[2441]2003    parent = getpid();
[2614]2004    sprintf(stderrFile, "/tmp/pymol%d.stderr", parent);
[691]2005
[2441]2006    /* Fork the new process.  Connect I/O to the new socket.  */
[691]2007
[1278]2008    child = fork();
2009    if (child < 0) {
[2381]2010        ERROR("can't fork process: %s\n", strerror(errno));
[1213]2011        return -3;
[940]2012    }
[2445]2013
2014    interp = Tcl_CreateInterp();
[2446]2015    Tcl_MakeSafe(interp);
[2445]2016
[1278]2017    if (child == 0) {
2018        int f;
[2441]2019        char path[200];
[691]2020
[940]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         */
[1278]2028        setpgid(child, 0);
[940]2029       
2030        /* Redirect stdin, stdout, and stderr to pipes before execing */
[1278]2031
[2441]2032        dup2(sin[0],  0);               // stdin
2033        dup2(sout[1], 1);               // stdout
[2614]2034        f = open(stderrFile, O_WRONLY | O_CREAT | O_TRUNC, 0600);
[2443]2035        if (f < 0) {
2036            ERROR("can't open server error file `%s': %s", path,
2037                  strerror(errno));
2038            exit(1);
2039        }
[2441]2040        dup2(f, 2);                     /* Redirect stderr to a file */
[940]2041       
[1278]2042        /* Close all other descriptors  */       
2043        for (f = 3; f < FD_SETSIZE; f++) {
2044            close(f);
2045        }
[2451]2046        INFO("attempting to start \"%s\"", argv[0]);
[940]2047       
2048        execvp(argv[0], argv);
[2427]2049        ERROR("can't exec `%s': %s", argv[0], strerror(errno));
[1161]2050        exit(-1);
[2451]2051    } else {
2052        signal(SIGCHLD, ChildHandler);
[940]2053    }
[1278]2054    stats.child = child;
[1161]2055
[2381]2056    Debug("Started %s DISPLAY=%s\n", argv[0], getenv("DISPLAY"));
[2373]2057
[1278]2058    /* close opposite end of pipe, these now belong to the child process  */
[1970]2059    close(sin[0]);
2060    close(sout[1]);
[691]2061
[1334]2062    memset(&proxy, 0, sizeof(PymolProxy));
[1970]2063    proxy.sin  = sin[1];
2064    proxy.sout = sout[0];
2065    proxy.cin  = cin;
2066    proxy.cout = cout;
[1336]2067    proxy.flags = CAN_UPDATE;
[1213]2068    proxy.frame = 1;
[1278]2069    proxy.interp = interp;
[691]2070
[1551]2071    Tcl_CreateCommand(interp, "cartoon",       CartoonCmd,       &proxy, NULL);
2072    Tcl_CreateCommand(interp, "cartoontrace",  CartoonTraceCmd,  &proxy, NULL);
[1336]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);
[2614]2081    Tcl_CreateCommand(interp, "ppm",           PpmCmd,           &proxy, NULL);
[1351]2082    Tcl_CreateCommand(interp, "print",         PrintCmd,         &proxy, NULL);
[1336]2083    Tcl_CreateCommand(interp, "raw",           RawCmd,           &proxy, NULL);
[1551]2084    Tcl_CreateCommand(interp, "representation",RepresentationCmd,&proxy, NULL);
[1336]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);
[1551]2089    Tcl_CreateCommand(interp, "spherescale",   SphereScaleCmd,   &proxy, NULL);
2090    Tcl_CreateCommand(interp, "stickradius",   StickRadiusCmd,   &proxy, NULL);
[1533]2091    Tcl_CreateCommand(interp, "transparency",  TransparencyCmd,  &proxy, NULL);
[1336]2092    Tcl_CreateCommand(interp, "viewport",      ScreenCmd,        &proxy, NULL);
2093    Tcl_CreateCommand(interp, "vmouse",        VMouseCmd,        &proxy, NULL);
2094    Tcl_CreateCommand(interp, "zoom",          ZoomCmd,          &proxy, NULL);
[691]2095
[1278]2096
2097    gettimeofday(&end, NULL);
2098    stats.start = end;
2099
[2432]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
[1278]2105    PollForEvents(&proxy);
[691]2106
[2614]2107    unlink(stderrFile);
[1970]2108    close(proxy.cout);
2109    close(proxy.sout);
2110    close(proxy.cin);
2111    close(proxy.sin);
[1213]2112
[2419]2113    if (waitpid(child, &result, 0) < 0) {
[2381]2114        ERROR("error waiting on pymol server to exit: %s", strerror(errno));
[914]2115    }
[2419]2116    INFO("attempting to signal (SIGTERM) pymol server.");
2117    kill(-child, SIGTERM);              // Kill process group
2118    alarm(5);
[1152]2119   
[2419]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
[1999]2134    Tcl_DeleteInterp(interp);
[1152]2135   
[1176]2136    if (WIFEXITED(result)) {
2137        result = WEXITSTATUS(result);
2138    }
[1161]2139    DoExit(result);
2140    return 0;
[691]2141}
2142
2143
[1278]2144static void
2145PollForEvents(PymolProxy *proxyPtr)
2146{
2147    Tcl_DString clientCmds;
[2441]2148    struct pollfd pollFd[3];
[1278]2149    int flags;
2150
[2460]2151    if (write(proxyPtr->cout, "PyMol 1.0\n", 10) != 10) {
2152        ERROR("short write of signature");
2153    }
2154
[2437]2155    flags = fcntl(proxyPtr->cin, F_GETFL);
[1970]2156    fcntl(proxyPtr->cin, F_SETFL, flags|O_NONBLOCK);
[1278]2157
[2442]2158#ifdef notdef
[2436]2159    flags = fcntl(proxyPtr->sout, F_GETFL);
[2437]2160    fcntl(proxyPtr->sout, F_SETFL, flags|O_NONBLOCK);
[2436]2161
2162    flags = fcntl(proxyPtr->cout, F_GETFL);
[2437]2163    fcntl(proxyPtr->cout, F_SETFL, flags|O_NONBLOCK);
[2442]2164#endif
[2436]2165
[2419]2166    /* Read file descriptors. */
[2441]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;
[1278]2170
[2419]2171    /* Write file descriptors. */
[2441]2172    pollFd[2].fd = proxyPtr->cin;       /* Client standard input. */
2173    pollFd[2].events = POLLOUT;
[1278]2174
[2451]2175    InitBuffer(&proxyPtr->client, "client", proxyPtr->cout);
2176    InitBuffer(&proxyPtr->server, "server", proxyPtr->sout);
[1278]2177
2178    Tcl_DStringInit(&clientCmds);
2179    for (;;) {
2180        int timeout, nChannels;
2181
[2441]2182        nChannels =  (proxyPtr->headPtr != NULL) ? 3 : 2;
[1278]2183#define PENDING_TIMEOUT         10  /* milliseconds. */
[1336]2184        timeout = (proxyPtr->flags & UPDATE_PENDING) ? PENDING_TIMEOUT : -1;
[2441]2185        nChannels = poll(pollFd, nChannels, timeout);
[1278]2186        if (nChannels < 0) {
[2381]2187            ERROR("POLL ERROR: %s", strerror(errno));
[1278]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         */
[2441]2196        if (pollFd[1].revents & POLLIN) { /* Server stdout */
[3644]2197            int numBytes;
[1278]2198            char *line;
2199           
[2381]2200            Debug("Reading pymol stdout\n");
[1278]2201            /* Don't care what's in the server output buffer. */
2202            FlushBuffer(&proxyPtr->server);
[3644]2203            line = NextLine(&proxyPtr->server, &numBytes);
[1278]2204            if (line != NULL) {
[3644]2205                Debug("STDOUT>%.*s", numBytes, line);
2206                INFO("Garbage found from pymol server: %.%s", numBytes, line);
2207            } else if (numBytes == BUFFER_CONTINUE) {
[2629]2208                Debug("No data found on pymol stdout\n");
[2628]2209                continue;
[1278]2210            } else {
[3644]2211                ERROR("can't read pymol stdout (numBytes=%d): %s\n", numBytes,
[2427]2212                      strerror(errno));
[2419]2213                goto error;             /* Get out on EOF or error. */
[1278]2214            }
2215        }
2216
2217        /* We have some descriptors ready. */
[2441]2218        if (pollFd[0].revents & POLLIN) { /* Client stdout */
[2381]2219            Debug("Reading client stdout\n");
[1278]2220            for (;;) {
[3644]2221                int numBytes;
[1278]2222                char *line;
2223               
[3644]2224                line = NextLine(&proxyPtr->client, &numBytes);
[1278]2225                if (line != NULL) {
2226                    const char *cmd;
2227
[3644]2228                    Tcl_DStringAppend(&clientCmds, line, numBytes);
[1278]2229                    cmd = Tcl_DStringValue(&clientCmds);
2230                    if (Tcl_CommandComplete(cmd)) {
[2451]2231                        int result;
2232
[1278]2233                        /* May execute more than one command. */
[2451]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                        }
[1278]2242                        Tcl_DStringSetLength(&clientCmds, 0);
2243                    }
2244                    continue;
2245                }
[3644]2246                if (numBytes == BUFFER_CONTINUE) {
[1278]2247                    break;
2248                }
[3644]2249                ERROR("can't read client stdout (numBytes=%d): %s\n", numBytes,
[2427]2250                      strerror(errno));
[1278]2251                goto error;             /* Get out on EOF or error. */
2252            }
[2381]2253            Debug("done with client stdout\n");
[1278]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
[1336]2265        /* Handle all the pending setting changes now. */
[1998]2266        UpdateSettings(proxyPtr);
[1278]2267
2268        /* Write the current image buffer. */
[1808]2269        if (proxyPtr->headPtr == NULL) {
[1278]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. */
[1336]2273            if ((nChannels == 0) || (proxyPtr->flags & FORCE_UPDATE)) {
2274                if (proxyPtr->flags & UPDATE_PENDING) {
[2614]2275                    Tcl_Eval(proxyPtr->interp, "ppm");
[1336]2276                    proxyPtr->flags &= ~UPDATE_PENDING;
[1278]2277                }
[1336]2278                proxyPtr->flags &= ~FORCE_UPDATE;
[1278]2279                continue;
2280            }
2281        }
[2441]2282        if ((proxyPtr->headPtr != NULL) && (pollFd[2].revents & POLLOUT)) {
2283            WriteImage(proxyPtr, pollFd[2].fd);
[1278]2284        }
2285    }
2286 error:
2287    Tcl_DStringFree(&clientCmds);
2288    return;
2289}
[1970]2290
[2370]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.