Changeset 5963 for pymolproxy
- Timestamp:
- Jan 20, 2016, 7:48:58 AM (9 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
pymolproxy/trunk/pymolproxy.c
r4627 r5963 1 1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 2 /* 3 3 * ---------------------------------------------------------------------- 4 * proxypymol 2.c4 * proxypymol.c 5 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 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 13 * 14 14 * Copyright (c) 2004-2012 HUBzero Foundation, LLC … … 20 20 21 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. 22 * Notes: 23 * 24 * The proxy should not maintain any state information from the 25 * client, other that what it needs for event (rotate, pan, zoom, 26 * atom scale, bond thickness, etc.) compression. This is because 27 * the connection is periodically broken (timeout, error, etc.). 28 * It's the responsibility of the client (molvisviewer) to restore 29 * the settings of the previous view. The proxy is just a relay 30 * between the client and the pymol server. 29 31 */ 30 32 /* 31 33 * +--------------+ +------------+ +----------+ 32 * | | (stdin) | | (stdin) | | 34 * | | (stdin) | | (stdin) | | 33 35 * | |--------->| |--------->| | 34 36 * | molvisviewer | | pymolproxy | | pymol | 35 37 * | (client) | | | | (server) |(stderr) 36 38 * | | (stdout) | | (stdout) | |-------> file 37 * | |<---------| |<---------| | 39 * | |<---------| |<---------| | 38 40 * +--------------+ +------------+ +----------+ 39 * 41 * 40 42 * We're using a simple 2 thread setup: one for read client requests and 41 43 * relaying them to the pymol server, another reading pymol server output, … … 46 48 * 47 49 * Reader thread: 48 * The communication between the pymol server and proxy is asynchronous. 50 * The communication between the pymol server and proxy is asynchronous. 49 51 * The proxy translates commands from the client and sends them to the 50 52 * server without waiting for a response. It watches the server's 51 * stdout in a separate thread snooping for image data. 53 * stdout in a separate thread snooping for image data. 52 54 * 53 55 * 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 communication between the client and the proxy is also asynchronous. 57 * The client commands are read when they become available on the socket. 56 58 * The proxy writes images to the client when it can, otherwise it stores 57 59 * them in a list in memory. This should prevent deadlocks from occuring: … … 98 100 static int statsFile = -1; 99 101 100 #define WANT_DEBUG 101 #define READ_DEBUG 102 #define WRITE_DEBUG 103 #define EXEC_DEBUG 102 #define WANT_DEBUG 0 103 #define READ_DEBUG 0 104 #define WRITE_DEBUG 0 105 #define EXEC_DEBUG 0 104 106 105 107 #define FORCE_UPDATE (1<<0) … … 115 117 116 118 #define IO_TIMEOUT (30000) 117 #define CLIENT_READ 118 #define CLIENT_WRITE 119 #define CLIENT_READ STDIN_FILENO 120 #define CLIENT_WRITE STDOUT_FILENO 119 121 120 122 #ifndef LOGDIR 121 #define LOGDIR 122 #endif 123 #define LOGDIR "/tmp" 124 #endif /* LOGDIR */ 123 125 124 126 #define CVT2SECS(x) ((double)(x).tv_sec) + ((double)(x).tv_usec * 1.0e-6) … … 155 157 } Image; 156 158 157 #define BUFFER_SIZE 159 #define BUFFER_SIZE 4096 158 160 159 161 typedef struct { … … 174 176 } ReadBuffer; 175 177 176 #define BUFFER_OK 177 #define BUFFER_ERROR 178 #define BUFFER_CONTINUE 179 #define BUFFER_EOF 178 #define BUFFER_OK 0 179 #define BUFFER_ERROR -1 180 #define BUFFER_CONTINUE -2 181 #define BUFFER_EOF -3 180 182 181 183 typedef struct { … … 203 205 204 206 #if WANT_DEBUG 205 #define DEBUG(...) 206 #else 207 #define DEBUG(...) 208 #endif 209 210 #define ERROR(...) 211 #define TRACE(...) 212 #define WARN(...) 213 #define INFO(...) 207 #define DEBUG(...) if (debug) PrintToLog(__VA_ARGS__) 208 #else 209 #define DEBUG(...) 210 #endif 211 212 #define ERROR(...) SysLog(LOG_ERR, __FILE__, __LINE__, __VA_ARGS__) 213 #define TRACE(...) SysLog(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) 214 #define WARN(...) SysLog(LOG_WARNING, __FILE__, __LINE__, __VA_ARGS__) 215 #define INFO(...) SysLog(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) 214 216 215 217 static const char *syslogLevels[] = { 216 "emergency", 217 "alert", 218 "critical", 219 "error", 220 "warning", 221 "notice", 222 "info", 223 "debug", 218 "emergency", /* System is unusable */ 219 "alert", /* Action must be taken immediately */ 220 "critical", /* Critical conditions */ 221 "error", /* Error conditions */ 222 "warning", /* Warning conditions */ 223 "notice", /* Normal but significant condition */ 224 "info", /* Informational */ 225 "debug", /* Debug-level messages */ 224 226 }; 225 227 … … 230 232 const char *format; 231 233 va_list args; 232 234 233 235 format = TCL_VARARGS_START(const char *, arg1, args); 234 236 fprintf(stderr, "pymolproxy: "); … … 255 257 s++; 256 258 } 257 length = snprintf(message, MSG_LEN, "pymolproxy (%d) %s: %s:%d ", 258 259 length = snprintf(message, MSG_LEN, "pymolproxy (%d) %s: %s:%d ", 260 getpid(), syslogLevels[priority], s, lineNum); 259 261 length += vsnprintf(message + length, MSG_LEN - length, fmt, lst); 260 262 message[MSG_LEN] = '\0'; 261 263 if (debug) { 262 264 DEBUG("%s\n", message); 263 265 } else { 264 266 syslog(priority, message, length); 265 267 } 266 268 } … … 271 273 const char *format; 272 274 va_list args; 273 275 274 276 format = TCL_VARARGS_START(const char *, arg1, args); 275 277 vfprintf(frecord, format, args); … … 289 291 return TCL_ERROR; 290 292 } 291 293 292 294 va_start(ap, format); 293 295 result = vsnprintf(buffer, BUFSIZ-1, format, ap); 294 296 va_end(ap); 295 297 296 298 #ifdef EXEC_DEBUG 297 299 DEBUG("to-pymol>(%s) code=%d", buffer, result); 298 300 #endif 299 301 if (recording) { 300 301 } 302 302 Record("%s\n", buffer); 303 } 304 303 305 /* Write the command out to the server. */ 304 306 length = strlen(buffer); 305 307 numWritten = write(proxyPtr->sin, buffer, length); 306 308 if (numWritten != length) { 307 308 309 ERROR("short write to pymol (wrote=%d, should have been %d): %s", 310 numWritten, length, strerror(errno)); 309 311 } 310 312 proxyPtr->status = result; … … 355 357 356 358 if (bp->newline > 0) { 357 359 return bp->newline; 358 360 } else { 359 360 361 362 p = (unsigned char *)memchr(bp->bytes + bp->mark, '\n', 363 364 365 366 367 368 369 370 371 } 372 } 373 374 /** 361 unsigned char *p; 362 size_t newline; 363 364 p = (unsigned char *)memchr(bp->bytes + bp->mark, '\n', 365 bp->fill - bp->mark); 366 if (p == NULL) { 367 newline = 0; 368 } else { 369 newline = (p - bp->bytes + 1); 370 } 371 bp->newline = newline; 372 return newline; 373 } 374 } 375 376 /** 375 377 * \brief Fills the buffer with available data. 376 * 377 * Any existing data in the buffer is moved to the front of the buffer, 378 * 379 * Any existing data in the buffer is moved to the front of the buffer, 378 380 * then the channel is read to fill the rest of the buffer. 379 381 * 380 382 * \return If an error occur when reading the channel, then ERROR is 381 * returned. ENDFILE is returned on EOF. If the buffer can't be filled, 383 * returned. ENDFILE is returned on EOF. If the buffer can't be filled, 382 384 * then CONTINUE is returned. 383 385 */ 384 static int 386 static int 385 387 FillReadBuffer(ReadBuffer *bp) 386 388 { … … 389 391 390 392 #if READ_DEBUG 391 DEBUG("Enter FillReadBuffer for %s: mark=%lu fill=%lu", 392 393 DEBUG("Enter FillReadBuffer for %s: mark=%lu fill=%lu", 394 bp->ident, bp->mark, bp->fill); 393 395 #endif 394 396 if (bp->mark >= bp->fill) { 395 FlushReadBuffer(bp); 397 FlushReadBuffer(bp); /* Fully consumed buffer */ 396 398 } 397 399 if (bp->mark > 0) { … … 401 403 DEBUG("memmove %lu bytes", bp->fill - bp->mark); 402 404 #endif 403 memmove(bp->bytes, bp->bytes + bp->mark, 404 405 memmove(bp->bytes, bp->bytes + bp->mark, 406 bp->fill - bp->mark); 405 407 bp->fill -= bp->mark; 406 408 bp->mark = 0; … … 415 417 /* EOF */ 416 418 #if READ_DEBUG 417 DEBUG("EOF found reading %s buffer (fill=%d): ", 418 419 DEBUG("EOF found reading %s buffer (fill=%d): ", 420 bp->ident, bp->fill); 419 421 #endif 420 422 return BUFFER_EOF; … … 440 442 * \brief Read the requested number of bytes from the buffer. 441 443 442 * Fails if the requested number of bytes are not immediately 443 * available. Never should be short. 444 * Fails if the requested number of bytes are not immediately 445 * available. Never should be short. 444 446 */ 445 447 static int … … 469 471 /* Didn't get enough bytes, need to read some more. */ 470 472 bp->lastStatus = FillReadBuffer(bp); 471 if ((bp->lastStatus == BUFFER_ERROR) || 472 473 if ((bp->lastStatus == BUFFER_ERROR) || 474 (bp->lastStatus == BUFFER_EOF)) { 473 475 return bp->lastStatus; 474 476 } … … 477 479 } 478 480 479 /** 481 /** 480 482 * \brief Returns the next available line (terminated by a newline) 481 * 483 * 482 484 * If insufficient data is in the buffer, then the channel is 483 485 * read for more data. If reading the channel results in a … … 504 506 *numBytesPtr = newline - bp->mark; 505 507 bp->mark = newline; 506 508 bp->newline = 0; 507 509 return BUFFER_OK; 508 510 } … … 537 539 538 540 if (IsLineAvailable(bp)) { 539 541 return 1; 540 542 } 541 543 FD_ZERO(&readFds); … … 553 555 554 556 #ifndef STATSDIR 555 #define STATSDIR 557 #define STATSDIR "/var/tmp/visservers" 556 558 #endif /*STATSDIR*/ 557 559 … … 569 571 570 572 if ((string == NULL) || (statsFile >= 0)) { 571 573 return statsFile; 572 574 } 573 575 /* By itself the client's key/value pairs aren't unique. Add in the … … 597 599 Tcl_DStringFree(&ds); 598 600 if (statsFile < 0) { 599 600 601 ERROR("can't open \"%s\": %s", fileName, strerror(errno)); 602 return -1; 601 603 } 602 604 return statsFile; … … 608 610 609 611 if (f >= 0) { 610 612 ssize_t numWritten; 611 613 612 614 numWritten = write(f, s, length); … … 619 621 620 622 static int 621 ServerStats(int code) 623 ServerStats(int code) 622 624 { 623 625 double start, finish; … … 628 630 629 631 { 630 631 632 633 634 635 636 637 } 638 /* 632 struct timeval tv; 633 634 /* Get ending time. */ 635 gettimeofday(&tv, NULL); 636 finish = CVT2SECS(tv); 637 tv = stats.start; 638 start = CVT2SECS(tv); 639 } 640 /* 639 641 * Session information: 640 642 * - Name of render server … … 649 651 * - Total elapsed time of session 650 652 * - Exit code of vizserver 651 * - User time 653 * - User time 652 654 * - System time 653 * - User time of children 654 * - System time of children 655 */ 655 * - User time of children 656 * - System time of children 657 */ 656 658 657 659 Tcl_DStringInit(&ds); 658 660 659 661 Tcl_DStringAppendElement(&ds, "render_stop"); 660 662 /* renderer */ … … 704 706 Tcl_DStringAppendElement(&ds, buf); 705 707 { 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 708 long clocksPerSec = sysconf(_SC_CLK_TCK); 709 double clockRes = 1.0 / clocksPerSec; 710 struct tms tms; 711 712 memset(&tms, 0, sizeof(tms)); 713 times(&tms); 714 /* utime */ 715 Tcl_DStringAppendElement(&ds, "utime"); 716 sprintf(buf, "%g", tms.tms_utime * clockRes); 717 Tcl_DStringAppendElement(&ds, buf); 718 /* stime */ 719 Tcl_DStringAppendElement(&ds, "stime"); 720 sprintf(buf, "%g", tms.tms_stime * clockRes); 721 Tcl_DStringAppendElement(&ds, buf); 722 /* cutime */ 723 Tcl_DStringAppendElement(&ds, "cutime"); 724 sprintf(buf, "%g", tms.tms_cutime * clockRes); 725 Tcl_DStringAppendElement(&ds, buf); 726 /* cstime */ 727 Tcl_DStringAppendElement(&ds, "cstime"); 728 sprintf(buf, "%g", tms.tms_cstime * clockRes); 729 Tcl_DStringAppendElement(&ds, buf); 728 730 } 729 731 Tcl_DStringAppend(&ds, "\n", -1); … … 737 739 738 740 static int 739 CartoonCmd(ClientData clientData, Tcl_Interp *interp, int argc, 740 741 CartoonCmd(ClientData clientData, Tcl_Interp *interp, int argc, 742 const char *argv[]) 741 743 { 742 744 PymolProxy *p = clientData; … … 751 753 if (strcmp(argv[i],"-defer") == 0) { 752 754 defer = TRUE; 753 755 } else if (strcmp(argv[i],"-push") == 0) { 754 756 push = TRUE; 755 757 } else if (strcmp(argv[i],"-model") == 0) { 756 758 if (++i < argc) { 757 759 model = argv[i]; 758 759 } else { 760 761 762 763 764 } 765 p->flags |= INVALIDATE_CACHE; 760 } 761 } else { 762 if (Tcl_GetBoolean(interp, argv[i], &bool) != TCL_OK) { 763 return TCL_ERROR; 764 } 765 } 766 } 767 p->flags |= INVALIDATE_CACHE; /* cartoon */ 766 768 if (!defer || push) { 767 769 p->flags |= UPDATE_PENDING; 768 770 } 769 771 if (push) { 770 772 p->flags |= FORCE_UPDATE; 771 773 } 772 774 if (bool) { 773 775 SendToPymol(p, "show cartoon,%s\n", model); 774 776 } else { 775 777 SendToPymol(p, "hide cartoon,%s\n", model); 776 778 } 777 779 return p->status; … … 779 781 780 782 static int 781 CartoonTraceCmd(ClientData clientData, Tcl_Interp *interp, int argc, 782 783 CartoonTraceCmd(ClientData clientData, Tcl_Interp *interp, int argc, 784 const char *argv[]) 783 785 { 784 786 PymolProxy *p = clientData; … … 792 794 if (strcmp(argv[i],"-defer") == 0) { 793 795 defer = TRUE; 794 796 } else if (strcmp(argv[i],"-push") == 0) { 795 797 push = TRUE; 796 798 } else if (strcmp(argv[i],"-model") == 0) { 797 799 if (++i < argc) { 798 800 model = argv[i]; 799 800 } else { 801 802 803 804 801 } 802 } else { 803 if (Tcl_GetBoolean(interp, argv[i], &bool) != TCL_OK) { 804 return TCL_ERROR; 805 } 806 } 805 807 } 806 808 p->flags |= INVALIDATE_CACHE; /* cartoon_trace */ 807 809 if (!defer || push) { 808 810 p->flags |= UPDATE_PENDING; 809 811 } 810 812 if (push) { 811 813 p->flags |= FORCE_UPDATE; 812 814 } 813 815 SendToPymol(p, "set cartoon_trace,%d,%s\n", bool, model); … … 816 818 817 819 static int 818 DisableCmd(ClientData clientData, Tcl_Interp *interp, int argc, 819 820 DisableCmd(ClientData clientData, Tcl_Interp *interp, int argc, 821 const char *argv[]) 820 822 { 821 823 PymolProxy *p = clientData; … … 834 836 } 835 837 836 p->flags |= INVALIDATE_CACHE; 838 p->flags |= INVALIDATE_CACHE; /* disable */ 837 839 if (!defer || push) { 838 840 p->flags |= UPDATE_PENDING; 839 841 } 840 842 if (push) { 841 843 p->flags |= FORCE_UPDATE; 842 844 } 843 845 SendToPymol(p, "disable %s\n", model); … … 847 849 848 850 static int 849 EnableCmd(ClientData clientData, Tcl_Interp *interp, int argc, 850 851 EnableCmd(ClientData clientData, Tcl_Interp *interp, int argc, 852 const char *argv[]) 851 853 { 852 854 PymolProxy *p = clientData; … … 860 862 if (strcmp(argv[i],"-defer") == 0) { 861 863 defer = TRUE; 862 864 } else if (strcmp(argv[i], "-push") == 0) { 863 865 push = TRUE; 864 866 } else { 865 867 model = argv[i]; 866 868 } 867 869 } 868 870 p->flags |= INVALIDATE_CACHE; /* enable */ 869 871 if (!defer || push) { 870 872 p->flags |= UPDATE_PENDING; 871 873 } 872 874 if (push) { 873 875 p->flags |= FORCE_UPDATE; 874 876 } 875 877 SendToPymol(p, "enable %s\n", model); … … 878 880 879 881 static int 880 FrameCmd(ClientData clientData, Tcl_Interp *interp, int argc, 881 882 FrameCmd(ClientData clientData, Tcl_Interp *interp, int argc, 883 const char *argv[]) 882 884 { 883 885 PymolProxy *p = clientData; … … 887 889 frame = 0; 888 890 push = defer = FALSE; 889 for(i = 1; i < argc; i++) { 891 for(i = 1; i < argc; i++) { 890 892 if (strcmp(argv[i],"-defer") == 0) { 891 893 defer = TRUE; 892 894 } else if (strcmp(argv[i],"-push") == 0) { 893 895 push = TRUE; 894 896 } else { 895 897 frame = atoi(argv[i]); 896 898 } 897 899 } 898 900 if (!defer || push) { 899 901 p->flags |= UPDATE_PENDING; 900 902 } 901 903 if (push) { 902 904 p->flags |= FORCE_UPDATE; 903 905 } 904 906 p->frame = frame; … … 910 912 } 911 913 912 /* 914 /* 913 915 * ClientInfoCmd -- 914 916 * 915 * 916 * clientinfo path list 917 * clientinfo path list 917 918 */ 918 919 static int 919 ClientInfoCmd(ClientData clientData, Tcl_Interp *interp, int argc, 920 920 ClientInfoCmd(ClientData clientData, Tcl_Interp *interp, int argc, 921 const char *argv[]) 921 922 { 922 923 Tcl_DString ds; … … 929 930 930 931 if (argc != 2) { 931 932 933 932 Tcl_AppendResult(interp, "wrong # of arguments: should be \"", argv[0], 933 " list\"", (char *)NULL); 934 return TCL_ERROR; 934 935 } 935 936 /* Use the initial client key value pairs as the parts for a generating … … 937 938 f = GetStatsFile(argv[1]); 938 939 if (f < 0) { 939 Tcl_AppendResult(interp, "can't open stats file: ", 940 Tcl_AppendResult(interp, "can't open stats file: ", 940 941 Tcl_PosixError(interp), (char *)NULL); 941 942 return TCL_ERROR; 942 943 } 943 944 Tcl_DStringInit(&ds); … … 947 948 /* renderer */ 948 949 Tcl_DStringAppendElement(&ds, "renderer"); 949 950 Tcl_DStringAppendElement(&ds, "pymol"); 950 951 /* pid */ 951 952 953 952 Tcl_DStringAppendElement(&ds, "pid"); 953 sprintf(buf, "%d", getpid()); 954 Tcl_DStringAppendElement(&ds, buf); 954 955 /* host */ 955 956 Tcl_DStringAppendElement(&ds, "host"); 956 957 958 957 gethostname(buf, BUFSIZ-1); 958 buf[BUFSIZ-1] = '\0'; 959 Tcl_DStringAppendElement(&ds, buf); 959 960 } else { 960 961 Tcl_DStringAppendElement(&ds, "render_info"); … … 972 973 /* Client arguments. */ 973 974 if (Tcl_SplitList(interp, argv[1], &numElems, &elems) != TCL_OK) { 974 975 return TCL_ERROR; 975 976 } 976 977 for (i = 0; i < numElems; i++) { 977 978 Tcl_DStringAppendElement(&ds, elems[i]); 978 979 } 979 980 free(elems); … … 985 986 986 987 static int 987 LabelCmd(ClientData clientData, Tcl_Interp *interp, int argc, 988 988 LabelCmd(ClientData clientData, Tcl_Interp *interp, int argc, 989 const char *argv[]) 989 990 { 990 991 PymolProxy *p = clientData; … … 1000 1001 if (strcmp(argv[i],"-defer") == 0) { 1001 1002 defer = TRUE; 1002 1003 } else if (strcmp(argv[i],"-push") == 0) { 1003 1004 push = TRUE; 1004 1005 } else if (strcmp(argv[i],"-model") == 0) { 1005 1006 if (++i < argc) { 1006 1007 model = argv[i]; 1007 1008 1008 } 1009 } else if (strcmp(argv[i],"-size") == 0) { 1009 1010 if (++i < argc) { 1010 1011 size = atoi(argv[i]); 1011 1012 1013 1014 1015 } 1016 p->flags |= INVALIDATE_CACHE; 1012 } 1013 } else if (Tcl_GetBoolean(interp, argv[i], &bool) != TCL_OK) { 1014 return TCL_ERROR; 1015 } 1016 } 1017 p->flags |= INVALIDATE_CACHE; /* label */ 1017 1018 if (!defer || push) { 1018 1019 p->flags |= UPDATE_PENDING; 1019 1020 } 1020 1021 if (push) { 1021 1022 } 1023 SendToPymol(p, "set label_color,white,%s\nset label_size,%d,%s\n", 1024 1022 p->flags |= FORCE_UPDATE; 1023 } 1024 SendToPymol(p, "set label_color,white,%s\nset label_size,%d,%s\n", 1025 model, size, model); 1025 1026 if (bool) { 1026 1027 SendToPymol(p, "label %s,\"%%s%%s\" %% (ID,name)\n", model); … … 1031 1032 } 1032 1033 1033 /* 1034 /* 1034 1035 * LoadPDBCmd -- 1035 1036 * 1036 * Load a PDB into pymol. We write the pdb data into a temporary file 1037 * and then let pymol read it and delete it. There is no good way to 1038 * load PDB data into pymol without using a file. The specially created 1039 * routine "loadandremovepdbfile" in pymol will remove the file after 1040 * loading it. 1041 * 1037 * Load a PDB into pymol. We write the pdb data into a temporary file 1038 * and then let pymol read it and delete it. There is no good way to 1039 * load PDB data into pymol without using a file. The specially created 1040 * routine "loadandremovepdbfile" in pymol will remove the file after 1041 * loading it. 1042 1042 */ 1043 1043 static int 1044 LoadPDBCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1045 1044 LoadPDBCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1045 const char *argv[]) 1046 1046 { 1047 1047 PymolProxy *p = clientData; … … 1055 1055 1056 1056 if (p == NULL){ 1057 1057 return TCL_ERROR; 1058 1058 } 1059 1059 clear_error(p); 1060 1060 defer = push = FALSE; 1061 1061 for(i = j = 1; i < argc; i++) { 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1062 if (strcmp(argv[i],"-defer") == 0) { 1063 defer = TRUE; 1064 } else if (strcmp(argv[i],"-push") == 0) { 1065 push = TRUE; 1066 } else { 1067 if (j < i) { 1068 argv[j] = argv[i]; 1069 } 1070 j++; 1071 } 1072 1072 } 1073 1073 argc = j; 1074 1074 if (argc < 4) { 1075 1076 1077 1078 1075 Tcl_AppendResult(interp, "wrong # arguments: should be \"", argv[0], 1076 " <data>|follows <model> <state> ?<numBytes>?\"", 1077 (char *)NULL); 1078 return TCL_ERROR; 1079 1079 } 1080 1080 string = argv[1]; 1081 1081 name = argv[2]; 1082 1082 if (Tcl_GetInt(interp, argv[3], &state) != TCL_OK) { 1083 1083 return TCL_ERROR; 1084 1084 } 1085 1085 numBytes = 0; 1086 1086 status = BUFFER_ERROR; 1087 1087 if (strcmp(string, "follows") == 0) { 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1088 int n; 1089 1090 if (argc != 5) { 1091 Tcl_AppendResult(interp, "wrong # arguments: should be \"", argv[0], 1092 " follows <model> <state> <numBytes>\"", (char *)NULL); 1093 return TCL_ERROR; 1094 } 1095 if (Tcl_GetInt(interp, argv[4], &n) != TCL_OK) { 1096 return TCL_ERROR; 1097 } 1098 if (n < 0) { 1099 Tcl_AppendResult(interp, "bad value for # bytes \"", argv[4], 1100 "\"", (char *)NULL); 1101 return TCL_ERROR; 1102 } 1103 numBytes = (size_t)n; 1104 1104 } 1105 1105 if (!defer || push) { 1106 1106 p->flags |= UPDATE_PENDING; 1107 1107 } 1108 1108 if (push) { 1109 1109 p->flags |= FORCE_UPDATE; 1110 1110 } 1111 1111 p->cacheId = state; … … 1116 1116 allocated = malloc(sizeof(char) * numBytes); 1117 1117 if (allocated == NULL) { 1118 1119 1120 1118 Tcl_AppendResult(interp, "can't allocate buffer for pdbdata.", 1119 (char *)NULL); 1120 return TCL_ERROR; 1121 1121 } 1122 1122 status = ReadFollowingData(&p->client, allocated, numBytes); 1123 1123 if (status != BUFFER_OK) { 1124 1125 1126 1127 1124 Tcl_AppendResult(interp, "can't read pdbdata from client.", 1125 (char *)NULL); 1126 free(allocated); 1127 return TCL_ERROR; 1128 1128 } 1129 1129 string = (const char *)allocated; 1130 1130 { 1131 1132 1133 1134 1135 1136 1137 1138 1139 Tcl_AppendResult(interp, "can't create temporary file \"", 1140 1141 1142 } 1143 1144 1145 1146 1147 1148 1149 1150 1151 SendToPymol(p, "loadandremovepdbfile %s,%s,%d\n", fileName, name, 1152 1153 1131 int f; 1132 ssize_t numWritten; 1133 char fileName[200]; 1134 1135 strcpy(fileName, "/tmp/pdb.XXXXXX"); 1136 p->status = TCL_ERROR; 1137 f = mkstemp(fileName); 1138 if (f < 0) { 1139 Tcl_AppendResult(interp, "can't create temporary file \"", 1140 fileName, "\":", Tcl_PosixError(interp), (char *)NULL); 1141 goto error; 1142 } 1143 numWritten = write(f, string, numBytes); 1144 if (numBytes != numWritten) { 1145 Tcl_AppendResult(interp, "can't write PDB data to \"", 1146 fileName, "\": ", Tcl_PosixError(interp), (char *)NULL); 1147 close(f); 1148 goto error; 1149 } 1150 close(f); 1151 SendToPymol(p, "loadandremovepdbfile %s,%s,%d\n", fileName, name, 1152 state); 1153 p->status = TCL_OK; 1154 1154 } 1155 1155 error: 1156 1156 if (allocated != NULL) { 1157 1157 free(allocated); 1158 1158 } 1159 1159 return p->status; … … 1161 1161 1162 1162 static int 1163 OrthoscopicCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1164 1163 OrthoscopicCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1164 const char *argv[]) 1165 1165 { 1166 1166 PymolProxy *p = clientData; … … 1173 1173 if (strcmp(argv[i],"-defer") == 0) { 1174 1174 defer = TRUE; 1175 1175 } else if (strcmp(argv[i],"-push") == 0) { 1176 1176 push = TRUE; 1177 } else { 1178 1179 1180 1181 1177 } else { 1178 if (Tcl_GetBoolean(interp, argv[i], &bool) != TCL_OK) { 1179 return TCL_ERROR; 1180 } 1181 } 1182 1182 } 1183 1183 p->flags |= INVALIDATE_CACHE; /* orthoscopic */ 1184 1184 if (!defer || push) { 1185 1185 p->flags |= UPDATE_PENDING; 1186 1186 } 1187 1187 if (push) { 1188 1188 p->flags |= FORCE_UPDATE; 1189 1189 } 1190 1190 SendToPymol(p, "set orthoscopic=%d\n", bool); … … 1192 1192 } 1193 1193 1194 /* 1194 /* 1195 1195 * PanCmd -- 1196 1196 * 1197 * 1198 * 1199 * 1200 * 1201 * 1197 * Issue "move" commands for changes in the x and y coordinates of the 1198 * camera. The problem here is that there is no event compression. 1199 * Consecutive "pan" commands are not compressed into a single 1200 * directive. The means that the pymol server will render scenes that 1201 * are not used by the client. 1202 1202 * 1203 * 1204 * 1205 * 1203 * Need to 1) defer the "move" commands until we find the next command 1204 * isn't a "pan". 2) Track the x and y coordinates as they are 1205 * compressed. 1206 1206 */ 1207 1207 static int … … 1216 1216 defer = push = FALSE; 1217 1217 for (i = 1; i < argc; i++) { 1218 1219 1220 1221 1222 } else { 1223 1224 1218 if (strcmp(argv[i],"-defer") == 0) { 1219 defer = 1; 1220 } else if (strcmp(argv[i],"-push") == 0) { 1221 push = 1; 1222 } else { 1223 break; 1224 } 1225 1225 } 1226 1226 if ((Tcl_GetDouble(interp, argv[i], &x) != TCL_OK) || 1227 1228 1229 } 1230 p->flags |= INVALIDATE_CACHE; 1227 (Tcl_GetDouble(interp, argv[i+1], &y) != TCL_OK)) { 1228 return TCL_ERROR; 1229 } 1230 p->flags |= INVALIDATE_CACHE; /* pan */ 1231 1231 if (!defer || push) { 1232 1232 p->flags |= UPDATE_PENDING; 1233 1233 } 1234 1234 if (push) { 1235 1235 p->flags |= FORCE_UPDATE; 1236 1236 } 1237 1237 if ((x != 0.0f) || (y != 0.0f)) { 1238 1239 1240 1238 p->xPan = x * 0.05; 1239 p->yPan = -y * 0.05; 1240 p->flags |= PAN_PENDING; 1241 1241 } 1242 1242 return p->status; … … 1280 1280 * The extra information is contained in the token we get from the 1281 1281 * molvisviewer client, the frame number, and rock offset. */ 1282 SendToPymol(p, "png -:%d:%d:%d,format=1\n", p->cacheId, p->frame, 1283 1282 SendToPymol(p, "png -:%d:%d:%d,format=1\n", p->cacheId, p->frame, 1283 p->rockOffset); 1284 1284 p->flags &= ~(UPDATE_PENDING|FORCE_UPDATE); 1285 1285 return p->status; … … 1288 1288 1289 1289 static int 1290 PrintCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1291 1290 PrintCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1291 const char *argv[]) 1292 1292 { 1293 1293 PymolProxy *p = clientData; … … 1298 1298 1299 1299 if (argc != 5) { 1300 Tcl_AppendResult(interp, "wrong # arguments: should be \"", 1301 1302 1300 Tcl_AppendResult(interp, "wrong # arguments: should be \"", 1301 argv[0], " token width height color\"", (char *)NULL); 1302 return TCL_ERROR; 1303 1303 } 1304 1304 token = argv[1]; 1305 1305 if (Tcl_GetInt(interp, argv[2], &width) != TCL_OK) { 1306 1306 return TCL_ERROR; 1307 1307 } 1308 1308 if (Tcl_GetInt(interp, argv[3], &height) != TCL_OK) { 1309 1309 return TCL_ERROR; 1310 1310 } 1311 1311 bgcolor = argv[4]; 1312 1312 /* Force pymol to update the current scene. */ 1313 1313 if (strcmp(bgcolor, "none") == 0) { 1314 1315 1314 SendToPymol(p, "set ray_opaque_background,off\n"); 1315 SendToPymol(p, "refresh\n", bgcolor); 1316 1316 } else { 1317 1318 1317 SendToPymol(p, "set ray_opaque_background,on\n"); 1318 SendToPymol(p, "bg_color %s\nrefresh\n", bgcolor); 1319 1319 } 1320 1320 /* This is a hack. We're encoding the filename to pass extra information 1321 1321 * to the MyPNGWrite routine inside of pymol. Ideally these would be 1322 1322 * parameters of a new "molvispng" command that would be passed all the 1323 * way through to MyPNGWrite. 1323 * way through to MyPNGWrite. 1324 1324 * 1325 1325 * The extra information is contained in the token we get from the 1326 1326 * molvisviewer client, the frame number, and rock offset. 1327 1327 */ 1328 SendToPymol(p, "png -:%s:0:0,width=%d,height=%d,ray=1,dpi=300\n", 1329 1328 SendToPymol(p, "png -:%s:0:0,width=%d,height=%d,ray=1,dpi=300\n", 1329 token, width, height); 1330 1330 SendToPymol(p, "bg_color black\n"); 1331 1331 return p->status; … … 1354 1354 p->flags |= INVALIDATE_CACHE; /* raw */ 1355 1355 if (!defer || push) { 1356 1356 p->flags |= UPDATE_PENDING; 1357 1357 } 1358 1358 if (push) { 1359 1359 p->flags |= FORCE_UPDATE; 1360 1360 } 1361 1361 SendToPymol(p,"%s\n", cmd); … … 1364 1364 1365 1365 static int 1366 ResetCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1367 1366 ResetCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1367 const char *argv[]) 1368 1368 { 1369 1369 PymolProxy *p = clientData; … … 1378 1378 push = 1; 1379 1379 } 1380 1380 1381 1381 p->flags |= INVALIDATE_CACHE; /* reset */ 1382 1382 if (!defer || push) { 1383 1383 p->flags |= UPDATE_PENDING; 1384 1384 } 1385 1385 if (push) { 1386 1386 p->flags |= FORCE_UPDATE; 1387 1387 } 1388 1388 SendToPymol(p, "reset\nzoom complete=1\n"); … … 1391 1391 1392 1392 static int 1393 RockCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1394 1393 RockCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1394 const char *argv[]) 1395 1395 { 1396 1396 PymolProxy *p = clientData; … … 1409 1409 y = atof( argv[arg] ); 1410 1410 } 1411 1411 1412 1412 /* Does not invalidate cache. */ 1413 1413 1414 1414 if (!defer || push) { 1415 1415 p->flags |= UPDATE_PENDING; 1416 1416 } 1417 1417 if (push) { 1418 1418 p->flags |= FORCE_UPDATE; 1419 1419 } 1420 1420 SendToPymol(p,"turn y, %f\n", y - p->rockOffset); … … 1424 1424 1425 1425 static int 1426 RepresentationCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1427 1426 RepresentationCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1427 const char *argv[]) 1428 1428 { 1429 1429 PymolProxy *p = clientData; … … 1439 1439 if (strcmp(argv[i],"-defer") == 0 ) { 1440 1440 defer = TRUE; 1441 1441 } else if (strcmp(argv[i],"-push") == 0) { 1442 1442 push = TRUE; 1443 1443 } else if (strcmp(argv[i],"-model") == 0) { 1444 1444 if (++i < argc) { 1445 1445 model = argv[i]; 1446 1446 } 1447 1447 } else { 1448 1448 rep = argv[i]; 1449 1449 } 1450 1450 } 1451 1451 if (rep == NULL) { 1452 1453 1454 1452 Tcl_AppendResult(interp, "missing representation argument", 1453 (char *)NULL); 1454 return TCL_ERROR; 1455 1455 } 1456 1456 1457 1457 p->flags |= INVALIDATE_CACHE; /* representation */ 1458 1458 if (!defer || push) { 1459 1459 p->flags |= UPDATE_PENDING; 1460 1460 } 1461 1461 if (push) { 1462 1462 p->flags |= FORCE_UPDATE; 1463 1463 } 1464 1464 if (strcmp(rep, "ballnstick") == 0) { /* Ball 'n Stick */ 1465 SendToPymol(p, 1466 1467 1468 1469 1470 1471 1472 } else if (strcmp(rep, "spheres") == 0) { /* spheres */ 1473 SendToPymol(p, 1474 1475 1476 1477 1478 1479 "set ambient,.2,%s\n", 1480 1481 } else if (strcmp(rep, "none") == 0) { /* nothing */ 1482 SendToPymol(p, 1483 "hide sticks,%s\n", 1484 1485 1486 1487 1488 } else if (strcmp(rep, "sticks") == 0) { /* sticks */ 1489 SendToPymol(p, 1490 1491 1492 1493 1494 1495 1496 } else if (strcmp(rep, "lines") == 0) { /* lines */ 1497 SendToPymol(p, 1498 1499 1500 1501 1502 1503 } else if (strcmp(rep, "cartoon") == 0) { /* cartoon */ 1504 SendToPymol(p, 1505 1506 1507 1508 "show cartoon,%s\n", 1509 1510 } 1465 SendToPymol(p, 1466 "set stick_color,white,%s\n" 1467 "show sticks,%s\n" 1468 "show spheres,%s\n" 1469 "hide lines,%s\n" 1470 "hide cartoon,%s\n", 1471 model, model, model, model, model); 1472 } else if (strcmp(rep, "spheres") == 0) { /* spheres */ 1473 SendToPymol(p, 1474 "hide sticks,%s\n" 1475 "show spheres,%s\n" 1476 "hide lines,%s\n" 1477 "hide cartoon,%s\n" 1478 "set sphere_quality,2,%s\n" 1479 "set ambient,.2,%s\n", 1480 model, model, model, model, model, model); 1481 } else if (strcmp(rep, "none") == 0) { /* nothing */ 1482 SendToPymol(p, 1483 "hide sticks,%s\n", 1484 "hide spheres,%s\n" 1485 "hide lines,%s\n" 1486 "hide cartoon,%s\n", 1487 model, model, model, model); 1488 } else if (strcmp(rep, "sticks") == 0) { /* sticks */ 1489 SendToPymol(p, 1490 "set stick_color,white,%s\n" 1491 "show sticks,%s\n" 1492 "hide spheres,%s\n" 1493 "hide lines,%s\n" 1494 "hide cartoon,%s\n", 1495 model, model, model, model, model); 1496 } else if (strcmp(rep, "lines") == 0) { /* lines */ 1497 SendToPymol(p, 1498 "hide sticks,%s\n" 1499 "hide spheres,%s\n" 1500 "show lines,%s\n" 1501 "hide cartoon,%s\n", 1502 model, model, model, model); 1503 } else if (strcmp(rep, "cartoon") == 0) { /* cartoon */ 1504 SendToPymol(p, 1505 "hide sticks,%s\n" 1506 "hide spheres,%s\n" 1507 "hide lines,%s\n" 1508 "show cartoon,%s\n", 1509 model, model, model, model); 1510 } 1511 1511 return p->status; 1512 1512 } 1513 1513 1514 /* 1514 /* 1515 1515 * RotateCmd -- 1516 1516 * 1517 * 1518 * 1519 * 1520 * 1521 * 1517 * Issue "turn" commands for changes in the angle of the camera. The 1518 * problem here is that there is no event compression. Consecutive 1519 * "rotate" commands are not compressed into a single directive. The 1520 * means that the pymol server will render many scene that are not used 1521 * by the client. 1522 1522 * 1523 * 1524 * 1523 * Need to 1) defer the "turn" commands until we find the next command 1524 * isn't a "rotate". 2) Track the rotation angles as they are compressed. 1525 1525 */ 1526 1526 static int 1527 RotateCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1528 1527 RotateCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1528 const char *argv[]) 1529 1529 { 1530 1530 PymolProxy *p = clientData; … … 1536 1536 xAngle = yAngle = zAngle = 0.0f; 1537 1537 for(arg = 1; arg < argc; arg++) { 1538 1539 defer = 1; 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 } 1538 if (strcmp(argv[arg],"-defer") == 0) { 1539 defer = 1; 1540 } else if (strcmp(argv[arg],"-push") == 0) { 1541 push = 1; 1542 } else if (varg == 1) { 1543 double value; 1544 if (Tcl_GetDouble(interp, argv[arg], &value) != TCL_OK) { 1545 return TCL_ERROR; 1546 } 1547 xAngle = (float)value; 1548 varg++; 1549 } else if (varg == 2) { 1550 double value; 1551 if (Tcl_GetDouble(interp, argv[arg], &value) != TCL_OK) { 1552 return TCL_ERROR; 1553 } 1554 yAngle = (float)value; 1555 varg++; 1556 } else if (varg == 3) { 1557 double value; 1558 if (Tcl_GetDouble(interp, argv[arg], &value) != TCL_OK) { 1559 return TCL_ERROR; 1560 } 1561 zAngle = (float)value; 1562 varg++; 1563 } 1564 } 1565 1565 p->flags |= INVALIDATE_CACHE; /* rotate */ 1566 1566 if (!defer || push) { 1567 1567 p->flags |= UPDATE_PENDING; 1568 1568 } 1569 1569 if (push) { 1570 1570 p->flags |= FORCE_UPDATE; 1571 1571 } 1572 1572 if ((xAngle != 0.0f) || (yAngle != 0.0f) || (zAngle != 0.0f)) { 1573 1574 1575 1576 1573 p->xAngle += xAngle; 1574 p->yAngle += yAngle; 1575 p->zAngle += zAngle; 1576 p->flags |= ROTATE_PENDING; 1577 1577 } 1578 1578 return p->status; … … 1580 1580 1581 1581 static int 1582 ScreenCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1583 1582 ScreenCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1583 const char *argv[]) 1584 1584 { 1585 1585 PymolProxy *p = clientData; … … 1591 1591 varg = 1; 1592 1592 for(i = 1; i < argc; i++) { 1593 if ( strcmp(argv[i],"-defer") == 0 ) 1593 if ( strcmp(argv[i],"-defer") == 0 ) 1594 1594 defer = 1; 1595 1595 else if ( strcmp(argv[i], "-push") == 0 ) … … 1606 1606 } 1607 1607 if ((width < 0) || (height < 0)) { 1608 1608 return TCL_ERROR; 1609 1609 } 1610 1610 p->flags |= INVALIDATE_CACHE; /* viewport */ 1611 1611 if (!defer || push) { 1612 1612 p->flags |= UPDATE_PENDING; 1613 1613 } 1614 1614 if (push) { 1615 1615 p->flags |= FORCE_UPDATE; 1616 1616 } 1617 1617 p->width = width; … … 1622 1622 1623 1623 static int 1624 SphereScaleCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1625 1624 SphereScaleCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1625 const char *argv[]) 1626 1626 { 1627 1627 int defer = 0, push = 0, i; … … 1635 1635 if ( strcmp(argv[i],"-defer") == 0 ) { 1636 1636 defer = 1; 1637 1637 } else if (strcmp(argv[i],"-push") == 0) { 1638 1638 push = 1; 1639 1639 } else if (strcmp(argv[i],"-model") == 0) { 1640 1640 if (++i < argc) { 1641 1641 model = argv[i]; 1642 1642 } 1643 1643 } else { 1644 1645 1646 1647 1644 if (Tcl_GetDouble(interp, argv[i], &scale) != TCL_OK) { 1645 return TCL_ERROR; 1646 } 1647 } 1648 1648 } 1649 1649 p->flags |= INVALIDATE_CACHE; /* sphere_scale */ 1650 1650 if (!defer || push) { 1651 1651 p->flags |= UPDATE_PENDING; 1652 1652 } 1653 1653 if (push) { 1654 1654 p->flags |= FORCE_UPDATE; 1655 1655 } 1656 1656 if (strcmp(model, "all") == 0) { 1657 1658 1657 p->flags |= ATOM_SCALE_PENDING; 1658 p->sphereScale = scale; 1659 1659 } else { 1660 1660 SendToPymol(p, "set sphere_scale,%f,%s\n", scale, model); 1661 1661 } 1662 1662 return p->status; … … 1664 1664 1665 1665 static int 1666 StickRadiusCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1667 1666 StickRadiusCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1667 const char *argv[]) 1668 1668 { 1669 1669 PymolProxy *p = clientData; … … 1677 1677 if (strcmp(argv[i],"-defer") == 0 ) { 1678 1678 defer = 1; 1679 1679 } else if (strcmp(argv[i],"-push") == 0) { 1680 1680 push = 1; 1681 1681 } else if (strcmp(argv[i],"-model") == 0) { 1682 1682 if (++i < argc) 1683 1683 model = argv[i]; 1684 1684 } else { 1685 1686 1687 1688 1685 if (Tcl_GetDouble(interp, argv[i], &scale) != TCL_OK) { 1686 return TCL_ERROR; 1687 } 1688 } 1689 1689 } 1690 1690 p->flags |= INVALIDATE_CACHE; /* stick_radius */ 1691 1691 if (!defer || push) { 1692 1692 p->flags |= UPDATE_PENDING; 1693 1693 } 1694 1694 if (push) { 1695 1695 p->flags |= FORCE_UPDATE; 1696 1696 } 1697 1697 1698 1698 if (strcmp(model, "all") == 0) { 1699 1700 1699 p->flags |= STICK_RADIUS_PENDING; 1700 p->stickRadius = scale; 1701 1701 } else { 1702 1702 SendToPymol(p, "set stick_radius,%f,%s\n", scale, model); 1703 1703 } 1704 1704 return p->status; … … 1706 1706 1707 1707 static int 1708 TransparencyCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1709 1708 TransparencyCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1709 const char *argv[]) 1710 1710 { 1711 1711 PymolProxy *p = clientData; … … 1722 1722 if ( strcmp(argv[i],"-defer") == 0 ) { 1723 1723 defer = 1; 1724 1724 } else if (strcmp(argv[i],"-push") == 0) { 1725 1725 push = 1; 1726 1726 } else if (strcmp(argv[i],"-model") == 0) { 1727 1727 if (++i < argc) { 1728 1728 model = argv[i]; 1729 1730 1731 1732 1729 } 1730 } else { 1731 transparency = atof(argv[i]); 1732 } 1733 1733 } 1734 1734 p->flags |= INVALIDATE_CACHE; /* transparency */ 1735 1735 if (!defer || push) { 1736 1736 p->flags |= UPDATE_PENDING; 1737 1737 } 1738 1738 if (push) { 1739 1740 } 1741 SendToPymol(p, 1742 1743 1744 1745 transparency, model, transparency, model, 1746 1739 p->flags |= FORCE_UPDATE; 1740 } 1741 SendToPymol(p, 1742 "set sphere_transparency,%g,%s\n" 1743 "set stick_transparency,%g,%s\n" 1744 "set cartoon_transparency,%g,%s\n", 1745 transparency, model, transparency, model, 1746 transparency, model); 1747 1747 return p->status; 1748 1748 } 1749 1749 1750 1750 static int 1751 VMouseCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1752 1751 VMouseCmd(ClientData clientData, Tcl_Interp *interp, int argc, 1752 const char *argv[]) 1753 1753 { 1754 1754 PymolProxy *p = clientData; … … 1781 1781 } 1782 1782 1783 p->flags |= INVALIDATE_CACHE; 1783 p->flags |= INVALIDATE_CACHE; /* vmouse */ 1784 1784 if (!defer || push) { 1785 1785 p->flags |= UPDATE_PENDING; 1786 1786 } 1787 1787 if (push) { 1788 1788 p->flags |= FORCE_UPDATE; 1789 1789 } 1790 1790 SendToPymol(p, "vmouse %d,%d,%d,%d,%d\n", arg1, arg2, arg3, arg4, arg5); … … 1793 1793 1794 1794 1795 /* 1795 /* 1796 1796 * ZoomCmd -- 1797 1797 * 1798 * 1799 * 1800 * 1801 * 1802 * 1798 * Issue "move" commands for changes in the z-coordinate of the camera. 1799 * The problem here is that there is no event compression. Consecutive 1800 * "zoom" commands are not compressed into a single directive. The means 1801 * that the pymol server will render scenes that are not used by the 1802 * client. 1803 1803 * 1804 * 1805 * 1804 * Need to 1) defer the "move" commands until we find the next command 1805 * isn't a "zoom". 2) Track the z-coordinate as they are compressed. 1806 1806 */ 1807 1807 static int … … 1815 1815 1816 1816 for(i = 1; i < argc; i++) { 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1817 if (strcmp(argv[i],"-defer") == 0) 1818 defer = 1; 1819 else if (strcmp(argv[i],"-push") == 0) 1820 push = 1; 1821 else if (varg == 1) { 1822 double value; 1823 if (Tcl_GetDouble(interp, argv[i], &value) != TCL_OK) { 1824 return TCL_ERROR; 1825 } 1826 factor = (float)value; 1827 varg++; 1828 } 1829 1829 } 1830 1830 p->flags |= INVALIDATE_CACHE; /* Zoom */ 1831 1831 if (!defer || push) { 1832 1832 p->flags |= UPDATE_PENDING; 1833 1833 } 1834 1834 if (push) { 1835 1835 p->flags |= FORCE_UPDATE; 1836 1836 } 1837 1837 if (factor != 0.0) { 1838 1839 1838 p->zoom = factor; 1839 p->flags |= ZOOM_PENDING; 1840 1840 } 1841 1841 return p->status; 1842 1842 } 1843 1843 1844 1845 1846 static int 1847 ExecuteCommand(Tcl_Interp *interp, Tcl_DString *dsPtr) 1844 1845 1846 static int 1847 ExecuteCommand(Tcl_Interp *interp, Tcl_DString *dsPtr) 1848 1848 { 1849 1849 struct timeval tv; … … 1860 1860 if (result != TCL_OK) { 1861 1861 #if EXEC_DEBUG 1862 1862 DEBUG("result was %s\n", Tcl_GetString(Tcl_GetObjResult(interp))); 1863 1863 #endif 1864 1864 } … … 1876 1876 { 1877 1877 if (p->flags & VIEWPORT_PENDING) { 1878 1879 1880 1878 SendToPymol(p, "viewport %d,%d\n", p->width, p->height); 1879 SendToPymol(p, "refresh\n"); 1880 p->flags &= ~VIEWPORT_PENDING; 1881 1881 } 1882 1882 } … … 1887 1887 if (p->flags & ZOOM_PENDING) { 1888 1888 SendToPymol(p, "move z,%f\n", p->zoom); 1889 1889 p->flags &= ~ZOOM_PENDING; 1890 1890 } 1891 1891 } … … 1895 1895 { 1896 1896 if (p->flags & PAN_PENDING) { 1897 1898 1897 SendToPymol(p, "move x,%f\nmove y,%f\n", p->xPan, p->yPan); 1898 p->flags &= ~PAN_PENDING; 1899 1899 } 1900 1900 } … … 1904 1904 { 1905 1905 if (p->flags & ROTATE_PENDING) { 1906 1907 1908 SendToPymol(p,"turn x,%f\nturn y,%f\nturn z,%f\n", p->xAngle, p->yAngle, 1909 1910 1911 1906 /* Every pymol command line generates a new rendering. Execute all 1907 * three turns as a single command line. */ 1908 SendToPymol(p,"turn x,%f\nturn y,%f\nturn z,%f\n", p->xAngle, p->yAngle, 1909 p->zAngle); 1910 p->xAngle = p->yAngle = p->zAngle = 0.0f; 1911 p->flags &= ~ROTATE_PENDING; 1912 1912 } 1913 1913 } … … 1917 1917 { 1918 1918 if (p->flags & ATOM_SCALE_PENDING) { 1919 1920 1919 SendToPymol(p, "set sphere_scale,%f,all\n", p->sphereScale); 1920 p->flags &= ~ATOM_SCALE_PENDING; 1921 1921 } 1922 1922 } … … 1926 1926 { 1927 1927 if (p->flags & STICK_RADIUS_PENDING) { 1928 1929 1928 SendToPymol(p, "set stick_radius,%f,all\n", p->stickRadius); 1929 p->flags &= ~STICK_RADIUS_PENDING; 1930 1930 } 1931 1931 } … … 1936 1936 /* Handle all the pending setting changes now. */ 1937 1937 if (p->flags & VIEWPORT_PENDING) { 1938 1938 SetViewport(p); 1939 1939 } 1940 1940 if (p->flags & ROTATE_PENDING) { 1941 1941 SetRotation(p); 1942 1942 } 1943 1943 if (p->flags & PAN_PENDING) { 1944 1944 SetPan(p); 1945 1945 } 1946 1946 if (p->flags & ZOOM_PENDING) { 1947 1947 SetZoom(p); 1948 1948 } 1949 1949 if (p->flags & ATOM_SCALE_PENDING) { 1950 1950 SetSphereScale(p); 1951 1951 } 1952 1952 if (p->flags & STICK_RADIUS_PENDING) { 1953 1953 SetStickRadius(p); 1954 1954 } 1955 1955 } … … 1963 1963 imgPtr = malloc(sizeof(Image) + dataLength); 1964 1964 if (imgPtr == NULL) { 1965 ERROR("can't allocate image of %lu bytes", 1966 1967 1965 ERROR("can't allocate image of %lu bytes", 1966 (unsigned long)(sizeof(Image) + dataLength)); 1967 abort(); 1968 1968 } 1969 1969 imgPtr->prevPtr = imgPtr->nextPtr = NULL; … … 1974 1974 #endif 1975 1975 if (listPtr->headPtr != NULL) { 1976 1976 listPtr->headPtr->prevPtr = imgPtr; 1977 1977 } 1978 1978 imgPtr->nextPtr = listPtr->headPtr; 1979 1979 if (listPtr->tailPtr == NULL) { 1980 1980 listPtr->tailPtr = imgPtr; 1981 1981 } 1982 1982 listPtr->headPtr = imgPtr; … … 1996 1996 WriteImages(ImageList *listPtr, int fd) 1997 1997 { 1998 Image *imgPtr, *prevPtr; 1998 Image *imgPtr, *prevPtr; 1999 1999 2000 2000 if (listPtr->tailPtr == NULL) { 2001 2002 2001 ERROR("Should not be here: no image available to write"); 2002 return; 2003 2003 } 2004 2004 #if WRITE_DEBUG 2005 2005 DEBUG("Entering WriteImages"); 2006 2006 #endif 2007 2007 for (imgPtr = listPtr->tailPtr; imgPtr != NULL; imgPtr = prevPtr) { 2008 2009 2010 2011 2008 ssize_t bytesLeft; 2009 2010 assert(imgPtr->nextPtr == NULL); 2011 prevPtr = imgPtr->prevPtr; 2012 2012 #if WRITE_DEBUG 2013 DEBUG("WriteImages: image %d of %d bytes.", imgPtr->id, 2014 2015 #endif 2016 2017 2013 DEBUG("WriteImages: image %d of %d bytes.", imgPtr->id, 2014 imgPtr->bytesLeft); 2015 #endif 2016 for (bytesLeft = imgPtr->bytesLeft; bytesLeft > 0; /*empty*/) { 2017 ssize_t numWritten; 2018 2018 #if WRITE_DEBUG 2019 2020 #endif 2021 2019 DEBUG("image %d: bytesLeft=%d", imgPtr->id, bytesLeft); 2020 #endif 2021 numWritten = write(fd, imgPtr->data + imgPtr->numWritten, bytesLeft); 2022 2022 #if WRITE_DEBUG 2023 2024 #endif 2025 2026 2023 DEBUG("image %d: wrote %d bytes.", imgPtr->id, numWritten); 2024 #endif 2025 if (numWritten < 0) { 2026 ERROR("Error writing fd=%d, %s", fd, strerror(errno)); 2027 2027 #if WRITE_DEBUG 2028 2029 #endif 2030 2031 2032 2033 2028 DEBUG("Abnormal exit WriteImages"); 2029 #endif 2030 return; 2031 } 2032 bytesLeft -= numWritten; 2033 if (bytesLeft > 0) { 2034 2034 #if WRITE_DEBUG 2035 DEBUG("image %d, wrote a short buffer, %d bytes left.", 2036 2037 #endif 2038 2039 2040 2035 DEBUG("image %d, wrote a short buffer, %d bytes left.", 2036 imgPtr->id, bytesLeft); 2037 #endif 2038 /* Wrote a short buffer, means we would block. */ 2039 imgPtr->numWritten += numWritten; 2040 imgPtr->bytesLeft = bytesLeft; 2041 2041 #if WRITE_DEBUG 2042 2043 #endif 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2042 DEBUG("Abnormal exit WriteImages"); 2043 #endif 2044 return; 2045 } 2046 imgPtr->numWritten += numWritten; 2047 } 2048 /* Check if image is on the head. */ 2049 listPtr->tailPtr = prevPtr; 2050 if (prevPtr != NULL) { 2051 prevPtr->nextPtr = NULL; 2052 } 2053 FreeImage(imgPtr); 2054 2054 } 2055 2055 listPtr->headPtr = NULL; … … 2061 2061 2062 2062 static void 2063 ChildHandler(int sigNum) 2063 ChildHandler(int sigNum) 2064 2064 { 2065 2065 ERROR("pymol (%d) died unexpectedly", stats.pid); … … 2074 2074 2075 2075 static CmdProc cmdProcs[] = { 2076 { "cartoon", CartoonCmd },2077 { "cartoontrace", CartoonTraceCmd },2078 { "clientinfo", ClientInfoCmd }, 2079 { "disable", DisableCmd },2080 { "enable", EnableCmd },2081 { "frame", FrameCmd }, 2082 { "label", LabelCmd }, 2083 { "loadpdb", LoadPDBCmd },2084 { "orthoscopic", OrthoscopicCmd },2085 { "pan", PanCmd },2086 { "png", PngCmd },2087 { "ppm", PpmCmd },2088 { "print", PrintCmd }, 2089 { "raw", RawCmd },2076 { "cartoon", CartoonCmd }, 2077 { "cartoontrace", CartoonTraceCmd }, 2078 { "clientinfo", ClientInfoCmd }, 2079 { "disable", DisableCmd }, 2080 { "enable", EnableCmd }, 2081 { "frame", FrameCmd }, 2082 { "label", LabelCmd }, 2083 { "loadpdb", LoadPDBCmd }, 2084 { "orthoscopic", OrthoscopicCmd }, 2085 { "pan", PanCmd }, 2086 { "png", PngCmd }, 2087 { "ppm", PpmCmd }, 2088 { "print", PrintCmd }, 2089 { "raw", RawCmd }, 2090 2090 { "representation", RepresentationCmd }, 2091 { "reset", ResetCmd }, 2092 { "rock", RockCmd }, 2093 { "rotate", RotateCmd }, 2094 { "screen", ScreenCmd }, 2095 { "spherescale", SphereScaleCmd }, 2096 { "stickradius", StickRadiusCmd }, 2097 { "transparency", TransparencyCmd }, 2098 { "viewport", ScreenCmd }, 2099 { "vmouse", VMouseCmd }, 2100 { "zoom", ZoomCmd }, 2101 { NULL, 2091 { "reset", ResetCmd }, 2092 { "rock", RockCmd }, 2093 { "rotate", RotateCmd }, 2094 { "screen", ScreenCmd }, 2095 { "spherescale", SphereScaleCmd }, 2096 { "stickradius", StickRadiusCmd }, 2097 { "transparency", TransparencyCmd }, 2098 { "viewport", ScreenCmd }, 2099 { "vmouse", VMouseCmd }, 2100 { "zoom", ZoomCmd }, 2101 { NULL, NULL } 2102 2102 }; 2103 2103 … … 2105 2105 InitProxy(PymolProxy *p, char *const *argv) 2106 2106 { 2107 int sin[2], sout[2]; 2107 int sin[2], sout[2]; /* Pipes to connect to server. */ 2108 2108 pid_t child; 2109 2109 struct timeval end; … … 2131 2131 } 2132 2132 2133 if (child == 0) { 2133 if (child == 0) { /* Child process */ 2134 2134 int f; 2135 2136 2137 /* 2135 char tmpname[200]; 2136 2137 /* 2138 2138 * Create a new process group, so we can later kill this process and 2139 2139 * all its children without affecting the process that created this 2140 2140 * one. 2141 2141 */ 2142 setpgid(child, 0); 2143 2144 /* Redirect stdin, stdout, and stderr to pipes before execing */ 2145 2146 dup2(sin[0], 0); 2147 dup2(sout[1], 1); 2148 2149 2150 2151 2152 2153 ERROR("can't open file `%s' to capture pymol stderr: %s", tmpname, 2154 2155 2156 2157 dup2(f, 2); 2158 2159 /* Close all other descriptors */ 2160 2142 setpgid(child, 0); 2143 2144 /* Redirect stdin, stdout, and stderr to pipes before execing */ 2145 2146 dup2(sin[0], 0); /* Server standard input */ 2147 dup2(sout[1], 1); /* Server standard output */ 2148 2149 /* Redirect child's stderr to a log file. */ 2150 sprintf(tmpname, "%s/PYMOL-%d-stderr.XXXXXX", LOGDIR, getpid()); 2151 f = mkstemp(tmpname); 2152 if (f < 0) { 2153 ERROR("can't open file `%s' to capture pymol stderr: %s", tmpname, 2154 strerror(errno)); 2155 exit(1); 2156 } 2157 dup2(f, 2); /* Redirect stderr to a log file */ 2158 2159 /* Close all other descriptors */ 2160 for (f = 3; f < FD_SETSIZE; f++) { 2161 2161 close(f); 2162 2162 } 2163 2163 INFO("attempting to start \"%s\"", argv[0]); 2164 2164 execvp(argv[0], argv); 2165 2165 ERROR("can't exec `%s': %s", argv[0], strerror(errno)); 2166 2166 exit(-1); 2167 2167 } else { 2168 2169 2168 pymolIsAlive = TRUE; 2169 signal(SIGCHLD, ChildHandler); 2170 2170 } 2171 2171 stats.pid = child; … … 2191 2191 /* Create safe interpreter and add pymol-specific commands to it. */ 2192 2192 { 2193 2194 2195 2196 2197 2198 2199 2193 Tcl_Interp *interp; 2194 CmdProc *cp; 2195 2196 interp = Tcl_CreateInterp(); 2197 Tcl_MakeSafe(interp); 2198 2199 for (cp = cmdProcs; cp->name != NULL; cp++) { 2200 2200 #if DEBUG 2201 2202 #endif 2203 2204 2205 2201 DEBUG("Adding command %s\n", cp->name); 2202 #endif 2203 Tcl_CreateCommand(interp, cp->name, cp->proc, p, NULL); 2204 } 2205 p->interp = interp; 2206 2206 } 2207 2207 gettimeofday(&end, NULL); … … 2236 2236 DEBUG("attempting to signal (SIGTERM) pymol server."); 2237 2237 #endif 2238 kill(-p->pid, SIGTERM); // Kill process group2238 kill(-p->pid, SIGTERM); /* Kill process group */ 2239 2239 alarm(5); 2240 2240 2241 2241 #if DEBUG 2242 2242 DEBUG("Waiting for pymol server to exit after SIGTERM"); 2243 2243 #endif 2244 2244 if (waitpid(p->pid, &result, 0) < 0) { 2245 2246 ERROR("error waiting on pymol server to exit after SIGTERM: %s", 2247 2248 2245 if (errno != ECHILD) { 2246 ERROR("error waiting on pymol server to exit after SIGTERM: %s", 2247 strerror(errno)); 2248 } 2249 2249 } 2250 2250 status = -1; 2251 2251 while ((status == -1) && (errno == EINTR)) { 2252 2252 #if DEBUG 2253 2254 #endif 2255 kill(-p->pid, SIGKILL); // Kill process group 2256 2253 DEBUG("Attempting to signal (SIGKILL) pymol server."); 2254 #endif 2255 kill(-p->pid, SIGKILL); /* Kill process group */ 2256 alarm(10); 2257 2257 #if DEBUG 2258 2259 #endif 2260 2261 alarm(0); 2258 DEBUG("Waiting for pymol server to exit after SIGKILL"); 2259 #endif 2260 status = waitpid(p->pid, &result, 0); 2261 alarm(0); 2262 2262 } 2263 2263 INFO("pymol server process ended (result=%d)", result); 2264 2264 2265 2265 if (WIFEXITED(result)) { 2266 2266 result = WEXITSTATUS(result); 2267 2267 } 2268 2268 return result; … … 2282 2282 Tcl_DStringInit(&command); 2283 2283 while (pymolIsAlive) { 2284 2284 tvPtr = NULL; 2285 2285 #if READ_DEBUG 2286 2287 #endif 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2286 DEBUG("Start I/O set"); 2287 #endif 2288 while ((pymolIsAlive) && (WaitForNextLine(&p->client, tvPtr))) { 2289 size_t numBytes; 2290 const char *line; 2291 int status; 2292 2293 status = GetLine(&p->client, &numBytes, &line); 2294 if (status != BUFFER_OK) { 2295 ERROR("can't read client stdout (numBytes=%d): %s\n", numBytes, 2296 strerror(errno)); 2297 goto done; 2298 } 2299 Tcl_DStringAppend(&command, line, numBytes); 2300 if (Tcl_CommandComplete(Tcl_DStringValue(&command))) { 2301 int result; 2302 2303 /* May execute more than one command. */ 2304 result = ExecuteCommand(p->interp, &command); 2305 if (result == TCL_BREAK) { 2306 2306 #if READ_DEBUG 2307 2308 #endif 2309 break;/* This was caused by a "imgflush"2307 DEBUG("TCL_BREAK found"); 2308 #endif 2309 break; /* This was caused by a "imgflush" 2310 2310 * command. Break out of the read 2311 2311 * loop and allow a new image to be 2312 2312 * rendered. */ 2313 2314 2313 } 2314 if (p->flags & FORCE_UPDATE) { 2315 2315 #if READ_DEBUG 2316 2317 #endif 2318 2319 2320 2321 2322 2316 DEBUG("FORCE_UPDATE set"); 2317 #endif 2318 break; 2319 } 2320 } 2321 tv.tv_sec = 0L; 2322 tv.tv_usec = 0L; /* On successive reads, we break 2323 2323 * out if no data is available. */ 2324 tvPtr = &tv; 2325 2324 tvPtr = &tv; 2325 } 2326 2326 #if READ_DEBUG 2327 2328 #endif 2329 2330 2331 2332 2333 2334 2335 2327 DEBUG("Finish I/O set"); 2328 #endif 2329 /* Handle all the pending setting changes now. */ 2330 UpdateSettings(p); 2331 2332 /* We might want to refresh the image if we're not currently 2333 * transmitting an image back to the client. The image will be 2334 * refreshed after the image has been completely transmitted. */ 2335 if (p->flags & UPDATE_PENDING) { 2336 2336 #if READ_DEBUG 2337 2338 #endif 2339 2340 2341 2337 DEBUG("calling ppm because of update"); 2338 #endif 2339 Tcl_Eval(p->interp, "ppm"); 2340 p->flags &= ~(UPDATE_PENDING|FORCE_UPDATE); 2341 } 2342 2342 } 2343 2343 done: … … 2359 2359 list.headPtr = list.tailPtr = NULL; 2360 2360 while (pymolIsAlive) { 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2361 while (WaitForNextLine(bp, NULL)) { 2362 Image *imgPtr; 2363 const char *line; 2364 char header[200]; 2365 size_t len; 2366 int numBytes; 2367 size_t hdrLength; 2368 int frameNum, rockOffset; 2369 char cacheId[200]; 2370 2371 /* Keep reading lines untils we find a "image follows:" line */ 2372 if (GetLine(bp, &len, &line) != BUFFER_OK) { 2373 2373 #if WRITE_DEBUG 2374 2375 #endif 2376 2377 2374 DEBUG("Leaving Writer thread"); 2375 #endif 2376 return NULL; 2377 } 2378 2378 #if WRITE_DEBUG 2379 2380 #endif 2381 2382 2383 2384 2385 2386 2387 2388 2389 } 2379 DEBUG("Writer: line found is %s\n", line); 2380 #endif 2381 if (strncmp(line, "image follows: ", 15) != 0) { 2382 continue; 2383 } 2384 if (sscanf(line, "image follows: %d %s %d %d\n", &numBytes, cacheId, 2385 &frameNum, &rockOffset) != 4) { 2386 ERROR("can't get # bytes from \"%s\"", line); 2387 DEBUG("Leaving Writer thread"); 2388 return NULL; 2389 } 2390 2390 #if WRITE_DEBUG 2391 2392 #endif 2393 sprintf(header, "nv>image %d %s %d %d\n", numBytes, cacheId, 2394 2395 2391 DEBUG("found image line \"%.*s\"", numBytes, line); 2392 #endif 2393 sprintf(header, "nv>image %d %s %d %d\n", numBytes, cacheId, 2394 frameNum, rockOffset); 2395 hdrLength = strlen(header); 2396 2396 #if WRITE_DEBUG 2397 2398 #endif 2399 2400 2401 if (ReadFollowingData(bp, imgPtr->data + hdrLength, 2402 2403 ERROR("can't read %d bytes for \"image follows\" buffer: %s", 2404 2397 DEBUG("Queueing image numBytes=%d cacheId=%s, frameNum=%d, rockOffset=%d size=%d\n", numBytes, cacheId, frameNum, rockOffset, numBytes + hdrLength); 2398 #endif 2399 imgPtr = NewImage(&list, numBytes + hdrLength); 2400 memcpy(imgPtr->data, header, hdrLength); 2401 if (ReadFollowingData(bp, imgPtr->data + hdrLength, 2402 (size_t)numBytes) != BUFFER_OK) { 2403 ERROR("can't read %d bytes for \"image follows\" buffer: %s", 2404 numBytes, strerror(errno)); 2405 2405 #if WRITE_DEBUG 2406 2407 #endif 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2406 DEBUG("Leaving Writer thread"); 2407 #endif 2408 return NULL; 2409 } 2410 stats.numFrames++; 2411 stats.numBytes += numBytes; 2412 { 2413 struct timeval tv; 2414 fd_set writeFds; 2415 2416 tv.tv_sec = tv.tv_usec = 0L; 2417 FD_ZERO(&writeFds); 2418 FD_SET(CLIENT_WRITE, &writeFds); 2419 if (select(CLIENT_WRITE+1, NULL, &writeFds, NULL, &tv) > 0) { 2420 WriteImages(&list, CLIENT_WRITE); 2421 } 2422 } 2423 } 2424 2424 } 2425 2425 #if WRITE_DEBUG … … 2440 2440 frecord = NULL; 2441 2441 if (recording) { 2442 2443 2444 2442 char fileName[200]; 2443 2444 sprintf(fileName, "/tmp/pymolproxy%d.py", getpid()); 2445 2445 frecord = fopen(fileName, "w"); 2446 2446 } … … 2462 2462 if (pthread_join(thread1, NULL) < 0) { 2463 2463 ERROR("Can't join reader thread: %s", strerror(errno)); 2464 } 2464 } 2465 2465 return FreeProxy(&proxy); 2466 2466 }
Note: See TracChangeset
for help on using the changeset viewer.