More installation info. Bump alpha version.
[python/dscho.git] / Mac / Python / macglue.c
blob51d73edb64508f53bf2705e1db5dd295ae66c801
1 /***********************************************************
2 Copyright 1991-1997 by Stichting Mathematisch Centrum, Amsterdam,
3 The Netherlands.
5 All Rights Reserved
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 #include "Python.h"
28 #include "macglue.h"
29 #include "marshal.h"
30 #include "import.h"
31 #include "importdl.h"
32 #include "pymactoolbox.h"
34 #include "pythonresources.h"
36 #ifdef WITHOUT_FRAMEWORKS
37 #include <OSUtils.h> /* for Set(Current)A5 */
38 #include <Files.h>
39 #include <StandardFile.h>
40 #include <Resources.h>
41 #include <Memory.h>
42 #include <Windows.h>
43 #include <Traps.h>
44 #include <Processes.h>
45 #include <Fonts.h>
46 #include <Menus.h>
47 #include <TextUtils.h>
48 #include <LowMem.h>
49 #include <Events.h>
50 #else
51 #include <Carbon/Carbon.h>
52 #endif
54 #ifdef __MWERKS__
55 #include <SIOUX.h>
56 extern void SIOUXSetupMenus(void);
57 extern void SIOUXDoAboutBox(void);
58 #endif
59 #ifdef USE_GUSI
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);
67 #endif
69 /* The ID of the Sioux apple menu */
70 #define SIOUX_APPLEID 32000
72 #include <signal.h>
73 #include <stdio.h>
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
81 #endif
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)
99 #include <signal.h>
101 /* XXX We should include Errors.h here, but it has a name conflict
102 ** with the python errors.h. */
103 #define fnfErr -43
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);
111 #endif
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 */
150 static OSErr
151 get_folder_parent (FSSpec * fss, FSSpec * parent)
153 CInfoPBRec rec;
154 short err;
156 * parent = * fss;
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))
163 return err;
164 parent->parID = rec.dirInfo.ioDrParID;
165 /* parent->name[0] = 0; */
166 return 0;
169 /* Given an FSSpec return a full, colon-separated pathname */
171 OSErr
172 PyMac_GetFullPathname (FSSpec *fss, char *buf, int length)
174 short err;
175 FSSpec fss_parent, fss_current;
176 char tmpbuf[1024];
177 int plen;
179 fss_current = *fss;
180 plen = fss_current.name[0];
181 if ( plen+2 > length ) {
182 *buf = 0;
183 return errFSNameTooLong;
185 memcpy(buf, &fss_current.name[1], plen);
186 buf[plen] = 0;
187 /* Special case for disk names */
188 if ( fss_current.parID <= 1 ) {
189 buf[plen++] = ':';
190 buf[plen] = 0;
191 return 0;
193 while (fss_current.parID > 1) {
194 /* Get parent folder name */
195 if (err = get_folder_parent(&fss_current, &fss_parent)) {
196 *buf = 0;
197 return err;
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) */
204 *buf = 0;
205 return errFSNameTooLong;
207 memcpy(tmpbuf, &fss_current.name[1], plen);
208 tmpbuf[plen] = ':';
209 strcpy(&tmpbuf[plen+1], buf);
210 if ( strlen(tmpbuf) > length ) {
211 *buf = 0;
212 return errFSNameTooLong;
214 strcpy(buf, tmpbuf);
216 return 0;
220 #ifdef USE_GUSI
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 */
230 void
231 PyMac_StopGUSISpin() {
232 PyMac_ConsoleIsDead = 1;
235 #endif /* USE_GUSI */
238 /* Convert C to Pascal string. Returns pointer to static buffer. */
239 unsigned char *
240 Pstring(char *str)
242 static Str255 buf;
243 int len;
245 len = strlen(str);
246 if (len > 255)
247 len = 255;
248 buf[0] = (unsigned char)len;
249 strncpy((char *)buf+1, str, len);
250 return buf;
254 #ifdef USE_STACKCHECK
255 /* Check for stack overflow */
257 PyOS_CheckStack()
259 char here;
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;
270 #endif
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()) {
278 return -1;
281 return 0;
283 #endif /* USE_STACKCHECK */
285 #if !TARGET_API_MAC_OSX
286 /* The catcher routine (which may not be used for all compilers) */
287 static RETSIGTYPE
288 intcatcher(sig)
289 int sig;
291 interrupted = 1;
292 signal(SIGINT, intcatcher);
295 void
296 PyOS_InitInterrupts()
298 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
299 signal(SIGINT, intcatcher);
302 void
303 PyOS_FiniInterrupts()
307 /* Check whether we are in the foreground */
308 static int
309 PyMac_InForeground(void)
311 static ProcessSerialNumber ours;
312 static inited;
313 ProcessSerialNumber curfg;
314 Boolean eq;
316 if ( inited == 0 ) {
317 (void)GetCurrentProcess(&ours);
318 inited = 1;
320 if ( GetFrontProcess(&curfg) < 0 )
321 eq = 1;
322 else if ( SameProcess(&ours, &curfg, &eq) < 0 )
323 eq = 1;
324 return (int)eq;
328 ** This routine scans the event queue looking for cmd-.
330 static void
331 scan_event_queue(force)
332 int force;
334 if ( interrupted || (!schedparams.check_interrupt && !force) )
335 return;
336 if ( CheckEventQueueForUserCancel() )
337 interrupted = 1;
341 PyErr_CheckSignals()
343 if (schedparams.enabled) {
344 if ( interrupted || (unsigned long)TickCount() > schedparams.next_check ) {
345 scan_event_queue(0);
346 if (interrupted) {
347 interrupted = 0;
348 PyErr_SetNone(PyExc_KeyboardInterrupt);
349 return -1;
351 if ( PyMac_Yield() < 0)
352 return -1;
353 schedparams.next_check = (unsigned long)TickCount()
354 + schedparams.check_interval;
357 return 0;
361 PyOS_InterruptOccurred()
363 scan_event_queue(0);
364 if ( !interrupted )
365 return 0;
366 interrupted = 0;
367 return 1;
369 #endif
372 PyMac_SetEventHandler(PyObject *evh)
374 if ( evh && python_event_handler ) {
375 PyErr_SetString(PyExc_RuntimeError, "Python event handler already set");
376 return 0;
378 if ( python_event_handler )
379 Py_DECREF(python_event_handler);
380 if ( evh )
381 Py_INCREF(evh);
382 python_event_handler = evh;
383 return 1;
387 ** Handle an event, either one found in the mainloop eventhandler or
388 ** one passed back from the python program.
390 void
391 PyMac_HandleEventIntern(evp)
392 EventRecord *evp;
394 #ifdef __MWERKS__
396 int siouxdidit;
398 /* If SIOUX wants it we're done */
399 siouxdidit = SIOUXHandleOneEvent(evp);
400 if ( siouxdidit )
401 return;
403 #else
404 /* Other compilers are just unlucky... */
405 #endif /* !__MWERKS__ */
409 ** Handle an event, either through HandleEvent or by passing it to the Python
410 ** event handler.
413 PyMac_HandleEvent(evp)
414 EventRecord *evp;
416 PyObject *rv;
418 if ( python_event_handler ) {
419 rv = PyObject_CallFunction(python_event_handler, "(O&)",
420 PyMac_BuildEventRecord, evp);
421 if ( rv )
422 Py_DECREF(rv);
423 else
424 return -1; /* Propagate exception */
425 } else {
426 PyMac_HandleEventIntern(evp);
428 return 0;
431 #if !TARGET_API_MAC_OSX
433 ** Yield the CPU to other tasks without processing events.
436 PyMac_DoYield(int maxsleep, int maycallpython)
438 EventRecord ev;
439 int gotone;
440 long latest_time_ready;
441 static int in_here = 0;
443 in_here++;
446 ** Check which of the eventloop cases we have:
447 ** - process events
448 ** - don't process events but do yield
449 ** - do neither
451 if( in_here > 1 || !schedparams.process_events ||
452 (python_event_handler && !maycallpython) ) {
453 if ( maxsleep >= 0 ) {
454 /* XXXX Need to do something here */
456 } else {
457 latest_time_ready = TickCount() + maxsleep;
458 do {
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
462 ** again.
464 gotone = WaitNextEvent(schedparams.process_events, &ev, maxsleep, NULL);
465 /* Get out quickly if nothing interesting is happening */
466 if ( !gotone || ev.what == nullEvent )
467 break;
468 if ( PyMac_HandleEvent(&ev) < 0 ) {
469 in_here--;
470 return -1;
472 maxsleep = latest_time_ready - TickCount();
473 } while ( maxsleep > 0 );
475 in_here--;
476 return 0;
480 ** Process events and/or yield the CPU to other tasks if opportune
483 PyMac_Yield() {
484 unsigned long maxsleep;
486 if( PyMac_InForeground() )
487 maxsleep = 0;
488 else
489 maxsleep = schedparams.bg_yield;
491 return PyMac_DoYield(maxsleep, 1);
495 ** Return current scheduler parameters
497 void
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
510 void
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;
521 else
522 schedparams.enabled = 0;
523 schedparams.next_check = 0; /* Check immedeately */
527 ** Install our menu bar.
529 void
530 PyMac_InitMenuBar()
532 MenuHandle applemenu;
533 Str255 about_text;
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 */
539 SIOUXSetupMenus();
540 if ( (sioux_mbar=GetMenuBar()) == NULL )
541 return;
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
553 void
554 PyMac_RestoreMenuBar()
556 MenuBarHandle curmenubar;
558 curmenubar = GetMenuBar();
559 if ( sioux_mbar ) {
560 SetMenuBar(sioux_mbar);
561 DrawMenuBar();
562 } else {
563 PyMac_InitMenuBar();
564 DrawMenuBar();
568 void
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 )
577 return;
578 if ( FrontWindow() != *SIOUXTextWindow )
579 BringToFront(*SIOUXTextWindow);
583 ** Our replacement about box
586 #include "patchlevel.h"
588 void
589 SIOUXDoAboutBox(void)
591 DialogPtr theDialog;
592 WindowPtr theWindow;
593 short item;
594 short fontID;
596 if( (theDialog = GetNewDialog(ABOUT_ID, NULL, (WindowPtr)-1)) == NULL )
597 return;
598 theWindow = GetDialogWindow(theDialog);
599 SetPortWindowPort(theWindow);
600 GetFNum("\pPython-Sans", &fontID);
601 if (fontID == 0)
602 fontID = kFontIDGeneva;
603 TextFont(fontID);
604 TextSize(9);
605 ParamText(Pstring(PY_VERSION), "\p", "\p", "\p");
606 ShowWindow(theWindow);
607 ModalDialog(NULL, &item);
608 DisposeDialog(theDialog);
611 #endif /* !TARGET_API_MAC_OSX */