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 ******************************************************************/
32 #include "pymactoolbox.h"
34 #include "pythonresources.h"
36 #ifdef WITHOUT_FRAMEWORKS
37 #include <OSUtils.h> /* for Set(Current)A5 */
39 #include <StandardFile.h>
40 #include <Resources.h>
44 #include <Processes.h>
47 #include <TextUtils.h>
51 #include <Carbon/Carbon.h>
56 extern void SIOUXSetupMenus(void);
57 extern void SIOUXDoAboutBox(void);
60 /* Functions we redefine because they're in obscure libraries */
61 extern void SpinCursor(short x
);
62 extern void RotateCursor(short x
);
63 extern pascal unsigned char * PLstrcpy(unsigned char *, const unsigned char *);
64 extern pascal short PLstrcmp(const unsigned char *, const unsigned char *);
65 extern pascal char *PLstrrchr(const unsigned char *, short);
69 /* The ID of the Sioux apple menu */
70 #define SIOUX_APPLEID 32000
76 ** When less than this amount of stackspace is left we
77 ** raise a MemoryError.
79 #ifndef MINIMUM_STACK_SIZE
80 #define MINIMUM_STACK_SIZE 8192
84 ** On MacOSX StackSpace() lies: it gives the distance from heap end to stack pointer,
85 ** but the stack cannot grow that far due to rlimit values. We cannot get at this value
86 ** from Carbon, so we set a maximum to the stack here that is based on the default
87 ** stack limit of 512K.
89 #define MAXIMUM_STACK_SIZE (256*1024)
92 ** We have to be careful, since we can't handle
93 ** things like updates (and they'll keep coming back if we don't
94 ** handle them). Note that we don't know who has windows open, so
95 ** even handing updates off to SIOUX under MW isn't going to work.
97 #define MAINLOOP_EVENTMASK (mDownMask|keyDownMask|osMask|activMask)
101 /* XXX We should include Errors.h here, but it has a name conflict
102 ** with the python errors.h. */
105 /* Interrupt code variables: */
106 static int interrupted
; /* Set to true when cmd-. seen */
107 static RETSIGTYPE
intcatcher(int);
109 #if !TARGET_API_MAC_OSX
110 static int PyMac_Yield(void);
114 ** These are the real scheduling parameters that control what we check
115 ** in the event loop, and how often we check. The values are initialized
116 ** from pyMac_SchedParamStruct.
119 struct real_sched_param_struct
{
120 int check_interrupt
; /* if true check for command-dot */
121 int process_events
; /* if nonzero enable evt processing, this mask */
122 int besocial
; /* if nonzero be a little social with CPU */
123 unsigned long check_interval
; /* how often to check, in ticks */
124 unsigned long bg_yield
; /* yield so long when in background */
125 /* these are computed from previous and clock and such */
126 int enabled
; /* check_interrupt OR process_event OR yield */
127 unsigned long next_check
; /* when to check/yield next, in ticks */
130 static struct real_sched_param_struct schedparams
=
131 { 1, MAINLOOP_EVENTMASK
, 1, 15, 15, 1, 0};
134 ** Workaround for sioux/gusi combo: set when we are exiting
136 int PyMac_ConsoleIsDead
;
139 ** Sioux menu bar, saved early so we can restore it
141 static MenuBarHandle sioux_mbar
;
144 ** The python-code event handler
146 static PyObject
*python_event_handler
;
148 /* Given an FSSpec, return the FSSpec of the parent folder */
151 get_folder_parent (FSSpec
* fss
, FSSpec
* parent
)
157 rec
.hFileInfo
.ioNamePtr
= parent
->name
;
158 rec
.hFileInfo
.ioVRefNum
= parent
->vRefNum
;
159 rec
.hFileInfo
.ioDirID
= parent
->parID
;
160 rec
.hFileInfo
.ioFDirIndex
= -1;
161 rec
.hFileInfo
.ioFVersNum
= 0;
162 if (err
= PBGetCatInfoSync (& rec
))
164 parent
->parID
= rec
.dirInfo
.ioDrParID
;
165 /* parent->name[0] = 0; */
169 /* Given an FSSpec return a full, colon-separated pathname */
172 PyMac_GetFullPathname (FSSpec
*fss
, char *buf
, int length
)
175 FSSpec fss_parent
, fss_current
;
180 plen
= fss_current
.name
[0];
181 if ( plen
+2 > length
) {
183 return errFSNameTooLong
;
185 memcpy(buf
, &fss_current
.name
[1], plen
);
187 /* Special case for disk names */
188 if ( fss_current
.parID
<= 1 ) {
193 while (fss_current
.parID
> 1) {
194 /* Get parent folder name */
195 if (err
= get_folder_parent(&fss_current
, &fss_parent
)) {
199 fss_current
= fss_parent
;
200 /* Prepend path component just found to buf */
201 plen
= fss_current
.name
[0];
202 if (strlen(buf
) + plen
+ 1 > 1024) {
203 /* Oops... Not enough space (shouldn't happen) */
205 return errFSNameTooLong
;
207 memcpy(tmpbuf
, &fss_current
.name
[1], plen
);
209 strcpy(&tmpbuf
[plen
+1], buf
);
210 if ( strlen(tmpbuf
) > length
) {
212 return errFSNameTooLong
;
222 ** SpinCursor (needed by GUSI) drags in heaps of stuff, so we
223 ** provide a dummy here.
225 void SpinCursor(short x
) { /* Dummy */ }
226 void RotateCursor(short x
) { /* Dummy */ }
229 /* Called at exit() time thru atexit(), to stop event processing */
231 PyMac_StopGUSISpin() {
232 PyMac_ConsoleIsDead
= 1;
235 #endif /* USE_GUSI */
238 /* Convert C to Pascal string. Returns pointer to static buffer. */
248 buf
[0] = (unsigned char)len
;
249 strncpy((char *)buf
+1, str
, len
);
254 #ifdef USE_STACKCHECK
255 /* Check for stack overflow */
260 static char *sentinel
= 0;
261 static PyThreadState
*thread_for_sentinel
= 0;
263 if ( sentinel
== 0 ) {
264 unsigned long stackspace
= StackSpace();
266 #ifdef MAXIMUM_STACK_SIZE
267 /* See the comment at the definition */
268 if ( stackspace
> MAXIMUM_STACK_SIZE
)
269 stackspace
= MAXIMUM_STACK_SIZE
;
271 sentinel
= &here
- stackspace
+ MINIMUM_STACK_SIZE
;
273 if ( thread_for_sentinel
== 0 ) {
274 thread_for_sentinel
= PyThreadState_Get();
276 if ( &here
< sentinel
) {
277 if (thread_for_sentinel
== PyThreadState_Get()) {
283 #endif /* USE_STACKCHECK */
285 #if !TARGET_API_MAC_OSX
286 /* The catcher routine (which may not be used for all compilers) */
292 signal(SIGINT
, intcatcher
);
296 PyOS_InitInterrupts()
298 if (signal(SIGINT
, SIG_IGN
) != SIG_IGN
)
299 signal(SIGINT
, intcatcher
);
303 PyOS_FiniInterrupts()
307 /* Check whether we are in the foreground */
309 PyMac_InForeground(void)
311 static ProcessSerialNumber ours
;
313 ProcessSerialNumber curfg
;
317 (void)GetCurrentProcess(&ours
);
320 if ( GetFrontProcess(&curfg
) < 0 )
322 else if ( SameProcess(&ours
, &curfg
, &eq
) < 0 )
328 ** This routine scans the event queue looking for cmd-.
331 scan_event_queue(force
)
334 if ( interrupted
|| (!schedparams
.check_interrupt
&& !force
) )
336 if ( CheckEventQueueForUserCancel() )
343 if (schedparams
.enabled
) {
344 if ( interrupted
|| (unsigned long)TickCount() > schedparams
.next_check
) {
348 PyErr_SetNone(PyExc_KeyboardInterrupt
);
351 if ( PyMac_Yield() < 0)
353 schedparams
.next_check
= (unsigned long)TickCount()
354 + schedparams
.check_interval
;
361 PyOS_InterruptOccurred()
372 PyMac_SetEventHandler(PyObject
*evh
)
374 if ( evh
&& python_event_handler
) {
375 PyErr_SetString(PyExc_RuntimeError
, "Python event handler already set");
378 if ( python_event_handler
)
379 Py_DECREF(python_event_handler
);
382 python_event_handler
= evh
;
387 ** Handle an event, either one found in the mainloop eventhandler or
388 ** one passed back from the python program.
391 PyMac_HandleEventIntern(evp
)
398 /* If SIOUX wants it we're done */
399 siouxdidit
= SIOUXHandleOneEvent(evp
);
404 /* Other compilers are just unlucky... */
405 #endif /* !__MWERKS__ */
409 ** Handle an event, either through HandleEvent or by passing it to the Python
413 PyMac_HandleEvent(evp
)
418 if ( python_event_handler
) {
419 rv
= PyObject_CallFunction(python_event_handler
, "(O&)",
420 PyMac_BuildEventRecord
, evp
);
424 return -1; /* Propagate exception */
426 PyMac_HandleEventIntern(evp
);
431 #if !TARGET_API_MAC_OSX
433 ** Yield the CPU to other tasks without processing events.
436 PyMac_DoYield(int maxsleep
, int maycallpython
)
440 long latest_time_ready
;
441 static int in_here
= 0;
446 ** Check which of the eventloop cases we have:
448 ** - don't process events but do yield
451 if( in_here
> 1 || !schedparams
.process_events
||
452 (python_event_handler
&& !maycallpython
) ) {
453 if ( maxsleep
>= 0 ) {
454 /* XXXX Need to do something here */
457 latest_time_ready
= TickCount() + maxsleep
;
459 /* XXXX Hack by Jack.
460 ** In time.sleep() you can click to another application
461 ** once only. If you come back to Python you cannot get away
464 gotone
= WaitNextEvent(schedparams
.process_events
, &ev
, maxsleep
, NULL
);
465 /* Get out quickly if nothing interesting is happening */
466 if ( !gotone
|| ev
.what
== nullEvent
)
468 if ( PyMac_HandleEvent(&ev
) < 0 ) {
472 maxsleep
= latest_time_ready
- TickCount();
473 } while ( maxsleep
> 0 );
480 ** Process events and/or yield the CPU to other tasks if opportune
484 unsigned long maxsleep
;
486 if( PyMac_InForeground() )
489 maxsleep
= schedparams
.bg_yield
;
491 return PyMac_DoYield(maxsleep
, 1);
495 ** Return current scheduler parameters
498 PyMac_GetSchedParams(PyMacSchedParams
*sp
)
500 sp
->check_interrupt
= schedparams
.check_interrupt
;
501 sp
->process_events
= schedparams
.process_events
;
502 sp
->besocial
= schedparams
.besocial
;
503 sp
->check_interval
= schedparams
.check_interval
/ 60.0;
504 sp
->bg_yield
= schedparams
.bg_yield
/ 60.0;
508 ** Set current scheduler parameters
511 PyMac_SetSchedParams(PyMacSchedParams
*sp
)
513 schedparams
.check_interrupt
= sp
->check_interrupt
;
514 schedparams
.process_events
= sp
->process_events
;
515 schedparams
.besocial
= sp
->besocial
;
516 schedparams
.check_interval
= (unsigned long)(sp
->check_interval
*60);
517 schedparams
.bg_yield
= (unsigned long)(sp
->bg_yield
*60);
518 if ( schedparams
.check_interrupt
|| schedparams
.process_events
||
519 schedparams
.besocial
)
520 schedparams
.enabled
= 1;
522 schedparams
.enabled
= 0;
523 schedparams
.next_check
= 0; /* Check immedeately */
527 ** Install our menu bar.
532 MenuHandle applemenu
;
534 static unsigned char about_sioux
[] = "\pAbout SIOUX";
536 if ( sioux_mbar
) return;
537 if ( (sioux_mbar
=GetMenuBar()) == NULL
|| GetMenuHandle(SIOUX_APPLEID
) == NULL
) {
538 /* Sioux menu not installed yet. Do so */
540 if ( (sioux_mbar
=GetMenuBar()) == NULL
)
543 if ( (applemenu
=GetMenuHandle(SIOUX_APPLEID
)) == NULL
) return;
544 GetMenuItemText(applemenu
, 1, about_text
);
545 if ( about_text
[0] == about_sioux
[0] &&
546 strncmp((char *)(about_text
+1), (char *)(about_sioux
+1), about_text
[0]) == 0 )
547 SetMenuItemText(applemenu
, 1, "\pAbout Python...");
551 ** Restore sioux menu bar
554 PyMac_RestoreMenuBar()
556 MenuBarHandle curmenubar
;
558 curmenubar
= GetMenuBar();
560 SetMenuBar(sioux_mbar
);
569 PyMac_RaiseConsoleWindow()
571 /* Note: this is a hack. SIOUXTextWindow is SIOUX's internal structure
572 ** and we happen to know that the first entry is the window pointer.
574 extern WindowRef
*SIOUXTextWindow
;
576 if ( SIOUXTextWindow
== NULL
|| *SIOUXTextWindow
== NULL
)
578 if ( FrontWindow() != *SIOUXTextWindow
)
579 BringToFront(*SIOUXTextWindow
);
583 ** Our replacement about box
586 #include "patchlevel.h"
589 SIOUXDoAboutBox(void)
596 if( (theDialog
= GetNewDialog(ABOUT_ID
, NULL
, (WindowPtr
)-1)) == NULL
)
598 theWindow
= GetDialogWindow(theDialog
);
599 SetPortWindowPort(theWindow
);
600 GetFNum("\pPython-Sans", &fontID
);
602 fontID
= kFontIDGeneva
;
605 ParamText(Pstring(PY_VERSION
), "\p", "\p", "\p");
606 ShowWindow(theWindow
);
607 ModalDialog(NULL
, &item
);
608 DisposeDialog(theDialog
);
611 #endif /* !TARGET_API_MAC_OSX */