The 0.5 release happened on 2/15, not on 2/14. :-)
[python/dscho.git] / Mac / Python / macglue.c
blob46493d3c7e4f966e98997f1c9b4b31c211b8346b
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 ******************************************************************/
25 #ifdef __CFM68K__
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__ */
33 #include <Events.h>
35 #ifdef __CFM68K__
36 #undef GetEventQueue
37 #endif /* __CFM68K__ */
39 #include "Python.h"
41 #include "macglue.h"
42 #include "marshal.h"
43 #include "import.h"
44 #include "importdl.h"
46 #include "pythonresources.h"
48 #include <OSUtils.h> /* for Set(Current)A5 */
49 #include <Files.h>
50 #include <StandardFile.h>
51 #include <Resources.h>
52 #include <Memory.h>
53 #include <Windows.h>
54 #include <Traps.h>
55 #include <Processes.h>
56 #include <Fonts.h>
57 #include <Menus.h>
58 #include <TextUtils.h>
59 #ifdef THINK_C
60 #include <OSEvents.h> /* For EvQElPtr */
61 #endif
62 #ifdef __MWERKS__
63 #include <SIOUX.h>
64 #endif
65 #ifdef USE_GUSI
66 #include <TFileSpec.h> /* For Path2FSSpec */
67 #include <LowMem.h> /* For SetSFCurDir, etc */
68 #include <GUSI.h>
69 #endif
71 /* The ID of the Sioux apple menu */
72 #define SIOUX_APPLEID 32000
74 #include <signal.h>
75 #include <stdio.h>
78 ** When less than this amount of stackspace is left we
79 ** raise a MemoryError.
81 #ifndef MINIMUM_STACK_SIZE
82 #ifdef __powerc
83 #define MINIMUM_STACK_SIZE 8192
84 #else
85 #define MINIMUM_STACK_SIZE 4096
86 #endif
87 #endif
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)
97 #include <signal.h>
99 /* XXX We should include Errors.h here, but it has a name conflict
100 ** with the python errors.h. */
101 #define fnfErr -43
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
147 struct hook_args {
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;
164 #ifdef USE_GUSI
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.
170 void
171 PyMac_FixGUSIcd()
173 WDPBRec pb;
174 FSSpec curdirfss;
176 if ( Path2FSSpec(":x", &curdirfss) != noErr )
177 return;
179 /* Set MacOS "working directory" */
180 pb.ioNamePtr= "\p";
181 pb.ioVRefNum= curdirfss.vRefNum;
182 pb.ioWDDirID= curdirfss.parID;
183 if (PBHSetVolSync(&pb) != noErr)
184 return;
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
197 static int
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;
204 #if 0
205 if (inForeground)
206 SpinCursor(msg == SP_AUTO_SPIN ? short(arg) : 1);
207 #endif
209 if (interrupted) return -1;
211 if ( msg == SP_AUTO_SPIN )
212 maxsleep = 0;
213 if ( msg==SP_SLEEP||msg==SP_SELECT )
214 maxsleep = arg;
216 PyMac_DoYield(maxsleep, 0); /* XXXX or is it safe to call python here? */
218 return 0;
221 void
222 PyMac_SetGUSISpin() {
223 GUSISetHook(GUSI_SpinHook, (GUSIHook)PyMac_GUSISpin);
226 /* Called at exit() time thru atexit(), to stop event processing */
227 void
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...
236 pascal void
237 PLstrcpy(to, fr)
238 unsigned char *to, *fr;
240 memcpy(to, fr, fr[0]+1);
243 pascal int
244 PLstrcmp(s1, s2)
245 unsigned char *s1, *s2;
247 int res;
248 int l = s1[0] < s2[0] ? s1[0] : s2[0];
250 res = memcmp(s1+1, s2+1, l);
251 if ( res != 0 )
252 return res;
254 if ( s1[0] < s2[0] )
255 return -1;
256 else if ( s1[0] > s2[0] )
257 return 1;
258 else
259 return 0;
262 pascal unsigned char *
263 PLstrrchr(str, chr)
264 unsigned char *str;
265 unsigned char chr;
267 unsigned char *ptr = 0;
268 unsigned char *p;
270 for(p=str+1; p<str+str[0]; p++)
271 if ( *p == chr )
272 ptr = p;
273 return ptr;
276 #endif /* USE_GUSI */
279 /* Convert C to Pascal string. Returns pointer to static buffer. */
280 unsigned char *
281 Pstring(char *str)
283 static Str255 buf;
284 int len;
286 len = strlen(str);
287 if (len > 255)
288 len = 255;
289 buf[0] = (unsigned char)len;
290 strncpy((char *)buf+1, str, len);
291 return buf;
294 /* Like strerror() but for Mac OS error numbers */
295 char *PyMac_StrError(int err)
297 static char buf[256];
298 Handle h;
299 char *str;
301 h = GetResource('Estr', err);
302 if ( h ) {
303 HLock(h);
304 str = (char *)*h;
305 memcpy(buf, str+1, (unsigned char)str[0]);
306 buf[(unsigned char)str[0]] = '\0';
307 HUnlock(h);
308 ReleaseResource(h);
309 } else {
310 sprintf(buf, "Mac OS error code %d", err);
312 return buf;
315 /* Exception object shared by all Mac specific modules for Mac OS errors */
316 PyObject *PyMac_OSErrException;
318 /* Initialize and return PyMac_OSErrException */
319 PyObject *
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 */
328 PyObject *
329 PyErr_Mac(PyObject *eobj, int err)
331 char *msg;
332 PyObject *v;
334 if (err == 0 && !PyErr_Occurred()) {
335 Py_INCREF(Py_None);
336 return Py_None;
338 if (err == -1 && PyErr_Occurred())
339 return NULL;
340 msg = PyMac_StrError(err);
341 v = Py_BuildValue("(is)", err, msg);
342 PyErr_SetObject(eobj, v);
343 Py_DECREF(v);
344 return NULL;
347 /* Call PyErr_Mac with PyMac_OSErrException */
348 PyObject *
349 PyMac_Error(OSErr err)
351 return PyErr_Mac(PyMac_GetOSErrException(), err);
354 #ifdef USE_STACKCHECK
355 /* Check for stack overflow */
357 PyOS_CheckStack()
359 long left;
361 left = StackSpace();
362 if ( left < MINIMUM_STACK_SIZE )
363 return -1;
364 return 0;
366 #endif /* USE_STACKCHECK */
368 /* The catcher routine (which may not be used for all compilers) */
369 static RETSIGTYPE
370 intcatcher(sig)
371 int sig;
373 interrupted = 1;
374 signal(SIGINT, intcatcher);
377 void
378 PyOS_InitInterrupts()
380 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
381 signal(SIGINT, intcatcher);
384 void
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.
396 static void
397 scan_event_queue(flush)
398 int flush;
400 register EvQElPtr q;
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) {
408 if ( flush )
409 FlushEvents(keyDownMask, 0);
410 interrupted = 1;
411 break;
417 PyErr_CheckSignals()
419 if (schedparams.enabled) {
420 if ( (unsigned long)LMGetTicks() > schedparams.next_check ) {
421 if ( PyMac_Yield() < 0)
422 return -1;
423 schedparams.next_check = (unsigned long)LMGetTicks()
424 + schedparams.check_interval;
425 if (interrupted) {
426 scan_event_queue(1); /* Eat events up to cmd-. */
427 interrupted = 0;
428 PyErr_SetNone(PyExc_KeyboardInterrupt);
429 return -1;
433 return 0;
437 PyOS_InterruptOccurred()
439 scan_event_queue(1);
440 return interrupted;
442 /* Check whether we are in the foreground */
444 PyMac_InForeground()
446 static ProcessSerialNumber ours;
447 static inited;
448 ProcessSerialNumber curfg;
449 Boolean eq;
451 if ( inited == 0 ) {
452 (void)GetCurrentProcess(&ours);
453 inited = 1;
455 if ( GetFrontProcess(&curfg) < 0 )
456 eq = 1;
457 else if ( SameProcess(&ours, &curfg, &eq) < 0 )
458 eq = 1;
459 return (int)eq;
464 PyMac_SetEventHandler(PyObject *evh)
466 if ( evh && python_event_handler ) {
467 PyErr_SetString(PyExc_RuntimeError, "Python event handler already set");
468 return 0;
470 if ( python_event_handler )
471 Py_DECREF(python_event_handler);
472 if ( evh )
473 Py_INCREF(evh);
474 python_event_handler = evh;
475 return 1;
479 ** Handle an event, either one found in the mainloop eventhandler or
480 ** one passed back from the python program.
482 void
483 PyMac_HandleEventIntern(evp)
484 EventRecord *evp;
486 if ( evp->what == mouseDown ) {
487 WindowPtr wp;
489 if ( FindWindow(evp->where, &wp) == inSysWindow ) {
490 SystemClick(evp, wp);
491 return;
494 #ifdef __MWERKS__
496 int siouxdidit;
498 /* If SIOUX wants it we're done */
499 siouxdidit = SIOUXHandleOneEvent(evp);
500 if ( siouxdidit )
501 return;
503 #else
504 /* Other compilers are just unlucky... */
505 #endif /* !__MWERKS__ */
509 ** Handle an event, either through HandleEvent or by passing it to the Python
510 ** event handler.
513 PyMac_HandleEvent(evp)
514 EventRecord *evp;
516 PyObject *rv;
518 if ( python_event_handler ) {
519 rv = PyObject_CallFunction(python_event_handler, "(O&)",
520 PyMac_BuildEventRecord, evp);
521 if ( rv )
522 Py_DECREF(rv);
523 else
524 return -1; /* Propagate exception */
525 } else {
526 PyMac_HandleEventIntern(evp);
528 return 0;
532 ** Yield the CPU to other tasks without processing events.
534 static int
535 PyMac_DoYield(int maxsleep, int maycallpython)
537 EventRecord ev;
538 int gotone;
539 long latest_time_ready;
540 static int in_here = 0;
542 in_here++;
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)
549 scan_event_queue(0);
551 /* XXXX Implementing an idle routine goes here */
554 ** Check which of the eventloop cases we have:
555 ** - process events
556 ** - don't process events but do yield
557 ** - do neither
559 if( in_here > 1 || !schedparams.process_events ||
560 (python_event_handler && !maycallpython) ) {
561 if ( maxsleep >= 0 ) {
562 SystemTask();
564 } else {
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
570 ** again.
572 gotone = WaitNextEvent(schedparams.process_events, &ev, maxsleep, NULL);
573 /* Get out quickly if nothing interesting is happening */
574 if ( !gotone || ev.what == nullEvent )
575 break;
576 if ( PyMac_HandleEvent(&ev) < 0 ) {
577 in_here--;
578 return -1;
580 maxsleep = latest_time_ready - LMGetTicks();
583 in_here--;
584 return 0;
588 ** Process events and/or yield the CPU to other tasks if opportune
591 PyMac_Yield() {
592 unsigned long maxsleep;
594 if( PyMac_InForeground() )
595 maxsleep = 0;
596 else
597 maxsleep = schedparams.bg_yield;
599 return PyMac_DoYield(maxsleep, 1);
603 ** Return current scheduler parameters
605 void
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
618 void
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;
629 else
630 schedparams.enabled = 0;
631 schedparams.next_check = 0; /* Check immedeately */
635 ** Install our menu bar.
637 void
638 PyMac_InitMenuBar()
640 MenuHandle applemenu;
642 if ( (sioux_mbar=GetMenuBar()) == NULL ) {
643 /* Sioux menu not installed yet. Do so */
644 SIOUXSetupMenus();
645 if ( (sioux_mbar=GetMenuBar()) == NULL )
646 return;
648 if ( (applemenu=GetMenuHandle(SIOUX_APPLEID)) == NULL ) return;
649 SetMenuItemText(applemenu, 1, "\pAbout Python...");
653 ** Restore sioux menu bar
655 void
656 PyMac_RestoreMenuBar()
658 if ( sioux_mbar ) {
659 SetMenuBar(sioux_mbar);
660 DrawMenuBar();
661 } else
662 PyMac_InitMenuBar();
667 ** Our replacement about box
670 #include "patchlevel.h"
672 void
673 SIOUXDoAboutBox(void)
675 DialogPtr theDialog;
676 WindowPtr theWindow;
677 short item;
678 short fontID;
680 if( (theDialog = GetNewDialog(ABOUT_ID, NULL, (WindowPtr)-1)) == NULL )
681 return;
682 theWindow = GetDialogWindow(theDialog);
683 SetPortWindowPort(theWindow);
684 GetFNum("\pPython-Sans", &fontID);
685 if (fontID == 0)
686 fontID = kFontIDGeneva;
687 TextFont(fontID);
688 TextSize(9);
689 ParamText(Pstring(PATCHLEVEL), "\p", "\p", "\p");
690 ShowWindow(theWindow);
691 ModalDialog(NULL, &item);
692 DisposeDialog(theDialog);
695 #if 0
697 PyMac_FileExists(char *name)
699 FSSpec fss;
701 if ( FSMakeFSSpec(0, 0, Pstring(name), &fss) == noErr )
702 return 1;
703 return 0;
705 #endif
708 ** Helper routine for GetDirectory
710 static pascal short
711 myhook_proc(short item, DialogPtr theDialog, struct hook_args *dataptr)
713 if ( item == sfHookFirstCall && dataptr->prompt) {
714 Handle prompth;
715 short type;
716 Rect rect;
718 GetDialogItem(theDialog, PROMPT_ITEM, &type, &prompth, &rect);
719 if ( prompth )
720 SetDialogItemText(prompth, (unsigned char *)dataptr->prompt);
721 } else
722 if ( item == SELECTCUR_ITEM ) {
723 item = sfItemCancelButton;
724 dataptr->selectcur_hit = 1;
726 return item;
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)
735 FSSpec *dirfss;
736 char *prompt;
738 static SFTypeList list = {'fldr', 0, 0, 0};
739 static Point where = {-1, -1};
740 StandardFileReply reply;
741 struct hook_args hook_args;
743 if ( !upp_inited ) {
744 myhook_upp = NewDlgHookYDProc(myhook_proc);
745 upp_inited = 1;
747 if ( prompt && *prompt )
748 hook_args.prompt = (char *)Pstring(prompt);
749 else
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) )
757 return 0;
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;
769 if ( !upp_inited ) {
770 myhook_upp = NewDlgHookYDProc(myhook_proc);
771 upp_inited = 1;
773 if ( prompt && *prompt )
774 hook_args.prompt = (char *)Pstring(prompt);
775 else
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");
789 return 0;
791 memcpy((char *)pr, PyString_AsString(v), 4);
792 return 1;
795 /* Convert an OSType value to a 4-char string object */
796 PyObject *
797 PyMac_BuildOSType(OSType t)
799 return PyString_FromStringAndSize((char *)&t, 4);
802 /* Convert an NumVersion value to a 4-element tuple */
803 PyObject *
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)
814 int len;
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");
818 return 0;
820 pbuf[0] = len;
821 memcpy((char *)(pbuf+1), PyString_AsString(v), len);
822 return 1;
825 /* Convert a Str255 to a Python string object */
826 PyObject *
827 PyMac_BuildStr255(Str255 s)
829 if ( s == NULL ) {
830 PyErr_SetString(PyExc_SystemError, "Str255 pointer is NULL");
831 return NULL;
833 return PyString_FromStringAndSize((char *)&s[1], (int)s[0]);
836 PyObject *
837 PyMac_BuildOptStr255(Str255 s)
839 if ( s == NULL ) {
840 Py_INCREF(Py_None);
841 return Py_None;
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)
859 Str255 path;
860 short refnum;
861 long parid;
862 OSErr err;
863 FSSpec *fs2;
865 /* first check whether it already is an FSSpec */
866 fs2 = mfs_GetFSSpecFSSpec(v);
867 if ( fs2 ) {
868 (void)FSMakeFSSpec(fs2->vRefNum, fs2->parID, fs2->name, fs);
869 return 1;
871 if ( PyString_Check(v) ) {
872 /* It's a pathname */
873 if( !PyArg_Parse(v, "O&", PyMac_GetStr255, &path) )
874 return 0;
875 refnum = 0; /* XXXX Should get CurWD here?? */
876 parid = 0;
877 } else {
878 if( !PyArg_Parse(v, "(hlO&); FSSpec should be fullpath or (vrefnum,dirid,path)",
879 &refnum, &parid, PyMac_GetStr255, &path)) {
880 return 0;
883 err = FSMakeFSSpec(refnum, parid, path, fs);
884 if ( err && err != fnfErr ) {
885 PyMac_Error(err);
886 return 0;
888 return 1;
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 */
908 PyObject *
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 */
926 PyObject *
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)",
939 &e->what,
940 &e->message,
941 &e->when,
942 &e->where.h,
943 &e->where.v,
944 &e->modifiers);
947 /* Convert a Rect to an EventRecord object */
948 PyObject *
949 PyMac_BuildEventRecord(EventRecord *e)
951 return Py_BuildValue("(hll(hh)h)",
952 e->what,
953 e->message,
954 e->when,
955 e->where.h,
956 e->where.v,
957 e->modifiers);
960 /* Convert Python object to Fixed */
962 PyMac_GetFixed(PyObject *v, Fixed *f)
964 double d;
966 if( !PyArg_Parse(v, "d", &d))
967 return 0;
968 *f = (Fixed)(d * 0x10000);
969 return 1;
972 /* Convert a Point to a Python object */
973 PyObject *
974 PyMac_BuildFixed(Fixed f)
976 double d;
978 d = f;
979 d = d / 0x10000;
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)) {
988 rv->hi = 0;
989 rv->lo = PyInt_AsLong(v);
990 if( rv->lo & 0x80000000 )
991 rv->hi = -1;
992 return 1;
994 return PyArg_Parse(v, "(ll)", &rv->hi, &rv->lo);
998 PyObject *
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);