cinap_lenrekadd hg and python4bb9e2215 years agoHistory
3165 lines · 70.9 KB · C
RawDownload
/***********************************************************Copyright (C) 1994 Steen Lumholt.                         All Rights Reserved ******************************************************************/ /* _tkinter.c -- Interface to libtk.a and libtcl.a. */ /* TCL/TK VERSION INFO: 	Only Tcl/Tk 8.2 and later are supported.  Older versions are not	supported.  (Use Python 2.2 if you cannot upgrade your Tcl/Tk	libraries.)*/ /* XXX Further speed-up ideas, involving Tcl 8.0 features:    - Register a new Tcl type, "Python callable", which can be called more   efficiently and passed to Tcl_EvalObj() directly (if this is possible). */  #include "Python.h"#include <ctype.h> #ifdef WITH_THREAD#include "pythread.h"#endif #ifdef MS_WINDOWS#include <windows.h>#endif /* Allow using this code in Python 2.[12] */#ifndef PyDoc_STRVAR#define PyDoc_STRVAR(name,str) static char name[] = str#endif #ifndef PyMODINIT_FUNC#define PyMODINIT_FUNC void#endif #ifndef PyBool_Check#define PyBool_Check(o)       0#define PyBool_FromLong       PyInt_FromLong#endif /* Starting with Tcl 8.4, many APIs offer const-correctness.  Unfortunately,   making _tkinter correct for this API means to break earlier   versions. USE_COMPAT_CONST allows to make _tkinter work with both 8.4 and   earlier versions. Once Tcl releases before 8.4 don't need to be supported   anymore, this should go. */#define USE_COMPAT_CONST /* If Tcl is compiled for threads, we must also define TCL_THREAD. We define   it always; if Tcl is not threaded, the thread functions in   Tcl are empty.  */#define TCL_THREADS #ifdef TK_FRAMEWORK#include <Tcl/tcl.h>#include <Tk/tk.h>#else#include <tcl.h>#include <tk.h>#endif /* For Tcl 8.2 and 8.3, CONST* is not defined (except on Cygwin). */#ifndef CONST84_RETURN#define CONST84_RETURN#undef CONST#define CONST#endif #define TKMAJORMINOR (TK_MAJOR_VERSION*1000 + TK_MINOR_VERSION) #if TKMAJORMINOR < 8002#error "Tk older than 8.2 not supported"#endif /* Unicode conversion assumes that Tcl_UniChar is two bytes.   We cannot test this directly, so we test UTF-8 size instead,   expecting that TCL_UTF_MAX is changed if Tcl ever supports   either UTF-16 or UCS-4.     Redhat 8 sets TCL_UTF_MAX to 6, and uses wchar_t for    Tcl_Unichar. This is also ok as long as Python uses UCS-4,   as well.*/#if TCL_UTF_MAX != 3 && !(defined(Py_UNICODE_WIDE) && TCL_UTF_MAX==6)#error "unsupported Tcl configuration"#endif #if !(defined(MS_WINDOWS) || defined(__CYGWIN__))#define HAVE_CREATEFILEHANDLER#endif #ifdef HAVE_CREATEFILEHANDLER /* This bit is to ensure that TCL_UNIX_FD is defined and doesn't interfere   with the proper calculation of FHANDLETYPE == TCL_UNIX_FD below. */#ifndef TCL_UNIX_FD#  ifdef TCL_WIN_SOCKET#    define TCL_UNIX_FD (! TCL_WIN_SOCKET)#  else#    define TCL_UNIX_FD 1#  endif#endif /* Tcl_CreateFileHandler() changed several times; these macros deal with the   messiness.  In Tcl 8.0 and later, it is not available on Windows (and on   Unix, only because Jack added it back); when available on Windows, it only   applies to sockets. */ #ifdef MS_WINDOWS#define FHANDLETYPE TCL_WIN_SOCKET#else#define FHANDLETYPE TCL_UNIX_FD#endif /* If Tcl can wait for a Unix file descriptor, define the EventHook() routine   which uses this to handle Tcl events while the user is typing commands. */ #if FHANDLETYPE == TCL_UNIX_FD#define WAIT_FOR_STDIN#endif #endif /* HAVE_CREATEFILEHANDLER */ #ifdef MS_WINDOWS#include <conio.h>#define WAIT_FOR_STDIN#endif #ifdef WITH_THREAD /* The threading situation is complicated.  Tcl is not thread-safe, except   when configured with --enable-threads.   So we need to use a lock around all uses of Tcl.  Previously, the Python   interpreter lock was used for this.  However, this causes problems when   other Python threads need to run while Tcl is blocked waiting for events.    To solve this problem, a separate lock for Tcl is introduced.  Holding it   is incompatible with holding Python's interpreter lock.  The following four   macros manipulate both locks together.    ENTER_TCL and LEAVE_TCL are brackets, just like Py_BEGIN_ALLOW_THREADS and   Py_END_ALLOW_THREADS.  They should be used whenever a call into Tcl is made   that could call an event handler, or otherwise affect the state of a Tcl   interpreter.  These assume that the surrounding code has the Python   interpreter lock; inside the brackets, the Python interpreter lock has been   released and the lock for Tcl has been acquired.    Sometimes, it is necessary to have both the Python lock and the Tcl lock.   (For example, when transferring data from the Tcl interpreter result to a   Python string object.)  This can be done by using different macros to close   the ENTER_TCL block: ENTER_OVERLAP reacquires the Python lock (and restores   the thread state) but doesn't release the Tcl lock; LEAVE_OVERLAP_TCL   releases the Tcl lock.    By contrast, ENTER_PYTHON and LEAVE_PYTHON are used in Tcl event   handlers when the handler needs to use Python.  Such event handlers are   entered while the lock for Tcl is held; the event handler presumably needs   to use Python.  ENTER_PYTHON releases the lock for Tcl and acquires   the Python interpreter lock, restoring the appropriate thread state, and   LEAVE_PYTHON releases the Python interpreter lock and re-acquires the lock   for Tcl.  It is okay for ENTER_TCL/LEAVE_TCL pairs to be contained inside   the code between ENTER_PYTHON and LEAVE_PYTHON.    These locks expand to several statements and brackets; they should not be   used in branches of if statements and the like.    If Tcl is threaded, this approach won't work anymore. The Tcl interpreter is   only valid in the thread that created it, and all Tk activity must happen in this   thread, also. That means that the mainloop must be invoked in the thread that   created the interpreter. Invoking commands from other threads is possible;   _tkinter will queue an event for the interpreter thread, which will then   execute the command and pass back the result. If the main thread is not in the   mainloop, and invoking commands causes an exception; if the main loop is running   but not processing events, the command invocation will block.    In addition, for a threaded Tcl, a single global tcl_tstate won't be sufficient   anymore, since multiple Tcl interpreters may simultaneously dispatch in different   threads. So we use the Tcl TLS API. */ static PyThread_type_lock tcl_lock = 0; #ifdef TCL_THREADSstatic Tcl_ThreadDataKey state_key;typedef PyThreadState *ThreadSpecificData;#define tcl_tstate (*(PyThreadState**)Tcl_GetThreadData(&state_key, sizeof(PyThreadState*)))#elsestatic PyThreadState *tcl_tstate = NULL;#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 \	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; \	} #else #define ENTER_TCL#define LEAVE_TCL#define ENTER_OVERLAP#define LEAVE_OVERLAP_TCL#define ENTER_PYTHON#define LEAVE_PYTHON#define CHECK_TCL_APPARTMENT #endif #ifndef FREECAST#define FREECAST (char *)#endif /**** Tkapp Object Declaration ****/ static PyTypeObject Tkapp_Type; typedef struct {	PyObject_HEAD	Tcl_Interp *interp;	int wantobjects;	int threaded; /* True if tcl_platform[threaded] */	Tcl_ThreadId thread_id;	int dispatching;	/* We cannot include tclInt.h, as this is internal.	   So we cache interesting types here. */	Tcl_ObjType *BooleanType;	Tcl_ObjType *ByteArrayType;	Tcl_ObjType *DoubleType;	Tcl_ObjType *IntType;	Tcl_ObjType *ListType;	Tcl_ObjType *ProcBodyType;	Tcl_ObjType *StringType;} TkappObject; #define Tkapp_Check(v) ((v)->ob_type == &Tkapp_Type)#define Tkapp_Interp(v) (((TkappObject *) (v))->interp)#define Tkapp_Result(v) Tcl_GetStringResult(Tkapp_Interp(v)) #define DEBUG_REFCNT(v) (printf("DEBUG: id=%p, refcnt=%i\n", \(void *) v, ((PyObject *) v)->ob_refcnt))  /**** Error Handling ****/ static PyObject *Tkinter_TclError;static int quitMainLoop = 0;static int errorInCmd = 0;static PyObject *excInCmd;static PyObject *valInCmd;static PyObject *trbInCmd;  static PyObject *Tkinter_Error(PyObject *v){	PyErr_SetString(Tkinter_TclError, Tkapp_Result(v));	return NULL;}  /**** Utils ****/ static int Tkinter_busywaitinterval = 20; #ifdef WITH_THREAD#ifndef MS_WINDOWS /* Millisecond sleep() for Unix platforms. */ static voidSleep(int milli){	/* XXX Too bad if you don't have select(). */	struct timeval t;	t.tv_sec = milli/1000;	t.tv_usec = (milli%1000) * 1000;	select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &t);}#endif /* MS_WINDOWS */ /* Wait up to 1s for the mainloop to come up. */ static intWaitForMainloop(TkappObject* self){	int i;	for (i = 0; i < 10; i++) {		if (self->dispatching)			return 1;		Py_BEGIN_ALLOW_THREADS		Sleep(100);		Py_END_ALLOW_THREADS	}	if (self->dispatching)		return 1;	PyErr_SetString(PyExc_RuntimeError, "main thread is not in main loop");	return 0;}#endif /* WITH_THREAD */ static char *AsString(PyObject *value, PyObject *tmp){	if (PyString_Check(value))		return PyString_AsString(value);#ifdef Py_USING_UNICODE	else if (PyUnicode_Check(value)) {		PyObject *v = PyUnicode_AsUTF8String(value);		if (v == NULL)			return NULL;		if (PyList_Append(tmp, v) != 0) {			Py_DECREF(v);			return NULL;		}		Py_DECREF(v);		return PyString_AsString(v);	}#endif	else {		PyObject *v = PyObject_Str(value);		if (v == NULL)			return NULL;		if (PyList_Append(tmp, v) != 0) {			Py_DECREF(v);			return NULL;		}		Py_DECREF(v);		return PyString_AsString(v);	}}  #define ARGSZ 64 static char *Merge(PyObject *args){	PyObject *tmp = NULL;	char *argvStore[ARGSZ];	char **argv = NULL;	int fvStore[ARGSZ];	int *fv = NULL;	int argc = 0, fvc = 0, i;	char *res = NULL; 	if (!(tmp = PyList_New(0)))	    return NULL; 	argv = argvStore;	fv = fvStore; 	if (args == NULL)		argc = 0; 	else if (!PyTuple_Check(args)) {		argc = 1;		fv[0] = 0;		if (!(argv[0] = AsString(args, tmp)))			goto finally;	}	else {		argc = PyTuple_Size(args); 		if (argc > ARGSZ) {			argv = (char **)ckalloc(argc * sizeof(char *));			fv = (int *)ckalloc(argc * sizeof(int));			if (argv == NULL || fv == NULL) {				PyErr_NoMemory();				goto finally;			}		} 		for (i = 0; i < argc; i++) {			PyObject *v = PyTuple_GetItem(args, i);			if (PyTuple_Check(v)) {				fv[i] = 1;				if (!(argv[i] = Merge(v)))					goto finally;				fvc++;			}			else if (v == Py_None) {				argc = i;				break;			}			else {				fv[i] = 0;				if (!(argv[i] = AsString(v, tmp)))					goto finally;				fvc++;			}		}	}	res = Tcl_Merge(argc, argv);	if (res == NULL)		PyErr_SetString(Tkinter_TclError, "merge failed");   finally:	for (i = 0; i < fvc; i++)		if (fv[i]) {			ckfree(argv[i]);		}	if (argv != argvStore)		ckfree(FREECAST argv);	if (fv != fvStore)		ckfree(FREECAST fv); 	Py_DECREF(tmp);	return res;}  static PyObject *Split(char *list){	int argc;	char **argv;	PyObject *v; 	if (list == NULL) {		Py_INCREF(Py_None);		return Py_None;	} 	if (Tcl_SplitList((Tcl_Interp *)NULL, list, &argc, &argv) != TCL_OK) {		/* Not a list.		 * Could be a quoted string containing funnies, e.g. {"}.		 * Return the string itself.		 */		return PyString_FromString(list);	} 	if (argc == 0)		v = PyString_FromString("");	else if (argc == 1)		v = PyString_FromString(argv[0]);	else if ((v = PyTuple_New(argc)) != NULL) {		int i;		PyObject *w; 		for (i = 0; i < argc; i++) {			if ((w = Split(argv[i])) == NULL) {				Py_DECREF(v);				v = NULL;				break;			}			PyTuple_SetItem(v, i, w);		}	}	Tcl_Free(FREECAST argv);	return v;} /* In some cases, Tcl will still return strings that are supposed to be   lists. SplitObj walks through a nested tuple, finding string objects that   need to be split. */ PyObject *SplitObj(PyObject *arg){	if (PyTuple_Check(arg)) {		int i, size;		PyObject *elem, *newelem, *result; 		size = PyTuple_Size(arg);

Lines 1–500 of 3165.