source: trunk/packages/vizservers/pymolproxy/pymolproxy2.c @ 2633

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