1/***********************************************************
2Copyright 1991-1997 by Stichting Mathematisch Centrum, Amsterdam,
3The Netherlands.
4
5                        All Rights Reserved
6
7Permission to use, copy, modify, and distribute this software and its
8documentation for any purpose and without fee is hereby granted,
9provided that the above copyright notice appear in all copies and that
10both that copyright notice and this permission notice appear in
11supporting documentation, and that the names of Stichting Mathematisch
12Centrum or CWI not be used in advertising or publicity pertaining to
13distribution of the software without specific, written prior permission.
14
15STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
16THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
18FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22
23******************************************************************/
24
25/* Macintosh OS-specific interface */
26
27#include "Python.h"
28#include "pymactoolbox.h"
29
30#include <Carbon/Carbon.h>
31#include <ApplicationServices/ApplicationServices.h>
32
33#include <arpa/inet.h>  /* for ntohl, htonl */
34
35
36#ifndef HAVE_OSX105_SDK
37typedef SInt16  FSIORefNum;
38#endif
39
40static PyObject *MacOS_Error; /* Exception MacOS.Error */
41
42#define PATHNAMELEN 1024
43
44/* ----------------------------------------------------- */
45
46/* Declarations for objects of type Resource fork */
47
48typedef struct {
49    PyObject_HEAD
50    FSIORefNum fRefNum;
51    int isclosed;
52} rfobject;
53
54static PyTypeObject Rftype;
55
56
57
58/* ---------------------------------------------------------------- */
59
60static void
61do_close(rfobject *self)
62{
63    if (self->isclosed ) return;
64    (void)FSCloseFork(self->fRefNum);
65    self->isclosed = 1;
66}
67
68static char rf_read__doc__[] =
69"Read data from resource fork"
70;
71
72static PyObject *
73rf_read(rfobject *self, PyObject *args)
74{
75    long n;
76    PyObject *v;
77    OSErr err;
78    ByteCount n2;
79
80    if (self->isclosed) {
81        PyErr_SetString(PyExc_ValueError, "Operation on closed file");
82        return NULL;
83    }
84
85    if (!PyArg_ParseTuple(args, "l", &n))
86        return NULL;
87
88    v = PyBytes_FromStringAndSize((char *)NULL, n);
89    if (v == NULL)
90        return NULL;
91
92    err = FSReadFork(self->fRefNum, fsAtMark, 0, n, PyString_AsString(v), &n2);
93    if (err && err != eofErr) {
94        PyMac_Error(err);
95        Py_DECREF(v);
96        return NULL;
97    }
98    _PyString_Resize(&v, n2);
99    return v;
100}
101
102
103static char rf_write__doc__[] =
104"Write to resource fork"
105;
106
107static PyObject *
108rf_write(rfobject *self, PyObject *args)
109{
110    char *buffer;
111    long size;
112    OSErr err;
113
114    if (self->isclosed) {
115        PyErr_SetString(PyExc_ValueError, "Operation on closed file");
116        return NULL;
117    }
118    if (!PyArg_ParseTuple(args, "s#", &buffer, &size))
119        return NULL;
120    err = FSWriteFork(self->fRefNum, fsAtMark, 0, size, buffer, NULL);
121    if (err) {
122        PyMac_Error(err);
123        return NULL;
124    }
125    Py_INCREF(Py_None);
126    return Py_None;
127}
128
129
130static char rf_seek__doc__[] =
131"Set file position"
132;
133
134static PyObject *
135rf_seek(rfobject *self, PyObject *args)
136{
137    long amount;
138    int whence = SEEK_SET;
139    int mode;
140    OSErr err;
141
142    if (self->isclosed) {
143        PyErr_SetString(PyExc_ValueError, "Operation on closed file");
144        return NULL;
145    }
146    if (!PyArg_ParseTuple(args, "l|i", &amount, &whence)) {
147        return NULL;
148    }
149
150    switch (whence) {
151    case SEEK_CUR:
152        mode = fsFromMark;
153        break;
154    case SEEK_END:
155        mode = fsFromLEOF;
156        break;
157    case SEEK_SET:
158        mode = fsFromStart;
159        break;
160    default:
161        PyErr_BadArgument();
162        return NULL;
163    }
164
165    err = FSSetForkPosition(self->fRefNum, mode, amount);
166    if (err != noErr) {
167        PyMac_Error(err);
168        return NULL;
169    }
170    Py_INCREF(Py_None);
171    return Py_None;
172}
173
174
175static char rf_tell__doc__[] =
176"Get file position"
177;
178
179static PyObject *
180rf_tell(rfobject *self, PyObject *args)
181{
182    long long where;
183    OSErr err;
184
185    if (self->isclosed) {
186        PyErr_SetString(PyExc_ValueError, "Operation on closed file");
187        return NULL;
188    }
189    if (!PyArg_ParseTuple(args, ""))
190        return NULL;
191
192    err = FSGetForkPosition(self->fRefNum, &where);
193    if (err != noErr) {
194        PyMac_Error(err);
195        return NULL;
196    }
197    return PyLong_FromLongLong(where);
198}
199
200static char rf_close__doc__[] =
201"Close resource fork"
202;
203
204static PyObject *
205rf_close(rfobject *self, PyObject *args)
206{
207    if (!PyArg_ParseTuple(args, ""))
208        return NULL;
209    do_close(self);
210    Py_INCREF(Py_None);
211    return Py_None;
212}
213
214
215static struct PyMethodDef rf_methods[] = {
216 {"read",       (PyCFunction)rf_read,   1,      rf_read__doc__},
217 {"write",      (PyCFunction)rf_write,  1,      rf_write__doc__},
218 {"seek",       (PyCFunction)rf_seek,   1,      rf_seek__doc__},
219 {"tell",       (PyCFunction)rf_tell,   1,      rf_tell__doc__},
220 {"close",      (PyCFunction)rf_close,  1,      rf_close__doc__},
221
222    {NULL,              NULL}           /* sentinel */
223};
224
225/* ---------- */
226
227
228static rfobject *
229newrfobject(void)
230{
231    rfobject *self;
232
233    self = PyObject_NEW(rfobject, &Rftype);
234    if (self == NULL)
235        return NULL;
236    self->isclosed = 1;
237    return self;
238}
239
240
241static void
242rf_dealloc(rfobject *self)
243{
244    do_close(self);
245    PyObject_DEL(self);
246}
247
248static PyObject *
249rf_getattr(rfobject *self, char *name)
250{
251    return Py_FindMethod(rf_methods, (PyObject *)self, name);
252}
253
254static char Rftype__doc__[] =
255"Resource fork file object"
256;
257
258static PyTypeObject Rftype = {
259    PyObject_HEAD_INIT(&PyType_Type)
260    0,                                  /*ob_size*/
261    "MacOS.ResourceFork",               /*tp_name*/
262    sizeof(rfobject),                   /*tp_basicsize*/
263    0,                                  /*tp_itemsize*/
264    /* methods */
265    (destructor)rf_dealloc,     /*tp_dealloc*/
266    (printfunc)0,               /*tp_print*/
267    (getattrfunc)rf_getattr,            /*tp_getattr*/
268    (setattrfunc)0,     /*tp_setattr*/
269    (cmpfunc)0,                 /*tp_compare*/
270    (reprfunc)0,                /*tp_repr*/
271    0,                          /*tp_as_number*/
272    0,                  /*tp_as_sequence*/
273    0,                  /*tp_as_mapping*/
274    (hashfunc)0,                /*tp_hash*/
275    (ternaryfunc)0,             /*tp_call*/
276    (reprfunc)0,                /*tp_str*/
277
278    /* Space for future expansion */
279    0L,0L,0L,0L,
280    Rftype__doc__ /* Documentation string */
281};
282
283
284/* End of code for Resource fork objects */
285/* -------------------------------------------------------- */
286
287/*----------------------------------------------------------------------*/
288/* Miscellaneous File System Operations */
289
290static char getcrtp_doc[] = "Get MacOS 4-char creator and type for a file";
291
292static PyObject *
293MacOS_GetCreatorAndType(PyObject *self, PyObject *args)
294{
295    PyObject *creator, *type, *res;
296    OSErr err;
297    FSRef ref;
298    FSCatalogInfo       cataloginfo;
299    FileInfo* finfo;
300
301    if (!PyArg_ParseTuple(args, "O&", PyMac_GetFSRef, &ref)) {
302#if APPLE_SUPPORTS_QUICKTIME
303        /* This function is documented to take an FSSpec as well,
304         * which only works in 32-bit mode.
305         */
306        PyErr_Clear();
307        FSSpec fss;
308        FInfo info;
309
310        if (!PyArg_ParseTuple(args, "O&", PyMac_GetFSSpec, &fss))
311            return NULL;
312
313        if ((err = FSpGetFInfo(&fss, &info)) != noErr) {
314            return PyErr_Mac(MacOS_Error, err);
315        }
316
317        info.fdCreator = ntohl(info.fdCreator);
318        info.fdType = ntohl(info.fdType);
319
320        creator = PyString_FromStringAndSize(
321                        (char *)&info.fdCreator, 4);
322        type = PyString_FromStringAndSize((char *)&info.fdType, 4);
323        res = Py_BuildValue("OO", creator, type);
324        Py_DECREF(creator);
325        Py_DECREF(type);
326        return res;
327#else   /* APPLE_SUPPORTS_QUICKTIME */
328        return NULL;
329#endif  /* APPLE_SUPPORTS_QUICKTIME */
330    }
331
332    err = FSGetCatalogInfo(&ref,
333                    kFSCatInfoFinderInfo|kFSCatInfoNodeFlags, &cataloginfo,
334                    NULL, NULL, NULL);
335    if (err != noErr) {
336        PyErr_Mac(MacOS_Error, err);
337        return NULL;
338    }
339
340    if ((cataloginfo.nodeFlags & kFSNodeIsDirectoryMask) != 0) {
341        /* Directory: doesn't have type/creator info.
342         *
343         * The specific error code is for backward compatibility with
344         * earlier versions.
345         */
346        PyErr_Mac(MacOS_Error, fnfErr);
347        return NULL;
348
349    }
350    finfo = (FileInfo*)&(cataloginfo.finderInfo);
351    finfo->fileCreator = ntohl(finfo->fileCreator);
352    finfo->fileType = ntohl(finfo->fileType);
353    creator = PyString_FromStringAndSize((char*)&(finfo->fileCreator), 4);
354    type = PyString_FromStringAndSize((char*)&(finfo->fileType), 4);
355
356    res = Py_BuildValue("OO", creator, type);
357    Py_DECREF(creator);
358    Py_DECREF(type);
359    return res;
360}
361
362static char setcrtp_doc[] = "Set MacOS 4-char creator and type for a file";
363
364static PyObject *
365MacOS_SetCreatorAndType(PyObject *self, PyObject *args)
366{
367    ResType creator, type;
368    FSRef ref;
369    FileInfo* finfo;
370    OSErr err;
371    FSCatalogInfo       cataloginfo;
372
373    if (!PyArg_ParseTuple(args, "O&O&O&",
374                    PyMac_GetFSRef, &ref, PyMac_GetOSType, &creator, PyMac_GetOSType, &type)) {
375#if APPLE_SUPPORTS_QUICKTIME
376        /* Try to handle FSSpec arguments, for backward compatibility */
377        FSSpec fss;
378        FInfo info;
379
380        if (!PyArg_ParseTuple(args, "O&O&O&",
381            PyMac_GetFSSpec, &fss, PyMac_GetOSType, &creator, PyMac_GetOSType, &type))
382            return NULL;
383
384        if ((err = FSpGetFInfo(&fss, &info)) != noErr)
385            return PyErr_Mac(MacOS_Error, err);
386
387        info.fdCreator = creator;
388        info.fdType = type;
389
390        if ((err = FSpSetFInfo(&fss, &info)) != noErr)
391            return PyErr_Mac(MacOS_Error, err);
392        Py_INCREF(Py_None);
393        return Py_None;
394#else /* APPLE_SUPPORTS_QUICKTIME */
395        return NULL;
396#endif /* APPLE_SUPPORTS_QUICKTIME */
397    }
398
399    err = FSGetCatalogInfo(&ref,
400                    kFSCatInfoFinderInfo|kFSCatInfoNodeFlags, &cataloginfo,
401                    NULL, NULL, NULL);
402    if (err != noErr) {
403        PyErr_Mac(MacOS_Error, err);
404        return NULL;
405    }
406
407    if ((cataloginfo.nodeFlags & kFSNodeIsDirectoryMask) != 0) {
408        /* Directory: doesn't have type/creator info.
409         *
410         * The specific error code is for backward compatibility with
411         * earlier versions.
412         */
413        PyErr_Mac(MacOS_Error, fnfErr);
414        return NULL;
415
416    }
417    finfo = (FileInfo*)&(cataloginfo.finderInfo);
418    finfo->fileCreator = creator;
419    finfo->fileType = type;
420
421    err = FSSetCatalogInfo(&ref, kFSCatInfoFinderInfo, &cataloginfo);
422    if (err != noErr) {
423        PyErr_Mac(MacOS_Error, fnfErr);
424        return NULL;
425    }
426
427    Py_INCREF(Py_None);
428    return Py_None;
429}
430
431
432static char geterr_doc[] = "Convert OSErr number to string";
433
434static PyObject *
435MacOS_GetErrorString(PyObject *self, PyObject *args)
436{
437    int err;
438    char buf[256];
439    Handle h;
440    char *str;
441    static int errors_loaded;
442
443    if (!PyArg_ParseTuple(args, "i", &err))
444        return NULL;
445
446    h = GetResource('Estr', err);
447    if (!h && !errors_loaded) {
448        /*
449        ** Attempt to open the resource file containing the
450        ** Estr resources. We ignore all errors. We also try
451        ** this only once.
452        */
453        PyObject *m, *rv;
454        errors_loaded = 1;
455
456        m = PyImport_ImportModuleNoBlock("macresource");
457        if (!m) {
458            if (Py_VerboseFlag)
459                PyErr_Print();
460            PyErr_Clear();
461        }
462        else {
463            rv = PyObject_CallMethod(m, "open_error_resource", "");
464            if (!rv) {
465                if (Py_VerboseFlag)
466                    PyErr_Print();
467                PyErr_Clear();
468            }
469            else {
470                Py_DECREF(rv);
471                /* And try again... */
472                h = GetResource('Estr', err);
473            }
474            Py_DECREF(m);
475        }
476    }
477    /*
478    ** Whether the code above succeeded or not, we won't try
479    ** again.
480    */
481    errors_loaded = 1;
482
483    if (h) {
484        HLock(h);
485        str = (char *)*h;
486        memcpy(buf, str+1, (unsigned char)str[0]);
487        buf[(unsigned char)str[0]] = '\0';
488        HUnlock(h);
489        ReleaseResource(h);
490    }
491    else {
492        PyOS_snprintf(buf, sizeof(buf), "Mac OS error code %d", err);
493    }
494
495    return Py_BuildValue("s", buf);
496}
497
498
499#ifndef __LP64__
500
501static char splash_doc[] = "Open a splash-screen dialog by resource-id (0=close)";
502
503static PyObject *
504MacOS_splash(PyObject *self, PyObject *args)
505{
506    int resid = -1;
507    static DialogPtr curdialog = NULL;
508    DialogPtr olddialog;
509    WindowRef theWindow;
510    CGrafPtr thePort;
511#if 0
512    short xpos, ypos, width, height, swidth, sheight;
513#endif
514
515    if (!PyArg_ParseTuple(args, "|i", &resid))
516        return NULL;
517    olddialog = curdialog;
518    curdialog = NULL;
519
520    if ( resid != -1 ) {
521        curdialog = GetNewDialog(resid, NULL, (WindowPtr)-1);
522        if ( curdialog ) {
523            theWindow = GetDialogWindow(curdialog);
524            thePort = GetWindowPort(theWindow);
525#if 0
526            width = thePort->portRect.right - thePort->portRect.left;
527            height = thePort->portRect.bottom - thePort->portRect.top;
528            swidth = qd.screenBits.bounds.right - qd.screenBits.bounds.left;
529            sheight = qd.screenBits.bounds.bottom - qd.screenBits.bounds.top - LMGetMBarHeight();
530            xpos = (swidth-width)/2;
531            ypos = (sheight-height)/5 + LMGetMBarHeight();
532            MoveWindow(theWindow, xpos, ypos, 0);
533            ShowWindow(theWindow);
534#endif
535            DrawDialog(curdialog);
536        }
537    }
538    if (olddialog)
539        DisposeDialog(olddialog);
540    Py_INCREF(Py_None);
541    return Py_None;
542}
543
544static char DebugStr_doc[] = "Switch to low-level debugger with a message";
545
546static PyObject *
547MacOS_DebugStr(PyObject *self, PyObject *args)
548{
549    Str255 message;
550    PyObject *object = 0;
551
552    if (!PyArg_ParseTuple(args, "O&|O", PyMac_GetStr255, message, &object))
553        return NULL;
554
555    DebugStr(message);
556    Py_INCREF(Py_None);
557    return Py_None;
558}
559
560
561static char SysBeep_doc[] = "BEEEEEP!!!";
562
563static PyObject *
564MacOS_SysBeep(PyObject *self, PyObject *args)
565{
566    int duration = 6;
567
568    if (!PyArg_ParseTuple(args, "|i", &duration))
569        return NULL;
570    SysBeep(duration);
571    Py_INCREF(Py_None);
572    return Py_None;
573}
574
575#endif /* __LP64__ */
576
577static char WMAvailable_doc[] =
578    "True if this process can interact with the display."
579    "Will foreground the application on the first call as a side-effect."
580    ;
581
582static PyObject *
583MacOS_WMAvailable(PyObject *self, PyObject *args)
584{
585    static PyObject *rv = NULL;
586
587    if (!PyArg_ParseTuple(args, ""))
588        return NULL;
589    if (!rv) {
590        ProcessSerialNumber psn;
591
592        /*
593        ** This is a fairly innocuous call to make if we don't have a window
594        ** manager, or if we have no permission to talk to it. It will print
595        ** a message on stderr, but at least it won't abort the process.
596        ** It appears the function caches the result itself, and it's cheap, so
597        ** no need for us to cache.
598        */
599#ifdef kCGNullDirectDisplay
600        /* On 10.1 CGMainDisplayID() isn't available, and
601        ** kCGNullDirectDisplay isn't defined.
602        */
603        if (CGMainDisplayID() == 0) {
604            rv = Py_False;
605        } else {
606#else
607        {
608#endif
609            if (GetCurrentProcess(&psn) < 0 ||
610                SetFrontProcess(&psn) < 0) {
611                rv = Py_False;
612            } else {
613                rv = Py_True;
614            }
615        }
616    }
617    Py_INCREF(rv);
618    return rv;
619}
620
621static char GetTicks_doc[] = "Return number of ticks since bootup";
622
623static PyObject *
624MacOS_GetTicks(PyObject *self, PyObject *args)
625{
626    return Py_BuildValue("i", (int)TickCount());
627}
628
629static char openrf_doc[] = "Open resource fork of a file";
630
631static PyObject *
632MacOS_openrf(PyObject *self, PyObject *args)
633{
634    OSErr err;
635    char *mode = "r";
636    FSRef ref;
637    SInt8 permission = fsRdPerm;
638    rfobject *fp;
639    HFSUniStr255 name;
640
641    if (!PyArg_ParseTuple(args, "O&|s", PyMac_GetFSRef, &ref, &mode))
642        return NULL;
643    while (*mode) {
644        switch (*mode++) {
645        case '*': break;
646        case 'r': permission = fsRdPerm; break;
647        case 'w': permission = fsWrPerm; break;
648        case 'b': break;
649        default:
650            PyErr_BadArgument();
651            return NULL;
652        }
653    }
654
655    err = FSGetResourceForkName(&name);
656    if (err != noErr) {
657        PyMac_Error(err);
658        return NULL;
659    }
660
661    if ( (fp = newrfobject()) == NULL )
662        return NULL;
663
664
665    err = FSOpenFork(&ref, name.length, name.unicode, permission, &fp->fRefNum);
666    if (err != noErr) {
667        Py_DECREF(fp);
668        PyMac_Error(err);
669        return NULL;
670    }
671    fp->isclosed = 0;
672    return (PyObject *)fp;
673}
674
675
676
677static PyMethodDef MacOS_Methods[] = {
678    {"GetCreatorAndType",               MacOS_GetCreatorAndType, 1,     getcrtp_doc},
679    {"SetCreatorAndType",               MacOS_SetCreatorAndType, 1,     setcrtp_doc},
680    {"GetErrorString",                  MacOS_GetErrorString,   1,      geterr_doc},
681    {"openrf",                          MacOS_openrf,           1,      openrf_doc},
682#ifndef __LP64__
683    {"splash",                          MacOS_splash,           1,      splash_doc},
684    {"DebugStr",                        MacOS_DebugStr,         1,      DebugStr_doc},
685    {"SysBeep",                         MacOS_SysBeep,          1,      SysBeep_doc},
686#endif /* __LP64__ */
687    {"GetTicks",                        MacOS_GetTicks,         1,      GetTicks_doc},
688    {"WMAvailable",                     MacOS_WMAvailable,              1,      WMAvailable_doc},
689    {NULL,                              NULL}            /* Sentinel */
690};
691
692
693void
694initMacOS(void)
695{
696    PyObject *m, *d;
697
698    if (PyErr_WarnPy3k("In 3.x, the MacOS module is removed.", 1))
699        return;
700
701    m = Py_InitModule("MacOS", MacOS_Methods);
702    d = PyModule_GetDict(m);
703
704    /* Initialize MacOS.Error exception */
705    MacOS_Error = PyMac_GetOSErrException();
706    if (MacOS_Error == NULL || PyDict_SetItemString(d, "Error", MacOS_Error) != 0)
707        return;
708    Rftype.ob_type = &PyType_Type;
709    Py_INCREF(&Rftype);
710    if (PyDict_SetItemString(d, "ResourceForkType", (PyObject *)&Rftype) != 0)
711        return;
712    /*
713    ** This is a hack: the following constant added to the id() of a string
714    ** object gives you the address of the data. Unfortunately, it is needed for
715    ** some of the image and sound processing interfaces on the mac:-(
716    */
717    {
718        PyStringObject *p = 0;
719        long off = (long)&(p->ob_sval[0]);
720
721        if( PyDict_SetItemString(d, "string_id_to_buffer", Py_BuildValue("i", off)) != 0)
722            return;
723    }
724#define PY_RUNTIMEMODEL "macho"
725    if (PyDict_SetItemString(d, "runtimemodel",
726                            Py_BuildValue("s", PY_RUNTIMEMODEL)) != 0)
727        return;
728#if defined(WITH_NEXT_FRAMEWORK)
729#define PY_LINKMODEL "framework"
730#elif defined(Py_ENABLE_SHARED)
731#define PY_LINKMODEL "shared"
732#else
733#define PY_LINKMODEL "static"
734#endif
735    if (PyDict_SetItemString(d, "linkmodel",
736                            Py_BuildValue("s", PY_LINKMODEL)) != 0)
737        return;
738
739}
740