home *** CD-ROM | disk | FTP | other *** search
- /***********************************************************
- Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam,
- The Netherlands.
-
- All Rights Reserved
-
- Permission to use, copy, modify, and distribute this software and its
- documentation for any purpose and without fee is hereby granted,
- provided that the above copyright notice appear in all copies and that
- both that copyright notice and this permission notice appear in
- supporting documentation, and that the names of Stichting Mathematisch
- Centrum or CWI not be used in advertising or publicity pertaining to
- distribution of the software without specific, written prior permission.
-
- STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
- THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
- FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
- FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
- ******************************************************************/
-
- #include "Python.h"
-
- #include "macglue.h"
- #include "marshal.h"
- #include "import.h"
-
- #include "pythonresources.h"
-
- #include <OSUtils.h> /* for Set(Current)A5 */
- #include <Files.h>
- #include <StandardFile.h>
- #include <Resources.h>
- #include <Memory.h>
- #include <Events.h>
- #include <Windows.h>
- #include <Desk.h>
- #include <Traps.h>
- #include <Processes.h>
- #include <Fonts.h>
- #include <Menus.h>
- #include <TextUtils.h>
- #ifdef THINK_C
- #include <OSEvents.h> /* For EvQElPtr */
- #endif
- #ifdef __MWERKS__
- #include <SIOUX.h>
- #endif
- #ifdef USE_GUSI
- #include <TFileSpec.h> /* For Path2FSSpec */
- #include <LowMem.h> /* For SetSFCurDir, etc */
- #endif
-
- #ifndef HAVE_UNIVERSAL_HEADERS
- #define GetResourceSizeOnDisk(x) SizeResource(x)
- typedef DlgHookYDProcPtr DlgHookYDUPP;
- #define NewDlgHookYDProc(userRoutine) ((DlgHookYDUPP) (userRoutine))
- typedef FileFilterYDProcPtr FileFilterYDUPP;
- #endif
-
- #include <signal.h>
- #include <stdio.h>
-
- /*
- ** We have to be careful, since we can't handle
- ** things like updates (and they'll keep coming back if we don't
- ** handle them). Note that we don't know who has windows open, so
- ** even handing updates off to SIOUX under MW isn't going to work.
- */
- #define MAINLOOP_EVENTMASK (mDownMask|keyDownMask|osMask)
-
- #include <signal.h>
-
- /* XXX We should include Errors.h here, but it has a name conflict
- ** with the python errors.h. */
- #define fnfErr -43
-
- /* Declared in macfsmodule.c: */
- extern FSSpec *mfs_GetFSSpecFSSpec();
-
- /* Interrupt code variables: */
- static int interrupted; /* Set to true when cmd-. seen */
- static RETSIGTYPE intcatcher Py_PROTO((int));
-
- /*
- ** We attempt to be a good citizen by giving up the CPU periodically.
- ** When in the foreground we do this less often and for shorter periods
- ** than when in the background. At this time we also check for events and
- ** pass them off to SIOUX, if compiling with mwerks.
- ** The counts here are in ticks of 1/60th second.
- ** XXXX The initial values here are not based on anything.
- ** FG-python gives up the cpu for 1/60th 5 times per second,
- ** BG-python for .2 second 10 times per second.
- */
- static long interval_fg = 12;
- static long interval_bg = 6;
- static long yield_fg = 1;
- static long yield_bg = 12;
- static long lastyield;
- static int in_foreground;
-
- /*
- ** When > 0, do full scanning for events (program is not event aware)
- ** when == 0, only scan for Command-period
- ** when < 0, don't do any event scanning
- */
- int PyMac_DoYieldEnabled = 1;
-
- /*
- ** Some stuff for our GetDirectory and PromptGetFile routines
- */
- struct hook_args {
- int selectcur_hit; /* Set to true when "select current" selected */
- char *prompt; /* The prompt */
- };
- static DlgHookYDUPP myhook_upp;
- static int upp_inited = 0;
-
- #ifdef USE_GUSI
- /*
- ** GUSI (1.6.0 and earlier, at the least) do not set the MacOS idea of
- ** the working directory. Hence, we call this routine after each call
- ** to chdir() to rectify things.
- */
- void
- PyMac_FixGUSIcd()
- {
- WDPBRec pb;
- FSSpec curdirfss;
-
- if ( Path2FSSpec(":x", &curdirfss) != noErr )
- return;
-
- /* Set MacOS "working directory" */
- pb.ioNamePtr= "\p";
- pb.ioVRefNum= curdirfss.vRefNum;
- pb.ioWDDirID= curdirfss.parID;
- if (PBHSetVol(&pb, 0) != noErr)
- return;
-
- #if 0
- /* Set standard-file working directory */
- LMSetSFSaveDisk(-curdirfss.vRefNum);
- LMSetCurDirStore(curdirfss.parID);
- #endif
- }
- #endif
-
-
- /* Convert C to Pascal string. Returns pointer to static buffer. */
- unsigned char *
- Pstring(char *str)
- {
- static Str255 buf;
- int len;
-
- len = strlen(str);
- if (len > 255)
- len = 255;
- buf[0] = (unsigned char)len;
- strncpy((char *)buf+1, str, len);
- return buf;
- }
-
- /* Like strerror() but for Mac OS error numbers */
- char *PyMac_StrError(int err)
- {
- static char buf[256];
- Handle h;
- char *str;
-
- h = GetResource('Estr', err);
- if ( h ) {
- HLock(h);
- str = (char *)*h;
- memcpy(buf, str+1, (unsigned char)str[0]);
- buf[(unsigned char)str[0]] = '\0';
- HUnlock(h);
- ReleaseResource(h);
- } else {
- sprintf(buf, "Mac OS error code %d", err);
- }
- return buf;
- }
-
- /* Exception object shared by all Mac specific modules for Mac OS errors */
- PyObject *PyMac_OSErrException;
-
- /* Initialize and return PyMac_OSErrException */
- PyObject *
- PyMac_GetOSErrException()
- {
- if (PyMac_OSErrException == NULL)
- PyMac_OSErrException = PyString_FromString("Mac OS Error");
- return PyMac_OSErrException;
- }
-
- /* Set a MAC-specific error from errno, and return NULL; return None if no error */
- PyObject *
- PyErr_Mac(PyObject *eobj, int err)
- {
- char *msg;
- PyObject *v;
-
- if (err == 0 && !PyErr_Occurred()) {
- Py_INCREF(Py_None);
- return Py_None;
- }
- if (err == -1 && PyErr_Occurred())
- return NULL;
- msg = PyMac_StrError(err);
- v = Py_BuildValue("(is)", err, msg);
- PyErr_SetObject(eobj, v);
- Py_DECREF(v);
- return NULL;
- }
-
- /* Call PyErr_Mac with PyMac_OSErrException */
- PyObject *
- PyMac_Error(OSErr err)
- {
- return PyErr_Mac(PyMac_GetOSErrException(), err);
- }
-
- /* The catcher routine (which may not be used for all compilers) */
- static RETSIGTYPE
- intcatcher(sig)
- int sig;
- {
- interrupted = 1;
- signal(SIGINT, intcatcher);
- }
-
- void
- PyOS_InitInterrupts()
- {
- if (signal(SIGINT, SIG_IGN) != SIG_IGN)
- signal(SIGINT, intcatcher);
- }
-
- /*
- ** This routine scans the event queue looking for cmd-.
- ** This is the only way to get an interrupt under THINK (since it
- ** doesn't do SIGINT handling), but is also used under MW, when
- ** the full-fledged event loop is disabled. This way, we can at least
- ** interrupt a runaway python program.
- */
- static void
- scan_event_queue(flush)
- int flush;
- {
- #if defined(__MWERKS__) && defined(__CFM68K__)
- return; /* No GetEvQHdr yet */
- #else
- register EvQElPtr q;
-
- q = (EvQElPtr) GetEvQHdr()->qHead;
-
- for (; q; q = (EvQElPtr)q->qLink) {
- if (q->evtQWhat == keyDown &&
- (char)q->evtQMessage == '.' &&
- (q->evtQModifiers & cmdKey) != 0) {
- if ( flush )
- FlushEvents(keyDownMask, 0);
- interrupted = 1;
- break;
- }
- }
- #endif
- }
-
- int
- PyOS_InterruptOccurred()
- {
- if (PyMac_DoYieldEnabled < 0)
- return 0;
- #ifdef THINK_C
- scan_event_queue(1);
- #endif
- PyMac_Yield();
- if (interrupted) {
- interrupted = 0;
- return 1;
- }
- return 0;
- }
-
- /* intrpeek() is like intrcheck(), but it doesn't flush the events. The
- ** idea is that you call intrpeek() somewhere in a busy-wait loop, and return
- ** None as soon as it returns 1. The mainloop will then pick up the cmd-. soon
- ** thereafter.
- */
- static int
- intrpeek()
- {
- #ifdef THINK_C
- scan_event_queue(0);
- #endif
- return interrupted;
- }
-
- /* Check whether we are in the foreground */
- int
- PyMac_InForeground()
- {
- static ProcessSerialNumber ours;
- static inited;
- ProcessSerialNumber curfg;
- Boolean eq;
-
- if ( inited == 0 )
- (void)GetCurrentProcess(&ours);
- inited = 1;
- if ( GetFrontProcess(&curfg) < 0 )
- eq = 1;
- else if ( SameProcess(&ours, &curfg, &eq) < 0 )
- eq = 1;
- return (int)eq;
-
- }
-
- /*
- ** Set yield timeouts
- */
- void
- PyMac_SetYield(long fgi, long fgy, long bgi, long bgy)
- {
- interval_fg = fgi;
- yield_fg = fgy;
- interval_bg = bgi;
- yield_bg = bgy;
- }
-
- /*
- ** Handle an event, either one found in the mainloop eventhandler or
- ** one passed back from the python program.
- */
- void
- PyMac_HandleEvent(evp)
- EventRecord *evp;
- {
-
- #ifdef __MWERKS__
- {
- int siouxdidit;
-
- /* If SIOUX wants it we're done */
- siouxdidit = SIOUXHandleOneEvent(evp);
- if ( siouxdidit )
- return;
- }
- #else
- /* Other compilers are just unlucky: we only weed out clicks in other applications */
- if ( evp->what == mouseDown ) {
- WindowPtr wp;
-
- if ( FindWindow(evp->where, &wp) == inSysWindow ) {
- SystemClick(evp, wp);
- return;
- }
- }
- #endif /* !__MWERKS__ */
- }
-
- /*
- ** Yield the CPU to other tasks.
- */
- static
- PyMac_DoYield()
- {
- EventRecord ev;
- long yield;
- static int no_waitnextevent = -1;
- int gotone;
-
- if ( no_waitnextevent < 0 ) {
- no_waitnextevent = (NGetTrapAddress(_WaitNextEvent, ToolTrap) ==
- NGetTrapAddress(_Unimplemented, ToolTrap));
- }
-
- if ( !PyMac_DoYieldEnabled ) {
- #ifndef THINK_C
- /* Under think this has been done before in intrcheck() or intrpeek() */
- scan_event_queue(0);
- #endif
- return;
- }
-
- in_foreground = PyMac_InForeground();
- if ( in_foreground )
- yield = yield_fg;
- else
- yield = yield_bg;
- while ( 1 ) {
- if ( no_waitnextevent ) {
- SystemTask();
- gotone = GetNextEvent(MAINLOOP_EVENTMASK, &ev);
- } else {
- gotone = WaitNextEvent(MAINLOOP_EVENTMASK, &ev, yield, NULL);
- }
- /* Get out quickly if nothing interesting is happening */
- if ( !gotone || ev.what == nullEvent )
- break;
- PyMac_HandleEvent(&ev);
- }
- lastyield = TickCount();
- }
-
- /*
- ** Yield the CPU to other tasks if opportune
- */
- void
- PyMac_Yield() {
- long iv;
-
- if ( in_foreground )
- iv = interval_fg;
- else
- iv = interval_bg;
- if ( TickCount() > lastyield + iv )
- PyMac_DoYield();
- }
-
- /*
- ** Idle routine for busy-wait loops.
- ** Gives up CPU, handles events and returns true if an interrupt is pending
- ** (but not actually handled yet).
- */
- int
- PyMac_Idle()
- {
- PyMac_DoYield();
- return intrpeek();
- }
- /*
- ** Returns true if the argument has a resource fork, and it contains
- ** a 'PYC ' resource of the correct name
- */
- int
- PyMac_FindResourceModule(module, filename)
- char *module;
- char *filename;
- {
- FSSpec fss;
- FInfo finfo;
- short oldrh, filerh;
- int ok;
- Handle h;
-
- if ( FSMakeFSSpec(0, 0, Pstring(filename), &fss) != noErr )
- return 0; /* It doesn't exist */
- if ( FSpGetFInfo(&fss, &finfo) != noErr )
- return 0; /* shouldn't happen, I guess */
- oldrh = CurResFile();
- filerh = FSpOpenResFile(&fss, fsRdPerm);
- if ( filerh == -1 )
- return 0;
- UseResFile(filerh);
- SetResLoad(0);
- h = Get1NamedResource('PYC ', Pstring(module));
- SetResLoad(1);
- ok = (h != NULL);
- CloseResFile(filerh);
- UseResFile(oldrh);
- return ok;
- }
-
- /*
- ** Load the specified module from a resource
- */
- PyObject *
- PyMac_LoadResourceModule(module, filename)
- char *module;
- char *filename;
- {
- FSSpec fss;
- FInfo finfo;
- short oldrh, filerh;
- Handle h;
- OSErr err;
- PyObject *m, *co;
- long num, size;
-
- if ( (err=FSMakeFSSpec(0, 0, Pstring(filename), &fss)) != noErr )
- goto error;
- if ( (err=FSpGetFInfo(&fss, &finfo)) != noErr )
- goto error;
- oldrh = CurResFile();
- filerh = FSpOpenResFile(&fss, fsRdPerm);
- if ( filerh == -1 ) {
- err = ResError();
- goto error;
- }
- UseResFile(filerh);
- h = Get1NamedResource('PYC ', Pstring(module));
- if ( h == NULL ) {
- err = ResError();
- goto error;
- }
- HLock(h);
- /*
- ** XXXX The next few lines are intimately tied to the format of pyc
- ** files. I'm not sure whether this code should be here or in import.c -- Jack
- */
- size = GetHandleSize(h);
- if ( size < 8 ) {
- PyErr_SetString(PyExc_ImportError, "Resource too small");
- co = NULL;
- } else {
- num = (*h)[0] & 0xff;
- num = num | (((*h)[1] & 0xff) << 8);
- num = num | (((*h)[2] & 0xff) << 16);
- num = num | (((*h)[3] & 0xff) << 24);
- if ( num != PyImport_GetMagicNumber() ) {
- PyErr_SetString(PyExc_ImportError, "Bad MAGIC in resource");
- co = NULL;
- } else {
- co = PyMarshal_ReadObjectFromString((*h)+8, size-8);
- }
- }
- HUnlock(h);
- CloseResFile(filerh);
- UseResFile(oldrh);
- if ( co ) {
- m = PyImport_ExecCodeModule(module, co);
- Py_DECREF(co);
- } else {
- m = NULL;
- }
- return m;
- error:
- {
- char buf[512];
-
- sprintf(buf, "%s: %s", filename, PyMac_StrError(err));
- PyErr_SetString(PyExc_ImportError, buf);
- return NULL;
- }
- }
-
- /*
- ** Helper routine for GetDirectory
- */
- static pascal short
- myhook_proc(short item, DialogPtr theDialog, struct hook_args *dataptr)
- {
- if ( item == sfHookFirstCall && dataptr->prompt) {
- Handle prompth;
- short type;
- Rect rect;
-
- GetDialogItem(theDialog, PROMPT_ITEM, &type, &prompth, &rect);
- if ( prompth )
- SetDialogItemText(prompth, (unsigned char *)dataptr->prompt);
- } else
- if ( item == SELECTCUR_ITEM ) {
- item = sfItemCancelButton;
- dataptr->selectcur_hit = 1;
- }
- return item;
- }
-
- /*
- ** Ask the user for a directory. I still can't understand
- ** why Apple doesn't provide a standard solution for this...
- */
- int
- PyMac_GetDirectory(dirfss, prompt)
- FSSpec *dirfss;
- char *prompt;
- {
- static SFTypeList list = {'fldr', 0, 0, 0};
- static Point where = {-1, -1};
- StandardFileReply reply;
- struct hook_args hook_args;
-
- if ( !upp_inited ) {
- myhook_upp = NewDlgHookYDProc(myhook_proc);
- upp_inited = 1;
- }
- if ( prompt && *prompt )
- hook_args.prompt = (char *)Pstring(prompt);
- else
- hook_args.prompt = NULL;
- hook_args.selectcur_hit = 0;
- CustomGetFile((FileFilterYDUPP)0, 1, list, &reply, GETDIR_ID, where, myhook_upp,
- NULL, NULL, NULL, (void *)&hook_args);
-
- reply.sfFile.name[0] = 0;
- if( FSMakeFSSpec(reply.sfFile.vRefNum, reply.sfFile.parID, reply.sfFile.name, dirfss) )
- return 0;
- return hook_args.selectcur_hit;
- }
-
- /*
- ** Slightly extended StandardGetFile: accepts a prompt */
- void PyMac_PromptGetFile(short numTypes, ConstSFTypeListPtr typeList,
- StandardFileReply *reply, char *prompt)
- {
- static Point where = {-1, -1};
- struct hook_args hook_args;
-
- if ( !upp_inited ) {
- myhook_upp = NewDlgHookYDProc(myhook_proc);
- upp_inited = 1;
- }
- if ( prompt && *prompt )
- hook_args.prompt = (char *)Pstring(prompt);
- else
- hook_args.prompt = NULL;
- hook_args.selectcur_hit = 0;
- CustomGetFile((FileFilterYDUPP)0, numTypes, typeList, reply, GETFILEPROMPT_ID, where,
- myhook_upp, NULL, NULL, NULL, (void *)&hook_args);
- }
-
- /* Convert a 4-char string object argument to an OSType value */
- int
- PyMac_GetOSType(PyObject *v, OSType *pr)
- {
- if (!PyString_Check(v) || PyString_Size(v) != 4) {
- PyErr_SetString(PyExc_TypeError,
- "OSType arg must be string of 4 chars");
- return 0;
- }
- memcpy((char *)pr, PyString_AsString(v), 4);
- return 1;
- }
-
- /* Convert an OSType value to a 4-char string object */
- PyObject *
- PyMac_BuildOSType(OSType t)
- {
- return PyString_FromStringAndSize((char *)&t, 4);
- }
-
-
- /* Convert a Python string object to a Str255 */
- int
- PyMac_GetStr255(PyObject *v, Str255 pbuf)
- {
- int len;
- if (!PyString_Check(v) || (len = PyString_Size(v)) > 255) {
- PyErr_SetString(PyExc_TypeError,
- "Str255 arg must be string of at most 255 chars");
- return 0;
- }
- pbuf[0] = len;
- memcpy((char *)(pbuf+1), PyString_AsString(v), len);
- return 1;
- }
-
- /* Convert a Str255 to a Python string object */
- PyObject *
- PyMac_BuildStr255(Str255 s)
- {
- return PyString_FromStringAndSize((char *)&s[1], (int)s[0]);
- }
-
-
- /*
- ** Convert a Python object to an FSSpec.
- ** The object may either be a full pathname or a triple
- ** (vrefnum, dirid, path).
- ** NOTE: This routine will fail on pre-sys7 machines.
- ** The caller is responsible for not calling this routine
- ** in those cases (which is fine, since everyone calling
- ** this is probably sys7 dependent anyway).
- */
- int
- PyMac_GetFSSpec(PyObject *v, FSSpec *fs)
- {
- Str255 path;
- short refnum;
- long parid;
- OSErr err;
- FSSpec *fs2;
-
- /* first check whether it already is an FSSpec */
- fs2 = mfs_GetFSSpecFSSpec(v);
- if ( fs2 ) {
- (void)FSMakeFSSpec(fs2->vRefNum, fs2->parID, fs2->name, fs);
- return 1;
- }
- if ( PyString_Check(v) ) {
- /* It's a pathname */
- if( !PyArg_Parse(v, "O&", PyMac_GetStr255, &path) )
- return 0;
- refnum = 0; /* XXXX Should get CurWD here?? */
- parid = 0;
- } else {
- if( !PyArg_Parse(v, "(hlO&); FSSpec should be fullpath or (vrefnum,dirid,path)",
- &refnum, &parid, PyMac_GetStr255, &path)) {
- return 0;
- }
- }
- err = FSMakeFSSpec(refnum, parid, path, fs);
- if ( err && err != fnfErr ) {
- PyErr_Mac(PyExc_ValueError, err);
- return 0;
- }
- return 1;
- }
-
-
- /* Convert a Python object to a Rect.
- The object must be a (left, top, right, bottom) tuple.
- (This differs from the order in the struct but is consistent with
- the arguments to SetRect(), and also with STDWIN). */
- int
- PyMac_GetRect(PyObject *v, Rect *r)
- {
- return PyArg_Parse(v, "(hhhh)", &r->left, &r->top, &r->right, &r->bottom);
- }
-
- /* Convert a Rect to a Python object */
- PyObject *
- PyMac_BuildRect(Rect *r)
- {
- return Py_BuildValue("(hhhh)", r->left, r->top, r->right, r->bottom);
- }
-
-
- /* Convert a Python object to a Point.
- The object must be a (h, v) tuple.
- (This differs from the order in the struct but is consistent with
- the arguments to SetPoint(), and also with STDWIN). */
- int
- PyMac_GetPoint(PyObject *v, Point *p)
- {
- return PyArg_Parse(v, "(hh)", &p->h, &p->v);
- }
-
- /* Convert a Point to a Python object */
- PyObject *
- PyMac_BuildPoint(Point p)
- {
- return Py_BuildValue("(hh)", p.h, p.v);
- }
-
-
- /* Convert a Python object to an EventRecord.
- The object must be a (what, message, when, (v, h), modifiers) tuple. */
- int
- PyMac_GetEventRecord(PyObject *v, EventRecord *e)
- {
- return PyArg_Parse(v, "(hll(hh)h)",
- &e->what,
- &e->message,
- &e->when,
- &e->where.h,
- &e->where.v,
- &e->modifiers);
- }
-
- /* Convert a Rect to an EventRecord object */
- PyObject *
- PyMac_BuildEventRecord(EventRecord *e)
- {
- return Py_BuildValue("(hll(hh)h)",
- e->what,
- e->message,
- e->when,
- e->where.h,
- e->where.v,
- e->modifiers);
- }
-
- /* Convert Python object to Fixed */
- int
- PyMac_GetFixed(PyObject *v, Fixed *f)
- {
- double d;
-
- if( !PyArg_Parse(v, "d", &d))
- return 0;
- *f = (Fixed)(d * 0x10000);
- }
-
- /* Convert a Point to a Python object */
- PyObject *
- PyMac_BuildFixed(Fixed f)
- {
- double d;
-
- d = f;
- d = d / 0x10000;
- return Py_BuildValue("d", d);
- }
-
-