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

Last change on this file since 1215 was 1215, checked in by gah, 16 years ago

changes to allow panning and zooming (via scrollwhell)

File size: 38.2 KB
Line 
1
2/*
3 * ----------------------------------------------------------------------
4 * proxypymol.c
5 *
6 *      This module creates the Tcl interface to the pymol server.  It acts as
7 *      a go-between establishing communication between a molvisviewer widget
8 *      and the pymol server. The communication protocol from the molvisviewer
9 *      widget is the Tcl language.  Commands are then relayed to the pymol
10 *      server.  Responses from the pymol server are translated into Tcl
11 *      commands * and send to the molvisviewer widget. For example, resulting
12 *      image rendered offscreen is returned as BMP-formatted image data.
13 *
14 *  Copyright (c) 2004-2006  Purdue Research Foundation
15 *
16 *  See the file "license.terms" for information on usage and
17 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
18 * ======================================================================
19 */
20
21#include <stdio.h>
22#include <time.h>
23#include <assert.h>
24#include <unistd.h>
25#include <stdlib.h>
26#include <string.h>
27#include <signal.h>
28#include <sys/stat.h>
29#include <sys/types.h>
30#include <sys/socket.h>
31#include <sys/wait.h>
32#include <sys/select.h>
33#include <poll.h>
34#include <sys/time.h>
35#include <sys/times.h>
36#include <fcntl.h>
37#include <netinet/in.h>
38#include <getopt.h>
39#include <errno.h>
40#include <sys/ioctl.h>
41#include <net/if.h>
42#include <tcl.h>
43
44#undef INLINE
45#ifdef __GNUC__
46#  define INLINE __inline__
47#else
48#  define INLINE
49#endif
50
51#define FALSE 0
52#define TRUE  1
53
54#define IO_TIMEOUT (30000)
55#define KEEPSTATS       1
56#define CVT2SECS(x)  ((double)(x).tv_sec) + ((double)(x).tv_usec * 1.0e-6)
57
58typedef struct {
59    pid_t child;                /* Child process. */
60    size_t nFrames;             /* # of frames sent to client. */
61    size_t nBytes;              /* # of bytes for all frames. */
62    size_t nCommands;           /* # of commands executed */
63    double cmdTime;             /* Elasped time spend executing commands. */
64    struct timeval start;       /* Start of elapsed time. */
65} Stats;
66
67static Stats stats;
68
69static FILE *flog;
70static int debug = TRUE;
71
72typedef struct {
73    char *data;
74    int   used;
75    int   allocated;
76} DyBuffer;
77
78typedef struct {
79    int p_stdin;
80    int p_stdout;
81    int p_stderr;
82    int c_stdin;
83    int c_stdout;
84    DyBuffer image;
85    int need_update;
86    int can_update;
87    int immediate_update;
88    int sync;
89    int labels;
90    int frame;
91    int rock_offset;
92    int cacheid;
93    int invalidate_cache;
94    int error;
95    int status;
96} PymolProxy;
97
98static void
99trace TCL_VARARGS_DEF(char *, arg1)
100{
101    if (debug) {
102        char *format;
103        va_list args;
104
105        format = TCL_VARARGS_START(char *, arg1, args);
106        fprintf(flog, "pymolproxy: ");
107        vfprintf(flog, format, args);
108        fprintf(flog, "\n");
109        fflush(flog);
110    }
111}
112
113#if KEEPSTATS
114
115static int
116WriteStats(const char *who, int code)
117{
118    double start, finish;
119    pid_t pid;
120    char buf[BUFSIZ];
121    Tcl_DString ds;
122
123    {
124        struct timeval tv;
125
126        /* Get ending time.  */
127        gettimeofday(&tv, NULL);
128        finish = CVT2SECS(tv);
129        tv = stats.start;
130        start = CVT2SECS(tv);
131    }
132    /*
133     * Session information:
134     *   1. Start date of session in seconds.
135     *   2. Process ID
136     *   3. Number of frames returned.
137     *   4. Number of bytes total returned (in frames).
138     *   5. Total elapsed time of all commands.
139     *   6. Total elapsed time of session.
140     *   7. Exit code of pymol server.
141     *   8. User time. 
142     *   9. System time.
143     *  10. Maximum resident size.
144     */
145    pid = getpid();
146    Tcl_DStringInit(&ds);
147   
148    sprintf(buf, "<session server=\"%s\" ", who);
149    Tcl_DStringAppend(&ds, buf, -1);
150
151    strcpy(buf, ctime(&stats.start.tv_sec));
152
153    buf[strlen(buf) - 1] = '\0';
154    Tcl_DStringAppend(&ds, "date=\"", -1);
155    Tcl_DStringAppend(&ds, buf, -1);
156    Tcl_DStringAppend(&ds, "\" ", -1);
157
158    sprintf(buf, "date_secs=\"%ld\" ", stats.start.tv_sec);
159    Tcl_DStringAppend(&ds, buf, -1);
160
161    sprintf(buf, "pid=\"%d\" ", pid);
162    Tcl_DStringAppend(&ds, buf, -1);
163    sprintf(buf, "num_frames=\"%lu\" ", (unsigned long int)stats.nFrames);
164    Tcl_DStringAppend(&ds, buf, -1);
165    sprintf(buf, "frame_bytes=\"%lu\" ", (unsigned long int)stats.nBytes);
166    Tcl_DStringAppend(&ds, buf, -1);
167    sprintf(buf, "num_commands=\"%lu\" ", (unsigned long int)stats.nCommands);
168    Tcl_DStringAppend(&ds, buf, -1);
169    sprintf(buf, "cmd_time=\"%g\" ", stats.cmdTime);
170    Tcl_DStringAppend(&ds, buf, -1);
171    sprintf(buf, "session_time=\"%g\" ", finish - start);
172    Tcl_DStringAppend(&ds, buf, -1);
173    sprintf(buf, "status=\"%d\" ", code);
174    Tcl_DStringAppend(&ds, buf, -1);
175    {
176        long clocksPerSec = sysconf(_SC_CLK_TCK);
177        double clockRes = 1.0 / clocksPerSec;
178        struct tms tms;
179
180        memset(&tms, 0, sizeof(tms));
181        times(&tms);
182        sprintf(buf, "utime=\"%g\" ", tms.tms_utime * clockRes);
183        Tcl_DStringAppend(&ds, buf, -1);
184        sprintf(buf, "stime=\"%g\" ", tms.tms_stime * clockRes);
185        Tcl_DStringAppend(&ds, buf, -1);
186        sprintf(buf, "cutime=\"%g\" ", tms.tms_cutime * clockRes);
187        Tcl_DStringAppend(&ds, buf, -1);
188        sprintf(buf, "cstime=\"%g\" ", tms.tms_cstime * clockRes);
189        Tcl_DStringAppend(&ds, buf, -1);
190    }
191    Tcl_DStringAppend(&ds, "/>\n", -1);
192
193    {
194        int f;
195        ssize_t length;
196        int result;
197
198#define STATSDIR        "/var/tmp/visservers"
199#define STATSFILE       STATSDIR "/" "data.xml"
200        if (access(STATSDIR, X_OK) != 0) {
201            mkdir(STATSDIR, 0770);
202        }
203        length = Tcl_DStringLength(&ds);
204        f = open(STATSFILE, O_APPEND | O_CREAT | O_WRONLY, 0600);
205        result = FALSE;
206        if (f < 0) {
207            goto error;
208        }
209        if (write(f, Tcl_DStringValue(&ds), length) != length) {
210            goto error;
211        }
212        result = TRUE;
213 error:
214        if (f >= 0) {
215            close(f);
216        }
217        Tcl_DStringFree(&ds);
218        return result;
219    }
220}
221#endif
222
223static void
224DoExit(int code)
225{
226    char fileName[200];
227#if KEEPSTATS
228    WriteStats("pymolproxy", code);
229#endif
230    sprintf(fileName, "/tmp/pymol%d.pdb", getpid());
231    unlink(fileName);
232    exit(code);
233}
234
235static int
236ExecuteCommand(Tcl_Interp *interp, Tcl_DString *dsPtr)
237{
238    struct timeval tv;
239    double start, finish;
240    int result;
241
242    gettimeofday(&tv, NULL);
243    start = CVT2SECS(tv);
244
245    result = Tcl_Eval(interp, Tcl_DStringValue(dsPtr));
246    trace("Executed (%s)", Tcl_DStringValue(dsPtr));
247    Tcl_DStringSetLength(dsPtr, 0);
248
249    gettimeofday(&tv, NULL);
250    finish = CVT2SECS(tv);
251
252    stats.cmdTime += finish - start;
253    stats.nCommands++;
254    return result;
255}
256
257
258INLINE static void
259dyBufferInit(DyBuffer *buffer)
260{
261    buffer->data = NULL;
262    buffer->used = 0;
263    buffer->allocated = 0;
264}
265
266INLINE static void
267dyBufferFree(DyBuffer *buffer)
268{
269    assert(buffer != NULL);
270    free(buffer->data);
271    dyBufferInit(buffer);
272}
273
274static void
275dyBufferSetLength(DyBuffer *buffer, int length)
276{
277    assert(buffer != NULL);
278    if (length == 0) {
279        dyBufferFree(buffer);
280    } else if (length > buffer->used) {
281        char *newdata;
282       
283        newdata = realloc(buffer->data, length);
284        if (newdata != NULL) {
285            buffer->data = newdata;
286            buffer->used = length;
287            buffer->allocated = length;
288        }
289    } else {
290        buffer->used = length;
291    }
292}
293
294static void
295dyBufferAppend(DyBuffer *buffer, const char *data, int length)
296{
297    int offset;
298
299    assert(buffer != NULL);
300    offset = buffer->used;
301    dyBufferSetLength(buffer, offset + length);
302    memcpy(buffer->data + offset, data, length);
303}
304
305static int
306bwrite(int sock, char *buffer, int size)
307{
308    int result;
309    int total = 0;
310    int left = size;
311
312    trace("bwrite: want to write %d bytes.", size);
313    while(1) {
314        result = write(sock,buffer+total,left);
315
316        if (result <= 0)
317            break;
318
319        total += result;
320        left -= result;
321
322        if (total == size)
323            break;
324    }
325    trace("bwrite: wrote %d bytes.", total);
326    return total;
327}
328
329static int
330bread(int sock, char *buffer, int size)
331{
332    int result, total, left;
333
334    for( total = 0, left = size; left > 0; left -= result) {
335        result = read(sock,buffer+total,left);
336       
337        if (result > 0) {
338            total += result;
339            continue;
340        }
341       
342        if ((result < 0) && (errno != EAGAIN) && (errno != EINTR)) {
343            trace("Error reading sock(%d), %d/%s.", sock, errno,
344                  strerror(errno));
345            break;
346        }
347       
348        result = 0;
349    }
350    return total;
351}
352
353#ifdef notdef
354
355static int
356bflush(int sock, char *buffer, int size, int bytes)
357{
358    int bsize;
359
360    while(bytes) {
361        if (bytes > size)
362            bsize = size;
363        else
364            bsize = bytes;
365       
366        bsize = bread(sock,buffer,bsize);
367       
368        bytes -= bsize;     
369    }
370}
371
372#undef timersub
373static void
374timersub(struct timeval *a, struct timeval *b, struct timeval *result)
375{
376    result->tv_sec = a->tv_sec - b->tv_sec;
377    result->tv_usec = a->tv_usec - b->tv_usec;
378
379    while(result->tv_usec < 0) {
380        result->tv_sec -= 1;
381        result->tv_usec += 1000000;
382    }
383}
384
385#endif
386
387static void
388timersub_ms(struct timeval *a, struct timeval *b, int *result)
389{
390    struct timeval tmp;
391
392    tmp.tv_sec = a->tv_sec - b->tv_sec;
393    tmp.tv_usec = a->tv_usec - b->tv_usec;
394
395    while(tmp.tv_usec < 0) {
396        tmp.tv_sec -= 1;
397        tmp.tv_usec += 1000000;
398    }
399
400    *result = (tmp.tv_sec * 1000) + (tmp.tv_usec / 1000);
401}
402
403#undef timeradd
404static void
405timeradd (struct timeval *a, struct timeval *b, struct timeval *result)
406{
407    result->tv_sec = a->tv_sec + b->tv_sec;
408    result->tv_usec = a->tv_usec + b->tv_usec;
409
410    while (result->tv_usec >= 1000000) {
411        result->tv_sec += 1;
412        result->tv_usec -= 1000000;
413    }
414}
415
416static void
417timerset_ms(struct timeval *result, int timeout)
418{
419    result->tv_sec = timeout / 1000;
420    result->tv_usec = (timeout % 1000) * 1000;
421}
422
423INLINE static void
424clear_error(PymolProxy *proxyPtr)
425{
426    proxyPtr->error = 0;
427    proxyPtr->status = TCL_OK;
428}
429
430static int
431getline(int sock, char *buffer, int size, int timeout)
432{
433    int pos = 0, status, timeo;
434    struct timeval now, end, tmo;
435    struct pollfd ufd;
436
437    gettimeofday(&now,NULL);
438    timerset_ms(&tmo, timeout);
439    timeradd(&now,&tmo,&end);
440
441    ufd.fd = sock;
442    ufd.events = POLLIN;
443
444    size--;
445
446    while(pos < size) {
447        if (timeout > 0) {
448            gettimeofday(&now,NULL);
449            timersub_ms(&now,&end,&timeo);
450        }
451        else
452            timeo = -1;
453
454        status = poll(&ufd, 1, timeo);
455
456        if (status > 0)
457            status = read(sock,&buffer[pos],1);
458
459        if ( (status < 0) && ( (errno == EINTR) || (errno == EAGAIN) ) )
460            continue; /* try again, if interrupted/blocking */
461
462        if (status <= 0) {
463            trace("getline: status=%d, errno=%d", status, errno);
464            break;
465        }
466        if (buffer[pos] == '\n') {
467            pos++;
468            break;
469        }
470        pos++;
471    }
472
473    buffer[pos]=0;
474    return pos;
475}
476
477static int
478Expect(PymolProxy *proxyPtr, char *match, char *buffer, int length)
479{
480    int sock;
481    char c;
482    size_t mlen;
483
484    assert(buffer != NULL);
485    if (proxyPtr->status != TCL_OK)
486        return proxyPtr->status;
487
488    sock = proxyPtr->p_stdout;
489    trace("Expect(%s)", match);
490    c = match[0];
491    mlen = strlen(match);
492    for (;;) {
493        if (getline(sock, buffer, length, IO_TIMEOUT) == 0) {
494            proxyPtr->error = 2;
495            proxyPtr->status = TCL_ERROR;
496            break;
497        }
498        trace("stdout-u>read(%s)", buffer);
499        if ((c == buffer[0]) && (strncmp(buffer, match, mlen) == 0)) {
500            trace("stdout-e> %s", buffer);
501            clear_error(proxyPtr);
502            break;
503        }
504    }
505
506    return proxyPtr->status;
507}
508
509static int
510Send(PymolProxy *proxyPtr, char *format, ...)
511{
512    va_list ap;
513    char iobuffer[BUFSIZ];
514
515    if (proxyPtr->error) {
516        return TCL_ERROR;
517    }
518    va_start(ap, format);
519    vsnprintf(iobuffer, 800, format, ap);
520    va_end(ap);
521
522    trace("to-pymol>(%s)", iobuffer);
523
524    /* Write the command out to the server. */
525    write(proxyPtr->p_stdin, iobuffer, strlen(iobuffer));
526
527    /* Now wait for the "PyMOL>" prompt. */
528    if (Expect(proxyPtr, "PyMOL>", iobuffer, BUFSIZ)) {
529        trace("timeout reading data (iobuffer=%s)", iobuffer);
530        proxyPtr->error = 1;
531        proxyPtr->status = TCL_ERROR;
532        return proxyPtr->status;
533    }
534    return  proxyPtr->status;
535}
536
537static int
538BallNStickCmd(ClientData clientData, Tcl_Interp *interp, int argc,
539              const char *argv[])
540{
541    float radius, transparency;
542    int ghost = 0, defer = 0, push = 0, arg;
543    const char *model = "all";
544    PymolProxy *proxyPtr = clientData;
545
546    clear_error(proxyPtr);
547
548    for(arg = 1; arg < argc; arg++) {
549        if ( strcmp(argv[arg],"-defer") == 0 )
550            defer = 1;
551        else if (strcmp(argv[arg],"-push") == 0)
552            push = 1;
553        else if (strcmp(argv[arg],"-ghost") == 0)
554            ghost = 1;
555        else if (strcmp(argv[arg],"-normal") == 0)
556            ghost = 0;
557        else if (strcmp(argv[arg],"-model") == 0) {
558            if (++arg < argc)
559                model = argv[arg];
560        }
561        else
562            model = argv[arg];
563    }
564
565    proxyPtr->invalidate_cache = 1;
566    proxyPtr->need_update = !defer || push;
567    proxyPtr->immediate_update |= push;
568
569    Send(proxyPtr, "hide everything,%s\n",model);
570    Send(proxyPtr, "set stick_color,white,%s\n",model);
571    if (ghost) {
572        radius = 0.1f;
573        transparency = 0.75f;
574    } else {
575        radius = 0.14f;
576        transparency = 0.0f;
577    }
578    Send(proxyPtr, "set stick_radius,%g,%s\n", radius, model);
579    Send(proxyPtr, "set sphere_scale=0.25,%s\n", model);
580    Send(proxyPtr, "set sphere_transparency,%g,%s\n", transparency, model);
581    Send(proxyPtr, "set stick_transparency,%g,%s\n", transparency, model);
582    Send(proxyPtr, "show sticks,%s\n",model);
583    Send(proxyPtr, "show spheres,%s\n",model);
584
585    if (proxyPtr->labels)
586        Send(proxyPtr, "show labels,%s\n", model);
587
588    return proxyPtr->status;
589}
590
591static int
592SpheresCmd(ClientData clientData, Tcl_Interp *interp, int argc,
593           const char *argv[])
594{
595    int defer = 0, ghost = 0, push = 0, arg;
596    const char *model = "all";
597    PymolProxy *proxyPtr = clientData;
598
599    clear_error(proxyPtr);
600
601    for(arg = 1; arg < argc; arg++) {
602        if ( strcmp(argv[arg],"-defer") == 0 )
603            defer = 1;
604        else if (strcmp(argv[arg],"-push") == 0)
605            push = 1;
606        else if (strcmp(argv[arg],"-ghost") == 0)
607            ghost = 1;
608        else if (strcmp(argv[arg],"-normal") == 0)
609            ghost = 0;
610        else if (strcmp(argv[arg],"-model") == 0) {
611            if (++arg < argc)
612                model = argv[arg];
613        }
614        else
615            model = argv[arg];
616    }
617
618    proxyPtr->invalidate_cache = 1;
619    proxyPtr->need_update = !defer || push;
620    proxyPtr->immediate_update |= push;
621
622    Send(proxyPtr, "hide everything, %s\n", model);
623    Send(proxyPtr, "set sphere_scale,0.41,%s\n", model);
624    //Send(proxyPtr, "set sphere_quality,2,%s\n", model);
625    Send(proxyPtr, "set ambient,.2,%s\n", model);
626
627    if (ghost)
628        Send(proxyPtr, "set sphere_transparency,.75,%s\n", model);
629    else
630        Send(proxyPtr, "set sphere_transparency,0,%s\n", model);
631
632    Send(proxyPtr, "show spheres,%s\n", model);
633
634    if (proxyPtr->labels)
635        Send(proxyPtr, "show labels,%s\n", model);
636
637    return proxyPtr->status;
638}
639
640static int
641LinesCmd(ClientData clientData, Tcl_Interp *interp, int argc,
642         const char *argv[])
643{
644    int ghost = 0, defer = 0, push = 0, arg;
645    const char *model = "all";
646    PymolProxy *proxyPtr = clientData;
647    float lineWidth;
648
649    clear_error(proxyPtr);
650
651    for(arg = 1; arg < argc; arg++) {
652        if ( strcmp(argv[arg],"-defer") == 0 )
653            defer = 1;
654        else if (strcmp(argv[arg],"-push") == 0)
655            push = 1;
656        else if (strcmp(argv[arg],"-ghost") == 0)
657            ghost = 1;
658        else if (strcmp(argv[arg],"-normal") == 0)
659            ghost = 0;
660        else if (strcmp(argv[arg],"-model") == 0) {
661            if (++arg < argc)
662                model = argv[arg];
663        }
664        else
665            model = argv[arg];
666    }
667
668    proxyPtr->invalidate_cache = 1;
669    proxyPtr->need_update = !defer || push;
670    proxyPtr->immediate_update |= push;
671
672    Send(proxyPtr, "hide everything,%s\n",model);
673
674    lineWidth = (ghost) ? 0.25f : 1.0f;
675    Send(proxyPtr, "set line_width,%g,%s\n", lineWidth, model);
676    Send(proxyPtr, "show lines,%s\n",model);
677    if (proxyPtr->labels)
678        Send(proxyPtr, "show labels,%s\n",model);
679
680    return proxyPtr->status;
681}
682
683static int
684DisableCmd(ClientData clientData, Tcl_Interp *interp, int argc,
685           const char *argv[])
686{
687    PymolProxy *proxyPtr = clientData;
688    const char *model = "all";
689    int arg, defer = 0, push = 0;
690
691    clear_error(proxyPtr);
692
693    for(arg = 1; arg < argc; arg++) {
694
695        if (strcmp(argv[arg], "-defer") == 0 )
696            defer = 1;
697        else if (strcmp(argv[arg], "-push") == 0 )
698            push = 1;
699        else
700            model = argv[arg];
701       
702    }
703
704    proxyPtr->need_update = !defer || push;
705    proxyPtr->immediate_update |= push;
706    proxyPtr->invalidate_cache = 1;
707
708    Send( proxyPtr, "disable %s\n", model);
709
710    return proxyPtr->status;
711}
712
713static int
714EnableCmd(ClientData clientData, Tcl_Interp *interp, int argc,
715          const char *argv[])
716{
717    PymolProxy *proxyPtr = clientData;
718    const char *model = "all";
719    int arg, defer = 0, push = 0;
720
721    clear_error(proxyPtr);
722
723    for(arg = 1; arg < argc; arg++) {
724               
725        if (strcmp(argv[arg],"-defer") == 0)
726            defer = 1;
727        else if (strcmp(argv[arg], "-push") == 0 )
728            push = 1;
729        else
730            model = argv[arg];
731
732    }
733
734    proxyPtr->need_update = !defer || push;
735    proxyPtr->immediate_update |= push;
736    proxyPtr->invalidate_cache = 1;
737
738    Send( proxyPtr, "enable %s\n", model);
739
740    return proxyPtr->status;
741}
742
743static int
744VMouseCmd(ClientData clientData, Tcl_Interp *interp, int argc,
745          const char *argv[])
746{
747    PymolProxy *proxyPtr = clientData;
748    int arg, defer = 0, push = 0, varg = 1;
749    int arg1 = 0, arg2 = 0, arg3 = 0, arg4 = 0, arg5 = 0;
750
751    clear_error(proxyPtr);
752
753    for(arg = 1; arg < argc; arg++) {
754        if (strcmp(argv[arg], "-defer") == 0)
755            defer = 1;
756        else if (strcmp(argv[arg], "-push") == 0)
757            push = 1;
758        else if (varg == 1) {
759            arg1 = atoi(argv[arg]);
760            varg++;
761        }
762        else if (varg == 2) {
763            arg2 = atoi(argv[arg]);
764            varg++;
765        }
766        else if (varg == 3) {
767            arg3 = atoi(argv[arg]);
768            varg++;
769        }
770        else if (varg == 4) {
771            arg4 = atoi(argv[arg]);
772            varg++;
773        }
774        else if (varg == 5) {
775            arg5 = atoi(argv[arg]);
776            varg++;
777        }
778    }
779
780    proxyPtr->need_update = !defer || push;
781    proxyPtr->immediate_update |= push;
782    proxyPtr->invalidate_cache = 1;
783
784    Send(proxyPtr, "vmouse %d,%d,%d,%d,%d\n", arg1, arg2, arg3,
785                    arg4, arg5);
786
787    return proxyPtr->status;
788}
789
790static int
791RawCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
792{
793    PymolProxy *proxyPtr = clientData;
794    DyBuffer buffer;
795    int arg, defer = 0, push = 0;
796    const char *cmd;
797    clear_error(proxyPtr);
798
799    dyBufferInit(&buffer);
800
801    cmd = NULL;
802    for(arg = 1; arg < argc; arg++) {
803        if (strcmp(argv[arg], "-defer") == 0)
804            defer = 1;
805        else if (strcmp(argv[arg], "-push") == 0)
806            push = 1;
807        else {
808            cmd = argv[arg];
809        }
810    }
811
812    proxyPtr->need_update = !defer || push;
813    proxyPtr->immediate_update |= push;
814    proxyPtr->invalidate_cache = 1;
815
816    Send(proxyPtr,"%s\n", cmd);
817    dyBufferFree(&buffer);
818
819    return proxyPtr->status;
820}
821
822static int
823LabelCmd(ClientData clientData, Tcl_Interp *interp, int argc,
824         const char *argv[])
825{
826    PymolProxy *proxyPtr = clientData;
827    int state = 1;
828    int arg, push = 0, defer = 0;
829
830    clear_error(proxyPtr);
831
832    for(arg = 1; arg < argc; arg++) {
833        if ( strcmp(argv[arg],"-defer") == 0 )
834            defer = 1;
835        else if (strcmp(argv[arg],"-push") == 0 )
836            push = 1;
837        else if (strcmp(argv[arg],"on") == 0 )
838            state = 1;
839        else if (strcmp(argv[arg],"off") == 0 )
840            state = 0;
841        else if (strcmp(argv[arg],"toggle") == 0 )
842            state =  !proxyPtr->labels;
843    }
844
845    proxyPtr->need_update = !defer || push;
846    proxyPtr->immediate_update |= push;
847    proxyPtr->invalidate_cache = 1;
848
849    if (state) {
850        Send(proxyPtr, "set label_color,white,all\n");
851        Send(proxyPtr, "set label_size,14,all\n");
852        Send(proxyPtr, "label all,\"%%s%%s\" %% (ID,name)\n");
853    }
854    else
855        Send(proxyPtr, "label all\n");
856
857    proxyPtr->labels = state;
858
859    return proxyPtr->status;
860}
861
862static int
863FrameCmd(ClientData clientData, Tcl_Interp *interp, int argc,
864         const char *argv[])
865{
866    PymolProxy *proxyPtr = clientData;
867    int frame = 0;
868    int arg, push = 0, defer = 0;
869
870    clear_error(proxyPtr);
871
872    for(arg = 1; arg < argc; arg++) {
873        if ( strcmp(argv[arg],"-defer") == 0 )
874            defer = 1;
875        else if (strcmp(argv[arg],"-push") == 0 )
876            push = 1;
877        else
878            frame = atoi(argv[arg]);
879    }
880               
881    proxyPtr->need_update = !defer || push;
882    proxyPtr->immediate_update |= push;
883
884    proxyPtr->frame = frame;
885
886    Send(proxyPtr,"frame %d\n", frame);
887       
888    return proxyPtr->status;
889}
890
891static int
892ResetCmd(ClientData clientData, Tcl_Interp *interp, int argc,
893         const char *argv[])
894{
895    PymolProxy *proxyPtr = clientData;
896    int arg, push = 0, defer = 0;
897
898    clear_error(proxyPtr);
899
900    for(arg = 1; arg < argc; arg++) {
901        if ( strcmp(argv[arg],"-defer") == 0 )
902            defer = 1;
903        else if (strcmp(argv[arg],"-push") == 0 )
904            push = 1;
905    }
906               
907    proxyPtr->need_update = !defer || push;
908    proxyPtr->immediate_update |= push;
909    proxyPtr->invalidate_cache = 1;
910       
911    Send(proxyPtr, "reset\n");
912    Send(proxyPtr, "zoom buffer=2\n");
913
914    return proxyPtr->status;
915}
916
917static int
918RockCmd(ClientData clientData, Tcl_Interp *interp, int argc,
919        const char *argv[])
920{
921    PymolProxy *proxyPtr = clientData;
922    float y = 0.0;
923    int arg, push = 0, defer = 0;
924
925    clear_error(proxyPtr);
926
927    for(arg = 1; arg < argc; arg++) {
928        if ( strcmp(argv[arg],"-defer") == 0 )
929            defer = 1;
930        else if (strcmp(argv[arg],"-push") == 0 )
931            push = 1;
932        else
933            y = atof( argv[arg] );
934    }
935               
936    proxyPtr->need_update = !defer || push;
937    proxyPtr->immediate_update |= push;
938
939    Send(proxyPtr,"turn y, %f\n", y - proxyPtr->rock_offset);
940
941    proxyPtr->rock_offset = y;
942
943    return proxyPtr->status;
944}
945
946static int
947ViewportCmd(ClientData clientData, Tcl_Interp *interp, int argc,
948            const char *argv[])
949{
950    PymolProxy *proxyPtr = clientData;
951    int width = 640, height = 480;
952    int defer = 0, push = 0, arg, varg = 1;
953
954    clear_error(proxyPtr);
955
956    for(arg = 1; arg < argc; arg++) {
957        if ( strcmp(argv[arg],"-defer") == 0 )
958            defer = 1;
959        else if ( strcmp(argv[arg], "-push") == 0 )
960            push = 1;
961        else if (varg == 1) {
962            width = atoi(argv[arg]);
963            height = width;
964            varg++;
965        }
966        else if (varg == 2) {
967            height = atoi(argv[arg]);
968            varg++;
969        }
970    }
971
972    proxyPtr->need_update = !defer || push;
973    proxyPtr->immediate_update |= push;
974    proxyPtr->invalidate_cache = 1;
975
976    Send(proxyPtr, "viewport %d,%d\n", width, height);
977
978    //usleep(205000); // .2s delay for pymol to update its geometry *HACK ALERT*
979       
980    return proxyPtr->status;
981}
982
983static int
984LoadPDBCmd(ClientData clientData, Tcl_Interp *interp, int argc,
985           const char *argv[])
986{
987    const char *pdbdata, *name;
988    PymolProxy *proxyPtr = clientData;
989    int state = 1;
990    int arg, defer = 0, push = 0, varg = 1;
991   
992    if (proxyPtr == NULL)
993        return TCL_ERROR;
994    clear_error(proxyPtr);
995    pdbdata = name = NULL;      /* Suppress compiler warning. */
996    for(arg = 1; arg < argc; arg++) {
997        if ( strcmp(argv[arg],"-defer") == 0 )
998            defer = 1;
999        else if (strcmp(argv[arg],"-push") == 0)
1000            push = 1;
1001        else if (varg == 1) {
1002            pdbdata = argv[arg];
1003            varg++;
1004        } else if (varg == 2) {
1005            name = argv[arg];
1006            varg++;
1007        } else if (varg == 3) {
1008            state = atoi( argv[arg] );
1009            varg++;
1010        }
1011    }
1012   
1013    proxyPtr->need_update = !defer || push;
1014    proxyPtr->immediate_update |= push;
1015
1016    {
1017        char fileName[200];
1018        FILE *f;
1019        ssize_t nBytes, nWritten;
1020
1021        sprintf(fileName, "/tmp/pymol%d.pdb", getpid());
1022        f = fopen(fileName, "w");
1023        if (f == NULL) {
1024            trace("can't open `%s': %s", fileName, strerror(errno));
1025            perror("pymolproxy");
1026        }
1027        nBytes = strlen(pdbdata);
1028        nWritten = fwrite(pdbdata, sizeof(char), nBytes, f);
1029        if (nBytes != nWritten) {
1030            trace("short write %d wanted %d bytes", nWritten, nBytes);
1031            perror("pymolproxy");
1032        }
1033        fclose(f);
1034        Send(proxyPtr, "load %s, %s, %d\n", fileName, name, state);
1035    }
1036    Send(proxyPtr, "zoom buffer=2\n");
1037    return proxyPtr->status;
1038}
1039
1040static int
1041RotateCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1042          const char *argv[])
1043{
1044    double turnx = 0.0;
1045    double turny = 0.0;
1046    double turnz = 0.0;
1047    PymolProxy *proxyPtr = clientData;
1048    int defer = 0, push = 0, arg, varg = 1;
1049
1050    clear_error(proxyPtr);
1051
1052    for(arg = 1; arg < argc; arg++) {
1053        if (strcmp(argv[arg],"-defer") == 0) {
1054            defer = 1;
1055        } else if (strcmp(argv[arg],"-push") == 0) {
1056            push = 1;
1057        } else  if (varg == 1) {
1058            turnx = atof(argv[arg]);
1059            varg++;
1060        } else if (varg == 2) {
1061            turny = atof(argv[arg]);
1062            varg++;
1063        } else if (varg == 3) {
1064            turnz = atof(argv[arg]);
1065            varg++;
1066        }
1067    }
1068    proxyPtr->need_update = !defer || push;
1069    proxyPtr->immediate_update  |= push;
1070    proxyPtr->invalidate_cache = 1;
1071
1072    if (turnx != 0.0)
1073        Send(proxyPtr,"turn x, %f\n", turnx);
1074       
1075    if (turny != 0.0)
1076        Send(proxyPtr,"turn y, %f\n", turny);
1077       
1078    if (turnz != 0.0)
1079        Send(proxyPtr,"turn z, %f\n", turnz);
1080
1081    return proxyPtr->status;
1082}
1083
1084static int
1085ZoomCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1086        const char *argv[])
1087{
1088    double factor = 0.0;
1089    PymolProxy *proxyPtr = clientData;
1090    int defer = 0, push = 0, arg, varg = 1;
1091
1092    clear_error(proxyPtr);
1093
1094    for(arg = 1; arg < argc; arg++) {
1095        if (strcmp(argv[arg],"-defer") == 0)
1096            defer = 1;
1097        else if (strcmp(argv[arg],"-push") == 0)
1098            push = 1;
1099        else if (varg == 1) {
1100            factor = atof(argv[arg]);
1101            varg++;
1102        }
1103    }
1104    proxyPtr->need_update = !defer || push;
1105    proxyPtr->immediate_update  |= push;
1106    proxyPtr->invalidate_cache = 1;
1107    if (factor != 0.0) {
1108        Send(proxyPtr,"move z,%f\n", factor);
1109    }
1110    return proxyPtr->status;
1111}
1112
1113static int
1114PanCmd(ClientData clientData, Tcl_Interp *interp, int argc,
1115        const char *argv[])
1116{
1117    PymolProxy *proxyPtr = clientData;
1118    double x, y;
1119    int i;
1120    int defer, push;
1121
1122    clear_error(proxyPtr);
1123    defer = push = FALSE;
1124    for (i = 1; i < argc; i++) {
1125        if (strcmp(argv[i],"-defer") == 0) {
1126            defer = 1;
1127        } else if (strcmp(argv[i],"-push") == 0) {
1128            push = 1;
1129        } else {
1130            break;
1131        }
1132    }
1133    if ((Tcl_GetDouble(interp, argv[i], &x) != TCL_OK) ||
1134        (Tcl_GetDouble(interp, argv[i+1], &y) != TCL_OK)) {
1135        return TCL_ERROR;
1136    }
1137    proxyPtr->need_update = !defer || push;
1138    proxyPtr->immediate_update  |= push;
1139    proxyPtr->invalidate_cache = 1;
1140    if (x != 0.0) {
1141        Send(proxyPtr,"move x,%f\n", x * 0.005);
1142    }   
1143    if (y != 0.0) {
1144        Send(proxyPtr,"move y,%f\n", -y * 0.005);
1145    }   
1146    return proxyPtr->status;
1147}
1148
1149static int
1150PNGCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
1151{
1152    char buffer[800];
1153    unsigned int nBytes=0;
1154    float samples = 10.0;
1155    PymolProxy *proxyPtr = clientData;
1156
1157    clear_error(proxyPtr);
1158
1159    if (proxyPtr->invalidate_cache)
1160        proxyPtr->cacheid++;
1161
1162    proxyPtr->need_update = 0;
1163    proxyPtr->immediate_update = 0;
1164    proxyPtr->invalidate_cache = 0;
1165
1166    Send(proxyPtr,"png -\n");
1167
1168    Expect(proxyPtr, "image follows: ", buffer, 800);
1169
1170    sscanf(buffer, "image follows: %d\n", &nBytes);
1171 
1172    write(3, &samples, sizeof(samples));
1173 
1174    dyBufferSetLength(&proxyPtr->image, nBytes);
1175
1176    bread(proxyPtr->p_stdout, proxyPtr->image.data, proxyPtr->image.used);
1177
1178    Expect(proxyPtr, " ScenePNG", buffer,800);
1179
1180    if ((nBytes > 0) && (proxyPtr->image.used == nBytes)) {
1181        sprintf(buffer, "nv>image %d %d %d %d\n",
1182                nBytes, proxyPtr->cacheid, proxyPtr->frame, proxyPtr->rock_offset);
1183        trace("to-molvis> %s", buffer);
1184        write(proxyPtr->c_stdin, buffer, strlen(buffer));
1185        bwrite(proxyPtr->c_stdin, proxyPtr->image.data, nBytes);
1186        stats.nFrames++;
1187        stats.nBytes += nBytes;
1188    }
1189    return proxyPtr->status;
1190}
1191
1192static int
1193BMPCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
1194{
1195    char buffer[800];
1196    unsigned int nBytes=0;
1197    float samples = 10.0;
1198    PymolProxy *proxyPtr = clientData;
1199
1200    clear_error(proxyPtr);
1201
1202    if (proxyPtr->invalidate_cache)
1203        proxyPtr->cacheid++;
1204
1205    proxyPtr->need_update = 0;
1206    proxyPtr->immediate_update = 0;
1207    proxyPtr->invalidate_cache = 0;
1208
1209    Send(proxyPtr,"bmp -\n");
1210
1211    Expect(proxyPtr, "image follows: ", buffer, 800);
1212
1213    sscanf(buffer, "image follows: %d\n", &nBytes);
1214    write(3,&samples,sizeof(samples));
1215
1216    dyBufferSetLength(&proxyPtr->image, nBytes);
1217
1218    bread(proxyPtr->p_stdout, proxyPtr->image.data, proxyPtr->image.used);
1219
1220    if ((nBytes > 0) && (proxyPtr->image.used == nBytes)) {
1221        sprintf(buffer, "nv>image %d %d %d %d\n",
1222                nBytes, proxyPtr->cacheid, proxyPtr->frame, proxyPtr->rock_offset);
1223        write(proxyPtr->c_stdin, buffer, strlen(buffer));
1224        trace("to-molvis buffer=%s", buffer);
1225        bwrite(proxyPtr->c_stdin, proxyPtr->image.data, nBytes);
1226        stats.nFrames++;
1227        stats.nBytes += nBytes;
1228    }
1229    return proxyPtr->status;
1230}
1231       
1232static int
1233ProxyInit(int c_in, int c_out, char *const *argv)
1234{
1235    int flags, status, result = 0;
1236    int pairIn[2];
1237    int pairOut[2];
1238    int pairErr[2];
1239    Tcl_Interp *interp;
1240    Tcl_DString cmdbuffer;
1241    DyBuffer dybuffer, dybuffer2;
1242    struct pollfd ufd[3];
1243    int pid;
1244    PymolProxy proxy;
1245    struct timeval now, end;
1246    int timeout;
1247
1248    /* Create three pipes for communication with the external application. One
1249     * each for the applications's: stdin, stdout, and stderr  */
1250
1251    if (pipe(pairIn) == -1)
1252        return -1;
1253
1254    if (pipe(pairOut) == -1) {
1255        close(pairIn[0]);
1256        close(pairIn[1]);
1257        return -1;
1258    }
1259
1260    if (pipe(pairErr) == -1) {
1261        close(pairIn[0]);
1262        close(pairIn[1]);
1263        close(pairOut[0]);
1264        close(pairOut[1]);
1265        return -1;
1266    }
1267
1268    /* Fork the new process.  Connect i/o to the new socket.  */
1269
1270    pid = fork();
1271       
1272    if (pid < 0) {
1273        fprintf(stderr, "can't fork process: %s\n", strerror(errno));
1274        return -3;
1275    }
1276    if (pid == 0) {
1277        int fd;
1278
1279        /* Child process */
1280       
1281        /*
1282         * Create a new process group, so we can later kill this process and
1283         * all its children without affecting the process that created this
1284         * one.
1285         */
1286        setpgid(pid, 0);
1287       
1288        /* Redirect stdin, stdout, and stderr to pipes before execing */
1289        dup2(pairIn[0] ,0);  // stdin
1290        dup2(pairOut[1],1);  // stdout
1291        dup2(pairErr[1],2);  // stderr
1292       
1293        for(fd = 3; fd < FD_SETSIZE; fd++)  /* close all other descriptors  */
1294            close(fd);
1295       
1296        execvp(argv[0], argv);
1297        trace("Failed to start pymol `%s'", argv[0]);
1298        exit(-1);
1299    }
1300    stats.child = pid;
1301
1302    /* close opposite end of pipe, these now belong to the child process        */
1303    close(pairIn[0]);
1304    close(pairOut[1]);
1305    close(pairErr[1]);
1306
1307    signal(SIGPIPE, SIG_IGN); // ignore SIGPIPE (ie if nanoscale terminates)
1308
1309    proxy.p_stdin = pairIn[1];
1310    proxy.p_stdout = pairOut[0];
1311    proxy.p_stderr = pairErr[0];
1312    proxy.c_stdin  = c_in;
1313    proxy.c_stdout = c_out;
1314    proxy.labels = 0;
1315    proxy.need_update = 0;
1316    proxy.can_update = 1;
1317    proxy.immediate_update = 0;
1318    proxy.sync = 0;
1319    proxy.frame = 1;
1320    proxy.rock_offset = 0;
1321    proxy.cacheid = 0;
1322    proxy.invalidate_cache = 0;
1323
1324    ufd[0].fd = proxy.c_stdout;
1325    ufd[0].events = POLLIN | POLLHUP; /* ensure catching EOF */
1326    ufd[1].fd = proxy.p_stdout;
1327    ufd[1].events = POLLIN | POLLHUP;
1328    ufd[2].fd = proxy.p_stderr;
1329    ufd[2].events = POLLIN | POLLHUP;
1330
1331    flags = fcntl(proxy.p_stdout, F_GETFL);
1332    fcntl(proxy.p_stdout, F_SETFL, flags|O_NONBLOCK);
1333
1334    interp = Tcl_CreateInterp();
1335    Tcl_MakeSafe(interp);
1336
1337    Tcl_DStringInit(&cmdbuffer);
1338    dyBufferInit(&proxy.image);
1339    dyBufferInit(&dybuffer);
1340    dyBufferInit(&dybuffer2);
1341
1342    Tcl_CreateCommand(interp, "bmp",     BMPCmd,        &proxy, NULL);
1343    Tcl_CreateCommand(interp, "png",     PNGCmd,        &proxy, NULL);
1344    Tcl_CreateCommand(interp, "screen",  ViewportCmd,   &proxy, NULL);
1345    Tcl_CreateCommand(interp, "viewport",ViewportCmd,   &proxy, NULL);
1346    Tcl_CreateCommand(interp, "rotate",  RotateCmd,     &proxy, NULL);
1347    Tcl_CreateCommand(interp, "zoom",    ZoomCmd,       &proxy, NULL);
1348    Tcl_CreateCommand(interp, "loadpdb", LoadPDBCmd,    &proxy, NULL);
1349    Tcl_CreateCommand(interp, "ballnstick",BallNStickCmd, &proxy, NULL);
1350    Tcl_CreateCommand(interp, "spheres", SpheresCmd,    &proxy, NULL);
1351    Tcl_CreateCommand(interp, "lines",   LinesCmd,      &proxy, NULL);
1352    Tcl_CreateCommand(interp, "raw",     RawCmd,        &proxy, NULL);
1353    Tcl_CreateCommand(interp, "label",   LabelCmd,      &proxy, NULL);
1354    Tcl_CreateCommand(interp, "reset",   ResetCmd,      &proxy, NULL);
1355    Tcl_CreateCommand(interp, "rock",    RockCmd,       &proxy, NULL);
1356    Tcl_CreateCommand(interp, "frame",   FrameCmd,      &proxy, NULL);
1357    Tcl_CreateCommand(interp, "vmouse",  VMouseCmd,     &proxy, NULL);
1358    Tcl_CreateCommand(interp, "disable", DisableCmd,    &proxy, NULL);
1359    Tcl_CreateCommand(interp, "enable",  EnableCmd,     &proxy, NULL);
1360    Tcl_CreateCommand(interp, "pan",     PanCmd,        &proxy, NULL);
1361
1362    // Main Proxy Loop
1363    //  accept tcl commands from socket
1364    //  translate them into pyMol commands, and issue them to child proccess
1365    //  send images back
1366
1367    gettimeofday(&end, NULL);
1368    stats.start = end;
1369    while(1) {
1370        gettimeofday(&now,NULL);
1371       
1372        if ( (!proxy.need_update) )
1373            timeout = -1;
1374        else if ((now.tv_sec > end.tv_sec) || ( (now.tv_sec == end.tv_sec) && (now.tv_usec >= end.tv_usec)) )
1375            timeout = 0;
1376        else {
1377            timeout = (end.tv_sec - now.tv_sec) * 1000;
1378           
1379            if (end.tv_usec > now.tv_usec)
1380                timeout += (end.tv_usec - now.tv_usec) / 1000;
1381            else
1382                timeout += (((1000000 + end.tv_usec) - now.tv_usec) / 1000) - 1000;
1383           
1384        }
1385       
1386        status = 0;
1387        errno = 0;
1388        if (!proxy.immediate_update) {
1389            status = poll(ufd, 3, timeout);
1390        }
1391        if ( status < 0 ) {
1392            trace("POLL ERROR: status = %d: %s", status, strerror(errno));
1393        } else if (status > 0) {
1394
1395            gettimeofday(&now,NULL);
1396            if (ufd[0].revents & POLLIN) {
1397                /* Client Stdout Connection: command input */
1398                ssize_t nRead;
1399                char ch;
1400
1401                nRead = read(ufd[0].fd, &ch, 1);
1402                if (nRead <= 0) {
1403                    trace("unexpected read error from client (stdout): %s",
1404                          strerror(errno));
1405                    if (errno != EINTR) {
1406                        trace("lost client connection: %s", strerror(errno));
1407                        break;
1408                    }
1409                } else {
1410                    Tcl_DStringAppend(&cmdbuffer, &ch, 1);
1411                   
1412                    if (ch == '\n' &&
1413                        Tcl_CommandComplete(Tcl_DStringValue(&cmdbuffer))) {
1414                        int result;
1415
1416                        result = ExecuteCommand(interp, &cmdbuffer);
1417                        if (timeout == 0) {
1418                            status = 0; // send update
1419                        }
1420                    }
1421                }
1422            } else if (ufd[0].revents & POLLHUP) {
1423                trace("received hangup on stdout from client.");
1424                break;
1425            }
1426
1427            if (ufd[1].revents & POLLIN) {
1428                ssize_t nRead;
1429                char ch;
1430
1431                /* This is supposed to suck up all the extra output from the
1432                 * pymol server output after we've matched the command
1433                 * prompt.  */
1434
1435                /* pyMol Stdout Connection: pymol (unexpected) output */
1436                nRead = read(ufd[1].fd, &ch, 1);
1437                if (nRead <= 0) {
1438                    /* It's possible to have already drained the channel in
1439                     * response to a client command handled above. Skip
1440                     * it if we're blocking. */
1441                    if ((errno == EAGAIN) || (errno == EINTR)) {
1442                        trace("try again to read (stdout) to pymol server.");
1443                        goto nextchannel;
1444                    }
1445                    trace("lost connection (stdout) from pymol server.");
1446                    break;
1447                } else {
1448                    dyBufferAppend(&dybuffer, &ch, 1);
1449                   
1450                    if (ch == '\n') {
1451                        ch = 0;
1452                        dyBufferAppend(&dybuffer, &ch, 1);
1453                        trace("STDOUT>%s",dybuffer.data);
1454                        dyBufferSetLength(&dybuffer,0);
1455                    }
1456                }
1457            } else if (ufd[1].revents & POLLHUP) {
1458                trace("received hangup on stdout to pymol server.");
1459                break;
1460            }
1461        nextchannel:
1462            if (ufd[2].revents & POLLIN) {
1463                ssize_t nRead;
1464                char ch;
1465
1466                /* pyMol Stderr Connection: pymol standard error output */
1467
1468                nRead = read(ufd[2].fd, &ch, 1);
1469                if (nRead <= 0) {
1470                    trace("unexpected read error from server (stderr): %s",
1471                          strerror(errno));
1472                    if (errno != EINTR) {
1473                        trace("lost connection (stderr) to pymol server.");
1474                        break;
1475                    }
1476                } else {
1477                    dyBufferAppend(&dybuffer2, &ch, 1);
1478                    if (ch == '\n') {
1479                        ch = 0;
1480                        dyBufferAppend(&dybuffer2, &ch, 1);
1481                        trace("stderr>%s", dybuffer2.data);
1482                        dyBufferSetLength(&dybuffer2,0);
1483                    }
1484                }
1485            } else if (ufd[2].revents & POLLHUP) {
1486                trace("received hangup on stderr from pymol server.");
1487                break;
1488            }
1489        }
1490       
1491        if (status == 0) {
1492            gettimeofday(&now,NULL);
1493           
1494            if (proxy.need_update && proxy.can_update)
1495                Tcl_Eval(interp, "bmp -\n");
1496           
1497            end.tv_sec = now.tv_sec;
1498            end.tv_usec = now.tv_usec + 150000;
1499           
1500            if (end.tv_usec >= 1000000) {
1501                end.tv_sec++;
1502                end.tv_usec -= 1000000;
1503            }
1504           
1505        }
1506       
1507    }
1508   
1509    status = waitpid(pid, &result, WNOHANG);
1510    if (status == -1) {
1511        trace("error waiting on pymol server to exit: %s", strerror(errno));
1512    } else if (status == 0) {
1513        trace("attempting to signal (SIGTERM) pymol server.");
1514        kill(-pid, SIGTERM); // kill process group
1515        alarm(5);
1516        status = waitpid(pid, &result, 0);
1517        alarm(0);
1518       
1519        while ((status == -1) && (errno == EINTR)) {
1520            trace("Attempting to signal (SIGKILL) pymol server.");
1521            kill(-pid, SIGKILL); // kill process group
1522            alarm(10);
1523            status = waitpid(pid, &result, 0);
1524            alarm(0);
1525        }
1526    }
1527   
1528    trace("pymol server process ended (result=%d)", result);
1529   
1530    dyBufferFree(&proxy.image);
1531   
1532    Tcl_DeleteInterp(interp);
1533    if (WIFEXITED(result)) {
1534        result = WEXITSTATUS(result);
1535    }
1536    DoExit(result);
1537    return 0;
1538}
1539
1540#ifdef STANDALONE
1541
1542int
1543main(int argc, char *argv[])
1544{
1545    flog = stderr;
1546    if (debug) {
1547        char fileName[200];
1548        sprintf(fileName, "/tmp/pymolproxy%d.log", getpid());
1549        flog = fopen(fileName, "w");
1550    }   
1551    ProxyInit(fileno(stdout), fileno(stdin), argv + 1);
1552    return 0;
1553}
1554
1555#endif
1556
Note: See TracBrowser for help on using the repository browser.