1 /***********************************************************
2 Copyright 1991-1997 by Stichting Mathematisch Centrum, Amsterdam,
7 Permission to use, copy, modify, and distribute this software and its
8 documentation for any purpose and without fee is hereby granted,
9 provided that the above copyright notice appear in all copies and that
10 both that copyright notice and this permission notice appear in
11 supporting documentation, and that the names of Stichting Mathematisch
12 Centrum or CWI not be used in advertising or publicity pertaining to
13 distribution of the software without specific, written prior permission.
15 STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
16 THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17 FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
18 FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21 OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 ******************************************************************/
26 /* cfm68k InterfaceLib exports GetEventQueue, but Events.h doesn't know this
27 ** and defines it as GetEvQHdr (which is correct for PPC). This fix is for
28 ** CW9, check that the workaround is still needed for the next release.
30 #define GetEvQHdr GetEventQueue
31 #endif /* __CFM68K__ */
37 #endif /* __CFM68K__ */
46 #include "pythonresources.h"
48 #include <OSUtils.h> /* for Set(Current)A5 */
50 #include <StandardFile.h>
51 #include <Resources.h>
55 #include <Processes.h>
58 #include <TextUtils.h>
60 #include <OSEvents.h> /* For EvQElPtr */
66 #include <TFileSpec.h> /* For Path2FSSpec */
67 #include <LowMem.h> /* For SetSFCurDir, etc */
71 /* The ID of the Sioux apple menu */
72 #define SIOUX_APPLEID 32000
78 ** When less than this amount of stackspace is left we
79 ** raise a MemoryError.
81 #ifndef MINIMUM_STACK_SIZE
83 #define MINIMUM_STACK_SIZE 8192
85 #define MINIMUM_STACK_SIZE 4096
90 ** We have to be careful, since we can't handle
91 ** things like updates (and they'll keep coming back if we don't
92 ** handle them). Note that we don't know who has windows open, so
93 ** even handing updates off to SIOUX under MW isn't going to work.
95 #define MAINLOOP_EVENTMASK (mDownMask|keyDownMask|osMask|activMask)
99 /* XXX We should include Errors.h here, but it has a name conflict
100 ** with the python errors.h. */
103 /* Declared in macfsmodule.c: */
104 extern FSSpec
*mfs_GetFSSpecFSSpec();
105 extern PyObject
*newmfssobject
Py_PROTO((FSSpec
*));
107 /* Interrupt code variables: */
108 static int interrupted
; /* Set to true when cmd-. seen */
109 static RETSIGTYPE intcatcher
Py_PROTO((int));
111 static int PyMac_DoYield
Py_PROTO((int, int));
112 static int PyMac_Yield
Py_PROTO((void));
115 ** These are the real scheduling parameters that control what we check
116 ** in the event loop, and how often we check. The values are initialized
117 ** from pyMac_SchedParamStruct.
120 struct real_sched_param_struct
{
121 int check_interrupt
; /* if true check for command-dot */
122 int process_events
; /* if nonzero enable evt processing, this mask */
123 int besocial
; /* if nonzero be a little social with CPU */
124 unsigned long check_interval
; /* how often to check, in ticks */
125 unsigned long bg_yield
; /* yield so long when in background */
126 /* these are computed from previous and clock and such */
127 int enabled
; /* check_interrupt OR process_event OR yield */
128 unsigned long next_check
; /* when to check/yield next, in ticks */
131 static struct real_sched_param_struct schedparams
=
132 { 1, MAINLOOP_EVENTMASK
, 1, 15, 15, 1, 0};
135 ** Workaround for sioux/gusi combo: set when we are exiting
137 int PyMac_ConsoleIsDead
;
140 ** Sioux menu bar, saved early so we can restore it
142 static Handle sioux_mbar
;
145 ** Some stuff for our GetDirectory and PromptGetFile routines
148 int selectcur_hit
; /* Set to true when "select current" selected */
149 char *prompt
; /* The prompt */
151 static DlgHookYDUPP myhook_upp
;
152 static int upp_inited
= 0;
155 ** The python-code event handler
157 static PyObject
*python_event_handler
;
160 ** Set to true if we're appearance-compliant
162 int PyMac_AppearanceCompliant
;
166 ** GUSI (1.6.0 and earlier, at the least) do not set the MacOS idea of
167 ** the working directory. Hence, we call this routine after each call
168 ** to chdir() to rectify things.
176 if ( Path2FSSpec(":x", &curdirfss
) != noErr
)
179 /* Set MacOS "working directory" */
181 pb
.ioVRefNum
= curdirfss
.vRefNum
;
182 pb
.ioWDDirID
= curdirfss
.parID
;
183 if (PBHSetVolSync(&pb
) != noErr
)
188 ** SpinCursor (needed by GUSI) drags in heaps of stuff, so we
189 ** provide a dummy here.
191 void SpinCursor(short x
) { /* Dummy */ }
192 void RotateCursor(short x
) { /* Dummy */ }
195 ** Replacement GUSI Spin function
198 PyMac_GUSISpin(spin_msg msg
, long arg
)
200 static Boolean inForeground
= true;
201 int maxsleep
= 6; /* 6 ticks is "normal" sleeptime */
203 if (PyMac_ConsoleIsDead
) return 0;
206 SpinCursor(msg
== SP_AUTO_SPIN
? short(arg
) : 1);
209 if (interrupted
) return -1;
211 if ( msg
== SP_AUTO_SPIN
)
213 if ( msg
==SP_SLEEP
||msg
==SP_SELECT
)
216 PyMac_DoYield(maxsleep
, 0); /* XXXX or is it safe to call python here? */
222 PyMac_SetGUSISpin() {
223 GUSISetHook(GUSI_SpinHook
, (GUSIHook
)PyMac_GUSISpin
);
226 /* Called at exit() time thru atexit(), to stop event processing */
228 PyMac_StopGUSISpin() {
229 PyMac_ConsoleIsDead
= 1;
233 ** Replacement routines for the PLstr... functions so we don't need
234 ** StdCLib. Moreover, that implementation is broken under cfm68k...
238 unsigned char *to
, *fr
;
240 memcpy(to
, fr
, fr
[0]+1);
245 unsigned char *s1
, *s2
;
248 int l
= s1
[0] < s2
[0] ? s1
[0] : s2
[0];
250 res
= memcmp(s1
+1, s2
+1, l
);
256 else if ( s1
[0] > s2
[0] )
262 pascal unsigned char *
267 unsigned char *ptr
= 0;
270 for(p
=str
+1; p
<str
+str
[0]; p
++)
276 #endif /* USE_GUSI */
279 /* Convert C to Pascal string. Returns pointer to static buffer. */
289 buf
[0] = (unsigned char)len
;
290 strncpy((char *)buf
+1, str
, len
);
294 /* Like strerror() but for Mac OS error numbers */
295 char *PyMac_StrError(int err
)
297 static char buf
[256];
301 h
= GetResource('Estr', err
);
305 memcpy(buf
, str
+1, (unsigned char)str
[0]);
306 buf
[(unsigned char)str
[0]] = '\0';
310 sprintf(buf
, "Mac OS error code %d", err
);
315 /* Exception object shared by all Mac specific modules for Mac OS errors */
316 PyObject
*PyMac_OSErrException
;
318 /* Initialize and return PyMac_OSErrException */
320 PyMac_GetOSErrException()
322 if (PyMac_OSErrException
== NULL
)
323 PyMac_OSErrException
= PyString_FromString("Mac OS Error");
324 return PyMac_OSErrException
;
327 /* Set a MAC-specific error from errno, and return NULL; return None if no error */
329 PyErr_Mac(PyObject
*eobj
, int err
)
334 if (err
== 0 && !PyErr_Occurred()) {
338 if (err
== -1 && PyErr_Occurred())
340 msg
= PyMac_StrError(err
);
341 v
= Py_BuildValue("(is)", err
, msg
);
342 PyErr_SetObject(eobj
, v
);
347 /* Call PyErr_Mac with PyMac_OSErrException */
349 PyMac_Error(OSErr err
)
351 return PyErr_Mac(PyMac_GetOSErrException(), err
);
354 #ifdef USE_STACKCHECK
355 /* Check for stack overflow */
362 if ( left
< MINIMUM_STACK_SIZE
)
366 #endif /* USE_STACKCHECK */
368 /* The catcher routine (which may not be used for all compilers) */
374 signal(SIGINT
, intcatcher
);
378 PyOS_InitInterrupts()
380 if (signal(SIGINT
, SIG_IGN
) != SIG_IGN
)
381 signal(SIGINT
, intcatcher
);
385 PyOS_FiniInterrupts()
390 ** This routine scans the event queue looking for cmd-.
391 ** This is the only way to get an interrupt under THINK (since it
392 ** doesn't do SIGINT handling), but is also used under MW, when
393 ** the full-fledged event loop is disabled. This way, we can at least
394 ** interrupt a runaway python program.
397 scan_event_queue(flush
)
402 q
= (EvQElPtr
) LMGetEventQueue()->qHead
;
404 for (; q
; q
= (EvQElPtr
)q
->qLink
) {
405 if (q
->evtQWhat
== keyDown
&&
406 (char)q
->evtQMessage
== '.' &&
407 (q
->evtQModifiers
& cmdKey
) != 0) {
409 FlushEvents(keyDownMask
, 0);
419 if (schedparams
.enabled
) {
420 if ( (unsigned long)LMGetTicks() > schedparams
.next_check
) {
421 if ( PyMac_Yield() < 0)
423 schedparams
.next_check
= (unsigned long)LMGetTicks()
424 + schedparams
.check_interval
;
426 scan_event_queue(1); /* Eat events up to cmd-. */
428 PyErr_SetNone(PyExc_KeyboardInterrupt
);
437 PyOS_InterruptOccurred()
442 /* Check whether we are in the foreground */
446 static ProcessSerialNumber ours
;
448 ProcessSerialNumber curfg
;
452 (void)GetCurrentProcess(&ours
);
455 if ( GetFrontProcess(&curfg
) < 0 )
457 else if ( SameProcess(&ours
, &curfg
, &eq
) < 0 )
464 PyMac_SetEventHandler(PyObject
*evh
)
466 if ( evh
&& python_event_handler
) {
467 PyErr_SetString(PyExc_RuntimeError
, "Python event handler already set");
470 if ( python_event_handler
)
471 Py_DECREF(python_event_handler
);
474 python_event_handler
= evh
;
479 ** Handle an event, either one found in the mainloop eventhandler or
480 ** one passed back from the python program.
483 PyMac_HandleEventIntern(evp
)
486 if ( evp
->what
== mouseDown
) {
489 if ( FindWindow(evp
->where
, &wp
) == inSysWindow
) {
490 SystemClick(evp
, wp
);
498 /* If SIOUX wants it we're done */
499 siouxdidit
= SIOUXHandleOneEvent(evp
);
504 /* Other compilers are just unlucky... */
505 #endif /* !__MWERKS__ */
509 ** Handle an event, either through HandleEvent or by passing it to the Python
513 PyMac_HandleEvent(evp
)
518 if ( python_event_handler
) {
519 rv
= PyObject_CallFunction(python_event_handler
, "(O&)",
520 PyMac_BuildEventRecord
, evp
);
524 return -1; /* Propagate exception */
526 PyMac_HandleEventIntern(evp
);
532 ** Yield the CPU to other tasks without processing events.
535 PyMac_DoYield(int maxsleep
, int maycallpython
)
539 long latest_time_ready
;
540 static int in_here
= 0;
544 ** First check for interrupts, if wanted.
545 ** This sets a flag that will be picked up at an appropriate
546 ** moment in the mainloop.
548 if (schedparams
.check_interrupt
)
551 /* XXXX Implementing an idle routine goes here */
554 ** Check which of the eventloop cases we have:
556 ** - don't process events but do yield
559 if( in_here
> 1 || !schedparams
.process_events
||
560 (python_event_handler
&& !maycallpython
) ) {
561 if ( maxsleep
>= 0 ) {
565 latest_time_ready
= LMGetTicks() + maxsleep
;
566 while ( maxsleep
>= 0 ) {
567 /* XXXX Hack by Jack.
568 ** In time.sleep() you can click to another application
569 ** once only. If you come back to Python you cannot get away
572 gotone
= WaitNextEvent(schedparams
.process_events
, &ev
, maxsleep
, NULL
);
573 /* Get out quickly if nothing interesting is happening */
574 if ( !gotone
|| ev
.what
== nullEvent
)
576 if ( PyMac_HandleEvent(&ev
) < 0 ) {
580 maxsleep
= latest_time_ready
- LMGetTicks();
588 ** Process events and/or yield the CPU to other tasks if opportune
592 unsigned long maxsleep
;
594 if( PyMac_InForeground() )
597 maxsleep
= schedparams
.bg_yield
;
599 return PyMac_DoYield(maxsleep
, 1);
603 ** Return current scheduler parameters
606 PyMac_GetSchedParams(PyMacSchedParams
*sp
)
608 sp
->check_interrupt
= schedparams
.check_interrupt
;
609 sp
->process_events
= schedparams
.process_events
;
610 sp
->besocial
= schedparams
.besocial
;
611 sp
->check_interval
= schedparams
.check_interval
/ 60.0;
612 sp
->bg_yield
= schedparams
.bg_yield
/ 60.0;
616 ** Set current scheduler parameters
619 PyMac_SetSchedParams(PyMacSchedParams
*sp
)
621 schedparams
.check_interrupt
= sp
->check_interrupt
;
622 schedparams
.process_events
= sp
->process_events
;
623 schedparams
.besocial
= sp
->besocial
;
624 schedparams
.check_interval
= (unsigned long)(sp
->check_interval
*60);
625 schedparams
.bg_yield
= (unsigned long)(sp
->bg_yield
*60);
626 if ( schedparams
.check_interrupt
|| schedparams
.process_events
||
627 schedparams
.besocial
)
628 schedparams
.enabled
= 1;
630 schedparams
.enabled
= 0;
631 schedparams
.next_check
= 0; /* Check immedeately */
635 ** Install our menu bar.
640 MenuHandle applemenu
;
642 if ( (sioux_mbar
=GetMenuBar()) == NULL
) {
643 /* Sioux menu not installed yet. Do so */
645 if ( (sioux_mbar
=GetMenuBar()) == NULL
)
648 if ( (applemenu
=GetMenuHandle(SIOUX_APPLEID
)) == NULL
) return;
649 SetMenuItemText(applemenu
, 1, "\pAbout Python...");
653 ** Restore sioux menu bar
656 PyMac_RestoreMenuBar()
659 SetMenuBar(sioux_mbar
);
667 ** Our replacement about box
670 #include "patchlevel.h"
673 SIOUXDoAboutBox(void)
680 if( (theDialog
= GetNewDialog(ABOUT_ID
, NULL
, (WindowPtr
)-1)) == NULL
)
682 theWindow
= GetDialogWindow(theDialog
);
683 SetPortWindowPort(theWindow
);
684 GetFNum("\pPython-Sans", &fontID
);
686 fontID
= kFontIDGeneva
;
689 ParamText(Pstring(PATCHLEVEL
), "\p", "\p", "\p");
690 ShowWindow(theWindow
);
691 ModalDialog(NULL
, &item
);
692 DisposeDialog(theDialog
);
697 PyMac_FileExists(char *name
)
701 if ( FSMakeFSSpec(0, 0, Pstring(name
), &fss
) == noErr
)
708 ** Helper routine for GetDirectory
711 myhook_proc(short item
, DialogPtr theDialog
, struct hook_args
*dataptr
)
713 if ( item
== sfHookFirstCall
&& dataptr
->prompt
) {
718 GetDialogItem(theDialog
, PROMPT_ITEM
, &type
, &prompth
, &rect
);
720 SetDialogItemText(prompth
, (unsigned char *)dataptr
->prompt
);
722 if ( item
== SELECTCUR_ITEM
) {
723 item
= sfItemCancelButton
;
724 dataptr
->selectcur_hit
= 1;
730 ** Ask the user for a directory. I still can't understand
731 ** why Apple doesn't provide a standard solution for this...
734 PyMac_GetDirectory(dirfss
, prompt
)
738 static SFTypeList list
= {'fldr', 0, 0, 0};
739 static Point where
= {-1, -1};
740 StandardFileReply reply
;
741 struct hook_args hook_args
;
744 myhook_upp
= NewDlgHookYDProc(myhook_proc
);
747 if ( prompt
&& *prompt
)
748 hook_args
.prompt
= (char *)Pstring(prompt
);
750 hook_args
.prompt
= NULL
;
751 hook_args
.selectcur_hit
= 0;
752 CustomGetFile((FileFilterYDUPP
)0, 1, list
, &reply
, GETDIR_ID
, where
, myhook_upp
,
753 NULL
, NULL
, NULL
, (void *)&hook_args
);
755 reply
.sfFile
.name
[0] = 0;
756 if( FSMakeFSSpec(reply
.sfFile
.vRefNum
, reply
.sfFile
.parID
, reply
.sfFile
.name
, dirfss
) )
758 return hook_args
.selectcur_hit
;
762 ** Slightly extended StandardGetFile: accepts a prompt */
763 void PyMac_PromptGetFile(short numTypes
, ConstSFTypeListPtr typeList
,
764 StandardFileReply
*reply
, char *prompt
)
766 static Point where
= {-1, -1};
767 struct hook_args hook_args
;
770 myhook_upp
= NewDlgHookYDProc(myhook_proc
);
773 if ( prompt
&& *prompt
)
774 hook_args
.prompt
= (char *)Pstring(prompt
);
776 hook_args
.prompt
= NULL
;
777 hook_args
.selectcur_hit
= 0;
778 CustomGetFile((FileFilterYDUPP
)0, numTypes
, typeList
, reply
, GETFILEPROMPT_ID
, where
,
779 myhook_upp
, NULL
, NULL
, NULL
, (void *)&hook_args
);
782 /* Convert a 4-char string object argument to an OSType value */
784 PyMac_GetOSType(PyObject
*v
, OSType
*pr
)
786 if (!PyString_Check(v
) || PyString_Size(v
) != 4) {
787 PyErr_SetString(PyExc_TypeError
,
788 "OSType arg must be string of 4 chars");
791 memcpy((char *)pr
, PyString_AsString(v
), 4);
795 /* Convert an OSType value to a 4-char string object */
797 PyMac_BuildOSType(OSType t
)
799 return PyString_FromStringAndSize((char *)&t
, 4);
802 /* Convert an NumVersion value to a 4-element tuple */
804 PyMac_BuildNumVersion(NumVersion t
)
806 return Py_BuildValue("(hhhh)", t
.majorRev
, t
.minorAndBugRev
, t
.stage
, t
.nonRelRev
);
810 /* Convert a Python string object to a Str255 */
812 PyMac_GetStr255(PyObject
*v
, Str255 pbuf
)
815 if (!PyString_Check(v
) || (len
= PyString_Size(v
)) > 255) {
816 PyErr_SetString(PyExc_TypeError
,
817 "Str255 arg must be string of at most 255 chars");
821 memcpy((char *)(pbuf
+1), PyString_AsString(v
), len
);
825 /* Convert a Str255 to a Python string object */
827 PyMac_BuildStr255(Str255 s
)
830 PyErr_SetString(PyExc_SystemError
, "Str255 pointer is NULL");
833 return PyString_FromStringAndSize((char *)&s
[1], (int)s
[0]);
837 PyMac_BuildOptStr255(Str255 s
)
843 return PyString_FromStringAndSize((char *)&s
[1], (int)s
[0]);
848 ** Convert a Python object to an FSSpec.
849 ** The object may either be a full pathname or a triple
850 ** (vrefnum, dirid, path).
851 ** NOTE: This routine will fail on pre-sys7 machines.
852 ** The caller is responsible for not calling this routine
853 ** in those cases (which is fine, since everyone calling
854 ** this is probably sys7 dependent anyway).
857 PyMac_GetFSSpec(PyObject
*v
, FSSpec
*fs
)
865 /* first check whether it already is an FSSpec */
866 fs2
= mfs_GetFSSpecFSSpec(v
);
868 (void)FSMakeFSSpec(fs2
->vRefNum
, fs2
->parID
, fs2
->name
, fs
);
871 if ( PyString_Check(v
) ) {
872 /* It's a pathname */
873 if( !PyArg_Parse(v
, "O&", PyMac_GetStr255
, &path
) )
875 refnum
= 0; /* XXXX Should get CurWD here?? */
878 if( !PyArg_Parse(v
, "(hlO&); FSSpec should be fullpath or (vrefnum,dirid,path)",
879 &refnum
, &parid
, PyMac_GetStr255
, &path
)) {
883 err
= FSMakeFSSpec(refnum
, parid
, path
, fs
);
884 if ( err
&& err
!= fnfErr
) {
891 /* Convert FSSpec to PyObject */
892 PyObject
*PyMac_BuildFSSpec(FSSpec
*v
)
894 return newmfssobject(v
);
897 /* Convert a Python object to a Rect.
898 The object must be a (left, top, right, bottom) tuple.
899 (This differs from the order in the struct but is consistent with
900 the arguments to SetRect(), and also with STDWIN). */
902 PyMac_GetRect(PyObject
*v
, Rect
*r
)
904 return PyArg_Parse(v
, "(hhhh)", &r
->left
, &r
->top
, &r
->right
, &r
->bottom
);
907 /* Convert a Rect to a Python object */
909 PyMac_BuildRect(Rect
*r
)
911 return Py_BuildValue("(hhhh)", r
->left
, r
->top
, r
->right
, r
->bottom
);
915 /* Convert a Python object to a Point.
916 The object must be a (h, v) tuple.
917 (This differs from the order in the struct but is consistent with
918 the arguments to SetPoint(), and also with STDWIN). */
920 PyMac_GetPoint(PyObject
*v
, Point
*p
)
922 return PyArg_Parse(v
, "(hh)", &p
->h
, &p
->v
);
925 /* Convert a Point to a Python object */
927 PyMac_BuildPoint(Point p
)
929 return Py_BuildValue("(hh)", p
.h
, p
.v
);
933 /* Convert a Python object to an EventRecord.
934 The object must be a (what, message, when, (v, h), modifiers) tuple. */
936 PyMac_GetEventRecord(PyObject
*v
, EventRecord
*e
)
938 return PyArg_Parse(v
, "(hll(hh)h)",
947 /* Convert a Rect to an EventRecord object */
949 PyMac_BuildEventRecord(EventRecord
*e
)
951 return Py_BuildValue("(hll(hh)h)",
960 /* Convert Python object to Fixed */
962 PyMac_GetFixed(PyObject
*v
, Fixed
*f
)
966 if( !PyArg_Parse(v
, "d", &d
))
968 *f
= (Fixed
)(d
* 0x10000);
972 /* Convert a Point to a Python object */
974 PyMac_BuildFixed(Fixed f
)
980 return Py_BuildValue("d", d
);
983 /* Convert wide to/from Python int or (hi, lo) tuple. XXXX Should use Python longs */
985 PyMac_Getwide(PyObject
*v
, wide
*rv
)
987 if (PyInt_Check(v
)) {
989 rv
->lo
= PyInt_AsLong(v
);
990 if( rv
->lo
& 0x80000000 )
994 return PyArg_Parse(v
, "(ll)", &rv
->hi
, &rv
->lo
);
999 PyMac_Buildwide(wide
*w
)
1001 if ( (w
->hi
== 0 && (w
->lo
& 0x80000000) == 0) ||
1002 (w
->hi
== -1 && (w
->lo
& 0x80000000) ) )
1003 return PyInt_FromLong(w
->lo
);
1004 return Py_BuildValue("(ll)", w
->hi
, w
->lo
);