612 lines
14 KiB
C
612 lines
14 KiB
C
/***********************************************************
|
|
Copyright 1991-1997 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 "importdl.h"
|
|
#include "pymactoolbox.h"
|
|
|
|
#include "pythonresources.h"
|
|
|
|
#ifdef WITHOUT_FRAMEWORKS
|
|
#include <OSUtils.h> /* for Set(Current)A5 */
|
|
#include <Files.h>
|
|
#include <StandardFile.h>
|
|
#include <Resources.h>
|
|
#include <Memory.h>
|
|
#include <Windows.h>
|
|
#include <Traps.h>
|
|
#include <Processes.h>
|
|
#include <Fonts.h>
|
|
#include <Menus.h>
|
|
#include <TextUtils.h>
|
|
#include <LowMem.h>
|
|
#include <Events.h>
|
|
#else
|
|
#include <Carbon/Carbon.h>
|
|
#endif
|
|
|
|
#ifdef __MWERKS__
|
|
#include <SIOUX.h>
|
|
extern void SIOUXSetupMenus(void);
|
|
extern void SIOUXDoAboutBox(void);
|
|
#endif
|
|
#ifdef USE_GUSI
|
|
/* Functions we redefine because they're in obscure libraries */
|
|
extern void SpinCursor(short x);
|
|
extern void RotateCursor(short x);
|
|
extern pascal unsigned char * PLstrcpy(unsigned char *, const unsigned char *);
|
|
extern pascal short PLstrcmp(const unsigned char *, const unsigned char *);
|
|
extern pascal char *PLstrrchr(const unsigned char *, short);
|
|
|
|
#endif
|
|
|
|
/* The ID of the Sioux apple menu */
|
|
#define SIOUX_APPLEID 32000
|
|
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
|
|
/*
|
|
** When less than this amount of stackspace is left we
|
|
** raise a MemoryError.
|
|
*/
|
|
#ifndef MINIMUM_STACK_SIZE
|
|
#define MINIMUM_STACK_SIZE 8192
|
|
#endif
|
|
|
|
/*
|
|
** On MacOSX StackSpace() lies: it gives the distance from heap end to stack pointer,
|
|
** but the stack cannot grow that far due to rlimit values. We cannot get at this value
|
|
** from Carbon, so we set a maximum to the stack here that is based on the default
|
|
** stack limit of 512K.
|
|
*/
|
|
#define MAXIMUM_STACK_SIZE (256*1024)
|
|
|
|
/*
|
|
** 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|activMask)
|
|
|
|
#include <signal.h>
|
|
|
|
/* XXX We should include Errors.h here, but it has a name conflict
|
|
** with the python errors.h. */
|
|
#define fnfErr -43
|
|
|
|
/* Interrupt code variables: */
|
|
static int interrupted; /* Set to true when cmd-. seen */
|
|
static RETSIGTYPE intcatcher(int);
|
|
|
|
#if !TARGET_API_MAC_OSX
|
|
static int PyMac_Yield(void);
|
|
#endif
|
|
|
|
/*
|
|
** These are the real scheduling parameters that control what we check
|
|
** in the event loop, and how often we check. The values are initialized
|
|
** from pyMac_SchedParamStruct.
|
|
*/
|
|
|
|
struct real_sched_param_struct {
|
|
int check_interrupt; /* if true check for command-dot */
|
|
int process_events; /* if nonzero enable evt processing, this mask */
|
|
int besocial; /* if nonzero be a little social with CPU */
|
|
unsigned long check_interval; /* how often to check, in ticks */
|
|
unsigned long bg_yield; /* yield so long when in background */
|
|
/* these are computed from previous and clock and such */
|
|
int enabled; /* check_interrupt OR process_event OR yield */
|
|
unsigned long next_check; /* when to check/yield next, in ticks */
|
|
};
|
|
|
|
static struct real_sched_param_struct schedparams =
|
|
{ 1, MAINLOOP_EVENTMASK, 1, 15, 15, 1, 0};
|
|
|
|
/*
|
|
** Workaround for sioux/gusi combo: set when we are exiting
|
|
*/
|
|
int PyMac_ConsoleIsDead;
|
|
|
|
/*
|
|
** Sioux menu bar, saved early so we can restore it
|
|
*/
|
|
static MenuBarHandle sioux_mbar;
|
|
|
|
/*
|
|
** The python-code event handler
|
|
*/
|
|
static PyObject *python_event_handler;
|
|
|
|
/* Given an FSSpec, return the FSSpec of the parent folder */
|
|
|
|
static OSErr
|
|
get_folder_parent (FSSpec * fss, FSSpec * parent)
|
|
{
|
|
CInfoPBRec rec;
|
|
short err;
|
|
|
|
* parent = * fss;
|
|
rec.hFileInfo.ioNamePtr = parent->name;
|
|
rec.hFileInfo.ioVRefNum = parent->vRefNum;
|
|
rec.hFileInfo.ioDirID = parent->parID;
|
|
rec.hFileInfo.ioFDirIndex = -1;
|
|
rec.hFileInfo.ioFVersNum = 0;
|
|
if (err = PBGetCatInfoSync (& rec))
|
|
return err;
|
|
parent->parID = rec.dirInfo.ioDrParID;
|
|
/* parent->name[0] = 0; */
|
|
return 0;
|
|
}
|
|
|
|
/* Given an FSSpec return a full, colon-separated pathname */
|
|
|
|
OSErr
|
|
PyMac_GetFullPathname (FSSpec *fss, char *buf, int length)
|
|
{
|
|
short err;
|
|
FSSpec fss_parent, fss_current;
|
|
char tmpbuf[1024];
|
|
int plen;
|
|
|
|
fss_current = *fss;
|
|
plen = fss_current.name[0];
|
|
if ( plen+2 > length ) {
|
|
*buf = 0;
|
|
return errFSNameTooLong;
|
|
}
|
|
memcpy(buf, &fss_current.name[1], plen);
|
|
buf[plen] = 0;
|
|
/* Special case for disk names */
|
|
if ( fss_current.parID <= 1 ) {
|
|
buf[plen++] = ':';
|
|
buf[plen] = 0;
|
|
return 0;
|
|
}
|
|
while (fss_current.parID > 1) {
|
|
/* Get parent folder name */
|
|
if (err = get_folder_parent(&fss_current, &fss_parent)) {
|
|
*buf = 0;
|
|
return err;
|
|
}
|
|
fss_current = fss_parent;
|
|
/* Prepend path component just found to buf */
|
|
plen = fss_current.name[0];
|
|
if (strlen(buf) + plen + 1 > 1024) {
|
|
/* Oops... Not enough space (shouldn't happen) */
|
|
*buf = 0;
|
|
return errFSNameTooLong;
|
|
}
|
|
memcpy(tmpbuf, &fss_current.name[1], plen);
|
|
tmpbuf[plen] = ':';
|
|
strcpy(&tmpbuf[plen+1], buf);
|
|
if ( strlen(tmpbuf) > length ) {
|
|
*buf = 0;
|
|
return errFSNameTooLong;
|
|
}
|
|
strcpy(buf, tmpbuf);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifdef USE_GUSI
|
|
/*
|
|
** SpinCursor (needed by GUSI) drags in heaps of stuff, so we
|
|
** provide a dummy here.
|
|
*/
|
|
void SpinCursor(short x) { /* Dummy */ }
|
|
void RotateCursor(short x) { /* Dummy */ }
|
|
|
|
|
|
/* Called at exit() time thru atexit(), to stop event processing */
|
|
void
|
|
PyMac_StopGUSISpin() {
|
|
PyMac_ConsoleIsDead = 1;
|
|
}
|
|
|
|
#endif /* USE_GUSI */
|
|
|
|
|
|
/* 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;
|
|
}
|
|
|
|
|
|
#ifdef USE_STACKCHECK
|
|
/* Check for stack overflow */
|
|
int
|
|
PyOS_CheckStack()
|
|
{
|
|
char here;
|
|
static char *sentinel = 0;
|
|
static PyThreadState *thread_for_sentinel = 0;
|
|
|
|
if ( sentinel == 0 ) {
|
|
unsigned long stackspace = StackSpace();
|
|
|
|
#ifdef MAXIMUM_STACK_SIZE
|
|
/* See the comment at the definition */
|
|
if ( stackspace > MAXIMUM_STACK_SIZE )
|
|
stackspace = MAXIMUM_STACK_SIZE;
|
|
#endif
|
|
sentinel = &here - stackspace + MINIMUM_STACK_SIZE;
|
|
}
|
|
if ( thread_for_sentinel == 0 ) {
|
|
thread_for_sentinel = PyThreadState_Get();
|
|
}
|
|
if ( &here < sentinel ) {
|
|
if (thread_for_sentinel == PyThreadState_Get()) {
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#endif /* USE_STACKCHECK */
|
|
|
|
#if !TARGET_API_MAC_OSX
|
|
/* 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);
|
|
}
|
|
|
|
void
|
|
PyOS_FiniInterrupts()
|
|
{
|
|
}
|
|
|
|
/* Check whether we are in the foreground */
|
|
static int
|
|
PyMac_InForeground(void)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/*
|
|
** This routine scans the event queue looking for cmd-.
|
|
*/
|
|
static void
|
|
scan_event_queue(force)
|
|
int force;
|
|
{
|
|
if ( interrupted || (!schedparams.check_interrupt && !force) )
|
|
return;
|
|
if ( CheckEventQueueForUserCancel() )
|
|
interrupted = 1;
|
|
}
|
|
|
|
int
|
|
PyErr_CheckSignals()
|
|
{
|
|
if (schedparams.enabled) {
|
|
if ( interrupted || (unsigned long)TickCount() > schedparams.next_check ) {
|
|
scan_event_queue(0);
|
|
if (interrupted) {
|
|
interrupted = 0;
|
|
PyErr_SetNone(PyExc_KeyboardInterrupt);
|
|
return -1;
|
|
}
|
|
if ( PyMac_Yield() < 0)
|
|
return -1;
|
|
schedparams.next_check = (unsigned long)TickCount()
|
|
+ schedparams.check_interval;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
PyOS_InterruptOccurred()
|
|
{
|
|
scan_event_queue(0);
|
|
if ( !interrupted )
|
|
return 0;
|
|
interrupted = 0;
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
int
|
|
PyMac_SetEventHandler(PyObject *evh)
|
|
{
|
|
if ( evh && python_event_handler ) {
|
|
PyErr_SetString(PyExc_RuntimeError, "Python event handler already set");
|
|
return 0;
|
|
}
|
|
if ( python_event_handler )
|
|
Py_DECREF(python_event_handler);
|
|
if ( evh )
|
|
Py_INCREF(evh);
|
|
python_event_handler = evh;
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
** Handle an event, either one found in the mainloop eventhandler or
|
|
** one passed back from the python program.
|
|
*/
|
|
void
|
|
PyMac_HandleEventIntern(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... */
|
|
#endif /* !__MWERKS__ */
|
|
}
|
|
|
|
/*
|
|
** Handle an event, either through HandleEvent or by passing it to the Python
|
|
** event handler.
|
|
*/
|
|
int
|
|
PyMac_HandleEvent(evp)
|
|
EventRecord *evp;
|
|
{
|
|
PyObject *rv;
|
|
|
|
if ( python_event_handler ) {
|
|
rv = PyObject_CallFunction(python_event_handler, "(O&)",
|
|
PyMac_BuildEventRecord, evp);
|
|
if ( rv )
|
|
Py_DECREF(rv);
|
|
else
|
|
return -1; /* Propagate exception */
|
|
} else {
|
|
PyMac_HandleEventIntern(evp);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if !TARGET_API_MAC_OSX
|
|
/*
|
|
** Yield the CPU to other tasks without processing events.
|
|
*/
|
|
int
|
|
PyMac_DoYield(int maxsleep, int maycallpython)
|
|
{
|
|
EventRecord ev;
|
|
int gotone;
|
|
long latest_time_ready;
|
|
static int in_here = 0;
|
|
|
|
in_here++;
|
|
|
|
/*
|
|
** Check which of the eventloop cases we have:
|
|
** - process events
|
|
** - don't process events but do yield
|
|
** - do neither
|
|
*/
|
|
if( in_here > 1 || !schedparams.process_events ||
|
|
(python_event_handler && !maycallpython) ) {
|
|
if ( maxsleep >= 0 ) {
|
|
/* XXXX Need to do something here */
|
|
}
|
|
} else {
|
|
latest_time_ready = TickCount() + maxsleep;
|
|
do {
|
|
/* XXXX Hack by Jack.
|
|
** In time.sleep() you can click to another application
|
|
** once only. If you come back to Python you cannot get away
|
|
** again.
|
|
**/
|
|
gotone = WaitNextEvent(schedparams.process_events, &ev, maxsleep, NULL);
|
|
/* Get out quickly if nothing interesting is happening */
|
|
if ( !gotone || ev.what == nullEvent )
|
|
break;
|
|
if ( PyMac_HandleEvent(&ev) < 0 ) {
|
|
in_here--;
|
|
return -1;
|
|
}
|
|
maxsleep = latest_time_ready - TickCount();
|
|
} while ( maxsleep > 0 );
|
|
}
|
|
in_here--;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** Process events and/or yield the CPU to other tasks if opportune
|
|
*/
|
|
int
|
|
PyMac_Yield() {
|
|
unsigned long maxsleep;
|
|
|
|
if( PyMac_InForeground() )
|
|
maxsleep = 0;
|
|
else
|
|
maxsleep = schedparams.bg_yield;
|
|
|
|
return PyMac_DoYield(maxsleep, 1);
|
|
}
|
|
|
|
/*
|
|
** Return current scheduler parameters
|
|
*/
|
|
void
|
|
PyMac_GetSchedParams(PyMacSchedParams *sp)
|
|
{
|
|
sp->check_interrupt = schedparams.check_interrupt;
|
|
sp->process_events = schedparams.process_events;
|
|
sp->besocial = schedparams.besocial;
|
|
sp->check_interval = schedparams.check_interval / 60.0;
|
|
sp->bg_yield = schedparams.bg_yield / 60.0;
|
|
}
|
|
|
|
/*
|
|
** Set current scheduler parameters
|
|
*/
|
|
void
|
|
PyMac_SetSchedParams(PyMacSchedParams *sp)
|
|
{
|
|
schedparams.check_interrupt = sp->check_interrupt;
|
|
schedparams.process_events = sp->process_events;
|
|
schedparams.besocial = sp->besocial;
|
|
schedparams.check_interval = (unsigned long)(sp->check_interval*60);
|
|
schedparams.bg_yield = (unsigned long)(sp->bg_yield*60);
|
|
if ( schedparams.check_interrupt || schedparams.process_events ||
|
|
schedparams.besocial )
|
|
schedparams.enabled = 1;
|
|
else
|
|
schedparams.enabled = 0;
|
|
schedparams.next_check = 0; /* Check immedeately */
|
|
}
|
|
|
|
/*
|
|
** Install our menu bar.
|
|
*/
|
|
void
|
|
PyMac_InitMenuBar()
|
|
{
|
|
MenuHandle applemenu;
|
|
Str255 about_text;
|
|
static unsigned char about_sioux[] = "\pAbout SIOUX";
|
|
|
|
if ( sioux_mbar ) return;
|
|
if ( (sioux_mbar=GetMenuBar()) == NULL || GetMenuHandle(SIOUX_APPLEID) == NULL) {
|
|
/* Sioux menu not installed yet. Do so */
|
|
SIOUXSetupMenus();
|
|
if ( (sioux_mbar=GetMenuBar()) == NULL )
|
|
return;
|
|
}
|
|
if ( (applemenu=GetMenuHandle(SIOUX_APPLEID)) == NULL ) return;
|
|
GetMenuItemText(applemenu, 1, about_text);
|
|
if ( about_text[0] == about_sioux[0] &&
|
|
strncmp((char *)(about_text+1), (char *)(about_sioux+1), about_text[0]) == 0 )
|
|
SetMenuItemText(applemenu, 1, "\pAbout Python...");
|
|
}
|
|
|
|
/*
|
|
** Restore sioux menu bar
|
|
*/
|
|
void
|
|
PyMac_RestoreMenuBar()
|
|
{
|
|
MenuBarHandle curmenubar;
|
|
|
|
curmenubar = GetMenuBar();
|
|
if ( sioux_mbar ) {
|
|
SetMenuBar(sioux_mbar);
|
|
DrawMenuBar();
|
|
} else {
|
|
PyMac_InitMenuBar();
|
|
DrawMenuBar();
|
|
}
|
|
}
|
|
|
|
void
|
|
PyMac_RaiseConsoleWindow()
|
|
{
|
|
/* Note: this is a hack. SIOUXTextWindow is SIOUX's internal structure
|
|
** and we happen to know that the first entry is the window pointer.
|
|
*/
|
|
extern WindowRef *SIOUXTextWindow;
|
|
|
|
if ( SIOUXTextWindow == NULL || *SIOUXTextWindow == NULL )
|
|
return;
|
|
if ( FrontWindow() != *SIOUXTextWindow )
|
|
BringToFront(*SIOUXTextWindow);
|
|
}
|
|
|
|
/*
|
|
** Our replacement about box
|
|
*/
|
|
|
|
#include "patchlevel.h"
|
|
|
|
void
|
|
SIOUXDoAboutBox(void)
|
|
{
|
|
DialogPtr theDialog;
|
|
WindowPtr theWindow;
|
|
short item;
|
|
short fontID;
|
|
|
|
if( (theDialog = GetNewDialog(ABOUT_ID, NULL, (WindowPtr)-1)) == NULL )
|
|
return;
|
|
theWindow = GetDialogWindow(theDialog);
|
|
SetPortWindowPort(theWindow);
|
|
GetFNum("\pPython-Sans", &fontID);
|
|
if (fontID == 0)
|
|
fontID = kFontIDGeneva;
|
|
TextFont(fontID);
|
|
TextSize(9);
|
|
ParamText(Pstring(PY_VERSION), "\p", "\p", "\p");
|
|
ShowWindow(theWindow);
|
|
ModalDialog(NULL, &item);
|
|
DisposeDialog(theDialog);
|
|
}
|
|
|
|
#endif /* !TARGET_API_MAC_OSX */
|