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