| /*********************************************************** |
| 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 |
| |
| #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; |
| |
| |
| /* 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); |
| } |