diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -174,53 +174,184 @@ */ -static PyThread_type_lock tcl_lock = 0; - + +struct _fhcdata; + +typedef struct { + PyObject *Tkapp_Type; + PyObject *Tkinter_TclError; + PyObject *excInCmd; + PyObject *valInCmd; + PyObject *trbInCmd; + PyObject *PyTclObject_Type; + PyObject *Tktt_Type; + PyThread_type_lock tcl_lock; + int quitMainLoop; + int errorInCmd; +#ifdef TKINTER_PROTECT_LOADTK + int tk_load_failed; +#endif + int Tkinter_busywaitinterval; +#ifdef HAVE_CREATEFILEHANDLER + struct _fhcdata *HeadFHCD; +#endif #ifdef TCL_THREADS -static Tcl_ThreadDataKey state_key; -typedef PyThreadState *ThreadSpecificData; -#define tcl_tstate (*(PyThreadState**)Tcl_GetThreadData(&state_key, sizeof(PyThreadState*))) + Tcl_ThreadDataKey state_key; #else -static PyThreadState *tcl_tstate = NULL; + PyThreadState *tcl_tstate; #endif - -#define ENTER_TCL \ - { PyThreadState *tstate = PyThreadState_Get(); Py_BEGIN_ALLOW_THREADS \ - if(tcl_lock)PyThread_acquire_lock(tcl_lock, 1); tcl_tstate = tstate; - -#define LEAVE_TCL \ - tcl_tstate = NULL; if(tcl_lock)PyThread_release_lock(tcl_lock); Py_END_ALLOW_THREADS} - -#define ENTER_OVERLAP \ +} _tkinterstate; + +#ifdef TCL_THREADS +#define get_tcl_tstate(gdata) \ + (*(PyThreadState**) \ + Tcl_GetThreadData(&gdata->state_key, sizeof(PyThreadState*))); +#define set_tcl_tstate(gdata, val) { \ + (*(PyThreadState**) \ + Tcl_GetThreadData(&gdata->state_key, sizeof(PyThreadState*))) = val; \ + } +#else +#define get_tcl_tstate(gdata) gdata->tcl_tstate +#define set_tcl_tstate(gdata, val) { gdata->tcl_tstate = val; } +#endif + + +#ifdef HAVE_CREATEFILEHANDLER +typedef struct _fhcdata { + PyObject *func; + PyObject *file; + int id; + struct _fhcdata *next; + _tkinterstate *gstate; +} FileHandler_ClientData; +#endif + + +#define _tkinter_state(o) ((_tkinterstate *)PyModule_GetState(o)) + +static int +_tkinter_clear(PyObject *m) +{ + _tkinterstate *gstate = _tkinter_state(m); +#ifdef HAVE_CREATEFILEHANDLER + FileHandler_ClientData *p; +#endif + Py_CLEAR(gstate->Tkapp_Type); + Py_CLEAR(gstate->Tkinter_TclError); + Py_CLEAR(gstate->excInCmd); + Py_CLEAR(gstate->valInCmd); + Py_CLEAR(gstate->trbInCmd); + Py_CLEAR(gstate->PyTclObject_Type); + Py_CLEAR(gstate->Tktt_Type); + +#ifdef HAVE_CREATEFILEHANDLER + p = gstate->HeadFHCD; + while (p != NULL) { + Py_CLEAR(p->func); + Py_CLEAR(p->file); + p = p->next; + } +#endif + return 0; +} + +static int +_tkinter_traverse(PyObject *m, visitproc visit, void *arg) +{ + _tkinterstate *gstate = _tkinter_state(m); + Py_VISIT(gstate->Tkapp_Type); + Py_VISIT(gstate->Tkinter_TclError); + Py_VISIT(gstate->excInCmd); + Py_VISIT(gstate->valInCmd); + Py_VISIT(gstate->trbInCmd); + Py_VISIT(gstate->PyTclObject_Type); + Py_VISIT(gstate->Tktt_Type); + return 0; +} + +static void +_tkinter_free(void *m) +{ + _tkinterstate *gstate = _tkinter_state(m); +#ifdef HAVE_CREATEFILEHANDLER + FileHandler_ClientData *p, **pp; +#endif + _tkinter_clear((PyObject *)m); + + if (gstate->tcl_lock) { + PyThread_free_lock(gstate->tcl_lock); + gstate->tcl_lock = NULL; + } + +#ifdef HAVE_CREATEFILEHANDLER + pp = &gstate->HeadFHCD; + while ((p = *pp) != NULL) { + *pp = p->next; + PyMem_DEL(p); + } +#endif +} + +static PyModuleDef _tkintermodule; + +#define _tkinterstate_global \ + ((_tkinterstate *)PyModule_GetState(PyState_FindModule(&_tkintermodule))) + + +#define ENTER_TCL(gstate) \ + { PyThreadState *tstate = PyThreadState_Get(); \ + Py_BEGIN_ALLOW_THREADS \ + if (gstate->tcl_lock) \ + PyThread_acquire_lock(gstate->tcl_lock, 1); \ + set_tcl_tstate(gstate, tstate); + +#define LEAVE_TCL(gstate) \ + set_tcl_tstate(gstate, NULL); \ + if (gstate->tcl_lock) \ + PyThread_release_lock(gstate->tcl_lock); \ + Py_END_ALLOW_THREADS \ + } + +#define ENTER_OVERLAP(gstate) \ Py_END_ALLOW_THREADS -#define LEAVE_OVERLAP_TCL \ - tcl_tstate = NULL; if(tcl_lock)PyThread_release_lock(tcl_lock); } - -#define ENTER_PYTHON \ - { PyThreadState *tstate = tcl_tstate; tcl_tstate = NULL; \ - if(tcl_lock)PyThread_release_lock(tcl_lock); PyEval_RestoreThread((tstate)); } - -#define LEAVE_PYTHON \ - { PyThreadState *tstate = PyEval_SaveThread(); \ - if(tcl_lock)PyThread_acquire_lock(tcl_lock, 1); tcl_tstate = tstate; } - -#define CHECK_TCL_APPARTMENT \ - if (((TkappObject *)self)->threaded && \ - ((TkappObject *)self)->thread_id != Tcl_GetCurrentThread()) { \ - PyErr_SetString(PyExc_RuntimeError, "Calling Tcl from different appartment"); \ - return 0; \ +#define LEAVE_OVERLAP_TCL(gstate) \ + set_tcl_tstate(gstate, NULL); \ + if (gstate->tcl_lock) \ + PyThread_release_lock(gstate->tcl_lock); } + +#define ENTER_PYTHON(gstate) \ + { PyThreadState *tstate = get_tcl_tstate(gstate); \ + set_tcl_tstate(gstate, NULL); \ + if (gstate->tcl_lock) \ + PyThread_release_lock(gstate->tcl_lock); \ + PyEval_RestoreThread((tstate)); \ } +#define LEAVE_PYTHON(gstate) \ + { PyThreadState *tstate = PyEval_SaveThread(); \ + if (gstate->tcl_lock) \ + PyThread_acquire_lock(gstate->tcl_lock, 1); \ + set_tcl_tstate(gstate, tstate); \ + } + +#define CHECK_TCL_APPARTMENT(gstate) \ + if (((TkappObject *)self)->threaded && \ + ((TkappObject *)self)->thread_id != Tcl_GetCurrentThread()) { \ + PyErr_SetString(PyExc_RuntimeError, \ + "Calling Tcl from different appartment"); \ + return 0; \ + } + #else -#define ENTER_TCL -#define LEAVE_TCL -#define ENTER_OVERLAP -#define LEAVE_OVERLAP_TCL -#define ENTER_PYTHON -#define LEAVE_PYTHON -#define CHECK_TCL_APPARTMENT +#define ENTER_TCL(gstate) +#define LEAVE_TCL(gstate) +#define ENTER_OVERLAP(gstate) +#define LEAVE_OVERLAP_TCL(gstate) +#define ENTER_PYTHON(gstate) +#define LEAVE_PYTHON(gstate) +#define CHECK_TCL_APPARTMENT(gstate) #endif @@ -230,7 +361,6 @@ /**** Tkapp Object Declaration ****/ -static PyTypeObject Tkapp_Type; typedef struct { PyObject_HEAD @@ -248,11 +378,12 @@ Tcl_ObjType *ListType; Tcl_ObjType *ProcBodyType; Tcl_ObjType *StringType; + _tkinterstate *gstate; } TkappObject; -#define Tkapp_Check(v) (Py_TYPE(v) == &Tkapp_Type) #define Tkapp_Interp(v) (((TkappObject *) (v))->interp) #define Tkapp_Result(v) Tcl_GetStringResult(Tkapp_Interp(v)) +#define Tkapp_gstate(v) (((TkappObject *) (v))->gstate) #define DEBUG_REFCNT(v) (printf("DEBUG: id=%p, refcnt=%i\n", \ (void *) v, Py_REFCNT(v))) @@ -261,22 +392,16 @@ /**** Error Handling ****/ -static PyObject *Tkinter_TclError; -static int quitMainLoop = 0; -static int errorInCmd = 0; -static PyObject *excInCmd; -static PyObject *valInCmd; -static PyObject *trbInCmd; - -#ifdef TKINTER_PROTECT_LOADTK -static int tk_load_failed = 0; -#endif - static PyObject * Tkinter_Error(PyObject *v) { - PyErr_SetString(Tkinter_TclError, Tkapp_Result(v)); + if (Tkapp_gstate(v)->Tkinter_TclError == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "No TclError, _tkinter is in shotdowning stage."); + return NULL; + } + PyErr_SetString(Tkapp_gstate(v)->Tkinter_TclError, Tkapp_Result(v)); return NULL; } @@ -284,7 +409,6 @@ /**** Utils ****/ -static int Tkinter_busywaitinterval = 20; #ifdef WITH_THREAD #ifndef MS_WINDOWS @@ -327,7 +451,6 @@ #define ARGSZ 64 - static PyObject * Split(char *list) { @@ -439,6 +562,7 @@ Tcl_AppInit(Tcl_Interp *interp) { const char * _tkinter_skip_tk_init; + _tkinterstate *gstate = _tkinterstate_global; if (Tcl_Init(interp) == TCL_ERROR) { PySys_WriteStderr("Tcl_Init error: %s\n", Tcl_GetStringResult(interp)); @@ -453,7 +577,7 @@ } #ifdef TKINTER_PROTECT_LOADTK - if (tk_load_failed) { + if (gstate->tk_load_failed) { PySys_WriteStderr("Tk_Init error: %s\n", TKINTER_LOADTK_ERRMSG); return TCL_ERROR; } @@ -461,7 +585,7 @@ if (Tk_Init(interp) == TCL_ERROR) { #ifdef TKINTER_PROTECT_LOADTK - tk_load_failed = 1; + gstate->tk_load_failed = 1; #endif PySys_WriteStderr("Tk_Init error: %s\n", Tcl_GetStringResult(interp)); return TCL_ERROR; @@ -483,12 +607,19 @@ static TkappObject * Tkapp_New(char *screenName, char *className, - int interactive, int wantobjects, int wantTk, int sync, char *use) + int interactive, int wantobjects, int wantTk, int sync, char *use, + _tkinterstate *gstate) { TkappObject *v; char *argv0; - v = PyObject_New(TkappObject, &Tkapp_Type); + if (gstate->Tkapp_Type == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "No TkappType, _tkinter is in shotdowning stage."); + return NULL; + } + + v = PyObject_New(TkappObject, (PyTypeObject *)gstate->Tkapp_Type); if (v == NULL) return NULL; @@ -498,19 +629,21 @@ TCL_GLOBAL_ONLY) != NULL; v->thread_id = Tcl_GetCurrentThread(); v->dispatching = 0; + v->gstate = gstate; #ifndef TCL_THREADS if (v->threaded) { - PyErr_SetString(PyExc_RuntimeError, "Tcl is threaded but _tkinter is not"); + PyErr_SetString(PyExc_RuntimeError, + "Tcl is threaded but _tkinter is not"); Py_DECREF(v); return 0; } #endif #ifdef WITH_THREAD - if (v->threaded && tcl_lock) { + if (v->threaded && gstate->tcl_lock) { /* If Tcl is threaded, we don't need the lock. */ - PyThread_free_lock(tcl_lock); - tcl_lock = NULL; + PyThread_free_lock(gstate->tcl_lock); + gstate->tcl_lock = NULL; } #endif @@ -553,7 +686,7 @@ "_tkinter_skip_tk_init", "1", TCL_GLOBAL_ONLY); } #ifdef TKINTER_PROTECT_LOADTK - else if (tk_load_failed) { + else if (gstate->tk_load_failed) { Tcl_SetVar(v->interp, "_tkinter_tk_failed", "1", TCL_GLOBAL_ONLY); } @@ -600,7 +733,7 @@ if ( _tkinter_tk_failed != NULL && strcmp(_tkinter_tk_failed, "1") == 0) { - tk_load_failed = 1; + gstate->tk_load_failed = 1; } } #endif @@ -636,18 +769,26 @@ PyObject_HEAD Tcl_Obj *value; PyObject *string; /* This cannot cause cycles. */ + _tkinterstate *gstate; } PyTclObject; -static PyTypeObject PyTclObject_Type; -#define PyTclObject_Check(v) ((v)->ob_type == &PyTclObject_Type) +#define PyTclObject_Check(v, gstate) \ + ((v)->ob_type == (PyTypeObject *)gstate->PyTclObject_Type) static PyObject * -newPyTclObject(Tcl_Obj *arg) +newPyTclObject(Tcl_Obj *arg, _tkinterstate *gstate) { PyTclObject *self; - self = PyObject_New(PyTclObject, &PyTclObject_Type); + if (gstate->PyTclObject_Type == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "No Tcl_Obj, _tkinter is in shotdowning stage."); + return NULL; + } + self = PyObject_New(PyTclObject, + (PyTypeObject *)gstate->PyTclObject_Type); if (self == NULL) return NULL; + Py_INCREF(gstate->PyTclObject_Type); Tcl_IncrRefCount(arg); self->value = arg; self->string = NULL; @@ -657,8 +798,12 @@ static void PyTclObject_dealloc(PyTclObject *self) { + PyTypeObject *type = Py_TYPE(self); Tcl_DecrRefCount(self->value); Py_XDECREF(self->string); + if ((void *)type->tp_dealloc == (void *)PyTclObject_dealloc) { + Py_DECREF(type); + } PyObject_Del(self); } @@ -670,7 +815,7 @@ /* Like _str, but create Unicode if necessary. */ PyDoc_STRVAR(PyTclObject_string__doc__, -"the string representation of this object, either as str or bytes"); + "the string representation of this object, either as str or bytes"); static PyObject * PyTclObject_string(PyTclObject *self, void *ignored) @@ -715,6 +860,7 @@ { int result; PyObject *v; + _tkinterstate *gstate; /* neither argument should be NULL, unless something's gone wrong */ if (self == NULL || other == NULL) { @@ -722,8 +868,11 @@ return NULL; } + assert(PyTclObject_Check(self, _tkinterstate_global)); + gstate = ((PyTclObject *)self)->gstate; + /* both arguments should be instances of PyTclObject */ - if (!PyTclObject_Check(self) || !PyTclObject_Check(other)) { + if (!PyTclObject_Check(other, gstate)) { v = Py_NotImplemented; goto finished; } @@ -779,52 +928,27 @@ {0}, }; -static PyTypeObject PyTclObject_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_tkinter.Tcl_Obj", /*tp_name*/ - sizeof(PyTclObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - /* methods */ - (destructor)PyTclObject_dealloc,/*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_reserved*/ - (reprfunc)PyTclObject_repr, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - (reprfunc)PyTclObject_str, /*tp_str*/ - PyObject_GenericGetAttr, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - PyTclObject_richcompare, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - 0, /*tp_methods*/ - 0, /*tp_members*/ - PyTclObject_getsetlist, /*tp_getset*/ - 0, /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - 0, /*tp_dictoffset*/ - 0, /*tp_init*/ - 0, /*tp_alloc*/ - 0, /*tp_new*/ - 0, /*tp_free*/ - 0, /*tp_is_gc*/ +static PyType_Slot PyTclObject_Type_slots[] = { + {Py_tp_dealloc, (destructor)PyTclObject_dealloc}, + {Py_tp_repr, (reprfunc)PyTclObject_repr}, + {Py_tp_str, (reprfunc)PyTclObject_str}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_richcompare, PyTclObject_richcompare}, + {Py_tp_getset, PyTclObject_getsetlist}, + {0, 0} }; +static PyType_Spec PyTclObject_Type_spec = { + "_tkinter.Tcl_Obj", + sizeof(PyTclObject), + 0, + Py_TPFLAGS_DEFAULT, + PyTclObject_Type_slots, +}; + + static Tcl_Obj* -AsObj(PyObject *value) +AsObj(PyObject *value, _tkinterstate *gstate) { Tcl_Obj *result; long longVal; @@ -846,12 +970,12 @@ return Tcl_NewDoubleObj(PyFloat_AS_DOUBLE(value)); else if (PyTuple_Check(value)) { Tcl_Obj **argv = (Tcl_Obj**) - ckalloc(PyTuple_Size(value)*sizeof(Tcl_Obj*)); + ckalloc(PyTuple_Size(value)*sizeof(Tcl_Obj*)); int i; - if(!argv) - return 0; - for(i=0;ivalue; Tcl_IncrRefCount(v); return v; @@ -907,7 +1031,7 @@ PyObject *v = PyObject_Str(value); if (!v) return 0; - result = AsObj(v); + result = AsObj(v, gstate); Py_DECREF(v); return result; } @@ -989,7 +1113,7 @@ #endif } - return newPyTclObject(value); + return newPyTclObject(value, app->gstate); } #ifdef WITH_THREAD @@ -1004,6 +1128,7 @@ PyObject **res; PyObject **exc_type, **exc_value, **exc_tb; Tcl_Condition *done; + _tkinterstate *gstate; } Tkapp_CallEvent; #endif @@ -1021,7 +1146,8 @@ interpreter thread, which may or may not be the calling thread. */ static Tcl_Obj** -Tkapp_CallArgs(PyObject *args, Tcl_Obj** objStore, int *pobjc) +Tkapp_CallArgs(PyObject *args, Tcl_Obj** objStore, int *pobjc, + _tkinterstate *gstate) { Tcl_Obj **objv = objStore; int objc = 0, i; @@ -1029,7 +1155,7 @@ /* do nothing */; else if (!PyTuple_Check(args)) { - objv[0] = AsObj(args); + objv[0] = AsObj(args, gstate); if (objv[0] == 0) goto finally; objc = 1; @@ -1053,7 +1179,7 @@ objc = i; break; } - objv[i] = AsObj(v); + objv[i] = AsObj(v, gstate); if (!objv[i]) { /* Reset objc, so it attempts to clear objects only up to i. */ @@ -1076,7 +1202,7 @@ Tkapp_CallResult(TkappObject *self) { PyObject *res = NULL; - if(self->wantobjects) { + if (self->wantobjects) { Tcl_Obj *value = Tcl_GetObjResult(self->interp); /* Not sure whether the IncrRef is necessary, but something may overwrite the interpreter result while we are @@ -1102,33 +1228,37 @@ static int Tkapp_CallProc(Tkapp_CallEvent *e, int flags) { + _tkinterstate *gstate = e->gstate; Tcl_Obj *objStore[ARGSZ]; Tcl_Obj **objv; int objc; int i; - ENTER_PYTHON - objv = Tkapp_CallArgs(e->args, objStore, &objc); + ENTER_PYTHON(gstate) + objv = Tkapp_CallArgs(e->args, objStore, &objc, gstate); if (!objv) { PyErr_Fetch(e->exc_type, e->exc_value, e->exc_tb); *(e->res) = NULL; } - LEAVE_PYTHON + LEAVE_PYTHON(gstate) if (!objv) goto done; i = Tcl_EvalObjv(e->self->interp, objc, objv, e->flags); - ENTER_PYTHON + ENTER_PYTHON(gstate) if (i == TCL_ERROR) { *(e->res) = NULL; *(e->exc_type) = NULL; *(e->exc_tb) = NULL; - *(e->exc_value) = PyObject_CallFunction( - Tkinter_TclError, "s", - Tcl_GetStringResult(e->self->interp)); + if (gstate->Tkinter_TclError == NULL) + *(e->exc_value) = PyObject_CallObject(PyExc_RuntimeError, NULL); + else + *(e->exc_value) = PyObject_CallFunction( + gstate->Tkinter_TclError, "s", + Tcl_GetStringResult(e->self->interp)); } else { *(e->res) = Tkapp_CallResult(e->self); } - LEAVE_PYTHON + LEAVE_PYTHON(gstate) Tkapp_CallDeallocArgs(objv, objStore, objc); done: @@ -1161,6 +1291,7 @@ int objc, i; PyObject *res = NULL; TkappObject *self = (TkappObject*)selfptr; + _tkinterstate *gstate = self->gstate; int flags = TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL; /* If args is a single tuple, replace with contents of tuple */ @@ -1187,14 +1318,21 @@ ev->exc_value = &exc_value; ev->exc_tb = &exc_tb; ev->done = &cond; + ev->gstate = gstate; Tkapp_ThreadSend(self, (Tcl_Event*)ev, &cond, &call_mutex); if (res == NULL) { - if (exc_type) - PyErr_Restore(exc_type, exc_value, exc_tb); - else - PyErr_SetObject(Tkinter_TclError, exc_value); + if (gstate->Tkinter_TclError == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "No TclError, _tkinter is in " + "shotdowning stage."); + } else { + if (exc_type) + PyErr_Restore(exc_type, exc_value, exc_tb); + else + PyErr_SetObject(gstate->Tkinter_TclError, exc_value); + } } Tcl_ConditionFinalize(&cond); } @@ -1202,22 +1340,22 @@ #endif { - objv = Tkapp_CallArgs(args, objStore, &objc); + objv = Tkapp_CallArgs(args, objStore, &objc, gstate); if (!objv) return NULL; - ENTER_TCL + ENTER_TCL(gstate) i = Tcl_EvalObjv(self->interp, objc, objv, flags); - ENTER_OVERLAP + ENTER_OVERLAP(gstate) if (i == TCL_ERROR) Tkinter_Error(selfptr); else res = Tkapp_CallResult(self); - LEAVE_OVERLAP_TCL + LEAVE_OVERLAP_TCL(gstate) Tkapp_CallDeallocArgs(objv, objStore, objc); } @@ -1228,6 +1366,7 @@ static PyObject * Tkapp_Eval(PyObject *self, PyObject *args) { + _tkinterstate *gstate = Tkapp_gstate(self); char *script; PyObject *res = NULL; int err; @@ -1235,22 +1374,23 @@ if (!PyArg_ParseTuple(args, "s:eval", &script)) return NULL; - CHECK_TCL_APPARTMENT; - - ENTER_TCL + CHECK_TCL_APPARTMENT(gstate); + + ENTER_TCL(gstate) err = Tcl_Eval(Tkapp_Interp(self), script); - ENTER_OVERLAP + ENTER_OVERLAP(gstate) if (err == TCL_ERROR) res = Tkinter_Error(self); else res = PyUnicode_FromString(Tkapp_Result(self)); - LEAVE_OVERLAP_TCL + LEAVE_OVERLAP_TCL(gstate) return res; } static PyObject * Tkapp_EvalFile(PyObject *self, PyObject *args) { + _tkinterstate *gstate = Tkapp_gstate(self); char *fileName; PyObject *res = NULL; int err; @@ -1258,58 +1398,58 @@ if (!PyArg_ParseTuple(args, "s:evalfile", &fileName)) return NULL; - CHECK_TCL_APPARTMENT; - - ENTER_TCL + CHECK_TCL_APPARTMENT(gstate); + + ENTER_TCL(gstate) err = Tcl_EvalFile(Tkapp_Interp(self), fileName); - ENTER_OVERLAP - if (err == TCL_ERROR) - res = Tkinter_Error(self); - - else - res = PyUnicode_FromString(Tkapp_Result(self)); - LEAVE_OVERLAP_TCL - return res; -} - -static PyObject * -Tkapp_Record(PyObject *self, PyObject *args) -{ - char *script; - PyObject *res = NULL; - int err; - - if (!PyArg_ParseTuple(args, "s", &script)) - return NULL; - - CHECK_TCL_APPARTMENT; - - ENTER_TCL - err = Tcl_RecordAndEval(Tkapp_Interp(self), script, TCL_NO_EVAL); - ENTER_OVERLAP + ENTER_OVERLAP(gstate) if (err == TCL_ERROR) res = Tkinter_Error(self); else res = PyUnicode_FromString(Tkapp_Result(self)); - LEAVE_OVERLAP_TCL + LEAVE_OVERLAP_TCL(gstate) + return res; +} + +static PyObject * +Tkapp_Record(PyObject *self, PyObject *args) +{ + _tkinterstate *gstate = Tkapp_gstate(self); + char *script; + PyObject *res = NULL; + int err; + + if (!PyArg_ParseTuple(args, "s", &script)) + return NULL; + + CHECK_TCL_APPARTMENT(gstate); + + ENTER_TCL(gstate) + err = Tcl_RecordAndEval(Tkapp_Interp(self), script, TCL_NO_EVAL); + ENTER_OVERLAP(gstate) + if (err == TCL_ERROR) + res = Tkinter_Error(self); + else + res = PyUnicode_FromString(Tkapp_Result(self)); + LEAVE_OVERLAP_TCL(gstate) return res; } static PyObject * Tkapp_AddErrorInfo(PyObject *self, PyObject *args) { + _tkinterstate *gstate = Tkapp_gstate(self); char *msg; if (!PyArg_ParseTuple(args, "s:adderrorinfo", &msg)) return NULL; - CHECK_TCL_APPARTMENT; - - ENTER_TCL + CHECK_TCL_APPARTMENT(gstate); + + ENTER_TCL(gstate) Tcl_AddErrorInfo(Tkapp_Interp(self), msg); - LEAVE_TCL - - Py_INCREF(Py_None); - return Py_None; + LEAVE_TCL(gstate) + + Py_RETURN_NONE; } @@ -1331,11 +1471,12 @@ PyObject **exc_type; PyObject **exc_val; Tcl_Condition *cond; + _tkinterstate *gstate; } VarEvent; #endif static int -varname_converter(PyObject *in, void *_out) +varname_converter(PyObject *in, void *_out, _tkinterstate *gstate) { char **out = (char**)_out; if (PyBytes_Check(in)) { @@ -1346,7 +1487,7 @@ *out = _PyUnicode_AsString(in); return 1; } - if (PyTclObject_Check(in)) { + if (PyTclObject_Check(in, gstate)) { *out = PyTclObject_TclString(in); return 1; } @@ -1374,12 +1515,13 @@ static int var_proc(VarEvent* ev, int flags) { - ENTER_PYTHON + _tkinterstate *gstate = ev->gstate; + ENTER_PYTHON(gstate) var_perform(ev); Tcl_MutexLock(&var_mutex); Tcl_ConditionNotify(ev->cond); Tcl_MutexUnlock(&var_mutex); - LEAVE_PYTHON + LEAVE_PYTHON(gstate) return 1; } @@ -1413,6 +1555,7 @@ ev->exc_val = &exc_val; ev->cond = &cond; ev->ev.proc = (Tcl_EventProc*)var_proc; + ev->gstate = self->gstate; Tkapp_ThreadSend(self, (Tcl_Event*)ev, &cond, &var_mutex); Tcl_ConditionFinalize(&cond); if (!res) { @@ -1431,45 +1574,46 @@ static PyObject * SetVar(PyObject *self, PyObject *args, int flags) { + _tkinterstate *gstate = Tkapp_gstate(self); char *name1, *name2; PyObject *newValue; PyObject *res = NULL; Tcl_Obj *newval, *ok; if (PyArg_ParseTuple(args, "O&O:setvar", - varname_converter, &name1, &newValue)) { + varname_converter, &name1, &newValue, gstate)) { /* XXX Acquire tcl lock??? */ - newval = AsObj(newValue); + newval = AsObj(newValue, gstate); if (newval == NULL) return NULL; - ENTER_TCL + ENTER_TCL(gstate) ok = Tcl_SetVar2Ex(Tkapp_Interp(self), name1, NULL, newval, flags); - ENTER_OVERLAP + ENTER_OVERLAP(gstate) if (!ok) Tkinter_Error(self); else { res = Py_None; Py_INCREF(res); } - LEAVE_OVERLAP_TCL + LEAVE_OVERLAP_TCL(gstate) } else { PyErr_Clear(); if (PyArg_ParseTuple(args, "ssO:setvar", &name1, &name2, &newValue)) { /* XXX must hold tcl lock already??? */ - newval = AsObj(newValue); - ENTER_TCL + newval = AsObj(newValue, gstate); + ENTER_TCL(gstate) ok = Tcl_SetVar2Ex(Tkapp_Interp(self), name1, name2, newval, flags); - ENTER_OVERLAP + ENTER_OVERLAP(gstate) if (!ok) Tkinter_Error(self); else { res = Py_None; Py_INCREF(res); } - LEAVE_OVERLAP_TCL + LEAVE_OVERLAP_TCL(gstate) } else { return NULL; @@ -1495,19 +1639,26 @@ static PyObject * GetVar(PyObject *self, PyObject *args, int flags) { + _tkinterstate *gstate = Tkapp_gstate(self); char *name1, *name2=NULL; PyObject *res = NULL; Tcl_Obj *tres; if (!PyArg_ParseTuple(args, "O&|s:getvar", - varname_converter, &name1, &name2)) + varname_converter, &name1, &name2, gstate)) return NULL; - ENTER_TCL + ENTER_TCL(gstate) tres = Tcl_GetVar2Ex(Tkapp_Interp(self), name1, name2, flags); - ENTER_OVERLAP + ENTER_OVERLAP(gstate) if (tres == NULL) { - PyErr_SetString(Tkinter_TclError, Tcl_GetStringResult(Tkapp_Interp(self))); + if (gstate->Tkinter_TclError == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "No TclError, _tkinter is in shotdowning stage."); + } else { + PyErr_SetString(gstate->Tkinter_TclError, + Tcl_GetStringResult(Tkapp_Interp(self))); + } } else { if (((TkappObject*)self)->wantobjects) { res = FromObj(self, tres); @@ -1516,7 +1667,7 @@ res = PyUnicode_FromString(Tcl_GetString(tres)); } } - LEAVE_OVERLAP_TCL + LEAVE_OVERLAP_TCL(gstate) return res; } @@ -1537,6 +1688,7 @@ static PyObject * UnsetVar(PyObject *self, PyObject *args, int flags) { + _tkinterstate *gstate = Tkapp_gstate(self); char *name1, *name2=NULL; int code; PyObject *res = NULL; @@ -1544,16 +1696,16 @@ if (!PyArg_ParseTuple(args, "s|s:unsetvar", &name1, &name2)) return NULL; - ENTER_TCL + ENTER_TCL(gstate) code = Tcl_UnsetVar2(Tkapp_Interp(self), name1, name2, flags); - ENTER_OVERLAP + ENTER_OVERLAP(gstate) if (code == TCL_ERROR) res = Tkinter_Error(self); else { Py_INCREF(Py_None); res = Py_None; } - LEAVE_OVERLAP_TCL + LEAVE_OVERLAP_TCL(gstate) return res; } @@ -1566,7 +1718,8 @@ static PyObject * Tkapp_GlobalUnsetVar(PyObject *self, PyObject *args) { - return var_invoke(UnsetVar, self, args, TCL_LEAVE_ERR_MSG | TCL_GLOBAL_ONLY); + return var_invoke(UnsetVar, self, args, + TCL_LEAVE_ERR_MSG | TCL_GLOBAL_ONLY); } @@ -1636,6 +1789,7 @@ static PyObject * Tkapp_ExprString(PyObject *self, PyObject *args) { + _tkinterstate *gstate = Tkapp_gstate(self); char *s; PyObject *res = NULL; int retval; @@ -1643,22 +1797,23 @@ if (!PyArg_ParseTuple(args, "s:exprstring", &s)) return NULL; - CHECK_TCL_APPARTMENT; - - ENTER_TCL + CHECK_TCL_APPARTMENT(gstate); + + ENTER_TCL(gstate) retval = Tcl_ExprString(Tkapp_Interp(self), s); - ENTER_OVERLAP + ENTER_OVERLAP(gstate) if (retval == TCL_ERROR) res = Tkinter_Error(self); else res = Py_BuildValue("s", Tkapp_Result(self)); - LEAVE_OVERLAP_TCL + LEAVE_OVERLAP_TCL(gstate) return res; } static PyObject * Tkapp_ExprLong(PyObject *self, PyObject *args) { + _tkinterstate *gstate = Tkapp_gstate(self); char *s; PyObject *res = NULL; int retval; @@ -1667,22 +1822,23 @@ if (!PyArg_ParseTuple(args, "s:exprlong", &s)) return NULL; - CHECK_TCL_APPARTMENT; - - ENTER_TCL + CHECK_TCL_APPARTMENT(gstate); + + ENTER_TCL(gstate) retval = Tcl_ExprLong(Tkapp_Interp(self), s, &v); - ENTER_OVERLAP + ENTER_OVERLAP(gstate) if (retval == TCL_ERROR) res = Tkinter_Error(self); else res = Py_BuildValue("l", v); - LEAVE_OVERLAP_TCL + LEAVE_OVERLAP_TCL(gstate) return res; } static PyObject * Tkapp_ExprDouble(PyObject *self, PyObject *args) { + _tkinterstate *gstate = Tkapp_gstate(self); char *s; PyObject *res = NULL; double v; @@ -1690,23 +1846,24 @@ if (!PyArg_ParseTuple(args, "s:exprdouble", &s)) return NULL; - CHECK_TCL_APPARTMENT; + CHECK_TCL_APPARTMENT(gstate); PyFPE_START_PROTECT("Tkapp_ExprDouble", return 0) - ENTER_TCL + ENTER_TCL(gstate) retval = Tcl_ExprDouble(Tkapp_Interp(self), s, &v); - ENTER_OVERLAP + ENTER_OVERLAP(gstate) PyFPE_END_PROTECT(retval) if (retval == TCL_ERROR) res = Tkinter_Error(self); else res = Py_BuildValue("d", v); - LEAVE_OVERLAP_TCL + LEAVE_OVERLAP_TCL(gstate) return res; } static PyObject * Tkapp_ExprBoolean(PyObject *self, PyObject *args) { + _tkinterstate *gstate = Tkapp_gstate(self); char *s; PyObject *res = NULL; int retval; @@ -1714,15 +1871,15 @@ if (!PyArg_ParseTuple(args, "s:exprboolean", &s)) return NULL; - CHECK_TCL_APPARTMENT; - ENTER_TCL + CHECK_TCL_APPARTMENT(gstate); + ENTER_TCL(gstate) retval = Tcl_ExprBoolean(Tkapp_Interp(self), s, &v); - ENTER_OVERLAP + ENTER_OVERLAP(gstate) if (retval == TCL_ERROR) res = Tkinter_Error(self); else res = Py_BuildValue("i", v); - LEAVE_OVERLAP_TCL + LEAVE_OVERLAP_TCL(gstate) return res; } @@ -1799,14 +1956,17 @@ typedef struct { PyObject *self; PyObject *func; + _tkinterstate *gstate; } PythonCmd_ClientData; static int -PythonCmd_Error(Tcl_Interp *interp) +PythonCmd_Error(Tcl_Interp *interp, _tkinterstate *gstate) { - errorInCmd = 1; - PyErr_Fetch(&excInCmd, &valInCmd, &trbInCmd); - LEAVE_PYTHON + gstate->errorInCmd = 1; + PyErr_Fetch(&gstate->excInCmd, + &gstate->valInCmd, + &gstate->trbInCmd); + LEAVE_PYTHON(gstate) return TCL_ERROR; } @@ -1817,11 +1977,12 @@ PythonCmd(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]) { PythonCmd_ClientData *data = (PythonCmd_ClientData *)clientData; + _tkinterstate *gstate = data->gstate; PyObject *func, *arg, *res; int i, rv; Tcl_Obj *obj_res; - ENTER_PYTHON + ENTER_PYTHON(gstate) /* TBD: no error checking here since we know, via the * Tkapp_CreateCommand() that the client data is a two-tuple @@ -1830,7 +1991,7 @@ /* Create argument list (argv1, ..., argvN) */ if (!(arg = PyTuple_New(argc - 1))) - return PythonCmd_Error(interp); + return PythonCmd_Error(interp, gstate); for (i = 0; i < (argc - 1); i++) { PyObject *s = PyUnicode_FromString(argv[i + 1]); @@ -1843,24 +2004,24 @@ s = PyUnicode_FromString("\0"); } else { Py_DECREF(arg); - return PythonCmd_Error(interp); + return PythonCmd_Error(interp, gstate); } } if (PyTuple_SetItem(arg, i, s)) { Py_DECREF(arg); - return PythonCmd_Error(interp); + return PythonCmd_Error(interp, gstate); } } res = PyEval_CallObject(func, arg); Py_DECREF(arg); if (res == NULL) - return PythonCmd_Error(interp); - - obj_res = AsObj(res); + return PythonCmd_Error(interp, gstate); + + obj_res = AsObj(res, gstate); if (obj_res == NULL) { Py_DECREF(res); - return PythonCmd_Error(interp); + return PythonCmd_Error(interp, gstate); } else { Tcl_SetObjResult(interp, obj_res); @@ -1868,9 +2029,7 @@ } Py_DECREF(res); - - LEAVE_PYTHON - + LEAVE_PYTHON(gstate) return rv; } @@ -1878,12 +2037,13 @@ PythonCmdDelete(ClientData clientData) { PythonCmd_ClientData *data = (PythonCmd_ClientData *)clientData; - - ENTER_PYTHON + _tkinterstate *gstate = data->gstate; + + ENTER_PYTHON(gstate) Py_XDECREF(data->self); Py_XDECREF(data->func); PyMem_DEL(data); - LEAVE_PYTHON + LEAVE_PYTHON(gstate) } @@ -1922,6 +2082,7 @@ Tkapp_CreateCommand(PyObject *selfptr, PyObject *args) { TkappObject *self = (TkappObject*)selfptr; + _tkinterstate *gstate = self->gstate; PythonCmd_ClientData *data; char *cmdName; PyObject *func; @@ -1947,6 +2108,7 @@ Py_INCREF(func); data->self = selfptr; data->func = func; + data->gstate = gstate; #ifdef WITH_THREAD if (self->threaded && self->thread_id != Tcl_GetCurrentThread()) { Tcl_Condition cond = NULL; @@ -1964,20 +2126,23 @@ else #endif { - ENTER_TCL + ENTER_TCL(gstate) err = Tcl_CreateCommand( Tkapp_Interp(self), cmdName, PythonCmd, (ClientData)data, PythonCmdDelete) == NULL; - LEAVE_TCL + LEAVE_TCL(gstate) } if (err) { - PyErr_SetString(Tkinter_TclError, "can't create Tcl command"); + if (gstate->Tkinter_TclError == NULL) + PyErr_SetString(PyExc_RuntimeError, + "No TclError, _tkinter is in shotdowning stage."); + else + PyErr_SetString(gstate->Tkinter_TclError, "can't create Tcl command"); PyMem_DEL(data); return NULL; } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } @@ -1986,6 +2151,7 @@ Tkapp_DeleteCommand(PyObject *selfptr, PyObject *args) { TkappObject *self = (TkappObject*)selfptr; + _tkinterstate *gstate = self->gstate; char *cmdName; int err; @@ -2010,34 +2176,27 @@ else #endif { - ENTER_TCL + ENTER_TCL(gstate) err = Tcl_DeleteCommand(self->interp, cmdName); - LEAVE_TCL + LEAVE_TCL(gstate) } if (err == -1) { - PyErr_SetString(Tkinter_TclError, "can't delete Tcl command"); + if (gstate->Tkinter_TclError == NULL) + PyErr_SetString(PyExc_RuntimeError, + "No TclError, _tkinter is in shotdowning stage."); + else + PyErr_SetString(gstate->Tkinter_TclError, "can't delete Tcl command"); return NULL; } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } - #ifdef HAVE_CREATEFILEHANDLER /** File Handler **/ -typedef struct _fhcdata { - PyObject *func; - PyObject *file; - int id; - struct _fhcdata *next; -} FileHandler_ClientData; - -static FileHandler_ClientData *HeadFHCD; - static FileHandler_ClientData * -NewFHCD(PyObject *func, PyObject *file, int id) +NewFHCD(PyObject *func, PyObject *file, int id, _tkinterstate *gstate) { FileHandler_ClientData *p; p = PyMem_NEW(FileHandler_ClientData, 1); @@ -2047,18 +2206,19 @@ p->func = func; p->file = file; p->id = id; - p->next = HeadFHCD; - HeadFHCD = p; + p->next = gstate->HeadFHCD; + p->gstate = gstate; + gstate->HeadFHCD = p; } return p; } static void -DeleteFHCD(int id) +DeleteFHCD(int id, _tkinterstate *gstate) { FileHandler_ClientData *p, **pp; - pp = &HeadFHCD; + pp = &gstate->HeadFHCD; while ((p = *pp) != NULL) { if (p->id == id) { *pp = p->next; @@ -2075,9 +2235,10 @@ FileHandler(ClientData clientData, int mask) { FileHandler_ClientData *data = (FileHandler_ClientData *)clientData; + _tkinterstate *gstate = data->gstate; PyObject *func, *file, *arg, *res; - ENTER_PYTHON + ENTER_PYTHON(gstate) func = data->func; file = data->file; @@ -2086,17 +2247,20 @@ Py_DECREF(arg); if (res == NULL) { - errorInCmd = 1; - PyErr_Fetch(&excInCmd, &valInCmd, &trbInCmd); + gstate->errorInCmd = 1; + PyErr_Fetch(&gstate->excInCmd, + &gstate->valInCmd, + &gstate->trbInCmd); } Py_XDECREF(res); - LEAVE_PYTHON + LEAVE_PYTHON(gstate) } static PyObject * Tkapp_CreateFileHandler(PyObject *self, PyObject *args) /* args is (file, mask, func) */ { + _tkinterstate *gstate = Tkapp_gstate(self); FileHandler_ClientData *data; PyObject *file, *func; int mask, tfile; @@ -2105,7 +2269,7 @@ &file, &mask, &func)) return NULL; - CHECK_TCL_APPARTMENT; + CHECK_TCL_APPARTMENT(gstate); tfile = PyObject_AsFileDescriptor(file); if (tfile < 0) @@ -2115,53 +2279,51 @@ return NULL; } - data = NewFHCD(func, file, tfile); + data = NewFHCD(func, file, tfile, gstate); if (data == NULL) return NULL; /* Ought to check for null Tcl_File object... */ - ENTER_TCL + ENTER_TCL(gstate) Tcl_CreateFileHandler(tfile, mask, FileHandler, (ClientData) data); - LEAVE_TCL - Py_INCREF(Py_None); - return Py_None; + LEAVE_TCL(gstate) + Py_RETURN_NONE; } static PyObject * Tkapp_DeleteFileHandler(PyObject *self, PyObject *args) { + _tkinterstate *gstate = Tkapp_gstate(self); PyObject *file; int tfile; if (!PyArg_ParseTuple(args, "O:deletefilehandler", &file)) return NULL; - CHECK_TCL_APPARTMENT; + CHECK_TCL_APPARTMENT(gstate); tfile = PyObject_AsFileDescriptor(file); if (tfile < 0) return NULL; - DeleteFHCD(tfile); + DeleteFHCD(tfile, gstate); /* Ought to check for null Tcl_File object... */ - ENTER_TCL + ENTER_TCL(gstate) Tcl_DeleteFileHandler(tfile); - LEAVE_TCL - Py_INCREF(Py_None); - return Py_None; + LEAVE_TCL(gstate) + Py_RETURN_NONE; } #endif /* HAVE_CREATEFILEHANDLER */ /**** Tktt Object (timer token) ****/ -static PyTypeObject Tktt_Type; - typedef struct { PyObject_HEAD Tcl_TimerToken token; PyObject *func; + _tkinterstate *gstate; } TkttObject; static PyObject * @@ -2192,17 +2354,24 @@ }; static TkttObject * -Tktt_New(PyObject *func) +Tktt_New(PyObject *func, _tkinterstate *gstate) { TkttObject *v; - v = PyObject_New(TkttObject, &Tktt_Type); + if (gstate->Tktt_Type == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "No TkttType, _tkinter is in shotdowning stage."); + return NULL; + } + v = PyObject_New(TkttObject, (PyTypeObject *)gstate->Tktt_Type); if (v == NULL) return NULL; + Py_INCREF(gstate->Tktt_Type); Py_INCREF(func); v->token = NULL; v->func = func; + v->gstate = gstate; /* Extra reference, deleted when called or when handler is deleted */ Py_INCREF(v); @@ -2212,11 +2381,15 @@ static void Tktt_Dealloc(PyObject *self) { + PyTypeObject *type = Py_TYPE(self); TkttObject *v = (TkttObject *)self; PyObject *func = v->func; Py_XDECREF(func); + if ((void *)type->tp_dealloc == (void *)Tktt_Dealloc) { + Py_DECREF(type); + } PyObject_Del(self); } @@ -2229,38 +2402,25 @@ v->func == NULL ? ", handler deleted" : ""); } -static PyTypeObject Tktt_Type = -{ - PyVarObject_HEAD_INIT(NULL, 0) - "tktimertoken", /*tp_name */ - sizeof(TkttObject), /*tp_basicsize */ - 0, /*tp_itemsize */ - Tktt_Dealloc, /*tp_dealloc */ - 0, /*tp_print */ - 0, /*tp_getattr */ - 0, /*tp_setattr */ - 0, /*tp_reserved */ - Tktt_Repr, /*tp_repr */ - 0, /*tp_as_number */ - 0, /*tp_as_sequence */ - 0, /*tp_as_mapping */ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - Tktt_methods, /*tp_methods*/ + + + +static PyType_Slot Tktt_Type_slots[] = { + {Py_tp_dealloc, Tktt_Dealloc}, + {Py_tp_repr, Tktt_Repr}, + {Py_tp_methods, Tktt_methods}, + {0, 0} }; +static PyType_Spec Tktt_Type_spec = { + "tktimertoken", + sizeof(TkttObject), + 0, + Py_TPFLAGS_DEFAULT, + Tktt_Type_slots, +}; + + /** Timer Handler **/ @@ -2269,6 +2429,7 @@ TimerHandler(ClientData clientData) { TkttObject *v = (TkttObject *)clientData; + _tkinterstate *gstate = v->gstate; PyObject *func = v->func; PyObject *res; @@ -2277,25 +2438,28 @@ v->func = NULL; - ENTER_PYTHON + ENTER_PYTHON(gstate) res = PyEval_CallObject(func, NULL); Py_DECREF(func); Py_DECREF(v); /* See Tktt_New() */ if (res == NULL) { - errorInCmd = 1; - PyErr_Fetch(&excInCmd, &valInCmd, &trbInCmd); + gstate->errorInCmd = 1; + PyErr_Fetch(&gstate->excInCmd, + &gstate->valInCmd, + &gstate->trbInCmd); } else Py_DECREF(res); - LEAVE_PYTHON + LEAVE_PYTHON(gstate) } static PyObject * Tkapp_CreateTimerHandler(PyObject *self, PyObject *args) { + _tkinterstate *gstate = Tkapp_gstate(self); int milliseconds; PyObject *func; TkttObject *v; @@ -2308,9 +2472,9 @@ return NULL; } - CHECK_TCL_APPARTMENT; - - v = Tktt_New(func); + CHECK_TCL_APPARTMENT(gstate); + + v = Tktt_New(func, gstate); if (v) { v->token = Tcl_CreateTimerHandler(milliseconds, TimerHandler, (ClientData)v); @@ -2327,6 +2491,7 @@ { int threshold = 0; TkappObject *self = (TkappObject*)selfptr; + _tkinterstate *gstate = self->gstate; #ifdef WITH_THREAD PyThreadState *tstate = PyThreadState_Get(); #endif @@ -2334,32 +2499,34 @@ if (!PyArg_ParseTuple(args, "|i:mainloop", &threshold)) return NULL; - CHECK_TCL_APPARTMENT; + CHECK_TCL_APPARTMENT(gstate); self->dispatching = 1; - quitMainLoop = 0; + gstate->quitMainLoop = 0; while (Tk_GetNumMainWindows() > threshold && - !quitMainLoop && - !errorInCmd) + !gstate->quitMainLoop && + !gstate->errorInCmd) { int result; #ifdef WITH_THREAD if (self->threaded) { /* Allow other Python threads to run. */ - ENTER_TCL + ENTER_TCL(gstate) result = Tcl_DoOneEvent(0); - LEAVE_TCL + LEAVE_TCL(gstate) } else { Py_BEGIN_ALLOW_THREADS - if(tcl_lock)PyThread_acquire_lock(tcl_lock, 1); - tcl_tstate = tstate; + if (gstate->tcl_lock) + PyThread_acquire_lock(gstate->tcl_lock, 1); + set_tcl_tstate(gstate, tstate); result = Tcl_DoOneEvent(TCL_DONT_WAIT); - tcl_tstate = NULL; - if(tcl_lock)PyThread_release_lock(tcl_lock); + set_tcl_tstate(gstate, NULL); + if (gstate->tcl_lock) + PyThread_release_lock(gstate->tcl_lock); if (result == 0) - Sleep(Tkinter_busywaitinterval); + Sleep(gstate->Tkinter_busywaitinterval); Py_END_ALLOW_THREADS } #else @@ -2374,30 +2541,34 @@ break; } self->dispatching = 0; - quitMainLoop = 0; - - if (errorInCmd) { - errorInCmd = 0; - PyErr_Restore(excInCmd, valInCmd, trbInCmd); - excInCmd = valInCmd = trbInCmd = NULL; + gstate->quitMainLoop = 0; + + if (gstate->errorInCmd) { + gstate->errorInCmd = 0; + PyErr_Restore(gstate->excInCmd, + gstate->valInCmd, + gstate->trbInCmd); + gstate->excInCmd = NULL; + gstate->valInCmd = NULL; + gstate->trbInCmd = NULL; return NULL; } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } static PyObject * Tkapp_DoOneEvent(PyObject *self, PyObject *args) { + _tkinterstate *gstate = Tkapp_gstate(self); int flags = 0; int rv; if (!PyArg_ParseTuple(args, "|i:dooneevent", &flags)) return NULL; - ENTER_TCL + ENTER_TCL(gstate) rv = Tcl_DoOneEvent(flags); - LEAVE_TCL + LEAVE_TCL(gstate) return Py_BuildValue("i", rv); } @@ -2408,9 +2579,8 @@ if (!PyArg_ParseTuple(args, ":quit")) return NULL; - quitMainLoop = 1; - Py_INCREF(Py_None); - return Py_None; + Tkapp_gstate(self)->quitMainLoop = 1; + Py_RETURN_NONE; } static PyObject * @@ -2426,27 +2596,34 @@ static PyObject * Tkapp_TkInit(PyObject *self, PyObject *args) { + _tkinterstate *gstate = Tkapp_gstate(self); Tcl_Interp *interp = Tkapp_Interp(self); const char * _tk_exists = NULL; int err; + if (gstate->Tkinter_TclError == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "No TclError, _tkinter is in shotdowning stage."); + return NULL; + } + #ifdef TKINTER_PROTECT_LOADTK /* Up to Tk 8.4.13, Tk_Init deadlocks on the second call when the * first call failed. * To avoid the deadlock, we just refuse the second call through * a static variable. */ - if (tk_load_failed) { - PyErr_SetString(Tkinter_TclError, TKINTER_LOADTK_ERRMSG); + if (gstate->tk_load_failed) { + PyErr_SetString(gstate->Tkinter_TclError, TKINTER_LOADTK_ERRMSG); return NULL; } #endif /* We want to guard against calling Tk_Init() multiple times */ - CHECK_TCL_APPARTMENT; - ENTER_TCL + CHECK_TCL_APPARTMENT(gstate); + ENTER_TCL(gstate) err = Tcl_Eval(Tkapp_Interp(self), "info exists tk_version"); - ENTER_OVERLAP + ENTER_OVERLAP(gstate) if (err == TCL_ERROR) { /* This sets an exception, but we cannot return right away because we need to exit the overlap first. */ @@ -2454,27 +2631,26 @@ } else { _tk_exists = Tkapp_Result(self); } - LEAVE_OVERLAP_TCL + LEAVE_OVERLAP_TCL(gstate) if (err == TCL_ERROR) { return NULL; } if (_tk_exists == NULL || strcmp(_tk_exists, "1") != 0) { - if (Tk_Init(interp) == TCL_ERROR) { - PyErr_SetString(Tkinter_TclError, Tcl_GetStringResult(Tkapp_Interp(self))); + if (Tk_Init(interp) == TCL_ERROR) { + PyErr_SetString(gstate->Tkinter_TclError, + Tcl_GetStringResult(Tkapp_Interp(self))); #ifdef TKINTER_PROTECT_LOADTK - tk_load_failed = 1; + gstate->tk_load_failed = 1; #endif return NULL; } } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } static PyObject * Tkapp_WantObjects(PyObject *self, PyObject *args) { - int wantobjects = -1; if (!PyArg_ParseTuple(args, "|i:wantobjects", &wantobjects)) return NULL; @@ -2482,18 +2658,15 @@ return PyBool_FromLong(((TkappObject*)self)->wantobjects); ((TkappObject*)self)->wantobjects = wantobjects; - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } static PyObject * Tkapp_WillDispatch(PyObject *self, PyObject *args) { - ((TkappObject*)self)->dispatching = 1; - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } @@ -2545,47 +2718,32 @@ static void Tkapp_Dealloc(PyObject *self) { + _tkinterstate *gstate = Tkapp_gstate(self); + PyTypeObject *type = Py_TYPE(self); /*CHECK_TCL_APPARTMENT;*/ - ENTER_TCL + ENTER_TCL(gstate) Tcl_DeleteInterp(Tkapp_Interp(self)); - LEAVE_TCL + LEAVE_TCL(gstate) PyObject_Del(self); DisableEventHook(); + if ((void *)type->tp_dealloc == (void *)Tkapp_Dealloc) { + Py_DECREF(type); + } } -static PyTypeObject Tkapp_Type = -{ - PyVarObject_HEAD_INIT(NULL, 0) - "tkapp", /*tp_name */ - sizeof(TkappObject), /*tp_basicsize */ - 0, /*tp_itemsize */ - Tkapp_Dealloc, /*tp_dealloc */ - 0, /*tp_print */ - 0, /*tp_getattr */ - 0, /*tp_setattr */ - 0, /*tp_reserved */ - 0, /*tp_repr */ - 0, /*tp_as_number */ - 0, /*tp_as_sequence */ - 0, /*tp_as_mapping */ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - Tkapp_methods, /*tp_methods*/ +static PyType_Slot Tkapp_Type_slots[] = { + {Py_tp_dealloc, Tkapp_Dealloc}, + {Py_tp_methods, Tkapp_methods}, + {0, 0} }; - +static PyType_Spec Tkapp_Type_spec = { + "tkapp", + sizeof(TkappObject), + 0, + Py_TPFLAGS_DEFAULT, + Tkapp_Type_slots, +}; /**** Tkinter Module ****/ @@ -2712,6 +2870,7 @@ int wantTk = 1; /* If false, then Tk_Init() doesn't get called */ int sync = 0; /* pass -sync to wish */ char *use = NULL; /* pass -use to wish */ + _tkinterstate *gstate; className = "Tk"; @@ -2721,9 +2880,10 @@ &sync, &use)) return NULL; + gstate = _tkinterstate_global; return (PyObject *) Tkapp_New(screenName, className, - interactive, wantobjects, wantTk, - sync, use); + interactive, wantobjects, wantTk, + sync, use, gstate); } static PyObject * @@ -2737,9 +2897,8 @@ "busywaitinterval must be >= 0"); return NULL; } - Tkinter_busywaitinterval = new_val; - Py_INCREF(Py_None); - return Py_None; + _tkinterstate_global->Tkinter_busywaitinterval = new_val; + Py_RETURN_NONE; } static char setbusywaitinterval_doc[] = @@ -2753,7 +2912,7 @@ static PyObject * Tkinter_getbusywaitinterval(PyObject *self, PyObject *args) { - return PyLong_FromLong(Tkinter_busywaitinterval); + return PyLong_FromLong(_tkinterstate_global->Tkinter_busywaitinterval); } static char getbusywaitinterval_doc[] = @@ -2786,25 +2945,29 @@ #endif #ifdef WITH_THREAD +/* Have to keep global variable because PyOS_InputHook has no state + and EventHook called without acquired python thread */ static PyThreadState *event_tstate = NULL; #endif static int EventHook(void) { + _tkinterstate *gstate; #ifndef MS_WINDOWS int tfile; #endif #ifdef WITH_THREAD PyEval_RestoreThread(event_tstate); #endif + gstate = _tkinterstate_global; stdin_ready = 0; - errorInCmd = 0; + gstate->errorInCmd = 0; #ifndef MS_WINDOWS tfile = fileno(stdin); Tcl_CreateFileHandler(tfile, TCL_READABLE, MyFileProc, NULL); #endif - while (!errorInCmd && !stdin_ready) { + while (!gstate->errorInCmd && !stdin_ready) { int result; #ifdef MS_WINDOWS if (_kbhit()) { @@ -2814,15 +2977,17 @@ #endif #if defined(WITH_THREAD) || defined(MS_WINDOWS) Py_BEGIN_ALLOW_THREADS - if(tcl_lock)PyThread_acquire_lock(tcl_lock, 1); - tcl_tstate = event_tstate; + if (gstate->tcl_lock) + PyThread_acquire_lock(gstate->tcl_lock, 1); + set_tcl_tstate(gstate, event_tstate); result = Tcl_DoOneEvent(TCL_DONT_WAIT); - tcl_tstate = NULL; - if(tcl_lock)PyThread_release_lock(tcl_lock); + set_tcl_tstate(gstate, NULL); + if (gstate->tcl_lock) + PyThread_release_lock(gstate->tcl_lock); if (result == 0) - Sleep(Tkinter_busywaitinterval); + Sleep(gstate->Tkinter_busywaitinterval); Py_END_ALLOW_THREADS #else result = Tcl_DoOneEvent(0); @@ -2834,10 +2999,14 @@ #ifndef MS_WINDOWS Tcl_DeleteFileHandler(tfile); #endif - if (errorInCmd) { - errorInCmd = 0; - PyErr_Restore(excInCmd, valInCmd, trbInCmd); - excInCmd = valInCmd = trbInCmd = NULL; + if (gstate->errorInCmd) { + gstate->errorInCmd = 0; + PyErr_Restore(gstate->excInCmd, + gstate->valInCmd, + gstate->trbInCmd); + gstate->excInCmd = NULL; + gstate->valInCmd = NULL; + gstate->trbInCmd = NULL; PyErr_Print(); } #ifdef WITH_THREAD @@ -2872,81 +3041,85 @@ } -/* all errors will be checked in one fell swoop in init_tkinter() */ -static void -ins_long(PyObject *d, char *name, long val) -{ - PyObject *v = PyLong_FromLong(val); - if (v) { - PyDict_SetItemString(d, name, v); - Py_DECREF(v); - } -} -static void -ins_string(PyObject *d, char *name, char *val) -{ - PyObject *v = PyUnicode_FromString(val); - if (v) { - PyDict_SetItemString(d, name, v); - Py_DECREF(v); - } -} - - static struct PyModuleDef _tkintermodule = { PyModuleDef_HEAD_INIT, "_tkinter", NULL, - -1, + sizeof(_tkinterstate), moduleMethods, NULL, - NULL, - NULL, - NULL + _tkinter_traverse, + _tkinter_clear, + _tkinter_free }; PyMODINIT_FUNC PyInit__tkinter(void) { - PyObject *m, *d, *uexe, *cexe; - - if (PyType_Ready(&Tkapp_Type) < 0) - return NULL; - -#ifdef WITH_THREAD - tcl_lock = PyThread_allocate_lock(); -#endif + PyObject *m, *uexe, *cexe; + _tkinterstate *gstate; m = PyModule_Create(&_tkintermodule); if (m == NULL) return NULL; - d = PyModule_GetDict(m); - Tkinter_TclError = PyErr_NewException("_tkinter.TclError", NULL, NULL); - PyDict_SetItemString(d, "TclError", Tkinter_TclError); - - ins_long(d, "READABLE", TCL_READABLE); - ins_long(d, "WRITABLE", TCL_WRITABLE); - ins_long(d, "EXCEPTION", TCL_EXCEPTION); - ins_long(d, "WINDOW_EVENTS", TCL_WINDOW_EVENTS); - ins_long(d, "FILE_EVENTS", TCL_FILE_EVENTS); - ins_long(d, "TIMER_EVENTS", TCL_TIMER_EVENTS); - ins_long(d, "IDLE_EVENTS", TCL_IDLE_EVENTS); - ins_long(d, "ALL_EVENTS", TCL_ALL_EVENTS); - ins_long(d, "DONT_WAIT", TCL_DONT_WAIT); - ins_string(d, "TK_VERSION", TK_VERSION); - ins_string(d, "TCL_VERSION", TCL_VERSION); - - PyDict_SetItemString(d, "TkappType", (PyObject *)&Tkapp_Type); - - if (PyType_Ready(&Tktt_Type) < 0) { - Py_DECREF(m); - return NULL; - } - PyDict_SetItemString(d, "TkttType", (PyObject *)&Tktt_Type); - - Py_TYPE(&PyTclObject_Type) = &PyType_Type; - PyDict_SetItemString(d, "Tcl_Obj", (PyObject *)&PyTclObject_Type); + gstate = _tkinter_state(m); +#ifdef WITH_THREAD + gstate->tcl_lock = PyThread_allocate_lock(); +#endif + gstate->Tkinter_busywaitinterval = 20; + + gstate->Tkapp_Type = PyType_FromSpec(&Tkapp_Type_spec); + if (gstate->Tkapp_Type == NULL) + goto fail; + if (PyModule_AddObject(m, "TkappType", gstate->Tkapp_Type)) + goto fail; + Py_INCREF(gstate->Tkapp_Type); + + gstate->Tkinter_TclError = PyErr_NewException("_tkinter.TclError", + NULL, NULL); + if (gstate->Tkinter_TclError == NULL) + goto fail; + if (PyModule_AddObject(m, "TclError", gstate->Tkinter_TclError)) + goto fail; + Py_INCREF(gstate->Tkinter_TclError); + + if (PyModule_AddIntConstant(m, "READABLE", TCL_READABLE)) + goto fail; + if (PyModule_AddIntConstant(m, "WRITABLE", TCL_WRITABLE)) + goto fail; + if (PyModule_AddIntConstant(m, "EXCEPTION", TCL_EXCEPTION)) + goto fail; + if (PyModule_AddIntConstant(m, "WINDOW_EVENTS", TCL_WINDOW_EVENTS)) + goto fail; + if (PyModule_AddIntConstant(m, "FILE_EVENTS", TCL_FILE_EVENTS)) + goto fail; + if (PyModule_AddIntConstant(m, "TIMER_EVENTS", TCL_TIMER_EVENTS)) + goto fail; + if (PyModule_AddIntConstant(m, "IDLE_EVENTS", TCL_IDLE_EVENTS)) + goto fail; + if (PyModule_AddIntConstant(m, "ALL_EVENTS", TCL_ALL_EVENTS)) + goto fail; + if (PyModule_AddIntConstant(m, "DONT_WAIT", TCL_DONT_WAIT)) + goto fail; + if (PyModule_AddStringConstant(m, "TK_VERSION", TK_VERSION)) + goto fail; + if (PyModule_AddStringConstant(m, "TCL_VERSION", TCL_VERSION)) + goto fail; + + gstate->Tktt_Type = PyType_FromSpec(&Tktt_Type_spec); + if (gstate->Tktt_Type == NULL) + goto fail; + if (PyModule_AddObject(m, "TkttType", gstate->Tktt_Type)) + goto fail; + Py_INCREF(gstate->Tktt_Type); + + gstate->PyTclObject_Type = PyType_FromSpec(&PyTclObject_Type_spec); + if (gstate->PyTclObject_Type == NULL) + goto fail; + if (PyModule_AddObject(m, "Tcl_Obj", gstate->PyTclObject_Type)) + goto fail; + Py_INCREF(gstate->PyTclObject_Type); #ifdef TK_AQUA /* Tk_MacOSXSetupTkNotifier must be called before Tcl's subsystems @@ -2974,10 +3147,8 @@ Py_DECREF(uexe); } - if (PyErr_Occurred()) { - Py_DECREF(m); - return NULL; - } + if (PyErr_Occurred()) + goto fail; #if 0 /* This was not a good idea; through bindings, @@ -2986,4 +3157,7 @@ Py_AtExit(Tcl_Finalize); #endif return m; +fail: + Py_DECREF(m); + return NULL; }