1 
2 /*
3    A* -------------------------------------------------------------------
4    B* This file contains source code for the PyMOL computer program
5    C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific.
6    D* -------------------------------------------------------------------
7    E* It is unlawful to modify or remove this copyright notice.
8    F* -------------------------------------------------------------------
9    G* Please see the accompanying LICENSE file for further information.
10    H* -------------------------------------------------------------------
11    I* Additional authors of this source file include:
12    -*
13    -*
14    -*
15    Z* -------------------------------------------------------------------
16 */
17 
18 
19 /* meaning of defines
20 
21 _PYMOL_MONOLITHIC: means that we're building PyMOL and its Python C
22 dependencies as one C library.  That means we need to explicitly call
23 the initialization functions for these libraries on startup.
24 
25 */
26 #include"os_python.h"
27 
28 #include"os_predef.h"
29 #include"os_std.h"
30 #include"Base.h"
31 
32 
33 /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
34 #ifdef WIN32
35 #include"os_proprietary.h"
36 #include<process.h>
37 #endif
38 
39 /* END PROPRIETARY CODE SEGMENT */
40 
41 #ifdef _PYMOL_MINGW
42 #define putenv _putenv
43 #endif
44 
45 #include"os_time.h"
46 #include"os_unix.h"
47 
48 #include"MemoryDebug.h"
49 #include"Base.h"
50 #include"Err.h"
51 #include"P.h"
52 #include"PConv.h"
53 #include"Ortho.h"
54 #include"Cmd.h"
55 #include"main.h"
56 #include"AtomInfo.h"
57 #include"CoordSet.h"
58 #include"Util.h"
59 #include"Executive.h"
60 #include"PyMOLOptions.h"
61 #include"PyMOL.h"
62 #include "Lex.h"
63 #include "Seeker.h"
64 
65 #ifdef _PYMOL_IP_PROPERTIES
66 #include"Property.h"
67 #endif
68 
69 #include <memory>
70 
71 /**
72  * Use with functions which return a PyObject - if they return NULL, then an
73  * unexpected exception has occured.
74  */
75 #define ASSERT_PYOBJECT_NOT_NULL(obj, return_value)                            \
76   if (!obj) {                                                                  \
77     PyErr_Print();                                                             \
78     return return_value;                                                       \
79   }
80 
label_copy_text(char * dst,const char * src,int len,int max)81 static int label_copy_text(char *dst, const char *src, int len, int max)
82 {
83   dst += len;
84   while(len < max) {
85     if(!*src)
86       break;
87     *(dst++) = *(src++);
88     len++;
89   }
90   *dst = 0;
91   return len;
92 }
93 
label_next_token(WordType dst,const char ** expr)94 static int label_next_token(WordType dst, const char **expr)
95 {
96   const char *p = *expr;
97   char *q = dst;
98   char ch;
99   int tok_len = 0;
100   int tok_max = sizeof(WordType) - 1;
101 
102   /* skip leading whitespace (if any) */
103 
104   while((ch = *p)) {
105     if(ch > 33)
106       break;
107     p++;
108   }
109 
110   /* copy the token */
111 
112   while((ch = *p)) {
113     if(((ch >= 'a') && (ch <= 'z')) ||
114        ((ch >= 'A') && (ch <= 'Z')) || ((ch >= '0') && (ch <= '9')) || ((ch == '_'))) {
115       if(tok_len < tok_max) {
116         *(q++) = ch;
117         tok_len++;
118       }
119     } else {
120       break;
121     }
122     p++;
123   }
124   *q = 0;
125   if(p != *expr)
126     *expr = p;
127   else if(*p)
128     *expr = p + 1;              /* always advance input by at least one character */
129 
130   /* let caller know whether we read anything */
131 
132   return (q != dst);
133 }
134 
PLabelExprUsesVariable(PyMOLGlobals * G,const char * expr,const char * var)135 int PLabelExprUsesVariable(PyMOLGlobals * G, const char *expr, const char *var)
136 {
137   char ch, quote = 0;
138   int escaped = false;
139   while((ch = *(expr++))) {
140     if(!quote) {
141       if(ch == '\'') {
142         quote = ch;
143       } else if(ch == '"') {
144         quote = ch;
145       } else if((ch < 33) || (ch == '+') || (ch == '(') || (ch == ')')) {
146         /* nop */
147       } else if(ch > 32) {
148         WordType tok;
149         expr--;
150         if(label_next_token(tok, &expr)) {
151           if(!strcmp(tok, var)) {
152 	    return 1;
153 	  }
154 	}
155       }
156     } else {
157       if(ch == quote) {
158         quote = 0;
159       } else if(ch == '\\') {
160         if(!escaped) {
161           escaped = true;
162         } else {
163           escaped = false;
164         }
165       }
166     }
167   }
168   return 0;
169 }
170 
PLabelAtomAlt(PyMOLGlobals * G,AtomInfoType * at,const char * model,const char * expr,int index)171 int PLabelAtomAlt(PyMOLGlobals * G, AtomInfoType * at, const char *model, const char *expr, int index)
172 {
173   /* alternate C implementation which bypasses Python expressions -- works
174      only for simple label formats "..."+property+... */
175 
176   int result = false;
177   OrthoLineType label;
178   int label_len = 0;
179   int label_max = sizeof(OrthoLineType);
180   OrthoLineType buffer;
181   char ch, quote = 0;
182   int escaped = false;
183   const char *origexpr = expr;
184 
185   label[0] = 0;
186   while((ch = *(expr++))) {
187     if(!quote) {
188       if(ch == '\'') {
189         quote = ch;
190       } else if(ch == '"') {
191         quote = ch;
192       } else if((ch < 33) || (ch == '+') || (ch == '(') || (ch == ')')) {
193         /* nop */
194       } else if(ch > 32) {
195         WordType tok;
196 	int tokresult = true;
197         expr--;
198         if(label_next_token(tok, &expr)) {
199           /* brain-dead linear string matching */
200           buffer[0] = 0;
201           if(!strcmp(tok, "model")) {
202             label_len = label_copy_text(label, model, label_len, label_max);
203           } else if(!strcmp(tok, "index")) {
204             sprintf(buffer, "%d", index + 1);
205           } else if(!strcmp(tok, "type")) {
206             if(at->hetatm)
207               label_len = label_copy_text(label, "HETATM", label_len, label_max);
208             else
209               label_len = label_copy_text(label, "ATOM", label_len, label_max);
210           } else if(!strcmp(tok, "name")) {
211             label_len = label_copy_text(label, LexStr(G, at->name), label_len, label_max);
212           } else if(!strcmp(tok, "resn")) {
213             label_len = label_copy_text(label, LexStr(G, at->resn), label_len, label_max);
214           } else if(!strcmp(tok, "resi")) {
215             sprintf(buffer, "%d%c", at->resv, at->inscode);
216           } else if(!strcmp(tok, "resv")) {
217             sprintf(buffer, "%d", at->resv);
218           } else if(!strcmp(tok, "chain")) {
219             label_len = label_copy_text(label, LexStr(G, at->chain), label_len, label_max);
220           } else if(!strcmp(tok, "alt")) {
221             label_len = label_copy_text(label, at->alt, label_len, label_max);
222           } else if(!strcmp(tok, "segi")) {
223             label_len = label_copy_text(label, LexStr(G, at->segi), label_len, label_max);
224           } else if(!strcmp(tok, "ss")) {
225             label_len = label_copy_text(label, at->ssType, label_len, label_max);
226           } else if(!strcmp(tok, "vdw")) {
227             sprintf(buffer, "%1.2f", at->vdw);
228           } else if(!strcmp(tok, "elec_radius")) {
229             sprintf(buffer, "%1.2f", at->elec_radius);
230           } else if(!strcmp(tok, "text_type")) {
231             const char *st = LexStr(G, at->textType);
232             label_len = label_copy_text(label, st, label_len, label_max);
233           } else if(!strcmp(tok, "custom")) {
234             const char *st = LexStr(G, at->custom);
235             label_len = label_copy_text(label, st, label_len, label_max);
236           } else if(!strcmp(tok, "elem")) {
237             label_len = label_copy_text(label, at->elem, label_len, label_max);
238           } else if(!strcmp(tok, "geom")) {
239             sprintf(buffer, "%d", at->geom);
240           } else if(!strcmp(tok, "valence")) {
241             sprintf(buffer, "%d", at->valence);
242           } else if(!strcmp(tok, "rank")) {
243             sprintf(buffer, "%d", at->rank);
244           } else if(!strcmp(tok, "flags")) {
245             if(at->flags) {
246               sprintf(buffer, "%X", at->flags);
247             } else {
248               strcpy(buffer, "0");
249             }
250           } else if(!strcmp(tok, "q")) {
251             sprintf(buffer, "%1.2f", at->q);
252           } else if(!strcmp(tok, "b")) {
253             sprintf(buffer, "%1.2f", at->b);
254           } else if(!strcmp(tok, "numeric_type")) {
255             if(at->customType != cAtomInfoNoType)
256               sprintf(buffer, "%d", at->customType);
257             else {
258               strcpy(buffer, "?");
259             }
260           } else if(!strcmp(tok, "partial_charge")) {
261             sprintf(buffer, "%1.3f", at->partialCharge);
262           } else if(!strcmp(tok, "formal_charge")) {
263             sprintf(buffer, "%d", at->formalCharge);
264           } else if(!strcmp(tok, "stereo")) {
265             strcpy(buffer, AtomInfoGetStereoAsStr(at));
266           } else if(!strcmp(tok, "color")) {
267             sprintf(buffer, "%d", at->color);
268           } else if(!strcmp(tok, "cartoon")) {
269             sprintf(buffer, "%d", at->cartoon);
270           } else if(!strcmp(tok, "ID")) {
271             sprintf(buffer, "%d", at->id);
272           } else if(!strcmp(tok, "str")) {
273             /* nop */
274           } else {
275 	    tokresult = false;
276 	  }
277           if(buffer[0]) {
278             label_len = label_copy_text(label, buffer, label_len, label_max);
279           }
280         } else {
281 	  if (tok[0]){
282 	    label_len = label_copy_text(label, "?", label_len, label_max);
283 	    label_len = label_copy_text(label, tok, label_len, label_max);
284 	  } else {
285 	    tokresult = false;
286 	  }
287 	}
288 	result |= tokresult;
289       } else {
290         if(label_len < label_max) {
291           label[label_len] = '?';
292           label_len++;
293 	  result = true;
294         }
295       }
296     } else {
297       if(ch == quote) {
298         quote = 0;
299 	result = true;
300       } else if(ch == '\\') {
301         if(!escaped) {
302           escaped = true;
303         } else {
304           if(label_len < label_max) {
305             label[label_len] = ch;
306             label_len++;
307           }
308           escaped = false;
309         }
310       } else {
311         if(label_len < label_max) {
312           label[label_len] = ch;
313           label_len++;
314           label[label_len] = 0;
315         }
316       }
317     }
318   }
319 
320   if (!result && !label[0]){
321     // if label is not set, just use expression as a string for label
322     strncpy(label, origexpr, OrthoLineLength);
323     result = true;
324   }
325 
326   LexDec(G, at->label);
327   at->label = result ? LexIdx(G, label) : 0;
328 
329   return (result);
330 }
331 
332 #ifndef _PYMOL_NOPY
333 
334 
335 /* all of the following Python objects must be invariant & global for the application */
336 
337 
338 /* these are module / module properties -- global and static for a given interpreter */
339 
340 
341 /* local to this C code module */
342 
343 static PyObject *P_pymol = NULL;
344 static PyObject *P_pymol_dict = NULL;   /* must be refomed into globals and instance properties */
345 static PyObject *P_cmd = NULL;
346 
347 static PyObject *P_povray = NULL;
348 static PyObject *P_traceback = NULL;
349 static PyObject *P_parser = NULL;
350 
351 static PyObject *P_main = NULL;
352 static PyObject *P_vfont = NULL;
353 
354 /* module import helper */
355 
356 static
PImportModuleOrFatal(const char * name)357 PyObject * PImportModuleOrFatal(const char * name) {
358   PyObject * mod = PyImport_ImportModule(name);
359   if(!mod) {
360     fprintf(stderr, "PyMOL-Error: can't find '%s'\n", name);
361     exit(EXIT_FAILURE);
362   }
363   return mod;
364 }
365 
366 static
PGetAttrOrFatal(PyObject * o,const char * name)367 PyObject * PGetAttrOrFatal(PyObject * o, const char * name) {
368   PyObject * attr = PyObject_GetAttrString(o, name);
369   if(!attr) {
370     fprintf(stderr, "PyMOL-Error: can't find '%s'\n", name);
371     exit(EXIT_FAILURE);
372   }
373   return attr;
374 }
375 
376 /* used elsewhere */
377 
378 PyObject *P_menu = NULL;        /* menu definitions are currently global */
379 PyObject *P_xray = NULL;        /* okay as global */
380 PyObject *P_chempy = NULL;      /* okay as global */
381 PyObject *P_models = NULL;      /* okay as global */
382 PyObject *P_setting = NULL;     /* okay as global -- just used for names */
383 PyObject *P_CmdException = nullptr;
384 PyObject *P_QuietException = nullptr;
385 
386 static PyMappingMethods wrapperMappingMethods, settingMappingMethods;
387 static PyTypeObject Wrapper_Type = {
388   PyVarObject_HEAD_INIT(NULL, 0)
389   "wrapper.Wrapper",            /* tp_name */
390   0,                            /* tp_basicsize */
391 };
392 static PyTypeObject settingWrapper_Type = {
393   PyVarObject_HEAD_INIT(NULL, 0)
394   "wrapper.SettingWrapper",     /* tp_name */
395   0,                            /* tp_basicsize */
396 };
397 
398 /*
399  * If `wob` is not in a valid state (outside iterate-family context), raise
400  * an error and return false.
401  */
check_wrapper_scope(WrapperObject * wobj)402 static bool check_wrapper_scope(WrapperObject * wobj) {
403   if (wobj && wobj->obj)
404     return true;
405 
406   PyErr_SetString(PyExc_RuntimeError,
407       "wrappers cannot be used outside the iterate-family commands");
408 
409   return false;
410 }
411 
412 /*
413  * key: Python int (setting index) or str (setting name)
414  *
415  * Return the setting index or -1 for unknown `key`
416  *
417  * Raise LookupError if `key` doesn't name a known setting
418  */
get_and_check_setting_index(PyMOLGlobals * G,PyObject * key)419 static int get_and_check_setting_index(PyMOLGlobals * G, PyObject * key) {
420   int setting_id;
421 
422   if(PyInt_Check(key)) {
423     setting_id = PyInt_AS_LONG(key);
424   } else {
425     key = PyObject_Str(key);
426     setting_id = SettingGetIndex(G, PyString_AS_STRING(key));
427     Py_DECREF(key);
428   }
429 
430   if (setting_id < 0 || setting_id >= cSetting_INIT) {
431     PyErr_SetString(PyExc_LookupError, "unknown setting");
432     return -1;
433   }
434 
435   return setting_id;
436 }
437 
438 /*
439  * Access a setting with iterate et. al.
440  *
441  * s[key]
442  *
443  * obj: `s` object in iterate-family namespace
444  *
445  * Raise LookupError if `key` doesn't name a known setting
446  */
447 static
SettingWrapperObjectSubScript(PyObject * obj,PyObject * key)448 PyObject *SettingWrapperObjectSubScript(PyObject *obj, PyObject *key){
449   auto& wobj = reinterpret_cast<SettingPropertyWrapperObject*>(obj)->wobj;
450   int setting_id;
451   PyObject *ret = NULL;
452 
453   if (!check_wrapper_scope(wobj)) {
454     return NULL;
455   }
456 
457   auto G = wobj->G;
458 
459   if ((setting_id = get_and_check_setting_index(G, key)) == -1) {
460     return NULL;
461   }
462 
463   if (wobj->idx >= 0){
464     // atom-state level
465     ret = SettingGetIfDefinedPyObject(G, wobj->cs, wobj->idx, setting_id);
466   }
467 
468   if (!ret){
469     // atom level
470     ret = SettingGetIfDefinedPyObject(G, wobj->atomInfo, setting_id);
471 
472     if (!ret) {
473       // object-state, object, or global
474       ret = SettingGetPyObject(G,
475           wobj->cs ? wobj->cs->Setting : NULL,
476           wobj->obj->Setting, setting_id);
477     }
478   }
479   return PConvAutoNone(ret);
480 }
481 
482 /*
483  * Set an atom or atom-state level setting with alter or alter_state.
484  *
485  * s[key] = val
486  *
487  * obj: `s` object in cmd.alter/cmd.alter_state namespace
488  *
489  * Return 0 on success or -1 on failure.
490  *
491  * Raise TypeError if setting not modifiable in the current context, and
492  * LookupError if `key` doesn't name a known setting
493  */
494 static
SettingWrapperObjectAssignSubScript(PyObject * obj,PyObject * key,PyObject * val)495 int SettingWrapperObjectAssignSubScript(PyObject *obj, PyObject *key, PyObject *val){
496   auto& wobj = reinterpret_cast<SettingPropertyWrapperObject*>(obj)->wobj;
497 
498   if (!check_wrapper_scope(wobj)) {
499     return -1;
500   }
501 
502   int setting_id;
503   auto G = wobj->G;
504 
505   if (wobj->read_only){
506     PyErr_SetString(PyExc_TypeError, "Use alter/alter_state to modify settings");
507     return -1;
508   }
509 
510   if ((setting_id = get_and_check_setting_index(G, key)) == -1) {
511     return -1;
512   }
513 
514   if (wobj->idx >= 0) {
515     // atom-state level
516     if(!SettingLevelCheck(G, setting_id, cSettingLevel_astate)) {
517       PyErr_SetString(PyExc_TypeError,
518           "only atom-state level settings can be set in alter_state function");
519       return -1; // failure
520     } else if (CoordSetSetSettingFromPyObject(G, wobj->cs, wobj->idx, setting_id, val)) {
521     }
522   } else {
523     // atom level
524     if(!SettingLevelCheck(G, setting_id, cSettingLevel_atom)) {
525       PyErr_SetString(PyExc_TypeError,
526           "only atom-level settings can be set in alter function");
527       return -1; // failure
528     } else if (AtomInfoSetSettingFromPyObject(G, wobj->atomInfo, setting_id, val)) {
529       AtomInfoSettingGenerateSideEffects(G, wobj->obj, setting_id, wobj->atm);
530     }
531   }
532 
533   return 0; // success
534 }
535 
536 /*
537  * Python iterator over atom or atom-state setting indices
538  */
SettingWrapperObjectIter(PyObject * self)539 static PyObject* SettingWrapperObjectIter(PyObject *self)
540 {
541   auto& wobj = reinterpret_cast<SettingPropertyWrapperObject*>(self)->wobj;
542 
543   if (!check_wrapper_scope(wobj)) {
544     return NULL;
545   }
546 
547   int unique_id = wobj->atomInfo->unique_id;
548 
549   if (wobj->idx >= 0) {
550     unique_id =
551       wobj->cs->atom_state_setting_id ?
552       wobj->cs->atom_state_setting_id[wobj->idx] : 0;
553   }
554 
555   PyObject * items = SettingUniqueGetIndicesAsPyList(wobj->G, unique_id);
556   PyObject * iter = PyObject_GetIter(items);
557   Py_XDECREF(items);
558 
559   return iter;
560 }
561 
562 /*
563  * Allows attribute-like syntax for item lookups
564  *
565  * o.key -> o[key] if `key` is not an attribute of `o`
566  */
PyObject_GenericGetAttrOrItem(PyObject * o,PyObject * key)567 static PyObject* PyObject_GenericGetAttrOrItem(PyObject *o, PyObject *key) {
568   PyObject *ret = PyObject_GenericGetAttr(o, key);
569   if (!PyErr_Occurred())
570     return ret;
571   PyErr_Clear();
572   return PyObject_GetItem(o, key);
573 }
574 
575 /*
576  * Allows attribute-like syntax for item assignment
577  *
578  * `o.key = value` -> `o[key] = value`
579  */
580 static
PyObject_GenericSetAttrAsItem(PyObject * o,PyObject * key,PyObject * value)581 int PyObject_GenericSetAttrAsItem(PyObject *o, PyObject *key, PyObject *value) {
582   return PyObject_SetItem(o, key, value);
583 }
584 
585 /*
586  * iterate-family namespace implementation: lookup
587  *
588  * Raise NameError if state attributes are accessed outside of iterate_state
589  */
590 static
WrapperObjectSubScript(PyObject * obj,PyObject * key)591 PyObject * WrapperObjectSubScript(PyObject *obj, PyObject *key){
592 
593   static PyObject * pystr_HETATM        = PyString_InternFromString("HETATM");
594   static PyObject * pystr_ATOM          = PyString_InternFromString("ATOM");
595   static PyObject * pystr_QuestionMark  = PyString_InternFromString("?");
596 
597   WrapperObject *wobj = (WrapperObject*)obj;
598 
599   if (!check_wrapper_scope(wobj))
600     return NULL;
601 
602   PyMOLGlobals * G = wobj->G;
603   const char *aprop;
604   AtomPropertyInfo *ap;
605   PyObject *ret = NULL;
606   bool borrowed = false;
607   PyObject *keyobj = PyObject_Str(key);
608   aprop = PyString_AS_STRING(keyobj);
609   ap = PyMOL_GetAtomPropertyInfo(wobj->G->PyMOL, aprop);
610   Py_DECREF(keyobj);
611   if (ap){
612 #ifdef _PYMOL_IP_EXTRAS
613 #ifdef NO_MMLIBS
614     // static -> only show these warnings once
615     static bool warning_shown_text_type = false;
616 #endif
617 
618     switch (ap->id) {
619     case ATOM_PROP_STEREO:
620       if (ObjectMoleculeUpdateMMStereoInfoForState(G, wobj->obj, wobj->state - 1) < 0) {
621         PyErr_SetString(PyExc_RuntimeError,
622             "please install rdkit or set SCHRODINGER variable");
623         return NULL;
624       }
625       break;
626     case ATOM_PROP_TEXT_TYPE:
627 #ifndef NO_MMLIBS
628       ObjectMoleculeUpdateAtomTypeInfoForState(G, wobj->obj, wobj->state - 1, 1, 0);
629 #else
630       if (!warning_shown_text_type && !(wobj->cs && wobj->cs->validTextType)) {
631         warning_shown_text_type = true;
632         PRINTFB(G, FB_ObjectMolecule, FB_Warnings)
633           " Notice: Automatic 'text_type' assignment feature removed in PyMOL 2.0.\n" ENDFB(G);
634       }
635 #endif
636       break;
637     }
638 #endif
639 
640     switch (ap->Ptype){
641     case cPType_string:
642       {
643 	char *val = (char*)(((char*)wobj->atomInfo) + ap->offset);
644 	ret = PyString_FromString(val);
645       }
646       break;
647     case cPType_schar:
648       {
649 	signed char val = *(signed char*)(((char*)wobj->atomInfo) + ap->offset);
650 	ret = PyInt_FromLong((long)val);
651       }
652       break;
653     case cPType_int:
654       {
655 	int val = *(int*)(((char*)wobj->atomInfo) + ap->offset);
656 	ret = PyInt_FromLong((long)val);
657       }
658       break;
659     case cPType_int_as_string:
660       {
661         const char *st = LexStr(wobj->G,
662             *reinterpret_cast<lexidx_t*>
663             (((char*)wobj->atomInfo) + ap->offset));
664 	ret = PyString_FromString(st);
665       }
666       break;
667     case cPType_float:
668       {
669 	float val = *(float*)(((char*)wobj->atomInfo) + ap->offset);
670 	ret = PyFloat_FromDouble(val);
671       }
672       break;
673     case cPType_char_as_type:
674       {
675 	ret = wobj->atomInfo->hetatm ? pystr_HETATM : pystr_ATOM;
676 	borrowed = true;
677       }
678       break;
679     case cPType_model:
680       ret = PyString_FromString(wobj->obj->Name);
681       break;
682     case cPType_index:
683       {
684 	ret = PyInt_FromLong((long)wobj->atm + 1);
685       }
686       break;
687     case cPType_int_custom_type:
688       {
689 	int val = *(int*)(((char*)wobj->atomInfo) + ap->offset);
690 	if(val != cAtomInfoNoType){
691 	  ret = PyInt_FromLong((long)val);
692 	} else {
693 	  ret = pystr_QuestionMark;
694 	  borrowed = true;
695 	}
696       }
697       break;
698     case cPType_xyz_float:
699       {
700 	if (wobj->idx >= 0){
701 	  ret = PyFloat_FromDouble(wobj->cs->coordPtr(wobj->idx)[ap->offset]);
702 	} else {
703           PyErr_SetString(PyExc_NameError,
704               "x/y/z only available in iterate_state and alter_state");
705 	}
706       }
707       break;
708     case cPType_settings:
709       if (!wobj->settingWrapperObject) {
710         wobj->settingWrapperObject = PyType_GenericNew(&settingWrapper_Type, Py_None, Py_None);
711         reinterpret_cast<SettingPropertyWrapperObject *>(wobj->settingWrapperObject)->wobj = wobj;
712       }
713       ret = wobj->settingWrapperObject;
714       borrowed = true;
715       break;
716     case cPType_properties:
717       PyErr_SetString(PyExc_NotImplementedError,
718           "'properties/p' not supported in Open-Source PyMOL");
719       break;
720     case cPType_state:
721       ret = PyInt_FromLong((long)wobj->state);
722       break;
723     default:
724       switch (ap->id) {
725       case ATOM_PROP_RESI:
726         {
727           char resi[8];
728           AtomResiFromResv(resi, sizeof(resi), wobj->atomInfo);
729           ret = PyString_FromString(resi);
730         }
731         break;
732       case ATOM_PROP_STEREO:
733         {
734           auto mmstereotype = AtomInfoGetStereoAsStr(wobj->atomInfo);
735           ret = PyString_FromString(mmstereotype);
736         }
737         break;
738       case ATOM_PROP_ONELETTER:
739         {
740           const char * st = LexStr(G, wobj->atomInfo->resn);
741           char abbr[2] = {SeekerGetAbbr(G, st, 'O', 'X'), 0};
742           ret = PyString_FromString(abbr);
743         }
744         break;
745       default:
746         PyErr_SetString(PyExc_SystemError, "unhandled atom property type");
747       }
748     }
749   } else {
750     /* if not an atom property, check if local variable in dict */
751     if (wobj->dict) {
752       ret = PyDict_GetItem(wobj->dict, key);
753     }
754     if (ret) {
755       borrowed = true;
756     } else {
757       PyErr_SetNone(PyExc_KeyError);
758     }
759   }
760 
761   if (borrowed)
762     PXIncRef(ret);
763   return ret;
764 }
765 
766 /*
767  * iterate-family namespace implementation: assignment
768  *
769  * Raise TypeError for read-only variables
770  */
771 static
WrapperObjectAssignSubScript(PyObject * obj,PyObject * key,PyObject * val)772 int WrapperObjectAssignSubScript(PyObject *obj, PyObject *key, PyObject *val){
773   WrapperObject *wobj = (WrapperObject*)obj;
774 
775   if (!check_wrapper_scope(wobj)) {
776     return -1;
777   }
778   {
779     char aprop[16];
780     PyObject *keyobj = PyObject_Str(key);
781     UtilNCopy(aprop, PyString_AS_STRING(keyobj), sizeof(aprop));
782     Py_DECREF(keyobj);
783 
784     AtomPropertyInfo *ap = PyMOL_GetAtomPropertyInfo(wobj->G->PyMOL, aprop);
785 
786     if (ap){
787 #ifdef _PYMOL_IP_EXTRAS
788       if (wobj->cs) {
789         switch (ap->id) {
790         case ATOM_PROP_STEREO:
791           wobj->cs->validMMStereo = MMPYMOLX_PROP_STATE_USER;
792           break;
793         case ATOM_PROP_TEXT_TYPE:
794           wobj->cs->validTextType = MMPYMOLX_PROP_STATE_USER;
795           break;
796         }
797       }
798 #endif
799 
800       short changed = false;
801       if (wobj->read_only){
802         PyErr_SetString(PyExc_TypeError,
803             "Use alter/alter_state to modify values");
804 	return -1;
805       }
806 
807       // alter_state: must be setting x/y/z or flags
808       if (wobj->idx >= 0) {
809         if (ap->Ptype == cPType_xyz_float) {
810           float * v = wobj->cs->coordPtr(wobj->idx) + ap->offset;
811           PConvPyObjectToFloat(val, v);
812           return 0;
813         }
814       }
815 
816       switch (ap->Ptype){
817       case cPType_string:
818 	{
819           PyObject *valobj = PyObject_Str(val);
820 	  const char *valstr = PyString_AS_STRING(valobj);
821 	  char *dest = (char*)(((char*)wobj->atomInfo) + ap->offset);
822 	  if (strlen(valstr) > ap->maxlen){
823 	    strncpy(dest, valstr, ap->maxlen);
824 	  } else {
825 	    strcpy(dest, valstr);
826 	  }
827           Py_DECREF(valobj);
828 	  changed = true;
829 	}
830 	break;
831       case cPType_schar:
832 	{
833 	  int valint = PyInt_AsLong(val);
834 	  signed char *dest;
835 	  if (valint == -1 && PyErr_Occurred())
836 	    break;
837 	  dest = (signed char*)(((char*)wobj->atomInfo) + ap->offset);
838 	  *dest = valint;
839 	  changed = true;
840 	}
841         break;
842       case cPType_int:
843 	{
844 	  int valint = PyInt_AsLong(val);
845 	  int *dest;
846 	  if (valint == -1 && PyErr_Occurred())
847 	    break;
848 	  dest = (int*)(((char*)wobj->atomInfo) + ap->offset);
849 	  *dest = valint;
850 	  changed = true;
851 	}
852 	break;
853       case cPType_int_as_string:
854 	{
855           auto dest = reinterpret_cast<lexidx_t*>
856             (((char*)wobj->atomInfo) + ap->offset);
857           PyObject *valobj = PyObject_Str(val);
858 	  const char *valstr = PyString_AS_STRING(valobj);
859 	  LexDec(wobj->G, *dest);
860 	  *dest = LexIdx(wobj->G, valstr);
861           Py_DECREF(valobj);
862 	  changed = true;
863 	}
864 	break;
865       case cPType_float:
866 	{
867 	  float *dest = (float*)(((char*)wobj->atomInfo) + ap->offset);
868 	  changed = PConvPyObjectToFloat(val, dest);
869 	}
870 	break;
871       case cPType_char_as_type:
872 	{
873           PyObject *valobj = PyObject_Str(val);
874           const char *valstr = PyString_AS_STRING(valobj);
875           wobj->atomInfo->hetatm = ((valstr[0] == 'h') || (valstr[0] == 'H'));
876           Py_DECREF(valobj);
877 	  changed = true;
878 	}
879 	break;
880       case cPType_int_custom_type:
881 	{
882           PyObject *valobj = PyObject_Str(val);
883 	  const char *valstr = PyString_AS_STRING(valobj);
884 	  int *dest = (int*)(((char*)wobj->atomInfo) + ap->offset);
885 	  if (valstr[0] == '?'){
886 	    *dest = cAtomInfoNoType;
887 	  } else {
888 	    int valint = PyInt_AS_LONG(val);
889 	    *dest = valint;
890 	  }
891           Py_DECREF(valobj);
892 	  changed = true;
893 	}
894 	break;
895       case cPType_xyz_float:
896         PyErr_SetString(PyExc_NameError,
897             "x/y/z only available in alter_state");
898         return -1;
899       default:
900         switch (ap->id) {
901         case ATOM_PROP_RESI:
902           if (PConvPyIntToInt(val, &wobj->atomInfo->resv)) {
903             wobj->atomInfo->inscode = '\0';
904           } else {
905             PyObject *valobj = PyObject_Str(val);
906             wobj->atomInfo->setResi(PyString_AS_STRING(valobj));
907             Py_DECREF(valobj);
908           }
909           break;
910         case ATOM_PROP_STEREO:
911           {
912             PyObject *valobj = PyObject_Str(val);
913             const char *valstr = PyString_AS_STRING(valobj);
914             AtomInfoSetStereo(wobj->atomInfo, valstr);
915             Py_DECREF(valobj);
916           }
917           break;
918         default:
919           PyErr_Format(PyExc_TypeError, "'%s' is read-only", aprop);
920           return -1;
921         }
922       }
923       if (changed){
924 	switch (ap->id){
925 	case ATOM_PROP_ELEM:
926 	  wobj->atomInfo->protons = 0;
927 	  wobj->atomInfo->vdw = 0;
928 	  AtomInfoAssignParameters(wobj->G, wobj->atomInfo);
929 	  break;
930 	case ATOM_PROP_RESV:
931 	  wobj->atomInfo->inscode = '\0';
932 	  break;
933 	case ATOM_PROP_SS:
934 	  wobj->atomInfo->ssType[0] = toupper(wobj->atomInfo->ssType[0]);
935 	  break;
936 	case ATOM_PROP_FORMAL_CHARGE:
937 	  wobj->atomInfo->chemFlag = false;
938 	  break;
939 	}
940       }
941     } else {
942       /* if not an atom property, then its a local variable, store it */
943       if (!wobj->dict) {
944         wobj->dict = PyDict_New();
945       }
946       PyDict_SetItem(wobj->dict, key, val);
947     }
948   }
949   return 0; /* 0 success, -1 failure */
950 }
951 
952 /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
953 #ifdef WIN32
954 static PyObject *P_time = NULL;
955 static PyObject *P_sleep = NULL;
956 #endif
957 
958 /* END PROPRIETARY CODE SEGMENT */
959 
960 static void PUnlockAPIWhileBlocked(PyMOLGlobals * G);
961 static void PLockAPIWhileBlocked(PyMOLGlobals * G);
962 
963 #define P_log_file_str "_log_file"
964 
965 /**
966  * Acquire the status lock (blocking)
967  * @pre GIL
968  */
PLockStatus(PyMOLGlobals * G)969 void PLockStatus(PyMOLGlobals * G)
970 {                               /* assumes we have the GIL */
971   PXDecRef(PYOBJECT_CALLMETHOD(G->P_inst->lock_api_status, "acquire", nullptr));
972 }
973 
974 /**
975  * Acquire the status lock (non-blocking)
976  * @return True if lock could be acquired
977  * @pre GIL
978  */
PLockStatusAttempt(PyMOLGlobals * G)979 int PLockStatusAttempt(PyMOLGlobals * G)
980 {                               /* assumes we have the GIL */
981 
982   unique_PyObject_ptr got_lock(
983       PYOBJECT_CALLMETHOD(G->P_inst->lock_api_status, "acquire", "i", 0));
984 
985   ASSERT_PYOBJECT_NOT_NULL(got_lock, true);
986 
987   return PyObject_IsTrue(got_lock.get());
988 }
989 
990 /**
991  * Release the status lock
992  * @pre GIL
993  */
PUnlockStatus(PyMOLGlobals * G)994 void PUnlockStatus(PyMOLGlobals * G)
995 {                               /* assumes we have the GIL */
996   PXDecRef(PYOBJECT_CALLMETHOD(G->P_inst->lock_api_status, "release", nullptr));
997 }
998 
999 /**
1000  * Acquire GLUT lock
1001  * @pre GIL
1002  */
PLockGLUT(PyMOLGlobals * G)1003 static void PLockGLUT(PyMOLGlobals * G)
1004 {                               /* assumes we have the GIL */
1005   PXDecRef(PYOBJECT_CALLMETHOD(G->P_inst->lock_api_glut, "acquire", nullptr));
1006 }
1007 
1008 /**
1009  * Release GLUT lock
1010  * @pre GIL
1011  */
PUnlockGLUT(PyMOLGlobals * G)1012 static void PUnlockGLUT(PyMOLGlobals * G)
1013 {                               /* assumes we have the GIL */
1014   PXDecRef(PYOBJECT_CALLMETHOD(G->P_inst->lock_api_glut, "release", nullptr));
1015 }
1016 
1017 static long P_glut_thread_id = -1;
1018 
1019 
1020 /* enables us to keep glut out if by chance it grabs the API
1021  * in the middle of a nested API based operation */
1022 
1023 static
1024 void PCatchInit(void);
1025 
1026 
1027 /*
1028   PyObject *GetBondsDict(PyMOLGlobals *G)
1029   {
1030   PyObject *result = NULL;
1031   result = PyObject_GetAttrString(P_chempy,"bonds");
1032   if(!result) ErrMessage(G,"PyMOL","can't find 'chempy.bonds.bonds'");
1033   return(result);
1034   }
1035 */
1036 
PGetFontDict(PyMOLGlobals * G,float size,int face,int style)1037 PyObject *PGetFontDict(PyMOLGlobals * G, float size, int face, int style)
1038 {                               /* assumes we have a valid interpreter lock */
1039   PyObject *result = NULL;
1040 
1041   if(!P_vfont) {
1042     P_vfont = PyImport_ImportModule("pymol.vfont");
1043   }
1044   if(!P_vfont) {
1045     PRINTFB(G, FB_Python, FB_Errors)
1046       " PyMOL-Error: can't find module 'vfont'" ENDFB(G);
1047   } else {
1048     result = PYOBJECT_CALLMETHOD(P_vfont, "get_font", "fii", size, face, style);
1049   }
1050   return (PConvAutoNone(result));
1051 }
1052 
PComplete(PyMOLGlobals * G,char * str,int buf_size)1053 int PComplete(PyMOLGlobals * G, char *str, int buf_size)
1054 {
1055   int ret = false;
1056   PyObject *result;
1057   const char *st2;
1058   PBlockAndUnlockAPI(G);
1059   if(G->P_inst->complete) {
1060     result = PYOBJECT_CALLFUNCTION(G->P_inst->complete, "s", str);
1061     if(result) {
1062       if(PyString_Check(result)) {
1063         st2 = PyString_AsString(result);
1064         UtilNCopy(str, st2, buf_size);
1065         ret = true;
1066       }
1067       Py_DECREF(result);
1068     }
1069   }
1070   PLockAPIAndUnblock(G);
1071   return (ret);
1072 }
1073 
PTruthCallStr0(PyObject * object,const char * method)1074 int PTruthCallStr0(PyObject * object, const char *method)
1075 {
1076   int result = false;
1077   PyObject *tmp;
1078   tmp = PYOBJECT_CALLMETHOD(object, method, "");
1079   if(tmp) {
1080     if(PyObject_IsTrue(tmp))
1081       result = 1;
1082     Py_DECREF(tmp);
1083   }
1084   return (result);
1085 }
1086 
PTruthCallStr(PyObject * object,const char * method,const char * argument)1087 int PTruthCallStr(PyObject * object, const char *method, const char *argument)
1088 {
1089   int result = false;
1090   PyObject *tmp;
1091   tmp = PYOBJECT_CALLMETHOD(object, method, "s", argument);
1092   if(tmp) {
1093     if(PyObject_IsTrue(tmp))
1094       result = 1;
1095     Py_DECREF(tmp);
1096   }
1097   return (result);
1098 }
1099 
PTruthCallStr1i(PyObject * object,const char * method,int argument)1100 int PTruthCallStr1i(PyObject * object, const char *method, int argument)
1101 {
1102   int result = false;
1103   PyObject *tmp;
1104   tmp = PYOBJECT_CALLMETHOD(object, method, "i", argument);
1105   if(tmp) {
1106     if(PyObject_IsTrue(tmp))
1107       result = 1;
1108     Py_DECREF(tmp);
1109   }
1110   return (result);
1111 }
1112 
PTruthCallStr1s(PyObject * object,const char * method,const char * argument)1113 int PTruthCallStr1s(PyObject * object, const char *method, const char *argument)
1114 {
1115   int result = false;
1116   PyObject *tmp;
1117   tmp = PYOBJECT_CALLMETHOD(object, method, "s", argument);
1118   if(tmp) {
1119     if(PyObject_IsTrue(tmp))
1120       result = 1;
1121     Py_DECREF(tmp);
1122   }
1123   return (result);
1124 }
1125 
PTruthCallStr4i(PyObject * object,const char * method,int a1,int a2,int a3,int a4)1126 int PTruthCallStr4i(PyObject * object, const char *method, int a1, int a2, int a3, int a4)
1127 {
1128   int result = false;
1129   PyObject *tmp;
1130   tmp = PYOBJECT_CALLMETHOD(object, method, "iiii", a1, a2, a3, a4);
1131   if(tmp) {
1132     if(PyObject_IsTrue(tmp))
1133       result = 1;
1134     Py_DECREF(tmp);
1135   }
1136   return (result);
1137 }
1138 
PXIncRef(PyObject * obj)1139 PyObject *PXIncRef(PyObject * obj)
1140 {
1141   if(!obj)
1142     obj = Py_None;
1143   Py_XINCREF(obj);
1144   return obj;
1145 }
1146 
PXDecRef(PyObject * obj)1147 void PXDecRef(PyObject * obj)
1148 {
1149   Py_XDECREF(obj);
1150 }
1151 
CacheCreateEntry(PyObject ** result,PyObject * input)1152 static ov_status CacheCreateEntry(PyObject ** result, PyObject * input)
1153 {
1154   ov_status status = OV_STATUS_FAILURE;
1155   if(input && PyTuple_Check(input)) {
1156     ov_size tuple_size = PyTuple_Size(input);
1157     ov_size tot_size = tuple_size;
1158     PyObject *hash_code = PyTuple_New(tuple_size);
1159     PyObject *entry = PyList_New(6);
1160     if(hash_code && entry) {
1161       /* compute hash codes & total input size */
1162       ov_size i;
1163       status = OV_STATUS_SUCCESS;
1164       for(i = 0; i < tuple_size; i++) {
1165         PyObject *item = PyTuple_GetItem(input, i);
1166         long hash_long;
1167         if(item != Py_None) {
1168           /* here we are assuming that different Python versions will
1169            * hash tuples of ints & floats in exactly the same way (at least to 31 bits of significance) */
1170           hash_long = 0x7FFFFFFF & PyObject_Hash(item); /* pos 32 bit # to preserve 32-bit/64-bit compat */
1171         } else {
1172           hash_long = 0;        /* None doesn't hash consistently from Python version to version */
1173         }
1174         PyTuple_SetItem(hash_code, i, PyInt_FromLong(hash_long));
1175         if(PyTuple_Check(item)) {
1176           tot_size += PyTuple_Size(item);
1177         }
1178       }
1179       PyList_SetItem(entry, 0, PyInt_FromLong(tot_size));
1180       PyList_SetItem(entry, 1, hash_code);
1181       PyList_SetItem(entry, 2, PXIncRef(input));
1182       PyList_SetItem(entry, 3, PXIncRef(NULL));
1183       PyList_SetItem(entry, 4, PyInt_FromLong(0));      /* access count */
1184       PyList_SetItem(entry, 5, PyFloat_FromDouble(0.0));        /* timestamp */
1185     }
1186     if(!OV_OK(status)) {
1187       PXDecRef(hash_code);
1188       PXDecRef(entry);
1189     } else {
1190       *result = entry;
1191     }
1192   }
1193   if(PyErr_Occurred())
1194     PyErr_Print();
1195   return status;
1196 }
1197 
PCacheSet(PyMOLGlobals * G,PyObject * entry,PyObject * output)1198 ov_status PCacheSet(PyMOLGlobals * G, PyObject * entry, PyObject * output)
1199 {
1200   ov_status status = OV_STATUS_FAILURE;
1201   if(G->P_inst->cache && output) {
1202     ov_size tuple_size = PyTuple_Size(output);
1203     ov_size tot_size = tuple_size + PyInt_AsLong(PyList_GetItem(entry, 0));
1204     status = OV_STATUS_SUCCESS;
1205     {
1206       ov_size i;
1207       for(i = 0; i < tuple_size; i++) {
1208         PyObject *item = PyTuple_GetItem(output, i);
1209         if(PyTuple_Check(item)) {
1210           tot_size += PyTuple_Size(item);
1211         }
1212       }
1213     }
1214     PyList_SetItem(entry, 0, PyInt_FromLong(tot_size)); /* update total size */
1215     PyList_SetItem(entry, 3, PXIncRef(output));
1216     PXDecRef(PYOBJECT_CALLMETHOD(G->P_inst->cmd, "_cache_set",
1217                                  "OiO", entry, SettingGetGlobal_i(G, cSetting_cache_max),
1218                                  G->P_inst->cmd));
1219     /* compute the hash codes */
1220   }
1221   if(PyErr_Occurred())
1222     PyErr_Print();
1223   return status;
1224 }
1225 
PCacheGet(PyMOLGlobals * G,PyObject ** result_output,PyObject ** result_entry,PyObject * input)1226 ov_status PCacheGet(PyMOLGlobals * G,
1227                     PyObject ** result_output, PyObject ** result_entry, PyObject * input)
1228 {
1229   ov_status status = OV_STATUS_NO;
1230   if(G->P_inst->cache) {
1231     PyObject *entry = NULL;
1232     PyObject *output = NULL;
1233 
1234     if(OV_OK(CacheCreateEntry(&entry, input))) {
1235       output = PYOBJECT_CALLMETHOD(G->P_inst->cmd, "_cache_get",
1236                                    "OOO", entry, Py_None, G->P_inst->cmd);
1237       if(output == Py_None) {
1238         Py_DECREF(output);
1239         output = NULL;
1240       } else {
1241         status = OV_STATUS_YES;
1242       }
1243     }
1244     /* compute the hash codes */
1245     if(OV_OK(status)) {
1246       *result_entry = entry;
1247       *result_output = output;
1248     } else {
1249       PXDecRef(entry);
1250       PXDecRef(output);
1251     }
1252   }
1253   if(PyErr_Occurred())
1254     PyErr_Print();
1255   return status;
1256 }
1257 
PSleepWhileBusy(PyMOLGlobals * G,int usec)1258 void PSleepWhileBusy(PyMOLGlobals * G, int usec)
1259 {
1260 #ifndef WIN32
1261   struct timeval tv;
1262   PRINTFD(G, FB_Threads)
1263     " PSleep-DEBUG: napping.\n" ENDFD;
1264   tv.tv_sec = 0;
1265   tv.tv_usec = usec;
1266   select(0, NULL, NULL, NULL, &tv);
1267   PRINTFD(G, FB_Threads)
1268     " PSleep-DEBUG: nap over.\n" ENDFD;
1269 #else
1270   /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
1271   PBlock(G);
1272   PXDecRef(PYOBJECT_CALLFUNCTION(P_sleep, "f", usec / 1000000.0));
1273   PUnblock(G);
1274   /* END PROPRIETARY CODE SEGMENT */
1275 #endif
1276 }
1277 
PSleepUnlocked(PyMOLGlobals * G,int usec)1278 void PSleepUnlocked(PyMOLGlobals * G, int usec)
1279 {                               /* can only be called by the glut process */
1280 #ifndef WIN32
1281   struct timeval tv;
1282   PRINTFD(G, FB_Threads)
1283     " PSleep-DEBUG: napping.\n" ENDFD;
1284   tv.tv_sec = 0;
1285   tv.tv_usec = usec;
1286   select(0, NULL, NULL, NULL, &tv);
1287   PRINTFD(G, FB_Threads)
1288     " PSleep-DEBUG: nap over.\n" ENDFD;
1289 #else
1290   /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
1291   PBlock(G);
1292   PXDecRef(PYOBJECT_CALLFUNCTION(P_sleep, "f", usec / 1000000.0));
1293   PUnblock(G);
1294   /* END PROPRIETARY CODE SEGMENT */
1295 #endif
1296 }
1297 
PSleep(PyMOLGlobals * G,int usec)1298 void PSleep(PyMOLGlobals * G, int usec)
1299 {                               /* can only be called by the glut process */
1300 #ifndef WIN32
1301   struct timeval tv;
1302   PUnlockAPIAsGlut(G);
1303   PRINTFD(G, FB_Threads)
1304     " PSleep-DEBUG: napping.\n" ENDFD;
1305   tv.tv_sec = 0;
1306   tv.tv_usec = usec;
1307   select(0, NULL, NULL, NULL, &tv);
1308   PRINTFD(G, FB_Threads)
1309     " PSleep-DEBUG: nap over.\n" ENDFD;
1310   PLockAPIAsGlut(G, true);
1311 #else
1312   /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
1313   PBlockAndUnlockAPI(G);
1314   PXDecRef(PYOBJECT_CALLFUNCTION(P_sleep, "f", usec / 1000000.0));
1315   PLockAPIAndUnblock(G);
1316   /* END PROPRIETARY CODE SEGMENT */
1317 #endif
1318 
1319 }
1320 
1321 static PyObject *PCatchWrite(PyObject * self, PyObject * args);
1322 static PyObject *PCatch_install(PyObject * self, PyObject * args);
1323 
1324 static
my_interrupt(int a)1325 void my_interrupt(int a)
1326 {
1327   exit(EXIT_FAILURE);
1328 }
1329 
PDumpTraceback(PyObject * err)1330 void PDumpTraceback(PyObject * err)
1331 {
1332   PYOBJECT_CALLMETHOD(P_traceback, "print_tb", "O", err);
1333 }
1334 
PDumpException()1335 void PDumpException()
1336 {
1337   PYOBJECT_CALLMETHOD(P_traceback, "print_exc", "");
1338 }
1339 
1340 static
WrapperObjectNew()1341 WrapperObject * WrapperObjectNew() {
1342   auto wobj = (WrapperObject *)PyType_GenericNew(&Wrapper_Type, Py_None, Py_None);
1343   wobj->dict = NULL;
1344   wobj->settingWrapperObject = NULL;
1345 #ifdef _PYMOL_IP_PROPERTIES
1346   wobj->propertyWrapperObject = NULL;
1347 #endif
1348   return wobj;
1349 }
1350 
PAlterAtomState(PyMOLGlobals * G,PyCodeObject * expr_co,int read_only,ObjectMolecule * obj,CoordSet * cs,int atm,int idx,int state,PyObject * space)1351 int PAlterAtomState(PyMOLGlobals * G, PyCodeObject *expr_co, int read_only,
1352                     ObjectMolecule *obj, CoordSet *cs, int atm, int idx,
1353                     int state, PyObject * space)
1354 
1355 /* assumes Blocked python interpreter */
1356 {
1357   int result = true;
1358 
1359   auto wobj = WrapperObjectNew();
1360   wobj->G = G;
1361   wobj->obj = obj;
1362   wobj->cs = cs;
1363   wobj->atomInfo = obj->AtomInfo + atm;
1364   wobj->atm = atm;
1365   wobj->idx = idx;
1366   wobj->read_only = read_only;
1367   wobj->state = state + 1;
1368 
1369   PXDecRef(PyEval_EvalCode(expr_co, space, (PyObject*)wobj));
1370   WrapperObjectReset(wobj);
1371 
1372   if(PyErr_Occurred()) {
1373     PyErr_Print();
1374     result = false;
1375   }
1376   return result;
1377 }
1378 
PAlterAtom(PyMOLGlobals * G,ObjectMolecule * obj,CoordSet * cs,PyCodeObject * expr_co,int read_only,int atm,PyObject * space)1379 int PAlterAtom(PyMOLGlobals * G,
1380                ObjectMolecule *obj, CoordSet *cs, PyCodeObject *expr_co, int read_only,
1381                int atm, PyObject * space)
1382 {
1383   int state = (obj->DiscreteFlag ? obj->AtomInfo[atm].discrete_state : 0) - 1;
1384   return PAlterAtomState(G, expr_co, read_only, obj, cs, atm, /* idx */ -1, state, space);
1385 }
1386 
1387 /*
1388  * String conversion which takes "label_digits" setting into account.
1389  */
1390 static
PLabelPyObjectToStrMaxLen(PyMOLGlobals * G,PyObject * obj,char * buffer,int maxlen)1391 int PLabelPyObjectToStrMaxLen(PyMOLGlobals * G, PyObject * obj, char *buffer, int maxlen)
1392 {
1393   if (obj && PyFloat_Check(obj)) {
1394     snprintf(buffer, maxlen + 1, "%.*f",
1395         SettingGetGlobal_i(G, cSetting_label_digits),
1396         PyFloat_AsDouble(obj));
1397     return true;
1398   }
1399   return PConvPyObjectToStrMaxLen(obj, buffer, maxlen);
1400 }
1401 
PLabelAtom(PyMOLGlobals * G,ObjectMolecule * obj,CoordSet * cs,PyCodeObject * expr_co,int atm)1402 int PLabelAtom(PyMOLGlobals * G, ObjectMolecule *obj, CoordSet *cs, PyCodeObject *expr_co, int atm)
1403 {
1404   int result = true;
1405   PyObject *P_inst_dict = G->P_inst->dict;
1406   PyObject *resultPyObject;
1407   OrthoLineType label;
1408   AtomInfoType * ai = obj->AtomInfo + atm;
1409 
1410   if (!expr_co){
1411     // unsetting label
1412     LexAssign(G, ai->label, 0);
1413     return true;
1414   }
1415 
1416   auto wobj = WrapperObjectNew();
1417   wobj->G = G;
1418   wobj->obj = obj;
1419   wobj->cs = cs;
1420   wobj->atomInfo = ai;
1421   wobj->atm = atm;
1422   wobj->idx = -1;
1423   wobj->read_only = true;
1424 
1425   if (obj->DiscreteFlag) {
1426     wobj->state = obj->AtomInfo[atm].discrete_state;
1427   } else {
1428     wobj->state = 0;
1429   }
1430 
1431   resultPyObject = PyEval_EvalCode(expr_co, P_inst_dict, (PyObject*)wobj);
1432   WrapperObjectReset(wobj);
1433 
1434   if(PyErr_Occurred()) {
1435     PyErr_Print();
1436     result = false;
1437   } else {
1438     result = true;
1439     if(!PLabelPyObjectToStrMaxLen(G, resultPyObject,
1440                                  label, sizeof(OrthoLineType) - 1))
1441       result = false;
1442     if(PyErr_Occurred()) {
1443       PyErr_Print();
1444       result = false;
1445     }
1446     if(result) {
1447       LexAssign(G, ai->label, label);
1448     } else {
1449       ErrMessage(G, "Label", "Aborting on error. Labels may be incomplete.");
1450     }
1451   }
1452   PXDecRef(resultPyObject);
1453   return (result);
1454 }
1455 
1456 /**
1457  * - Release API lock with flushing
1458  * - Pop valid context
1459  * - Release GLUT lock
1460  * @pre no GIL
1461  */
PUnlockAPIAsGlut(PyMOLGlobals * G)1462 void PUnlockAPIAsGlut(PyMOLGlobals * G)
1463 {                               /* must call with unblocked interpreter */
1464   PBlock(G);
1465   PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->unlock, "iO", 0, G->P_inst->cmd));  /* NOTE this may flush the command buffer! */
1466   PLockStatus(G);
1467   PyMOL_PopValidContext(G->PyMOL);
1468   PUnlockStatus(G);
1469   PUnlockGLUT(G);
1470   PUnblock(G);
1471 }
1472 
1473 /**
1474  * - Release API lock without flushing
1475  * - Pop valid context
1476  * - Release GLUT lock
1477  * @pre no GIL
1478  */
PUnlockAPIAsGlutNoFlush(PyMOLGlobals * G)1479 void PUnlockAPIAsGlutNoFlush(PyMOLGlobals * G)
1480 {                               /* must call with unblocked interpreter */
1481   PBlock(G);
1482   PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->unlock, "iO", -1, G->P_inst->cmd)); /* prevents flushing of the buffer */
1483   PLockStatus(G);
1484   PyMOL_PopValidContext(G->PyMOL);
1485   PUnlockStatus(G);
1486   PUnlockGLUT(G);
1487   PUnblock(G);
1488 }
1489 
1490 /**
1491  * Acquire API lock
1492  * @param block_if_busy If false and PyMOL is busy, do not block
1493  * @return False if API lock could not be acquired
1494  * @pre GIL
1495  */
get_api_lock(PyMOLGlobals * G,int block_if_busy)1496 static int get_api_lock(PyMOLGlobals * G, int block_if_busy)
1497 {
1498   if (!block_if_busy) {
1499     unique_PyObject_ptr got_lock(
1500         PYOBJECT_CALLFUNCTION(G->P_inst->lock_attempt, "O", G->P_inst->cmd));
1501 
1502     ASSERT_PYOBJECT_NOT_NULL(got_lock, false);
1503 
1504     if (PyObject_IsTrue(got_lock.get())) {
1505       return true;
1506     }
1507 
1508     PLockStatus(G);
1509     bool busy = PyMOL_GetBusy(G->PyMOL, false);
1510     PUnlockStatus(G);
1511 
1512     if (busy) {
1513       return false;
1514     }
1515   }
1516 
1517   PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->lock, "O", G->P_inst->cmd));
1518   return true;
1519 }
1520 
1521 /**
1522  * - Acquire GLUT lock
1523  * - Push valid context
1524  * - Acquire API lock
1525  * - Wait for glut_thread_keep_out == 0
1526  *
1527  * @param block_if_busy If false and PyMOL is busy, API lock is non-blocking
1528  * @return False if locks could not be acquired
1529  * @pre no GIL
1530  */
PLockAPIAsGlut(PyMOLGlobals * G,int block_if_busy)1531 int PLockAPIAsGlut(PyMOLGlobals * G, int block_if_busy)
1532 {
1533   PBlock(G);
1534 
1535   PLockGLUT(G);
1536 
1537   PLockStatus(G);
1538   PyMOL_PushValidContext(G->PyMOL);
1539   PUnlockStatus(G);
1540 
1541   while (true) {
1542     if(!get_api_lock(G, block_if_busy)) {
1543       PLockStatus(G);
1544       PyMOL_PopValidContext(G->PyMOL);
1545       PUnlockStatus(G);
1546       PUnlockGLUT(G);
1547       PUnblock(G);
1548       return false;               /* busy -- so allow main to update busy status display (if any) */
1549     }
1550 
1551     if (G->P_inst->glut_thread_keep_out == 0) {
1552       break;
1553     }
1554 
1555     /* IMPORTANT: keeps the glut thread out of an API operation... */
1556     /* NOTE: the keep_out variable can only be changed or read by the thread
1557        holding the API lock, therefore it is safe even through increment
1558        isn't atomic. */
1559 
1560     PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->unlock, "iO", -1, G->P_inst->cmd));       /* prevent buffer flushing */
1561 #ifndef WIN32
1562     {
1563       struct timeval tv;
1564 
1565       PUnblock(G);
1566       tv.tv_sec = 0;
1567       tv.tv_usec = 50000;
1568       select(0, NULL, NULL, NULL, &tv);
1569       PBlock(G);
1570     }
1571 #else
1572     /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
1573     PXDecRef(PYOBJECT_CALLFUNCTION(P_sleep, "f", 0.050));
1574     /* END PROPRIETARY CODE SEGMENT */
1575 #endif
1576   }
1577 
1578   PUnblock(G);                  /* API is now locked, so we can free up Python... */
1579 
1580   return true;
1581 }
1582 
1583 
1584 /* THESE CALLS ARE REQUIRED FOR MONOLITHIC COMPILATION TO SUCCEED UNDER WINDOWS. */
1585 #ifndef _PYMOL_EMBEDDED
1586 
1587 #ifdef __cplusplus
1588 extern "C" {
1589 #endif
1590 
1591 /*
1592  *  void        initExtensionClass(void);
1593  *   void        initsglite(void);
1594  */
1595 void init_champ(void);
1596 
1597 #ifdef __cplusplus
1598 }
1599 #endif
1600 #endif
1601 
1602 #ifdef _PYMOL_MONOLITHIC
1603 #ifndef _PYMOL_EMBEDDED
1604 
1605 /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
1606 #ifdef WIN32
1607 #ifdef _PYMOL_NUMPY_INIT
1608 void init_numpy();
1609 void initmultiarray();
1610 void initarrayfns();
1611 void initlapack_lite();
1612 void initumath();
1613 void initranlib();
1614 #endif
1615 #endif
1616 
1617 /* END PROPRIETARY CODE SEGMENT */
1618 #endif
1619 #endif
1620 
1621 #ifdef _PYMOL_MONOLITHIC
1622 #ifndef _PYMOL_EMBEDDED
1623 #ifdef __cplusplus
1624 extern "C" {
1625 #endif
1626 
1627 /*
1628  * void        initExtensionClass(void);
1629  * void        initsglite(void);
1630  */
1631 void init_champ(void);
1632 #ifdef _PYMOL_PYOMM
1633 void init_pyomm(void);
1634 #endif
1635 
1636 #ifdef __cplusplus
1637 }
1638 #endif
1639 #endif
1640 #endif
1641 
1642 
1643 /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
1644 #if 0 // def WIN32
1645 static int IsSecurityRequired()
1646 {
1647   DWORD WindowsVersion = GetVersion();
1648   DWORD WindowsMajorVersion = (DWORD) (LOBYTE(LOWORD(WindowsVersion)));
1649   DWORD WindowsMinorVersion = (DWORD) (HIBYTE(LOWORD(WindowsVersion)));
1650 
1651   if(WindowsVersion >= 0x80000000)
1652     return FALSE;
1653 
1654   return TRUE;
1655 }
1656 #endif
1657 
1658 /* END PROPRIETARY CODE SEGMENT */
1659 
PSetupEmbedded(PyMOLGlobals * G,int argc,char ** argv)1660 void PSetupEmbedded(PyMOLGlobals * G, int argc, char **argv)
1661 {
1662   /* This routine is called if we are running with an embedded Python interpreter */
1663   PyObject *args;
1664 
1665   /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
1666 #if 0 // def WIN32
1667 
1668   /* Windows PyMOL now ships with Python 2.5 for both
1669      32 and 64 bit */
1670 
1671 #ifndef EMBEDDED_PYTHONHOME
1672 #define EMBEDDED_PYTHONHOME "\\py27"
1673 #endif
1674 
1675   {                             /* Automatically hide the window if this process was started as a
1676                                    vanilla console application (by double-clicking).
1677                                    Conveniently, this doesn't hide the window when launched from a
1678                                    command window. */
1679     HWND hwndFound;
1680     if(hwndFound = FindWindowA(NULL, argv[0])) {
1681       ShowWindow(hwndFound, SW_HIDE);
1682     }
1683   }
1684 
1685   {                             /* if PYMOL_PATH and/or PYTHONHOME isn't in the environment coming
1686                                    in, then the user may simply have clicked PyMOL.exe, in which
1687                                    case we need to consult the registry regarding the location of
1688                                    the install */
1689 
1690     static char line1[8092];
1691     static char line2[8092];
1692 
1693     {                           /* If no PYMOL_PATH specific, but we were launched with an
1694                                  * absolute path, then try using that path first.  With embedded
1695                                  * builds, the .EXE should always be located at the root of
1696                                  * $PYMOL_PATH */
1697 
1698       char *pymol_path = getenv("PYMOL_PATH");
1699       if((!pymol_path) && (argc > 0) && argv[0][0] && (argv[0][1] == ':')
1700          && (argv[0][2] == '\\')) {
1701 
1702         char *p;
1703         strcpy(line1, "PYMOL_PATH=");
1704         strcat(line1, argv[0]);
1705         p = line1 + strlen(line1);
1706         while(*p != '\\') {
1707           *p = 0;
1708           p--;
1709         }
1710         *p = 0;
1711         putenv(line1);
1712       }
1713     }
1714 
1715     {
1716       OrthoLineType path_buffer;
1717       HKEY phkResult;
1718       DWORD lpcbData;
1719       DWORD lpType = REG_SZ;
1720       int r1, r2;
1721       char *pymol_path;
1722       char *pythonhome;
1723       int pythonhome_set = false;
1724       int restart_flag = false;
1725 
1726       pymol_path = getenv("PYMOL_PATH");
1727       pythonhome = getenv("PYTHONHOME");
1728       if((!pymol_path) || (!pythonhome)) {
1729         lpcbData = sizeof(OrthoLineType) - 1;
1730         r1 = RegOpenKeyExA(HKEY_CLASSES_ROOT,
1731 #ifdef PYMOL_EVAL
1732 			"Software\\PyMOL\\PyMOL Eval\\PYMOL_PATH",
1733 #else
1734 			"Software\\PyMOL\\PyMOL\\PYMOL_PATH",
1735 #endif
1736                           0, KEY_EXECUTE, &phkResult);
1737         if(r1 != ERROR_SUCCESS) {
1738           r1 = RegOpenKeyExA(HKEY_CURRENT_USER,
1739 #ifdef PYMOL_EVAL
1740 			"Software\\PyMOL\\PyMOL Eval\\PYMOL_PATH",
1741 #else
1742 			"Software\\PyMOL\\PyMOL\\PYMOL_PATH",
1743 #endif
1744                             0, KEY_EXECUTE, &phkResult);
1745         }
1746         if(r1 == ERROR_SUCCESS) {
1747           r2 = RegQueryValueExA(phkResult, "", NULL, &lpType, (LPBYTE) path_buffer, &lpcbData);
1748           if(r2 == ERROR_SUCCESS) {
1749             /* use environment variable PYMOL_PATH first, registry entry
1750                second */
1751             if(!pymol_path) {
1752               strcpy(line1, "PYMOL_PATH=");
1753               strcat(line1, path_buffer);
1754               _putenv(line1);
1755               if(!pythonhome) { /* only set PYTHONHOME if already
1756                                    setting new PYMOL_PATH */
1757                 pythonhome_set = true;
1758                 strcpy(line2, "PYTHONHOME=");
1759                 strcat(line2, path_buffer);
1760                 strcat(line2, EMBEDDED_PYTHONHOME);
1761                 restart_flag = true;
1762                 _putenv(line2);
1763               }
1764             }
1765           }
1766           RegCloseKey(phkResult);
1767         }
1768         /* this allows us to just specify PYMOL_PATH with no registry entries */
1769         if((!pythonhome_set) && (pymol_path) && (!pythonhome)) {
1770           strcpy(line2, "PYTHONHOME=");
1771           strcat(line2, pymol_path);
1772           strcat(line2, EMBEDDED_PYTHONHOME);
1773           _putenv(line2);
1774           restart_flag = true;
1775         }
1776       }
1777       if(restart_flag && getenv("PYMOL_PATH") && getenv("PYTHONHOME")) {
1778 
1779         /* now that we have the environment defined, restart the process
1780          * so that Python can use the new environment.  If we don't do
1781          * this, then Python won't see the new environment vars. Why not? */
1782 
1783         /* note that we use CreateProcesss to launch the console
1784          * application instead of exec or spawn in order to hide the
1785          * console window. Otherwise a console window might appear, and
1786          * that would suck. */
1787 
1788         char command[8092];
1789         static char cmd_line[8092];
1790         char *p, *q;
1791         int a;
1792 
1793         /* copy arguments, installing quotes around them */
1794 
1795         sprintf(command, "%s\\pymol.exe", getenv("PYMOL_PATH"));
1796         p = cmd_line;
1797 
1798         sprintf(p, "\"%s\"", command);
1799         p += strlen(p);
1800         *(p++) = ' ';
1801         *p = 0;
1802 
1803         for(a = 1; a <= argc; a++) {
1804           q = argv[a];
1805           if(q) {
1806             if(*q != '"') {     /* add quotes if not present */
1807               *(p++) = '"';
1808               while(*q) {
1809                 *(p++) = *(q++);
1810               }
1811               *(p++) = '"';
1812             } else {
1813               while(*q) {
1814                 *(p++) = *(q++);
1815               }
1816             }
1817             *(p++) = 32;
1818             *p = 0;
1819           }
1820         }
1821 
1822         {
1823           LPSECURITY_ATTRIBUTES lpSA = NULL;
1824           PSECURITY_DESCRIPTOR lpSD = NULL;
1825           STARTUPINFOA si;
1826           PROCESS_INFORMATION pi;
1827           HANDLE hProcess = GetCurrentProcess();
1828 
1829           ZeroMemory(&si, sizeof(STARTUPINFOA));
1830           si.cb = sizeof(STARTUPINFOA);
1831           si.dwFlags = STARTF_USESHOWWINDOW;
1832           si.wShowWindow = SW_HIDE;
1833 
1834           if(IsSecurityRequired()) {
1835             lpSD = GlobalAlloc(GPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
1836             InitializeSecurityDescriptor(lpSD, SECURITY_DESCRIPTOR_REVISION);
1837             SetSecurityDescriptorDacl(lpSD, -1, 0, 0);
1838 
1839             lpSA = (LPSECURITY_ATTRIBUTES) GlobalAlloc(GPTR, sizeof(SECURITY_ATTRIBUTES));
1840             lpSA->nLength = sizeof(SECURITY_ATTRIBUTES);
1841             lpSA->lpSecurityDescriptor = lpSD;
1842             lpSA->bInheritHandle = TRUE;
1843           }
1844 
1845           if(CreateProcessA(NULL, (LPSTR) cmd_line, lpSA, NULL, TRUE,
1846                             0, NULL, NULL, &si, &pi)) {
1847 
1848             WaitForSingleObject(pi.hProcess, INFINITE);
1849           } else {
1850             printf("ERROR: Unable to restart PyMOL process with new environment:\n");
1851             system("set");      /* dump the environment. */
1852             printf("CreateProcess failed, code %d: %s\n", GetLastError(), cmd_line);
1853             printf("PyMOL will now terminate.\n");
1854           }
1855 
1856           if(lpSA != NULL)
1857             GlobalFree(lpSA);
1858           if(lpSD != NULL)
1859             GlobalFree(lpSD);
1860           _exit(0);
1861         }
1862       }
1863     }
1864   }
1865 #endif
1866   /* END PROPRIETARY CODE SEGMENT */
1867 
1868   /* compatibility for old compile-time defines */
1869 
1870 #ifdef _PYMOL_SETUP_PY21
1871 #ifndef _PYMOL_SETUP_PY_EXT
1872 #define _PYMOL_SETUP_PY_EXT
1873 #endif
1874 #endif
1875 #ifdef _PYMOL_SETUP_PY22
1876 #ifndef _PYMOL_SETUP_PY_EXT
1877 #define _PYMOL_SETUP_PY_EXT
1878 #endif
1879 #endif
1880 #ifdef _PYMOL_SETUP_PY23
1881 #ifndef _PYMOL_SETUP_PY_EXT
1882 #define _PYMOL_SETUP_PY_EXT
1883 #endif
1884 #endif
1885 #ifdef _PYMOL_SETUP_PY24
1886 #ifndef _PYMOL_SETUP_PY_EXT
1887 #define _PYMOL_SETUP_PY_EXT
1888 #endif
1889 #endif
1890 #ifdef _PYMOL_SETUP_PY25
1891 #ifndef _PYMOL_SETUP_PY_EXT
1892 #define _PYMOL_SETUP_PY_EXT
1893 #endif
1894 #endif
1895 #ifdef _PYMOL_SETUP_PY26
1896 #ifndef _PYMOL_SETUP_PY_EXT
1897 #define _PYMOL_SETUP_PY_EXT
1898 #endif
1899 #endif
1900 
1901   /* should we set up PYTHONHOME in the ext directory? */
1902 
1903 #ifdef _PYMOL_SETUP_PY_EXT
1904   {
1905     static char line1[8092];
1906     static char line2[8092];
1907     if(!getenv("PYMOL_PATH")) { /* if PYMOL_PATH isn't defined... */
1908 
1909       /* was our startup path absolute? */
1910 
1911       if((argc > 0) && (argv[0][0] == '/')) {
1912         /* PYMOL was started with an absolute path, so try using that... */
1913         char *p;
1914         strcpy(line1, "PYMOL_PATH=");
1915         strcat(line1, argv[0]);
1916         p = line1 + strlen(line1);
1917         while(*p != '/') {
1918           *p = 0;
1919           p--;
1920         }
1921         *p = 0;
1922         putenv(line1);
1923       } else if((argc > 0) && getenv("PWD")
1924                 && ((argv[0][0] == '.') || (strstr(argv[0], "/")))) {
1925         /* was the path relative? */
1926         char *p;
1927         strcpy(line1, "PYMOL_PATH=");
1928         strcat(line1, getenv("PWD"));
1929         strcat(line1, "/");
1930         strcat(line1, argv[0]);
1931         p = line1 + strlen(line1);
1932         while(*p != '/') {
1933           *p = 0;
1934           p--;
1935         }
1936         *p = 0;
1937         putenv(line1);
1938       } else {                  /* otherwise, just try using the current working directory */
1939         if(getenv("PWD")) {
1940           strcpy(line1, "PYMOL_PATH=");
1941           strcat(line1, getenv("PWD"));
1942           putenv(line1);
1943         }
1944       }
1945     }
1946 
1947     /* now set PYTHONHOME so that we use the right binary libraries for
1948        this executable */
1949 
1950     if(getenv("PYMOL_PATH")) {
1951       strcpy(line2, "PYTHONHOME=");
1952       strcat(line2, getenv("PYMOL_PATH"));
1953       strcat(line2, "/ext");
1954       putenv(line2);
1955     }
1956   }
1957 #endif
1958 
1959 #ifndef _PYMOL_EMBEDDED
1960   Py_Initialize();
1961   PyEval_InitThreads();
1962 #endif
1963 
1964   init_cmd();
1965 
1966 #ifdef _PYMOL_MONOLITHIC
1967 #ifndef _PYMOL_EMBEDDED
1968   /*
1969    * initExtensionClass();
1970    * initsglite();
1971    */
1972   /* initialize champ */
1973   init_champ();
1974 
1975 #ifdef _PYMOL_PYOMM
1976   init_pyomm();
1977 #endif
1978 
1979   /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
1980 #ifdef WIN32
1981 #ifdef _PYMOL_NUMPY_INIT
1982   /* initialize numeric python */
1983   init_numpy();
1984   initmultiarray();
1985   initarrayfns();
1986   initlapack_lite();
1987   initumath();
1988   initranlib();
1989 #endif
1990 #endif
1991 
1992   /* END PROPRIETARY CODE SEGMENT */
1993 #endif
1994 #endif
1995 
1996   PyRun_SimpleString("import os\n");
1997   PyRun_SimpleString("import sys\n");
1998   /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
1999 #ifdef WIN32
2000   {
2001     /* getenv('PYMOL_PATH') and os.environ['PYMOL_PATH'] aren't
2002        automatically synchronized on Windows, so here we do the job
2003        manually... */
2004 
2005     char *pymol_path = getenv("PYMOL_PATH");
2006     if(pymol_path) {
2007       PyObject *os = PyImport_AddModule("os");  /* borrowed ref */
2008       char *buffer = pymol::malloc<char>(strlen(pymol_path) + 100);
2009       if(os && buffer) {
2010         PyObject *envir = PyObject_GetAttrString(os, "environ");
2011         if(envir) {
2012           if(!PTruthCallStr1s(envir, "__contains__", "PYMOL_PATH")) {
2013             sprintf(buffer, "os.environ['PYMOL_PATH']=r'''%s'''\n", pymol_path);
2014             PyRun_SimpleString(buffer);
2015           }
2016         }
2017         PXDecRef(envir);
2018       }
2019       FreeP(buffer);
2020     }
2021   }
2022   /* ultimate fallback -- try using the current working directory */
2023   PyRun_SimpleString
2024     ("if 'PYMOL_PATH' not in os.environ: os.environ['PYMOL_PATH']=os.getcwd()\n");
2025 #endif
2026   /* END PROPRIETARY CODE SEGMENT */
2027 
2028   P_main = PyImport_AddModule("__main__");
2029   if(!P_main)
2030     ErrFatal(G, "PyMOL", "can't find '__main__'");
2031 
2032   args = PConvStringListToPyList(argc, argv);   /* prepare our argument list */
2033   if(!args)
2034     ErrFatal(G, "PyMOL", "can't process arguments.");
2035 
2036   /* copy arguments to __main__.pymol_argv */
2037   PyObject_SetAttrString(P_main, "pymol_argv", args);
2038   PyRun_SimpleString
2039     ("import __main__\nif not hasattr(sys,'argv'): sys.argv=__main__.pymol_argv");
2040 
2041   PyRun_SimpleString("if (os.environ['PYMOL_PATH']+'/modules') not in sys.path: sys.path.insert(0,os.environ['PYMOL_PATH']+'/modules')\n");     /* needed for semistatic pymol */
2042 }
2043 
PConvertOptions(CPyMOLOptions * rec,PyObject * options)2044 void PConvertOptions(CPyMOLOptions * rec, PyObject * options)
2045 {
2046   const char *load_str;
2047 
2048   rec->pmgui = !PyInt_AsLong(PyObject_GetAttrString(options, "no_gui"));
2049   rec->internal_gui = PyInt_AsLong(PyObject_GetAttrString(options, "internal_gui"));
2050   rec->internal_feedback =
2051     PyInt_AsLong(PyObject_GetAttrString(options, "internal_feedback"));
2052   rec->show_splash = PyInt_AsLong(PyObject_GetAttrString(options, "show_splash"));
2053   rec->security = PyInt_AsLong(PyObject_GetAttrString(options, "security"));
2054   rec->game_mode = PyInt_AsLong(PyObject_GetAttrString(options, "game_mode"));
2055   rec->force_stereo = PyInt_AsLong(PyObject_GetAttrString(options, "force_stereo"));
2056   rec->winX = PyInt_AsLong(PyObject_GetAttrString(options, "win_x"));
2057   rec->winY = PyInt_AsLong(PyObject_GetAttrString(options, "win_y"));
2058   rec->winPX = PyInt_AsLong(PyObject_GetAttrString(options, "win_px"));
2059   rec->winPY = PyInt_AsLong(PyObject_GetAttrString(options, "win_py"));
2060   rec->blue_line = PyInt_AsLong(PyObject_GetAttrString(options, "blue_line"));
2061   rec->external_gui = PyInt_AsLong(PyObject_GetAttrString(options, "external_gui"));
2062   rec->siginthand = PyInt_AsLong(PyObject_GetAttrString(options, "sigint_handler"));
2063   rec->reuse_helper = PyInt_AsLong(PyObject_GetAttrString(options, "reuse_helper"));
2064   rec->auto_reinitialize =
2065     PyInt_AsLong(PyObject_GetAttrString(options, "auto_reinitialize"));
2066   rec->keep_thread_alive =
2067     PyInt_AsLong(PyObject_GetAttrString(options, "keep_thread_alive"));
2068   rec->quiet = PyInt_AsLong(PyObject_GetAttrString(options, "quiet"));
2069 #ifdef _PYMOL_IP_EXTRAS
2070   rec->incentive_product = true;
2071   PyObject_SetAttrString(options, "incentive_product", PyInt_FromLong(1));
2072 #else
2073   rec->incentive_product =
2074     PyInt_AsLong(PyObject_GetAttrString(options, "incentive_product"));
2075 #endif
2076   rec->multisample = PyInt_AsLong(PyObject_GetAttrString(options, "multisample"));
2077   rec->window_visible = PyInt_AsLong(PyObject_GetAttrString(options, "window_visible"));
2078   rec->read_stdin = PyInt_AsLong(PyObject_GetAttrString(options, "read_stdin"));
2079   rec->presentation = PyInt_AsLong(PyObject_GetAttrString(options, "presentation"));
2080   rec->defer_builds_mode =
2081     PyInt_AsLong(PyObject_GetAttrString(options, "defer_builds_mode"));
2082   rec->full_screen = PyInt_AsLong(PyObject_GetAttrString(options, "full_screen"));
2083   load_str = PyString_AsString(PyObject_GetAttrString(options, "after_load_script"));
2084   rec->sphere_mode = PyInt_AsLong(PyObject_GetAttrString(options, "sphere_mode"));
2085   rec->stereo_capable = PyInt_AsLong(PyObject_GetAttrString(options, "stereo_capable"));
2086   rec->stereo_mode = PyInt_AsLong(PyObject_GetAttrString(options, "stereo_mode"));
2087   rec->zoom_mode = PyInt_AsLong(PyObject_GetAttrString(options, "zoom_mode"));
2088   rec->no_quit = PyInt_AsLong(PyObject_GetAttrString(options, "no_quit"));
2089   rec->launch_status = PyInt_AsLong(PyObject_GetAttrString(options, "launch_status"));
2090   rec->gldebug = PyInt_AsLong(PyObject_GetAttrString(options, "gldebug"));
2091   rec->openvr_stub = PyInt_AsLong(PyObject_GetAttrString(options, "openvr_stub"));
2092 
2093   if(load_str) {
2094     if(load_str[0]) {
2095       UtilNCopy(rec->after_load_script, load_str, PYMOL_MAX_OPT_STR);
2096     }
2097   }
2098   if(PyErr_Occurred()) {
2099     PyErr_Print();
2100   }
2101 }
2102 
PGetOptions(CPyMOLOptions * rec)2103 void PGetOptions(CPyMOLOptions * rec)
2104 {
2105   PyObject *pymol, *invocation, *options;
2106 
2107   pymol = PImportModuleOrFatal("pymol");
2108   invocation = PGetAttrOrFatal(pymol, "invocation");     /* get a handle to the invocation module */
2109   options = PGetAttrOrFatal(invocation, "options");
2110 
2111   PConvertOptions(rec, options);
2112   Py_XDECREF(invocation);
2113   Py_XDECREF(options);
2114   Py_XDECREF(pymol);
2115 }
2116 
2117 /**
2118  * Runs a string in the namespace of the pymol global module
2119  * @pre GIL
2120  */
PRunStringModule(PyMOLGlobals * G,const char * str)2121 void PRunStringModule(PyMOLGlobals * G, const char *str)
2122 {
2123   PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->exec, "Os", P_pymol, str));
2124 }
2125 
2126 /**
2127  * Runs a string in the namespace of the pymol instance
2128  * @pre GIL
2129  */
PRunStringInstance(PyMOLGlobals * G,const char * str)2130 void PRunStringInstance(PyMOLGlobals * G, const char *str)
2131 {
2132   PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->exec, "Os", G->P_inst->obj, str));
2133 }
2134 
WrapperObjectReset(WrapperObject * wo)2135 void WrapperObjectReset(WrapperObject *wo){
2136   if (wo->settingWrapperObject) {
2137     reinterpret_cast<SettingPropertyWrapperObject *>(wo->settingWrapperObject)->wobj = NULL;
2138     Py_DECREF(wo->settingWrapperObject);
2139   }
2140 #ifdef _PYMOL_IP_PROPERTIES
2141   if (wo->propertyWrapperObject) {
2142     reinterpret_cast<SettingPropertyWrapperObject *>(wo->propertyWrapperObject)->wobj = NULL;
2143     Py_DECREF(wo->propertyWrapperObject);
2144   }
2145 #endif
2146   Py_XDECREF(wo->dict);
2147   Py_DECREF(wo);
2148 }
2149 
PInit(PyMOLGlobals * G,int global_instance)2150 void PInit(PyMOLGlobals * G, int global_instance)
2151 {
2152   /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
2153 #ifdef WIN32
2154 #ifdef _PYMOL_MONOLITHIC
2155 #ifndef _PYMOL_EMBEDDED
2156 #define _PYMOL_INIT_MODULES
2157 #endif
2158 #endif
2159 #endif
2160   /* END PROPRIETARY CODE SEGMENT */
2161 
2162 #ifdef _PYMOL_INIT_MODULES
2163   /* Win32 module build: includes pyopengl, numpy, and sglite */
2164   /* sglite
2165    * initExtensionClass();
2166    * initsglite();
2167    */
2168   init_champ();
2169 #ifdef _PYMOL_PYOMM
2170   init_pyomm();
2171 #endif
2172 
2173   /* initialize numeric python */
2174 #ifdef _PYMOL_NUMPY_INIT
2175   init_numpy();
2176   initmultiarray();
2177   initarrayfns();
2178   initlapack_lite();
2179   initumath();
2180   initranlib();
2181 #endif
2182   /* initialize PyOpenGL */
2183 #endif
2184 
2185 #if PY_MAJOR_VERSION < 3
2186   // Support implicit utf-8 encoding (important for labeling!)
2187   //   str(u"...unicode...") -> b"...utf-8..."
2188   //   PyString_AsString(unicodeobj) -> "...utf-8..."
2189   PyUnicode_SetDefaultEncoding("utf-8");
2190 #endif
2191 
2192   if(true /* global_instance */) {
2193     PCatchInit();               /* setup standard-output catch routine */
2194   }
2195 
2196   P_pymol = PImportModuleOrFatal("pymol");
2197   P_pymol_dict = PyModule_GetDict(P_pymol);
2198   Py_XINCREF(P_pymol_dict);
2199   if(!P_pymol_dict)
2200     ErrFatal(G, "PyMOL", "can't find globals for 'pymol'");
2201 
2202   if(global_instance) {         /* if global singleton PyMOL... */
2203     G->P_inst = pymol::calloc<CP_inst>(1);
2204     G->P_inst->obj = P_pymol;
2205     G->P_inst->dict = P_pymol_dict;
2206     {
2207       int a;
2208       SavedThreadRec *str = G->P_inst->savedThread;
2209       for(a = 0; a < MAX_SAVED_THREAD; a++) {
2210         (str++)->id = -1;
2211       }
2212     }
2213   }
2214 
2215   {
2216     G->P_inst->exec = PGetAttrOrFatal(P_pymol, "exec_str");
2217 
2218     if(global_instance) {
2219       PCatch_install(NULL, NULL);
2220     }
2221 
2222     P_traceback = PImportModuleOrFatal("traceback");
2223     P_cmd = PImportModuleOrFatal("pymol.cmd");
2224 
2225     if(global_instance) {
2226       /* implies global singleton pymol, so set up the global handle */
2227       PyObject_SetAttrString(P_cmd, "_COb",
2228                              PyCObject_FromVoidPtr((void *) &SingletonPyMOLGlobals,
2229                                                    NULL));
2230 
2231       /* cmd module is itself the api for the global PyMOL instance */
2232       G->P_inst->cmd = P_cmd;
2233     }
2234 
2235     /* right now, all locks are global -- eventually some of these may
2236        become instance-specific in order to improve concurrency */
2237 
2238     G->P_inst->lock = PGetAttrOrFatal(G->P_inst->cmd, "lock");
2239     G->P_inst->lock_attempt = PGetAttrOrFatal(G->P_inst->cmd, "lock_attempt");
2240     G->P_inst->unlock = PGetAttrOrFatal(G->P_inst->cmd, "unlock");
2241     G->P_inst->lock_api_status = PGetAttrOrFatal(G->P_inst->cmd, "lock_api_status");
2242     G->P_inst->lock_api_glut = PGetAttrOrFatal(G->P_inst->cmd, "lock_api_glut");
2243 
2244     /* 'do' command */
2245 
2246     G->P_inst->cmd_do = PGetAttrOrFatal(G->P_inst->cmd, "do");
2247 
2248     /* cache */
2249     G->P_inst->cache = PyObject_GetAttrString(G->P_inst->obj, "_cache");
2250 
2251     /* invariant stuff */
2252 
2253     P_menu = PImportModuleOrFatal("pymol.menu");
2254     P_setting = PImportModuleOrFatal("pymol.setting");
2255     P_povray = PImportModuleOrFatal("pymol.povray");
2256 
2257 #ifdef _PYMOL_XRAY
2258     P_xray = PImportModuleOrFatal("pymol.xray");
2259 #endif
2260 
2261     /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
2262 #ifdef WIN32
2263     P_time = PImportModuleOrFatal("time");
2264     P_sleep = PGetAttrOrFatal(P_time, "sleep");
2265 #endif
2266     /* END PROPRIETARY CODE SEGMENT */
2267 
2268     P_parser = PImportModuleOrFatal("pymol.parser");
2269 
2270     {
2271       PyObject *fn_closure = PyObject_GetAttrString(P_parser, "new_parse_closure");
2272       G->P_inst->parse = PYOBJECT_CALLFUNCTION(fn_closure, "O", G->P_inst->cmd);
2273       PXDecRef(fn_closure);
2274       if(!G->P_inst->parse)
2275         ErrFatal(G, "PyMOL", "can't create 'parse' function closure");
2276     }
2277 
2278     {
2279       PyObject *fn_closure = PyObject_GetAttrString(P_parser, "new_complete_closure");
2280       G->P_inst->complete = PYOBJECT_CALLFUNCTION(fn_closure, "O", G->P_inst->cmd);
2281       PXDecRef(fn_closure);
2282       if(!G->P_inst->complete)
2283         ErrFatal(G, "PyMOL", "can't create 'complete' function closure");
2284     }
2285 
2286     {
2287       PyObject *fn_closure = PGetAttrOrFatal(P_pymol, "_colortype");
2288       G->P_inst->colortype = PYOBJECT_CALLFUNCTION(fn_closure, "O", G->P_inst->cmd);
2289       PXDecRef(fn_closure);
2290     }
2291 
2292     P_chempy = PImportModuleOrFatal("chempy");
2293     P_models = PImportModuleOrFatal("chempy.models");
2294     P_CmdException = PGetAttrOrFatal(P_pymol, "CmdException");
2295     P_QuietException = PGetAttrOrFatal(P_cmd, "QuietException");
2296 
2297     /* backwards compatibility */
2298 
2299     PRunStringModule(G, "glutThread = thread.get_ident()");
2300 
2301     P_glut_thread_id = PyThread_get_thread_ident();
2302 
2303 #ifndef WIN32
2304     if(G->Option->siginthand) {
2305       signal(SIGINT, my_interrupt);
2306     }
2307 #endif
2308 
2309   if (!Wrapper_Type.tp_basicsize) {
2310     Wrapper_Type.tp_basicsize = sizeof(WrapperObject);
2311     Wrapper_Type.tp_flags = Py_TPFLAGS_DEFAULT;
2312     wrapperMappingMethods.mp_length = NULL;
2313     wrapperMappingMethods.mp_subscript = &WrapperObjectSubScript;
2314     wrapperMappingMethods.mp_ass_subscript = &WrapperObjectAssignSubScript;
2315     Wrapper_Type.tp_as_mapping = &wrapperMappingMethods;
2316 
2317     settingWrapper_Type.tp_basicsize = sizeof(SettingPropertyWrapperObject);
2318     settingWrapper_Type.tp_flags = Py_TPFLAGS_DEFAULT;
2319     settingWrapper_Type.tp_iter = &SettingWrapperObjectIter;
2320     settingMappingMethods.mp_length = NULL;
2321     settingMappingMethods.mp_subscript = &SettingWrapperObjectSubScript;
2322     settingMappingMethods.mp_ass_subscript = &SettingWrapperObjectAssignSubScript;
2323     settingWrapper_Type.tp_as_mapping = &settingMappingMethods;
2324     settingWrapper_Type.tp_getattro = PyObject_GenericGetAttrOrItem;
2325     settingWrapper_Type.tp_setattro = PyObject_GenericSetAttrAsItem;
2326 
2327     if (PyType_Ready(&Wrapper_Type) < 0
2328         || PyType_Ready(&settingWrapper_Type) < 0
2329         ){
2330       PRINTFB(G, FB_Python, FB_Errors)
2331 	" PInit: Wrapper_Type, settingWrapper_Type, propertyWrapper_Type not ready\n" ENDFB(G);
2332       return;
2333     }
2334     Py_INCREF(&Wrapper_Type);
2335     Py_INCREF(&settingWrapper_Type);
2336   }
2337   }
2338 
2339 #ifdef _PYMOL_NO_MSGPACKC
2340   // fallback MMTF support
2341   PyRun_SimpleString(
2342       "import pymol.importing;"
2343       "pymol.importing.loadfunctions.setdefault('mmtf',"
2344       "pymol.importing.load_mmtf)");
2345 #endif
2346 }
2347 
PPovrayRender(PyMOLGlobals * G,const char * header,const char * inp,const char * file,int width,int height,int antialias)2348 int PPovrayRender(PyMOLGlobals * G, const char *header, const char *inp, const char *file, int width,
2349                   int height, int antialias)
2350 {
2351   PyObject *result;
2352   int ok;
2353   PBlock(G);
2354   result =
2355     PYOBJECT_CALLMETHOD(P_povray, "render_from_string", "sssiii", header, inp, file,
2356                         width, height, antialias);
2357   ok = PyObject_IsTrue(result);
2358   Py_DECREF(result);
2359   PUnblock(G);
2360   return (ok);
2361 }
2362 
PFree(PyMOLGlobals * G)2363 void PFree(PyMOLGlobals * G)
2364 {
2365   PXDecRef(G->P_inst->parse);
2366   PXDecRef(G->P_inst->complete);
2367   PXDecRef(G->P_inst->colortype);
2368 }
2369 
2370 /**
2371  * Terminates the application with a call to `exit(code)`.
2372  */
PExit(PyMOLGlobals * G,int code)2373 void PExit(PyMOLGlobals * G, int code)
2374 {
2375   ExecutiveDelete(G, "all");
2376   PBlock(G);
2377 
2378   PyMOL_PushValidContext(G->PyMOL);
2379   PyMOL_Stop(G->PyMOL);
2380   PyMOL_PopValidContext(G->PyMOL);
2381 
2382 #ifndef _PYMOL_NO_MAIN
2383   if(G->Main) {
2384     MainFree();
2385   }
2386 #endif
2387 
2388   PyMOL_Free(G->PyMOL);
2389 
2390 #if 1
2391   /* we're having trouble with threading errors after calling Py_Exit,
2392      so for the time being, let's just take the process down at this
2393      point, instead of allowing PyExit to be called. */
2394 
2395   exit(code);
2396 #else
2397 
2398   Py_Exit(code);
2399 #endif
2400 }
2401 
2402 /**
2403  * Add `str` to the command queue
2404  */
PParse(PyMOLGlobals * G,const char * str)2405 void PParse(PyMOLGlobals * G, const char *str)
2406 {
2407   OrthoCommandIn(G, str);
2408 }
2409 
2410 /**
2411  * Call `cmd.do(str)`
2412  *
2413  * Effectively calls `PParse(str.splitlines())` with logging and echo.
2414  */
PDo(PyMOLGlobals * G,const char * str)2415 void PDo(PyMOLGlobals * G, const char *str)
2416 {                               /* assumes we already hold the re-entrant API lock */
2417   int blocked;
2418   PyObject *ret ;
2419   blocked = PAutoBlock(G);
2420   ret = PYOBJECT_CALLFUNCTION(G->P_inst->cmd_do, "s", str);
2421   Py_XDECREF(ret);
2422   PAutoUnblock(G, blocked);
2423 }
2424 
2425 /*
2426  * Write `str` to the log file (if one is open).
2427  *
2428  * str: command or expression to log
2429  * format: cPLog_pml (`str` is PyMOL command)
2430  *         cPLog_pym (`str` is Python expression)
2431  *         cPLog_no_flush (write `str` as is)
2432  *         cPLog_pml_lf (unused TODO remove?)
2433  *
2434  * See also equivalent Python impelemtation: cmd.log()
2435  */
PLog(PyMOLGlobals * G,const char * str,int format)2436 void PLog(PyMOLGlobals * G, const char *str, int format)
2437 {
2438   int mode;
2439   int a = sizeof(OrthoLineType) - 15;
2440   int blocked;
2441   PyObject *log;
2442   OrthoLineType buffer = "";
2443   mode = SettingGetGlobal_i(G, cSetting_logging);
2444   if(mode) {
2445     blocked = PAutoBlock(G);
2446     log = PyDict_GetItemString(P_pymol_dict, P_log_file_str);
2447     if(log && (log != Py_None)) {
2448       if(format == cPLog_no_flush) {
2449         PYOBJECT_CALLMETHOD(log, "write", "s", str);    /* maximize responsiveness (for real-time) */
2450       } else {
2451         switch (mode) {
2452         case cPLog_pml:        /* .pml file */
2453           switch (format) {
2454           case cPLog_pml_lf:
2455             strcpy(buffer, str);
2456             break;
2457           case cPLog_pml:
2458           case cPLog_pym:
2459             strcpy(buffer, str);
2460             strcat(buffer, "\n");
2461             break;
2462           }
2463           break;
2464         case cPLog_pym:        /* .pym file */
2465           if((str[0] == '_') && (str[1]) == ' ')
2466             str += 2;
2467           switch (format) {
2468           case cPLog_pml_lf:
2469             a = strlen(str);
2470             while(a && str[a - 1] < 32) a--; /* trim CR/LF etc. */
2471           case cPLog_pml:
2472             if (str[0] == '/') {
2473               strncat(buffer, str + 1, a - 1);
2474               strcat(buffer, "\n");
2475             } else {
2476               strcpy(buffer, "cmd.do('''");
2477               char * b = buffer + strlen(buffer);
2478               for (; a && *str; --a) {
2479                 if (*str == '\\' || *str == '\'') {
2480                   *(b++) = '\\';
2481                 }
2482                 *(b++) = *(str++);
2483               }
2484               strcpy(b, "''')\n");
2485             }
2486             break;
2487           case cPLog_pym:
2488             strcpy(buffer, str);
2489             strcat(buffer, "\n");
2490             break;
2491           }
2492         }
2493         PYOBJECT_CALLMETHOD(log, "write", "s", buffer);
2494         PYOBJECT_CALLMETHOD(log, "flush", "");
2495       }
2496     }
2497     PAutoUnblock(G, blocked);
2498   }
2499 }
2500 
2501 /**
2502  * Call flush() on the open log file handle
2503  */
PLogFlush(PyMOLGlobals * G)2504 void PLogFlush(PyMOLGlobals * G)
2505 {
2506   int mode;
2507   PyObject *log;
2508   int blocked;
2509   mode = SettingGetGlobal_i(G, cSetting_logging);
2510   if(mode) {
2511     blocked = PAutoBlock(G);
2512     log = PyDict_GetItemString(P_pymol_dict, P_log_file_str);
2513     if(log && (log != Py_None)) {
2514       PYOBJECT_CALLMETHOD(log, "flush", "");
2515     }
2516     PAutoUnblock(G, blocked);
2517   }
2518 }
2519 
2520 #define PFLUSH_REPORT_UNCAUGHT_EXCEPTIONS(G)                                   \
2521   if (PyErr_Occurred()) {                                                      \
2522     PyErr_Print();                                                             \
2523     PRINTFB(G, FB_Python, FB_Errors)                                           \
2524     " %s: Uncaught exception.  PyMOL may have a bug.\n", __func__ ENDFB(G);    \
2525   }
2526 
2527 /**
2528  * Execute commands from the command queue.
2529  * If the current thread holds the GIL, do nothing. Otherwise the queue will
2530  * be empty after this call.
2531  * @return False if the queue was empty
2532  */
PFlush(PyMOLGlobals * G)2533 int PFlush(PyMOLGlobals * G)
2534 {
2535   /* NOTE: ASSUMES unblocked Python threads and a locked API */
2536   if(!OrthoCommandWaiting(G))
2537     return false;
2538 
2539   if (PAutoBlock(G)) {
2540     if(!(PIsGlutThread() && G->P_inst->glut_thread_keep_out)) {
2541       /* don't run if we're currently banned */
2542       auto ortho = G->Ortho;
2543       while(!OrthoCommandIsEmpty(*ortho)){
2544         auto buffer = OrthoCommandOut(*ortho);
2545         OrthoCommandSetBusy(G, true);
2546         OrthoCommandNest(G, 1);
2547 
2548         // TODO if we release here, another thread might acquire the API lock
2549         // and block us when we try to lock again. (see PYMOL-3249)
2550         // Example: set_key F1, ray async=1
2551         //          => hitting F1 will block GUI updates, even though ray
2552         //             is running async
2553         // PUnlockAPIWhileBlocked(G);
2554 
2555         PFLUSH_REPORT_UNCAUGHT_EXCEPTIONS(G);
2556         PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->parse, "si", buffer.c_str(), 0));
2557         PFLUSH_REPORT_UNCAUGHT_EXCEPTIONS(G);
2558 
2559         // TODO see above
2560         // PLockAPIWhileBlocked(G);
2561 
2562         OrthoCommandSetBusy(G, false);
2563         /* make sure no commands left at this level */
2564         while(OrthoCommandWaiting(G))
2565           PFlushFast(G);
2566         OrthoCommandNest(G, -1);
2567       }
2568     }
2569     PUnblock(G);
2570   }
2571   return true;
2572 }
2573 
2574 /**
2575  * Execute commands from the command queue.
2576  * @return False if the queue was empty
2577  * @pre GIL
2578  * @post queue is empty
2579  */
PFlushFast(PyMOLGlobals * G)2580 int PFlushFast(PyMOLGlobals * G)
2581 {
2582   /* NOTE: ASSUMES we currently have blocked Python threads and an unlocked API */
2583   int did_work = false;
2584   auto ortho = G->Ortho;
2585   while(!OrthoCommandIsEmpty(*ortho)){
2586     auto buffer = OrthoCommandOut(*ortho);
2587     OrthoCommandSetBusy(G, true);
2588     OrthoCommandNest(G, 1);
2589     did_work = true;
2590 
2591     PFLUSH_REPORT_UNCAUGHT_EXCEPTIONS(G);
2592     PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->parse, "si", buffer.c_str(), 0));
2593     PFLUSH_REPORT_UNCAUGHT_EXCEPTIONS(G);
2594 
2595     OrthoCommandSetBusy(G, false);
2596     /* make sure no commands left at this level */
2597     while(OrthoCommandWaiting(G))
2598       PFlushFast(G);
2599     OrthoCommandNest(G, -1);
2600   }
2601   return did_work;
2602 }
2603 
PBlockLegacy()2604 void PBlockLegacy()
2605 {
2606   PBlock(SingletonPyMOLGlobals);
2607 }
2608 
PUnblockLegacy()2609 void PUnblockLegacy()
2610 {
2611   PUnblock(SingletonPyMOLGlobals);
2612 }
2613 
2614 /**
2615  * Acquire the GIL.
2616  * @pre no GIL
2617  * @post GIL
2618  */
PBlock(PyMOLGlobals * G)2619 void PBlock(PyMOLGlobals * G)
2620 {
2621 
2622   if(!PAutoBlock(G)) {
2623     ErrFatal(G, "PBlock", "Threading error detected.  Terminating...");
2624   }
2625 }
2626 
2627 /**
2628  * Acquire the GIL.
2629  * Return false if the current thread already holds the GIL.
2630  */
PAutoBlock(PyMOLGlobals * G)2631 int PAutoBlock(PyMOLGlobals * G)
2632 {
2633 #ifndef _PYMOL_EMBEDDED
2634   SavedThreadRec *SavedThread = G->P_inst->savedThread;
2635 
2636   auto id = PyThread_get_thread_ident();
2637 
2638   for (auto a = MAX_SAVED_THREAD - 1; a; --a) {
2639     if (SavedThread[a].id == id) {
2640       // Aquire GIL
2641       PyEval_RestoreThread(SavedThread[a].state);
2642 
2643       SavedThread[a].id = -1;
2644 
2645       return 1;
2646     }
2647   }
2648   return 0;
2649 #else
2650   return 1;
2651 #endif
2652 }
2653 
2654 /**
2655  * Can be called anytime, no lock conditions
2656  * @return True if the current thread is the GUI thread
2657  */
PIsGlutThread(void)2658 int PIsGlutThread(void)
2659 {
2660   return (PyThread_get_thread_ident() == P_glut_thread_id);
2661 }
2662 
2663 /**
2664  * Release GIL
2665  * @pre GIL
2666  * @post no GIL
2667  */
PUnblock(PyMOLGlobals * G)2668 void PUnblock(PyMOLGlobals * G)
2669 {
2670 #ifndef _PYMOL_EMBEDDED
2671   SavedThreadRec *SavedThread = G->P_inst->savedThread;
2672   auto a = MAX_SAVED_THREAD - 1;
2673   /* NOTE: ASSUMES a locked API */
2674 
2675   /* reserve a space while we have a lock */
2676   for (; a; --a) {
2677     if (SavedThread[a].id == -1) {
2678       SavedThread[a].id = PyThread_get_thread_ident();
2679       break;
2680     }
2681   }
2682 
2683   // Release GIL
2684   SavedThread[a].state = PyEval_SaveThread();
2685 #endif
2686 }
2687 
2688 /**
2689  * Release GIL if flag=true
2690  */
PAutoUnblock(PyMOLGlobals * G,int flag)2691 void PAutoUnblock(PyMOLGlobals * G, int flag)
2692 {
2693   if(flag)
2694     PUnblock(G);
2695 }
2696 
2697 /**
2698  * Acquire GIL, release API lock and flush
2699  * @pre no GIL
2700  * @post GIL
2701  * @post no API lock
2702  */
PBlockAndUnlockAPI(PyMOLGlobals * G)2703 void PBlockAndUnlockAPI(PyMOLGlobals * G)
2704 {
2705   PBlock(G);
2706   PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->unlock, "iO", 0, G->P_inst->cmd));
2707 }
2708 
2709 /**
2710  * Acquire API lock
2711  * @param block_if_busy If false and PyMOL is busy, do not block
2712  * @return False if API lock could not be acquired
2713  * @pre no GIL
2714  */
PLockAPI(PyMOLGlobals * G,int block_if_busy)2715 int PLockAPI(PyMOLGlobals * G, int block_if_busy)
2716 {
2717   int result = true;
2718   PBlock(G);
2719   if(block_if_busy) {
2720     PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->lock, "O", G->P_inst->cmd));
2721   } else {                      /* not blocking if PyMOL is busy */
2722 
2723     PyObject *got_lock =
2724       PYOBJECT_CALLFUNCTION(G->P_inst->lock_attempt, "O", G->P_inst->cmd);
2725 
2726     if(got_lock) {
2727       result = PyInt_AsLong(got_lock);
2728       Py_DECREF(got_lock);
2729     }
2730   }
2731   PUnblock(G);
2732   return result;
2733 }
2734 
2735 /**
2736  * Release API lock with flushing
2737  * @pre no GIL
2738  * @pre API lock
2739  */
PUnlockAPI(PyMOLGlobals * G)2740 void PUnlockAPI(PyMOLGlobals * G)
2741 {
2742   PBlock(G);
2743   PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->unlock, "iO", 0, G->P_inst->cmd));
2744   PUnblock(G);
2745 }
2746 
2747 /**
2748  * Release API lock without flushing
2749  * @pre GIL
2750  * @pre API lock
2751  * @post no API lock
2752  */
PUnlockAPIWhileBlocked(PyMOLGlobals * G)2753 static void PUnlockAPIWhileBlocked(PyMOLGlobals * G)
2754 {
2755   PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->unlock, "iO", -1, G->P_inst->cmd));
2756 }
2757 
2758 /**
2759  * Acquire API lock
2760  * @pre GIL
2761  * @post GIL
2762  * @post API lock
2763  */
PLockAPIWhileBlocked(PyMOLGlobals * G)2764 static void PLockAPIWhileBlocked(PyMOLGlobals * G)
2765 {
2766   PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->lock, "O", G->P_inst->cmd));
2767 }
2768 
2769 /**
2770  * Try to acquire API lock (non-blocking if busy) and release GIL
2771  * @return False if API lock could not be acquired
2772  * @pre GIL
2773  * @post no GIL if API lock could be acquired
2774  */
PTryLockAPIAndUnblock(PyMOLGlobals * G)2775 int PTryLockAPIAndUnblock(PyMOLGlobals * G)
2776 {
2777   int result = get_api_lock(G, false);
2778   if(result) {
2779     PUnblock(G);
2780   }
2781   return result;
2782 }
2783 
2784 /**
2785  * Acquire API lock and release the GIL
2786  * @pre GIL
2787  * @post no GIL
2788  * @post API Lock
2789  */
PLockAPIAndUnblock(PyMOLGlobals * G)2790 void PLockAPIAndUnblock(PyMOLGlobals * G)
2791 {
2792   PXDecRef(PYOBJECT_CALLFUNCTION(G->P_inst->lock, "O", G->P_inst->cmd));
2793   PUnblock(G);
2794 }
2795 
2796 /**
2797  * Assign a variable in the pymol namespace, like
2798  * @verbatim
2799    import pymol
2800    setattr(pymol, name, value)
2801    @endverbatim
2802  * @pre no GIL
2803  */
PDefineFloat(PyMOLGlobals * G,const char * name,float value)2804 void PDefineFloat(PyMOLGlobals * G, const char *name, float value)
2805 {
2806   char buffer[OrthoLineLength];
2807   sprintf(buffer, "%s = %f\n", name, value);
2808   PBlock(G);
2809   PRunStringModule(G, buffer);
2810   PUnblock(G);
2811 }
2812 
2813 
2814 
2815 /* A static module */
2816 
PCatchWrite(PyObject * self,PyObject * args)2817 static PyObject *PCatchWrite(PyObject * self, PyObject * args)
2818 {
2819   char *str;
2820   PyArg_ParseTuple(args, "s", &str);
2821   if(str[0]) {
2822     if(SingletonPyMOLGlobals) {
2823       if(Feedback(SingletonPyMOLGlobals, FB_Python, FB_Output)) {
2824         OrthoAddOutput(SingletonPyMOLGlobals, str);
2825       }
2826     }
2827   }
2828   return PConvAutoNone(Py_None);
2829 }
2830 
PCatchWritelines(PyObject * self,PyObject * args)2831 static PyObject *PCatchWritelines(PyObject * self, PyObject * args)
2832 {
2833   PyObject *seq;
2834   int len;
2835   PyArg_ParseTuple(args, "O", &seq);
2836   if(seq && PySequence_Check(seq)) {
2837     if((len = PySequence_Size(seq)) > 0) {
2838       int i;
2839       for(i = 0; i < len; i++) {
2840         PyObject *obj = PySequence_GetItem(seq, i);
2841         if(obj && PyString_Check(obj)) {
2842           const char *str = PyString_AsString(obj);
2843           if(SingletonPyMOLGlobals) {
2844             if(Feedback(SingletonPyMOLGlobals, FB_Python, FB_Output)) {
2845               OrthoAddOutput(SingletonPyMOLGlobals, str);
2846             }
2847           }
2848         }
2849         Py_XDECREF(obj);
2850       }
2851     }
2852   }
2853   return PConvAutoNone(Py_None);
2854 }
2855 
PCatchFlush(PyObject * self,PyObject * args)2856 static PyObject *PCatchFlush(PyObject * self, PyObject * args)
2857 {
2858   fflush(stdout);
2859   fflush(stderr);
2860   return PConvAutoNone(Py_None);
2861 }
2862 
PCatchIsAtty(PyObject * self,PyObject * args)2863 static PyObject *PCatchIsAtty(PyObject * self, PyObject * args)
2864 {
2865   Py_RETURN_FALSE;
2866 }
2867 
PCatch_install(PyObject * self,PyObject * args)2868 static PyObject *PCatch_install(PyObject * self, PyObject * args)
2869 {
2870   PyRun_SimpleString(
2871       "import sys, pcatch\n"
2872       "if sys.stdout is not pcatch:"
2873       "pcatch.closed = False;"
2874       "pcatch.encoding = 'UTF-8';"
2875       "sys.stderr = sys.stdout = pcatch");
2876   return PConvAutoNone(Py_None);
2877 }
2878 
2879 static PyMethodDef PCatch_methods[] = {
2880   {"writelines", PCatchWritelines, METH_VARARGS},
2881   {"write", PCatchWrite, METH_VARARGS},
2882   {"flush", PCatchFlush, METH_VARARGS},
2883   {"isatty", PCatchIsAtty, METH_VARARGS}, // called by pip.main(["install", "..."])
2884   {"_install", PCatch_install, METH_VARARGS},
2885   {NULL, NULL}                  /* sentinel */
2886 };
2887 
PCatchInit(void)2888 void PCatchInit(void)
2889 {
2890 #if PY_MAJOR_VERSION < 3
2891   PyImport_AddModule("pcatch");
2892   Py_InitModule("pcatch", PCatch_methods);
2893 #else
2894   static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT,
2895     "pcatch", NULL, -1, PCatch_methods };
2896   PyObject * pcatch = PyModule_Create(&moduledef);
2897   if (pcatch) {
2898     PyDict_SetItemString(PyImport_GetModuleDict(), "pcatch", pcatch);
2899     Py_DECREF(pcatch);
2900   }
2901 #endif
2902 }
2903 #endif
2904