home *** CD-ROM | disk | FTP | other *** search
/ ETO Development Tools 4 / ETO Development Tools 4.iso / Essentials / MPW 411 / TechNotesHelp < prev   
Encoding:
Text File  |  1991-04-08  |  1.6 MB  |  37,008 lines  |  [TEXT/MPS ]

Text Truncated. Only the first 1MB is shown below. Download the file for the complete contents.
  1. æKY CopyrightNotice
  2. æC  Copyright Apple Computer, Inc. 1985-1991, All rights reserved.
  3. 411 - TechNotes Help - MPW 3.2 Final Release.
  4. Monday, May 14, 1990 9:00:00 AM
  5.  
  6. æKY Help
  7. TechNotesHelp
  8. æC  
  9. 276: Gimmie Depth Or Gimmie Death
  10. 275: 32-Bit QuickDraw:  Version 1.2 Features
  11. 274: The Compleat Guide to TeachText
  12. 273: SCSI Termination
  13. 272: What Your Sony Drives For You
  14. 271: Macintosh IIfx:  The Inside Story
  15. 270: AppleTalk Timers Explained
  16. 269: 'ckid' Resource Format
  17. 268: MacinTalk—The Final Chapter
  18. 267: TextEdit Technicalities
  19. 266: Absolute Pointing Device Memory Structure
  20. 265: Pascal to C:  PROCEDURE Parameters
  21. 264: Script Manager 2.0 Date & Time Problems
  22. 263: International Canceling
  23. 262: Controlling Status Calls
  24. 261: Cache As Cache Can
  25. 260: NuBus Power Allocation
  26. 259: Old Style Colors
  27. 258: Our Checksum Bounced
  28. 257: Slot Interrupt Prio-Technics
  29. 256: Globals in Stand-Alone Code?
  30. 255: Macintosh Portable ROM Expansion
  31. 254: Macintosh Portable PDS Development
  32. 253: 'SICN' Tired of Large Icons in Menus?
  33. 252: Plotting Small Icons
  34. 251: Safe cdevs
  35. 250: AppleTalk Phase 2 on the Macintosh                         
  36. 249: Opening the Serial Driver
  37. 248: DAs & Drivers in Need of (a Good) Time
  38. 247: Giving the (Desk)Hook to INITs
  39. 246: Mixing HFS and C File I/O
  40. 245: Font Family Numbers
  41. 244: A Leading Cause of Color Cursor Cursing
  42. 243: Script Manager Variables
  43. 242: Fonts and the Script Manager
  44. 241: Script Manager’s _Pixel2Char
  45. 240: Using MPW for Non-Macintosh 68000 Systems
  46. 239: Inside Object Pascal
  47. 238: Getting a Full Pathname
  48. 237: TextEdit Record Size Limitations Revisited
  49. 236: Speedy the Math Coprocessor
  50. 235: Cooperating with the Coprocessor
  51. 234: NuBus Physical Designs—Beware
  52. 233: MultiFinder and _SetGrowZone232: Strip With _OpenResFile and _OpenRFPerm
  53. 231: Macintosh Allegro Common LISP Features
  54. 230: Pertinent Information About the Macintosh SE/30
  55. 229: A/UX 1.1 Toolbox Bugs
  56. 228: Use Care When Swapping MMU Mode
  57. 227: Toolbox Karma
  58. 226: Moving Your Cat
  59. 225: Using RegisterName
  60. 224: Opening AppleTalk
  61. 223: Assembly Language Use of _InitGraf with MPW
  62. 222: Custom Menu Flashing Bug
  63. 221: NuBus Interrupt Latency (I was a teenage DMA junkie)
  64. 220: Segment Loader Limitations
  65. 219: New Memory Manager Glue Routines
  66. 218: New High-Level File Manager Calls
  67. 217: Where Have My Font Icons Gone?
  68. 216: AppleShare 1.1 and 2.0 Limits
  69. 215: “New” cdev Messages
  70. 214: New Resource Manager Calls
  71. 213: _StripAddress:  The Untold Story
  72. 212: The Joy of Being 32-Bit Clean
  73. 211: Palette Manager Changes in System 6.0.2
  74. 210: The Desktop file’s Outer Limits
  75. 209: High Sierra & ISO 9660 CD ROM Formats
  76. 208: Setting and Restoring A5
  77. 207: Styled TextEdit Changes in System 6.0 
  78. 206: Space aliens ate my mouse!!  (ADB—The untold story)
  79. 205: MultiFinder Revisited:  The 6.0 System Release
  80. 204: HFS Tidbits
  81. 203: Don’t Abuse the Managers
  82. 202: Resetting the Event Mask
  83. 201: ReadPacket Clarification
  84. 200: MPW 2.0.2 Bugs
  85. 199: KillNBP Clarification
  86. 198: Font/DA Mover, Styled Fonts, and 'NFNT's
  87. 197: Chooser Enhancements
  88. 196: CDEF Parameters
  89. 195: ASP and AFP Description Discrepancies
  90. 194: WMgrPortability
  91. 193: So many BitMaps, so little time
  92. 192: Surprises in LaserWriter 5.0 and newer
  93. 191: Font Names
  94. 190: Working Directories and MultiFinder
  95. 189: Version Territory
  96. 188: ChangedResource: Too much of a good thing.
  97. 187: Don’t Look at ioPosOffset
  98. 186: PBLock/UnlockRange 
  99. 185: OpenRFPerm: What your mother never told you.
  100. 184: Notification Manager
  101. 183: Position-Independent PostScript
  102. 182: How to Construct Word-Break Tables
  103. 181: Every Picture [Comment] Tells Its Story, Don’t It?
  104. 180: MultiFinder Miscellanea
  105. 179: Setting ioNamePtr in File Manager Calls
  106. 178: Modifying the Standard String Comparison
  107. 177: Problem with WaitNextEvent in MultiFinder 1.0
  108. 176: Macintosh Memory Configurations
  109. 175: SetLineWidth Revealed 
  110. 174: Accessing the Script Manager Print Action Routine
  111. 173: PrGeneral Bug
  112. 172: Parameters for MDEF Message #3
  113. 170: HyperCard File Format
  114. 171: Things You Wanted to Know About _PackBits*
  115. 169: HyperCard 1.0.1 and 1.1 Anomalies
  116. 168: HyperCard ‘snd ’ Resources
  117. 167: AppleShare Foreground Applications
  118. 166: MPW C Functions Using Strings or Points as Arguments
  119. 165: Creating Files Inside an AppleShare Drop Folder
  120. 164: MPW C Functions: To declare or not to declare, that is the question.
  121. 163: Adding Color With CopyBits
  122. 162: MPW 2.0 Pascal Compiler Bug
  123. 161: When to Call PrOpen and PrClose
  124. 160: Key Mapping
  125. 159: Hard Disk Hacking
  126. 158: Frequently Asked MultiFinder Questions
  127. 157: Problem with GetVInfo
  128. 156: Checking for Specific Functionality
  129. 155: Handles and Pointers—Identity Crisis
  130. 154: Displaying Large PICT Files 
  131. 153: Changes in International Utilities and Resources
  132. 152: Using Laser Prep Routines
  133. 151: System Error 33, “zcbFree has gone negative”
  134. 150: Macintosh SE Disk Driver Bug
  135. 149: Document Names and the Printing Manager
  136. 148: Suppliers for Macintosh II Board Developers
  137. 147: Finder Notes: “Get Info” Default & Icon Masks
  138. 146: Notes on MPW Pascal’s -mc68881 Option
  139. 145: Debugger FKEY
  140. 144: Macintosh II Color Monitor Connections
  141. 143: Don’t Call ADBReInit on the SE with System 4.1
  142. 142: Avoid Use of Network Events
  143. 141: Maximum Number of Resources in a File
  144. 140: Why PBHSetVol is Dangerous
  145. 139: Macintosh Plus ROM Versions
  146. 138: Using KanjiTalk with a non-Japanese Macintosh Plus
  147. 137: AppleShare 1.1 Server FPMove Bug
  148. 136: Register A5 Within GrowZone Functions
  149. 135: Getting through CUSToms
  150. 134: Hard Disk Medic & Booting Camp
  151. 133: Am I Talking To A Spooler?
  152. 132: AppleTalk Interface Update
  153. 131: TextEdit Bugs in System 4.2
  154. 130: Clearing ioCompletion
  155. 129: _SysEnvirons:  System 6.0 and Beyond
  156. 128: PrGeneral
  157. 127: TextEdit EOL Ambiguity
  158. 126: Sublaunching: Playing the Shell Game
  159. 125: The Effect of Spool-a-page/Print-a-page on Shared Printers
  160. 124: Using Low-Level Printing Calls With AppleTalk ImageWriters
  161. 123: Bugs in LaserWriter ROMs
  162. 122: Device-Independent Printing
  163. 121: Using the High-Level AppleTalk Routines
  164. 120: Drawing Into an Off-Screen PixMap
  165. 119: Determining If Color QuickDraw Exists
  166. 118: How to Check and Handle Printing Errors
  167. 117: Compatibility: Why & How 
  168. 116: AppleShare-able Applications and the Resource Manager
  169. 115: Application Configuration with Stationery Pads
  170. 114: AppleShare and Old Finders
  171. 113: Boot Blocks
  172. 112: FindDItem
  173. 111: MoveHHi and SetResPurge
  174. 110: MPW: Writing Standalone Code
  175. 109: Bug in MPW 1.0 Language Libraries
  176. 108: _AddDrive, _DrvrInstall, and _DrvrRemove
  177. 107: Nulls in Filenames
  178. 106: The Real Story: VCBs and Drive Numbers
  179. 105: MPW Object Pascal Without MacApp
  180. 104: MPW: Accessing Globals From Assembly Language
  181. 103: Using MaxApplZone and MoveHHi from Assembly Language
  182. 102: HFS Elucidations
  183. 101: CreateResFile and the Poor Man’s Search Path
  184. 100: Compatibility with Large-Screen Displays
  185.  99: Standard File Bug in System 3.2
  186.  98: Short-Circuit Booleans in Lisa Pascal
  187.  97: PrSetError Problem
  188.  96: SCSI Bugs
  189.  95: How To Add Items to the Print Dialogs
  190.  94: Tags
  191.  93: MPW: {$LOAD}; _DataInit;%_MethTables
  192.  92: The Appearance of Text
  193.  91: Optimizing for the LaserWriter—Picture Comments 
  194.  90: SANE Incompatibilities 
  195.  89: DrawPicture Bug
  196.  88: Signals
  197.  87: Error in FCBPBRec
  198.  86: MacPaint Document Format
  199.  85: GetNextEvent; Blinking Apple Menu
  200.  84: Edit File Format
  201.  83: System Heap Size Warning
  202.  82: TextEdit: Advice & Descent
  203.  81: Caching
  204.  80: Standard File Tips
  205.  79: _ZoomWindow
  206.  78: Resource Manager Tips
  207.  77: HFS Ruminations
  208.  76: The Macintosh Plus Update Installation Script
  209.  75: The Installer and Scripts
  210.  74: Don’t Use the Resource Fork for Data
  211.  73: Color Printing
  212.  72: Optimizing for the LaserWriter — Techniques
  213.  71: Finding Drivers in the Unit Table
  214.  70: Forcing Disks to be Either 400K or 800K
  215.  69: Setting ioFDirIndex in PBGetCatInfo Calls
  216.  68: Searching All Directories on an HFS Volume
  217.  67: Finding the “Blessed Folder”
  218.  66: Determining Which File System is Active
  219.  65: Macintosh Plus Pinouts    
  220.  64: IAZNotify
  221.  63: WriteResource Bug Patch
  222.  62: Don’t Use Resource Header Application Bytes
  223.  61: GetItemStyle Bug
  224.  60: Drawing Characters into a Narrow GrafPort
  225.  59: Pictures and Clip Regions
  226.  58: International Utilities Bug
  227.  57: Macintosh Plus Overview
  228.  56: Break/CTS Device Driver Event Structure
  229.  55: Drawing Icons
  230.  54: Limit to Size of Resources
  231.  53: MoreMasters Revisited
  232.  52: Calling Launch From a High-Level Language
  233.  51: Debugging With PurgeMem and CompactMem
  234.  50: Calling SetResLoad
  235.  48: Bundles
  236.  47: Customizing Standard File
  237.  46: Separate Resource Files
  238.  45: Inside Macintosh Quick Reference
  239.  44: HFS Compatibility
  240.  43: Calling LoadSeg
  241.  42: Pascal Routines Passed by Pointer
  242.  41: Drawing Into an Offscreen Bitmap
  243.  40: Finder Flags
  244.  39: Segment Loader Patch
  245.  38: The ROM Debugger
  246.  37: Differentiating Between Logic Boards
  247.  36: Drive Queue Elements
  248.  35: DrawPicture Problem
  249.  34: User Items in Dialogs
  250.  33: ImageWriter II Paper Motion
  251.  32: Reserved Resource Types
  252.  30: Font Height Tables
  253.  29: Resources Contained in the Desktop File
  254.  28: Finders and Foreign Drives
  255.  27: MacDraw’s ‘PICT’ File Format
  256.  26: Character vs. String Operations in QuickDraw
  257.  25: Don’t Depend on Register A5 Within Trap Patches
  258.  24: Available Volumes
  259.  23: Life With Font/DA Mover—Desk Accessories
  260.  22: TEScroll Bug
  261.  21: QuickDraw’s Internal Picture Definition
  262.  20: Data Servers on AppleTalk
  263.  19: How To Produce Continuous Sound Without Clicking
  264.  18: TextEdit Conversion Utility
  265.  17: Low-Level Print Driver Calls
  266.  16: MacWorks XL
  267.  15: Finder 4.1
  268.  14: The INIT 31 Mechanism
  269.  13: MacWrite Clipboard Format
  270.  12: Disk-Based MacWrite Format
  271.  11: Memory-Based MacWrite File Format
  272.  10: Pinouts
  273.   9: Will Your AppleTalk Application Support Internets?
  274.   8: RecoverHandle Bug in AppleTalk Pascal Interfaces
  275.   7: A Few Quick Debugging Tips
  276.   6: Shortcut for Owned Resources
  277.   5: Using Modeless Dialogs from Desk Accessories
  278.   4: Error Returns from GetNewDialog
  279.   3: Command-Shift-Number Keys
  280.   2: Compatibility Guidelines
  281.   1: Desk Accessories and System Resources
  282.   0: About Macintosh Technical Notes
  283.   
  284. æKY 0
  285. æC #0:  About Macintosh Technical Notes
  286. _______________________________________________________________________________
  287.  
  288. Technical Note #0 (this card) accompanies each release of Technical Notes.
  289. This version of the stack is the third update of Technical Notes in HyperCard,
  290. and it includes all Macintosh Technical Notes (0-261) released as of October
  291. 1989.  This version also introduces links to “SpInside Macintosh,” an on-line
  292. development prototype of Apple’s “Inside Macintosh” in HyperCard.
  293.  
  294. You can access the Notes by number, subject, and index, or you may want to use
  295. HyperCard’s built-in search function to find a topic if the index does not list
  296. it. (To use the search function, click on the icon of a magnifying glass.)
  297.  
  298. You do not need to be familiar with HyperCard to use this stack; if at any time
  299. you are unclear about how something in the stack works, try clicking on the
  300. object in question, and its functionality should become apparent.
  301.  
  302. If there are any subjects which you would like to see treated in a Technical
  303. Note (or if you have any questions or comments about existing Technical Notes
  304. or this Tech Note Stack), please contact us at one of the following addresses:
  305.  
  306.           Macintosh Technical Notes
  307.           Developer Technical Support
  308.           Apple Computer, Inc.
  309.           20525 Mariani Avenue, M/S 75-3T
  310.           Cupertino, CA  95014
  311.           MCI:  MacDTS
  312.           AppleLink:  MacDTS
  313.  
  314. We want Technical Notes to be distributed as widely as possible, so they are
  315. sent to all Partners and Associates at no charge; they are also posted on
  316. AppleLink on the Developer Services bulletin board and other electronic
  317. sources, including the Apple FTP site (IP 130.43.2.2).  You can also order them through
  318. APDA.  As an APDA customer, you have access to the tools and documentation
  319. necessary to develop Apple-compatible products.  For more information about
  320. APDA, contact:
  321.  
  322.           APDA
  323.           Apple Computer, Inc.
  324.           20525 Mariani Avenue, M/S 33-G
  325.           Cupertino, CA 95014-6299
  326.           (800) 282-APDA or (800) 282-2732
  327.           Fax:  (408) 562-3971
  328.           Telex:  171-576
  329.           AppleLink:  APDA
  330.  
  331. We place no restrictions on copying Technical Notes (except that they cannot be
  332. resold), so read, enjoy, and share.  We hope that Macintosh Technical Notes
  333. will provide you with lots of valuable information while you’re developing
  334. Macintosh software.
  335.  
  336. æKY 1
  337. æC #1: Desk Accessories and System Resources
  338.  
  339. See also:     The Resource Manager
  340.  
  341. Written by:     Bryan Stearns     February 25, 1985
  342. Updated:                          March 1, 1988
  343. _______________________________________________________________________________
  344.  
  345. This note formerly described a strategy for dealing with system resources from
  346. desk accessories. We no longer recommend calling ReleaseResource or
  347. DetachResource for a system resource. When you are done with a system resource,
  348. leave it alone; do not try to dispose or release it.
  349.  
  350. æKY 2
  351. æC #2: Compatibility Guidelines
  352.  
  353. Written by:     Cary Clark           January 21, 1986
  354.                 Scott Knaster 
  355. Modified by:    Louella Pizzuti      February 9, 1987
  356. Updated:                             March 1, 1988
  357. _______________________________________________________________________________
  358.  
  359. Apple has many enhancements planned for the Macintosh family of computers. To
  360. help ensure your software’s compatibility with these enhancements, check each
  361. item in this note to be sure that you’re following the recommendations.
  362.  
  363. If your software is written in a high-level language like Pascal or C and if
  364. you adhere to the guidelines listed in Inside Macintosh, many of the questions
  365. in this note won’t concern you. If you develop in assembly language, you should
  366. read each question carefully. If you answer any question “yes,” your software
  367. may encounter difficulty running on future Macintosh computers, and you should
  368. take the recommended action to change your software.
  369.  
  370. Do you depend on 68000 instructions which require that the processor be in
  371. supervisor mode?
  372.  
  373.    In general, your software should not include instructions which depend on 
  374.    supervisor mode. These include modifying the contents of the status register.
  375.  
  376.    Most programs which modify the status register are only changing the 
  377.    Condition Code Register (CCR) half of the status register, so an instruction 
  378.    which addresses the CCR will work fine. Also, your software should not use 
  379.    the User Stack Pointer (USP) or turn interrupts on and off.
  380.  
  381.  
  382. Do you have code which executes in response to an exception and relies on the
  383. position of data in the exception’s local stack frame?
  384.  
  385.    Exception stack frames vary on different microprocessors in the 68000 family,
  386.    some of which may be used in future Macintosh computers. You should avoid 
  387.    using the TRAP instruction. Note: You can determine which microprocessor is 
  388.    installed by examining the low-memory global CPUFlag (a byte at $12F). These
  389.    are the values:
  390.  
  391.       CPUFlag   microprocessor
  392.         $00       68000
  393.         $01       68010
  394.         $02       68020
  395.         $03       68030
  396.  
  397.  
  398. Do you use low-memory globals not documented in Inside Macintosh?
  399.  
  400.    Other microprocessors in the 68000 family use the exception vectors in 
  401.    locations $0 through $FF in different ways. No undocumented location below 
  402.    the system heap ($100 through $13FF) is guaranteed to be available for use 
  403.    in future systems.
  404.  
  405.  
  406. Do you make assumptions about the file system which are not consistent with both the
  407. original Macintosh File System and the Hierarchical File System?
  408.  
  409.    Your applications should be compatible with both file systems. The easiest 
  410.    way to do this is to stick to the old files system trap calls (which work 
  411.    with both file systems) and avoid direct manipulation of data structures 
  412.    such as file control blocks and volume control blocks whenever possible.
  413.  
  414.  
  415. Do you depend on the system or application heaps starting at a hard-coded
  416. address?
  417.  
  418.    The starting addresses and the size of the system and application heaps has 
  419.    already changed (Macintosh vs. Macintosh Plus) and will change again in the 
  420.    future. Use the global ApplZone to find the application heap and SysZone to 
  421.    find the system heap. Also, don’t count on the application heap zone 
  422.    starting at an address less than 65536 (that is, a system heap smaller than 
  423.    64K).
  424.  
  425.  
  426. Do you look through the system’s queues directly?
  427.  
  428.    In general, you should avoid examining queue elements directly. Instead, use 
  429.    the Operating System calls to manipulate queue elements.
  430.  
  431.  
  432. Do you directly address memory-mapped hardware such as the VIA, the SCC, or the IWM?
  433.  
  434.    You should avoid accessing this memory directly and use trap calls instead 
  435.    (disk driver, serial driver, etc.). Future machines may include a memory 
  436.    management unit (MMU) which may prevent access to memory-mapped hardware. 
  437.    Also, these memory-mapped devices may not be present on future machines. The 
  438.    addresses of these devices are likely to change, so if you must access the 
  439.    hardware directly, get the base address of the device from the appropriate 
  440.    low-memory global (obtainable from includes and interface files):
  441.  
  442.       device    global
  443.         VIA      $1D4
  444.         SCCRd    $1D8
  445.         SCCWr    $1DC
  446.         IWM      $1E0
  447.  
  448.  
  449. Do you assume the location or size of the screen?
  450.  
  451.    The location, size, and bit depth of the screen is different in various 
  452.    machines. You can determine its location and size by examining the QuickDraw 
  453.    global variable screenBits on machines without Color QuickDraw. On machines 
  454.    with Color QuickDraw, the device list, described in the Graphics Devices 
  455.    chapter of Inside Macintosh, tells the location and size and bit depth of 
  456.    each screen, screenBits contains the location and size of the main device, 
  457.    and GrayRgn contains a region describing the shape and size of the desktop.
  458.  
  459.  
  460. Does your software fail on some Macintosh models or on A/UX?
  461.  
  462.    If so, you should determine the reason. Failure to run on all versions of 
  463.    the Macintosh may indicate problems which will prevent your software from 
  464.    working on future machines. Failture to run on A/UX, Apple’s Unix for the 
  465.    Macintosh, also may indicate such problems.
  466.  
  467.  
  468. Do you change master pointer flags of relocatable blocks directly with BSET or
  469. BCLR instructions?
  470.  
  471.    In the future and on A/UX, all 32 bits of a master pointer may be used, with 
  472.    the flags byte moved elsewhere. Use the Memory Manager calls HPurge, 
  473.    HNoPurge, HLock, HUnlock, HSetRBit, HClrRBit, HGetState, and HSetState to 
  474.    manipulate the master pointer flags. (See the Memory Manager chapter of 
  475.    Inside Macintosh Volume IV for information on these calls.)
  476.  
  477.  
  478. Do you check for 128K, 512K, and 1M RAM sizes?
  479.  
  480.    You should be flexible enough to allow for non-standard memory sizes. This 
  481.    will allow your software to work in environments like MultiFinder.
  482.  
  483.  
  484. Is your software incompatible with a third-party vendor’s hardware?
  485.  
  486.    If so, the incompatibility may prevent your software from working on future 
  487.    machines. You should research the incompatibility and try to determine a 
  488.    solution.
  489.  
  490.  
  491. Do you rely on system resources being in RAM?
  492.  
  493.    On most of our systems, some system resources are in ROM. You should not 
  494.    assume, for example, that you can regain RAM space by releasing system
  495.    resources.
  496.  
  497.  
  498. Does your software have timing-sensitive code?
  499.  
  500.    Various Macintoshes run at different clock speeds, so timing loops will be 
  501.    invalid. You can use the trap call Delay for timing, or you can examine the 
  502.    global variable Ticks.
  503.  
  504.  
  505. Do you have code which writes to addresses within the code itself?
  506.  
  507.    A memory management unit (MMU) may one day prevent code from writing to 
  508.    addresses within code memory. Also, some microprocessors in the 68000 family 
  509.    cache code as it’s encountered. Your data blocks should be allocated on the 
  510.    stack or in heap blocks separate from the code, and your code should not 
  511.    modify itself.
  512.  
  513.  
  514. Do you rely on keyboard key codes rather than ASCII codes?
  515.  
  516.    The various keyboards are slightly different; future keyboards may be 
  517.    different from them. For textual input, you should read ASCII codes rather 
  518.    than key codes.
  519.  
  520.  
  521. Do you rely on the format of packed addresses in the trap dispatch table?
  522.  
  523.    The trap dispatch table is different on various Macintoshes. There’s no 
  524.    guarantee of the trap table’s format in the future. You should use the 
  525.    system calls GetTrapAddress and SetTrapAddress to manipulate the trap 
  526.    dispatch table.
  527.  
  528.  
  529. Do you use the Resource Manager calls AddReference or RmveReference?
  530.  
  531.    These calls have been removed from the 128K ROM. They are no longer 
  532.    supported.
  533.  
  534.  
  535. Do you store information in the application parameters area (the 32 bytes
  536. between the application and unit globals and the jump table)?
  537.  
  538.    This space is reserved for use by Apple.
  539.  
  540.  
  541. Do you depend on values in registers after a trap call, other than those
  542. documented in Inside Macintosh?
  543.  
  544.    These values aren’t guaranteed. The register conventions documented in 
  545.    Inside Macintosh will, of course, be supported. Often, you may not realize 
  546.    that your code is depending on these undocumented values, so check your 
  547.    register usage carefully.
  548.  
  549.  
  550. Do you use the IMMED bit in File Manager calls?
  551.  
  552.    This bit, which was documented in early versions of Inside Macintosh as a 
  553.    special form of File Manager call, actually did nothing for File Manager 
  554.    calls, and was used only for Device Manager calls. With the advent of the 
  555.    Hierarchical File System, this bit indicates that the call has a parameter 
  556.    block with hierarchical information.
  557.  
  558.  
  559. Do you make assumptions about the number and size of disk drives?
  560.  
  561.    There are now five sizes of Apple disks for the Macintosh (400K, 800K, and 
  562.    20M, 40M, 80M), as well as many more from third-party vendors. You should 
  563.    use Standard File and File Manager calls to determine the number and size of 
  564.    disk drives.
  565.  
  566.  
  567. Do you depend on alternate (page 2) sound or video buffers?
  568.  
  569.    Some Macintoshes do not support alternate sound and video buffers.
  570.  
  571.  
  572. Do you print by sending ASCII directly to the printer driver?
  573.  
  574.    To retain compatibility with both locally-connected and AppleTalk-connected 
  575.    printers, you should print using Printing Managerr, as documented in Inside 
  576.    Macintosh.
  577.  
  578.  
  579. Does your application fail when it’s the startup application (i.e., without the
  580. Finder being run first)?
  581.  
  582.    If so, you’re probably not initializing a variable. If your application does 
  583.    not work as the startup application, you should determine why and fix the 
  584.    problem, since it may cause your application to fail in the future.
  585.  
  586.  
  587. æKY 3
  588. æC #3: Command-Shift-Number Keys
  589.  
  590. See also:     The Toolbox Event Manager
  591.               Technical Note #110 — MPW: Writing Standalone Code
  592.  
  593. Written by:      Harvey Alcabes       March 3,1985
  594. Modified by:     Ginger Jernigan      April 25,1985
  595. Updated:                              March 1, 1988
  596. _______________________________________________________________________________
  597.  
  598. In the standard system, there are two Command-Shift-number key combinations that are
  599. automatically captured and processed by GetNextEvent. The combinations are:
  600.  
  601.      Command-Shift-1          Eject internal disk
  602.      Command-Shift-2          Eject external disk
  603.  
  604. Numbers from 3 to 9 are also captured by GetNextEvent, but are processed by 
  605. calling ‘FKEY’ resources. You can implement your own actions for Command-Shift
  606. -number combinations for numbers 5 to 9 by defining your own ‘FKEY’ resource.
  607. The routine must have no parameters. The ID of the resource must correspond to
  608. the number you want the routine to respond to. For example, if you want to
  609. define an action for Command-Shift-8, you would create an ‘FKEY’ resource with
  610. an ID of 8. The ‘FKEY’ resource should contain the code that you want to
  611. execute when the key is pressed.
  612.  
  613. The following Command-Shift-number key combinations are implemented with ‘FKEY’
  614. resources in the standard System file.
  615.  
  616.      Command-Shift-3     Save current screen as MacPaint file named Screen 0, 
  617.                          Screen 1, … Screen
  618.                          (Works in one-bit mode only on Mac II)
  619.      Command-Shift-4     Print the active window (to an ImageWriter)
  620.                          (with Caps Lock on)Print the entire screen 
  621.                          (to an ImageWriter)
  622.  
  623.  
  624. æKY 4
  625. æC #4: Error Returns from GetNewDialog
  626.  
  627. See also:       The Dialog Manager
  628.  
  629. Written by:     Russ Daniels     April 4, 1985
  630. Updated:                         March 1, 1988
  631. _______________________________________________________________________________
  632.  
  633. When calling GetNewDialog to retrieve a dialog template from a previously opened
  634. resource file, how are error conditions indicated to the caller?
  635.  
  636. Unfortunately, they aren’t. The Dialog Manager calls GetResource and assumes the
  637. returned value is good. Since the Dialog Manager doesn’t check, you have two
  638. choices. Your first choice is to call GetResource for the dialog template, item
  639. list, and any resources needed by items in the item list yourself. But what do
  640. you do when you find the resources aren’t there? Try to display an alert
  641. telling the user your application has been mortally wounded? What if resources
  642. needed for the alert aren’t available?
  643.  
  644. The second, simpler alternative is to assure that the dialog template and other
  645. resources will be available when you build your product. This is really an
  646. adequate solution:
  647. If somebody uses a resource editor to remove your dialog template, you can
  648. hardly be blamed for its not executing properly. 
  649.  
  650. A good debugging technique to catch this sort of problem is to put the value
  651. $50FFC001 at absolute memory location 0 (the first long word of memory). If you
  652. do that, when the Dialog Manager tries to dereference the nil handle returned
  653. by the Resource Manager, you’ll get an address error or bus error with some
  654. register containing $50FFC001. If you list the instructions around the program
  655. counter, you’ll often see something like:
  656.  
  657.         MOVE.L (A2),A1       ; in effect (0),A1
  658.         MOVE.L (A1),A1       ; the error occurs here
  659.  
  660. GetNewWindow and most of the other “GetSomething” calls will return nil if the
  661. “something” is not found.
  662.  
  663.  
  664. æKY 5
  665. æC #5: Using Modeless Dialogs from Desk Accessories
  666.  
  667. See also:     The Toolbox Event Manager
  668.               The Dialog Manager
  669.               The Desk Manager
  670.  
  671. Written by:     Russ Daniels     April 4, 1985
  672. Updated:                         March 1, 1988
  673. _______________________________________________________________________________
  674.  
  675. When a desk accessory creates a window (including a modeless dialog window) it 
  676. must set the windowKind to its refnum—a negative number. When the application
  677. calls GetNextEvent, the Event Manager calls SystemEvent, which checks to see if the event belongs to a
  678. desk accessory. SystemEvent checks the windowKind of the frontmost window, and
  679. uses the (negative) number for the refnum to make a control call, giving the 
  680. desk accessory a shot at the event. Then SystemEvent returns TRUE, and 
  681. GetNextEvent returns FALSE.
  682.  
  683. So, your desk accessory gets an event from SystemEvent. Since your window is a
  684. modeless dialog, you call IsDialogEvent, which mysteriously returns FALSE.
  685. What is going on?
  686.  
  687. Like SystemEvent, IsDialogEvent checks the windowKind of windows in the window
  688. list, looking for dialog windows. It does this by looking for windows with a
  689. windowKind of 2. In this case, it finds none, and does nothing.
  690.  
  691. The solution is to change the windowkind of your window to 2 before calling
  692. IsDialogEvent. This allows the Dialog Manager to recognize and handle the event
  693. properly. Before returning to SystemEvent, be sure to restore the windowKind.
  694. That way, when the application calls the Dialog Manager with the same event 
  695. (the application should pass all events to Dialog Manager if it has any modeless
  696. dialogs itself), the Dialog Manager will ignore it.
  697.  
  698. æKY 6
  699. æC #6: Shortcut for Owned Resources
  700.  
  701. See also:     The Resource Manager
  702.               Technical Note #23 — Life With Font/DA Mover—Desk Accessories
  703.  
  704. Written by:     Bryan Stearns     May 10, 1986
  705. Updated:                          March 1, 1988
  706. _______________________________________________________________________________
  707.  
  708. To allow the Font/DA Mover to renumber desk accessories as needed when moving
  709. them between system files, desk accessories should use the “owned resource” 
  710. protocol described in the Resource Manager chapter of Inside Macintosh Volume I.
  711.  
  712. All resource IDs in a desk accessory should be zero-based. At runtime, a routine
  713. can be called to find the current “base” value to add to a resource’s zero-based
  714. value to get the actual current ID of that resource. Then, when a resource is
  715. needed, its zero-based value can be added to the resource base value, giving the
  716. actual resource ID to be used in future Resource Manager calls.
  717.  
  718. Here’s the source to a handy routine to get the resource base value, GetResBase:
  719.  
  720.      ;FUNCTION GetResBase(driverNumber: INTEGER): INTEGER;
  721.      ;
  722.      ;GetResBase takes the driver number and returns the ID
  723.      ;of the first resource owned by that driver. This is
  724.      ;according to the private resource numbering convention
  725.      ;documented in the Resource Manager.
  726.  
  727.      GetResBase     FUNC
  728.  
  729.                     MOVE.L     (SP)+,A0     ; Get return address
  730.                     MOVE.W     (SP)+,D0     ; Get driver number
  731.                     NOT.W      D0           ; Change to unit number
  732.                     ASL.W      #5,D0        ; Move it over in the word
  733.                     ORI.W      #$C000,D0    ; Add the magic bits
  734.                     MOVE.W     D0,(SP)      ; Return function result
  735.                     JMP        (A0)         ; and return
  736.  
  737.                     END
  738. æKY 7
  739. æC #7: A Few Quick Debugging Tips
  740.  
  741. Written by:     Jim Friedlander     April 16, 1986
  742. Updated:                            March 1, 1988
  743. _______________________________________________________________________________
  744.  
  745. This presents a few tips which may make your debugging easier.
  746. _______________________________________________________________________________
  747.  
  748. Setting memory location 0 to something odd
  749.  
  750. Dereferencing nil handles can cause real problems for an application. If location 0
  751. (nil) is something even, the dereference will not cause an address error, and the
  752. application can run on for quite a while, making tracing back to the problem quite
  753. difficult. If location 0 contains something odd, such as $50FFC001, an address error
  754. will be generated immediately when a nil handle is dereferenced. On Macintoshes with
  755. 68020s, like the Mac II, this same value 
  756. ($50FFC001) will cause a bus error. An address error or bus error will also be generated,
  757. of course, when the ROM tries to dereference a nil handle, such as when you call
  758. HNoPurge(hndl), where hndl is nil.
  759.  
  760. Some versions of the TMON debugger set location 0 to 'NIL!' ($4E494C21) or $50FFC001.
  761. If you are using MacsBug, you should include code in your program that sets location
  762. 0. Of course, there is no need to ship your application with this code in it—it’s
  763. just for debugging purposes. Old versions of the Finder used to set location 0 to the
  764. value $464F424A (‘FOBJ’). On newer machines, newly launched applications get location
  765. 0 set to $00F80000 by the Segment Loader.
  766.  
  767.  
  768. Checksumming for slow motion mode
  769.  
  770. Entering the Macsbug command “SS 400000 400000” will cause Macsbug to do a checksum
  771. of the location $400000 every time an instruction is executed. Checksum the ROM,
  772. because it will not change while your program is executing (the ROM may change in
  773. between launches of your application, but that’s OK)! This will cause the Macintosh
  774. to go into slow motion mode. For example, you will need to hold down the mouse button
  775. for about 10 seconds to get a menu to pull down—you can see how the ROM draws menus,
  776. grays text, etc.
  777.  
  778. This technique is very handy for catching problems like multiple updates of your
  779. windows, redrawing scroll bars more than once, that troublesome flashing grow icon,
  780. etc. To turn slow motion mode off, simply enter MacsBug and type 
  781. “SS”.
  782.  
  783. TMON performs this function in a different way. Instead of calculating the checksum
  784. after each instruction, it only calculates checksums after each trap. You can checksum
  785. different amounts of the ROM depending on how much you want things to slow down.
  786.  
  787.  
  788. Checksumming MemErr
  789.  
  790. A lot of programs don’t call MemError as often as they should. If you are having
  791. strange, memory-related problems, one thing that you can do to help find them is to
  792. checksum on MemErr (the low memory global word at $220). In MacsBug, type “SS 220
  793. 221”. In TMON, enter 220 and 221 as limits on the ‘Checksum (bgn end):’ line and on
  794. the line above, enter the range of traps you wish to have the checksum calculated
  795. after.
  796. When MemErr changes, the debugger will appear, and you can check your code to make
  797. sure that you are checking MemErr. If not, you might have found a problem that could
  798. cause your program to crash!
  799.  
  800.  
  801. Checksumming on a master pointer
  802.  
  803. Due to fear of moving memory, some programmers lock every handle that they create. Of
  804. course, handles need only be locked if they are going to be dereferenced AND if a
  805. call will be made that can cause relocation. Unnecessarily locking handles can cause
  806. unwanted heap fragmentation. If you suspect that a particular memory block is moving
  807. on you when you have its handle dereferenced, you can checksum the master pointer
  808. (the handle you got back from NewHandle is the address of the master pointer). Your
  809. program will drop into the debugger each time your handle changes—that is, either
  810. when the block it refers to is relocated, or when the master pointer’s flags byte
  811. changes.
  812.  
  813.  
  814. æKY 8
  815. æC #8: RecoverHandle Bug in AppleTalk Pascal Interfaces
  816.  
  817. See also:       AppleTalk Manager
  818.  
  819. Written by:     Bryan Stearns     April 21, 1986
  820. Updated:                          March 1, 1988
  821. _______________________________________________________________________________
  822.  
  823. Previous versions of this note described a bug in the AppleTalk Pascal Interfaces.
  824. This bug was fixed in MPW 1.0 and newer.
  825.  
  826. æKY 9
  827. æC #9: Will Your AppleTalk Application Support Internets?
  828.  
  829. Written by:    Sriram Subramanian & Pete Helme                       April 1990
  830. Written by:    Bryan Stearns                                         April 1986
  831.  
  832. This Technical Note discusses how AppleTalk applications should work across internets,
  833. groups of interconnected AppleTalk networks.  It explains the differences between life
  834. on a single AppleTalk network and life on an internet. Changes since March 1988:  Removed
  835. the section on AppleTalk retry timers, as it is no longer accurate; see Technical Note #270,
  836. AppleTalk Timers Explained, for more information on retry timers.
  837. _______________________________________________________________________________
  838.  
  839. You can read about internets (AppleTalk networks connect by one or more bridges) in Inside
  840. AppleTalk.  What do you need to do about them?
  841.  
  842. Use a High-Level Network Protocol
  843.  
  844. Make sure you use the Datagram Delivery Protocol (DDP), or a higher AppleTalk protocol based
  845. on DDP, like the AppleTalk Transaction Protocol (ATP).  Be warned that Link Access Protocol
  846. (LAP) packets do not make it across bridges to other AppleTalk networks.  Also, don’t
  847. broadcast; broadcast packets are not forwarded by bridges (broadcasting using protocols
  848. above LAP is discouraged, anyway).
  849.  
  850. Use Name Binding
  851.  
  852. As usual, use the Name Binding Protocol (NBP) to announce your presence on the network,
  853. as well as to find other entities on the network.  Pay special attention to zone name fields;
  854. the asterisk (as in “MyLaser:LaserWriter:*”) in a name you look up is now important; it means
  855. “my zone only” (see the Zone Information Protocol (ZIP) chapter of Inside AppleTalk for
  856. information on finding out what other zones exist).  The zone field should always be an
  857. asterisk when registering a name.
  858.  
  859. Pay Attention to Network Number Fields
  860.  
  861. When handling the network addresses returned by NBPLookUp (or anyone else),
  862. don’t be surprised if the network number field is non-zero.
  863.  
  864. Am I Running on an Internet?
  865.  
  866. The low-memory global ABridge is used to keep track of a bridge on the local AppleTalk 
  867. network (NBP and DDP use this value).  If ABridge is non-zero, then you’re running on an 
  868. internet; if it’s zero, chances are, you’re not (this is not guaranteed, however, due to 
  869. the fact that the ABridge value is “aged”, and if NBP hasn’t heard from the bridge in a 
  870. long time, the value is cleared).
  871.  
  872. Watch for Out-Of-Sequence and Non-Exactly-Once Requests
  873.  
  874. Due to a “race” condition on an internet, it’s possible for an exactly-once ATP packet 
  875. to slip through twice; to keep this from happening, send a sequence number as part of 
  876. the data with each ATP packet; whenever you make a request, bump the sequence number, 
  877. and never honor an old sequence number.
  878.  
  879.  
  880. Further Reference:
  881. _______________________________________________________________________________
  882.   •  Inside AppleTalk
  883.   •  Inside Macintosh, Volumes II & V, The AppleTalk Manager
  884.   •  Technical Note #250, AppleTalk Phase 2 on the Macintosh
  885.   •  Technical Note #270, AppleTalk Timers Explained
  886.  
  887. æKY 10
  888. æC #10: Pinouts
  889.  
  890. See also:   Macintosh Hardware Reference Manual
  891.             Technical Note #65 — Macintosh Plus Pinouts
  892.  
  893. Written by:      Mark Baumwell       April 26, 1985
  894. Modified:                            July 23, 1985
  895. Updated:                             March 1, 1988
  896. _______________________________________________________________________________
  897.  
  898. This note gives pinouts for Macintosh ports, cables, and other products.
  899. _______________________________________________________________________________
  900.  
  901. Below are pinout descriptions for the Macintosh ports, cables, and various other
  902. products. Please refer to the Hardware chapter of Inside Macintosh and the Macintosh
  903. Hardware Reference Manual for more information, especially about power limits. Note
  904. that unconnected pins are omitted.
  905.  
  906.  
  907. Macintosh Port Pinouts
  908.  
  909. Macintosh Serial Connectors (DB-9)
  910.      Pin   Name       Description/Notes
  911.      1     Ground
  912.      2     +5V        See Inside Macintosh for power limits
  913.      3     Ground
  914.      4     TxD+       Transmit Data line
  915.      5     TxD–       Transmit Data line
  916.      6     +12V       See Macintosh Hardware chapter for power limits
  917.      7     HSK        HandShaKe: CTS or TRxC, depends on Zilog 8530 mode
  918.      8     RxD+       Receive Data line; ground this line to emulate RS232
  919.      9     RxD–       Receive Data line
  920.  
  921. Macintosh Mouse Connector (DB-9)
  922.      Pin   Name       Description/Notes
  923.      1     Ground
  924.      2     +5V        See Inside Macintosh for power limits
  925.      3     GND        Ground
  926.      4     X2         Horizontal movement line (connected to VIA PB4 line)
  927.      5     X1         Horizontal movement line (connected to SCC DCDA– line)
  928.      7     SW–        Mouse button line (connected to VIA PB3)
  929.      8     Y2         Vertical movement line (connected to VIA PB5 line)
  930.      9     Y1         Vertical movement line (connected to SCC DCDB– line)
  931.  
  932. Macintosh Keyboard Connector (RJ-11 Telephone-style jack)
  933.      Pin   Name       Description/Notes
  934.      1     Ground
  935.      2     KBD1       Keyboard clock
  936.      3     KBD2       Keyboard data 
  937.      4     +5V        See Inside Macintosh for power limits
  938.  
  939. Macintosh External Drive Connector (DB-19)
  940.      Pin   Name       Description/Notes
  941.      1     Ground
  942.      2     Ground
  943.      3     Ground
  944.      4     Ground
  945.      5     –12V       See Inside Macintosh for power limits
  946.      6     +5V        See Inside Macintosh for power limits
  947.      7     +12V       See Inside Macintosh for power limits
  948.      8     +12V       See Inside Macintosh for power limits
  949.      10    PWM        Regulates speed of the drive
  950.      11    PH0        Control line to send commands to the drive
  951.      12    PH1        Control line to send commands to the drive
  952.      13    PH2        Control line to send commands to the drive
  953.      14    PH3        Control line to send commands to the drive
  954.      15    WrReq–     Turns on the ability to write data to the drive
  955.      16    HdSel      Control line to send commands to the drive
  956.      17    Enbl2–     Enables the Rd line (else Rd is tri-stated)
  957.      18    Rd         Data actually read from the drive
  958.      19    Wr         Data actually written to the drive
  959.  
  960.  
  961. Other Pinouts
  962.  
  963. Macintosh XL Serial Connector A (DB-25)
  964.      Pin   Name       Description/Notes
  965.      1     Ground
  966.      2     TxD        Transmit Data line
  967.      3     RxD        Receive Data line
  968.      4     RTS        Request to Send
  969.      5     CTS        Clear To Send
  970.      6     DSR        Data Set Ready
  971.      7     Ground
  972.      8     DCD        Data Carrier Detect
  973.      15    TxC        Connected to TRxCA
  974.      17    RxC        Connected to RTxCA
  975.      24    TEXT       Connected to TRxCA
  976.  
  977.  
  978. Macintosh XL Serial Connector B (DB-25)
  979.      Pin   Name       Description/Notes
  980.      1     Ground
  981.      2     TxD–       Transmit Data line
  982.      3     RxD–       Receive Data line
  983.      6     HSK/DSR    TRxCB or CTSB
  984.      7     Ground
  985.      19    RxD+       Receive Data line
  986.      20    TXD+/DTR   connected to DTRB
  987.  
  988. Apple 300/1200 Modem Serial Connector (DB-9)
  989.      Modem  Name     Description/Notes
  990.      2      DSR      Output from modem
  991.      3      Ground
  992.      5      RxD      Output from modem
  993.      6      DTR      Input to modem
  994.      7      DCD      Output from modem
  995.      8      Ground
  996.      9      TxD      Input to modem
  997.  
  998.  
  999. Apple ImageWriter Serial Connector (DB-25)
  1000. ImageWriter  Name     Description/Notes
  1001.      1       Ground
  1002.      2       SD       Send Data; Output from ImageWriter
  1003.      3       RD       Receive Data; Input to ImageWriter
  1004.      4       RTS      Output from ImageWriter
  1005.      7       Ground
  1006.      14      FAULT–   False when deselected; Output from ImageWriter
  1007.      20      DTR      Output from ImageWriter
  1008.  
  1009.  
  1010. Apple LaserWriter AppleTalk Connector (DB-9)
  1011.      LaserWriter  Name     Description/Notes
  1012.      1            Ground
  1013.      3            Ground
  1014.      4            TxD+     Transmit Data line
  1015.      5            TxD–     Transmit Data line
  1016.      7            RXCLK    TRxC of Zilog 8530
  1017.      8            RxD+     Receive Data line
  1018.      9            RxD–     Receive Data line
  1019.  
  1020.  
  1021. Apple LaserWriter Serial Connector (DB-25)
  1022.      LaserWriter  Name     Description/Notes
  1023.      1            Ground
  1024.      2            TXD–     Transmit Data; Output from LaserWriter
  1025.      3            RXD–     Receive Data; Input to LaserWriter
  1026.      4            RTS–     Output from LaserWriter
  1027.      5            CTS      Input to LaserWriter
  1028.      6            DSR      Input to LaserWriter (connected to DCBB– of 8530)
  1029.      7            Ground
  1030.      8            DCD      Input to LaserWriter (connected to DCBA– of 8530)
  1031.      20           DTR–     Output from LaserWriter
  1032.      22           RING     Input to LaserWriter
  1033.  
  1034.  
  1035. Macintosh Cable Pinouts 
  1036.  
  1037. Note for the cable descriptions below:
  1038.  
  1039. The arrows (“->”) show which side is an input and which is an output. For example,
  1040. the notation “a -> b” means that signal “a” is an output and “b” is an input.
  1041.  
  1042. When pins are said to be connected on a side in the Notes column, it means the pins
  1043. are connected on that side of the connector.
  1044.  
  1045.  
  1046. Macintosh ImageWriter Cable
  1047. (part number 590-0169)
  1048.      Mac      Name           ImageWriter   Notes
  1049.      (DB9)                   (DB25)
  1050.      1        Ground         1
  1051.      3        Ground         7             pins 3, 8 connected on Mac side
  1052.      5        TxD– ->  RD    3             RD = Receive Data
  1053.      7        HSK  <-  DTR   20
  1054.      8        RxD+ =   GND                 Not connected on ImageWriter side
  1055.      9        RxD– <-  SD    2             SD = Send Data
  1056.  
  1057.  
  1058. Macintosh Modem Cable (Warning! Don’t use this cable to connect 2 Macintoshes!)
  1059. (part number 590-0197-A)
  1060.      Mac     Name             Modem     Notes
  1061.      (DB9)                    (DB9)
  1062.      3       Ground           3         pins 3, 8 connected on EACH side
  1063.      5       TxD– ->  TxD9
  1064.      6       +12V ->  DTR6
  1065.      7       HSK  <-  DCD     7
  1066.      8       No wire          8
  1067.      9       RxD– <-  RxD     5
  1068.  
  1069.  
  1070. Macintosh to Macintosh Cable (Macintosh Modem Cable with pin 6 clipped on both ends.)
  1071.      Macintosh     Name     Macintosh     Notes
  1072.      (DB9)                  (DB9)
  1073.      3     Ground           3             pins 3, 8 connected on EACH side
  1074.      5     TxD– ->  RxD–    9
  1075.      7     HSK  <-  DCD     7
  1076.      8     No wire          8
  1077.      9     RxD– <-  TxD–    5
  1078.  
  1079.  
  1080. Macintosh External Drive Cable 
  1081. (part number 590-0183-B)
  1082.      Macintosh   Name          Sony Drive
  1083.      (DB9)                     (20 Pin Ribbon)
  1084.      1           Ground        1
  1085.      2           Ground        3
  1086.      3           Ground        5
  1087.      4           Ground        7
  1088.      6           +5V           11
  1089.      7           +12V          13
  1090.      8           +12V          15
  1091.      10          PWM           20
  1092.      11          PH0           2
  1093.      12          PH1           4
  1094.      13          PH2           6
  1095.      14          PH3           8
  1096.      15          WrReq–        10
  1097.      16          HdSel         12
  1098.      17          Enbl2–        14
  1099.      18          Rd            16
  1100.      19          Wr            18
  1101.  
  1102.  
  1103. Macintosh XL Null Modem Cable 
  1104. (part number 590-0166-A)
  1105.      Mac XL   Name                 DTE     Notes
  1106.      (DB25)                        (DB25)
  1107.      1        Ground1
  1108.      2        TxD–    -> RxD       3
  1109.      3        RxD–    <- TxD       2
  1110.      4,5      RTS,CTS -> DCD       8        pins 4, 5 connected together
  1111.      6        DSR     <- DTR       20
  1112.      7        Ground               7
  1113.      8        DCD     <- RTS,CTS   4,5      pins 4, 5 connected together
  1114.      20       DTR     -> DSR       6
  1115.  
  1116.  
  1117. Macintosh to Non-Apple Product Cable Pinouts
  1118.  
  1119.  
  1120. Macintosh to IBM PC Serial Cable #1 (not tested)
  1121.      Mac   Name               IBM PC    Notes
  1122.      (DB9)                    (DB25)
  1123.      3     Ground             7         pins 3, 8 connected on Macintosh side
  1124.      5     TxD– ->  RxD       3
  1125.      7     HSK  <-  DTR       20
  1126.      8     RxD+ =   Ground              Not connected on IBM side
  1127.      9     RxD– <-  TxD       2
  1128.      C     TS   <-  RTS       4-5       pins 4, 5 connected on IBM side
  1129.      D     SR   <-  DCD,DTR   6-8-20    pins 6, 8, 20 connected on IBM side
  1130.  
  1131.  
  1132. Macintosh to IBM PC Serial Cable #2 (not tested)
  1133.      Mac   Name               IBM PC    Notes
  1134.      (DB9)                    (DB25)
  1135.      1     Ground             1
  1136.      3     Ground             7         pins 3, 8 connected on Macintosh side
  1137.      5     TxD– -> RxD3
  1138.      9     RxD– <- TxD2
  1139.      C     TS   <- RTS        4-5       pins 4, 5 connected on IBM side
  1140.      D     SR   <- DTR        6-8       pins 6, 8 connected on IBM side
  1141.  
  1142.  
  1143. æKY 11
  1144. æC #11:    Memory–Based MacWrite Format
  1145.  
  1146. Revised:                                                            August 1989
  1147.  
  1148. This Technical Note formerly described the format of files created by
  1149. MacWrite(R) 2.2.
  1150. Changes since March 1988:  Updated the Claris address.
  1151. _______________________________________________________________________________
  1152.  
  1153. This Note formerly discussed the memory–based MacWrite 2.2 file format.  For information
  1154. on MacWrite and other Claris products, contact Claris at:
  1155.  
  1156.                 Claris Corporation
  1157.                 5201 Patrick Henry Drive
  1158.                 P.O. Box 58168
  1159.                 Santa Clara, CA 95052-8168
  1160.  
  1161.                 Technical Support
  1162.                 Telephone:  (408) 727-9054
  1163.                 AppleLink:  Claris.Tech
  1164.  
  1165.                 Customer Relations
  1166.                 Telephone:  (408) 727-8227
  1167.                 AppleLink:  Claris.CR
  1168.  
  1169. MacWrite is a registered trademark of Claris Corporation.
  1170.  
  1171. æKY 12
  1172. æC #12:    Disk–Based MacWrite Format
  1173.  
  1174. Revised:                                                            August 1989
  1175.  
  1176. This Technical Note formerly described the format of files created by
  1177. MacWrite(R), which is now published by Claris.
  1178. Changes since March 1988:  Updated the Claris address.
  1179. _______________________________________________________________________________
  1180.  
  1181. This Note formerly discussed the disk–based MacWrite file format.  For information on
  1182. MacWrite and other Claris products, contact Claris at:
  1183.  
  1184.                 Claris Corporation
  1185.                 5201 Patrick Henry Drive
  1186.                 P.O. Box 58168
  1187.                 Santa Clara, CA 95052-8168
  1188.  
  1189.                 Technical Support
  1190.                 Telephone:  (408) 727-9054
  1191.                 AppleLink:  Claris.Tech
  1192.  
  1193.                 Customer Relations
  1194.                 Telephone:  (408) 727-8227
  1195.                 AppleLink:  Claris.CR
  1196.  
  1197. MacWrite is a registered trademark of Claris Corporation.
  1198.  
  1199. æKY 13
  1200. æC #13:    MacWrite Clipboard Format
  1201.  
  1202. Revised:                                                            August 1989
  1203.  
  1204. This Technical Note formerly described the clipboard format used by MacWrite(R),
  1205. which is now published by Claris.
  1206. Changes since March 1988:  Updated the Claris address.
  1207. _______________________________________________________________________________
  1208.  
  1209. This Note formerly discussed the MacWrite clipboard format.  For information on MacWrite
  1210. and other Claris products, contact Claris at:
  1211.  
  1212.                 Claris Corporation
  1213.                 5201 Patrick Henry Drive
  1214.                 P.O. Box 58168
  1215.                 Santa Clara, CA 95052-8168
  1216.  
  1217.                 Technical Support
  1218.                 Telephone:  (408) 727-9054
  1219.                 AppleLink:  Claris.Tech
  1220.  
  1221.                 Customer Relations
  1222.                 Telephone:  (408) 727-8227
  1223.                 AppleLink:  Claris.CR
  1224.  
  1225. MacWrite is a registered trademark of Claris Corporation.
  1226.  
  1227. æKY 14
  1228. æC #14: The INIT 31 Mechanism
  1229.  
  1230. See:    The System Resource File
  1231.         The Start Manager
  1232.  
  1233. Written by:   Bryan Stearns     March 13, 1986
  1234. Updated:                        March 1, 1988
  1235. _______________________________________________________________________________
  1236.  
  1237. This note formerly described things that are now covered in the System Resource File
  1238. chapter of Inside Macintosh Volume IV and the Start Manager chapter of Inside Macintosh
  1239. Volume V. Please refer to Inside Macintosh.
  1240.  
  1241. æKY 15
  1242. æC #15: Finder 4.1
  1243.  
  1244. Written by:   Harvey Alcabes    April 12, 1985
  1245. Updated:                        March 1, 1988
  1246. _______________________________________________________________________________
  1247.  
  1248. This note formerly described Finder 4.1, which is now recommended only for use with
  1249. 64K ROM machines. Information specific to 64K ROM machines has been deleted from
  1250. Macintosh Technical Notes for reasons of clarity.
  1251.  
  1252. æKY 16
  1253. æC #16: MacWorks XL
  1254.  
  1255. Written by:   Harvey Alcabes    May 11, 1985
  1256.               Mark Baumwell
  1257. Updated:                        March 1, 1988
  1258. _______________________________________________________________________________
  1259.  
  1260. Earlier versions of this note described MacWorks XL, the system software that allowed
  1261. you to use Macintosh applications on the Macintosh XL. Information specific to Macintosh
  1262. XL machines has been deleted from Macintosh Technical Notes for reasons of clarity.
  1263.  
  1264. æKY 17
  1265. æC #17: Low-Level Print Driver Calls
  1266.  
  1267. See also:     The Print Manager
  1268.  
  1269. Written by:   Ginger Jernigan    April 14, 1986
  1270. Updated:                         March 1, 1988
  1271. _______________________________________________________________________________
  1272.  
  1273. This technical note has been replaced by information in Inside Macintosh Volume V.
  1274. Please refer to the Print Manager chapter of Inside Macintosh Volume V for information
  1275. on low-level print driver calls.
  1276.  
  1277.  
  1278.  
  1279.  
  1280.  
  1281.  
  1282.  
  1283. æKY 18
  1284. æC #18: TextEdit Conversion Utility
  1285.  
  1286. See also:       Macintosh Memory Management: An Introduction TextEdit
  1287.  
  1288. Written by:     Harvey Alcabes       April 10, 1985
  1289. Updated:                             March 1, 1988
  1290. _______________________________________________________________________________
  1291.  
  1292. Text sometimes must be converted between a Pascal string and “pure” text in a handle.
  1293. This note illustrates a way to do this using MPW Pascal.
  1294. _______________________________________________________________________________
  1295.  
  1296. Text contained in TextEdit records sometimes must be passed to routines which expect
  1297. a Pascal string of type Str255 (a length byte followed by up to 255 characters). The
  1298. following MPW Pascal unit can be used to convert between TextEdit records and Pascal
  1299. strings:
  1300.  
  1301. UNIT TEConvert;
  1302.  
  1303.    {General utilities for conversion between TextEdit and strings}
  1304.  
  1305.    INTERFACE
  1306.  
  1307.       USES MemTypes,QuickDraw,OSIntf,ToolIntf;
  1308.  
  1309.       PROCEDURE TERecToStr(hTE: TEHandle; VAR str: Str255);
  1310.       {TERecToStr converts the TextEdit record hTE to the string str.}
  1311.       {If necessary, the text will be truncated to 255 characters.}
  1312.  
  1313.       PROCEDURE StrToTERec(str: Str255; hTE: TEHandle);
  1314.       {StrToTERec converts the string str to the TextEdit record hTE. }
  1315.  
  1316.    IMPLEMENTATION
  1317.  
  1318.       PROCEDURE TERecToStr(hTE: TEHandle; VAR str: Str255);
  1319.  
  1320.          BEGIN
  1321.             GetIText(hTE^^.hText, str);
  1322.          END;
  1323.  
  1324.       PROCEDURE StrToTERec(str: Str255; hTE: TEHandle);
  1325.  
  1326.          BEGIN
  1327.             TESetText(POINTER(ORD4(@str) + 1), ORD4(length(str)), hTE);
  1328.          END;
  1329.  
  1330. END.
  1331.  
  1332. æKY 19
  1333. æC #19:    How To Produce Continuous Sound Without Clicking
  1334.  
  1335. Revised by:    Jim Reekes                                             June 1989
  1336. Written by:    Ginger Jernigan                                       April 1985
  1337.  
  1338. This Technical Note formerly described how to use the Sound Driver to produce continuous
  1339. sound without clicking.
  1340. Changes since March 1988:  The continuous sound technique is no longer recommended.
  1341. _______________________________________________________________________________
  1342.  
  1343. Apple currently discourages use of the Sound Driver due to compatibility issues.  The
  1344. hardware support for sound designed into the early Macintosh architecture was minimal.
  1345.  (Many things have changed since 1983–1984.)  The new Macintosh computers contain a
  1346. custom chip to provide better support for sound, namely the Apple Sound Chip (ASC). 
  1347. The ASC is present in the complete Macintosh II family as well as the Macintosh SE/30
  1348. and later machines.  When the older hardware of the Macintosh Plus and SE are accessed,
  1349. it is likely to cause a click.  This click is a hardware problem.  The software solution
  1350. to this problem was to continuously play silence.  This is not a real solution to the
  1351. problem and is not advisable for the following reasons:
  1352.  
  1353.   •  The Sound Driver is no longer supported.  There have always been,
  1354.      and still are, bugs in the glue code for StartSound.
  1355.  
  1356.   •  The Sound Driver may not be present in future System Software
  1357.      releases, or future hardware may not be able to support it.
  1358.      The Sound Manager is the application’s interface to the sound
  1359.      hardware.
  1360.  
  1361.   •  The technique used to create a continuous sound should have only
  1362.      been used on a Macintosh Plus or SE, since these are the only
  1363.      models that have the “embarrassing click.”  Do not use this method
  1364.      on a Macintosh which has the Apple Sound Chip.
  1365.  
  1366.   •  Using the continuous sound technique, or the Sound Driver for that
  1367.      matter, will cause problems for the system and those applications
  1368.      that properly use the Sound Manager.  Also realize that _SysBeep,
  1369.      which is a common routine that everything uses, is a Sound Manager
  1370.      routine.
  1371.  
  1372.   •  The continuous sound technique wastes CPU time by playing silence.
  1373.      With multimedia applications and the advent of MultiFinder, it is
  1374.      important to allow the CPU to do as much work as possible.  The
  1375.      continuous sound technique used the CPU to continuously play silence,
  1376.      thus stealing valuable time from other, more important, jobs.
  1377.  
  1378.  
  1379. Further Reference:
  1380. _______________________________________________________________________________
  1381.   •  The Sound Manager, Interim Chapter by Jim Reekes, October 2, 1988
  1382.   •  Technical Note #180, MultiFinder Miscellanea
  1383.  
  1384.  
  1385. æKY 20
  1386. æC #20: Data Servers on AppleTalk
  1387.  
  1388. See also:     The AppleTalk Manager
  1389.               Inside LaserWriter
  1390.  
  1391. Written by:   Bryan Stearns     April 29, 1985
  1392. Updated:                        March 1, 1988
  1393. _______________________________________________________________________________
  1394.  
  1395. Many applications could benefit from the ability to share common data between several
  1396. Macintoshes, without requiring a file server. This technical note discusses one technique
  1397. for managing this AppleTalk communication.
  1398. _______________________________________________________________________________
  1399.  
  1400. There are four main classes of network “server” devices: 
  1401.  
  1402. DEVICE SERVERS, such as the LaserWriter, allow several users to share a single hardware
  1403. device; other examples of this (currently under development by third parties) are
  1404. modem servers and serial servers (to take advantage of 
  1405. non-intelligent printers such as the ImageWriter). 
  1406.  
  1407. FILE SERVERS, such as AppleShare, which support file access operations over the network.
  1408. A user station sends high-level requests over the network (such as 
  1409. “Open this file,” “Read 137 bytes starting at the current file position of this file,”
  1410. “Close this file,” etc.).
  1411.  
  1412. BLOCK SERVERS, which answer to block requests over the network. These requests impart
  1413. no file system knowledge about the blocks being passed, i.e., the server doesn’t know
  1414. which files are open by which users, and therefore cannot protect one user’s open
  1415. file from other users. Examples of this type of server are available from third-party
  1416. developers.
  1417.  
  1418. DATA SERVERS, which answer to requests at a higher level than file servers, such as
  1419. “Give me the first four records from the database which match the following search
  1420. specification.” This note directs its attention at this type of server.
  1421.  
  1422. A data server is like a file server in that it responds to intelligent requests, but
  1423. the requests that it responds to can be more specialized, because the code in the
  1424. server was written to handle that specific type of request. This has several added
  1425. benefits: user station processing can be reduced, if the data server is used for
  1426. sorting or searching operations; and network traffic is reduced, because of the specificity
  1427. of the requests passed over the network. The data server can even be designed to do
  1428. printing (over the network to a LaserWriter, or on a local ImageWriter), given that
  1429. it has the data and can be directed as to the format in which it should be printed.
  1430.  
  1431.  
  1432. ATP: The AppleTalk Transaction Protocol
  1433.  
  1434. ATP, the assured-delivery AppleTalk Transaction Protocol, can be used to support all
  1435. types of server communications (the LaserWriter uses ATP for its communications!).
  1436. Here is a possible scenario between two user stations 
  1437. (“Dave” and “Bill”) and a data server station (“OneServer”, a server of type 
  1438. “MyServer”). We’ve found that the “conversational” analogy is helpful when planning
  1439. AppleTalk communications; this example is therefore presented as a conversation,
  1440. along with appropriate AppleTalk Manager calls (Note that no error handling is presented,
  1441. however; your application should contain code for handling errors, specifically the
  1442. “half-open connection” problem described below).
  1443.  
  1444.  
  1445. Establishing the Connection
  1446.  
  1447. Each station uses ATPLoad to make sure that AppleTalk is loaded. The server station,
  1448. since it wants to accept requests, opens a socket and registers its name using NBPRegister.
  1449. The user stations use NBPLookUp to find out the server’s network address. This looks
  1450. like this, conversationally:
  1451.  
  1452. Server: “I’m ready to accept   ATPLoad        Opens AppleTalk
  1453.          requests!”            OpenSocket     Creates socket
  1454.                                NBPRegister    Assigns name to socket
  1455.                                ATPGetRequest  queue a few asynchronous
  1456.                                ATPGetRequest  calls, to be able to handle
  1457.                                ATPGetRequest  several users
  1458.  
  1459. Dave: “Any ‘MyServers’         ATPLoad        Opens AppleTalk
  1460.        out there?”             NBPLookup      look for servers, finds OneServer
  1461.  
  1462. Dave: “Hey, MyServer! What     ATPRequest     Ask the server which socket to
  1463.        socket should I talk                   use for further communications
  1464.        to you on?”
  1465.  
  1466. Bill: “Any ‘MyServers’        ATPLoad         Opens AppleTalk
  1467.        out there?”            NBPLookup       look for servers, finds OneServer
  1468.  
  1469. Bill: “Hey, MyServer! What    ATPRequest      Ask the server which socket to
  1470.        socket should I talk                   use for further communications
  1471.        to you on?”
  1472.  
  1473. Server: “Hi, Dave!            ATPOpenSkt      Get a new socket for talking to 
  1474.          Use Socket N.”                       Dave
  1475.                               ATPResponse     Send Dave the socket number
  1476.                               ATPGetRequest   Replace the used GetRequest
  1477.  
  1478. Server: “Hi, Bill!             ATPOpenSkt     Get a new socket for talking to 
  1479.          Use socket M.”                        Bill
  1480.                                ATPResponse    Send Bill the socket number
  1481.                                ATPGetRequest  Replace the used GetRequest
  1482.  
  1483. From this point on, the server knows that any requests received on socket N are from
  1484. Dave, and those received on socket M are from Bill. The conversations continue, after
  1485. a brief discussion of error handling.
  1486.  
  1487.  
  1488. Half-Open Connections
  1489.  
  1490. There is a possibility that one side of a connection could go down (be powered off,
  1491. rebooted accidently, or simply crash) before the connection has been officially broken.
  1492. If a user station goes down, the server must throw away any saved state information
  1493. and close that user’s open socket. This can be done by requiring that the user stations
  1494. periodically “tickle” the server: every 30 seconds (for example) the user station
  1495. sends a dummy request to the server, which sends a dummy response. This lets each
  1496. side of the connection know that the other side is still “alive.”
  1497.  
  1498. When the server detects that two intervals have gone by without a tickle request, it
  1499. can assume that the user station has crashed, and close that user’s socket and throw
  1500. away any accumulated state information.
  1501.  
  1502. The user station should use a vertical-blanking task to generate these tickle requests
  1503. asyncronously, rather than generating them within the GetNextEvent loop; this avoids
  1504. problems with long periods away from GetNextEvent (such as when a modal dialog box is
  1505. running). This task can look at the time that the last request was made of the server,
  1506. and if it’s approaching the interval time, queue an asynchronous request to tickle
  1507. the server (it’s important that any AppleTalk calls made from interrupt or completion
  1508. routines be asynchronous).
  1509.  
  1510. If a user station’s request (including a tickle request) goes unanswered, the user
  1511. station should recover by looking for the server and reestablishing communications as
  1512. shown above (beginning with the call to NBPLookUp).
  1513.  
  1514. More information about half-open connections can be found in the Printer Access Protocol
  1515. chapter of Inside LaserWriter, available from APDA.
  1516.  
  1517.  
  1518. Using the Connection
  1519.  
  1520. The user stations Dave and Bill have established communications with the server, each
  1521. on its own socket (note that the user stations have not had to open their own sockets,
  1522. or register names of their own, to do this—the names we’re using are merely for explanational
  1523. convenience). They are also automatically tickling the server as necessary.
  1524.  
  1525. Now the user stations make requests of the server as needed: 
  1526.  
  1527. Bill: “I’d like to use the sales   ATPRequest    Bill opens a database.
  1528.        figures for this year.”
  1529.  
  1530. Server: “Ok, Bill.”                ATPResponse   The server checks to make sure 
  1531.                                                  that no one else is using that 
  1532.                                                  database.
  1533.  
  1534. Dave: “Hey, Server                 ATPRequest    Dave notices that the 
  1535.       - I’m still here!”                         interval time is approaching,  
  1536.                                                  and makes a tickle request.
  1537.  
  1538. Server: “Ok, Dave.”                ATPResponse   The server resets its “last 
  1539.                                                  time I heard from Dave”.
  1540.  
  1541. Bill: “Please print the figures    ATPRequest    Bill asks for specific data.
  1542.        for January thru June.”
  1543.  
  1544. Server: “Ok, Bill.”                ATPResponse   The server does a database 
  1545.                                                  search sorts the results, and 
  1546.                                                  prints them on a local 
  1547.                                                  Imagewriter.
  1548.  
  1549. Dave: “I’d like to use the sales   ATPRequest    Dave opens a database.
  1550.        figures for this year.”
  1551.  
  1552. Server: “Sorry, Dave, I can’t do   ATPResponse   The server finds that Bill is
  1553.          that. Bill is using that               using that data.
  1554.          database.”
  1555.  
  1556.  
  1557. Closing the Connection
  1558.  
  1559. The user stations continue making requests of the server, until each is finished. The
  1560. type of work being done by the server determines how long the conversation will last:
  1561. since the number of sockets openable by the server is limited, it may be desirable to
  1562. structure the requests in such a way that the average conversation is very short. It
  1563. may also be necessary to have a (NBP named) socket open on the user station, if the
  1564. server needs to communicate with the user on other than a request-response basis.
  1565. Here is how our example connections ended:
  1566.  
  1567. Dave: “Thank you, server, I’m done  ATPRequest   Dave tells the server he’s 
  1568.        now. You’ve been a big help.”             finished.
  1569.  
  1570. Server: “Ok, Dave. Bye now.”        ATPResponse  The server kisses Dave goodbye.
  1571.                                     ATPCloseSkt  After the Response operation 
  1572.                                                  completes, the server closes
  1573.                                                  the socket Dave was using. It 
  1574.                                     ATPCloseSkt  also notices that Bill hasn’t 
  1575.                                                  sent a request in more than
  1576.                                                  two intervals, and closes 
  1577.                                                  Bill’s socket, too.
  1578.  
  1579. The user station can forget about the socket it was using on the server; if it needs
  1580. to talk with the server again, it starts at the NBPLookUp (just in case the server
  1581. has moved, gone down and come up, etc.).
  1582.  
  1583.  
  1584.  
  1585. æKY 21
  1586. æC #21: QuickDraw’s Internal Picture Definition
  1587.  
  1588. See also:     QuickDraw
  1589.               Color QuickDraw
  1590.               Using Assembly Language
  1591.               Technical Note #59—Pictures and Clip Regions
  1592.  
  1593. Written by:   Ginger Jernigan     April 24, 1985
  1594. Modified by:  Rick Blair          November 15, 1986
  1595. Updated:                          March 1, 1988
  1596. _______________________________________________________________________________
  1597.  
  1598. This technical note describes the internal format of the QuickDraw picture data structure.
  1599. This revision corrects some errors in the opcode descriptions and provides some examples.
  1600. _______________________________________________________________________________
  1601.  
  1602. This technical note describes the internal definition of the QuickDraw picture. The
  1603. information given here only applies to QuickDraw picture format version 1.0 (which is
  1604. always created by Macintoshes without Color QuickDraw). Picture format version 2.0 is
  1605. documented in the Color QuickDraw chapter of Inside Macintosh. This information should
  1606. not be used to write your own picture bottleneck procedures; if we add new objects to
  1607. the picture definition, your program will not be able to operate on pictures created
  1608. using standard QuickDraw. Your program will not know the size of the new objects and
  1609. will, therefore, not be able to proceed past the new objects. (What this ultimately
  1610. means is that you can’t process a new picture with an old bottleneck proc.)
  1611.  
  1612.  
  1613. Terms
  1614.  
  1615. An opcode is a number that DrawPicture uses to determine what object to draw or what
  1616. mode to change for subsequent drawing. The following list gives the opcode, the name
  1617. of the object (or mode), the associated data, and the total size of the opcode and
  1618. data. To better interpret the sizes, please refer to page I-91 of the Using Assembly
  1619. Language chapter of Inside Macintosh. For types not described there, here is a quick
  1620. list:
  1621.  
  1622.     opcode       byte
  1623.     mode         word
  1624.     point        4 bytes
  1625.     0..255       byte
  1626.     –128..127    signed byte
  1627.     rect         8 bytes
  1628.     poly         10+ bytes (starts with word size for poly (incl. size word)
  1629.     region       10+ bytes (starts with word size for region (incl. size word)
  1630.     fixed point  long
  1631.     pattern      8 bytes
  1632.     rowbytes     word (always even)
  1633.     bit data     rowbytes * (bounds.bottom - bounds.top) bytes
  1634.  
  1635. Each picture definition begins with a picsize (word), then a picframe (rect), and
  1636. then the picture definition, which consists of a combination of the following opcodes:
  1637.  
  1638. Opcode Name          Additional Data                Total Size (bytes)
  1639.   00   NOP           none                                1
  1640.   01   clipRgn       rgn                                 1+rgn
  1641.   02   bkPat         pattern                             9
  1642.   03   txFont        font (word)                         3
  1643.   04   txFace        face (byte)                         2
  1644.   05   txMode        mode (word)                         3
  1645.   06   spExtra       extra (fixed point)                 5
  1646.   07   pnSize        pnSize (point)                      5
  1647.   08   pnMode        mode (word)                         3
  1648.   09   pnPat         pattern                             9
  1649.   0A   thePat        pattern                             9
  1650.   0B   ovSize        point                               5
  1651.   0C   origin        dh, dv (words)                      5
  1652.   0D   txSize        size (word)                         3
  1653.   0E   fgColor       color (long)                        5
  1654.   0F   bkColor       color (long)                        5
  1655.  
  1656.   10   txRatio       numer (point),denom (point)         9
  1657.   11   picVersion    version (byte)                      2
  1658.  
  1659.   20   line          pnLoc (point),newPt (point)         9
  1660.   21   line from     newPt (point)                       5
  1661.   22   short line    pnLoc (point); dh,dv(-128..127)     7
  1662.   23 short line from dh, dv (-128..127)3
  1663.  
  1664.   28   long text     txLoc ( point ),count (0..255),text 6+text
  1665.   29   DH text       dh (0..255), count (0..255), text   3+text
  1666.   2A   DV text       dv (0..255), count (0..255), text   3+text
  1667.   2B   DHDV text     dh, dv (0..255),count (0..255),text 4+text
  1668.  
  1669.   30   frameRect     rect                                9
  1670.   31   paintRect     rect                                9
  1671.   32   eraseRect     rect                                9
  1672.   33   invertRect    rect                                9
  1673.   34   fillRect      rect                                9
  1674.  
  1675.   38   frameSameRect  rect                               1
  1676.   39   paintSameRect  rect                               1
  1677.   3A   eraseSameRect  rect                               1
  1678.   3B   invertSameRect rect                               1
  1679.   3C   fillSameRect   rect                               1
  1680.  
  1681.   40   frameRRect    rect ( ovalwidth, height; see 1, below )  9
  1682.   41   paintRRect    rect ( ovalwidth, height; see 1, below )  9
  1683.   42   eraseRRect    rect ( ovalwidth, height; see 1, below )  9
  1684.   43   invertRRect   rect ( ovalwidth, height; see 1, below )  9
  1685.   44   fillRRect     rect ( ovalwidth, height; see 1, below )  9
  1686.  
  1687.   48   frameSameRRect  rect                              1
  1688.   49   paintSameRRect  rect                              1
  1689.   4A   eraseSameRRect  rect                              1
  1690.   4B   invertSameRRect rect                              1
  1691.  
  1692.   4C   fillSameRRect   rect                              1
  1693.  
  1694.   50   frameOval       rect                              9
  1695.   51   paintOval       rect                              9
  1696.   52   eraseOval       rect                              9
  1697.   53   invertOval      rect                              9
  1698.   54   fillOval        rect                              9
  1699.  
  1700.   58   frameSameOval   rect                              1
  1701.   59   paintSameOval   rect                              1
  1702.   5A   eraseSameOval   rect                              1
  1703.   5B   invertSameOval  rect                              1
  1704.   5C   fillSameOval    rect                              1
  1705.  
  1706.   60   frameArc        rect, startAngle, arcAngle        13
  1707.   61   paintArc        rect, startAngle, arcAngle        13
  1708.   62   eraseArc        rect, startAngle, arcAngle        13
  1709.   63   invertArc       rect, startAngle, arcAngle        13
  1710.   64   fillArc         rect, startAngle, arcAngle        13
  1711.  
  1712.   68   frameSameArc    startAngle, arcAngle              5
  1713.   69   paintSameArc    startAngle, arcAngle              5
  1714.   6A   eraseSameArc    startAngle, arcAngle              5
  1715.   6B   invertSameArc   startAngle, arcAngle              5
  1716.   6C   fillSameArc     startAngle, arcAngle              5
  1717.  
  1718.   70   framePoly       poly                              1+poly
  1719.   71   paintPoly       poly                              1+poly
  1720.   72   erasePoly       poly                              1+poly
  1721.   73   invertPoly      poly                              1+poly
  1722.   74   fillPoly        poly                              1+poly
  1723.  
  1724.   78   frameSamePoly   (not yet implemented 
  1725.                          — same as 70, etc.)             1
  1726.   79   paintSamePoly   (not yet implemented)             1
  1727.   7A   eraseSamePoly   (not yet implemented)             1
  1728.   7B   invertSamePoly  (not yet implemented)             1
  1729.   7C   fillSamePoly    (not yet implemented)             1
  1730.  
  1731.   80   frameRgn        rgn                               1+rgn
  1732.   81   paintRgn        rgn                               1+rgn
  1733.   82   eraseRgn        rgn                               1+rgn
  1734.   83   invertRgn       rgn                               1+rgn
  1735.   84   fillRgn         rgn                               1+rgn
  1736.  
  1737.   88   frameSameRgn    (not yet implemented
  1738.                           —same as 80, etc.)             1
  1739.   89   paintSameRgn    (not yet implemented)             1
  1740.   8A   eraseSameRgn    (not yet implemented)             1
  1741.   8B   invertSameRgn   (not yet implemented)             1
  1742.   8C   fillSameRgn     (not yet implemented)             1 
  1743.  
  1744.   90   BitsRect        rowBytes, bounds, srcRect,
  1745.                        dstRect, mode, unpacked 
  1746.                        bitDatabitData                    29+unpacked
  1747.   91   BitsRgnrowBytes, bounds, srcRect, dstRect, mode,  29+rgn+
  1748. maskRgn, unpacked bitDatabitData
  1749.   98   PackBitsRectrowBytes, bounds, srcRect, dstRect, mode,29+packed
  1750. packed bitData for each rowbitData
  1751.   99   PackBitsRgnrowBytes, bounds, srcRect, dstRect, mode,29+rgn+
  1752. maskRgn, packed bitData for each rowpacked bitData
  1753.  
  1754.   A0   shortComment    kind(word)                          3
  1755.   A1   longComment     kind(word), size(word), data        5+data
  1756.  
  1757.   FF   EndOfPicture    none                                1
  1758.  
  1759.  
  1760. Notes
  1761.  
  1762. Rounded-corner rectangles use the setting of the ovSize point (see opcode $0B, above).
  1763.  
  1764. OpenPicture and DrawPicture set up a default set of port characteristics when they
  1765. start. When drawing occurs, if the user’s settings don’t match the defaults, mode
  1766. opcodes are generated. This is why there is usually a clipRgn code after the picVersion:
  1767. the default clip region is an empty rectangle.
  1768.  
  1769. The only savings that the “same” opcodes achieve under the current implementation is
  1770. for rectangles. DrawPicture keeps track of the last rectangle used and if a “same”
  1771. opcode is encountered that requests a rectangle, the last rect. will be used (and no
  1772. rectangle will appear in the opcode’s data).
  1773.  
  1774. This last section contains some Pascal program fragments that generate pictures. Each
  1775. section starts out with the picture itself (yes, they’re dull) followed by the code
  1776. to create and draw it, and concludes with a commented hex dump of the picture.
  1777.  
  1778. {variables used in all examples}
  1779.  
  1780. VAR
  1781.     err:     OSErr;
  1782.     ph:      PicHandle;
  1783.     h:       Handle;
  1784.     r:       Rect;
  1785.     smallr:  Rect;
  1786.     orgr:    Rect;
  1787.     pstate:  PenState; {are they in the Rose Bowl, or the state pen?}
  1788.  
  1789. •••Click on the Illustration button below to view Figures 1-3.••• 
  1790.  
  1791. I.   {Rounded-corner rectangle}
  1792.      SetRect(r, 20, 10, 120, 175);
  1793.      ClipRect(myWindow^.portRect);
  1794.      ph := OpenPicture(r);
  1795.      FrameRoundRect (r, 5, 4); {r,width,height}
  1796.      ClosePicture;
  1797.      DrawPicture(ph, r);
  1798.  
  1799. 'PICT' (1)  0026 {size}  000A 0014 00AF 0078 {picFrame}
  1800.       1101 {version 1}  01 000A 0000 0000 00FA 0190 {clipRgn — 10 byte region}
  1801.       0B 0004 0005 {ovSize point}  40 000A 0014 00AF 0078 {frameRRect rectangle}
  1802.       FF {fin} 
  1803.  
  1804. _______________________________________________________________________________
  1805.  
  1806. •••Click on the Illustration button below to view Figures 1-3.••• 
  1807.  
  1808. II. {Overpainted arc}
  1809.     GetPenState(pstate); {save}
  1810.     SetRect(r, 20, 10, 120, 175);
  1811.     ClipRect(myWindow^.portRect);
  1812.     ph := OpenPicture(r);
  1813.     PaintArc(r, 3, 45); {r,startangle,endangle}
  1814.     PenPat(gray);
  1815.     PenMode(patXor); {turn the black to gray}
  1816.     PaintArc(r, 3, 45); {r,startangle,endangle}
  1817.     ClosePicture;
  1818.     SetPenState(pstate); {restore}
  1819.     DrawPicture(ph, r);
  1820.  
  1821. data 'PICT' (2)  0036 {size}  000A 0014 00AF 0078 {picFrame}
  1822.       1101 {version 1}  01 000A 0000 0000 00FA 0190 {clipRgn — 10 byte region}
  1823.       61 000A 0014 00AF 0078 0003 002D {paintArc rectangle,startangle,endangle}
  1824.       08 000A {pnMode patXor — note that the pnMode comes before the pnPat}
  1825.       09 AA55 AA55 AA55 AA55 {pnPat gray}
  1826.       69 0003 002D {paintSameArc startangle,endangle}
  1827.       FF {fin} 
  1828.  
  1829.  
  1830. •••Click on the Illustration button below to view Figures 1-3.••• 
  1831.  
  1832. III. {CopyBits nopack, norgn, nowoman, nocry}
  1833.      GetPenState(pstate);
  1834.      SetRect(r, 20, 10, 120, 175);
  1835.      SetRect(smallr, 20, 10, 25, 15);
  1836.      SetRect(orgr, 0, 0, 30, 20);
  1837.      ClipRect(myWindow^.portRect);
  1838.      ph := OpenPicture(r);
  1839.      PaintRect(r);
  1840.      CopyBits (myWindow^.portBits, myWindow^.portBits,
  1841.              smallr, orgr, notSrcXor, NIL); 
  1842.      {note: result BitMap is 8 bits wide instead of the 5 specified by smallr}
  1843.      ClosePicture;
  1844.      SetPenState(pstate); {restore the port's original pen state}
  1845.      DrawPicture(ph, r);
  1846.  
  1847. data 'PICT' (3)  0048 {size}  000A 0014 00AF 0078 {picFrame}
  1848.      1101 {version 1}  01 000A 0000 0000 00FA 0190 {clipRgn — 10 byte region}
  1849.      31 000A 0014 00AF 0078 {paintRect rectangle}
  1850.      90 0002 000A 0014 000F 001C {BitsRect rowbytes bounds (note that bounds is
  1851.      wider than smallr)}
  1852.      000A 0014 000F 0019 {srcRect}
  1853.      0000 0000 0014 001E {dstRect}
  1854.      00 06 {mode=notSrcXor}
  1855.      0000 0000 0000 0000 0000 {5 rows of empty bitmap (we copied from a 
  1856.      still-blank window)}
  1857.      FF {fin}
  1858.  
  1859.  
  1860.  
  1861. æKY 22
  1862. æC #22: TEScroll Bug
  1863.  
  1864. See also:      TextEdit
  1865.                Technical Note #131 — TextEdit Bugs
  1866.  
  1867. Written by:    Bryan Stearns        April 21,  1986
  1868. Updated:                            March 1, 1988
  1869. _______________________________________________________________________________
  1870.  
  1871. A bug in TextEdit causes the following problem: a call to TEScroll with no horizontal
  1872. or vertical displacement (that is, both dh and dv set to zero) results in disappearance
  1873. of the insertion point. Since such calls do nothing, they should be avoided:
  1874.  
  1875.     IF (dh <> 0) OR (dv <> 0) THEN TEScroll(dh,dv,myTEHandle);
  1876.  
  1877. æKY 23
  1878. æC #23: Life With Font/DA Mover—Desk Accessories
  1879.  
  1880. See also:    The Resource Manager
  1881.              Technical Note #6—Shortcut for Owned Resources
  1882.  
  1883. Written by:  Ginger Jernigan     April 25, 1985
  1884. Updated:                         March 1, 1988
  1885. _______________________________________________________________________________
  1886.  
  1887. This technical note describes how to make sure that your desk accessory will work
  1888. after being moved by Font/Desk Accessory Mover.
  1889. _______________________________________________________________________________
  1890.  
  1891. If you want your desk accessory to work properly after being moved by the 
  1892. Font/DA Mover, there are some eccentricities that you need to be aware of. When the
  1893. Font/DA Mover moves a desk accessory, it renumbers to avoid conflicts in ID numbers.
  1894. It will also renumber all of your desk accessory’s owned resources. See the Resource
  1895. Manager chapter of Inside Macintosh for more information on owned resources.
  1896.  
  1897. Since these owned resources are renumbered, your code will need to calculate the
  1898. resource ID of any owned resource it uses. For example, if your desk accessory has an
  1899. owned ‘DLOG’ resource, and calls GetNewDialog with the ID you assigned to it originally,
  1900. the Resource Manager will not find it. The solution is that every time your desk
  1901. accessory references an owned resource, it must figure out (at execution time) the ID
  1902. of the resource according to the current driver resource ID.
  1903.  
  1904. When the Font/DA Mover renumbers, it does its best to keep resources pointing to each
  1905. other properly. This means that it tries to renumber resource IDs embedded in other
  1906. resources as well as the resources themselves. For example, the reference to a ‘DITL’
  1907. within a ‘DLOG’ or ‘ALRT’ resource gets changed automatically. Font/DA Mover knows
  1908. about the standard embedded resource IDs in most of the standard resources, but if
  1909. you define your own, the Font/DA Move won’t be able to renumber them for you. The
  1910. embedded resource IDs which the Font/DA Mover knows about are listed below.
  1911.  
  1912. Note that certain resources can never be owned, because their resource IDs are restricted
  1913. to a certain range. One such example is a WDEF. Since the ID of a WDEF is specified
  1914. along with a four bit variation code, the range of WDEF IDs that can be used is 0-16363.
  1915. Since none of this falls within the owned resource ID range, WDEFs cannot be owned.
  1916. For the same reason, MDEFs, CDEFs, and MBDFs can’t be owned either.
  1917.  
  1918. As a rule of thumb, before you ship a desk accessory, move it to a disk with another
  1919. desk accessory of the same ID. This will cause the Font/DA Mover to renumber your
  1920. desk accessory. If the moved copy doesn’t work, then there is probably something
  1921. wrong with the way you are handling your owned resources.
  1922.  
  1923. Embedded resources known by Font/DA Mover
  1924.  
  1925. These are all true for Font/DA Mover 3.3 and newer:
  1926.  
  1927.   • references to ‘DITL’ resources in ‘DLOG’/‘ALRT’ resources
  1928.   • references to ‘ICON’, ‘PICT’, ‘CTRL’ in ‘DITL’ resources
  1929.   • references to ‘MENU’ resources inside the resources themselves(menuID field)
  1930.   • references to ‘MENU’ resources in ‘MBAR’ resources
  1931.  
  1932. Anything not on this list has to be fixed by the desk accessory.
  1933.  
  1934.  
  1935. By the way...
  1936.  
  1937. Before Font/DA Mover, desk accessories could have an ID in the range 12 to 31. Now,
  1938. and in the future, desk accessories can only have IDs in the range 12 to 26, because
  1939. Font/DA Mover will only assign numbers in this range. Numbers 27 thru 31 are reserved.
  1940.  
  1941.  
  1942.  
  1943.  
  1944. æKY 24
  1945. æC #24: Available Volumes
  1946.  
  1947. See also:       The File Manager
  1948.  
  1949. Written by:     Bryan Stearns      April 26, 1985
  1950. Modified by:    Bryan Stearns      October 15, 1985
  1951. Updated:                           March 1, 1988
  1952. _______________________________________________________________________________
  1953.  
  1954. Standard File lets the user select one file from any available volume; it is sometimes
  1955. necessary for an application to find which volumes are present. This technical note
  1956. gives the proper method of accomplishing this.
  1957. _______________________________________________________________________________
  1958.  
  1959. There is a little-noticed feature of the low-level file manager call PBHGetVInfo
  1960. which allows specification of a “volume index” to select the volume. This volume
  1961. index selects the nth volume in the VCB queue. The following function uses PBHGetVInfo
  1962. to find out about a given volume.  In MPW Pascal:
  1963.  
  1964.     FUNCTION GetIndVolume(whichVol: INTEGER; VAR volName: Str255;
  1965.                           VAR volRefNum: INTEGER): OSErr;
  1966.  
  1967.     {Return the name and vRefNum of volume specified by whichVol.}
  1968.  
  1969.        VAR
  1970.           volPB : HParamBlockRec;
  1971.           error : OSErr;
  1972.  
  1973.        BEGIN
  1974.           WITH volPB DO BEGIN        {makes it easier to fill in!}
  1975.              ioNamePtr  := @volName; {make sure it returns the name}
  1976.              ioVRefNum  := 0;        {0 means use ioVolIndex}
  1977.              ioVolIndex := whichVol; {use this to determine the volume}
  1978.           END; {with}
  1979.           error := PBHGetVInfo(@volPB,false); {do it}
  1980.           IF error = noErr THEN BEGIN      {if no error occurred }
  1981.              volRefNum := volPB.ioVRefNum; {return the volume reference}
  1982.           END; {if no error}
  1983.           {other information is available from this record; see the FILE}
  1984.           {Manager's description of PBHGetVInfo for more details...}
  1985.           GetIndVolume := error; {return error code}
  1986.        END;
  1987.  
  1988. In MPW C:
  1989.  
  1990. OSErr GetIndVolume(whichVol,volName,volRefNum)
  1991. short int whichVol;
  1992. char    *volName;
  1993. short int *volRefNum;
  1994.  
  1995. {
  1996.     /*Return the name and vRefNum of volume specified by whichVol.*/
  1997.  
  1998.     HVolumeParam    volPB;
  1999.     OSErr            error;
  2000.  
  2001.        
  2002.     volPB.ioNamePtr = volName; /*make sure it returns the name*/
  2003.     volPB.ioVRefNum = 0;       /*0 means use ioVolIndex*/
  2004.     volPB.ioVolIndex = whichVol; /*use this to determine the volume*/
  2005.  
  2006.     error = PBHGetVInfo(&volPB,false); /*do it*/
  2007.     if (error == noErr)   /*if no error occurred */
  2008.         *volRefNum = volPB.ioVRefNum; /*return the volume reference*/
  2009.  
  2010.     /*other information is available from this record; see the FILE*/
  2011.     /*Manager's description of PBHGetVInfo for more details...*/
  2012.           
  2013.     return(error);   /*always return error code*/
  2014. } /* GetIndVolume */
  2015.  
  2016. To find out about all volumes on-line, you can call this routine several times, starting
  2017. at whichVol := 1 and incrementing whichVol until the routine returns nsvErr.
  2018.  
  2019.  
  2020.  
  2021.  
  2022. æKY 25
  2023. æC #25: Don’t Depend on Register A5 Within Trap Patches
  2024.  
  2025. See also:       The Operating System Utilities
  2026.  
  2027. Written by:     Bryan Stearns     June 25, 1986
  2028. Updated:                          March 1, 1988
  2029. _______________________________________________________________________________
  2030.  
  2031. Future software may allow desk accessories to have their own globals by changing
  2032. register A5 when the accessory is entered and exited. This can cause problems for
  2033. applications that patch traps without following certain rules.
  2034. _______________________________________________________________________________
  2035.  
  2036. If your application patches any traps, it’s important that the patches not depend on
  2037. register A5. This is because you may have intercepted a trap used by a desk accessory.
  2038.  
  2039. If you need access to your globals within your patch, you can save A5 (on the stack,
  2040. perhaps), load A5 from the low-memory global CurrentA5 (this is guaranteed to be
  2041. correct for your application), do whatever you have to do within your patch, then
  2042. restore A5 on the way out. Note that if you make any traps within your patch (or call
  2043. the “real” version of the routine you patched), you should restore the caller’s A5
  2044. before doing so.
  2045.  
  2046. There are several ways of depending on A5 within a patch that you should watch out
  2047. for:
  2048.  
  2049. • Are you making any references to your global variables, or those of any units 
  2050.   that you’re using, such as thePort from QuickDraw? 
  2051.   These are accessed with A5-relative references with negative offsets.
  2052.  
  2053. • Are you making any inter-segment subroutine calls? 
  2054.   These are accessed with A5-relative references with positive offsets.
  2055.  
  2056. • Are you using any system calls (either traps or “glue” routines) which will 
  2057.   depend on A5 during their execution? 
  2058.   In this case, you need to be sure that you restore the caller’s A5 before 
  2059.   executing the call.
  2060.  
  2061. To be safest, patched traps should follow the same rules as interrupt handlers.
  2062.  
  2063.  
  2064. Note
  2065.  
  2066. In general, applications should not have to patch any traps, and risk compatibility
  2067. problems if they do! If you’d like help in removing your dependence on patching,
  2068. please contact Macintosh Developer Technical Support.
  2069.  
  2070.  
  2071.  
  2072.  
  2073. æKY 26
  2074. æC #26: Character vs. String Operations in QuickDraw
  2075.  
  2076. See also:      QuickDraw
  2077.                The Font Manager
  2078.  
  2079. Written by:    Bryan Stearns       April 26,1985
  2080. Updated:                           March 1, 1988
  2081. _______________________________________________________________________________
  2082.  
  2083. This technical note discusses the fact that the width of a string is not always the
  2084. same as the sum of the widths of all the characters in the string, due to roundoff
  2085. error.
  2086. _______________________________________________________________________________
  2087.  
  2088. When measuring and drawing strings of text characters, QuickDraw uses fixed-point
  2089. math. This results in a rounding error when drawing or measuring scaled characters
  2090. using successive calls to DrawString or DrawChar, as compared with single calls to
  2091. StringWidth or CharWidth. The problem occurs when using scaled fonts or fractional
  2092. width fonts.
  2093.  
  2094. The following program (MPW Tool) demonstrates this problem. It assumes that the current
  2095. System file does not contain a real version of the system font in 11 point, but must
  2096. instead scale the 12-point version.
  2097.  
  2098.    PROGRAM WidthTest;
  2099.  
  2100.    USES MemTypes, QuickDraw;
  2101.  
  2102.    VAR
  2103.        port          : GrafPort;
  2104.        i, width      : INTEGER;
  2105.        oldPt, newPt  : Point;
  2106.        testString    : Str255;
  2107.  
  2108.    BEGIN
  2109.        InitGraf(@thePort);
  2110.     
  2111.        {create a port to measure in}
  2112.        OpenPort(@port);
  2113.        TextSize(11);    {a size that is not usually available}
  2114.  
  2115.        {make a string to measure
  2116.        testString := 'Test String';
  2117.  
  2118.        {try DrawString}
  2119.        GetPen(oldPt);
  2120.        DrawString(testString);
  2121.        GetPen(newPt);
  2122.        WRITELN('DrawString = ', newPt.h - oldPt.h :1);
  2123.  
  2124.        {try StringWidth}
  2125.        WRITELN('StringWidth = ', StringWidth(testString) :1);
  2126.     
  2127.        {try DrawChar}
  2128.        GetPen(oldPt);
  2129.        FOR i := 1 TO Length(testString) DO
  2130.            DrawChar(testString[i]);
  2131.        GetPen(newPt);
  2132.        WRITELN('DrawChar = ', newPt.h - oldPt.h :1);
  2133.  
  2134.        {try CharWidth}
  2135.        width := 0;
  2136.        FOR i := 1 TO Length(testString) DO
  2137.            width := width + CharWidth(testString[i]);
  2138.        WRITELN('CharWidth = ', width :1);
  2139.    END.
  2140.  
  2141. This tool’s output will look like this:
  2142.  
  2143.        DrawString = 64
  2144.        StringWidth = 64
  2145.        DrawChar = 59
  2146.        CharWidth = 59
  2147.  
  2148. If the TextFont(11) line is removed, the result will be:
  2149.  
  2150.        DrawString = 70
  2151.        StringWidth = 70
  2152.        DrawChar = 70
  2153.        CharWidth = 70
  2154.  
  2155. Note that the measuring method always matches the drawing method; if you use successive
  2156. CharWidth calls to measure, followed by calls to DrawChar to draw, the actual width
  2157. of the string will match the calculated width.
  2158.  
  2159.  
  2160.  
  2161. æKY 27
  2162. æC #27:    MacDraw’s PICT File Format
  2163.  
  2164. Revised:                                                            August 1989
  2165. Written by:    Ginger Jernigan                                      August 1986
  2166.  
  2167. This Technical Note formerly described the PICT file format used by MacDraw(R) and
  2168. the picture comments the MacDraw used to communicate with the LaserWriter driver.
  2169. Changes since March 1988:  Updated the Claris address.
  2170. _______________________________________________________________________________
  2171.  
  2172. This Note formerly discussed the PICT file format used by MacDraw, which is now published
  2173. by Claris.  For information on MacDraw (its specific use of the PICT format) and
  2174. other Claris products, contact Claris at:
  2175.  
  2176.                 Claris Corporation
  2177.                 5201 Patrick Henry Drive
  2178.                 P.O. Box 58168
  2179.                 Santa Clara, CA 95052-8168
  2180.  
  2181.                 Technical Support
  2182.                 Telephone:  (408) 727-9054
  2183.                 AppleLink:  Claris.Tech
  2184.  
  2185.                 Customer Relations
  2186.                 Telephone:  (408) 727-8227
  2187.                 AppleLink:  Claris.CR
  2188.  
  2189. Inside Macintosh, Volume V–39, Color QuickDraw and Technical Note #21, QuickDraw’s
  2190. Internal Picture Format, now document the PICT file format.  Technical Note #91,
  2191. Optimizing for the LaserWriter—Picture Comments, now documents the picture comments
  2192. which the LaserWriter driver supports.
  2193.  
  2194.  
  2195. Further Reference:
  2196. _______________________________________________________________________________
  2197.   •  Inside Macintosh, Volume V–39, Color QuickDraw
  2198.   •  Technical Note #21, QuickDraw’s Internal Picture Format
  2199.   •  Technical Note #91, Optimizing for the LaserWriter—Picture Comments
  2200.  
  2201. MacDraw is a registered trademark of Claris Corporation.
  2202.  
  2203.  
  2204. æKY 28
  2205. æC #28: Finders and Foreign Drives
  2206.  
  2207. Written by:    Ginger Jernigan     May 7, 1984
  2208. Updated:                           March 1, 1988
  2209. _______________________________________________________________________________
  2210.  
  2211. This technical note describes the differences in the way the 1.1g, 4.1, 5.0 and newer
  2212. Finders communicate with foreign (non-Sony) disk drives.
  2213. _______________________________________________________________________________
  2214.  
  2215. Identifying Foreign Drives
  2216.  
  2217. Non-Sony disk drives can send an icon and a descriptive string to the Finder; this
  2218. icon is used on the desktop to represent the drive. The string is displayed in the
  2219. “Get Info” box for any object belonging to that disk. When the Finder notices a non-Sony
  2220. drive in the VCB queue, it will issue 1 or 2 control calls to the disk driver to get
  2221. the icon and string.
  2222.  
  2223. Finder 1.1g issues one control call to the driver with csCode = 20 and the driver
  2224. returns the icon ID in csParam. This method has problems because the icon ID is tied
  2225. to a particular system file. So, if the Finder switch-launches to a different floppy,
  2226. the foreign disk’s icon reverts to the Sony’s.
  2227.  
  2228. Finders 4.1 and newer issue a newer control call and, if that fails, they issue the
  2229. old Control call. The new call has csCode = 21, and the driver should return a pointer
  2230. in csParam. The pointer points to an 'ICN#' followed by a 1 to 31 byte Pascal string
  2231. containing the descriptor. This implies that the icon and the string must be part of
  2232. the disk driver’s code because only the existence of the driver indicates that the
  2233. disk is attached.
  2234.  
  2235. This has implications about the translation of the driver for overseas markets, but
  2236. the descriptor will usually be a trademarked name which isn’t translated. However,
  2237. the driver install program could be made responsible for inserting the translated
  2238. name into the driver.
  2239.  
  2240. Drivers should respond to both control calls if compatibility with both Finders is
  2241. desired.
  2242.  
  2243.  
  2244. Formatting Foreign Drives
  2245.  
  2246. When the user chooses the Erase Disk option in the Finder, a non-Sony driver needs to
  2247. know that this has happened so it can format the disk. Finder 4.1 and newer notify
  2248. the driver that the drive needs to be formatted and verified. They first issue a
  2249. Control call to the driver with the csCode = 6 to tell the disk driver to format the
  2250. drive. Then they issue a Control call with a csCode = 5 to tell the driver to verify
  2251. the drive.
  2252.  
  2253. Other Nifty Things to Know About
  2254.  
  2255. Finders 4.1 and newer also permit the user to drag any online disk to the trash can.
  2256. The Finder will clean up the disk state, issue an Eject call followed by an Unmount
  2257. call to the disk and then, an event loop later, reclaim all the memory. This means
  2258. any program/accessory used to mount volumes should reconcile its private data, menus,
  2259. etc. to the current state of the VCB queue. These Finders also notice if a volume
  2260. disappears and will clean up safely. But, because of a quirk in timing, a mount manager
  2261. cannot unmount one volume then mount another immediately; it must wait for the Finder
  2262. to loop around and clean up the first disk before it notices the second. (It should
  2263. have cleaned up old ones before it notices new ones, but it doesn’t.)
  2264.  
  2265. Finders 5.0 and newer allow you to drag the startup disk to the trash; Finder 4.1
  2266. just ignored you. Finders 5.0 and newer take the volume offline as if you had chosen
  2267. Eject.
  2268.  
  2269.  
  2270.  
  2271. æKY 29
  2272. æC #29: Resources Contained in the Desktop File
  2273.  
  2274. See also:       The Finder Interface
  2275.  
  2276. Written by:     Ginger Jernigan      May 7, 1985
  2277. Modified by:    Ginger Jernigan      December 2, 1985
  2278. Updated:                             March 1, 1988
  2279. _______________________________________________________________________________
  2280.  
  2281. This technical note describes the resources found in the Desktop file. 
  2282. NOTE: Don’t base anything critical on the format of the Desktop file. AppleShare
  2283. already uses another scheme; AppleShare volumes don’t have Desktop files. The format
  2284. of this file can, and probably will, change in the future.
  2285. _______________________________________________________________________________
  2286.  
  2287. The Desktop file contains almost the same resources for both the Macintosh File System
  2288. (MFS) and the Hierarchical File System (HFS). This technical note describes the resources
  2289. found in both. This information is for reading only. This means your application can
  2290. read it but it should NEVER write out information of its own, because the Finder, as
  2291. well as Macintosh Developer Technical Support, won’t like it.
  2292.  
  2293. The Desktop is a resource file which contains the folder information on an MFS volume,
  2294. the “Get Info” comments, the application bundles, ‘FREF’s and ‘ICN#’s, and information
  2295. concerning the whereabouts of applications on an HFS disk. Everything except the
  2296. comments are preloaded when the desktop is opened, making it easier for the Finder to
  2297. find things.
  2298.  
  2299. The contents of the Desktop file are described below. The resource types are the same
  2300. for both MFS and HFS volumes unless otherwise stated.
  2301.  
  2302. ‘APPL’:
  2303.    This resource type is used by the HFS to locate applications. This is used
  2304.    by the Finder to locate the right application when a document is opened. 
  2305.    Each application is identified by the creator, the directory number, and 
  2306.    the application name. This is used only by HFS.
  2307.  
  2308. ‘BNDL’:
  2309.    This resource type contains a copy of all of the bundles for all of the 
  2310.    applications that are either on the disk or are the creators of documents 
  2311.    that are on the disk. This is used by the Finder to find the right icons for 
  2312.    documents and applications. If you have a document whose creator the Finder 
  2313.    has not seen yet, it will not be in the Desktop file and the default 
  2314.    document icon will be used.
  2315.  
  2316. ‘FREF’:
  2317.    This contains a copy of all of the FREFs referenced in the bundles.
  2318.  
  2319. ‘FCMT’:
  2320.    This resource contains all of the “Get Info” comments for applications and 
  2321.    documents. On MFS volumes the ID is a hash of the object’s name. The hashing 
  2322.    algorithm is as follows:
  2323.  
  2324.     ; FUNCTION HashString(str: Str255): INTEGER;
  2325.  
  2326.     ; The ID for the FCMT returned in function result
  2327.  
  2328.     HashString
  2329.           MOVE.L   (SP)+,A0       ; get return address
  2330.           MOVE.L   (SP)+,A1       ; get string pointer
  2331.  
  2332.           MOVEQ    #0,D0          ; get string length
  2333.           MOVE.B   (A1)+,D0
  2334.  
  2335.           MOVEQ    #0,D2          ; accumulate ID here
  2336.     @2
  2337.           MOVE.B   (A1)+,D1       ; get next char
  2338.           EOR.B    D1,D2          ; XOR in
  2339.           ROR.W    #1,D2          ; stir things up
  2340.           BMI.S    @1             ; ID must be negative
  2341.           NEG.W    D2
  2342.     @1
  2343.           SUBQ.W   #1,D0          ; loop until done
  2344.           BNE.S    @2             ; until end of string
  2345.  
  2346.           MOVE     D2,(SP)        ; return the hashed code
  2347.           JMP      (A0)
  2348.  
  2349. For HFS volumes, the ID of the resource is randomly generated using UniqueID. To find
  2350. the ID of the comment for a file or directory call PBGetCatInfo. The comment ID for a
  2351. file is kept in ioFlXFndrInfo.fdComment. The comment ID for a directory is kept in
  2352. ioDrFndrInfo.frComment.
  2353.  
  2354. ‘FOBJ’:
  2355.    This resource type contains all of the folder information for an MFS volume. 
  2356.    The format of this resource is not available. This is only in an MFS 
  2357.    volume’s Desktop file.
  2358.  
  2359. ‘ICN#’:
  2360.    This resource type contains a copy of all of the ‘ICN#’ resources referenced 
  2361.    in the bundles and any others that may be present.
  2362.  
  2363. ‘STR ’:
  2364.    This is a string that identifies the version of the Finder, but it isn’t 
  2365.    always correct.
  2366.  
  2367. Creators:
  2368.    A resource with a type equal to the creator of each application with a 
  2369.    bundle is stored in the Desktop file for reference purposes only. The data 
  2370.    stored in these resources is for the Finder’s use only.
  2371.  
  2372. Be aware that if a resource is copied from an application resource file and there is
  2373. an ID conflict, the Finder will renumber the resource in the Desktop file.
  2374.  
  2375.  
  2376.  
  2377. æKY 30
  2378. æC #30: Font Height Tables
  2379.  
  2380. See Also:      The Font Manager
  2381.                The Resource Manager
  2382.     
  2383. Written by:    Gene Pope       April 25, 1986
  2384. Updated:                       March 1, 1988
  2385. _______________________________________________________________________________
  2386.  
  2387. This technical note describes how the Font Manager (except in 64K ROMs) calculates
  2388. height tables for fonts and how you can force recalculation.
  2389. _______________________________________________________________________________
  2390.  
  2391. In order to expedite the processing of fonts, the Font Manager (in anything newer
  2392. than the 64K ROMs) calculates a height table for all of the characters in a font when
  2393. the font is first loaded into memory. This height table is then appended to the end
  2394. of the font resource in memory; if some program (such as a font editor) subsequently
  2395. saves the font, the height table will be saved with the font and will not have to be
  2396. built again. This is fine for most cases except, for example, when the tables really
  2397. should be recalculated, such as in a font editor when the ascent and/or descent have
  2398. changed.
  2399.  
  2400. The following is an example of how to eliminate the height table from a font:
  2401.  
  2402.     IF (BitAnd(hStrike^^.fontTyp,$1)=1) THEN BEGIN {We have a height table}
  2403.        {Truncate the height table}
  2404.        SetHandleSize(Handle(hStrike),GetHandleSize(Handle(hStrike)-
  2405.                      (2*(hStrike^^.lastChar-hStrike^^.firstChar)+3)));
  2406.        {We no longer have a height table so set the flag to indicate that}
  2407.        hStrike^^.format := BitAnd(hStrike^^.fontType,$FFFFFFFE);
  2408.     END;
  2409.  
  2410. In MPW C:
  2411.  
  2412.     if ((**hStrike).fontType & 0x1 ==1) { /*We have a height table*/
  2413.        /*Truncate the height table*/
  2414.        SetHandleSize((Handle)hStrike,GetHandleSize((Handle)hStrike)-
  2415.                      (2*((**hStrike).lastChar-(**hStrike).firstChar)+3));
  2416.        /*We no longer have a height table so set the flag to indicate that*/
  2417.        (**hStrike).fontType = (**hStrike).fontType & 0xFFFFFFFE;
  2418.     }
  2419. here hStrike is a handle to the ‘FONT’ or ‘NFNT’ resource (handle to a FontRec).
  2420.  
  2421. NOTE: After the height table has been eliminated, the modified font should be saved
  2422. to disk (with ChangedResource and WriteResource) and purged from memory (using ReleaseResource).
  2423. This is an important step, because the Font Manager does not expect other code to go
  2424. behind its back removing height tables that it has calculated.
  2425.  
  2426.  
  2427.  
  2428.  
  2429. æKY 32
  2430. æC #32: Reserved Resource Types
  2431.  
  2432. See:           The Resource Manager
  2433.     
  2434. Written by:    Scott Knaster     May 13, 1985
  2435. Updated:                         March 1, 1988
  2436. _______________________________________________________________________________
  2437.  
  2438. Your applications and desk accessories can create their own resource types. To avoid
  2439. using type names which have been or will be used in the system, Apple has reserved
  2440. all resource type names which consist entirely of spaces ($20), lower-case letters
  2441. ($61 through $7A), and “international” characters (greater than $7F).
  2442.  
  2443. In addition Apple has reserved a number of resource types which contain upper-case
  2444. letters and the “#” character. For a list of these resource types, see The Resource
  2445. Manager Chapter of Inside Macintosh (starting with Volume V).
  2446.  
  2447.  
  2448.  
  2449.  
  2450. æKY 33
  2451. æC #33: ImageWriter II Paper Motion
  2452.  
  2453. Written by:    Ginger Jernigan      April 30, 1986
  2454. Updated:                            March 1, 1988
  2455. _______________________________________________________________________________
  2456.  
  2457. The purpose of this technical note is to answer the many questions asked about why
  2458. the paper moves the way it does on the ImageWriter II.
  2459. _______________________________________________________________________________
  2460.  
  2461. Many people have asked why the paper is rolled backward at the beginning of a Macintosh
  2462. print job on the ImageWriter II. First, note that this only happens with pin-feed
  2463. paper (i.e. not with hand-feed or the sheet-feeder) and only at the beginning of a
  2464. job.
  2465.  
  2466. It is not a bug, and it is not malicious programming. It is simply that users are
  2467. told in the manual to load pin-feed paper with the top edge at the pinch-rollers,
  2468. making it easy to rip off the printed page(s) without wrecking the paper that is
  2469. still in the printer or having to roll the paper up and down manually. At the end of
  2470. every job, the software makes sure that the paper is left in this position, leaving
  2471. the print-head roughly an inch from the edge. If something is to be printed higher
  2472. than that, the paper has to be rolled backwards.
  2473.  
  2474. As you are probably aware, the “printable rectangle” (rPage) reported to the application
  2475. by the print code begins 1/2 inch from the top edge, not one inch. The reason for
  2476. that is that we want a document to print exactly the same way whether you are printing
  2477. on the ImageWriter I or II. On the ImageWriter I, the paper starts with the print-head
  2478. 1/2 inch from the top edge, so the top of rPage is at that position for both printers.
  2479.  
  2480. There is no way to eliminate the reverse-feed action, because the user would have to
  2481. load the paper a different way AND the software would have to know that this was
  2482. done. 
  2483.  
  2484. Incidentally, in addition to the paper motion described above, there is also the
  2485. “burp.” This is a 1/8-inch motion back and forth to take up the slop in the printer’s
  2486. gear-train. It is needed on the old-model printer, and there is debate about whether
  2487. or not it’s needed on ALL ImageWriter IIs, or only some, or none. The burp has been
  2488. in and out of the ImageWriter II code in various releases; right now it’s in.
  2489.  
  2490.  
  2491.  
  2492. æKY 34
  2493. æC #34: User Items in Dialogs
  2494.  
  2495. See also:      The Dialog Manager
  2496.  
  2497. Written by:    Bryan Stearns      May 29,1985
  2498. Updated:                          March 1, 1988 
  2499. _______________________________________________________________________________
  2500.  
  2501. The Dialog Manager doesn’t go into detail about how to manage user items in dialogs.
  2502. This note describes the process.
  2503. _______________________________________________________________________________
  2504.  
  2505. To use Dialog Manager userItems, you must define a dialog, load the dialog and install
  2506. your userItem, and respond to events related to the userItem.
  2507.  
  2508.  
  2509. Defining a dialog box with a userItem
  2510.  
  2511. You should define the dialog box in your resource file as follows. Note that it is
  2512. defined as invisible; this is because we have to play with the userItem before it can
  2513. be drawn.
  2514.  
  2515.     resource 'DLOG' (1001) {    /* type/ID for box */
  2516.        {100,100,300,400},    /* rectangle for window */
  2517.        dBoxProc, invisible, noGoAway, 0x0,    /* note it is invisible */
  2518.        1001,
  2519.        "Test Dialog"
  2520.     };
  2521.  
  2522.     resource 'DITL' (1001) {    /* matching item list */
  2523.        {
  2524.           {160, 190, 180, 280},    /* rectangle for button */
  2525.              button { enabled, "OK" };    /* an OK button */
  2526.           {104, 144, 120, 296},    /* rectangle for item */
  2527.              userItem { enabled }    /* a user item! */
  2528.        }
  2529.     };
  2530.  
  2531.  
  2532. Loading and preparing to run the dialog box
  2533.  
  2534. Before we can actually show the dialog box to the user, we need two support routines.
  2535. The first procedure is called by the Dialog Manager whenever our userItem needs to be
  2536. drawn. It is installed (as shown below) after GetNewDialog but before ShowWindow. It
  2537. just draws the user item.
  2538.  
  2539. A simple draw procedure might be as follows:
  2540.  
  2541. PROCEDURE MyDraw(theDialog: DialogPtr; theItem: INTEGER);
  2542.  
  2543.        VAR
  2544.           iType : INTEGER;         {returned item type}
  2545.           iBox  : Rect;            {returned bounds rect}
  2546.           iHdl  : Handle;          {returned item handle}
  2547.  
  2548.        BEGIN
  2549.           GetDItem(theDialog,theItem,iType,iHdl,iBox); {get the box}
  2550.           FillRect(iBox,ltGray);   {fill with light gray}
  2551.           FrameRect(iBox);         {frame it}
  2552.        END; {MyDraw}
  2553.  
  2554. In MPW C:
  2555.  
  2556.    pascal void MyDraw(theDialog,theItem)
  2557.    DialogPtr    theDialog; 
  2558.    short int    theItem;
  2559.  
  2560.     {
  2561.        short int    iType;        /*returned item type*/
  2562.        Rect        iBox;               /*returned bounds rect*/
  2563.        Handle    iHdl;            /*returned item handle*/
  2564.  
  2565.        GetDItem(theDialog,theItem,&iType,&iHdl,&iBox); /*get the box*/
  2566.        FillRect(&iBox,qd.ltGray);       /*fill with light gray*/
  2567.        FrameRect(&iBox);             /*frame it*/
  2568.    } /*MyDraw*/
  2569.  
  2570. The other necessary procedure is a filter procedure, called by the Dialog Manager
  2571. whenever ModalDialog receives an event (note that this only applies when calling
  2572. ModalDialog; see below for modeless dialogs). The default filterproc checks to see if
  2573. the user has typed the return or enter keys; if so, it simulates pressing of the OK
  2574. button (or whatever item 1 is). To support userItems, the filterproc must handle
  2575. events for any userItems in the box, in addition to performing the default filterproc
  2576. tasks. This short filter proc does this; when the user clicks in the userItem, it
  2577. inverts it:
  2578.  
  2579.     FUNCTION MyFilter(theDialog: DialogPtr; VAR theEvent: EventRecord;
  2580.                       VAR itemHit: INTEGER): BOOLEAN;
  2581.  
  2582.        VAR
  2583.           key      : SignedByte;   {for enter/return}
  2584.           iType    : INTEGER;      {returned item type}
  2585.           iBox     : Rect;         {returned boundsrect}
  2586.           iHdl     : Handle;       {returned item handle}
  2587.           mouseLoc : Point;        {we'll play w/ mouse}
  2588.  
  2589.        BEGIN
  2590.           MyFilter := FALSE;       {assume not our event}
  2591.           CASE theEvent.what OF    {which event?}
  2592.              keyDown,autoKey: BEGIN {he hit a key}
  2593.                 key := theEvent.message; {get keycode}
  2594.                 IF key IN [13,3] THEN BEGIN {he hit CR or Enter}
  2595.                    MyFilter := TRUE; {we handled it}
  2596.                    itemHit := 1;   {he hit the 1st item}
  2597.                 END;               {he hit CR or enter}
  2598.              END;                  {keydown}
  2599.              mouseDown: BEGIN      {he clicked}
  2600.                 mouseLoc := theEvent.where; {get the mouse pos'n}
  2601.                 GlobalToLocal(mouseLoc); {convert to local}
  2602.                 GetDItem(theDialog,2,iType,iHdl,iBox); {get our box}
  2603.                 IF PtInRect(mouseLoc,iBox) THEN BEGIN {he hit our item}
  2604.                    InvertRect(iBox);
  2605.                    MyFilter := TRUE; {we handled it}
  2606.                    itemHit := 2;   {he hit the userItem}
  2607.                 END;               {if he hit our userItem}
  2608.              END;                  {mousedown}
  2609.           END;                     {event case}
  2610.        END;                        {MyFilter}
  2611.  
  2612. In MPW C:
  2613.  
  2614. pascal Boolean MyFilter(theDialog,theEvent,itemHit)
  2615. DialogPtr    theDialog; 
  2616. EventRecord    *theEvent;
  2617. short int    *itemHit;
  2618.  
  2619. {
  2620.     char        key;           /*for enter/return*/
  2621.     short int    iType;          /*returned item type*/
  2622.     Rect        iBox;             /*returned boundsrect*/
  2623.     Handle    iHdl;           /*returned item handle*/
  2624.     Point        mouseLoc;        /*we'll play w/ mouse*/
  2625.  
  2626.     SetPort(theDialog);
  2627.     switch (theEvent->what)        /*which event?*/
  2628.     {
  2629.         case keyDown:
  2630.         case autoKey:        /*he hit a key*/
  2631.             key = theEvent->message; /*get ascii code*/
  2632.             if ((key ==3) || (key == 13))
  2633.             {             /*he hit CR or Enter*/
  2634.                 *itemHit = 1;    /*he hit the 1st item*/
  2635.                 return(true);    /*we handled it*/
  2636.             } /*he hit CR or enter*/
  2637.                  break;        /* case keydown, case autoKey */
  2638.         case mouseDown:        /*he clicked*/
  2639.                 mouseLoc = theEvent->where;    /*get the mouse pos'n*/
  2640.                 GlobalToLocal(&mouseLoc);    /*convert to local*/
  2641.                 GetDItem(theDialog,2,&iType,&iHdl,&iBox); /*get our box*/
  2642.                 if (PtInRect(mouseLoc,&iBox))  
  2643.             {             /*he hit our item*/
  2644.                     InvertRect(&iBox);
  2645.                        *itemHit = 2;    /*he hit the userItem*/
  2646.                        return(true);    /*we handled it*/
  2647.                 } /*if he hit our userItem*/
  2648.                  break; /*case mouseDown */
  2649.     }                     /*event switch*/
  2650.     return(false);  /* we’re still here, so return false (we didn't                
  2651. handle the event) */
  2652. } /*MyFilter*/
  2653.  
  2654. Invoking the dialog box
  2655.  
  2656. When we need this dialog box, we load it into memory as follows:
  2657.  
  2658.     PROCEDURE DoOurDialog;
  2659.  
  2660.        VAR
  2661.           myDialog : DialogPtr;    {the dialog pointer}
  2662.           iType    : INTEGER;      {returned item type}
  2663.           iBox     : Rect;         {returned boundsRect}
  2664.           iHdl     : Handle;       {returned item Handle}
  2665.  
  2666.        BEGIN
  2667.           myDialog := GetNewDialog(1001,nil,POINTER(-1)); {get the box}
  2668.           GetDItem(myDialog,2,iType,iHdl,iBox); {2 is the item number}
  2669.           SetDItem(myDialog,2,iType,@myDraw,iBox); {install draw proc}
  2670.           ShowWindow(theDialog);   {make it visible}
  2671.           REPEAT
  2672.              ModalDialog(@MyFilter,whichItem); {let dialog manager run it}
  2673.           UNTIL whichItem = 1;     {until he hits ok.}
  2674.           DisposDialog(myDialog);  {throw it away}
  2675.        END;                        {DoOurDialog}
  2676.  
  2677. In MPW C:
  2678.  
  2679. void DoOurDialog()
  2680.  
  2681. {
  2682.  
  2683.     DialogPtr    myDialog;        /*the dialog pointer*/
  2684.     short int    iType;          /*returned item type*/
  2685.     short int    itemHit;          /*returned from ModalDialog*/
  2686.     Rect        iBox;             /*returned boundsRect*/
  2687.     Handle    iHdl;           /*returned item Handle*/
  2688.  
  2689.     myDialog = GetNewDialog(1001,nil,(WindowPtr)-1); /*get the box*/
  2690.     GetDItem(myDialog,2,&iType,&iHdl,&iBox); /*2 is the item number*/
  2691.     SetDItem(myDialog,2,iType,MyDraw,&iBox); /*install draw proc*/
  2692.     ShowWindow(myDialog);   /*make it visible*/
  2693.           
  2694.     while (itemHit != 1) ModalDialog(MyFilter, &itemHit);
  2695.     DisposDialog(myDialog);  /*throw it away*/
  2696. }                        /*DoOurDialog*/
  2697.  
  2698.  
  2699.  
  2700. Using userItems with modeless dialogs
  2701.  
  2702. If you’re using userItems in a modeless dialog box, the draw procedure will get called
  2703. when DialogSelect receives an updateEvent for the dialog box. When the user presses
  2704. the mouse button on your userItem, DialogSelect will return TRUE and itemHit equal to
  2705. the item number of the userItem. Your code can then handle this just like the mouseDown
  2706. case in the example above.
  2707.  
  2708.  
  2709.  
  2710. æKY 35
  2711. æC #35: DrawPicture Problem
  2712.  
  2713. Written by:   Mark Baumwell      June 19, 1986
  2714. Updated:                         March 1, 1988
  2715. _______________________________________________________________________________
  2716.  
  2717. This note formerly described a problem with DrawPicture that occurred only on 64K ROM
  2718. machines. Information specific to 64K ROM machines has been deleted from Macintosh
  2719. Technical Notes for reasons of clarity.
  2720.  
  2721.  
  2722. æKY 36
  2723. æC #36: Drive Queue Elements
  2724.  
  2725. See also:      The File Manager
  2726.                The Device Manager
  2727.  
  2728. Written by:    Bryan Stearns      June 12, 1985
  2729. Updated:                          March 1, 1988
  2730. _______________________________________________________________________________
  2731.  
  2732. This note expands on Inside Macintosh’s definition of the drive queue, which is given
  2733. in the File Manager chapter.
  2734. _______________________________________________________________________________
  2735.  
  2736. As shown in Inside Macintosh, a drive queue element has the following structure:
  2737.  
  2738.     DrvQEl = RECORD
  2739.         qLink:    QElemPtr; {next queue entry}
  2740.         qType:    INTEGER;  {queue type}
  2741.         dQDrive:  INTEGER;  {drive number}
  2742.         dQRefNum: INTEGER;  {driver reference number}
  2743.         dQFSID:   INTEGER;  {file-system identifier}
  2744.         dQDrvSz:  INTEGER;  {number of logical blocks on drive}
  2745.         dQDrvSz2: INTEGER;  {additional field to handle large drive size}
  2746.     END;
  2747.  
  2748. Note that dQDrvSz2 is only used if qType is 1. In this case, dQDrvSz2 contains the
  2749. high-order word of the size, and dQDrvSz contains the low-order word.
  2750.  
  2751. Inside Macintosh also mentions four bytes of flags that preced each drive queue entry.
  2752. How are these flags accessed? The flags begin 4 bytes before the address pointed to
  2753. by the DrvQElPtr. In assembly language, accessing this isn’t a problem:
  2754.  
  2755.           MOVE.L  -4(A0),D0   ;A0 = DrvQElPtr; get drive queue flags
  2756.  
  2757. If you’re using Pascal, it’s a little more complicated. You can get to the flags with
  2758. this routine:
  2759.  
  2760.     FUNCTION DriveFlags(aDQEPtr: DrvQElPtr): LONGINT;
  2761.  
  2762.        VAR
  2763.           flagsPtr : ^LONGINT; {we'll point at drive queue flags with this}
  2764.  
  2765.        BEGIN
  2766.           {subtract 4 from the DrvQElPtr, and get the LONGINT there}
  2767.           flagsPtr := POINTER(ORD4(aDQEPtr) - 4);
  2768.           DriveFlags := flagsPtr^;
  2769.        END;
  2770.  
  2771. From MPW C, you can use:
  2772.  
  2773.     long DriveFlags(aDQEPtr)
  2774.     DrvQElPtr    aDQEPtr;
  2775.  
  2776.     { /* DriveFlags */
  2777.         return(*((long *)aDQEPtr - 1));  /* coerce flagsPtr to a (long *)
  2778.                                             so that subtracting 1 from it
  2779.                                             will back us up 4 bytes */
  2780.     } /* DriveFlags */
  2781.  
  2782.  
  2783. Creating New Drives
  2784.  
  2785. To add a drive to the drive queue, assembly-language programmers can use the function
  2786. defined below. It takes two parameters: the driver reference number of the driver
  2787. which is to “own” this drive, and the size of the new drive in blocks. It returns the
  2788. drive number created. It is vital that you NOT hard-code the drive number; if the
  2789. user has installed other non-standard drives in the queue, the drive number you’re
  2790. expecting may already be taken. (Note that the example function below arbitrates to
  2791. find an unused drive number, taking care of this problem for you. Also, note that
  2792. this function doesn’t mount the new volume; your code should take care of that, calling
  2793. the Disk Initialization Package to reformat the volume if necessary).
  2794.  
  2795. AddMyDrive  PROC     EXPORT
  2796. ;---------------------------------------------------------------------------
  2797. ;FUNCTION AddMyDrive(drvSize: LONGINT; drvrRef: INTEGER): INTEGER;
  2798. ;---------------------------------------------------------------------------
  2799. ;Add a drive to the drive queue. Returns the new drive number, or a negative
  2800. ;error code (from trying to allocate the memory for the queue element).
  2801. ;---------------------------------------------------------------------------
  2802. DQESize     EQU      18          ;size of a drive queue element
  2803. ;We use a constant here because the number in SysEqu.a doesn't take into
  2804. ;account the flags LONGINT before the element, or the size word at the end.
  2805. ;---------------------------------------------------------------------------
  2806. StackFrame  RECORD   {link},DECR
  2807. result      DS.W     1           ;function result
  2808. params      EQU      *
  2809. drvSize     DS.L     1           ;drive size parameter
  2810. drvrRef     DS.W     1           ;drive refNum parameter
  2811. paramSize   EQU      params-*
  2812. return      DS.L     1           ;return address
  2813. link        DS.L     1           ;saved value of A6 from LINK
  2814. block       DS.B     ioQElSize   ;parameter block for call to MountVol
  2815. linkSize    EQU      *
  2816.             ENDR
  2817. ;---------------------------------------------------------------------------
  2818.          WITH     StackFrame     ;use the offsets declared above
  2819.  
  2820.          LINK     A6,#linkSize   ;create stack frame
  2821.             
  2822.          ;search existing drive queue for an unused number
  2823.             
  2824.          LEA      DrvQHdr,A0     ;get the drive queue header
  2825.          MOVEQ    #4,D0          ;start with drive number 4
  2826. CheckDrvNum
  2827.          MOVE.L   qHead(A0),A1   ;start with first drive
  2828. CheckDrv
  2829.          CMP.W    dqDrive(A1),D0 ;does this drive already have our number?
  2830.          BEQ.S    NextDrvNum     ;yep, bump the number and try again.
  2831.          CMP.L    A1,qTail(A0)   ;no, are we at the end of the queue?
  2832.          BEQ.S    GotDrvNum      ;if yes, our number's unique! Go use it.
  2833.          MOVE.L   qLink(A1),A1   ;point to next queue element
  2834.          BRA.S    CheckDrv       ;go check it.
  2835.          
  2836. NextDrvNum
  2837.          ;this drive number is taken, pick another
  2838.  
  2839.          ADDQ.W   #1,D0          ;bump to next possible drive number
  2840.          BRA.S    CheckDrvNum    ;try the new number
  2841.          
  2842. GotDrvNum
  2843.          ;we got a good number (in D0.W), set it aside
  2844.          
  2845.          MOVE.W   D0,result(A6)  ;return it to the user
  2846.          
  2847.          ;get room for the new DQE
  2848.          
  2849.          MOVEQ    #DQESize,D0    ;size of drive queue element, adjusted
  2850.          _NewPtr  sys            ;get memory for it
  2851.          BEQ.S    GotDQE         ;no error...continue
  2852.          MOVE.W   D0,result(A6)  ;couldn't get the memory! return error
  2853.          BRA.S    FinishUp       ;and exit
  2854.  
  2855. GotDQE
  2856.          ;fill out the DQE
  2857.          
  2858.          MOVE.L   #$80000,(A0)+  ;flags: non-ejectable; bump past flags
  2859.          
  2860.          MOVE.W   #1,qType(A0)   ;qType of 1 means we do use dQDrvSz2
  2861.          CLR.W    dQFSID(A0)     ;"local file system"
  2862.          MOVE.W   drvSize(A6),dQDrvSz2(A0)  ;high word of number of blocks
  2863.          MOVE.W   drvSize+2(A6),dQDrvSz(A0) ;low word of number of blocks
  2864.          
  2865.          ;call AddDrive
  2866.          
  2867.          MOVE.W   result(A6),D0  ;get the drive number back
  2868.          SWAP     D0             ;put it in the high word
  2869.          MOVE.W   drvrRef(A6),D0 ;move the driver refNum in the low word
  2870.          _AddDrive               ;add this drive to the drive queue
  2871.  
  2872. FinishUp
  2873.          UNLK     A6             ;get rid of stack frame
  2874.          MOVE.L   (SP)+,A0       ;get return address
  2875.          ADDQ     #paramSize,SP  ;get rid of parameters
  2876.          JMP      (A0)           ;back to caller
  2877. ;---------------------------------------------------------------------------
  2878.          ENDPROC
  2879.  
  2880.  
  2881.  
  2882.  
  2883. æKY 37
  2884. æC #37: Differentiating Between Logic Boards
  2885.  
  2886. See:           Technical Note #129 — SysEnvirons
  2887.  
  2888. Written by:    Mark Baumwell       June 19, 1986
  2889. Updated:                           March 1, 1988
  2890. _______________________________________________________________________________
  2891.  
  2892. Earlier versions of this note are obsoleted by existence of SysEnvirons, which is
  2893. documented in Technical Note #129.
  2894.  
  2895.  
  2896. æKY 38
  2897. æC #38: The ROM Debugger
  2898.  
  2899. Written by:    Louella Pizzuti     June 20, 1986
  2900. Updated:                           March 1, 1988
  2901. _______________________________________________________________________________
  2902.  
  2903. The debugger in ROM (not present on the Macintosh 128, Macintosh 512, or Macintosh
  2904. XL) recognizes the following commands:
  2905.  
  2906. PC [expr]   (program counter)
  2907.  
  2908. Typing PC on a line by itself displays the program counter. Typing PC 50000 sets the
  2909. program counter to $50000.
  2910.  
  2911. SM [address [number(s)]]   (set memory)
  2912.  
  2913. Typing SM on a line by itself displays the next 96 bytes of memory. Typing SM 50000
  2914. will display memory starting at $50000. Typing SM 50000 4849 2054 6865 7265 2120 will
  2915. set memory starting at $50000 to $4849… Subsequently hitting Return will increment
  2916. the display a screen at a time.
  2917.  
  2918. DM [address]   (display memory)
  2919.  
  2920. Typing DM on a line by itself displays the next 96 bytes of memory. Typing DM 50000
  2921. will display memory at $50000. Subsequently hitting Return will increment the display
  2922. a screen at a time.
  2923.  
  2924. SR [expr]   (status register)
  2925.  
  2926. Typing SR on a line by itself displays the status register. Typing SR 2004 sets the
  2927. status register to $2004.
  2928.  
  2929. TD   (total display)
  2930.  
  2931. Displays memory at the “magic” location $3FFC80, which contains the current values of
  2932. the registers. The registers are displayed in the following order: D0-D7, A0-A7, PC,
  2933. SR.
  2934.  
  2935. G [address]  (go)
  2936.  
  2937. Executes instructions starting at address. If G is typed on a line by itself, execution
  2938. begins at the address indicated by the program counter.
  2939.  
  2940. NOTE: If you want to exit to the shell, you just need to type: SM 0 A9F4,
  2941. then G 0
  2942.  
  2943. NOTE: If you crash into the debugger and the system hangs, try turning off
  2944. your modem.
  2945.  
  2946.  
  2947.  
  2948.  
  2949. æKY 39
  2950. æC #39: Segment Loader Patch
  2951.  
  2952. Written by:     Russ Daniels         August 1, 1985
  2953.                 Bryan Stearns
  2954. Modified by:    Jim Friedlander      November 15, 1986
  2955. Updated:                             March 1, 1988
  2956. _______________________________________________________________________________
  2957.  
  2958. This note formerly described a patch to the Segment Loader for 64K ROM machines.
  2959. Information specific to 64K ROM machines has been deleted from Macintosh Technical
  2960. Notes for reasons of clarity.
  2961.  
  2962.  
  2963.  
  2964. æKY 40
  2965. æC #40: Finder Flags
  2966.  
  2967. See also:       The File Manager
  2968.  
  2969. Written by:     Jim Friedlander      June 16, 1986
  2970. Modified by:    Jim Friedlander      March 2, 1987
  2971. Updated:                             March 1, 1988
  2972. _______________________________________________________________________________
  2973.  
  2974. This revision corrects the meanings of bits 6 and 7, which were interchanged in the
  2975. older version of this technical note. ResEdit uses these bits incorrectly in versions
  2976. older than 1.2.
  2977. _______________________________________________________________________________
  2978.  
  2979. The Finder keeps and uses a series of file information flags for each file. These
  2980. flags are located in the fdFlags field (a word at offset $28 into an HParamBlockRec)
  2981. of the ioFlFndrInfo record of a parameter block. They may change with newer versions
  2982. of the Finder. Finders 5.4 and newer assign the following meanings to the flags:
  2983.  
  2984.    Bit    Meaning
  2985.      0    Set if file/folder is on the desktop (Finder 5.0 and later)
  2986.      1    bFOwnAppl (used internally)
  2987.      2    reserved (currently unused)
  2988.      3    reserved (currently unused)
  2989.      4    bFNever (never SwitchLaunch) (not implemented)
  2990.      5    bFAlways (always SwitchLaunch) 
  2991.      6    Set if file is a shareable application
  2992.      7    reserved (used by System)
  2993.      8    Inited (seen by Finder)
  2994.      9    Changed (used internally by Finder)
  2995.     10    Busy (copied from File System busy bit)
  2996.     11    NoCopy (not used in 5.0 and later, formerly called BOZO)
  2997.     12    System (set if file is a system file)
  2998.     13    HasBundle
  2999.     14    Invisible
  3000.     15    Locked
  3001.  
  3002.  
  3003.  
  3004.  
  3005. æKY 41
  3006. æC #41: Drawing Into an Offscreen Bitmap
  3007.  
  3008. Revised by:    Jon Zap                                               April 1990
  3009. Written by:    Jim Friedlander & Ginger Jernigan                      July 1985
  3010.  
  3011. This Technical Note provides an example of creating an off-screen bitmap, drawing to it,
  3012. and then copying from it to the screen. Changes since March 1988:  Replaced the examples
  3013. with more complete and general examples and added some comments about when to use this
  3014. technique.
  3015. _______________________________________________________________________________
  3016.  
  3017. The following is an example of creating and drawing to an off-screen bitmap, then copying
  3018. from it to an on-screen window.  We supply this example in both MPW Pascal and C.
  3019.  
  3020. MPW Pascal
  3021.  
  3022. First, let’s look at a general purpose function to create an off-screen bitmap.  This 
  3023. function creates the GrafPort on the heap.  You could also create it on the stack and pass
  3024. the uninitialized structure to a function similar to this one.
  3025.  
  3026. FUNCTION CreateOffscreenBitMap(VAR newOffscreen:GrafPtr;
  3027.                                inBounds:Rect) : BOOLEAN;
  3028.  
  3029. VAR
  3030.   savePort  : GrafPtr;
  3031.   newPort   : GrafPtr;
  3032.  
  3033. BEGIN
  3034.   GetPort(savePort);    {need this to restore thePort after OpenPort changes it}
  3035.  
  3036.   newPort := GrafPtr(NewPtr(sizeof(GrafPort)));    {allocate the GrafPort}
  3037.   IF MemError <> noErr THEN BEGIN
  3038.     CreateOffscreenBitMap := false;                {failed to allocate it}
  3039.     EXIT(CreateOffscreenBitMap);
  3040.   END;
  3041.   {
  3042.   the OpenPort call does the following . . . 
  3043.     allocates space for visRgn (set to screenBits.bounds)
  3044.     and clipRgn (set wide open)
  3045.     sets portBits to screenBits
  3046.     sets portRect to screenBits.bounds
  3047.     etc. (see IM I-163,164)
  3048.     side effect: does a SetPort(offScreen)
  3049.   }
  3050.  
  3051.   OpenPort(newPort);
  3052.   {make bitmap exactly the size of the bounds that caller supplied}
  3053.   WITH newPort^ DO BEGIN {portRect, clipRgn, and visRgn are in newPort}
  3054.     portRect := inBounds;
  3055.     RectRgn(clipRgn, inBounds);        {avoid wide-open clipRgn, to be safe}
  3056.     RectRgn(visRgn, inBounds);         {in case inBounds is > screen bounds}
  3057.   END;
  3058.  
  3059.   WITH newPort^.portBits DO BEGIN {baseAddr, rowBytes and bounds are in newPort}
  3060.     bounds := inBounds;
  3061.     {rowBytes is size of row  It must be rounded up to even number of bytes}
  3062.     rowBytes := ((inBounds.right - inBounds.left + 15) DIV 16) * 2;
  3063.  
  3064.     {number of bytes in BitMap is rowBytes * number of rows}
  3065.     {see note at end of Technical Note about using _NewHandle }
  3066.     { rather than _NewPtr}
  3067.     baseAddr := NewPtr(rowBytes * LONGINT(inBounds.bottom - inBounds.top));
  3068.   END;
  3069.   IF MemError <> noErr THEN BEGIN    {see if we had enough room for the bits}
  3070.     SetPort(savePort);
  3071.     ClosePort(newPort);              { dump the visRgn and clipRgn }
  3072.     DisposPtr(Ptr(newPort));         { dump the GrafPort}
  3073.     CreateOffscreenBitMap := false;
  3074.   END
  3075.   ELSE BEGIN
  3076.     { since the bits are just memory, let's erase them before we start }
  3077.     EraseRect(inBounds);            {OpenPort did a SetPort(newPort)}
  3078.     newOffscreen := newPort;
  3079.     SetPort(savePort);
  3080.     CreateOffscreenBitMap := true;
  3081.   END;
  3082. END;
  3083.  
  3084. Here is the procedure to get rid of an off-screen bitmap created by the previous function:
  3085.  
  3086. PROCEDURE DestroyOffscreenBitMap(oldOffscreen : GrafPtr);
  3087. BEGIN
  3088.   ClosePort(oldOffscreen);                       { dump the visRgn and clipRgn }
  3089.   DisposPtr(oldOffscreen^.portBits.baseAddr);    { dump the bits }
  3090.   DisposPtr(Ptr(oldOffscreen));                  { dump the port }
  3091. END;
  3092.  
  3093. Now that you know how to create and destroy an off-screen bitmap, let’s go through the motions
  3094. of using one.  First, let’s define a few things to make the _NewWindow call a little clearer.
  3095.  
  3096. CONST
  3097.   kIsVisible   = true;
  3098.   kNoGoAway    = false;
  3099.   kMakeFrontWindow = -1;
  3100.   myString     = 'The EYE';  {string to display}
  3101.  
  3102. Here’s the body of the test code:
  3103.  
  3104. VAR
  3105.   offscreen : GrafPtr;    {our off-screen bitmap}
  3106.   ovalRect  : Rect;       {used for example drawing}
  3107.   myWBounds : Rect;       {for creating window}
  3108.   OSRect    : Rect;       {portRect and bounds for off-screen bitmap}
  3109.   myWindow  : WindowPtr;
  3110.  
  3111. BEGIN
  3112.   InitToolbox;                       {exercise left to the reader}
  3113.  
  3114.   myWBounds := screenBits.bounds;    { size of main screen }
  3115.   InsetRect(myWBounds, 50,50);       { make it fit better }
  3116.   myWindow := NewWindow(NIL, myWBounds, 'Test Window', kIsVisible,
  3117.                         noGrowDocProc, WindowPtr(kMakeFrontWindow),
  3118.                         kNoGoAway, 0);
  3119.  
  3120.   IF NOT CreateOffscreenBitMap(offscreen,myWindow^.portRect) THEN BEGIN 
  3121.     SysBeep(1);
  3122.     ExitToShell;
  3123.   END;
  3124.  
  3125.   { Example drawing to our off-screen bitmap }
  3126.   SetPort(offscreen);
  3127.   OSRect := offscreen^.portRect;    { offscreen bitmap's local coordinate rect }
  3128.   ovalRect := OSRect;
  3129.   FillOval(ovalRect, black);
  3130.   InsetRect(ovalRect, 1, 20);
  3131.   FillOval(ovalRect, white);
  3132.   InsetRect(ovalRect, 40, 1);
  3133.   FillOval(ovalRect, black);
  3134.   WITH ovalRect DO
  3135.     MoveTo((left+right-StringWidth(myString)) DIV 2, (top+bottom-12) DIV 2);
  3136.   TextMode(srcXor);
  3137.   DrawString(myString);
  3138.  
  3139.   { copy from the off-screen bitmap to the on-screen window.  Note that in this
  3140.   case the source and destination rects are the same size and both cover the
  3141.   entire area.  These rects are allowed to be portions of the source and/or
  3142.   destination and do not have to be the same size.  If they are not the same
  3143.   size then _CopyBits scales the image accordingly
  3144.   }
  3145.   SetPort(myWindow);
  3146.   CopyBits(offscreen^.portBits, myWindow^.portBits,
  3147.            offscreen^.portRect, myWindow^.portRect, srcCopy, NIL);
  3148.  
  3149.   DestroyOffscreenBitMap(offscreen);    {remove the evidence}
  3150.  
  3151.   WHILE NOT Button DO;                  {give user a chance to see the results}
  3152. END.
  3153.  
  3154. MPW C
  3155.  
  3156. First, let’s look at a general purpose function to create an off-screen bitmap.  This function
  3157. creates the GrafPort on the heap.  You could also create it on the stack and pass the
  3158. uninitialized structure to a function similar to this one.
  3159.  
  3160. Boolean CreateOffscreenBitMap(GrafPtr *newOffscreen, Rect *inBounds)
  3161. {
  3162.   GrafPtr savePort;
  3163.   GrafPtr newPort;
  3164.  
  3165.   GetPort(&savePort);    /* need this to restore thePort after OpenPort */
  3166.  
  3167.   newPort = (GrafPtr) NewPtr(sizeof(GrafPort));    /* allocate the grafPort */
  3168.   if (MemError() != noErr)
  3169.     return false;                 /* failed to allocate the off-screen port */
  3170.  
  3171.   /*
  3172.   the call to OpenPort does the following . . . 
  3173.     allocates space for visRgn (set to screenBits.bounds)
  3174.     and clipRgn (set wide open)
  3175.     sets portBits to screenBits
  3176.     sets portRect to screenBits.bounds
  3177.     etc. (see IM I-163,164)
  3178.     side effect: does a SetPort(&offScreen)
  3179.   */
  3180.   OpenPort(newPort);
  3181.   /* make bitmap the size of the bounds that caller supplied */
  3182.   newPort->portRect = *inBounds;
  3183.   newPort->portBits.bounds = *inBounds;
  3184.   RectRgn(newPort->clipRgn, inBounds);/* avoid wide-open clipRgn, to be safe  */
  3185.   RectRgn(newPort->visRgn, inBounds); /* in case newBounds is > screen bounds */
  3186.  
  3187.   /*rowBytes is size of row, it must be rounded up to an even number of bytes*/
  3188.   newPort->portBits.rowBytes =
  3189.            ((inBounds->right - inBounds->left + 15) >> 4) << 1;
  3190.  
  3191.   /* number of bytes in BitMap is rowBytes * number of rows */
  3192.   /* see notes at end of Technical Note about using _NewHandle
  3193.      rather than _NewPtr */
  3194.   newPort->portBits.baseAddr =
  3195.            NewPtr(newPort->portBits.rowBytes * (long)
  3196.            (inBounds->bottom - inBounds->top));
  3197.   if (MemError()!=noErr) { /* check to see if we had enough room for the bits */
  3198.     SetPort(savePort);
  3199.     ClosePort(newPort);      /* dump the visRgn and clipRgn */
  3200.     DisposPtr((Ptr)newPort); /* dump the GrafPort */
  3201.     return false;            /* tell caller we failed */
  3202.     }
  3203.   /* since the bits are just memory, let's clear them before we start */
  3204.   EraseRect(inBounds);     /* OpenPort did a SetPort(newPort) so we are ok */
  3205.   *newOffscreen = newPort;
  3206.   SetPort(savePort);
  3207.   return true;               /* tell caller we succeeded! */
  3208. }
  3209.  
  3210. Here is the function to get rid of an off-screen bitmap created by the previous function:
  3211.  
  3212. void DestroyOffscreenBitMap(GrafPtr oldOffscreen)
  3213. {
  3214.   ClosePort(oldOffscreen);                     /* dump the visRgn and clipRgn */
  3215.   DisposPtr(oldOffscreen->portBits.baseAddr);  /* dump the bits */
  3216.   DisposPtr((Ptr)oldOffscreen);                /* dump the port */
  3217. }
  3218.  
  3219. Now that you know how to create and destroy an off-screen bitmap, let’s go through the
  3220. motions of using one.  First, let’s define a few things to make the _NewWindow call a 
  3221. little clearer.
  3222.  
  3223. #define kIsVisible true
  3224. #define kNoGoAway false
  3225. #define kNoWindowStorage 0L
  3226. #define kFrontWindow ((WindowPtr) -1L)
  3227.  
  3228. Here’s the body of the test code:
  3229.  
  3230. main()
  3231. {
  3232.   char* myString = "\pThe EYE";  /* string to display */
  3233.  
  3234.   GrafPtr   offscreen;           /* our off-screen bitmap */
  3235.   Rect      ovalRect;            /* used for example drawing */
  3236.   Rect      myWBounds;           /* for creating window */
  3237.   Rect      OSRect;              /* portRect and bounds for off-screen bitmap*/
  3238.   WindowPtr myWindow;
  3239.  
  3240.   InitToolbox();                 /* exercise for the reader */
  3241.   myWBounds = qd.screenBits.bounds;  /* size of main screen */
  3242.   InsetRect(&myWBounds, 50,50);  /* make it fit better */
  3243.   myWindow = NewWindow(kNoWindowStorage, &myWBounds, "\pTest Window",
  3244.                        kIsVisible, noGrowDocProc, kFrontWindow, kNoGoAway, 0);
  3245.   if (!CreateOffscreenBitMap(&offscreen, &myWindow->portRect)) {
  3246.     SysBeep(1);
  3247.     ExitToShell();
  3248.     }
  3249.   /* Example drawing to our off-screen bitmap*/
  3250.   SetPort(offscreen);
  3251.   OSRect = offscreen->portRect;  /* offscreen bitmap's local coordinate rect */
  3252.   ovalRect = OSRect;
  3253.   FillOval(&ovalRect, qd.black);
  3254.   InsetRect(&ovalRect, 1, 20);
  3255.   FillOval(&ovalRect, qd.white);
  3256.   InsetRect(&ovalRect, 40, 1);
  3257.   FillOval(&ovalRect, qd.black);
  3258.   MoveTo((ovalRect.left + ovalRect.right - StringWidth(myString)) >> 1,
  3259.          (ovalRect.top + ovalRect.bottom - 12) >> 1);
  3260.   TextMode(srcXor);
  3261.   DrawString(myString);
  3262.  
  3263.   /* copy from the off-screen bitmap to the on-screen window.  Note that
  3264.      in this case the source and destination rects are the same size and
  3265.      both cover the entire area.  These rects are allowed to be portions
  3266.      of the source and/or destination and do not have to be the same size.
  3267.      If they are not the same size then _CopyBits scales the image accordingly.
  3268.   */
  3269.   SetPort(myWindow);
  3270.   CopyBits(&offscreen->portBits, &(*myWindow).portBits,
  3271.            &offscreen->portRect, &(*myWindow).portRect, srcCopy, 0L);
  3272.  
  3273.   DestroyOffscreenBitMap(offscreen);    /* dump the off-screen bitmap */
  3274.   while (!Button());     /* give user a chance to see our work of art */
  3275. }
  3276.  
  3277.  
  3278. Comments
  3279.  
  3280. In the example code, the bits of the BitMap structure, which are pointed to by the baseAddr
  3281. field, are allocated by a _NewPtr call.  If your off-screen bitmap is close to the size of 
  3282. the screen, then the amount of memory needed for the bits can be quite large (on the order 
  3283. of 20K for the Macintosh SE or 128K for a large screen).  This is quite a lot of memory to
  3284. lock down in your heap and it can easily lead to fragmentation if you intend to keep the 
  3285. off-screen bitmap around for any length of time.  One alternative that lessens this problem
  3286. is to get the bits via _NewHandle so the Memory Manager can move them when necessary.  To 
  3287. implement this approach, you need to keep the handle separate from the GrafPort (for example,
  3288. in a structure that combines a GrafPort and a Handle).  When you want to use the off-screen 
  3289. bitmap you would then lock the handle and put the dereferenced handle into the baseAddr field.
  3290. When you are not using the off-screen bitmap you can then unlock it.
  3291.  
  3292. This example does not demonstrate one of the more typical uses of off-screen bitmaps, which
  3293. is to preserve the contents of windows so that after another window or a dialog box obscures
  3294. part of your window you can quickly handle the update event without recreating all of the
  3295. intermediate drawing commands.
  3296.  
  3297. In some cases it can be just as fast and convenient to simply define a picture
  3298. (PICT) and then draw it into your window when necessary.  There are cases, however, such as
  3299. text rotation, where it is advantageous to do the drawing off the screen, manipulate the bit
  3300. image, and then copy the result to the visible window (thus avoiding the dangers inherent in
  3301. writing directly to the screen).  In addition, this technique reduces flicker, because all of
  3302. the drawing done off the screen appears on the screen at once.
  3303.  
  3304. It is also important to realize that, if you plan on using the pre-Color QuickDraw eight-color
  3305. model, an off-screen bitmap loses any color information and you do not see your colors on a
  3306. system that is capable of displaying them.  In this case you should either use a PICT to save
  3307. the drawing information or check for the presence of Color QuickDraw and, when it is present,
  3308. use a PixMap instead of a BitMap and the color toolbox calls (Inside Macintosh, Volume V)
  3309. instead of the standard QuickDraw calls (Inside Macintosh, Volume I).
  3310.  
  3311. You may also want to refer to the OffScreen library (DTS Sample Code #15) which provides both
  3312. high- and low-level off-screen bitmap support for the 128K and later ROMs.  The OffSample
  3313. application (DTS Sample Code #16) demonstrates the use of this library.
  3314.  
  3315.  
  3316. Further Reference:
  3317. _______________________________________________________________________________
  3318.   •  Inside Macintosh, Volumes I & IV, QuickDraw
  3319.   •  Inside Macintosh, Volume V, Color QuickDraw
  3320.   •  Technical Note #120, Drawing Into an Off-Screen Pixel Map
  3321.   •  MacDTS Sample Code #15, OffScreen & #16, OffSample
  3322.  
  3323.  
  3324.  
  3325. æKY 42
  3326. æC #42: Pascal Routines Passed by Pointer
  3327.  
  3328. See also:      Macintosh Memory Management: An Introduction
  3329.  
  3330. Written by:    Scott Knaster       July 22, 1985
  3331. Updated:                           March 1, 1988
  3332. _______________________________________________________________________________
  3333.  
  3334. Routines passed by pointer are used in many places in conjunction with Macintosh
  3335. system routines. For example, filter procedures for modal dialogs are passed by pointer,
  3336. as are controls’ action procedures (when calling TrackControl), and I/O completion
  3337. routines.
  3338.  
  3339. If you're using MPW Pascal, the syntax is usually
  3340.  
  3341.     partCode := TrackControl(theControl, startPt, @MyProc)
  3342.  
  3343. where MyProc is the procedure passed by pointer (using the @ symbol).
  3344.  
  3345. Because of the way that MPW Pascal (and some other compilers) construct stack frames,
  3346. any procedure or function passed by pointer must not have its declaration nested
  3347. within another procedure or function. If its declaration is nested, the program will
  3348. crash, probably with an illegal instruction error. The following example demonstrates
  3349. this:
  3350.  
  3351.     PROGRAM CertainDeath;
  3352.  
  3353.        PROCEDURE CallDialog;
  3354.  
  3355.           VAR
  3356.              x : INTEGER;
  3357.  
  3358.           FUNCTION MyFilter(theDialog: DialogPtr; VAR theEvent: EventRecord;
  3359.                             VAR itemHit: INTEGER): Boolean;
  3360.           {note that MyFilter's declaration is nested within CallDialog}
  3361.  
  3362.           BEGIN {MyFilter}
  3363.             {body of MyFilter}
  3364.           END; {MyFilter}
  3365.  
  3366.        BEGIN {CallDialog}
  3367.           ModalDialog(@MyFilter,itemHit) {<------------ will crash here}
  3368.        END; {CallDialog}
  3369.  
  3370.     BEGIN {main program}
  3371.           CallDialog;
  3372.     END.
  3373.  
  3374.  
  3375.  
  3376.  
  3377. æKY 43
  3378. æC #43: Calling LoadSeg
  3379.  
  3380. See also:       The Segment Loader
  3381.  
  3382. Written by:     Gene Pope        October 15, 1985
  3383. Updated:                         March 1, 1988
  3384. _______________________________________________________________________________
  3385.  
  3386. Earlier versions of this note described a way to call the LoadSeg trap, which is used
  3387. internally by the Segment Loader. We no longer recommend calling LoadSeg directly.
  3388.  
  3389.  
  3390. æKY 44
  3391. æC #44: HFS Compatibility
  3392.  
  3393. See also:       The File Manager
  3394.  
  3395. Written by:     Jim Friedlander      October 9, 1985
  3396. Modified by:    Scott Knaster        December 5, 1985
  3397.                 Jim Friedlander
  3398. Updated:                             March 1, 1988
  3399. _______________________________________________________________________________
  3400.  
  3401. This technical note tells you how to make sure that your applications run under the
  3402. Hierarchical File System (HFS).
  3403. _______________________________________________________________________________
  3404.  
  3405. The Hierarchical File System (HFS) provides fast, efficient management of larger
  3406. volumes than the original Macintosh File System (MFS). Since HFS is hierarchical, HFS
  3407. folders have a meaning different from MFS folders. In MFS, a folder has only graphical
  3408. significance—it is only used by the Finder as a means of visually grouping files. The
  3409. MFS directory structure is actually flat (all files are at the ‘root’ level). Under
  3410. HFS, a folder is a directory that can contain files and other directories.
  3411.  
  3412. A folder is accessed by use of a WDRefNum (Working Directory reference number). Calls
  3413. that return a vRefNum when running under MFS may return a WDRefNum when running under
  3414. HFS. You may use a WDRefNum wherever a vRefNum may be used. 
  3415.  
  3416. In order to provide for compatibility with software written for MFS, the HFS calls
  3417. that open files search both the default directory and the directory that contains the
  3418. System and the Finder (HFS marks this last directory so it always knows where to look
  3419. for the System and the Finder).
  3420.  
  3421. Your goal should be to write programs that are file system independent. Your programs
  3422. should not only be able to access files on other volumes, but also files that are in
  3423. other directories. Accomplishing this is not difficult—most applications that were
  3424. written for MFS work correctly under HFS. If you find that your current applications
  3425. do not run correctly under HFS, you should check to see if you are doing any of the
  3426. following five things: 
  3427.  
  3428.  
  3429. Are you using Standard File?
  3430.  
  3431. This is very important to ensure that your application will run correctly under HFS.
  3432. HFS uses an extended Standard File, which allows the user to select from files in
  3433. different directories. This increased functionality was implemented without changing
  3434. Standard File’s external specification—the only difference is that SFReply.vRefNum
  3435. can now be a WDRefNum. Please note that using Standard File’s dialog hook and filter
  3436. procs or adding controls of your own will not cause compatibility problems with HFS.
  3437.  
  3438. Existing applications that use Standard File properly run without modification under
  3439. HFS. Applications that take the SFReply.vRefNum and convert that to a volume name,
  3440. then append it to SFReply.fName (as in #2 below) do not function correctly under
  3441. HFS—the user can only open files in the root directory. If you call Open with SFReply.vRefNum
  3442. and SFReply.fName, everything will work correctly. Remember, SFReply.vRefNum may be a
  3443. WDRefNum . Using Standard File will virtually guarantee that your application will be
  3444. compatible with MFS, HFS, and future file systems.
  3445.  
  3446.  
  3447. Are you concatenating volume names to file names, i.e. using file names of the form
  3448. VOLUME:fileName?
  3449.  
  3450. Applications that do this do not work correctly under HFS (in fact, they do not even
  3451. run correctly under MFS). Instead of this, use a vRefNum to access a volume or a
  3452. directory. Fully qualified pathnames (such as volume:folder1:folder2:filename) work
  3453. correctly, but we don’t recommend that you use them. Please don’t ever make a user
  3454. type in a full pathname! 
  3455.  
  3456.  
  3457. Are you searching directories for files using a loop such as
  3458.         FOR index:= 1 to ioVNmFls DO ...    
  3459. where ioVNmFls was returned from a PBGetVinfo call?
  3460.  
  3461. This technique should not be used. Instead, use repeated calls to PBGetFInfo using
  3462. ioFDirIndex until fnfErr is returned. Indexed calls to PBGetFInfo will return files
  3463. in the directory specified by the vRefNum that you put in the parameter block.
  3464.  
  3465. Are you assuming that a vRefNum will actually refer to a volume?
  3466.  
  3467. A vRefNum can now be a WDRefNum. A WDRefNum indicates which working directory 
  3468. (folder) a file is in, not which volume the file is on. Don’t think of a vRefNum as a
  3469. way to access a volume, but rather as a means of telling the file system where to
  3470. find a file. 
  3471.  
  3472.  
  3473. Are you walking through the VCB queue?
  3474.  
  3475. You should let us do the walking for you. Using indexed calls to PBGetVInfo will
  3476. allow you to get information about any mounted volume. You shouldn’t walk through the
  3477. VCB queue because it changed for HFS and might change in the future. The routines
  3478. that we supply will correctly access information in the VCB queue. 
  3479.  
  3480.  
  3481. Are you using the file system’s “IMMED” bit? (assembly language only)
  3482.  
  3483. Inside Macintosh describes bit 9 of the trap word as the immediate bit. In fact,
  3484. setting this bit under MFS did not work as documented; it did not have the desired
  3485. effect of bypassing the file I/O queue. Under HFS, this bit is used; it distinguishes
  3486. HFS varieties of calls from MFS varieties. For example, the PBOpen call has this bit
  3487. clear; PBHOpen has it set. Therefore, you must be sure that your file system calls do
  3488. not use this bit as the immediate bit.
  3489.  
  3490.  
  3491.  
  3492. æKY 45
  3493. æC #45: Inside Macintosh Quick Reference
  3494.  
  3495. Compiled by:    Jim Friedlander      August 2, 1985
  3496. Updated:                             March 1, 1988
  3497. _______________________________________________________________________________
  3498.  
  3499. This note formerly listed the traps from Inside Macintosh Volumes I-III. Better references
  3500. are now available elsewhere.
  3501.  
  3502.  
  3503. æKY 46
  3504. æC #46: Separate Resource Files
  3505.  
  3506. See also:      The Resource Manager
  3507.  
  3508. Written by:    Bryan Stearns      October 16, 1985
  3509. Updated:                          March 1, 1988
  3510. _______________________________________________________________________________
  3511.  
  3512. During application development, you use a resource compiler (RMaker or Rez) to convert
  3513. a resource definition file into an executable application. You rarely change anything
  3514. but your CODE resources during development, and the resource compiler spends a lot of
  3515. time compiling other resources which have not changed since they were originally
  3516. created.
  3517.  
  3518. To save time, some developers have adopted the technique of storing all of these
  3519. “static” resources in a separate resource file. This file should be placed on the
  3520. same volume as your application; when your application starts up, use OpenResFile to
  3521. open the separate file. This will cause the resource map for the separate file to be
  3522. searched before the normal application resource file's map (which now contains mostly
  3523. CODE resources, along with any brand-new resources still being tested).
  3524.  
  3525. This will have little or no effect on the rest of your program. Any time that a resource
  3526. is needed, both resource files will be searched automatically so you DON'T need to
  3527. change each GetResource call. (Actually, having the extra resource file open has a
  3528. minor impact on memory management, and uses one more file-control block; unless you're
  3529. using a lot of open files at once, or are running at the limits of available memory
  3530. without segmentation, this shouldn't affect you.)
  3531.  
  3532. Once your application is close to being finished, you can use ResEdit to move all the
  3533. resources back into the main application file, and remove the extra OpenResFile at
  3534. the beginning of your application. You should do this for any major release (alpha,
  3535. beta, and any other ‘heavy-testing’ releases). Other minor modifications (such as
  3536. fine-tuning dialog box item positions) may also be done with ResEdit at this time.
  3537.  
  3538. The only catch is that you must be careful if your application adds resources to its
  3539. own resource file. Most applications do not do this (it's not really a great idea,
  3540. and causes problems with file servers).
  3541.  
  3542.  
  3543.  
  3544.  
  3545. æKY 47
  3546. æC #47: Customizing Standard File
  3547.  
  3548. See also:      The Standard File Package
  3549.  
  3550. Written by:    Jim Friedlander      October 11, 1985
  3551. Updated:                            March 1, 1988
  3552. _______________________________________________________________________________
  3553.  
  3554. This note contains an example program that demonstrates how SFPGetFile can be customized
  3555. using the dialog hook and file filter functions.
  3556. _______________________________________________________________________________
  3557.  
  3558. SFPGetFile’s dialog hook function and file filter function enable you to customize
  3559. SFPGetFile’s behavior to fit the needs of your application. This technical note consists
  3560. primarily of a short example program that            
  3561.    1) changes the title of the Open button to ‘MyOpen’,
  3562.    2) adds two radio buttons so that the user can choose to display either text 
  3563.       files or text files and applications.
  3564.    3) adds a quit button to the SFPGetFile dialog,        
  3565.  
  3566. All this is done in a way so as to provide compatibility with the Macintosh File
  3567. System (MFS), the Hierarchical File System (HFS) and (hopefully) future systems. If
  3568. you have any questions as you read, the complete source of the demo program and the
  3569. resource compiler input file is provided at the end of this technical note.
  3570.  
  3571. Basically, we need to do three things: add our extra controls to the resource compiler
  3572. input file, write a dialog hook function, and write a file filter function.
  3573.  
  3574.  
  3575. Modifying the Resource Compiler Input File
  3576.  
  3577. First we need to define a dialog in our resource file. It will be DLOG #128:
  3578.  
  3579.     CONST myDLOGID = 128;
  3580.  
  3581. and it’s Rez description is:
  3582.  
  3583.     resource 'DLOG' (128, purgeable) {
  3584.         {0, 0, 200, 349},
  3585.         dBoxProc, invisible, noGoAway,
  3586.         0x0,
  3587.         128,
  3588.         "MyGF"
  3589.     };
  3590.  
  3591. The above coordinates (0 0 200 349) are from the standard Standard File dialog. If
  3592. you need to change the size of the dialog to accommodate new controls, change these
  3593. coordinates. Next we need to add a DITL in our resource file that is the same as the
  3594. standard HFS DITL #–4000 except for one item. We need to change the left coordinate
  3595. of UserItem #4, or part of the dialog will be hidden if we’re running under MFS:
  3596.  
  3597.  
  3598.     /* [4] */
  3599.     /* left coordinate changed from 232 to 252 so program will     
  3600.        work on MFS */
  3601.     {39, 252, 59, 347},
  3602.     UserItem {
  3603.          disabled
  3604.     };
  3605.  
  3606. None of the other items of the DITL should be changed, so that your program will
  3607. remain as compatible as possible with different versions of Standard File. Finally,
  3608. we need to add three items to this DITL, two radio buttons and one button (to serve
  3609. as a quit button) 
  3610.  
  3611.     /* [11] textButton */
  3612.     {1, 14, 20, 142},
  3613.     RadioButton {
  3614.         enabled,
  3615.         "Text files only"
  3616.     };
  3617.     /* [12] textAppButton */
  3618.     {19, 14, 38, 176},
  3619.     RadioButton {
  3620.         enabled,
  3621.         "Text and applications"
  3622.     };
  3623.     /* [13] quitButton */
  3624.     {6, 256, 24, 336},
  3625.     Button {
  3626.         enabled,
  3627.         "Quit"
  3628.     }
  3629.  
  3630. Because we’ve added three items, we need also need to change the item count for the
  3631. DITL from 10 to 13. We also include the following in our resource file:
  3632.  
  3633. resource 'STR#' (256) {
  3634.     {/* array StringArray: 1 elements */
  3635.         /* [1] */
  3636.         "MyOpen"
  3637.     }
  3638. };
  3639.  
  3640.  
  3641. That’s all there is to modify in the resource file.
  3642.  
  3643.  
  3644. The Dialog Hook
  3645.  
  3646. We will be calling SFPGetFile as follows:
  3647.  
  3648.     SFPGetFile (wher, '', @SFFileFilter, NumFileTypes,
  3649.             MyFileTypes, @MySFHook, reply, myDLOGID,nil);
  3650.  
  3651. Notice that we’re passing @MySFHook to Standard File. This is the address of our
  3652. dialog hook routine. Our dialog hook is declared as:
  3653.  
  3654.     FUNCTION MySFHook (MySFitem: INTEGER; theDialog: DialogPtr):INTEGER;
  3655.  
  3656. A dialog hook routine allows us to see every item hit before standard file acts on
  3657. it. This allows us to handle controls that aren’t in the standard SFPGetFile’s DITL
  3658. or to handle standard controls in non-standard ways. The dialog hook in this example
  3659. consists of a case statement with MySFitem as the case selector. Before SFPGetFile
  3660. displays its dialog, it calls our dialog hook, passing it a –1 as MySFitem. This
  3661. gives us a chance to initialize our controls. Here we will set the textAppButton to
  3662. off and the textButton to on:
  3663.  
  3664.     GetDItem(theDialog,textAppButton,itemType,itemToChange,itemBox);
  3665.      SetCtlValue(controlHandle(itemToChange),btnOff);
  3666.      GetDItem(theDialog,textButton,itemType,itemToChange,itemBox);
  3667.      SetCtlValue(controlHandle(itemToChange),btnOn);
  3668.  
  3669. and we can also change the title of an existing control. Here’s how we might change
  3670. the title of the Open button using a string that we get from a resource file:
  3671.  
  3672.     GetIndString(buttonTitle,256,1);
  3673.      If buttonTitle <> '' then Begin           { if we really got the resource}
  3674.          GetDItem(theDialog,getOpen,itemType,itemToChange,itemBox); 
  3675.          SetCtitle(controlHandle(itemToChange),buttonTitle);
  3676.     End; {if}         {if we didn't get the resource, don't change the title }  
  3677.  
  3678. Upon completion of our routine that handles the –1, we return a –1 to standard file: 
  3679.   
  3680.  
  3681.        MySFHook:= MySFItem;                {pass back the same item we were sent}
  3682.  
  3683. We now have a SFPGetFile dialog displayed that has a quit button and two radio buttons
  3684. (the textOnly button is on, the TextApp button is off). In addition, the standard
  3685. Open button has been renamed to MyOpen (or whatever STR is the first string in STR#
  3686. 256). This was all done before SFPGetFile displayed the dialog. Once our hook is
  3687. exited, SFPGetFile displays the dialog and calls ModalDialog.
  3688.  
  3689. When the user clicks on an item in the dialog, our hook is called again. We can then
  3690. take appropriate actions, such as highlighting the textButton and un-highlighting the
  3691. textAppButton if the user clicks on the textButton. At this time, we can also update
  3692. a global variable (textOnly) that we will use in our file filter function to tell us
  3693. which files to display. Notice that we can redisplay the file list by returning a 101
  3694. as the result of MySFHook. (Standard File for Systems newer than 4.3 will also read
  3695. the low memory globals, CurDirStore and SFSaveDisk, and switch directories when necessary
  3696. if a 101 is returned as the result. Thus, you can point Standard File to a new directory,
  3697. or a new disk.) For example, when the textButton is hit we turn the textAppButton
  3698. off, turn the textButton on, update the global variable textOnly, and tell SFPGetFile
  3699. to redisplay the list of files the user can choose from:
  3700.  
  3701.     if not textOnly then Begin    {if textOnly was turned off, turn it on now}
  3702.          GetDItem(theDialog,textAppButton,itemType,itemToChange,itemBox);
  3703.          SetCtlValue(controlHandle(itemToChange),btnOff);
  3704.          GetDItem(theDialog,textButton,itemType,itemToChange,itemBox);
  3705.          SetCtlValue(controlHandle(itemToChange),btnOn);
  3706.          textOnly:=TRUE;     {toggle our global variable for use in the filter}
  3707.          MySFHook:= reDrawList;{101}       {we must tell SF to redraw the list}
  3708.      End;  {if not textOnly}
  3709.  
  3710. If our quit button is hit, we can pass SFPGetFile back the cancel button:
  3711.  
  3712.     MySFHook:= getCancel;
  3713.  
  3714. If one of SFPGetFile’s standard items is hit, it is very important to pass that item
  3715. back to SFPGetFile:
  3716.  
  3717.     MySFHook:= MySFItem; {pass back the same item we were sent}
  3718.  
  3719.  
  3720. The File Filter
  3721.  
  3722. Remember, we called SFPGetFile as follows:
  3723.  
  3724.     SFPGetFile (wher, '', @SFFileFilter, NumFileTypes,
  3725.                  MyFileTypes, @MySFHook, reply,myDLOGID,nil);
  3726.  
  3727. Notice that we’re passing @SFFileFilter to SFPGetFile. This is the address of our
  3728. file filter routine. A file filter is declared as:
  3729.  
  3730.     FUNCTION SFFileFilter (p: ParmBlkPtr): BOOLEAN;
  3731.  
  3732. A file filter routine allows us to control which files SFPGetFile will display for
  3733. the user. Our file filter is called for every file (of the type(s) specified in the
  3734. typelist) on an MFS disk, or for every file (of the type(s) specified in the typelist)
  3735. in the current directory on an HFS disk. In addition, SFPGetFile displays HFS folders
  3736. for us automatically. Our file filter selects which files should appear in the dialog
  3737. by returning FALSE for every file that should be shown and TRUE for every file that
  3738. shouldn’t.
  3739. For example, using our global variable textOnly (which we set in our dialog hook,
  3740. remember?):
  3741.  
  3742.     FUNCTION SFFileFilter(p:parmBlkPtr):boolean; 
  3743.  
  3744.     Begin {SFFileFilter}
  3745.         SFFileFilter:= TRUE;                          {Don't show it -- default}
  3746.  
  3747.         if textOnly then
  3748.            if p^.ioFlFndrInfo.fdType = 'TEXT' then
  3749.                SFFileFilter:= FALSE                      {Show TEXT files only}
  3750.            else Begin
  3751.            End  {dummy else}
  3752.        else
  3753.            if (p^.ioFlFndrInfo.fdType = 'TEXT') or 
  3754.                     (p^.ioFlFndrInfo.fdType = 'APPL') then 
  3755.                          SFFileFilter:= FALSE;       { show TEXT or APPL files}
  3756.     End;  {SFFileFilter}
  3757.  
  3758. SFPGetFile calls the file filter after it has called our dialog hook. Please remember
  3759. that the filter is passed every file of the types specified in the typelist (MyFileTypes).
  3760. If you want your application to be able to choose from all files, pass SFPGetFile a
  3761. –1 as numTypes. For information about parameters to SFPGetFile that haven’t been
  3762. discussed in this technical note, see the Standard File Package chapter of Inside
  3763. Macintosh.
  3764.  
  3765. That’s all there is to it!! Now that you know how to modify SFPGetFile to suit your
  3766. needs, please don’t rush off and load up the dialog window with all kinds of controls
  3767. and text. Please make sure that you adhere to Macintosh interface standards. Similar
  3768. techniques can be used with SFGetFile, SFPutFile and SFPPutFile.
  3769.  
  3770. The complete source of the demo program and of the resource compiler input file follows:
  3771.  
  3772. MPW Pascal Source
  3773.  
  3774. {$R-}
  3775.  
  3776. {Jim Friedlander    Macintosh Technical Support        9/30/85}
  3777.  
  3778. program SFGetDemo;
  3779.  
  3780. USES
  3781.      MemTypes,
  3782.      QuickDraw,
  3783.      OSIntf,
  3784.      ToolIntf,
  3785.      PackIntf;
  3786. {$D+}
  3787.  
  3788. CONST
  3789.   myDLOGID = 128;    {ID of our dialog for use with SFPGetFile}
  3790.  
  3791. VAR
  3792.   wher: Point;             { where to display dialog }
  3793.   reply: SFReply;          { reply record }
  3794.   textOnly: BOOLEAN;       { tells us which files are currently being displayed}
  3795.   myFileTypes: SFTypeList; { we won't actually use this }
  3796.   NumFileTypes: integer;
  3797.  
  3798.  
  3799. {------------------------------------------------------------------------------}
  3800. FUNCTION MySFHook(MySFitem:integer; theDialog:DialogPtr): integer;
  3801.  
  3802. CONST
  3803.   textButton        = 11;      {DITL item number of textButton}
  3804.   textAppButton     = 12;      {DITL item number of textAppButton}
  3805.   quitButton        = 13;      {DITL item number of quitButton}
  3806.  
  3807.  
  3808.   stayInSF     =  0;    {if we want to stay in SF after getting an Open hit,         
  3809.  
  3810.                         we can pass back a 0 from our hook  (not used in          
  3811.  
  3812.                         this example) }
  3813.   firstTime     = -1;   {the first time our hook is called, it is passed a -1}
  3814.  
  3815.   {The following line is the key to the whole routine -- the magic 101!!}
  3816.   reDrawList    = 101;    {returning 101 as item number will cause the          
  3817.  
  3818.                             file list to be recalculated}
  3819.   btnOn         = 1;    {control value for on}
  3820.   btnOff        = 0;    {control value for off}
  3821.  
  3822. VAR
  3823.   itemToChange: Handle;            {needed for GetDItem and SetCtlValue}
  3824.   itemBox:Rect;                    {needed for GetDItem}
  3825.   itemType:integer;                {needed for GetDItem}
  3826.   buttonTitle: Str255;             {needed for GetIndString}
  3827.  
  3828. Begin {MySFHook}
  3829.   case MySFItem of
  3830.  
  3831.     firstTime: Begin              { before the dialog is drawn, our hook gets       
  3832.  
  3833.                     called with a -1 (firstTime) as the item so we can change        
  3834.   
  3835.                     things like button titles, etc. }
  3836.  
  3837. {Here we will set the textAppButton to OFF, the textButton to ON}
  3838.         GetDItem(theDialog,textAppButton,itemType,itemToChange,itemBox);
  3839.         SetCtlValue(controlHandle(itemToChange),btnOff);
  3840.         GetDItem(theDialog,textButton,itemType,itemToChange,itemBox);
  3841.         SetCtlValue(controlHandle(itemToChange),btnOn);
  3842.  
  3843.         GetIndString(buttonTitle,256,1);                  {get the button title from
  3844. a resource file}
  3845.         If buttonTitle <> '' then Begin    { if we really got the resource}
  3846.             GetDItem(theDialog,getOpen,itemType,itemToChange,itemBox); {get a handle
  3847. to the                                                                               
  3848.       open button}
  3849.             SetCtitle(controlHandle(itemToChange),buttonTitle);
  3850.         End; {if}        {if we can't get the resource, we just won't change         
  3851.  the open button's title}
  3852.         MySFHook:= MySFItem;        {pass back the same item we were sent}
  3853.     End;  {firstTime}
  3854.  
  3855.  
  3856. {Here we will turn the textAppButton OFF, the textButton ON and redraw the list}
  3857.     textButton: Begin
  3858.         if not textOnly then Begin
  3859.             GetDItem(theDialog,textAppButton,itemType,itemToChange,itemBox);
  3860.             SetCtlValue(controlHandle(itemToChange),btnOff);
  3861.             GetDItem(theDialog,textButton,itemType,itemToChange,itemBox);
  3862.             SetCtlValue(controlHandle(itemToChange),btnOn);
  3863.             textOnly:=TRUE;
  3864.             MySFHook:= reDrawList;    {we must tell SF to redraw the list}
  3865.         End;  {if not textOnly}
  3866.     End;  {textOnlyButton}
  3867.  
  3868. {Here we will turn the textButton OFF, the textAppButton ON and redraw the list}
  3869.     textAppButton: Begin
  3870.         if textOnly then Begin
  3871.             GetDItem(theDialog,TextButton,itemType,itemToChange,itemBox);
  3872.             SetCtlValue(controlHandle(itemToChange),BtnOff);
  3873.             GetDItem(theDialog,TextAppButton,itemType,itemToChange,itemBox);
  3874.             SetCtlValue(controlHandle(itemToChange),BtnOn);
  3875.             TextOnly:=FALSE;
  3876.             MySFHook:= reDrawList;    {we must tell SF to redraw the list}
  3877.           End;  {if not textOnly}
  3878.         End;  {textAppButton}
  3879.  
  3880.     quitButton:  MySFHook:= getCancel;    {Pass SF back a 'cancel button'}
  3881.  
  3882. {!!!!very important !!!! We must pass SF's 'standard' item hits back to SF}
  3883.     otherwise  Begin
  3884.                   MySFHook:= MySFItem;    { the item hit was one of SF's standard
  3885. items... }
  3886.     End;  {otherwise}        { so just pass it back}
  3887.   End;  {case}
  3888. End;  {MySFHook}
  3889.  
  3890. {-----------------------------------------------------------------------------}
  3891.  
  3892. FUNCTION SFFileFilter(p:parmBlkPtr):boolean; {general strategy -- check value of
  3893. global var                                                    textOnly to see which
  3894. files to display}
  3895.  
  3896. Begin {SFFileFilter}
  3897.   SFFileFilter:= TRUE;          {Don't show it -- default}
  3898.  
  3899.   if textOnly then
  3900.       if p^.ioFlFndrInfo.fdType = 'TEXT' then
  3901.           SFFileFilter:= FALSE        {Show it}
  3902.       else Begin
  3903.       End  {dummy else}
  3904.   else
  3905.       if (p^.ioFlFndrInfo.fdType = 'TEXT') or (p^.ioFlFndrInfo.fdType = 'APPL') then
  3906.           SFFileFilter:= FALSE;        {Show it}
  3907. End;  {SFFileFilter}
  3908.  
  3909.  
  3910. {-----------------------------------------------------------------------------}
  3911.  
  3912. Begin {main program}
  3913.    InitGraf (@thePort);
  3914.    InitFonts;
  3915.    InitWindows;
  3916.    TEInit;
  3917.    InitDialogs (nil);
  3918.  
  3919.    wher.h:=80;
  3920.    wher.v:=90;
  3921.    NumFileTypes:= -1;          {Display all files}
  3922.  
  3923. { we don't need to initialize MyFileTypes, because we want to get a chance to filter
  3924. every file    on the disk in SFFileFilter - we will decide what to show and what not
  3925. to. If you want to        filter just certain types of files by name, you would set
  3926. up MyFileTypes and NumFileTypes        accordingly}
  3927.  
  3928.    repeat
  3929.        textOnly:= TRUE;{each time SFPGetFile is called, initial display will be text-only
  3930.     files}
  3931.        SFPGetFile (wher, '', @SFFileFilter, NumFileTypes, MyFileTypes, @MySFHook,    
  3932. reply,myDLOGID,nil);
  3933.    until reply.good = FALSE;
  3934.           {until we get a cancel button hit ( or a Quit button -- thanks to our dialog
  3935. hook ) }
  3936. End.
  3937.  
  3938.  
  3939.  
  3940. MPW C Source
  3941.  
  3942. #include <Types.h>
  3943. #include <Quickdraw.h>
  3944. #include <Resources.h>
  3945. #include <Fonts.h>
  3946. #include <Windows.h>
  3947. #include <Menus.h>
  3948. #include <TextEdit.h>
  3949. #include <Events.h>
  3950. #include <Dialogs.h>
  3951. #include <Packages.h>
  3952. #include <Files.h>
  3953. #include <Controls.h>
  3954. #include <ToolUtils.h>
  3955.  
  3956.     /*DITL item number of textButton*/
  3957. #define    textButton          11            
  3958.  
  3959.     /*DITL item number of textAppButton*/
  3960. #define    textAppButton    12            
  3961.  
  3962.     /*DITL item number of quitButton*/
  3963. #define    quitButton        13            
  3964.  
  3965.     /*if we want to stay in SF after getting an Open hit, we can pass back a 0
  3966.     from our hook  (not used in this example) */
  3967. #define    stayInSF        0              
  3968.   
  3969.     /*the first time our hook is called, it is passed a -1*/
  3970. #define    firstTime    -1            
  3971.  
  3972.     /*The following line is the key to the whole routine -- the magic 101!!*/
  3973.     /*returning 101 as item number will cause the file list to be recalculated*/
  3974. #define    reDrawList    101            
  3975.  
  3976.     /*control value for on*/
  3977. #define    btnOn        1            
  3978.  
  3979.     /*control value for off*/
  3980. #define    btnOff        0            
  3981.  
  3982.     /*resource ID of our DLOG for SFPGetFile*/
  3983. #define myDLOGID    128
  3984.  
  3985. Boolean        textOnly;            /* tells us which files are currently being displayed*/
  3986.  
  3987. main()
  3988. {    /*main program*/
  3989.  
  3990.         pascal short MySFHook();  
  3991.         pascal Boolean flFilter();
  3992.                 
  3993.         Point          wher;                /* where to display dialog */
  3994.         SFReply      reply;                /* reply record */
  3995.         SFTypeList    myFileTypes;          /* we won't actually use this */
  3996.         short int      NumFileTypes = -1;
  3997.  
  3998.     InitGraf(&qd.thePort);
  3999.     InitFonts();
  4000.     FlushEvents(everyEvent, 0);
  4001.     InitWindows();
  4002.     TEInit();
  4003.     InitDialogs(nil);
  4004.     InitCursor();
  4005.  
  4006.  
  4007.  
  4008.    wher.h=80;
  4009.    wher.v=90;
  4010.  
  4011. /* we don't need to initialize MyFileTypes, because we want to get a chance to filter
  4012. every file on  the disk in flFilter - we will decide what to show and what not to. if
  4013. you want to filter just certain types of files by name, you would set up MyFileTypes
  4014. and NumFileTypes accordingly*/
  4015.  
  4016.    do
  4017.    {    textOnly= true;     /*each time SFPGetFile is called, initial display will be
  4018.     text-only files*/
  4019.        SFPGetFile(&wher, "",flFilter, NumFileTypes,myFileTypes,MySFHook, &reply,myDLOGID,nil);
  4020.    }while (reply.good);    /*until we get a cancel button hit ( or a Quit button in
  4021. this case ) */
  4022. } /* main */
  4023.  
  4024.  
  4025. pascal short MySFHook(MySFItem,theDialog)
  4026. short MySFItem;
  4027. DialogPtr theDialog;
  4028.  
  4029. {
  4030.  
  4031. Handle  itemToChange;            /*needed for GetDItem and SetCtlValue*/
  4032. Rect    itemBox;                /*needed for GetDItem*/
  4033. short    itemType;                   /*needed for GetDItem*/
  4034. char    buttonTitle[256];        /*needed for GetIndString*/
  4035.  
  4036.   switch (MySFItem)
  4037.   {
  4038.     case firstTime:          
  4039.         /* before the dialog is drawn, our hook gets called with a -1 (firstTime)...*/
  4040.         /* as the item so we can change things like button titles, etc. */
  4041.         /*Here we will set the textAppButton to OFF, the textButton to ON*/
  4042.         GetDItem(theDialog,textAppButton,&itemType,&itemToChange,&itemBox);
  4043.         SetCtlValue(itemToChange,btnOff);
  4044.         GetDItem(theDialog,textButton,&itemType,&itemToChange,&itemBox);
  4045.         SetCtlValue(itemToChange,btnOn);
  4046.  
  4047.         GetIndString((char *)buttonTitle,256,1);                  /*get the button
  4048. title from a resource file*/
  4049.         if (buttonTitle[0] != 0)    /* check the length of the p-string to 
  4050.                          see if we really got the resource*/
  4051.         {    
  4052.             GetDItem(theDialog,getOpen,&itemType,&itemToChange,&itemBox); /*get a    
  4053.                     handle to the open button*/
  4054.             SetCTitle(itemToChange,buttonTitle);
  4055.           }  /*if we can't get the resource, we just won't change the open button's
  4056. title*/
  4057.         return MySFItem;     /*pass back the same item we were sent*/
  4058.         break;
  4059.  
  4060. /*Here we will turn the textAppButton OFF, the textButton ON and redraw the list*/
  4061.     case textButton: 
  4062.         if (!textOnly)
  4063.         {
  4064.                     GetDItem(theDialog,textAppButton,&itemType,&itemToChange,&itemBox);
  4065.                     SetCtlValue(itemToChange,btnOff);
  4066.                     GetDItem(theDialog,textButton,&itemType,&itemToChange,&itemBox);
  4067.                     SetCtlValue(itemToChange,btnOn);
  4068.                     textOnly=true;
  4069.                     return(reDrawList);                  /*we must tell SF to redraw
  4070. the list*/
  4071.                }  /*if !textOnly*/
  4072.         return MySFItem;
  4073.         break;
  4074.  
  4075. /*Here we will turn the textButton OFF, the textAppButton ON and redraw the list*/
  4076.     case textAppButton:
  4077.         if (textOnly)
  4078.             {   
  4079.             GetDItem(theDialog,textButton,&itemType,&itemToChange,&itemBox);
  4080.                     SetCtlValue(itemToChange,btnOff);
  4081.                     GetDItem(theDialog,textAppButton,&itemType,&itemToChange,&itemBox);
  4082.                     SetCtlValue(itemToChange,btnOn);
  4083.                     textOnly=false;
  4084.                     return(reDrawList);                  /*we must tell SF to redraw
  4085. the list*/
  4086.            }  /*if not textOnly*/
  4087.         return MySFItem;     /*pass back the same item we were sent*/
  4088.         break;
  4089.  
  4090.     case quitButton:     
  4091.         return(getCancel);                  /*Pass SF back a 'cancel button'*/
  4092.  
  4093. /*!!!!!!very important !!!!!!!! We must pass SF's 'standard' item hits back to SF*/
  4094.     default:
  4095.             return(MySFItem);    /* the item hit was one of SF's standard items...
  4096. */
  4097.   }  /*switch*/
  4098.       return(MySFItem);    /* return what we got */
  4099. }  /*MySFHook*/
  4100.  
  4101.  
  4102. pascal Boolean flFilter(pb)
  4103. FileParam    *pb;
  4104.  
  4105. {
  4106.  
  4107. /* is this gross or what??? */
  4108. return((textOnly) ? ((pb->ioFlFndrInfo.fdType) != 'TEXT') :
  4109.     ((pb->ioFlFndrInfo.fdType) != 'TEXT') && 
  4110.     ((pb->ioFlFndrInfo.fdType) != 'APPL'));
  4111. }  /*flFilter*/
  4112.  
  4113.  
  4114.  
  4115. Rez Input File
  4116.  
  4117. #include "types.r"
  4118.  
  4119. resource 'STR#' (256) {
  4120.     {
  4121.         "MyOpen"
  4122.     }
  4123. };
  4124.  
  4125. resource 'DLOG' (128, purgeable) {
  4126.     {0, 0, 200, 349},
  4127.     dBoxProc,
  4128.     invisible,
  4129.     noGoAway,
  4130.     0x0,
  4131.     128,
  4132.     "MyGF"
  4133. };
  4134.  
  4135. resource 'DITL' (128, purgeable) {
  4136.     {
  4137.         /* [1] */
  4138.         {138, 256, 156, 336},
  4139.         Button { enabled, "Open" };
  4140.         /* [2] */
  4141.         {1152, 59, 1232, 77},
  4142.         Button { enabled, "Hidden" };
  4143.         /* [3] */
  4144.         {163, 256, 181, 336},
  4145.         Button { enabled, "Cancel" };
  4146.         /* [4] */
  4147.         {39, 252, 59, 347},
  4148.         UserItem { disabled };
  4149.         /* [5] */
  4150.         {68, 256, 86, 336},
  4151.         Button { enabled, "Eject" };
  4152.         /* [6] */
  4153.         {93, 256, 111, 336},
  4154.         Button { enabled, "Drive" };
  4155.         /* [7] */
  4156.         {39, 12, 185, 230},
  4157.         UserItem { enabled };
  4158.         /* [8] */
  4159.         {39, 229, 185, 245},
  4160.         UserItem { enabled };
  4161.         /* [9] */
  4162.         {124, 252, 125, 340},
  4163.         UserItem { disabled };
  4164.         /* [10] */
  4165.         {1044, 20, 1145, 116},
  4166.         StaticText { disabled, "" };
  4167.         /* [11] */
  4168.         {1, 14, 20, 142},
  4169.         RadioButton { enabled, "Text files only" };
  4170.         /* [12] */
  4171.         {19, 14, 38, 176},
  4172.         RadioButton { enabled, "Text and applications" };
  4173.         /* [13] */
  4174.         {6, 256, 24, 336},
  4175.         Button { enabled, "Quit" }
  4176.     }
  4177. };
  4178.  
  4179.  
  4180.  
  4181.  
  4182. æKY 48
  4183. æC #48: Bundles
  4184.  
  4185. See also:       The Finder Interface
  4186.  
  4187. Written by:     Ginger Jernigan     November 1, 1985
  4188. Updated:                            March 1, 1988
  4189. _______________________________________________________________________________
  4190.  
  4191. This note describes what a bundle is and how to create one.
  4192. _______________________________________________________________________________
  4193.  
  4194. A bundle is a collection of resources. Bundles can be used for a number of different
  4195. purposes, and are currently used by the Finder ito tie an icon to a file type, allowing
  4196. your application or data file to have its own icon.
  4197.  
  4198.  
  4199. How to Create a Bundle
  4200.  
  4201. A bundle is a collection of resources. To make a bundle for finder icons, we need to
  4202. set up four types of resources: an ICN#, an FREF, a creator STR and a BNDL.
  4203.  
  4204. The ICN# resource type is an icon list. Each ICN# resource contains one or more icons,
  4205. on after another. For Finder bundle icons, there are two icons in each ICN#: one for
  4206. the icon itself and one for the mask. In our sample bundle, we have two file types,
  4207. each with its own icon. To define the icons for these files we would enter this into
  4208. our Rez input file:
  4209.  
  4210.     resource 'ICN#' (732) { /* first icon: the ID number can be anything */
  4211.        {                    /* first, the icon */
  4212.           $"FF FF FF FF"    /* each line is 4 bytes (32 bits) */
  4213.           $"F0 09 CD DD"    /* 32 lines total for icon */
  4214.           ...
  4215.           $"FF FF FF FF"    /* 32nd line of icon */
  4216.           ,                 /* now, the mask */
  4217.           $"FF FF FF FF"    /* 32 lines total for mask */
  4218.           $"FF FF FF FF"
  4219.           ...
  4220.           $"FF FF FF FF"    /* 32nd line of mask*/
  4221.        }
  4222.     };
  4223.     resource 'ICN#' (733) { /* second icon */
  4224.        {
  4225.           $"FF FF FF FF"
  4226.           ...
  4227.           ,
  4228.           $"FF FF FF FF"
  4229.           ...
  4230.        }
  4231.     };
  4232.  
  4233. Now that we’ve defined our icons we can set up the FREFs. An FREF is a file type
  4234. reference; you need one for each file type that has an icon. It ties a file type to a
  4235. local icon resource ID. This will be mapped by the BNDL onto an actual resource ID
  4236. number of an ICN# resource. Our FREFs will look like this:
  4237.  
  4238.     resource 'FREF' (816) { /* file type reference for application icon */
  4239.        {
  4240.           'APPL', 605, /* the type is APPL(ication), the local ID is 605 */
  4241.           ""           /* this string should be empty (it is unused) */
  4242.        }
  4243.     };
  4244.  
  4245.     resource 'FREF' (816) { /* file type reference for a document icon */
  4246.        {
  4247.           'TEXT', 612, /* the type is TEXT, the local ID is 612 */
  4248.           ""           /* this string should be empty (it is unused) */
  4249.        }
  4250.     };
  4251.  
  4252. The reason that you specify the local ID, rather than the actual resource ID of the
  4253. ICN# is that the Finder will copy all of the bundle resources into the Desktop file
  4254. and renumber them to avoid conflicts. This means that the actual IDs will change, but
  4255. the local IDs will remain the same.
  4256.  
  4257. Every application (or other file with a bundle) has a unique four-character signature.
  4258. The Finder uses this to identify an application. The creator resource that contains a
  4259. single string, and should be defined like this:
  4260.  
  4261.     type 'MINE' as 'STR '; /* MINE is the signature */
  4262.     resource 'MINE' (0) { /* the creator resource ID must be 0 */
  4263.        "MyProgram 1.0 Copyright 1988"
  4264.     };
  4265.  
  4266. Now for the BNDL resource. The BNDL resource associates local resource IDs with actual
  4267. resource IDs, and also tells the Finder what file types exist, and which ICN#s and
  4268. FREFs are part of the bundle. The resource looks like this:
  4269.  
  4270.     resource 'BNDL' (128) { /* the bundle resource ID should be 0 */
  4271.        'MINE', /* signature of this application */
  4272.        0, /* the creator resource ID (this must be 0) */
  4273.        {
  4274.           'ICN#', /* local resource ID mapping for icons */
  4275.           {
  4276.              605, 732, /* ICN# local ID 605 maps to 732 */
  4277.              612, 733  /* ICN# local ID 612 maps to 733 */
  4278.           },
  4279.           'FREF', /* local resource ID mapping for file type references */
  4280.           {
  4281.              523, 816, /* FREF local ID 523 maps to 816 */
  4282.              555, 817  /* FREF local ID 555 maps to 817 */
  4283.           },
  4284.  
  4285. When you are in the Finder, your application, type APPL (FREF 816), will be displayed
  4286. with icon local ID 605 (from the FREF resource). This is ICN# 732. Files of type TEXT
  4287. (FREF 817) created by your application will be displayed with icon local ID 612 (from
  4288. the FREF resource). This is ICN# 733.
  4289.  
  4290.  
  4291. How the Finder Uses Bundles
  4292.  
  4293. If a file has the bundle bit set, but the bundle isn’t in the Desktop file, the Finder
  4294. looks for a BNDL resource. If the BNDL resource matches the signature of theapplication,
  4295. the Finder then makes a copy of the bundle and puts it in the Desktop file. The file
  4296. is then displayed with its associated icon.
  4297.  
  4298. If a file has lost its icon (it’s on a disk without the file containing bundle and
  4299. the Desktop file doesn’t contain the bundle), then it will be displayed with the
  4300. default document icon until the Finder encounters a copy of the file that contains
  4301. the right bundle. The Finder then makes a copy of the application’s bundle (renumbering
  4302. resources if necessary) and places it in the Desktop file of that disk.
  4303.  
  4304.  
  4305. Problems That May Arise
  4306.  
  4307. There are times when you have set up these resource types properly but the icon is
  4308. either the wrong one or it has defaulted to the standard application or data file
  4309. icon. There are a number of possible reasons for this. 
  4310.  
  4311. If you are using the Macintosh-based RMaker, the first thing to check is whether
  4312. there are any extraneous spaces in your resource compiler input file. The Macintosh-based
  4313. RMaker is very picky about extra spaces.
  4314.  
  4315. If your icon is defaulting to the standard icon, check to see that the bundle bit is
  4316. set. If the bundle bit isn’t set , the Finder doesn’t know to place the bundle in the
  4317. Desktop file. If it isn’t in the Desktop file, the Finder displays the file with a
  4318. default icon.
  4319.  
  4320. If you changed the icon and remade the resource file, but the file still has the same
  4321. old icon when displayed in the Finder. The old icon is still in the Desktop file. The
  4322. Finder doesn’t know that you’ve changed it, so it uses what it has. To get it to use
  4323. the new icon you need to rebuild the Desktop file. To force the Finder to rebuild the
  4324. Desktop file, you can hold down the Option and Command keys on startup or on insertion
  4325. of the disk in question if it isn't the boot disk. The Finder will ask whether or not
  4326. you want to rebuild the desktop (meaning the Desktop file).
  4327.  
  4328. Have a bundle of fun!
  4329.  
  4330.  
  4331.  
  4332.  
  4333. æKY 50
  4334. æC #50: Calling SetResLoad
  4335.  
  4336. See also:      The Resource Manager
  4337.                Technical Note #1—DAs and System Resources
  4338.      
  4339. Written by:    Jim Friedlander      October 25, 1985
  4340. Updated:                            March 1, 1988
  4341. _______________________________________________________________________________
  4342.  
  4343. Calling SetResLoad(FALSE) can be useful if you need to get a handle to a resource,
  4344. without causing the resource to be loaded from disk if it isn’t already in memory.
  4345. This technique is used in Technical Note #1. SetResLoad changes the value of the
  4346. low-memory global ResLoad (at location $A5E).
  4347.  
  4348. It is very important that your program not leave ResLoad set to FALSE when it exits.
  4349. Doing this will cause the system to reboot or crash when it does a GetResource call
  4350. for the next code segment to be loaded (usually the Finder). The system will crash
  4351. because GetResource will not actually load the code from disk when ResLoad is FALSE.
  4352.  
  4353. So, make sure that you call SetResLoad(TRUE) before exiting your program. 
  4354.  
  4355.  
  4356.  
  4357.  
  4358. æKY 51
  4359. æC #51: Debugging With PurgeMem and CompactMem
  4360.  
  4361. See also:      The Memory Manager
  4362.  
  4363. Written by:    Jim Friedlander       October 19, 1985
  4364. Updated:                             March 1, 1988
  4365. _______________________________________________________________________________
  4366.  
  4367. If you are having problems finding bugs like handles that aren’t locked down when
  4368. they should be, or resources that aren’t there when they’re supposed to be, there is
  4369. a handy technique for forcing these problems to the surface. Every time through the
  4370. main event loop call:
  4371.  
  4372.     PurgeMem(MaxSize);                       {MaxSize = $800000}
  4373.     size:= CompactMem(MaxSize);
  4374.  
  4375. PurgeMem will purge all purgeable blocks and CompactMem will rearrange the heap,
  4376. trying to find a contiguous free block of MaxSize bytes. Obviously, this will move
  4377. things around quite a bit, so, if there are any unlocked handles that you have de-referenced,
  4378. you will find out about them very quickly.
  4379.  
  4380. Don’t be alarmed when you see the performance of your program deteriorate drastically
  4381. —it’s because lots of resources are being loaded and purged every time through the
  4382. main event loop. You might want to have a debugging menu item that toggles between
  4383. glacial and normal execution speeds.
  4384.  
  4385. PLEASE be sure to remove these two lines from any code that you ship!! In fact, neither
  4386. of these two calls should normally be made from your application. They tend to undo
  4387. work that has been done by the Memory and Resource Managers.
  4388.  
  4389.  
  4390.  
  4391.  
  4392. æKY 52
  4393. æC #52:    Calling _Launch From a High-Level Language
  4394.  
  4395. Revised by:    Rich Collyer                                          April 1989
  4396. Written by:    Jim Friedlander                                    November 1985
  4397.  
  4398. This Technical Note formerly discussed calling _Launch from a high-level language
  4399. which allows inline assembly code.
  4400. Changes since March 1988:  Merged contents into Technical Note #126.
  4401. _______________________________________________________________________________
  4402.  
  4403. This Note formerly discussed calling _Launch from a high-level language.  The information
  4404. on calling _Launch is now contained in Technical Note #126, 
  4405. Sub(Launching) From a High-Level Language, which also covers sublaunching other applications.
  4406.  
  4407.  
  4408. æKY 53
  4409. æC #53: MoreMasters Revisited
  4410.  
  4411. See also:       The Memory Manager
  4412.  
  4413. Written by:     Jim Friedlander      October 28, 1985
  4414. Updated:                             March 1, 1988
  4415. _______________________________________________________________________________
  4416.  
  4417. MoreMasters should be called from CODE segment 1. The number of master pointers that
  4418. a program needs can be determined empirically. MoreMasters can be tricked into creating
  4419. the exact number of master pointers desired.
  4420. _______________________________________________________________________________
  4421.  
  4422. If you ask Macintosh programmers when and how many times MoreMasters should be called,
  4423. you will get a variety of answers, ranging from “four times in the initialization
  4424. segment” to “once, anywhere.” As you might suspect, the answer is somewhat different
  4425. from either of these.
  4426.  
  4427. MoreMasters allocates a block of master pointers in the current heap zone. In the
  4428. application heap, a block of master pointers consists of 64 master pointers; in the
  4429. system heap, a block consists of 32 master pointers. Since master pointer blocks are
  4430. NON-RELOCATABLE, we want to be sure to allocate them early. The system will allocate
  4431. one master pointer block as your program loads. It’s the first object in the application
  4432. heap—its size is $108 bytes. 
  4433.  
  4434. A lot of programmers call MoreMasters from an “initialization” segment, but as we
  4435. shall see, that’s not such a good idea. The problem occurs when we unload our “initialization”
  4436. segment and it gets purged from memory.
  4437.  
  4438. •••Click on the Illustration button below to see diagrams of the application heap
  4439. that illustrate what happens if we call MoreMasters from CODE segment 2 
  4440. (MPB stands for Master Pointer Block).•••
  4441.  
  4442. Notice that we now have some heap fragmentation—not serious, but it can be avoided by
  4443. making all MoreMasters calls in CODE segment 1. Because InitWindows creates the Window
  4444. Manager Port (WMgrPort), it should also be called from CODE segment 1. Both MoreMasters
  4445. and InitWindows should be called before another CODE segment is loaded, or the non-relocatable
  4446. objects they allocate will be put above the CODE segment and you’ll get fragmentation
  4447. when the CODE segment is purged. If you want to call an initialization segment before
  4448. calling MoreMasters and InitWindows, make sure that you unload it before you call
  4449. either routine.
  4450.  
  4451. Now that we know when to call MoreMasters, how many times do we call it? The answer
  4452. depends on your application. If you don’t call MoreMasters enough times, the system
  4453. will call it when it needs more master pointers. This can happen at very inconvenient
  4454. times, causing heap fragmentation. If you call MoreMasters too often, you can be
  4455. wasting valuable memory. This is preferable, however, to allocating too few master
  4456. pointer blocks!
  4457.  
  4458. The number of times you should call MoreMasters can be empirically determined. Once
  4459. your application is almost finished, remove all MoreMasters calls. Exercise your
  4460. application as completely as possible, opening windows, using handles, opening desk
  4461. accessories, etc. You can then go in with a debugger and see how many times the system
  4462. called MoreMasters. You do that by counting the non-relocatables of size $108. Due to
  4463. Memory Manager size correction, the master pointer blocks can also have a size of
  4464. $10C or $110 bytes. You should give yourself about 20% leeway — that is, if the system
  4465. called MoreMasters 10 times for you, you should call it 12 times. If you’re more
  4466. cautious, you might want to call MoreMasters 15 times.
  4467.  
  4468. Another technique that can save time at initialization is to calculate the number of
  4469. master pointers you will need, then set the MoreMast files of the heap zone header to
  4470. that number, and then call MoreMasters once:
  4471.  
  4472.     PROCEDURE MyMoreMasters(numMastPtrs : INTEGER);
  4473.  
  4474.     VAR
  4475.        oldMoreMast : INTEGER;      {saved value of MoreMast}
  4476.        zone        : THz;          {heap zone}
  4477.  
  4478.     BEGIN
  4479.          zone := GetZone;            {get the heap zone}
  4480.        WITH zone^ DO BEGIN
  4481.           oldMoreMast := MoreMast; {get the old value of MoreMast}
  4482.           MoreMast := numMastPtrs; {put the value we want in the zone header}
  4483.           MoreMasters;             {allocate the master pointers}
  4484.           MoreMast := oldMoreMast; {restore the old value of MoreMast}
  4485.        END;
  4486.     END;
  4487.  
  4488. In MPW C:
  4489.  
  4490.     void MyMoreMasters(numMastPtrs)
  4491.     short numMastPtrs;
  4492.  
  4493.     { /* MyMoreMasters */
  4494.        short     oldMoreMast;          /* saved value of MoreMast*/
  4495.        THz       oZone;                /* heap zone*/
  4496.  
  4497.        oZone = GetZone();              /* get the heap zone*/
  4498.        oldMoreMast = oZone->moreMast;  /* get the old value of MoreMast*/
  4499.        oZone->moreMast = numMastPtrs;  /* put the value we want in the
  4500.                                           zone header */
  4501.        MoreMasters();                  /*allocate the master pointers*/
  4502.        oZone->moreMast = oldMoreMast;  /*restore the old value of MoreMast*/
  4503.     } /* MyMoreMasters */
  4504.  
  4505.  
  4506.  
  4507.  
  4508. æKY 54
  4509. æC #54: Limit to Size of Resources
  4510.  
  4511. Written by:     Jim Friedlander    October 23, 1985
  4512. Updated:        March 1, 1988
  4513. _______________________________________________________________________________
  4514.  
  4515. This note formerly described a bug in WriteResource on 64K ROM machines. Information
  4516. specific to 64K ROM machines has been deleted from Macintosh Technical Notes for
  4517. reasons of clarity.
  4518.  
  4519.  
  4520. æKY 55
  4521. æC #55: Drawing Icons
  4522.  
  4523. See also:      QuickDraw
  4524.                Toolbox Utilities
  4525.  
  4526. Written by:    Jim Friedlander       October 21, 1985
  4527. Updated:                             March 1, 1988
  4528. _______________________________________________________________________________
  4529.  
  4530. Using resources of type ICON allows drawing of icons in srcOr mode.  Using resources
  4531. of type ICN# allows for more variety when drawing icons.
  4532. _______________________________________________________________________________
  4533.  
  4534. There are two different kinds of resources that contain icons: ICON and ICN#. An ICON
  4535. is a 32 by 32 bit image of an icon and can be drawn using the following Toolbox Utilities
  4536. calls:
  4537.  
  4538.     MyIconHndl:= GetIcon(iconID);
  4539.     PlotIcon(destRect,iconID);
  4540.  
  4541. While very convenient, this method only allows the drawing of icons in SrcOr mode (as
  4542. in the MiniFinder). The Finder uses resources of type ICN# to draw icons on the desktop.
  4543. Because the Finder uses ICN#s, it can draw icons in a variety of ways.
  4544.  
  4545. An ICN# resource is a list of 32 by 32 bit images that are grouped together. Common
  4546. convention has been to group two 32 by 32 bit images together in each ICN#. The first
  4547. image is the actual icon, the second image is the mask for the icon. To get a handle
  4548. to an ICN#, we would use something like this:
  4549.  
  4550.     TYPE
  4551.       iListHndl    = ^iListPtr;
  4552.       iListPtr     = ^iListStruct;
  4553.       iListStruct  = record
  4554.                         icon : packed array[0..31] of Longint;
  4555.                         mask : packed array[0..31] of Longint;
  4556.                       End; {iListStruct}
  4557.  
  4558.     VAR
  4559.        myILHndl      : iListHndl;            {handle to an ICN#}
  4560.          iBitMap       : BitMap;          {BitMap for the icon}
  4561.          mBitMap       : BitMap;          {BitMap for the mask}
  4562.  
  4563.     MyILHndl:= iListHndl(GetResource('ICN#',iconID));          if MyILHndl = NIL then
  4564. HandleError; { and exit or whatever is appropriate}
  4565.  
  4566. Once we have a handle to the icons, we need to set up two bitMaps that we will be
  4567. using later in CopyBits: 
  4568.  
  4569.         SetRect(icnRect,0,0,32,32);                  { define the icon's 'bounds'}
  4570.     With iBitMap do Begin
  4571.          baseAddr:= @MyILHndl^^.icon;    
  4572.          rowbytes:= 4;                   { 4 * 8 =32}
  4573.          bounds:= icnRect;
  4574.      End; {with}
  4575.       With mBitMap do Begin
  4576.          baseAddr:= @MyILHndl^^.mask;
  4577.          rowbytes:= 4;
  4578.          bounds:= icnRect;
  4579.       End; {with}
  4580.  
  4581. Icons can represent desktop objects that are either selected or not. Folder and volume
  4582. icons can either be open or not. The object (or the volume it is on) can either be
  4583. online or offline. The Finder draws icons using all permutations of open, selected
  4584. and online:
  4585.  
  4586. •••Click on the Illustration button below to view examples of icon highlighting.
  4587.  
  4588. Drawing icons as non-open is basically the same for online and offline volumes. We
  4589. need to punch a hole in the desktop for the icon. This is analogous to punching a
  4590. hole in dough with an irregular shaped cookie-cutter. We can then sprinkle jimmies*
  4591. all over the cookie and they will only stick in the area that we punched out (the
  4592. mask). We do this by copyBitsing the mask onto the desktop 
  4593. (whatever pattern) to our destRect. For non-open, non-selected icons, we use the
  4594. SrcBic mode so that we punch a white hole:
  4595.  
  4596.     SetRect(destRect,left,top,left+32,top+32);
  4597.     CopyBits(mBitMap,thePort^.portBits,icnRect,destRect,SrcBic,NIL); 
  4598.  
  4599. Then we XOR in the icon:
  4600.  
  4601.       CopyBits(iBitMap,thePort^.portBits,icnRect,destRect,SrcXor,NIL);
  4602.  
  4603. That’s all there is to drawing an icon as non-open, non-selected. To draw the icon as
  4604. non-open, selected, we will OR in the mask, causing a mask-shaped BLACK hole to be
  4605. punched in the desktop:
  4606.  
  4607.     CopyBits(mBitMap,thePort^.portBits,icnRect,destRect,SrcOr,NIL); 
  4608.  
  4609. Then, as before, we XOR in the icon:
  4610.  
  4611.     CopyBits(iBitMap,thePort^.portBits,icnRect,destRect,SrcXOr,NIL);
  4612.  
  4613.  
  4614. To draw icons as non-opened for offline volumes, we need to do a little more work. We
  4615. need to XOR a ltGray pattern into the boundsRect of the icon. We will then punch the
  4616. hole, draw the icon and then XOR out the ltgray pattern that does not fall inside the
  4617. mask. So, to draw the icon as offline, non-open, non-selected we would:
  4618.  
  4619.     GetPenState(OldPen);             {save the pen state so we can restore it}
  4620.     PenMode(patXor);
  4621.       PenPat(ltGray);
  4622.       PaintRect(destRect);                  {paint a ltGray background for icon}
  4623.  
  4624.       CopyBits(mBitMap,thePort^.portBits,icnRect,destRect,SrcBic,NIL);   {punch}
  4625.      PaintRect(destRect);  {XOR out bits outside of the mask, leaving the mask}      
  4626.                                           {filled with ltGray}
  4627.     CopyBits(iBitMap,thePort^.portBits,icnRect,destRect,SrcOr,NIL);  { OR in }       
  4628.                                            { the icon to the ltGray mask}
  4629.       SetPenState(OldPen);                           {restore the old pen state}
  4630.  
  4631.  
  4632. To draw the icon as offline, non-open, selected, we would use a similar approach:
  4633.  
  4634.         GetPenState(OldPen);            { save the pen state so we can restore it}
  4635.     PenMode(patXor);
  4636.     PenPat(dkGray);                { the icon is selected, so we need dkGray }
  4637.       PaintRect(destRect);                { paint a dkGray background for icon }
  4638.  
  4639.       CopyBits(mBitMap,thePort^.portBits,icnRect,destRect,SrcBic,NIL);   {punch}
  4640.     PaintRect(destRect);  {XOR out bits outside of the mask, leaving the mask}       
  4641.             {filled with dkGray}
  4642.     CopyBits(iBitMap,thePort^.portBits,icnRect,destRect,SrcBic,NIL); {BIC the}       
  4643.                                                 {icon to the dkGray mask}
  4644.       SetPenState(OldPen);                           {restore the old pen state}
  4645.  
  4646. Drawing the opened icons requires one less step. We don’’t have to CopyBits the icon
  4647. in, we just use the mask. Online and offline icons are drawn the same way. To draw
  4648. icons as open, selected, we do the following:
  4649.  
  4650.         GetPenState(OldPen);             {save the pen state so we can restore it}
  4651.       PenMode(patXor);
  4652.       PenPat(dkGray);                { the icon is selected, so we need dkGray }
  4653.        PaintRect(destRect);                 { paint a dkGray background for icon}
  4654.       CopyBits(mBitMap,thePort^.portBits,icnRect,destRect,SrcBic,NIL);   {punch}
  4655.       PaintRect(destRect);  {XOR out bits outside of the mask, leaving the mask}     
  4656.                       {filled with dkGray}
  4657.       SetPenState(OldPen);                           {restore the old pen state}
  4658.  
  4659.  
  4660. To draw icons as open, non-selected, we just need to change one line from above.
  4661. Instead of XORing with a dkGray pattern, we use a ltGray pattern:
  4662.  
  4663.       PenPat(ltGray);            { the icon is non-selected, so we need ltGray }
  4664.  
  4665.  
  4666. These techniques will work on any background, window-white or desktop-gray and all
  4667. patterns in between. Have fun.
  4668.  
  4669. * jimmies : little bits of chocolate
  4670.  
  4671.  
  4672.  
  4673. æKY 56
  4674. æC #56: Break/CTS Device Driver Event Structure
  4675.  
  4676. See also:  The Device Manager
  4677.            Serial Drivers
  4678.            Zilog Z8030/Z8530 SCC Serial Communications
  4679.            Controller Technical Manual
  4680.  
  4681. Written by:     Mark Baumwell    December 2, 1986
  4682. Updated:                         March 1, 1988
  4683. _______________________________________________________________________________
  4684.  
  4685. This technical note documents the event record information that gets passed when the
  4686. serial driver posts an event for a break/CTS status change.
  4687. _______________________________________________________________________________
  4688.  
  4689. The serial driver can be programmed to post a device driver event upon encountering a
  4690. break status change or CTS change (via the SerHShake call). The structure of device
  4691. driver events is driver-specific. This technical note documents the event record
  4692. information that gets passed when the serial driver posts a device driver event for a
  4693. break/CTS status change.
  4694.  
  4695. When the event is posted, the message field of the event record will be a long word
  4696. (four bytes). The most significant byte will contain the value of SCC Read Register 0
  4697. (see below for the relevant Read Register 0 values). The next byte will contain the
  4698. changed (since the last interrupt) bits of the SCC read register 0. The lower two
  4699. bytes (word) will contain the DCtlRefNum.
  4700.  
  4701. The values for Read Register 0 are as follows:
  4702.  
  4703.    • If a break occurred, bit 7 will be set.
  4704.    • If CTS changed, bit 5 will reflect the state of the CTS pin (0 means the 
  4705.      handshake line is asserted and that it is OK to transmit). 
  4706.  
  4707. We discourage posting these events because interrupts would be disabled for a long
  4708. time while the event is being posted. However, it is possible to detect a break or
  4709. read the value of the CTS line in another way. A break condition will always terminate
  4710. a serial driver input request (but not an output request), and the error breakRecd
  4711. (–90) will be returned. (This constant is defined in the SysEqu file.) You could
  4712. therefore detect a break by checking the returned error code.
  4713.  
  4714. The state of the CTS line can be checked by making a SerStatus call and checking the
  4715. value of the ctsHold flag in the SerStaRec record. See the Serial Drivers chapter of
  4716. Inside Macintosh for details.
  4717.  
  4718.  
  4719.  
  4720.  
  4721. æKY 57
  4722. æC #57: Macintosh Plus Overview
  4723.  
  4724. See:        Inside Macintosh Volume IV
  4725.  
  4726. Written by:     Scott Knaster    January 8, 1986
  4727. Updated:        March 1, 1988
  4728. _______________________________________________________________________________
  4729.  
  4730. This note was originally meant as interim Macintosh Plus documentation and has been
  4731. replaced by Inside Macintosh Volume IV, which is more complete and more accurate.
  4732.  
  4733.  
  4734. æKY 58
  4735. æC #58: International Utilities Bug
  4736.  
  4737. Written by:    Jim Friedlander    January 24, 1986
  4738. Updated:        March 1, 1988
  4739. _______________________________________________________________________________
  4740.  
  4741. This note formerly described a bug in System 2.0, which is now recommended only for
  4742. use with 64K ROM machines. Information specific to 64K ROM machines has been deleted
  4743. from Macintosh Technical Notes for reasons of clarity.
  4744.  
  4745.  
  4746. æKY 59
  4747. æC #59: Pictures and Clip Regions
  4748.  
  4749. See also:      QuickDraw
  4750.  
  4751. Written by:    Ginger Jernigan      January 16, 1986
  4752. Updated:                            March 1, 1988
  4753. _______________________________________________________________________________
  4754.  
  4755. This note describes a problem that affects creation of QuickDraw pictures.
  4756. _______________________________________________________________________________
  4757.  
  4758. When a GrafPort is created, the fields in the GrafPort are given default values; one
  4759. of these is the clip region, which is set to the rectangle (–32767, –32767, 32767,
  4760. 32767). If you create a picture, then call DrawPicture with a destination rectangle
  4761. that is not the same size as the picFrame without ever changing the default clip
  4762. region, nothing will be drawn.
  4763.  
  4764. When the picture frame is compared with the destination rectangle and the picture is
  4765. scaled, the clip region is scaled too. In the process of scaling, the clip region you
  4766. end up overflows and becomes empty, and your picture doesn’t get drawn. If you call
  4767. ClipRect(thePort^.portRect) before you record the picture, the picture will be drawn
  4768. correctly. The clipping on the destination port when playing back the picture is
  4769. irrelevant: once a picture is incorrectly recorded, it is too late.
  4770.  
  4771.  
  4772.  
  4773.  
  4774. æKY 60
  4775. æC #60: Drawing Characters into a Narrow GrafPort
  4776.  
  4777. See also:      QuickDraw
  4778.  
  4779. Written by:    Ginger Jernigan      January 20, 1986
  4780. Updated:                            March 1, 1988
  4781. _______________________________________________________________________________
  4782.  
  4783. When you draw a character into a GrafPort, your program will die with an address
  4784. error if the width of the GrafPort is smaller than the width of the character. If you
  4785. check before drawing the character to see if the GrafPort is wide enough, you can
  4786. avoid this unfortunate tragedy.
  4787.  
  4788. æKY 61
  4789. æC #61: GetItemStyle Bug
  4790.  
  4791. Written by:     Jim Friedlander      January 21, 1986
  4792. Updated:                             March 1, 1988
  4793. _______________________________________________________________________________
  4794.  
  4795. This note formerly described a bug (in GetItemStyle) which occurs only on 64K ROM
  4796. machines. Information specific to 64K ROM machines has been deleted from Macintosh
  4797. Technical Notes for reasons of clarity.
  4798.  
  4799.  
  4800. æKY 62
  4801. æC #62: Don’t Use Resource Header Application Bytes
  4802.  
  4803. See also:      The Resource Manager
  4804.  
  4805. Written by:    Bryan Stearns      January 23, 1986
  4806. Updated:                          March 1, 1988
  4807. _______________________________________________________________________________
  4808.  
  4809. The section of the Resource Manager chapter of Inside Macintosh which describes the
  4810. internal format of a resource file shows an area of the resource header labeled “available
  4811. for application data.” You SHOULD NOT use this area—it is used by the Resource Manager.
  4812.  
  4813.  
  4814. æKY 63
  4815. æC #63: WriteResource Bug Patch
  4816.  
  4817. Written by:      Rick Blair             January 15, 1986
  4818.                  Jim Friedlander
  4819.                  Bryan Stearns
  4820. Modified by :    Jim Friedlander        March 3, 1986
  4821. Updated:                                March 1, 1988
  4822. _______________________________________________________________________________
  4823.  
  4824. This note formerly contained a patch to fix a bug in WriteResource on 64K ROM machines.
  4825. Information specific to 64K ROM machines has been deleted from Macintosh Technical
  4826. Notes for reasons of clarity.
  4827.  
  4828.  
  4829. æKY 64
  4830. æC #64: IAZNotify
  4831.  
  4832. Written by:      Jim Friedlander      January 15, 1986
  4833. Modified by:     Jim Friedlander      August 18, 1986
  4834. Updated:                              March 1, 1988
  4835. _______________________________________________________________________________
  4836.  
  4837. Previous versions of this technical note recommended use of a low memory hook called
  4838. IAZNotify. We no longer recommend use of IAZNotify, since the IAZNotify hook is never
  4839. called under MultiFinder.
  4840.  
  4841.  
  4842. æKY 65
  4843. æC #65: Macintosh Plus Pinouts    
  4844.  
  4845. See also:       Macintosh Hardware Reference Manual
  4846.  
  4847. Written by:     Mark Baumwell    January 27, 1986
  4848. Modified by:    Mark Baumwell    March 20, 1986
  4849. Updated:        March 1, 1988
  4850. _______________________________________________________________________________
  4851.  
  4852. This note gives pinout descriptions for some of the Macintosh Plus ports and Macintosh
  4853. Plus cables that are different than the Macintosh 128K and 512K. 
  4854. _______________________________________________________________________________
  4855.  
  4856. Below are pinout descriptions for some Macintosh Plus ports and cables that are different
  4857. than the Macintosh 128K and 512K. Note that any unconnected pins are omitted.
  4858.  
  4859. •••Click on the Illustration button below to view these pinouts.••• 
  4860.  
  4861. Macintosh Plus Port Pinouts
  4862.  
  4863. Macintosh Plus Serial Connectors (Mini DIN-8)
  4864.  
  4865. Pin    Name        Description/Notes
  4866.  1     HSKo        Output Handshake (from Zilog 8530 DTR pin)
  4867.  2     HSKi/       Clock   Input Handshake (CTS) or TRxC (depends on 8530 mode)
  4868.        External
  4869.  3     TxD–        Transmit Data line
  4870.  4     Ground
  4871.  5     RxD–        Receive Data line
  4872.  6     TxD+        Transmit Data line
  4873.  7     Not connected
  4874.  8     RxD+        Receive Data line; ground this line to emulate RS232
  4875.  
  4876.  
  4877. Macintosh Plus SCSI Connector (DB-25)
  4878.  
  4879.  Pin    Name        Description/Notes
  4880.   1     REQ–
  4881.   2     MSG–
  4882.   3     I/O–
  4883.   4     RST–
  4884.   5     ACK–
  4885.   6     BSY–
  4886.   7     Ground
  4887.   8     DB0–
  4888.   9     Ground
  4889.   10    DB3–
  4890.   11    DB5–
  4891.   12    DB6–
  4892.   13    DB7–
  4893.   14    Ground
  4894.   15    C/D–
  4895.   16    Ground
  4896.   17    ATN–
  4897.   18    Ground
  4898.   19    SEL–
  4899.   20    DBP–
  4900.   21    DB1–
  4901.   22    DB2–
  4902.   23    DB4–
  4903.   24    Ground
  4904.   25    TPWR         Not connected
  4905.  
  4906. Macintosh Plus Cable Pinouts 
  4907.  
  4908. Apple System Peripheral-8 Cable (connects Macintosh Plus to ImageWriter II and Apple
  4909. Personal Modem )
  4910. (Product part number: M0187)
  4911. (Cable assembly part number: 590-0340-A (stamped on cable itself).
  4912.  
  4913.  ( DIN-8 )    ( DIN-8 )
  4914.     1            2
  4915.     2            1
  4916.     3            5
  4917.     4            4
  4918.     5            3
  4919.     6            8
  4920.     7            7
  4921.     8            6
  4922.  
  4923.  
  4924. Macintosh Plus Adapter Cable (connects Macintosh Plus DIN-8 to existing Macintosh
  4925. DB-9 cables)
  4926. (Apple part number: M0189)
  4927. (Cable assembly part number: 590-0341-A (stamped on cable itself).
  4928.  
  4929. ( DIN-8 )    Name    (DB-9)    Notes
  4930.     1        +12V       6
  4931.     2        HSK        7
  4932.     3        TxD–       5
  4933.     4        Ground     3      Jumpered to DB-9 pin 1 (in DB-9 connector)
  4934.     5        RxD–       9
  4935.     6        TxD+       4
  4936.     7        no wire
  4937.     8        RxD+       8
  4938.              Ground     1      Jumpered to DB-9 pin 3 (in DB-9 connector)
  4939.  
  4940.  
  4941.  
  4942.  
  4943. æKY 66
  4944. æC #66: Determining Which File System is Active
  4945.  
  4946. See also:       The File Manager
  4947.                 Technical Note #129 — SysEnvirons
  4948.  
  4949. Written by:     Jim Friedlander       December 21, 1985
  4950. Updated:                              March 1, 1988
  4951. _______________________________________________________________________________
  4952.  
  4953. Under certain circumstances it is necessary to determine which file system is currently
  4954. running, MFS or HFS. Note that this is usually not necessary, because all ROMs except
  4955. the original 64K ROMs include HFS. If your program only runs on these 128K ROMs or
  4956. newer, you do not need to check for HFS. Be sure to check that the machine you are
  4957. running has ROMs that are new enough. You can do this with SysEnvirons, discussed in
  4958. Technical Note #129.
  4959.  
  4960. To check for HFS on 64K ROM machines, check the low-memory global FSFCBLen (at location
  4961. $3F6). This global is one word in length (two bytes) and will be equal to –1 if MFS
  4962. is active and some positive number (currently $5E) if HFS is active. From Pascal, we
  4963. could check as follows:
  4964.  
  4965.     CONST
  4966.         FSFCBLen = $3F6;  {address of the low-memory global}
  4967.  
  4968.     VAR
  4969.         HFS: ^INTEGER;
  4970.  
  4971.     ...
  4972.     HFS:= POINTER(FSFCBLen);
  4973.     IF HFS^ > 0 THEN
  4974.         {we’re running HFS}
  4975.     ELSE
  4976.         {we’re running MFS}
  4977.     END;
  4978.  
  4979. Once you’ve determined that you are running under HFS, don’t assume that all mounted
  4980. volumes are hierarchical. You can check if a volume is or isn’t hierarchical by calling
  4981. PBHGetVinfo and then checking the directory signature (the ioVSigWord field of an
  4982. HParamBlockRec). A directory signature of $D2D7 means you’re looking at an MFS volume,
  4983. a signature of $4244 means you’re looking at an HFS volume.
  4984.  
  4985.  
  4986.  
  4987.  
  4988. æKY 67
  4989. æC #67: Finding the “Blessed Folder”
  4990.  
  4991. See also:      Technical Note #129 — SysEnvirons
  4992.  
  4993. Written by:    Jim Friedlander      January 18, 1986
  4994. Updated:                            March 1, 1988
  4995. _______________________________________________________________________________
  4996.  
  4997. This technical note describes how to determine which folder on an HFS volume is the
  4998. blessed folder, that is, the folder that contains both the System file and the Finder.
  4999. _______________________________________________________________________________
  5000.  
  5001. Knowing which folder on an HFS disk contains the System file and Finder can be very
  5002. useful information. This is available from SysEnvirons, discussed in Technical Note
  5003. #129. Sometimes it is also useful to find the blessed folder on HFS volumes other
  5004. than the one containing the currently open System file.
  5005.  
  5006. Finding the blessed folder on one of these volumes is quite simple—it is accomplished
  5007. with a call to PBHGetVInfo.
  5008.  
  5009. FUNCTION GetBlessed(vRefNum: INTEGER; VAR blessed: LONGINT): OSErr;
  5010.  
  5011. VAR
  5012.     myHPB:    HParamBlockRec;
  5013.     error:    OSErr;  
  5014.  
  5015. BEGIN      
  5016.     blessed := 0;
  5017.  
  5018.     WITH myHPB DO BEGIN
  5019.         ioNamePtr:= NIL;
  5020.         ioVRefNum:= vRefNum;    {get for default volume}
  5021.         ioVolIndex:= 0;        {we’re not making indexed calls}
  5022.         error := PBHGetVInfo(@myHPB, FALSE);
  5023.         IF error = noErr THEN
  5024.             blessed := ioVFndrInfo[1];
  5025.     END; {WITH}
  5026.  
  5027.     GetBlessed := error;
  5028. END;
  5029.  
  5030. The dirID of the blessed folder is myHPB.ioVFndrInfo[1]. If myHPB.ioVFndrInfo[1] is
  5031. 0, it means that there is no “blessed folder” (or it is an MFS volume). If myHPB.ioVFndrInfo[1]
  5032. is 2 (fsRtDirID), it means that the System file and Finder are at the root level.
  5033. myHPB.ioVFndrInfo[2] is the dirID of the startup application.
  5034.  
  5035. Note that the above routine will not work on machines without HFS, since they have no
  5036. PBHGetVInfo call, and the ioVFndrInfo field does not exist in the PBGetVInfo call.
  5037.  
  5038.  
  5039.  
  5040.  
  5041. æKY 68
  5042. æC #68:  Searching All Directories on an HFS Volume
  5043.  
  5044. See also:     Inside Macintosh, The File Manager
  5045.               Technical Note #66: Determining Which File System is Active
  5046.               Technical Note #67: Finding the “Blessed Folder”
  5047.  
  5048. Written by:    Jim Friedlander      December 23, 1985
  5049. Updated:                            March 1, 1988
  5050. Revised by:    Rick Blair           October 1, 1988
  5051. _______________________________________________________________________________
  5052.  
  5053. This Technical Note provides a simple algorithm for searching all directories on an
  5054. HFS volume.
  5055. Changes since March 1, 1988:  Previously we stopped the whole search when we encountered
  5056. any error, which caused trouble with privilege errors on AppleShare volumes.  Now an
  5057. error will only stop the enumeration of a particular catalog.
  5058. _______________________________________________________________________________
  5059.  
  5060. Now that we have a Hierarchical File System, it may be necessary to search the hierarchy
  5061. for files.  WARNING:  This can be a very time consuming process on a large volume. 
  5062. Generally speaking, your application should avoid searching entire volumes, relying
  5063. instead on files being in specific directories (same directory as the application, or
  5064. in the “blessed folder”) or on having the user find files with Standard File.
  5065.  
  5066. Under MFS, indexed calls to _PBGetFileInfo would return information about all files
  5067. on a given volume.  Under HFS, the same technique only returns information about
  5068. files in the current directory.
  5069.  
  5070. The most convenient way to search a tree structure is with a recursive search.  The
  5071. only potential problem with a recursive search is that it can use up a lot of stack
  5072. space.  The default stack space on the Macintosh is 8K; therefore, we have to try to
  5073. keep the stack frame small.  This example does that by enclosing the actual recursive
  5074. routine in a shell that can hold most of the variables that we will need, thus dramatically
  5075. reducing the size of the stack frame.  This example uses only 26 bytes of stack space
  5076. each time the routine recurses.  That is, it could search 100 levels deep (pretty
  5077. unlikely) and only use 2600 bytes of stack space.
  5078.  
  5079. Please notice that when the routine comes back from recursing, it has to clear the
  5080. non-local variable err, since the reason that the routine came back from recursing is
  5081. that _PBGetCatInfo returned an error:
  5082.  
  5083. EnumerateCatalog(myCPB.ioDrDirID);
  5084.      err:= 0; {clear error return on way back}
  5085.  
  5086. Please notice also that you must set myCPB.ioDrDirId each time you call _PBGetCatInfo.
  5087.  This is because if _PBGetCatInfo gets information about a file, it returns ioFlNum
  5088. (the file number) in the same location that the ioDrDirID previously occupied.
  5089.  
  5090. One other thing to watch for is when you check the file attributes bit to see if
  5091. you’ve got a file or a folder.  You need to check bit 4, the fifth least significant
  5092. bit.  Remember, the Toolbox bit manipulation routines order the bits in reverse order
  5093. from standard 68000 notation.  That’s why a BitTst(@myCPB.ioFlAttrib,3) is used.
  5094.  
  5095. Here is the routine in MPW Pascal:
  5096.  
  5097.  PROCEDURE EnumerShell(DirIDToSearch:Longint);
  5098.  VAR
  5099.  FName:     Str255;
  5100.  myCPB:     CInfoPBRec;
  5101.  err:       OSErr;
  5102.  myWDPB:    WDPBRec;
  5103.  TotalFiles,TotalDirectories:     integer;
  5104.  
  5105.     PROCEDURE EnumerateCatalog(dirIDToSearch: longint);
  5106.     VAR
  5107.     index:   integer;
  5108.  
  5109.     Begin {EnumerateCatalog}
  5110.         index:= 1;
  5111.         repeat
  5112.              FName:= '';
  5113.              myCPB.ioFDirIndex:= index;
  5114.              myCPB.ioDrDirID:= dirIDToSearch; {we need to do this 
  5115.                                                every time through}
  5116.  
  5117.              err:= PBGetCatInfo(@myCPB,FALSE);
  5118.  
  5119.              If err = noErr then 
  5120.                  if BitTst(@myCPB.ioFlAttrib,3) then Begin {we have a dir}
  5121.                      TotalDirectories:=TotalDirectories+1;
  5122.                      EnumerateCatalog(myCPB.ioDrDirID);
  5123.                      err:= 0;  {clear error return on way back}
  5124.                      End {if BitTst}
  5125.                  Else Begin {we have a file}
  5126.                      TotalFiles:= TotalFiles + 1;
  5127.                      {here we could call a routine that compares a
  5128.                       string with myCPB.ioNamePtr^}
  5129.                       End; {else} 
  5130.              index:= index + 1;
  5131.          until (err <> noErr);
  5132.     End;  {EnumerateCatalog}
  5133.  
  5134. Begin    {EnumerShell}
  5135.     TotalFiles:= 0;
  5136.     TotalDirectories:= 0;
  5137.     myWDPB.ioNamePtr := NIL;
  5138.     err:= PBHGetVol(@myWDPB,FALSE);     {get the default volume's vRefNum}
  5139.  
  5140.     with MyCPB do Begin
  5141.         ioNamePtr:= @FName;
  5142.         ioVRefNum:= myWDPB.ioWDVRefNum;     {for now, default vol,
  5143.                                              set this to what you want}
  5144.     End; {with}
  5145.     EnumerateCatalog(fsRtDirID); {the root level}
  5146. End;    {EnumerShell}
  5147.  
  5148.  
  5149. In MPW C:
  5150.  
  5151. #include <types.h>
  5152. #include <quickdraw.h>
  5153. #include <resources.h>
  5154. #include <fonts.h>
  5155. #include <windows.h>
  5156. #include <menus.h>
  5157. #include <textedit.h>
  5158. #include <events.h>
  5159. #include <files.h>
  5160. #include <packages.h>
  5161. #include <memory.h>
  5162.  
  5163.  
  5164. void EnumerateCatalog();                /* forward declaration */
  5165.  
  5166. /* the following variables will be global */
  5167.  
  5168. HFileInfo       myCPB;               /* for the PBGetCatInfo call */
  5169. char            fName[256];          /* place to hold file name */
  5170. short int       TotalFiles=0,TotalDirectories=0;
  5171.  
  5172. main()
  5173.  
  5174. {
  5175.     WindowPtr       MyWindow;
  5176.     Rect            myWRect;
  5177.      
  5178.     InitGraf(&qd.thePort);
  5179.     InitFonts();
  5180.     FlushEvents(everyEvent, 0);
  5181.     InitWindows();
  5182.     InitMenus();
  5183.     TEInit();
  5184.      
  5185.     SetRect(&myWRect,50,260,350,340);
  5186.     MyWindow = NewWindow(nil,&myWRect,"\pJim's Window",true,0,(WindowPtr)-1,false,0);
  5187.     SetPort(MyWindow);
  5188.      
  5189.     /* set up to call EnumerateCatalog */
  5190.     myCPB.ioNamePtr = fName;
  5191.     myCPB.ioVRefNum = 0;
  5192.     /* the default volume */
  5193.     EnumerateCatalog(fsRtDirID);          /* the root level */
  5194.      
  5195.     /* we're back, now display the total number of files and directories */
  5196.      
  5197.     NumToString(TotalFiles,fName);
  5198.     MoveTo(20,20);
  5199.     EraseRect(&(qd.thePort->portRect));   /* erase what used to be there */
  5200.     DrawString("\pTotal Files = ");
  5201.     DrawString(fName);
  5202.  
  5203.     NumToString(TotalDirectories,fName);
  5204.     MoveTo(20,40);
  5205.     DrawString("\pTotal Directories = ");
  5206.     DrawString(fName);
  5207.  
  5208.     MoveTo(20,60);
  5209.     DrawString("\pClick To Exit");     
  5210.  
  5211.     while (!Button());
  5212. } /*main*/
  5213.  
  5214. /*--------------------------------------------------------------------------*/
  5215.  
  5216. void EnumerateCatalog(dirIDToSearch)
  5217. long int    dirIDToSearch;
  5218.  
  5219. { /*EnumerateCatalog */
  5220.  
  5221.     short int      index=1;               /* for ioFDirIndex */
  5222.     OSErr              err;               /* the usual */
  5223.  
  5224.     do
  5225.     {
  5226.          myCPB.ioFDirIndex= index;         /* set up the index */
  5227.  
  5228. /* we need to do this every time through, since GetCatInfo returns ioFlNum in this
  5229. field*/
  5230.          myCPB.ioDirID= dirIDToSearch;     
  5231.          err= PBGetCatInfo(&myCPB,false);
  5232.  
  5233.          if (err == noErr) 
  5234.          {
  5235.              /* just for demo's sake, we will display the file/folder names */
  5236.              MoveTo(20,20);
  5237.              EraseRect(&(qd.thePort->portRect));   /* clear out the last name */
  5238.              DrawString(fName);               /* display the name */
  5239.  
  5240.              /* check the file attributes for folderhood */
  5241.              if (((myCPB.ioFlAttrib>>4) & 0x01) == 1)
  5242.  
  5243.              { /*we have a dir*/
  5244.                   /* we have a directory, increment the file counter */
  5245.                   TotalDirectories += 1;
  5246.                   EnumerateCatalog(myCPB.ioDirID);     /* recurse */
  5247.                   err = 0;     /* clear error return on way back*/
  5248.              } /*if ((myCPB.ioFlAttrib>>4) & 0x01 == 1)*/
  5249.              else 
  5250.              { /*we have a file*/
  5251.                   /* we have a file, increment the file counter */
  5252.                   TotalFiles += 1;
  5253.                   /* we could list the file here */
  5254.              } /*else*/
  5255.         index += 1;     /* increment the index for GetCatInfo */
  5256.         } /* if (err == noErr) */
  5257.     } while (err == noErr);
  5258. } /* EnumerateCatalog */
  5259.  
  5260. /* that's all folks!! */
  5261.  
  5262. Please make sure that you are running under HFS before you use this routine.  You can
  5263. do partial searches of a volume by specifying a starting dirId other than fsRtDirID.
  5264.  
  5265.  
  5266. æKY 69
  5267. æC #69: Setting ioFDirIndex in PBGetCatInfo Calls
  5268.  
  5269. See also:      The File Manager 
  5270.                Technical Note #24 — Available Volumes and Files
  5271.                Technical Note #67 — Finding the Blessed Folder
  5272.  
  5273. Written by:    Jim Friedlander      February 15, 1986
  5274. Updated:                            March 1, 1988
  5275. _______________________________________________________________________________
  5276.  
  5277. This technical note describes how to set ioFDirIndex for PBGetCatInfo.
  5278. _______________________________________________________________________________
  5279.  
  5280. The File Manager chapter of Inside Macintosh volume IV is not very specific in describing
  5281. how to use ioFDirIndex when calling PBGetCatInfo. It correctly says that ioFDirIndex
  5282. should be positive if you are making indexed calls to PBGetCatInfo (analogous to
  5283. making indexed calls to PBGetVInfo as described in Technical Note #24). However, the
  5284. statement “If ioFDirIndex is negative or 0, the File Manager returns information
  5285. about the file having the name in ioNamePtr...” is not specific enough.
  5286.  
  5287. If ioFDirIndex is 0, you will get information about files or directories, depending
  5288. on what is specified by ioNamePtr^. 
  5289.  
  5290. If ioFDirIndex is –1, you will get information about directories only. The name in
  5291. ioNamePtr^ is ignored. 
  5292.  
  5293. •••Click on the Illustration button below to view the example.••• 
  5294.  
  5295. Calling PBGetCatInfo
  5296.  
  5297. We will now make calls to PBGetCatInfo of the form:
  5298.  
  5299.     err:= PBGetCatInfo(@myCInfoPBRec,FALSE);
  5300.  
  5301.  
  5302. Note: We will assume that we just have a WDRefnum and a file name—the information
  5303. that SFGetFile returns. 
  5304.  
  5305.  
  5306. Setting up the parameter block
  5307.  
  5308. We will use the following fields in the parameter block. Before the call, ioCompletion
  5309. will always be set to NIL, ioNamePtr will always point at a str255, ioVRefNum will
  5310. always contain a WDRefNum that references the directory 'SubFiles', and offset 48
  5311. (dirID/flNum) will always contain a zero:
  5312.  
  5313.     Offset in
  5314.     parameter block    Variable name(s)
  5315.                12        ioCompletion
  5316.                18        ioNamePtr    
  5317.                22        ioVRefNum
  5318.                28        ioFDirIndex
  5319.                48        ioDirID/ioFLNum/ioDrDirID
  5320.               100        ioDrParID/ioFlParID    
  5321.  
  5322.  
  5323. Sample calls to PBGetCatInfo
  5324.  
  5325. The first example will call PBGetCatInfo for the file 'File3'—we will get information
  5326. about the file (ioFDirIndex = 0):
  5327.  
  5328.     Before the call        After the call
  5329.     ioNamePtr^:    'File3'    ioNamePtr^:    'File3'
  5330.     ioFDirIndex:    0    Offset 48(ioFLNum):    a file number
  5331.             Offset 100(parID):    57
  5332.  
  5333. Now we will get information about the directory that is specified by the iovRefNum
  5334. (ioFDirIndex = –1). Notice that ioNamePtr^ is ignored:
  5335.  
  5336.     Before the call        After the call
  5337.     ioNamePtr^:    ignored    ioNamePtr^:    'SubFiles'
  5338.     ioFDirIndex:    –1    Offset 48(dirID):    57
  5339.             Offset 100(parID):    37
  5340.  
  5341. Notice that, since ioNamePtr^ is ignored, Offset 48 contains the dirID of the directory
  5342. specified by the iovRefNum that we passed in and that Offset 100 contains the parent
  5343. ID of that directory.
  5344. Notice that if we try to get information about the directory SubFiles by calling
  5345. PBGetCatInfo with ioFDirIndex set to 0, we will get an error –43 (File not found
  5346. error) back because there is neither a file nor a directory with the name 'SubFiles'
  5347. in the directory that ioVRefNum refers to.
  5348.  
  5349. If you specify a full pathname in ioNamePtr^, then the call returns information about
  5350. that path, whether it is a directory or a file. The ioVRefNum is ignored:
  5351.  
  5352.     Before the call        After the call
  5353.     ioNamePtr^:    'Root:Sys'    ioNamePtr^:    'Root:Sys'
  5354.     ioFDirIndex:    0    Offset 48 (dirID):    17
  5355.     ioVRefNum:    refers to 'SubFiles'    Offset 100 (parID):    2
  5356.  
  5357. Or, if the full pathname specifies a file, the iovRefNum is overridden:
  5358.  
  5359.     Before the call        After the call
  5360.     ioNamePtr^:    'Root:Sys:Finder'    ioNamePtr^:    'Root:Sys:Finder'
  5361.     ioFDirIndex:    0    Offset 48 (flNum):    fileNumber
  5362.     ioVRefNum:    refers to 'SubFiles'    Offset 100 (parID):    17
  5363.  
  5364. Or, given an ioVRefNum that refers to MyFiles2 and a partial pathname in ioNamePtr^,
  5365. we’ll get information about the directory 'SubFiles':
  5366.  
  5367.     Before the call        After the call
  5368.     ioNamePtr^:    'SubFiles'    ioNamePtr^:    'SubFiles'
  5369.     ioFDirIndex:    0    Offset 48 (dirID):    57
  5370.     ioVRefNum:    refers to 'MyFiles2'    Offset 100 (parID):    37
  5371.  
  5372.  
  5373. PBGetCatInfo and The Poor Man’s Search Path (PMSP)
  5374.  
  5375. If no ioDirID is specified (ioDirID is set to zero), calls to PBGetCatInfo will return
  5376. information about a file in the specified directory, but, if no such file is found,
  5377. will continue searching down the Poor Man’s Search Path. Note: the PMSP is not used
  5378. if ioFDirIndex is non-zero ( either –1 or >0). The default PMSP includes the directory
  5379. specified by ioVRefNum (or, if ioVRefNum is 0, the default directory) and the directory
  5380. that contains the System File and the Finder—the blessed folder. So for example:
  5381.  
  5382.     Before the call        After the call
  5383.     ioNamePtr^:    'System'    ioNamePtr^:    'System'
  5384.     ioFDirIndex:    0    Offset 48 (ioFLNum):    a file number
  5385.             Offset 100 (parID):    17
  5386.  
  5387. You must be careful when using PBGetCatInfo in this way to make sure that the file
  5388. you’re getting information about is in the directory that you think it is, and not in
  5389. directory further down the Poor Man’s Search Path. Of course, this does not present a
  5390. problem if you are using the fName and the vRefNum that SFGetFile returns.
  5391. If you want to specifically look at a file in the blessed folder, please use the
  5392. technique described in technical note #67 to get the dirID of the 'blessed folder'
  5393. and then use that dirID as input in the ioDirID field of the parameter block (offset
  5394. 48).
  5395.  
  5396.  
  5397. Summary (DirID = 0 in all the following):
  5398.     If ioFDirIndex is set to 0: 
  5399.         1) Information will be returned about files.
  5400.         2) Information will be returned about directories as follows:
  5401.             A) If a partial pathname is specified by ioNamePtr^ then the volume
  5402.               and directory will be taken from ioVRefNum.
  5403.             B) If a full pathname is specified by ioNamePtr^. In this case,
  5404.                ioVRefNum is ignored.
  5405.  
  5406.     If ioFDirIndex is set to –1:
  5407.         1) Only information about directories will be returned.
  5408.         2) The name pointed to by ioNamePtr is ignored.
  5409.         3) If DirID and ioVRefNum are 0, you’ll get information about the
  5410.            default directory.
  5411.  
  5412.  
  5413.  
  5414.  
  5415. æKY 70
  5416. æC #70: Forcing Disks to be Either 400K or 800K
  5417.  
  5418. See also:      The Disk Driver
  5419.                The Disk Initialization Package
  5420.  
  5421. Written by:    Rick Blair       February 13, 1986
  5422. Updated:                        March 1, 1988
  5423. _______________________________________________________________________________
  5424.  
  5425. This document explains how to initialize a disk as either single- or double- sided.
  5426. It only applies to 800K drives, of course.
  5427. _______________________________________________________________________________
  5428.  
  5429. You can call the disk driver to initialize a disk and determine programmatically
  5430. whether it should be initialized as single - (MFS) or double - (HFS) sided. All you
  5431. have to do is call the .Sony driver directly to do the formatting then the Disk Initialization
  5432. Package to write the directory information.
  5433.  
  5434. NOTE: This is not the way you should normally format disks within an application. If
  5435. the user puts in an unformatted disk, you should let her or him decide whether it
  5436. becomes single- or double-sided via the Disk Initialization dialog. This automatically
  5437. happens when you call DIBadMount or the user inserts a disk while in Standard File.
  5438. The intent of this technical note is to provide a means for specific applications to
  5439. produce, say, 400K disks. An example might be a production disk copying program.
  5440.  
  5441.  
  5442. From MPW Pascal:
  5443.  
  5444.     VAR
  5445.        error:      OSErr;
  5446.        IPtr:       ^INTEGER;
  5447.        paramBlock: ParamBlockRec; {needs OSIntf}
  5448.     ...
  5449.     WITH paramBlock DO BEGIN
  5450.        ioRefNum := -5;    {.Sony driver}
  5451.         ioVRefNum := 1;    {drive number}
  5452.        csCode := 6;    {format control code}
  5453.        IPtr:=@csParam;    {pretend it's an INTEGER}
  5454.         IPtr^:=1;        {number of sides}
  5455.     END;
  5456.      error:=PBControl(@paramBlock, FALSE);    {do the call}
  5457.      IF error=ControlErr THEN
  5458.       {you are under MFS, which doesn't support control code 6, but it}
  5459.        {would always get formatted single-sided anyway.}
  5460.      {other errors are possible: ioErr, etc.}
  5461.     END;
  5462.  
  5463. From MPW C:
  5464.  
  5465.     OSErr            error;
  5466.     CntrlParam        paramBlock; 
  5467.     
  5468.  
  5469.     paramBlock.ioCRefNum = -5;    /*.Sony driver*/
  5470.     paramBlock.ioVRefNum = 1;    /*drive number*/
  5471.     paramBlock.csCode = 6;        /*format control code*/
  5472.     paramBlock.csParam[0]=1;         /*for single sided,2 for double-sided*/
  5473.  
  5474.     error=PBControl(¶mBlock, false);/*do the call*/
  5475.     if (error==controlErr) ;
  5476.     /*you are under MFS, which doesn't support control code 6, but it*/
  5477.     /*would always get formatted single-sided anyway.*/
  5478.     /*other errors are possible: ioErr, etc.*/
  5479.  
  5480. You then call DIZero to write a standard (MFS or HFS) directory. It will produce MFS
  5481. if you formatted it single-sided, and HFS if you formatted 
  5482. double-sided.
  5483.  
  5484.  
  5485.  
  5486.  
  5487. æKY 71
  5488. æC #71: Finding Drivers in the Unit Table
  5489.  
  5490. See also:      The Device Manager
  5491.  
  5492. Written by:    Rick Blair    February 4, 1986
  5493. Updated:                     March 1, 1988
  5494. _______________________________________________________________________________
  5495.  
  5496. This note will explain how code can be written to determine the reference number of a
  5497. previously installed driver when only the name is known. 
  5498. Changes since 2/86: Since the driver can be purged and the DCE still be allocated,
  5499. the code now tests for dCtlDriver being NIL as well.
  5500. _______________________________________________________________________________
  5501.  
  5502. You should already be familiar with The Device Manager chapter of Inside Macintosh
  5503. before reading this technical note.
  5504.  
  5505. The Pascal code at the end of this note demonstrates how to obtain the reference
  5506. number of a driver that has been installed in the Unit Table. The reference number
  5507. may then be used in subsequent calls to the Device Manager such as Open, Control and
  5508. Prime.
  5509.  
  5510. One thing to note is that the dRAMBased bit really only tells you whether dCtlDriver
  5511. is a pointer or a handle, not necessarily whether the driver is in ROM or RAM. SCSI
  5512. drivers, for instance, are in RAM but not relocatable; their DCE entries contain
  5513. pointers to them.
  5514.  
  5515. From MPW Pascal:
  5516.  
  5517.     PROCEDURE GetDrvrRefNum(driverName: Str255; VAR drvrRefNum: INTEGER);
  5518.  
  5519.        TYPE
  5520.           WordPtr      = ^INTEGER;
  5521.  
  5522.        CONST
  5523.           UTableBase   = $11C;     {low memory globals}
  5524.           UnitNtryCnt  = $1D2;
  5525.  
  5526.           dRAMBased    = 6;        {bit in dCtlFlags that indicates ROM/RAM}
  5527.           drvrName     = $12;      {length byte and name of driver [string]}
  5528.  
  5529.        VAR
  5530.           negCount    : INTEGER;
  5531.           DCEH        : DCtlHandle;
  5532.           drivePtr    : Ptr;
  5533.           s           : Str255;
  5534.  
  5535.        BEGIN
  5536.           UprString(driverName, FALSE); {force same case for compare}
  5537.  
  5538.           negCount := - WordPtr(UnitNtryCnt)^; {get -(table size)}
  5539.  
  5540.           {Check to see that driver is installed, obtain refNum.}
  5541.           {Assumes that an Open was done previously -- probably by an INIT.}
  5542.           {Driver doesn't have to be open now, though.}
  5543.  
  5544.           drvrRefNum := - 12 + 1;  {we'll start with driver refnum = -12,
  5545.                                     right after .ATP entry}
  5546.  
  5547.           {Look through unit table until we find the driver or reach the end.}
  5548.  
  5549.           REPEAT
  5550.              drvrRefNum := drvrRefNum - 1; {bump to next refnum}
  5551.              DCEH := GetDCtlEntry(drvrRefNum); {get handle to DCE}
  5552.  
  5553.              s := '';              {no driver, no name}
  5554.  
  5555.              IF DCEH <> NIL THEN
  5556.                 WITH DCEH^^ DO BEGIN {this is safe -- no chance of heap moving
  5557.                                       before dCtlFlags/dCtlDriver references}
  5558.          IF (dCtlDriver <> NIL) THEN BEGIN
  5559.                     IF BTST(dCtlFlags, dRAMBased) THEN
  5560.                        drivePtr := Handle(dCtlDriver)^ {zee deréference}
  5561.                     ELSE
  5562.                        drivePtr := Ptr(dCtlDriver);
  5563.  
  5564.                     IF drivePtr <> NIL THEN BEGIN
  5565.                        s := StringPtr(ORD4(drivePtr) + drvrName)^;
  5566.                        UprString(s,FALSE); {force same case for compare}
  5567.                     END;
  5568.          END;             {IF}
  5569.                 END;               {WITH}
  5570.           UNTIL (s = driverName) OR (drvrRefNum = negCount);
  5571.  
  5572.           {Loop until we find it or we've just looked at the last slot.}
  5573.  
  5574.           IF s <> driverName THEN drvrRefNum := 0; {can't find driver}
  5575.        END;
  5576.  
  5577. From MPW C:
  5578.  
  5579. short       GetDrvrRefNum(driverName)
  5580. char        *driverName[256];
  5581.  
  5582. {  /* GetDrvrRefNum */
  5583.  
  5584.     #define        UnitNtryCnt    0x1d2
  5585.  
  5586.     /*bit in dCtlFlags that indicates ROM/RAM*/
  5587.     #define          dRAMBased     6        
  5588.     /*length byte and name of driver [string]*/
  5589.     #define          drvrName      0x12      
  5590.  
  5591.     short            negCount,dRef;
  5592.     DCtlHandle        DCEH;
  5593.     char            *drivePtr,*s;
  5594.  
  5595.     negCount = -*(short *)(UnitNtryCnt); /*get -(table size)*/
  5596.         
  5597.     /*Check to see that driver is installed, obtain refNum.*/
  5598.     /*Assumes that an Open was done previously -- probably by an INIT.*/
  5599.     /*Driver doesn't have to be open now, though.*/
  5600.         
  5601.     dRef = -12 + 1;  /*we'll start with driver refnum == -12,
  5602.                         right after .ATP entry*/
  5603.         
  5604.     /*Look through unit table until we find the driver or reach the     end.*/
  5605.         
  5606.     do
  5607.     {
  5608.         dRef -= 1; /*bump to next refnum*/
  5609.         DCEH = GetDCtlEntry(dRef); /*get handle to DCE*/
  5610.             
  5611.         s = "";
  5612.  
  5613.         if ((DCEH != nil) && ( (**DCEH).dCtlDriver != nil) )
  5614.         {
  5615.             if (((**DCEH).dCtlFlags >> dRAMBased) & 1)
  5616.                                 /* test dRamBased bit */
  5617.                 drivePtr = *(Handle) (**DCEH).dCtlDriver;
  5618.                                 /*zee deréference*/
  5619.             else
  5620.                 drivePtr = (**DCEH).dCtlDriver;
  5621.             
  5622.             if (drivePtr != nil)  
  5623.                 s = drivePtr + drvrName;
  5624.         }
  5625.     } while (EqualString(s,driverName,0,0) && (dRef != negCount));
  5626.     /*Loop until we find it or we've just looked at the last slot.*/
  5627.         
  5628.     if (EqualString(s,driverName,0,0))  
  5629.         return dRef;
  5630.     else
  5631.         return 0; /*can't find driver*/
  5632. }/* GetDrvrRefNum */
  5633.  
  5634.  
  5635. That’s all there is to locating a driver and picking up the reference number.
  5636.  
  5637.  
  5638.  
  5639. æKY 72
  5640. æC #72: Optimizing for the LaserWriter — Techniques
  5641.  
  5642. See also:    The Print Manager
  5643.              QuickDraw
  5644.              TextEdit
  5645.              The AppleTalk Manager
  5646.              LaserWriter Reference Manual
  5647.              Technical Note #41 — Drawing Into an Offscreen Bitmap
  5648.              Technical Note #118 — How to Check and Handle Printing Errors
  5649.              PostScript Language Reference Manual, Adobe Systems
  5650.              PostScript Language Tutorial and Cookbook, Adobe Systems
  5651.  
  5652. Written by:     Ginger Jernigan         February 3, 1986
  5653. Modified by:    Scott “ZZ” Zimmerman    January 1, 1988
  5654. Updated:                                March 1, 1988
  5655. _______________________________________________________________________________
  5656.  
  5657. This technical note describes techniques for optimizing your code for printing on the
  5658. LaserWriter.
  5659. _______________________________________________________________________________
  5660.  
  5661. Although the Print Manager was originally designed to allow application code to be
  5662. printer independent, there are some things about the LaserWriter that in some cases
  5663. have to be addressed in a printer dependent way. This technical note describes what
  5664. the LaserWriter can and can’t do, memory considerations, speed considerations, as
  5665. well as other things you need to watch out for if you want to make your printing more
  5666. efficient on the LaserWriter.
  5667.  
  5668.  
  5669. How To Determine Which Printer Is Currently Selected
  5670.  
  5671. With the addition of new picture comments as well as the PrGeneral procedure, your
  5672. application should never need to know the type of device it is connected to. However,
  5673. some developers feel that their application should be able to take advantage of all
  5674. of the features provided by a particular device, not just those provided by the Print
  5675. Manager. In doing this, the application becomes device dependent, and also can produce
  5676. unpredictable results on third party, as well as new Apple printing devices. For this
  5677. reason, it is strongly recommended that you use only the features provided by the
  5678. Print Manager, and do not try to use unsupported device features.
  5679.  
  5680. Even though there is no supported method for determining a device’s type, there is
  5681. one method described in the original Inside Macintosh, that will still work for ImageWriter
  5682. and LaserWriter printer drivers. This method is not supported, meaning that at some
  5683. point in the future it will no longer work. If you use this method in your application,
  5684. it is up to you to weigh the value of the feature against the compatibility risk. The
  5685. following method will work for all ImageWriter, ImageWriter II, and LaserWriter (I,
  5686. I+, IINT, IINTX) drivers. Since all new devices released from Apple and third party
  5687. developers will have their own unique ID, it is up to you to decide what to do with
  5688. an ID that your application does not recognize:
  5689.  
  5690. If you are using the high-level Print Manager interface, first call PrValidate to
  5691. make sure you have the correct print record. Look at the high byte of the wdev word
  5692. in the PrStl subrecord of the print record.(Note: If you have your own driver and
  5693. want to have your own number, please let us know and we’ll register it.) Here is the
  5694. current list of printer ID’s:
  5695.  
  5696.     Printer                                    wDev
  5697.     ImageWriter I, ImageWriter II:              1
  5698.     LaserWriter, LaserWriter Plus,
  5699.     LaserWriter II NT, LaserWriter II NTX:      3
  5700.     LaserWriter II SC:                          4
  5701.     ImageWriter LQ:                             5
  5702.  
  5703. If you are using the low-level Print Manager interface, there is no dependable way of
  5704. getting the wDev information. You should not attempt to determine the device ID when
  5705. using the low-level Print Manager interface.
  5706.  
  5707.  
  5708. Using QuickDraw With the LaserWriter
  5709.  
  5710. When you print to the LaserWriter, all of the QuickDraw calls you make are translated
  5711. (via QuickDraw bottlenecks), into PostScript, which is in the LaserWriter ROM. Most
  5712. of the operations available in QuickDraw are available in PostScript with a few exceptions.
  5713. The LaserWriter driver does not support:
  5714.  
  5715.  • XOR and NotXOR transfer modes.
  5716.  
  5717.  • The grafverb invert.
  5718.  
  5719.  • SetOrigin calls within PrOpenPage and PrClosePage calls. Use OffsetRect 
  5720.    instead. (This is fixed in 3.0 and newer versions of the driver.)
  5721.  
  5722.  • Regions are ignored. You can simulate regions using polygons or bitmaps. 
  5723.    Take a look at Technical Note #41 for how to create offscreen bitmaps.
  5724.  
  5725.  • Clip regions should be limited to rectangles.
  5726.  
  5727.  • There is a small difference in character widths between screen fonts and 
  5728.    printer fonts. Only the endpoints of text strings will be the same.
  5729.  
  5730.  
  5731. What You See Is Not Always What You Get
  5732.  
  5733. Unfortunately, what you see on the screen is not always what you get. If you are
  5734. using standard graphical objects, like rectangles, circles, etc., the object will be
  5735. the same size on the LaserWriter as it is on the screen. There are, however, two
  5736. types of objects where this is not the case: text and bitmaps. 
  5737.  
  5738. As stated above, there is a difference between the widths of characters on the screen
  5739. and the widths of characters on the printer because of the difference in resolution.
  5740. However, to maintain the integrity of line breaks, the driver will change the word
  5741. spacing and the character spacing to maintain the endpoints of the lines as specified
  5742. (see below). What this all means is that you can’t count on the positions or the
  5743. widths of printed characters being exactly the same as they are on the screen. That’s
  5744. why in MacDraw, for example, if you carefully place text in a rectangle and print it,
  5745. sometimes the text extends beyond the bounds of the rectangle. If you are doing your
  5746. own line layout, that is positioning the words on the line your self, you may want to
  5747. disable the LaserWriter’s line layout routines. To do this, use the LineLayoutOff
  5748. picture comment described in the LaserWriter Reference Manual and Technical Note
  5749. #91.
  5750.  
  5751. The exception to this is if you are running on the 128K ROMs. The 128K ROM Font Manager
  5752. supports the specification of fractional pixel widths for screen fonts, increasing
  5753. the screen to printer accuracy. This fractional width feature is disabled by default.
  5754. To enable it, you can use the SetFractEnable procedure, after you have called InitFonts.
  5755.  
  5756. You can use picComments to left; right; or center-justify text. Only the left, right,
  5757. or center endpoints will be accurate. If the text is fully justified, both endpoints
  5758. will be accurate. These picComments are discussed in Technical Note #91.
  5759.  
  5760.  
  5761. Memory Considerations
  5762.  
  5763. In order to print to the LaserWriter, you need to make sure that you have enough
  5764. memory available to load the driver’s code. The best way to do this is to have all
  5765. the code you need for printing in a separate segment and unload everything else. When
  5766. you print to the LaserWriter you are only able to print in Draft mode. You will not
  5767. be able to spool, (as the ImageWriter does in the standard or high quality settings,)
  5768. and your print code, data and the driver code will have to be resident in memory. In
  5769. terms of memory requirements, you will need to have from 15 to 20K available (for the
  5770. 1.0 driver and AppleTalk; more for 3.0 driver), every time you print.
  5771.  
  5772.  
  5773. Printable Paper Area
  5774.  
  5775. On the LaserWriter there is a 0.45 inch border that surrounds the printable area of
  5776. the paper (this is assuming an 8.5" x 11" paper). This is different than the available
  5777. print area of the ImageWriter. You cannot print a larger area because PostScript
  5778. takes the available amount of memory in the printer and centers it on the paper and
  5779. we don’t have enough RAM in the LaserWriter to image an entire sheet of paper.
  5780.  
  5781. Page Sizes
  5782.  
  5783. Many developers have expressed a desire to support page sizes other than those provided
  5784. by the Apple printer drivers. Even though some devices can physically support other
  5785. page sizes, there is no way for an application to tell the driver to use this size.
  5786. On the ImageWriter driver, it is possible to modify certain fields in the print record,
  5787. and expand the printable area of the page. However, each of the Apple drivers implements
  5788. the page sizes in a different way. No one method will work for all the drivers. Because
  5789. of this, it is strongly recommended that applications do not attempt to change the
  5790. page sizes provided in the “Style” dialog. If your application currently supports
  5791. page sizes other than those provided by the printer driver, you are taking a serious
  5792. compatibility risk with future Apple and third party printer drivers.
  5793.  
  5794.  
  5795. Speed Considerations
  5796.  
  5797. Although the LaserWriter is pretty speedy as it is, there are some things you can do
  5798. in your code that will make sure it remains speedy.
  5799.  
  5800.  • Try to avoid using the QuickDraw Erase calls (i.e. EraseRect, EraseOval, 
  5801.    etc.). It takes a lot of time to handle the erase function because every bit 
  5802.    (90,000 bits/sq. in.) has to be cleared. Erasing is unnecessary because the 
  5803.    paper does not need to be erased the way the screen does.
  5804.  
  5805.  • Printing patterns takes time, since the bitmap for the pattern has to be 
  5806.    built. The patterns, black, white, and all the gray patterns have been 
  5807.    optimized to use the PostScript gray scales. If you use a different pattern, 
  5808.    it works but just takes longer than usual. Also, in the 1.0 driver patterns 
  5809.    are NOT rotated; in 3.0 they are.
  5810.  
  5811.  • Try to avoid changing fonts frequently. PostScript has to build each 
  5812.    character it need by either using the drawing commands for the built-in 
  5813.    fonts (Times, Helvetica, etc.) or by resizing bitmaps downloaded from 
  5814.    screen fonts from the Macintosh. As each character is built it is cached 
  5815.    (if there’s room), so if that character is needed again PostScript gets if 
  5816.    from the cache. When the font changes, the characters have to be built from 
  5817.    scratch in the new font. This takes time. Also, if the font isn’t in the 
  5818.    LaserWriter, time is taken in downloading it from the Macintosh. If the 
  5819.    user has the option of choosing fonts, you have no control over that. 
  5820.    However, if you are the one controlling which fonts to use, keep this in 
  5821.    mind.
  5822.  
  5823.  • Avoid using TextBox. It makes calls to EraseRect for every line of text it 
  5824.    draws. The calls to EraseRect slow the printer down. You might want to use 
  5825.    a different method of displaying text (like DrawString or DrawText) or write 
  5826.    your own version of TextBox. If you are making TextBox calls now, switching 
  5827.    to something else can improve your speed on the order of 5 to 1.
  5828.  
  5829.  • Because of the way rectangle intersections are determined, if your clip 
  5830.    region falls outside of the rPage rectangle, you will slow down the printer 
  5831.    substantially. By making sure your clip region is entirely within the rPage 
  5832.    rectangle, you can get a speed improvement of approximately 4 to 1.
  5833.  
  5834.  • Do not use spool-a-page/print-a-page as some applications do when printing 
  5835.    on the ImageWriter. It will slow things down considerably because of all of 
  5836.    the preparation that has to be done when a job is initiated. See the 
  5837.    discussion below.
  5838.  
  5839.  • Using DrawChar to place every character you’re printing can take tons of 
  5840.    time. One reason, of course, is because it has to go through the bottlenecks 
  5841.    for every character that is drawn. The other is that the printer driver does 
  5842.    its best to do line layout, making the character spacing just right. 
  5843.    If you’re trying to position characters and the driver is trying to position 
  5844.    characters too, there is conflict and printing takes much longer than it 
  5845.    needs to. In the 3.0 driver there are picture comments that turn off the 
  5846.    line layout optimization, alleviating some of the problem.
  5847.  
  5848.  
  5849. Clipping Within Text Strings
  5850.  
  5851. When clipping characters out of a string, make sure that the clipping rectangle or
  5852. region is greater than the bounding box of the text you want to clip. The reason is
  5853. that if you clip part of a character, (like a descender), the clipped character will
  5854. have to be rebuilt (see above). This takes time. Actually, because of the difference
  5855. between screen fonts and printer fonts, chances are that you will not be able to
  5856. accurately clip the right characters unless you are running on the 128K ROMs and have
  5857. fractional pixel widths turned on.
  5858.  
  5859.  
  5860. When to Validate the Print Record
  5861.  
  5862. To validate the print record, call PrValidate. You need validation to check to see if
  5863. all of the fields are accurate according to the current printer selected and the
  5864. current version of the driver. You should call PrValidate when your application starts
  5865. and you’ve allocated a new print record, or whenever you need to access information
  5866. from the print record (like when you get rPage). The routines PrStlDialog and PrJobDialog
  5867. do call PrValidate when they are called, so you don’t have to worry about it then.
  5868.  
  5869.  
  5870. Spool-a-Page/Print-a-Page
  5871.  
  5872. Many applications, when printing to the ImageWriter, will use the approach of spool a
  5873. page, then print a page, in order to get around problems with limited disk space.
  5874. This causes problems with the AppleTalk ImageWriter and the LaserWriter. As noted
  5875. above, there is no spooling when printing to the LaserWriter. To optimize, you may
  5876. want to consider having two print loops. One would be spool-a-page/print-a-page for
  5877. the ImageWriter. The other would be for printing without spooling for the LaserWriter.
  5878.  
  5879.  
  5880. Empty QuickDraw Objects
  5881.  
  5882. QuickDraw objects that are empty (meaning they have no pixels in them) and are filled
  5883. but not framed, will not print on the ImageWriter and will not show up on the screen.
  5884. On the LaserWriter, however, they are real objects and will be printed.
  5885.  
  5886.  
  5887. Cancelling or Pausing the Printing Process
  5888.  
  5889. If you install a procedure for handling requests to cancel printing, with the option
  5890. of pausing the printing process, beware of timeout problems. Communication between
  5891. the Macintosh and the LaserWriter has to be maintained. If there is no communication
  5892. for a period of time (up to two minutes), the printer will timeout and the print job
  5893. will be terminated. You may want to disable your Pause button while printing to the
  5894. LaserWriter, enabling it only for the ImageWriter. It should be noted that to do
  5895. this, you will need to determine which type of printer you are connected to. This
  5896. will make your application device dependent and will probably lead to compatibility
  5897. problems in the future.
  5898.  
  5899.  
  5900. Error Messages
  5901.  
  5902. The errors below are reported by the Print Manager. If an error occurs that does not
  5903. belong to the Print Manager, it will put the error in low memory (retrievable by
  5904. PrError) and terminate printing if necessary.
  5905.  
  5906. If you encounter an error in the middle of a print loop, do not jump out; fall through
  5907. and let the Print Manager terminate properly. For example: if you get an error when
  5908. calling PrOpenDoc, make sure you call PrCloseDoc before exiting. If you get an error
  5909. on PrOpenPage, make sure you call PrClosePage and PrCloseDoc before exiting the Print
  5910. loop. For more information on this, see Technical Note #118.
  5911.  
  5912. Error Code   Constant      Description
  5913.          0   noErr         No error
  5914.        128   iPrAbort      Abort the printing process (Command-period)
  5915.         –1   iPrSavePFil   Problem saving print file
  5916.        –17   controlErr    Un-implemented Control call
  5917.        –27   iIOAbort      I/O problems
  5918.       –108   iMemFullErr   Not enough heap space
  5919.  
  5920. The following are LaserWriter-specific:
  5921.  
  5922.      –4101                 Printer not found or closed
  5923.      –4100                 Connection just closed
  5924.      –4099                 Write request too big
  5925.      –4098                 Request already active
  5926.      –4097                 Bad connection refnum
  5927.      –4096                 No free CCBs (Connect Control Blocks) available
  5928.      –8133                 PostScript error occurred during transmission of 
  5929.                            data to printer. Most often caused by a bug in the 
  5930.                            PostScript code being downloaded.
  5931.      –8132                 Timeout occured. This error is returned when no data 
  5932.                            has been sent to the printer for 2 minutes.  Usually 
  5933.                            caused by extremely long imaging times.
  5934.  
  5935. The most common error encountered will be –4101. This is generated if no LaserWriter
  5936. is selected. Since this is so common, it is a good idea to put up an alert asking the
  5937. user to open the Chooser and select a printer when this error is encountered.
  5938.  
  5939. You can also encounter AppleTalk errors. For a list of these see the AppleTalk Manager
  5940. chapter of Inside Macintosh.
  5941.  
  5942.  
  5943.  
  5944. æKY 73
  5945. æC #73: Color Printing
  5946.  
  5947. See also:       QuickDraw
  5948.                 The Printing Manager
  5949.                 PostScript Language Reference Manual, Adobe Systems
  5950.     
  5951. Written by:     Ginger Jernigan         February 3, 1986
  5952. Modified by:    Scott “ZZ” Zimmerman    January 1, 1988
  5953. Updated:                                March 1, 1988
  5954. _______________________________________________________________________________
  5955.  
  5956. This discusses color printing in a Macintosh application.
  5957. _______________________________________________________________________________
  5958.  
  5959. Whereas the original eight-color model of QuickDraw was sufficient for printing in
  5960. color on the ImageWriter II, the introduction of Color QuickDraw has created the need
  5961. for more sophisticated printing methods.
  5962.  
  5963. The first section describes using the eight-color QuickDraw model with the ImageWriter
  5964. II and ImageWriter LQ drivers. Since the current Print Manager does not support Color
  5965. GrafPorts, the eight-color model is the only method available for the ImageWriters.
  5966.  
  5967. The next section describes a technique that can be used for printing halftone images
  5968. using PostScript (when it is available). Also described is a device independent technique
  5969. for sending the PostScript data. This technique can be used on any LaserWriter driver
  5970. 3.0 or later. It will work with all LaserWriters except the the LaserWriter IISC.
  5971.  
  5972. It is very likely that better color support will be added to the Print Manager in the
  5973. future. Until then, these are the best methods available.
  5974.  
  5975.  
  5976. Part 1, ImageWriters
  5977.  
  5978. The ImageWriter drivers are capable of generating each of the eight standard colors
  5979. defined in QuickDraw by the following constants:
  5980.  
  5981.     whiteColor
  5982.     blackColor
  5983.     redColor
  5984.     greenColor
  5985.     blueColor
  5986.     cyanColor
  5987.     magentaColor
  5988.     yellowColor
  5989.  
  5990. To generate color all you need to do is set the foreground and background colors
  5991. before you begin drawing (initially they are set to blackColor foreground and whiteColor
  5992. background). To do this you call the QuickDraw routines ForeColor and BackColor as
  5993. described in Inside Macintosh. If you are using QuickDraw pictures, make sure you set
  5994. the foreground and background colors before you call ClosePicture so that they are
  5995. recorded in the picture. Setting the colors before calling DrawPicture doesn’t work.
  5996.  
  5997. The drivers also recognize two of the transfer modes: srcCopy and srcOr. The effect
  5998. of the other transfer modes is not well defined and has not been tested. It may be
  5999. best to stay away from them.
  6000.  
  6001. Caveats
  6002.  
  6003. When printing a large area of more than one color you will encounter a problem with
  6004. the ribbon. When you print a large area of one color, the printer’s pins pick up the
  6005. color from the back of the ribbon. When another large area of color is printed, the
  6006. pins deposit the previous color onto the back of the ribbon. Eventually the first
  6007. color will come through to the front of the ribbon, contaminating the second color.
  6008. You can get the same kind of effect if you set, for example, a foreground color of
  6009. yellow and a background color of blue. The ribbon will pick up the blue as it tries
  6010. to print yellow on top of it. This problem is partially alleviated in the 2.3 version
  6011. of the ImageWriter driver by using a different printing technique.
  6012.  
  6013. The ribbon goes through the printer rather quickly when printing large areas. When
  6014. the ribbon comes through the second time the colors don’t look too great.
  6015.  
  6016.  
  6017. Part 2, LaserWriters
  6018.  
  6019. Using the PostScript ‘image’ Operator to Print Halftones
  6020.  
  6021. About ‘image’
  6022.  
  6023. The PostScript image operator is used to send Bitmaps or Pixmaps to the LaserWriter.
  6024. The image operator can handle depths from 1 to 8 bits per pixel. Our current LaserWriters
  6025. can only image about twenty shades of gray, but the printed page will look like there’s
  6026. more. Being that the image operator is still a PostScript operator, it expects its
  6027. data in the form of hexidecimal bytes. The bytes are represented by two ASCII characters(0-9,A-F).
  6028. The image operator takes these parameters:
  6029.  
  6030.     width  height  depth  matrix  image-data
  6031.  
  6032. The first three are the width, height, and depth of the image, and the matrix is the
  6033. transformation matrix to be applied to the current matrix. See the PostScript Language
  6034. Reference Manual for more information. The image data is where the actual hex data
  6035. should go. Instead of inserting the data between the first parameters and the image
  6036. operator itself, it is better to use a small, PostScript procedure to read the data
  6037. starting from right after the image operator. For example:
  6038.  
  6039.     640 480 8 [640 0 0 480 0 0]
  6040.     {currentfile picstr readhexstring pop}
  6041.     image
  6042.     FF 00 FF 00 FF 00 FF 00 ...
  6043.  
  6044. In the above example, the width of the image is 640, the height is 480, and the depth
  6045. is 8. The matrix (enclosed in brackets) is setup to draw the image starting at QuickDraw’s
  6046. 0,0 (top left of page), and with no scaling. The PostScript code (enclosed in braces)
  6047. is not executed. Instead, it is passed to the image operator, and the image operator
  6048. will call it repeatedly until it has enough data to draw the image. In this case, it
  6049. will be expecting 640*480 bytes. When the image operator calls the procedure, it does
  6050. the following:
  6051.  
  6052.  1. Pushes the current file which in this case is the stream of data coming to 
  6053.     the LaserWriter over AppleTalk. This is the first parameter to 
  6054.     readhexstring.
  6055.  
  6056.  2. Next picstr is pushed. picstr is a string variable defined to hold one row 
  6057.     of hex data. The PostScript to create the picstr is:
  6058.  
  6059.         /picstr 640 def
  6060.  
  6061.  3. Now readhexstring is called to fill picstr with data from the current file.
  6062.     It begins reading bytes which are the characters following the image 
  6063.     operator.
  6064.  
  6065.  4. Since readhexstring leaves both the string we want, and a boolean that we 
  6066.     don’t want on the stack, we do one pop to kill of the boolean. Now the 
  6067.     string is left behind for the image operator to use.
  6068.  
  6069. So using the above PostScript code you can easily print an image. Just fill in the
  6070. width height and depth, and send the hex data immediately following the PostScript
  6071. code.
  6072.  
  6073. Setting Up for ‘image’
  6074.  
  6075. Most of the users of this technique are going to want to print a Color QuickDraw
  6076. PixMap. Although the image command does a lot of the work for you, there are still a
  6077. couple of tricks that are recommended for performance.
  6078.  
  6079. Assume the Maximum Depth
  6080.  
  6081. Since the current version of the image operator has a maximum depth of 8 bits/pixel,
  6082. it is wise to convert the source image to the same depth before imaging. This can be
  6083. done very simply by using an offscreen GrafPort that is set to 8 bits/pixel, and then
  6084. using CopyBits to do the depth conversion for you. This will do a nice job of converting
  6085. lower resolution images to 8 bits/pixel.
  6086.  
  6087. Build a Color Table
  6088.  
  6089. An 8 bit deep image can only use 256 colors. Since the image that you are starting
  6090. with is probably color, and the image you get will be grayscale, you need to convert
  6091. the colors in the source color table into PostScript grayscale values. This is actually
  6092. easy to do using the Color Manager. First create a table that can hold 512 bytes.
  6093. This is 2 bytes for each color value from 0 to 255. Since PostScript wants the values
  6094. in ASCII, you need two characters for each pixel. Now loop through the colors in the
  6095. color table. Call Index2Color to get the real RGB color for that index, and then call
  6096. RGB2HSL to convert the RGB color into a luminance value. This value will be expressed
  6097. as a SmallFract which can then be scaled into a value from 0 to 255. This value should
  6098. then be converted to ASCII, and stored at the appropriate location in the table. When
  6099. you are done, you should be able to use a pixel value as an index into your table of
  6100. PostScript color values. For each pixel in the image, send two characters to the
  6101. LaserWriter.
  6102.  
  6103. Sending the Data
  6104.  
  6105. Once you have set up the color table, all that left to do is to loop through all of
  6106. the pixels, and send their PostScript representation to the LaserWriter. There are a
  6107. couple of ways to do this. First is to use the low-level Print Manager interface and
  6108. stream the PostScript using the stdBuf PrCtlCall. Although this seems like it would
  6109. be the fastest way, the latest version of the LaserWriter driver (5.0) converts all
  6110. low-level calls to their high level equivalent before executing them. Because of
  6111. this, the low-level interface is no longer faster than the high level. In an FKEY I
  6112. have written, I use the high-level Print Manager interface, and send the data via the
  6113. PostScriptHandle PicComment. This way, I can buffer a large amount of data, before
  6114. actually sending it. Using this technique, I have been able to image a Mac II screen
  6115. in about 5 minutes on a LaserWriter Plus, and about 1.5 minutes on a LaserWriter II
  6116. NTX.
  6117.  
  6118.  
  6119.  
  6120. æKY 74
  6121. æC #74: Don’t Use the Resource Fork for Data
  6122.  
  6123. See also:      The Resource Manager
  6124.                Technical Note #62 — Resource Header Application Bytes
  6125.  
  6126. Written by:    Bryan Stearns        March 13, 1986
  6127. Updated:                            March 1, 1988
  6128. _______________________________________________________________________________
  6129.  
  6130. Don’t use the resource fork of a file for non-resource data. Parts of the system
  6131. (including the File Manager and the Finder) assume that if this fork exists, it will
  6132. contain valid Resource Manager information.
  6133.  
  6134. PBOpenRF was provided to allow copying of the resource fork of a file in its entirety,
  6135. without Resource Manager interpretation. Do not use it to open 
  6136. “another data fork.”
  6137.  
  6138. The File Manager assumes that the first block of the resource fork of a file will be
  6139. part of the resource header, and puts information there to aid in scavenging. Note
  6140. that this means that if you copy a resource file (opened with PBOpenRF), the duplicate
  6141. may not be exactly like the original.
  6142.  
  6143.  
  6144.  
  6145. æKY 75
  6146. æC #75: The Installer and Scripts
  6147.  
  6148. Revised by:    Wayne Correia                                         April 1990
  6149. Written by:    Scott Douglass                                        March 1986
  6150.  
  6151. This Technical Note documents Installer 3.0.1 scripting and changes from previous
  6152. versions of the Installer. Changes since March 1988:  Removed references to Installer
  6153. 2.6 and earlier and updated to cover Installer 3.0.1, which was introduced with System
  6154. Software 6.0.5.
  6155. _______________________________________________________________________________
  6156.  
  6157. Installer 3.0.1 is a complete rewrite of the Apple Installer.  All scripts designed for
  6158. use with previous versions of the Installer need to be rewritten to be utilized by
  6159. Installer 3.0.1.  Scripts for Installer 3.0.1 are upward compatible with future versions
  6160. of the Installer.  With the release of Installer 3.0.1, Apple no longer supports previous
  6161. versions of the Installer.
  6162.  
  6163. Installer 3.0.1 scripting architecture allows developers to implement the following new
  6164. features:
  6165.  
  6166.   •  Multiple source disks.
  6167.   •  Splash screen with custom options.
  6168.   •  Automatic install options based upon target machine model.
  6169.   •  Easy Install (single button install) and Custom Install (power user) modes.
  6170.   •  AppleShare network server-based installation
  6171.   •  Folder-specific installation.
  6172.  
  6173. In addition, Installer 3.0.1 adds versatility with the use of 'atom' resource types, reduces
  6174. disk swapping by maximizing its use of memory, and calculates target disk space requirements
  6175. much faster than previous versions.
  6176.  
  6177. Installer 3.0.1 has a complete reference suite which includes:
  6178.  
  6179.   •  A ScriptWriter’s Guide to Installer 3.0
  6180.   •  Technical Reference for Writing Installer Scripts
  6181.   •  ScriptCheck (MPW Tool)
  6182.   •  InstallerTypes.r (Rez file)
  6183.  
  6184. and is available on the Developer CD Series Volume III, A Disc Called Wanda, on AppleLink in
  6185. the Developer Services bulletin board (Developer Services: Macintosh Developer Technical Support:
  6186. Interim Technical Documentation), and the Apple FTP site (IP 130.43.2.2) on the Internet
  6187. (~ftp/pub/dts/mac/docs/).
  6188.  
  6189.  
  6190. Further Reference:
  6191. _______________________________________________________________________________
  6192.   •  A ScriptWriter’s Guide to Installer 3.0
  6193.   •  Technical Reference for Writing Installer Scripts
  6194.   •  ScriptCheck (MPW Tool)
  6195.   •  InstallerTypes.r (Rez file)
  6196.  
  6197. æKY 76
  6198. æC #76: The Macintosh Plus Update Installation Script
  6199.  
  6200. Written by:     scott douglass    February 24, 1986
  6201. Updated:                          March 1, 1988
  6202. _______________________________________________________________________________
  6203.  
  6204. Earlier versions of this note described the Macintosh Plus Update installation script,
  6205. because it was the first script created for the Installer. Since then, many versions
  6206. of this script have been created which no longer match what was described here. In
  6207. addition, many other scripts now exist.
  6208.  
  6209.  
  6210. æKY 77
  6211. æC #77: HFS Ruminations
  6212.  
  6213. See also:     The File Manager 
  6214.               Technical Note #66 — Determining Which File System is Active
  6215.               Technical Note #67 — Finding the “Blessed Folder”
  6216.               Technical Note #68 — Searching All Directories on an HFS Volume
  6217.  
  6218. Written by:   Jim Friedlander     June 7, 1986
  6219. Updated:                          March 1, 1988
  6220. _______________________________________________________________________________
  6221.  
  6222. This technical note contains some thoughts concerning HFS.
  6223. _______________________________________________________________________________
  6224.  
  6225. HFS numbers
  6226.  
  6227.    A drive number is a small positive word (e.g. 3). 
  6228.  
  6229.    A VRefNum (as opposed to a WDRefNum) is a small negative word (e.g. $FFFE).
  6230.  
  6231.    A WDRefNum is a large negative word (e.g. $8033).
  6232.  
  6233.    A DirID is a long word (e.g. 38). The root directory of an HFS volume always 
  6234.    has a dirID of 2.
  6235.  
  6236.  
  6237. Working Directories
  6238.  
  6239. Normally an application doesn’t need to open working directories (henceforth WDs)
  6240. using PBOpenWD, since SFGetFile returns a WDRefnum if the selected file is in a directory
  6241. on a hierarchical volume and you are running HFS. There are times, however, when
  6242. opening a WD is desirable (see the discussion about BootDrive below).
  6243.  
  6244. If you do open a WD, it should be created with an ioWDProcID of ‘ERIK’ ($4552494B)
  6245. and it will be deallocated by the Finder. Note that under MultiFinder, ioWDProcID
  6246. will be ignored, so you should only use ‘ERIK’.
  6247.  
  6248. SFGetFile also creates WDs with an ioWDProcID of ‘ERIK’. If SFGetFile opens two files
  6249. from the same directory (during the same application), it will only create one working
  6250. directory.
  6251.  
  6252. There are no WDRefnums that refer to the root—the root directory of a volume is always
  6253. referred to by a vRefNum.
  6254.  
  6255.  
  6256. When you can use HFS calls
  6257.  
  6258. All of the HFS ‘H’ calls, except for PBHSetVInfo, can be made without regard to file
  6259. system as long as you pass in a pointer to an HFS parameter block. PBHGetVol, PBHSetVol
  6260. (see the warnings in the File Manager chapter of Inside Macintosh), PBHOpen, PBHOpenRF,
  6261. PBHCreate, PBHDelete, PBHGetFInfo, PBHSetFInfo, PBHSetFLock, PBHRstFLock and PBHRename
  6262. differ from their MFS counterparts only in that a dirID can be passed in at offset
  6263. $30.
  6264.  
  6265. The only difference between, for example, PBOpen and PBHOpen is that bit 9 of the
  6266. trap word is set, which tells HFS to use a larger parameter block. MFS ignores this
  6267. bit, so it will use the smaller parameter block (not including the dirID). Remember
  6268. that all of these calls will accept a WDRefNum in the ioVRefNum field of the parameter
  6269. block.
  6270.  
  6271. PBHGetVInfo returns more information than PBGetVInfo, so, if you’re counting on getting
  6272. information that is returned in the HFS parameter block, but not in the MFS parameter
  6273. block, you should check to see which file system is active.
  6274.  
  6275. HFS-specific calls can only be made if HFS is active. These calls are: PBGetCatInfo,
  6276. PBSetCatInfo, PBOpenWD, PBCloseWD, PBGetFCBInfo,PBGetWDInfo, PBCatMove and PBDirCreate.
  6277. PBHSetVInfo has no MFS equivalent. If any of these calls are made when MFS is running,
  6278. a system error will be generated. If PBCatMove or PBDirCreate are called for an MFS
  6279. volume, the function will return the error code –123 (wrong volume type). If PBGetCatInfo
  6280. or PBSetCatInfo are called on MFS volumes, it’s just as if PBGetFInfo and PBSetFInfo
  6281. were called.
  6282.  
  6283.  
  6284. Default volume
  6285.  
  6286. If HFS is running, a call to GetVol (before you’ve made any SetVol calls) will return
  6287. the WDRefNum of your application’s parent directory in the vRefNum parameter. If your
  6288. application was launched by the user clicking on one or more documents, the WDRefNums
  6289. of those documents’ parent directories are available in the vRefNum field of the
  6290. AppFile record returned from GetAppFiles.
  6291.  
  6292. If MFS is running, a call to GetVol (before you’ve made any SetVol calls) will return
  6293. the vRefNum of the volume your application is on in the vRefNum parameter. If your
  6294. application was launched by the user clicking on one or more documents, the vRefNum
  6295. of those documents’ volume are available in the vRefNum field of the AppFile record
  6296. returned from GetAppFiles.
  6297.  
  6298.  
  6299. BootDrive
  6300.  
  6301. If your application or desk accessory needs to get the WDRefNum of the “blessed folder”
  6302. of the boot drive (for example, you might want to store a configuration file there),
  6303. it can not rely on the low-memory global BootDrive (a word at $210) to contain the
  6304. correct value. If your application is the startup application, BootDrive will contain
  6305. the WDRefNum of the directory/volume that your application is in (not the WDRefNum of
  6306. the “blessed folder”); Your application could have been _Launched from an application
  6307. that has modified BootDrive; if you are a desk accessory, you might find that some
  6308. applications alter BootDrive.
  6309.  
  6310. To get the “real” WDRefNum of the “blessed folder” that contains the currently open
  6311. System file, you should call SysEnvirons (discussed in Tech Note #129). If that is
  6312. impossible, you can do something like this:
  6313. (NOTE: if you are running under MFS, BootDrive always contains the vRefNum of the
  6314. volume on which the currently open System file is located):
  6315.  
  6316.    ...
  6317.    CONST
  6318.       SysWDProcID  = $4552494B;    {“ERIK”}
  6319.       BootDrive    = $210;   {address of Low-Mem global BootDrive}
  6320.       FSFCBLen     = $3F6;   {address of Low-Mem global to distinguish file systems}
  6321.       SysMap       = $A58;   {address of Low-Mem global that contains                
  6322.          system map reference number}
  6323.  
  6324.    TYPE
  6325.       WordPtr = ^Integer;    {Pointer to a word(2 bytes)}
  6326.     ...
  6327.  
  6328.    FUNCTION HFSExists: BOOLEAN;
  6329.  
  6330.    Begin {HFSExists}
  6331.       HFSExists := WordPtr(FSFCBLen)^ > 0;
  6332.    End;  {HFSExists}
  6333.  
  6334.  
  6335.    FUNCTION GetRealBootDrive: INTEGER;
  6336.  
  6337.    VAR
  6338.        MyHPB     : HParamBlockRec;
  6339.        MyWDPB           : WDPBRec;
  6340.        err       : OSErr;
  6341.        sysVRef   : integer;   {will be the vRefNum of open system’s vol}
  6342.  
  6343.    Begin {GetRealBootDrive}
  6344.        if HFSExists then Begin    {If we’re running under HFS... }
  6345.  
  6346.           {get the VRefNum of the volume that }
  6347.           {contains the open System File      }
  6348.           err:= GetVRefNum(WordPtr(SysMap)^,sysVRef);
  6349.  
  6350.           with MyHPB do Begin
  6351.           {Get the “System” vRefNum and “Blessed” dirID}
  6352.               ioNamePtr    := NIL;
  6353.               ioVRefNum    := sysVRef; {from the GetVrefNum call}
  6354.               ioVolIndex    := 0;
  6355.           End; {with}
  6356.           err := PBHGetVInfo(@MyHPB, FALSE);
  6357.  
  6358.           with myWDPB do Begin      {Open a working directory there}
  6359.               ioNamePtr   := NIL;
  6360.               ioVRefNum   := sysVRef;
  6361.               ioWDProcID  := SysWDProcID; {Using the system proc ID}
  6362.               ioWDDirID  := myHPB.ioVFndrInfo[1];{ see TechNote 67}
  6363.           End; {with}
  6364.           err := PBOpenWD(@myWDPB, FALSE);
  6365.  
  6366.           GetRealBootDrive := myWDPB.ioVRefNum;
  6367.           {We’ve got the real WD}
  6368.       End Else {we’re running MFS}
  6369.           GetRealBootDrive := WordPtr(BootDrive)^; 
  6370.           {BootDrive is valid under MFS}
  6371.    End;  {GetRealBootDrive}
  6372.  
  6373. From MPW C:
  6374.  
  6375. /*"ERIK"*/
  6376. #define    SysWDProcID 0x4552494B    
  6377. #define    BootDrive   0x210
  6378. /*address of Low-Mem global that contains system map reference number*/
  6379. #define    SysMap      0xA58                     
  6380. #define    FSFCBLen    0x3F6
  6381. #define    HFSIsRunning ((*(short int *)(FSFCBLen)) > 0)
  6382.  
  6383. OSErr GetRealBootDrive(BDrive)
  6384. short int *BDrive;
  6385. { /*GetRealBootDrive*/
  6386.  
  6387.     /*three different parameter blocks are used here for clarity*/
  6388.     HVolumeParam   myHPB;
  6389.     FCBPBRec       myFCBRec;
  6390.     WDPBRec        myWDPB;
  6391.     OSErr          err;
  6392.     short int      sysVRef; /*will be the vRefNum of open system’s vol*/
  6393.  
  6394.     if (HFSIsRunning)
  6395.         
  6396.     { /*if we’re running under HFS... */
  6397.  
  6398.     /*get the vRefNum of the volume that contains the open System File*/
  6399.         myFCBRec.ioNamePtr= nil;
  6400.         myFCBRec.ioVRefNum = 0;
  6401.         myFCBRec.ioRefNum = *(short int *)(SysMap);
  6402.         myFCBRec.ioFCBIndx = 0;
  6403.  
  6404.         err = PBGetFCBInfo(&myFCBRec,false);
  6405.         if (err != noErr) return(err);
  6406.     /*now we need the dirID of the "Blessed Folder" on this volume*/
  6407.         myHPB.ioNamePtr = nil;
  6408.         myHPB.ioVRefNum = myFCBRec.ioFCBVRefNum;
  6409.         myHPB.ioVolIndex = 0;
  6410.  
  6411.         err = PBHGetVInfo(&myHPB,false);
  6412.         if (err != noErr) return(err);
  6413.  
  6414. /*we can now open a WD for the directory that contains the open system file one will
  6415. most likely already be open, so PBOpenWD will just return that WDRefNum*/
  6416.  
  6417.         myWDPB.ioNamePtr = nil;
  6418.         myWDPB.ioVRefNum = myHPB.ioVRefNum;
  6419.         myWDPB.ioWDProcID = SysWDProcID; /*'ERIK'*/
  6420.         myWDPB.ioWDDirID = myHPB.ioVFndrInfo[0]; /* see Technote # 67                
  6421.                [c has 0-based arrays]*/
  6422.  
  6423.         err = PBOpenWD(&myWDPB,false);
  6424.         if (err != noErr) return err;
  6425.  
  6426.         *BDrive = myWDPB.ioVRefNum; /*that's all!*/
  6427.     } /* if (HFSIsRunning) */
  6428.     else
  6429.         *BDrive = *(short int *)(BootDrive);
  6430.         /*BootDrive is valid under MFS*/
  6431.     
  6432.     return noErr;
  6433. }                           /*GetRealBootDrive*/
  6434.  
  6435. The Poor Man’s Search Path (PMSP)
  6436.  
  6437. If HFS is running, the PMSP is used for any file system call that can return a file-not-
  6438. found error, such as PBOpen, PBClose, PBDelete, PBGetCatInfo, etc. It is NOT used for
  6439. indexed calls (that is, where ioFDirIndex is positive) or when a file is created
  6440. (PBCreate) or when a file is being moved between directories (PBCatMove). The PMSP is
  6441. also NOT used when a non-zero dirID is specified.
  6442.     
  6443. Here’s a brief description of how the default PMSP works.
  6444.  
  6445. 1) The directory that you specify (specified by WDRefNum or pathname) is 
  6446.    searched; if the specified file is not found, then 
  6447.  
  6448. 2) the volume/directory specified by BootDrive (low-memory global at $210) is 
  6449.    searched IF it is on the same volume as the directory you specified (see #1 
  6450.    above); if the specified file is not found, or the directory specified by 
  6451.    BootDrive is not on the same volume as the directory that you specified, 
  6452.    then
  6453.  
  6454. 3) if there is a “blessed folder” on the same volume as the directory you 
  6455.    specified (see #1 above), it is searched. Please note that if #2 above 
  6456.    specifies the same directory as #3, then that directory is NOT searched 
  6457.    twice. If no file is found, then
  6458.  
  6459. 4) fnfErr is returned.
  6460.  
  6461.  
  6462.  
  6463. ioDirId and ioFlNum
  6464.  
  6465. Two fields of the HParamBlockRec record share the same location. ioDirID and ioFlNum
  6466. are both at offset $30 from the start of the parameter block. This causes a problem,
  6467. since, in some calls (e.g. PBGetCatInfo), a dirID is passed in and a file number is
  6468. returned in the same field.
  6469.  
  6470. Future versions of Apple’s HFS interfaces will omit the ioFlNum designator, so, if
  6471. you need to get the file number of a file, it will be in the ioDirID of the parameter
  6472. block AFTER you have made the call. If you are making successive calls that depend on
  6473. ioDirID being set correctly, you must “reset” the ioDirID field before each call. The
  6474. program fragment in Technical Note #68 does this.
  6475.  
  6476.  
  6477. PBHGetVInfo
  6478.  
  6479. Normally, PBHGetVInfo will be called specifying a vRefNum. There are times, however,
  6480. when you may make the call and only specify a volume name. If this is so, there are a
  6481. couple of things to look out for.
  6482.  
  6483. Let’s say that we have two volumes mounted: “Vol1:” (the default volume) and 
  6484. “Vol2:”. We also have a variable of type HParamBlockRec called MyHPB. We want to get
  6485. information about Vol2:, so we put a pointer to a string (let’s call it fName) in
  6486. MyHPB.ioNamePtr. The string fName is equal to “Vol2” (Please note the missing colon).
  6487. We also initialize MyHPB.ioVRefNum to 0. Then we make the call. We are very surprised
  6488. to find out that we are returned an error of 0 (noErr) and that the ioVRefNum that we
  6489. get back is NOT the vRefNum of Vol2:, but rather that of Vol1:.
  6490.  
  6491. Here’s what’s happening: PBHGetVInfo looks at the volume name, and sees that it is
  6492. improper (it is missing a colon). So, being a forgiving sort of call, it goes on to
  6493. look at the ioVRefNum field that you passed it (see pp. 99 of Inside Macintosh, vol.
  6494. II). It sees a 0 there, so it returns information about the default volume.
  6495.  
  6496. If you want to get information about a volume, and you just have its name and you are
  6497. not sure that the name is a proper one, you should set MyHPB.ioVRefNum to –32768
  6498. ($8000). No vRefNum or WDRefNum can be equal to $8000. By doing this, you are forcing
  6499. PBHGetVInfo to use the volume name and, if that name is invalid, to return a –35
  6500. error (nsvErr), “No such volume.”
  6501.  
  6502.  
  6503. PBGetWDInfo and Register D1
  6504.  
  6505. There was a problem with PBGetWDInfo that sometimes caused the call to inaccurately
  6506. report the dirID of a directory. It is fixed in System 3.2 and later. To be absolutely
  6507. sure that you won’t get stung by this, clear register D1 (CLR.L D1) before a call to
  6508. PBGetWDInfo. You can do this either with an INLINE (Lisa Pascal and most C’s) or with
  6509. a short assembly-language routine before the call to PBGetWDInfo.
  6510.  
  6511.  
  6512.  
  6513. æKY 78
  6514. æC #78: Resource Manager Tips
  6515.  
  6516. See also:     The Resource Manager
  6517.               The Memory Manager
  6518.               The Menu Manager
  6519.               Technical Note #129 — SysEnvirons
  6520.  
  6521. Written by:   Jim Friedlander    June 8, 1986
  6522. Updated:                         March 1, 1988
  6523. _______________________________________________________________________________
  6524.  
  6525. This note discusses some problems with the Resource Manager and how to work around
  6526. them.
  6527. _______________________________________________________________________________
  6528.  
  6529.  
  6530. OpenResFile Bug
  6531.  
  6532. This section of the note formerly described a bug in OpenResFile on 64K ROM machines.
  6533. Information specific to 64K ROM machines has been deleted from Macintosh Technical
  6534. Notes for reasons of clarity.
  6535.  
  6536.  
  6537. GetMenu and ResErrProc
  6538.  
  6539. If your application makes use of ResErrProc (a pointer to a procedure stored in low-memory
  6540. global $AF2) to detect resource errors, you will get unexpected calls to your ResErrProc
  6541. procedure when calling GetMenu on 128K ROMs. The Menu Manager call GetMenu makes a
  6542. call to GetResInfo, requesting resource information about MDEF 0. Unfortunately,
  6543. ROMMapInsert is set to FALSE, so this call fails, setting ResErr to –192 (resNotFound).
  6544. This in turn will cause a call to your ResErrProc, procedure even though the GetMenu
  6545. call has worked correctly. This is only a problem if you are using ResErrProc. 
  6546.  
  6547. The workaround is to:
  6548.     1) save the address of your ResErrProc procedure
  6549.     2) clear ResErrProc
  6550.     3) do a GetResource call on the MENU resource you want to get
  6551.     4) check to see if you get a nil handle back, if you do, you can handle the 
  6552.        error in whatever way is appropriate for your application
  6553.     5) call GetMenu, and
  6554.     6) when you are done calling GetMenu, restore ResErrProc
  6555.  
  6556.  
  6557. SetResAttrs on read-only resource maps
  6558.  
  6559. SetResAttrs does not return an error if you are setting the resource attributes of a
  6560. resource in a resource file that has a read-only resource map. The workaround is to
  6561. check to see if the map is read-only and proceed from there:
  6562.  
  6563.     CONST
  6564.         MapROBit = 8; {Toolbox bit ordering for bit 7 of low-order byte}
  6565.  
  6566.     BEGIN
  6567.         ...
  6568.         attrs:= GetResFileAttrs(refNum);
  6569.         IF BitTst(@attrs,MapROBit) THEN ... {write-protected map}
  6570.  
  6571.  
  6572.  
  6573.  
  6574. æKY 79
  6575. æC #79: _ZoomWindow
  6576.  
  6577. Revised by:    Craig Prouse                                          April 1990
  6578. Written by:    Jim Friedlander                                        June 1986
  6579.  
  6580. This Technical Note contains some hints about using _ZoomWindow.
  6581. Changes since February 1990:  Fixed a bug in DoWZoom which caused crashes if the content
  6582. of a window did not intersect with any device’s gdRect.  Also made DoWZoom more robust by
  6583. making savePort a local variable and checking for off-screen and inactive GDevice records.
  6584. (One variable name has changed.)  Additional minor changes:  Corrected original sample code
  6585. to use _EraseRect before zooming and added references to Human Interface Note #7, Who’s
  6586. Zooming Whom? for more subtle and application-specific considerations.
  6587. _______________________________________________________________________________
  6588.  
  6589.  
  6590. Basics
  6591.  
  6592. _ZoomWindow allows a window to be toggled between two states (where “state” means size and
  6593. location):  a default state and a user-selectable state.  The default state stays the same
  6594. unless the application changes it, while the user-selectable state is altered when the user
  6595. changes the size or location of a zoomable window.  The code to handle zoomable windows in
  6596. a main event loop would look something like the examples which follow.
  6597.  
  6598. Note:  _ZoomWindow assumes that the window that you are zooming is the
  6599.        current GrafPort.  If thePort is not set to the window that is
  6600.        being zoomed, an address error is generated.
  6601.  
  6602. MPW Pascal
  6603.  
  6604.        CASE myEvent.what OF
  6605.          mouseDown: BEGIN
  6606.            partCode:= FindWindow(myEvent.where, whichWindow);
  6607.              CASE partCode OF
  6608.                inZoomIn, InZoomOut:
  6609.                  IF TrackBox(whichWindow, myEvent.where, partCode) THEN
  6610.                    BEGIN
  6611.                      GetPort(oldPort);
  6612.                      SetPort(whichWindow);
  6613.                      EraseRect(whichWindow^.portRect);
  6614.                      ZoomWindow(whichWindow, partCode, TRUE);
  6615.                      SetPort(oldPort);
  6616.                    END; {IF}
  6617.                ...{and so on}
  6618.              END; {CASE}
  6619.            END; {mouseDown}
  6620.          ...{and so on}
  6621.        END; {CASE}
  6622.  
  6623. MPW C
  6624.  
  6625.        switch (myEvent.what) {
  6626.          case mouseDown:
  6627.            partCode = FindWindow(myEvent.where, &whichWindow);
  6628.            switch (partCode) {
  6629.              case inZoomIn:
  6630.              case inZoomOut:
  6631.                if (TrackBox(whichWindow, myEvent.where, partCode)) {
  6632.                  GetPort(&oldPort);
  6633.                  SetPort(whichWindow);
  6634.                  EraseRect(whichWindow->portRect);
  6635.                  ZoomWindow(whichWindow, partCode, true);
  6636.                  SetPort(oldPort);
  6637.                  } /* if */
  6638.                break;
  6639.              ... /* and so on */
  6640.            } /* switch */
  6641.          ... /* and so on */
  6642.        } /* switch */
  6643.  
  6644. If a window is zoomable, that is, if it has a window definition ID = 8 (using the standard
  6645. 'WDEF'), WindowRecord.dataHandle points to a structure that consists of two rectangles.
  6646. The user-selectable state is stored in the first rectangle, and the default state is stored
  6647. in the second rectangle.  An application can modify either of these states, though modifying
  6648. the user-selectable state might present a surprise to the user when the window is zoomed
  6649. from the default state.  An application should also be careful to not change either rectangle
  6650. so that the title bar of the window is hidden by the menu bar.
  6651.  
  6652. Before modifying these rectangles, an application must make sure that DataHandle is not NIL.
  6653. If it is NIL for a window with window definition ID = 8, that means that the program is not
  6654. executing on a system or machine that supports zooming windows.
  6655.  
  6656. One need not be concerned about the use of a window with window definition
  6657. ID = 8 making an application machine-specific—if the system or machine that the application
  6658. is running on doesn’t support zooming windows, _FindWindow never returns inZoomIn or inZoomOut,
  6659. so neither _TrackBox nor _ZoomWindow are called.
  6660.  
  6661. If DataHandle is not NIL, an application can set the coordinates of either rectangle.  For example,
  6662. the Finder sets the second rectangle (default state) so that a zoomed-out window does not cover
  6663. the disk and trash icons.
  6664.  
  6665.  
  6666. For the More Adventurous (or Seeing Double)
  6667.  
  6668. Developers should long have been aware that they should make no assumptions about the screen size
  6669. and use screenBits.bounds to avoid limiting utilization of large video displays.  Modular Macintoshes
  6670. and Color QuickDraw support multiple display devices, which invalidates the use of screenBits.bounds
  6671. unless the boundary of only the primary display (the one with the menu bar) is desired.  When dragging
  6672. and growing windows in a multi-screen environment, developers are now urged to use the bounding
  6673. rectangle of the GrayRgn.  In most cases, this is not a major modification and does not add a
  6674. significant amount of code.  Simply define a variable
  6675.  
  6676.        desktopExtent := GetGrayRgn^^.rgnBBox;
  6677.  
  6678. and use this in place of screenBits.bounds.  When zooming a document window, however, additional
  6679. work is required to implement a window-zooming strategy which fully conforms with Apple’s Human
  6680. Interface Guidelines.
  6681.  
  6682. One difficulty is that when a new window is created with _NewWindow or
  6683. _GetNewWindow, its default stdState rectangle (the rectangle determining the size and position
  6684. of the zoomed window) is set by the Window Manager to be the gray region of the main display device
  6685. inset by three pixels on each side.  If a window has been moved to reflect a position on a secondary
  6686. display, that window still zooms onto the main device, requiring the user to pan across the desktop
  6687. to follow the window.  The preferred behavior is to zoom the window onto the device containing the
  6688. largest portion of the unzoomed window.  This is a perfect example of a case where it is necessary
  6689. for the application to modify the default state rectangle before zooming.
  6690.  
  6691. DoWZoom is a Pascal procedure which implements this functionality.  It is a good example of how to
  6692. manipulate both a WStateData record and the Color QuickDraw device list.  On machines without Color
  6693. QuickDraw (e.g., Macintosh Plus, Macintosh SE, Macintosh Portable) the stdState rectangle is left
  6694. unmodified and the procedure reduces to five instructions, just like it is illustrated under
  6695. “Basics.”  If Color QuickDraw is present, a sequence of calculations determines which display device
  6696. contains most of the window prior to zooming.  That device is considered dominant and is the device
  6697. onto which the window is zoomed.  A new stdState rectangle is computed based on the gdRect of the
  6698. dominant GDevice.  Allowances are made for the window’s title bar, the menu bar if necessary, and
  6699. for the standard three-pixel margin.  (Please note that DoWZoom only mimics the behavior of the 
  6700. default _ZoomWindow trap as if it were implemented to support multiple displays.  It does not
  6701. account for the
  6702. “natural size” of a window for a particular purpose.  See Human Interface Note
  6703. #7, Who’s Zooming Whom?, for details on what constitutes the natural size of a window.)  It is not
  6704. necessary to set stdState prior to calling _ZoomWindow when zooming back to userState, so the extra
  6705. code is not executed in this case.
  6706.  
  6707. DoWZoom is too complex to execute within the main event loop as shown in
  6708. “Basics,” but if an application is already using a similar scheme, it can simply add the DoWZoom
  6709. procedure and replace the conditional block of code following
  6710.  
  6711.        IF TrackBox…
  6712.  
  6713. with
  6714.  
  6715.        DoWZoom(whichWindow, partCode);.
  6716.  
  6717. Happy Zooming.
  6718.  
  6719. PROCEDURE DoWZoom (theWindow: WindowPtr; zoomDir: INTEGER);
  6720. VAR
  6721.   windRect, theSect, zoomRect : Rect;
  6722.   nthDevice, dominantGDevice : GDHandle;
  6723.   sectArea, greatestArea : LONGINT;
  6724.   bias : INTEGER;
  6725.   sectFlag : BOOLEAN;
  6726.   savePort : GrafPtr;
  6727. BEGIN
  6728.   { theEvent is a global EventRecord from the main event loop }
  6729.   IF TrackBox(theWindow,theEvent.where,zoomDir) THEN
  6730.     BEGIN
  6731.       GetPort(savePort);
  6732.       SetPort(theWindow);
  6733.       EraseRect(theWindow^.portRect);    {recommended for cosmetic reasons}
  6734.       
  6735.       { If there is the possibility of multiple gDevices, then we  }
  6736.       { must check them to make sure we are zooming onto the right }
  6737.       { display device when zooming out. }
  6738.       { sysConfig is a global SysEnvRec set up during initialization  }
  6739.       IF (zoomDir = inZoomOut) AND sysConfig.hasColorQD THEN
  6740.         BEGIN
  6741.           { window's portRect must be converted to global coordinates }
  6742.           windRect := theWindow^.portRect;
  6743.           LocalToGlobal(windRect.topLeft);
  6744.           LocalToGlobal(windRect.botRight);
  6745.           { must calculate height of window's title bar }
  6746.           bias :=   windRect.top - 1
  6747.               -  WindowPeek(theWindow)^.strucRgn^^.rgnBBox.top;
  6748.           windRect.top := windRect.top - bias; {Thanks, Wayne!}
  6749.           nthDevice := GetDeviceList;
  6750.           greatestArea := 0;
  6751.           { This loop checks the window against all the gdRects in the   }
  6752.           { gDevice list and remembers which gdRect contains the largest }
  6753.           { portion of the window being zoomed. }
  6754.           WHILE nthDevice <> NIL DO
  6755.             IF TestDeviceAttribute(nthDevice,screenDevice) THEN
  6756.               IF TestDeviceAttribute(nthDevice,screenActive) THEN
  6757.                 BEGIN
  6758.                   sectFlag := SectRect(windRect,nthDevice^^.gdRect,theSect);
  6759.                   WITH theSect DO
  6760.                     sectArea := LONGINT(right - left) * (bottom - top);
  6761.                   IF sectArea > greatestArea THEN
  6762.                     BEGIN
  6763.                       greatestArea := sectArea;
  6764.                       dominantGDevice := nthDevice;
  6765.                     END;
  6766.                   nthDevice := GetNextDevice(nthDevice);
  6767.                 END; {of WHILE}
  6768.           { We must create a zoom rectangle manually in this case. }
  6769.           { account for menu bar height as well, if on main device }
  6770.           IF dominantGDevice = GetMainDevice THEN
  6771.             bias := bias + GetMBarHeight;
  6772.           WITH dominantGDevice^^.gdRect DO
  6773.             SetRect(zoomRect,left+3,top+bias+3,right-3,bottom-3);
  6774.           { Set up the WStateData record for this window. }
  6775.           WStateDataHandle(WindowPeek(theWindow)^
  6776.                            .dataHandle)^^.stdState := zoomRect;
  6777.         END; {of Color QuickDraw conditional stuff}
  6778.       
  6779.       ZoomWindow(theWindow,zoomDir,TRUE);
  6780.       SetPort(savePort);
  6781.     END;
  6782. END;
  6783.  
  6784. In an attempt to avoid declaring additional variables, the original version of this document
  6785. was flawed.  In addition, the assignment statement responsible for setting the stdState 
  6786. rectangle is relatively complex and involves two type-casts.  The following may look like C,
  6787. but it really is Pascal.  Trust me.
  6788.  
  6789.        WStateDataHandle(WindowPeek(theWindow)^
  6790.                         .dataHandle)^^.stdState := zoomRect;
  6791.  
  6792. It could be expanded into a more readable form such as:
  6793.  
  6794.        VAR
  6795.           theWRec : WindowPeek;
  6796.           zbRec   : WStateDataHandle;
  6797.  
  6798.        theWRec := WindowPeek(theWindow);
  6799.        zbRec := WStateDataHandle(theWRec^.dataHandle);
  6800.        zbRec^^.stdState := zoomRect;
  6801.  
  6802.  
  6803. Further Reference:
  6804. _______________________________________________________________________________
  6805.   •  Inside Macintosh, Volume IV, The Window Manager (pp. 49–52)
  6806.   •  Inside Macintosh, Volume V, Graphics Devices (p. 124),
  6807.                                  The Window Manager (p. 210)
  6808.   •  Human Interface Note #7, Who’s Zooming Whom?
  6809.  
  6810.  
  6811. æKY 80
  6812. æC #80: Standard File Tips
  6813.  
  6814. See also:      The Standard File Package
  6815.  
  6816. Written by:    Jim Friedlander    June 7, 1986
  6817. Updated:                          March 1, 1988
  6818. _______________________________________________________________________________
  6819.  
  6820.  
  6821. SFSaveDisk and CurDirStore
  6822.  
  6823. Low-memory location $214 (SFSaveDisk—a word) contains –1* the vRefNum of the volume
  6824. that SF is displaying (MFS and HFS). It never contains –1* a WDRefNum.
  6825.  
  6826. Low-memory location $398 (CurDirStore—a long word) contains the dirID of the directory
  6827. that SF is displaying (HFS only).
  6828.  
  6829. This information can be particularly useful at hook time, when the vRefNum field of
  6830. the reply record has not yet been filled in. NOTE: reply.fName is filled in correctly
  6831. at hook time if a file has been selected. If a directory has been selected, reply.fType
  6832. is non-zero (it contains the dirID of the selected directory). If neither a file nor
  6833. a directory is selected, both reply.fName[0] and reply.fType are 0.
  6834.  
  6835.  
  6836. Setting Standard File’s default volume and directory
  6837.  
  6838. If you want SFGetFile or SFPutFile to display a certain volume when it draws its
  6839. dialog, you can put –1 * the vRefNum of the volume you wish it to display into the
  6840. low-memory global SFSaveDisk (a word at $214).
  6841.  
  6842. In Pascal, you would use something like:
  6843.  
  6844.     ...
  6845.     TYPE
  6846.        WordPtr = ^INTEGER;              {pointer to a two-byte location}
  6847.     CONST
  6848.        SFSaveDisk = $214;               {location of low-memory global}
  6849.     VAR
  6850.        SFSaveVRef    : WordPtr;
  6851.        myVRef    : INTEGER;
  6852.     BEGIN
  6853.     ...
  6854.     {myVRef gets assigned here}
  6855.     ...
  6856.     SFSaveVRef := WordPtr(SFSaveDisk);  {point to SFSaveDisk}         SFSaveVRef^:=
  6857. –1 * myVRef;          {“stuff” the value in}
  6858.     SFGetFile(...
  6859.  
  6860. In C you would use something like this (where a variable of type “short” occupies 2
  6861. bytes):
  6862.  
  6863.     #define SFSaveDisk (*(short *)0x214)
  6864.  
  6865.     short myVRef;
  6866.     ...
  6867.     /* myVRef gets assigned here */
  6868.     ...
  6869.     SFSaveDisk = –1 * myVRef; /* “stuff” the value in */
  6870.     SFGetFile(...
  6871.  
  6872. If you are running HFS and would like to have Standard File display a particular
  6873. directory as well as a particular volume, you can’t just put a WDRefNum into SFSaveDisk.
  6874. If you do put a WDRefNum into SFSaveDisk, Standard File will display the root directory
  6875. of the default volume. Instead, you must put –1 * the vRefNum into SFSaveDisk (see
  6876. above) and put the dirID of the directory that you wish to have displayed in CurDirStore.
  6877. If you put an invalid dirID into CurDirStore, Standard File will display the root
  6878. level of the volume referred to by SFSaveDisk. To change CurDirStore you can use a
  6879. technique similar to the above, but remember that CurDirStore is a four-byte value.
  6880. If your application is running under MFS, Standard File ignores CurDirStore, so you
  6881. can use the same code regardless of file system.
  6882.  
  6883.  
  6884.  
  6885.  
  6886. æKY 81
  6887. æC #81: Caching
  6888.  
  6889. See also:     The File Manager
  6890.               The Device Manager
  6891.               Technical Note #14 — The INIT 31 Mechanism
  6892.  
  6893. Written by:      Rick Blair       June 17, 1986
  6894. Updated:                          March 1, 1988
  6895. _______________________________________________________________________________
  6896.  
  6897. This technical note describes disk and File System caching on the Macintosh, with
  6898. particular emphasis on the high-level File System cache. Of the three caches used for
  6899. file I/O, this is the one which could have the most impact on your program. 
  6900. NOTE: This big File System cache is not available on 64K ROM machines.
  6901. _______________________________________________________________________________
  6902.  
  6903.  
  6904. A term
  6905.  
  6906. In this note I will use the term “HFS” to mean the Hierarchical File System AND the
  6907. Sony driver which can access the 800K drives. Both RAM-based HFS (Hard Disk 20 file)
  6908. and the 128K ROM version include the second-generation Sony driver.
  6909.  
  6910.  
  6911. There’s always a cache (type 1)
  6912.  
  6913. The first type of cache used by the File System has been around since the days of the
  6914. Macintosh File System. Under MFS, each volume has a one-block buffer for all file/volume
  6915. data. This prevents a read of two bytes followed by a read 
  6916. (at the next file position) of 4 bytes from causing actual disk I/O. The volume allocation
  6917. map also gets saved in the system heap but it’s not really part of the cache.
  6918.  
  6919. This type of caching is still used by HFS, which includes MFS-format volumes which
  6920. may be mounted while running HFS. With HFS, the cache is a little bigger: each volume
  6921. gets 1 block of buffering for the bitmap, 2 blocks for volume 
  6922. (including file) data, and 16 blocks for HFS B*-tree control buffering.
  6923.  
  6924. This cache lives in the system heap (unless HFS is using the new File System caching
  6925. mechanism, in which case things become more complicated. See “type 3” below).
  6926.  
  6927.  
  6928. Cache track fever (type 2)
  6929.  
  6930. The track cache, only present with the enhanced Sony driver, will cache the current
  6931. track (up to twelve blocks) so that subsequent reads to that track may use the cache.
  6932. The track cache is “write through”; all writes go to both the cache and the Sony disk
  6933. so flushing is never required.
  6934.  
  6935. Track caching only takes place for synchronous I/O calls; when an application makes
  6936. asynchronous calls it expects to use the time while the disk is seeking, etc. to
  6937. execute other code.
  6938.  
  6939. The track cache gets its storage space from the system heap.
  6940.  
  6941.  
  6942. Cache me if you can (type 3)
  6943.  
  6944. The last type of cache to be discussed is only available under the 128K and greater
  6945. ROMs. This user-controlled cache is NOT “write-through”.
  6946.  
  6947. Based on how much space the user has allocated via the control panel, the File System
  6948. will set up a cache which can accommodate a certain number of blocks. This storage
  6949. will come from the application heap in the space above BufPtr (see technical note #14
  6950. and below). This is really the space above the jump table and the “A5 world”, not
  6951. technically part of the application heap. However, moving BufPtr down will cause a
  6952. corresponding reduction in the space available to the application heap.
  6953.  
  6954. The installation code will also grab the space used by the old File System cache
  6955. (type 1) since all types of disk blocks can be accommodated by this new cache.
  6956.  
  6957. The bulk of the caching CODE used for this RAM cache is also loaded above BufPtr at
  6958. application launch time. This is accomplished by the INIT 35 resource which is installed
  6959. in the system heap and initialized at boot time. At application launch time, INIT 35
  6960. checks the amount of cache allocated via the control panel and moves BufPtr down
  6961. accordingly before bringing in the balance of the caching code. The RAM caching code
  6962. is in the 'CACH' 1 resource in the System File.
  6963.  
  6964. The caching code always makes sure there is room for 128K of application heap and 32K
  6965. of cache. If the user-requested amount would reduce the heap/cache below these values
  6966. then the cache space is readjusted accordingly.
  6967.  
  6968. Up to 36 separate files may be buffered by the cache. Each queue is a list of blocks
  6969. cached for that file. Information is kept about the “age” of each block and the blocks
  6970. are also kept in a list in the order in which they occur in the file. The aging information
  6971. tells which blocks were least recently used; these are the first to be released when
  6972. new blocks become eligible for caching. The file order information is useful for
  6973. flushing the cache to the disk in an efficient manner, i.e. the file order approximates
  6974. disk order.
  6975.  
  6976. Assuming this cache has been enabled by the user, all files which are read from or
  6977. written to by File System (HFS) calls are subject to caching under the current implementation.
  6978. The cache is not “write through” like the track cache. When a File System write (PBWrite,
  6979. WriteResource, etc.) is done, the block is buffered until the block is released (age
  6980. discrimination), a volume flush is done or the application terminates. 
  6981.  
  6982. It may be useful to an application to prevent this process of reading and writing “in
  6983. place”. The Finder disables caching of newly read/written blocks while doing file
  6984. copies since it would be silly to cache files that the Finder was reading into memory
  6985. anyway. Copy protection schemes may also need this capability. Disabling reading and
  6986. writing in place is accomplished by setting a bit in a low memory flag byte, CacheCom
  6987. (see below). When you set this flag, no new candidates for caching will be accepted.
  6988. Blocks already saved may still be read from the cache, of course.
  6989.  
  6990. CacheCom is at $39C. Bit 7 is the bit to set to disable subsequent caching, as follows:
  6991.  
  6992.         MOVE.B CacheCom,saveTemp ;save away the old value
  6993.         BSET.B #7,CacheCom       ;tell caching code to stop R/W I.P.
  6994.         ...
  6995.         BTST.B #7,saveTemp       ;check saved value
  6996.         BNE.S  @69
  6997.         BCLR.B #7,CacheCom       ;clear it if it was cleared before
  6998.     @69
  6999.  
  7000. Bit 6 contains another flag which can force all I/O to go to the disk. If that flag
  7001. is set then every time even one byte is requested from the File System the disk will
  7002. be hit. I can think of no good reason to use this except to test the system code
  7003. itself. The other bits should likewise be left alone.
  7004.  
  7005. Please don’t use this feature unnecessarily; the user should retain control over
  7006. caching. IMPORTANT: if your program doesn’t have enough space to run due to caching
  7007. you should ask the user to disable (or reduce) it with the control panel and then
  7008. relaunch your application. This may be the subject of a future technical note. 
  7009.  
  7010.  
  7011. BufPtr
  7012.  
  7013. The RAM-resident caching software arbitrates BufPtr in the friendliest manner possible.
  7014. It saves the old value away before changing it, and then when it is time to release
  7015. its space it looks at it again. If BufPtr has been moved again, it knows that it
  7016. can’t restore the old value it saved until BufPtr is put back to where it left it. In
  7017. this manner any subsequent code or data put up under BufPtr is assured of not being
  7018. obliterated by the caching routines.
  7019.  
  7020.  
  7021. A final note
  7022.  
  7023. To avoid problems with data in the cache not getting written out to disk, call FlushVol
  7024. after each time you write a file to disk. This ensures that the cache is written, in
  7025. case a crash occurs soon thereafter.
  7026.  
  7027.  
  7028.  
  7029.  
  7030. æKY 82
  7031. æC #82: TextEdit: Advice & Descent
  7032.  
  7033. See also:     TextEdit
  7034.               Technical Note #22 — TEScroll Bug
  7035.               Technical Note #127 — TextEdit EOL Ambiguity
  7036.               Technical Note #131 — TextEdit Bugs
  7037.  
  7038. Written by:      Rick Blair    June 21, 1986
  7039. Updated:                       March 1, 1988
  7040. _______________________________________________________________________________
  7041.  
  7042. This technical note will point out some bugs (and possible workarounds), and other
  7043. items of interest for the TextEdit programmer.
  7044. _______________________________________________________________________________
  7045.  
  7046.  
  7047. TESelRect
  7048.  
  7049. Multiple line selections are often more complex shapes than simple rectangles. If
  7050. this is the case, the teSelRect field of the TERec is set to the last 
  7051. (bottommost) rectangle in the selection. The teHiHook is called to invert each line
  7052. of the selection.
  7053.  
  7054. The ROM limits the selection range (i.e. the lines that get set into teSelRect) to
  7055. only those lines which will fit into the viewRect. This means that teSelRect will be
  7056. left at the last visible line. (The old 64K ROMs made all the calls for the complete
  7057. selection and just let clipping take care of the rest.)
  7058.  
  7059.  
  7060. TEDoText
  7061.  
  7062. The parameters of this special hook into TextEdit need a little additional explanation.
  7063. D3 and D4 are described on page 391 of Inside Macintosh Volume I as being the first
  7064. and last characters to be redrawn. This is true but specific to the –1 “DoDraw” case.
  7065. In fact, all the calls to TEDoText are interested in these first and last character
  7066. positions. They determine the selection for a (1) highlight call, the caret position
  7067. for a (–2) DoCaret call (where D4 is ignored as it’s assumed to equal D3), etc.
  7068.  
  7069. Note that the DoCaret (–2) call behaves differently than described in Inside Macintosh,
  7070. as well. Good old page 391 says it sets up the pen position for caret drawing. Since
  7071. an InvertRect call is used to draw the caret if you use the default teCarHook, the
  7072. ROMs just set up teSelRect, they don’t bother with the QuickDraw pen.
  7073.  
  7074. TEScrpLength
  7075.  
  7076. Inside Macintosh describes TEScrpLength as a long integer; indeed, four bytes are
  7077. reserved for this value with the intent of someday using that range of values. However,
  7078. the ROMs use word operations in their accesses to TEScrpLength and make word calculations
  7079. with it. This means that the high word of TEScrpLength is used for calculations. This
  7080. is something to watch out for.
  7081.  
  7082.  
  7083. CharWidth
  7084.  
  7085. Inside Macintosh says that CharWidth takes stylistic variations into account when
  7086. determining the width of a character. In fact, for italic and outlined styles the
  7087. extra width is not taken into account. TextEdit relies on CharWidth for positioning
  7088. of the caret, etc. If you have chosen to use, for instance, italic style in your TE
  7089. record you will find that as you type the caret actually overlaps the character to
  7090. the left and so when the caret is erased some of that character will get erased, too.
  7091. This is somewhat disconcerting to the user but the program will still function correctly.
  7092.  
  7093.  
  7094. Clikloops
  7095.  
  7096. If you add your own click loop and try to do something like update scroll bars you
  7097. may run into trouble. Before your routine gets called, TextEdit will have set clipping
  7098. down to just the viewRect. You will have to save away the old clipping region, set it
  7099. out to sufficient size (–32767, –32767, 32767, 32767 is probably OK), do your drawing,
  7100. then restore TextEdit’s clipping area so that it can function properly.
  7101.  
  7102.  
  7103.  
  7104.  
  7105. æKY 83
  7106. æC #83: System Heap Size Warning
  7107.  
  7108. See also:      The Memory Manager
  7109.  
  7110. Written by:    Jim Friedlander    June 21, 1986
  7111. Updated:                          March 1, 1988
  7112. _______________________________________________________________________________
  7113.  
  7114. Earlier versions of this note pointed out that, due to varying system heap sizes, the
  7115. application heap does not always start at $CB00. The start of the application heap
  7116. has not been fixed for some time now; programs that depend on it never work on the
  7117. Macintosh SE or the Macintosh II.
  7118.  
  7119.  
  7120. æKY 84
  7121. æC #84: Edit File Format
  7122.  
  7123. Written by:     Harvey Alcabes    April 11, 1985
  7124. Modified by:    Bryan Johnson     August 15, 1986
  7125. Updated:                          March 1, 1988
  7126. _______________________________________________________________________________
  7127.  
  7128. This technical note describes the format of the files created by Edit. It has been
  7129. verified for versions 1.x and 2.0.
  7130. _______________________________________________________________________________
  7131.  
  7132. Edit, a text editor licensed by Apple and included in the Consulair 68000 Development
  7133. System, can read any text-only file whose file type is TEXT. Files created by Edit
  7134. have a creator ID of EDIT. Edit is a disk-based editor so the file length is not
  7135. limited by available memory. Files created or modified by Edit, have the format described
  7136. below; if they are not too long they can be read by any application which can read
  7137. TEXT files (eg: MacWrite, Microsoft Word, or the APDA example program File).
  7138.  
  7139.    The data fork contains text (ASCII characters). Carriage return characters 
  7140.    indicate line breaks; tab characters are displayed as described below. No 
  7141.    other characters have special significance.
  7142.  
  7143.    The resource fork contains resources of type ETAB and EFNT. If Edit opens a 
  7144.    text-only file that does not have these resources it will add them.
  7145.  
  7146.    The ETAB (Editor TAB) resource, resource ID 1004, contains two integers. The 
  7147.    first is the number of pixels to display for each space within a tab (not 
  7148.    necessarily the same as for the space character). The second integer is the 
  7149.    number of these spaces which will be displayed for each tab character.
  7150.  
  7151.    The EFNT (Editor FoNT) resource, resource ID 1003, contains an integer 
  7152.    followed by a Pascal string (length byte followed by characters). The 
  7153.    integer is the point size of the document’s font. The string contains the 
  7154.    font name. If the string size (including the length byte) is odd, an extra 
  7155.    byte is added so that the resource size is even.
  7156.  
  7157. For more information about Edit, contact:
  7158.  
  7159.     Consulair Corp.
  7160.     140 Campo Drive
  7161.     Portola Valley, CA  94025
  7162.     (415) 851-3272
  7163.  
  7164.  
  7165.  
  7166.  
  7167. æKY 85
  7168. æC #85: GetNextEvent; Blinking Apple Menu
  7169.  
  7170. See also:     The Menu Manager
  7171.               The Toolbox Event Manager
  7172.               The Desk Manager
  7173.  
  7174. Written by:   Rick Blair    August 14, 1986
  7175. Updated:                    March 1, 1988
  7176. _______________________________________________________________________________
  7177.  
  7178. Wherein arcane mysteries are unraveled so you can make the Alarm Clock (or a similar
  7179. desk accessory) blink the Apple menu at the appointed second.
  7180. Also, why GetNextEvent is a good thing.
  7181. _______________________________________________________________________________
  7182.  
  7183. The obvious
  7184.  
  7185. Don’t disable interrupts within an application! There will almost certainly come a
  7186. time (or Macintosh) where you won’t be able to change the interrupt mask because the
  7187. processor is running in user mode. The one-second interrupt is used to blink the
  7188. apple.
  7189.  
  7190.  
  7191. The not-so-obvious
  7192.  
  7193. You must call GetNextEvent periodically. GetNextEvent uses a filter (GNE filter)
  7194. which allows for a routine to be installed which overrides (or augments) the behavior
  7195. of the system. The GNE filter is installed by pointing the low-memory global jGNEFilter
  7196. (a long word at $29A) to the routine. After all other GNE processing is complete, the
  7197. routine will be called with A1 pointing to the event record and D0 containing the
  7198. boolean result. The filter may then modify the event record or change the function
  7199. result by altering the word on the stack at 4(A7). This word will match D0 initially,
  7200. of course.
  7201.  
  7202. A GNE filter is used to do the blinking when the interrupt handler has announced that
  7203. the moment is at hand. GetOSEvent won’t do. If you don’t have a standard main event
  7204. loop, it is generally a good idea to give GetNextEvent (and SystemTask, too) a call
  7205. whenever you have any idle time. GetNextEvent “extra” services include, but aren’t
  7206. limited to, the following:
  7207.  
  7208.  1. Calling the GNE filter.
  7209.  2. Removing lingering disk-switched windows (uncommon unless memory is tight).
  7210.  3. Making Window Manager activate, deactivate and update events happen.
  7211.  4. Getting various events from a journaling driver when one is playing.
  7212.  5. Giving SystemEvent a chance at each event.
  7213.  6. Running command-shift function key routines (e.g. command-shift-4 to print 
  7214.     the screen to an ImageWriter).
  7215.  
  7216.  
  7217. The more subtle
  7218.  
  7219. When the (default) GNE filter sees that the interrupt handler has set the “time to
  7220. blink” flag, it looks at the first menu in MenuList. The title of that menu must
  7221. consist solely of the “apple” character or no blinking will occur. It really just
  7222. looks at the first word of the string to see if it is $0114. This is a Pascal string
  7223. which has only the $14 “apple” character in it. So you musn’t have any spaces or any
  7224. other characters in the title of your first menu or you’ll get no blinkin’ results.
  7225.  
  7226.  
  7227.  
  7228.  
  7229. æKY 86
  7230. æC #86:    MacPaint Document Format
  7231.  
  7232. Revised by:    Jim Reekes                                             June 1989
  7233. Written by:    Bill Atkinson                                               1983
  7234.  
  7235. This Technical Note describes the internal format of a MacPaint® document, which is a
  7236. standard used by many other programs.  This description is the same as that found in
  7237. the “Macintosh Miscellaneous” section of early Inside Macintosh versions.
  7238. Changes since October 1988:  Fixed bugs in the example code.
  7239. _______________________________________________________________________________
  7240.  
  7241. MacPaint documents are easy to read and write, and they have become a standard interchange
  7242. format for full–page images on the Macintosh.  This Note describes the MacPaint internal
  7243. document format to help developers generate and interpret files in this format.
  7244.  
  7245. MacPaint documents have a file type of “PNTG,” and since they use only the data fork,
  7246. you can ignore the resource fork.  The data fork contains a 512–byte header followed
  7247. by compressed data which represents a single bitmap (576 pixels wide by 720 pixels
  7248. tall).  At a resolution of 72 pixels per inch, this bitmap occupies the full 8 inch
  7249. by 10 inch printable area of a standard ImageWriter printer page.
  7250.  
  7251. Header
  7252.  
  7253. The first 512 bytes of the document form a header of the following format:
  7254.  
  7255.   •  4–byte version number (default = 2)
  7256.   •  38*8 = 304 bytes of patterns
  7257.   •  204 unused bytes (reserved for future expansion)
  7258.  
  7259. As a Pascal record, the document format could look like the following:
  7260.  
  7261.     MPHeader = RECORD
  7262.         Version:     LONGINT;
  7263.         PatArray:    ARRAY [1..38] of Pattern;
  7264.         Future:      PACKED ARRAY [1..204] of SignedByte;
  7265.     END;
  7266.  
  7267. If the version number is zero, the document uses default patterns, so you can ignore
  7268. the rest of the header block, and if your program generates MacPaint documents, you
  7269. can write 512 bytes of zero for the document header.  Most programs which read MacPaint
  7270. documents can skip the header when reading.
  7271.  
  7272. Bitmap
  7273.  
  7274. Following the header are 720 compressed scan lines of data which form the 576 pixel
  7275. wide by 720 pixel tall bitmap.  Without compression, this bitmap would occupy 51,840
  7276. bytes and chew up disk space pretty fast; typical MacPaint documents compress to
  7277. about 10K using the _PackBits procedure to compress runs of equal bytes within each
  7278. scan line.  The bitmap part of a MacPaint document is simply the output of _PackBits
  7279. called 720 times, with 72 bytes of input each time.
  7280.  
  7281. To determine the maximum size of a MacPaint file, it is worth noting what Inside
  7282. Macintosh says about _PackBits:
  7283.  
  7284.     “The worst case would be when _PackBits adds one byte to the row
  7285.     of bytes when packing.”
  7286.  
  7287. If we include an extra 512 bytes for the file header information to the size of an
  7288. uncompressed bitmap (51,840), then the total number of bytes would be 52,352.  If we
  7289. take into account the extra 720 “potential” bytes (one for each row) to the previous
  7290. total, the maximum size of a MacPaint file becomes 53,072 bytes.
  7291.  
  7292. Reading Sample
  7293.  
  7294. PROCEDURE ReadMPFile;
  7295. { This is a small example procedure written in Pascal that demonstrates
  7296.   how to read MacPaint files. As a final step, it takes the data that
  7297.   was read and displays it on the screen to show that it worked.
  7298.   Caveat: This is not intended to be an example of good programming
  7299.   practice, in that the possible errors merely cause the program to exit.
  7300.   This is VERY uninformative, and there should be some sort of error handler
  7301.   to explain what happened. For simplicity, and thus clarity, those types
  7302.   of things were deliberately not included. This example will not work
  7303.   on a 128K Macintosh, since memory allocation is done too simplistically.
  7304. }
  7305.  
  7306. CONST
  7307.     DefaultVolume = 0;
  7308.     HeaderSize = 512;                      { size of MacPaint header in bytes } 
  7309.     MaxUnPackedSize = 51840;               { maximum MacPaint size in bytes }
  7310.                                            { 720 lines * 72 bytes/line }
  7311. VAR
  7312.     srcPtr:      Ptr;
  7313.     dstPtr:      Ptr;
  7314.     saveDstPtr:  Ptr;
  7315.     lastDestPtr: Ptr;
  7316.     srcFile:     INTEGER;
  7317.     srcSize:     LONGINT;
  7318.     errCode:     INTEGER;
  7319.     scanLine:    INTEGER;
  7320.     aPort:       GrafPort;
  7321.     theBitMap:   BitMap;
  7322.  
  7323. BEGIN
  7324.     errCode := FSOpen('MP TestFile', DefaultVolume, srcFile); { Open the file. }
  7325.     IF errCode <> noErr THEN ExitToShell;
  7326.         
  7327.     errcode := SetFPos(srcFile, fsFromStart, HeaderSize);   { Skip the header. }
  7328.     IF errCode <> noErr THEN ExitToShell;
  7329.  
  7330.     errCode := GetEOF(srcFile, srcSize);    { Find out how big the file is, }
  7331.     IF errCode <> noErr THEN ExitToShell;   { and figure out source size. }
  7332.  
  7333.     srcSize := srcSize - HeaderSize ;       { Remove the header from count. }
  7334.     srcPtr := NewPtr(srcSize);              { Make buffer just the right size. }
  7335.     IF srcPtr = NIL THEN ExitToShell;
  7336.         
  7337.     errCode := FSRead(srcFile, srcSize, srcPtr);{Read the data into the buffer.}
  7338.     IF errCode <> noErr THEN ExitToShell;   { File marker is past header. }
  7339.  
  7340.     errCode := FSClose(srcFile);            { Close the file we just read. }
  7341.     IF errCode <> noErr THEN ExitToShell;
  7342.  
  7343.     { Create a buffer that will be used for the Destination BitMap. }
  7344.     dstPtr := NewPtrClear(MaxUnPackedSize); {MPW library routine, see TN 219}
  7345.     IF dstPtr = NIL THEN ExitToShell;
  7346.     saveDstPtr := dstPtr;
  7347.  
  7348.     { Unpack each scan line into the buffer. Note that 720 scan lines are
  7349.       guaranteed to be in the file. (They may be blank lines.) In the
  7350.       UnPackBits call, the 72 is the count of bytes done when the file was
  7351.       created.  MacPaint does one scan line at a time when creating the file.
  7352.       The destination pointer is tested each time through the scan loop.
  7353.       UnPackBits should increment this pointer by 72, but in the case where
  7354.       the packed file is corrupted UnPackBits may end up sending bits into
  7355.       uncharted territory.  A temporary pointer "lastDstPtr" is used for
  7356.       testing the result.}
  7357.  
  7358.     FOR scanLine := 1 TO 720 DO BEGIN
  7359.          lastDstPtr := dstPtr;
  7360.          UnPackBits(srcPtr, dstPtr, 72);    { bumps both pointers }
  7361.          IF ORD4(lastDstPtr) + 72 <> ORD4(dstPtr) THEN ExitToShell;
  7362.     END;
  7363.  
  7364.     { The buffer has been fully unpacked. Create a port that we can draw into.
  7365.       You should save and restore the current port.  }
  7366.     OpenPort(@aPort);
  7367.  
  7368.     { Create a BitMap out of our saveDstPtr that can be copied to the screen. }
  7369.     theBitMap.baseAddr := saveDstPtr;
  7370.     theBitMap.rowBytes := 72;               { width of MacPaint picture }
  7371.     SetPt(theBitMap.bounds.topLeft, 0, 0);
  7372.     SetPt(theBitMap.bounds.botRight, 72*8, 720); {maximum rectangle}
  7373.  
  7374.     { Now use that BitMap and draw the piece of it to the screen.
  7375.       Only draw the piece that is full screen size (portRect). }
  7376.     CopyBits(theBitMap, aPort.portBits, aPort.portRect,
  7377.              aPort.portRect, srcCopy, NIL);
  7378.  
  7379.     { We need to dispose of the memory we’ve allocated.  You would not
  7380.       dispose of the destPtr if you wish to edit the data.  }
  7381.     DisposPtr(srcPtr);                  { dispose of the source buffer }
  7382.     DisposPtr(dstPtr);                  { dispose of the destination buffer }
  7383. END;
  7384.  
  7385.  
  7386. Writing Sample
  7387.  
  7388. PROCEDURE WriteMPFile;
  7389. { This is a small example procedure written in Pascal that demonstrates how 
  7390.   to write MacPaint files. It will use the screen as a handy BitMap to be
  7391.   written to a file.
  7392. }
  7393.     
  7394. CONST
  7395.     DefaultVolume = 0;
  7396.     HeaderSize = 512;                      { size of MacPaint header in bytes } 
  7397.     MaxFileSize = 53072;                   { maximum MacPaint file size. }
  7398.  
  7399. VAR
  7400.     srcPtr:      Ptr;
  7401.     dstPtr:      Ptr;
  7402.     dstFile:     INTEGER;
  7403.     dstSize:     LONGINT;
  7404.     errCode:     INTEGER;
  7405.     scanLine:    INTEGER;
  7406.     aPort:       GrafPort;
  7407.     dstBuffer:   PACKED ARRAY[1..HeaderSize] OF BYTE;
  7408.     I:           LONGINT;
  7409.     picturePtr:  Ptr;
  7410.     tempPtr:     BigPtr;
  7411.     theBitMap:   BitMap;
  7412.     
  7413. BEGIN
  7414.     { Make an empty buffer that is the picture size. }
  7415.     picturePtr := NewPtrClear(MaxFileSize);    {MPW library routine, see TN 219}
  7416.     IF picturePtr = NIL THEN ExitToShell;
  7417.  
  7418.     { Open a port so we can get to the screen's BitMap easily.  You should save
  7419.       and restore the current port. }
  7420.     OpenPort(@aPort);
  7421.  
  7422.     { Create a BitMap out of our dstPtr that can be copied to the screen. }
  7423.     theBitMap.baseAddr := picturePtr;
  7424.     theBitMap.rowBytes := 72;                  { width of MacPaint picture }
  7425.     SetPt(theBitMap.bounds.topLeft, 0, 0);
  7426.     SetPt(theBitMap.bounds.botRight, 72*8, 720); {maximum rectangle}
  7427.  
  7428.     { Draw the screen over into our picture buffer. }
  7429.     CopyBits(aPort.portBits, theBitMap, aPort.portRect,
  7430.              aPort.portRect, srcCopy, NIL);
  7431.  
  7432.     { Create the file, giving it the right Creator and File type.}
  7433.     errCode := Create('MP TestFile', DefaultVolume, 'MPNT', 'PNTG');
  7434.     IF errCode <> noErr THEN ExitToShell;
  7435.  
  7436.     { Open the data file to be written. }
  7437.     errCode := FSOpen(dstFileName, DefaultVolume, dstFile);
  7438.     IF errCode <> noErr THEN ExitToShell;
  7439.  
  7440.     FOR I := 1 to HeaderSize DO               { Write the header as all zeros. }
  7441.          dstBuffer[I] := 0;
  7442.     errCode := FSWrite(dstFile, HeaderSize, @dstBuffer);
  7443.     IF errCode <> noErr THEN ExitToShell;
  7444.  
  7445.  
  7446.     { Now go into a loop where we pack each line of data into the buffer,
  7447.       then write that data to the file. We are using the line count of 72
  7448.       in order to make the file readable by MacPaint. Note that the
  7449.       Pack/UnPackBits can be used for other purposes. }
  7450.     srcPtr := theBitMap.baseAddr;              { point at our picture BitMap }
  7451.     FOR scanLine := 1 to 720 DO
  7452.          BEGIN
  7453.             dstPtr := @dstBuffer;              { reset the pointer to bottom }
  7454.             PackBits(srcPtr, dstPtr, 72);             { bumps both ptrs }
  7455.             dstSize := ORD(dstPtr)-ORD(@dstBuffer);   { calc packed size }
  7456.             errCode := FSWrite(dstFile, dstSize, @dstBuffer);
  7457.             IF errCode <> noErr THEN ExitToShell;
  7458.          END;
  7459.  
  7460.     errCode := FSClose(dstFile);               { Close the file we just wrote. }
  7461.     IF errCode <> noErr THEN ExitToShell;
  7462. END;
  7463.  
  7464.  
  7465. Further Reference:
  7466. _______________________________________________________________________________
  7467.   •  Inside Macintosh, Volume I-135, QuickDraw
  7468.   •  Inside Macintosh, Volume I-465, Toolbox Utilities
  7469.   •  Inside Macintosh, Volume II-77, The File Manager
  7470.   •  Technical Note #219, New Memory Manager Glue Routines
  7471.  
  7472. MacPaint is a registered trademark of Claris Corporation.
  7473.  
  7474.  
  7475. æKY 87
  7476. æC #87: Error in FCBPBRec
  7477.  
  7478. See also:      The File Manager
  7479.  
  7480. Written by:    Jim Friedlander    August 18, 1986
  7481. Updated:                          March 1, 1988
  7482. _______________________________________________________________________________
  7483.  
  7484. The declaration of a FCBPBRec is wrong in Inside Macintosh Volume IV  and early versions
  7485. of MPW. This has been fixed in MPW 1.0 and newer.
  7486. _______________________________________________________________________________
  7487.  
  7488. An error was made in the declaration of an FCBPBRec parameter block that is used in
  7489. PBGetFCBInfo calls.  The field ioFCBIndx was incorrectly listed as a LONGINT.  The
  7490. following declaration (found in Inside Macintosh):
  7491.  
  7492.    ...
  7493.    ioRefNum:    INTEGER;
  7494.    filler:      INTEGER;
  7495.    ioFCBIndx:   LONGINT;
  7496.    ioFCBFlNm:   LONGINT;
  7497.    ...
  7498.  
  7499. should be changed to:
  7500.     
  7501.    ...
  7502.    ioRefNum:     INTEGER;
  7503.    filler:       INTEGER;
  7504.    ioFCBIndx:    INTEGER;
  7505.    ioFCBFiller1: INTEGER;
  7506.    ioFCBFlNm:    LONGINT;
  7507.    ...
  7508.  
  7509.  
  7510.  
  7511.  
  7512. æKY 88
  7513. æC #88: Signals
  7514.  
  7515. See also:       Using Assembly Language (Mixing Pascal & Assembly)
  7516.  
  7517. Written by:     Rick Blair    August 1, 1986
  7518. Updated:                      March 1, 1988
  7519. _______________________________________________________________________________
  7520.  
  7521. Signals are a form of intra-program interrupt which can greatly aid clean, inexpensive
  7522. error trapping in stack frame intensive languages. A program may invoke the Signal
  7523. procedure and immediately return to the last invocation of CatchSignal, including the
  7524. complete stack frame state at that point.
  7525. _______________________________________________________________________________
  7526.  
  7527. Signals allow a program to leave off execution at one point and return control to a
  7528. convenient error trap location, regardless of how many levels of procedure nesting
  7529. are in between.
  7530.  
  7531. The example is provided with a Pascal interface, but it is easily adapted to other
  7532. languages. The only qualification is that the language must bracket its procedures
  7533. (or functions) with LINK and UNLK instructions. This will allow the signal code to
  7534. clean up at procedure exit time by removing CatchSignal entries from its internal
  7535. queue.
  7536. Note: only procedures and/or functions that call CatchSignal need to be bracketed
  7537. with LINK and UNLK instructions.
  7538.  
  7539. Important: InitSignals must be called from the main program so that A6 can be set up
  7540. properly.
  7541.  
  7542. Note that there is no limit to the number of local CatchSignals which may occur within
  7543. a single routine. Only the last one executed will apply, of course, unless you call
  7544. FreeSignal. FreeSignal will “pop” off the last CatchSignal. If you attempt to Signal
  7545. with no CatchSignals pending, Signal will halt the program with a debugger trap.
  7546.  
  7547. InitSignals creates a small relocatable block in the application heap to hold the
  7548. signal queue. If CatchSignal is unable to expand this block (which it does 5 elements
  7549. at a time), then it will signal back to the last successful CatchSignal with code =
  7550. 200. A Signal(0) acts as a NOP, so you may pass OSErrs, for instance, after making
  7551. File System type calls, and, if the OSErr is equal to NoErr, nothing will happen.
  7552.  
  7553. CatchSignal may not be used in an expression if the stack is used to evaluate that
  7554. expression. For example, you can’t write:
  7555.  
  7556.     c:= 3*CatchSignal;
  7557.  
  7558.  
  7559. “Gotcha” summary
  7560.  
  7561.  1. Routines which call CatchSignal must have stack frames.
  7562.  2. InitSignals must be called from the outermost (main) level.
  7563.  3. Don’t put the CatchSignal function in an expression. Assign the result to 
  7564.     an INTEGER variable; i.e. i:=CatchSignal.
  7565.  4. It’s safest to call a procedure to do the processing after CatchSignal 
  7566.     returns. See the Pascal example TestSignals below. This will prevent the 
  7567.     use of a variable which may be held in a register.
  7568.  
  7569. Below are three separate source files. First is the Pascal interface to the signaling
  7570. unit, then the assembly language which implements it in MPW Assembler format. Finally,
  7571. there is an example program which demonstrates the use of the routines in the unit.
  7572.  
  7573. {File ErrSignal.p}
  7574. UNIT ErrSignal;
  7575.  
  7576. INTERFACE
  7577.  
  7578. {Call this right after your other initializations (InitGraf, etc.)--in other words as
  7579. early as you can in the application}
  7580. PROCEDURE InitSignals;
  7581.  
  7582. {Until the procedure which encloses this call returns, it will catch subsequent Signal
  7583. calls, returning the code passed to Signal. When CatchSignal is encountered initially,
  7584. it returns a code of zero. These calls may "nest"; i.e. you may have multiple CatchSignals
  7585. in one procedure.
  7586. Each nested CatchSignal call uses 12 bytes of heap space }
  7587. FUNCTION CatchSignal:INTEGER;
  7588.  
  7589. {This undoes the effect of the last CatchSignal. A Signal will then invoke the CatchSignal
  7590. prior to the last one.}
  7591. PROCEDURE FreeSignal;
  7592.  
  7593. {Returns control to the point of the last CatchSignal. The program will then behave
  7594. as though that CatchSignal had returned with the code parameter supplied to Signal.}
  7595. PROCEDURE Signal(code:INTEGER);
  7596.  
  7597. END.
  7598. {End of ErrSignal.p}
  7599.  
  7600.  
  7601. Here’s the assembly source for the routines themselves:
  7602.  
  7603. ; ErrSignal code w. InitSignal, CatchSignal,FreeSignal, Signal
  7604. ; defined
  7605. ;
  7606. ;               Version 1.0 by Rick Blair
  7607.  
  7608.     PRINT    OFF
  7609.     INCLUDE    'Traps.a'
  7610.     INCLUDE    'ToolEqu.a'
  7611.     INCLUDE    'QuickEqu.a'
  7612.     INCLUDE    'SysEqu.a'
  7613.     PRINT    ON
  7614.                         
  7615. CatchSigErr    EQU    200    ;"insufficient heap" message
  7616. SigChunks    EQU    5     ;number of elements to expand by
  7617. FrameRet    EQU    4     ;return addr. for frame (off A6)
  7618. SigBigA6    EQU    $FFFFFFFF    ;maximum positive A6 value
  7619.  
  7620.  
  7621. ; A template in MPW Assembler describes the layout of a collection of data 
  7622. ; without actually allocating any memory space. A template definition starts ; with a
  7623. RECORD directive and ends with an ENDR directive.
  7624.  
  7625. ; To illustrate how the template type feature works, the following template 
  7626. ; is declared and used. By using this, the assembler source appromixates very ; closely
  7627. Pascal source for referencing the corresponding information.
  7628.  
  7629. ;template for our table elements
  7630. SigElement    RECORD    0    ;the zero is the template origin
  7631. SigSP    DS.L    1    ;the SP at the CatchSignal—(DS.L just like EQU)
  7632. SigRetAddr    DS.L    1    ;the address where the CatchSignal returned
  7633. SigFRet    DS.L    1    ;return addr. for encl. procedure
  7634. SigElSize    EQU    *    ;just like EQU 12
  7635.     ENDR
  7636.  
  7637.  
  7638. ; The global data used by these routines follows. It is in the form of a 
  7639. ; RECORD, but, unlike above, no origin is specified, which means that memory 
  7640. ; space *will* be allocated.
  7641. ; This data is referenced through a WITH statement at the beginning of the 
  7642. ; procs that need to get at this data. Since the Assembler knows when it is 
  7643. ; referencing data in a data module (since they must be declared before they 
  7644. ; are accessed), and since such data can only be accessed based on A5, there 
  7645. ; is no need to explicitly specify A5 in any code which references the data 
  7646. ; (unless indexing is used). Thus, in this program we have omitted all A5 
  7647. ; references when referencing the data.
  7648.  
  7649. SigGlobals RECORD        ;no origin means this is a data record
  7650.             ;not a template(as above)
  7651. SigEnd    DS.L    1    ;current end of table
  7652. SigNow    DS.L    1    ;the MRU element
  7653. SigHandle    DC.L    0    ;handle to the table
  7654.     ENDR                
  7655.  
  7656. InitSignals PROC    EXPORT    ;PROCEDURE InitSignals;
  7657.                 IMPORT    CatchSignal
  7658.     WITH    SigElement,SigGlobals
  7659.  
  7660. ;the above statement makes the template SigElement and the global data 
  7661. ;record SigGlobals available to this procedure
  7662.                 MOVE.L    #SigChunks*SigElSize,D0
  7663.     _NewHandle    ;try to get a table
  7664.     BNE.S    forgetit    ;we couldn't get that!?
  7665.             
  7666.     MOVE.L    A0,SigHandle    ;save it
  7667.     MOVE.L    #-SigElSize,SigNow ;point "now" before start
  7668.     MOVE.L    #SigChunks*SigElSize,SigEnd ;save the end
  7669.     MOVE.L    #SigBigA6,A6    ;make A6 valid for Signal
  7670. forgetit    RTS
  7671.     ENDP
  7672.  
  7673. CatchSignal PROC    EXPORT    ;FUNCTION CatchSignal:INTEGER;    IMPORT    SiggySetup,Signal,SigDeath
  7674.     WITH    SigElement,SigGlobals
  7675.                 MOVE.L    (SP)+,A1    ;grab return address
  7676.     MOVE.L    SigHandle,D0    ;handle to table
  7677.     BEQ    SigDeath    ;if NIL then croak
  7678.     MOVE.L    D0,A0    ;put handle in A-register
  7679.     MOVE.L    SigNow,D0
  7680.     ADD.L    #SigElSize,D0
  7681.     MOVE.L    D0,SigNow    ;save new position
  7682.     CMP.L    SigEnd,D0    ;have we reached the end?
  7683.     BNE.S    catchit    ;no, proceed
  7684.                 ADD.L    #SigChunks*SigElSize,D0 ;we'll try to expand
  7685.     MOVE.L    D0,SigEnd    ;save new (potential) end
  7686.     _SetHandleSize
  7687.     BEQ.S    @0    ;jump around if it worked!
  7688.             
  7689. ;signals, we use 'em ourselves
  7690.     MOVE.L    SigNow,SigEnd    ;restore old ending offset
  7691.     MOVE.L    #SigElSize,D0
  7692.     SUB.L    D0,SigNow    ;ditto for current position
  7693.     MOVE.W    #catchSigErr,(SP);we'll signal a "couldn't
  7694.                          ;                catch" error
  7695.     JSR    Signal    ;never returns of course
  7696.  
  7697.  
  7698. @0    MOVE.L    SigNow,D0
  7699.             
  7700. catchit    MOVE.L    (A0),A0    ;deref.
  7701.     ADD.L    D0,A0    ;point to new entry
  7702.     MOVE.L    SP,SigSP(A0)    ;save SP in entry
  7703.     MOVE.L    A1,SigRetAddr(A0) ;save return address there
  7704.     CMP.L    #SigBigA6,A6    ;are we at the outer level?
  7705.     BEQ.S    @0    ;yes, no frame or cleanup needed 
  7706.     MOVE.L    FrameRet(A6),SigFRet(A0);save old frame return
  7707.                                 ;               address
  7708.     LEA    SiggyPop,A0
  7709.     MOVE.L    A0,FrameRet(A6) ;set cleanup code address
  7710. @0    CLR.W    (SP)    ;no error code (before its time)
  7711.     JMP    (A1)    ;done setting the trap
  7712.             
  7713. SiggyPop    JSR    SiggySetup    ;get pointer to element
  7714.     MOVE.L    SigFRet(A0),A0 ;get proc's real return address
  7715.     SUB.L    #SigElSize,D0
  7716.     MOVE.L    D0,SigNow    ;"pop" the entry
  7717.     JMP    (A0)    ;gone
  7718.     ENDP
  7719.  
  7720. FreeSignal    PROC    EXPORT    ;PROCEDURE FreeSignal;
  7721.     IMPORT    SiggySetup
  7722.     WITH    SigElement,SigGlobals
  7723.     JSR    SiggySetup    ;get pointer to current entry
  7724.     MOVE.L    SigFRet(A0),FrameRet(A6) ;"pop" cleanup code
  7725.     SUB.L    #SigElSize,D0
  7726.     MOVE.L    D0,SigNow    ;"pop" the entry
  7727.     RTS
  7728.     ENDP
  7729.  
  7730. Signal    PROC    EXPORT    ;PROCEDURE Signal(code:INTEGER);
  7731.     EXPORT    SiggySetup,SigDeath
  7732.     WITH    SigElement,SigGlobals
  7733.     MOVE.W    4(SP),D1    ;get code
  7734.     BNE.S    @0    ;process the signal if code is non-zero
  7735.     MOVE.L    (SP),A0    ;save return address
  7736.     ADDQ.L    #6,SP    ;adjust stack pointer
  7737.     JMP    (A0)    ;return to caller(code was 0)
  7738.  
  7739. @0    JSR    SiggySetup    ;get pointer to entry
  7740.     BRA.S    SigLoop1
  7741.             
  7742. SigLoop    UNLK    A6    ;unlink stack by one frame
  7743. SigLoop1    CMP.L    SigSP(A0),A6    ;is A6 beyond the saved stack?
  7744.     BLO.S    SigLoop          ;yes, keep unlinking
  7745.     MOVE.L   SigSP(A0),SP     ;bring back our SP
  7746.     MOVE.L   SigRetAddr(A0),A0 ;get return address
  7747.     MOVE.W   D1,(SP)          ;return code to CatchSignal
  7748.     JMP      (A0)             ;Houston, boost the Signal!
  7749.         ;(or Hooston if you're from the Negative Zone) 
  7750.  
  7751. SiggySetup    MOVE.L    SigHandle,A0
  7752.     MOVE.L   (A0),A0    ;deref.
  7753.     MOVE.L   A0,D0      ;to set CCR
  7754.     BEQ.S    SigDeath   ;nil handle means trouble
  7755.     MOVE.L   SigNow,D0  ;grab table offset to entry
  7756.     BMI.S    SigDeath   ;if no entries then give up
  7757.     ADD.L    D0,A0      ;point to current element
  7758.     RTS
  7759.  
  7760. SigDeath    _Debugger        ;a signal sans catch is bad news
  7761.  
  7762.     ENDP
  7763.     END
  7764.  
  7765.  
  7766. Now for the example Pascal program:
  7767.  
  7768. PROGRAM TestSignals;
  7769. USES ErrSignal;
  7770.  
  7771. VAR i:INTEGER;
  7772.  
  7773. PROCEDURE DoCatch(s:STR255; code:INTEGER);
  7774. BEGIN
  7775.   IF code<>0 THEN BEGIN
  7776.     Writeln(s,code);
  7777.     Exit(TestSignals);
  7778.   END;
  7779. END; {DoCatch}
  7780.  
  7781. PROCEDURE Easy;
  7782.   PROCEDURE Never;
  7783.     PROCEDURE DoCatch(s:STR255; code:INTEGER);
  7784.     BEGIN
  7785.       IF code<>0 THEN BEGIN
  7786.         Writeln(s,code);
  7787.         Exit(Never);
  7788.       END;
  7789.     END; {DoCatch}
  7790.  
  7791.   BEGIN {Never}
  7792.   i:=CatchSignal;
  7793.   DoCatch('Signal caught from Never, code = ', i );
  7794.  
  7795.   i:=CatchSignal;
  7796.   IF i<>0 THEN DoCatch('Should never get here!',i);
  7797.        
  7798.   FreeSignal; {"free" the last CatchSignal}
  7799.   Signal(7); {Signal a 7 to the last CatchSignal}
  7800.   END;{Never}
  7801. BEGIN {Easy}
  7802. Never;
  7803. Signal(69);     {this won't be caught in Never}
  7804. END;{Easy}    {all local CatchSignals are freed when a procedure exits.}
  7805.  
  7806. BEGIN {PROGRAM}
  7807. InitSignals; {You must call this early on!}
  7808.  
  7809. {catch Signals not otherwise caught by the program}
  7810. i:=CatchSignal;
  7811. IF i<>0 THEN
  7812.  DoCatch('Signal caught from main, code = ',i);
  7813.  
  7814. Easy;
  7815. END.
  7816.  
  7817.  
  7818. The example program produces the following two lines of output:
  7819.  
  7820.  Signal caught from Never, code = 7
  7821.  Signal caught from main, code = 69
  7822.  
  7823.  
  7824.  
  7825.  
  7826. æKY 89
  7827. æC #89: DrawPicture Bug
  7828.  
  7829. Written by:     Ginger Jernigan    August 16, 1986
  7830. Updated:                           March 1, 1988
  7831. _______________________________________________________________________________
  7832.  
  7833. Earlier versions of this note described a bug in DrawPicture. This bug never occurred
  7834. on 64K ROM machines, and has been fixed in System 3.2 and newer. Use of Systems older
  7835. than 3.2 on non-64K ROM machines is no longer recommended.
  7836.  
  7837.  
  7838. æKY 90
  7839. æC #90: SANE Incompatibilities 
  7840.  
  7841. Written by:    Mark Baumwell    August 14, 1986
  7842. Updated:                        March 1, 1988
  7843. _______________________________________________________________________________
  7844.  
  7845. Earlier versions of this note described a problem with SANE and System 2.0. Use of
  7846. System 2.0 is only recommended for Macintosh 128 machines, which contain the 64K
  7847. ROMs. Information specific to 64K ROM machines has been deleted from Macintosh Technical
  7848. Notes for reasons of clarity.
  7849.  
  7850.  
  7851. æKY 91
  7852. æC #91: Optimizing for the LaserWriter—Picture Comments
  7853.  
  7854. See also:     The Print Manager
  7855.               QuickDraw
  7856.               Technical Note #72 — Optimizing for the LaserWriter—Techniques
  7857.               Technical Note #27 — MacDraw Picture Comments
  7858.               PostScript Language Reference Manual, Adobe Systems
  7859.               PostScript Language Tutorial and Cookbook, Adobe Systems
  7860.               LaserWriter Reference Manual
  7861.  
  7862. Written by:     Ginger Jernigan     November 15, 1986
  7863. Modified by:    Ginger Jernigan     March 2, 1987
  7864. Updated:                            March 1, 1988
  7865. _______________________________________________________________________________
  7866.  
  7867. This technical note is a continuation of Technical Note #72. This technical note
  7868. discusses the picture comments that the LaserWriter driver recognizes.
  7869.  
  7870. This technical note has been modified to include corrected descriptions of the SetLineWidth,
  7871. PostScriptFile and ResourcePS comments and to include some additional warnings.
  7872. _______________________________________________________________________________
  7873.  
  7874. The implementation of QuickDraw’s picComment facility by the LaserWriter driver allows
  7875. you to take advantage of features (like rotated text) which are available in PostScript
  7876. but may not be available in QuickDraw.
  7877.  
  7878. WARNING: Using PostScript-specific comments will make your code printer-dependent and
  7879. may cause compatibility problems with non-PostScript devices, so don’t use them unless
  7880. you absolutely have to.
  7881.  
  7882. Some of the picture comments below are designed to be issued along with QuickDraw
  7883. commands that simulate the commented commands on the Macintosh screen. When the comments
  7884. are used, the accompanying QuickDraw comments are ignored. If you are designing a
  7885. picture to be printed by the LaserWriter, the structure and use of these comments
  7886. must be precise, otherwise nothing will print. If another printer driver (like the
  7887. ImageWriter I/II driver) has not implemented these comments, the comments are ignored
  7888. and the accompanying QuickDraw commands are used.
  7889.  
  7890. Below are the picture comments that the LaserWriter driver recognizes:
  7891.  
  7892. Type           Kind  DataSize  Data        Description
  7893.  
  7894. TextBegin       150      6     TTxtPicRec    Begin text function
  7895. TextEnd         151      0     NIL           End text function
  7896. StringBegin     152      0     NIL           Begin pieces of original string
  7897. StringEnd       153      0     NIL           End pieces of original string
  7898. TextCenter      154      8     TTxtCenter    Offset to center of rotation
  7899.  
  7900. *LineLayoutOff  155      0     NIL           Turns LaserWriter line layout off
  7901. *LineLayoutOn   156      0     NIL           Turns LaserWriter line layout on
  7902.  
  7903. PolyBegin       160      0     NIL           Begin special polygon
  7904. PolyEnd         161      0     NIL           End special polygon
  7905. PolyIgnore      163      0     NIL           Ignore following poly data
  7906. PolySmooth      164      1     PolyVerb      Close, Fill, Frame
  7907. picPlyClo       165      0     NIL           Close the poly
  7908.  
  7909. *DashedLine     180      -     TDashedLine   Draw following lines as dashed
  7910. *DashedStop     181      0     NIL           End dashed lines
  7911. *SetLineWidth   182      4     Point         Set fractional line widths
  7912.  
  7913. *PostScriptBegin  190    0    NIL            Set driver state to PostScript
  7914. *PostScriptEnd    191    0    NIL            Restore QuickDraw state
  7915. *PostScriptHandle 192    -    PSData         PostScript data in handle
  7916. *†PostScriptFile  193    -    FileName       FileName in data handle
  7917. *TextIsPostScript 194    0    NIL            QuickDraw text is sent as
  7918.                                              PostScript
  7919. *†ResourcePS      195    8    Type/ID/Index  PostScript data in a resource file
  7920.  
  7921. **RotateBegin   200      4    TRotation      Begin rotated port
  7922. **RotateEnd     201      0    NIL            End rotation
  7923. **RotateCenter  202      8    Center         Offset to center of rotation
  7924.  
  7925. **FormsPrinting    210   0    NIL    Don’t clear print buffer after each page
  7926. **EndFormsPrinting 211   0    NIL    End forms printing after PrClosePage
  7927.  
  7928. *  These comments are only implemented in LaserWriter driver 3.0 or later.
  7929. ** These comments are only implemented in LaserWriter driver 3.1 or later.
  7930. †  These comments are not available when background printing is enabled.
  7931.  
  7932. Each of these comments are discussed below in six groups: Text, Polygons, Lines,
  7933. PostScript, Rotation, and Forms. Code examples are given where appropriate. For other
  7934. examples of how to use picture comments for printing please see the Print example
  7935. program in the Software Supplement (currently available through APDA as “Macintosh
  7936. Example Applications and Sources 1.0”).
  7937.  
  7938.  • Note: The examples used in the LaserWriter Reference Manual are incorrect. 
  7939.    Please use the examples presented here instead.
  7940.  
  7941.  
  7942. Text
  7943.  
  7944. In order to support the What-You-See-Is-What-You-Get paradigm, the LaserWriter driver
  7945. uses a line layout algorithm to assure that the placement of the line on the printer
  7946. closely approximates the placement of the line on the screen. This means that the
  7947. printer driver gets the width of the line from QuickDraw, then tells PostScript to
  7948. place the text in exactly the same place with the same width.
  7949.  
  7950. The TextBegin comment allows the application to specify the layout and the orientation
  7951. of the text that follows it by specifying the following information:
  7952.  
  7953. TTxtPicRec = PACKED RECORD
  7954.     tJus:     Byte;     {0,1,2,3,4 or greater => none, left, center, right, 
  7955.                         full justification }
  7956.     tFlip:    Byte;     {0,1,2 => none, horizontal, vertical coordinate flip }
  7957.     tRot:     INTEGER;  {0..360 => clockwise rotation in degrees }
  7958.     tLine:    Byte;     {1,2,3.. => single, 1-1/2, double.. spacing }
  7959.     tCmnt:    Byte;     {Reserved }
  7960. END; { TTxtPicRec }
  7961.  
  7962. Left, right or center justification, specified by tJust, tells the driver to maintain
  7963. only the left, right or center point, without recalculating the interword spacing.
  7964. Full justification specifies that both endpoints be maintained and interword spacing
  7965. be recalculated. This means that the driver makes sure that the specified points are
  7966. maintained on the printer without caring whether the overall width has changed. Full
  7967. justification means that the overall width of the line has been maintained. tFlip and
  7968. tRot specify the orientation of the text, allowing the application to take advantage
  7969. of the rotation features of PostScript. tLine specifies the interline spacing. When
  7970. no TextBegin comment is used, the defaults are full justification, no rotation and
  7971. single-spaced lines.
  7972.  
  7973. String Reconstruction
  7974.  
  7975. The StringBegin and StringEnd comments are used to bracket short strings of text that
  7976. are actually sections of an original long string. MacDraw, for instance, breaks long
  7977. strings into shorter pieces to avoid stack overflow problems with QuickDraw in the
  7978. 64K ROM. When these smaller strings are bracketed by StringBegin and StringEnd, the
  7979. LaserWriter driver assumes that the enclosed strings are parts of one long string and
  7980. will perform its line layout accordingly. Erasing or filling of background rectangles
  7981. should take place before the StringBegin comment to avoid confusing the process of
  7982. putting the smaller strings back together.
  7983.  
  7984. Text Rotation
  7985.  
  7986. In order to rotate a text object, PostScript needs to have information concerning the
  7987. center of rotation. The TextCenter comment provides this information when a rotation
  7988. is specified in the TextBegin comment. This comment contains the offset from the
  7989. present pen location to the center of rotation. The offset is given as the y-component,
  7990. then the x-component, which are declared as fixed-point numbers. This allows the
  7991. center to be in the middle of a pixel. This comment should appear after the TextBegin
  7992. comment and before the first following StringBegin comment.
  7993.  
  7994. The associated comment data looks like this:
  7995.  
  7996. TTxtCenter = RECORD
  7997.     y,x: Fixed;    {offset from current pen location to center of rotation}
  7998. END; { TTxtCenter }
  7999.  
  8000. Right after a TextBegin comment, the LaserWriter driver expects to see a TextCenter
  8001. comment specifying the center of rotation for any text enclosed within the text comment
  8002. calls. It will ignore all further CopyBits calls, and print all standard text calls
  8003. in the rotation specified by the information in TTxtPicRec. The center of rotation is
  8004. the offset from the beginning position of the first string following the TextCenter
  8005. comment. The printer driver also expects the string locations to be in the coordinate
  8006. system of the current QuickDraw port. The printer driver rotates the entire port to
  8007. draw the text so it can draw several strings with one rotation comment and one center
  8008. comment. It is good practice to enclose an entire paragraph or paragraphs of text in
  8009. a single rotation comment so that the driver makes the fewest number of rotations.
  8010.  
  8011. The printer driver can draw non-textual objects within the bounds of the text rotation
  8012. comments but it must unrotate to draw the object, then re-rotate to draw the next
  8013. string of text. To do this the printer driver must receive another TextCenter comment
  8014. before each new rotation. So, rotated text and unrotated objects can be drawn inter-mixed
  8015. within one TextBegin/TextEnd comment pair, but performance is slowed.
  8016.  
  8017. Note that all bit maps and all clip regions are ignored during text rotation so that
  8018. clip regions can be used to clip out the strings on printers that can’t take advantage
  8019. of these comments. This has the unfortunate side effect of not allowing rotated text
  8020. to be clipped.
  8021.  
  8022. Rotated text comments are not associated with landscape and portrait orientation of
  8023. the printer paper as selected by the Page Setup dialog. These are rotations with
  8024. reference to the current QuickDraw port only.
  8025.  
  8026. All of the above text comments are terminated by a TextEnd comment.
  8027.  
  8028. Turning Off Line Layout
  8029.  
  8030. If your application is using its own line layout algorithm (it uses its own character
  8031. widths or does its own character or word placement), the printer driver doesn’t need
  8032. to do it too. To turn off line layout, you can use the LineLayoutOff comment. LineLayoutOn
  8033. turns it on again.
  8034.  
  8035. Turning on FractEnable for the 128K ROMs has the same effect as LineLayoutOff. When
  8036. the driver detects that FractEnable has been turned on, line layout is not performed.
  8037. The driver assumes that all text being printed is already spaced correctly for the
  8038. LaserWriter and just sends it as is.
  8039.  
  8040.  
  8041. Polygons
  8042.  
  8043. The polygon comments are recognized by the LaserWriter driver because they are used
  8044. by MacDraw as an alternate method of defining polygons.
  8045.  
  8046. The PolyBegin and PolyEnd comments bracket polygon line segments, giving an alternate
  8047. way to specify a polygon. All StdLine calls between these two comments are part of
  8048. the polygon. The endpoints of the lines are the vertices of the polygon.
  8049.  
  8050. The picPlyClo comment specifies that the current polygon should be closed. This comes
  8051. immediately after PolyBegin, if at all. It is not sufficient to simply check for
  8052. begPt = endPt, since MacDraw allows you to create a “closed” polygon that isn’t really
  8053. closed. This comment is especially critical for smooth curves because it can make the
  8054. difference between having a sharp corner or not in the curve.
  8055.  
  8056. These comments also work with the StdPoly call. If a FillRgn is encountered before
  8057. the PolyEnd comment, then the polygon is filled. Unlike QuickDraw polygons, comment
  8058. polygons do not require an initial MoveTo call within the scope of the polygon comment.
  8059. The polygon will be drawn using the current pen location at the time the polygon
  8060. comment is received. The pen must be set before the polygon comment is called.
  8061.  
  8062. Splines
  8063.  
  8064. A spline is a method used to determine the smallest number of points that define a
  8065. curve. In MacDraw, splines are used as a method for smoothing polygons. The vertices
  8066. of the underlying unsmoothed polygon are the control nodes for the quadratic B-spline
  8067. curve which is drawn. PostScript has a direct facility for cubic B-splines and the
  8068. LaserWriter translates the quadratic B-spline nodes it gets into the appropriate
  8069. nodes for a cubic B-spline that will exactly emulate the original quadratic B-spline.
  8070.  
  8071. The PolySmooth comment specifies that the current polygon should be smoothed. This
  8072. comment also contains data that provides a means of specifying which verbs to use on
  8073. the smoothed polygon (bits 7 through 3 are not currently assigned):
  8074.  
  8075. TPolyVerb = PACKED RECORD
  8076.     f7, f6, f5, f4, f3, fPolyClose, fPolyFill, fPolyframe : Boolean;
  8077. END; { TPolyVerb }
  8078.  
  8079. Although the closing information is redundant with the picPlyClo comment, it is included
  8080. for the convenience of the LaserWriter.
  8081.  
  8082. The LaserWriter uses the pen size at the time the PolyBegin comment is received to
  8083. frame the smoothed polygon if framing is called for by the TPolyVerb information.
  8084. When the PolyIgnore comment is received by the LaserWriter driver, all further StdLine
  8085. calls are ignored until the PolyEnd comment is encountered. For polygons that are to
  8086. be smoothed, set the initial pen width to zero after the PolyBegin comment so that
  8087. the unsmoothed polygon will not be drawn by other printers not equipped to handle
  8088. polygon comments. To fill the polygon, call StdRgn with the fill verb and the appropriate
  8089. pattern set, as well as specifying fill in the PolySmooth comment.
  8090.  
  8091.  
  8092. Lines
  8093.  
  8094. The DashedLine and DashedLineStop comments are used to communicate PostScript information
  8095. for drawing dashed lines.
  8096.  
  8097. The DashedLine comment contains the following additional data:
  8098.  
  8099. TDashedLine = PACKED RECORD
  8100.     offset:   SignedByte;    {Offset as specified by PostScript}  centered:    SignedByte;
  8101.   {Whether dashed line should be centered to begin and end points}
  8102.     dashed:   Array[0..1] of SignedByte;    {1st byte is # bytes following}
  8103. END; { TDashedLine }
  8104.  
  8105. The printer driver sets up the PostScript dashed line command, as defined on page 214
  8106. of Adobe’s PostScript Language Reference Manual, using the parameters specified in
  8107. the comment. You can specify that the dashed line be centered between the begin and
  8108. end points of the lines by making the centered field nonzero.
  8109.  
  8110. The SetLineWidth comment allows you to set the pen width of all subsequent objects
  8111. drawn. The additional data is a point. The vertical portion of the point is the numerator
  8112. and the horizontal portion is the denominator of the scaling factor that the horizontal
  8113. and vertical components of the pen are then multiplied by to obtain the new pen width.
  8114. For example, if you have a pen size of 1,2 and in your line width comment you use 2
  8115. for the horizontal of the point and 7 for the vertical, the pen size will then be
  8116. (7/2)*1 pixels wide and (7/2)*2 pixels high.
  8117.  
  8118. Below is an example of how to use the line comments:
  8119.  
  8120. PROCEDURE LineTest;
  8121. {This procedure shows how to do dashed lines and how to change the line width}
  8122. CONST
  8123.   DashedLine = 180;
  8124.   DashedStop = 181;
  8125.   SetLineWidth = 182;
  8126.  
  8127. TYPE
  8128.   DashedHdl = ^DashedPtr;
  8129.   DashedPtr = ^TDashedLine;
  8130.   TDashedLine = PACKED RECORD
  8131.     offset: SignedByte;
  8132.     Centered: SignedByte;
  8133.     dashed: Array[0..1] of SignedByte;   { the 0th element is the length }
  8134.   END; { TDashedLine }
  8135.   widhdl = ^widptr;
  8136.   widptr = ^widpt;
  8137.   widpt = Point;
  8138.  
  8139.  
  8140. VAR
  8141.   arect    : rect;
  8142.   Width    : Widhdl;
  8143.   dashedln : DashedHdl;
  8144.  
  8145. BEGIN {LineTest}
  8146.   Dashedln := dashedhdl(NewHandle(sizeof(tdashedline)));
  8147.   Dashedln^^.offset := 0;       { No offset}
  8148.   Dashedln^^.centered := 0;     { don’t center}
  8149.   Dashedln^^.dashed[0] := 1;    { this is the length }
  8150.   Dashedln^^.dashed[1] := 8;    { this means 8 points on, 8 points off }
  8151.  
  8152.   Width := widhdl(NewHandle(sizeof(widpt)));
  8153.   Width^^.h := 2;           { denominator is 2}
  8154.   Width^^.v := 7;               { numerator is 7}
  8155.  
  8156.   myPic := OpenPicture(theWorld);
  8157.     SetPen(1,2);                { Set the pen size to 1 wide x 2 high }
  8158.     ClipRect(theWorld);
  8159.     MoveTo(20,20);
  8160.     DrawString('Do line test');
  8161.     PicComment(DashedLine,GetHandleSize(Handle(dashedln)),Handle(dashedln)); 
  8162.     PicComment(SetLineWidth,4,Handle(width));   {SetLineWidth}
  8163.     SetRect(arect,100,100,500,500);
  8164.     FrameRect(aRect);
  8165.     MoveTo(500,500);
  8166.     Lineto(100,100);
  8167.     PicComment(DashedStop,0,nil);    {DashedStop}
  8168.   ClosePicture;
  8169.   DisposHandle(handle(width));                {Clean up}
  8170.   DisposHandle(handle(dashedln));
  8171.   PrintThePicture;            {print it please}
  8172.   KillPicture(MyPic);
  8173. END; {LineTest}
  8174.  
  8175.  
  8176. PostScript
  8177.  
  8178. The PostScript comments tell the printer driver that the application is going to be
  8179. communicating with the LaserWriter directly using PostScript commands instead of
  8180. QuickDraw. The driver sends the accompanying PostScript to the printer with no preprocessing
  8181. and no error checking. The application can specify data in the comment handle itself
  8182. or point to another file which contains text to send to the printer. When the application
  8183. is finished sending PostScript, the PostScriptEnd comment tells the printer driver to
  8184. resume normal QuickDraw mode.
  8185.  
  8186. Any Quickdraw drawing commands made by the application between the PostScriptBegin
  8187. and PostScriptEnd comments will be ignored by PostScript printers. In order to use
  8188. PostScript in a device independent way, you should always include two representations
  8189. of your document. The first representation should be a series of Quickdraw drawing
  8190. commands. The second representation of your document should be a series of PostScript
  8191. commands, sent to the Printing Manager via picture comments. This way, when you are
  8192. printing to a PostScript device, the picture comments will be executed, and the Quickdraw
  8193. commands ignored. When printing to a non-PostScript device, the picture comments will
  8194. be ignored, and the Quickdraw commands will be executed. This method allows you to
  8195. use PostScript, without having to ask the device if it supports it. This allows your
  8196. application to get the best results with any printer, without being device dependent.
  8197.  
  8198. Here are some guidelines you need to remember: 
  8199.  
  8200.  • The graphic state set up during QuickDraw calls is maintained and is not
  8201.    affected by PostScript calls made with these comments.
  8202.  
  8203.  • The header has changed a number of parameters so sometimes you won’t get the 
  8204.    results you expect. You may want to take a look at the header listed in 
  8205.    The LaserWriter Reference Manual available through APDA.
  8206.  
  8207.  • The header changes the PostScript coordinate system so that the origin is at 
  8208.    the top-left corner of the page instead of at the bottom-left corner. This 
  8209.    is done so that the QuickDraw coordinates that are used don’t have to be 
  8210.    remapped into the standard PostScript coordinate system. If you don’t allow 
  8211.    for this, all drawing is printed upside down. Please see the PostScript 
  8212.    Language Reference Manual for details about transformation matrices.
  8213.  
  8214.  • Don’t call showpage. This is done for you by the driver. If you do, you 
  8215.    won’t be able to switch back to QuickDraw mode and an additional page will 
  8216.    be printed when you call PrClosePage.
  8217.  
  8218.  • Don’t call exitserver. You may get very strange results.
  8219.  
  8220.  • Don’t call initgraphics. Graphics states are already set up by the header.
  8221.  
  8222.  • Don’t do anything that you expect to live across jobs.
  8223.  
  8224.  • You won’t be able to interrogate the printer to get information back through 
  8225.    the driver.
  8226.  
  8227. The PostScriptBegin comment sets the driver state to prepare for the generation of
  8228. PostScript by the application by calling gsave to save the current state. PostScript
  8229. is then sent to the printer by using comments 192 through 195. The QuickDraw state of
  8230. the driver is then restored by the PostScriptEnd comment. All QuickDraw operations
  8231. that occur outside of these comments are performed; no clipping occurs as with the
  8232. text rotation comments.
  8233.  
  8234. PostScript From a Text Handle
  8235.  
  8236. When the PostScriptHandle comment is used, the handle PSData points to the PostScript
  8237. commands which are sent. PSData is a generic handle that points to text, without a
  8238. length byte. The text is terminated by a carriage return. This comment is terminated
  8239. by a PostScriptEnd comment.
  8240.  
  8241. Note: Due to a bug in the 3.1 LaserWriter driver, PostScriptEnd will not restore the
  8242. QuickDraw state after the use of a PostScriptHandle comment. The workaround is to
  8243. only use this comment at the end of your drawing, after you have made all the QuickDraw
  8244. calls you need. This problem is fixed in more recent versions of the driver.
  8245.  
  8246. Here’s an example of how to use this comment:
  8247.  
  8248. PROCEDURE PostHdl;
  8249. {this procedure shows how to use PostScript from a text Handle}
  8250. CONST
  8251.   PostScriptBegin = 190;
  8252.   PostScriptEnd = 191;
  8253.   PostScriptHandle = 192;
  8254.  
  8255. VAR
  8256.   MyString  : Str255;
  8257.   tempstr   : String[1];
  8258.   MyHandle  : Handle;
  8259.   err       : OSErr;
  8260.  
  8261. BEGIN { PostHdl }
  8262.   MyString := '/Times-Roman findfont 12 scalefont setfont 230 600 moveto
  8263.                (Hello World) show';
  8264.   tempstr:=' ';
  8265.   tempstr[1] := chr(13); {has to be terminated by a carriage return }
  8266.   MyString := Concat(MyString, tempstr); { in order for it to execute}
  8267.   err := PtrToHand (Pointer(ord(@myString)+1), MyHandle, length(MyString));
  8268.   MyPic := OpenPicture(theWorld);
  8269.     ClipRect(theWorld);
  8270.     MoveTo(20,20);
  8271.     DrawString('PostScript from a Handle');
  8272.     PicComment(PostScriptBegin,0,nil);    {Begin PostScript}
  8273.     PicComment(PostScriptHandle,length(mystring),MyHandle); 
  8274.     PicComment(PostScriptEnd,0,nil);    {PostScript End}
  8275.   ClosePicture;
  8276.   DisposHandle(MyHandle);        {Clean up}
  8277.   PrintThePicture;            {print it please}
  8278.   KillPicture(MyPic);
  8279. END; { PostHdl }
  8280.  
  8281. Defining PostScript as QuickDraw Text
  8282.  
  8283. All QuickDraw text following the TextIsPostScript comment is sent as PostScript. No
  8284. error checking is performed. This comment is terminated by a PostScriptEnd comment.
  8285.  
  8286. Here is an example:
  8287.  
  8288. PROCEDURE PostText;
  8289. {Shows how to use PostScript in strings in a QuickDraw picture}
  8290. CONST
  8291.   PostScriptBegin = 190;
  8292.   PostScriptEnd = 191;
  8293.   TextIsPostScript = 194;
  8294.  
  8295. BEGIN { PostTest }
  8296.   MyPic := OpenPicture(theWorld);
  8297.     ClipRect(theWorld);
  8298.     MoveTo(20,20);
  8299.     DrawString('TextIsPostScript Comment');
  8300.     PicComment(PostScriptBegin,0,nil);      {Begin PostScript}
  8301.     PicComment(TextIsPostScript,0,nil);     {following text is PostScript}
  8302.       DrawString('0 728 translate');    {move the origin and rotate the}
  8303.       DrawString('1 -1 scale');    {coordinate system}
  8304.  
  8305.       DrawString('newpath');
  8306.       DrawString('100 470 moveto');
  8307.       DrawString('500 470 lineto');
  8308.       DrawString('100 330 moveto');
  8309.       DrawString('500 330 lineto');
  8310.       DrawString('230 600 moveto');
  8311.       DrawString('230 200 lineto');
  8312.       DrawString('370 600 moveto');
  8313.       DrawString('370 200 lineto');
  8314.       DrawString('10 setlinewidth');
  8315.       DrawString('stroke');
  8316.       DrawString('/Times-Roman findfont 12 scalefont setfont');
  8317.       DrawString('230 600 moveto');
  8318.       DrawString('(Hello World) show');
  8319.     PicComment(PostScriptEnd,0,nil);        {PostScriptEnd}
  8320.   ClosePicture;
  8321.   PrintThePicture;                    {print it please}
  8322.   KillPicture(MyPic);
  8323. END; { PostText }
  8324.  
  8325. PostScript From a File
  8326.  
  8327. The PostScriptFile and ResourcePS comments allow you to send PostScript to the printer
  8328. from a resource file. Before these comments are described there are some restrictions
  8329. you need to follow:
  8330.  
  8331.  • Don’t ever copy a picture containing these comments to the clipboard. If it 
  8332.    is pasted into another application and the specified file or resource is not 
  8333.    available, printing will be aborted and the user won’t know what went wrong. 
  8334.    This could be very confusing to a user. If you want the PostScript 
  8335.    information to be available when printed from another application, use one 
  8336.    of the other comments and include the information in the picture.
  8337.  
  8338.  • Don’t keep the PostScript in a separate file from the actual data file. If 
  8339.    the data file ever gets moved without the PostScript file, when the picture 
  8340.    is printed the data file may not be found and the print job will be aborted, 
  8341.    again without the user knowing what went wrong. Keeping the data and 
  8342.    PostScript in the same file will forestall many headaches for you and the 
  8343.    user.
  8344.  
  8345. Now, a description of the comments:
  8346.  
  8347. The PostScriptFile comment tells the driver to use the POST type resources contained
  8348. in the file FileNameString. FileNameString is declared as a Str255.
  8349.  
  8350. When this comment is encountered, the driver calls OpenResFile using the file name
  8351. specified in FileNameString. It then calls GetResource('POST',theID); repeatedly,
  8352. where theID begins at 501 and is incremented by one for each GetResource call. If the
  8353. driver gets a ResNotFound error, it closes the specified resource file. If the first
  8354. byte of the resource is a 3, 4, or 5 then the remaining data is sent and the file is
  8355. closed.
  8356.  
  8357. The format of the POST resource is as follows: The IDs of the resources start at 501
  8358. and are incremented by one for each resource. Each resource begins with a 2 byte data
  8359. field containing the data type in the first byte and a zero in the second. The possible
  8360. values for the first byte are:
  8361.  
  8362.    0  ignore the rest of this resource (a comment)
  8363.    1  data is ASCII text
  8364.    2  data is binary and is first converted to ASCII before being sent
  8365.    3  AppleTalk end of file. The rest of the data, if there is any, is 
  8366.       interpreted as ASCII text and will be sent after the EOF.
  8367.    4  open the data fork of the current resource file and send the ASCII text 
  8368.       there
  8369.    5  end of the resource file
  8370.  
  8371. The second byte of the field must always be zero. Resources should be kept small,
  8372. around 2K. Text and binary should not be mixed in the same resource. Make sure you
  8373. include either a space or a return at the end of each PostScript string to separate
  8374. it from the following command.
  8375.  
  8376. Here’s an example:
  8377.  
  8378.     PROCEDURE PostFile;
  8379.     {This procedure shows how to use PostScript from a specified FILE}
  8380.     CONST
  8381.         PostScriptBegin = 190;
  8382.         PostScriptFile = 193;
  8383.         PostScriptEnd = 191;
  8384.  
  8385.     VAR
  8386.         MyString     : Str255;
  8387.         MyHandle     : Handle;
  8388.         err        : OSErr;
  8389.  
  8390.     BEGIN     { PostFile }
  8391.         {You should never do this in a real program. This is only a test.}
  8392.         MyString := 'HardDisk:MPW:Print Examples:PSTestDoc';
  8393.         err := PtrToHand(pointer(MyString),MyHandle,length(MyString) + 1);
  8394.         MyPic := OpenPicture(theWorld);
  8395.         ClipRect(theWorld);
  8396.         MoveTo(20,20);
  8397.         DrawString('PostScriptFile Comment');
  8398.         PicComment(PostScriptBegin,0,nil); {Begin PostScript}
  8399.         PicComment(PostScriptFile,GetHandleSize(MyHandle),MyHandle);
  8400.         PicComment(PostScriptEnd,0,nil); {PostScriptEnd}
  8401.         MoveTo(50,50);
  8402.         DrawString('PostScriptEnd has terminated');
  8403.         ClosePicture;
  8404.         DisposHandle(MyHandle); {Clean up}
  8405.         PrintthePicture; {print it please}
  8406.         KillPicture(MyPic);
  8407.     END;        { PostFile }
  8408.  
  8409. Here are the resources:
  8410.  
  8411.     type 'POST' {
  8412.         switch {
  8413.             case Comment:           /* this is a comment */
  8414.                   key bitstring[8] = 0;
  8415.                   fill byte;
  8416.                   string;                                    case ASCII:           
  8417. /* this is just ASCII text */
  8418.                   key bitstring[8] = 1;
  8419.                   fill byte;
  8420.                   string;                                    case Bin:             /*
  8421. this is binary */
  8422.                   key bitstring[8] = 2;
  8423.                   fill byte;
  8424.                   string;    
  8425.  
  8426.             case ATEOF:             /* this is an AppleTalk EOF */
  8427.                   key bitstring[8] = 3;
  8428.                   fill byte;
  8429.                   string;                        
  8430.             case DataFork:         /* send the text in the data fork */
  8431.                   key bitstring[8] = 4;
  8432.                   fill byte;
  8433.  
  8434.             case EOF:             /* no more */
  8435.                   key bitstring[8] = 5;
  8436.                   fill byte;
  8437.             };
  8438.     };
  8439.  
  8440.     resource 'POST' (501) {
  8441.     ASCII{"0 728 translate "}};
  8442.  
  8443.     resource 'POST' (502) {
  8444.     ASCII{"1 -1 scale "}};
  8445.  
  8446.     resource 'POST' (503) {
  8447.     ASCII{"newpath "}};
  8448.  
  8449.     resource 'POST' (504) {
  8450.     ASCII{"100 470 moveto "}};
  8451.  
  8452.     resource 'POST' (505) {
  8453.     ASCII{"500 470 lineto "}};
  8454.  
  8455.     resource 'POST' (506) {
  8456.     ASCII{"100 330 moveto "}};
  8457.  
  8458.     resource 'POST' (507) {
  8459.     ASCII{"500 330 lineto "}};
  8460.  
  8461.     resource 'POST' (508) {
  8462.     ASCII{"230 600 moveto "}};
  8463.  
  8464.     resource 'POST' (509) {
  8465.     ASCII{"230 200 lineto "}};
  8466.  
  8467.     resource 'POST' (510) {
  8468.     ASCII{"370 600 moveto "}};
  8469.  
  8470.     resource 'POST' (511) {
  8471.     ASCII{"370 200 lineto "}};
  8472.  
  8473.     resource 'POST' (512) {
  8474.     ASCII{"10 setlinewidth "}};
  8475.  
  8476.     resource 'POST' (513) {
  8477.     ASCII{"stroke "}};
  8478.  
  8479.     resource 'POST' (514) {
  8480.     ASCII{"/Times-Roman findfont 12 scalefont setfont "}};
  8481.  
  8482.     resource 'POST' (515) {
  8483.     ASCII{"230 600 moveto "}};
  8484.  
  8485.     resource 'POST' (516) {
  8486.     ASCII{"(Hello World) show "}};
  8487.  
  8488.   /* It will stop reading and close the file after 517 */
  8489.     resource 'POST' (517) {
  8490.     EOF
  8491.     {}};
  8492.  
  8493.   /* it never gets here */
  8494.     resource 'POST' (518) {
  8495.     DataFork
  8496.     {}};
  8497.  
  8498. When the ResourcePS comment is encountered, the LaserWriter driver sends the text
  8499. contained in the specified resource as PostScript to the printer. The additional data
  8500. is defined as
  8501.  
  8502.     PSRsrc = RECORD
  8503.                PSType : ResType;
  8504.                PSID   : INTEGER;
  8505.                PSIndex: INTEGER;
  8506.              END;
  8507.  
  8508. The resource can be of type STR or STR#. If the Type is STR then the index should be
  8509. 0. Otherwise an index should be given.
  8510.  
  8511. This comment is essentially the same as the PrintF control call to the driver. The
  8512. imbedded command string it uses is '^r^n', which basically tells the driver to send
  8513. the string specified by the additional data, then send a newline. For more information
  8514. about printer control calls see the LaserWriter Reference Manual.
  8515.  
  8516. Here’s an example:
  8517.  
  8518.     PROCEDURE PostRSRC;
  8519.     {This procedure shows how to get PostScript from a resource FILE}
  8520.         CONST
  8521.             PostScriptBegin = 190;
  8522.             PostScriptEnd = 191;
  8523.             ResourcePS = 195;
  8524.  
  8525.         TYPE
  8526.             theRSRChdl = ^theRSRCptr;
  8527.             theRSRCptr = ^theRSRC;
  8528.             theRSRC = RECORD
  8529.                 theType: ResType;
  8530.                 theID: INTEGER;
  8531.                 Index: INTEGER;
  8532.             END;
  8533.  
  8534.         VAR
  8535.             temp    : Rect;
  8536.             TheResource    : theRSRChdl;
  8537.             i,j    : INTEGER;
  8538.             myport    : GrafPtr;
  8539.             err    : INTEGER;
  8540.             atemp    : Boolean;
  8541.  
  8542.         BEGIN     { PostRSRC }
  8543.             TheResource := theRSRChdl(NewHandle(SizeOf(theRSRC)));
  8544.             TheResource^^.theID := 500;
  8545.             TheResource^^.Index := 0;
  8546.             TheResource^^.theType := 'STR ';
  8547.             HLock(Handle(TheResource));
  8548.             MyPic := OpenPicture(theWorld);
  8549.             DrawString('ResourcePS Comment');
  8550.             PicComment(PostScriptBegin,0,nil); {Begin PostScript}
  8551.             PicComment(ResourcePS,8,Handle(TheResource)); {Send postscript}
  8552.             PicComment(PostScriptEnd,0,nil); {PostScriptEnd}
  8553.             ClosePicture;
  8554.             DisposHandle(Handle(TheResource)); {Clean up}
  8555.             PrintthePicture; {print it please}
  8556.             KillPicture(MyPic);
  8557.         END;        { PostRSRC }
  8558.  
  8559. Here’s the resource:
  8560.  
  8561.     resource 'STR ' (500) 
  8562.     {"0 728 translate 1 -1 scale newpath 100 470 moveto 500 470 lineto 100 330    
  8563. moveto 500 330 lineto 230 600 moveto 230 200 lineto 370 600 moveto 370 200     lineto
  8564. 10 setlinewidth stroke /Times-Roman findfont 12 scalefont setfont 230     600 moveto
  8565. (Hello World) show"
  8566.     };
  8567.  
  8568.  
  8569. Rotation
  8570.  
  8571. The concept of rotation doesn’t apply to text alone. PostScript can rotate any object.
  8572. The rotation comments work exactly like text rotation except that all objects drawn
  8573. between the two comments are drawn in the rotated coordinate system specified by the
  8574. center of rotation comment, not just text. Also, no clipping of CopyBits calls occurs.
  8575. These comments only work on the 3.1 and newer LaserWriter drivers.
  8576.  
  8577. The RotateBegin comment tells the driver that the following objects will be drawn in
  8578. a rotated plane. This comment contains the following data structure:
  8579.  
  8580. Rotation = RECORD
  8581.     Flip:    INTEGER;    {0,1,2 => none, horizontal, vertical coordinate flip }
  8582.     Angle:   INTEGER;    {0..360 => clockwise rotation in degrees }
  8583. END; { Rotation }
  8584.  
  8585. When you are finished, the RotateEnd comment returns the coordinate system to normal,
  8586. terminating the rotation.
  8587.  
  8588. The relative center of rotation is specified by the RotateCenter comment in exactly
  8589. the same manner as the TextCenter comments. The difference, however, is that this
  8590. comment must appear before the RotateBegin comment. The data structure of the accompanying
  8591. handle is exactly like that for the TextCenter comment.
  8592.  
  8593. Here’s an example of how to use rotation comments:
  8594.  
  8595. PROCEDURE Test;
  8596. {This procedure shows how to do rotations}
  8597. CONST
  8598.   RotateBegin = 200;
  8599.   RotateEnd = 201;
  8600.   RotateCenter = 202;
  8601.  
  8602. TYPE
  8603.     rothdl = ^rotptr;
  8604.     rotptr = ^trot;
  8605.     trot = RECORD
  8606.       flip : INTEGER;
  8607.       Angle : INTEGER;
  8608.     END; { trot }
  8609.     centhdl = ^centptr;
  8610.     centptr = ^cent;
  8611.     Cent = PACKED RECORD
  8612.              yInt: INTEGER;
  8613.              yFrac: INTEGER;
  8614.              xInt: INTEGER;
  8615.              xFrac: INTEGER;
  8616.     END; { Cent }
  8617.  
  8618. VAR
  8619.   arect    : Rect;
  8620.   rotation : rothdl;
  8621.   center   : centhdl;
  8622.  
  8623. BEGIN { Test }
  8624.   rotation := rothdl(NewHandle(sizeof(trot)));
  8625.   rotation^^.flip := 0;            {no flip}
  8626.   rotation^^.angle := 15;       {15 degree rotation}
  8627.  
  8628.   center := centhdl(NewHandle(sizeof(cent)));
  8629.   center^^.xInt := 50;            {center at 50,50}
  8630.   center^^.yInt := 50;
  8631.   center^^.xFrac := 0;            {no fractional part}
  8632.   center^^.yFrac := 0;
  8633.  
  8634.   myPic := OpenPicture(theWorld);
  8635.     ClipRect(theWorld);
  8636.     MoveTo(20,20);
  8637.     DrawString('Begin Rotation');
  8638.  
  8639.     {set the center of Rotation}
  8640.     PicComment(RotateCenter,GetHandleSize(Handle(center)),Handle(center));    
  8641.     {Begin Rotation}  
  8642.    PicComment(RotateBegin,GetHandleSize(Handle(rotation)),Handle(rotation));
  8643.     SetRect(arect,100,100,500,500);
  8644.     FrameRect(aRect);
  8645.     MoveTo(500,500);
  8646.     Lineto(100,100);
  8647.     PicComment(RotateEnd,0,nil);     {RotateEnd}
  8648.   ClosePicture;
  8649.   DisposHandle(handle(rotation));            {Clean up}
  8650.   DisposHandle(handle(center));
  8651.   PrintThePicture;                               {print it please}
  8652.   KillPicture(MyPic);
  8653. END; { Test }
  8654.  
  8655.  
  8656. Forms
  8657.  
  8658. The two form printing comments allow you to prepare a template to use for printing.
  8659. When the FormsBegin comment is used, the LaserWriter’s buffer is not cleared after
  8660. PrClosePage. This allows you to download a form then change it for each subsequent
  8661. page, inserting the information you want. FormsEnd allows the buffer to be cleared at
  8662. the next PrClosePage.
  8663.  
  8664.  
  8665.  
  8666. æKY 92
  8667. æC #92: The Appearance of Text
  8668.  
  8669. See also:     The Printing Manager
  8670.               The Font Manager
  8671.               Technical Note #91 — 
  8672.               Optimizing for the LaserWriter—Picture Comments
  8673.  
  8674. Written by:     Ginger Jernigan     November 15, 1986
  8675. Updated:                            March 1, 1988
  8676. _______________________________________________________________________________
  8677.  
  8678. This technical note describes why text doesn’t always look the way you expect depending
  8679. on the environment you are in.
  8680. _______________________________________________________________________________
  8681.  
  8682. There are a number of Macintosh text editing applications where layout is critical. 
  8683. Unfortunately, text on a newer machine sometimes prints differently than text on a
  8684. 64K ROM Macintosh. Let’s examine some differences you should expect and why.
  8685.  
  8686. The differences we will consider here are only differences in the layout of text
  8687. lines (line layout), not differences in the appearance of fonts or the differences
  8688. between different printers. Differences in line layout may affect the position of
  8689. line, paragraph and page breaks. The four variables that can affect line layout are
  8690. fonts, the printer driver, the font manager mode, and ROMs.
  8691.  
  8692.  
  8693. Fonts
  8694.  
  8695. Every font on a Macintosh contains its own table of widths which tells QuickDraw how
  8696. wide characters are on the screen. For every style point size there is a separate
  8697. table which may contain widths that vary from face to face and from point size to
  8698. point size. Character widths can vary between point sizes of characters even in the
  8699. same face. In other words, fonts on the screen are not necessarily linearly scalable.
  8700.  
  8701. Non-linearity is not normally a problem since most fonts are designed to be as close
  8702. to linear as possible. A font face in 6 point has very nearly the same scaled widths
  8703. of the same font face in 24 point (plus or minus round-off or truncation differences).
  8704. QuickDraw, however, requires only one face of any particular font to be in the System
  8705. file to use it in any point size. If only a 10 point face actually exists, QuickDraw
  8706. may scale that face to 9, 18, 24 (or whatever point size) by performing a linear
  8707. scale of the 10 point face. 
  8708.  
  8709. This can cause problems. Suppose a document is created on one Macintosh containing a
  8710. font that only exists in that System file in one point size, say 9 point. The document
  8711. is then taken to another Macintosh with a System file containing that same font but
  8712. only in 24 point. The document may, in fact, appear differently on the two screens,
  8713. and when it is printed, will have line breaks (and thus paragraph and page breaks)
  8714. occurring in different places simply because of the differences in character widths
  8715. that exist between the 9 point and 24 point faces.
  8716.  
  8717.  
  8718. The Printer Driver
  8719.  
  8720. Even when the printer you are using has a much higher resolution than what the screen
  8721. can show, printer drivers perform line layout to match the screen layout as closely
  8722. as possible.
  8723.  
  8724. The line layout performed by printer drivers is limited to single lines of text and
  8725. does not change line break positions within multiple lines. The driver supplies metric
  8726. information to the application about the page size and printable area to allow the
  8727. application to determine the best place to make line and page breaks.
  8728.  
  8729. Printer driver line layout does affect word spacing, character spacing and even word
  8730. positioning within a line. This may affect the overall appearance of text, particularly
  8731. when font substitutions are made or various forms of page or text scaling are involved.
  8732. But print drivers NEVER change line, paragraph or page break positions from what the
  8733. application or screen specified. This means that where line breaks appear on the
  8734. screen, they will always appear in the same place on the printer regardless of how
  8735. the line layout may affect the appearance within the line.
  8736.  
  8737.  
  8738. Operating System and ROMs
  8739.  
  8740. In this context, operating system refers to the ROM trap routines which handle fonts
  8741. and QuickDraw. Changes have occurred between the ROMs in the handling of fonts. Fonts
  8742. in the 64K ROMs contain width tables (as described above) which are limited to integer
  8743. values. Several new tables, however, have been added to fonts for the newer ROMs. The
  8744. newer ROMs add an optional global width table containing fractional or fixed point
  8745. decimal values. In addition, there is another optional table containing fractional
  8746. values which can be scaled for the entire range of point sizes for any one face.
  8747. There is also an optional table which provides for the addition (or removal) of width
  8748. to a font when its style is changed to another value such as bold, outline or condensed.
  8749. It is also possible, under the 128K ROMs, to add fonts to the system with inherent
  8750. style properties containing their own width tables that produce different character
  8751. widths from derived style widths.
  8752.  
  8753. One or all of the above tables may or may not be invoked depending on, first, their
  8754. presence, and second, the mode of the operating system. The Font Manager in the newer
  8755. ROMs allows the application to arbitrarily operate in either the fractional mode or
  8756. integer mode (determined, in most cases, by the setting of FractEnable) as it chooses,
  8757. with the default being integer. There is one case where fractional widths will be
  8758. used if they exist even though fractional mode is disabled. When FScaleDisable is
  8759. used fractional widths are always used if they exist regardless of the setting of
  8760. FractEnable.
  8761.  
  8762. Differences in line layout (and thus line breaks) may be affected by any combination
  8763. of the presence or absence of the optional tables, and the operating mode, either
  8764. fractional or integer, of the application. Any of the combinations can produce different
  8765. results from the original ROMs (and from each other).
  8766.  
  8767. The integer mode on the newer ROMs is very similar to, but not exactly the same as,
  8768. the original 64K ROMs. When fonts with the optional tables present are used on Macintoshes
  8769. with 64K ROMs, they continue to work in the old way with the integer widths. However,
  8770. on newer ROMs, even in the integer mode, there may be variations in line width from
  8771. what is seen on the old ROMs. In the plain text style there is very little if any
  8772. difference (except if the global width table is present), but as various type styles
  8773. are selected, line widths may vary more between ROMs.
  8774.  
  8775. Variations in the above options, by far, account for the greatest variation in the
  8776. appearance of lines when a document is transported between one Macintosh and another.
  8777. Line breaks may change position when documents created on one system (say a Macintosh)
  8778. are moved to another system (like a Macintosh Plus). Variations are more pronounced
  8779. as the number and sizes of various type styles increase within a document.
  8780.  
  8781. In all cases, however, a printer driver will produce exactly the same line breaks as
  8782. appear on the screen with any given system combination.
  8783.  
  8784.  
  8785.  
  8786.  
  8787. æKY 93
  8788. æC #93: MPW: {$LOAD}; _DataInit;%_MethTables
  8789.  
  8790. See also:      MPW Reference Manuals
  8791.  
  8792. Written by:    Jim Friedlander    November 15, 1986
  8793. Modified by:   Jim Friedlander    January 12, 1987
  8794. Updated:                          March 1, 1988
  8795. _______________________________________________________________________________
  8796.  
  8797. This technical note discusses the Pascal {$LOAD} directive as well as how to unload
  8798. the _DataInit and %_MethTables segments.
  8799. _______________________________________________________________________________
  8800.  
  8801.  
  8802. {$LOAD}
  8803.  
  8804. MPW Pascal has a {$LOAD} directive that can dramatically speed up compiles.
  8805.  
  8806.     {$LOAD HD:MPW:PLibraries:PasSymDump} 
  8807.  
  8808. will combine symbol tables of all units following this directive (until another {$LOAD}
  8809. directive is encountered), and dump them out to HD:MPW:PLibraries:PasSymDump. In
  8810. order to avoid using fully specified pathnames, you can use {$LOAD} in conjunction
  8811. with the -k option for Pascal:
  8812.  
  8813.     Pascal -k "{PLibraries}" myfile
  8814.  
  8815. combined with the following lines in myfile:
  8816.  
  8817.     USES
  8818.         {$LOAD PasSymDump}
  8819.             MemTypes,QuickDraw, OSIntf, ToolIntf, PackIntf,
  8820.         {$LOAD} {This “turns off” $LOAD for the next unit}
  8821.             NonOptimized,
  8822.         {$LOAD MyLibDump}
  8823.             MyLib;
  8824.  
  8825. will do the following: the first time a program containing these lines is compiled,
  8826. two symbol table dump files (in this case PasSymDump and MyLibDump) will be created
  8827. in the directory specified by the -k option (in this case {PLibraries}). No dump file
  8828. will be generated for the unit NonOptimized. The compiler will compile MemTypes,
  8829. QuickDraw, OSIntf, ToolIntf, PackIntf (quite time consuming) and dump those units’
  8830. symbols to PasSymDump and it will compile the interface to MyLib and dump its symbols
  8831. to MyLib. For subsequent compiles of this program (or any program that uses the same
  8832. dump file(s)), the interface files won’t be recompiled, the compiler will simply read
  8833. in the symbol table.
  8834.  
  8835. Compiling a sample five line program on a Macintosh Plus/HD20SC takes 62 seconds
  8836. without using the {$LOAD} directive. The same program takes 10 seconds to compile
  8837. using the {$LOAD} directive (once the dump file exists). For further details about
  8838. this topic, please see the MPW Pascal Reference Manual .
  8839.  
  8840. Note: If any of the units that are dumped into a dump file change, you need to make
  8841. sure that the dump file is deleted, so that it can be regenerated by the Pascal compiler
  8842. with the correct information. The best way to do this is to use a makefile to check
  8843. the dump file against the files it depends on, and delete the dump file if it is out
  8844. of date with respect to any of the units that it contains. An excellent (and well
  8845. commented) example of doing this is in the MPW Workshop Manual.
  8846.  
  8847.  
  8848. The _DataInit Segment
  8849.  
  8850. The Linker will generate a segment whose resource name is %A5Init for any program
  8851. compiled by the C or Pascal compilers. This segment is called by a program’s main
  8852. segment. This segment is loaded into the application heap and locked in place. It is
  8853. up to your program to unload this segment (otherwise, it will remain locked in memory,
  8854. possibly causing heap fragmentation). To do this from Pascal, use the following lines:
  8855.  
  8856.     PROCEDURE _DataInit;EXTERNAL;
  8857.     ...
  8858.  
  8859.     BEGIN     {main PROGRAM}
  8860.     UnloadSeg(@_DataInit);
  8861.     {remove data initialization code before any allocations}
  8862.     ...
  8863.  
  8864. From C, use the following lines:
  8865.  
  8866.     extern _DataInit();
  8867.     ...
  8868.     { /* main */
  8869.         UnloadSeg(_DataInit);
  8870.         /*remove data initialization code before any allocations*/
  8871.     ...
  8872.  
  8873. For further details about Data Initialization, see the MPW Reference Manual.
  8874.  
  8875.  
  8876. %_MethTables and %_SelProcs
  8877.  
  8878. Object use in Pascal produces two segments which can cause heap problems. These are
  8879. %_MethTables and %_SelProcs which are used when method calls are made. MacApp deals
  8880. with them correctly, so this only applies to Object Pascal programs that don’t use
  8881. MacApp. You can make the segments locked and preloaded 
  8882. (probably the easiest route), so they will be loaded low in the heap, or you can
  8883. unload them temporarily while you are doing heap initialization. In the latter case,
  8884. make sure there are no method calls while they are unloaded. To reload %_MethTables
  8885. and %_SelProcs, call the dummy procedure %_InitObj. 
  8886. %_InitObj loads %MethTables —calling any method will then load %_SelProcs. 
  8887.  
  8888. Reminder: The linker is case sensitive when dealing with module names. Pascal converts
  8889. all module names to upper-case (unless a routine is declared to be a C routine). The
  8890. Assembler default is the same as the Pascal default, though it can be changed with
  8891. the CASE directive. C preserves the case of module names 
  8892. (unless a routine is declared to be pascal, in which case the module name is converted
  8893. to upper- case letters). 
  8894.  
  8895. Make sure that any external routines that you reference are capitalized the same in
  8896. both the external routine and the external declaration (especially in 
  8897. C). If the capitalization differs, you will get the following link error 
  8898. (library routine = findme, program declaration = extern FindMe();): 
  8899.  
  8900.     ### Link: Error  Undefined entry, name: FindMe
  8901.  
  8902.  
  8903.  
  8904.  
  8905. æKY 94
  8906. æC #94: Tags
  8907.  
  8908. See also:      The File Manager
  8909.  
  8910. Written by:    Bryan Stearns     November 15, 1986
  8911. Updated:                         March 1, 1988
  8912. _______________________________________________________________________________
  8913.  
  8914. Apple has decided to eliminate support for file-system tags on its future products;
  8915. this technical note explains this decision.
  8916. _______________________________________________________________________________
  8917.  
  8918. Some of Apple’s disk products (and some third-party products) have the ability to
  8919. store 532 bytes per sector, instead of the normal 512. Twelve of the extra bytes are
  8920. used to store redundant file system information, known as “tags”, to be used by a
  8921. scavenging utility to reconstruct damaged disks.
  8922.  
  8923. Apple has decided to eliminate support for these tags on its products; this was decided
  8924. for several reasons:
  8925.  
  8926. 1) Tags were implemented back when we had to deal with “Twiggy” drives on Lisa. 
  8927.    These drives were less reliable than current drives, and it was expected 
  8928.    that tags would be needed for data integrity.
  8929.  
  8930. 2) We’re working on a scavenging utility (Disk First Aid), and we’ve found that 
  8931.    tags don’t help us in reconstructing damaged disks (ie, if we can’t fix it 
  8932.    without using tags, tags wouldn’t help us fix it). So, at least the first 
  8933.    two versions of our scavenging utility will not use tags, and a third 
  8934.    version (which we’ve planned for, but will probably never implement) can 
  8935.    probably work without them. 
  8936.  
  8937. 3) 532-byte-per-sector drives and controllers tend to cost more, even at 
  8938.    Apple’s volumes. Thus, the demise of tags saves us (and our customers) money.
  8939.  
  8940.    The Apple Hard Disk 20SC currently supports tags; this may not always be the 
  8941.    case, however; we’ll probably drop the large sectors when we run out of our 
  8942.    current stock of drives.
  8943.  
  8944. The Hierarchical File System (HFS) documentation didn’t talk about tags because the
  8945. writer had no information available about how they worked under HFS. Because of this
  8946. decision, it is unlikely that we’ll ever have documentation on how to correctly implement
  8947. them under HFS.
  8948.  
  8949.  
  8950.  
  8951.  
  8952. æKY 95
  8953. æC #95: How To Add Items to the Print Dialogs
  8954.  
  8955. See also:      The Printing Manager
  8956.                The Dialog Manager
  8957.     
  8958. Written by:    Ginger Jernigan    November 15, 1986
  8959.                Lew Rollins
  8960. Updated:                          March 1, 1988
  8961. _______________________________________________________________________________
  8962.  
  8963. This technical note discusses how to add your own items to the Printing 
  8964. Manager’s dialogs.
  8965. _______________________________________________________________________________
  8966.  
  8967. When the Printing Manager was initially designed, great care was taken to make the
  8968. interface to the printer drivers as generic as possible in order to allow applications
  8969. to print without being device-specific. There are times, however, when this type of
  8970. non-specific interface interferes with the flexibility of an application. An application
  8971. may require additional information before printing which is not part of the general
  8972. Printing Manager interface. This technical note describes a method that an application
  8973. can use to add its own items to the existing style and job dialogs.
  8974.  
  8975. Before continuing, you need to be aware of some guidelines that will increase your
  8976. chances of being compatible with the printing architecture in the future:
  8977.  
  8978.  • Only add items to the dialogs as described in this technical note. Any other 
  8979.    methods will decrease your chances of survival in the future.
  8980.  
  8981.  • Do not change the position of any item in the current dialogs. This means
  8982.    don’t delete items from the existing item list or add items in the middle. 
  8983.    Add items only at the end of the list.
  8984.  
  8985.  • Don’t count on an item retaining its current position in the list. If you 
  8986.    depend on the Draft button being a particular number in the ImageWriter’s 
  8987.    style dialog item list, and we change the Draft button’s item number for 
  8988.    some reason, your program may no longer function correctly.
  8989.  
  8990.  • Don’t use more than half the screen height for your items. Apple reserves 
  8991.    the right to expand the items in the standard print dialogs to fill the top 
  8992.    half of the screen.
  8993.  
  8994.  • If you are adding lots of items to the dialogs (which may confuse users), 
  8995.    you should consider having your own separate dialog in addition to the 
  8996.    existing Printing Manager dialogs.
  8997.  
  8998.  
  8999. The Heart
  9000.  
  9001. Before we talk about how the dialogs work, you need to know this: at the heart of the
  9002. printer dialogs is a little-known data structure partially documented in the MacPrint
  9003. interface file. It’s a record called TPrDlg and it looks like this:
  9004.  
  9005.     TPrDlg = RECORD   {Print Dialog: The Dialog Stream object.}
  9006.        dlg       : DialogRecord;   {dialog window}
  9007.        pFltrProc : ProcPtr;        {filter proc.}
  9008.        pItemProc : ProcPtr;        {item evaluating proc.}
  9009.        hPrintUsr : THPrint;        {user’s print record.}
  9010.        fDoIt     : BOOLEAN;
  9011.        fDone     : BOOLEAN;
  9012.        lUser1    : LONGINT;        {four longs reserved by Apple}
  9013.        lUser2    : LONGINT;
  9014.        lUser3    : LONGINT;
  9015.        lUser4    : LONGINT;
  9016.        iNumFst   : INTEGER;        {numeric edit items for std filter}
  9017.        iNumLst   : INTEGER;      
  9018.     {... plus more stuff needed by the particular printing dialog.}
  9019.     END;
  9020.     TPPrDlg = ^TPrDlg;             {== a dialog ptr}
  9021.  
  9022. All of the information pertaining to a print dialog is kept in the TPrDlg record.
  9023. This record will be referred to frequently in the discussion below.
  9024.  
  9025.  
  9026. How the Dialogs Work
  9027.  
  9028. When your application calls PrStlDialog and PrJobDialog, the printer driver actually
  9029. calls a routine called PrDlgMain. This function is declared as follows:
  9030.  
  9031.     FUNCTION PrDlgMain (hprint: THPrint; pDlgInit: ProcPtr): BOOLEAN;
  9032.  
  9033. PrDlgMain first calls the pDlgInit routine to set up the appropriate dialog (in Dlg),
  9034. dialog hook (pItemProc) and dialog event filter (pFilterProc) in the TPrDlg record
  9035. (shown above). For the job dialog, the address of PrJobInit is passed to PrDlgMain.
  9036. For the style dialog, the address of PrStlInit is passed. These routines are declared
  9037. as follows:
  9038.  
  9039.     FUNCTION PrJobInit (hPrint: THPrint): TPPrDlg;
  9040.     FUNCTION PrStlInit (hPrint: THPrint): TPPrDlg;
  9041.  
  9042. After the initialization routine sets up the TPrDlg record, PrDlgMain calls ShowWindow
  9043. (the window is initially invisible), then it calls ModalDialog, using the dialog
  9044. event filter pointed to by the pFltrProc field. When an item is hit, the routine
  9045. pointed to by the pItemProc field is called and the items are handled appropriately.
  9046. When the OK button is hit (this includes pressing Return or Enter) the print record
  9047. is validated. The print record is not validated if the Cancel button is hit.
  9048.  
  9049.  
  9050. How to Add Your Own Items
  9051.  
  9052. To modify the print dialogs, you need to change the TPrDlg record before the dialog
  9053. is drawn on the screen. You can add your own items to the item list, replace the
  9054. addresses of the standard dialog hook and event filter with the addresses of your own
  9055. routines and then let the dialog code continue on its merry way. 
  9056.  
  9057. For example, to modify the job dialog, first call PrJobInit. PrJobInit will fill in
  9058. the TPrDlg record for you and return a pointer to that record. Then call PrDlgMain
  9059. directly, passing in the address of your own initialization function. The example
  9060. code’s initialization function adds items to the dialog item list, saves the address
  9061. of the standard dialog hook (in our global variable prPItemProc) and puts the address
  9062. of our dialog hook into the pItemProc field of the TPrDlg record. Please note that
  9063. your dialog hook must call the standard dialog hook to handle all of the standard
  9064. dialog’s items.
  9065.  
  9066. Note: If you wish to have an event filter, handle it the same way that you do a dialog
  9067. hook.
  9068.  
  9069. Now, here is an example (written in MPW Pascal) that modifies the job dialog. The
  9070. same code works for the style dialog if you globally replace ‘Job’ with ‘Stl’. Also
  9071. included is a function (AppendDITL) provided by Lew Rollins (originally written in C,
  9072. translated for this technical note to MPW Pascal) which demonstrates a method of
  9073. adding items to the item list, placing them in an appropriate place, and expanding
  9074. the dialog window’s rectangle.
  9075.  
  9076.  
  9077. The MPW Pascal Example Program
  9078.  
  9079. PROGRAM ModifyDialogs;
  9080.  
  9081.     USES
  9082.         {$LOAD PasDump.dump}
  9083.         MemTypes,QuickDraw,OSIntf,ToolIntf,PackIntf,MacPrint;
  9084.  
  9085.     CONST
  9086.         MyDITL         = 256;
  9087.         MyDFirstBox  = 1;          {Item number of first box in my DITL}
  9088.         MyDSecondBox = 2;
  9089.  
  9090.     VAR
  9091.         PrtJobDialog: TPPrDlg;     { pointer to job dialog }
  9092.         hPrintRec    : THPrint;     { Handle to print record }
  9093.         FirstBoxValue,              { value of our first additional box }
  9094.         SecondBoxValue: Integer; { value of our second addtl. box }
  9095.         prFirstItem,                 { save our first item here }
  9096.         prPItemProc : LongInt;     { we need to store the old itemProc here }
  9097.         itemType     : Integer;     { needed for GetDItem/SetDItem calls }
  9098.         itemH         : Handle;
  9099.         itemBox        : Rect;
  9100.         err            : OSErr;
  9101.         {------------------------------------------------------------------------}
  9102.  
  9103.     PROCEDURE _DataInit;
  9104.         EXTERNAL;
  9105.  
  9106.     {------------------------------------------------------------------------}
  9107.  
  9108.     PROCEDURE CallItemHandler(theDialog: DialogPtr; theItem: Integer; theProc: LongInt);
  9109.         INLINE $205F,$4E90;         {    MOVE.L (A7)+,A0 
  9110.                                                                                    
  9111. JSR (A0)                         }
  9112.  
  9113. { this code pops off theProc and then does a JSR to it, which puts the
  9114.  real return address on the stack. }
  9115.  
  9116.     {------------------------------------------------------------------------}
  9117.  
  9118.     FUNCTION AppendDITL(theDialog: DialogPtr; theDITLID: Integer): Integer;
  9119.     { version 0.1 9/11/86 Lew Rollins of Human-Systems Interface Group}
  9120.     { this routine still needs some error checking }
  9121.  
  9122. { This routine appends all of the items of a specified DITL
  9123. onto the end of a specified DLOG — We don’t even need to know the format
  9124. of the DLOG }
  9125.  
  9126. { this will be done in 3 steps:
  9127.  1. append the items of the specified DITL onto the existing DLOG
  9128.  2. expand the original dialog window as required
  9129.  3. return the adjusted number of the first new user item
  9130. }
  9131.         TYPE
  9132.             DITLItem      = RECORD { First, a single item }
  9133.                                     itmHndl: Handle; { Handle or procedure pointer
  9134. for this item }
  9135.                                     itmRect: Rect; { Display rectangle for this item
  9136. }
  9137.                                     itmType: SignedByte; { Item type for this item —
  9138. 1 byte }
  9139.                                     itmData: ARRAY [0..0] OF SignedByte; { Length
  9140. byte of data }
  9141.                                 END;     {DITLItem}
  9142.  
  9143.             pDITLItem     = ^DITLItem;
  9144.             hDITLItem     = ^pDITLItem;
  9145.  
  9146.             ItemList      = RECORD { Then, the list of items }
  9147.                                     dlgMaxIndex: Integer; { Number of items minus 1
  9148. }
  9149.                                     DITLItems: ARRAY [0..0] OF DITLItem; { Array of
  9150. items }
  9151.                                 END;     {ItemList}
  9152.  
  9153.             pItemList     = ^ItemList;
  9154.             hItemList     = ^pItemList;
  9155.  
  9156.             IntPtr         = ^Integer;
  9157.  
  9158.         VAR
  9159.             offset       : Point;  { Used to offset rectangles of items being appended
  9160. }
  9161.             maxRect      : Rect;     { Used to track increases in window size }
  9162.             hDITL        : hItemList; { Handle to DITL being appended }
  9163.             pItem        : pDITLItem; { Pointer to current item being appended }
  9164.             hItems       : hItemList; { Handle to DLOG’s item list }
  9165.             firstItem    : Integer; { Number of where first item is to be appended
  9166. }
  9167.             newItems,                 { Count of new items }
  9168.             dataSize,                 { Size of data for current item }
  9169.             i                : Integer; { Working index }
  9170.             USB            : RECORD  {we need this because itmData[0] is unsigned}
  9171.                                   CASE Integer OF
  9172.                                       1:
  9173.                                           (SBArray: ARRAY [0..1] OF SignedByte);
  9174.                                       2:
  9175.                                           (Int: Integer);
  9176.                               END;     {USB}
  9177.  
  9178.         BEGIN                          {AppendDITL}
  9179. {
  9180.  Using the original DLOG
  9181.  
  9182.  1. Remember the original window Size.
  9183.  2. Set the offset Point to be the bottom of the original window.
  9184.  3. Subtract 5 pixels from bottom and right, to be added
  9185.     back later after we have possibly expanded window.
  9186.  4. Get working Handle to original item list.
  9187.  5. Calculate our first item number to be returned to caller.
  9188.  6. Get locked Handle to DITL to be appended.
  9189.  7. Calculate count of new items.
  9190. }
  9191.             maxRect := DialogPeek(theDialog)^.window.port.portRect;
  9192.             offset.v := maxRect.bottom;
  9193.             offset.h := 0;
  9194.             maxRect.bottom := maxRect.bottom - 5;
  9195.             maxRect.right := maxRect.right - 5;
  9196.             hItems := hItemList(DialogPeek(theDialog)^.items);
  9197.             firstItem := hItems^^.dlgMaxIndex + 2;
  9198.             hDITL := hItemList(GetResource('DITL',theDITLID));
  9199.             HLock(Handle(hDITL));
  9200.             newItems := hDITL^^.dlgMaxIndex + 1;
  9201. {
  9202.  For each item,
  9203.   1. Offset the rectangle to follow the original window.
  9204.   2. Make the original window larger if necessary.
  9205.   3. fill in item Handle according to type.
  9206. }
  9207.  
  9208.             pItem := @hDITL^^.DITLItems;
  9209.             FOR i := 1 TO newItems DO BEGIN
  9210.                 OffsetRect(pItem^.itmRect,offset.h,offset.v);
  9211.                 UnionRect(pItem^.itmRect,maxRect,maxRect);
  9212.  
  9213.                 USB.Int := 0;         {zero things out}
  9214.                 USB.SBArray[1] := pItem^.itmData[0];
  9215.  
  9216.                 { Strip enable bit since it doesn’t matter here. }
  9217.                 WITH pItem^ DO
  9218.                     CASE BAND(itmType,$7F) OF
  9219.                         userItem:     { Can’t do anything meaningful with user items.
  9220. }
  9221.                             itmHndl := NIL;
  9222.                         ctrlItem + btnCtrl,ctrlItem + chkCtrl,ctrlItem + radCtrl:{build
  9223. Control }
  9224.                             itmHndl := Handle(NewControl(theDialog, { theWindow }
  9225.                                                                   itmRect, { boundsRect
  9226. }
  9227.                                                                   StringPtr(@itmData[0])^,
  9228. { title }
  9229.                                                                   true, { visible
  9230. }
  9231.                                                                   0,0,1, { value,
  9232. min, max }
  9233.                                                                   BAND(itmType,$03),
  9234. { procID }
  9235.                                                                   0)); { refCon }
  9236.                         ctrlItem + resCtrl: BEGIN { Get resource based Control }
  9237.                             itmHndl := Handle(GetNewControl(IntPtr(@itmData[1])^, {
  9238. controlID }
  9239.                                                                       theDialog)); {
  9240. theWindow }
  9241.                             ControlHandle(itmHndl)^^.contrlRect := itmRect; {give it
  9242. the right
  9243.                                                                                      
  9244.        rectangle}
  9245.                             {An actionProc for a Control should be installed here}
  9246.                         END;             {Case ctrlItem + resCtrl}
  9247.                         statText,editText: { Both need Handle to a copy of their
  9248. text. }
  9249.                             err := PtrToHand(@itmData[1], { Start of data }
  9250.                                                   itmHndl, { Address of new Handle
  9251. }
  9252.                                                   USB.Int); { Length of text }
  9253.                         iconItem:     { Icon needs resource Handle. }
  9254.                             pItem^.itmHndl := GetIcon(IntPtr(@itmData[1])^); { ICON
  9255. resID }
  9256.                         picItem:      { Picture needs resource Handle. }
  9257.                             pItem^.itmHndl := Handle(GetPicture(IntPtr(@itmData[1])^));{PICT
  9258. resID}
  9259.                         OTHERWISE
  9260.                             itmHndl := NIL;
  9261.                     END;                 {Case}
  9262.  
  9263.                 dataSize := BAND(USB.Int + 1,$FFFE);
  9264.                 {now advance to next item}
  9265.                 pItem := pDITLItem(Ptr(ord4(@pItem^) + dataSize + sizeof(DITLItem)));
  9266.             END;                         {for}
  9267.             err := PtrAndHand
  9268.              (@hDITL^^.DITLItems,Handle(hItems),GetHandleSize(Handle(hDITL)));
  9269.             hItems^^.dlgMaxIndex := hItems^^.dlgMaxIndex + newItems;
  9270.             HUnlock(Handle(hDITL));
  9271.             ReleaseResource(Handle(hDITL));
  9272.             maxRect.bottom := maxRect.bottom + 5;
  9273.             maxRect.right := maxRect.right + 5;
  9274.             SizeWindow(theDialog,maxRect.right,maxRect.bottom,true);
  9275.             AppendDITL := firstItem;
  9276.         END;                             {AppendDITL}
  9277.  
  9278.     {------------------------------------------------------------------------}
  9279.  
  9280.     PROCEDURE MyJobItems(theDialog: DialogPtr; itemNo: Integer);
  9281.  { 
  9282.  This routine replaces the routine in the pItemProc field in the
  9283.     TPPrDlg record.  The steps it takes are:
  9284.     1. Check to see if the item hit was one of ours. This is done by “localizing”
  9285.           the number, assuming that our items are numbered from 0..n
  9286.     2. If it’s one of ours  then case it and Handle appropriately
  9287.     3. If it isn’t one of ours then call the old item handler
  9288.  }
  9289.  
  9290.         VAR
  9291.             MyItem,firstItem: Integer;
  9292.             thePt           : Point;
  9293.             thePart         : Integer;
  9294.             theValue        : Integer;
  9295.             debugPart       : Integer;
  9296.  
  9297.         BEGIN                          {MyJobItems}
  9298.             firstItem := prFirstItem; { remember, we saved this in myJobDlgInit }
  9299.             MyItem := itemNo - firstItem + 1; { “localize” current item No }
  9300.             IF MyItem > 0 THEN BEGIN { if localized item > 0, it’s one of ours }
  9301.                 { find out which of our items was hit }
  9302.                 GetDItem(theDialog,itemNo,itemType,itemH,itemBox);
  9303.                 CASE MyItem OF
  9304.                     MyDFirstBox: BEGIN
  9305.                         { invert value of FirstBoxValue and redraw it }
  9306.                         FirstBoxValue := 1 - FirstBoxValue;
  9307.                         SetCtlValue(ControlHandle(itemH),FirstBoxValue);
  9308.                     END;                 {case MyDFirstBox}
  9309.                     MyDSecondBox: BEGIN
  9310.                         { invert value of SecondBoxValue and redraw it }
  9311.                         SecondBoxValue := 1 - SecondBoxValue;
  9312.                         SetCtlValue(ControlHandle(itemH),SecondBoxValue);
  9313.                     END;                 {case MyDSecondBox}
  9314.                     OTHERWISE
  9315.                         Debug;         { OH OH — We got an item we didn’t expect
  9316. }
  9317.                 END;                     {Case}
  9318.             END                         { if MyItem > 0 }
  9319.             ELSE                  { chain to standard item handler, whose address is
  9320. saved                                                                         in
  9321. prPItemProc }
  9322.                 CallItemHandler(theDialog,itemNo,prPItemProc);
  9323.         END;                             { MyJobItems }
  9324.  
  9325.     {------------------------------------------------------------------------}
  9326.  
  9327.     FUNCTION MyJobDlgInit(hPrint: THPrint): TPPrDlg;
  9328. {
  9329.  This routine appends items to the standard job dialog and sets up the
  9330.  user fields of the printing dialog record TPRDlg
  9331.  This routine will be called by PrDlgMain
  9332.  This is what it does:
  9333.  1. First call PrJobInit to fill in the TPPrDlg record.
  9334.  2. Append our items onto the old DITL. Set them up appropriately.
  9335.  3. Save the address of the old item handler and replace it with ours.
  9336.  4. Return the Fixed dialog to PrDlgMain.
  9337.  }
  9338.  
  9339.         VAR
  9340.             firstItem    : Integer; { first new item number }
  9341.  
  9342.         BEGIN                          {MyJobDlgInit}
  9343.             firstItem := AppendDITL(DialogPtr(PrtJobDialog),MyDITL); 
  9344.  
  9345.             prFirstItem := firstItem; { save this so MyJobItems can find it }
  9346.  
  9347.             { now we’ll set up our DITL items — The "First Box" }
  9348.             GetDItem(DialogPtr(PrtJobDialog),firstItem,itemType,itemH,itemBox);
  9349.             SetCtlValue(ControlHandle(itemH),FirstBoxValue);
  9350.  
  9351.             { now we’ll set up the second of our DITL items — The "Second Box" }
  9352.             GetDItem(DialogPtr(PrtJobDialog),firstItem + 1,itemType,itemH,itemBox);
  9353.             SetCtlValue(ControlHandle(itemH),SecondBoxValue);
  9354.  
  9355. { Now comes the part where we patch in our item handler.  We have to save
  9356.  the old item handler address, so we can call it if one of the standard
  9357.  items is hit, and put our item handler’s address
  9358.  in pItemProc field of the TPrDlg struct}
  9359.  
  9360.             prPItemProc := LongInt(PrtJobDialog^.pItemProc);
  9361.  
  9362.             { Now we’ll tell the modal item handler where our routine is }
  9363.             PrtJobDialog^.pItemProc := ProcPtr(@MyJobItems);
  9364.  
  9365.             { PrDlgMain expects a pointer to the modified dialog to be returned....
  9366. }
  9367.             MyJobDlgInit := PrtJobDialog;
  9368.  
  9369.         END;                             {myJobDlgInit}
  9370.  
  9371.     {------------------------------------------------------------------------}
  9372.  
  9373.     FUNCTION Print: OSErr;
  9374.  
  9375.         VAR
  9376.             bool            : BOOLEAN;
  9377.  
  9378.         BEGIN                          {Print}
  9379.             hPrintRec := THPrint(NewHandle(sizeof(TPrint)));
  9380.             PrintDefault(hPrintRec);
  9381.             bool := PrValidate(hPrintRec);
  9382.             IF (PrError <> noErr) THEN BEGIN
  9383.                 Print := PrError;
  9384.                 Exit(Print);
  9385.             END;                         {If}
  9386.  
  9387.             { call PrJobInit to get pointer to the invisible job dialog }
  9388.             PrtJobDialog := PrJobInit(hPrintRec);
  9389.             IF (PrError <> noErr) THEN BEGIN
  9390.                 Print := PrError;
  9391.                 Exit(Print);
  9392.             END;                         {If}
  9393.  
  9394.  {Here’s the line that does it all!}
  9395.             IF NOT (PrDlgMain(hPrintRec,@MyJobDlgInit)) THEN BEGIN
  9396.                 Print := cancel;
  9397.                 Exit(Print);
  9398.             END;                         {If}
  9399.  
  9400.             IF PrError <> noErr THEN Print := PrError;
  9401.  
  9402.             { that’s all for now }
  9403.  
  9404.         END;                             { Print }
  9405.  
  9406.     {------------------------------------------------------------------------}
  9407.  
  9408.     BEGIN                              {PROGRAM}
  9409.  
  9410.         UnloadSeg(@_DataInit);     {remove data initialization code before any allocations}
  9411.         InitGraf(@thePort);
  9412.         InitFonts;
  9413.         FlushEvents(everyEvent,0);
  9414.         InitWindows;
  9415.         InitMenus;
  9416.         TEInit;
  9417.         InitDialogs(NIL);
  9418.         InitCursor;
  9419.  
  9420.         { call the routine that does printing }
  9421.         FirstBoxValue := 0;         { value of our first additional box }
  9422.         SecondBoxValue := 0;      { value of our second addtl. box }
  9423.         PrOpen;    { Open the Print Manager }
  9424.         IF PrError = noErr THEN
  9425.             err := Print     { This actually brings up the modified Job dialog }
  9426.         ELSE BEGIN
  9427.             {tell the user that PrOpen failed}
  9428.         END;
  9429.  
  9430.         PrClose;    { Close the Print Manager and leave }
  9431.     END.
  9432.  
  9433.  
  9434. The Lightspeed C Example Program
  9435.  
  9436. /* NOTE: Apple reserves the top half of the screen (where the current DITL items are
  9437. located). Applications may use the bottom half of the screen to add items, but should
  9438. not change any items in the top half of the screen. An application should expand the
  9439. print dialogs only as much as is absolutely necessary. */
  9440.  
  9441. /* Note: A global search and replace of 'Job' with 'Stl' will produce code that modifies
  9442. the style dialogs */
  9443. #include <DialogMgr.h>
  9444. #include <MacTypes.h>
  9445. #include <Quickdraw.h>
  9446. #include <ResourceMgr.h>
  9447. #include <WindowMgr.h>
  9448. #include <pascal.h>
  9449. #include <printmgr.h>
  9450. #define nil 0L
  9451.  
  9452.  
  9453. static TPPrDlg PrtJobDialog;        /* pointer to job dialog */
  9454.  
  9455. /*    This points to the following structure:
  9456.     
  9457.       struct { 
  9458.           DialogRecord   Dlg;          (The Dialog window)
  9459.           ProcPtr        pFltrProc;    (The Filter Proc.)
  9460.           ProcPtr        pItemProc;    (The Item evaluating proc. -- we'll change
  9461. this)
  9462.           THPrint        hPrintUsr;    (The user's print record.)
  9463.           Boolean        fDoIt;    
  9464.           Boolean        fDone;    
  9465.               (Four longs -- reserved by Apple Computer)
  9466.           long           lUser1;         
  9467.           long           lUser2;        
  9468.           long           lUser3;    
  9469.           long           lUser4;        
  9470.    } TPrDlg; *TPPrDlg;          
  9471. */
  9472.  
  9473.  
  9474. /*    Declare ‘pascal’ functions and procedures */
  9475. pascal Boolean PrDlgMain();      /* Print manager’s dialog handler */
  9476. pascal TPPrDlg PrJobInit();      /* Gets standard print job dialog. */
  9477. pascal TPPrDlg MyJobDlgInit();   /* Our extention to PrJobInit */
  9478. pascal void MyJobItems();        /* Our modal item handler */
  9479.  
  9480. #define MyDITL 256   /* resource ID of my DITL to be spliced on to job dialog */
  9481.  
  9482.  
  9483. THPrint hPrintRec;          /* handle to print record */
  9484. short FirstBoxValue = 0;    /* value of our first additional box */
  9485. short SecondBoxValue = 0;   /* value of our second addtl. box */
  9486. long prFirstItem;           /* save our first item here */
  9487. long prPItemProc;           /* we need to store the old itemProc here */
  9488.  
  9489. /*-----------------------------------------------------------------------*/
  9490.     WindowPtr   MyWindow;
  9491.     OSErr       err;
  9492.     Str255      myStr;
  9493. main()
  9494. {    
  9495.     Rect        myWRect;
  9496.     
  9497.     InitGraf(&thePort);
  9498.     InitFonts();
  9499.     InitWindows();
  9500.     InitMenus();
  9501.     InitDialogs(nil);
  9502.     InitCursor();
  9503.        SetRect(&myWRect,50,260,350,340);
  9504.     
  9505.     /* call the routine that does printing */
  9506.     PrOpen();
  9507.     err = Print();
  9508.     
  9509.     PrClose();
  9510. } /* main */
  9511.  
  9512. /*------------------------------------------------------------------------*/
  9513.  
  9514. OSErr Print()
  9515.  
  9516. {
  9517.     /* call PrJobInit to get pointer to the invisible job dialog */
  9518.     hPrintRec = (THPrint)(NewHandle(sizeof(TPrint)));
  9519.     PrintDefault(hPrintRec);
  9520.     PrValidate(hPrintRec);
  9521.     if (PrError() != noErr)
  9522.         return PrError();        
  9523.  
  9524.     PrtJobDialog = PrJobInit(hPrintRec);
  9525.     if (PrError() != noErr)
  9526.         return PrError();        
  9527.  
  9528.     
  9529.     if (!PrDlgMain(hPrintRec, &MyJobDlgInit)) /* this line does all the stuff */
  9530.         return Cancel;
  9531.  
  9532.     if (PrError() != noErr)
  9533.         return PrError();        
  9534.     
  9535. /* that's all for now */
  9536.         
  9537. } /* Print */
  9538.  
  9539. /*------------------------------------------------------------------------*/
  9540.  
  9541. pascal TPPrDlg MyJobDlgInit (hPrint)
  9542. THPrint hPrint;
  9543. /* this routine appends items to the standard job dialog and sets up the user fields
  9544. of the printing dialog record TPRDlg
  9545.     This routine will be called by PrDlgMain */
  9546. {
  9547.     short      firstItem;    /* first new item number */
  9548.     
  9549.     short      itemType;     /* needed for GetDItem/SetDItem call */
  9550.     Handle     itemH;
  9551.     Rect       itemBox;
  9552.     
  9553.     firstItem = AppendDITL (PrtJobDialog, MyDITL); /*call routine to do this */
  9554.     
  9555.     prFirstItem = firstItem; /* save this so MyJobItems can find it */
  9556.     
  9557. /* now we'll set up our DITL items -- The "First Box" */
  9558.     GetDItem(PrtJobDialog,firstItem,&itemType,&itemH,&itemBox);
  9559.     SetCtlValue(itemH,FirstBoxValue);
  9560.     
  9561. /* now we'll set up the second of our DITL items  -- The "Second Box" */
  9562.     GetDItem(PrtJobDialog,firstItem+1,&itemType,&itemH,&itemBox);
  9563.     SetCtlValue(itemH,SecondBoxValue);
  9564.     
  9565. /* Now comes the part where we patch in our item handler.  We have to save the old
  9566. item handler address, so we can call it if one of the standard items is hit, and put
  9567. our item handler's address in pItemProc field of the TPrDlg struct */
  9568.  
  9569.     prPItemProc = (long)PrtJobDialog->pItemProc;
  9570.     
  9571. /* Now we'll tell the modal item handler where our routine is */
  9572.     PrtJobDialog->pItemProc = (ProcPtr)&MyJobItems;
  9573.     
  9574. /* PrDlgMain expects a pointer to the modified dialog to be returned.... */
  9575.     return PrtJobDialog;
  9576.     
  9577. } /*myJobDlgInit*/
  9578.  
  9579.  
  9580. /*-----------------------------------------------------------------------*/
  9581.  
  9582. /* here's the analogue to the SF dialog hook */
  9583.  
  9584. pascal void MyJobItems(theDialog,itemNo)
  9585. TPPrDlg     theDialog;
  9586. short       itemNo;
  9587.  
  9588. { /* MyJobItems */
  9589.     short   myItem;
  9590.     short   firstItem;
  9591.     
  9592.     short   itemType;        /* needed for GetDItem/SetDItem call */
  9593.     Handle  itemH;
  9594.     Rect    itemBox;
  9595.     
  9596.     firstItem = prFirstItem; /* remember, we saved this in myJobDlgInit */
  9597.     myItem = itemNo-firstItem+1;    /* "localize" current item No */
  9598.     if (myItem > 0)     /* if localized item > 0, it's one of ours */
  9599.     {
  9600.         /* find out which of our items was hit */
  9601.         GetDItem(theDialog,itemNo,&itemType,&itemH,&itemBox);
  9602.         switch (myItem)
  9603.         {
  9604.             case 1:
  9605.                 /* invert value of FirstBoxValue and redraw it */
  9606.                 FirstBoxValue ^= 1;
  9607.                 SetCtlValue(itemH,FirstBoxValue);
  9608.                 break;
  9609.  
  9610.             case 2:
  9611.                 /* invert value of SecondBoxValue and redraw it */
  9612.                 SecondBoxValue ^= 1;
  9613.                 SetCtlValue(itemH,SecondBoxValue);
  9614.                 break;
  9615.             default: Debugger(); /* OH OH */    
  9616.         } /* switch */
  9617.     } /* if (myItem > 0) */
  9618.     else /* chain to standard item handler, whose address is saved in 
  9619.               prPItemProc */
  9620.     {
  9621.         CallPascal(theDialog,itemNo,prPItemProc);
  9622.     }
  9623. } /* MyJobItems */
  9624.  
  9625.  
  9626. The Rez Source
  9627.  
  9628. #include "types.r"
  9629.  
  9630. resource 'DITL' (256) {
  9631.     {    /* array DITLarray: 2 elements */
  9632.         /* [1] */
  9633.         {8, 0, 24, 112},
  9634.         CheckBox {
  9635.             enabled,
  9636.             "First Box"
  9637.         };
  9638.         /* [2] */
  9639.         {8, 175, 24, 287},
  9640.         CheckBox {
  9641.             enabled,
  9642.             "Second Box"
  9643.         }
  9644.     }
  9645. };
  9646.  
  9647.  
  9648.  
  9649. æKY 96
  9650. æC #96: SCSI Bugs
  9651.  
  9652. See also:    The SCSI Manager
  9653.              Technical Note #139, Macintosh Plus ROM Versions
  9654.              SCSI Developer’s Package
  9655.  
  9656. Written by:     Steve Flowers    October 1, 1986
  9657. Modified by:    Bryan Stearns    November 15, 1986
  9658. Modified by:    Bo3b Johnson     July 1, 1987
  9659. Updated:                         March 1, 1988
  9660. _______________________________________________________________________________
  9661.  
  9662. There are a number of problems in the SCSI Manager; this note lists the ones we know
  9663. about, along with an explanation of what we’re doing about them. 
  9664. Changes made for the 2/88 release are made to more accurately reflect the state of
  9665. the SCSI Manager. System 4.1 and 4.2 are very similar; one bug was fixed in System
  9666. 4.2.
  9667. _______________________________________________________________________________
  9668.  
  9669. There are several categories of SCSI Manager problems: 
  9670.  
  9671. 1. Those in the ROM boot code (Before the System file has been opened, and 
  9672.    hence, before any patches could possibly fix them.)
  9673. 2. Those that have been fixed in System 3.2
  9674. 3. Those that have been fixed in System 4.1/4.2
  9675. 4. Those that are new in System 4.1/4.2 
  9676. 5. Those that have not yet been fixed. 
  9677.  
  9678. The problems in the ROM boot code can only be fixed by changing the ROMs. Most of the
  9679. bugs in the SCSI Manager itself have been fixed by the patch code in the System 3.2
  9680. file. There are a few problems, though, that are not fixed with System 3.2—most of
  9681. these bugs have been corrected in System 4.1/4.2. Any that are not fixed will be
  9682. detailed here. ROM code for future machines will, of course, include the corrections.
  9683.  
  9684.  
  9685. ROM boot code problems
  9686.  
  9687.  • In the process of looking for a bootable SCSI device, the boot code issues a 
  9688.    SCSI bus reset before each attempt to read block 0 from a device. If the 
  9689.    read fails for any reason, the boot code goes on to the next device. SCSI 
  9690.    devices which implement the Unit Attention condition as defined by the 
  9691.    Revision 17B SCSI standard will fail to boot in this case. The read will 
  9692.    fail because the drive is attempting to report the Unit Attention condition 
  9693.    for the first command it receives after the SCSI bus reset. The boot code 
  9694.    does not read the sense bytes and does not retry the failed command; it 
  9695.    simply resets the SCSI bus and goes on to the next device.
  9696.  
  9697.    If no other device is bootable, the boot code will eventually cycle back to 
  9698.    the same SCSI device ID, reset the bus (causing Unit Attention in the drive 
  9699.    again), and try to read block 0 (which fails for the same reason).
  9700.     
  9701.    The ‘new’ Macintosh Plus ROMs that are included in the platinum Macintosh 
  9702.    Plus have only one change. The change was to simply do a single SCSI Bus 
  9703.    Reset after power up instead of a Reset each time through the SCSI boot loop.
  9704.  
  9705.    This was done to allow Unit Attention drives to be bootable. It was an 
  9706.    object code patch (affecting approximately 30 bytes) and no other bugs were 
  9707.    fixed. For details on the three versions of Macintosh Plus ROMs, 
  9708.    see Technical Note #139. 
  9709.  
  9710.    We recommend that you choose an SCSI controller which does not require the
  9711.    Unit Attention feature—either an older controller (most of the SCSI 
  9712.    controllers currently available were designed before Revision 17B), or one 
  9713.    of the newer Revision-17B-compatible controllers which can enable/disable 
  9714.    Unit Attention as a formatting option (such as those from Seagate, Rodime, 
  9715.    et al). Since the vast majority of Macintosh Plus computers have the ROMs 
  9716.    which cannot use Unit Attention drives, we still recommend that you choose 
  9717.    an SCSI controller that does not require the Unit Attention feature.
  9718.  
  9719.  • If an SCSI device goes into the Status phase after being selected by the 
  9720.    boot code, this leads to the SCSI bus being left in the Status phase 
  9721.    indefinitely, and no SCSI devices can be accessed. The current Macintosh 
  9722.    Plus boot code does not handle this change to Status phase, which means that 
  9723.    the presence of an SCSI device with this behavior (as in some tape 
  9724.    controllers we’ve seen) will prevent any SCSI devices from being accessed by 
  9725.    the SCSI Manager, even if they already had drivers loaded from them. The 
  9726.    result is that any SCSI peripheral that is turned on at boot time must not 
  9727.    go into Status phase immediately after selection; otherwise, the Macintosh 
  9728.    Plus SCSI bus will be left hanging. Unless substantially revised ROMs are 
  9729.    released for the Macintosh Plus (highly unlikely within the next year or so),
  9730.  
  9731.    this problem will never be fixed on the Macintosh Plus, so you should design 
  9732.    for old ROMs.
  9733.  
  9734.  • The Macintosh Plus would try to read 256 bytes of blocks 0 and 1, ignoring 
  9735.    the extra data. The Macintosh SE and Macintosh II try to read 512 bytes from 
  9736.    blocks 0 and 1, ignoring errors if the sector size is larger (but not 
  9737.    smaller) than 512 bytes. Random access devices (disks, tapes, CD ROMS, etc.) 
  9738.    can be booted as long as the blocks are at least 512 bytes, blocks 0, 1 and 
  9739.    other partition blocks are correctly set up, and there is a driver on it. 
  9740.    With the new partition layout (documented in Inside Macintosh volume V), 
  9741.    more than 256 bytes per sector may be required in some partition map entries.
  9742.  
  9743.    This is why we dropped support for 256-byte sectors. Disks with tag bytes (
  9744.    532-byte sectors) or larger block sizes (1K, 2K, etc.) can be booted on any 
  9745.    Macintosh with an SCSI port. Of course, the driver has to take care of data 
  9746.    blocking and de-blocking, since HFS likes to work with 512-byte sectors.
  9747.  
  9748.  
  9749. Problems with ROM SCSI Manager routines
  9750.  
  9751. Note that the following problems are fixed after the System file has been opened; for
  9752. a device to boot properly, it must not depend on these fixes. The sample SCSI driver,
  9753. available from APDA, contains an example of how to find out if the fixes are in place.
  9754.  
  9755.  • Prior to System file 3.2, blind transfers (both reads and writes) would not 
  9756.    work properly with many SCSI controllers. Since blind operation depends on 
  9757.    the drive’s ability to transfer data fast enough, it is the responsibility 
  9758.    of the driver writer to make sure blind operation is safe for a particular 
  9759.    device.
  9760.  
  9761.  • Prior to System file 3.2, the SCSI Manager dropped a byte when the driver 
  9762.    did two or more SCSIReads or SCSIRBlinds in a row. (Each Read or RBlind has 
  9763.    to have a Transfer Information Block (TIB) pointer passed in.) The TIB 
  9764.    itself can be as big and complex as you want—it is the process of returning 
  9765.    from one SCSIRead or SCSIRBlind and entering another one (while still on the 
  9766.    same SCSI command) that causes the first byte for the other SCSIReads to be 
  9767.    lost. 
  9768.  
  9769.    Note that this precludes use of file-system tags. Apple no longer 
  9770.    recommends that you support tags; see Technical Note #94 for more 
  9771.    information.
  9772.  
  9773.  • Prior to System file 3.2, SCSIStat didn’t work; the new version works 
  9774.    correctly.
  9775.  
  9776.  • Running under System file 3.2, the SCSI Manager does not check to make sure 
  9777.    that the last byte of a write operation (to the peripheral) was handshaked 
  9778.    while operating in pseudo-DMA mode. The SCSI Manager writes the final byte 
  9779.    to the NCR 5380’s one-byte buffer and then turns pseudo-DMA mode off shortly 
  9780.    thereafter (reported to be 10-15 microseconds). If the peripheral is 
  9781.    somewhat slow in actually reading the last byte of data, it asserts REQ 
  9782.    after the Macintosh has already turned off pseudo-DMA mode and never gets an 
  9783.    ACK. The CPU then expects to go into the Status phase since it thinks 
  9784.    everything went OK, but the peripheral is still waiting for ACK. Unless the 
  9785.    driver can recover from this somehow, the SCSI bus is ‘hung’ in the Data Out 
  9786.    phase. In this case, all successive SCSI Manager calls will fail until the 
  9787.    bus is reset.
  9788.  
  9789.  • Running under System file 4.1/4.2, the SCSI Manager waits for the last byte 
  9790.    of a write operation to be handshaked while operating in pseudo-DMA mode; it 
  9791.    checks for a final DRQ (or a phase change) at the end of a SCSIWrite or 
  9792.    SCSIWBlind before turning off the pseudo-DMA mode. Drivers that could 
  9793.    recover from this problem by writing the last byte again if the bus was 
  9794.    still in a Data Out phase will still work correctly, as long as they were 
  9795.    checking the bus state. 
  9796.  
  9797.  • Running under System file 3.2, the SCSI Manager does not time out if the 
  9798.    peripheral fails to finish transferring the expected number of bytes for 
  9799.    polled reads and writes. (Blind operation does poll for the first byte of 
  9800.    each requested data transfer in the Transfer Information Block.)
  9801.  
  9802.  • Running under System file 4.1/4.2, SCSIRead and SCSIWrite return an error to 
  9803.    the caller if the peripheral changes the bus phase in the middle of a 
  9804.    transfer, as might happen if the peripheral fails to transfer the expected 
  9805.    number of bytes. The computer is no longer left in a hung state.
  9806.  
  9807.  • Running under System file 3.2, the Selection timeout value is very short 
  9808.    (900 microseconds). Patches to the SCSI Manager in System 4.1/4.2 ensure 
  9809.    that this value is the recommended 250 milliseconds. 
  9810.  
  9811.  • Running under System file 3.2, the SCSI Manager routine SCSIGet (which 
  9812.    arbitrates for the bus) will fail if the BSY line is still asserted. Some 
  9813.    devices are a bit slow in releasing BSY after the completion of an SCSI 
  9814.    operation, meaning that BSY may not have been released before the driver 
  9815.    issues a SCSIGet call to start the next SCSI operation. A work-around for 
  9816.    this is to call SCSIGet again if it failed the first time. (Rarely has it 
  9817.    been necessary to try it a third time.) This assumes, of course, that the 
  9818.    bus has not been left ‘hanging’ by an improperly terminated SCSI operation 
  9819.    before calling SCSIGet.
  9820.     
  9821.  • Running under System file 4.1/4.2, the SCSIGet function has been made more 
  9822.    tolerant of devices that are slow to release the BSY line after a SCSI 
  9823.    operation. The SCSI Manager now waits up to 200 milliseconds before 
  9824.    returning an error. 
  9825.  
  9826.  
  9827. Problems with the SCSI Manager that haven’t been fixed yet
  9828.  
  9829. These problems currently exist in the Macintosh Plus, SE, and II SCSI Manager. We
  9830. plan to fix these problems in a future release of the System Tools disk, but in the
  9831. mean time, you should try to work around the problems (but don’t 
  9832. “require” the problems!). 
  9833.  
  9834.  • Multiple calls to SCSIRead or SCSIRBlind after issuing a command and before 
  9835.    calling SCSIComplete may not work. Suppose you want to read some mode sense 
  9836.    data from the drive. After sending the command with SCSICmd, you might want 
  9837.    to call SCSIRead with a TIB that reads four bytes (typically a header). 
  9838.    After reading the field (in the four-byte header) that tells how many 
  9839.    remaining bytes are available, you might call SCSIRead again with a TIB to 
  9840.    read the remaining bytes. The problem is that the first byte of the second 
  9841.    SCSIRead data will be lost because of the way the SCSI Manager handles reads 
  9842.    in pseudo-DMA mode. The work-around is to issue two separate SCSI commands: 
  9843.    the first to read only the four-byte header, the second to read the four-
  9844.    byte header plus the remaining bytes. We recommend that you not use a clever 
  9845.    TIB that contains two data transfers, the second of which gets the transfer 
  9846.    length from the first transfer’s received data (the header). These two step 
  9847.    TIBs will not work in the future. This bug will probably not be fixed. 
  9848.  
  9849.  • On read operations, some devices may be slow in deasserting REQ after 
  9850.    sending the last byte to the CPU. The current SCSI Manager (all machines) 
  9851.    will return to the caller without waiting for REQ to be deasserted. Usually 
  9852.    the next call that the driver would make is SCSIComplete. On the Macintosh 
  9853.    SE and II, the SCSIComplete call will check the bus to be sure that it is in 
  9854.    Status phase. If not, the SCSI Manager will return a new error code that 
  9855.    indicates the bus was in Data In/Data Out phase when SCSIComplete was called.
  9856.  
  9857.    The combination of the speed of the Macintosh II and a slow peripheral can 
  9858.    cause SCSIComplete to detect that the bus is still in Data In phase before 
  9859.    the peripheral has finally changed the bus to Status phase. This results in 
  9860.    a false error being passed back by SCSIComplete.
  9861.  
  9862.  • The scComp (compare) TIB opcode does not work in System 4.1 on the Macintosh 
  9863.    Plus only. It returns an error code of 4 (bad parameters). This has been 
  9864.    fixed in System 4.2. 
  9865.  
  9866.  
  9867. Other SCSI Manager Issues
  9868.  
  9869.  • At least one third-party SCSI peripheral driver used to issue SCSI commands 
  9870.    from a VBL task. It didn’t check to see if the bus was in the free state 
  9871.    before sending the command! This is guaranteed to wipe out any other SCSI 
  9872.    command that may have been in progress, since the SCSI Manager on the 
  9873.    Macintosh Plus does not mask out (or use) interrupts.
  9874.  
  9875.    We strongly recommend that you avoid calling the SCSI Manager from interrupt 
  9876.    handlers (such as VBL tasks). If you must send SCSI commands from a VBL task 
  9877.    (like for a removable media system), do a SCSIStat call first to see if the 
  9878.    bus is currently busy. If it’s free (BSY is not asserted), then it’s 
  9879.    probably safe; otherwise the VBL task should not send the command. Note that 
  9880.    you can’t call SCSIStat before the System file fixes are in place. Since 
  9881.    SCSI operations during VBL are not guaranteed, you should check all errors 
  9882.    from SCSI Manager calls.
  9883.  
  9884.  • A new SCSI Manager call will be added in the future. This will be a high-
  9885.    level call; it will have some kind of parameter block in which you give a 
  9886.    pointer to a command buffer, a pointer to your TIB, a pointer to a sense 
  9887.    data buffer (in case something goes wrong, the SCSI Manager will 
  9888.    automatically read the sense bytes into the buffer for you), and a few other 
  9889.    fields. The SCSI Manager will take care of arbitration, selection, sending 
  9890.    the command, interpreting the TIB for the data transfer, and getting the 
  9891.    status and message bytes (and the sense bytes, if there was an error). It 
  9892.    should make SCSI device drivers much easier to write, since the driver will 
  9893.    no longer have to worry about unexpected phase changes, getting the sense 
  9894.    bytes, and so on. In the future, this will be the recommended way to use the 
  9895.    SCSI Manager.
  9896.  
  9897.  • The SCSI Manager (all machines) does not currently support interrupt-driven 
  9898.    (asynchronous) operations. The Macintosh Plus can never support it since 
  9899.    there is no interrupt capability, although a polled scheme may be 
  9900.    implemented by the SCSI Manager. The Macintosh SE has a maskable interrupt 
  9901.    for IRQ, and the Macintosh II has maskable interrupts for both IRQ and DRQ. 
  9902.    Apple is working on an implementation of the SCSI Manager that will support 
  9903.    asynchronous operations on the Macintosh II and probably on the SE as well. 
  9904.    Because the interrupt hardware will interact adversely with any asynchronous 
  9905.    schemes that are polled, it is strongly recommended that third parties do 
  9906.    not attempt asynchronous operations until the new SCSI Manager is released. 
  9907.    Apple will not attempt to be compatible with any products that bypass some 
  9908.    or all of the SCSI Manager. In order to implement software-based (polled) 
  9909.    asynchronous operations it is necessary to bypass the SCSI Manager.
  9910.  
  9911.    The SCSI Manager section of the alpha draft of Inside Macintosh volume V 
  9912.    documented the Disconnect and Reselect routines which were intended to be 
  9913.    used for asynchronous I/O. Those routines cannot be used. Those routines 
  9914.    have been removed from the manual. Any software that uses those routines 
  9915.    will have to be revised when the SCSI Manager becomes interrupt-driven. 
  9916.    Drivers which send SCSI commands from VBL tasks may also have to be modified.
  9917.  
  9918.  
  9919. Hardware in the SCSI
  9920.  
  9921. There is some confusion on how many terminators can be used on the bus, and the best
  9922. way to use them. There can be no more than two terminators on the bus. If you have
  9923. more than one SCSI drive you must have two terminators. If you only have one drive,
  9924. you should use a single terminator. If you have more than one drive, the two terminators
  9925. should be on opposite ends of the chain. The idea is to terminate both ends of the
  9926. wire that goes through all of the devices. One terminator should be on the end of the
  9927. system cable that comes out of the Macintosh. The other terminator would be on the
  9928. very end of the last device on the chain. If you have an SE or II with an internal
  9929. hard disk, there is already one terminator on the front of the chain, inside the
  9930. computer.
  9931.  
  9932. On the Macintosh SE and II, there is additional hardware support for the SCSI bus
  9933. transfers in pseudo-DMA mode. The hardware makes it possible to handshake the data in
  9934. Blind mode so that the Blind mode is safe for all transfers. On the Macintosh Plus,
  9935. the Blind transfers are heavily timing dependent and can overrun or underrun during
  9936. the transfer with no error generated. Assuring that Blind mode is safe on the Macintosh
  9937. Plus depends upon the peripheral being used. On the SE and II, the transfer is hardware
  9938. assisted to prevent overruns or underruns.
  9939.  
  9940.  
  9941. Changes in SCSI for SE and II
  9942.  
  9943. The changes made to the SCSI Manager found in the Macintosh SE and Macintosh II are
  9944. primarily bug fixes. No new functionality was added. The newer SCSI Manager is more
  9945. robust and has more error checking. Since the Macintosh Plus SCSI Manager only did
  9946. limited error checking, it is possible to have code that would function (with bugs)
  9947. on the Macintosh Plus, but will not work correctly on the SE or II. The Macintosh
  9948. Plus could mask some bugs in the caller by not checking errors. An example of this is
  9949. sending or receiving the wrong number of bytes in a blind transfer. On the Macintosh
  9950. Plus, no error would be generated since there was no way to be sure how many bytes
  9951. were sent or received. On the SE and II, if the wrong number of bytes are transferred
  9952. an error will be returned to the caller. The exact timing of transfers has changed on
  9953. the SE and II as well, since the computers run at different speeds. Devices that are
  9954. unwittingly dependent upon specific timing in transfers may have problems on the
  9955. newer computers. To find problems of this sort it is usually only necessary to examine
  9956. the error codes that are passed back by the SCSI Manager routines. The error codes
  9957. will generally point out where the updated SCSI Manager found errors. 
  9958.  
  9959.  
  9960. To report other bugs or make suggestions
  9961.  
  9962. Please send additional bug reports and suggestions to us at the address in Technical
  9963. Note #0. Let us know what SCSI controller you’re using in your peripheral, and whether
  9964. you’ve had any particularly good or bad experiences with it. We’ll add to this note
  9965. as more information becomes available.
  9966.  
  9967.  
  9968.  
  9969.  
  9970. æKY 97
  9971. æC #97: PrSetError Problem
  9972.  
  9973. Written by:     Mark Baumwell     November 15, 1986
  9974. Updated:                          March 1, 1988
  9975. _______________________________________________________________________________
  9976.  
  9977. This note formerly described a problem in Lisa Pascal glue for the PrSetError routine.
  9978. The glue in MPW (and most, if not all, third party compilers) does not have this
  9979. problem.
  9980.  
  9981.  
  9982. æKY 98
  9983. æC #98: Short-Circuit Booleans in Lisa Pascal
  9984.  
  9985. Written by:     Mark Baumwell     November 15, 1986
  9986. Updated:                          March 1, 1988
  9987. _______________________________________________________________________________
  9988.  
  9989. This note formerly described problems with the Lisa Pascal compiler. These problems
  9990. have been fixed in the MPW Pascal compiler.
  9991.  
  9992.  
  9993. æKY 99
  9994. æC #99: Standard File Bug in System 3.2
  9995.  
  9996. See also:    The Standard File Package
  9997.  
  9998. Written by:     Jim Friedlander     November 15, 1986
  9999. Updated:                            March 1, 1988
  10000. _______________________________________________________________________________
  10001.  
  10002. This note formerly described a bug in Standard File in System 3.2. This bug has been
  10003. fixed in more recent Systems.
  10004.  
  10005.  
  10006. æKY 100
  10007. æC #100: Compatibility with Large-Screen Displays
  10008.  
  10009. See also:    Technical Note #2 — Macintosh Compatibility Guidelines
  10010.  
  10011. Written by:    Bryan Stearns     November 15, 1986
  10012. Updated:                         March 1, 1988
  10013. _______________________________________________________________________________
  10014.  
  10015. A number of third-party developers have announced large-screen display peripherals
  10016. for Macintosh. One of them, Radius Inc., has issued a set of guidelines for developers
  10017. who wish to remain compatible with their Radius FPD; unfortunately, one of their
  10018. recommendations can cause system crashes. This note suggests a more correct approach.
  10019. _______________________________________________________________________________
  10020.  
  10021. On the first page of the appendix to their guidelines, “How to be FPD Aware,” Radius
  10022. recommends the following:
  10023.  
  10024. “First, to detect the presence of a Radius FPD, you should check address $C00008…”
  10025.  
  10026. Unfortunately, this assumes that you’re running on a Macintosh or Macintosh Plus;
  10027. this test will not work on Macintosh XL, nor on a Macintosh II. Since these displays
  10028. weren’t designed to work with systems other than Macintosh and Macintosh Plus, you
  10029. should make sure you’re running on one of these systems before addressing I/O locations
  10030. (such as those for an add-on display).
  10031.  
  10032. Before testing for the presence of any large-screen display, you should first check
  10033. the machine ID; it’s the byte located at (ROMBASE)+8 (that is, take the long integer
  10034. at the low-memory location ROMBASE [$2AE], and add 8 to get the address of the machine
  10035. ID byte. On a Macintosh or Macintosh Plus, this address will work out to be $400008;
  10036. however, use the low-memory location, to be compatible with future systems that may
  10037. have the ROM at a different address!).
  10038.  
  10039. The machine ID byte will be $00 for all current Macintosh systems. If the value isn’t
  10040. $00, you can assume that no large-screen display is present, but don’t forget to
  10041. follow Technical Note #2’s guidelines for screen size independence!
  10042.  
  10043. NOTE: If you are a developer of an add-on large-screen display, we’d be happy 
  10044.       to review your guidelines for developers in advance of distribution; 
  10045.       please send them to us at the address for comments in Technical Note #0. 
  10046.       Future versions of this note may recommend general guidelines for dealing 
  10047.       with add-on large-screen displays.
  10048.  
  10049.  
  10050. æKY 101
  10051. æC #101: CreateResFile and the Poor Man’s Search Path
  10052.  
  10053. See also:     The File Manager
  10054.               The Resource Manager
  10055.               Technical Note #77 — HFS Ruminations
  10056.  
  10057. Written by:    Jim Friedlander    January 12, 1987
  10058. Updated:                          March 1, 1988
  10059. _______________________________________________________________________________
  10060.  
  10061. CreateResFile checks to see if a resource file with a given name exists, and if it
  10062. does, returns a dupFNErr (–48) error. Unfortunately, to do this check, CreateResFile
  10063. uses a call that follows the Poor Man’s Search Path (PMSP).
  10064. _______________________________________________________________________________
  10065.  
  10066. CreateResFile checks to see if a resource file with a given name exists, and if it
  10067. does, returns a dupFNErr (–48) error. Unfortunately, to do the check, CreateResFile
  10068. calls PBOpenRF, which uses the Poor Man’s Search Path (PMSP). For example, if we have
  10069. a resource file in the System folder named ‘MyFile’ (and no file with that name in
  10070. the current directory) and we call CreateResFile('MyFile'), ResError will return a
  10071. dupFNErr, since PBOpenRF will search the current directory first, then search the
  10072. blessed folder on the same volume. This makes it impossible to use CreateResFile to
  10073. create the resource file ‘MyFile’ in the current directory if a file with the same
  10074. name already exists in a directory that’s in the PMSP.
  10075.  
  10076. To make sure that CreateResFile will create a resource file in the current directory
  10077. whether or not a resource file with the same name already exists further down the
  10078. PMSP, call _Create (PBCreate or Create) before calling CreateResFile:
  10079.  
  10080.     err := Create('MyFile',0,myCreator,myType);
  10081.         {0 for VRefNum means current volume/directory}
  10082.     CreateResFile('MyFile');
  10083.     err := ResError; {check for error}
  10084.  
  10085. In MPW C:
  10086.  
  10087.     err = Create("\pMyFile",0,myCreator,myType);
  10088.     CreateResFile("\pMyFile");
  10089.     err = ResError();
  10090.  
  10091. This works because _Create does not use the PMSP. If we already have ‘MyFile’ in the
  10092. current directory, _Create will fail with a dupFNErr, then, if ‘MyFile’ has an empty
  10093. resource fork, CreateResFile will write a resource map, otherwise, CreateResFile will
  10094. return dupFNErr. If there is no file named ‘MyFile’ in the current directory, _Create
  10095. will create one and then CreateResFile will write the resource map.
  10096. Notice that we are intentionally ignoring the error from _Create, since we are calling
  10097. it only to assure that a file named ‘MyFile’ does exist in the current directory.
  10098.  
  10099. Please note that SFPutFile does not use the PMSP, but that FSDelete does. SFPutFile
  10100. returns the vRefNum/WDRefNum of the volume/folder that the user selected. If your
  10101. program deletes a resource file before creating one with the same name based on information
  10102. returned from SFPutFile, you can use the following strategy to avoid deleting the
  10103. wrong file, that is, a file that is not in the directory specified by the vRefNum/WDRefNum
  10104. returned by SFPutFile, but in some other directory in the PMSP:
  10105.  
  10106.     VAR    
  10107.         wher    : Point;
  10108.         reply   : SFReply;
  10109.         err     : OSErr;
  10110.         oldVol  : Integer;
  10111.  
  10112.     ...
  10113.  
  10114.         wher.h := 80; wher.v := 90;
  10115.         SFPutFile(wher,'','',NIL,reply);
  10116.         IF reply.good THEN BEGIN
  10117.             err := GetVol(NIL,oldVol); {So we can restore it later}
  10118.             err := SetVol(NIL,reply.vRefNum);{for the CreateResFile call}
  10119.  
  10120.         {Now for the Create/CreateResFile calls to create a resource file that
  10121.          we know is in the current directory}
  10122.  
  10123.             err := Create(reply.fName,reply.vRefNum,myCreator,myType);
  10124.             CreateResFile(reply.fName); {we’ll use the ResError from this ...}
  10125.  
  10126.             CASE ResError OF
  10127.                 noErr:{the create succeeded, go ahead and work with the new          
  10128.                resource file -- NOTE: at this point, we don’t know
  10129.                          what’s in the data fork of the file!!} ;
  10130.                 dupFNErr: BEGIN {duplicate file name error}
  10131.                     {the file already existed, so, let’s delete it. We’re now        
  10132.              sure that we’re deleting the file in the current directory}
  10133.  
  10134.                         err:= FSDelete(reply.fName,reply.vRefNum);
  10135.                         
  10136.                     {now that we’ve deleted the file, let’s create the new one,      
  10137.                again, we know this will be in the current directory}
  10138.  
  10139.                         err:= Create(reply.fName,reply.vRefNum,myCreator,myType);
  10140.                         CreateResFile(reply.fName);
  10141.                 END; {CASE dupFNErr}
  10142.                 OTHERWISE       {handle other errors} ;
  10143.             END;                {Case ResError}
  10144.             err := SetVol(NIL,oldVol);{restore the default directory}
  10145.         END;                    {If reply.good}
  10146.     ...
  10147.  
  10148. In MPW C:
  10149.  
  10150.         Point     wher;
  10151.         SFReply   reply;
  10152.         OSErr     err;
  10153.         short     oldVol;
  10154.  
  10155.  
  10156.         wher.h = 80; wher.v = 90;
  10157.         SFPutFile(wher,"","",nil,&reply);
  10158.         if (reply.good )
  10159.         {
  10160.             err = GetVol(nil,&oldVol);        /*So we can restore it later*/
  10161.             err = SetVol(nil,reply.vRefNum);  /*for the CreateResFile call*/
  10162.  
  10163.             /*Now for the Create/CreateResFile calls to create a resource file       
  10164.      that we know is in the current directory*/
  10165.  
  10166.             err = Create(&reply.fName,reply.vRefNum,myCreator,myType);
  10167.             CreateResFile(&reply.fName);/*we’ll use the ResError from this ...*/
  10168.  
  10169.             switch    (ResError()) 
  10170.             {
  10171.                 case noErr:;/*the create succeeded, go ahead and work with the       
  10172.                          new resource file -- NOTE: at this point, we don’t know
  10173. what’s in the data fork of the file!!*/ 
  10174.                      break; /* case noErr*/
  10175.                 case dupFNErr: /*duplicate file name error*/
  10176.                         /*the file already existed, so, let’s delete it. We’re now
  10177. sure that we’re deleting the file in the current directory*/
  10178.  
  10179.                           err= FSDelete(&reply.fName,reply.vRefNum);
  10180.                         
  10181.                             /*now that we’ve deleted the file, let’s create the      
  10182.                       new one, again, we know this will be in the current directory*/
  10183.  
  10184.                             err= Create(&reply.fName,reply.vRefNum,                  
  10185.                       myCreator,myType);
  10186.                             CreateResFile(&reply.fName);
  10187.                     break;    /*case dupFNErr*/
  10188.                 default:;     /*handle other errors*/ 
  10189.             }     /* switch */
  10190.             err = SetVol(nil,oldVol);/*restore the default directory*/
  10191.         }                    /*if reply.good*/
  10192.  
  10193.  
  10194. NOTE: OpenResFile uses the PMSP too, so you may have to adopt similar 
  10195.       strategies to make sure that you are opening the desired resource file 
  10196.       and not some other file further down the PMSP. This is normally not a 
  10197.       problem if you use SFGetFile, since SFGetFile does not use the PMSP, in 
  10198.       fact, SFGetFile does not open or close files, so it doesn’t run into this 
  10199.       problem.
  10200.  
  10201.  
  10202.  
  10203. æKY 102
  10204. æC #102: HFS Elucidations
  10205.  
  10206. See also:     The File Manager
  10207.               Technical Note #77—HFS Ruminations
  10208.  
  10209. Written by:    Bryan “Bo3b” Johnson     January 12, 1987
  10210. Updated:                                March 1, 1988
  10211. _______________________________________________________________________________
  10212.  
  10213. This technical note will describe a few problems that can occur while using HFS. It
  10214. will also describe ways to avoid these problems.
  10215. _______________________________________________________________________________
  10216.  
  10217. This technical note will discuss the following problems:
  10218.  
  10219. 1) It is very important to be careful about how files are opened and closed. 
  10220.    There must be no more than one close for every open. 
  10221.  
  10222. 2) Don’t use Driver names, like .Bout, .Print or .Sony, in place of file names 
  10223.    or the file system will become confused.
  10224.  
  10225. 3) Be aware of the ioFlVersNum byte in all file calls. A number of pieces of 
  10226.    the Macintosh system do not use, and may in fact ignore, files created with 
  10227.    non-zero ioFlVersNums.
  10228.  
  10229. Each of these can lead to strange occurrences, as well as problems for the users.
  10230. Doing any or all of these marginally illegal operations will not necessarily lead to
  10231. a System Error. In some cases the confusion generated may be worse than a System
  10232. Error.
  10233.  
  10234.  
  10235. One Close is always enough
  10236.  
  10237. If a file is closed twice, it is possible to corrupt the file system on a disk. If a
  10238. program has been creating unreadable disks, this may be the cause.
  10239.  
  10240. One aspect of the file system that is not well documented is how it allocates access
  10241. paths to files that are currently open. As a result of this, it is possible to get a
  10242. rather cavalier attitude about opening and closing files. This discussion will explain
  10243. why it is necessary to be very careful about opening and closing files.
  10244.  
  10245. When the File Manager receives an Open call, it will look at the parameters passed in
  10246. the parameter block and create a new access path for the file that is being opened.
  10247. The access path is how the File Manager keeps track of where to send data that is
  10248. written, and where to get data that is read from that file. An access path is nothing
  10249. more than: 1) a buffer that the file system uses to read and write data, and 2) a
  10250. File Control Block that describes how the file is stored on a disk.
  10251.  
  10252. A call like:
  10253.  
  10254.     ErrStuff := FSOpen ('FirstFile', theVRefNum, FirstRefNum);
  10255.  
  10256. will create the access path as a buffer and a File Control Block (FCB) in the FCB
  10257. queue. 
  10258.  
  10259. Note: The following information is here for illustrative purposes only; dependence on
  10260. it may cause compatibility problems with future system software.
  10261.  
  10262. The structure of the queue can be visualized as:
  10263.  
  10264. •••Click on the Illustration button below, and refer to Figure 1.••• 
  10265.  
  10266. where FCBSPtr is a low-memory global (at $34E) that holds the address of a nonrelocatable
  10267. block. That block is the File Control Block buffer, and is composed of the two byte
  10268. header which gives the length of the block, followed by the FCB records themselves.
  10269. The records are of fixed length, and give detailed information about an open file. As
  10270. depicted, any given record can be found by adding the length of the previous FCB
  10271. records to the start of the block, adding 2 for the two byte header; giving an offset
  10272. to the record itself. The size of the block, and hence the number of files that can
  10273. be open at any given time, is determined at startup time. The call to open ‘FirstFile’
  10274. above will pass back the File Reference Number to that file in FirstRefNum. This is
  10275. the number that will be used to access that file from that point on. The File Manager
  10276. passes back an offset into the FCB queue as the RefNum. This offset is the number of
  10277. bytes past the beginning of the queue to that FCB record in the queue. That FCB record
  10278. will describe the file that was opened. An example of a number that might get passed
  10279. back as a RefNum is $1D8. That also means that the FCB record is $1D8 bytes into the
  10280. FCB block.
  10281.  
  10282.  
  10283. A visual example of a record being in use, and how the RefNum is related is:
  10284.  
  10285. •••Click on the Illustration button below, and refer to Figure 2.••• 
  10286.  
  10287. Base is merely the address of the nonrelocatable block that is the FCB buffer. FCBSPtr
  10288. points to it. The RefNum (a number like $1D8) is added to Base, to give an address in
  10289. the block. That address is what the file system will use to read and write to an open
  10290. file, which is why you are required to pass the RefNum to the PBRead and PBWrite
  10291. calls.
  10292.  
  10293. Since that RefNum is merely an offset into the queue, let’s step through a dangerous
  10294. imaginary sequence and see what happens to a given record in the FCB Buffer. Here’s
  10295. the sequence we will step through:
  10296.  
  10297.     ErrStuff := FSOpen ('FirstFile', theVRefNum, FirstRefNum);
  10298.     ErrStuff := FSClose ( FirstRefNum );
  10299.     ErrStuff := FSOpen ('SecondFile', theVRefNum, SecondRefNum);
  10300.     ErrStuff := FSClose ( FirstRefNum ); {the wrong file gets closed!!!}
  10301.     {the above line will close 'SecondFile', not 'FirstFile', which is already     
  10302. closed}
  10303.  
  10304. Before any operations:
  10305. the record at $1D8 is not used.
  10306.  
  10307. •••Click on the Illustration button below, and refer to Figure 3.••• 
  10308.  
  10309.  
  10310. After the call:
  10311.     ErrStuff := FSOpen ('FirstFile', theVRefNum, FirstRefNum);
  10312. FirstRefNum = $1D8 and the record is in use.
  10313.  
  10314. •••Click on the Illustration button below, and refer to Figure 2.••• 
  10315.  
  10316. After the call:
  10317.     ErrStuff := FSClose (FirstRefNum);
  10318. FirstRefNum is still equal to $1D8, but the FCB record is unused.
  10319.  
  10320. •••Click on the Illustration button below, and refer to Figure 3.••• 
  10321.  
  10322. After the call:
  10323.     ErrStuff := FSOpen ('SecondFile', theVRefNum, SecondRefNum);
  10324. SecondRefNum = $1D8, FirstRefNum = $1D8, and the record is reused.
  10325.  
  10326. •••Click on the Illustration button below, and refer to Figure 2.••• 
  10327.  
  10328. After the call:
  10329.     ErrStuff := FSClose (FirstRefNum);
  10330. The FirstRefNum = $1D8, SecondRefNum = $1D8, 
  10331.  
  10332. the queue element is cleared. This happens, even though FirstFile was already closed.
  10333. Actually, SecondFile was closed:
  10334.  
  10335. •••Click on the Illustration button below, and refer to Figure 3.••• 
  10336.  
  10337. Note that the second close is using the old RefNum. The second close will still close
  10338. a file, and in fact will return noErr as its result. Any subsequent accesses to the
  10339. SecondRefNum will return an error, since the file ‘SecondFile’ was closed. The File
  10340. Control Blocks are reused, and since they are just offsets, it is possible to get the
  10341. same file RefNum back for two different files. In this case, FirstRefNum = SecondRefNum
  10342. since ‘FirstFile’ was closed before opening ‘SecondFile’ and the same FCB record was
  10343. reused for ‘SecondFile’.
  10344.  
  10345. There are worse cases than this, however. As an example, think of what can happen if
  10346. a program were to close a file, then the user inserted an HFS disk. The FCB could be
  10347. reused for the Catalog File on that HFS disk. If the program had a generic error
  10348. handler that closed all of its files, it could inadvertently close “its” file again.
  10349. If it thought “its” file was still open it would do the close, which could close the
  10350. Catalog file on the HFS disk. This is catastrophic for the disk since the file could
  10351. easily be closed in an inconsistent state. The result is a bad disk that needs to be
  10352. reformatted. 
  10353.  
  10354. There are any number of nasty cases that can arise if a file is closed twice, reusing
  10355. an old RefNum. A common programming practice is to have an error handler or cleanup
  10356. routine that goes through the files that a program creates and closes them all, even
  10357. if some may already be closed. If an FCB element was not reused, the Close will return
  10358. the expected fnOpnErr. If the FCB had been reused, then the Close could be closing
  10359. the wrong file. This can be very dangerous, particularly for all those paranoid hard
  10360. disk users.
  10361.  
  10362.  
  10363. How to avoid the problem:
  10364.  
  10365. A very simple technique is to merely clear the RefNum after each close. If the variable
  10366. that the program uses is cleared after each close, then there is no way of reusing a
  10367. RefNum in the program. An example of this technique would be:
  10368.  
  10369.     ErrStuff := FSOpen ('FirstFile', theVRefNum, FirstRefNum);
  10370.     ErrStuff := FSClose (FirstRefNum);
  10371.     FirstRefNum := 0; { We just closed it, so clear our refnum }
  10372.     ErrStuff := FSOpen ('SecondFile', theVRefNum, SecondRefNum);
  10373.     ErrStuff := FSClose (FirstRefNum); { returns an error }
  10374.  
  10375. This makes the second Close pass back an error. In this case, the second close will
  10376. try to close RefNum = 0, which will pass back a fnOpnErr and do no damage. Note: Be
  10377. sure to use 0, which will never be a valid RefNum, since the first FCB entry is beyond
  10378. the FCB queue length word. Don’t confuse this with the 0 that the Resource Manager
  10379. uses to represent the System file.
  10380.  
  10381. Thus, if an error handler were cleaning up possibly open files, it could blithely
  10382. close all the files it knew about, since it would legitimately get an error back on
  10383. files that are already closed. This is not done automatically, however. The programmer
  10384. must be careful about the opening and closing of files. The problem can get quite
  10385. complex if an error is received halfway through opening a sequence of ten files, for
  10386. example. By merely clearing the RefNum that is stored after each close, it is possible
  10387. to avoid the complexities of trying to track which files are open and which are closed.
  10388.  
  10389.  
  10390. This .file name looks outrageous.
  10391.  
  10392. There is a potential conflict between file names and driver names. If a file name is
  10393. named something like .Bout, .Print or .Sony, then the file system will open the driver
  10394. instead of the file. Drivers have priority on the 128K ROMs, and will always be opened
  10395. before a file of the same name. This may mean that an application will get an error
  10396. back when opening these types of files, or worse, it will get back a driver RefNum
  10397. from the call. What the application thought was a file open call was actually a driver
  10398. open call. If the program uses that access path as a file RefNum, it is possible to
  10399. get all kinds of strange things to happen. For example, if .Sony is opened, the Sony
  10400. driver’s RefNum would be passed back, instead of a file RefNum. If the application
  10401. does a Write call using that RefNum, it will actually be a driver call, using whatever
  10402. parameters happen to be in the parameter block. Disks may be searching for new life
  10403. after this type of operation. If a program creates files, it should not allow a file
  10404. to be created whose name begins with ‘.’. 
  10405.  
  10406.  
  10407. This file’s not my type.
  10408.  
  10409. This has been discussed in other places, but another aspect of the File Manager that
  10410. can cause confusion is the ioFlVersNum byte that is passed to the low-level File
  10411. Manager calls. This is called ioFileType from Assembly, and should not be confused
  10412. with ioFVersNum. This byte must be set to zero for normal Macintosh files. There are
  10413. a number of parts of the system that will not deal correctly with files that have the
  10414. wrong versions: the Standard File package will not display any file with a non-zero
  10415. ioFlVersNum; the Segment Loader and Resource Manager cannot open files that have
  10416. non-zero ioFlVersNums. It is not sufficient to ignore this byte when a file is created.
  10417. The byte must be cleared in order to avoid this type of problem. Strictly speaking,
  10418. it is not a problem unless a file is being created on an MFS disk. The current system
  10419. will easily allow the user to access 400K disks however, so it is better to be safe
  10420. than confused.
  10421.  
  10422.  
  10423.  
  10424.  
  10425. æKY 103
  10426. æC #103: Using MaxApplZone and MoveHHi from Assembly Language
  10427.  
  10428. See also:     Using Assembly Language
  10429.               The Memory Manager
  10430.               Technical Note #129 — SysEnvirons
  10431.  
  10432. Written by:     Bryan “Bo3b” Johnson     January 12, 1987
  10433. Updated:                                 March 1, 1988
  10434. _______________________________________________________________________________
  10435.  
  10436. When calling MaxApplZone and MoveHHi from assembly language, be sure to get the correct
  10437. code. 
  10438. _______________________________________________________________________________
  10439.  
  10440. MaxApplZone and MoveHHi were marked [Not in ROM] in Inside Macintosh, Volumes I-III .
  10441. They are ROM calls in the 128K ROM. Since they are not in the 64K ROM, if you want
  10442. your program to work on 64K ROM routines it is necessary to call the routines by a
  10443. JSR to a glue (library) routine instead of using the actual trap macro. The glue
  10444. calls the ROM routines if they are available, or executes its copy of them (linked
  10445. into your program) if not.
  10446.  
  10447.  
  10448. How to do it:
  10449.  
  10450. Whenever you need to use these calls, just call the library routine. It will check
  10451. ROM85 to determine which ROMs are running, and do the appropriate thing.
  10452.  
  10453. For MDS, include the Memory.Rel library in your link file and use: 
  10454.  
  10455.     XREF  MoveHHi    ; we need to use this 'ROM' routine
  10456.     ...
  10457.     JSR  MoveHHi     ; jump to the glue routine that will check ROM85 for us
  10458.  
  10459. For MPW link with Interface.o and use:
  10460.  
  10461.     IMPORT  MoveHHi   ; we need to use this
  10462.     ...
  10463.     JSR  MoveHHi      ; jump to the glue routine that will check ROM85 for us
  10464.  
  10465. Avoid calling _MaxApplZone or _MoveHHi directly if you want your software to work on
  10466. the 64K ROMs, since that will assemble to an actual trap, not to a JSR to the library.
  10467.  
  10468. If your program is going to be run only on machines with the 128K ROM or newer, you
  10469. can call the traps directly. Be sure to check for the 64K ROMs, and report an error
  10470. to the user. You can check for old ROMs using the SysEnvirons trap as described in
  10471. Technical Note #129.
  10472.  
  10473.  
  10474.  
  10475.  
  10476. æKY 104
  10477. æC #104: MPW: Accessing Globals From Assembly Language
  10478.  
  10479. See also:      MPW Reference Manual
  10480.  
  10481. Written by:    Jim Friedlander     January 12, 1987
  10482. Updated:                           March 1, 1988
  10483. _______________________________________________________________________________
  10484.  
  10485. This technical note demonstrates how to access MPW Pascal and MPW C globals from the
  10486. MPW Assembler.
  10487. _______________________________________________________________________________
  10488.  
  10489. To allow access of MPW Pascal globals from the MPW Assembler, you need to identify
  10490. the variables that you wish to access as external. To do this, use the {$Z+} compiler
  10491. option. Using the {$Z+} option can substantially increase the size of the object file
  10492. due to the additional symbol information (no additional code is generated and the
  10493. symbol information is stripped by the linker). If you are concerned about object file
  10494. size, you can “bracket” the variables you wish to access as external variables with
  10495. {$Z+} and {$Z-}. 
  10496. Here’s a trivial example:
  10497.  
  10498. Pascal Source
  10499.  
  10500.     PROGRAM MyPascal;
  10501.     USES
  10502.        MemTypes,QuickDraw,OSIntf,ToolIntf;
  10503.     
  10504.     VAR
  10505.        myWRect: Rect;
  10506.     {$Z+} {make the following external}
  10507.        myInt: Integer;
  10508.     {$Z-} {make the following local to this file (not lexically local)}
  10509.        err: Integer;
  10510.  
  10511.     PROCEDURE MyAsm; EXTERNAL; {routine doubles the value of myInt}
  10512.  
  10513.     BEGIN {PROGRAM}
  10514.        myInt:= 5;
  10515.        MyAsm; {call the routine, myInt will be 10 now}
  10516.        writeln('The value of myInt after calling myAsm is ', myInt:1);
  10517.     END. {PROGRAM}
  10518.  
  10519. Assembly Source for Pascal
  10520.  
  10521.             CASE   OFF          ;treat upper and lower case identically
  10522.     MyAsm   PROC   EXPORT       ;CASE OFF is the assembler's default
  10523.             IMPORT myInt:DATA   ;we need :DATA, the assembler assumes CODE
  10524.             ASL.W  #1,myInt     ;multiply by two
  10525.             RTS                 ;all done with this extensive routine, whew!
  10526.             END
  10527.  
  10528. The variable myInt is accessible from assembler. Neither myWRect nor err are accessible.
  10529. If you try to access  myWRect, for example, from assembler, you will get the following
  10530. linker error:
  10531.  
  10532.     ### Link: Error   Undefined entry name:   MYWRECT.
  10533.  
  10534.  
  10535. C Source
  10536.  
  10537. In an MPW C program, one need only make sure that MyAsm is declared as an external
  10538. function, that myInt is a global variable (capitalizations must match) and that the
  10539. CASE ON directive is used in the Assembler: 
  10540.  
  10541.     #include <types.h>
  10542.     #include <quickdraw.h>
  10543.     #include <fonts.h>
  10544.     #include <windows.h>
  10545.     #include <events.h>
  10546.     #include <textedit.h>
  10547.     #include <dialogs.h>
  10548.     #include <stdio.h>
  10549.  
  10550.     extern MyAsm();    /* assembly routine that doubles the value of myInt */
  10551.     short myInt;      /* we’ll change the value of this variable from MyAsm */
  10552.  
  10553.     main()
  10554.     {
  10555.     WindowPtr MyWindow;
  10556.     Rect myWRect;
  10557.  
  10558.     myInt = 5;
  10559.     MyAsm();
  10560.     printf(" The value of myInt after calling myAsm is %d\n",myInt);
  10561.     } /*main*/
  10562.  
  10563. Assembly source for C
  10564.  
  10565.             CASE   ON           ;treat upper and lower case distinct
  10566.     MyAsm   PROC   EXPORT       ;this is how C treats upper and lower case
  10567.             IMPORT myInt:DATA   ;we need :DATA, the assembler assumes CODE
  10568.             ASL.W  #1,myInt     ;multiply by two
  10569.             RTS                 ;all done with this extensive routine, whew!
  10570.             END
  10571.  
  10572.  
  10573.  
  10574.  
  10575. æKY 105
  10576. æC #105: MPW Object Pascal Without MacApp
  10577.  
  10578. See also:      Technical Note #93—{$LOAD};_DataInit;%_MethTables
  10579.  
  10580. Written by:    Rick Blair     January 12, 1987
  10581. Updated:                      March 1, 1988
  10582. _______________________________________________________________________________
  10583.  
  10584. Object Pascal must have a CODE segment named %_MethTables in order to access object
  10585. methods. In MacApp this is taken care of “behind the scenes” so you don’t have to
  10586. worry about it . However, if you are doing a straight Object Pascal program, you must
  10587. make sure that %_MethTables is around when you need it. If it’s unloaded when you
  10588. call a method, your Macintosh will begin executing wild noncode and die a gruesome
  10589. and horrible death.
  10590.  
  10591. The MPW Pascal compiler must see some declaration of an object in order to produce a
  10592. reference to the magic segment. You can achieve this cheaply by simply including
  10593. ObjIntf.p in your Uses declaration. This must be in the main program, by the way. The
  10594. compiler will produce a call to %_InitObj which is in %_MethTables.
  10595.  
  10596. If you’re a more adventurous soul, you can call %_InitObj explicitly from the initialization
  10597. section of your main program (you must use the {$%+} compiler directive to allow the
  10598. use of “%” in identifiers). This will load the %_MethTables segment. See Technical
  10599. Note #93 for ideas about locking down segments that are needed forever without fragmenting
  10600. the heap.
  10601.  
  10602.  
  10603.  
  10604.  
  10605. æKY 106
  10606. æC #106: The Real Story: VCBs and Drive Numbers
  10607.  
  10608. See also:      The File Manager
  10609.                Technical Note #36—Drive Queue Element Format
  10610.  
  10611. Written by:    Rick Blair     January 12, 1987
  10612. Updated:                      March 1, 1988
  10613. _______________________________________________________________________________
  10614.  
  10615. The top of page IV-178 in The File Manager chapter of Inside Macintosh in attempts to
  10616. explain the behavior of two fields in a volume control block when the corresponding
  10617. disk is offline or ejected. Due to the fact that a little bit is left unsaid, this
  10618. paragraph is rather misleading. The two fields in question are vcbDrvNum and vcbDRefNum
  10619. (referred to as ioVDrvInfo and ioVDRefNum in C and Pascal). PBHGetVInfo can be used
  10620. to access these fields.
  10621.  
  10622.  
  10623. Offline
  10624.  
  10625. When a mounted volume is placed offline, vcbDrvNum is cleared and vcbDRefNum is set
  10626. to the two’s complement of the drive number. Since drive numbers are assigned positive
  10627. values (starting with one), this will be a negative number. If vcbDrvNum is zero and
  10628. vcbDRefNum is negative, you know that the volume is offline.
  10629.  
  10630.  
  10631. Ejected
  10632.  
  10633. When a volume is ejected, vcbDrvNum is cleared and vcbDRefNum is set to the positive
  10634. drive number. If vcbDrvNum is zero and vcbDRefNum is positive, you know that the
  10635. volume is ejected. Ejection implies being offline. There is no such thing as “premature
  10636. ejection”.
  10637.  
  10638.  
  10639. Summary
  10640.             online           offline        ejected
  10641. vcbDrvNum     >0 (DrvNum)       0              0
  10642. vcbDRefNum    <0 (DRefNum)     <0 (-DrvNum)   >0 (DrvNum)
  10643.  
  10644. Please refrain from assuming anything about a VCB queue element beyond what is documented
  10645. in Inside Macintosh, and don’t expect it to always be 178 bytes in size. It grew when
  10646. we went from MFS to HFS, and it may grow again. It’s safest to use calls like PBHGetVInfo
  10647. to get the information that you need.
  10648.  
  10649.  
  10650.  
  10651. æKY 107
  10652. æC #107: Nulls in Filenames
  10653.  
  10654. See also:       The File Manager
  10655.  
  10656. Written by:     Rick Blair      March 2, 1987
  10657. Updated:                        March 1, 1988
  10658. _______________________________________________________________________________
  10659.  
  10660. Some applications (loosely speaking so as to include Desk Accessories, INITs, and
  10661. what-have-you) generate or rename special files on the fly so that they are not explicitly
  10662. named by the user via SFPutFile. Since the Macintosh file system is very liberal
  10663. about filenames and only excludes colons from the list of acceptable characters, this
  10664. can lead to some difficulties, both for the end user and for writers of other programs
  10665. which may see these files.
  10666.  
  10667. Other programs which might be backing up your disk or something similar may get confused.
  10668. A program written in C will think it has found the end of a string when it hits a
  10669. null (ASCII code 0) character, so nulls in filenames are especially risky.
  10670.  
  10671. As a rule, filenames should only include characters which the user can see and edit.
  10672. The only reasonable exception might be invisible files, but it can be argued that
  10673. they are of dubious value anyway. You can argue “but what about my help file, I don’t
  10674. want it renamed” but we already have what we think is the best approach for that
  10675. situation. If you can’t find a configuration or other file because the user has renamed
  10676. or moved it, then call SFGetFile and let the user find it. If the user cancels, and
  10677. you can’t run without the file, then quit with an appropriate message.
  10678.  
  10679. Please consider carefully before you put non-displaying characters in filenames!
  10680.  
  10681.  
  10682.  
  10683.  
  10684. æKY 108
  10685. æC #108:   _AddDrive, _DrvrInstall, and _DrvrRemove
  10686.  
  10687. See also:   Technical Note #36, Drive Queue Elements
  10688.             SCSI Development Package (APDA)
  10689.  
  10690. Written by:   Jim Friedlander      March 2, 1987
  10691. Revised by:   Pete Helme           December 1988
  10692. _______________________________________________________________________________
  10693.  
  10694. _AddDrive, _DrvrInstall, and _DrvrRemove are used in the sample SCSI driver in the
  10695. SCSI Development Package, which is available from APDA.  This Technical Note documents
  10696. the parameters for these calls.
  10697.  
  10698. Changes since March 1, 1988:  Updated the _DrvrInstall text to reflect the use of
  10699. register A0, which should contain a pointer to the driver when called.  Also added
  10700. simple glue code for _DrvrInstall and _DrvrRemove since none is available in the MPW
  10701. interfaces.
  10702. _______________________________________________________________________________
  10703.  
  10704.  
  10705. _AddDrive
  10706.  
  10707. _AddDrive adds a drive to the drive queue, and is discussed in more detail in Technical
  10708. Note #36, Drive Queue Elements:
  10709.  
  10710.    FUNCTION AddDrive(DQE:DrvQE1;driveNum,refNum:INTEGER):OSErr;
  10711.  
  10712.       A0 (input)             ->   pointer to DQE
  10713.       D0 high word(input)    ->   drive number
  10714.       D0 low word(input)     ->   driver RefNum
  10715.       D0 (output)           <-    error code
  10716.                                      noErr (always returned)
  10717.  
  10718.  
  10719. _DrvrInstall
  10720.  
  10721. _DrvrInstall is used to install a driver.  A DCE for the driver is created and its
  10722. handle entered into the specified Unit Table position (–1 through –64).  If the unit
  10723. number is –4 through –9, the corresponding ROM-based driver will be replaced:
  10724.  
  10725.    FUNCTION DrvrInstall(drvrHandle:Handle; refNum: INTEGER): OSErr;
  10726.  
  10727.       A0 (input)    ->   pointer to driver
  10728.       D0 (input)    ->   driver RefNum (–1 through –64)
  10729.       D0 (output)  <-    error code
  10730.                              noErr
  10731.                              badUnitErr
  10732.  
  10733.  
  10734. _DrvrRemove
  10735.  
  10736. _DrvrRemove is used to remove a driver.  A RAM-based driver is purged from the system
  10737. heap (using _ReleaseResource).  Memory for the DCE is disposed:
  10738.  
  10739.    FUNCTION DrvrRemove(refNum: INTEGER):OSErr;
  10740.  
  10741.       D0 (input)    ->   Driver RefNum
  10742.       D0 (output)  <-   error code 
  10743.                              noErr
  10744.                              qErr
  10745.  
  10746.  
  10747. Interfaces
  10748.  
  10749. Through a sequence of cataclysmic events, the glue code for _DrvrInstall and _DrvrRemove
  10750. was never actually added to the MPW interfaces (i.e., “We forgot.”), so we will include
  10751. simple glue here at no extra expense to you.
  10752.  
  10753. It would be advisable to first lock the handle to your driver with _HLock before
  10754. making either of these calls since memory may be moved.
  10755.  
  10756.    ;---------------------------------------------------------------
  10757.    ; FUNCTION DRVRInstall(drvrHandle:Handle; refNum:INTEGER):OSErr;
  10758.    ;---------------------------------------------------------------
  10759.    
  10760.    DRVRInstall  PROC   EXPORT
  10761.       MOVEA.L   (SP)+, A1   ; pop return address
  10762.       MOVE.W    (SP)+, D0   ; driver reference number
  10763.       MOVEA.L   (SP)+, A0   ; handle to driver 
  10764.       MOVEA.L   (A0), A0    ; pointer to driver
  10765.       _DrvrInstall          ; $A03D
  10766.       MOVE.W    D0, (SP)    ; get error
  10767.       JMP      (A1)         ; & split
  10768.       ENDPPROC
  10769.  
  10770.  
  10771.    ;---------------------------------------------------------------
  10772.    ; FUNCTION DRVRRemove(refNum:INTEGER):OSErr;
  10773.    ;---------------------------------------------------------------
  10774.    
  10775.    DRVRRemove   PROC   EXPORT
  10776.       MOVEA.L   (SP)+, A1   ; pop return address
  10777.       MOVE.W    (SP)+, D0   ; driver reference number
  10778.       _DrvrRemove           ; $A03E
  10779.       MOVE.W    D0, (SP)    ; get error
  10780.       JMP       (A1)        ; & split
  10781.       ENDPPROC
  10782.  
  10783.  
  10784.  
  10785. æKY 109
  10786. æC #109: Bug in MPW 1.0 Language Libraries
  10787.  
  10788. See also:    MPW Reference Manual
  10789.  
  10790. Written by:    Scott Knaster    March 2, 1987
  10791. Updated:                        March 1, 1988
  10792. _______________________________________________________________________________
  10793.  
  10794. This note formerly described a problem in the language libraries for MPW 1.0. This
  10795. bug is fixed in MPW 1.0.2, available from APDA.
  10796.  
  10797.  
  10798. æKY 110
  10799. æC #110: MPW: Writing Standalone Code
  10800.  
  10801.  
  10802. Revised by:    Keith Rollin                                       February 1990
  10803. Written by:    Jim Friedlander                                       March 1987
  10804.  
  10805. MPW Pascal and C can be used to write stand-alone code such as 'WDEF', 'LDEF',
  10806. 'INIT', and 'FKEY' resources.  This Technical Note, which is not intended to be a complete
  10807. discussion of the issues involved in writing stand-alone code resources, shows how to produce
  10808. such stand-alone code using the MPW Pascal and C compilers and the linker, and includes an
  10809. example of an 'INIT' and a shell for making a 'WDEF'. Changes since March 1988:  Added a note
  10810. about the 32K size limit on stand-alone code resources; included an example of how to load
  10811. and execute stand-alone code from an application; and added references to Technical Note #256,
  10812. Globals in Stand-Alone Code, concerning the use of global variables, and Technical Note
  10813. #240, Using MPW for Non-Macintosh 68000 Systems, concerning breaking the 32K limit.
  10814. _______________________________________________________________________________
  10815.  
  10816.  
  10817. Size Does Matter
  10818.  
  10819. There is a somewhat hard size limit of 32K bytes on code segments, including
  10820. 'CODE' resources in an application and stand-alone code such as 'XCMD', 'FKEY', 'DRVR', and
  10821. 'WDEF' resources.  This limitation exists because Macintosh code has to be relocatable,
  10822. requiring the use of PC-relative instructions.  Unfortunately, the 68000 supports only 16-bit
  10823. signed offsets for the purpose.  These offsets limit code to a maximum jump of 32K bytes
  10824. either forward or backward.  For a procedure at the beginning of a code segment to branch to a
  10825. procedure at the very end, that procedure cannot be more than 32K bytes away.
  10826.  
  10827. This limitation applies to all code segments, including those that comprise an application.
  10828. All those whizzy 790K word processors and spreadsheets are actually composed of many, many
  10829. 'CODE' resources, all of which are smaller than 32K.  However, special support is available
  10830. for applications in the form of a jump table.  This jump table keeps track of the entry points
  10831. within these code segments, so that there is a way to branch from one to another.  Unfortunately,
  10832. you cannot do the same thing for stand-alone code resources, as the system doesn’t support the
  10833. use of more than one jump table.  For more information on the jump table, see Inside Macintosh,
  10834. Volume II, The Segment Loader.
  10835.  
  10836. The reason why this 32K limit is only a “somewhat hard” limit is because, if you are really
  10837. determined, you can break this limit.  If you can write your code in such a way that you don’t ever
  10838. need to make a jump that is longer than 32K bytes, then you should be able to get away with
  10839. stretching the limit.  For more of the gory details, see the section “Segmenting and the Jump Table”
  10840. in Technical Note #240, Using MPW for Non-Macintosh 68000 Systems.
  10841.  
  10842.  
  10843. Calling Stand-Alone Code From An Application
  10844.  
  10845. Assume that you are writing an application and would like to support external routines in the
  10846. form of stand-alone code.  Applications like HyperCard and Apple File Exchange support such a
  10847. mechanism.  How do you go about putting in this functionality?
  10848.  
  10849. The first thing to do is establish some standard means for communicating.  This is shown with
  10850. HyperCard 'XCMD' resources, where a clearly defined parameter block is passed between HyperCard
  10851. and the 'XCMD'.
  10852.  
  10853.        XCmdBlock = RECORD
  10854.                paramCount:  INTEGER;
  10855.                params:      ARRAY [1..16] OF Handle;
  10856.                returnValue: Handle;
  10857.                passFlag:    BOOLEAN;
  10858.                entryPoint:  ProcPtr;            {to call back to HyperCard}
  10859.                request:     INTEGER;
  10860.                result:      INTEGER;
  10861.                inArgs:      ARRAY [1..8] OF LONGINT;
  10862.                outArgs:     ARRAY [1..4] OF LONGINT;
  10863.                END;
  10864.        XCmdPtr = ^XCmdBlock;
  10865.  
  10866. When HyperCard calls an 'XCMD', it passes a pointer to this parameter block.  The entry point
  10867. to such an 'XCMD' could be declared as follows:
  10868.  
  10869.        PROCEDURE XStringWidth(paramPtr: XCmdPtr);
  10870.  
  10871. To call the 'XCMD', you need to load it into memory, lock it down, fill in a parameter block,
  10872. and then call the 'XCMD'.  When you are done, you need to remove the 'XCMD' from memory:
  10873.  
  10874.        h := Get1NamedResource('XCMD', 'XStringWidth');
  10875.        HLock(h);
  10876.        WITH parameterBlock DO BEGIN
  10877.            < fill it in >
  10878.        END;
  10879.        CallXCMD(@parameterBlock, h);
  10880.        HUnlock(h);
  10881.  
  10882. CallXCMD is some in-line code that takes the Handle h and executes the necessary machine
  10883. language commands to jump to it.  It does this by taking the handle off of the stack, turning
  10884. it into a pointer to the stand-alone code, and performing a JSR to it.  In this way, the
  10885. parameter block is left on the stack for the stand-alone code to access:
  10886.  
  10887.        PROCEDURE CallXCMD(pb: XCMDPtr; XCMD: Handle);
  10888.               INLINE $205F,   { MOVE.L (A7)+,A0 }
  10889.                      $2050,   { MOVE.L (A0),A0 }
  10890.                      $4E90;   { JSR (A0) }
  10891.  
  10892.  
  10893. Writing an 'INIT' in Pascal
  10894.  
  10895. An 'INIT' resource is stand-alone code that is executed on startup in the manner
  10896. specified in the System Resource File and Startup Manager chapters of Inside Macintosh.
  10897. 'INIT' resources are commonly written is assembly language, but can also be written in
  10898. high-level languages such as Pascal and C.  Following is the source for a simple, but
  10899. nonetheless highly obnoxious, 'INIT' written in MPW Pascal:
  10900.  
  10901.        UNIT MyInit; {stand-alone code is written as a UNIT}
  10902.  
  10903.        INTERFACE
  10904.  
  10905.        USES
  10906.           MemTypes, QuickDraw, OSIntf, ToolIntf;
  10907.  
  10908.        PROCEDURE BeepTwice;
  10909.  
  10910.        IMPLEMENTATION
  10911.  
  10912.           PROCEDURE BeepTwice;
  10913.  
  10914.           VAR finalTicks: LongInt;
  10915.  
  10916.           BEGIN {BeepTwice}
  10917.              SysBeep(1);
  10918.              Delay(120, finalTicks); {Delay two seconds, this'll annoy 'em!}
  10919.              SysBeep(1);
  10920.           END;  {BeepTwice}
  10921.  
  10922.        END.   {UNIT}
  10923.  
  10924. That’s all there is to the Pascal.  Now you can compile and link the code to produce
  10925. a stand-alone module.  Following are the commands that you use:
  10926.  
  10927.        pascal Init.p 
  10928.  
  10929. Compile the unit to output file Init.p.o.
  10930.  
  10931.        link ∂
  10932.         -rt INIT=0 ∂                # resource type and ID
  10933.         -ra =16                     # INITs must be locked
  10934.         -m BEEPTWICE ∂              # Pascal generates uppercase module names
  10935.         Init.p.o ∂                  # Link this object file first!!! Then ...
  10936.         "{Libraries}"Interface.o ∂  # need this for the glue for Delay()
  10937.         -o MyInit                   # output to this file
  10938.  
  10939. This links the INIT, puts it in the file MyInit and gives the INIT the resource type 
  10940. 'INIT', ID = 0.  You should also set the “locked” bit in the resource attributes of the
  10941. INIT ('INIT' resources must be marked locked because INIT 31 does not lock them).  The main
  10942. entry point is specified by the -m option.  Pascal Units do not have a main entry point,
  10943. and, since you are linking with
  10944. "{Libraries}"Interface.o (you need the glue for _Delay ), you need to tell the linker what
  10945. to strip against.  You could link this without the -m option, but then all the code for 
  10946. "{Libraries}"Interface.o would wind up in the 'INIT', making it much larger than it needs
  10947. to be.  Notice also that you need to capitalize BEEPTWICE, since Pascal converts module
  10948. names to upper case.
  10949.  
  10950. Next you specify the files with which you wish to link.  Since the linker links files in the
  10951. order they are specified, you need to list Init.p.o first, otherwise the first instruction
  10952. for your code is not BeepTwice, but rather the glue for _Delay (which is disastrous).  'INIT'
  10953. resources are entered at the beginning, regardless of where the main entry point is.
  10954.  
  10955. If you have any doubts about what the entry point is (and you can read assembler) you can use
  10956. the command DumpCode MyInit -rt INIT to look at the code.  In this case, the first code that
  10957. is executed should be:
  10958.  
  10959.        LINK A6, #$FFFC        ; make room for the local var 'long'
  10960.  
  10961. If you had incorrectly specified "{Libraries}"Interface.o first, the first code executed would
  10962. have been the following glue for _Delay (and the code for the
  10963. 'INIT' would never have been executed):
  10964.  
  10965.        MOVE.L (A7)+,D0       ; execute ourselves
  10966.        MOVE.L (A7)+,A1       ; address of VAR parameter
  10967.        ...                   ; finish getting ready for Delay
  10968.        _Delay                ; do the Delay
  10969.        MOVE.L D0,(A1)        ; the VAR parameter
  10970.        RTS                   ; return — but to where???
  10971.        LINK A6, #$FFFC       ; the correct code, but it'll never
  10972.         ...                  ; be executed
  10973.  
  10974.        SetFile MyInit -t INIT -c JAF1 && ∂
  10975.            duplicate -y MyInit "{SystemFolder}"
  10976.  
  10977. This command sets the file type of MyInit to “INIT” (so that the INIT 31 mechanism runs it)
  10978. and the creator to “JAF1”. (Yes, JAF1 is registered with Developer Technical Support.  Is 
  10979. your file type?)  If the SetFile succeeds, you then courageously duplicate the INIT into the
  10980. system folder, so it is executed the next time the system is rebooted.
  10981.  
  10982. That’s all there is to it.
  10983.  
  10984. Now for a couple of caveats.  First of all, you cannot easily use globals in stand-alone code.
  10985. If you put the line VAR gLong: Longint; right after the keyword INTERFACE, the code compiles
  10986. and links okay, and probably executes okay.  You get no warning that you are using someone
  10987. else’s global space.  If you use the statement gLong := 4; the long word value four is placed
  10988. at -4(A5), thus destroying whatever was there (generally, the start of the application’s globals).
  10989. This is not really a problem with 'INIT' resources (it definitely is a problem in the 'WDEF'
  10990. example below), but, in general, you should not use globals in stand-alone code.
  10991.  
  10992. Another limitation of stand-alone code is that it cannot use other globals such as QuickDraw
  10993. globals.  For example, if you try to make a QuickDraw call such as SetPort(@thePort); (which
  10994. uses the QuickDraw global variable thePort) you are informed about your transgression:
  10995.  
  10996.         ### link: Error  Undefined entry, name: QUICKDRAW
  10997.          Referenced from: BEEPTWICE in file: Init.p.o
  10998.  
  10999. You can access QuickDraw globals from stand-alone code by using A5 (available from high-level
  11000. languages in the low-memory global CurrentA5 (a long word at $904)) which is a pointer to a
  11001. pointer to thePort (@thePort = (A5)).  Some of the standard Pascal library routines require
  11002. the use of globals, you get similar linker errors if you use these routines.
  11003.  
  11004. If something isn’t working correctly, you might look for inadvertent use of globals.  If your
  11005. use of globals is intentional, then make sure you are using them in accordance with Technical
  11006. Note #256, Globals in Stand-Alone Code.
  11007.  
  11008.  
  11009. Writing an 'INIT' in C
  11010.  
  11011. Following is the source for the same 'INIT' in MPW C:
  11012.  
  11013.        #include <OSUtils.h>
  11014.  
  11015.        void BeepTwice()
  11016.        {
  11017.               long int finalTicks;
  11018.  
  11019.               SysBeep(1);
  11020.               Delay(120,&finalTicks);
  11021.               SysBeep(1);
  11022.        }
  11023.  
  11024. The link instruction for C is:
  11025.  
  11026.        link ∂
  11027.         -rt INIT=0 ∂                  # resource type
  11028.         -ra =16 ∂                     # INITs must be locked
  11029.         -m BeepTwice ∂                # note that C is case sensitive
  11030.         TN110Init.c.o ∂               # link this object file first!!! then ...
  11031.         "{CLibraries}"CInterface.o ∂  # need this for the glue for Delay()
  11032.         -o tn110INIT                  # output to this file
  11033.  
  11034.  
  11035. Writing a 'WDEF' in Pascal
  11036.  
  11037. Writing a 'WDEF' is like writing an 'INIT', except that 'WDEF' resources have standard headers
  11038. that are incorporated into the code.  In this example, the
  11039. 'WDEF' is the Pascal MyWindowDef.  To create the header, you use an assembly language stub:
  11040.  
  11041.        StdWDEF    MAIN EXPORT             ; this will be the entry point
  11042.                   IMPORT MyWindowDef      ; name of Pascal FUNCTION that is
  11043.                                           ; the WDEF we IMPORT externally
  11044.                                           ; referenced routines from Pascal
  11045.                                           ; (in this case, just this one)
  11046.                   BRA.S    @0             ; branch around the header to
  11047.                                           ; the actual code
  11048.                   DC.W     0              ; flags word
  11049.                   DC.B     'WDEF'         ; type
  11050.                   DC.W     3              ; ID number
  11051.                   DC.W     0              ; version
  11052.        @0         JMP      MyWindowDef    ; this calls the Pascal WDEF
  11053.                   END
  11054.  
  11055. Now for the Pascal source for the 'WDEF'.  Only the shell of what needs to be done is listed,
  11056. the actual code is left as an exercise for the reader (for further information about writing
  11057. a 'WDEF', see Inside Macintosh, Volume I, The Window Manager (pp. 297-302).
  11058.  
  11059.        UNIT WDef;
  11060.  
  11061.        INTERFACE
  11062.  
  11063.          USES MemTypes, QuickDraw, OSIntf, ToolIntf;
  11064.  
  11065.        {this is the only external routine}
  11066.          FUNCTION MyWindowDef(varCode: Integer; theWindow: WindowPtr;
  11067.                               message: Integer; param: LongInt): LongInt;
  11068.                              {As defined in IM p. I-299}
  11069.  
  11070.        IMPLEMENTATION
  11071.  
  11072.          FUNCTION MyWindowDef(varCode: Integer; theWindow: WindowPtr;
  11073.                               message: Integer; param: LongInt): LongInt;
  11074.  
  11075.            TYPE
  11076.              RectPtr = ^Rect;
  11077.  
  11078.            VAR
  11079.              aRectPtr : RectPtr;
  11080.  
  11081.          {here are the routines that are dispatched to by MyWindowDef}
  11082.  
  11083.            PROCEDURE DoDraw(theWind: WindowPtr; DrawParam: LongInt);
  11084.              BEGIN {DoDraw}
  11085.                {Fill in the code!}
  11086.              END; {DoDraw}
  11087.  
  11088.            FUNCTION DoHit(theWind: WindowPtr; theParam: LongInt): LongInt;
  11089.              BEGIN {DoHit}
  11090.                {Code for this FUNCTION goes here}
  11091.              END;  {DoHit}
  11092.  
  11093.            PROCEDURE DoCalcRgns(theWind: WindowPtr);
  11094.              BEGIN {DoCalcRgns}
  11095.                {Code for this PROCEDURE goes here}
  11096.              END;  {DoCalcRgns}
  11097.  
  11098.            PROCEDURE DoGrow(theWind: WindowPtr; theGrowRect: Rect);
  11099.              BEGIN {DoGrow}
  11100.                {Code for this PROCEDURE goes here}
  11101.              END;  {DoGrow}
  11102.  
  11103.            PROCEDURE DoDrawSize(theWind: WindowPtr);
  11104.              BEGIN {DoDrawSize}
  11105.                {Code for this PROCEDURE goes here}
  11106.              END;  {DoDrawSize}
  11107.  
  11108. {now for the main body to MyWindowDef}
  11109.          BEGIN  { MyWindowDef }
  11110.          {case out on the message and jump to the appropriate routine}
  11111.            MyWindowDef := 0; {initialize the function result}
  11112.  
  11113.            CASE message OF
  11114.  
  11115.              wDraw:  { draw window frame}
  11116.                DoDraw(theWindow,param);
  11117.  
  11118.              wHit:   { tell what region the mouse was pressed in}
  11119.                MyWindowDef := DoHit(theWindow,param);
  11120.  
  11121.              wCalcRgns: { calculate structRgn and contRgn}
  11122.                DoCalcRgns(theWindow);
  11123.  
  11124.              wNew:   { do any additional initialization}
  11125.                { we don’t need to do any} 
  11126.                ;
  11127.  
  11128.              wDispose:{ do any additional disposal actions}
  11129.                { we don’t need to do any}
  11130.                ;
  11131.  
  11132.              wGrow:  { draw window’s grow image}
  11133.                BEGIN
  11134.                  aRectPtr := RectPtr(param);
  11135.                  DoGrow(theWindow,aRectPtr^);
  11136.                END; {CASE wGrow}
  11137.  
  11138.              wDrawGIcon:{ draw Size box in content region}
  11139.                DoDrawSize(theWindow);
  11140.  
  11141.            END; {CASE}
  11142.          END; {MyWindowDef}
  11143. END. {of UNIT}
  11144.  
  11145. Following are the MPW shell commands necessary to build this 'WDEF':
  11146.  
  11147.        pascal MyWDEF.p
  11148.        asm MyWDEF.a
  11149.        link    -rt WDEF=3 ∂
  11150.                MyWDEF.a.o ∂ # MUST link with this first
  11151.                MyWDEF.p.o ∂
  11152.                "{Libraries}"Interface.o ∂
  11153.                -o MyWDEF3
  11154.  
  11155. Notice that you do not need the -m option; since MyWDEF.a.o contains the main
  11156. entry point, the linker knows what to strip against.
  11157.  
  11158. That’s all there is to it.
  11159.  
  11160.  
  11161. Writing a 'WDEF' in C
  11162.  
  11163. Writing a 'WDEF' in MPW C is very similar to writing one in Pascal.  You can use
  11164. the same assembly language header, and all you need to make sure of is that the
  11165. main dispatch routine (in this case: MyWindowDef) is first in your source file.
  11166. Here’s the same 'WDEF' shell in MPW C:
  11167.  
  11168.        /* first, the mandatory includes */
  11169.        #include <types.h>
  11170.        #include <quickdraw.h>
  11171.        #include <resources.h>
  11172.        #include <fonts.h>
  11173.        #include <windows.h>
  11174.        #include <menus.h>
  11175.        #include <textedit.h>
  11176.        #include <events.h>
  11177.  
  11178.        /* declarations */
  11179.        void DoDrawSize();
  11180.        void DoGrow();
  11181.        void DoCalcRgns();
  11182.        long int DoHit();
  11183.        void DoDraw();
  11184.  
  11185.        /*---------------------- Main Proc within WDEF ----------------------*/
  11186.        pascal long int MyWindowDef(varCode,theWindow,message,param)
  11187.        short int    varCode;
  11188.        WindowPtr    theWindow;
  11189.        short int    message;
  11190.        long int     param;
  11191.  
  11192.        {    /* MyWindowDef */
  11193.  
  11194.          Rect       *aRectPtr;
  11195.          long int   theResult=0;       /*this is what the function returns,
  11196.                                          init to 0 */
  11197.  
  11198.          switch (message) 
  11199.          {
  11200.            case wDraw:                 /* draw window frame*/
  11201.              DoDraw(theWindow,param);
  11202.              break;
  11203.            case wHit:                  /* tell what region the mouse
  11204.                                           was pressed in*/
  11205.              theResult = DoHit(theWindow,param);
  11206.              break;
  11207.            case wCalcRgns:             /* calculate structRgn and contRgn*/
  11208.              DoCalcRgns(theWindow);
  11209.              break;
  11210.            case wNew:                  /* do any additional initialization*/
  11211.              break;                    /* nothing here */
  11212.            case wDispose:              /* do any additional disposal actions*/
  11213.              break;                    /* we don't need to do any*/
  11214.            case wGrow:                 /* draw window's grow image*/
  11215.              aRectPtr = (Rect *)param;
  11216.              DoGrow(theWindow,*aRectPtr);
  11217.              break;
  11218.            case wDrawGIcon:            /* draw Size box in content region*/
  11219.              DoDrawSize(theWindow);
  11220.              break;
  11221.          }  /* switch */
  11222.          return theResult;
  11223.        }    /* MyWindowDef */
  11224.  
  11225.        /* here are the routines that are dispatched to by MyWindowDef
  11226.  
  11227.        /*----------------------- DoDraw function -------------------------*/
  11228.        void DoDraw(WindToDraw,DrawParam)
  11229.        WindowPtr    WindToDraw;
  11230.        long int     DrawParam;
  11231.  
  11232.        {  /* DoDraw */
  11233.              /* code for DoDraw goes here */
  11234.        }  /* DoDraw */
  11235.  
  11236.        /*------------------------ DoHit function -------------------------*/
  11237.        long int DoHit(WindToTest,theParam)
  11238.        WindowPtr    WindToTest;
  11239.        long int     theParam;
  11240.  
  11241.        {  /* DoHit */
  11242.              /* code for DoHit goes here */
  11243.        }  /* DoHit */
  11244.  
  11245.        /*-------------------- DoCalcRgns procedure -----------------------*/
  11246.        void DoCalcRgns(WindToCalc)
  11247.        WindowPtr    WindToCalc;
  11248.  
  11249.        {  /* DoCalcRgns */
  11250.              /* code for DoCalcRgns goes here */
  11251.        }  /* DoCalcRgns */
  11252.  
  11253.        /*---------------------- DoGrow procedure -------------------------*/
  11254.        void DoGrow(WindToGrow,theGrowRect)
  11255.        WindowPtr    WindToGrow;
  11256.        Rect         theGrowRect;
  11257.  
  11258.        {  /* DoGrow */
  11259.              /* code for DoGrow goes here */
  11260.        }  /* DoGrow */
  11261.  
  11262.  
  11263.        /*-------------------- DoDrawSize procedure -----------------------*/
  11264.        void DoDrawSize(WindToDraw)
  11265.        WindowPtr    WindToDraw; 
  11266.  
  11267.        {  /* DoDrawSize */
  11268.              /* code for DoDrawSize goes here */
  11269.        }  /* DoDrawSize */
  11270.  
  11271. To link this 'WDEF', you can use the following link command:
  11272.  
  11273.        Link    -rt WDEF=3 ∂ 
  11274.                tn110.WDEFHeader.a.o ∂  # must link with this first
  11275.                tn110.wdef.c.o ∂
  11276.                "{CLibraries}"CInterface.o ∂
  11277.                -o tn110.wdef
  11278.  
  11279.  
  11280. Further Reference:
  11281. _______________________________________________________________________________
  11282.   •  Inside Macintosh, Volume I, The Window Manager
  11283.   •  Inside Macintosh, Volume II, The Segment Loader
  11284.   •  Inside Macintosh, Volume II, The System Resource File
  11285.   •  Inside Macintosh, Volume V, The Start Manager
  11286.   •  MPW Reference Manual
  11287.   •  Technical Note #240, Using MPW for Non-Macintosh 68000 Systems
  11288.   •  Technical Note #256, Globals in Stand-Alone Code?
  11289.  
  11290.  
  11291.  
  11292. æKY 111
  11293. æC #111: MoveHHi and SetResPurge
  11294.  
  11295. See also:     The Memory Manager
  11296.               The Resource Manager
  11297.  
  11298. Written by:    Jim Friedlander    March 2, 1987
  11299. Updated:                          March 1, 1988
  11300. _______________________________________________________________________________
  11301.  
  11302. SetResPurge(TRUE) is called to make the Memory Manager call the Resource Manager
  11303. before purging a block specified by a handle. If the handle is a handle to a resource,
  11304. and its resChanged bit is set, the resource data will be written out (using WriteResource).
  11305.  
  11306. When MoveHHi is called, even though the handle’s block is not actually being purged,
  11307. the resource data specified by the handle will be written out. An application can
  11308. prevent this by calling SetResPurge(FALSE) before calling MoveHHi (and then calling
  11309. SetResPurge(TRUE) after the MoveHHi call).
  11310.  
  11311.  
  11312. æKY 112
  11313. æC #112: FindDItem
  11314.  
  11315. See also:     The Dialog Manager
  11316.  
  11317. Written by:    Rick Blair    March 2, 1987
  11318. Updated:                     March 1, 1988
  11319. _______________________________________________________________________________
  11320.  
  11321. FindDItem is a potentially useful call which returns the number of a dialog item
  11322. given a point in local coordinates and a dialog handle. It returns an item number of
  11323. –1 if no item’s rectangle overlaps the point. This is all well and good, except you
  11324. don’t get back quite what you would expect.
  11325.  
  11326. The item number returned is zero-based, so you have to add one to the result:
  11327.  
  11328.     theitem := FindDItem(theDialog, thePoint) + 1;
  11329.  
  11330. æKY 113
  11331. æC #113: Boot Blocks
  11332.  
  11333. See also:     The Segment Loader
  11334.     
  11335. Written by:   Bo3b Johnson    March 2, 1987
  11336. Updated:                      March 1, 1988
  11337. _______________________________________________________________________________
  11338.  
  11339. There are two undocumented features of the Boot Blocks. This note will describe how
  11340. they currently work.
  11341.  
  11342. Warning: The format and functionality of the Boot Blocks will change in the future;
  11343. dependence on this information may cause your program to fail on future hardware or
  11344. with future System software.
  11345. _______________________________________________________________________________
  11346.  
  11347. The first two sectors of a bootable Macintosh disk are used to store information on
  11348. how to start up the computer. The blocks contain various parameters that the system
  11349. uses to startup such as the name of the system file, the name of the Finder, the
  11350. first application to run at boot time, the number of events to allow, etc.
  11351.  
  11352. Changing System Heap Size
  11353.  
  11354. The boot blocks dictate what size the system heap will be after booting. Any common
  11355. sector editing program will allow you to change the data in the boot blocks. Changing
  11356. the system heap size is accomplished by changing two parameters in the boot blocks:
  11357. the long word value at location $86 in Block 0 indicates the size of the system heap;
  11358. the word value at location $6 is the version number of the boot blocks. Changing the
  11359. version number to be greater than $14 ($15 is recommended) tells the ROM to use the
  11360. value at $86 for the system heap size, otherwise the value at $86 is ignored. The $86
  11361. location only applies to computers with more than 128K of RAM. 
  11362.  
  11363. Secondary Sound and Video Pages
  11364.  
  11365. Another occasionally useful feature of the boot blocks is the ability to specify that
  11366. the secondary sound and video pages be allocated at boot time. This is done before a
  11367. debugger is loaded, so the debugger will load below the alternate screen. This is
  11368. useful for debugging software that uses the alternate video page, like page-flipping
  11369. demos or games. To allocate the second video and sound buffers, change the two bytes
  11370. starting at location $8 in the boot blocks. Change the value (normally 0) to a negative
  11371. number ($FFFF) to allocate both video and sound buffers. Change the value to a positive
  11372. number ($0001) to allocate only the secondary sound buffer.
  11373.  
  11374. WARNING: MacsBug may not work properly if you allocate additional pages for 
  11375.          sound and video.
  11376.  
  11377.  
  11378.  
  11379.  
  11380. æKY 114
  11381. æC #114: AppleShare and Old Finders
  11382.  
  11383. See also:     AppleShare User’s Guide
  11384.  
  11385. Written by:   Bryan Stearns    March 2, 1987
  11386. Updated:                       March 1, 1988
  11387. _______________________________________________________________________________
  11388.  
  11389. A rumor has been spread that if you use a pre-AppleShare Finder on a workstation to
  11390. access AppleShare volumes, you can bypass AppleShare’s 
  11391. “access privilege” mechanisms.
  11392.  
  11393. This is not true. Access controls are enforced by the server, NOT by the Finder. If
  11394. you use an older Finder, you are still prevented (by the server) from gaining access
  11395. to protected files and folders; however, you will not get the proper user-interface
  11396. feedback that you would if you were using the correct Finder: for instance, folders
  11397. on the server will always appear plain white 
  11398. (that is, without the permission feedback you’d normally get), and error messages
  11399. would not be as explanatory as those from Finders that “know” about AppleShare servers.
  11400.  
  11401.  
  11402. æKY 115
  11403. æC #115: Application Configuration with Stationery Pads
  11404.  
  11405. See also:     The File Manager
  11406.               Technical Note #116 — AppleShare-able Applications
  11407.               Technical Note #47 — Customizing SFGetFile
  11408.               Technical Note #48 — Bundles
  11409.               “Application Development in a Shared Environment”
  11410.  
  11411. Written by:   Bryan Stearns    March 2, 1987
  11412. Updated:                       March 1, 1988
  11413. _______________________________________________________________________________
  11414.  
  11415. With the introduction of AppleShare (Apple’s file server) there are restrictions on
  11416. self-modification of application resource files and the placement of configuration
  11417. files. This note describes one way to get around the necessity for configuration
  11418. files.
  11419. _______________________________________________________________________________
  11420.  
  11421. Configuration Files
  11422.  
  11423. Some applications need to store information about configuration; others could benefit
  11424. simply from allowing users to customize default ruler settings, window placement,
  11425. fonts, etc.
  11426.  
  11427. There are applications which store this information as additional resources in the
  11428. application’s resource file; when the user changes the configuration, the application
  11429. writes to itself to change the saved information.
  11430.  
  11431. AppleShare, however, requires that if an application is to be used by more than one
  11432. user at a time, it must not need write access to itself. This means that the above
  11433. method of storing configuration information cannot be used. (For more information
  11434. about making your application sharable, see Technical Note #116.)
  11435.  
  11436. Storing configuration in a special configuration file can be a problem; the user must
  11437. keep the file in the system folder or the application must search for it. This process
  11438. has design issues of its own.
  11439.  
  11440.  
  11441. An alternative to configuration files: Stationery Pads
  11442.  
  11443. A basis for one solution to this problem was a user-interface feature of the Lisa
  11444. Office System architecture. Lisa introduced the concept of “stationery pads”, special
  11445. documents that created copies of themselves to allow users to save a pre-set-up document
  11446. for future use.  On Lisa, this was the way Untitled documents were created.
  11447.  
  11448. Your Macintosh application can provide the option of saving a document as a stationery
  11449. pad, to provide similar functionality. Here’s how:
  11450.  
  11451.  • You’ll need to add a checkbox to your SFPutFile dialog box (if you don’t 
  11452.    know how to do this, check out Technical Note #47); if the user checks this 
  11453.    box, save the document as you normally would, but use a different file type 
  11454.    (the file type of a document is usually set when the document is created, 
  11455.    using the File Manager Create procedure, or later using SetFileInfo). 
  11456.  
  11457. •••Click on the Illustration button below, and refer to Figure 1.••• 
  11458.  
  11459.  • Be sure to use a different  but similar icon for the stationery pad file.  
  11460.    This is easy if you differentiate between stationery and normal files solely 
  11461.    by file type—the Finder uses the type to determine which icon to display, 
  11462.    see Technical Note #48 for help with the “bundle” mechanism used to 
  11463.    associate a file type with an icon.
  11464.  
  11465.  • When opening a stationery pad file, the window should come up named 
  11466.    “Untitled”, with the contents of the stationery pad file.
  11467.  
  11468.  • “Revert” should re-read the stationery pad file.
  11469.  
  11470.  • Don’t forget to add the stationery pad’s file type to the file-types list 
  11471.    that you pass to Standard File, so that the new files will appear in the 
  11472.    list when the user chooses Open.  This file type should be registered with 
  11473.    Macintosh Developer Technical Support.
  11474.  
  11475.  
  11476.  
  11477. æKY 116
  11478. æC #116: AppleShare-able Applications and the Resource Manager
  11479.  
  11480. See also:     The Resource Manager
  11481.               “Application Development in a Shared Environment”
  11482.               Technical Note #40—Finder Flags
  11483.  
  11484. Written by:   Bryan Stearns    March 2, 1987
  11485. Updated:                       March 1, 1988
  11486. _______________________________________________________________________________
  11487.  
  11488. Normally, applications on an AppleShare server volume cannot be executed by more than
  11489. one user at a time. This technical note explains why, and tells how you can enable
  11490. your application to be shared.
  11491. _______________________________________________________________________________
  11492.  
  11493.  
  11494. The Resource Manager versus Shared Files
  11495.  
  11496. Part of the explanation of why applications are not automatically sharable is based
  11497. on the design of the Resource Manager.  The Resource Manager is a great little database.
  11498. It was originally conceived as a way to keep applications localizable (a task it has
  11499. performed admirably), and was found to be an excellent foundation for the Segment
  11500. Loader, Font Manager, and a large part of the rest of the Macintosh operating system.
  11501.  
  11502. However, it was never designed to be a multi-user database. When the Resource Manager
  11503. opens a resource file (such as an application), it reads the file’s resource map into
  11504. memory. This map remains in memory until the resource file is closed by the Segment
  11505. Loader, which regains control when the application exits. Sometimes it is necessary
  11506. to write the map out to disk; normally, this is only done by UpdateResFile and CloseResFile.
  11507.  
  11508. If two users opened the same resource file at the same time, and one of them had
  11509. write access to the file and added a resource to it, the other user’s Resource Manager
  11510. wouldn’t know about it; this would make the other user’s copy of the file’s original
  11511. resource map invalid. This could cause (at least) a crash; if both users had write
  11512. access, it’s not unlikely that the resource file involved would become corrupted.
  11513. Also, although you can tell the Resource Manager to write out an updated resource
  11514. map, there’s no way for another user to tell it to refresh the copy of the map in
  11515. memory if the file changes.
  11516.  
  11517.  
  11518. What does all this have to do with running my application twice?
  11519.  
  11520. Your application is stored as a resource file; code segments, alert and dialog templates,
  11521. etc., are resources. If you write to your application’s resource file (for instance,
  11522. to add configuration information, like print records), your application can’t be
  11523. shared.
  11524.  
  11525. In Apple’s compatibility testing of existing applications (during development of
  11526. AppleShare), we found quite a few applications, some of them quite popular, that
  11527. wrote to their own resource files. So we decided, to improve the safety of using
  11528. AppleShare, to always launch applications using a combination of access privileges
  11529. such that only one user at a time could use a given application 
  11530. (these privileges will be discussed in a future Technical Note). In fact, AppleShare
  11531. opens all resource files this way, unless the resource file is opened with OpenRFPerm
  11532. and read-only permission is specified.
  11533.  
  11534.  
  11535. But my application doesn’t write to itself!
  11536.  
  11537. We realize that many applications do not. However, there are other considerations
  11538. (covered in detail, with suggestions for fixes, in “Application Development in a
  11539. Shared Environment”, available from APDA ). In brief, here are the big ones we know
  11540. about:
  11541.  
  11542.  • Does your application create temporary files with fixed names in a fixed 
  11543.    place (such as the directory containing the application)? Without 
  11544.    AppleShare’s protection, two applications trying to use the same temporary 
  11545.    file could be disastrous.
  11546.  
  11547.  • Is your application at least “conscious” of the fact that it may be in a 
  11548.    multi-user environment? For instance, does it work correctly if a volume 
  11549.    containing an existing document is on a locked volume? Does it check all 
  11550.    result codes returned from File Manager calls, and ResError after relevant 
  11551.    Resource Manager calls?
  11552.  
  11553.  
  11554. OK, I follow the rules. What do I do to make my application sharable?
  11555.  
  11556. There is a flag in each file’s Finder information (stored in the file’s directory
  11557. entry) known as the “shared” bit. If you set this bit on your application’s resource
  11558. file, the Finder will launch your application using read-only permissions; if anyone
  11559. else launches your application, they’ll also get it read-only (their Finder will see
  11560. the same “shared” bit set.).
  11561.  
  11562. Three important warnings accompany this information:
  11563.  
  11564.  • The definition of the “shared” bit was incorrect in previous releases of 
  11565.    information and software from Apple. This includes the June 16, 1986 version 
  11566.    of Technical Note #40 (fixed in the March 2, 1987 version), as well as all 
  11567.    versions of ResEdit before and including 1.1b3 (included with MPW 2.0). For 
  11568.    now, the most reliable way to set this bit is to get the 1.1b3 version of 
  11569.    ResEdit, use it to Get Info on your application, and check the box labeled 
  11570.    “cached” (the incorrect documentation upon which ResEdit [et al.] was based 
  11571.    called the real shared bit “cached”; the bit labeled as “shared” is the real 
  11572.    cached bit [a currently unused but reserved bit which should be left clear]).
  11573.  
  11574.  • By checking this bit, you’re promising (to your users) that your application 
  11575.    will work entirely correctly if launched by more than one user. This means 
  11576.    that you follow the other rules, in addition to simply not writing to your 
  11577.    application’s own resource file. See “Application Development for a Shared 
  11578.    Environment,” and test carefully!
  11579.  
  11580.  • Setting this bit has nothing to do with allowing your application’s 
  11581.    documents to be shared; you must design this feature into your application 
  11582.    (it’s not something that Apple system software can take care of behind your 
  11583.    application’s back.). You should realize from reading this note, however, 
  11584.    that if you store your document’s data in resource files, you won’t be able 
  11585.    to allow multiple users to access them simultaneously.
  11586.  
  11587.  
  11588.  
  11589. æKY 117
  11590. æC #117: Compatibility: Why & How 
  11591.  
  11592. See Also:      Technical Note #2—Compatibility Guidelines
  11593.                Technical Note #7—A Few Quick Debugging Tips
  11594.  
  11595. Written by:    Bo3b Johnson    February 9, 1987
  11596. Updated:                       March 1, 1988
  11597. _______________________________________________________________________________
  11598.  
  11599. While creating or revising any program for the Macintosh, you should be aware of the
  11600. most common reasons why programs fail on various versions of the Macintosh. This note
  11601. will detail some common failure modes, why they occur, and how to avoid them.
  11602. _______________________________________________________________________________
  11603.  
  11604. We’ve tried to explain the issues in depth, but recognize that not everyone is interested
  11605. in every issue. For example, if your application is not copy protected, you’re probably
  11606. not very interested in the section on copy protection. That’s why we’ve included the
  11607. outline form of the technical note. The first two pages outline the problems and the
  11608. solutions that are detailed later. Feel free to skip around at will, but remember
  11609. that we’re sending this enormous technical note because the suggestions it provides
  11610. may save you hasty compatibility revisions when we announce a new machine.
  11611.  
  11612. We know it’s a lot, and we’re here to help you if you need it. Our address (electronic
  11613. and physical) is on page three—contact us with any questions—that’s what we’re here
  11614. for!
  11615.  
  11616.  
  11617. Compatibility: the outline
  11618.  
  11619. Don’t assume the screen is a fixed size
  11620.    To get the screen size:
  11621.    • check the QuickDraw global screenBits.bounds
  11622.  
  11623. Don’t assume the screen is in a fixed location
  11624.    To get the screen location:
  11625.    • check the QuickDraw global screenBits.baseAddr
  11626.  
  11627. Don’t assume that rowBytes is equal to the width of the screen
  11628.    To get the number of bytes on a line:
  11629.    • check the QuickDraw global screenBits.rowBytes
  11630.    To get the screen width:
  11631.    • check the QuickDraw global screenBits.bounds.right
  11632.    To do screen-size calculations:
  11633.    • Use LongInts
  11634.  
  11635. Don’t write to or read from nil Handles or nil Pointers
  11636.  
  11637. Don’t create or Use Fake Handles
  11638.    To avoid creating or using fake handles:
  11639.    • Always let the Memory Manager perform operations with handles
  11640.    • Never write code that assigns something to a master pointer
  11641.  
  11642. Don’t write code that modifies itself
  11643. Self modifying code will not live across incarnations of the 68000
  11644.  
  11645. Think carefully about code designed strictly as copy protection
  11646.    To avoid copy protection-related incompatibilities:
  11647.    • Avoid copy protection altogether
  11648.    • Rely on schemes that don’t require specific hardware
  11649.    • Make sure your scheme doesn’t perform illegal operations 
  11650.  
  11651. Don’t ignore errors
  11652.    To get valuable information:
  11653.    • Check all pertinent calls for errors 
  11654.    • Always write defensive code 
  11655.  
  11656. Don’t access hardware directly
  11657.    To avoid hardware-related incompatibilities:
  11658.    • Don’t read or write the hardware
  11659.    • If you can’t get the support from the ROM, ask the system where the 
  11660.      hardware is
  11661.    • Use low-memory globals
  11662.  
  11663. Don’t use bits that are reserved
  11664.    To avoid compatibility problems when bit status changes:
  11665.    • Don’t use undocumented stuff
  11666.    • When using low-memory globals, check only what you want to know
  11667.  
  11668. Summary
  11669.    Minor bugs are getting harder and harder to get away with:
  11670.    • Good luck
  11671.    • We’ll help
  11672.    • AppleLink: MacDTS, MCI: MacDTS
  11673.    • U.S. Mail: 20525 Mariani Ave.; M/S 27-T; Cupertino, CA 95014
  11674.  
  11675.  
  11676. WHAT IT IS
  11677.  
  11678. The basic idea is to make sure that your programs will run, regardless of which Macintosh
  11679. they are being run on. The current systems to be concerned with include:
  11680.  
  11681.     • Macintosh 128K        • Macintosh 512Ke
  11682.     • Macintosh 512K        • Macintosh Plus
  11683.     • Macintosh XL          • Macintosh SE
  11684.                             • Macintosh II
  11685.  
  11686. If you perform operations in a generic fashion, there is rarely any reason to know
  11687. what machine is running. This means that you should avoid writing code to determine
  11688. which version of the machine you are running on, unless it is absolutely necessary.
  11689.  
  11690. For the purposes of this discussion, the term “programs” will be used to describe any
  11691. code that runs on a Macintosh. This includes applications, INITs, FKEYs, Desk Accessories
  11692. and Drivers.
  11693.  
  11694.  
  11695. What the “Rules” mean
  11696.  
  11697. Compatibility across all Macintosh computers (which may sound like it involves more
  11698. work for you) may actually mean that you have less work to do, since it may not be
  11699. necessary to revise your program each time Apple brings out a new computer or System
  11700. file. Users, as a group, do not understand compatibility problems; all they see is
  11701. that the program does not run on their system.
  11702.  
  11703. The benefits of being compatible are many-fold: your customers/users stay happy, you
  11704. have less programming to do, you can devote your time to more valuable goals, there
  11705. are fewer versions to deal with, your code will probably be more efficient, your
  11706. users will not curse you under their breath, and your outlook on life will be much
  11707. merrier.
  11708.  
  11709. Now that we know what being compatible is all about, recognize that nobody is requiring
  11710. you to be compatible with anything. Apple does not employ roving gangs of thought
  11711. police to be sure that developers are following the recommended guidelines. Furthermore,
  11712. when the guidelines comprise 1200 pages of turgid prose (Inside Macintosh), you can
  11713. be expected to miss one or two of the 
  11714. “rules.” It is no sin to be incompatible, nor is it a punishable offense. If it were,
  11715. there would be no Macintosh programs, since virtually all developers would be incarcerated.
  11716. What it does mean, however, is that your program will be unfavorably viewed until it
  11717. steps in line with the current system (which is a moving target). If a program becomes
  11718. incompatible with a new Macintosh, it usually requires rethinking the offending code,
  11719. and releasing a new version. You may read something like “If the developers followed
  11720. Apple guidelines, they would be compatible with the transverse-hinged diatomic quark
  11721. realignment system.” This means that if you made any mistakes (you read all 1200
  11722. pages carefully, right?), you will not be compatible. It is extremely difficult to
  11723. remain completely compatible, particularly in a system as complex as the Macintosh.
  11724. The rules haven’t changed, but what you can get away with has. There are, however, a
  11725. number of things that you can do to improve your odds—some of which will be explained
  11726. here.
  11727.  
  11728.  
  11729. It’s your choice
  11730.  
  11731. It is still your choice whether you will be concerned with compatibility or not.
  11732. Apple will not put out a warrant for your arrest. However, if you are doing things
  11733. that are specifically illegal, Apple will also not worry about “breaking” your program.
  11734.  
  11735.  
  11736. Bad Things
  11737.  
  11738. The following list is not intended to be comprehensive, but these are the primary
  11739. reasons why programs break from one version of the system to the next. These are the
  11740. current top ten commandments:
  11741.  
  11742. I     Thou shalt not assume the screen is a fixed size.
  11743. II    Thou shalt not assume the screen is at a fixed location.
  11744. III   Thou shalt not assume that rowBytes is equal to the width of the screen.
  11745. IV    Thou shalt not use nil handles or nil pointers.
  11746. V     Thou shalt not create or use fake handles.
  11747. VI    Thou shalt not write code that modifies itself.
  11748. VII   Thou shalt think twice about code designed strictly as copy protection.
  11749. VIII  Thou shalt check errors returned as function results.
  11750. IX    Thou shalt not access hardware directly.
  11751. X     Thou shalt not use any of the bits that are reserved (unused means 
  11752.       reserved).
  11753.  
  11754. This has been determined from extensive testing of our diverse software base.
  11755.  
  11756. I. Assuming the screen is a fixed size
  11757.  
  11758.    Do not assume that the Macintosh screen is 512 x 342 pixels. Programs that 
  11759.    do generally have problems on (or special case for) the Macintosh XL, which 
  11760.    has a wider screen. Most applications have to create the bounding rectangle 
  11761.    where a window can be dragged. This is the boundsRect that is passed to the 
  11762.    call:
  11763.  
  11764.        DragWindow (myWindowPtr, theEvent.where, boundsRect);
  11765.  
  11766.    Some ill-advised programs create the boundsRect by something like:
  11767.  
  11768.        SetRect (boundsRect, 0,0,342,512);  { oops, this is hard-coded…}
  11769.  
  11770. Why it’s Bad
  11771.  
  11772. This is bad because it is never necessary to specifically put in the bounding rectangle
  11773. for the screen. On a Macintosh XL for example, the screen size is 760x364 (and sometimes
  11774. 608x431 with alternate hardware). If a program uses the hard-coded 0,0,342,512 as a
  11775. bounding rectangle, end users will not be able to move their windows past the fictitious
  11776. boundary of 512. If something similar were done to the GrowWindow call, it would make
  11777. it impossible for users to grow their window to fill the entire screen. (Always a
  11778. saddening waste of valuable screen real-estate.)
  11779.  
  11780. Assuming screen size makes it more difficult to use the program on Macintoshes with
  11781. big screens, by making it difficult to grow or move windows, or by drawing in strange
  11782. places where they should not be drawing (outside of windows). Consider the case of
  11783. running on a Macintosh equipped with one of the full page displays, or Ultra-Large
  11784. screens. No one who paid for a big screen wants to be restricted to using only the
  11785. upper-left corner of it.
  11786.  
  11787. How to avoid becoming a screening fascist
  11788.  
  11789. Never hard code the numbers 512 and 342 for screen dimensions. You should avoid using
  11790. constants for system values that can change. Parameters like these are nearly always
  11791. available in a dynamic fashion. Programs should read the appropriate variables while
  11792. the program is running (at run-time, not at compile time).
  11793.  
  11794. Here’s how smart programs get the screen dimensions:
  11795.  
  11796.     InitGraf(@thePort); { QuickDraw global variables have to be initialized.}
  11797.     …
  11798.     boundsRect := screenBits.bounds;   { The Real way to get screen size }
  11799.                                        { Use QuickDraw global variable. }
  11800.  
  11801. This is smart, because the program never has to know specifically what the numbers
  11802. are. All references to rectangles that need to be related to the screen (like the
  11803. drag and grow areas of windows) should use screenBits.bounds to avoid worrying about
  11804. the screen size.
  11805.  
  11806. Note that this does not do anything remotely like assume that “if the computer is not
  11807. a standard Macintosh, then it must be an XL.” Special casing for the various versions
  11808. of the Macintosh has always been suspicious at best; it is now grounds for breaking.
  11809. (At least with respect to screen dimensions.)
  11810.  
  11811. By the way, remember to take into account the menu bar height when using this rectangle.
  11812. On 128K ROMs (and later) you can use the low-memory global mBarHeight (a word at
  11813. $BAA). But since we didn’t provide a low-memory global for the menu bar height in the
  11814. 64K ROMs, you’ll have to hard code it to 20 ($14). (You’re not the only ones to forget
  11815. the future holds changes.)
  11816.  
  11817. How to find fascist screenism in current programs
  11818.  
  11819. The easiest way is to exercise your program on one of the Ultra-Large screen Macintoshes.
  11820. There should be no restrictions on sizing or moving the windows, and all drawing
  11821. should have no problems. If there are any anomalies in the program’s usage, there is
  11822. probably a lurking problem. Also, do a global find in the source code to see if the
  11823. numbers 512 or 342 occur in the program. If so, and if they are in reference to the
  11824. screen, excise them.
  11825.  
  11826.  
  11827. II. Assuming the screen is at a fixed location
  11828.  
  11829.    Some programs use a fixed screen address, assuming that the screen location 
  11830.    will be the same on various incarnations of the Macintosh. This is not the 
  11831.    case. For example, the screen is located at memory location $1A700 on a 128K 
  11832.    Macintosh, at $7A700 on a 512K Macintosh, at $F8000 on the Macintosh XL, and 
  11833.    at $FA700 on the Macintosh Plus.
  11834.  
  11835. Why it’s Bad
  11836.  
  11837. When a program relies upon the screen being in a fixed location, Murphy’s Law dictates
  11838. that an unknowing user will run it upon a computer with the screen in a different
  11839. location. This usually causes the system to crash, since the offending program will
  11840. write to memory that was used for something important. Programs that crash have been
  11841. proven to be less useful than those that don’t.
  11842.  
  11843. How to avoid being a base screener
  11844.  
  11845. Suffice it to say that there is no way that the address of the screen will remain
  11846. static, but there are rare occasions where it is necessary to go directly to the
  11847. screen memory. On these occasions, there are bad ways and not-as-bad ways to do it. A
  11848. bad way:
  11849.     
  11850.     myScreenBase := Pointer ($7A700);  { not good.  Hard-coded number. }
  11851.  
  11852. A not-as-bad way:
  11853.  
  11854.     InitGraf(@thePort);   { do this only once in a program. }
  11855.     …
  11856.     myScreenBase := screenBits.baseAddr;  { Good.  Always works. }
  11857.                              {Yet another QuickDraw global variable}
  11858.  
  11859. Using the latter approach is guaranteed to work, since QuickDraw has to know where to
  11860. draw, and the operating system tells QuickDraw where the screen can be found. When in
  11861. doubt, ask QuickDraw. This will work on Macintosh computers from now until forever,
  11862. so if you use this approach you won’t have to revise your program just because the
  11863. screen moved in memory.
  11864.  
  11865. If you have a program (such as an INIT) that cannot rely upon QuickDraw being initialized
  11866. (via InitGraf), then it is possible to use the ScrnBase low-memory global variable (a
  11867. long word at $824). This method runs a distant second to asking QuickDraw, but is
  11868. sometimes necessary.
  11869.  
  11870. How to find base screeners
  11871.  
  11872. The easiest way to find base screeners is to run the offending program on machines
  11873. that have different screen addresses. If any addresses are being used in a base manner,
  11874. the system will usually crash. The offending program may also occasionally refuse to
  11875. draw. Some programs afflicted with this problem may also hang the computer (sometimes
  11876. known as accessing funny space). Also, do a global find on the source code to look
  11877. for numbers like $7A700 or $1A700. When found, exercise caution while altering the
  11878. offending lines.
  11879.  
  11880.  
  11881. III. Assuming that rowbytes is equal to the width of the screen
  11882.  
  11883. According to the definition of a bitMap found in Inside Macintosh (p I-144), 
  11884. you can see that rowBytes is the number of actual bytes in memory that are used to
  11885. determine the bitMap. We know the screen is just a big hunk of memory, and we know
  11886. that QuickDraw uses that memory as a bitMap. rowBytes accomplishes the translation of
  11887. a big hunk of memory into a bitMap. To do this, rowBytes tells the system how long a
  11888. given row is in memory and, more importantly, where in memory the next row starts.
  11889. For conventional Macintoshes, rowBytes (bytes per Row) * 8 (Pixels per Byte) gives
  11890. the final horizontal width of the screen as Pixels per Row. This does not have to be
  11891. the case. It is possible to have a Macintosh screen where the rowBytes extends beyond
  11892. what is actually visible on the screen. You can think of it as having the screen
  11893. looking in on a larger bitMap. Diagrammatically, it might look like:
  11894.  
  11895. •••Click on the Illustration button below, and refer to Figure 1.••• 
  11896.  
  11897. With an Ultra-Large screen, the number of bytes used for screen memory may be in the
  11898. 500,000 byte range. Whenever calculations are being made to find various locations in
  11899. the screen, the variables used should be able to handle larger screen sizes. For
  11900. example, a 16 bit Integer will not be able to hold the 500,000 number, so a LongInt
  11901. would be required.  Do not assume that the screen size is 21,888 bytes long.  bitMaps
  11902. can be larger than 32K or 64K.
  11903.  
  11904. Why it’s Bad
  11905.  
  11906. Programs that assume that all of the bytes in a row are visible may make bad calculations,
  11907. causing drawing routines to produce unusual, and unreadable, results.  Also, programs
  11908. that use the rowBytes to figure out the width of the screen rectangle will find that
  11909. their calculated rectangle is not the real screenBits.Bounds.  Drawing into areas
  11910. that are not visible will not necessarily crash the computer, but it will probably
  11911. give erroneous results, and displays that don’t match the normal output of the program.
  11912.  
  11913. Programs that assume that the number of bytes in the screen memory will be less than
  11914. 32768 may have problems drawing into Ultra-Large screens, since those screens will
  11915. often have more memory than a normal Macintosh screen.  These particular problems do
  11916. not evidence themselves by crashing the system.  They generally appear as loss of
  11917. functionality (not being able to move a window to the bottom of the screen), or as
  11918. drawing routines that no longer look correct.  These problems can prevent an otherwise
  11919. wonderful program from being used.
  11920.  
  11921. How to avoid being a row byter
  11922.  
  11923. In any calculations, the rowBytes variable should be thought of as the way to get to
  11924. the next row on the screen.  This is distinct from thinking of it as the width of the
  11925. screen.  The width should always be found from screenBits.bounds.right– screenBits.bounds.left.
  11926.   
  11927.  
  11928. It is also inappropriate to use the rectangle to decide how many bytes there are on a
  11929. row.  Programs that do something like:
  11930.  
  11931.     bytesLine := screenBits.bounds.right DIV 8;  { bad use of bounds }
  11932.     rightSide := screenBits.rowBytes * 8;    { bad use of rowBytes }
  11933.  
  11934. will find that the screen may have more rowBytes than previously thought.  The best
  11935. way to avoid being a row byter is to use the proper variables for the proper things. 
  11936. Without the proper mathematical basis to the screen, life becomes much more difficult.
  11937.  Always do things like:
  11938.  
  11939.     bytesLine := screenBits.rowBytes;  { always the correct number }
  11940.     rightSide := screenBits.bounds.right;  { always the correct screen size }
  11941.  
  11942. It is sometimes necessary to do calculations involving the screen.  If so, be sure to
  11943. use LongInts for all the math, and be sure to use the right variables (i.e. use LongInts).
  11944.  For example, if we need to find the address of the 500th row in the screen (500
  11945. lines from the top):
  11946.     
  11947. VAR    myAddress:    LongInt;
  11948.     myRow:    LongInt;    { so the calculations don’t round off. }
  11949.     myOffset:    LongInt;    { could easily be over 32768 ... }
  11950.     bytesLine:    LongInt;
  11951.  
  11952.     ...
  11953.     myAddress := ord4(screenBits.baseAddr); {start w/the real base address }
  11954.     myRow := 500;           {the row we want to address }
  11955.     bytesLine := screenBits.rowBytes;  {the real bytes per line }
  11956.     myOffset := myRow * bytesLine;        {lines * bytes per lines gives bytes }
  11957.     myAddress := myAddress + myOffset;    {final address of the 500th line }
  11958.  
  11959. This is not something you want to do if you can possibly avoid it, but if you simply
  11960. must go directly to the screen, be careful.  The big-screen machines (Ultra-Large
  11961. screens) will thank you for it.  If QuickDraw cannot be initialized, there is also
  11962. the low-memory global screenRow (a word at $106) that will give you the current rowBytes.
  11963.  
  11964.  
  11965. How to find row byters
  11966.  
  11967. To find current problems with row byter programs, run them on a machine equipped with
  11968. Ultra-Large screens and see if any anomalies crop up.  Look for drawing sequences
  11969. that don’t work right, and for drawing that clips to an imaginary edge.  For source-level
  11970. inspection, look for uses of the rowBytes variables and be sure that they are being
  11971. used in a mathematically sound fashion.  Be highly suspicious of any code that uses
  11972. rowBytes for the screen width.  Any calculations involving those system variables
  11973. should be closely inspected for round-off errors and improper use.  Search for the
  11974. number 8.  If it is being used in a calculation where it is the number of bits per
  11975. byte, then watch that code closely for improper conceptualization.  This is code that
  11976. could leap out and grab you by the throat at anytime.  Be careful!
  11977.  
  11978. IV. Using nil Handles or nil Pointers
  11979.  
  11980. A nil pointer is a pointer that has a value of 0.  Recognize that pointers are merely
  11981. addresses in memory.  This means that a nil pointer is pointing to memory location 0.
  11982.  Any use of memory location 0 is strictly forbidden, since it is owned by Motorola. 
  11983. Trespassers may be shot on sight, but they may not die until much later.  Sometimes
  11984. trespassers are only wounded and act strangely.  Any use of memory location 0 can be
  11985. considered a bug, since there are no valid reasons for Macintosh programs to read or
  11986. write to that memory.  However, nil pointers themselves are not necessarily bad.  It
  11987. is occasionally necessary to pass nil pointers to ROM routines.  This should not be
  11988. confused with reading or writing to memory location 0.  A pointer normally points to
  11989. (contains the address of) a location in memory.  It could look like this:
  11990.  
  11991. •••Click on the Illustration button below, and refer to Figure 2.••• 
  11992.  
  11993. If a pointer has been cleared to nil, it will point to memory location 0.  This is OK
  11994. as long as the program does not try to read from or write to that pointer.  An example
  11995. of a nil pointer could look like:
  11996.  
  11997. •••Click on the Illustration button below, and refer to Figure 3.••• 
  11998.  
  11999. nil handles are related to the problem, since a handle is merely the address of a
  12000. pointer (or a pointer to a pointer).  An example of what a normal handle might look
  12001. like is:
  12002.  
  12003. •••Click on the Illustration button below, and refer to Figure 4.••• 
  12004.  
  12005. When the first pointer (h) becomes nil, that implies that memory location 0 can be
  12006. used as a pointer.  This is strictly illegal.  There are no cases where it is valid
  12007. to read from or write to a nil handle.   A pictorial representation of what a nil
  12008. handle could look like:
  12009.  
  12010. •••Click on the Illustration button below, and refer to Figure 5.••• 
  12011.  
  12012. If the memory at 0 contains an odd number (numerically odd), then using it as a pointer
  12013. will cause a system error with ID=2.  This can be very useful, since that tells you
  12014. exactly where the program is using this illegal handle, making it easy to fix.  Unfortunately,
  12015. there are cases where it is appropriate to pass a nil handle to ROM routines (such as
  12016. GetScrap).  These cases are rare, and it is never legal to read from or write to a
  12017. nil handle.
  12018.  
  12019. There is also the case of an empty handle.  An empty handle is one where the handle
  12020. itself (the first pointer) points to a valid place in memory; that place in memory is
  12021. also a pointer, and if it is nil the entire handle is termed empty. There are occasions
  12022. where it is necessary to use the handle itself, but using the nil pointer that it
  12023. contains is not valid.  An example of an empty handle could be:
  12024.  
  12025. •••Click on the Illustration button below, and refer to Figure 6.••• 
  12026.  
  12027. Fundamentally, any reading or writing to memory using a pointer or handle that is nil
  12028. is punishable by death (of your program). 
  12029.  
  12030. Why it’s Bad
  12031.  
  12032. The use of nil pointers can lead to the use of make-believe data.  This make-believe
  12033. data often changes for different versions of the computer.  This changing data makes
  12034. it difficult to predict what will happen when a program uses nil pointers.  Programs
  12035. may not crash as a result of using a nil pointer, and they may behave in a consistent
  12036. fashion.  This does not mean that there isn’t a bug.  This merely means that the
  12037. program is lucky, and that it should be playing the lottery, not running on a Macintosh.
  12038.  If a program acts differently on different versions of the Macintosh, you should
  12039. think “could there be a nasty nil pointer problem here?”   Use of a nil handle usually
  12040. culminates in reading or writing to obscure places in memory.  As an example:
  12041.  
  12042.     VAR   myHandle:  TEHandle;
  12043.  
  12044.     myHandle := nil;
  12045.  
  12046. That’s pretty straightforward, so what’s the problem?  If you do something like:
  12047.  
  12048.     myHandle^^.viewRect := myRect;  { very bad idea with myHandle = nil }
  12049.  
  12050. memory location zero will be used as a pointer to give the address of a TextEdit
  12051. record.  What if that memory location points to something in the system heap?  What
  12052. if it points to the sound buffer?  In cases like these, eight bytes of rectangle data
  12053. will be written to wherever memory location 0 points.  
  12054.  
  12055. Use of a nil handle will never be useful.  This memory is reserved and used by the
  12056. 68000 for various interrupt vectors and Valuable Stuff.  This Valuable Stuff is composed
  12057. of things that you definitely do not want to change.  When changed, the 68000 finds
  12058. out, and decides to get back at your program in the most strange and wonderful ways. 
  12059. These strange results can range from a System Error all the way to erasing hard disks
  12060. and destroying files.  There really is no limit to the havoc that can be wreaked. 
  12061. This tends to keep the users on the edge of their seat, but this is not really the
  12062. desired effect.  As noted above, it won’t necessarily cause traumatic results.  A
  12063. program can be doing naughty things and not get caught.  This is still a bug that
  12064. needs to be fixed, since it is nearly guaranteed to give different results on different
  12065. versions of the Macintosh.  Programs exhibiting schizophrenia have been proven to be
  12066. less enjoyable to use.
  12067.  
  12068. How to avoid being a Niller
  12069.  
  12070. Whenever a program uses pointers and handles, it should ensure that the pointer or
  12071. handle will not be nil.  This could be termed defensive programming, since it assumes
  12072. that everyone is out to get the program (which is not far from the truth on the Macintosh).
  12073.  You should always check the result of routines that claim to pass back a handle. If
  12074. they pass you back a nil handle, you could get in trouble if you use them.  Don’t
  12075. trust the ROM.  The following example of a defensive use of a handle involves the
  12076. Resource Manager.  The Resource Manager passes back a handle to the resource data. 
  12077. There are any number of places where it may be forced to pass back a nil handle.  For
  12078. example:
  12079.     
  12080.     VAR   myRezzie:  MyHandle;
  12081.  
  12082.     myRezzie := MyHandle(GetResource(myResType, myResNumber));    { could be missing…}
  12083.     IF myRezzie = nil  THEN  ErrorHandler('We almost got Nilled')
  12084.     ELSE  myRezzie^^.myRect := newRect;       { We know it is OK }
  12085.  
  12086. As another example, think of how handles can be purged from memory in tight memory
  12087. conditions.  If a block is marked purgeable, the Memory Manager may throw it away at
  12088. any time.  This creates an empty handle.  The defensive programmer will always make
  12089. sure that the handles being used are not empty.  
  12090.  
  12091.     VAR   myRezzie:  myHandle;
  12092.  
  12093.     myRezzie := myHandle(GetResource(myResType, myResNumber));  { could be           
  12094.        missing… }
  12095.     IF myRezzie = nil  THEN  ErrorHandler('We almost got Nilled')
  12096.     ELSE  myRezzie^^.myRect := newRect;    { We know it is OK }
  12097.     tempHandle := NewHandle (largeBlock);  {might dispose a purgeable myRezzie}
  12098.     IF myRezzie^ = nil  THEN LoadResource(Handle(myRezzie)); {Re-load empty          
  12099.              handle}
  12100.     IF ResError = noErr  THEN 
  12101.         myRezzie^^.StatusField := OK;    { guaranteed not empty, and actually        
  12102.     gets read back in, if necessary }
  12103.  
  12104. Be especially careful of places where memory is being allocated.  The NewHandle and
  12105. NewPtr calls will return a nil handle or pointer if there is not enough memory.  If
  12106. you use that handle or pointer without checking, you will be guilty of being a Niller.
  12107.  
  12108. How to find Nillers
  12109.  
  12110. The best way to find these nasty nil pointer problems is to set memory location zero
  12111. to be an odd number (a good choice is 'NIL!' = $4E494C21, which is numerically odd,
  12112. as well as personality-wise).  Please see Technical Note #7 for details on how to do
  12113. this.  
  12114.  
  12115. If you use TMON, you can use the extended user area with Discipline.  Discipline will
  12116. set memory location 0 to 'NIL!' to help catch those nasty pointer problems.  If you
  12117. use Macsbug, just type SM 0 'NIL! and go.  Realize of course, that if a program has
  12118. made a transgression and is actually using nil pointers, this may make the program
  12119. crash with an ID=2 system error.  This is good!  This means that you have found a bug
  12120. that may have been causing you untold grief.  Once you know where a program crashes,
  12121. it is usually very easy to use a debugger to find where the error is in the source
  12122. code.  When the program is compiled, turn on the debugging labels (usually a $D+
  12123. option).  Set memory location 0 to be 'NIL!'.  When the program crashes, look at
  12124. where the program is executing and see what routine it was in (from a disassembly). 
  12125. Go back to that routine in the source code and remove the offending code with a grim
  12126. smile on your face.  Another scurvy bug has been vanquished.  The intoxicating smell
  12127. of victory wafts around your head.
  12128.  
  12129. Another way to find problems is to use a debugger to do a checksum on the first four
  12130. bytes in memory (from 0 to 3 inclusive).  If the program ever traps into the debugger
  12131. claiming that the memory changed, see which part of the program altered memory location
  12132. 0.  Any code that writes to memory location zero is guilty of high treason against
  12133. the state and must be removed.  Remember to say, “bugs are not my friends.”
  12134.  
  12135.  
  12136. V. Creating or Using Fake Handles
  12137.  
  12138. A fake handle is one that was not manufactured by the system, but was created by the
  12139. program itself.  An example of a fake handle is:
  12140.  
  12141.     CONST    aMem = $100;
  12142.     VAR    myHandle: Handle;
  12143.         myPointer: Ptr;
  12144.  
  12145.     myPointer := Ptr (aMem);   { the address of some memory }
  12146.     myHandle := @myPointer;    {the address of the pointer variable. Very bad.}
  12147.  
  12148. The normal way to create and use handles is to call the Memory Manager NewHandle
  12149. function. 
  12150.  
  12151. Why it’s Bad
  12152.  
  12153. A handle that is manufactured by the program is not a legitimate handle as far as the
  12154. operating system is concerned.  Passing a fake handle to routines that use handles is
  12155. a good way to discover the meaning of “Death by ROM.”  For example, think how confused
  12156. the operating system would get if the fake handle were passed to DisposHandle.  What
  12157. would it dispose?  It never allocated the memory, so how can it release it?  Programs
  12158. that manufacture handles may find that the operating system is no longer their friend.
  12159.  
  12160. When handles are passed to various ROM routines, there is no telling what sorts of
  12161. things will be done to the handle.  There are any number of normal handle manipulation
  12162. calls that the ROM may use, such as SetHandleSize, HLock, HNoPurge, MoveHHi and so
  12163. on.  Since a program cannot guarantee that the ROM will not be doing things like this
  12164. to handles that the program passes in, it is wise to make sure that a real handle is
  12165. being used, so that all these type of operations will work as the ROM expects.  For
  12166. fake handles, the calls like HLock and SetHandleSize have no bearing.  Fake handles
  12167. are very easy to create, and they are very bad for the health of otherwise upstanding
  12168. programs.  Whenever you need a handle, get one from the Memory Manager.  
  12169.  
  12170. As a particularly bad use of a fake handle:
  12171.  
  12172.     VAR   myHandle:  Handle;
  12173.           myStuff:  myRecord;
  12174.  
  12175.     myHandle := NewHandle (SIZEOF(myStuff));   { create a new normal handle }
  12176.     myHandle^ := @myStuff;  {YOW!  Intended to make myHandle a handle to
  12177.                     the myStuff record.  What it really does is
  12178.                     blow up a Master Pointer block, Heap corruption,
  12179.                     and death by Bad Heap.  Never do this. }
  12180.  
  12181. This can be a little confusing, since it is fine to use your own pointers, but very
  12182. bad to use your own handles.  The difference is that handles can move in memory, and
  12183. pointers cannot, hence the pointers are not dangerous.  This does not mean you should
  12184. use pointers for everything since that causes other problems.  It merely means that
  12185. you have to be careful how you use the handles. 
  12186.  
  12187. The use of fake handles usually causes system errors, but can be somewhat mysterious
  12188. in its effects.  Fake handles can be particularly hard to track down since they often
  12189. cause damage that is not uncovered for many minutes of use.  Any use of fake handles
  12190. that causes the heap to be altered will usually crash the system.  Heap corruption is
  12191. a common failure mode.  In clinical studies, 9 out of 10 programmers recommend uncorrupted
  12192. heaps to their users who use heaps.
  12193.  
  12194. How to avoid being a fakir
  12195.  
  12196. The correct way to make a handle to some data is to make a copy of the data:
  12197.  
  12198.     VAR   myHandle:  Handle;
  12199.           myStuff:  myRecord;
  12200.  
  12201.     errCode := PtrToHand (@myStuff, myHandle, SIZEOF(myStuff));
  12202.     IF  errCode <> noErr  THEN ErrorHandler ('Out of memory');
  12203.  
  12204. Always, always, let the Memory Manager perform operations with handles.  Never write
  12205. code that assigns something to a master pointer, like:
  12206.  
  12207.     VAR   myDeath:  Handle;
  12208.     myDeath^ := stuff;  { Don’t change the Master pointer. }
  12209.  
  12210. If there is code like this, it usually means the heap is being corrupted, or a fake
  12211. handle is being used.  It is, however, OK to pass around the handle itself, like:
  12212.  
  12213.     myCopyHandle := myHandle;   { perfectly OK, nobody will yell about this. }
  12214.  
  12215. This is far different than using the ^ operator to accidentally modify things in the
  12216. system.  Whenever it is necessary to write code to use handles, be careful.  Watch
  12217. things carefully as they are being written.  It is much easier to be careful on the
  12218. way in than it is to try to find out why something is crashing.  Be very careful of
  12219. the @ operator.  This operator can unleash untold problems upon unsuspecting programs.
  12220.  If at all possible, try to avoid using it, but if it is necessary, be absolutely
  12221. sure you know what it is doing.  It is particularly dangerous since it turns off the
  12222. normal type checking that can help you find errors (in Pascal).  In short, don’t get
  12223. crazy with pointer and handle manipulations, and they won’t get crazy with you. 
  12224.  
  12225. How to find fakirs
  12226.  
  12227. Problems of this form are particularly insidious because it can be very difficult to
  12228. find them after they have been created.  They tend to not crash immediately, but
  12229. rather to crash sometime long after the real damage has been done.  The best way to
  12230. find these problems is to run the program with Discipline.  (Discipline is a programmer’s
  12231. tool that will check all parameters passed to the ROM to see if they are legitimate. 
  12232. Discipline can be found as a stand-alone tool, but the most up-to-date version will
  12233. be found in the Extended User Area for the TMON debugger.  The User Area is public
  12234. domain, but TMON itself is not.  TMON has a number of other useful features, and is
  12235. well worth the price.)  Discipline will check handles that are passed to the ROM to
  12236. see if they are real handles or not, and if not, will stop the program at the offending
  12237. call.  This can lead you back to the source at a point that may be close to where the
  12238. bad handle was created.  If a program passes the Discipline test, it will be a healthy,
  12239. robust program with drastically improved odds for compatibility.  Programs that do
  12240. not pass Discipline can sleep poorly at night, knowing that they have broken at least
  12241. one or two of the “rules.”   
  12242.  
  12243. A way to find programs that are damaging the heap is to use a debugger (TMON or Macsbug)
  12244. and turn on the Heap Check operation.  This will check the heap for errors at each
  12245. trap call, and if the heap is corrupted will break into the debugger.  Hopefully this
  12246. will be close to where the code is that caused the damage.  Unfortunately, it may not
  12247. be close enough; this will force you to look further back.  
  12248.  
  12249. Looking in the source code, look for all uses of the @ operator, and examine the code
  12250. carefully to see if it is breaking the rules.  If it is, change it to step in line
  12251. with the rest of the happy programs here in happy valley.  Also, look for any code
  12252. that changes a master pointer like the myHandle^ := stuff.  Any code of this form is
  12253. highly suspect, and probably a member of the Anti-Productivity League.  The APL has
  12254. been accused of preventing software sales and the rise of the Yen.  These problems
  12255. can be quite difficult to find at times, but don’t give up.  These fake handles are
  12256. high on the list of guilty parties, and should never be trusted.  
  12257.  
  12258.  
  12259. VI. Writing code that modifies itself 
  12260.  
  12261. Self-modifying code is software that changes itself.  Code that alters itself runs
  12262. into two main groupings:  code that modifies the code itself and code that changes
  12263. the block the code is stored in.  Copy protection code often modifies the code itself,
  12264. to change the way it operates (concealing the meaning of what the code does).  Changing
  12265. the code itself is very tricky, and also prone to having problems, particularly when
  12266. the microprocessor itself changes.  There are third-party upgrades available that add
  12267. a 68020 to a Macintosh.  Because of the 68020’s cache, programs that modify themselves
  12268. stand a good chance of having problems when run on a 68020.  This is a compatibility
  12269. point that should not be missed (nudge, nudge, wink, wink).  Code that changes other
  12270. code (or itself) is prone to be incompatible when the microprocessor changes.  
  12271.  
  12272. The second group is code that changes the block that the code is stored in.  Keeping
  12273. variables in the CODE segment itself is an example of this.  This is uncommon with
  12274. high-level languages, but it is easy to do in assembly language (using the DC directive).
  12275.  Variables defined in the code itself should be read-only (constants).  Code that
  12276. modifies itself has signed a tacit agreement that says “I’m being tricky, if I die,
  12277. I’ll revise it.”
  12278.  
  12279. Why it’s Bad
  12280.  
  12281. There are now three different versions of the microprocessor, the 68000, 68010, and
  12282. the 68020.  They are intended to be compatible with each other, but may not be compatible
  12283. with code that modifies itself.  As the Macintosh evolves, the system may have compatibility
  12284. problems with programs that try to “push the envelope.”
  12285.  
  12286. How to avoid being an abuser
  12287.  
  12288. Well, the obvious answer is to avoid writing self-modifying code.  If you feel obliged
  12289. to write self-modifying code, then you are taking an oath to not complain when you
  12290. break in the future.  But don’t worry about accidentally taking the oath: you won’t
  12291. do it without knowing it.  If you choose to abuse, you also agree to personal visits
  12292. from the Apple thought police, who will be hired as soon as we find out.
  12293.  
  12294. How to find abusers
  12295.  
  12296. Run the program on a 68020 system.  If it fails, it could be related to this problem,
  12297. but since there are other bugs that might cause failures, it is not guaranteed to be
  12298. a self-modifying code problem.  Self-modifying code is often used in copy protection,
  12299. which brings us to the next big topic.
  12300.  
  12301.  
  12302. VII. Code designed strictly as copy protection
  12303.  
  12304. Copy protection is used to make it difficult to make copies of a program.  The basic
  12305. premise is to make it impossible to copy a program with the Finder.  This will not be
  12306. a discussion as to the pros and cons of copy protection.  Everyone has an opinion. 
  12307. This will be a description of reality, as it relates to compatibility.  
  12308.  
  12309. Why it’s Bad
  12310.  
  12311. System changes will never be made merely to cause copy protection schemes to fail,
  12312. but given the choice between improving the system and making a copy protection scheme
  12313. remain compatible, the system improvement will always be chosen.
  12314.  
  12315.  • Copy protection is number one on the list of why programs fail the 
  12316.    compatibility test.
  12317.  • Copy protection by its very nature tends to do the most “illegal” things.  
  12318.  • Programs that are copy protected are assumed to have signed a tacit
  12319.    agreement to revise the program when the system changes.  
  12320.  
  12321. Copy protection itself is not necessarily bad.  What is bad is when programs that
  12322. would otherwise be fully compatible do not work due only to the copy protection. 
  12323. This is very sad, since it requires extra work, revisions to the software, and time
  12324. lost while the revision is being produced.  The users are not generally humored when
  12325. they can no longer use their programs.  Copy protection schemes that fail generally
  12326. cause system errors when they are run.  They also can refuse to run when they should.
  12327.  
  12328.  
  12329. How to avoid being a protectionist
  12330.  
  12331. The simple answer is to do without copy protection altogether.  If you think of compatibility
  12332. as a probability game, if you leave out the copy protection, your odds of winning
  12333. skyrocket.  As noted above, copy protection is the single biggest reason why programs
  12334. fail on the various versions of the Macintosh.  For those who are required to use
  12335. copy protection, try to rely on schemes that do not require specific hardware and
  12336. make sure that the scheme used is not performing illegal operations.  If a program
  12337. runs, an experienced Macintosh programmer armed with a debugger can probably make a
  12338. copy of it, (no matter how sophisticated the copy protection scheme) so a moderate
  12339. scheme that does not break the rules is probably a better compatibility bet.  The
  12340. trickier and more devious the scheme, the higher the chance of breaking a rule. 
  12341. Tread lightly.
  12342.  
  12343. How to find protectionists
  12344.  
  12345. The easiest way to see if a scheme is being overly tricky is to run it on a Macintosh
  12346. XL.  Since the floppy disk hardware is different this will usually demonstrate an
  12347. unwanted hardware dependency.  Be wary of schemes that don’t allow installation on a
  12348. hard disk.  If the program cannot be installed on a hard disk, it may be relying upon
  12349. things that are prone to change.  Don’t use schemes that access the hardware directly.
  12350.  All Macintosh software should go through the various managers in the ROM to maintain
  12351. compatibility.  Any code that sidesteps the ROM will be viewed as having said “It’s
  12352. OK to make me revise myself.”  
  12353.  
  12354. VIII. Check errors returned as function results
  12355.  
  12356. All of the Operating System functions, as well as some of the Toolbox functions, will
  12357. return result codes as the value of the function.  Don’t ignore these result codes. 
  12358. If a program ignores the result codes, it is possible to have any number of bad things
  12359. happen to the program.  The result code is there to tell the program that something
  12360. went wrong; if the program ignores the fact that something is wrong, that program
  12361. will probably be killed by whatever went wrong.  (Bugs do not like to be ignored.) 
  12362. If a program checks errors, an anomaly can be nipped in the bud, before something
  12363. really bizarre happens.
  12364.  
  12365. Why it’s Bad
  12366.  
  12367. A program that ignores result codes is skipping valuable information.  This information
  12368. can often prevent a program from crashing and keep it from losing data.  
  12369.  
  12370. How to avoid becoming a skipper
  12371.  
  12372. Always write code that is defensive.  Assume that everyone and everything is out to
  12373. kill you.  Trust no one.  An example of error checking is:
  12374.     
  12375.     myRezzie := GetResource (myResType, myResId);
  12376.     IF  myRezzie = nil  THEN  ErrorHandler ('Who stole my resource...');
  12377.  
  12378. Another example:
  12379.  
  12380.     fsErrCode := FSOpen ('MyFile', myVRefNum, myFileRefNum);
  12381.     IF fsErrCode <> noErr  THEN ErrorHandler (fsErrCode, 'File error');
  12382.  
  12383. And another:
  12384.  
  12385.     myTPPrPort := PrOpenDoc (myTHPrint, nil, nil);
  12386.     IF  PRError <> noErr  THEN  ErrorHandler (PRError, 'Printing error');
  12387.  
  12388. Any use of Operating System functions should presume that something nasty can happen,
  12389. and have code to handle the nasty situations.  Printing calls, File Manager calls,
  12390. Resource Manager calls, and Memory Manager calls are all examples of Operating System
  12391. functions that should be watched for returning errors.  Always, always check the
  12392. result codes from Memory Manager calls.  Big memory machines are pretty common now,
  12393. and it is easy to get cavalier about memory, but realize that someone will always
  12394. want to run the program under Switcher, or on smaller Macintoshes.  It never hurts to
  12395. check, and always hurts to ignore it.  
  12396.  
  12397. How to find skippers
  12398.  
  12399. This is easy: just do weird things while the program is running.  Put in locked or
  12400. unformatted disks while the program is running.  Use unconventional command sequences.
  12401.  Run out of disk space.  Run on 128K Macintoshes to see how the program deals with
  12402. running out of memory.  Run under Switcher for the same reason.  (Programs that die
  12403. while running under Switcher are often not Switcher’s fault, and are in fact due to
  12404. faulty memory management.)  Print with no printer connected to the Macintosh.   Pop
  12405. disks out of the drives with the Command-Shift sequence, and see if the program can
  12406. deal with no disk.  When a disk-switch dialog comes up, press Command-period to pass
  12407. back an error to the requesting program (128K ROMs only).  Torturing otherwise well-
  12408. behaved programs can be quite enjoyable, and a number of users enjoy torturing the
  12409. program as much as the program enjoys torturing them.  For the truly malicious, run
  12410. the debugger and alter error codes as they come back from various routines.  Sure
  12411. it’s a dirty low-down rotten thing to do to a program, but we want to see how far we
  12412. can push the program.  (This is also a good way to check your error handling.)  It’s
  12413. one thing to be an optimist, but it’s quite another to assume that nothing will go
  12414. wrong while a program is running. 
  12415.  
  12416.  
  12417. IX. Accessing hardware directly
  12418.  
  12419. Sometimes it is necessary to go directly to the Macintosh hardware to accomplish a
  12420. specific task for which there is no ROM support.  Early hard disks that used the
  12421. serial ports had no ROM support.  Those disks needed to use the SCC chip (the 8530
  12422. communication chip) in a high-speed clocked fashion.  Although it is a valid function,
  12423. it is not something that is supported in the ROM.  It was therefore necessary to go
  12424. play with the SCC chip directly, setting and testing various hardware registers in
  12425. the chip itself.  Another example of a valid function that has no ROM support is the
  12426. use of the alternate video page for page-flipping animation.  Since there is no ROM
  12427. call to flip pages, it is necessary to go play with the right bit in the VIA chip
  12428. (6522 Versatile Interface Adapter).  Going directly to the hardware does not automatically
  12429. throw a program into the incompatible group, but it certainly lowers its odds.
  12430.  
  12431. Why it’s bad
  12432.  
  12433. Going directly to the hardware poses any number of problems for enlightened programs
  12434. that are trying to maintain compatibility across the various versions of the Macintosh.
  12435.  On the Macintosh XL for example, a lot of the hardware is found in different locations,
  12436. and in some cases the hardware doesn’t exist.  On the XL there is no sound chip. 
  12437. Programs that go directly to the sound hardware will find they don’t work correctly
  12438. on an XL.  If the same program were to go through the Sound Manager, it would work
  12439. fine, although the sound would not be the same as expected.  Since the Macintosh is
  12440. heavily oriented to the software side of things, expecting various hardware to always
  12441. be available is not a safe bet.  Choosy programmers choose to leave the hardware to
  12442. the ROM.  
  12443.  
  12444. How to avoid having a hard attack
  12445.  
  12446. Don’t read or write the hardware.  Exhaust every possible conventional approach before
  12447. deciding to really get down and dirty.  If there is a Manager in the ROM for the
  12448. operation you wish to perform, it is far better to use the Manager than to go directly
  12449. to the hardware.  Compatibility at the hardware level can very rarely be maintained,
  12450. but compatibility at the Manager level is a prime consideration.  If a program is
  12451. down to the last ditch effort, and cannot get the support from the ROM that is desired,
  12452. then access the hardware in an enlightened approach.  The really bad way to do it:
  12453.  
  12454.     VIA := Pointer ($EFE1FE);  { sure it’s the base address today…}
  12455.                         { This is bad.  Hard-coded number. }
  12456.  
  12457. The with-it, inspired programmer of the eighties does something like:
  12458.  
  12459.     TYPE LongPointer = ^LongInt;
  12460.  
  12461.     VAR  VIA: LongPointer;
  12462.          VIABase: LongInt;
  12463.  
  12464.     VIA := Pointer ($1D4);  { the address of the low-memory global. }
  12465.     VIABase := VIA^;        { get the low-memory variable’s value }
  12466.                    { Now VIABase has the address of the chip }
  12467.  
  12468. The point here is that the best way to get the address of a hardware chip is to ask
  12469. the system where it currently is to be found.  The system always knows where the
  12470. pieces of the system are, and will always know for every incarnation of the Macintosh.
  12471.  There are low-memory global variables for all of the pieces of hardware currently
  12472. found in the Macintosh.  This includes the VIA, the SCC, the Sound Chip, the IWM, and
  12473. the video display.  Whenever you are stuck with going to the hardware, use the low-memory
  12474. globals.  The fact that a program goes directly to the hardware means that it is
  12475. risking imminent incompatibility, but using the low-memory global will ensure that
  12476. the program has the best odds.  It’s like going to Las Vegas:  if you don’t gamble at
  12477. all, you don’t lose any money; if you have to gamble, play the game that you lose the
  12478. least on.
  12479.  
  12480. How to find hard attacks
  12481.  
  12482. Run the suspicious program on the Macintosh XL.  Nearly all of the hardware is in a
  12483. different memory location on the XL.  If a program has a hard-coded hardware address
  12484. in it, it will fail.  It may crash, or it might not perform the desired task, but it
  12485. won’t work as advertised.  This unfortunately, is not a completely legitimate test,
  12486. since the XL does not have some of the hardware of other Macintoshes, and some of the
  12487. hardware that is there has the register mapping different.  This means that it is
  12488. possible to play by the rule of using the low-memory global and still be incompatible.
  12489.  
  12490.  
  12491. X. Don’t use bits that are reserved
  12492.  
  12493. Occasionally during the life of a Macintosh programmer, there comes a time when it is
  12494. necessary to bite the bullet and use a low-memory global.  These are very sad days,
  12495. since it has been demonstrated (by history) that low-memory global variables are a
  12496. mysterious lot, and not altogether friendly.  One fellow in particular is known as
  12497. ROM85, a word located at $28E.  This particular variable has been documented as the
  12498. way to determine if a program is running on the 128K ROMs or not.  Notably, the top
  12499. most bit of that word is the determining bit.  This means that the rest of the bits
  12500. in that word are reserved, since nothing is described about any further bits.  Remember,
  12501. if it doesn’t say, assume it’s reserved.  If it’s reserved, don’t depend upon it. 
  12502. Take the cautious way out and assume that the other bits that aren’t documented are
  12503. used for Switcher local variables, or something equally wild.  An example of a bad
  12504. way to do the comparison is:
  12505.  
  12506.     VAR  Rom85Ptr: WordPtr;
  12507.          RomsAre64: Boolean;
  12508.  
  12509.     Rom85Ptr := Pointer ($28E);    { point at the low-memory global }
  12510.     IF  Rom85Ptr^ = $7FFF  THEN  RomsAre64 := False  { Bad test. }
  12511.     ELSE  RomsAre64 := True;
  12512.  
  12513. This is a bad test since the comparison is testing the value of all of the bits, not
  12514. only the one that is valid.  Since the other bits are undocumented, it is impossible
  12515. to know what they are used for.   Assume they are used for something that is arbitrarily
  12516. random, and take the safe way out.
  12517.  
  12518. How to avoid being bitten
  12519.  
  12520.     VAR     ROM85Ptr: Ptr
  12521.  
  12522.     Rom85Ptr := Pointer ($28E);    { point at the low-memory global }
  12523.     IF BitTst(ROM85Ptr,0) THEN RomsAre64 := True {Good--tests only hi-bit}
  12524.     ELSE  RomsAre64 := False;
  12525.  
  12526. This technique will ensure that when those bits are documented, your program won’t be
  12527. using them for the wrong things.  Beware of trojan bits.  
  12528.  
  12529. Don’t use undocumented stuff.  Be very careful when you use anything out of the ordinary
  12530. stream of a high-level language.  For instance, in the ROM85 case, it is very easy to
  12531. make the mistake of checking for an absolute value instead of testing the actual bit
  12532. that encodes the information.  Whenever a program is using low-memory globals, be
  12533. sure that only the information desired is being used, and not some undocumented (and
  12534. hence reserved) bits.  It’s not always easy to determine what is reserved and what
  12535. isn’t, so conservative programmers always use as little as possible.  Be wary of the
  12536. strange bits, and accept rides from none of them.  The ride you take might cause you
  12537. to revise your program.
  12538.  
  12539. How to find those bitten
  12540.  
  12541. Since there are such a multitude of possible places to get killed, there is no simple
  12542. way to see what programs are using illegal bits.  As time goes by it will be possible
  12543. to find more of these cases by running on various versions of the Macintosh, but
  12544. there will probably never be a comprehensive way of finding out who is accepting
  12545. strange rides, and who is not.  Whenever the use of a bit changes from reserved status
  12546. to active, it will be possible to find those bugs via extensive testing.  From a
  12547. source level, it would be advisable to look over any use of low-memory globals, and
  12548. eye them closely for inappropriate bit usage.  Do a global search for the $ (which
  12549. describes those ubiquitous hexadecimal numbers), and when found see if the use of the
  12550. number is appropriate.  Trust no one that is not known.  If they are documented, they
  12551. will stay where they are, and have the same meaning.  Be very careful in realms that
  12552. are undocumented.  Bits that suddenly jump from reserved to active status have been
  12553. known to cause more than one program to have a sudden anxiety attack.  It is very
  12554. unnerving to watch a program go from calm and reassuring to rabid status.  Users have
  12555. been known to drop their keyboards in sudden shock (which is bad on the keyboards).  
  12556.  
  12557.  
  12558.  
  12559. Summary
  12560.  
  12561. So what does all this mean?  It means that it is getting harder and harder to get
  12562. away with minor bugs in programs.  The minor bugs of yesterday are the major ones of
  12563. today.  No one will yell at you for having bugs in your program, since all programs
  12564. have bugs of one form or another.  The goal should be to make the programs run as
  12565. smoothly and effortlessly as possible.  The end-users will never object to bug-reduced
  12566. programs.
  12567.  
  12568. What is the best way to test a program?  A reasonably comprehensive test is to exercise
  12569. all of the program’s functions under the following situations:
  12570.  
  12571. • Use Discipline to be sure the program does not pass illegal things to the ROM.
  12572. • Use heap scramble and heap purge to be sure that handles are being used 
  12573.   correctly, and that the memory management of the program is correct.
  12574. • Run with a checksum on memory locations 0...3 to see if the program writes to 
  12575.   these locations.
  12576. • Run on a 128K Macintosh, or under Switcher with a small partition, to see how 
  12577.   the program deals with memory-critical situations.
  12578. • Run on a 68020 system to see if the program is 68020-compatible and to make
  12579.   sure that changing system speed won’t confuse the program.
  12580. • Run on a Macintosh XL to be sure that the program does not assume too much 
  12581.   about the operating system, and to test screen handling.
  12582. • Run on an Ultra-Large screen to be sure that the screen handling is correct, 
  12583.   and that there are no hard-coded screen dimensions.
  12584. • Run on 64K ROM machines to be sure new traps are not being used when they 
  12585.   don’t exist.
  12586. • Run under both HFS and MFS to be sure that the program deals with the file 
  12587.   system correctly.  (400K floppies are usually MFS.)
  12588.  
  12589. If a program can live through all of this with no Discipline traps, no checksum breaks,
  12590. no system errors, no anomalies, no data loss and still get useful work done, then you
  12591. deserve a gold medal for programming excellence.   Maybe even an extra medal for
  12592. conduct above and beyond the call of duty.  In any case, you will know that you have
  12593. done your job about as well as it can be done, with today’s version of the rules, and
  12594. today’s programming tools.
  12595.  
  12596. Sounds like a foreboding task, doesn’t it?  The engineers in Macintosh Technical
  12597. Support are available to help you with compatibility issues (we won’t always be able
  12598. to talk about new products, since we love our jobs, but we can give you some hints
  12599. about compatibility with what the future holds).
  12600.  
  12601. Good luck.
  12602.  
  12603.  
  12604.  
  12605.  
  12606. æKY 118
  12607. æC #118: How to Check and Handle Printing Errors
  12608.  
  12609. See also:    The Printing Manager
  12610.  
  12611. Written by:     Ginger Jernigan    May 4, 1987
  12612. Updated:        March 1, 1988
  12613. _______________________________________________________________________________
  12614.  
  12615. This technical note describes how to check and properly handle errors that occur
  12616. during printing with the high-level printing calls.
  12617. _______________________________________________________________________________
  12618.  
  12619. Most people are aware of the need for checking File Manager errors, Resource Manager
  12620. errors, and the like, but sometimes Printing Manager errors get neglected; you should
  12621. always check for error conditions while printing. This can be done by calling PrError.
  12622. Errors returned by PrError will include any Printing Manager errors (and some AppleTalk
  12623. and OS errors) that occur during printing. 
  12624.  
  12625. The best place to start is with the code fragment on page 155 of Inside Macintosh,
  12626. vol. II:
  12627.  
  12628. myPrPort := PrOpenDoc (prRecHdl, NIL, NIL);  {open printing grafPort}
  12629. FOR pg := 1 TO myPgCount DO    {page loop: ALL pages of document}
  12630.     IF PrError = noErr THEN
  12631.         BEGIN
  12632.         PrOpenPage(myPrPort,NIL);    {start new page}
  12633.         IF PrError = noErr THEN
  12634.             MyDrawingProc(pg);    {draw page with QuickDraw}
  12635.         PrClosePage(myPrPort);    {end current page}
  12636.         END;
  12637. PrCloseDoc(myPrPort);
  12638. IF prRecHdl^^.prJob.bJDocLoop = bSpoolLoop AND PrError = noErr THEN
  12639.         BEGIN
  12640.         MySwapOutProc;    {swap out code and data}
  12641.         PrPicFile(prRecHdl,NIL,NIL,NIL,myStRec);  {print spooled document}
  12642.         END;
  12643. IF PrError <> noErr THEN MyPrErrAlertProc;   {report any errors}
  12644.  
  12645. Here are some error-handling guidelines:
  12646.  
  12647.  • You should avoid calling PrError within your PrIdle procedure; errors that 
  12648.    occur while it is executing are usually temporary and serve only as internal 
  12649.    flags for communication within the printer driver—they are not intended for 
  12650.    the application. If you absolutely must call PrError within your idle 
  12651.    procedure, and an error occurs, never abort printing within the idle 
  12652.    procedure itself. Wait until the last called printing procedure returns and 
  12653.    then check to see if the error still remains. Attempting to abort printing 
  12654.    within an idle procedure is a guarantee of certain death.
  12655.  
  12656.  • If you detect that an error has occurred after the completion of a printing 
  12657.    routine, just stop where you are, i. e. stop drawing.  Proceed to the next 
  12658.    print procedure to close any open calls you have made.  For example, if you 
  12659.    called PrOpenDoc and received an error, skip to the next PrCloseDoc. Or if 
  12660.    you called PrOpenPage and got an error, skip to the next PrClosePage and 
  12661.    PrCloseDoc. Remember that if some PrOpen… procedure has been called, then 
  12662.    you must call the corresponding PrClose… procedure to ensure that printing 
  12663.    closes properly and that all temporary memory allocations are released and 
  12664.    returned to the heap. 
  12665.  
  12666.  • Do not raise any alerts or dialogs to report an error until the end of the 
  12667.    print loop.  At the end of the print loop, check for the error again; if 
  12668.    there is no error assume that printing completed normally.  If it’s still 
  12669.    there, you can raise an alert.  
  12670.  
  12671.    This is important for two reasons. First, if an alert is raised in the 
  12672.    middle of the print loop, it can cause errors that will terminate an 
  12673.    otherwise normal job.  For example, if the printer is an AppleTalk printer, 
  12674.    the connection can be terminated abnormally.  While your alert is sitting 
  12675.    there waiting for a response from the user, the driver is unable to respond 
  12676.    to AppleTalk requests coming in from the printer.  If the printer doesn’t 
  12677.    hear from the Macintosh within a short time period (30 seconds) then it will 
  12678.    timeout, assuming that the Macintosh is no longer there. This results in the 
  12679.    connection being broken prematurely causing another error that the 
  12680.    application has to respond to.
  12681.  
  12682.    The driver may also have already put up its own alert in response to the 
  12683.    error.  In this instance, the driver will post an error to let your 
  12684.    application know that something went wrong and that it’s time to abort 
  12685.    printing.  For example, when the driver detects that the version of Laser 
  12686.    Prep that has been downloaded to the LaserWriter is different from the 
  12687.    version that the user is trying to print with, the LaserWriter driver raises 
  12688.    the appropriate alert telling the user that the printer was initialized with 
  12689.    an incompatible version of the driver and gives the option of reinitializing.
  12690.  
  12691.    If the user chooses to cancel, the driver posts an error to let the 
  12692.    application know that it needs to abort, but since the driver has already 
  12693.    taken care of the error by putting up an alert, the error is reset to zero 
  12694.    before the printing loop is complete.  The application should check for the 
  12695.    error again at the end of the printing loop and if it still indicates an 
  12696.    error, it should raise an alert.
  12697.  
  12698.  
  12699.  
  12700. æKY 119
  12701. æC #119: Determining If Color QuickDraw Exists
  12702.  
  12703. See:           Technical Note #129 — SysEnvirons
  12704.  
  12705. Written by:    Jim Friedlander    May 4, 1987
  12706. Updated:                          March 1, 1988
  12707. _______________________________________________________________________________
  12708.  
  12709. This note formely described a way to determine if Color QuickDraw is present on a
  12710. particular machine. We now recommend that you call SysEnvirons to find out, as described
  12711. in Technical Note #129.
  12712.  
  12713.  
  12714. æKY 120
  12715. æC #120:    Drawing Into an Off-Screen Pixel Map
  12716.  
  12717. Revised by:    Rich Collyer                                          April 1989
  12718. Written by:    Jim Friedlander & Rick Blair                            May 1987
  12719.  
  12720. This Technical Note provides a simple example of drawing to, then copying from, an
  12721. off-screen pixel map.
  12722. Changes since October 1988:  Made changes to the code which convert GDevice color
  12723. look-up tables (clut) to pixel map color look-up tables so _CopyBits will copy the
  12724. color information correctly.  This information is especially important for color
  12725. printing.
  12726. _______________________________________________________________________________
  12727.  
  12728. The following example demonstrates how to draw something in an off-screen pixel map,
  12729. and then use _CopyBits to copy it back to the screen.  It handles the case of multiple
  12730. screens with different pixel depths.  Before making any calls to Color QuickDraw, you
  12731. must make sure it is present (refer to Technical Note #129, _SysEnvirons:  System 6.0
  12732. and Beyond).
  12733.  
  12734. MPW Pascal
  12735.  
  12736. CONST
  12737.     OffLeft      = 30;
  12738.     OffTop       = 30;
  12739.     OffBottom    = 250;
  12740.     OffRight     = 400;
  12741.  
  12742.     {These constants for the bounds of the off-screen PixMap are chosen because
  12743.      we know what the extent of the drawing will be and we want to restrict the
  12744.      size of the map as much as possible.}
  12745.  
  12746. TYPE
  12747.     BitMapPtr    = ^BitMap;    {for type coercion in the _CopyBits call}
  12748.  
  12749. VAR    
  12750.     offRowBytes     : LONGINT;
  12751.     sizeOfOff       : LONGINT;
  12752.     myBits          : Ptr;
  12753.     destRect        : Rect;
  12754.     globRect        : Rect;
  12755.     bRect           : Rect;
  12756.     theDepth        : INTEGER;
  12757.     i               : INTEGER;
  12758.     err             : INTEGER;
  12759.     myCGrafPort     : CGrafPort;
  12760.     myCGrafPtr      : CGrafPtr;
  12761.     ourCMHandle     : CTabHandle;
  12762.     theMaxDevice    : GDHandle;
  12763.     oldDevice       : GDHandle;
  12764.  
  12765.  
  12766. First you create a color window, then you need to determine the device with the maximum
  12767. depth to which you will copy the off-screen image with _CopyBits.
  12768.  
  12769.     myCWindow := GetNewCWindow(SomeID,NIL,WindowPtr(-1));
  12770.  
  12771.     SetPort(myCWindow); {set to this port for the localToGlobals that follow}
  12772.  
  12773.     SetRect(bRect,OffLeft,OffTop,OffRight,OffBottom);
  12774.     IF NOT SectRect(myCWindow^.portRect,bRect,globRect) THEN
  12775.         NothingToCopy;  {nothing to do, clean up and EXIT}
  12776.  
  12777.     {still here, so let’s convert to globals}
  12778.     LocalToGlobal(globRect.topLeft); 
  12779.     LocalToGlobal(globRect.botRight); 
  12780.  
  12781.     {figure out how much space we need for our pixel image.
  12782.     we will call GetMaxDevice and get the pixel map from that --
  12783.     we do this to cover the case where the pixel image that we wish
  12784.     to CopyBits to spans multiple devices (of possibly different depths)}
  12785.  
  12786.     theMaxDevice:= GetMaxDevice(globRect);{get the maxDevice}
  12787.  
  12788. You need to set theGDevice to the device with the maximum pixel depth (the one you
  12789. found in the last step), so the pixel map of the new CGrafPort will be copied from
  12790. one of the proper depth.  Now you should open a new CGrafPort to use for your off-screen
  12791. drawing.
  12792.  
  12793.     oldDevice := GetGDevice;    {save theGDevice so we can restore it later}
  12794.     SetGDevice(theMaxDevice);   {Set to the maxdevice}
  12795.  
  12796.     myCGrafPtr := @myCGrafPort; {initialize this guy}
  12797.     OpenCPort(myCGrafPtr);      {open a new color port — this calls InitCPort}
  12798.     theDepth:= myCGrafPtr^.portPixMap^^.pixelSize;
  12799.  
  12800. You are now ready to calculate the size of the pixel image you will need, then you
  12801. can set the location-specific and size-specific information of the pixel map.  Since
  12802. Color QuickDraw distinguishes between a bitmap and a pixel map by checking the high
  12803. bit of rowBytes, you need to add $8000 to OffRowBytes as shown.
  12804.  
  12805.     {similar formula to Technical Note #41, except we must include pixel depth}
  12806.     offRowBytes := ((((theDepth * (OffRight - OffLeft)) + 15)) DIV 16) * 2;
  12807.     {make sure LONGINT math is done on the next line!}
  12808.     sizeOfOff := LONGINT(OffBottom - OffTop) * offRowBytes;
  12809.     OffSetRect(bRect, - OffLeft, - OffTop);    {adjust for local coordinates}
  12810.  
  12811.     {Set up baseAddr, rowBytes, bounds, and pixelSize of the PixMap
  12812.      in our fresh, new CPort}
  12813.     myBits := NewPtr(sizeOfOff);        {allocate space for the pixel image}
  12814.     {real programs do error checking here}
  12815.  
  12816.     WITH myCGrafPtr^.portPixMap^^ DO BEGIN
  12817.         baseAddr := myBits;
  12818.         rowbytes := offRowBytes + $8000;    {remember to be a PixMap}
  12819.         bounds  := bRect;
  12820.     END;                                    {with}
  12821.  
  12822.  
  12823. Next you can clone the color table of the maxDevice and put it into your off-screen
  12824. pixel map.
  12825.  
  12826.     ourCMHandle := theMaxDevice^^.gdPMap^^.pmTable;
  12827.     err := HandToHand(Handle(ourCMHandle));    {clone it}
  12828.     {real programs do error checking here}
  12829.     FOR i := 0 TO ourCMHandle^^.ctSize DO
  12830.         ourCMHandle^^.ctTable[i].value := i;
  12831.     ourCMHandle^^.ctFlags := BAnd (ourCMHandle^^.ctFlags ,$7fff);
  12832.     ourCMHandle^^.ctSeed := GetCTSeed();
  12833.     { This code is necessary for converting GDevice cluts to Pixmap cluts }
  12834.  
  12835.     {put the cloned, correctly set-up Color Table into the off-screen map}
  12836.     myCGrafPtr^.portPixMap^^.pmTable := ourCMHandle;
  12837.     {Set the port to the off-screen port}
  12838.     SetPort(GrafPtr(myCGrafPtr));
  12839.  
  12840. Now you can call DrawIt (which in turn calls FillInColor) to draw an image in the
  12841. off-screen port.
  12842.  
  12843. FUNCTION FillInColor(r,g,b: Integer): RGBColor;
  12844. {small utility routine to return an RGBColor}
  12845.     VAR
  12846.         theColor    : RGBColor;
  12847.  
  12848.     BEGIN    {FillInColor}
  12849.         WITH theColor DO BEGIN
  12850.             red := r;
  12851.             green := g;
  12852.             blue := b;
  12853.         END;
  12854.         FillInColor := theColor;
  12855.     END;    {FillInColor}
  12856.  
  12857. PROCEDURE DrawIt;
  12858.  
  12859.     VAR
  12860.         OvalRect            : Rect;
  12861.         myRed,myBlue,myWhite,
  12862.         myGreen, myBlack    : RGBColor;
  12863.  
  12864.     BEGIN    { DrawIt }
  12865.         {get our colors set up}
  12866.         myRed := FillInColor(-1,0,0);
  12867.         myBlue := FillInColor(0,0,-1);
  12868.         myGreen := FillInColor(0,-1,0);
  12869.         myWhite := FillInColor(-1,-1,-1);
  12870.         myBlack := FillInColor(0,0,0);
  12871.         PenMode(PatCopy);
  12872.         RGBBackColor(myBlue);            {set the backcolor of the current port}
  12873.         EraseRect(thePort^.portRect);    {blue it out}
  12874.         RGBBackColor(myWhite);           {set back to white}
  12875.  
  12876.         RGBForeColor(myRed);             {set the forecolor of the current port}
  12877.         SetRect(OvalRect,30,30,190,150);
  12878.         PaintOval(OvalRect);
  12879.  
  12880.         InsetRect(OvalRect,1,20);
  12881.         EraseOval(OvalRect);             {erase oval to white}
  12882.  
  12883.         RGBForeColor(myGreen);           {draw the final oval in green}
  12884.         InsetRect(OvalRect,40,1);
  12885.         PaintOval(OvalRect);
  12886.         RGBForeColor(myBlack); 
  12887.     END;    { DrawIt }
  12888.  
  12889. Since you are done drawing, you need to set thePort and theGDevice back to their
  12890. former values, and then you can draw the image on the screen by calling 
  12891. _CopyBits to copy the bits from the portPix of the off-screen pixel map to the portPix
  12892. of MyCWindow.
  12893.  
  12894.     SetPort(MyCWindow);
  12895.     SetGDevice(oldDevice);
  12896.  
  12897.     destRect := bRect;
  12898.     OffSetRect(destRect,OffLeft,OffTop);    {adjust for coordinates}
  12899.     CopyBits(BitMapPtr(MyCGrafPtr^.portPixMap^)^, MyCWindow^.portBits,
  12900.         bRect, destRect, 0, NIL);
  12901.  
  12902. Finally, you clean up after yourself by closing the CGrafPort you created, freeing
  12903. the space you reserved for the pixel image of the off-screen pixel map, and disposing
  12904. of the color table you allocated.
  12905.  
  12906.     CloseCPort(myCGrafPtr);             {Close our port}
  12907.     DisposPtr(MyBits);                  {clean up}
  12908.     DisposHandle(Handle(ourCMHandle));  {get rid of color table we cloned}
  12909.  
  12910. MPW C
  12911.  
  12912. You should note that most of the Pascal comments also apply to this C code, so if you
  12913. are not sure what the C code is doing, try referring to the equivalent Pascal code
  12914. and comments to gain a better understanding.
  12915.  
  12916. /* Define constants for the Off-Screen Rect */
  12917. #define    OffLeft     30
  12918. #define    OffTop      30
  12919. #define    OffBottom   250
  12920. #define    OffRight    400
  12921.  
  12922. /* typedef BitMapPtr for use during CopyBits operation */
  12923. typedef    BitMap    *BitMapPtr;
  12924.  
  12925. long          offRowBytes, sizeOfOff;
  12926. Ptr           myBits;
  12927. Rect          destRect, globRect, bRect;
  12928. int           theDepth, i, err;
  12929. CGrafPort     myCGrafPort;
  12930. CGrafPtr      myCGrafPtr;
  12931. CTabHandle    ourCMHandle;
  12932. GDHandle      theMaxDevice, oldDevice;
  12933. Point         tempP;
  12934.  
  12935. Create a color window on screen.  In MPW C, myWindow is declared as a WindowPtr, not
  12936. a CWindowPtr, which is contrary to the way Inside Macintosh, Volume V documents it.
  12937.  
  12938.     myWindow = GetNewCWindow(SomeID,nil,(WindowPtr) -1);
  12939.  
  12940.     /* set to this port for the localToGlobals that follow */
  12941.     SetPort((WindowPtr) myWindow);
  12942.  
  12943.     SetRect(&bRect,OffLeft,OffTop,OffRight,OffBottom);
  12944.     if (!SectRect(&(*myWindow).portRect,&bRect,&globRect))
  12945.         ExitToShell();            /*nothing to do, clean up and EXIT*/
  12946.  
  12947. Since MPW does not have topLeft or botRight elements for Rect structures, you need to
  12948. set the tempPoint, call _LocalToGlobal, then reset globRect.
  12949.  
  12950.     tempP.v = globRect.top;
  12951.     tempP.h = globRect.left;
  12952.     LocalToGlobal(&tempP);
  12953.     globRect.top = tempP.v;
  12954.     globRect.left = tempP.h;
  12955.  
  12956.     tempP.v = globRect.bottom;
  12957.     tempP.h = globRect.right;
  12958.     LocalToGlobal(&tempP); 
  12959.     globRect.bottom = tempP.v;
  12960.     globRect.right = tempP.h;
  12961.  
  12962.     theMaxDevice = GetMaxDevice(&globRect);    /*get the maxDevice*/
  12963.  
  12964.     oldDevice = GetGDevice();                  /*save theGDevice so we can
  12965.                                                  restore it later*/
  12966.     SetGDevice(theMaxDevice);                  /*Set to the maxdevice*/
  12967.  
  12968. Now you can set up the off-screen pixel map.
  12969.  
  12970.     myCGrafPtr = &myCGrafPort;                 /*initialize this guy*/
  12971.     OpenCPort(myCGrafPtr);                     /*open a new color port,
  12972.                                                  this calls InitCPort*/
  12973.     theDepth = (**(*myCGrafPtr).portPixMap).pixelSize;
  12974.  
  12975.     /* Bitshift and adjust for local coordinates */
  12976.     offRowBytes = (((theDepth * (OffRight - OffLeft)) + 15) >> 4) << 1;
  12977.     sizeOfOff = (long) (OffBottom - OffTop) * offRowBytes;
  12978.     OffsetRect(&bRect, - OffLeft, - OffTop);
  12979.  
  12980.     myBits = NewPtr(sizeOfOff);
  12981.  
  12982.     /* Remember to be a PixMap */
  12983.     (**(*myCGrafPtr).portPixMap).baseAddr = myBits;
  12984.     (**(*myCGrafPtr).portPixMap).rowBytes = offRowBytes + 0x8000;
  12985.     (**(*myCGrafPtr).portPixMap).bounds = bRect;
  12986.  
  12987.     ourCMHandle = (**(**theMaxDevice).gdPMap).pmTable;
  12988.     err = HandToHand(&((Handle) ourCMHandle));
  12989.     /* Real programs do error checking here */
  12990.     for (i = 0; i <= (**ourCMHandle ).ctSize; ++i)
  12991.         (**ourCMHandle ).ctTable[i].value = i;
  12992.     (**ourCMHandle ).ctFlags &= 0x7fff;
  12993.     (**ourCMHandle ).ctSeed = GetCTSeed();
  12994.     /* This code is necessary for converting GDevice cluts to Pixmap cluts */
  12995.  
  12996.     (**(*myCGrafPtr).portPixMap).pmTable = ourCMHandle;
  12997.     SetPort((GrafPtr) myCGrafPtr);
  12998.  
  12999.  
  13000.     /************************************************/
  13001.     /*                                              */
  13002.     /*     function for setting the wanted color    */
  13003.     /*                                              */
  13004.     /************************************************/
  13005.     RGBColor FillInColor(r,g,b)
  13006.     int    r,g,b;
  13007.  
  13008.     {    /*FillInColor*/
  13009.  
  13010.         RGBColor    theColor;
  13011.  
  13012.         theColor.red = r;
  13013.         theColor.green = g;
  13014.         theColor.blue = b;
  13015.         return (theColor);
  13016.     }
  13017.  
  13018.     /******************************************************/
  13019.     /*                                                    */
  13020.     /*  Drawing routine which makes the background blue   */
  13021.     /* then draws a red oval, white oval, and green oval  */
  13022.     /* After drawing to the off-screen it CopyBits to the */
  13023.     /*                      screen                        */
  13024.     /*                                                    */
  13025.     /******************************************************/
  13026.     void DrawIt()
  13027.  
  13028.     {
  13029.         Rect        OvalRect;
  13030.         RGBColor    myRed,myBlue,myWhite,myGreen,myBlack;
  13031.  
  13032.         myRed = FillInColor(-1,0,0);
  13033.         myBlue = FillInColor(0,0,-1);
  13034.         myGreen = FillInColor(0,-1,0);
  13035.         myWhite = FillInColor(-1,-1,-1);
  13036.         myBlack = FillInColor(0,0,0);
  13037.         PenMode(patCopy);
  13038.         RGBBackColor(&myBlue);
  13039.         EraseRect(&(*qd.thePort).portRect);
  13040.         RGBBackColor(&myWhite);
  13041.         RGBForeColor(&myRed);
  13042.         SetRect(&OvalRect,30,30,190,150);
  13043.         PaintOval(&OvalRect);
  13044.  
  13045.         InsetRect(&OvalRect,1,20);
  13046.         EraseOval(&OvalRect);
  13047.  
  13048.         RGBForeColor(&myGreen);
  13049.         InsetRect(&OvalRect,40,1);
  13050.         PaintOval(&OvalRect);
  13051.         RGBForeColor(&myBlack); 
  13052.  
  13053.         SetPort((WindowPtr) myWindow);
  13054.         SetGDevice(oldDevice);
  13055.  
  13056.         destRect = bRect;
  13057.         OffsetRect(&destRect,OffLeft,OffTop);
  13058.         CopyBits((BitMapPtr) *(*myCGrafPtr).portPixMap,
  13059.             &(*myWindow).portBits,&bRect, &destRect, 0, nil);
  13060.  
  13061.         return;
  13062.     }
  13063.  
  13064. Once again, you clean up after yourself.
  13065.  
  13066.     CloseCPort(myCGrafPtr);
  13067.     DisposPtr(myBits);
  13068.     DisposHandle((Handle) ourCMHandle);
  13069.  
  13070. Note:    For optimal performance, you want to make sure that the source and
  13071.          destination pixel maps are aligned.
  13072.  
  13073.  
  13074. Further Reference:
  13075. _______________________________________________________________________________
  13076.     •    Inside Macintosh, Volumes I-11 & IV-23, QuickDraw
  13077.     •    Inside Macintosh, Volume V-39, Color QuickDraw
  13078.     •    Technical Note #41, Drawing Into an Off-Screen Bitmap
  13079.     •    Technical Note #129, _SysEnvirons:  System 6.0 and Beyond
  13080.  
  13081.  
  13082. æKY 121
  13083. æC #121: Using the High-Level AppleTalk Routines
  13084.  
  13085. See also:     The AppleTalk Manager
  13086.               Inside AppleTalk
  13087.               AppleTalk Manager Update
  13088.  
  13089. Written by:    Fred A. Huxham     May 4, 1987
  13090. Updated:                          March 1, 1988
  13091. _______________________________________________________________________________
  13092.  
  13093. What you need to do in order to use high-level AppleTalk routines depends upon the
  13094. interfaces you are using. Some differences are outlined below.
  13095. _______________________________________________________________________________
  13096.  
  13097. MPW before 2.0
  13098.  
  13099. When calling the old high-level AppleTalk routines, many programmers get mysterious
  13100. “resource not found” errors (-192) from such seemingly harmless routines as MPPOpen.
  13101. The resource that is not being found is ‘atpl’, a resource that contains all the glue
  13102. code to the high-level routines. In order to use the high-level routines, your application
  13103. must have this resource in its resource fork. The ‘atpl’ resource is included in a
  13104. file called “AppleTalk” with any compilers that use this outdated version of the
  13105. AppleTalk interface.
  13106.  
  13107.  
  13108. MPW 2.0 and newer
  13109.  
  13110. A newer version of the alternate interfaces is available in MPW 2.0; it includes bug
  13111. fixes and increased Macintosh II compatibility. With this version of the interface,
  13112. the ‘atpl’ resource is no longer used. Glue code is now linked into your application.
  13113.  
  13114. This will be the final release of the current-style interface. It will be supported
  13115. for some time as the alternate interface. We have moved to a more straightforward and
  13116. simple preferred interface, which is also implemented in MPW 2.0 and newer, and is
  13117. described in the AppleTalk Manager chapter of Inside Macintosh vol. V. Developers are
  13118. free to continue to use the alternate interface, but in the long run it will be advantageous
  13119. to move to the preferred interface.
  13120.  
  13121.  
  13122. Third Party Compilers
  13123.  
  13124. Third party compilers use interfaces that are built from Apple’s MPW interfaces. Some
  13125. compilers may not have upgraded to the new interfaces yet. Contact the individual
  13126. compiler manufacturers for more information.
  13127.  
  13128.  
  13129.  
  13130. æKY 122
  13131. æC #122: Device-Independent Printing
  13132.  
  13133. See also:       The Printing Manager
  13134.  
  13135. Written by:     Ginger Jernigan      May 4, 1987
  13136. Updated:                             March 1, 1988
  13137. _______________________________________________________________________________
  13138.  
  13139. The Printing Manager was designed to give Macintosh applications a device- independent
  13140. method of printing, but we have provided device-dependent information, such as the
  13141. contents of the print record. Due to the large number of printer-type drivers becoming
  13142. available (even for non-printer devices) device independence is more necessary than
  13143. ever. What this means to you, as a developer, is that we will no longer be providing
  13144. (or supporting) information regarding the internal structure of the print record.
  13145.  
  13146.  
  13147. We realize that there are situations where the application may know the best method
  13148. for printing a particular document and may want to bypass our dialogs. Unfortunately,
  13149. using your own dialogs or not using the dialogs at all, requires setting the necessary
  13150. fields in the print record yourself. There are a number of problems:
  13151.  
  13152.  • Many of the fields in the print record are undocumented, and, as we change 
  13153.    the internal architecture of the Printing Manager to accommodate new devices,
  13154.  
  13155.    those undocumented fields are likely to change.
  13156.  
  13157.  • Each driver uses the private, and many of the public, fields in the print 
  13158.    record differently. The implications are that you would need intimate 
  13159.    knowledge of how each field is used by each available driver, and you would 
  13160.    have to set the fields in the record differently depending on the driver 
  13161.    chosen. As the number of available printer-type drivers increases, this can 
  13162.    become a cumbersome task.
  13163.  
  13164.  
  13165. Summary
  13166.  
  13167. To be compatible with future printer-like devices, it is essential that your application
  13168. print in a device-independent manner. Avoid testing undocumented fields, setting
  13169. fields in the print record directly and bypassing the existing print dialogs. Use the
  13170. Printing Manager dialogs, PrintDefault and PrValidate to set up the print record for
  13171. you.
  13172.  
  13173.  
  13174.  
  13175. æKY 123
  13176. æC #123: Bugs in LaserWriter ROMs
  13177.  
  13178. See also:     The Printing Manager
  13179.               PostScript Language Reference Manual, Adobe Systems
  13180.  
  13181. Written by:     Ginger Jernigan     May 4, 1987
  13182. Modified by:    Ginger Jernigan     July 1, 1987
  13183. Updated:                            March 1, 1988
  13184. _______________________________________________________________________________
  13185.  
  13186. These are LaserWriter bugs that your users may encounter when printing from any Macintosh
  13187. application. These are for your information; you cannot code around them. The bugs
  13188. described here occur in the 1.0 and 2.0 LaserWriter ROMs.
  13189. _______________________________________________________________________________
  13190.  
  13191. To determine which ROMs their LaserWriter contains, users can look at the test page
  13192. that the LaserWriter prints at start-up time. In addition to other information (detailed
  13193. in the LaserWriter user’s manual), the ROM version is shown at the bottom of the line
  13194. graph. The original LaserWriter contained version 1.0 ROMs. The currently shipping
  13195. LaserWriter and those upgraded to the LaserWriter Plus contain version 2.0 ROMs.
  13196.  
  13197. These are some of the problems we know of:
  13198.  
  13199. 1. If the level of paper in the paper tray is getting low, and the user prints 
  13200.    a document that will cause the tray to become empty, a PostScript error may 
  13201.    occur. This problem exists in both the 1.0 and 2.0 LaserWriter ROMs and will 
  13202.    not be fixed in the next ROM version.
  13203.  
  13204. 2. If a user prints more than 15 copies of a document, a timeout condition may 
  13205.    occur causing the print job to abort. With LaserShare, this problem can 
  13206.    occur with as few as 9 copies. This problem is a result of the LaserWriter 
  13207.    turning AppleTalk off while it is printing. It doesn’t send out any packets 
  13208.    to tell the world it’s still alive while it is printing, so the connection 
  13209.    times out after about 2 minutes. This problem exists in both the 1.0 and 2.0 
  13210.    LaserWriter ROMs and will not be fixed in the next ROM version.
  13211.  
  13212. 3. When printing a document that contains more than 10 patterns, users may 
  13213.    receive intermittent PostScript errors. This usually occurs when trying to 
  13214.    print a lot of patterns, and a bitmap image on the same page. The code for 
  13215.    imaging patterns allocates almost all of the available RAM for itself, so 
  13216.    when the bitmap imaging code tries to allocate space, and there isn’t enough 
  13217.    (and it doesn’t know how to reclaim memory from the previous operation), a 
  13218.    limitcheck error occurs. This problem exists in 2.0 LaserWriter ROMs. It 
  13219.    will be improved but not fixed in the next ROM version.
  13220.  
  13221. 4. If a user chooses US Letter or B5 paper and has a different sized tray in 
  13222.    the printer, and prints using manual feed, the LaserWriter will print 
  13223.    assuming that the paper being fed manually is the same size as that in the 
  13224.    tray. For example, if they have a US letter tray in the LaserWriter and 
  13225.    print a document formatted for B5 letter using manual feed, the image will 
  13226.    not be centered on the page. The printer assumes that the manually fed paper 
  13227.    is also US letter size and prints the image positioned accordingly, despite 
  13228.    the driver’s instructions. This is a bug in the Note operator in PostScript, 
  13229.    which the driver uses for specifying the US letter and B5 letter paper sizes.
  13230.  
  13231.    The workaround is to tell the user to put an B5 tray in the printer when 
  13232.    printing B5 manually. This problem exists in the 1.0 and 2.0 ROMs and will 
  13233.    not be fixed in the next ROM version. 
  13234.  
  13235.    By the way, an interesting, but annoying, occurance of this bug happens when 
  13236.    manually printing Legal sized documents with the 4.0 LaserWriter driver. 
  13237.    When the Larger Print Area option in the style dialog is deselected (which 
  13238.    is the default) the driver uses the Note operator to specify the page size. 
  13239.    When the user prints the document using manual feed, and has a US letter 
  13240.    tray in the printer, the image is shifted up on the page cutting off the top 
  13241.    of the image. If you tell the user to turn on the Larger Print Area option 
  13242.    in the style dialog, the driver specifies the page size using Legal instead 
  13243.    of Note and the image is printed properly.
  13244.  
  13245.  
  13246.  
  13247. æKY 124
  13248. æC #124: Using Low-Level Printing Calls With AppleTalk ImageWriters
  13249.  
  13250. See also:       The Printing Manager
  13251.  
  13252. Written by:     Ginger Jernigan          May 4, 1987
  13253. Update by:      Scott “ZZ” Zimmerman     Febuary ?, 1988
  13254. Updated:                                 March 1, 1988
  13255. _______________________________________________________________________________
  13256.  
  13257. When you use the low-level printer driver to print, you don’t get the benefits of the
  13258. error checking that is done when you use the high-level Printing Manager. So, if the
  13259. user prints to an AppleTalk ImageWriter (including an AppleTalk ImageWriter LQ) that
  13260. is busy printing another job, the driver doesn’t know whether the printer is busy,
  13261. offline, or disconnected.  Because of this, PrError will return (and PrintErr will
  13262. contain) abortErr.
  13263.  
  13264. Since there is no way to tell when you are printing to an AppleTalk ImageWriter, the
  13265. only workaround for this is to use high-level Printing Manager interface.
  13266.  
  13267.  
  13268. æKY 125
  13269. æC #125: The Effect of Spool-a-page/Print-a-page on Shared Printers
  13270.  
  13271. See also:      Printing Manager
  13272.                Technical Note #72 — Optimizing for the LaserWriter—Techniques
  13273.  
  13274. Written by:    Ginger Jernigan    May 4, 1987
  13275. Updated:                          March 1, 1988
  13276. _______________________________________________________________________________
  13277.  
  13278. This technical note discusses drawbacks of using the spool-a-page/ print-a-page method
  13279. of printing.
  13280. _______________________________________________________________________________
  13281.  
  13282. The “spool-a-page/print-a-page” method of printing prints each page of a document as
  13283. a separate job instead of calling PrPicFile to print the entire picture file. Many
  13284. applications adopted this method of printing to avoid running out of disk space while
  13285. the ImageWriter driver was spooling the document to disk. As long as you are printing
  13286. to a directly connected ImageWriter, you’re fine, but if you are printing to remote
  13287. or shared devices 
  13288. (like the AppleTalk ImageWriter and the LaserWriter), this method may create significant
  13289. problems for the user.
  13290.  
  13291. When a job is initiated by the application, the driver establishes a connection with
  13292. the printer via AppleTalk. When the job is completed, the driver closes the connection,
  13293. allowing another job the opportunity to print. If each page is a job in itself, then
  13294. the connection is closed and reopened between each page, allowing another application
  13295. to print between the pages of the document, which, as you might imagine, could present
  13296. a significant problem. If two people are printing to the same AppleTalk ImageWriter
  13297. at the same time and their applications use the “spool-a-page/print-a-page” method of
  13298. printing, the pages of each document will be interleaved at the printer.
  13299.  
  13300. Although there are good reasons for using this method of printing, it is only useful
  13301. for a directly connected printer. From a compatibility point of view, this method of
  13302. printing is built-in device dependence. Also, this method could create serious problems
  13303. for other types of remote devices. Therefore, we are recommending that applications
  13304. avoid using this method indiscriminately. You should check available disk space to
  13305. see how much room you have before you print. If there isn’t enough space for your
  13306. entire document, then print as much as you can (to minimize the interleaving) before
  13307. starting another job. Whenever possible, applications should use the print loop described
  13308. on page II-155 in The Printing Manager chapter of Inside Macintosh.
  13309.  
  13310.  
  13311.  
  13312. æKY 126
  13313. æC #126:    Sub(Launching) from a High-Level Language
  13314.  
  13315. Revised by:    Rich Collyer & Mark Johnson                           April 1989
  13316. Written by:    Rick Blair & Jim Friedlander                            May 1987
  13317.  
  13318. Note:    Developer Technical Support takes the view that launching and
  13319.          sublaunching are features which are best avoided for compatibility
  13320.          (and other) reasons, but we want to make sure that when it is
  13321.          absolutely necessary to implement it, it is done in the safest
  13322.          possible way.
  13323.  
  13324. This Technical Note discusses the “safest” method of calling _Launch from a high-level
  13325. language that supports inline assembly language with the option of launching or sublaunching
  13326. another application.
  13327. Changes since August 1988:  Incorporated Technical Note #52 on calling _Launch from a
  13328. high-level language, changed the example to offer a choice between launching or sublaunching,
  13329. added a discussion of the _Launch trap under MultiFinder, and updated the MPW C code
  13330. to include inline assembly language.
  13331. _______________________________________________________________________________
  13332.  
  13333. The Segment Loader chapter of Inside Macintosh II-53 states the following about the
  13334. _Launch trap:
  13335.  
  13336.         “The routines below are provided for advanced programmers;
  13337.         they can be called only from assembly language.”
  13338.  
  13339. While this statement is technically true, it is easy to call _Launch from any high-level
  13340. language which supports inline assembly code, and this Note provides examples of
  13341. calling _Launch in MPW Pascal and C.
  13342.  
  13343. Before calling _Launch, you need to declare the inline procedure, which takes a variable
  13344. of type pLaunchStruct as a parameter.  Since the compiler pushes a pointer to this
  13345. parameter on the stack, you need to include code to put this pointer into A0.  The
  13346. way to do this is with a MOVE.L (SP)+,A0 instruction, which is $205F in hexadecimal,
  13347. so the first word after INLINE is $205F.  This instruction sets up A0 to contain a
  13348. pointer to the filename and 4(A0) to contain the configuration parameter, so the last
  13349. part of the inline is the 
  13350. _Launch trap itself, which is $A9F2 in hexadecimal.  The configuration parameter,
  13351. which is normally zero, determines whether the application uses alternate screen and
  13352. sound buffers.  Since not all Macintosh models support these alternate buffers, you
  13353. should avoid using them unless you have a specific circumstance which requires them.
  13354.  
  13355. The Finder does a lot of hidden cleanup and other tasks without user knowledge; therefore,
  13356. it is best if you do not try to replace the Finder with a “mini” or try to launch
  13357. other programs and have them return to your application.  In the future, the Finder
  13358. may provide better integration for applications, and you will circumvent this if you
  13359. try to act in its place by sublaunching other programs.
  13360.  
  13361. If you have a situation where your application must launch another and have it return,
  13362. and where you are not worried about incompatibility with future System Software versions,
  13363. there is a “preferred” way of doing this which fits into the current system well. 
  13364. System file version 4.1 (or later) includes a mechanism for allowing a call to another
  13365. application; we term this call a “sublaunch.”  You can perform a sublaunch by adding
  13366. a set of simple extensions to the parameter block you pass to the _Launch trap.
  13367.  
  13368.  
  13369. _Launch and MultiFinder
  13370.  
  13371. Under MultiFinder, a sublaunch behaves differently than under the Finder.  The application
  13372. you sublaunch becomes the foreground application, and when the user quits that application,
  13373. the system returns control to the next frontmost layer, which will not necessarily be
  13374. your application.
  13375.  
  13376. If you set both high bits of LaunchFlags, which requests a sublaunch, your application
  13377. will continue to execute after the call to _Launch.  Under MultiFinder, the actual
  13378. launch (and suspend of your application) will not happen in the _Launch trap, but
  13379. rather after a call or more to _WaitNextEvent.
  13380.  
  13381. Under MultiFinder, _Launch currently returns an error if there is not enough memory
  13382. to launch the desired application, if it cannot locate the desired application, or if
  13383. the desired application is already open.  In the latter case, that application will
  13384. not be made active.  If you attempted to launch, MultiFinder will call _SysBeep, your
  13385. application will terminate, and control will given to the next frontmost layer.  If
  13386. you attempted to sublaunch, control will return to your application, and it is up to
  13387. you to report the error to the user.
  13388.  
  13389. Currently, _Launch returns an error in register D0 for a sublaunch, and you should
  13390. check it for errors (D0<0) after any attempts at sublaunching.  If D0>=0 then your
  13391. sublaunch was successful.
  13392.  
  13393. You should refer to the Programmer’s Guide to MultiFinder (APDA) and Macintosh Technical
  13394. Notes #180, MultiFinder Miscellanea and #205, MultiFinder Revisited:  The 6.0 System
  13395. Release, for further discussion of the _Launch trap under MultiFinder.)
  13396.  
  13397.  
  13398. Working Directories and Sublaunching With the Finder
  13399.  
  13400. Putting aside the compatibility issue for the moment, the only problem sublaunching
  13401. creates under the current system is one of Working Directory Control Blocks (WDCBs). 
  13402. Unless the application you are launching is at the root directory or on an MFS volume,
  13403. you must create a new WDCB and set it as the current directory when you launch the
  13404. application.
  13405.  
  13406. In the example which follows, the new working directory is opened (allocated) by
  13407. Standard File and its WDRefNum is returned in reply.vRefNum.  If you do not use Standard
  13408. File and cannot assume, for instance, that the application was in the blessed folder
  13409. or root directory, then you must open a new working directory explicitly via a call
  13410. to _OpenWD.  You should give the new WDCB a WDProcID of 'ERIK', so the Finder (or
  13411. another shell) would know to deallocate when it saw it was allocated by a “sublaunchee.”
  13412.  
  13413. Although the sublaunching process is recursive (i.e., programs which are sublaunched
  13414. may, in turn, sublaunch other programs), there is a limit of 40 on the number of
  13415. WDCBs which can be created.  With this limit, you could run out of available WDCBs
  13416. very quickly if many programs were playing the shell game or neglecting to deallocate
  13417. the WDCBs they had created.  Make sure you check for all errors after calling _PBOpenWD.
  13418.  A tMWDOErr (–121) means that all available WDCBs have been allocated, and if you
  13419. receive this error, you should alert the user that the sublaunch failed and continue
  13420. as appropriate.
  13421.  
  13422. Warning:    Although the example included in this Note covers sublaunching,
  13423.             Developer Technical Support strongly recommends that developers
  13424.             not use this feature of the _Launch trap.  This trap will change
  13425.             in the not-too-distant future, and when it does change,
  13426.             applications which perform sublaunching will break.  The only
  13427.             circumstance in which you could consider sublaunching is if you
  13428.             are implementing an integrated development system and are prepared
  13429.             to deal with the possibility of revising it every time Apple
  13430.             releases a new version of the System Software.
  13431.  
  13432.  
  13433. MPW Pascal
  13434.  
  13435. {It is assumed that the Signals are caught elsewhere; see Technical
  13436.  Note #88 for more information on the Signal mechanism}
  13437.  
  13438. {the extended parameter block to _Launch}
  13439. TYPE
  13440.    pLaunchStruct = ^LaunchStruct;
  13441.    LaunchStruct = RECORD
  13442.       pfName      : StringPtr;
  13443.       param       : INTEGER;
  13444.       LC          : PACKED ARRAY[0..1] OF CHAR; {extended parameters:}
  13445.       extBlockLen : LONGINT; {number of bytes in extension = 6}
  13446.       fFlags      : INTEGER; {Finder file info flags (see below)}
  13447.       launchFlags : LONGINT; {bit 31,30=1 for sublaunch, others reserved}
  13448.    END; {LaunchStruct}
  13449.  
  13450. FUNCTION LaunchIt(pLaunch: pLaunchStruct): OSErr; {< 0 means error}
  13451.    INLINE $205F, $A9F2, $3E80; 
  13452. { pops pointer into A0, calls Launch, pops D0 error code into result:
  13453.   MOVE.L  (A7)+,A0
  13454.   _Launch
  13455.   MOVE.W  D0,(A7)  ; since it MAY return }
  13456.  
  13457. PROCEDURE DoLaunch(subLaunch: BOOLEAN);  {Sublaunch if true and launch if false}
  13458.  
  13459.     VAR
  13460.        myLaunch     : LaunchStruct;   {launch structure}
  13461.        where        : Point;          {where to display dialog}
  13462.        reply        : SFReply;        {reply record}
  13463.        myFileTypes  : SFTypeList;     {we only want APPLs}
  13464.        numFileTypes : INTEGER;
  13465.        myPB         : CInfoPBRec;
  13466.        dirNameStr   : str255;
  13467.  
  13468.     BEGIN
  13469.        where.h := 20;
  13470.        where.v := 20;
  13471.        numFileTypes:= 1;
  13472.        myFileTypes[0]:= 'APPL';       {applications only!}
  13473.     {Let the user choose the file to Launch}
  13474.        SFGetFile(where, '', NIL, numFileTypes, myFileTypes, NIL, reply);
  13475.  
  13476.  
  13477.        IF reply.good THEN BEGIN
  13478.           dirNameStr:= reply.fName;   {initialize to file selected}
  13479.  
  13480.     {Get the Finder flags}
  13481.           WITH myPB DO BEGIN
  13482.               ioNamePtr:= @dirNameStr;
  13483.               ioVRefNum:= reply.vRefNum;
  13484.               ioFDirIndex:= 0;
  13485.               ioDirID:= 0;
  13486.           END; {WITH}
  13487.           Signal(PBGetCatInfo(@MyPB,FALSE));
  13488.     {Set the current volume to where the target application is}
  13489.           Signal(SetVol(NIL, reply.vRefNum));
  13490.  
  13491.     {Set up the launch parameters}
  13492.           WITH myLaunch DO BEGIN
  13493.              pfName := @reply.fName;   {pointer to our fileName}
  13494.              param := 0;       {we don't want alternate screen or sound buffers}
  13495.              LC := 'LC';       {here to tell Launch that there is non-junk next}
  13496.              extBlockLen := 6; {length of param. block past this long word}
  13497.              {copy flags; set bit 6 of low byte to 1 for RO access:}
  13498.              fFlags := myPB.ioFlFndrInfo.fdFlags;  {from GetCatInfo}
  13499.  
  13500.     {Test subLaunch and set LaunchFlags accordingly}
  13501.           IF subLaunch THEN
  13502.              LaunchFlags := $C0000000      {set BOTH high bits for a sublaunch}
  13503.           ELSE
  13504.              LaunchFlags := $00000000;     {Just launch then quit}
  13505.           END; {WITH}
  13506.  
  13507.      {launch; you might want to put up a dialog which explains that
  13508.       the selected application couldn't be launched for some reason.}
  13509.           Signal(LaunchIt(@myLaunch));
  13510.        END; {IF reply.good}
  13511.  
  13512.     END; {DoLaunch}
  13513.  
  13514.  
  13515. MPW C
  13516.  
  13517. typedef struct LaunchStruct {
  13518.     char         *pfName;        /* pointer to the name of launchee */
  13519.     short int    param;
  13520.     char         LC[2];          /*extended parameters:*/
  13521.     long int     extBlockLen;    /*number of bytes in extension == 6*/
  13522.     short int    fFlags;         /*Finder file info flags (see below)*/
  13523.     long int     launchFlags;    /*bit 31,30==1 for sublaunch, others reserved*/
  13524. } *pLaunchStruct;
  13525.  
  13526. pascal OSErr LaunchIt( pLaunchStruct pLnch) /* < 0 means error */
  13527.     = {0x205F, 0xA9F2, 0x3E80};
  13528.  
  13529. /* pops pointer into A0, calls Launch, pops D0 error code into result:
  13530.       MOVE.L  (A7)+,A0
  13531.       _Launch
  13532.       MOVE.W  D0,(A7)  ; since it MAY return */
  13533.  
  13534. OSErr DoLaunch(subLaunch)
  13535.     Boolean        subLaunch;       /* Sublaunch if true and launch if false*/
  13536. {  /* DoLaunch */
  13537.     struct LaunchStruct    myLaunch;
  13538.     Point            where;         /*where to display dialog*/
  13539.     SFReply          reply;         /*reply record*/
  13540.     SFTypeList       myFileTypes;   /* we only want APPLs */
  13541.     short int        numFileTypes=1;
  13542.     HFileInfo        myPB;
  13543.     char             *dirNameStr;
  13544.     OSErr            err;
  13545.  
  13546.     where.h = 80;
  13547.     where.v = 90;
  13548.     myFileTypes[0] = 'APPL';        /* we only want APPLs */ 
  13549.     /*Let the user choose the file to Launch*/
  13550.     SFGetFile(where, "", nil, numFileTypes, myFileTypes, nil, &reply);
  13551.  
  13552.     if (reply.good)
  13553.     {
  13554.         dirNameStr = &reply.fName;  /*initialize to file selected*/
  13555.  
  13556.     /*Get the Finder flags*/
  13557.         myPB.ioNamePtr= dirNameStr;
  13558.         myPB.ioVRefNum= reply.vRefNum;
  13559.         myPB.ioFDirIndex= 0;
  13560.         myPB.ioDirID = 0;
  13561.         err = PBGetCatInfo((CInfoPBPtr) &myPB,false);
  13562.         if (err != noErr)
  13563.             return err;
  13564.  
  13565.     /*Set the current volume to where the target application is*/
  13566.         err = SetVol(nil, reply.vRefNum);
  13567.         if (err != noErr)
  13568.             return err;
  13569.  
  13570.     /*Set up the launch parameters*/
  13571.         myLaunch.pfName = &reply.fName;    /*pointer to our fileName*/
  13572.         myLaunch.param = 0;                /*we don't want alternate screen
  13573.                                              or sound buffers*/
  13574.     /*set up LC so as to tell Launch that there is non-junk next*/
  13575.         myLaunch.LC[0] = 'L'; myLaunch.LC[1] = 'C';
  13576.         myLaunch.extBlockLen = 6;          /*length of param. block past
  13577.                                              this long word*/
  13578.     /*copy flags; set bit 6 of low byte to 1 for RO access:*/
  13579.         myLaunch.fFlags = myPB.ioFlFndrInfo.fdFlags;    /*from _GetCatInfo*/
  13580.     /* Test subLaunch and set launchFlags accordingly    */
  13581.     if ( subLaunch )
  13582.         myLaunch.launchFlags = 0xC0000000; /*set BOTH hi bits for a sublaunch*/
  13583.     else
  13584.         myLaunch.launchFlags = 0x00000000; /* Just launch then quit          */
  13585.  
  13586.         err = LaunchIt(&myLaunch);         /* call _Launch                   */
  13587.         if (err < 0)
  13588.         {
  13589.         /* the launch failed, so put up an alert to inform the user */
  13590.             LaunchFailed();
  13591.             return err;
  13592.         }
  13593.         else
  13594.             return noErr;
  13595.     } /*if reply.good*/
  13596. } /*DoLaunch*/
  13597.  
  13598.  
  13599. Further Reference:
  13600. _______________________________________________________________________________
  13601.     •    Inside Macintosh, Volumes I-12, II-53, & IV-83, The Segment Loader
  13602.     •    Programmer’s Guide to MultiFinder (APDA)
  13603.     •    Technical Note #129, _SysEnvirons:  System 6.0 and Beyond
  13604.     •    Technical Note #180, MultiFinder Miscellanea
  13605.     •    Technical Note #205, MultiFinder Revisited:  The 6.0 System Release
  13606.  
  13607.  
  13608. æKY 127
  13609. æC #127: TextEdit EOL Ambiguity
  13610.  
  13611. See also:       TextEdit
  13612.  
  13613. Written by:     Rick Blair     May 4, 1987
  13614. Updated:                       March 1, 1988
  13615. _______________________________________________________________________________
  13616.  
  13617. TESetSelect may be used to position the insertion point at the end of a line. There
  13618. is an ambiguity, though; should the insertion point appear at the end of the preceding
  13619. line or the start of the following one? It is possible to determine what will happen,
  13620. as you are about to see.
  13621. _______________________________________________________________________________
  13622.  
  13623. There is an internal flag used by TextEdit to determine where the insertion point at
  13624. the end of a line appears. This flag is part of the clikStuff field in the TERec. It
  13625. is there mainly for the use of TEClick, but it is also used by TESetSelect (although
  13626. it defaults to the right side of the previous line).
  13627.  
  13628. The following code can be used to force the insertion point to appear at the left of
  13629. the following line when it is positioned at the end of a line; in MPW Pascal:
  13630.  
  13631.     TEDeactivate(tH);
  13632.     tH^^.clikStuff := 255;                      {position caret on left}
  13633.     TESetSelect(eolcharpos, eolcharpos, tH);    {ambiguous point}
  13634.     TEActivate(tH);
  13635.  
  13636. In MPW C:
  13637.  
  13638.     TEDeactivate(tH);
  13639.     (**tH).clikStuff = 255;                     /*position caret on left*/
  13640.     TESetSelect(eolcharpos, eolcharpos, tH);    /*ambiguous point*/
  13641.     TEActivate(tH);
  13642.  
  13643. If you want to ensure that the caret is on the right side (to which it normally defaults)
  13644. then substitute a zero for the 255.
  13645.  
  13646.  
  13647.  
  13648. æKY 128
  13649. æC #128: PrGeneral
  13650.  
  13651. See also:     The Printing Manager
  13652.               Technical Note #118 — How to Check and Handle Printing Errors
  13653.  
  13654. Written by:    Ginger Jernigan     May 4, 1987
  13655. Updated:                           March 1, 1988
  13656. _______________________________________________________________________________
  13657.  
  13658. The Printing Manager architecture has been expanded to include a new procedure called
  13659. PrGeneral. The features described here are advanced, special-purpose features, intended
  13660. to solve specific problems for those applications that need them. The calls to determine
  13661. printer resolution introduce a good deal of complexity into the application’s code,
  13662. and should be used only when necessary.
  13663. _______________________________________________________________________________
  13664.  
  13665. Version 2.5 (and later) of the ImageWriter driver and version 4.0 (and later) of the
  13666. LaserWriter driver implement a generic Printing Manager procedure called PrGeneral.
  13667. This procedure allows the Print Manager to expand in functionality, by allowing printer
  13668. drivers to implement various new functions. The Pascal declaration of PrGeneral is:
  13669.  
  13670.     PROCEDURE PrGeneral (pData: Ptr);
  13671.  
  13672. The pData parameter is a pointer to a data block. The structure of the data block is
  13673. declared as follows:
  13674.  
  13675.    TGnlData = RECORD {1st 8 bytes are common for all PrGeneral calls)
  13676.       iOpCode   : INTEGER;  {input}
  13677.       iError    : INTEGER;  {output}
  13678.       lReserved : LONGINT;  {reserved for future use}
  13679.       {more fields here, depending on particular call}
  13680.    END;
  13681.  
  13682. The first field is a 2-byte opcode, iOpCode, which acts like a routine selector. The
  13683. currently available opcodes are described below.
  13684.  
  13685. The second field is the error result, iError, which is returned by the print code.
  13686. This error only reflects error conditions that occur during the PrGeneral call. For
  13687. example, if you use an opcode that isn’t implemented in a particular printer driver
  13688. then you will get a OpNotImpl error.
  13689.  
  13690. Here are the errors currently defined:
  13691.  
  13692.    CONST    
  13693.       noErr = 0;      {everything’s hunky}
  13694.       NoSuchRsl = 1;  {the resolution you chose isn’t available}
  13695.       OpNotImpl = 2;  {the driver doesn’t support this opcode}
  13696.  
  13697. After calling PrGeneral you should always check PrError. If noErr is returned, then
  13698. you can proceed. If ResNotFound is returned, then the current printer driver doesn’t
  13699. support PrGeneral and you should proceed appropriately. See Technical Note #118 for
  13700. details on checking errors returned by the Printing Manager. 
  13701.  
  13702. IError is followed by a four byte reserved field (that means don’t use it). The contents
  13703. of the rest of the data block depends on the opcode that the application uses. There
  13704. are currently five opcodes used by the ImageWriter and LaserWriter drivers.
  13705.  
  13706.  
  13707. The Opcodes
  13708.  
  13709. Initially, the following calls are implemented via PrGeneral:
  13710.  
  13711.    • GetRslData (get resolution data): iOpCode = 4
  13712.    • SetRsl (set resolution): iOpCode = 5
  13713.    • DraftBits (bitmaps in draft mode): iOpCode = 6
  13714.    • noDraftBits (no bitmaps in draft mode): iOpCode = 7
  13715.    • GetRotn (get rotation): iOpCode = 8
  13716.  
  13717. The GetRslData and SetRsl allow the application to find out what physical resolutions
  13718. the printer supports, and then specify a supported resolution. DraftBits and noDraftBits
  13719. invoke a new feature of the ImageWriter, allowing bitmaps (imaged via CopyBits) to be
  13720. printed in draft mode. GetRotn lets an application know whether landscape has been
  13721. selected. Below is a detailed description of how each routine works.
  13722.  
  13723.  
  13724. The GetRslData Call
  13725.  
  13726. GetRslData (iOpCode = 4) returns a record that lets the application know what resolutions
  13727. are supported by the current printer. The application can then use SetRsl (description
  13728. follows) to tell the printer driver which one it will use. This is the format of the
  13729. input data block for the GetRslData call:
  13730.  
  13731.     TRslRg = RECORD   {used in TGetRslBlk}
  13732.         iMin, iMax:   Integer; {0 if printer only supports discrete resolutions}
  13733.     END;
  13734.  
  13735.     TRslRec = RECORD   {used in TGetRslBlk}
  13736.         iXRsl, iYRsl: Integer; {a discrete, physical resolution}
  13737.     END;
  13738.  
  13739.     TGetRslBlk = RECORD       {data block for GetRslData call}
  13740.         iOpCode:    Integer;  {input; = getRslDataOp}
  13741.         iError:     Integer;  {output}
  13742.         lReserved:  LongInt;  {reserved for future use}
  13743.         iRgType:    Integer;  {output; version number}
  13744.         XRslRg:     TRslRg;   {output; range of X resolutions}
  13745.         YRslRg:     TRslRg;   {output; range of Y resolutions}
  13746.         iRslRecCnt: Integer;  {output; how many RslRecs follow}
  13747.         rgRslRec:   ARRAY[1..27] OF TRslRec;  {output; number filled depends on
  13748.                                                printer type}
  13749.     END;
  13750.     
  13751. The iRgType field is much like a version number; it determines the interpretation of
  13752. the data that follows. At present, a iRgType value of 1 applies both to the LaserWriter
  13753. and to the ImageWriter.
  13754.  
  13755. For variable-resolution printers like the LaserWriter, the resolution range fields
  13756. XRslRg and YRslRg express the ranges of values to which the X and Y resolutions can
  13757. be set. For discrete-resolution printers like the ImageWriter, the values in the
  13758. resolution range fields are zero.
  13759.  
  13760. Note: In general, X and Y in these records are the horizontal and vertical directions
  13761. of the printer, not the document! In landscape orientation, X is horizontal on the
  13762. printer but vertical on the document.
  13763.  
  13764. After the resolution range information there is a word which gives the number of
  13765. resolution records that contain information. These records indicate the physical
  13766. resolutions at which the printer can actually print dots. Each resolution record
  13767. gives an X value and a Y value.
  13768.  
  13769. When you call PrGeneral you pass in a data block that looks like this:
  13770.  
  13771. •••Click on the Illustration button below, and refer to Figure 1.••• 
  13772.  
  13773. Below is the data block returned for the LaserWriter:
  13774.  
  13775. •••Click on the Illustration button below, and refer to Figure 2.••• 
  13776.  
  13777. Note that all the resolution range numbers happen to be the same for this printer.
  13778. There is only one resolution record, which gives the physical X and Y resolutions of
  13779. the printer (300x300).
  13780.  
  13781. Below is the data block returned for the ImageWriter.
  13782.  
  13783. •••Click on the Illustration button below, and refer to Figure 3.••• 
  13784.  
  13785. All the resolution range values are zero, because only discrete resolutions can be
  13786. specified for this printer. There are four resolution records giving these discrete
  13787. physical resolutions.
  13788.  
  13789. Note that GetRslData always returns the same information for a particular printer
  13790. type—it is not dependent on what the user does or on printer configuration information.
  13791.  
  13792.  
  13793. The SetRsl Call
  13794.  
  13795. SetRsl (iOpCode = 5) is used to specify the desired imaging resolution, after using
  13796. GetRslData to determine a workable pair of values. Below is the format of the data
  13797. block:
  13798.  
  13799.    TSetRslBlk =   RECORD    {data block for SetRsl call}
  13800.        iOpCode:   Integer;  {input; = setRslOp}
  13801.        iError:    Integer;  {output}
  13802.        lReserved: LongInt;  {reserved for future use}
  13803.        hPrint:    THPrint;  {input; handle to a valid print record}
  13804.        iXRsl:     Integer;  {input; desired X resolution}
  13805.        iYRsl:     Integer;  {input; desired Y resolution}
  13806.    END;
  13807.  
  13808. hPrint should be the handle of a print record that has previously been passed to
  13809. PrValidate. If the call executes successfully, the print record is updated with the
  13810. new resolution; the data block comes back with 0 for the error and is otherwise unchanged.
  13811.  
  13812. However, if the desired resolution is not supported, the error is set to noSuchRsl
  13813. and the resolution fields are set to the printer’s default resolution
  13814.  
  13815. Note that you can undo the effect of a previous call to SetRsl by making another call
  13816. that specifies an unsupported resolution (such as 0x0), forcing the default resolution.
  13817.  
  13818.  
  13819. The DraftBits Call
  13820.  
  13821. DraftBits (iOpCode = 6) is implemented on both the ImageWriter and the LaserWriter.
  13822. (On the LaserWriter it does nothing, since the LaserWriter is always in draft mode
  13823. and can always print bitmaps.) Below is the format of the data block:
  13824.  
  13825.    TDftBitsBlk =   RECORD    {data block for DraftBits and NoDraftBits calls}
  13826.        iOpCode:    Integer;  {input; = draftBitsOp or noDraftBitsOp}
  13827.        iError:     Integer;  {output}
  13828.        lReserved:  LongInt;  {reserved for future use}
  13829.        hPrint:     THPrint;  {input; handle to a valid print record}
  13830.    END;
  13831.  
  13832. hPrint should be the handle of a print record that has previously been passed to
  13833. PrValidate.
  13834.  
  13835. This call forces draft-mode (i.e., immediate) printing, and will allow bitmaps to be
  13836. printed via CopyBits calls. The virtue of this is that you avoid spooling large masses
  13837. of bitmap data onto the disk, and you also get better performance.
  13838.  
  13839. The following restrictions apply:
  13840.  
  13841.  • This call should be made before bringing up the print dialogs because it 
  13842.    affects their appearance. On the ImageWriter, calling DraftBits disables the 
  13843.    landscape icon in the Style dialog, and the Best, Faster, and Draft buttons 
  13844.    in the Job dialog.
  13845.  
  13846.  • If the printer does not support draft mode, already prints bitmaps in draft 
  13847.    mode, or does not print bitmaps at all, this call does nothing.
  13848.  
  13849.  • Only text and bitmaps can be printed.
  13850.  
  13851.  • As in the normal draft mode, landscape format is not allowed.
  13852.  
  13853.  • Everything on the page must be strictly Y-sorted, i.e. no reverse paper 
  13854.    motion between one string or bitmap and the next. Note that this means you 
  13855.    can’t have two or more objects (text or bitmaps) side by side; the top 
  13856.    boundary of each object must be no higher than the bottom of the preceding 
  13857.    object.
  13858.  
  13859. The last restriction is important. If you violate it, you will not like the results.
  13860. But note that if you want two or more bitmaps side by side, you can combine them into
  13861. one before calling CopyBits to print the result. Similarly, if you are just printing
  13862. bitmaps you can rotate them yourself to achieve landscape printing.
  13863.  
  13864.  
  13865. The NoDraftBits Call
  13866.  
  13867. NoDraftBits (iOpCode = 7) is implemented on both the ImageWriter and the LaserWriter.
  13868. (On the LaserWriter it does nothing, since the LaserWriter is always in draft mode
  13869. and can always print bitmaps.) The format of the data block is the same as that for
  13870. the DraftBits call.
  13871.  
  13872. This call cancels the effect of any preceding DraftBits call. If there was no preceding
  13873. DraftBits call, or the printer does not support draft-mode printing anyway, this call
  13874. does nothing.
  13875.  
  13876.  
  13877. The GetRotn Call
  13878.  
  13879. GetRotn (iOpCode = 8) is implemented on the ImageWriter and LaserWriter. Here is the
  13880. format of the data block:
  13881.  
  13882.    TGetRotnBlk =   RECORD      {data block for GetRotn call}
  13883.        iOpCode:    Integer;    {input; = getRotnOp}
  13884.        iError:     Integer;    {output}
  13885.        lReserved:  LongInt;    {reserved for future use}
  13886.        hPrint:     THPrint;    {input; handle to a valid print record}
  13887.        fLandscape: Boolean;    {output; Boolean flag}
  13888.        bXtra:      SignedByte; {reserved}
  13889.    END;
  13890.  
  13891. hPrint should be the handle to a print record that has previously been passed to
  13892. PrValidate.
  13893.  
  13894. If landscape orientation is selected in the print record, then fLandscape is true.
  13895.  
  13896.  
  13897. How To Use The PrGeneral Opcodes
  13898.  
  13899. The SetRsl and DraftBits calls may require the print code to suppress certain options
  13900. in the Style and/or Job dialogs, therefore they should always be called before any
  13901. call to the Style or Job dialogs. An application might use these calls as follows:
  13902.  
  13903.  • Get a new print record by calling PrintDefault, or take an existing one from 
  13904.    a document and call PrValidate on it.
  13905.  
  13906.  • Call GetRslData to find out what the printer is capable of, and decide what 
  13907.    resolution to use. Check PrError to be sure the PrGeneral call is supported 
  13908.    on this version of the print code; if the error is ResNotFound, you have 
  13909.    older print code and must print accordingly. But if the PrError return is 0, 
  13910.    proceed:
  13911.  
  13912.  • Call SetRsl with the print record and the desired resolution if you wish.
  13913.  
  13914.  • Call DraftBits to invoke the printing of bitmaps in draft mode if you wish.
  13915.  
  13916. Note that if you call either SetRsl or DraftBits, you should do so before the user
  13917. sees either of the printing dialogs.
  13918.  
  13919.  
  13920.  
  13921. æKY 129
  13922. æC #129:    _SysEnvirons:  System 6.0 and Beyond
  13923.  
  13924. Revised by:    Craig Prouse, Guillermo Ortiz & Dave Radcliffe        April 1990
  13925. Written by:    Jim Friedlander                                         May 1987
  13926.  
  13927. This Technical Note discusses changes and enhancements in the _SysEnvirons call
  13928. in System Software 6.0 and later. Changes since October 1989:  Added a
  13929. machineType constant for the Macintosh IIfx and emphasized that the high-level
  13930. _SysEnvirons documentation in Inside Macintosh, Volume V is incorrect.
  13931. _______________________________________________________________________________
  13932.  
  13933.  
  13934. _SysEnvirons and New Machines
  13935.  
  13936. _SysEnvirons is the standard way to determine the features available on a given
  13937. machine, and its main characteristic is that it continually evolves to provide
  13938. the necessary information as new machines and System Software appear.  As 
  13939. originally conceived, _SysEnvirons would check the versionRequested parameter
  13940. to determine what level of information you were prepared to handle, but this
  13941. technique means updating _SysEnvirons for every new hardware product Apple produces.
  13942. With System Software 6.0, _SysEnvirons introduced version 2 of environsVersion to
  13943. provide information about new hardware as we introduce it; this new version
  13944. returns the same SysEnvRec as version 1.
  13945.  
  13946. Beginning with System Software 6.0.1, Apple only releases a new version of _SysEnvirons
  13947. when engineering make changes to its structure (i.e., when they add new fields to
  13948. SysEnvRec); all existing versions return accurate information about the machine
  13949. environment even if part of that information was not originally defined for the
  13950. version you request.  For example, if you call
  13951. _SysEnvirons with versionRequested = 1 on a Macintosh IIfx, it returns a machineType
  13952. of envMacIIfx even though this machine type originally was not defined for version 1
  13953. of the call.
  13954.  
  13955. You should use version 2 of _SysEnvirons until Apple releases a newer version.  MPW
  13956. 3.0 defines a constant curSysEnvVers, which can be used to minimize the need for
  13957. source code revisions when _SysEnvirons evolves.  Regardless of the version used,
  13958. however, your software should be prepared to handle unexpected values and should
  13959. not make assumptions about functionality based on current expectations.  For example,
  13960. if your software currently requires a Macintosh II, testing for machineType >= envMacII
  13961. may result in your software trying to run on a machine which does not support the
  13962. features it requires, so test for specific functionality (i.e., hasFPU, hasColorQD, etc.).
  13963.  
  13964. You should always check the environsVersion when returning from _SysEnvirons since
  13965. the glue always returns as much information as possible, with environsVersion indicating
  13966. the highest version available, even if the call returns an envSelTooBig (–5502) error.
  13967.  
  13968.  
  13969. Calling _SysEnvirons From a High-Level Language
  13970.  
  13971. Due to a documentation error in Inside Macintosh, Volume V, DTS still receives questions
  13972. about how to call _SysEnvirons properly from Pascal and C.  Inside Macintosh defines
  13973. the Pascal interface to _SysEnvirons as follows:
  13974.  
  13975.     FUNCTION SysEnvirons (versRequested: INTEGER;
  13976.                           VAR theWorld: SysEnvRecPtr) : OSErr;
  13977.  
  13978. Because theWorld is passed by reference (as a VAR parameter), it is not correct to
  13979. pass a SysEnvRecPtr in the second argument.  Pascal would then generate a pointer
  13980. to this pointer and pass a fake handle to the _SysEnvirons trap in A0.  (The
  13981. assembly-language information is essentially correct; _SysEnvirons really does
  13982. want a pointer to a SysEnvRec in A0.)  The correct Pascal interface to
  13983. _SysEnvirons is therefore:
  13984.  
  13985.     FUNCTION SysEnvirons (versionRequested: INTEGER;
  13986.                           VAR theWorld: SysEnvRec) : OSErr;
  13987.  
  13988. In this case, Pascal pushes a pointer to theWorld on the stack.  The Pascal
  13989. interface glue then pops this pointer off the stack directly into A0 and calls
  13990. _SysEnvirons.  Everything is copacetic.  C programmers should recognize their
  13991. corresponding interface:
  13992.  
  13993.     pascal OSErr SysEnvirons (short versionRequested, SysEnvRec *theWorld);
  13994.  
  13995. Inside Macintosh defines the type SysEnvPtr = ^SysEnvRec.  It also sometimes
  13996. refers to this type as SysEnvRecPtr.  The inconsistency is insignificant 
  13997. because in reality MPW does not define any such type, under either name;
  13998. therefore, it is never needed.
  13999.  
  14000. Inside Macintosh also states that “all of the Toolbox Managers must be 
  14001. initialized before calling SysEnvirons.”  This statement is not necessarily
  14002. true.  Startup documents (INITs), for instance, may wish to call _SysEnvirons
  14003. without initializing any of the Toolbox Managers.  Keep in mind that the
  14004. atDrvrVersNum field returns a zero result if the AppleTalk drivers are not
  14005. initialized.  The system version, machine type, processor type, and other
  14006. key data return normally.
  14007.  
  14008.  
  14009. New Constants
  14010.  
  14011. The following are new _SysEnvirons constants which are not documented in
  14012. Inside Macintosh; however, you should refer to Inside Macintosh, Volume V-1,
  14013. Compatibility Guidelines, for the rest of the story.
  14014.  
  14015.     machineType
  14016.     envMacIIx = 5              {Macintosh IIx}
  14017.     envMacIIcx = 6             {Macintosh IIcx}
  14018.     envSE30 = 7                {Macintosh SE/30}
  14019.     envPortable = 8            {Macintosh Portable}
  14020.     envMacIIci = 9             {Macintosh IIci}
  14021.     envMacIIfx = 11            {Macintosh IIfx}
  14022.     
  14023.     processor
  14024.     env68030 = 4               {MC68030 processor}
  14025.     
  14026.     keyBoardType
  14027.     envPortADBKbd = 6          {Portable Keyboard}
  14028.     envPortISOADBKbd = 7       {Portable Keyboard (ISO)}
  14029.     envStdISOADBKbd = 8        {Apple Standard Keyboard (ISO)}
  14030.     envExtISOADBKbd = 9        {Apple Extended Keyboard (ISO)}
  14031.  
  14032.  
  14033. Further Reference:
  14034. _______________________________________________________________________________
  14035.   •  Inside Macintosh, Volume V-1, Compatibility Guidelines
  14036.  
  14037.  
  14038. æKY 130
  14039. æC #130: Clearing ioCompletion
  14040.  
  14041. See also:      The File Manager
  14042.  
  14043. Written by:    Jim Friedlander    May 4, 1987
  14044. Updated:                          March 1, 1988
  14045. _______________________________________________________________________________
  14046.  
  14047. When making synchronous calls to the File Manager, it is not necessary to clear ioCompletion
  14048. field of the parameter block, since that is done for you.
  14049.  
  14050. Some earlier technotes explicitly cleared ioCompletion, with the knowledge that this
  14051. was unnecessary, to try to encourage developers to fill in all fields of parameter
  14052. blocks as indicated in Inside Macintosh. 
  14053.  
  14054. By the way, this is true of all parameter calls—you only have to set fields that are
  14055. explicitly required.
  14056.  
  14057.  
  14058. æKY 131
  14059. æC #131: TextEdit Bugs in System 4.2
  14060.  
  14061. Written by:     Chris Derossi    June 1, 1987
  14062. Updated:                         March 1, 1988
  14063. _______________________________________________________________________________
  14064.  
  14065. This note formerly described the known bugs with the version of Styled TextEdit that
  14066. was provided with System 4.1. Many of these bugs were fixed in System 4.2. This updated
  14067. Technical Note describes the remaining known problems.
  14068. _______________________________________________________________________________
  14069.  
  14070.  
  14071. TEStylInsert
  14072.  
  14073. Calling TEStylInsert while the TextEdit record is deactivated causes unpredictable
  14074. results, so make sure to only call TEStylInsert when the TextEdit record is active.
  14075.  
  14076.  
  14077. TESetStyle
  14078.  
  14079. When using the doFace mode with TESetStyle, the style that you pass as a parameter is
  14080. ORed into the style of the currently selected text. If you pass the empty set (no
  14081. styles) though, TESetStyle is supposed to remove all styles from the selected text.
  14082. But TESetStyle checks an entire word instead of just the high-order byte of the tsFace
  14083. field. The style information is contained completely in the high-order byte, and the
  14084. low-order byte may contain garbage.
  14085.  
  14086. If the low-order byte isn’t zero, TESetStyle thinks that the tsFace field isn’t empty,
  14087. so it goes ahead and ORs it with the selected text’s style. Since the actual style
  14088. portion of the tsFace field is zero, no change occurs with the text. If you want to
  14089. have TESetStyle remove all styles from the text, you can explicitly set the tsFace
  14090. field to zero like this:
  14091.  
  14092.     VAR
  14093.        myStyle  : TextStyle;
  14094.        anIntPtr : ^Integer;
  14095.  
  14096.     BEGIN
  14097.        ...
  14098.        anIntPtr := @myStyle.tsFace;
  14099.        anIntPtr^ := 0;
  14100.        TESetStyle(doFace, myStyle, TRUE, textH);
  14101.        ...
  14102.     END;
  14103.  
  14104.  
  14105. TEStylNew
  14106.  
  14107. The line heights array does not get initialized when TEStylNew is called. Because of
  14108. this, the caret is initially drawn in a random height. This is easily solved by calling
  14109. TECalText immediately after calling TEStylNew. Extra calls to TECalText don’t hurt
  14110. anything anyway, so this will be compatible with future Systems.
  14111.  
  14112. An extra character run is placed at the beginning of the text which corresponds to
  14113. the font, size, and style which were in the grafPort when TEStylNew was called. This
  14114. can cause the line height for the first line to be too large. To avoid this, call
  14115. TextSize with the desired text size before calling TEStylNew. If the text’s style
  14116. information cannot be determined in advance, then call TextSize with a small value
  14117. (like 9) before calling TEStylNew.
  14118.  
  14119.  
  14120. TEScroll
  14121.  
  14122. The bug documented in Technical Note #22 remains in the new TextEdit. TEScroll called
  14123. with zero for both vertical and horizontal displacements causes the insertion point
  14124. to disappear. The workaround is the same as before; check to make sure that dV and dH
  14125. are not both zero before calling TEScroll.
  14126.  
  14127.  
  14128. Growing TextEdit Record
  14129.  
  14130. TextEdit is supposed to dynamically grow and shrink the LineStarts array in the TERec
  14131. so that it has one entry per line. Instead, when lines are added, TextEdit expands
  14132. the array without first checking to see if it’s already big enough. In addition,
  14133. TextEdit never reduces the size of this array.
  14134.  
  14135. Because of this, the longer a particular TextEdit record is used, the larger it will
  14136. get. This can be particularly nasty in programs that use a single TERec for many
  14137. operations during the program’s execution. 
  14138.  
  14139.  
  14140. Restoring Saved TextEdit Records
  14141.  
  14142. Applications have used a technique for saving and restoring styled text which involves
  14143. saving the contents of all of the TextEdit record handles. When restoring, TEStylNew
  14144. is called and the TextEdit record’s handles are disposed. The saved handles are then
  14145. loaded and put into the TextEdit record. This technique should not be used for the
  14146. nullStyle handle in the style record.
  14147.  
  14148. Instead, when TEStylNew is called, the nullStyle handle from the style record should
  14149. be copied into the saved style record. This will ensure that the fields in the null-style
  14150. record point to valid data.
  14151.  
  14152.  
  14153.  
  14154. æKY 132
  14155. æC #132: AppleTalk Interface Update
  14156.  
  14157. See also:      The AppleTalk Manager
  14158.                Inside AppleTalk (for ZIP information)
  14159.                Technical Note #121 — Using the High-Level AppleTalk Routines
  14160.  
  14161. Written by:    Bryan Stearns    July 1, 1987
  14162. Updated:                        March 1, 1988
  14163. _______________________________________________________________________________
  14164.  
  14165. Technical Note #121 announced that we would be moving to a simplified AppleTalk Manager
  14166. interface. That interface is available now, as part of MPW 2.0 and newer.
  14167.  
  14168. Documentation for this new interface is contained in the AppleTalk Manager chapter of
  14169. Inside Macintosh Volume V. This technical note contains some of the preliminary documentation
  14170. for this interface and some useful points about information about it, and AppleTalk
  14171. in general.
  14172. _______________________________________________________________________________
  14173.  
  14174. The original AppleTalk Pascal Interfaces, known as ABPasIntf, were designed to simplify
  14175. use of AppleTalk from high-level languages. Instead, they’ve caused us a few compatibility
  14176. problems. We’ve decided to encourage use of the same interface that assembly-language
  14177. AppleTalk uses, a parameter-block interface in the same style as the low-level interfaces
  14178. to the File and Device Managers.
  14179.  
  14180. The original calls are still supported (and will be for a while) as an 
  14181. “alternate” interface, but we suggest that you consider moving to the new 
  14182. “preferred” calls. Be warned that use of the original calls may cause compatibility
  14183. problems with future system software. Also, new protocols (like ASP, the AppleTalk
  14184. Session Protocol) are only provided with the new interfaces.
  14185.  
  14186. The new interface uses parameter blocks like those used by the File and Device Managers;
  14187. you fill out the call-specific fields of the block, and a small amount of glue code
  14188. (provided with development environments like MPW) turns the parameter block into a
  14189. Control call to the appropriate AppleTalk driver.
  14190.  
  14191. Most calls have an interface like:
  14192.  
  14193.     FUNCTION PSomeCall(thePBPtr: ATPPBptr; asyncFlag: BOOLEAN): OSErr;
  14194.  
  14195. The glue fills in the fields csCode and ioRefNum with the appropriate value for the
  14196. call you’re making.
  14197. Synchronous and Asynchronous calls
  14198.  
  14199. You can still make calls synchronously (“do it now”) or asynchronously (“start it
  14200. now, finish it soon”). If you choose to make a call asynchronously, be sure to provide
  14201. a completion routine in the ioCompletion field (to be called when the call finally
  14202. finishes), or poll the ioResult field of the parameter block 
  14203. (the call is done if ioResult is less than or equal to 0).
  14204.  
  14205. You must not move or dispose of a parameter block before the call finishes; when the
  14206. call does complete, you are responsible for throwing the parameter block away (if you
  14207. allocated it using Memory Manager routines). 
  14208.  
  14209. Note that the alternate interfaces generated a network event on completion of an
  14210. asynchronous call; this service is not provided by the preferred interfaces, partly
  14211. because of future compatibility problems. See Technical Note #142 for background
  14212. information.
  14213.  
  14214.  
  14215. Packed data structures
  14216.  
  14217. Several of the data structures used by the new interfaces are packed; Pascal doesn’t
  14218. deal well with these structures. Special calls are provided for building LAP and DPP
  14219. write-data structures, NBP names-table elements, and ATP buffer data structures.
  14220.  
  14221. For example, when registering a name (using PRegisterName), you’ll use a NamesTableEntry
  14222. structure. This structure consists of a few unpacked fields, followed by an entity-name:
  14223. three strings (representing the object, type, and zone fields of the name) packed
  14224. together. You can call NBPSetNTE to pack the strings into the NamesTableEntry structure.
  14225. When you remove the name 
  14226. (PRemoveName), you’ll use the entity-name by itself; you can use NBPSetEntity to pack
  14227. it in.
  14228.  
  14229.  
  14230. Zone Interface Protocol
  14231.  
  14232. A function, GetBridgeAddress, is provided to obtain the node ID of a bridge, for use
  14233. in ZIP transactions (zero is returned if no bridge is present on your network). You
  14234. make ZIP calls using ATP requests, as described in the Inside AppleTalk chapter on
  14235. ZIP.
  14236.  
  14237.  
  14238.  
  14239. æKY 133
  14240. æC #133: Am I Talking To A Spooler?
  14241.  
  14242. See also:     PostScript Language Reference Manual
  14243.               Adobe Systems Document Structuring Conventions
  14244.  
  14245. Written by:    Ginger Jernigan    July 1, 1987
  14246. Updated:                          March 1, 1988
  14247. _______________________________________________________________________________
  14248.  
  14249. When the LaserShare spooler is on an AppleTalk network, it acts like a LaserWriter-type
  14250. device, which can be chosen and communicated with much like a real LaserWriter. Some
  14251. applications, however, must communicate with a LaserWriter directly, not a spooler.
  14252. If this is true for your application, you can check whether you are actually talking
  14253. to a real LaserWriter by sending to the LaserWriter the following query:
  14254.  
  14255.     %!PS-Adobe-1.2 Query
  14256.     %%Title: Query to Spooler/Non-Spooler status
  14257.     %%?BeginSpoolerQuery
  14258.     (0) = flush
  14259.     %%?EndSpoolerQuery 1
  14260.     %%EOF
  14261.  
  14262. (The query has to be sent using the Printer Access Protocol (PAP). The object code
  14263. for PAP is available from Licensing.) If the string returned begins with a ‘%%’ then
  14264. it is a status string and you can ignore it and wait for another string. If the LaserWriter
  14265. is actually a LaserShare spooler, then the string that is returned will be ‘1’. If
  14266. the LaserWriter is a real LaserWriter then the string returned will be ‘0’.
  14267.  
  14268.  
  14269.  
  14270. æKY 134
  14271. æC #134: Hard Disk Medic & Booting Camp
  14272.  
  14273. See also:      Hard Disk Users Manual
  14274.                Technical Note 67  — Finding the ‘Blessed Folder’
  14275.                Technical Note 113 — Boot Blocks
  14276.                Technical Note 139 - Macintosh Plus ROM Versions
  14277.  
  14278. Written by:    Bo3b Johnson    July 1, 1987
  14279. Updated:                       March 1, 1988
  14280. _______________________________________________________________________________
  14281.  
  14282. The death of a hard disk with megabytes worth of data can be exceedingly traumatic.
  14283. This technical note will describe techniques for recovering a hard disk and the data
  14284. that is on it. The discussion will also include some tips on how to avoid problems.
  14285. _______________________________________________________________________________
  14286.  
  14287. You should never need this information. However, software problems can wreak havoc
  14288. upon otherwise functional disks. When they have the equivalent of a heart attack,
  14289. there are a number of steps that can be taken to try to recover the disk. There are
  14290. occasions when the disk itself is not bad, and it may be possible to correct the disk
  14291. without having to reformat the disk and restore the data from a backup. This note
  14292. will describe some of the steps that can be used with Apple Hard Disks, but most of
  14293. the information pertains to all hard disks. For example, the HD SC Setup program is
  14294. specific to the Apple drives, but there is probably a similar utility for every hard
  14295. disk. This is primarily a discussion of what to do from the user standpoint, but
  14296. there are a few suggestions on ways of retrieving data via programmatic means.
  14297.  
  14298. This discussion will focus on the SCSI disks since they are more complex in terms of
  14299. the booting sequence. For other hard disks, like the standard HD-20, most of the
  14300. information still applies, but SCSI-specific sequences can be ignored. For example,
  14301. the standard HD-20 also has an installer program, although it is different than HD SC
  14302. Setup.
  14303.  
  14304.  
  14305. Attack of the Nasties
  14306.  
  14307. There are a number of unusual conditions that a hard disk may get itself in:
  14308.  
  14309.  1) The data is intact, but the hard disk won’t boot.
  14310.  2) The SCSI disk won’t boot and only shows up after running HD SC Setup.
  14311.  3) The disk will boot but hangs part way through the boot process.
  14312.  4) There are data errors while the disk is running.
  14313.  5) The disk is very slow returning to the Finder.
  14314.  6) The computer crashes or hangs when returning to the Finder.
  14315.  7) The disk appears in a “This disk is bad” dialog.
  14316.  8) The disk never shows up at all.
  14317.  
  14318. These problems can develop from a number of sources, including system crashes, rebooting
  14319. at bad times, power fluctuations, malicious software, old software, buggy software,
  14320. etc. In general, these problems will be software-related, since the hardware itself
  14321. is very rarely defective.
  14322.  
  14323. This technical note will discuss:
  14324.  
  14325.  1) The normal stages in the booting process.
  14326.  2) Results of errors during the various stages in the booting process.
  14327.  3) A step-by-step procedure to follow in order to maximize your chances of 
  14328.     recovering the disk and the data.
  14329.  
  14330.  
  14331. A Boot to the Head
  14332.  
  14333. This discussion will detail a normal boot process of a Macintosh with a single hard
  14334. disk attached. For clarity, this section will deliberately ignore potential problems
  14335. and the complexities involved in different configurations. The following sections
  14336. will detail some errors that may occur, and give more information in terms of what
  14337. the ROM will do to boot the system. A SCSI disk can be thought of in the following
  14338. fashion:
  14339.  
  14340. •••Click on the Illustration button below, and refer to Figure 1.••• 
  14341.  
  14342. The important thing to note from this diagram is that the Macintosh volume is a subset
  14343. of the entire SCSI Disk. There can be more than one Macintosh volume on a given disk,
  14344. or even other volumes that are not Macintosh volumes.
  14345.  
  14346.  
  14347. 1) Check the SCSI port:
  14348.  
  14349. Immediately after the RAM check, the system looks at the SCSI port to see if there
  14350. are any drives connected. If a SCSI drive is found the system reads the SCSI partition
  14351. information in block 0. This block is specific to SCSI drives and is always found at
  14352. block 0 of the disk. The SCSI Manager then reads in the SCSI driver from the disk.
  14353. Once the driver is loaded into memory, the system will use the driver to read and
  14354. write blocks from the disk, instead of the ROM boot code. The driver reads and writes
  14355. blocks relative to the beginning of the Macintosh volume on the SCSI drive, which can
  14356. start anywhere on the physical disk.
  14357.  
  14358. 2) Decide which disk is to be the startup disk:
  14359.  
  14360. The Macintosh then looks at the floppy disks to see if there is a disk that it should
  14361. try to use. If so, it will always boot from the floppy. If there are no floppy disks,
  14362. the startup hard disk is chosen. The Macintosh boot blocks are read off of the chosen
  14363. disk to determine if the volume is bootable. The two Macintosh boot blocks (same boot
  14364. blocks as those found on floppies) are read using the SCSI Driver. The Macintosh boot
  14365. blocks are found as the first two blocks on the Macintosh volume, but are much higher
  14366. in terms of where they are found on the disk itself. See the figure for the difference
  14367. between the Macintosh volume and the SCSI disk. The driver cannot normally read the
  14368. SCSI partition information, or any blocks outside of the Macintosh volume.
  14369.  
  14370. 3) Execute the Macintosh boot blocks:
  14371.  
  14372. The boot blocks are composed of strings and parameters which determine various system
  14373. functions, and code that finishes the job of booting the system.
  14374.     
  14375. The hard disk is mounted as a volume, using the PBMountVol call. The volume has the
  14376. two Macintosh boot blocks, as well as the volume header. The PBMountVol will use the
  14377. driver to read the volume header and other information from the disk. Once the volume
  14378. is mounted, there are only volume reads and writes, and the driver is responsible for
  14379. the actual SCSI disk reads.
  14380.     
  14381. The System file is opened on the volume. The patch code for the current ROM is read
  14382. into the system, including the patches to the SCSI Manager.
  14383.     
  14384. The Finder is launched.
  14385.  
  14386. 4) The Finder uses the Desktop file on the volume to draw the desktop.
  14387.  
  14388. The Icons that make up the desktop representation of the Macintosh volume are stored
  14389. in the Desktop file. The Desktop file is invisible and used only by the Finder.
  14390.  
  14391. That is a rather simplistic view of the boot process. There are a number of complications
  14392. that arise due to the wild variety of devices that can be attached to a Macintosh.
  14393. The full boot process is essentially a series of special cases, leading to the final
  14394. booted System at the Finder’s desktop (or in the startup application). The following
  14395. section will go into painstaking detail in order to give you enough information to
  14396. determine what step in the boot process failed.
  14397.  
  14398. Tough Boots
  14399.  
  14400. To further explain the boot process:
  14401.  
  14402. 1) Check the SCSI port:
  14403.   a) Before starting the boot process, the screen will be filled with a grey 
  14404.      pattern.
  14405.   b) Before the Macintosh will check for any SCSI devices, it will first reset 
  14406.      the SCSI bus using a SCSIReset. This is to make sure the bus was not left 
  14407.      in a bad state.
  14408.   c) The Macintosh will then start a cycle through all 7 SCSI IDs (from 6..0) 
  14409.      to see which disks are connected, and keeps a table of all disks that are 
  14410.      connected.
  14411.   d) For each disk that is connected to the Macintosh, the ROM boot code will 
  14412.      use the SCSI Manager to read in the SCSI partition information to find 
  14413.      where the driver is located on the disk. The signature of the SCSI 
  14414.      partition information is also checked to be sure that the device is valid.
  14415.   e) The SCSI Manager will then be used to read the driver into memory. Once 
  14416.      the driver is loaded for a given disk, the driver is called to install 
  14417.      itself. The driver will usually post a Disk Inserted event to have its 
  14418.      volume mounted by the Finder.
  14419.   f) Steps d and e are repeated for each disk connected. At this point, there 
  14420.      may be a number of drivers in memory, but there are no volumes, since 
  14421.      none have been mounted yet. Generally there is one driver per disk, but
  14422.      some drivers can handle more than one disk at a time.
  14423.  
  14424. 2) Decide which disk is to be the startup disk:
  14425.   a) The next stage is to determine which volume will become the startup disk. 
  14426.      If there is a floppy available it will always be the startup disk. During 
  14427.      this process the disk chosen as the startup disk is not known to be valid. 
  14428.      The System file and boot blocks are checked later.
  14429.   b) The standard HD-20 is connected to the system in a fashion that is very 
  14430.      similar to a floppy, so if a bootable HD-20 is connected it will be the 
  14431.      startup disk.
  14432.   c) There is no search for floppy devices like there is for SCSI disks since 
  14433.      the driver for the floppies will post a Disk Inserted event when it 
  14434.      detects a floppy in the drive. The first floppy device that is found will 
  14435.      be used as the startup disk. If there are multiple floppy devices, the 
  14436.      others will be mounted by the Finder, not at boot time. The SCSI devices 
  14437.      that are online are not mounted at this time, either. There is a pending 
  14438.      Disk Inserted event for each disk that will be handled by the Finder.
  14439.   d) At boot time, there is only one volume that is mounted (during execution 
  14440.      of the Macintosh boot blocks). The others will be mounted when their Disk 
  14441.      Inserted event is processed at a GetNextEvent call.
  14442.   e) On the new Control Panel there is a Control Device (cdev) called the 
  14443.      Startup Device. This Startup Device cdev allows the user to choose which 
  14444.      device the system should try to boot from first. This can only be used on 
  14445.      the Macintosh II and SE. The drive number, driver reference number, and 
  14446.      driver OS type are stored in parameter RAM to allow a chosen device to be 
  14447.      the boot disk. The floppy drives will still have precedence over the SCSI 
  14448.      devices. The standard HD-20 can be chosen as the Startup Device as well, 
  14449.      since it uses a different driver reference number. If the drive number 
  14450.      that is stored as the Startup Device is invalid, or had a read/write 
  14451.      error, then another disk in the chain will be chosen as the next bootable 
  14452.      candidate. Remember that there is only one boot/startup/system disk, and 
  14453.      it is the only one that is explicitly mounted at boot time. All other 
  14454.      devices in the system will be handled once the system is booted.
  14455.  
  14456. 3) Execute the Macintosh boot blocks:
  14457.   a) Once the Startup Disk has been chosen (whether floppy, SCSI or other disk) 
  14458.      then it is time to read the Macintosh boot blocks off of blocks 0 and 1 
  14459.      of the volume. Those boot blocks determine various parameters in the 
  14460.      system, such as whether a Macsbug-like debugger will be loaded, the name 
  14461.      of the startup program (not always the Finder), how big to make the event 
  14462.      queue, how big to make the system heap, and so on. They also contain a 
  14463.      signature identifying them as Macintosh boot blocks, and a version number 
  14464.      to differentiate between different boot blocks.
  14465.   b) After the boot blocks are read and the signature verified, the smiling 
  14466.      Macintosh is displayed on the screen. The smiling Macintosh basically 
  14467.      means that valid Macintosh boot blocks were found.
  14468.   c) On 64K ROMs the boot blocks are executed by jumping to the code that 
  14469.      follows the header information in boot block 0. On the newer Macintoshes 
  14470.      the boot block version number is checked, and if it is ‘old’ the boot 
  14471.      blocks will be skipped. The same code that would have been found in the 
  14472.      boot blocks is found in the ROM itself. Regardless of which kind of 
  14473.      Macintosh it is, the following steps apply. For the newer Macintoshes the 
  14474.      boot blocks are usually used only for the parameters stored in the header.
  14475.   d) Do the PBMountVol on the chosen startup volume. If PBMountVol fails, the 
  14476.      process starts over at the point where a startup disk is being chosen 
  14477.      (step 2 above). The failing volume is marked out of the list of 
  14478.      candidates so that it won’t be used again.
  14479.   e) Find the System file and create a Working Directory, if needed, for the 
  14480.      System folder. This is only done for HFS volumes of course, and the 
  14481.      directory ID is set to the blessed folder. The blessed folder is saved in 
  14482.      the volume header as part of the FinderInfo field. See Technical Note #67 
  14483.      for more information on the blessed folder. If the directory ID is wrong, 
  14484.      the System file won’t be found, causing it to start over again (at step 2 
  14485.      above). If the Working Directory was created successfully, that WDRefNum 
  14486.      is set as the default volume with SetVol.
  14487.   f) The System file is opened with OpenResFile. If the file could not be 
  14488.      opened, the process starts over again at the point where a suitable boot 
  14489.      device is being chosen (step 2 again).
  14490.   g) The Startup Screen is loaded and displayed. If there was no Startup Screen,
  14491.  
  14492.      the normal “Welcome to Macintosh” message will be displayed. The Startup 
  14493.      Screen or “Welcome...” means that the System file was found and opened 
  14494.      successfully. On the Macintosh Plus and 64K ROM machines, the Startup 
  14495.      Screen is displayed before the System file is opened. (reverse steps f & g)
  14496.   h) The debugger and disassembler are installed if found. The names of the 
  14497.      debugger and disassembler are found in the header of the boot blocks and 
  14498.      are usually Macsbug and Disassembler respectively.
  14499.   i) The data fork of the System file is opened and executed. The data fork 
  14500.      contains code to read in the PTCH resources which patch the ROM.
  14501.   j) The INITs that are in the System file are executed. The last INIT is INIT 
  14502.      31 which then looks in the System Folder for other INITs to be executed.
  14503.   k) The file specified by the boot blocks as the startup application (Set 
  14504.      Startup at the Finder) is found on the volume, using another field in the 
  14505.      FinderInfo field of the volume header in order to get the Directory ID. If 
  14506.      the file exists, it is launched. If not, the Finder is launched. If the
  14507.      Finder is not found, SysError is called with error code of 41 which is the 
  14508.      “Can’t launch Finder” alert.
  14509.  
  14510. 4) The Finder uses the Desktop file on the volume to draw the desktop.
  14511.     
  14512. If the startup application was the Finder, it opens the Desktop file on the startup
  14513. volume in order to draw the desktop. When it finishes with the startup volume, it
  14514. calls GetNextEvent. If there are any pending Disk Inserted events, the volume specified
  14515. is mounted (by the ROM) and the result passed to the Finder. If PBMountVol failed for
  14516. any reason, the bad result will be passed to the Finder. At that point the Finder
  14517. would put up the “This disk is damaged” alert and ask if the volume should be initialized
  14518. or ejected. If ejected, the driver for that volume still exists, but the volume is
  14519. unmounted. For each volume that the Finder sees, it opens the Desktop file on the
  14520. volume to get the information that it needs to build the desktop. If the Desktop file
  14521. was not found on a volume, it is created. If there are any errors while creating or
  14522. using the Desktop file, the Finder will display the “This disk needs minor repairs”
  14523. message. If the OK button is clicked, the Finder will delete the old file and create
  14524. a new one. If that fails, the volume is unmounted and deemed unusable by the Finder.
  14525. This happens if the disk is locked, or too full to add a Desktop file. If that was
  14526. the startup volume, the computer is rebooted since it was forced to unmount the startup
  14527. volume, and cannot run if there is no startup volume.
  14528.  
  14529. If you follow the previous sequence closely, you can predict what errors are causing
  14530. a given end result. For example, if you have the effect where the smiley Macintosh
  14531. appears, but immediately goes away and the disk does not boot, you can look through
  14532. the sequence to see what might be going wrong. In this case, we know that the boot
  14533. blocks were found on our startup volume, since the smiley Macintosh was displayed. We
  14534. know that the System file was not found, or failed to open, since we never got the
  14535. Welcome message. This usually calls for throwing away all of the System Folders on
  14536. the volume, and starting again with a new System Folder to fix the problem. If there
  14537. is more than one System Folder on a volume it is possible to confuse the system.
  14538.  
  14539.  
  14540. Other tidbits of information that may be useful (in no particular order) some which
  14541. will be mentioned in the step-by-step operation below:
  14542.  
  14543. 1) The SCSI cables have a lot of wires in them, and are rather bulky because of 
  14544.    it. It is best to avoid bending the cables too much or too often, since the 
  14545.    wires inside will break if overstressed. Don’t put wild kinks in the cable
  14546.    in order to make it fit behind the Macintosh.
  14547. 2) If there is no default volume stored in the parameter RAM with the Startup
  14548.    Device cdev, then the first drive that is in the drive queue will be the
  14549.    Startup Device. Since SCSI drives are added in highest ID order, that means
  14550.    the larger SCSI IDs will have a higher ‘priority’. Macintosh IIs will
  14551.    default to the internal hard disk.
  14552. 3) If the parameter RAM is trashed for some reason, the boot process can fail
  14553.    since a driver OS type is stored as well. If the OS type is wrong, the ROM
  14554.    will skip that driver, making the disk unbootable. On the Macintosh II/SE,
  14555.    the battery is no longer removable to fix parameter RAM problems. To correct
  14556.    this problem the Control Panel now has a feature that will allow you to
  14557.    clear parameter RAM. Holding down the Option-Command-Shift keys while
  14558.    opening the Control Panel will reset parameter RAM, forcing it to be rebuilt
  14559.    and therefore losing all of your settings, but possibly fixing some booting
  14560.    problems.
  14561. 4) The Macintosh II and SE both have a new feature that will allow you to skip 
  14562.    having the any hard disk mounted. Holding down the Option-Command-Shift-
  14563.    Delete combination will have the startup code skip the SCSI hard disks on
  14564.    the system. This can be useful if you are booting an old System file that
  14565.    does not understand HFS disks (like System 2.0/Finder 4.1), and want to 
  14566.    avoid having your hard disks on line while you do something shaky. With 
  14567.    external hard disks it is easier to just turn them off, but with internal 
  14568.    disks it is not so easy.
  14569. 5) Since the parameter RAM can be trashed in a manner that makes it impossible 
  14570.    to boot a volume (looking for the wrong OS type), a new feature was added to
  14571.    the HD SC Setup program to have it fix this problem as well. If you have
  14572.    version 1.3 or greater, the parameter RAM bytes that determine booting will
  14573.    be reset to fix some boot problems that occur. The parameter RAM is fixed
  14574.    when the Update button is clicked. This does not invalidate the rest of
  14575.    parameter RAM, it merely fixes the bytes used for the Startup Device.
  14576. 6) When the Finder copies a new System Folder onto a disk that does not already
  14577.    have a System Folder, that new folder will become the blessed folder. Its
  14578.    Directory ID will be saved in the volume header. In addition, the Macintosh
  14579.    boot blocks will be copied from the current startup device to the
  14580.    destination device. This is the best way to fix System Folder or Macintosh
  14581.    boot block problems. In order for the blessed folder to be set correctly,
  14582.    all System Folders on the volume should be deleted before copying the new
  14583.    folder there.
  14584. 7) If the Desktop file is damaged for whatever reason, it can be deleted with
  14585.    a number of programs. This will force the Finder to rebuild it from scratch.
  14586.    You can also have the Finder rebuild the Desktop file by holding down the
  14587.    Option-Command keys when the Finder is launched. When the Desktop file is
  14588.    rebuilt you lose the Finder Comments in the Get Info boxes.
  14589. 8) On the 64K ROMs, whenever something goes wrong during booting (like System
  14590.    file not found, bad boot blocks, and so on) the Sad Mac Icon is displayed.
  14591.    Starting with the 128K ROMs, whenever something goes wrong the ROM jumps
  14592.    back to the start to try to find another disk to use.
  14593.  
  14594. Bo3b’s Boot Repair
  14595.  
  14596. This section will detail step-by-step processes that can be used to fix some common
  14597. booting and volume problems. It is not intended to cover every possible case. The
  14598. purpose of the preceding sections was to give you the information that will allow you
  14599. to figure out what might be going wrong.
  14600.  
  14601. For most hard disk users, it is not sufficient to merely have the device running. It
  14602. is generally a good idea to make the system as robust as possible in order to avoid
  14603. some of the problems that might cause a volume to become wholly unreadable. The ultimate
  14604. fix is to reinitialize the volume from scratch and rebuild the volume with the Finder
  14605. or a restore operation that uses the File Manager. This is guaranteed to fix anything
  14606. except hardware problems, and will give you the most solid system. If your system is
  14607. acting funny, you can try the following sequence that is the next best thing to initializing
  14608. the disk. This sequence will not make you rebuild the disk, but can be fooled by some
  14609. disk problems. If everything passes, then the disk is in good shape; maybe not perfect,
  14610. but good.
  14611.  
  14612.  1) Power down the entire system, including the hard disk that is suspect.
  14613.  2) Run the HD SC Setup program (or equivalent) and Update the drivers on the
  14614.     disk. For HD SC, this also fixes the parameter RAM. For non-Apple drives,
  14615.     the parameter RAM can be reset with the Control Panel.
  14616.  3) Run the Test Disk option in HD SC Setup (or equivalent). If the test fails,
  14617.     reinitialize the volume, since it is not worth risking future problems.
  14618.  4) Run the Disk First Aid utility. This utility will work on all HFS volumes.
  14619.     Have it check the volume for consistency. If it reports any errors, you can
  14620.     have it fix the problem, but the safest tack is to reinitialize. There are
  14621.     some problems that Disk First Aid won’t catch. If Disk First Aid says the
  14622.     volume cannot be verified, it is time to reinitialize.
  14623.  5) Rebuild the Desktop file by holding down Option-Command when returning to
  14624.     the Finder.
  14625.  
  14626. If you can successfully perform all of these steps, the volume will be as solid as it
  14627. can get without reinitializing the disk. If things are still funny, it is time to
  14628. take the last recourse, reinitialize.
  14629.  
  14630.  
  14631. Based on the previous sections, it is now time to go through all of the Nasties to
  14632. give a step-by-step sequence for fixing these problems.
  14633.  
  14634. 1) The data is intact, but the hard disk won’t boot.
  14635.  
  14636. This is for the case where the volume won’t boot, but if the computer is booted with
  14637. a floppy disk the volume shows up at the desktop and can run normally. For this case,
  14638. we know that the driver is being loaded and working, since the volume shows up at the
  14639. desktop. The volume is also mountable, since it shows up with no problem. This implies
  14640. that the Macintosh boot blocks are wrong, or the blessed folder is wrong. Clues such
  14641. as the smiling Macintosh can tell you how far the process got before it failed. For
  14642. example, if the smiling Macintosh never appeared, we know that Macintosh boot blocks
  14643. were not read successfully. When the volume is fixed and bootable, it would be a good
  14644. idea to go through the steps above to make the volume as solid as possible.
  14645. The sequence to follow:
  14646.  a) Power down the entire computer, including the hard disk. Try to boot again.
  14647.     If it works, you are done.
  14648.  b) Use the Control Panel’s Startup Device to set the hard disk as the Startup
  14649.     Device. This will also reset some of the bytes in parameter RAM. Try
  14650.     rebooting to see if it has fixed the problem.
  14651.  c) Run HD SC Setup (or equivalent) and perform the Update Drivers procedure.
  14652.     In the HD SC Setup case this will also rewrite the parameter RAM. If you
  14653.     are not using HD SC Setup, blast the parameter RAM with the Control Panel.
  14654.     Try rebooting.
  14655.  d) Delete all System Folders from the hard disk. Using Find File or something
  14656.     similar, be sure that there are no stray copies of the System or Finder
  14657.     buried in some long lost folder. Copy a new System Folder to the volume,
  14658.     using the Finder. This process will fix bad boot blocks, as well as a bad
  14659.     blessed folder. Try rebooting.
  14660.  e) If it still won’t boot, there is something very strange happening. Whenever
  14661.     things get too weird it is usually time to start over: reinitialize.
  14662.  
  14663. 2) The disk won’t boot and only shows up after running HD SC Setup.
  14664.  
  14665. The disk does not even show up at the Finder when the system is booted with a floppy.
  14666. After running the HD SC Setup (or equivalent) the volume will appear on the desktop
  14667. and be usable. The HD SC Setup and most similar utilities will do an explicit PBMountVol
  14668. of the volume in order to make the volume usable. Since the volume does not show up
  14669. at the Finder at first, this implies that the driver itself is not getting loaded or
  14670. is working improperly, since there was no Disk Inserted Event for the Finder to use.
  14671. The sequence:
  14672.  a) Power down completely, including the hard disk.
  14673.  b) Run HD SC Setup (or equivalent) and Update the Drivers. For non-Apple
  14674.     drives, update the drivers on the volume (this rewrites the SCSI partition
  14675.     information as well) using the utility that came with the disk. Reset the
  14676.     parameter RAM using the Control Panel.
  14677.  c) If it still cannot be booted or does not show up at the Finder after
  14678.     booting with a floppy, the volume is too weird and should be reinitialized.
  14679.  
  14680. 3) The disk will boot but hangs part way through the boot process.
  14681.  
  14682. This is when you can see the volume is being accessed by the run light (LED) on the
  14683. front panel, and the booting seems to work but never makes it to the Finder. This
  14684. implies that all is well until the System tries to actually launch the Finder or
  14685. Startup Application. It could also be that the System file is causing something to
  14686. hang.
  14687. The sequence:
  14688.  a) Power down completely.
  14689.  b) Boot with a floppy so that the floppy is the startup disk and the volume in
  14690.     question can be seen at the Finder.
  14691.  c) Delete all System Folders on the hard disk. Put a new System Folder on the
  14692.     disk. This will presumably fix a corrupted System file.
  14693.  d) If still funky, show the disk who’s boss.
  14694.  
  14695. 4) There are data errors while the disk is running.
  14696.  
  14697. This case usually evidences itself by messages at the Finder when trying to copy
  14698. files. Messages like “The file ^0 could not be read and was skipped” usually mean
  14699. that the drive is passing back I/O errors. This usually means that there is a hardware
  14700. failure, but it can occasionally be caused by bad sectors on the disk itself. If the
  14701. sectors are actually bad, it is generally necessary to reinitialize the volume.
  14702. The sequence:
  14703.  a) Power down completely. Reboot and see if the same file gives the same error.
  14704.  b) Run the HD SC Setup (or utility that came with your drive) and perform the
  14705.     Test operation. This will fail if there are bad blocks on the device. If
  14706.     there are bad blocks, it is necessary to reinitialize the volume.
  14707.  c) Check the SCSI terminators to be sure they are plugged in correctly. There
  14708.     can be no more than two terminators on the bus. If you have more than one
  14709.     SCSI drive you must have two terminators. If you only have one drive, use a
  14710.     single terminator. If you have more than one drive, the two terminators
  14711.     should be on opposite ends of the chain. The idea is to terminate both ends
  14712.     of this wire that goes through all of the devices. If you have a Macintosh
  14713.     II or SE with an internal drive, that drive will already have a terminator
  14714.     inside the Macintosh at the front of the cable.
  14715.  d) Make sure the SCSI cables you are using are OK, by swapping them with known
  14716.     good ones. If the problem disappears, the cable is suspect.
  14717.  e) Swap the terminators in use with known good ones to be sure they are OK.
  14718.  f) Try the drive and cable on a different Macintosh to be sure the Macintosh
  14719.     is OK.
  14720.  
  14721. 5) The disk is very slow returning to the Finder.
  14722.  
  14723. If the computer has gotten slower with age, it is probably due to a problem with the
  14724. Desktop file. If a volume has been used for a long time, the Desktop file can grow to
  14725. be very large (Hundreds of K). Reading and using a file that big can slow down the
  14726. Finder when it is drawing the desktop. If you have a large number of files in the
  14727. root directory, this will also slow the computer down. A large number (500-1000) of
  14728. files in a given folder can cause performance problems as well. If a volume has been
  14729. used for a long time, it can also have become fragmented.
  14730. The sequence:
  14731.  a) Rebuild the Desktop file and see if it gets faster.
  14732.  b) Look for large numbers of files in a given directory and break them up into
  14733.     other folders if needed.
  14734.  c) Run Disk First Aid to be sure the volume is not damaged.
  14735.  d) Reinitialize the volume and restore the data using File Manager calls to
  14736.     fix a fragmentation problem. Using the Finder, or a backup program that
  14737.     reads and writes files is a way to use only File Manager calls. You cannot
  14738.     fix a fragmentation problem by doing an image backup and restore.
  14739.  
  14740. 6) The computer crashes or hangs when returning to the Finder.
  14741.  
  14742. This can happen if the Desktop file becomes corrupted. There are occasions when this
  14743. can happen if the HFS structures on the volume are damaged.
  14744. The sequence:
  14745.  a) Rebuild the Desktop file.
  14746.  b) Run Disk First Aid to be sure the volume is not damaged; a boot floppy with 
  14747.     the Set Startup set to Disk First Aid can allow you to test a volume that 
  14748.     cannot be displayed at the Finder.
  14749.  c) The path of ultimate recourse if nothing else seems wrong with the volume.
  14750.  
  14751. 7) The disk appears in a “This disk is bad” dialog.
  14752.  
  14753. This is the worst of the possible errors that generally happen to hard disks. If the
  14754. message is “This disk is bad” or “This is not a Macintosh disk”, the HFS structures
  14755. on the volume have been damaged. In particular, the Master Directory block on the
  14756. volume has been damaged. The driver and SCSI partition information are probably OK,
  14757. since this dialog shows up when the Finder tries to mount a damaged volume. This
  14758. means that the PBMountVol call failed. Don’t click the Initialize button unless you
  14759. are sure you want the volume to be erased. In these cases, it is nearly always better
  14760. to just reinitialize the volume after you have saved whatever information you can.
  14761. The sequence:
  14762.  a) Power down completely. Occasionally the controller in the hard disk itself
  14763.     can crash.
  14764.  b) Run Disk First Aid. For these cases, it is usually necessary to create a
  14765.     boot floppy with Set Startup set to Disk First Aid. When the floppy is
  14766.     booted, Disk First Aid will be run before the Disk Inserted events are
  14767.     processed. When Disk First Aid sees the Disk Inserted event it will check
  14768.     the result from the PBMountVol and still allow you to test the volume, even
  14769.     if it can’t be mounted.
  14770.  c) If Disk First Aid cannot repair the disk, it might be worth writing a
  14771.     simple program to call the driver to read and write blocks. There is a copy
  14772.     of the Master Directory Block on the end of the volume, and the volume can
  14773.     sometimes be fixed by copying that block over a damaged block in sector 2.
  14774.     You can write a program that will find out how big the volume is by looking
  14775.     in the Drive Queue Element for the volume, reading the block that is one
  14776.     sector from the end (N-1), and writing that copy over sector 2. At this
  14777.     point, the volume is probably inconsistent, but it may allow you to use it
  14778.     long enough to get information off of it. It is sometimes possible to have
  14779.     Disk First Aid repair the volume at this point as well. Copying the sectors
  14780.     can also be done with sector edit utilities, if you can get them to
  14781.     recognize the volume at all.
  14782.  d) If making a new copy of sector 2 does not work, but the driver is still
  14783.     being loaded at boot time, it is possible to write a program that will read
  14784.     sectors from the disk looking for information that you might need. You can
  14785.     have a reader program go through blocks looking for a specific pattern,
  14786.     like a known file name. This is usually done in desperation, but sometimes
  14787.     there is no other choice. If the data desired can be found in some form, it
  14788.     can sometimes be massaged back to a useful form much easier than recreating
  14789.     it.
  14790.  e) Sometimes the volume will be so badly damaged that the SCSI partition
  14791.     information is also damaged and cannot be fixed with the Update in the hard
  14792.     disk utility. In this case, it is usually still possible to perform direct
  14793.     SCSI reads, without going through the driver. Using the driver is
  14794.     preferable, since it knows how to talk to the drive better than you would,
  14795.     but sometimes the driver is not available. Using direct SCSI reads should
  14796.     be a last ditch effort since the SCSI Manager can be very challenging to
  14797.     use. This should only be used if there is irreplaceable data on the volume
  14798.     that cannot be read by any other means.
  14799.  f) Even if the volume is recovered, it still should be reinitialized (after
  14800.     the data is recovered) to be sure that any hidden damage is repaired.
  14801.  
  14802. 8) The disk never shows up at all.
  14803.  
  14804. The disk appears to be missing. The volume does not show up at the Finder, and does
  14805. not show up in HD SC Setup. At boot time the access light (LED) does not flash. This
  14806. is usually a hardware problem as well. The drive is not responding to SCSI requests
  14807. at all, so the system cannot tell a drive is attached.
  14808. The sequence:
  14809.  a) Power down the system, including the hard disk.
  14810.  b) Make sure that the SCSI ID on the drive does not conflict with any other in
  14811.     the system, including the Macintosh, which is ID 7. (If you have an
  14812.     internal hard drive, it should be ID 0.)
  14813.  c) Check the SCSI terminators to be sure they are plugged in correctly. There
  14814.     can be no more than two terminators on the bus. If you have more than one
  14815.     SCSI drive you must have two terminators. If you only have one drive, you
  14816.     should use a single terminator. If you have more than one drive, the two
  14817.     terminators should be on opposite ends of the chain. The idea is to
  14818.     terminate both ends of this wire that goes through all of the devices. If
  14819.     you have a Macintosh II or SE with an internal drive, that drive will
  14820.     already have one terminator inside the Macintosh at the front of the cable
  14821.  d) Make sure the SCSI cables you are using are OK, by swapping them with known 
  14822.     good ones.
  14823.  e) Swap the terminators in use with known good ones to be sure they are OK.
  14824.  f) Try the drive and cable on a different Macintosh to be sure the Macintosh
  14825.     is OK.
  14826.  
  14827.  
  14828. These boots are made for wokking
  14829.  
  14830. Remember, the goal here is to make the system be as stable as possible. If things are
  14831. acting strange, it doesn’t hurt to go through the entire process of testing the drive.
  14832. The test procedure takes a little time but is non-destructive for the data that is
  14833. there. If something catastrophic has happened to the disk, it is better to spend some
  14834. time backing up the data, initializing the volume, and restoring the data than it is
  14835. to lose some work later on due to some other permutation of the same problem. Unless
  14836. you are sure that the volume is in an undamaged state, you are better off using a
  14837. file-by-file backup operation than an image backup, since the image backup will copy
  14838. any damage as well as the data.
  14839.  
  14840. If there are situations that you run into that are not covered by this technical
  14841. note, please let us know so that they can added. 
  14842.  
  14843. If this technical note helps even one person save some data that would otherwise be
  14844. lost, it will have been worthwhile. Hope it helps.
  14845.  
  14846.  
  14847.  
  14848. æKY 135
  14849. æC #135: Getting through CUSToms
  14850.  
  14851. See also:    Technical Note #88 — Signals
  14852.              Technical Note #110 — MPW: Writing Standalone Code
  14853.  
  14854. Written by:    Rick Blair    July 1, 1987
  14855. Updated:        March 1, 1988
  14856. _______________________________________________________________________________
  14857.  
  14858. This technical note provides a way for developers to allow sophisticated users to add
  14859. code to an off-the-shelf application.  Using this scheme, the user can easily install
  14860. the code module; the application has to know how to call it and, optionally, be able
  14861. to respond to a set of predefined calls from the custom package. 
  14862. _______________________________________________________________________________
  14863.  
  14864.  
  14865. Note 
  14866.  
  14867. The following code makes heavy use of features of the Macintosh Programmer’s Workshop.
  14868. It also assumes a basic familiarity with the standard Sample program included with
  14869. MPW. The Pascal code (which is here only as an example implementation of the mechanism)
  14870. is presented as only those sections which differ from Sample.p. The assembly language
  14871. code also includes MPW-only features, such as record templates. Some of these are
  14872. explained in Technical Note #88, “Signals.”
  14873.  
  14874. In addition, since the order in which parameters to various routines are passed is
  14875. critical, special care will have to be taken in writing interfaces for use with C. It
  14876. is probably best to declare them as Pascal in the C source.
  14877.  
  14878.  
  14879. Concepts 
  14880.  
  14881. Basically, we create a code resource of type CUST with an entry point at the beginning
  14882. which takes several parameters on the stack; this code is reached via a dispatching
  14883. routine which is written in assembly language.
  14884.  
  14885. The data passed on the stack to this dispatcher includes: 
  14886.  
  14887.  • a selector (to specify the operation desired) 
  14888.  • the address of a section of application globals (for communication back and 
  14889.    forth between the application and the module when the stack parameters are 
  14890.    insufficient) 
  14891.  • a handle which references the custom code resource on the stack. 
  14892.  
  14893. Other parameters may be added (as long as they are pushed on the stack before the
  14894. required ones) if desired. Since these extra parameters would always have to be included
  14895. in any calls to a given package, it might be more convenient to use the application
  14896. global space area which is accessed through the appaddr parameter.
  14897.  
  14898.  
  14899. Template 
  14900.  
  14901. Your application must contain the following global data and procedure declarations to
  14902. support this model:
  14903.  
  14904. VAR
  14905.      custhandle: Handle;
  14906.  
  14907.      {the following globals constitute the data known to the custom code}
  14908.      appdispatch: ProcPtr; {address of dispatch routine custom code can call}
  14909.     {examples of further application globals for the custom package:}
  14910.      (*
  14911.      paramptr: Ptr; {general pointer used as param. to appdispatch code}
  14912.      paramword1: INTEGER;
  14913.      paramword2: INTEGER;
  14914.      CUSTerr: INTEGER;
  14915.      *)
  14916.      {any other globals the module should get at}
  14917.  
  14918.     {the two assembly language glue routines which are linked into the
  14919.      application}
  14920.    PROCEDURE CustomInit(resID: INTEGER; VAR custhandle: Handle);
  14921.      EXTERNAL; {the routine used to set up the custhandle resource handle}
  14922.  
  14923.     PROCEDURE CustomCall({application & package-specific paramters}                  
  14924.   selector: INTEGER; appaddr: UNIV Ptr; ourhandle: Handle);
  14925.      EXTERNAL; {this is the code dispatcher}
  14926.  
  14927.    {this is called by the custom package to perform a service which is more        
  14928. easily provided by the application; since we pass a pointer to it to the
  14929.      package, CustDispatch must be at the outermost nesting level in the main
  14930.      segment}
  14931.     PROCEDURE CustDispatch(selector: INTEGER);
  14932.  
  14933.      BEGIN
  14934.         CASE selector OF
  14935.          {.
  14936.           .
  14937.           .}
  14938.         END; {CASE}
  14939.      END; {CustDispatch}
  14940.  
  14941.    {your initialization code should contain the following:}
  14942.  
  14943.      {Custom package initialization stuff}
  14944.      appdispatch := @CustDispatch; {put pointer where the package can see it}
  14945.      CustomInit(69,custhandle);    {our CUST resource has ID = 69}
  14946.  
  14947.    {then whenever you want to invoke the package you use CustomCall}
  14948.  
  14949. You must also assemble CustomInit and CustomCall and link them with into your application.
  14950. The custom package itself can be written in any language which can produce stand-alone
  14951. code. See Technical Note #110 for how to write stand-alone code in MPW Pascal.
  14952.  
  14953.  
  14954. The example 
  14955.  
  14956. CustomCall is only referenced once in this example. When a variety of unrelated functions
  14957. are provided, however, it is more convenient to provide a separate interfacing procedure
  14958. to invoke each one and have them make their own CustomCall calls.
  14959.  
  14960. Note that this example is somewhat contrived; you probably wouldn’t 
  14961. “externalize” the code for finding a word or sequence of characters like this. This
  14962. is an idealized situation. More realistic uses would be: to add-on special routines
  14963. to a database to perform custom calculations or the like; allow for localization when
  14964. code is required (and hooks aren’t already provided); let documents carry around code
  14965. which may vary among  software versions, etc. so that older documents would be able
  14966. to work alongside the new ones, etc.
  14967.  
  14968.  
  14969. What it does 
  14970.  
  14971. We simply add a new menu to the sample program which allows Find by characters or
  14972. word. We just pass the menu item to the package and let it do the finding; it then
  14973. calls back to the application dispatch routine to highlight text or display the “not
  14974. found” message.
  14975.  
  14976. The Pascal source for the example application appears first:
  14977.  
  14978.    {$R-}
  14979.    {$D+}
  14980.    PROGRAM P;
  14981.  
  14982.     USES
  14983.      {$LOAD ::PInterfaces:most.dump}
  14984.      Memtypes,Quickdraw,OSIntf,ToolIntf,PackIntf {,MacPrint}
  14985.      {$LOAD}
  14986.      , {$U ErrSignal.p} ErrSignal;
  14987.  
  14988.     CONST
  14989.      appleID = 128; {resource IDs/menu IDs for Apple, File and Edit menus}
  14990.      fileID = 129;
  14991.      editID = 130;
  14992.      findID = 131;
  14993.  
  14994.      appleM = 1; {index for each menu in myMenus (array of menu handles)}
  14995.      fileM = 2;
  14996.      editM = 3;
  14997.      findM = 4;
  14998.  
  14999.      menuCount = 4; {total number of menus}
  15000.  
  15001.      windowID = 128; {resource ID for application’s window}
  15002.  
  15003.      undoCommand = 1; {menu item numbers identifying commands in Edit menu}
  15004.      cutCommand = 3;
  15005.      copyCommand = 4;
  15006.      pasteCommand = 5;
  15007.      clearCommand = 6;
  15008.  
  15009.      findcharsCommand = 1; {menu items for Custom menu}
  15010.      findwordCommand = 2;
  15011.  
  15012.      aboutMeCommand = 1; {menu item in apple menu for About sample item}
  15013.  
  15014.      aboutMeDLOG = 128;
  15015.      findDLOG = 129;
  15016.      infoDLOG = 130;
  15017.  
  15018.      {application dispatching code selectors}
  15019.      hilightSel = 0;
  15020.      notifySel = 1;
  15021.  
  15022.    VAR
  15023.    •
  15024.    •
  15025.    •
  15026.    errCode: INTEGER;
  15027.    dlogString: Str255;
  15028.    custhandle: Handle;
  15029.  
  15030.    {here is the area known to the custom code}
  15031.    appdispatch: ProcPtr; {address of dispatch routine custom code can call}
  15032.    {examples of further application globals for the custom package}
  15033.    paramptr: Ptr; {general pointer used as param. to appdispatch code}
  15034.    paramword1: INTEGER;
  15035.    paramword2: INTEGER;
  15036.    {any other globals the module should get at}
  15037.  
  15038.  
  15039.    PROCEDURE CustomInit(resID: INTEGER; VAR custhandle: Handle);
  15040.      EXTERNAL; {the routine used to set up the custhandle resource handle}
  15041.  
  15042.     PROCEDURE CustomCall(text: Ptr; count: INTEGER; findstr: StringPtr;         selector:
  15043. INTEGER; appaddr: UNIV Ptr; ourhandle: Handle);
  15044.      EXTERNAL; {this is the code dispatcher}
  15045.  
  15046.  
  15047.   {this will do the “about” dialog and the info dialog requested by the custom
  15048.   pack.}
  15049.  
  15050.     PROCEDURE ShowADialog(meDlog: INTEGER);
  15051.  
  15052.     CONST
  15053.        okButton = 1;
  15054.        authorItem = 2;
  15055.        languageItem = 3;
  15056.        infoItem = 2;
  15057.  
  15058.     VAR
  15059.        itemHit,itemType: INTEGER;
  15060.        itemHdl: Handle;
  15061.        itemRect: Rect;
  15062.        theDialog: DialogPtr;
  15063.  
  15064.     BEGIN
  15065.        theDialog := GetNewDialog(meDlog,NIL,WindowPtr( - 1));
  15066.  
  15067.         CASE meDlog OF
  15068.            aboutMeDLOG: BEGIN
  15069.            GetDitem(theDialog,authorItem,itemType,itemHdl,itemRect);
  15070.               SetIText(itemHdl,'Ming The Vaseless');
  15071.               GetDitem(theDialog,languageItem,itemType,itemHdl,itemRect);
  15072.               SetIText(itemHdl,'Pascal et al');
  15073.            END;
  15074.  
  15075.            infoDLOG: BEGIN {display the message requested by the custom package}
  15076.               GetDitem(theDialog,infoItem,itemType,itemHdl,itemRect);
  15077.               SetIText(itemHdl,StringPtr(paramptr)^);
  15078.            END;
  15079.         END; {CASE}
  15080.  
  15081.         REPEAT
  15082.            ModalDialog(NIL,itemHit)
  15083.         UNTIL (itemHit = okButton);
  15084.  
  15085.         CloseDialog(theDialog);
  15086.      END; {of ShowADialog}
  15087.  
  15088.  
  15089.    {this will put up the Find dialog to allow the user to type in the 
  15090.    characters to search for}
  15091.    FUNCTION DoCustomDialog: BOOLEAN;
  15092.  
  15093.      CONST
  15094.        okButton = 1;
  15095.        cancelButton = 2;
  15096.        fixedItem = 3;
  15097.        editItem = 4;
  15098.  
  15099.      VAR
  15100.        itemHit,itemType: INTEGER;
  15101.        itemHdl: Handle;
  15102.        itemRect: Rect;
  15103.        theDialog: DialogPtr;
  15104.  
  15105.      BEGIN
  15106.        theDialog := GetNewDialog(findDLOG,NIL,WindowPtr( - 1));
  15107.        GetDitem(theDialog,editItem,itemType,itemHdl,itemRect);
  15108.        SetIText(itemHdl,dlogString);
  15109.        TESetSelect(0,MAXINT,DialogPeek(theDialog)^.textH);
  15110.  
  15111.        REPEAT
  15112.           ModalDialog(NIL,itemHit)
  15113.        UNTIL (itemHit IN [okButton,cancelButton]);
  15114.        GetIText(itemHdl,dlogString);
  15115.        DoCustomDialog := itemHit = okButton;
  15116.  
  15117.        CloseDialog(theDialog);
  15118.     END; {of DoCustomDialog}
  15119.  
  15120.  
  15121.    PROCEDURE DoCommand(mResult: LONGINT);
  15122.    •
  15123.    •
  15124.    •
  15125.    (* partial procedure fragment *)
  15126.  
  15127.    {here is one of the case sections for the DoCommand procedure}
  15128.  
  15129.           findID:
  15130.              IF DoCustomDialog THEN
  15131.                  BEGIN
  15132.                  MoveHHi(Handle(textH)); {stop it from fragmenting the heap}
  15133.                  WITH textH^^ DO BEGIN
  15134.              HLock(hText); {since we don’t know what the package might be up to}
  15135.         {now call the package to find characters or words}
  15136.                  CustomCall(POINTER(ORD(hText^) + selEnd),
  15137.                    teLength - selEnd, @dlogString, theItem, @appdispatch,
  15138.                    custhandle);
  15139.                   HUnLock(textH^^.hText);
  15140.                 END; {WITH}
  15141.                 END;
  15142.  
  15143.          END; {OF menu CASE} {to indicate completion of command,}
  15144.         HiliteMenu(0); {call Menu Manager to unhighlight }
  15145.         {menu title (highlighted by    }
  15146.         {MenuSelect)        }
  15147.      END; {OF DoCommand}
  15148.  
  15149.    {this is called by the custom package to set the new selection or display a 
  15150.     message; it must be in CODE 1 at the outermost lexical level}
  15151.     PROCEDURE CustDispatch(selector: INTEGER);
  15152.  
  15153.      BEGIN
  15154.         CASE selector OF
  15155.            hilightSel: {hilight the characters selected by the custom pack.}
  15156.           {paramptr=pointer to text to select, paramword1¶mword2=start,end       
  15157.  chars}
  15158.               WITH textH^^ DO
  15159.               {we’ll subtract the start of text from paramptr to get the base        
  15160. offset…}
  15161.                 TESetSelect(ORD(paramptr) - StripAddress (ORD(hText^)) +             
  15162.  paramword1, ORD(paramptr) - StripAddress (ORD(hText^))                + paramword2,textH);
  15163.  
  15164.            notifySel: {put up message per request from custom pack.}
  15165.            {paramptr points to string to display}
  15166.                ShowADialog(infoDLOG);
  15167.  
  15168.          END; {CASE}
  15169.       END; {CustDispatch}
  15170.  
  15171.    BEGIN {main program}
  15172.      { Initialization }
  15173.      InitGraf(@thePort); {initialize QuickDraw}
  15174.      InitFonts; {initialize Font Manager}
  15175.      FlushEvents(everyEvent - diskMask,0); {call OS Event Mgr to discard
  15176.                                            non-disk-inserted events}
  15177.      InitWindows; {initialize Window Manager}
  15178.      InitMenus; {initialize Menu Manager}
  15179.      TEInit; {initialize TextEdit}
  15180.      InitDialogs(NIL); {initialize Dialog Manager}
  15181.      InitCursor; {call QuickDraw to make cursor (pointer) an arrow}
  15182.  
  15183.      InitSignals;
  15184.      errCode := CatchSignal;
  15185.      IF errCode <> 0 THEN BEGIN
  15186.         Debugger;
  15187.         Exit(P);
  15188.      END;
  15189.  
  15190.      SetUpMenus; {set up menus and menu bar}
  15191.      UnLoadSeg(@SetUpMenus); {remove the once-only code}
  15192.  
  15193.      {Custom package initialization stuff}
  15194.      appdispatch := @CustDispatch;
  15195.     CustomInit(69,custhandle); {should test custhandle for NIL and alert             
  15196.                           the user}
  15197.      dlogString := '';
  15198.    ...
  15199.    {etc. with the rest of initialization and the main event loop}
  15200.    END.
  15201.  
  15202.    ; now for the assembly language code
  15203.    ; first, the dispatching and initializing code that must be linked into
  15204.     ; the application
  15205. ; CustomCalling
  15206. ; Custom packages initializing and dispatching
  15207. ;
  15208. ;   Rick Blair    May, 1987
  15209. ;
  15210. ;        PRINT    OFF
  15211. ;        INCLUDE     'Traps.a'
  15212. ;        INCLUDE     'ToolEqu.a'
  15213. ;        INCLUDE     'QuickEqu.a'
  15214. ;        INCLUDE 'SysEqu.a'
  15215. ;        PRINT    ON
  15216.  
  15217.         LOAD    'most.dmp'    ; from a dump of the files above
  15218.  
  15219. appdata        EQU        12
  15220.             
  15221. ;Initialize a custom module
  15222. ; Pascal call format:
  15223. ;  CustomInit(resID:INTEGER;VAR custhandle:Handle);
  15224. ;
  15225. ; This will load the CUST module with the given resource ID, install a
  15226. ; handle to it in custhandle, and set the module’s appdata pointer to
  15227. ; point to the address appaddr.
  15228. ;
  15229. resID        EQU       8
  15230. custhandle   EQU       4
  15231.  
  15232. CustomInit  PROC   EXPORT
  15233.            SUBQ.L  #4,A7    ;make room for handle from GetResource
  15234.            MOVE.L  #'CUST',-(A7)
  15235.            MOVE.W  resID+8(A7),-(A7);resource ID
  15236.            _GetResource
  15237.            MOVE.L  (A7)+,A0
  15238.            MOVE.L  custhandle(A7),A1
  15239.            MOVE.L  A0,(A1)  ;store handle in app’s custhandle global
  15240.                               ;(return with nil handle if GR failed)
  15241.            MOVE.L  (A7),A0  ;get return address
  15242.            ADD.L   #10,A7    ;strip everything
  15243.            JMP     (A0)        ;adieu
  15244.             
  15245.             
  15246. ;Call a custom module
  15247. ;Pascal format:
  15248. ; CustomCall( {parameters as desired} selector: INTEGER; appaddr: Ptr;
  15249. ;              module: Handle);
  15250. ;
  15251. ;This will call the code whose handle is passed on the stack. If the 
  15252. ;application was written in assembly language you would just
  15253. ;dereference the handle and call it directly (you wouldn’t need this at
  15254. ;        all).
  15255. ;
  15256. CustomCall PROC    EXPORT
  15257.            IMPORT  Signal
  15258.            MOVE.L  4(A7),A0  ;get handle
  15259.            MOVE.L  (A0),D0
  15260.            BNE.S   @0        ;if hasna’ been purged, ga’ ahead
  15261.            MOVE.L  A0,-(A7)  ;push handle
  15262.            _LoadResource
  15263.            MOVE.W  ResErr,-(A7)
  15264.            JSR     Signal    ;Signal is a NOP if a zero is passed to it
  15265.            MOVE.L  4(A7),A0  ;handle again
  15266.    ; we don't lock the handle here (we can't save it so we can unlock it
  15267.    ;  later), so it's up to the package to lock/unlock itself
  15268.    @0      MOVE.L  (A0),A0   ;dereference
  15269.            JMP     (A0)      ;call CUST code
  15270.             
  15271.        END
  15272.  
  15273. ; here is the module for the custom package itself
  15274.  
  15275. ; CustomPack
  15276. ; Example custom code package
  15277. ;
  15278. ;   Rick Blair    May, 1987
  15279. ;
  15280. ; This demonstrates the recommend structure of a code module which a 
  15281. ; sophisticated user could add to an existing application which supported
  15282. ; this mechanism. Aside from allowing for multiple routines within the 
  15283. ; module (via a selector), provision is made for calling a routine
  15284. ; dispatcher within the application itself.
  15285.  
  15286. ;Finding text
  15287. ;We support a call to find a string anywhere within a block of text
  15288. ; (selector=0), and one to find the string only as a separate "word"
  15289. ; with spaces around it (selector=1).
  15290. ;PROCEDURE CustomCall(text:Ptr; count:INTEGER; findstr:^STRING;
  15291. ;                selector:INTEGER; appaddr: UNIV Ptr; ourhandle:Handle);
  15292. ;Rather than return a result indicating whether they succeeded or not,
  15293. ;these routines take whatever action is appropriate (the application
  15294. ;may not even know what these routines actually do).
  15295. ;Once a call succeeds or fails, it then takes action by making a call to
  15296. ;one of the services provided by the application. In this case the two
  15297. ;functions provided are just what we need; the ability to select text and
  15298. ;the ability to put up a message saying "Text not found".
  15299.  
  15300.        STRING    ASIS
  15301.             
  15302. ;      PRINT    OFF
  15303. ;      INCLUDE     'Traps.a'
  15304. ;      INCLUDE     'ToolEqu.a'
  15305. ;      INCLUDE     'QuickEqu.a'
  15306. ;      INCLUDE 'SysEqu.a'
  15307. ;      PRINT    ON
  15308.  
  15309.        LOAD    'most.dmp'    ; from a dump of the files above
  15310.  
  15311. CustPack  PROC    EXPORT
  15312.  
  15313.           BRA.S   Entry  ;skip header
  15314.             
  15315.           DC.W    0      ;flags
  15316.           DC.B    'CUST' ;custom add-on code module
  15317.           DC.W    69     ;resource ID (picked by Mr. Peabody & Sherman)
  15318.           DC.W    $10    ;version 1.0
  15319.  
  15320.  
  15321. StackFrame  RECORD {A6Link},DECR
  15322. paramsize   EQU    *-8
  15323. ;    call-specific parameters… (optional)
  15324. text        DS.L   1    ;pointer to text block
  15325. count       DS.W   1    ;word count of characters in text
  15326. findstr     DS.L   1    ;pointer to p-string to find
  15327. ;    selector(word, optional - you might only have 1 call)
  15328. selector    DS.W   1
  15329. fcharsCmd   EQU    1    ; selector for "find characters"
  15330. fwordCmd    EQU    2    ; selector for "find word"
  15331. ;    pointer to app. globals    (long)
  15332. appaddr     DS.L   1
  15333. ;    handle to this resource    (long)
  15334. ourhandle   DS.L   1
  15335. ;    TOS:return address (long)
  15336. return      DS.L   1
  15337. ;the stack link is built off the origin of the saved old A6 on the stack
  15338. A6Link      DS.L   1
  15339. LocalSize   EQU    *
  15340.        ENDR
  15341.  
  15342. ;offsets into our application globals area
  15343. AppGlobals   RECORD   {appdispatch},DECR
  15344. appdispatch  DS.L   1
  15345. paramptr     DS.L   1
  15346. paramword1   DS.W   1
  15347. paramword2   DS.W   1
  15348. ;CUSTerr     DS.W   1    ;if we had possible errors
  15349.        ENDR
  15350.             
  15351. Entry
  15352.        WITH    StackFrame,AppGlobals
  15353.        LINK    A6,#LocalSize
  15354. ;      MOVEM.L …    ;we’d save any non-trashable regs here
  15355. ;first lock us down…
  15356.        MOVE.L    ourhandle(A6),A0
  15357.        _HLock
  15358.  
  15359.        MOVE.W    selector(A6),D0
  15360.        CMP.W    #fcharsCmd,D0
  15361.        BEQ.S    charfind    ;go find characters
  15362.        CMP.W    #fwordCmd,D0
  15363.        BEQ.S    wordfind    ;go find a word
  15364. ;well, M. App didn’t call us with a selector we know, so…
  15365.  
  15366. ;unlock ourselves, clean up, return
  15367. ; (if we wanted to return an error code we could stuff it into the app.
  15368. ;  global area)
  15369. duhn    MOVE.L  ourhandle(A6),A0
  15370.         _HUnLock
  15371. ;       MOVEM.L …             ;restore any registers here
  15372.         UNLK    A6
  15373.         MOVE.L  (A7)+,A0      ;return address
  15374.         ADD.L   #paramsize,A7 ;strip parameters
  15375.         JMP     (A0)
  15376.  
  15377. ;selector codes for calls to application
  15378. hilight  EQU   0    ;highlight characters, please
  15379. notify   EQU   1    ;beep a little
  15380.  
  15381. ;find the string "findstr" anywhere in the block "text"
  15382. charfind
  15383.          JSR     findchars       ;see if findstr is anywhere in text
  15384.          BEQ.S   nofind          ;if not then skip
  15385.          JSR     calcsels        ;compute selstart and selend
  15386. didfind  MOVE.L  appaddr(A6),A0  ;get pointer to appl. globals area
  15387.          MOVE.L  text(A6),paramptr(A0)  ;setup text pointer and…
  15388.          MOVE.W  D0,paramword1(A0)      ;start character position,
  15389.          MOVE.W  D1,paramword2(A0)      ;end character position
  15390.          MOVE.W  #hilight,-(A7)         ;pass proper selector
  15391. goapp    MOVE.L    appdispatch(A0),A0   ;get dispatch address
  15392.          JSR     (A0)    ;call the application to select the range
  15393.          BRA.S   duhn    ;return to application (dejà vu)
  15394.             
  15395. nofind   MOVE.L    appaddr(A6),A0   ;get pointer to appl. globals area
  15396.          LEA       oopstring,A1     ;get pointer to "Not found" message
  15397.          MOVE.L    A1,paramptr(A0)  ;put string pointer in "paramptr"
  15398.          MOVE.W    #notify,-(A7)    ;tell app. to display message
  15399.          BRA.S     goapp
  15400.             
  15401. ;figure selstart and selend
  15402. calcsels NEG.W    D0           ;negate # characters unskipped in text
  15403.          SUBQ.W   #1,D0        ;include 1st character
  15404.          ADD.W    count(A6),D0 ;compute 1st character position for select
  15405.          MOVE.L   findstr(A6) ,A1
  15406.          MOVE.B   (A1),D1     ;get length of string
  15407.          EXT.W    D1
  15408.          ADD.W    D0,D1       ;compute last char. pos. for select
  15409.          RTS
  15410.             
  15411. ;find the characters, but only if surrounded by space (including end or beg.)
  15412. ;we could extend the test to check for other delimiters (";",".",etc.)
  15413. wordfind
  15414.        JSR     findchars
  15415. wloop  BEQ.S   nofind
  15416.        MOVE.W  D0,D2          ;save count of text remaining
  15417.        JSR     calcsels       ;figure start and end offsets
  15418.        MOVE.L  text(A6),A1    ;point to text
  15419.        TST.W   D0             ;start=beginning of text?
  15420.        BEQ.S   @0             ;yep, so it passes
  15421.        CMP.B   #' ',-1(A1,D0) ;preceded by a space?
  15422.        BNE.S   @1             ;nope, keep looking
  15423. @0     CMP.W   count(A6),D1   ;D1=length of text?
  15424.        BEQ.S   didfind        ;yep, so it passes
  15425.        CMP.B   #' ',(A1,D1)   ;followed by a space?
  15426.        BEQ.S   didfind        ;yes, so we’ve found it
  15427.  
  15428. ;this wasn’t paydirt, so keep panning
  15429. @1     MOVE.W  D2,D0          ;restore chars remaining count
  15430.        BMI.S   nofind         ;forget it if we ran out of text
  15431.        JSR     bigloop        ;keep looking
  15432.        BRA.S   wloop            
  15433.             
  15434. ;this code will find the string if it lies anywhere in the text
  15435. findchars  MOVE.L   text(A6),A0    ;point A0 to chars to search
  15436.            MOVE.W   count(A6),D0   ;size of text block
  15437. bigloop    MOVE.L   findstr(A6),A1 ;point A1 to chars to find
  15438.            MOVE.W   (A1)+,D1       ;get length byte and 1st char. (skip ’em)
  15439.            CMP.W    #255,D1
  15440.            BGT.S    @1             ;enter loop if length<>0
  15441.            ADDQ.L   #4,A7          ;strip findchar’s return address
  15442.            BRA      duhn           ;return having done nothing
  15443.             
  15444. ;search for first character
  15445. @0     CMP.B    (A0)+,D1    ;this one match 1st character?
  15446. @1     DBEQ     D0,@0       ;branch until found or done ’em all
  15447.        BNE.S    cnofind     ;skip out if no match on 1st character
  15448.             
  15449.        MOVE.B   -2(A1),D1   ;length of findstr
  15450.        EXT.W    D1
  15451.        SUBQ.W   #1,D1       ;length sans 1st character
  15452.        BEQ.S    cfound      ;if Length(findstr)=1, we’re done
  15453.        CMP.W    D1,D0
  15454.        BLT.S    cnofind     ;fail if findstr is longer than text left
  15455.        MOVE.L   A0,D2       ;save this character position
  15456.        CMP.W    D1,D1       ;force EQuality
  15457.        BRA.S    @3          ;enter loop
  15458.             
  15459. @2      CMP.B    (A0)+,(A1)+ ;match so far?
  15460. @3      DBNE     D1,@2       ;check until mismatch or end of findstr
  15461.  
  15462.         MOVEA.L  D2,A0       ;restore position (cc’s unaffected)
  15463.         BNE.S    bigloop     ;if no match then keep looking
  15464.  
  15465. cfound  MOVEQ    #1,D1       ;return TRUE
  15466.          RTS
  15467.             
  15468. cnofind  SUB.W    D1,D1    ;return FALSE
  15469.             RTS
  15470.  
  15471.  
  15472.         STRING    PASCAL
  15473. oopstring  DC.B    'Pattern not found.'
  15474.  
  15475.         END
  15476.  
  15477.  
  15478. #additions to the resource file
  15479.  
  15480. resource 'DLOG' (129, "Find dialog") {
  15481.     {72, 64, 164, 428},
  15482.     dBoxProc,
  15483.     visible,
  15484.     noGoAway,
  15485.     0x0,
  15486.     129,
  15487.     "Find"
  15488. };
  15489.  
  15490. resource 'DLOG' (130, "Info") {
  15491.     {66, 102, 224, 400},
  15492.     dboxproc, visible, nogoaway, 0x0, 130, ""
  15493. };
  15494.  
  15495. resource 'DITL' (130) {
  15496.      {
  15497. /* 1 */ {130, 205, 150, 284},
  15498.         button {
  15499.             enabled,
  15500.             "OK already"
  15501.         };
  15502. /* 2 */ {8, 32, 120, 296},                /* info */
  15503.         statictext {
  15504.             disabled,
  15505.             ""
  15506.         }
  15507.     }
  15508. };
  15509.  
  15510. resource 'DITL' (129) {
  15511.      {    /* array DITLarray: 4 elements */
  15512.         /* [1] */
  15513.         {64, 48, 84, 121},
  15514.         Button {
  15515.             enabled,
  15516.             "OK"
  15517.         };
  15518.         /* [2] */
  15519.         {64, 231, 84, 304},
  15520.         Button {
  15521.             enabled,
  15522.             "Cancel"
  15523.         };
  15524.         /* [3] */
  15525.         {8, 8, 24, 352},
  15526.         StaticText {
  15527.             disabled,
  15528.             "Find what?"
  15529.         };
  15530.         /* [4] */
  15531.         {32, 8, 48, 352},
  15532.         EditText {
  15533.             disabled,
  15534.             ""
  15535.         }
  15536.     }
  15537. };
  15538.  
  15539. resource 'MENU' (131, "Custom", preload) {
  15540.     131, textMenuProc, 0x3, enabled, "Custom",
  15541.     {
  15542.         "Find Chars…",
  15543.             noicon, "F", nomark, plain;
  15544.         "Find Word…",
  15545.             noicon, "W", nomark, plain
  15546.     }
  15547. };
  15548.  
  15549.  
  15550. type 'CTST' as 'STR ';
  15551.  
  15552. resource 'CTST' (0) {
  15553.     "Custom Application - Version 1.0"
  15554. };
  15555.  
  15556. include "CustomPack.code";
  15557.  
  15558.  
  15559. #  This makefile puts the program together incl. the CUST pack.
  15560.  
  15561. CustomTest        ƒƒ  CustomCalling.a.o CustomTest.p.o ErrSignal.a.o
  15562. # the predefined rule for assembly will build CustomCalling.a.o,
  15563. #  CustomPack.code
  15564.     Link CustomTest.p.o CustomCalling.a.o ErrSignal.a.o ∂
  15565.         "{Libraries}"Interface.o ∂
  15566.         "{Libraries}"Runtime.o ∂
  15567.         "{PLibraries}"Paslib.o ∂
  15568.         -o CustomTest
  15569. CustomPack.code    ƒ    CustomPack.a.o
  15570.     Link CustomPack.a.o -rt CUST=69 -o CustomPack.code
  15571. # Put the resource file together (including the custom code resource)
  15572. CustomTest        ƒƒ    CustomTest.r CustomPack.code
  15573.     Rez CustomTest.r -a -o CustomTest
  15574.  
  15575.  
  15576.  
  15577. æKY 136
  15578. æC #136: Register A5 Within GrowZone Functions
  15579.  
  15580. See also:     The Memory Manager
  15581.               Technical Note #25 — Register A5 Within Trap Patches
  15582.  
  15583. Written by:    Chris Derossi    July 1, 1987
  15584. Updated:                        March 1, 1988
  15585. _______________________________________________________________________________
  15586.  
  15587. If you have a grow zone function, it may get called when a system routine is trying
  15588. to allocate memory. Because this can happen, you can’t be guaranteed that register A5
  15589. will be correct.
  15590.  
  15591. If your grow zone function depends on A5, you should save register A5, load A5 from
  15592. the low-memory global CurrentA5 (a long word at $904), and restore the caller’s A5
  15593. before you exit.
  15594.  
  15595. From high-level languages, you can also use the Operating System Utility calls SetUpA5
  15596. and RestoreA5 (page 386 of Inside Macintosh Volume II). SetUpA5 stores the ‘old’ A5
  15597. on the stack and puts the value stored at CurrentA5 into A5. Make sure to call RestoreA5
  15598. when you’re done so that it can pop the saved value of A5 off the stack.
  15599.  
  15600. Your grow zone function depends on A5 if it does any of the following:
  15601.  
  15602.  • Accesses your application’s global variables (which are stored at negative 
  15603.    offsets from A5).
  15604.  
  15605.  • Accesses the QuickDraw globals. (A5 contains the address of a pointer to the 
  15606.    QuickDraw global variables.)
  15607.  
  15608.  • Makes any ROM trap calls.
  15609.  
  15610.  • Makes any intersegment calls to routines in your application.
  15611.  
  15612. To do any of these, A5 needs to contain the value from CurrentA5. Please note that
  15613. this is different than the method for calling the ROM from trap patches, where A5
  15614. should retain the value it had upon entry to your patch.
  15615.  
  15616.  
  15617.  
  15618. æKY 137
  15619. æC #137: AppleShare 1.1 Server FPMove Bug
  15620.  
  15621. See also:       AppleTalk Filing Protocol
  15622.  
  15623. Written by:     Rich Andrews     June 16, 1987
  15624. Modified by:    Bryan Stearns    July 1, 1987
  15625. Updated:                         March 1, 1988
  15626. _______________________________________________________________________________
  15627.  
  15628. A bug has been discovered in AppleShare 1.1’s implementation of the AppleTalk Filing
  15629. Protocol FPMove call. This bug only affects developers implementing custom workstation
  15630. access code that will access AppleShare 1.1 servers from non-Macintosh systems (such
  15631. as MS-DOS systems); if the guidelines below are not followed, data loss may result.
  15632. _______________________________________________________________________________
  15633.  
  15634. The AppleShare file server supports an AFP call known as FPMove, used to move a file
  15635. or directory tree from one place to another on an AppleShare volume.  In addition to
  15636. moving, the caller can specify a new name for the file or directory being moved; in
  15637. essence, a move and a rename can be accomplished by a single call.
  15638.  
  15639. The AppleShare 1.1 server implements this call as follows: the file is moved from the
  15640. source directory to an invisible holding directory, renamed, then moved to the destination
  15641. directory. The problem occurs when a locked file is moved and renamed in this manner:
  15642. the initial move succeeds, the rename fails, and the file is left in the holding
  15643. directory (essentially lost, as it will be deleted when the server is shut down).
  15644.  
  15645. Macintosh AppleShare 1.1 workstation software never uses the move-and-rename combination,
  15646. so this problem cannot occur on a Macintosh; however, if you’re implementing your own
  15647. workstation-access software for some other machine or operating system, and wish to
  15648. use this feature, you must follow this procedure:
  15649.  
  15650. When a move and rename call comes from the native file system, issue an FPGetFileDirParms
  15651. call to see if the object is a locked file.  If it is, issue an FPSetFileParms call
  15652. to unlock the file.  Then issue the FPMove call, followed by another FPSetFileParms
  15653. call to lock the file again.
  15654.  
  15655. AFP does not allow locked files to be renamed, whereas some native file systems (such
  15656. as MS-DOS) do.  You must therefore preflight for this condition to maintain transparency.
  15657.    
  15658. This problem will be corrected in a future version of the AppleShare server software.
  15659.  
  15660.  
  15661.  
  15662. æKY 138
  15663. æC #138: Using KanjiTalk with a non-Japanese Macintosh Plus
  15664.  
  15665. See also:     KanjiTalk Usage Notes
  15666.               Script Manager Developers Package
  15667.  
  15668. Written by:    Priscilla Oppenheimer    July 1, 1987
  15669. Updated:                                March 1, 1988
  15670. _______________________________________________________________________________
  15671.  
  15672. This Technical Note describes the minor differences between using KanjiTalk with the
  15673. Japanese Macintosh Plus and KanjiTalk with a standard Macintosh Plus.
  15674. _______________________________________________________________________________
  15675.  
  15676. There are two differences between the Japanese Macintosh Plus and the standard Macintosh
  15677. Plus: The Japanese Macintosh Plus has the Kanji 12 and 18 point fonts in ROM and it
  15678. is shipped with the Kana keyboard. It is not necessary to have this keyboard in order
  15679. to use KanjiTalk. (See the KanjiTalk Usage Notes for details on how to use it with a
  15680. non-Kana keyboard.) It is, however, necessary to have 12 point Kanji in order to use
  15681. KanjiTalk; the 18 point Kanji is optional.
  15682.  
  15683. When using KanjiTalk with a standard (non-Japanese) Macintosh, the user supplies
  15684. these fonts on disk and the Macintosh loads them into RAM. At boot time, the Macintosh
  15685. looks for the 12 point Kanji font file in the system folder of the boot disk. If it
  15686. cannot find the font, it will look through the root directory of all mounted volumes.
  15687. (The font has to be at the root level; it cannot be in a folder.) If it still doesn’t
  15688. find the font, it will prompt the user to insert a disk with the font file in the
  15689. root directory. Once KanjiTalk finds the 12 point font, it will go through the same
  15690. process looking for the 18 point font. The user can cancel this search if the optional
  15691. 18 point font is not necessary.
  15692.  
  15693. When KanjiTalk finds the fonts, it loads them into memory. The 12 point font takes up
  15694. approximately 100K of memory and the optional 18 point font takes up approximately
  15695. 250K of memory. The KanjiTalk code itself takes up about 180K of memory. Because the
  15696. fonts take up quite a bit of memory, many applications will not work on a Macintosh
  15697. 512K with the Kanji fonts installed.
  15698.  
  15699. Accessing the fonts from ROM is faster, but we have not noticed any significant speed
  15700. problems when the fonts are accessed from RAM. There is, however, a noticeable difference
  15701. in speed when the Macintosh is booted. It takes a couple of seconds to load the 12
  15702. point font and about 6 seconds to load the 18 point font.
  15703.  
  15704. Note that the Japanese Macintosh is unique; Apple has not produced other foreign
  15705. versions of the Macintosh for different scripts. The introduction of the Arabic Interface
  15706. System, for example, did not include an Arabic ROM version.
  15707.  
  15708.  
  15709.  
  15710. æKY 139
  15711. æC #139: Macintosh Plus ROM Versions
  15712.  
  15713. Written by:    Cameron Birse     July 1, 1987
  15714. Updated:                         March 1, 1988
  15715. _______________________________________________________________________________
  15716.  
  15717. Readers Digest condensed version of Macintosh Plus ROM history, or the truth according
  15718. to Bo3bdar the everpresent:
  15719.  
  15720. 1st version (Lonely Hearts, checksum 4D 1E EE E1):
  15721.    Bug in the SCSI driver; won’t boot if external drive is turned off. We only 
  15722.    produced about one and a half months worth of these.
  15723.  
  15724. 2nd version (Lonely Heifers, checksum 4D 1E EA E1):
  15725.    Fixed boot bug. This version is the vast majority of beige Macintosh Pluses.
  15726.  
  15727. 3rd version (Loud Harmonicas, checksum 4D 1F 81 72):
  15728.    Fixed bug for drives that return Unit Attention on power up or reset. 
  15729.    Basically took the SCSI bus Reset command out of the boot sequence loop, so 
  15730.    it will only reset once during boot sequence. This version shipped with the 
  15731.    platinum Macintosh Pluses.
  15732.  
  15733. And Bo3bdar saith: “Thou shalt not rev them damn ROMs no more!”
  15734.  
  15735. Later that same day...
  15736.  
  15737. Bo3bdar Saith Also:
  15738.  
  15739.     Lonely Heifer was about a 2 byte change,
  15740.     Loud Harmonica was about 30 byte change.
  15741.     No other bug fixes in SCSI or elsewhere.
  15742.     Modified object code directly.
  15743.     Not possible to get a specific ROM since they are all the same part number.
  15744.     Shouldn’t rely on a specific ROM, there will be no upgrade.
  15745.     Bo3b Bo3b a boola, a wiff Ba2m Bo1om.
  15746.  
  15747.  
  15748.  
  15749. æKY 140
  15750. æC #140: Why PBHSetVol is Dangerous
  15751.  
  15752. See also:       The File Manager
  15753.  
  15754. Written by:     Chris Derossi     July 1, 1987
  15755. Updated:                          March 1, 1988
  15756. _______________________________________________________________________________
  15757.  
  15758. This note explains PBHSetVol, and why its use is not recommended.
  15759. _______________________________________________________________________________
  15760.  
  15761. PBHSetVol, like SetVol and PBSetVol, allows you to set the current default volume and
  15762. directory to be used with subsequent File Manager calls. Unlike SetVol and PBSetVol,
  15763. though, PBHSetVol lets you specify the volume and the directory separately, using the
  15764. ioVRefNum and ioWDDirID fields.
  15765.  
  15766. PBHSetVol lets you specify a WDRefNum for the ioVRefNum in addition to a partial
  15767. pathname in ioNamePtr.  PBHSetVol will start at the specified working directory and
  15768. use the partial pathname to determine the final directory. This directory might not
  15769. correspond to an already existing working directory, so the File Manager cannot refer
  15770. to this directory with a WDRefNum. Instead it must use the actual volume refNum and
  15771. the dirID number (which is assigned when the directory is created, and doesn’t change).
  15772.  
  15773. The net effect of all of this is, if you call PBHSetVol, the File Manager stores the
  15774. actual volume RefNum as the default volume,  and the default DirID separately. This
  15775. happens on all calls to PBHSetVol. Subsequent calls to GetVol or PBGetVol will return
  15776. only the volume RefNum in the ioVRefNum field of the parameter block. If any code
  15777. tries to use the RefNum returned by GetVol, it will be accessing the root of the
  15778. volume, and not the current default directory as expected.
  15779.  
  15780. This is particularly nasty for desk accessories because they don’t know that your
  15781. code has called PBHSetVol and they don’t get what they expect if they call GetVol.
  15782.  
  15783. It is therefore recommended that you avoid using PBHSetVol because of this side effect.
  15784. None of the other ‘H’ calls that allow you to specify a DirID do this, so they’re
  15785. still OK.
  15786.  
  15787.  
  15788.  
  15789. æKY 141
  15790. æC #141: Maximum Number of Resources in a File
  15791.  
  15792. See also:       The Resource Manager
  15793.  
  15794. Written by:     Cameron Birse     July 1, 1987
  15795. Updated:                          March 1, 1988
  15796. _______________________________________________________________________________
  15797.  
  15798. This note describes the limitation of the number of resources in a single resource
  15799. file.
  15800. _______________________________________________________________________________
  15801.  
  15802. There is a limit to the number of the resources in a single resource file. This limitation
  15803. is imposed by the resource map. There are two bytes at the end of the resource map
  15804. which are the offset from the beginning of the resource map to the beginning of the
  15805. resource names list. If there is only one type of resource, then the overhead, from
  15806. the beginning of the resource map to the beginning of the reference list, is 38 bytes.
  15807. Since the offset is a two byte value, and is a signed number, its highest possible
  15808. value is 32767. This is the limitation. If you subtract 38 bytes for the overhead,
  15809. and divide the difference by 12 (the number of bytes for each reference) you get
  15810. about 2727.4—the limit to the number of resources in a single file is 2727.
  15811.  
  15812. The Resource Manager was not intended to manage large numbers of resources, and as a
  15813. result, its performance is particularly bad with many resources. Because of these
  15814. restrictions, we recommend that developers avoid using the Resource Manager as a data
  15815. base tool.
  15816.  
  15817.  
  15818.  
  15819. æKY 142
  15820. æC #142: Avoid Use of Network Events
  15821.  
  15822. See also:       AppleTalk Manager
  15823.  
  15824. Written by:     Bryan Stearns     July 1, 1987
  15825. Updated:                          March 1, 1988
  15826. _______________________________________________________________________________
  15827.  
  15828. Future System software enhancements will not support network events. This note gives
  15829. hints on weaning your application from the use of network events.
  15830. _______________________________________________________________________________
  15831.  
  15832.  
  15833. What are network events?
  15834.  
  15835. When the Event Manager was designed, an event number was reserved for future support
  15836. of “network events”. Later, when the AppleTalk Pascal Interfaces were written, a
  15837. completion routine was created that, when an asynchronous AppleTalk operation finished,
  15838. would post an event using networkEvt in the evtNum field.
  15839.  
  15840. Only the AppleTalk Pascal Interfaces generate network events. Assembly-language users
  15841. of the AppleTalk drivers (and those who called the AppleTalk drivers directly from
  15842. high-level languages, using PBControl calls) either provide a completion routine of
  15843. their own, or poll the ioResult field of the parameter block passed with the call
  15844. (when ioResult became negative or zero, the call is complete).
  15845.  
  15846.  
  15847. Why not use network events?
  15848.  
  15849. In some cases, network events can be lost. If the Event Manager finds that the queue
  15850. is full while posting an event, it discards the oldest event. In a situation (such as
  15851. a server) where multiple asynchronous ATP requests may complete at once, there is a
  15852. chance that events may be dropped off the end of the queue. This is more likely if
  15853. the same machine is also handling user-interface events (like keypresses and mouse
  15854. actions).
  15855.  
  15856. Also, in developing improvements to our operating system, it has become apparent that
  15857. to continue support of network events, we would have to compromise future enhancements
  15858. to our system. So, future versions of the Macintosh operating system may ignore network
  15859. events instead of passing them to the application.
  15860.  
  15861.  
  15862. How can I tell that my calls have completed without using network events?
  15863.  
  15864. As described on page II-275 of Inside Macintosh, you can poll the abResult field of
  15865. the call’s ABusRecord; when this value becomes negative or zero, the call has completed.
  15866. You can do this in your main event loop.
  15867.  
  15868. With this technique, you can ignore any network events returned by GetNextEvent,
  15869. since the AppleTalk Pascal Interfaces will be posting events anyway. If your application
  15870. starts enough asynchronous operations, it’s possible that their network events will
  15871. cause other non-network events to be lost. To prevent this, you should call FlushEvents(networkMask,0)
  15872. frequently to purge any accumulated network events from the event queue.
  15873.  
  15874. You may also consider using the new preferred high-level interface calls; see Technical
  15875. Note #132 for more information.
  15876.  
  15877.  
  15878.  
  15879. æKY 143
  15880. æC #143: Don’t Call ADBReInit on the SE with System 4.1
  15881.  
  15882. See also:       The Apple Desktop Bus
  15883.  
  15884. Written by:     Mark Baumwell     July 1, 1987
  15885. Updated:                          March 1, 1988
  15886. _______________________________________________________________________________
  15887.  
  15888. Because of a bug (which causes auto-repeat) in the ROM version of the Macintosh SE
  15889. keyboard driver, a patch was placed in System 4.1. If ADBReInit is called, the ROM
  15890. version of the keyboard driver will be reloaded, and the RAM version of the driver
  15891. with the patches will not be used. Therefore, it is recommended that ADBReInit not be
  15892. called on the Macintosh SE until the problem is fixed. (There is no need to call
  15893. ADBReInit.) This problem will not occur with the Macintosh II ROM version of the
  15894. keyboard driver.
  15895.  
  15896. æKY 144
  15897. æC #144: Macintosh II Color Monitor Connections
  15898.  
  15899. Revised by:    Wayne Correia                                      February 1990
  15900. Written by:    Mark Baumwell                                          July 1987
  15901.  
  15902. This Technical Note describes how to connect the Macintosh II Video Card to third-party
  15903. monitors. Changes since March 1988:  Updated for newer Macintosh II Video Cards,
  15904. including the Macintosh IIci On-Board Video (OBV).
  15905. _______________________________________________________________________________
  15906.  
  15907. Following are the pinout descriptions of the Macintosh II Video Cards and the
  15908. Macintosh IIci On-Board Video (OBV):
  15909.  
  15910.         Macintosh II
  15911.         Video Card Pin    Signal Name
  15912.         ______________________________________________________________
  15913.         1,6,11,13,14      Ground
  15914.         2                 Red
  15915.         3                 C-Sync (composite sync)
  15916.         4                 Monitor ID, Bit 1 (ground this pin to signal
  15917.                           that a 640 x 480 monitor is connected)
  15918.         5,12              Green (with sync)
  15919.         7                 Monitor ID, Bit 2
  15920.         9                 Blue
  15921.         10                Monitor ID, Bit 3
  15922.         8,15              Not connected
  15923.         ______________________________________________________________
  15924.  
  15925. Note:  The Macintosh II High-Resolution Display Video Card is the newer
  15926.        replacement for the original four- and eight-bit Macintosh II
  15927.        Video Card (M0211 and M5640).  This new card is sold in four- and
  15928.        eight-bit configurations (M0322 and M0324, respectively).
  15929.  
  15930. Note:  The newer Macintosh II Video Cards and Macintosh IIci OBV require
  15931.        that pin 4 (Monitor ID, Bit 1) be connected to Ground to signal
  15932.        the connection of a 640 x 480 monitor.  Do not connect pins 7 or
  15933.        10 as they are unused on original Macintosh II Video Cards and
  15934.        there are built-in pullup resistors on the newer Macintosh II
  15935.        Video Card and Macintosh IIci to terminate these pins when not in use.
  15936.  
  15937.  
  15938. Sony Multiscan (CPD-1302)
  15939.  
  15940. To connect a Macintosh II to a Sony Multiscan monitor, you need to make an
  15941. adapter cable from the video card to the monitor (which has a 9-pin D-type
  15942. connector).  Following is the pinout description for the adapter cable
  15943. (using the automatic sync-on-green configuration):
  15944.  
  15945.                 Macintosh II      Sony
  15946.                 Video Card Pin    Pin    Signal Name
  15947.                 _____________________________________
  15948.                 1                 1      Ground
  15949.                 2                 3      Red
  15950.                 4                 1      Ground
  15951.                 5                 4      Green (sync)
  15952.                 9                 5      Blue
  15953.                 _____________________________________
  15954.  
  15955.  
  15956. NEC MultiSync (JC-140IP3A)
  15957.  
  15958. To connect a Macintosh II to a NEC MultiSync monitor, you need to make an
  15959. adapter cable from the video card to the monitor (which has a 9-pin D-type
  15960. connector).  Following is the pinout description for the adapter cable
  15961. (using the automatic sync-on-green configuration):
  15962.  
  15963.                 Macintosh II      NEC
  15964.                 Video Card Pin    Pin        Signal Name
  15965.                 _________________________________________
  15966.                 1                 6,7,8,9    Ground
  15967.                 2                 1          Red
  15968.                 4                 6,7,8,9    Ground
  15969.                 5                 2          Green (sync)
  15970.                 9                 3          Blue
  15971.                 _________________________________________
  15972.  
  15973. The monitor must be set to Analog mode and Manual mode.  This adaptor cable
  15974. also works with an equivalent monitor such as the Taxan Super Vision 770.
  15975.  
  15976.  
  15977. æKY 145
  15978. æC #145: Debugger FKEY
  15979.  
  15980. Written by:    Mark Baumwell    July 1, 1987
  15981. Updated:                        March 1, 1988
  15982. _______________________________________________________________________________
  15983.  
  15984. It is often more convenient to enter the debugger using the keyboard rather than
  15985. having to reach around to press the interrupt switch. This technical note shows how
  15986. to make a simple FKEY that will trap to the debugger.
  15987. _______________________________________________________________________________
  15988.  
  15989. This technical note shows how to make a simple FKEY that will trap to the debugger.
  15990. It is written in MPW Assembler. The assembler source is given below.
  15991.  
  15992.  
  15993. MPW Assembler source file listing:
  15994.  
  15995. ; File: DebugKey.a
  15996. ;
  15997. ; An FKEY to invoke the debugger via command-shift-8
  15998. ;
  15999. ; To build this:
  16000. ;    Asm DebugKey.a
  16001. ;    Link DebugKey.a.o -o "{SystemFolder}System" -rt FKEY=6
  16002.  
  16003. DebugKey    MAIN
  16004.  
  16005.         BRA.S    CallDB    ;Invoke the debugger
  16006.  
  16007.         ;standard header
  16008.  
  16009.         DC.W    $0000    ;flags
  16010.         DC.L    'FKEY'   ;'FKEY' is 464B4559 hex
  16011.         DC.W    $0008    ;FKEY Number
  16012.         DC.W    $0000    ;Version number
  16013.  
  16014. CallDB        DC.W    $A9FF    ;Debugger trap
  16015.         RTS
  16016.  
  16017.         END
  16018.  
  16019.  
  16020.  
  16021. æKY 146
  16022. æC #146: Notes on MPW Pascal’s -mc68881 Option
  16023.  
  16024. See also:     Apple Numerics Manual
  16025.               MPW Pascal Reference
  16026.  
  16027. Written by:    Bryan Stearns    July 1, 1987
  16028. Updated:                        March 1, 1988
  16029. _______________________________________________________________________________
  16030.  
  16031. For improved performance, the MPW Pascal compiler (version 2.0 and newer) represents
  16032. Extended values in 96 bits (instead of 80, as with software SANE) when the -mc68881
  16033. option is used. This can cause problems when using non-SANE system calls that expect
  16034. 80-bit Extended values.
  16035. _______________________________________________________________________________
  16036.  
  16037.  
  16038. The Pascal Compiler and Extended values
  16039.  
  16040. The MPW 2.0 Pascal compiler provides a command-line option, -mc68881, to generate
  16041. inline code to use the Motorola 68881 Floating-Point Coprocessor 
  16042. (included with Macintosh II). This allows you to sacrifice compatibility with other
  16043. Macintosh systems (those not equipped with the 68020/68881 combination) in exchange
  16044. for much-increased numeric performance.
  16045.  
  16046. When this option is used, the compiler stores all Extended values in the 96-bit format
  16047. used by the 68881, instead of the 80-bit software SANE format:
  16048.  
  16049. •••Click on the Illustration button below, and refer to Figure 1.••• 
  16050.  
  16051. This affects all procedures that accept floating-point values as arguments, since all
  16052. floating-point arguments are converted to Extended before being passed, no matter how
  16053. they’re declared (that is, Real, Single, Double, or Comp).
  16054. You must link with a special SANELib library file (“SANE881Lib.o”) when compiling
  16055. with this option; the interface source file “SANE.p” contains conditional-compilation
  16056. statements to make sure that the correct library’s interface is compiled. In this
  16057. situation, SANE procedures are used for certain transcendental functions only (see
  16058. note below), and these functions (in 
  16059. “SANE881Lib.o”) expect their Extended parameters in 96-bit format.
  16060.  
  16061. However, numeric routines that are not compiled by Pascal (such as any assembly-language
  16062. routines that you’ve written) have no way of finding out that their parameters will
  16063. be in 96-bit format. If you don’t wish to (or can’t) rewrite these routines for 96-bit
  16064. values, you can use the SANELib routines X96ToX80 and X80ToX96 to convert back and
  16065. forth; it might be simplest to define a new interface routine to make the conversions
  16066. happen automatically:
  16067.  
  16068.    {An assembly-language function that accepts }
  16069.    {an 80-bit Extended parameter and returns an}
  16070.    {80-bit result (We’ve changed the types to  }
  16071.    {reflect that these are not 96-bit values). }
  16072.  
  16073.    FUNCTION FPFunc(x: Extended80): Extended80; EXTERNAL;
  16074.  
  16075.    {Given that we’re compiling in -mc68881 mode,}
  16076.    {call our assembly-language function. Note   }
  16077.    {that the compiler thinks that Extended      }
  16078.    {values are 96 bits long, but FPFunc wants an}
  16079.    {80-bit parameter and produces an 80-bit     }
  16080.    {result; we convert.                         }
  16081.  
  16082.    FUNCTION FPFunc96(x: Extended): Extended; {x is a 96-bit extended!}
  16083.    BEGIN
  16084.      {convert our argument, call the function, then convert the result}
  16085.      MyFPFunc := X80ToX96(FPFunc(X96ToX80(x))); {call the real FPFunc}
  16086.    END;
  16087.  
  16088. It’s best to avoid compiling some parts of an application with the -mc68881 option
  16089. on, and other parts with it off; very strange bugs can occur if you try this. Note
  16090. that 80-bit code and 96-bit code cannot reference the same Extended variables. There
  16091. is no way to tell whether a given stored value is in 80-bit format or 96-bit format.
  16092.  
  16093.  
  16094. SANE on Macintosh II
  16095.  
  16096. The version of SANE provided in the Macintosh II ROM recognizes the presence of the
  16097. 68881 and uses it for most calculations automatically. SANE still expects 
  16098. (and produces) 80-bit-format Extended values; it converts to and from 96-bit format
  16099. internally when using the 68881.
  16100.  
  16101.  
  16102. A Note about 68881 Accuracy and Numeric Compatibility
  16103.  
  16104. SANE is more accurate than the 68881 when calculating results of certain functions
  16105. (Sin, Cos, Arctan, Exp, Ln, Tan, Exp1, Exp2, Ln1, and Log2). To maintain this accuracy,
  16106. SANE doesn’t use 68881 instructions to directly perform these functions. Thus, the
  16107. results you’ll get from SANE calculations will still be identical on all Macintosh
  16108. systems.
  16109.  
  16110. To preserve this numeric compatibility with other SANE implementations, MPW Pascal
  16111. normally doesn’t generate inline 68881 calls to the above functions, even when the
  16112. -mc68881 option is used; instead, it generates SANE calls to accomplish them.  If
  16113. you’re willing to sacrifice numeric compatibility to gain extra speed, you can override
  16114. this compiler feature with the compile-time variable Elems881; include the option “-d
  16115. Elems881=TRUE” on the compiler command line to cause the compiler to generate direct
  16116. 68881 instructions.
  16117.  
  16118. For  certain other transcendental functions provided by the 68881 that aren’t provided
  16119. by SANE, MPW Pascal will generate direct 68881 calls if the -mc68881 option is on,
  16120. independent of the setting of the Elems881 variable. These operations are Arctanh,
  16121. Cosh, Sinh, Tanh, Log10, Exp10, Arccos, Arcsin, and Sincos.
  16122.  
  16123.  
  16124.  
  16125. æKY 147
  16126. æC #147: Finder Notes: “Get Info” Default & Icon Masks
  16127.  
  16128. See also:       Technical Note #48 — Bundles
  16129.  
  16130. Written by:     Bryan Stearns     July 1, 1987
  16131. Updated:                          March 1, 1988
  16132. _______________________________________________________________________________
  16133.  
  16134. The Finder has undergone a couple of changes you should keep in mind when creating
  16135. the “bundle” information for your application.
  16136. _______________________________________________________________________________
  16137.  
  16138.  
  16139. Creator String will be the default “Get Info” comment text
  16140.  
  16141. The “creator” (or “signature”) string (contained in a resource whose type is your
  16142. application’s four-character creator type, and whose ID is 0) will be used as the
  16143. default for the comment text displayed by the Finder’s “Get Info” command. Thus, you
  16144. should set up this string (when you build your application) to contain the name of
  16145. your program and a version number and date.
  16146.  
  16147.  
  16148. Icon Masks should match their icons
  16149.  
  16150. Your application’s BNDL (“bundle”) resource ties the file types that it uses for its
  16151. documents with the icons to be displayed for those documents. For each icon, a “mask”
  16152. icon is also provided; this mask is used to punch a hole in the gray desktop before
  16153. drawing the icon.
  16154.  
  16155. Some applications use a cleverly-modified mask to provide an “action icon” that looks
  16156. different when it’s selected. This causes problems; it is important that the mask be
  16157. what it’s supposed to be (a solid black copy of the icon).
  16158.  
  16159.  
  16160.  
  16161. æKY 148
  16162. æC #148: Suppliers for Macintosh II Board Developers
  16163.  
  16164. See also:    Designing Cards and Drivers for the Macintosh II and Macintosh SE
  16165.  
  16166. Written by:    Mark Baumwell    July 1, 1987
  16167. Updated:                        March 1, 1988
  16168. _______________________________________________________________________________
  16169.  
  16170. This note lists suppliers of parts that may be helpful for Macintosh II board developers.
  16171. If your company supplies these parts, but is not listed here, please send a message
  16172. to us (at the address on Technical Note #0) and we’ll include you in the next revision
  16173. of this technical note.
  16174. _______________________________________________________________________________
  16175.  
  16176. This is a list of companies that supply the Macintosh II expansion port cover 
  16177. (p/n 805-5064-05) (Foldout 2 in Designing Cards and Drivers or the Macintosh II and
  16178. Macintosh SE ). It is not intended to be an endorsement or an indication of quality;
  16179. it is just our list of known suppliers.
  16180.  
  16181.     Galgon Industries, Inc.
  16182.     37399 Centralmont Place
  16183.     Fremont, CA 94536
  16184.     Attn: Ron Haddox—General Sales
  16185.     (415) 792-8211
  16186.  
  16187.     Vector Electronics
  16188.     12460 Gladstone Ave
  16189.     Sylmar, CA 91342
  16190.     (818) 365-9661
  16191.     FAX# 818-356-5718
  16192.     Attn: Norm Brunell
  16193.  
  16194.     North American Tool and Die
  16195.     999 Beecher Street
  16196.     San Leandro, CA 94577
  16197.     (415) 632-9263
  16198.     Attn: Glenn Erikson
  16199.  
  16200. In addition to supplying the expansion port cover, Vector Electronics supplies Macintosh
  16201. II NuBus extender boards and prototyping boards.
  16202.  
  16203.  
  16204.  
  16205. æKY 149
  16206. æC #149: Document Names and the Printing Manager
  16207.  
  16208. See also:     The Printing Manager
  16209.               Technical Note #122 — Device-Independent Printing
  16210.     
  16211. Written by:    Bryan Stearns    July 1, 1987
  16212. Updated:                        March 1, 1988
  16213. _______________________________________________________________________________
  16214.  
  16215. Our compatibility testing for LaserShare (Apple’s LaserWriter spooler) has turned up
  16216. a number of applications that do not provide the Printing Manager with a document
  16217. name; although this feature is not required, it is nice for users that share printers.
  16218. _______________________________________________________________________________
  16219.  
  16220. Some printers (usually those that are shared between many users, like the LaserWriter)
  16221. can provide the names of the users who are printing and the documents that are being
  16222. printed to others interested in using the printer. 
  16223.  
  16224. If the chosen printer uses a document name, the Printing Manager gets the name from
  16225. the frontmost window’s title. If there is no front window, or if the window’s title
  16226. is empty, the Printing Manager defaults to “unknown.”
  16227.  
  16228. This method was chosen because it works most transparently to applications; however,
  16229. it won’t work if your application doesn’t display windows when printing (for instance,
  16230. many applications that use windows for their documents do not open their documents
  16231. when printing in response to a Finder “Print” command).
  16232.  
  16233. As a general solution to this problem, you can put up a window containing a message
  16234. like “Press <Command-Period> to cancel printing”, and give it the document’s title.
  16235. If the window  is one that doesn’t have a title bar (like dBoxProc), this title will
  16236. not be displayed. MacApp takes this approach. If for some reason you don’t want to
  16237. put up a visible window, you can create a tiny window and hide it behind the menu
  16238. bar: for instance, global coordinates of 
  16239. (1,1,2,2). Make sure you use a plainDBox, so that no title will be drawn 
  16240. (otherwise, in the unlikely case that a user is using a Macintosh II with two stacked
  16241. screens, main screen on the bottom, the title might be visible on the upper screen).
  16242.  
  16243. Since the Printing Manager checks the name at PrValidate time, call PrValidate after
  16244. PrCloseDoc and before the next PrOpenDoc, if you want unique names. 
  16245.  
  16246. A number of applications set the document name in the print record directly. You
  16247. should not do this because a) not all printers support this field, and b) none are
  16248. guaranteed to support it in the future. (Apple does not guarantee that internal fields
  16249. of the Printing Manager’s data structures will remain the same; the Printing Manager
  16250. is targeted for substantial internal change!)
  16251.  
  16252.  
  16253.  
  16254. æKY 150
  16255. æC #150: Macintosh SE Disk Driver Bug
  16256.  
  16257. Written by:     Mark Baumwell    July 1, 1987
  16258. Updated:                         March 1, 1988
  16259. _______________________________________________________________________________
  16260.  
  16261. A bug in the Macintosh SE ROMs causes the top drive to be slower than the bottom one
  16262. in two-drive machines. This bug is fixed in System 4.2 and newer.
  16263.  
  16264.  
  16265. æKY 151
  16266. æC #151: System Error 33, “zcbFree has gone negative”
  16267.  
  16268. See also:       The Memory Manager
  16269.  
  16270. Written by:     Bryan Stearns    July 1, 1987
  16271. Updated:                         March 1, 1988
  16272. _______________________________________________________________________________
  16273.  
  16274. System 3.2 introduced a new system error, ID=33, generated by the Memory Manager when
  16275. it notices that a heap had been corrupted in a certain way. This error is listed in
  16276. the file “SysErr.a” as “negZcbFreeErr”.
  16277. _______________________________________________________________________________
  16278.  
  16279. The Memory Manager will trigger an “ID=33” system error when, during some operation
  16280. which scans the objects in the heap, it sees that its running count of free bytes
  16281. (zcbFree, an internal value) has become negative (an impossible feat in normal operation).
  16282. This is nearly always caused by writing zeros past the end of one of the Memory Manager’s
  16283. heap blocks (overwriting and corrupting the next block’s header, making it appear to
  16284. be a free block).
  16285.  
  16286. If you get this error, use a debugger (like Macsbug or TMON) when you attempt to
  16287. reproduce the error, to check the consistency of the heap up to the point where the
  16288. error occurs. You may need to do this repeatedly until you isolate the operation that
  16289. is corrupting the heap.
  16290.  
  16291. Note that although the heap may become corrupted during a system call, this doesn’t
  16292. mean you’ve found a bug in the ROM; your code could be passing incorrect or invalid
  16293. parameters to this or a previous system call, or could have corrupted a data structure
  16294. used by a system call. More debugging is usually in order in this case; tools like
  16295. Discipline (included in TMON; also available from users’ groups and electronic services)
  16296. can help detect invalid parameters in system calls. Also, there is a Macsbug command,
  16297. AH, that can check the consistency of the heap on every system call. See the documentation
  16298. that came with your debugger to see what special features it offers.
  16299.  
  16300.  
  16301. A note about “SysErr.a”
  16302.  
  16303. Technical Support is often asked for an up-to-date list of error codes. In general,
  16304. this is provided in “SysErr.a”, the file of error numbers shipped with the most current
  16305. version of MPW. Admittedly, the documentation value of “SysErr.a” is sometimes low
  16306. (as in the case of negZCBFreeErr), but it may give you a clue as to what the error
  16307. might mean.
  16308.  
  16309.  
  16310.  
  16311. æKY 152
  16312. æC #152: Using Laser Prep Routines
  16313.  
  16314. See also:     The Printing Manager
  16315.               PostScript Language Reference Manual
  16316.               Technical Note #122 — Device-Independent Printing
  16317.  
  16318. Written by:    Ginger Jernigan    July 1, 1987
  16319. Updated:                          March 1, 1988
  16320. _______________________________________________________________________________
  16321.  
  16322. This technical note addresses the issues involved in depending on the procedures and
  16323. constants defined in the Laser Prep dictionary.
  16324. _______________________________________________________________________________
  16325.  
  16326. When you are writing an application that uses PostScript heavily, it is very tempting
  16327. to call the procedures already defined in the Laser Prep dictionary, rather than
  16328. taking up the space in the printer’s memory with PostScript procedures of your own.
  16329. Or, if a procedure in the dictionary doesn’t do what you need it to do, it is tempting
  16330. to go in and change it to do what you really want.
  16331.  
  16332. Unfortunately, we cannot guarantee that either the name or the function of a particular
  16333. procedure in the dictionary will stay the same when the LaserWriter driver changes.
  16334. In addition, some procedures may become obsolete and go away when the driver changes.
  16335. Since the Laser Prep dictionary is considered part of the source for the LaserWriter
  16336. driver, Apple reserves the right to make any changes to it that are deemed necessary.
  16337.  
  16338. Because we cannot guarantee the permanence of the contents of the Laser Prep dictionary,
  16339. relying on its contents can pose a significant compatibility problem. If you rely on
  16340. the procedures defined in the Laser Prep dictionary, your application will have to be
  16341. revised every time Apple releases a new LaserWriter driver.
  16342.  
  16343. If you feel that you absolutely must use or modify procedures in the Laser Prep dictionary,
  16344. you must always check the version that is loaded into the printer before you print.
  16345. This allows your application to take appropriate action if the version of the dictionary
  16346. that has been downloaded to the printer isn’t one that you know about.
  16347.  
  16348.  
  16349. How To Check The Laser Prep Dictionary Version
  16350.  
  16351. To determine the version of Laser Prep that the printer may contain, you have to
  16352. communicate with the printer using the Printer Access Protocol (PAP); you can’t just
  16353. send your query through the LaserWriter driver because there is no way to get an
  16354. answer back. The object code and documentation for PAP are available from Apple’s
  16355. Licensing department.
  16356.  
  16357. To determine whether the dictionary has been downloaded and whether it is the right
  16358. one, send the following PostScript code to the printer:
  16359.  
  16360.     %!PS-Adobe-1.2 Query
  16361.     %%Title: Query to establish Laser Prep ProcSet version propriety
  16362.     %%?BeginProcSetQuery: AppleDict md xx
  16363.     /md where{
  16364.     /md get /av get cvi xx eq
  16365.     {(1)}{(2)}ifelse}
  16366.     {(0)}ifelse = flush
  16367.     %%?EndProcSetQuery: unknown
  16368.     %%EOF
  16369.  
  16370. md is the name of the Apple dictionary and xx is the version number you want.
  16371.  
  16372. NOTE: /av is a constant in the md dictionary which contains the dictionary’s version
  16373. number. This is the only object in the dictionary whose name and function are guaranteed
  16374. not to change.
  16375.  
  16376. From the printer you will receive a string. If the string returned begins with 
  16377. ‘%%’, it is a Status response. You can ignore it and wait for another string.
  16378.  
  16379. If the response is ‘0’, the dictionary hasn’t been downloaded yet; you will need to
  16380. determine how to best handle this situation for your application.
  16381.  
  16382. If the response is ‘1’, the printer is loaded with the correct version of the dictionary.
  16383.  
  16384. If the response is ‘2’, then the dictionary exists but it isn’t the version you need.
  16385. In this case you need to either let the user know, or proceed in as standard a fashion
  16386. as possible, without calling or modifying routines contained in the Laser Prep dictionary.
  16387.  
  16388.  
  16389. Translating PostScript Files
  16390.  
  16391. Some applications interpret the PostScript files that are generated by the LaserWriter
  16392. driver when the user presses command-F (generates document only) or command-K (generates
  16393. dictionary and document) after clicking on the OK button in the Job dialog. A typical
  16394. application might translate these PostScript files into another page description
  16395. language.
  16396.  
  16397. This kind of application requires intimate knowledge of the contents of the dictionary
  16398. in order to be able to do the translation, because it may have to expand the procedures
  16399. used to their actual values before it can then translate the PostScript to another
  16400. language. This poses a significant compatibility problem. Since we cannot guarantee
  16401. that the contents of the dictionary will not change, these types of applications will
  16402. have to be revised every time we release a new version of the LaserWriter driver.
  16403. Also, there is no way to know which version of the LaserWriter driver generated the
  16404. PostScript file the application is interpreting. You will have to require that a
  16405. particular version of the LaserWriter driver be used to generate the PostScript files
  16406. that your application will interpret.
  16407.  
  16408.  
  16409. Printer Independence
  16410.  
  16411. Applications that are written to take advantage of the routines in the Laser Prep
  16412. dictionary are, of course, highly device dependent. Being device dependent can drastically
  16413. reduce your chances of being compatible with future printer-type devices. For a more
  16414. detailed discussion of this issue, please refer to Technical Note #122.
  16415.  
  16416.  
  16417.  
  16418. æKY 153
  16419. æC #153: Changes in International Utilities and Resources
  16420.  
  16421. See also:     The International Utilities
  16422.               The Script Manager
  16423.         
  16424. Written by:    Priscilla Oppenheimer    July 1, 1987
  16425. Updated:                                March 1, 1988
  16426.  
  16427. _______________________________________________________________________________
  16428.  
  16429. The International Utilities package and the international resources have been changed
  16430. with System file 4.1 to take advantage of the Script Manager.
  16431. _______________________________________________________________________________
  16432.  
  16433.  
  16434. INTL vs. itl
  16435.  
  16436. In the past, there were two INTL resources in the System file, INTL 0 and 
  16437. INTL 1, which contained international formatting options. Starting with System 4.1,
  16438. itl0 and itl1 replace INTL 0 and INTL 1. (INTL 0 and INTL 1 are still included with
  16439. the System file so that applications that do a GetResource for them will still find
  16440. them. Only these erroneous applications that use GetResource will access these resources
  16441. now; the System will no longer look at them.) There can now be a set of international
  16442. resources (itls) for each script that is installed. That is, where once there was one
  16443. resource type (INTL) with IDs 0 and 1 there are now many different resource types
  16444. (itl0, itl1, itl2, itlb, itlc, KCHR) with distinct resource IDs for the various countries.
  16445. The U.S. System file uses resource ID 0 for all itl resources.
  16446.  
  16447. If your existing application calls IUGetIntl to get the appropriate international
  16448. resource, you will have no problems under System 4.1. If, however, you call GetResource
  16449. on INTL 0 or INTL 1 and then modify them, and expect that the System will use the
  16450. modified resource, you will no longer work correctly. If your application has been
  16451. released in the past with a modified System file to include your own INTL 0 and/or
  16452. INTL 1, you may want to release your application with a modified itl0 and/or itl1.
  16453.  
  16454.  
  16455. The International Sorting Routine 
  16456.  
  16457. The international sorting routine is used to handle cases where letters are equal in
  16458. primary ordering but different in secondary ordering (e.g., “ä” and “a”). The routine
  16459. can also handle cases where one character sorts as if it were two, or two characters
  16460. would sort as if they were one, or even character reversals.
  16461.  
  16462. Prior to System 4.1, the international sorting routine was stored starting with the
  16463. localRtn field of INTL 1. As of System 4.1, this routine is now stored in itl2. An
  16464. INTL 2 resource is also included with the international resources, just for consistency
  16465. (the System never uses it). It is exactly the same as itl2.
  16466. Writing your own sorting routine can be dangerous if it is not done correctly. Before
  16467. attempting to do this, we would highly recommend you check with Apple to determine if
  16468. we are already doing one for the language of interest. We plan to do many international
  16469. versions of System file 4.1, and most developers’ needs will be met by these. A new
  16470. policy at Apple will now make it possible to license foreign versions of the System
  16471. file from Software Licensing. Contact Software Licensing at (408) 973-4667 for more
  16472. information about this.
  16473.  
  16474.  
  16475. Error in APDA draft of Inside Macintosh Volume V 
  16476.  
  16477. There is an error in the APDA draft of the International Utilities Package chapter of
  16478. Inside Macintosh Volume V. It says that there is a flag in the Script Manager globals
  16479. that overrides the current font and always uses the INTL 0 and INTL 1 resources. This
  16480. is not true. INTL 0 and 1 have been replaced by itl0 and itl1, and are not used except
  16481. by applications that explicitly get them by doing a GetResource. There is a relevant
  16482. Script Manager flag called IntlForce that is correctly but incompletely explained in
  16483. the Script Manager chapter. When IntlForce is false, the resources used by the International
  16484. Utilities are determined by the current script. The script that is in use is determined
  16485. by which font is in use for the port in use. For example, if you switch to the Kyoto
  16486. font, then you will be using the Japanese Script Interface System (KanjiTalk). If you
  16487. do this with IntlForce set to false, then you will use the resources that are associated
  16488. with the Japanese Script Interface System.
  16489.  
  16490. When IntlForce is true, the International Utilities always use the resources that are
  16491. associated with the system script; this is usually Roman. IntlForce is true by default.
  16492. Actually, the Script Manager initialization routine reads the itlc resource to determine
  16493. what the default is, and Apple plans to release the various international System
  16494. files with this field set to true in the itlc resource. Applications that want to
  16495. change the value of IntlForce can use the Script Manager call SetEnvirons with the
  16496. smIntlForce verb. There is a Script Manager call, IntlScript, to find out the current
  16497. value.
  16498.  
  16499.  
  16500. Bug in System 4.1 version of itl1
  16501.  
  16502. There is a bug in the itl1 resource in System 4.1. The itl1 resource contains arrays
  16503. of day and month names, as did the INTL 1 resource. Their formats are:
  16504.  
  16505. days ARRAY [1..7] of STRING[15]
  16506. months ARRAY[1..12] OF STRING[15]
  16507.  
  16508. Every day and month is supposed to be coded as a Pascal string with a maximum of 15
  16509. characters, and the actual length in the first byte of the string. In System 4.0 and
  16510. System 4.1, the contents of each string is always a Pascal string of length 15, with
  16511. the unused characters set to nulls. In other words, the length byte is set to hex 0F.
  16512. This will be fixed in a future release of the System file. If it is currently causing
  16513. your application problems, you can change the lengths with ResEdit, or change them at
  16514. run time, or use your own itl1 resource and release your application with an installer
  16515. script. The recommended approach is to wait for a fixed version of the System file
  16516. from Apple.
  16517.  
  16518.  
  16519.  
  16520. æKY 154
  16521. æC #154: Displaying Large PICT Files 
  16522.  
  16523. See also:     QuickDraw
  16524.               Technical Note #21 — Internal Picture Format
  16525.               Technical Note #35 — DrawPicture Problem
  16526.               Technical Note #88 - Signals
  16527.  
  16528. Written by:    Rick Blair    July 1, 1987
  16529. Updated:                     March 1, 1988
  16530. _______________________________________________________________________________
  16531.  
  16532. Now that we have scanners and other massive-picture producing types of applications,
  16533. there is a need to address the problem of how to display a PICT format object that is
  16534. bigger than a current PICT resource is allowed to be. Note that this technique applies
  16535. equally well to version 1 and version 2 
  16536. (word-opcode) pictures as produced by the Macintosh II.
  16537. _______________________________________________________________________________
  16538.  
  16539.  
  16540. Future Compatibility
  16541.  
  16542. Think of the handle returned by a GetResource('PICT',ID) as a “handle” in the more
  16543. general sense of being an abstract “tag”—something that the ROM routines can use to
  16544. draw the picture with. Don’t assume that the entire picture has been read into memory
  16545. or that you can directly read any bytes beyond the basic Picture record structure
  16546. (picSize followed by picFrame). Someday we may provide a mechanism for the resource
  16547. to be disk- instead of memory-based. The QuickDraw bottleneck procedures will know
  16548. how to get data from and put data into the pictures in any case.
  16549.  
  16550.  
  16551. Spooling from a PICT file
  16552.  
  16553. In order to display pictures of arbitrary size, your application should be able to
  16554. import a QuickDraw picture from a file of type PICT. This is the file produced by a
  16555. “Save as…” from MacDraw with the PICT option selected.
  16556.  
  16557. What follows is a small program fragment that demonstrates how to spool in a picture
  16558. from [the data fork of] a PICT file. The picture can be larger than the historical
  16559. 32K resource size. See technical note #88 if you are unfamiliar with the Signal mechanism.
  16560. We assume that a CatchSignals has been done before GetandDrawPICTFile is called.
  16561.  
  16562.  
  16563. MPW Pascal Example
  16564.  
  16565.     {the following variable must be at the top level}
  16566.  
  16567.     VAR
  16568.        globalRef   : INTEGER;      {refNum of the file to read from}
  16569.  
  16570.     {the following procedure must be at the top level}
  16571.  
  16572.     PROCEDURE GetPICTData(dataPtr: Ptr; byteCount: INTEGER);
  16573.     {replacement for the QuickDraw bottleneck routine}
  16574.  
  16575.        VAR
  16576.           err         : OSErr;
  16577.           longCount   : LONGINT;
  16578.  
  16579.        BEGIN
  16580.           longCount := byteCount;
  16581.           err := FSRead(globalRef,longCount,dataPtr);
  16582.           {can't check for an error because we don't know how to handle it}
  16583.        END;
  16584.  
  16585.     CONST
  16586.        abortPICT    = 128;         {error code if DrawPicture aborted}
  16587.  
  16588.     PROCEDURE GetDrawPICTFile;     {read in a PICT FILE selected by the user}
  16589.  
  16590.        VAR
  16591.           wher        : Point;     {where to display dialog}
  16592.           reply       : SFReply;   {reply record}
  16593.           myFileTypes : SFTypeList; {more Standard FILE goodies}
  16594.           numFileTypes: INTEGER;
  16595.  
  16596.           savedProcs  : QDProcsPtr;
  16597.           myProcs     : QDProcs;   {use CQDProcs for a color window}
  16598.  
  16599.           myPicture   : PicHandle; {we need a picture handle for DrawPicture}
  16600.           longCount   : LONGINT;
  16601.           myEOF       : LONGINT;
  16602.           myFilePos   : LONGINT;
  16603.  
  16604.        BEGIN
  16605.           wher.h := 20;
  16606.           wher.v := 20;
  16607.           numFileTypes := 1;       {display PICT files}
  16608.           myFileTypes[0] := 'PICT';
  16609.           SFGetFile(wher,'',NIL,numFileTypes,myFileTypes,NIL,reply);
  16610.  
  16611.           IF reply.good THEN BEGIN
  16612.              SetStdProcs(myProcs); {use SetStdCProcs for a CGrafPort}
  16613.              myProcs.getPicProc := @GetPICTData;
  16614.              savedProcs := thePort^.grafProcs; {set the grafProcs to ours}
  16615.              thePort^.grafProcs := @myProcs;
  16616.  
  16617.              myPicture := PicHandle(NewHandle(SizeOf(myPicture)));
  16618.  
  16619.              Signal(FSOpen(reply.fname,reply.vRefNum,globalRef));
  16620.              Signal(GetEOF(globalRef,myEOF)); {get EOF for later check}
  16621.              Signal(SetFPos(globalRef,fsFromStart,512)); {skip header}
  16622.  
  16623.              {read in the (obsolete) size word and the picFrame}
  16624.              longCount := SizeOf(myPicture);
  16625.              Signal(FSRead(globalRef,longCount,Ptr(myPicture^)));
  16626.  
  16627.              DrawPicture(myPicture,myPicture^^.picFrame); {draw the picture}
  16628.  
  16629.              Signal(GetFPos(globalRef,filePos)); {get position for check}
  16630.              Signal(FSClose(globalRef));
  16631.  
  16632.              DisposHandle(Handle(myPicture));
  16633.  
  16634.              thePort^.grafProcs := savedProcs; {restore the procs}
  16635.  
  16636.              {Check for errors. If there wasn't enough room,}
  16637.              {DrawPicture will abort; the FILE position mark}
  16638.              {won't be at the end of the FILE.}
  16639.              IF filePos <> myEOF THEN Signal(abortPICT);
  16640.           END; {IF reply.good}
  16641.        END; {GetDrawPICTFile}
  16642.  
  16643.  
  16644. MPW C Example
  16645.  
  16646. /*replacement for the QuickDraw bottleneck routine*/
  16647. pascal void GetPICTData(dataPtr,byteCount)
  16648. Ptr            dataPtr; 
  16649. short            byteCount;
  16650.  
  16651.  
  16652. { /* GetPICTData */
  16653.     OSErr            err;
  16654.     long            longCount;
  16655.  
  16656.     longCount = byteCount;
  16657.     err = FSRead(globalRef,&longCount,dataPtr);
  16658.     /*can't check for an error because we don't know how to handle it*/
  16659. } /* GetPICTData */
  16660.  
  16661. /*error code if DrawPicture aborted*/
  16662. #define       abortPICT     128         
  16663.  
  16664. OSErr GetDrawPICTFile()        /*read in a PICT FILE selected by the user*/
  16665.  
  16666. {    /* GetDrawPICTFile */   
  16667.  
  16668.           Point            wher;     /*where to display dialog*/
  16669.           SFReply        reply;      /*reply record*/
  16670.           SFTypeList        myFileTypes;/*more Standard FILE goodies*/
  16671.           short            numFileTypes;
  16672.         OSErr            err;
  16673.           QDProcsPtr        savedProcs;
  16674.           QDProcs        myProcs;/*use CQDProcs for a color window*/
  16675.           PicHandle        myPicture;
  16676.                     /*we need a picture handle for DrawPicture*/
  16677.           long            longCount,myEOF,filePos;
  16678.  
  16679.           wher.h = 20;
  16680.           wher.v = 20;
  16681.           numFileTypes = 1;                  /*display PICT files*/
  16682.           myFileTypes[0] = 'PICT';
  16683.           SFGetFile(wher,'',nil,numFileTypes,myFileTypes,nil,&reply);
  16684.  
  16685.           if (reply.good)  
  16686.         {
  16687.             SetStdProcs(&myProcs);
  16688.             /*use SetStdCProcs for a CGrafPort*/
  16689.                  myProcs.getPicProc = GetPICTData;
  16690.                  savedProcs = (*qd.thePort).grafProcs;
  16691.                                        /*set the grafProcs to ours*/
  16692.                  (*qd.thePort).grafProcs = &myProcs;
  16693.  
  16694.                  myPicture = (PicHandle)NewHandle(sizeof(Picture));
  16695.  
  16696.             err = FSOpen(&reply.fName,reply.vRefNum,&globalRef);
  16697.             if (err != noErr) return err;
  16698.  
  16699.               err = GetEOF(globalRef,&myEOF);
  16700.             /*get EOF for later check*/
  16701.             if (err != noErr) return err;
  16702.  
  16703.             err = SetFPos(globalRef,fsFromStart,512);/*skip header*/
  16704.                  if (err != noErr) return err;
  16705.  
  16706.                  /*read in the (obsolete) size word and the picFrame*/
  16707.                  longCount = sizeof(Picture);
  16708.                  err = FSRead(globalRef,&longCount,(Ptr)*myPicture);
  16709.                  if (err != noErr) return err;
  16710.  
  16711.                  DrawPicture(myPicture,&(**myPicture).picFrame); /*draw the picture*/
  16712.  
  16713.             err = GetFPos(globalRef,&filePos);/*get position for check*/
  16714.                  if (err != noErr) return err;
  16715.                  err = FSClose(globalRef);
  16716.                  if (err != noErr) return err;
  16717.  
  16718.                  DisposHandle((Handle)myPicture);
  16719.  
  16720.                  (*qd.thePort).grafProcs = savedProcs;/*restore the procs*/
  16721.  
  16722.                  /*Check for errors. if there wasn't enough room,*/
  16723.                  /*DrawPicture will abort; the FILE position mark*/
  16724.                  /*won't be at the end of the FILE.*/
  16725.  
  16726.             if (filePos != myEOF)  return abortPICT;
  16727.             else return noErr;
  16728.           } /*if (reply.good) */
  16729. }     /* GetDrawPICTFile */
  16730.  
  16731.  
  16732. More on Picture Compatibility
  16733.  
  16734. Many applications already support PICT resources larger than 32K. The 128K ROMs (and
  16735. later) allow pictures as large as memory (or spooling) will accommodate. This was
  16736. made possible by having QuickDraw ignore the size word and simply read the picture
  16737. until the end-of-picture opcode was reached.
  16738.  
  16739. For maximum safety and convenience, let QuickDraw generate and interpret your pictures.
  16740.  
  16741. While Apple has provided you with the data formats that allow you to read or write
  16742. picture data directly, we recommend that you always let DrawPicture or OpenPicture
  16743. and ClosePicture process the opcodes.
  16744.  
  16745. One reason to read a picture directly by scanning the opcodes would be to disassemble
  16746. it to, for example, extract a Color QuickDraw pixel map to save off in a private data
  16747. structure. This shouldn’t normally be necessary.
  16748.  
  16749. If you do look at the picture data be sure and check the version information. You may
  16750. want to put up an alert in your application that indicates to the user when a picture
  16751. was created using a later version of the picture format than your application recognizes,
  16752. letting them know that some elements of the picture cannot be displayed. If the version
  16753. information indicates a QuickDraw picture version later than the one recognized by
  16754. your application, your program should skip over the new opcodes and only attempt to
  16755. parse the ones it knows.
  16756.  
  16757. As with reading picture data directly, it is best to use QuickDraw to create data in
  16758. the PICT format. If you do need to create PICT format data directly, it is essential
  16759. that you use the latest opcode specifications and that you thoroughly test the data
  16760. produced on both color and black and white Macintosh machines. Contact Macintosh
  16761. Developer Technical Support if you are not sure that you have the latest specifications.
  16762.  
  16763. Apple does not guarantee that a picture which wasn’t produced by QuickDraw will work.
  16764.  
  16765.  
  16766.  
  16767. æKY 155
  16768. æC #155: Handles and Pointers—Identity Crisis
  16769.  
  16770. See also:     QuickDraw
  16771.               The Memory Manager
  16772.  
  16773. Written by:    Jim Friedlander    September 1, 1987
  16774. Updated:                          March 1, 1988
  16775. _______________________________________________________________________________
  16776.  
  16777. A handle is a handle and a pointer is a pointer. Applications should avoid embedding
  16778. non-relocatable objects (that the system assumes will never move) in handles.
  16779. _______________________________________________________________________________
  16780.  
  16781. In order to avoid fragmentation, some applications embed pointers (non-relocatable
  16782. memory manager objects) in handles, so that the handles can be moved around as needed.
  16783. This can cause several problems, especially with the Macintosh II, and should be
  16784. avoided.
  16785.  
  16786. For example, use of a handle to store a GrafPort can be particularly dangerous. A
  16787. GrafPort must not move between the time that it is opened (OpenPort, NewWindow, NewDialog,
  16788. etc.) and the time that it is closed (ClosePort, DisposeWindow, DisposDialog, etc.).
  16789. Color QuickDraw keeps a list of open ports and pointers to them, so, if you create a
  16790. GrafPort and it moves while still open, Color QuickDraw will (unknowingly) have a
  16791. pointer to outer space instead of a pointer to a GrafPort. When it needs to use that
  16792. pointer, it will get hopelessly confused and probably issue a system error to let you
  16793. know. 
  16794.  
  16795. As an aside, if you open a port by calling OpenPort or OpenCPort, you should always
  16796. close the port by calling ClosePort or CloseCPort before calling DisposPtr on the
  16797. port or you will orphan handles (visRgn, clipRgn and more).
  16798.  
  16799.  
  16800.  
  16801. æKY 156
  16802. æC #156: Checking for Specific Functionality
  16803.  
  16804. See also:     Operating System Utilities
  16805.               Assembly Language
  16806.               Technical Note #129 — SysEnvirons
  16807.  
  16808. Written by:    Jim Friedlander    September 1, 1987
  16809. Updated:                          March 1, 1988
  16810. _______________________________________________________________________________
  16811.  
  16812. This technical note explains how to check at run time to see if specific functionality,
  16813. such as the “new” TextEdit, is present.
  16814. _______________________________________________________________________________
  16815.  
  16816. Applications should strive to be compatible across all Macintoshes, but there are
  16817. times when an application must have knowledge about the machine that it is running
  16818. on.  The new trap, SysEnvirons, will give an application most of the information that
  16819. it requires (what hardware, what version of system software…).  
  16820.  
  16821. Using SysEnvirons
  16822.  
  16823. In most cases, if you examine why you want to test for the existence of a specific
  16824. trap, you will find that there is an alternative method, for example:
  16825.  
  16826. I need to see if the “new” TextEdit calls are available.
  16827.  
  16828. Call SysEnvirons and check to see that SysEnvRec.machineType >= 0 (128K ROMs or newer)
  16829. and that we are running System 4.1 or later (System 4.1 and later support the new
  16830. TextEdit on 128K and greater ROM machines—we can check this by just seeing if the
  16831. SysEnvirons trap exists, if we get an envNotPresent error, we know it doesn’t).  In
  16832. Pascal:
  16833.  
  16834.   CONST
  16835.       CurrentVersion = 1;  {Current version of SysEnvirons}
  16836.   VAR
  16837.       newTextAvail  : BOOLEAN;
  16838.       theWorld      : SysEnvRec;
  16839.   BEGIN
  16840.   {
  16841.  
  16842. This code checks to see if System 4.1 or later is running by calling
  16843. SysEnvirons. If SysEnvirons returns an envNotPresent error, we know that
  16844. we are running a system prior to 4.1, so we know we don’t have the new
  16845. TextEdit. If SysEnvirons doesn’t return envNotPresent, we check machine
  16846. type to make sure we aren't running on 64K ROMs (note: we assume that envMachUnknown
  16847. doesn't have 64K ROMs when we check machineType >= 0)
  16848.  
  16849.   }
  16850.       IF SysEnvirons(CurrentVersion,theWorld) = envNotPresent THEN
  16851.           newTextAvail:= FALSE
  16852.       ELSE
  16853.           newTextAvail:= (theWorld.machineType >= 0);
  16854.   END;
  16855.  
  16856. In C:
  16857.  
  16858.   /* Current version of SysEnvirons */
  16859.   #define     CurrentVersion    1
  16860.   {
  16861.       Boolean     newTextAvail;
  16862.       SysEnvRec     theWorld;
  16863.  
  16864.   /* 
  16865.       see comment in the above Pascal
  16866.   */ 
  16867.   if (SysEnvirons(CurrentVersion,&theWorld) == envNotPresent)
  16868.   newTextAvail = false; 
  16869.       else 
  16870.           newTextAvail = (theWorld.machineType >= 0);
  16871.   }
  16872.  
  16873. I need to see if PopUpMenuSelect is implemented.
  16874.  
  16875. The same answer as above applies here, since the “new” Menu Manager calls are only
  16876. implemented in System 4.1 on 128K or larger ROM machines (and, as we found above,
  16877. PopUpMenuSelect has the same trap number as Rename, so calling NGetTrapAddress won’t
  16878. work on 64K ROMs).
  16879.  
  16880. If you find that you need information that is not contained in SysEnvirons, please
  16881. send suggestions for extending it to Macintosh Developer Technical Support at the
  16882. address in Technical Note #0.
  16883.  
  16884. Checking for Specific Functionality
  16885.  
  16886. There are rare times when you may feel that it is necessary to test for specific
  16887. functionality.  In order to allow for testing of specific trap functionality, there
  16888. is an official unimplemented trap.  This trap ($A89F) is unimplemented on all Macintoshes.
  16889.  To test to see if a particular trap that you wish to use is implemented, you can
  16890. compare its address with the address of the unimplemented trap. Here are two fragments
  16891. that show how to check to see if Shutdown is implemented.  First, Pascal: 
  16892.  
  16893.   CONST
  16894.     ShutDownTrapNum    = $95;{trap number of Shutdown}
  16895.     UnImplTrapNum    = $9F;{trap number of “unimplemented trap”}
  16896.  
  16897.   VAR
  16898.     ShutdownIsImplemented   : BOOLEAN;  {is Shutdown implemented}
  16899.  
  16900.   BEGIN
  16901.   {Is Shutdown implemented?}
  16902.        ShutdownIsImplemented := NGetTrapAddress(ShutDownTrapNum,ToolTrap) <>         
  16903.    NGetTrapAddress(UnImplTrapNum,ToolTrap);
  16904.   END;
  16905.  
  16906. Here’s a C fragment:
  16907.  
  16908.   /*trap number of Shutdown*/
  16909.   #define     ShutDownTrapNum    0x95
  16910.   /*trap number of “unimplemented trap”*/
  16911.   #define     UnImplTrapNum     0x9F
  16912.  
  16913.   {
  16914.       Boolean ShutdownIsImplemented;
  16915.  
  16916.         /*Is Shutdown implemented?*/
  16917.          ShutdownIsImplemented = NGetTrapAddress(  ShutDownTrapNum,ToolTrap) !=      
  16918.       NGetTrapAddress(UnImplTrapNum,ToolTrap);
  16919.   }
  16920.  
  16921. NGetTrapAddress is used because it ensures that you will get the correct trap in case
  16922. there is a ToolTrap and an OSTrap with the same number.  Please note that calling
  16923. NGetTrapAddress does not cause compatibility problems with 64K ROMS.  When run on
  16924. those ROMs, it just becomes a GetTrapAddress call.  You have to be careful on 64K
  16925. ROMs—you can’t test for PopUpMenuSelect ($A80B), for example, because it has the same
  16926. trap number as Rename($A00B).  The 64K ROM didn’t really differentiate between ToolTraps
  16927. and OSTraps (there was no overlap in trap numbers).  So, if you wanted to test for
  16928. PopUpMenuSelect, you would need to first check to make sure you weren’t running on
  16929. 64K ROMs (see below).
  16930.  
  16931. You can get the trap number of the trap you wish to test for from Inside Macintosh
  16932. (Appendix C of Volumes I-III and Appendix B of Volume IV).  You can tell if the trap
  16933. is an OSTrap or a ToolTrap by checking to see if bit 11 in the trap word is set, that
  16934. is, traps like $A8xx (or $A9xx or $AAxx) that have the 
  16935. “8” component set, are ToolTraps and traps that don’t ($A0xx) are OSTraps. The trap
  16936. number that you pass to NGetTrapAddress for ToolTraps is the low 10 bits of the trap
  16937. word (the trap number for PopUpMenuSelect[$A80B] is $00B). The trap number that you
  16938. pass to NGetTrapAddress for OSTraps is the low 8 bits of the trap word (the trap
  16939. number for MoveHHi[$A064] is $064).
  16940.  
  16941. Shutdown ($A895) is just an example of a trap that we might need to check before
  16942. calling.  Most applications won’t call ShutDown, so this is just an example of how to
  16943. do the testing.
  16944.  
  16945.  
  16946.  
  16947. æKY 157
  16948. æC #157: Problem with GetVInfo
  16949.  
  16950. See also:    File Manager
  16951.  
  16952. Written by:    Jim Friedlander    September 1, 1987
  16953. Updated:                          March 1, 1988
  16954. _______________________________________________________________________________
  16955.  
  16956. The high-level call GetVInfo (and its low-level counterpart PBGetVInfo) may return
  16957. inaccurate results for freeBytes when running HFS.
  16958. _______________________________________________________________________________
  16959.  
  16960. The high-level File Manager call GetVInfo returns the number of free bytes on a volume
  16961. as one of its parameters. Since GetVInfo is really only glue that fills in a parameter
  16962. block for you and then calls PBGetVInfo, the values returned from it are subject to
  16963. the limitations (imposed for MFS) discussed in the File Manager chapter of Inside
  16964. Macintosh Volume IV (p. 130): “Warning: IOVNmAlBlks and ioVFrBlks, which are actually
  16965. unsigned integers, are clipped to 31744 
  16966. ($7C00) regardless of the size of the volume.” This will be fixed in future versions
  16967. of the glue (newer than MPW 2.0.2), but for now, you need to call PBHGetVInfo yourself
  16968. instead, as shown below.
  16969.  
  16970. The value that GetVInfo returns in freeBytes (ioVFrBlks * ioVAlBlkSize) will thus be
  16971. less than or equal to the actual number of free bytes on a volume. This isn’t catastrophic,
  16972. but can be highly inconvenient if you really need to know how much free space is on a
  16973. given volume.
  16974.  
  16975. Note: IOVNmAlBlks returned from PB[H]GetVInfo does not reflect the actual total number
  16976. of blocks on an HFS disk, but rather only the blocks that are “available” for program
  16977. use (it doesn’t count blocks used for catalog information).
  16978.  
  16979. Here are two functions (one in MPW Pascal, one in MPW C) that return the actual number
  16980. of free bytes on a volume, regardless of File System (if MFS is running, the PBHGetVInfo
  16981. call will actually generate a PBGetVInfo call which will return the proper values for
  16982. MFS volumes):
  16983.  
  16984. In MPW Pascal:
  16985.  
  16986.   FUNCTION FreeSpaceOnVol(vRef:Integer; VAR freeBytes:Longint): OSErr;
  16987.  
  16988.   TYPE         
  16989.      {we need this to convert an unsigned integer to an unsigned
  16990.      longint}
  16991.      TwoIntsMakesALong  = RECORD
  16992.                           CASE Integer OF
  16993.                           1: (long: LongInt);
  16994.                           2: (ints: ARRAY [0..1] OF Integer);
  16995.                      END; {TwoIntsMakesALong} 
  16996.   VAR         
  16997.      HPB      : HParamBlockRec;          
  16998.      convert  : TwoIntsMakesALong;         
  16999.      err      : OSErr;         
  17000.  
  17001. BEGIN {FreeSpaceOnVol}                 
  17002.     WITH HPB DO BEGIN {set up parameter block for the PBGetVInfo call}
  17003.          ioNamePtr := NIL;   {we don’t care about the name}
  17004.          ioVRefNum := vRef;  {this was passed in as a parameter}
  17005.          ioVolIndex := 0;    {use ioVRefNum only}
  17006.     END;              {WITH}
  17007.     err := PBHGetVInfo(@HPB,false);
  17008.     FreeSpaceOnVol:= err;     {return error from HGetVInfo}    
  17009.  
  17010. {
  17011. This next section needs some explanation. ioVFrBlk is an unsigned integer. If we were
  17012. to assign it(or coerce it) to a longint, it would get sign extended by MPW Pascal and
  17013. would thus be negative. Since we don’t want that, we use a variant record that maps
  17014. two integers into the space of one longint. The high word (convert.ints[0]) is cleared
  17015. in the first line, then the low word is assigned the value of HPB.ioVFrBlk. The resulting
  17016. longint (convert.long) is now a correctly signed (positive) long integer representing
  17017. the number of free blocks. NOTE: this is only necessary if the number of free blocks
  17018. on a disk exceeds 32767 ($7FFF).
  17019. }
  17020.      IF err = 0 THEN BEGIN
  17021.         convert.ints[0] := 0;
  17022.         convert.ints[1] := HPB.ioVFrBlk;
  17023.              freeBytes:= convert.long * HPB.ioVAlBlkSiz; 
  17024.         END ELSE {PBHGetVInfo failed}
  17025.              freeBytes:= 0; {return this if the routine failed}
  17026.   END;  {FreeSpaceOnVol}     
  17027.  
  17028. In MPW C:
  17029.  
  17030. OSErr freeSpaceOnVol(vRef,pfreeBytes) 
  17031. short int vRef;
  17032. unsigned long int *pfreeBytes; /* C does this correctly!! */
  17033.  
  17034. {    /* freeSpaceOnVol */
  17035.     HVolumeParam HPB;
  17036.     OSErr err;
  17037.  
  17038.     HPB.ioNamePtr = 0L;        /* we don’t care about the name */
  17039.     HPB.ioVRefNum = vRef;      /* this was passed in as a parameter */
  17040.     HPB.ioVolIndex = 0;        /* use ioVRefNum only */
  17041.     err = PBHGetVInfo(&HPB,false);
  17042.     if (err == noErr)
  17043.         *pfreeBytes = (unsigned long int)HPB.ioVFrBlk *                         
  17044. HPB.ioVAlBlkSiz;
  17045.     else
  17046.         *pfreeBytes = 0L;     /* return this if the routine failed */
  17047.     return(err);              /* function result */
  17048. }   /* freeSpaceOnVol */
  17049.  
  17050.  
  17051.  
  17052. æKY 158
  17053. æC #158: Frequently Asked MultiFinder Questions
  17054.  
  17055. See also:     Technical Note #129 — SysEnvirons
  17056.               Technical Note #156 — Checking For Specific Functionality
  17057.               MultiFinder Developer’s Package
  17058.  
  17059. Written by:    Jim Friedlander    September 1, 1987
  17060. Updated:                          March 1, 1988
  17061. _______________________________________________________________________________
  17062.  
  17063. This technical note provides answers to some of the more frequently asked questions
  17064. about MultiFinder. The development name for MultiFinder was Juggler, so the term
  17065. “juggle” is used in this technical note to denote a context switch.
  17066. _______________________________________________________________________________
  17067.  
  17068.  
  17069. How can I tell if WaitNextEvent is implemented?
  17070.  
  17071. Most applications should not need to tell if MultiFinder is running. Most of the
  17072. time, the application really needs to know something like: “How can I tell if WaitNextEvent
  17073. is implemented?” Here’s a Pascal fragment that demonstrates how to check to see if
  17074. WaitNextEvent is implemented:
  17075.  
  17076.     FUNCTION TrapAvailable(tNumber: INTEGER; tType: TrapType): BOOLEAN;
  17077.     
  17078.        CONST
  17079.           UnimplementedTrapNumber = $A89F;  {number of "unimplemented trap"}
  17080.     
  17081.     BEGIN {TrapAvailable}
  17082.     
  17083.     {Check and see if the trap exists.}
  17084.     {On 64K ROM machines, tType will be ignored.}
  17085.     
  17086.        TrapAvailable := ( NGetTrapAddress(tNumber, tType) <>
  17087.                           GetTrapAddress(UnimplementedTrapNumber) );
  17088.     
  17089.     END;  {TrapAvailable}
  17090.     
  17091.     FUNCTION WNEIsImplemented: BOOLEAN;
  17092.     
  17093.        CONST
  17094.           WNETrapNumber = $A860; {trap number of WaitNextEvent}
  17095.     
  17096.        VAR
  17097.           theWorld      : SysEnvRec; {to check if machine has new traps}
  17098.           discardError  : OSErr; {to ignore OSErr return from SysEnvirons}
  17099.     
  17100.     BEGIN {WNEIsImplemented}
  17101.     
  17102.     {  Since WaitNextEvent and HFSDispatch both have the same trap
  17103.        number ($60), we can only call TrapAvailable for WaitNextEvent
  17104.        if we are on a machine that supports separate OS and Toolbox
  17105.        trap tables. We call SysEnvirons and check if machineType < 0.}
  17106.      
  17107.        discardError := SysEnvirons(1, theWorld);
  17108.     
  17109.     {  Even if we got an error from SysEnvirons, the SysEnvirons glue
  17110.        has set up machineType.}
  17111.     
  17112.        IF theWorld.machineType < 0 THEN
  17113.           WNEIsImplemented := FALSE
  17114.           {this ROM doesn't have separate trap tables or WaitNextEvent}
  17115.        ELSE
  17116.           WNEIsImplemented := TrapAvailable(WNETrapNumber, ToolTrap);
  17117.           {check for WaitNextEvent}
  17118.     
  17119.     END;  {WNEIsImplemented}
  17120.     
  17121.     {Note that we call SystemTask if WaitNextEvent isn't available.}
  17122.     
  17123.     ...
  17124.        hasWNE := WNEIsImplemented;
  17125.     ...
  17126.        IF hasWNE THEN BEGIN
  17127.           {call WaitNextEvent}
  17128.           ...
  17129.        END ELSE BEGIN
  17130.           {call SystemTask and GetNextEvent}
  17131.           ...
  17132.        END;
  17133.     ...
  17134.     
  17135. Here’s a C fragment:
  17136.  
  17137.     Boolean
  17138.     TrapAvailable(tNumber, tType)
  17139.     short    tNumber
  17140.     TrapType tType
  17141.     {
  17142.     
  17143.     /* define trap number for old MPW or non-MPW C */
  17144.     #ifndef _Unimplemented
  17145.     #define _Unimplemented 0xA89F
  17146.     #endif
  17147.     
  17148.     /* Check and see if the trap exists. */
  17149.     /* On 64K ROM machines, tType will be ignored. */
  17150.     
  17151.        return( NGetTrapAddress(tNumber, tType) !=
  17152.                GetTrapAddress(_Unimplemented) );
  17153.     
  17154.     }
  17155.     
  17156.     Boolean
  17157.     WNEIsImplemented()
  17158.     {
  17159.     
  17160.     /* define trap number for old MPW or non-MPW C */
  17161.     #ifndef _WaitNextEvent
  17162.     #define _WaitNextEvent 0xA860
  17163.     #endif
  17164.     
  17165.        SysEnvRec theWorld; /* used to check if machine has new traps */
  17166.     
  17167.     /* Since WaitNextEvent and HFSDispatch both have the same trap
  17168.        number ($60), we can only call TrapAvailable for WaitNextEvent
  17169.        if we are on a machine that supports separate OS and Toolbox
  17170.        trap tables. We call SysEnvirons and check if machineType < 0. */
  17171.      
  17172.        SysEnvirons(1, &theWorld);
  17173.     
  17174.     /* Even if we got an error from SysEnvirons, the SysEnvirons glue
  17175.        has set up machineType. */
  17176.     
  17177.        if (theWorld.machineType < 0) {
  17178.           return(false)
  17179.           /* this ROM doesn't have separate trap tables or WaitNextEvent */
  17180.        } else {
  17181.           return(TrapAvailable(_WaitNextEvent, ToolTrap));
  17182.            /* check for WaitNextEvent */
  17183.        }
  17184.     
  17185.     }
  17186.     
  17187.     /* Note that we call SystemTask if WaitNextEvent isn't available. */
  17188.     
  17189.     ...
  17190.        hasWNE = WNEIsImplemented();
  17191.     ...
  17192.        if (hasWNE) {
  17193.           /* call WaitNextEvent */
  17194.           ...
  17195.        } else {
  17196.           /* call SystemTask and GetNextEvent */
  17197.           ...
  17198.        }
  17199.     ...
  17200.     
  17201. Note: Testing to see if WaitNextEvent is implemented is not the same as testing to
  17202. see whether MultiFinder is running. Systems 6.0 and newer include WaitNextEvent whether
  17203. or not MultiFinder is running.
  17204.  
  17205.  
  17206. How can I tell if the MultiFinder Temporary Memory Allocation calls are implemented?
  17207.  
  17208. The technique that’s used to determine this is similar to the above technique. The
  17209. TrapAvailable routine above is reused. In Pascal:
  17210.  
  17211.     FUNCTION TempMemCallsAvailable: BOOLEAN;
  17212.     
  17213.        CONST
  17214.           OSDispatchTrapNumber = $A88F; {number for temporary memory calls}
  17215.     
  17216.     BEGIN {TempMemCallsAvailable}
  17217.     
  17218.     {  Since OSDispatch has a trap number that was always defined
  17219.        to be a toolbox trap ($8F), we can always call TrapAvailable.
  17220.        If we are on a machine that does not have separate OS and
  17221.        Toolbox trap tables, we’ll still get the right trap address.}
  17222.      
  17223.        TempMemCallsAvailable := TrapAvailable(OSDispatchTrapNumber, ToolTrap);
  17224.        {check for OSDispatch}
  17225.     
  17226.     END;  {TempMemCallsAvailable}
  17227.     
  17228.  
  17229. In C:
  17230.  
  17231.     Boolean
  17232.     TempMemCallsAvailable()
  17233.     {
  17234.     
  17235.     /* define trap number for old MPW or non-MPW C */
  17236.     #ifndef _OSDispatch
  17237.     #define _OSDispatch 0xA88F
  17238.     #endif
  17239.     
  17240.     /* Since OSDispatch has a trap number that was always defined to
  17241.        be a toolbox trap ($8F), we can always call TrapAvailable.
  17242.        If we are on a machine that does not have separate OS and
  17243.        Toolbox trap tables, we’ll still get the right trap address. */
  17244.      
  17245.        return(TrapAvailable(_OSDispatch, ToolTrap));
  17246.        /* check for OSDispatch */
  17247.     
  17248.     }
  17249.     
  17250.  
  17251.  
  17252. How can I tell if my application is running in the background?
  17253.  
  17254. To run in the background under MultiFinder, an application must have set the canBackground
  17255. bit (bit 12 of the first word) in the SIZE resource. In addition, the acceptSuspendResumeEvents
  17256. bit (bit 14) should be set. An application can tell it is running in the background
  17257. if it has received a suspend event but not a resume event.
  17258.  
  17259.  
  17260. When exactly does juggling take place?
  17261.  
  17262. Juggling takes place at WaitNextEvent/GetNextEvent/EventAvail time. If you have the
  17263. acceptSuspendResumeEvents bit set in the SIZE resource, you will receive suspend/resume
  17264. events. When you get a suspend event (or, when you call EventAvail and a suspend
  17265. event has been posted), you will be juggled out the next time that you call WNE/GNE/EventAvail.
  17266. When you receive a suspend event, you are going to be juggled, so don’t do anything
  17267. to try to retain control 
  17268. (such as calling ModalDialog).
  17269.  
  17270. Speaking of ModalDialog, MultiFinder will not suspend your application when the frontmost
  17271. window is a modal dialog, though background tasks will continue to get time.
  17272.  
  17273.  
  17274. Can I disable suspend/resume events by passing the appropriate event mask to WNE/GNE/EventAvail?
  17275.  
  17276. suspend/resume events are not queued, so be careful when masking out app4Evts. You
  17277. will still get the event, all that will happen if you mask out app4Evts is that your
  17278. application won’t know when it is going to be juggled out (your application will
  17279. still be juggled out when you call WNE/GNE/EventAvail). If your application sets a
  17280. boolean to tell whether or not it’s in the foreground or the background, you definitely
  17281. don’t want to mask out app4Evts. 
  17282.  
  17283.  
  17284. Should my application use WaitNextEvent?
  17285.  
  17286. Yes, this will enable background tasks to get as much time as possible. All user
  17287. events that your program needs to handle will be passed to your application as quickly
  17288. as possible. Applications that run in the background should try to be as friendly as
  17289. possible. It’s best to do things a small chunk at a time so as to give maximum time
  17290. to the foreground application. 
  17291. “Cooperative multi-tasking” requires cooperation!
  17292.  
  17293. If your application calls WaitNextEvent, it shouldn’t call SystemTask.
  17294.  
  17295.  
  17296. Is there anything else that I can do to be MultiFinder friendly?
  17297.  
  17298. It is very important that you save the positions of windows that you open, so that
  17299. the next time the user launches your application, the windows will go where they had
  17300. them last. This greatly enhances the usability of MultiFinder. With data files, the
  17301. window positions can be stored in either the resource or the data fork.
  17302.  
  17303. If you have windows that aren’t data windows (i.e. separate files), you can store
  17304. information about their positions in one of two ways: in a separate configuration
  17305. file or in a resource in your application. Using a separate configuration file is
  17306. necessary if your application is shareable on AppleShare, since resource forks are
  17307. not. The configuration file should be put in the folder that contains the currently
  17308. open system folder (this is guaranteed to be a local, non-shared volume as opposed to
  17309. a server volume). The vRefNum/WDRefNum of this folder can be obtained by calling
  17310. SysEnvirons (SysEnvRec.sysVRefNum).
  17311.  
  17312. Can I use a debugger with MultiFinder?
  17313.  
  17314. Yes, MacsBug will load normally, since it is loaded well before MultiFinder. Since
  17315. TMON is currently installed as a startup application, you should Set Startup to it,
  17316. then launch MultiFinder manually (by holding down Option-Command while double-clicking
  17317. the MultiFinder icon) or use a program that will run multiple startup applications
  17318. (such as Sequencer), making sure that TMON is run before MultiFinder. If you try to
  17319. run TMON after MultiFinder has been installed, a system crash will result. The latest
  17320. version of TMON (2.8) has an INIT that loads it before MultiFinder is present.
  17321.  
  17322. It is necessary to check CurApName ($910) when you first enter a debugger (TMON users
  17323. can anchor a window to $910) to see which layer (whose code, which 
  17324. low-memory globals and so on) is currently executing, especially if you entered the
  17325. debugger by pressing the interrupt button.
  17326.  
  17327.  
  17328. What happened to animated icons under MultiFinder?
  17329.  
  17330. Finders 6.0 and newer no longer use the mask that you supply in an ICN# to 
  17331. “punch a hole” in the desktop. Instead, the Finder uses a default mask that consists
  17332. of a solid black copy of the icon with no hole.
  17333.  
  17334.  
  17335. How can I ensure maximal compatibility with MultiFinder?
  17336.  
  17337. If you follow the guidelines presented in the MultiFinder Developer’s Package you
  17338. will stand a very good chance of being fully compatible with MultiFinder.
  17339.  
  17340.  
  17341.  
  17342. æKY 159
  17343. æC #159: Hard Disk Hacking
  17344.  
  17345. See also:     Technical Note #96—SCSI Bugs
  17346.               Technical Note #134—Hard Disk Medic
  17347.               The Device Manager
  17348.  
  17349. Written by:    Bo3b Johnson    September 1, 1987
  17350. Updated:                       March 1, 1988
  17351. _______________________________________________________________________________
  17352.  
  17353. For those of a technical bent with some extra time, you can build your own hard disk
  17354. system from a cheap SCSI drive and a driver that you write. This is not a project for
  17355. those short on time, so beware.
  17356. _______________________________________________________________________________
  17357.  
  17358. We often get questions regarding the feasibility of connecting a ‘generic’ SCSI drive
  17359. to the Macintosh, usually the Macintosh II. It is possible to use a standard drive,
  17360. but it is important to be aware that there is a reason why a fully assembled drive
  17361. costs more. When buying a hard disk you have two choices:
  17362.  
  17363.  1) buy a fully assembled drive, formatting and driver software included
  17364.  2) buy the pieces necessary to assemble your own: the drive itself, power 
  17365.     supply if needed, cables, and development system to write a driver and 
  17366.     formatter.
  17367.  
  17368. The second choice will often appear to be cheaper, since you don’t have to pay for a
  17369. fancy case with a fancy label. However, you are also missing the chance to pay for
  17370. some fancy software that took some fancy amounts of time to write.
  17371.  
  17372. Do not underestimate the difficulty of building your own hard disk. SCSI drives are
  17373. only partially standardized so a driver written for one drive will probably not work
  17374. (at least not well) on another drive. All drives come with a formatting utility that
  17375. also contains a driver for reading and writing sectors to the disk. For example, the
  17376. Apple drives come with a program called HD SC Setup. Most third-party drives have a
  17377. similar utility that is specific to their drive. The formatting operation varies
  17378. widely depending on the drive, and the driver also may have to know about specific
  17379. timing problems with a given drive. HD SC Setup only supports the drives which we
  17380. produce.
  17381.  
  17382. If you decide that you want to hack together your own drive, you will need to write
  17383. this formatter/driver program. It is non-trivial, and this is part of what you pay
  17384. for when you buy an off-the-shelf drive. If you have the time, you may save some
  17385. money. If you are writing your own formatter/driver program we can help you with
  17386. problems you run into, but you must be familiar with SCSI terminology, the SCSI Manager,
  17387. and be able to use an assembly level debugger like Macsbug or TMon. You may run into
  17388. timing difficulties that require the use of a logic analyzer or SCSI analyzer to
  17389. resolve.
  17390.  
  17391. This may sound like it is hard to write your own driver. It is. This may sound like
  17392. we are trying to scare you off from writing your own driver. We are.
  17393.  
  17394.  
  17395.  
  17396. æKY 160
  17397. æC #160: Key Mapping
  17398.  
  17399. See also:        The Script Manager
  17400.  
  17401. Written by:      Cameron Birse     September 1, 1987
  17402. Updated:                           March 1, 1988
  17403. _______________________________________________________________________________
  17404.  
  17405. This technical note describes the key code mapping scheme used in the Macintosh SE,
  17406. Macintosh II, and System file 4.1 and later. It also provides a “safe” method for
  17407. remapping keyboards.
  17408. _______________________________________________________________________________
  17409.  
  17410. The keystroke mapping mechanism has been changed for the Macintosh SE, the Macintosh
  17411. II, and version 4.1 of the System file. Originally, a keystroke caused an interrupt,
  17412. and the interrupt handler dispatched the keycode to a translation routine pointed to
  17413. by a low memory pointer (Key1Trans or Key2Trans). This routine was installed at boot
  17414. time by the System, and was generally replaced entirely or in part for remapping the
  17415. keyboard. When the keycode was mapped, it was returned to the interrupt handler, and
  17416. the handler then posted the event. The translation routine and the key map were both
  17417. contained in the System file resources INIT 0 and 1.
  17418.  
  17419. In the new System file, the low-memory pointers are still there and are still called
  17420. by the Macintosh Plus, but are not called by ADB systems. They are preserved so that
  17421. applications that call them will still be able to use them to translate keycodes.
  17422. They now point to a routine that implements the new System mechanism. In this new
  17423. mechanism, the keystroke causes an interrupt, and the interrupt handler maps the raw
  17424. keycode to a “virtual” keycode, which is then sent to a trap called KeyTrans. This
  17425. routine maps the virtual keycode to an ASCII value, and returns that to the handler,
  17426. which posts the event.
  17427.  
  17428. With the advent of new keyboards, a mechanism was needed to map the different raw
  17429. keycodes to a standard virtual keycode that could be mapped to the ASCII and special
  17430. character sets. The mapping from raw to virtual keycodes is done for keyboard hardware
  17431. independence. The raw mapping routine uses a table resident in the System resource
  17432. KMAP.  Basically, the raw keycode is used to index into the table in KMAP. The value
  17433. at the indexed location in KMAP is what is returned as the virtual keycode. The format
  17434. of the KMAP resource is described below. 
  17435.  
  17436. The mapping of the virtual keycode to the ASCII character is done by a new trap in
  17437. the Macintosh SE and Macintosh II ROMs, and in System 4.1 (and later) for the Macintosh
  17438. Plus. The new trap is called KeyTrans (not to be confused with the Key1Trans or Key2Trans
  17439. pointers), and it  maps the virtual keycode to an ASCII keycode (based on modifiers,
  17440. if any) using tables that reside in the System resource KCHR. The format of the KCHR
  17441. resource is also described below.
  17442.  
  17443. FUNCTION KeyTrans(transData:Ptr;keycode:INTEGER;VAR state: LONGINT):LONGINT
  17444.  
  17445. The transData parameter is a pointer to the KCHR image in memory. The keycode parameter
  17446. is an integer comprised of the modifier flags in bits 8-15, an up/down stroke flag in
  17447. bit 7 (1=up), and the virtual key code in bits 6-0. The state parameter is a value
  17448. internal to KeyTrans which should be preserved across calls if dead keys are desired.
  17449. It is dependent on the KCHR information, so if the KCHR is changed, state should be
  17450. reset to 0.
  17451.  
  17452. The LONGINT returned is actually two 16-bit characters to be posted as events (usually
  17453. the high byte of each is 0), high word first. A returned value of 0 in either word
  17454. should not be posted. Do not depend on which word the character will end up in. If
  17455. both words are valid, then the hi word should be posted first.
  17456.  
  17457. In the Macintosh SE and Macintosh II, the KMAP and KCHR resources are in ROM. In the
  17458. Macintosh Plus, the System uses a KCHR resource in the System file. In order to remap
  17459. the keyboard, you must supply a KCHR resource and have the System use it. In addition
  17460. to the KCHR resource, there is an associated SICN resource. The SICN resource provides
  17461. a graphic representation of the current keyboard mapping. For example, the French
  17462. keyboard layout has a SICN of a French flag to designate that particular map is currently
  17463. active. The SICN resource should be some representation of your particular remap, and
  17464. its ID# must be the same as the KCHR’s. The KCHR resource must be named appropriately,
  17465. as there will be a Control Panel option that will allow the user to choose the appropriate
  17466. map. 
  17467.  
  17468.  
  17469. Remapping the keyboard
  17470.  
  17471. Remapping the keyboard can be done two ways: either at boot time, or from within an
  17472. application. Remapping from within an application can be made permanent (until the
  17473. next boot), or only for the life of the application. The remapping is accomplished by
  17474. modifying a KCHR resource, and telling the System to use the new KCHR. The KCHR must
  17475. have an ID number in the range of the appropriate script, and must have an associated
  17476. SICN resource with the same ID number. The Roman script, for example, uses the range
  17477. 0 to 16383, and the standard KCHR IDs for each country are the same as the country
  17478. code (US = 0, French = 1, German = 2, Italian = 3, etc.). Alternative Roman keyboards
  17479. should have numbers somewhere in the script range, e.g. “Dvorak” at ID 500.
  17480.  
  17481. To remap the keyboard at boot time, there must be a modified KCHR resource in the
  17482. System file with an ID in the range of the appropriate script. In addition to the
  17483. KCHR resource, there must be an associated SICN resource. To make the System use the
  17484. modified KCHR at boot time, you must change the entries in the itlb resource in the
  17485. System file to reflect the ID of the modified KCHR, and SICN resources. The itlb
  17486. resource format is described below.
  17487.  
  17488. Apple’s System Software Licensing policy forbids shipping a modified System file; we
  17489. suggest an installer program that the user can execute to perform the installation of
  17490. the new resources. The installer program should assign the new resources their IDs
  17491. based on what is currently in the System file so there won’t be a conflict.
  17492. itlb resource format
  17493.  
  17494.     /*------------itlb • International Script Bundle------------*/
  17495.     type 'itlb' {
  17496.     unsigned integer;   /* itl0 id number                       */
  17497.     unsigned integer;   /* itl1 id number                       */
  17498.     unsigned integer;   /* itl2 id number                       */
  17499.     unsigned integer;   /* reserved                             */
  17500.     unsigned integer;   /* reserved                             */
  17501.     unsigned integer;   /* reserved                             */
  17502.     unsigned integer;   /* language code {e.g. langFrench}      */
  17503.     unsigned integer;   /* number/date codes                    */
  17504.     unsigned integer;   /* KCHR id number                       */
  17505.     unsigned integer;   /* SICN id number                       */
  17506.     };
  17507.  
  17508. To remap the keyboard after boot, you need a KCHR (and SICN) in your application or
  17509. in the System file (with an ID in the range of the appropriate script), and you need
  17510. to use the following calls to the Script Manager (the Script Manager is included in
  17511. System 4.1 and later). The first call to SetScript sets the Script Manager’s global
  17512. variable for the KCHR resource ID, and the second call to SetScript sets the Script
  17513. Manager’s global variable for the SICN resource ID. When these calls are completed,
  17514. the call to keyScript will load the resources, and set up the System to use them.
  17515.  
  17516.     CONST
  17517.        DvorakID = 500;
  17518.  
  17519.     VAR
  17520.        err: OSErr;
  17521.  
  17522.     BEGIN
  17523.        err := SetScript(smRoman, smScriptKeys, DvorakID);
  17524.        err := SetScript(smRoman, smScriptIcon, DvorakID);
  17525.        KeyScript(smRoman);
  17526.     END;
  17527.  
  17528.  
  17529. KCHR resource format
  17530.  
  17531.    Version                                 2 bytes (integer)
  17532.    Indexes (to character table)            256 bytes (array of bytes)
  17533.    Count of character table arrays         2 bytes (integer)
  17534.    Character table arrays                  128*n bytes (array of char)
  17535.    Count of DeadKey Records                2 bytes (integer)
  17536.       table number                         1 byte
  17537.       virtual keycode                      1 byte
  17538.       count of Completor Records           2 bytes (integer)
  17539.          completor character               1 byte (char)
  17540.          substituting character            1 byte (char)
  17541.       no match character                   1 byte (char)
  17542.    Extra room for 16 bit Character Codes   1 byte (char)
  17543.  
  17544. The KCHR resource consists of a 2 byte version number followed by a 256 byte modifier
  17545. table, mapping all 256 possible modifier states to a table number. This is followed
  17546. by a 2 byte count of tables, which is, in turn, followed by that many 128 byte ASCII
  17547. tables. The ASCII table maps the virtual keycode to an ASCII value; 0 signifies a
  17548. dead key, in this case the dead key table must be searched. 
  17549.  
  17550. The dead key table is comprised of a count of dead key records (2 bytes), and that
  17551. many dead key records. A dead key record consists of a one byte table number, a one
  17552. byte virtual keycode (without up/down bit), a completor table, and a no-match character.
  17553.  
  17554. When KeyTrans searches the dead key records, it checks for a match with the table
  17555. number and the keycode. If there is no match, it is no a dead key, and a zero is
  17556. returned. If there is a match, it is recorded in the state variable. If the previous
  17557. key was a dead key,  the completor table will be searched. The completor table is
  17558. comprised of a count of completor records, followed by that number of completor records.
  17559.  
  17560. A completor record is simply a substitution list for ASCII characters. If the ASCII
  17561. character matches the first byte in the completor record, the second byte will be
  17562. substituted for it. When there is no substitution to be made, the original ASCII
  17563. character is preceded by the no match character found at the at the end of the dead
  17564. key record.
  17565.  
  17566.  
  17567. KMAP Resource format
  17568.  
  17569.    ID                            2 bytes (integer)
  17570.    Version                       2 bytes (integer)
  17571.    Raw to virtual keycode map    128 bytes (array)
  17572.    Count of Exception arrays     2 bytes (integer)
  17573.    Exception arrays              (array)
  17574.       raw keycode                1 byte
  17575.       noXor, Xor                 1 bit (boolean)
  17576.       fill bits                  3 bits
  17577.       ADB opcode                 4 bits (bitstring)
  17578.       data string                variable (Pascal string)
  17579.  
  17580. The KMAP resource starts with a two byte ID, followed by a two byte version number. 
  17581. This is followed by the 128 byte keycode mapping table, described above in this technical
  17582. note.  The table is followed by a list of exceptions.
  17583.  
  17584. The 128 byte table is simply a one-to-one mapping of real keycodes to virtual keycodes,
  17585. the first byte is the virtual keycode for $00, the second for $01, etc.  The high bit
  17586. of the virtual keycode signals an exception entry in the exception list.
  17587.  
  17588. The exception list is used to enable the device driver to initiate communication with
  17589. the device, usually to perform a state change. The exception list begins with a two
  17590. byte record count followed by that many records.  The format of the exception record
  17591. is shown above.  The raw keycode is the keycode as generated by the device.  The XOR
  17592. bit informs the driver to invert the state of the key instead of using the state
  17593. provided by the hardware.  This can be used to provide keys that lock in software. 
  17594. The ADB opcode is described in the Inside Macintosh  chapter on the Apple DeskTop Bus
  17595. in Volume V.  Finally the data string is a Pascal string that is passed to the ADBOp
  17596. trap.
  17597.  
  17598.  
  17599.  
  17600. æKY 161
  17601. æC #161:    When to Call _PrOpen and _PrClose
  17602.  
  17603. Revised by:    Pete “Luke” Alexander                               October 1989
  17604. Written by:    Ginger Jernigan                                   September 1987
  17605.  
  17606. This Technical Note discusses opening and closing the Printing Manager with calls to
  17607. _PrOpen and _PrClose.
  17608. Changes since August 1989:  Added code for pIdle procedure support and improved the
  17609. error handling in the page loop.
  17610. _______________________________________________________________________________
  17611.  
  17612.  
  17613. Introduction
  17614.  
  17615. At one time, Apple recommended that developers call _PrOpen at the beginning of their
  17616. application and _PrClose at the end, before returning to the Finder.  This recommendation
  17617. was in the ancient past when an application only had to deal with a single printer
  17618. driver.
  17619.  
  17620. As more printer drivers became available, it became important for an application to
  17621. consider the presence of other applications and how opening and closing the printer
  17622. driver affected them.  The user could open the Chooser at any time and change the
  17623. current printer driver without the current
  17624. application’s knowledge.  If an application followed the old philosophy and a user
  17625. changed the current printer driver while running the application, the next time the
  17626. user attempted to print, the wrong driver would be open, the Printing Manager would
  17627. not be able to find the necessary resources, and the user would get an error.
  17628.  
  17629.  
  17630. The Current Recommendation
  17631.  
  17632. Macintosh Developer Technical Support currently recommends that applications open and
  17633. close the printer driver each time the application uses the Printing Manager.
  17634.  
  17635. MPW Pascal
  17636.  
  17637. {*------ PrintStuff ---------------------------------------------------------*}
  17638. {**
  17639.  **   PrintStuff will call all of the necessary Print Manager calls to print
  17640.  **   a document.  It checks PrError() after each Print Manager call.  If an
  17641.  **   error is found, all of the Print Manager open calls (i.e., PrOpen,
  17642.  **   PrOpenDoc...) will have a corresponding close call before the error
  17643.  **   is posted to the user.  You want to use this approach to make sure the
  17644.  **   Print Manager closes properly and all temporary memory is released.
  17645.  **}
  17646.  
  17647. PROCEDURE PrintStuff;
  17648.  
  17649. VAR
  17650.   copies,
  17651.   firstPage,
  17652.   lastPage,
  17653.   loop,
  17654.   numberOfCopies,
  17655.   pageNumber,
  17656.   printmgrsResFile,
  17657.   realNumberOfPagesInDoc    : Integer;
  17658.   PrintError                : LongInt;
  17659.   oldPort                   : GrafPtr;
  17660.   thePrRecHdl               : THPrint;
  17661.   thePrPort                 : TPPrPort;
  17662.   theStatus                 : TPrStatus;
  17663.  
  17664. BEGIN
  17665.    GetPort(oldPort);
  17666.  
  17667.    {**
  17668.        UnLoadTheWorld will swap out ALL unneeded code segments and data
  17669.        that are NOT required during print time. Your print code must be a
  17670.        separate code segment.
  17671.     **}
  17672.    UnLoadTheWorld;
  17673.  
  17674.    thePrRecHdl := THPrint(NewHandle(SIZEOF(TPrint)));
  17675.  
  17676.    IF (MemError = noErr) AND (thePrRecHdl <> NIL) THEN
  17677.      BEGIN
  17678.       PrOpen;
  17679.         IF (PrError = noErr) THEN
  17680.           BEGIN
  17681.             {**
  17682.                 Save the current resource file (i.e. the printer driver's)
  17683.                 so the driver will not lose its resources upon return from
  17684.                 the pIdleProc.
  17685.              **}
  17686.             printmgrsResFile := CurResFile;
  17687.             PrintDefault(thePrRecHdl);
  17688.  
  17689.              IF (PrError = noErr) THEN
  17690.               BEGIN
  17691.                 IF (PrStlDialog(thePrRecHdl)) THEN
  17692.                  BEGIN
  17693.                    {**
  17694.                       DetermineNumberOfPagesinDoc determines the number of
  17695.                       pages contained in the document by comparing the size
  17696.                       of the document with rPage from the TPrInfo record
  17697.                       (IM II-150). It returns the number of pages required
  17698.                       to print the document for the currently selected printer.
  17699.                    **}
  17700.  
  17701.                    realNumberOfPagesinDoc := DetermineNumberOfPagesinDoc
  17702.                                                 (thePrRecHdl^^.prInfo.rPage);
  17703.  
  17704.                    IF (PrJobDialog(thePrRecHdl)) THEN 
  17705.                      BEGIN
  17706.                       {**
  17707.                            Get the number of copies of the document that
  17708.                            the user wants printed from iCopies of the TPrJob
  17709.                            record (IM II-151).
  17710.                       **}
  17711.  
  17712.                       numberOfCopies := thePrRecHdl^^.prJob.iCopies;
  17713.  
  17714.                        {**
  17715.                             Get the first and last pages of the document that
  17716.                             were requested to be printed by the user from
  17717.                             iFstPage and iLastPage from the TPrJob record
  17718.                             (IM II-151).
  17719.                        **}
  17720.  
  17721.                        firstPage := thePrRecHdl^^.prJob.iFstPage;
  17722.                        lastPage := thePrRecHdl^^.prJob.iLstPage;
  17723.  
  17724.                        {**
  17725.                              Print "all" pages in the print loop
  17726.                        **}
  17727.  
  17728.                        thePrRecHdl^^.prJob.iFstPage := 1;
  17729.                        thePrRecHdl^^.prJob.iLstPage := 9999;
  17730.  
  17731.                        {**
  17732.                             Determine the "real" number of pages contained
  17733.                             in the document.  Without this test, you would
  17734.                             print 9999 pages.
  17735.                        **}
  17736.  
  17737.                        IF (realNumberOfPagesinDoc < lastPage) THEN
  17738.                          lastPage := realNumberOfPagesinDoc;
  17739.  
  17740.                        {**
  17741.                             Print the number of copies of the document
  17742.                             requested by the user from the Print Job Dialog.
  17743.                        **}
  17744.  
  17745.                        For copies := 1 To numberOfCopies Do
  17746.                           BEGIN
  17747.                             {**
  17748.                                Install and call your "Print Status Dialog".
  17749.                             **}
  17750.                             PrintingStatusDialog := GetNewDialog(257, NIL,
  17751.                                                                  POINTER(-1));
  17752.                             thePrRecHdl^^.prJob.pIdleProc :=
  17753.                                                     @checkMyPrintDialogButton;
  17754.  
  17755.                             {**
  17756.                                 Restore the resource file to
  17757.                                 the printer driver's.
  17758.                             **}
  17759.                             UseResFile(printmgrsResFile);
  17760.  
  17761.                             thePrPort := PrOpenDoc(thePrRecHdl, NIL, NIL);
  17762.  
  17763.                             IF (PrError = noErr) THEN
  17764.                              BEGIN
  17765.                                {**
  17766.                                    Print the range of pages of the document
  17767.                                    requested by the user from the Print
  17768.                                    Job Dialog.
  17769.                                **}
  17770.  
  17771.                                pageNumber := firstPage;
  17772.                                WHILE ((pageNumber <= lastPage)
  17773.                                      AND (PrError = noErr)) DO
  17774.                                  BEGIN
  17775.  
  17776.                                     PrOpenPage(thePrPort, NIL);
  17777.  
  17778.                                    IF (PrError = noErr) THEN
  17779.                                      BEGIN
  17780.                                        {**
  17781.                                            rPage (IM II-150) is the printable
  17782.                                            area for the currently selected
  17783.                                            printer. By passing the current
  17784.                                            enables your app to use the same
  17785.                                            routine to draw to the screen and
  17786.                                            the printer's GrafPort.
  17787.                                        **}
  17788.  
  17789.                                         DrawStuff (thePrRecHdl^^.prInfo.rPage, 
  17790.                                                    GrafPtr (thePrPort), 
  17791.                                                    pageNumber);
  17792.                                      END;
  17793.                                    PrClosePage(thePrPort);
  17794.                                    pageNumber := pageNumber + 1;
  17795.                                  END;  {**  End pagenumber loop  **}
  17796.                              END;
  17797.                             PrCloseDoc(thePrPort);
  17798.                           END;  {**  End copies loop  **}
  17799.  
  17800.                          {**
  17801.                               The printing job is being canceled by the request
  17802.                               of the user from the Print Style Dialog or the
  17803.                               Print Job Dialog PrError will be set to iPrAbort
  17804.                               to tell the Print Manager to abort the current
  17805.                               printing job.
  17806.                           **}
  17807.                      END
  17808.                       ELSE
  17809.                         PrSetError(iPrAbort); {** Cancel from the job dialog **}
  17810.                  END
  17811.                   ELSE
  17812.                     PrSetError(iPrAbort); {** Cancel from the style dialog **}
  17813.             END;
  17814.         END;
  17815.      IF (thePrRecHdl^^.prJob.bJDocLoop = bSpoolLoop) and (PrError = noErr) THEN
  17816.        PrPicFile(thePrRecHdl, NIL, NIL, NIL, theStatus);
  17817.  
  17818.      {**
  17819.          Grab the printing error before you close
  17820.          the Print Manager and the error disappears.
  17821.      **}
  17822.  
  17823.      PrintError := PrError;
  17824.  
  17825.    PrClose;
  17826.  
  17827.    {**
  17828.         You do not want to report any printing errors until you have fallen
  17829.         through the printing loop. This will make sure that ALL of the Print
  17830.         Manager's open calls have their corresponding close calls, thereby
  17831.         enabling the Print Manager to close properly and that all temporary
  17832.         memory allocations are released.
  17833.    **}
  17834.  
  17835.    IF (PrintError <> noErr) THEN
  17836.      PostPrintingErrors (PrintError);
  17837.  
  17838.   END;
  17839.  
  17840.   IF (thePrRecHdl <> NIL) THEN 
  17841.     DisposHandle(Handle (thePrRecHdl));
  17842.  
  17843.   IF (PrintingStatusDialog <> NIL) THEN 
  17844.     DisposDialog(PrintingStatusDialog);
  17845.  
  17846.   SetPort(oldPort);
  17847. END;  {**  PrintStuff  **}
  17848.  
  17849.  
  17850. MPW C
  17851.  
  17852. /*------ PrintStuff ---------------------------------------------------------*/
  17853.  **
  17854.  **   PrintStuff will call all of the necessary Print Manager calls to print
  17855.  **   a document. It checks PrError() after each Print Manager call. If an
  17856.  **   error is found, all of the Print Manager open calls (i.e., PrOpen,
  17857.  **   PrOpenDoc...) will have a corresponding close call before the error is
  17858.  **   posted to the user.  You want to use this approach to make sure the
  17859.  **   Print Manager closes properly and all temporary memory is released.
  17860.  **/
  17861.  
  17862. void PrintStuff ()
  17863. {
  17864.     GrafPtr          oldPort;
  17865.     short            copies,
  17866.                      firstPage,
  17867.                      lastPage,
  17868.                      numberOfCopies,
  17869.                      printmgrsResFile,
  17870.                      realNumberOfPagesinDoc,
  17871.                      pageNumber,
  17872.                      PrintError;
  17873.     THPrint          thePrRecHdl;
  17874.     TPPrPort         thePrPort;
  17875.     TPrStatus        theStatus;
  17876.  
  17877.  GetPort(&oldPort);
  17878.  
  17879.  /**
  17880.       UnLoadTheWorld will swap out ALL unneeded code segments and data
  17881.       that are NOT required during print time. Your print code must be a
  17882.       separate code segment.
  17883.  **/
  17884.  
  17885.  UnLoadTheWorld ();
  17886.  thePrRecHdl = (THPrint)  NewHandle (sizeof (TPrint));
  17887.  
  17888.  /**
  17889.       Check to make sure that the memory manager did not produce an error
  17890.       when it allocated the print record handle and make sure it did not
  17891.       pass back a nil handle.
  17892.  **/
  17893.  
  17894.  if (MemError() == noErr && thePrRecHdl != nil)
  17895.    {
  17896.     PrOpen();
  17897.  
  17898.     if (PrError() == noErr)
  17899.       {
  17900.        /**
  17901.              Save the current resource file (i.e. the printer driver's) so the 
  17902.              driver will not lose its resources upon return from the pIdleProc.
  17903.        **/
  17904.        printmgrsResFile = CurResFile();
  17905.        PrintDefault(thePrRecHdl);
  17906.  
  17907.        if (PrError() == noErr)
  17908.          {
  17909.             if (PrStlDialog(thePrRecHdl))
  17910.              {
  17911.               /**
  17912.                   DetermineNumberOfPagesinDoc determines the number of pages
  17913.                   contained in the document by comparing the size of the
  17914.                   document with rPage from the TPrInfo record (IM II-150). It
  17915.                   returns the number of pages required to print the document
  17916.                   for the currently selected printer.
  17917.               **/
  17918.  
  17919.               realNumberOfPagesinDoc = DetermineNumberOfPagesinDoc
  17920.                                               ((**thePrRecHdl).prInfo.rPage);
  17921.  
  17922.               if (PrJobDialog(thePrRecHdl)) 
  17923.                 {
  17924.                   /**
  17925.                       Get the number of copies of the document that
  17926.                       the user wants printed from iCopies of the TPrJob
  17927.                       record (IM II-151).
  17928.                   **/
  17929.  
  17930.                   numberOfCopies = (**thePrRecHdl).prJob.iCopies;
  17931.  
  17932.                   /**
  17933.                       Get the first and last pages of the document that were
  17934.                       requested to be printed by the user from iFstPage and
  17935.                       iLastPage from the TPrJob record (IM II-151).
  17936.                   **/
  17937.  
  17938.                   firstPage = (**thePrRecHdl).prJob.iFstPage;
  17939.                   lastPage = (**thePrRecHdl).prJob.iLstPage;
  17940.  
  17941.                   /**
  17942.                         Print "all" pages in the print loop
  17943.                   **/
  17944.  
  17945.                   (**thePrRecHdl).prJob.iFstPage = 1;
  17946.                   (**thePrRecHdl).prJob.iLstPage = 9999;
  17947.  
  17948.                   /**
  17949.                       Determine the "real" number of pages contained in the
  17950.                       document.  Without this test, you would print 9999 pages.
  17951.                   **/
  17952.  
  17953.                   if (realNumberOfPagesinDoc < lastPage)
  17954.                     lastPage = realNumberOfPagesinDoc;
  17955.  
  17956.                   /**
  17957.                       Print the number of copies of the document
  17958.                       requested by the user from the Print Job Dialog.
  17959.                   **/
  17960.                   for (copies = 1; copies <= numberOfCopies; copies++)
  17961.                     {
  17962.                      /**
  17963.                           Install and call your "Print Status Dialog".
  17964.                       **/
  17965.  
  17966.                     PrintingStatusDialog = GetNewDialog(257, nil,
  17967.                                                         (WindowPtr) -1);
  17968.                     (**thePrRecHdl).prJob.pIdleProc = checkMyPrintDialogButton;
  17969.  
  17970.                      /**
  17971.                           Restore the resource file to the printer driver's.
  17972.                      **/
  17973.  
  17974.                      UseResFile(printmgrsResFile);
  17975.                      thePrPort = PrOpenDoc(thePrRecHdl, nil, nil);
  17976.  
  17977.                      if (PrError() == noErr)
  17978.                        {
  17979.  
  17980.                          /**
  17981.                              Print the range of pages of the document 
  17982.                              requested by the user from the Print Job Dialog.
  17983.                          **/
  17984.                         pageNumber = firstPage;
  17985.                         while (pageNumber <= lastPage && PrError() == noErr)
  17986.                             {
  17987.                               PrOpenPage(thePrPort, nil);
  17988.  
  17989.                               if (PrError() == noErr) 
  17990.                                 {
  17991.                                  /**
  17992.                                      rPage (IM II-150) is the printable area
  17993.                                      for the currently selected printer. By
  17994.                                      passing the current port to the draw
  17995.                                      routine, enables your app to use the same
  17996.                                      routine to draw to the screen and the
  17997.                                      printer's GrafPort.
  17998.                                  **/
  17999.                                  DrawStuff ((**thePrRecHdl).prInfo.rPage, 
  18000.                                             (GrafPtr) thePrPort, pageNumber);
  18001.                                 }
  18002.  
  18003.                               PrClosePage(thePrPort);
  18004.                               pageNumber++;
  18005.                            }  /**  End pageNumber loop  **/
  18006.                         }
  18007.                        PrCloseDoc(thePrPort);
  18008.                     } /**  End copies loop  **/
  18009.                  }
  18010.                     /**
  18011.                      The printing job is being canceled by the request of the
  18012.                      user from the Print Style Dialog or the Print Job Dialog.
  18013.                      PrError will be set to PrAbort to tell the Print Manager
  18014.                      to abort the current printing job.
  18015.                     **/
  18016.                else
  18017.                  PrSetError (iPrAbort);   /**  cancel from the job dialog  **/
  18018.             }
  18019.           else
  18020.             PrSetError (iPrAbort);   /**  cancel from the style dialog  **/ 
  18021.         }
  18022.       }
  18023.  
  18024.     if (((**thePrRecHdl).prJob.bJDocLoop == bSpoolLoop) && (PrError() == noErr))
  18025.       PrPicFile(thePrRecHdl, nil, nil, nil, &theStatus);
  18026.  
  18027.     /**
  18028.         Grab the printing error before you close the
  18029.         Print Manager and the error disappears.
  18030.     **/
  18031.  
  18032.    PrintError = PrError();
  18033.  
  18034.    PrClose();
  18035.       
  18036.    /**
  18037.         You do not want to report any printing errors until you have fallen
  18038.         through the printing loop. This will make sure that ALL of the Print
  18039.         Manager's open calls have their corresponding close calls, thereby
  18040.         enabling the Print Manager to close properly and that all temporary
  18041.         memory allocations are released.
  18042.    **/
  18043.    if (PrintError != noErr) 
  18044.      PostPrintingErrors (PrintError);
  18045.   }
  18046.  
  18047.   if (thePrRecHdl != nil)
  18048.     DisposHandle((Handle) thePrRecHdl);
  18049.        
  18050.   if (PrintingStatusDialog != nil)
  18051.     DisposDialog(PrintingStatusDialog);
  18052.  
  18053.   SetPort(oldPort);
  18054. }  /**  PrintStuff  **/
  18055.  
  18056.  
  18057. Further Reference:
  18058. _______________________________________________________________________________
  18059.   •  Inside Macintosh, Volume II-145, The Printing Manager
  18060.   •  Technical Note #118, How to Check and Handle Printing Errors
  18061.   •  Technical Note #122, Device-Independent Printing
  18062.  
  18063. æKY 162
  18064. æC #162: MPW 2.0 Pascal Compiler Bug
  18065.  
  18066. Written by:    Jim Friedlander    September 1, 1987
  18067. Updated:                          March 1, 1988
  18068. _______________________________________________________________________________
  18069.  
  18070. This note formerly described a bug in the MPW 2.0 Pascal compiler. This bug has been
  18071. fixed in MPW 2.0.2.
  18072.  
  18073.  
  18074. æKY 163
  18075. æC #163: Adding Color With CopyBits
  18076.  
  18077. See also:       Color QuickDraw
  18078.  
  18079. Written by:     Chris Derossi     November 2, 1987
  18080. Updated:                          March 1, 1988
  18081. _______________________________________________________________________________
  18082.  
  18083. Inside Macintosh Volume V states that the foreground and background colors are applied
  18084. to an image during a CopyBits or CopyMask call. Accidental use of this feature can
  18085. create bizarre coloring effects. This note explains what happens, how to avoid problems,
  18086. and how to use it.
  18087. _______________________________________________________________________________
  18088.  
  18089.  
  18090. What Happens
  18091.  
  18092. Color QuickDraw has a feature that will allow you to convert a monochrome image to a
  18093. color image. During a CopyBits or CopyMask call, if the foreground and background
  18094. colors are not black and white, respectively, Color QuickDraw performs the following
  18095. operation on every pixel being copied:
  18096.  
  18097.     NOTE: color table index = pixel value
  18098.  
  18099.     s = color table index of source pixel
  18100.     fg = color table index of foreground color
  18101.     bg = color table index of background color
  18102.  
  18103.     ColoredPixelValue = (NOT(s) AND bg) OR (s AND fg)
  18104.  
  18105. If your source image contains only black and white pixels, then all black pixels
  18106. would become the foreground color and all white pixels would become the background
  18107. color. This is because the color table index for white is all zeros and the color
  18108. table index for black is all ones.
  18109.  
  18110. For example, suppose your source image was a 4-bit deep color PixMap. Then the color
  18111. table index for white (in binary) is 0000 and the index for black is 1111. And let’s
  18112. suppose that your foreground color is green with an index of 1101 while your background
  18113. color is red with an index of 0011. Then for the black pixels, the above procedure
  18114. produces:
  18115.  
  18116.     ColoredPixelValue = (NOT(1111) AND 0011) OR (1111 AND 1101)
  18117.          1101         = (  0000    AND 0011) OR (1111 AND 1101)
  18118.  
  18119. And the operation on the white pixels yields:
  18120.  
  18121.     ColoredPixelValue = (NOT(0000) AND 0011) OR (0000 AND 1101)
  18122.          0011         = (  1111    AND 0011) OR (0000 AND 1101)
  18123.  
  18124.  
  18125. Possible Problems
  18126.  
  18127. This colorizing will only work on 2-color (i.e. black and white) images, and then
  18128. only if those colors occupy the first and last entries in the color table. Trying to
  18129. colorize colors that are not the first and last color table entries will yield unexpected
  18130. results.
  18131.  
  18132. This is mainly due to the fact that the colorizing algorithm uses a pixel’s color
  18133. table index value rather than its actual RGB color. To illustrate this, let’s assume
  18134. that foreground and background colors are as above, and your image contains yellow
  18135. with a color table index of 1000. The colorizing operation would give:
  18136.  
  18137.     ColoredPixelValue = (NOT(1000) AND 0011) OR (1000 AND 1101)
  18138.          1011         = (  0111    AND 0011) OR (1000 AND 1101)
  18139.  
  18140. Since the color table may have any RGB color at the resulting index position, the
  18141. final color may not even be close to the source, foreground, or background colors.
  18142.  
  18143. Similar things occur if you are trying to colorize a black and white image when white
  18144. and black do not occupy the first and last positions in the color table.
  18145.  
  18146. The bottom line rules for CopyBitsing in a color environment are these:
  18147.  
  18148.  • Thou shalt set thy background color to white and thy foreground color to 
  18149.    black before calling CopyBits or CopyMask, unless thou art coloring a 
  18150.    monochrome image.
  18151.  
  18152.  • Thou shalt, when colorizing, make sure that the first color table entry is 
  18153.    white and the last color table entry is black.
  18154.  
  18155. The second rule is easy to follow because the default color tables are constructed
  18156. properly, and if you are using the Palette Manager (and you are, right?) then it will
  18157. make sure that the color tables obey this rule.
  18158.  
  18159.  
  18160. How To Colorize — An Example
  18161.  
  18162. This code fragment shows how to implement a color fill, like the paint bucket in
  18163. MacPaint. It relies on three main things: SeedCFill for calculating the fill area,
  18164. CopyMask for actually changing the bits, and QuickDraw colorizing.
  18165.  
  18166. PROCEDURE PaintBucket(where: Point; paintColor: RGBColor);
  18167.  
  18168.     VAR
  18169.         savedFG : RGBColor;
  18170.         offBits : BitMap;
  18171.  
  18172.     BEGIN
  18173.         {First, create an offscreen bitmap.}
  18174.         offBits.bounds := myWindow^.portRect;
  18175.         WITH offBits.bounds DO BEGIN
  18176.             offBits.rowBytes := ((right - left + 15) DIV 16) * 2;
  18177.             offBits.baseAddr := NewPtr((bottom-top) * offBits.rowBytes);
  18178.         END;
  18179.  
  18180.         {Check MemError here! Make sure NewPtr succeeded!}
  18181.  
  18182.         SeedCFill(myWindow^.portBits,offBits,myWindow^.portRect,
  18183.             myWindow^.portRect,where.h,where.v,NIL,0);
  18184.         GetForeColor(savedFG);
  18185.         RGBForeColor(paintColor);
  18186.         CopyMask(offBits,offBits,myWindow^.portBits,myWindow^.portRect,
  18187.             myWindow^.portRect,myWindow^.portRect);
  18188.         RGBForeColor(savedFG);
  18189.  
  18190.         DisposPtr(offBits.BaseAddr);
  18191.     END;
  18192.  
  18193. The variable offBits is an offscreen BitMap (not a PixMap) with bounds = myWindow^.portRect.
  18194.  SeedCFill effectively creates, in the offscreen BitMap,  a monochrome image of the
  18195. bits that we want to paint. Since offBits contains the exact bits that we want to
  18196. paint, it is used as both the source image and the mask for CopyMask.
  18197.  
  18198. By setting the foreground color to the desired paint color, the result is a colorized
  18199. version of the mask (the paint area) being copied onto the window’s PixMap without
  18200. affecting any other bits.
  18201.  
  18202.  
  18203.  
  18204. æKY 164
  18205. æC #164: MPW C Functions: To declare or not to declare, that is the question.
  18206.  
  18207. See also:     MPW C Manual
  18208.               Using Assembly Language
  18209.               The C Programming Language, Kernighan & Ritchie
  18210.               Technical Note 166 — MPW C Functions
  18211.               Using Strings or Points as Arguments
  18212.  
  18213. Written by:    Fred A. Huxham     November 2, 1987
  18214. Updated:                          March 1, 1988
  18215. _______________________________________________________________________________
  18216.  
  18217. Here’s the low-down on when C functions need not be declared in include files.
  18218. _______________________________________________________________________________
  18219.  
  18220. “The include files are all screwed up!” 
  18221.  
  18222. This is a common misconception people have when they look through the MPW C include
  18223. files.  People report that the declaration of a ROM or system call foo() has been
  18224. mistakenly left out of this or that include file.  Here’s the low-down on when functions
  18225. do not have to be declared in an include file.
  18226.  
  18227.  
  18228. The Law
  18229.  
  18230. A C function does not need to be declared in an include file if it requires glue code
  18231. and returns a short or long integer as a result.
  18232.  
  18233. Routines that require glue code include:
  18234.  
  18235.  • All routines that are marked [Not in ROM] in Inside Macintosh
  18236.  • All register based routines (Operating System routines)
  18237.  • All routines which have strings or points as arguments (and have mixed case 
  18238.    spellings)
  18239.  
  18240.  
  18241.  
  18242. æKY 165
  18243. æC #165: Creating Files Inside an AppleShare Drop Folder
  18244.  
  18245. See also:     The File Manager
  18246.               AppleShare Administrator’s Guide
  18247.               Software Applications in a Shared Environment
  18248.  
  18249. Written by:    Rich Andrews       August 3, 1987
  18250. Modified by:   Fred A. Huxham     November 2, 1987
  18251. Updated:                          March 1, 1988
  18252. _______________________________________________________________________________
  18253.  
  18254. This technical note outlines the steps an application must take to create files inside
  18255. AppleShare drop folders.
  18256. _______________________________________________________________________________
  18257.  
  18258. The AppleShare File Server allows the creation of drop folders. These are folders for
  18259. which the user has the Make Changes privilege (write access), but not See Files (read
  18260. access) or See Folders (search access). For an application to create a file in such a
  18261. folder, the following procedure must be executed in strict order:
  18262.  
  18263.  • Issue the Create call to create the new file.
  18264.  • Issue a GetCatInfo call (if desired, to preserve the creation date).
  18265.  • Set the file’s creator and type (and creation date if desired), with a 
  18266.    SetCatInfo call.
  18267.  • Open each fork of the file with an OpenDeny call with deny readers and deny 
  18268.    writers access (an attempt to open with read access will fail). If your 
  18269.    application will need to write to both forks of the file, it must open both 
  18270.    now.
  18271.  • Write each fork.
  18272.    (Do not try to read, any attempt will fail with a privilege error.)
  18273.  • Close each fork.
  18274.  
  18275. This sequence can be followed for creating any file, not just those in drop folders,
  18276. so your application can always create files in this manner. There is no need to special
  18277. case for drop folders.
  18278.  
  18279. Note that you will not be able to do a final SetCatInfo to set the modification date
  18280. to a different value. For example, if you were making a copy of an existing file and
  18281. wanted the copy to retain the original creation and modification dates, it will not
  18282. be possible if the destination is in a drop folder.
  18283.  
  18284.  
  18285.  
  18286. æKY 166
  18287. æC #166: MPW C Functions Using Strings or Points as Arguments
  18288.  
  18289. See also:    MPW C Manual, Appendix H:
  18290.             “Functions Using Strings or Points as Parameters”
  18291.  
  18292. Written by:    Fred A. Huxham     November 2, 1987
  18293. Updated:                          March 1, 1988
  18294. _______________________________________________________________________________
  18295.  
  18296. MPW 2.0 includes new C interfaces to ROM routines which no longer do string and point
  18297. conversions. These new interfaces are described here.
  18298. _______________________________________________________________________________
  18299.  
  18300. In MPW prior to 2.0, the C interfaces to Macintosh OS and Toolbox routines that had
  18301. strings or points as arguments required following these rules:
  18302.  
  18303.     1. Strings must be passed as C strings (null terminated).
  18304.     2. Points must be passed by address.
  18305.  
  18306. With this method, all these functions would end up calling glue code to:
  18307.  
  18308.     1. Convert the C strings to Pascal strings.
  18309.     2. Dereference the address of the Point before pushing it onto the stack.
  18310.  
  18311. Because of this your applications ended up being slightly larger (due to the glue
  18312. code needed) and slightly slower (due to the time it takes to execute the glue code).
  18313.  
  18314. MPW 2.0 C interfaces include a new set of ALL CAPS routines that have strings or
  18315. points as arguments. These routines require following these rules:
  18316.  
  18317.     1. Strings must be passed as Pascal strings (preceded by a length byte).
  18318.     2. Points must be passed by value.
  18319.  
  18320. Calling these new routines results in smaller and faster code.  In other words, these
  18321. new interfaces are your friend.
  18322.  
  18323.  
  18324. Some Examples
  18325.  
  18326. First, some Point examples:
  18327.  
  18328. The routine PtInRect, (old style, mixed case), requires a point passed by address. 
  18329. The new interface:
  18330.  
  18331.    pascal Boolean PTINRECT(pt,r)
  18332.       Point pt;
  18333.       Rect *r;
  18334.       extern 0xA8AD;
  18335.  
  18336. requires that the Point argument be passed by value.
  18337.  
  18338. And now, some string examples:
  18339.  
  18340. The routine StringWidth, (old style, mixed case), required a C string as an argument.
  18341.  The new interface:
  18342.  
  18343.    pascal short STRINGWIDTH(s)
  18344.       Str255 *s;
  18345.       extern 0xA88C;
  18346.  
  18347. requires that the argument be passed as a Pascal string.
  18348.  
  18349.  
  18350. Pascal Strings
  18351.  
  18352. Another new feature of MPW 2.0 C is the creation of Pascal strings.  You can now
  18353. create a Pascal string by using the “\p” option.  The following example demonstrates
  18354. this new feature:
  18355.  
  18356.     cString1 = "This is a C string"
  18357.     pString2 = "\pThis is a Pascal string"
  18358.  
  18359. The first line will create a C, null terminated, string, while the second line will
  18360. create a Pascal, preceded by a length byte, string.
  18361.  
  18362.  
  18363.  
  18364. æKY 167
  18365. æC #167: AppleShare Foreground Applications
  18366.  
  18367. See also:    AppleShare Administrator’s Guide
  18368.  
  18369. Written by:    Fred A. Huxham     November 2, 1987
  18370. Updated:                          March 1, 1988
  18371. _______________________________________________________________________________
  18372.  
  18373. This technical note outlines the requirements and restrictions of an AppleShare foreground
  18374. application. This information pertains to AppleShare versions 1.1 and newer.
  18375. _______________________________________________________________________________
  18376.  
  18377. An AppleShare server requires a dedicated Macintosh. The server, however, is implemented
  18378. as an interrupt-driven application that runs in the system heap of the server machine.
  18379. This allows the running of a concurrent or foreground application that will live in
  18380. the application heap of the server machine. An example of a foreground application is
  18381. LaserShare, the LaserWriter spooler available from Apple.
  18382.  
  18383. An AppleShare foreground application has a few additional restrictions and requirements
  18384. beyond that of a normal Macintosh application:
  18385.  
  18386. 1. In order for AppleShare to recognize your program as a foreground 
  18387.    application, it must contain a resource of type 'fgnd', ID=1, containing a 
  18388.    longword of $00000000.
  18389.  
  18390. 2. Do not make any file system calls outside of server volumes’ Server Folders. 
  18391.    If a foreground application needs to create files, it is recommended that 
  18392.    the application create a folder inside the Server Folder and then create all 
  18393.    its files within that folder. For example, all print spooler or e-mail files 
  18394.    must reside within the Server Folder, and preferably, within a folder that 
  18395.    is inside the Server Folder. To find the Server Folder:
  18396.  
  18397.    • Make a PBHGetVlnfo call on the volume.
  18398.    • Examine ioVFndrInfo[8] (long integer)
  18399.    • If ioVFndrInfo[8] is non-zero, it is the directory ID of the Server Folder.
  18400.  
  18401. 3. Do not to make file system calls or to modify the following in any way: the 
  18402.    AppleShare server application, the Parallel Directory Structure, or the User 
  18403.    or Group data bases within the Server Folder of any volume. Also, do not 
  18404.    rely on the presence or formats of these structures, as they are subject to 
  18405.    change!
  18406.  
  18407. 4. Do not eject or unmount a volume that is not in drive 1 or 2.
  18408.  
  18409. 5. Do not call the Shutdown trap; instad, quit by calling ExitToShell or by 
  18410.    dropping out of the main event loop.
  18411.  
  18412.  
  18413.  
  18414. æKY 168
  18415. æC #168: HyperCard ‘snd ’ Resources
  18416.  
  18417. See also:    The Sound Manager
  18418.  
  18419. Written by:    Chris Knepper      November 2, 1987
  18420. Updated:                          March 1, 1988
  18421. _______________________________________________________________________________
  18422.  
  18423. HyperCard’s play command expects the name of a format 2 ‘snd ’ resource, and accepts
  18424. stop as a parameter (an undocumented feature).
  18425. _______________________________________________________________________________
  18426.  
  18427. Playing Sounds
  18428.  
  18429. HyperCard has the ability to play sounds stored in ‘snd ’ resources. The HyperTalk
  18430. command to play these sounds is:
  18431.  
  18432.     play <"voice"> [tempo <tempo>] <"notes"> [# | b] [octave] [duration]
  18433.  
  18434. The “voice” parameter is the name of an ‘snd ’ resource. The ‘snd ’ resource which
  18435. HyperTalk’s play command expects is the format 2 ‘snd ’ resource. All format 2 ‘snd ’
  18436. resources can be played by HyperCard. These use the “Sampled Sound Synthesizer.” Both
  18437. format 1 and format 2 ‘snd ’ resources are documented in Inside Macintosh Volume V.
  18438. The description of format 2 is missing from earlier versions.
  18439.  
  18440. To see if a sound resource is format 1 or 2, use ResEdit or the MPW Tool DeRez to
  18441. look at the first word of the resource. This will be either $0001 or $0002 depending
  18442. on whether the resource is a format 1 or 2 resource.  Note that all 
  18443. ‘snd ’ resources in the System file are format 1.  Also, note that the ‘snd ’ resources
  18444. which come with HyperCard, “Silence”, “Harpsichord”, and “Boing” are all mistakenly
  18445. labeled as format 1 resources even though they are, in fact, format 2.
  18446.  
  18447. The HyperCard Technical Reference Package (available from APDA) has a file 
  18448. “Flute” containing digitized sound of a flute, produced by the SoundWave program. To
  18449. convert this file to a format 2 ‘snd ’ resource, use the HyperCard stack SoundCapMover
  18450. which comes with the Package. This stack calls an XCMD whose source is also included
  18451. on the disk: SoundCapToRes.c.
  18452.  
  18453.  
  18454. Stopping Sounds
  18455.  
  18456. There is an undocumented feature of HyperTalk’s play command:
  18457.  
  18458.     play stop
  18459.  
  18460. This command stops any sound currently being played.
  18461.  
  18462.  
  18463.  
  18464. æKY 169
  18465. æC #169: HyperCard 1.0.1 and 1.1 Anomalies
  18466.  
  18467. See also:    HyperCard Script Language Guide (APDA)
  18468.  
  18469. Written by:    Chris Knepper      November 2, 1987
  18470. Updated:                          March 1, 1988
  18471. _______________________________________________________________________________
  18472.  
  18473. This technical note describes some HyperCard anomalies with which developers should
  18474. be familiar when developing stackware and indicates differences between HyperCard
  18475. versions 1.0.1 and 1.1, where appropriate.
  18476. _______________________________________________________________________________
  18477.  
  18478.  
  18479. visual effect on a Macintosh II
  18480.  
  18481. The following steps are necessary in order to see the visual effects chosen with
  18482. HyperTalk’s visual effect command on a Macintosh II: set the screen to 2 grays (1 bit
  18483. depth) in the Control Panel’s Monitor, move HyperCard’s window to the main screen
  18484. (the one with the menu bar), and make sure that HyperCard’s window doesn’t cross
  18485. multiple monitors.
  18486.  
  18487. For example, on a Macintosh II the following handler in a HyperCard button script
  18488. will display the checkerboard effect only if the above steps are followed:
  18489.  
  18490.    on mouseUp
  18491.      visual effect checkerboard
  18492.      go to next card
  18493.    end mouseUp
  18494.  
  18495.  
  18496. HyperCard Title Bar Highlighting
  18497.  
  18498. Under MultiFinder 1.0, HyperCard’s title bar highlighting is incorrect when it is
  18499. switched to the background when either the Message box, and/or the Tools tear-off
  18500. menu, and/or the Patterns tear-off menu, and/or the FatBits window are displayed.  In
  18501. these cases, HyperCard’s title bar is highlighted, as if it were the foreground application.
  18502.  This is especially noticeable on larger screens, such as the monitors supplied with
  18503. the Macintosh II.
  18504.  
  18505.  
  18506. High Disk Space Requirement for Background Printing
  18507.  
  18508. Running HyperCard under MultiFinder 1.0 with Background Printing enabled requires
  18509. extensive available disk space for printing large jobs, such as printing a stack
  18510. (with Print Stack… in the File menu) of 100 cards. This is due to Print Monitor’s
  18511. spooling the job to disk.  For example, a 10 card stack may only be 20K in size, but
  18512. the spool file might be greater than 1MB.
  18513.  
  18514.  
  18515. Label Printing
  18516.  
  18517. 1.0.1: When printing, a bug in the ImageWriter print drivers results in a reverse
  18518. form feed at the end of each page, when the user selects “No gaps between pages” in
  18519. the Page Setup… dialog.  This option should allow label printing on continuous-feed
  18520. label sheets with the Print Report… dialog.
  18521.  
  18522. 1.1: The bug in the ImageWriter print drivers has been circumvented so that label
  18523. printing on continuous-feed label sheets works correctly.
  18524.  
  18525.  
  18526. LaserWriter timeout errors
  18527.  
  18528. 1.1: The LaserWriter will stop printing with a timeout error after printing continuously
  18529. for one hour. This makes printing of large stacks, such as the Help stack, difficult.
  18530. To work around this bug, print in smaller batches using a script.
  18531.  
  18532.  
  18533. Print Report… to an ImageWriter
  18534.  
  18535. The font used to print reports (via the Print Report… item in the File menu) is Geneva
  18536. 10.  If this font and size is missing from the user’s System File, the report looks
  18537. bad when printed on an ImageWriter.
  18538.  
  18539.  
  18540. Word wrap with quoted phrases
  18541.  
  18542. HyperCard’s word wrap algorithm handles quoted phrases differently than standard
  18543. TextEdit handles these phrases. For example, when a quoted phrase begins near the end
  18544. of a line in a field, the quoted words, such as “La Traviata”, would wrap between the
  18545. initial quote and the first letter quoted.
  18546.  
  18547. Thus, beginning a quoted phrase near the end of a line in the To Do stack’s field
  18548. would render the following:
  18549.  
  18550. •••Click on the Illustration button below, and refer to Figure 1.••• 
  18551.  
  18552. This may be fixed by resizing the field slightly narrower with the field tool, or by
  18553. putting a carriage return before the quoted word.  This latter workaround renders the
  18554. following:
  18555.  
  18556. •••Click on the Illustration button below, and refer to Figure 2.••• 
  18557.  
  18558. Beware the on idle Handler
  18559.  
  18560. An on idle handler in a HyperCard script executes while the user types into a field. 
  18561. A nasty side-effect effect is that the insertion point may disappear while the user
  18562. types into a field, resulting in the computer beeping since it can’t interpret the
  18563. characters typed, or in the characters being displayed in the Message Box if it is
  18564. open. A particularly insidious example is the following handler in a background script:
  18565.  
  18566.     on idle
  18567.       put the time into bkgnd field "Time"
  18568.     end idle
  18569.  
  18570. If the user types into a field with the above handler in a background script, the
  18571. insertion point disappears from the field whenever the time changes, for example from
  18572. "6:42 PM" to "6:43 PM", requiring HyperCard to change the contents of bkgnd field
  18573. "Time".
  18574.  
  18575.  
  18576. Launching Applications under MultiFinder 1.0 with open
  18577.  
  18578. 1.0.1: HyperCard enables users to launch applications and, optionally, to specify
  18579. that the application open a document. The syntax follows:
  18580.  
  18581.         open <application>
  18582.         open <document> with <application>
  18583.  
  18584. Essentially, HyperCard’s algorithm is: it quits, <application> launches and, optionally,
  18585. opens <document>. If MultiFinder isn’t running when the user quits from <application>,
  18586. then HyperCard launches and returns automatically to the card from which the open
  18587. command was issued. If MultiFinder is running, the application quits to the Finder.
  18588. Then, the next time the user launches HyperCard, it goes to the card from which the
  18589. open command was issued.
  18590.  
  18591. The values of global variables are not saved automatically when HyperCard quits after
  18592. the open, so the stack’s scripts must save them explicitly—perhaps by saving the
  18593. current state of globals to an invisible field in the stack or to a disk file via a
  18594. HyperTalk export text script (using HyperTalk’s open file fileName, write, and close
  18595. file fileName commands) or via an XCMD. The HyperTalk messages suspend and resume are
  18596. sent, respectively, when HyperCard quits because of HyperTalk’s open command and when
  18597. HyperCard next launches after an open command.
  18598.  
  18599. A bug with open when running under System 4.1 or 4.2 and MultiFinder 1.0 results in
  18600. some difficulty with locating <document> if it’s specified in the open command.
  18601.  
  18602. For example, with MacWrite in the folder Applications on the disk HD 20, and with the
  18603. document Sample in the folder Documents on the disk HD 20, the following commands
  18604. from the Message Box, or from a HyperTalk script fails to open Sample with MacWrite.
  18605. [Note that the directories in the Home stack (on the Documents card ":Documents:" or
  18606. "HD 20:Documents:" and on the Applications card ":Applications:" or "HD 20:Applications:")
  18607. have been set correctly.]:
  18608.  
  18609.     open "HD 20:Documents:Sample" with "HD 20:Applications:MacWrite"
  18610.     open ":Documents:Sample" with ":Applications:MacWrite"
  18611.     open "Sample" with "MacWrite"
  18612.  
  18613. Although MacWrite launches correctly, a Stop Alert appears with an error message
  18614. “This document can’t be opened.” After dismissing the Alert by pressing OK, the document
  18615. may be opened with the Open… command in MacWrite’s File menu.
  18616.  
  18617. If this functionality is desired, one workaround is to turn MultiFinder off, then
  18618. open will correctly open Sample. Another workaround is to put the document Sample at
  18619. the root of the volume. In this case, Sample is located at “HD 20:Sample” and MacWrite
  18620. may be placed in the Applications folder. Then, the command:
  18621.  
  18622.     open "Sample" with "MacWrite"
  18623.  
  18624. works correctly.
  18625.  
  18626. 1.1:  open no longer quits under MultiFinder when launching <application>. If there
  18627. is insufficient memory to launch <application> then HyperCard does nothing. Also, the
  18628. bug with locating Sample, as described above, is fixed.
  18629.  
  18630.  
  18631. Optimizing find
  18632.  
  18633. HyperTalk’s find command works best when at least 3 characters per find criteria are
  18634. specified and the stack has recently been compacted with the Compact Stack item in
  18635. the File menu. Please note that specifying less than 3 characters, or using word
  18636. break characters, such as the space or dash, for any of these 3 characters will result
  18637. in a slower find.
  18638.  
  18639. The more trigrams that are specified for the find (strings with at least 3 characters),
  18640. the faster the find. Thus, the following find in a company phone list:
  18641.  
  18642.     find "Fred Dev Tech"
  18643.  
  18644. would find “Fred Huxham” in “Developer Technical Support” faster than would:
  18645.  
  18646.     find "Fr"
  18647.     find "Fred"
  18648.     find "Fred H"
  18649.     find "Fred Huxham"
  18650.  
  18651.  
  18652. Selecting contents of a field with HyperTalk
  18653.  
  18654. There are several ways of selecting text in a field with HyperTalk: by simulating a
  18655. double click, a Shift-Click, or a drag.  The three button scripts presented here show
  18656. how to do this.
  18657.  
  18658. The first shows a method of simulating a double click. This script performs a double
  18659. click at the loc of a card field (approximately the center of the field’s rectangle)
  18660. and then puts the selection into another card field:
  18661.  
  18662.     on mouseUp -- card button "Double Click"
  18663.       click at the loc of field "myField1"
  18664.       click at the loc of field "myField1"
  18665.       if the selection is empty
  18666.       then put "No selection" into card field "DoubleClickSelection"
  18667.       else put the selection into card field "DoubleClickSelection"
  18668.     end mouseUp
  18669.  
  18670. •••Click on the Illustration button below, and refer to Figure 3.••• 
  18671.  
  18672. This second script demonstrates a method of selecting text in a card field with the
  18673. Shift click, and puts the selection in another card field:
  18674.  
  18675.     on mouseUp -- card button "Shift Click"
  18676.       get the rect of bkgnd field "myField2"
  18677.       click at item 3 to 4 of it
  18678.       click at item 1 to 2 of it with ShiftKey
  18679.       if the selection is empty
  18680.       then put "No selection" into card field "ShiftClickSelection"
  18681.       else put the selection into card field "ShiftClickSelection"
  18682.     end mouseUp
  18683.  
  18684. •••Click on the Illustration button below, and refer to Figure 4.••• 
  18685.  
  18686. The third script presents a method of selecting text by dragging over it.  This method,
  18687. however, requires with shiftKey as a parameter to drag in order to work correctly:
  18688.  
  18689.     on mouseUp -- card button "Drag Shift"
  18690.       get the rect of bkgnd field "myField3"
  18691.       drag from item 1 to 2 of it to item 3 to 4 of it with shiftKey
  18692.       if the selection is empty
  18693.       then put "No selection" into card field "DragShiftSelection"
  18694.       else put the selection into card field "DragShiftSelection"
  18695.     end mouseUp
  18696.  
  18697. •••Click on the Illustration button below, and refer to Figure 5.••• 
  18698.  
  18699.  
  18700. dial
  18701.  
  18702. 1.0.1: The dial command ignores "#" and "*" when dialing with the Macintosh speaker. 
  18703. Thus, the following command doesn’t work:
  18704.  
  18705.     dial "#"
  18706.  
  18707. Note that it is still possible to dial "#" and "*" with the with modem option for
  18708. dial as follows:
  18709.  
  18710.     dial "*" with modem "ATDT"
  18711.  
  18712. Since the Phone stack shipped with HyperCard 1.0.1 strips out "#" and "*" before
  18713. passing any arguments to its dial handler, this last example won’t work in the Phone
  18714. stack.
  18715.  
  18716. 1.1: This bug with dial is fixed and the Phone stack no longer strips "#" and 
  18717. "*".
  18718.  
  18719.  
  18720. Paint tools and HyperTalk
  18721.  
  18722. All painting tools except the polygon tool can be controlled by HyperTalk.
  18723.  
  18724.  
  18725. closeField Message
  18726.  
  18727. If the user has manually changed text in a field, then the closeField message is sent
  18728. to the field when the Enter key is pressed, when the Tab key is pressed to move to
  18729. the next field, when the user clicks outside of the field, or in certain cases, when
  18730. the user then clicks on a button. One of the cases in which the message is sent is
  18731. when the button script moves the user to the next or previous cards (go to prev card
  18732. or go to next card). Otherwise, the closeField message is not sent to the field.
  18733.  
  18734. If your stack depends on the closeField message being sent every time a field is
  18735. changed, regardless of which button the user subsequently clicks, then you should
  18736. modify your scripts to ensure this. This may be done as follows.
  18737.  
  18738. In the card script, modify the openCard handler to include the following:
  18739.  
  18740.     on openCard
  18741.       global myField
  18742.  
  18743.       put bkgnd field 1 into myField
  18744.     end openCard
  18745.  
  18746. In the field script, modify the closeField handler to include the following:
  18747.  
  18748.     on closeField
  18749.       global myField
  18750.  
  18751.       put bkgnd field 1 into myField
  18752.     end closeField
  18753.  
  18754. In the button script, modify the mouseDown (or mouseUp) handler to incorporate the
  18755. following:
  18756.  
  18757.     on mouseDown -- bkgnd button 2
  18758.       global myField
  18759.  
  18760.       if myField is not bkgnd field 1 then send closeField to bkgnd field 1
  18761.     end mouseDown
  18762.  
  18763. Then, when the user edits bkgnd field 1 and then clicks on bkgnd button 2, HyperCard
  18764. will send the closeField message to bkgnd field 1 if and only if the user has changed
  18765. its contents and the closeField message has not already been sent.
  18766.  
  18767.  
  18768. exit to HyperCard
  18769.  
  18770. 1.0.1: The exit to HyperCard command, which exits from anywhere in a script back to
  18771. HyperCard, doesn’t close files opened with HyperTalk's open file command.  So be sure
  18772. to close them with HyperTalk's close file command before calling exit to HyperCard.
  18773.  
  18774. 1.1:  exit to HyperCard now closes files opened with HyperTalk's open file command.
  18775.  
  18776.  
  18777. Maximum Limit on Number of Fields
  18778.  
  18779. 1.0.1: The Compact Stack item in the File menu corrupts stacks with one or more cards
  18780. or backgrounds containing more than 126 fields.
  18781.  
  18782. To find out how many background fields are in the current background, type the following
  18783. HyperTalk command into the Message Box and press Return:
  18784.  
  18785.     the number of fields
  18786.  
  18787. To find out how many card fields are in the current card, type the following HyperTalk
  18788. command into the Message Box and press Return:
  18789.  
  18790.     the number of card fields
  18791.  
  18792. 1.1:  This bug with Compact Stack is fixed -- except that the disk space allocated
  18793. for the fields on cards or backgrounds with more than 126 fields is not reclaimed (as
  18794. it is for fields on cards or backgrounds with less than 127 fields).
  18795.  
  18796.  
  18797. Import[ant] tip!!!
  18798.  
  18799. Scripts which create many new cards, eg. scripts which import data, should be sure to
  18800. keep a counter of the number of cards created and call doMenu Compact Stack every 200
  18801. or so cards.  Scripts which lack this feature may cause a variety of strange results,
  18802. including stack corruption, when Compact Stack is eventually selected.
  18803.  
  18804.  
  18805. mouseDown and mouseUp Messages in Scrolling Fields
  18806.  
  18807. The mouseDown and mouseUp messages aren’t sent to scrolling fields when the mouse is
  18808. clicked in the field’s scrollbar.
  18809.  
  18810.  
  18811. The “TouchTone” DRVR
  18812.  
  18813. 1.0.1: HyperCard included a “.TouchTone” DRVR resource ID = 21 to generated the touch
  18814. tone sounds used when dialing phone numbers.
  18815.  
  18816. 1.1:   The “.TouchTone” DRVR has been removed.
  18817.  
  18818.  
  18819. Passing “it” as a Parameter to Handlers
  18820.  
  18821. Passing it as a parameter to a handler is no different than passing any other parameter
  18822. to a handler.  This means that: it is always a local variable to the handler in which
  18823. it is used, and it is passed to a handler by value, not by reference.
  18824.  
  18825.  
  18826. Private Access
  18827.  
  18828. 1.0.1: HyperCard’s Private Access feature, which enables stack authors to require
  18829. users to type in the Password when opening the stack, has a fatal bug.  There is an
  18830. instance in which changing a stack to Private Access will prevent HyperCard from ever
  18831. again opening that stack.
  18832.  
  18833. This bug occurs as follows: create a new stack, assign a password via the Protect
  18834. Stack... item, Quit HyperCard.  Launch HyperCard, go to the stack just created, set
  18835. Private Access via the Protect Stack... item, Quit HyperCard.  The stack is now inaccessible
  18836. to HyperCard.
  18837.  
  18838. A workaround: it is probably best not to set the Private Access feature after setting
  18839. the password.  Instead, set the Private Access feature before assigning a password. 
  18840. Then, HyperCard prompts for the password.  Choose a password and HyperCard may now
  18841. access the stack and the Private Access feature works correctly.
  18842.  
  18843. 1.1:  The bug is fixed.
  18844.  
  18845.  
  18846. find in Card fields
  18847.  
  18848. A feature of HyperTalk’s find command when used to find a string in card fields is
  18849. that the find appears not to work.  In a stack with the card field “Name” whose contents
  18850. are “Jim Friedlander,” the following two find commands result in a beep:
  18851.  
  18852.     find "Jim" in card field 1
  18853.     find "Jim" in card field "Name"
  18854.  
  18855. This feature may be changed in future versions.  Until then, restrict use of find to
  18856. bkgnd fields.
  18857.  
  18858.  
  18859.  
  18860. La Bomba
  18861.  
  18862. 1.1: HyperCard quits when the user deletes a card from a stack and the Background
  18863. script containing an on openCard handler which calls the doMenu command. For example,
  18864. when the user deletes a card whose background script contains the following handler:
  18865.  
  18866.     on openCard
  18867.       doMenu "About HyperCard..."
  18868.     end openCard
  18869.  
  18870. HyperCard displays an Alert reading “Unexpected error 54321” with a button titled
  18871. “Sorry”.  HyperCard then quits when the user clicks the button.  This is a bug which
  18872. may be fixed in future versions.
  18873.  
  18874.  
  18875. La Bomba II
  18876.  
  18877. 1.1: It is imperative that scripts test the global property the heapspace in a variety
  18878. of scripting situations which reduce the amount of available heap space.  When this
  18879. property gets small, the script should do something to free up heap space.
  18880.  
  18881. For example, in scripts which import large amounts of data into a variable, test this
  18882. global property frequently and when the heapspace gets small, dump the contents of
  18883. the variable into a field.
  18884.  
  18885. Another good use for this global property would be in testing whether or not XCMDs or
  18886. XFCNs were properly disposing of memory.  A test script would call the external command
  18887. repeatedly, displaying the heapspace in the Message Box each time.  For example, the
  18888. following script would repeatedly call the XCMD flash, putting the heapspace in the
  18889. Message Box after each call, until the shift key were depressed.  If the value displayed
  18890. in the Message Box degenerated, it would be clear that the XCMD was eating up memory,
  18891. perhaps by allocating blocks with calls to NewHandle() which weren't disposed of with
  18892. DisposHandle().
  18893.  
  18894.     on mouseUp
  18895.       repeat while the shiftKey is up -- depress the Shift Key to stop
  18896.         flash 2 -- call your XCMD or XFCN
  18897.         put the heapspace -- monitor this value to see if it's decreasing
  18898.       end repeat
  18899.     end mouseUp
  18900.  
  18901.  
  18902.  
  18903.  
  18904. æKY 170
  18905. æC #170: HyperCard File Format
  18906.  
  18907. See also:    HyperCard User’s Guide
  18908.  
  18909. Written by:    Chris Knepper    November 2, 1987
  18910. Updated:                        March 1, 1988
  18911. _______________________________________________________________________________
  18912.  
  18913. The file format of a HyperCard stack is proprietary and will not be documented.  In
  18914. most cases, you will not need to know the file format of a HyperCard stack, but instead
  18915. can access data for input and output by using HyperTalk.
  18916. _______________________________________________________________________________
  18917.  
  18918. Currently there are no plans to document the file format of the stacks which HyperCard
  18919. produces.  However, a more flexible alternative, such as a toolbox of routines to
  18920. interface code and HyperCard files, will be available in the near future.  At that
  18921. time, all Certified Developers will be notified.
  18922.  
  18923. Since HyperCard allows developers to control data input and output to files using
  18924. HyperTalk, most needs for file formats may be easily met by using HyperTalk and writing
  18925. scripts.  In particular, the following HyperTalk commands are useful:
  18926.  
  18927.     open file fileName
  18928.     close file fileName
  18929.     read from file fileName until <delimiter>
  18930.  
  18931. Versions of HyperCard after 1.0.1 come with three example scripts for demonstrating
  18932. file I/O: an Import script, an Export Data script, and an Export Text script.  These
  18933. will provide frameworks for building your own custom file 
  18934. I/O routines.
  18935.  
  18936.  
  18937.  
  18938. æKY 171
  18939. æC  #171:    Things You Wanted to Know About _PackBits*
  18940.                    *But Were Afraid to Ask
  18941.  
  18942. Revised by:    Guillermo Ortiz & Jon Zap                             April 1990
  18943. Written by:    Cameron Birse                                      November 1987
  18944.  
  18945. This Technical Note describes the format of data packed by the Toolbox utility
  18946. _PackBits and documents a change to the srcBytes limit and possible worst case.
  18947. Although you can simply unpack this data using _UnPackBits, Apple provides this
  18948. information for the terminally curious and for those manipulating MacPaint®
  18949. documents or PICT files by hand.  Warning:  This format information is subject
  18950. to change. Changes since February 1989:  Added information about the new srcBytes
  18951. limit and worst case results and clarified the description of how to decode
  18952. packed bytes.
  18953. _______________________________________________________________________________
  18954.  
  18955.  
  18956. Length Doesn’t Matter
  18957.  
  18958. Inside Macintosh, Volume I-470, The Toolbox Utilities, describes the Pascal
  18959. interface to the _PackBits trap as follows:
  18960.  
  18961.     PROCEDURE PackBits(VAR srcPtr,dstPtr:Ptr; srcBytes:INTEGER);
  18962.  
  18963. The accompanying text states that srcBytes, the length of your uncompressed data,
  18964. should not be greater than 127, and that in the worst case, the compressed data
  18965. can be srcBytes + 1.  To pack more than 127 bytes, you had to break the data up
  18966. into 127-byte groups and call _PackBits on each group.  Beginning with System
  18967. Software 6.0.2, this limit of 127 bytes is no longer valid.  The new limit is
  18968. 32,767 bytes, which is the maximum positive number that srcBytes can hold.  The 
  18969. worst case can be determined according to the following formula:
  18970.  
  18971.     (srcBytes + (srcBytes+126) DIV 127)
  18972.  
  18973. which is comparable to what you would get if you broke up the data into 127-byte
  18974. groups and picked up an additional byte for each group.
  18975.  
  18976.  
  18977. Mommy, How Do They Make Packed Bits?
  18978.  
  18979. The first byte is a flag-counter byte that specifies whether or not the the
  18980. following data is packed, and the number of bytes involved.  If this first byte
  18981. is a negative number, then the following data is packed.  In this case, the number
  18982. is the two’s complement of a zero-based count of the number of times the data byte
  18983. repeats when expanded.  There is one data byte following this first byte in packed
  18984. data.  The byte after the data byte is the next flag-counter byte.
  18985.  
  18986. If the flag-counter byte is a positive number, then the following data is unpacked.
  18987. In this case, the number is a zero-based count of the number of incompressible data
  18988. bytes that follow.  There are (flag-counter+1) data bytes following the flag-counter
  18989. byte.  The byte after the last data byte is the next flag-counter byte.
  18990.  
  18991. Note that there is no way to know, given a pointer to the start of packed data, when
  18992. you have reached the end of the packed data.  This is why you need to know either
  18993. the length of the packed or unpacked data before you start unpacking.
  18994. _UnPackBits requires the length of the unpacked data.
  18995.  
  18996. Consider the following example:
  18997.  
  18998. Unpacked data:
  18999.  
  19000.     AA AA AA 80 00 2A AA AA AA AA 80 00 2A 22 AA AA AA AA AA AA AA AA AA AA
  19001.  
  19002. After being packed by _PackBits:
  19003.  
  19004.     FE AA                    ; (-(-2)+1) = 3 bytes of the pattern $AA
  19005.     02 80 00 2A              ; (2)+1 = 3 bytes of discrete data
  19006.     FD AA                    ; (-(-3)+1) = 4 bytes of the pattern $AA
  19007.     03 80 00 2A 22           ; (3)+1 = 4 bytes of discrete data
  19008.     F7 AA                    ; (-(-9)+1) = 10 bytes of the pattern $AA
  19009.  
  19010.        or
  19011.  
  19012.     FE AA 02 80 00 2A FD AA 03 80 00 2A 22 F7 AA
  19013.     *     *           *     *              *
  19014.  
  19015. The bytes with the asterisk (*) under them are the flag-counter bytes.
  19016. _PackBits only packs the data when there are three or more consecutive bytes with
  19017. the same data, otherwise it just copies the data byte for byte (and adds the count
  19018. byte).
  19019.  
  19020. Note:  The data associated with some PICT opcodes, $0098 (PackBitsRect)
  19021.        and $0099 (PackBitsRgn), contain PixData which is basically made
  19022.        of _PackBits data.  It should be noted, though, that the format
  19023.        for PixData includes a byteCount or length in addition to the data
  19024.        described in this Note.
  19025.  
  19026. For example, the following is the result of decoding a sample PICT2:
  19027.  
  19028. data 'PICT' (25534) {
  19029.     0936 0000 0000 0007 001E                /* pic size, picFrame */
  19030.     0011 02FF                               /* pict2              */
  19031.     0C00                                    /* header             */
  19032.          FFFF FFFF 0000 0000 0000 0000 001E 0000 0007 0000 0000 0000
  19033.     001E                                    /* def hilite         */
  19034.     0001                                    /* clipRgn            */
  19035.          000A 0000 0000 0007 001E
  19036.     0098                                    /* PackBitsRect       */
  19037.          801E                               /* rowbytes of 30     */
  19038.          0000 0000 0007 001E                /* Bounds             */
  19039.          0000                               /* packType           */
  19040.          0000                               /* version            */
  19041.          0000 0000                          /* packSize           */
  19042.          0048 0000                          /* hRes               */
  19043.          0048 0000                          /* vRes               */
  19044.          0000                               /* pixelType          */
  19045.          0008                               /* pixelSize          */
  19046.          0001                               /* cmpCount           */
  19047.          0008                               /* cmpSize            */
  19048.          0000 0000                          /* planeBytes         */
  19049.          0000 1F10                          /* pmTable            */
  19050.          0000 0000                          /* pmReserved         */
  19051.         /*color table*/
  19052.               0000 4CBC                     /* ctSeed             */
  19053.               8000                          /* ctFlags            */
  19054.               00FF                          /* ctSize             */
  19055.                    0000 FFFF FFFF FFFF
  19056.                    ...                 /* 254 ColorSpec's omitted */
  19057.                    0000 0000 0000 0000
  19058.          0000 0000 0007 001E                /* srcRect            */
  19059.          0000 0000 0007 001E                /* dstRect            */
  19060.          0000                               /* srcCopy            */
  19061.  
  19062.          /* Now we have the scan line data packed as follows:
  19063.             [bytecount for current scan line] [data as defined above]
  19064.             If rowBytes is > 250 then byteCount is a word else is a byte
  19065.             (in this case, byteCount is a byte)
  19066.             note that each unpacked row adds to 30 rowBytes
  19067.          */
  19068.  
  19069.          /* line 1, byte count is 2 (best case for a row)   */
  19070.          02
  19071.             E3 FF                /* -(-29) + 1 = 30 FF's    */
  19072.          /* line 2, byte count is 19 (0x13)                 */
  19073.          13
  19074.             01 FF 23             /* 1+1 data bytes          */
  19075.             FE 00                /* -(-2)+1 0's             */
  19076.             FC 23                /* -(-4)+1 0x23's          */
  19077.             FE 00                /* 3 0's                   */
  19078.             FC 23                /* 5 0x23's                */
  19079.             FE 00                /* 3 0's                   */
  19080.             FC 23                /* 5 0x23's                */
  19081.             FE 00                /* 3 0's                   */
  19082.             00 FF                /* 1 data byte             */
  19083.          /* line 3, byte count is 28                        */
  19084.          1C
  19085.             02 FF 00 23          /* 3 data bytes            */
  19086.             FE 00                /* 3 0's                   */
  19087.             FE 23                /* 3 0x23's                */
  19088.             01 00 23             /* 2 data bytes            */
  19089.             FE 00                /* 3 0's                   */
  19090.             FE 23                /* 3 0x23's                */
  19091.             01 00 23             /* 2 data bytes            */
  19092.             FE 00                /* 3 0's                   */
  19093.             FE 23                /* 3 0x23's                */
  19094.             04 00 23 00 00 FF    /* 5 data bytes            */
  19095.          /* line 4, byte count is 31 (worst case for a row) */
  19096.          1F
  19097.             03 FF 00 00 23       /* 4 data bytes            */
  19098.             FE 00                /* 3 0's                   */
  19099.             00 23                /* 1 data byte             */
  19100.             FE 00                /* 3 0's                   */
  19101.             00 23                /* 1 data byte             */
  19102.             FE 00                /* 3 0's                   */
  19103.             00 23                /* 1 data byte             */
  19104.             FE 00                /* 3 0's                   */
  19105.             00 23                /* 1 data byte             */
  19106.             FE 00                /* 3 0's                   */
  19107.             00 23                /* 1 data byte             */
  19108.             FE 00                /* 3 0's                   */
  19109.             02 23 00 FF          /* 3 data bytes            */
  19110.  
  19111.          /* line 5, byte count is 28                        */
  19112.          1C
  19113.             01 FF 00             /* 2 data bytes            */
  19114.             FE 23                /* 3 0x23's                */
  19115.             01 00 23             /* 2 data bytes            */
  19116.             FE 00                /* 3 0's                   */
  19117.             FE 23                /* 3 0x23's                */
  19118.             01 00 23             /* 2 data bytes            */
  19119.             FE 00                /* 3 0's                   */
  19120.             FE 23                /* 3 0x23's                */
  19121.             01 00 23             /* 2 data bytes            */
  19122.             FE 00                /* 3 0's                   */
  19123.             FE 23                /* 3 0x23's                */
  19124.             00 FF                /* 1 data byte             */
  19125.          /* line 6, byte count is 18                        */
  19126.          12
  19127.             00 FF                /* 1 data byte             */
  19128.             FC 23                /* 5 0x23's                */
  19129.             FE 00                /* 3 0's                   */
  19130.             FC 23                /* 5 0x23's                */
  19131.             FE 00                /* 3 0's                   */
  19132.             FC 23                /* 5 0x23's                */
  19133.             FE 00                /* 3 0's                   */
  19134.             FD 23                /* 4 0x23's                */
  19135.             00 FF                /* 1 data byte             */
  19136.          /* line 7, byte count is 2 (best case for a row)   */
  19137.          02
  19138.             E3 FF                /* 30 0xFF's               */
  19139.          00  /* pad so next command starts at word boundary */
  19140.  
  19141.     00FF                         /*end of pic               */
  19142. };
  19143.  
  19144.  
  19145. Further Reference:
  19146. _______________________________________________________________________________
  19147.   •  Inside Macintosh, Volume I-465, The Toolbox Utilities
  19148.   •  Inside Macintosh, Volume V-39, Color QuickDraw
  19149.   •  Technical Note #86, MacPaint Document Format
  19150.  
  19151. MacPaint is a registered trademark of Claris Corporation
  19152.  
  19153. æKY 172
  19154. æC #172: Parameters for MDEF Message #3
  19155.  
  19156. See also:       The Menu Manager
  19157.  
  19158. Written by:     Chris Derossi       November 2, 1987
  19159. Updated:                            March 1, 1988
  19160. _______________________________________________________________________________
  19161.  
  19162. In order to support popup menus, menu definition procedures (MDEFs) must now respond
  19163. to a new message, mPopupMsg. mPopupMsg is message number 3. When your MDEF is called
  19164. with this message, it should calculate the rectangle in which the popup menu should
  19165. appear.
  19166.  
  19167. The interface to an MDEF is
  19168.  
  19169.     PROCEDURE MyMDEF(message: Integer; theMenu: MenuHandle; VAR menuRect:
  19170.                         Rect; hitPt: Point; VAR whichItem: Integer);
  19171.  
  19172. For mPopupMsg, the message parameter will be 3 and theMenu will be a MenuHandle to
  19173. your menu. The MDEF should compute a rectangle for the menu such that the item passed
  19174. in whichItem will be displayed at hitPt. 
  19175.  
  19176. •••Click on the Illustration button below, and refer to Figure 1.••• 
  19177.  
  19178. The hitPt parameter, though, is NOT a Point. Instead, this parameter is used to pass
  19179. the top left of the item, passing the top coordinate and then the left coordinate.
  19180. This is the opposite order of the fields in a Point. The values can be used together
  19181. as a LongInt, with left in the high word and top in the low word, or separately as
  19182. two Integers.
  19183.  
  19184. A more correct Pascal interface to the MDEF (for the mPopupMsg only) would be:
  19185.  
  19186.     PROCEDURE MyMDEF(message: Integer; theMenu: MenuHandle; VAR menuRect:
  19187.                         Rect; top, left: Integer; VAR whichItem: Integer);
  19188.     
  19189.  
  19190. NOTE: The MPW interface files incorrectly list mPopupMsg as 4; it should be 3.
  19191.  
  19192.  
  19193.  
  19194. æKY 173
  19195. æC #173: PrGeneral Bug
  19196.  
  19197. See also:     The Printing Manager
  19198.               Technical Note #128 — PrGeneral
  19199.  
  19200. Written by:    Scott “ZZ” Zimmerman     November 2, 1987
  19201. Updated:                                March 1, 1988
  19202. _______________________________________________________________________________
  19203.  
  19204. This technical note documents a bug in the implementation of the PrGeneral procedure
  19205. in the LaserWriter driver version 4.0. The bug has to do with the format of the information
  19206. returned by the GetRslData opcode. This technical note will also describe a workaround
  19207. for the problem.
  19208. _______________________________________________________________________________
  19209.  
  19210. One of the opcodes supported by the PrGeneral procedure (Technical Note #128) is
  19211. named GetRslData. The GetRslData operation initializes a resolution record that is of
  19212. the following form:
  19213.     
  19214.     TRslRg = RECORD {used in TGetRslBlk}
  19215.         iMin:    Integer;    {0 if printer only supports discrete resolutions}
  19216.         iMax:    Integer;    {0 if printer only supports discrete resolutions}
  19217.     END;
  19218.                         
  19219.     TRslRec = RECORD    {used in TGetRslBlk}
  19220.         iXRsl:    Integer;    {a discrete, physical X resolution}
  19221.         iYRsl:    Integer;    {a discrete, physical Y resolution}
  19222.     END;
  19223.     
  19224.     TGetRslBlk = RECORD {data block for GetRslData call}
  19225.         iOpCode:    Integer;    {input; = getRslDataOp}
  19226.         iError:     Integer;    {output}
  19227.         lReserved:  LongInt;    {reserved for future use}
  19228.         iRgType:    Integer;    {output; this declaration is for RgType1}
  19229.         XRslRg:     TRslRg;     {output; range of X resolutions}
  19230.         YRslRg:     TRslRg;     {output; range of Y resolutions}
  19231.         iRslRecCnt: Integer;    {output; how many RslRecs follow}
  19232.         rgRslRec:   ARRAY[1..27] 
  19233.                 OF TRslRec;     {output; number used depends on printer type}
  19234.     END;
  19235.  
  19236. The LaserWriter 4.0 implementation has a bug that affects the YRslRg and XRslRg fields
  19237. of the TGetRslBlk record. The correct values for the fields are:
  19238.  
  19239.         TGetRslBlk.XRslRg.iMin := 25;
  19240.         TGetRslBlk.XRslRg.iMax := 1500;
  19241.         TGetRslBlk.YRslRg.iMin := 25;
  19242.         TGetRslBlk.YRslRg.iMax := 1500;
  19243.  
  19244. Unfortunately, the information returned by the LaserWriter 4.0 version of PrGeneral
  19245. is:
  19246.  
  19247.         TGetRslBlk.XRslRg.iMin := 25;
  19248.         TGetRslBlk.XRslRg.iMax := 25;
  19249.         TGetRslBlk.YRslRg.iMin := 1500;
  19250.         TGetRslBlk.YRslRg.iMax := 1500;
  19251.  
  19252. The recommended workaround for this problem is to use the PrDrvrVers function 
  19253. (Inside Macintosh II-163) to find out which version of the print driver you are using.
  19254. If you are using 4.0, modify the resolution data before using it. The following code
  19255. fragment illustrates this workaround:
  19256.  
  19257.     PROCEDURE CheckRslRecord(VAR theRslRecord: TGetRslBlk);
  19258.     CONST
  19259.         BogusDriver = 40;
  19260.     BEGIN
  19261.         IF PrDrvrVers = BogusDriver THEN BEGIN
  19262.             theRslRecord.XRslRg.iMax := theRslRecord.YRslRg.iMax;
  19263.             theRslRecord.YRslRg.iMin := theRslRecord.XRslRg.iMin;
  19264.         END;        
  19265.     END;
  19266.  
  19267. When the bug is fixed in a future version of the driver, the CheckRslRecord procedure
  19268. will no longer have any effect on the resolution record. This will make sure your
  19269. application gets the correct resolution data no matter which version of the driver is
  19270. being used.
  19271.  
  19272.  
  19273.  
  19274. æKY 174
  19275. æC #174: Accessing the Script Manager Print Action Routine
  19276.  
  19277. See also:    The Script Manager 
  19278.         
  19279. Written by:    Mark Davis    November 2, 1987
  19280. Updated:                     March 1, 1988
  19281. _______________________________________________________________________________
  19282.  
  19283. This technical note describes how Print Drivers can access the Script Manager Print
  19284. Action routine to print unconventional text, such as Japanese or Arabic.
  19285. _______________________________________________________________________________
  19286.  
  19287.  
  19288. General Notes
  19289.  
  19290. Scripts such as Japanese or Arabic modify the normal QuickDraw text handling in order
  19291. to represent text properly. On the screen, this is done by trapping StdText and StdTxtMeasure,
  19292. and transforming the text before printing. For example, for Hebrew or Arabic the text
  19293. might be reversed, since text normally goes from right to left in those scripts.
  19294.  
  19295. Print drivers require slightly different handling, for two reasons:
  19296.  
  19297. 1. A print driver might not call the standard QuickDraw procedures. For example,
  19298.  
  19299.    the LaserWriter writes directly in PostScript instead.
  19300.  
  19301. 2. A print driver might need to format the text, for accurate line-layout. In 
  19302.    this case, the text needs to be transformed before the driver performs line-
  19303.    layout. If the driver is spooling the text, and will replay the text a 
  19304.    second time, the text cannot be transformed a second time, since that would 
  19305.    ruin the appearance.
  19306.  
  19307. For example, the ImageWriter driver calls QuickDraw procedures twice, once to spool
  19308. and once to unwind the spooling. The text must be transformed when spooling, so that
  19309. line layout can be done, but when unwinding, the transformation must be turned off
  19310. completely.
  19311.  
  19312. Note that some drivers, such as the LaserWriter, use QuickDraw re-entrantly: the
  19313. application program calls a QuickDraw routine, which is directed to the driver’s
  19314. grafProcs, which in turn call QuickDraw internally to put up status messages on the
  19315. screen. The Print Action procedure handles the text properly so that the text transformations
  19316. are enabled during the re-entrant calls, so that the status messages will be properly
  19317. formatted.
  19318.  
  19319.  
  19320. When To Call the Print Action Routine
  19321.  
  19322. The Script Manager Print Action routine allows the print driver to be independent of
  19323. the particular scripts being used. The printing driver should call this routine whenever
  19324. it changes the grafProcs in the printing grafPort. The Print Action routine will then
  19325. substitute grafProcs of its own in the grafProcs record, saving the original routine
  19326. addresses.
  19327.  
  19328. The Print Action routine will actually call a Print Action routine for each script
  19329. system that is currently installed. Each of the script Print Action routines will do
  19330. the appropriate tasks for its system.
  19331.  
  19332.  
  19333. Calling the Print Action Routine
  19334.  
  19335. To call the Print Action routine, the driver should use the following code:
  19336.  
  19337. intlGlobals     equ   $ba0    ; international globals
  19338. printActionOff  equ   $16     ; offset to PrintAction proc ptr
  19339.     
  19340. ; get procedure pointer to call
  19341.     
  19342.    tst.w    Rom85                  ; on a Macintosh + or better?
  19343.    blt.s    @PrintActionDone       ; no, skip
  19344.    move.l   intlGlobals,d2         ; get international globals
  19345.    ble.s    @PrintActionDone       ; not there, skip
  19346.    move.l   d2,a0                  ; in address register
  19347.    move.l   printActionOff(a0),d2  ; get print action address
  19348.    beq.s    @PrintActionDone       ; not there, skip
  19349.    move.l   d2,a0                  ; in address register
  19350.     
  19351. ; set up arguments to call
  19352.     
  19353.    move.l   <myPort>,d0            ; pass the port
  19354.    move.w   <myVerb>,d1            ; pass the verb
  19355.    jsr      (a0)                   ; call the procedure
  19356. @PrintActionDone
  19357.  
  19358.  
  19359. Print Action Routine Verbs
  19360.  
  19361. There are currently three verbs to pass to the Print Action routine.
  19362.  
  19363.     paUnwindText      equ   –1
  19364.     paSpoolText       equ    1
  19365.     paNoQuickDraw     equ    3
  19366.  
  19367. Use the paUnwind verb to ensure that the text is not transformed before your StdText
  19368. procedure receives the text. This verb is used when playing back stored text that has
  19369. already been transformed.
  19370.  
  19371. The other two verbs (paNoQuickDraw and paSpoolText) are used to ensure that the text
  19372. is transformed before your StdText procedure receives the text. The paSpoolText verb
  19373. is used when your driver will use QuickDraw to image the text in the printing grafPort.
  19374. The paNoQuickDraw verb is used when the text is not drawn into the printing port by
  19375. going through QuickDraw (e.g. the LaserWriter). In that case some languages (e.g.
  19376. Japanese) which use an extended font structure may need to recast the text calls as
  19377. CopyBits calls.
  19378.  
  19379. As mentioned above, some applications may call QuickDraw from within the driver, as
  19380. when a status window is updated. During any StdTxtMeasure calls in the driver during
  19381. the application’s call to StdText, the port is checked against the printer port. If
  19382. they match, then the text is not transformed. Otherwise, the text is transformed.
  19383.  
  19384. The solutions adopted by the Print Action routine assume that the print driver does
  19385. not measure or draw text except within calls to StdTxtMeasure or StdText. If your
  19386. driver does text buffering (as for line layout), make sure that any measurements are
  19387. performed within these two calls. For example, you might buffer both the text and its
  19388. screen width as measured by QuickDraw.
  19389.  
  19390.  
  19391.  
  19392. æKY 175
  19393. æC #175: SetLineWidth Revealed 
  19394.  
  19395. See also:     LaserWriter Reference Manual
  19396.               PostScript Language Reference Manual
  19397.               PostScript Language Tutorial and Cookbook
  19398.         
  19399. Written by:    Scott “ZZ” Zimmerman    November 2, 1987
  19400. Updated:                               March 1, 1988
  19401. _______________________________________________________________________________
  19402.  
  19403. This technical note describes the internal implementation, and correct method of
  19404. using, the SetLineWidth Picture Comment.
  19405. _______________________________________________________________________________
  19406.  
  19407. The SetLineWidth picture comment provides a way of accessing PostScript’s 
  19408. 'setlinewidth' operator. Since the LaserWriter resolution is roughly four times that
  19409. of the Macintosh screen, fractional line widths can be printed. The SetLineWidth
  19410. PicComment provides a way for applications to access these fractional line widths
  19411. through PostScript, without having to use floating point numbers.
  19412.  
  19413. First of all, the LaserWriter has an internal state that is stored in a number of
  19414. PostScript variables. For more information on PostScript variables, see the PostScript
  19415. Language Reference Manual. Some operations performed on the LaserWriter cause the
  19416. values of these variables to change. One of these variables contains the width of the
  19417. printer’s pen. The SetLineWidth picture comment works by changing the value of this
  19418. variable.
  19419.  
  19420. Before we look at what the SetLineWidth comment does, let’s look at the argument
  19421. passed to the comment. The argument is represented as a QuickDraw Point, however it
  19422. is interpreted by the LaserWriter as a fraction. The LaserWriter interprets a point(h,v)
  19423. to be a real number whose value is (v / h). This means that a point whose value is
  19424. h=2, v=1, will be converted to 0.5 before being used by the LaserWriter. If you wanted
  19425. to pass a value of 0.25, you would pass a point whose value is h=4, v=1. For 1.25,
  19426. pass a point, h=4, 
  19427. v= 5.
  19428.  
  19429. In addition to the pen width variable, there is a variable that is used for scaling
  19430. the pen’s width. This variable, named pnm for PeN Multiplier, contains a real number
  19431. which is applied to the pen width. The default value of pnm is 1.0, which causes no
  19432. scaling of the line width.
  19433.  
  19434. Whenever the SetLineWidth PicComment is sent to the LaserWriter, the current value of
  19435. pnm is replaced by the value passed to the PicComment. The current pen size is then
  19436. scaled by the new value of pnm. The following example will display four lines of
  19437. different sizes. It is meant to illustrate the interaction between the QuickDraw
  19438. PenSize procedure and the SetLineWidth PicComment.
  19439.  
  19440. TYPE
  19441.     widthHdl    = ^widthPtr;
  19442.     widthPtr    = ^widthPt;
  19443.     widthPt     = Point;
  19444.  
  19445. VAR
  19446.     theWidth:   widthHdl;
  19447.  
  19448. BEGIN
  19449.  
  19450.     (* Initialize the print manager as per Inside Macintosh II-155. *)
  19451.  
  19452. At this point, it is assumed that PrPageOpen has been called, and the print manager
  19453. is ready to accept data.
  19454.  
  19455. The first thing we do is set the scaling factor to 1.0. This way, no scaling will be
  19456. performed when we call PenSize.
  19457.  
  19458.     theWidth := widthHdl(NewHandle(SizeOf(widthPt)));
  19459.     (*Real programs do error checking here... *)
  19460.     SetPt(theWidth^^, 1, 1);
  19461.     PicComment(SetLineWidth, SIZEOF(widthPt), Handle(theWidth));
  19462.  
  19463. Here we call PenSize. Because the pnm has been set to 1.0, the pen size(1,1) times
  19464. the multiplier (1.0) yields 1,1.
  19465.  
  19466.     PenSize(1, 1);
  19467.     MoveTo(50, 100);
  19468.     LineTo(500, 100);
  19469.     MoveTo(50, 125);
  19470.     DrawString('1 point thickness.');
  19471.  
  19472. Now we will use the SetLineWidth PicComment to change the pen size. Note that when we
  19473. change the scaling factor, the pen size changes as well.
  19474.  
  19475.     SetPt(theWidth^^, 1, 5);
  19476.     PicComment(SetLineWidth, SIZEOF(widthPt), Handle(theWidth));
  19477.     MoveTo(50, 200);
  19478.     LineTo(500, 200);
  19479.     MoveTo(50, 225);
  19480.     DrawString('5.0 times 1 point pen size = 5 point thickness.');
  19481.  
  19482. If any calls to PenSize are made at this point, the new pen size will be scaled by
  19483. 5.0. This is because the SetLineWidth PicComment is still in effect. We will now send
  19484. a SetLineWidth PicComment to revert the scaling factor back to 1.0.
  19485.  
  19486.     SetPt(theWidth^^, 5, 1);
  19487.     PicComment(SetLineWidth, SIZEOF(widthPt), Handle(theWidth));
  19488.     MoveTo(50, 300);
  19489.     LineTo(500,300); 
  19490.     MoveTo(50, 325);
  19491.     DrawString('0.2 times 5 point pen size = 1 point thickness.');
  19492.  
  19493.  
  19494. Since the scaling is once again 1.0, PenSize calls at this point will not be scaled.
  19495. Here we explicitly set the scaling factor to 1.0 before changing the pen size. This
  19496. makes it easier to see what scaling will be applied to the next call to PenSize.
  19497.  
  19498.     SetPt(theWidth^^, 1, 1);
  19499.     PicComment(SetLineWidth, SIZEOF(widthPt), Handle(theWidth));
  19500.     PenSize(1, 1);
  19501.     MoveTo(50, 400);
  19502.     LineTo(500,400); 
  19503.     MoveTo(50, 425);
  19504.     DrawString('1.0 times 1 point pen size = 1 point thickness');
  19505.     (* Dispose of the handle when you are through with it! *)
  19506.     DisposHandle(Handle(theWidth));
  19507.  
  19508. When printed, the above example will produce the following:
  19509.  
  19510. •••Click on the Illustration button below, and refer to Figure 1.••• 
  19511.  
  19512. To summarize, there are four things to remember when using the SetLineWidth PicComment:
  19513.  
  19514. 1. The argument to the SetLineWidth PicComment is specified as a point, though 
  19515.    it is actually interpreted by the LaserWriter as a real number. The point 
  19516.    value is specified as h,v, and the LaserWriter interprets the value as v / h.
  19517.  
  19518. 2. The SetLineWidth PicComment affects both the height and width of the pen, 
  19519.    even though the name suggests otherwise.
  19520.  
  19521. 3. When you send the SetLineWidth PicComment, the current pen size will be 
  19522.    scaled. Any drawing that is done after the PicComment is set, will be done 
  19523.    with the scaled pen size.
  19524.  
  19525. 4. When you call the QuickDraw PenSize procedure, the pen size will be scaled 
  19526.    after it has been set. For example, if your scaling factor is 0.5, and you 
  19527.    set the pen size to 2,2, the actual pen size will be 1,1. If you don’t want 
  19528.    the scaling to occur, make sure to send a SetLineWidth PicComment, with the 
  19529.    point argument set to 1,1.  The next call to PenSize will then be scaled by 
  19530.    1.0, which will have no effect.
  19531.  
  19532.  
  19533.  
  19534. æKY 176
  19535. æC #176:    Macintosh Memory Configurations
  19536.  
  19537. Revised by:    Craig Prouse & Dennis Hescox                        October 1989
  19538. Written by:    Cameron Birse                                      November 1987
  19539.  
  19540. This Technical Note describes the different possible memory configurations of all
  19541. models of the Macintosh family that use Single Inline Memory Modules (SIMMs) as well
  19542. as the non-SIMM memory upgrade options of the Macintosh Portable.  Special thanks to
  19543. Brian Howard for the Macintosh Plus and original SE drawings, and for the inspiration
  19544. for the other drawings.
  19545. Changes since April 1989:  Added configurations for the Macintosh IIci and Macintosh
  19546. Portable and a section describing special problems relating to the use of four megabit
  19547. (Mbit) DRAM SIMMs in the Macintosh II and IIx.
  19548. _______________________________________________________________________________
  19549.  
  19550. Macintosh Developer Technical Support receives numerous questions about the many
  19551. different possible configurations of RAM on the different Macintoshes, so we’ll attempt
  19552. to answer these questions in this Technical Note, as well as provide a showcase for
  19553. some outstanding artwork by Apple engineer Brian Howard.
  19554.  
  19555. Warning:  Because the video monitor is built in, there are dangerous
  19556.           voltages inside the cases of the Macintosh Plus and Macintosh SE
  19557.           computers.  The video tube and video circuitry may hold dangerous
  19558.           charges long after the computer’s power is turned off.  Opening
  19559.           the case of the Macintosh Plus and Macintosh SE computers requires
  19560.           special tools and may invalidate your warranty.  Installation of
  19561.           RAM in the SIMM sockets in these computers should be done by
  19562.           qualified service personnel only.
  19563.  
  19564.  
  19565. Macintosh Plus
  19566.  
  19567. The Macintosh Plus has the following possible configurations (see Figure 1):
  19568.  
  19569.     512K, using two 256 Kbit SIMMs
  19570.     1 MB, using four 256 Kbit SIMMs
  19571.     2 MB, using two 1Mbit SIMMs
  19572.     2.5 MB, using two 1Mbit SIMMs and two 256Kbit SIMMs
  19573.     4MB, using four 1Mbit SIMMs
  19574.  
  19575. It is important to place the SIMMs in the correct location when using a combination
  19576. of SIMM sizes, as in the 2.5 MB example, and to make sure the right resistors are
  19577. cut.  Refer to Figure 1 for the correct location of the SIMMs and size resistors.
  19578.  
  19579. •••Click on the Illustration button, and refer to Figure 1.••• 
  19580.  
  19581. Macintosh SE
  19582.  
  19583. The Macintosh SE configurations (the original motherboard as well as the revised
  19584. motherboard with a memory jumper selector) are the same as the Macintosh Plus, except
  19585. physical locations on the motherboard are different.  In addition, memory configurations
  19586. with only two SIMMs (e.g., 512K and 2 MB) use slots 3 and 4 on the revised SE motherboard
  19587. instead of slots 1 and 2 like the original motherboard and Macintosh Plus.  Refer to
  19588. Figures 2 and 3 for the correct locations and settings.
  19589.  
  19590. •••Click on the Illustration button, and refer to Figures 2 and 3.•••
  19591.  
  19592. Macintosh SE/30, II, IIx, and IIcx
  19593.  
  19594. Since these machines use a 32-bit data bus with eight-bit SIMMs, you must always
  19595. upgrade memory in four SIMM chunks.  The eight SIMM connectors are divided into two
  19596. banks of four SIMM slots, Bank A and Bank B.
  19597.  
  19598. On the Macintosh SE/30, Bank A is located next to the ROM SIMM while Bank B is next
  19599. to the 68882 co-processor.  On the Macintosh II and IIx, Bank A is the bank closest
  19600. to the edge of the board, while on the Macintosh IIcx, Bank A is the bank closest to
  19601. the disk drives and power supply.  Refer to Figure 4 for the proper locations of
  19602. Banks A and B on the SE/30, II, and IIx, and refer to Figure 5 for the proper locations
  19603. on the IIcx.
  19604.  
  19605. Unlike the Macintosh Plus and the Macintosh SE, these machines have no resistors to
  19606. cut and no jumpers to set; you need only install the SIMMS in the correct banks and
  19607. you’ll be up and running.  You can implement the following configurations:
  19608.  
  19609.     1MB, using four 256 Kbit SIMMs in Bank A
  19610.     2MB, using eight 256 Kbit SIMMs in Banks A and B
  19611.     4MB, using four 1 Mbit SIMMs in Bank A
  19612.     5MB, using four 1 Mbit SIMMs in Bank A and four 256 Kbit SIMMs in Bank B
  19613.     8MB, using eight 1 Mbit SIMMs in Banks A and B
  19614.  
  19615. Again, it is important to make sure the right size SIMMs are in the right Bank; when
  19616. you are using a combination of SIMMs, the larger SIMMs (in terms of Mbits) must be in
  19617. Bank A.  When you are using only four SIMMs, they must be in Bank A as well.
  19618.  
  19619. •••Click on the Illustration button, and refer to Figure 4.••• 
  19620.  
  19621. Macintosh IIci
  19622.  
  19623. The Macintosh IIci motherboard layout is somewhat different from the IIcx, but the
  19624. location of the RAM SIMMs is unchanged.  Bank A is still the bank closest to the disk
  19625. drives.  Refer to Figure 5 for the proper locations of Banks A and B on the IIci.
  19626.  
  19627. The IIci has a much-improved RAM interface and allows a great deal more freedom when
  19628. installing SIMMs.  Banks A and B are interchangeable, meaning that when mixing two
  19629. sizes of RAM, the larger SIMMs do not necessarily have to go in Bank A.  In fact, for
  19630. best performance when using on-board video, Apple recommends that the smaller SIMMs
  19631. be installed in Bank A.  Note, however, that if on-board video is used, then RAM must
  19632. be present in Bank A.
  19633.  
  19634. The IIci requires that SIMMs be 80 ns RAS-access time or faster and the same speed
  19635. within a row.  You can implement the following memory configurations with 256K and
  19636. 1MB SIMMs:
  19637.  
  19638.     1 MB using four 256 Kbit SIMMs in Bank A or in Bank B
  19639.     2 MB using eight 256 Kbit SIMMs in Banks A and B
  19640.     4 MB using four 1 Mbit SIMMs in Bank A or in Bank B
  19641.     5 MB using four 256 Kbit SIMMs in Bank A and four 1 Mbit SIMMs in Bank B
  19642.     5 MB using four 1 MBit SIMMs in Bank A and four 256 Kbit SIMMs in Bank A
  19643.     8 MB using eight 1 Mbit SIMMs in Banks A and B
  19644.  
  19645. The 1 MB and 4 MB configurations using only Bank B are not compatible with on-board
  19646. video, since Bank A must contain memory when using on-board video.  The first 5 MB
  19647. configuration (with 256 Kbit SIMMs in Bank A) is recommended for 5 MB configurations
  19648. using on-board video.
  19649.  
  19650. Parity RAM
  19651.  
  19652. Some specially-ordered versions of the Macintosh IIci are equipped with a PGC chip
  19653. and support parity for RAM error detection.  These machines require parity RAM. 
  19654. SIMMs for these machines are nine bits wide instead of eight, so there is generally
  19655. an extra RAM IC on the SIMM.  There is no difference in the installation of 256K x 9
  19656. or 1M x 9 SIMMs.
  19657.  
  19658.  
  19659. Macintosh Portable
  19660.  
  19661. Memory expansion on the Macintosh Portable is different from other members of the
  19662. Macintosh family since the Portable uses memory expansion cards in place of SIMMs. 
  19663. The base Portable is equipped with 1 MB of RAM on the motherboard and has one RAM
  19664. expansion card slot.  Apple currently supplies a 1 MB memory expansion kit which
  19665. takes the Portable to 2 MB total.  Apple and third-party developers may produce higher
  19666. capacity expansion boards (2 MB to 8 MB) in the future.
  19667.  
  19668. Since the Portable has only one RAM expansion slot, you may use only one memory expansion
  19669. board at a time.  This limit means that a 1 MB expansion board would have to be completely
  19670. replaced by a higher capacity board when it became available.
  19671.  
  19672. Total RAM for the Portable will always be 1 MB plus the size of your one RAM expansion
  19673. board (if installed).  Refer to Figure 5 for the location of the RAM expansion slot.
  19674.  
  19675. •••Click on the Illustration button, and refer to Figure 5.••• 
  19676.  
  19677.  
  19678. 4 Mbit DRAMs in Revolt
  19679.  
  19680. When the Macintosh II was originally designed, Apple engineers intended for it to
  19681. accept large amounts of memory in the form of 4 MB and 16 MB DRAM SIMMs.  That was in
  19682. 1986, when 1 Mbit DRAM was difficult to find and the higher-density chips did not yet
  19683. exist.  The engineers anticipated the pinouts of the yet-to-be introduced 4 MB SIMMs
  19684. and provided all the necessary hardware and address multiplexing to allow installation
  19685. of these parts when they became available.
  19686.  
  19687. Woe that Cupertino is not Camelot, James Brown is in jail, and 4 MB SIMMs do not work
  19688. as advertised in most cases.  This is the story of the Revolt of the 4 MB DRAM SIMMs.
  19689.  
  19690. Preliminary Notes
  19691.  
  19692. Before diving into the problem with 4 Mbit DRAMs, there is some preliminary ground
  19693. which must be covered.
  19694.  
  19695. First, there are a couple ways to construct a 4 MB SIMM.  Using old technology, it is
  19696. possible to cram together 32 DRAM ICs of 1M x 1 density.  Using new technology, it
  19697. only takes eight 4M x 1 ICs, resulting in a much smaller, lower-power module.  If a 4
  19698. MB SIMM is of the large, so-called composite type (i.e., it is constructed of thirty-two
  19699. 1 Mbit ICs), then everything is fine except on the original Macintosh II.
  19700.  
  19701. This exception is due to an undocumented feature in the ROM firmware shipped with the
  19702. original Macintosh II.  Unfortunately, the original Macintosh II ROM startup code
  19703. does not know about 4 MB SIMMs and dies a horrible death before the cursor even appears.
  19704.  Thus, a Macintosh II with original ROMs is limited to using 1 MB SIMMs and 8 MB RAM
  19705. maximum.  Subsequent  Macintosh models have revised ROMs which recognize 4 MB SIMMs.
  19706.  
  19707. A Macintosh II CPU can receive a ROM upgrade enabling it to accept 4 MB SIMMs.  This
  19708. upgrade requires installation (strangely enough) of the 1.4 MB SuperDrive package. 
  19709. This requirement is presumably because the SuperDrive package includes the Macintosh
  19710. IIx ROMs, which can handle 4 MB SIMMs, but  which also expect the presence of a SWIM
  19711. chip in place of the old IWM.
  19712.  
  19713. With the SuperDrive upgrade, the Macintosh II is on equal footing with the Macintosh
  19714. IIx.  That is, SIMMs made exclusively of the new 4 Mbit ICs still won’t work, regardless
  19715. of whether you are using a Macintosh II or IIx; therefore, for the remainder of this
  19716. discussion, Macintosh II is used to refer to not only the original Macintosh II, but
  19717. also the IIx.
  19718.  
  19719. The 4 Mbit Problem
  19720.  
  19721. DRAM ICs are now available in 4 Mbit density, but they come with a very nasty surprise.
  19722.  JEDEC, the committee overseeing the standardization of new solid-state devices, has
  19723. added an additional built-in test mode to high-density DRAMs.  The test mode is invoked
  19724. by a sequence of electrical signals which was ignored by earlier-generation DRAM. 
  19725. The crux of the situation is this:  under certain conditions, the Macintosh II unwittingly
  19726. activates this new test mode and large amounts of memory become very forgetful.
  19727.  
  19728. More Specifically…
  19729.  
  19730. Those who are interested in the specific phenomenon occurring within the memory ICs
  19731. should consult the detailed technical data supplied by the DRAM manufacturers.  This
  19732. Note only explains how the Macintosh II offends this new feature of the 4 Mbit DRAM,
  19733. and hence, what might be done to work around the problem.
  19734.  
  19735. The Macintosh II uses /CAS-before-/RAS refresh cycles to keep RAM up-to-date on its
  19736. contents.  For 1 Mbit DRAM, the state of the /W control line is ignored during this
  19737. type of refresh cycle.  No longer.  DRAM of the 4 Mbit variety goes off into test
  19738. mode if /W is asserted (low, so that the RAM thinks it is write-enabled) during a
  19739. /CAS-before-/RAS refresh cycle.  The problem with the Macintosh II is that /W is the
  19740. same signal as the MPU R/W line, and if the MPU is writing to an I/O address or a
  19741. NuBus™ card concurrently with a refresh cycle, all the conditions are right for a
  19742. waltz into test mode.  Unfortunately, this condition is not all that unusual, since
  19743. video card accesses qualify.
  19744.  
  19745. The Salvage Process
  19746.  
  19747. All is not necessarily lost, and although the situation is ugly, there is still a way
  19748. to use 4 Mbit DRAM ICs to construct 4 MB SIMMs which work in the Macintosh II.  A
  19749. solution lies in the addition of a ninth IC to the SIMM.  Programmed with suitable
  19750. logic, a high-speed (-D or -E suffix) PAL™ on the SIMM itself can recognize and intercept
  19751. /CAS-before-/RAS refresh cycles and set /W appropriately before any damage is done. 
  19752. More or less, the PAL becomes an intelligent buffer between the MPU read/write line
  19753. and the DRAM write-enable lines.  When the PAL senses a refresh cycle commencing, it
  19754. holds /W high, ensuring that the ICs are not corrupted by the potentially dangerous
  19755. processor-generated R/W signal.
  19756.  
  19757. What the Future Holds…
  19758.  
  19759. It is unlikely that Apple will recall the affected machines to install a fix or even
  19760. change the design of current-model Macintosh II computers produced in the future. 
  19761. New members of the Macintosh family should correct the problem, however.  Note that
  19762. the Macintosh SE/30, IIcx, and IIci all address this problem.  There are currently no
  19763. specifications available for 16 Mbit DRAM; therefore, it is unknown at this time
  19764. whether any current Macintosh models will be compatible with these devices.
  19765.  
  19766. Consolation for SIMM manufacturers:  SIMMs constructed with an on-board PAL are not
  19767. necessarily Macintosh II-specific.  SIMMs constructed in this manner should work
  19768. without modification in any application calling for 4 MB SIMMs (except in the unlikely
  19769. event an application requires the new test mode).
  19770.  
  19771.  
  19772. Further Reference:
  19773. _______________________________________________________________________________
  19774.   •  Inside Macintosh, Volume V-1, Compatibility Guidelines
  19775.   •  Guide to Macintosh Family Hardware, Chapter 5, Macintosh Memory
  19776.  
  19777. NuBus is a trademark of Texas Instruments
  19778. PAL is a trademark of Monolithic Memories, Inc.
  19779.  
  19780.  
  19781. æKY 177
  19782. æC #177: Problem with WaitNextEvent in MultiFinder 1.0
  19783.  
  19784. See also:     Technical Note #158 — Frequently Asked MultiFinder Questions
  19785.               Technical Note #180 — MultiFinder Miscellanea
  19786.  
  19787. Written by:    Jim Friedlander    November 2, 1987
  19788. Updated:                          March 1, 1988
  19789. _______________________________________________________________________________
  19790.  
  19791. This Technical Note discusses a bug in WaitNextEvent in MultiFinder 1.0. This bug
  19792. only occurs when WaitNextEvent is called from the background. This bug will be fixed
  19793. in the next release of MultiFinder. 
  19794. Change since 11/87: the bug will be fixed in Systems with versions greater than $04FF.
  19795. _______________________________________________________________________________
  19796.  
  19797. In MultiFinder 1.0, applications that use WaitNextEvent:
  19798.  
  19799.     FUNCTION WaitNextEvent(mask: INTEGER; VAR event: EventRecord;
  19800.                            sleep: LONGINT; mouseRgn: RgnHandle): BOOLEAN;
  19801.  
  19802.     pascal Boolean WaitNextEvent(mask,event,sleep,mouseRgn)
  19803.         unsigned short mask;
  19804.         EventRecord *event;
  19805.         unsigned long sleep;
  19806.         RgnHandle mouseRgn;
  19807.  
  19808.  
  19809. should not call WaitNextEvent from the background with a value of sleep that is greater
  19810. than 50. This value has been determined empirically for a Macintosh II; larger values
  19811. can be used on the Macintosh Plus and the Macintosh SE. If an application uses a
  19812. large value of sleep when running in the background, MultiFinder 1.0 will hang under
  19813. the following circumstances:
  19814.  
  19815.  • The application that is calling WaitNextEvent with a large sleep value has 
  19816.    been put in the background.
  19817.  • That application becomes the foreground application when another application 
  19818.    (including the desk accessory handler) quits.
  19819.  
  19820. If you use a value of sleep that is small enough, this problem will not occur. If you
  19821. use an algorithm to calculate sleep, make sure that the maximum is clipped to 50.
  19822.  
  19823. This problem will be fixed in the next release of MultiFinder. You can call SysEnvirons
  19824. to test for the system version number. This bug will not happen when the System version
  19825. is greater than $04FF.
  19826.  
  19827.  
  19828.  
  19829. æKY 178
  19830. æC #178: Modifying the Standard String Comparison
  19831.  
  19832. See also:    The International Utilities
  19833.         
  19834. Written by:    Mark Davis                November 2, 1987
  19835.                Priscilla Oppenheimer
  19836. Updated:                                 March 1, 1988
  19837. _______________________________________________________________________________
  19838.  
  19839. This technical note describes how to modify the standard string comparison by constructing
  19840. an itl2 resource. Developers may want to modify the standard string comparison if
  19841. Apple’s comparison doesn’t meet their needs or if Apple has not written a string
  19842. comparison routine for the language that concerns them.
  19843. _______________________________________________________________________________
  19844.  
  19845.  
  19846. General Structure
  19847.  
  19848. The itl2 resource contains a number of procedures that are used for accurate comparison
  19849. of text by the International Utilities Package. Refer to Inside Macintosh, volume V
  19850. for an explanation of the algorithm used. The default itl2 for standard English text,
  19851. which does no special processing, has the following form:
  19852.  
  19853.     ; normal Include/Load statements
  19854.     Include 'hd:mpw:aincludes:ScriptEqu.a'
  19855.     Print    On,NoMDir
  19856.  
  19857.     String    AsIs
  19858.  
  19859. ;------------------------------------------------------------------------
  19860. ;     dispatch table at the front of the code.
  19861. ;------------------------------------------------------------------------
  19862. Intl1        Proc
  19863.     With    IUSortFrame,IUStrData
  19864. HookDispatch
  19865.     dc.w    ReturnEQ-HookDispatch    ; InitProc = 0
  19866.     dc.w    ReturnEQ-HookDispatch    ; FetchHook = 2
  19867.     dc.w    ReturnEQ-HookDispatch    ; VernierHook = 4
  19868.     dc.w    ReturnEQ-HookDispatch    ; ProjectHook = 6
  19869.     dc.w    ReturnEQ-HookDispatch    ; ReservedHook1 = 8
  19870.     dc.w    ReturnEQ-HookDispatch    ; ReservedHook2 = 10
  19871.  
  19872. ;------------------------------------------------------------------------
  19873. ; Some common exit points
  19874. ;------------------------------------------------------------------------
  19875. ReturnNE    
  19876.     tst.w     MinusOne    ; set cc NE
  19877.     rts
  19878. ReturnEQ
  19879.     cmp.w    d0,d0        ; set cc EQ
  19880.     rts
  19881. ;------------------------------------------------------------------------
  19882.     EndWith
  19883.     EndWith
  19884.     End
  19885.  
  19886. If modifications need to be made to the comparison process, then one or more of the
  19887. dispatches will be modified to point to different routines:
  19888.  
  19889.     dc.w    InitProc-HookDispatch      ; InitProc = 0
  19890.     dc.w    FetchProc-HookDispatch     ; FetchHook = 2
  19891.     dc.w    VernierProc-HookDispatch   ; VernierHook = 4
  19892.     dc.w    ProjectProc-HookDispatch   ; ProjectHook = 6
  19893.  
  19894.  
  19895. There are a number of different changes that can be made to the comparison routines.
  19896. Some of the common modifications include:
  19897.  
  19898.  1. Comparing two bytes as one character
  19899.     Yugoslavian “l” < “lj” < “m”; Japanese…       [InitProc, FetchProc]
  19900.  2. Comparing characters in different order
  19901.     Norwegian “z” < “å”                           [ProjectProc]
  19902.  3. Comparing one character as two
  19903.     German “ä” ≈ “ae”                             [ProjectProc]
  19904.  4. Ignoring characters unless strings are otherwise equal:
  19905.     “blackbird” < “black-bird” < “blackbirds”     [ProjectProc]
  19906.  5. Changing the secondary ordering
  19907.     Bibliographic “a” < “A”                       [VernierProc]
  19908.  
  19909. The comparison hook procedures are all assembly language based, with arguments described
  19910. below. Since the routines may be called once per character in both strings, the routines
  19911. should be as fast as possible.
  19912.  
  19913. The condition codes are used to return information about the status of the hook routine.
  19914. Typically the normal processing of characters will be skipped if the CCR is set to
  19915. NE, so the default return should always have EQ set. Each of these routines has access
  19916. to the stack frame (A6) used in the comparison routine, which has the following form:
  19917.  
  19918. IUSortFrame    Record  {oldA6},Decrement
  19919.    result       ds.w     1
  19920.    argTop       equ      *
  19921.    aStrText     ds.l     1
  19922.    bStrText     ds.l     1
  19923.    aStrLen      ds.w     1
  19924.    bStrLen      ds.w     1
  19925.    argSize      equ      argTop-*
  19926.    return       ds.l     1
  19927.    oldA6        ds.l     1
  19928.    aInfo        ds       IUStrData
  19929.    bInfo        ds       IUStrData
  19930.    wantMag      ds.b     1            ; 1-MagStrig 0-MagIdString.
  19931.    weakEq       ds.b     1            ; Signals at most weak equality
  19932.    msLock       ds.b     1            ; high byte of master ptr.
  19933.    weakMag      ds.b     1            ; -1 weak, 1 strong compare
  19934.    supStorage   ds.b     18           ; extra storage.
  19935.    localSize    equ      *            ; frame size.
  19936.                 EndR
  19937.  
  19938. There are three fields in this frame that are of interest for altering text comparison.
  19939. The supStorage field is an area reserved for use by the comparison hook procedures as
  19940. they see fit. The aInfo and bInfo records contain information about the current byte
  19941. positions in the two compared strings A and B, and information about the status of
  19942. current characters in those string. The IUStrData record has the following form:
  19943.  
  19944. IUStrData    Record    0
  19945.    curChar     ds.w    1    ; current character.
  19946.    mapChar     ds.w    1    ; projected character.
  19947.    decChar     ds.w    1    ; decision char for weak equality
  19948.    bufChar     ds.b    1    ; buffer for expansion.
  19949.    justAfter   ds.b    1    ; boolean for AE vs ligature-AE.
  19950.    ignChar     ds.b    1    ; flag: ignore char.
  19951.    noFetch     ds.b    1    ; flag: no fetch of next.
  19952.    strCnt      ds.w    1    ; length word.
  19953.    strPtr      ds.l    1    ; current ptr to string.
  19954.                EndR
  19955.  
  19956.  
  19957. The Init Procedure
  19958.  
  19959. The Init Procedure is used to initialize the comparison process. The main use for
  19960. this procedure is for double-byte scripts. As an optimization, the International
  19961. Utilities will perform an initial check on the two strings, comparing for simple
  19962. byte-to-byte equality. Thus any common initial substrings are checked before the Init
  19963. procedure is called. The string pointers and lengths in the IUStrData records have
  19964. been updated to point just past the common substrings.
  19965.  
  19966. Languages such as Japanese or Yugoslavian, which may consider two bytes to be one
  19967. character, may have to back up one byte, as shown below.
  19968.  
  19969. ;------------------------------------------------------------------------
  19970. ; Routine    InitProc
  19971. ; Input      A6          Local Frame
  19972. ; Output     CCR         NE to skip entire sort (usually set EQ)
  19973. ; Trashes    Standard regs:                   A0/A1/D0-D2
  19974. ; Function   Initialize any special international hooks.
  19975. ;            Double-byte scripts must synchronize AInfo.StrPtr & 
  19976. ;            BInfo.StrPtr here!
  19977. ;------------------------------------------------------------------------
  19978. ; Note: this should also check for single-byte nigori or maru, as below
  19979.  
  19980. InitProc
  19981.     move.w     AStrLen(a6), d0          ; A length
  19982.     sub.w      AInfo.StrCnt(a6),d0      ; see if its changed
  19983.     beq.s      @FixB                    ; A is done if not
  19984.     sub.l      #2,sp                    ; return param
  19985.     move.l     AStrText(a6),-(sp)       ; textBuf
  19986.     move.w     d0,-(sp)                 ; textOffset
  19987.     _CharByte
  19988.     tst.w      (sp)+                    ; on character boundary?
  19989.     ble.s      @FixB                    ; yes, continue
  19990.     sub.l      #1,AInfo.StrPtr(A6)      ; adjust pointer
  19991.     add.w      #1,AInfo.StrCnt(A6)      ; adjust count
  19992. @FixB
  19993.     move.w     BStrLen(a6), d0          ; B length
  19994.     sub.w      BInfo.StrCnt(a6),d0      ; see if its changed
  19995.     beq.s      Quit Init                ; B is done if not
  19996.     sub.l      #2,sp                    ; return param
  19997.     move.l     BStrText(a6), -(sp)      ; textBuf
  19998.     move.w     d0, -(sp)                ; textOffset
  19999.     _CharByte
  20000.     tst.w      (sp)+                    ; on character boundary?
  20001.     ble.w      @QuitInit                ; yes, continue
  20002.     sub.l      #1,BInfo.StrPtr(A6)      ; adjust pointer
  20003.     add.w      #1,BInfo.StrCnt(A6)      ; adjust count
  20004. @QuitInit
  20005.     bra.s      ReturnEQ                 ; return to the caller.
  20006.     EndWith
  20007.  
  20008.  
  20009. The Fetch Procedure
  20010.  
  20011. The Fetch Procedure is used to fetch a character from a string, updating the pointer
  20012. and length to reflect the remainder of the string. For example, the following code
  20013. changes the  text comparison for Yugoslavian:
  20014.  
  20015. ;------------------------------------------------------------------------
  20016. ; Routine  FetchProc
  20017. ; Input    A2         String Data Structure
  20018. ;          A3         String pointer (one past fetched char)
  20019. ;          A6         Local Frame
  20020. ;          D4.W       Character: top byte is fetched character, bottom is zero
  20021. ;          D5.B       1 if string is empty, otherwise 0
  20022. ; Output   D4.W       Character: top byte set to character, bottom to extension
  20023. ;          D5.B       1 if string is empty, otherwise 0
  20024. ; Trashes     Standard regs:    A0/A1/D0-D2
  20025. ; Function    This routine returns the characters that are fetched from
  20026. ;             the string, if they are not just a sequence of single bytes.
  20027. ;------------------------------------------------------------------------
  20028.  
  20029. FetchProc
  20030.     tst.b     d5               ; more characters in string?
  20031.     bne.s     ReturnEq         ; no -> bail out.
  20032.  
  20033.     move.w    d4,d0            ; load high byte.
  20034.     move.b    (a3),d0          ; load low byte.
  20035.     
  20036.     lea       pairTable,a1     ; load table address
  20037.  
  20038. @compareChar
  20039.     move.w    (a1)+,d1         ; pair = 0?
  20040.     beq.s     ReturnEq         ; yes -> end of table.
  20041.     cmp.w     d0,d1            ; legal character pair?
  20042.     bne.s     @compareChar     ; no -> try the next one.
  20043.     add.w     #1,a3            ; increment pointer.
  20044.     sub.w     #1,StrCnt(a2)    ; decrement length.
  20045.     addx.w    d5,d5            ; empty -> set the flag.
  20046.     move.w    d0,d4            ; copy character pair.
  20047.     rts                        ; return to caller with CCR=NE
  20048.  
  20049. pairTable
  20050.     dc.b     'Lj'              ; Lj
  20051.     dc.b     'LJ'              ; LJ
  20052.     dc.b     'lJ'              ; lJ
  20053.     dc.b     'lj'              ; lj
  20054.     
  20055.     dc.b     'Nj'              ; Nj
  20056.     dc.b     'NJ'              ; NJ
  20057.     dc.b     'nJ'              ; nJ
  20058.     dc.b     'nj'              ; nj
  20059.  
  20060.     dc.b     'D', $be          ; Dz-hat
  20061.     dc.b     'D', $ae          ; DZ-hat
  20062.     dc.b     'd', $ae          ; dZ-hat
  20063.     dc.b     'd', $be          ; dz-hat
  20064.  
  20065.     DC.B     $00, $00          ; table end
  20066.  
  20067. The same sort of procedure is used for Japanese or other double-byte scripts, in
  20068. order to combine two bytes into a single character for comparison.
  20069.  
  20070. FetchProc
  20071.     with      IUStrData
  20072.     tst.b     d5                ; empty string?
  20073.     bne.s     ReturnEq          ; exit if length = 0
  20074.     
  20075. ; if we have a double-byte char, add the second byte
  20076.     lea       CurChar(a2),a0    ; pass pointer
  20077.     move.w    d4,(a0)           ; set value at ptr
  20078.     clr.w     d0                ; pass length
  20079.  
  20080.     sub.l     #2,SP             ; allocate return
  20081.     move.l    a0,-(sp)          ; pointer
  20082.     move.w    d0,-(sp)          ; offset
  20083.     _CharByte
  20084.     tst.w     (sp)+             ; test return
  20085.     bmi.s     @DoubleByte       ; skip if high byte (first two) 
  20086.  
  20087. ; we don’t have a double byte, but two special cases combine second bytes
  20088.     move.b    (a3),d0          ; get next byte
  20089.     cmp.b     #$DE,d0          ; nigori?
  20090.     beq.s     @DoubleByte      ; add in
  20091.     cmp.b     #$DF,d0          ; maru?
  20092.     bne.s     ReturnEq         ; exit: single byte
  20093.  
  20094. @DoubleByte
  20095.     move.b    (a3)+,d4         ; get next byte
  20096.     subq.w    #1,StrCnt(A2)    ; dec string length
  20097.     addx.w    d5,d5            ; set x=1 if string len = 0
  20098.     rts                        ; return to caller with CCR=NE
  20099.  
  20100. The Project Procedure
  20101.  
  20102. The Project Procedure is used to find the primary ordering for a character. This
  20103. routine will map characters that differ only in the secondary ordering onto a single
  20104. character, typically the unmodified, uppercase character. For example, the following
  20105. changes the comparison order for some Norwegian characters, so that they occur after
  20106. ‘Z.’
  20107.  
  20108. ;------------------------------------------------------------------------
  20109. ; Routine  ProjectProc
  20110. ; Input      A2          String Data Structure
  20111. ;            D4.W        Character (top byte is char, bottom is extension
  20112. ;                        (the extension is zero unless set by FetchProc))
  20113. ; Output     D4.W        Projected Character
  20114. ;            CCR         NE to skip normal Project
  20115. ; Trashes    Standard regs:    A0/A1/D0-D2
  20116. ; Function   This routine projects the secondary characters onto primary
  20117. ;                  characters.
  20118. ;        Example: a,ä,Ä -> A
  20119. ;------------------------------------------------------------------------
  20120.  
  20121. ProjectProc
  20122.     lea      ProjTable,A1       ; load table address.
  20123. @findChar
  20124.     move.l   (a1)+,D0           ; get entry
  20125.     cmp.w    d0,d4              ; original ≥ entry?
  20126.     bhi.s    @findChar          ; no, try the next entry.
  20127.     bne.s    ReturnEq           ; not equal, process normally
  20128.  
  20129. @replaceChar
  20130.     swap      d0                ; get replacement
  20131.     move.w    d0,d4             ; set new character word.
  20132. @doneChar
  20133.     rts                         ; CCR is NE to skip project.
  20134.     
  20135. ProjTable
  20136. ;    Table contains entries of the form  r1, r2, o1, o2,
  20137. ;    where r1,r2 are the replacement word, and 
  20138. ;    o1, o2 are the original character.
  20139. ;    The entries are sorted by o1,o2 for use in the above algorithm
  20140.  
  20141.     DC.B    'Z', 3, 'Å', 0    ; Å after Ø
  20142.     DC.B    'Z', 3, 'å', 0    ; å after Ø
  20143.     DC.B    'Z', 1, 'Æ', 0    ; Æ after Z
  20144.     DC.B    'Z', 2, 'Ø', 0    ; Ø after Æ
  20145.     DC.B    'Z', 1, 'æ', 0    ; æ after Z
  20146.     DC.B    'Z', 2, 'ø', 0    ; ø after Æ
  20147.     DC.L    $FFFFFFFF         ; table end
  20148.  
  20149. The Project procedure can also be used to undo the effects of the normal projection.
  20150. For example, suppose that “œ” is not to be expanded into “oe”: in that case, a simple
  20151. test can be made against 'œ',0, returning NE if there is a match, so that the normal
  20152. processing is not done. To expand one character into two, the routine should return
  20153. the first replacement character in D4.W, and modify two fields in the IUStrData field.
  20154. For example, given that A1 points to a table entry of the form (primaryCharacter:
  20155. Word; secondaryCharacters: Word), the following code could be used:
  20156.     …
  20157.     move.w    (a1)+,d4             ; return first, primary character
  20158.     move.w    (a1)+,CurChar(A2)    ; original => first, modified char.
  20159.     addq.b    #1,JustAfter(A2)     ; set to one (otherwise zero)
  20160.     move.b    (a1),BufChar(A2)     ; store second character (BYTE!)
  20161.     …
  20162.  
  20163. CurChar is where the original character returned by FetchChar is stored. If characters
  20164. are different even after being projected onto their respective primary characters,
  20165. then the CurChar values for each string will be compared. JustAfter indicates that
  20166. the expanded character should sort after the corresponding unexpanded form. This
  20167. field must be set whenever CurChar is modified in order for the comparison to be
  20168. fully ordered. BufChar stores the next byte to be retrieved from the string by FetchChar.
  20169.  
  20170. To handle the case where characters are ignored unless the two compared strings are
  20171. otherwise equal, the IgnChar flag can be set. This can be used to handle characters
  20172. such as the hyphen in English, or vowels in Arabic.
  20173.     …
  20174.     cmp.w    #hyphen,d0     ; is it a ignorable?
  20175.     seq      IgnChar(a2)    ; set whether or not
  20176.     …
  20177.  
  20178.  
  20179. The Vernier Procedure
  20180.  
  20181. The Vernier Procedure is used to make a final comparison among characters that have
  20182. the same primary ordering. It is only needed if the CurChar values are not ordered
  20183. properly. For example, according to the binary encoding, å < Ã. To change this ordering
  20184. so that uppercase letters are before lowercase letters, Ã is mapped to $7F in normal
  20185. comparison. Notice that only the characters in the secondary ordering are affected: Ã
  20186. can be mapped onto Z, but not onto Ä, since that would cause a collision.
  20187.  
  20188. ;------------------------------------------------------------------------
  20189. ; Routine    VernierProc
  20190. ; Input       D4.B        High byte of character
  20191. ;             D5.B        Low byte of character
  20192. ; Output      D4.B        High byte of character
  20193. ;             D5.B        Low byte of character
  20194. ;             CCR         NE if to skip standard Vernier
  20195. ; Trashes     Standard regs:    A0/A1/D0-D2
  20196. ; Function    The Vernier routine compares characters within the secondary
  20197. ;             ordering if two strings are otherwise equal.
  20198. ;             Example: (a,A,Ä,ä)
  20199. ;------------------------------------------------------------------------
  20200.  
  20201. VernierProc    
  20202.     not.b    d4             ; invert secondary ordering
  20203.     not.b    d5             ; ditto for lower byte
  20204.     bra.s    ReturnEq       ; normal processing afterwards
  20205.  
  20206.  
  20207. Installing an itl2 resource 
  20208.  
  20209. To write an itl2 resource, follow the guidelines in Technical Note #110 for writing
  20210. standalone code in MPW. The code should be written in assembly language, and it must
  20211. follow the specifications given in this technical note or serious system errors could
  20212. occur whenever string comparisons are made.
  20213.  
  20214. The default comparison routine is in the itl2 resource of the System file. In order
  20215. to use a comparison routine other than the standard one, you should include an itl2
  20216. resource in your application with the same name and resource ID as the one in the
  20217. System file that you wish to change. The Resource Manager will look for the resource
  20218. in the application resource file before it looks in the System resource file, so your
  20219. string comparison routine will be used instead of the default one.
  20220.  
  20221. It is generally a dangerous practice to change a system resource since other applications
  20222. may depend on it, but if you have good reasons to permanently change the system itl2
  20223. resource so that all applications use a different comparison routine, then you should
  20224. write an installer script to change the itl2 resource in the System resource file.
  20225. Writing an installer script is documented in Technical Note #75. You are required to
  20226. write an installer script if you are planning to ship your application on a licensed
  20227. system software disk and your application makes a permanent change to any resources
  20228. in the System file. We strongly discourage changing the System itl2 as that would
  20229. change the behavior of string comparison and sorting for all applications. If that is
  20230. your intent, then you should write an installer script. However, if you are changing
  20231. the itl2 resource in the System file for academic or internal use, then you can use a
  20232. resource editor such as ResEdit to copy your itl2 resource into the System file.
  20233.  
  20234.  
  20235.  
  20236. æKY 179
  20237. æC #179: Setting ioNamePtr in File Manager Calls
  20238.  
  20239. See also:    The File Manager
  20240.  
  20241. Written by:    Jim Friedlander    November 2, 1987
  20242. Updated:                          March 1, 1988
  20243. _______________________________________________________________________________
  20244.  
  20245. It is very important to set ioNamePtr when making PB calls, even if you don’t want 
  20246. those calls to return a name.  Whenever Inside Macintosh indicates that ioNamePtr is
  20247. either required for input or returns something, you must set ioNamePtr to either nil
  20248. (if you aren’t using a name) or to point to storage for a Str255.  
  20249.  
  20250. If you don’t explicitly set ioNamePtr, strange and unusual crashes may occur, depending
  20251. on the machine/configuration your code is run on.  
  20252.  
  20253.  
  20254. æKY 180
  20255. æC #180:    MultiFinder Miscellanea
  20256.  
  20257. Revised by:    Dave Radcliffe & Keith Rollin                        August 1989
  20258. Written by:    Jim Friedlander                                    November 1987
  20259.  
  20260. This Technical Note discusses MultiFinder issues of which programmers should be aware.
  20261. Changes since June 1988:  Updated and generalized sample code to reflect new MPW 3.0
  20262. calls in both C and Pascal for saving and restoring A5 for interrupt code that accesses
  20263. application globals.  Removed text that can be found in Programmer’s Guide to MultiFinder,
  20264. and added a note about _PostEvent.
  20265. _______________________________________________________________________________
  20266.  
  20267.  
  20268. Switching
  20269.  
  20270. For conceptual clarity, it is best to think of MultiFinder 6.0 and earlier as using
  20271. three types of switching:  major, minor, and update.  All switching occurs at a well
  20272. defined times, namely, when a call is made to either
  20273. _WaitNextEvent, _GetNextEvent, or _EventAvail.
  20274.  
  20275. Major switching is a complete context switch, that is, an application’s windows are
  20276. moved from the background to the foreground or vice versa.  A5 worlds are switched,
  20277. and the application’s low-memory world is switched.  If the application accepts Suspend
  20278. and Resume events, it is so notified at major switch time.
  20279.  
  20280. Major switching will not occur when a modal dialog is the frontmost window of the
  20281. front layer, though minor and update switching will occur.  To determine whether
  20282. major switching will occur, MultiFinder checks (among other things) if the window
  20283. definition procedure of that window is dBoxProc.  If it is, then MultiFinder won’t
  20284. allow a switch via the user clicking on another application.  A window definition
  20285. procedure of dBoxProc is specifically reserved for modal dialogs—when most users see
  20286. a dBoxProc, they are expecting a modal situation.  If you are using a dBoxProc for a
  20287. non-modal window, we strongly recommend that you change it to some other window type,
  20288. or risk the wrath of the
  20289. User–Interface Thought Police (UITP).
  20290.  
  20291. Minor switching occurs when an application needs to be switched out to give time to
  20292. background processes.  In a minor switch, A5 worlds are switched, as are low-memory
  20293. worlds, but the application’s layer of windows is not switched, and the application
  20294. won’t be notified of the switch via Suspend and Resume events.
  20295.  
  20296. Update switching occurs when MultiFinder detects that one or more of the windows of
  20297. an application that is not frontmost needs updating.  This happens whether or not the
  20298. application has the canBackground bit in the 'SIZE' –1 resource set.  This switch is
  20299. very similar to minor switching, except that update events are sent to the application
  20300. whose window need updating.
  20301.  
  20302. Both minor and update switches should be transparent to the frontmost application.
  20303.  
  20304.  
  20305. Suspend and Resume Events
  20306.  
  20307. If your application does not accept Suspend and Resume events (as set in the
  20308. 'SIZE' –1 resource), then if a mouse-click event occurs in a window that isn’t yours,
  20309. MultiFinder will send your application a mouse-down event with code inMenuBar (with
  20310. menuID equal to the ID of the Apple menu and menuItem set to
  20311. “About MultiFinder…”).  The reason that MultiFinder does this is to force your application
  20312. to think that a desk accessory is opening, so that it will convert any private scrap
  20313. that it might be keeping.  MultiFinder is expecting your application to call _MenuSelect--if
  20314. you don’t, it will currently issue a few more mouse-down events in the menu bar before
  20315. finally giving up.  This isn’t really a problem, but a lot of developers have run
  20316. into it, especially in
  20317. “quick and dirty” applications.
  20318.  
  20319. If you are switching menu bars with _SetMenuBar (and switching the Apple menu) during
  20320. the execution of your application, then you should definitely make sure that your
  20321. application accepts Suspend and Resume events.  MultiFinder records the ID of the
  20322. original Apple menu that you use and won’t keep track of any changes that you make to
  20323. the Apple menu.  So, in the above situation, MultiFinder will give you a mouse-down
  20324. event in the menu bar with the menuItem set to the item number of “About MultiFinder…”
  20325. that was in the original Apple menu, which could be quite a confusing situation.  If
  20326. you set the MultiFinder friendly bit in the 'SIZE' resource, MultiFinder will never
  20327. give you these mouse-down events.
  20328.  
  20329.  
  20330. Referencing Global Data (A5 and MultiFinder)
  20331.  
  20332. MultiFinder maintains a separate A5 world for each application.  MultiFinder switches
  20333. A5 worlds as appropriate, so most applications don’t have to worry about A5 at all
  20334. (except to make sure that it points to a valid QuickDraw global record at _GetNextEvent
  20335. or _WaitNextEvent time).  MultiFinder also switches
  20336. low-memory globals for you.  To get the value of the application’s A5, use the routines
  20337. from Technical Note #208, Setting and Restoring A5.
  20338.  
  20339. If an application uses routines that execute at interrupt time and accesses globals,
  20340. then it needs to be concerned about A5.  MultiFinder affects four basic types of
  20341. interrupt routines:
  20342.  
  20343.   •  VBL tasks 
  20344.   •  Completion routines 
  20345.   •  Time Manager tasks 
  20346.   •  Interrupt service routines
  20347.  
  20348. VBL Tasks
  20349.  
  20350. If an application installs a VBL task into its application heap, MultiFinder will
  20351. currently “unhook” that VBL routine when it switches that application out
  20352. (using either a major or a minor switch).  It will “rehook” it when the application
  20353. is switched back in.  A VBL task that is installed in the system heap will always
  20354. receive time, that is, it will never be “unhooked.”  Given this condition, it is
  20355. technically not necessary for a VBL task that is in the application’s heap to worry
  20356. about its A5 context, since it will only be running when that application’s partition
  20357. is switched in.  However, we would still like to encourage you to set up A5 by carrying
  20358. its value around with the VBL, since we may change the way this works in future versions
  20359. of MultiFinder (and even without MultiFinder, the VBL could trigger at a time when A5
  20360. is not correct).
  20361.  
  20362. The following short MPW examples show how to do this using the new MPW 3.0 calls
  20363. mentioned in Technical Note #208.  Please note that this technique does not involve
  20364. writing into your code segment (we’ll get to that later), we just put our value of
  20365. the application’s A5 in a location where we can find it from our VBL task.  Nor does
  20366. it depend on the VBL task information being allocated globally.  This gives you more
  20367. flexibility setting up your VBL.
  20368.  
  20369. This example also serves to demonstrate how one might write a completion routine for
  20370. an asynchronous Device Manager call.  It is not intended to be a complete program,
  20371. nor to demonstrate optimal techniques for displaying information.
  20372.  
  20373. MPW Pascal 3.0
  20374.  
  20375. UNIT    VBLS;
  20376.  
  20377. {$R-}
  20378.  
  20379. INTERFACE
  20380.  
  20381. USES
  20382.     Dialogs, Events, OSEvents, Retrace, Packages, Types, Traps;
  20383.  
  20384. CONST
  20385.     Interval = 6;
  20386.     rInfoDialog = 140;
  20387.     rStatTextItem = 1;
  20388.     
  20389. TYPE
  20390.     { Define a record to keep track of what we need.  Put theVBLTask into the 
  20391.     record first because its address will be passed to our VBL task in A0. }
  20392.     VBLRec = RECORD
  20393.         theVBLTask:    VBLTask;      { the actual VBLTask }
  20394.         VBLA5:         LongInt;      { saved CurrentA5 where we can find it }
  20395.     END;
  20396.     VBLRecPtr = ^VBLRec;
  20397.  
  20398. VAR
  20399.     gCounter:    LongInt;            { Global counter incremented by VBL }
  20400.     
  20401. PROCEDURE InstallVBL;
  20402.  
  20403. IMPLEMENTATION
  20404.  
  20405. { GetVBLRec returns the address of the VBLRec associated with our VBL task.
  20406.   This works because on entry into the VBL task, A0 points to the theVBLTask
  20407.   field in the VBLRec record, which is the first field in the record and that
  20408.   is the address we return.  Note that this method works whether the VBLRec
  20409.   is allocated globally, in the heap (as long as the record is locked in 
  20410.   memory) or if it is allocated on the stack as is the case in this example.
  20411.   In the latter case this is OK as long as the procedure which installed the
  20412.   task does not exit while the task is running.  This trick allows us to get
  20413.   to the saved A5, but it could also be used to get to anything we wanted to
  20414.   store in the record. }
  20415. FUNCTION GetVBLRec: VBLRecPtr;
  20416.     INLINE $2E88;                    { MOVE.L A0,(A7) }
  20417.  
  20418. PROCEDURE DoVBL (VRP: VBLRecPtr);
  20419. { DoVBL is called only by StartVBL }
  20420. BEGIN 
  20421.     gCounter := gCounter + 1;                  { Show we can set a global }
  20422.     VRP^.theVBLTask.vblCount := Interval;      { Set ourselves to run again }
  20423. END;
  20424.  
  20425. PROCEDURE StartVBL;
  20426. { This is the actual VBL task code.  It uses GetVBLRec to get our VBL record
  20427.   and properly set up A5.  Having done that, it calls DoVBL to increment a
  20428.   global counter and sets itself to run again.  Because of the vagaries of
  20429.   MPW C 3.0 optimization, it calls a separate routine to actually access
  20430.   global variables.  See Tech Note #208 for the reasons for this, as well
  20431.   as for a description of SetA5. }
  20432. VAR
  20433.     curA5:         LongInt;
  20434.     recPtr:        VBLRecPtr;
  20435.  
  20436. BEGIN 
  20437.     recPtr := GetVBLRec;             { First get our record }
  20438.     curA5:= SetA5(recPtr^.VBLA5);    { Get our application's A5 }
  20439.  
  20440.                                      { Now we can access globals }
  20441.     DoVBL (recPtr);                  { Call another routine to do actual work }
  20442.     
  20443.     curA5:= SetA5(curA5);            { restore original A5, ignoring result }
  20444. END;
  20445.  
  20446.  
  20447. PROCEDURE InstallVBL;
  20448. { InstallVBL creates a dialog just to demonstrate that the global variable
  20449.   is being updated by the VBL Task.  Before installing the VBL, we store
  20450.   our A5 in the actual VBL Task record, using SetCurrentA5 described in
  20451.   Tech Note #208.  We'll run the VBL, showing the counter being incremented,
  20452.   until the mouse button is clicked.  Then we remove the VBL Task, close the
  20453.   dialog, and remove the mouse down events to prevent the application from
  20454.   being inadvertently switched by MultiFinder. }
  20455.  
  20456. VAR
  20457.     theVBLRec:        VBLRec;
  20458.     infoDPtr:         DialogPtr;
  20459.     infoDStorage:     DialogRecord;
  20460.     numStr:           Str255;
  20461.     theErr:           OSErr;
  20462.     theItemHandle:    Handle;
  20463.     theItemType:      INTEGER;
  20464.     theRect:          Rect;
  20465.  
  20466. BEGIN
  20467.     gCounter:= 0;                    { initialize the global variable }
  20468.     infoDPtr:= GetNewDialog(rInfoDialog, @infoDStorage, Pointer(-1));
  20469.     DrawDialog(infoDPtr); 
  20470.     GetDItem(infoDPtr, rStatTextItem, theItemType, theItemHandle, theRect);
  20471.  
  20472.     theVBLRec.VBLA5:= SetCurrentA5;  { get our A5 }    
  20473.     WITH theVBLRec.theVBLTask DO
  20474.         BEGIN
  20475.             vblAddr:= @StartVBL;     { pointer to VBL code }
  20476.             vblCount:= Interval;     { frequency of VBL in System ticks }
  20477.             qType:= ORD(vType);      { qElement is a VBL type }
  20478.             vblPhase:= 0;            { no phases }
  20479.         END;
  20480.         
  20481.     theErr:= VInstall(@theVBLRec.theVBLTask);    { install this VBL task }
  20482.     IF theErr = noErr THEN           { we'll show the global value in }
  20483.         BEGIN                        { the dialog until a mouse click }
  20484.             REPEAT
  20485.                 NumToString(gCounter, numStr);
  20486.                 SetIText(theItemHandle, numStr);
  20487.             UNTIL Button;
  20488.         theErr:= VRemove(@theVBLRec.theVBLTask);    { remove the VBL task }
  20489.         END;
  20490.     
  20491.     CloseDialog(infoDPtr);            { get rid of the info dialog }
  20492.     FlushEvents(mDownMask, 0);        { remove all mouse down events }
  20493. END;
  20494.  
  20495. END.
  20496.  
  20497. MPW C 3.0
  20498.  
  20499. #include <Events.h>
  20500. #include <OSEvents.h>
  20501. #include <OSUtils.h>
  20502. #include <Dialogs.h>
  20503. #include <Packages.h>
  20504. #include <Retrace.h>
  20505. #include <Traps.h>
  20506.  
  20507. #define INTERVAL         6
  20508. #define rInfoDialog      140
  20509. #define rStatTextItem    1
  20510.  
  20511. /*
  20512.  *  These are globals which will be referenced from our VBL Task
  20513.  */
  20514. long    gCounter;      /* Counter incremented each time our VBL gets called */
  20515.  
  20516. /*
  20517.  *  Define a struct to keep track of what we need.  Put theVBLTask into the 
  20518.  *  struct first because its address will be passed to our VBL task in A0
  20519.  */
  20520. struct VBLRec {
  20521.     VBLTask     theVBLTask;   /* the VBL task itself */
  20522.     long        VBLA5;        /* saved CurrentA5 where we can find it */
  20523. };
  20524. typedef struct VBLRec VBLRec, *VBLRecPtr;
  20525.  
  20526. /*
  20527.  *  GetVBLRec returns the address of the VBLRec associated with our VBL task.
  20528.  *  This works because on entry into the VBL task, A0 points to the theVBLTask
  20529.  *  field in the VBLRec record, which is the first field in the record and that
  20530.  *  is the address we return.  Note that this method works whether the VBLRec
  20531.  *  is allocated globally, in the heap (as long as the record is locked in 
  20532.  *  memory) or if it is allocated on the stack as is the case in this example.
  20533.  *  In the latter case this is OK as long as the procedure which installed the
  20534.  *  task does not exit while the task is running.  This trick allows us to get
  20535.  *  to the saved A5, but it could also be used to get to anything we wanted to
  20536.  *  store in the record.
  20537.   */
  20538. VBLRecPtr GetVBLRec ()
  20539.     = 0x2008;                  /* MOVE.L    A0,D0 */
  20540.  
  20541. /*
  20542.  *  DoVBL is called only by StartVBL ()
  20543.  */
  20544. void DoVBL (VRP)
  20545. VBLRecPtr    VRP;
  20546. {
  20547.     gCounter++;                /* Show we can set a global */
  20548.     VRP->theVBLTask.vblCount = INTERVAL;    /* Set ourselves to run again */
  20549. }
  20550.  
  20551. /*
  20552.  *  This is the actual VBL task code.  It uses GetVBLRec to get our VBL record
  20553.  *  and properly set up A5.  Having done that, it calls DoVBL to increment a
  20554.  *  global counter and sets itself to run again.  Because of the vagaries of 
  20555.  *  MPW C 3.0 optimization, it calls a separate routine to actually access 
  20556.  *  global variables.  See Tech Note #208 - "Setting and Restoring A5" for 
  20557.  *  the reasons for this, as well as for a description of SetA5.
  20558.  */
  20559. void StartVBL ()
  20560. {
  20561.     long         curA5;
  20562.     VBLRecPtr    recPtr;
  20563.     
  20564.     recPtr = GetVBLRec ();            /* First get our record */
  20565.     curA5 = SetA5 (recPtr->VBLA5);    /* Get the saved A5 */
  20566.  
  20567.                                /* Now we can access globals */
  20568.     DoVBL (recPtr);            /* Call another routine to do actual work */
  20569.     
  20570.     (void) SetA5 (curA5);             /* Restore old A5 */
  20571. }
  20572.  
  20573. /*
  20574.  *  InstallVBL creates a dialog just to demonstrate that the global variable
  20575.  *  is being updated by the VBL Task.  Before installing the VBL, we store
  20576.  *  our A5 in the actual VBL Task record, using SetCurrentA5 described in
  20577.  *  Tech Note #208.  We'll run the VBL, showing the counter being incremented,
  20578.  *  until the mouse button is clicked.  Then we remove the VBL Task, close the
  20579.  *  dialog, and remove the mouse down events to prevent the application from
  20580.  *  being inadvertently switched by MultiFinder.
  20581.  */
  20582. void InstallCVBL ()
  20583. {
  20584.     VBLRec          theVBLRec;
  20585.     DialogPtr       infoDPtr;
  20586.     DialogRecord    infoDStorage;
  20587.     Str255          numStr;
  20588.     OSErr           theErr;
  20589.     Handle          theItemHandle;
  20590.     short           theItemType;
  20591.     Rect            theRect;
  20592.  
  20593.     gCounter = 0;              /* Initialize our global counter */
  20594.     infoDPtr = GetNewDialog (rInfoDialog, (Ptr) &infoDStorage, (WindowPtr) -1);
  20595.     DrawDialog (infoDPtr);
  20596.     GetDItem (infoDPtr, rStatTextItem, &theItemType, &theItemHandle, 
  20597.         &theRect);
  20598.     
  20599.     /* 
  20600.      *  Store the current value of A5 in the MyA5 field.  For more
  20601.      *  information on SetCurrentA5, see Tech Note #208- "Setting and
  20602.      *  Restoring A5".
  20603.      */
  20604.     theVBLRec.VBLA5 = SetCurrentA5 ();
  20605.     /* Set the address of our routine */
  20606.     theVBLRec.theVBLTask.vblAddr = (VBLProcPtr) StartVBL;
  20607.     theVBLRec.theVBLTask.vblCount = INTERVAL; /* Frequency of task, in ticks */
  20608.     theVBLRec.theVBLTask.qType = vType;       /* qElement is a VBL task */
  20609.     theVBLRec.theVBLTask.vblPhase = 0;
  20610.     
  20611.     /* Now install the VBL task */
  20612.     theErr = VInstall ((QElemPtr)&theVBLRec.theVBLTask);
  20613.     
  20614.     if (!theErr) {
  20615.         do {
  20616.             NumToString (gCounter, numStr);
  20617.             SetIText (theItemHandle, numStr);
  20618.         } while (!Button ());
  20619.         theErr = VRemove ((QElemPtr)&theVBLRec.theVBLTask); /* Remove it
  20620.                                                                when done */
  20621.     }
  20622.     
  20623.     /* Finish up */
  20624.     CloseDialog (infoDPtr);                /* Get rid of our dialog */
  20625.     FlushEvents (mDownMask, 0);            /* Flush all mouse down events */
  20626. }
  20627.  
  20628. Completion Routines
  20629.  
  20630. Currently, MultiFinder will not do a major, minor, or update switch if an asynchronous
  20631. File Manager call is pending.  This may not be true in the future.  We recommend that
  20632. you use the above technique to save A5 for asynchronous File Manager calls.  MultiFinder
  20633. does allow a switch if an asynchronous Device Manager or Sound Manager call is pending.
  20634.  When the call completes, the completion routine has no way of knowing whose partition
  20635. is active, that is, it doesn’t know if A5 is valid (it needs A5 if it wants to access
  20636. a global).  Sounds pretty hopeless, huh?
  20637.  
  20638. Well, actually this one is quite easy, you just need to put the value of A5 that
  20639. “belongs” to your partition in a place where you can find it from your completion
  20640. routine.  It is guaranteed that A0 will point to your parameter block when your completion
  20641. routine is called, so you can use the same technique shown with VBL tasks to put the
  20642. value of A5 at a known offset from the beginning of the parameter block, and then
  20643. reference it from A0.  Completion routines are normally written in assembly language,
  20644. though you can also write them in a high-level language.  A simple example of how to
  20645. do this in MPW Pascal and C can be found in the previous section about VBL tasks (it
  20646. was easier to provide a clear, concise example for VBL tasks than for asynchronous
  20647. Device Manager completion routines).
  20648.  
  20649. Time Manager Tasks
  20650.  
  20651. The Time Manager was rewritten for System 6.0.3.  The new version will put a pointer
  20652. to the TMTask record in A1.  This is not true in System 6.0.2 or earlier.  The technique
  20653. shown in the example VBL for accessing an application’s globals is possible using
  20654. System 6.0.3 and the Time Manager.  Prior to System 6.0.3, the task must also store
  20655. the application’s A5 into its code.  This method is not a very good idea and runs the
  20656. risk of incompatibility (self–modifying code).
  20657.  
  20658. Interrupt Service Routines
  20659.  
  20660. If your application needs to get to its application globals, and it replaces the
  20661. standard 68xxx interrupt vectors (levels 1-7) with pointers to its own routines, it
  20662. must also store the application’s A5 into its code (since there is no parameter block
  20663. for interrupt service routines).  This method is not a very good idea and runs the
  20664. risk of compatibility (self–modifying code).
  20665.  
  20666. Note:  WDEFs should also maintain a copy of A5 in the same fashion as Time
  20667.        Manager tasks (prior to System Software 6.0.3) and set up A5 when
  20668.        called; WDEFs should also be non-purgeable.
  20669.  
  20670.  
  20671. Launching and MultiFinder
  20672.  
  20673. Technical Note #126 discusses the sublaunching feature of Systems 4.1 and newer.  If
  20674. you are running MultiFinder, and you use the technique demonstrated in that Technical
  20675. Note, your application will be able to launch the desired application and remain
  20676. open.  Note: MultiFinder does not support _Chain; your application should never call
  20677. this trap.
  20678.  
  20679. The application that you launch will become the foreground application.  Unlike non-MultiFinder
  20680. systems, when the user quits the application that you have sublaunched, control will
  20681. not necessarily return to your application, but rather to the next frontmost layer.
  20682.  
  20683. Note:  The warnings in Technical Note #126 about sublaunching still apply,
  20684.        but, if you still wish to sublaunch, we strongly recommend that you
  20685.        set both high bits of LaunchFlags.
  20686.  
  20687.  
  20688. The Scrap and MultiFinder
  20689.  
  20690. MultiFinder 6.0 and earlier keeps separate scrap variables for each partition.  MultiFinder
  20691. only checks to see whether or not to increment the other partitions’ scrapCount variables
  20692. in response to a user-initiated Cut or Copy.  To do this, it watches for a call to
  20693. _SysEdit (SystemEdit) or a menu event to determine if an official Cut or Copy command
  20694. has been issued.
  20695.  
  20696. When an application calls _PutScrap or _ZeroScrap in response to a Cut or Copy menu
  20697. selection, the other partitions’ scrapCount variables will be incremented
  20698. (the other partitions will know that something new has been put in the scrap).
  20699.  
  20700.  
  20701. _UnmountVol and MultiFinder
  20702.  
  20703. _UnmountVol was changed in System 4.2 so that it would work better in a shared environment.
  20704.  In systems 4.1 and prior, _UnmountVol would successfully unmount a volume even if
  20705. files were open on that volume.  Under MultiFinder, that would be disastrous, since
  20706. one application could unmount a volume that another application was using (this exact
  20707. problem could occur when MultiFinder is not active, if a DA unmounted a volume “out
  20708. from under” an application).
  20709.  
  20710. System 4.2 changes the behavior of _UnmountVol (whether or not MultiFinder is active)
  20711. so that it returns a -47 (FBsyErr) error if any files are open on the volume you wish
  20712. to unmount.  Since the Finder always has a Desktop file open for each volume, a call
  20713. to _UnmountVol asks it to close the Desktop file so you won’t get an error if the
  20714. only file open is the Desktop file.  However, there is a bug with this new behavior. 
  20715. In System 6.0.3, and earlier, _UnmountVol does not close the Desktop file for MFS–formatted
  20716. volumes.  Only the Finder can unmount a MFS volume (when the user drags the disk icon
  20717. to the trash).
  20718.  
  20719.  
  20720. Displaying a Splash Screen
  20721.  
  20722. Some applications like  to put up a “splash screen” to give the user something to
  20723. look at while the application is loading. If your application does this and has the
  20724. canBackground bit set in the size resource, then it should call
  20725. _EventAvail several times (or _WaitNextEvent or _GetNextEvent) before putting up the
  20726. splash screen, or the splash screen will come up behind the frontmost layer.  If the
  20727. canBackground bit is set, MultiFinder will not move your layer to the front until you
  20728. call _GetNextEvent, _WaitNextEvent, or _EventAvail.
  20729.  
  20730.  
  20731. The Apple Menu and MultiFinder
  20732.  
  20733. Applications should avoid doing anything untoward with the Apple menu.  For example,
  20734. if your application puts an icon next to the “About MyApplication…” item, MultiFinder
  20735. may unceremoniously write over it.  It is important to consider the Apple Menu owned
  20736. by the system.  You can have the standard about item, but other than this, you should
  20737. avoid using the Apple menu.  Don’t make any assumptions about the contents of this
  20738. menu.  Even reading from its data may be a compatibility risk since its structure may
  20739. change.
  20740.  
  20741.  
  20742. Interprocess Communication
  20743.  
  20744. MultiFinder 6.0, and earlier, does not have full-fledged interprocess communication
  20745. facilities.  There is no standard way to communicate between applications in MultiFinder
  20746. 6.0.  There are, however, a couple of ways to communicate between applications.  For
  20747. recommendations on attempting interprocess communication with MultiFinder 6.0, contact
  20748. MacDTS at the address listed in Technical Note #0.
  20749.  
  20750. Note:  It is in your best interest to wait until Apple implements
  20751.        Interapplication Communication (IAC) in System 7.0.
  20752.  
  20753.  
  20754. _PostEvent
  20755.  
  20756. Even though you can have many applications running at once, each with a fairly independent
  20757. world, the Event Manager maintains only one event queue.  Because of this single
  20758. queue, and because there is no facility implemented to keep track of which events
  20759. belong to which layer, all events in the queue are passed to the frontmost application.
  20760.  This situation can cause problems for applications that take advantage of application-defined
  20761. events.  If the application is in the background and posts one of these events, then
  20762. it is the foreground application that receives it.
  20763.  
  20764. This does not apply to events which are not really stored in the event queue.  The
  20765. list of these events include, but is not limited to, activate and update events,
  20766. which are generated by the Window Manager as needed, and are correctly routed to the
  20767. right application.
  20768.  
  20769.  
  20770. Miscellaneous Miscellanea
  20771.  
  20772. The sound driver glue that shipped with MPW 1.0 and 2.0 is not MultiFinder compatible
  20773. and should not be used.  This also includes much of the glue supplied with older
  20774. development systems.  Instead, applications should be using the Sound Manager.
  20775.  
  20776. All code needs to be aware of the shared environment; this includes screen savers. 
  20777. Screen savers should make sure that background processing continues.  A simple scenario
  20778. for a screen saver that’s an INIT might be:  patch _PostEvent at INIT time, put up a
  20779. full-screen black window spider, call _WaitNextEvent, and watch _PostEvent to see if
  20780. an event that should cause the screen saver to go away has occurred.
  20781.  
  20782.  
  20783. Further Reference:
  20784. _______________________________________________________________________________
  20785.   •  Inside Macintosh, Volume V, Compatibility Guidelines
  20786.   •  Programmer’s Guide to MultiFinder (APDA)
  20787.   •  Technical Note #126, Sub(Launching) from a High-Level Language
  20788.   •  Technical Note #129, _SysEnvirons:  System 6.0 and Beyond
  20789.   •  Technical Note #158, Frequently Asked MultiFinder Questions
  20790.   •  Technical Note #205, MultiFinder Revisited:  The 6.0 System Release
  20791.   •  Technical Note #208, Setting and Restoring A5
  20792.  
  20793.  
  20794. æKY 181
  20795. æC #181: Every Picture [Comment] Tells Its Story, Don’t It?
  20796.  
  20797. See also:     QuickDraw
  20798.               Technical Note #91
  20799.                   — Optimizing for the LaserWriter—Picture Comments
  20800.  
  20801. Written by:    Rick Blair      November 2, 1987
  20802. Updated:                       March 1, 1988
  20803. _______________________________________________________________________________
  20804.  
  20805. Application-specific picture comment conflict and registration is addressed, along
  20806. with Developer Technical Support’s method for solving it.
  20807. _______________________________________________________________________________
  20808.  
  20809. I will assume that the nature and usefulness of picture comments are already well
  20810. known. The problem I am addressing is that, as it stands, developers must register
  20811. their comments with us (Developer Technical Support) or run the risk of using the
  20812. same comments as those used by Apple or within another third party product.
  20813.  
  20814. The idea here is to provide a “metacomment” which will contain information about
  20815. which application owns the comment as well as the comment itself:
  20816.  
  20817. ApplicationComment (long comment)    kind = 100  size = n + 6
  20818.     data = application signature (i.e. 'MPNT' for MacPaint) = 4 bytes
  20819.            application “local” kind = 2 bytes
  20820.            comment data = n bytes
  20821.  
  20822. In this way each comment may be specific to an application. It is still up to a developer
  20823. to publish information about the comments they have defined if they wish them to be
  20824. understood and used by other programs.
  20825.  
  20826. Previously assigned and registered comments will still be valid. This means that
  20827. those  defined for the LaserWriter or MacDraw, for instance, will retain their normal
  20828. meaning.
  20829.  
  20830. Suppose your application (creator = 'MYAP') wanted to define a comment. The appearance
  20831. of the comment would be as follows (assuming you chose 128 to be the “local” kind for
  20832. the comment):
  20833.  
  20834.     kind = 100; data = 'MYAP' [4 bytes] + 128 [2 bytes] + additional data
  20835.  
  20836.  
  20837.  
  20838. æKY 182
  20839. æC #182: How to Construct Word-Break Tables
  20840.  
  20841. See also:    The Script Manager 
  20842.     
  20843. Written by:    Mark Davis      November 2, 1987
  20844. Updated:                       March 1, 1988
  20845. _______________________________________________________________________________
  20846.  
  20847. This technical note describes how to construct auxiliary break tables for use with
  20848. the FindWord routine in the Script Manager.
  20849. _______________________________________________________________________________
  20850.  
  20851.  
  20852. Constructing break tables
  20853.  
  20854. The FindWord algorithm finds word boundaries by determining where words should not be
  20855. broken. For example, “re-do” is one word: it should not be broken at the hyphen. In
  20856. other words, a sequence of the form: (letter, hyphen, letter) should not be broken
  20857. between the first and second or second and third character. This is called a continuation
  20858. sequence. The algorithm used by the FindWord routine allows for continuation sequences
  20859. of lengths one, two and three. Examples of a sequence of length two include (letter,
  20860. letter), or (number, number). For a length of one, there is only one sequence, consisting
  20861. of the characters of type nonBreaking: these characters are never separated from
  20862. preceding or following characters.
  20863.  
  20864. For most scripts, this information about continuation sequences is packed into a
  20865. table for use by the FindWord algorithm. (For complex scripts like Japanese, a different
  20866. algorithm is used for portions of the script.) The default break tables for a given
  20867. script can be overridden by a user-specified breakTable parameter, but should only be
  20868. used for known scripts. That is, before overriding the breakTable parameter, the
  20869. programmer should first check the script of the current font.
  20870.  
  20871. A break table consists of two sections, a 256 byte character type table followed by a
  20872. character triple table.
  20873.  
  20874. •••Click on the Illustration button below, and refer to Figure 1.••• 
  20875.  
  20876. The character type table is indexed by the character’s ASCII code and contains one
  20877. type value for each character. The character types in the table are limited to values
  20878. between 1 and 31. There are two distinguishing values: the type nonBreaking (= 1)
  20879. indicates that the character is non-breaking; it always continues a word. The type
  20880. wild (=0) indicates that the character may or may not break, depending on information
  20881. in the character triple table, as described below.  Otherwise, the choice of numbers
  20882. to represent character types is completely arbitrary.
  20883.  
  20884. For example, the following in MPW Assembler defines character types for use in a
  20885. word-selection break table, then sets up a character type table using an assembly
  20886. macro (setByte) to store character type values in an array.  (Note that the character
  20887. types could  have been defined with equate definitions (EQU), rather than using the
  20888. record structure.)   Writing the setByte macro is left as an exercise to the reader.
  20889. Note that the break value is the default. This value is not distinguished, but should
  20890. have no continuation sequences.
  20891.  
  20892. ;============================================================
  20893. charWordRec  record   0
  20894. wild          ds.b    1    ; constant! not in char table.
  20895. nonbreak      ds.b    1    ; constant! non-breaking space.
  20896. letter        ds.b    1    ; letters.
  20897. number        ds.b    1    ; digits.
  20898. break         ds.b    1    ; always breaks.
  20899. midLetter     ds.b    1    ; a'a.
  20900. midLetNum     ds.b    1    ; a'a 1'1.
  20901. preNum        ds.b    1    ; $, etc.
  20902. postNum       ds.b    1    ; %, etc.
  20903. midNum        ds.b    1    ; 1,1.
  20904. preMidNum     ds.b    1    ; .1234.
  20905. blank         ds.b    1    ; spaces and tabs.
  20906. cr            ds.b    1    ; add carriage return
  20907.               endr
  20908. ;============================================================
  20909.     with        charWordRec
  20910. wordTable
  20911.     dcb.b      256,break
  20912.     setByte    wordTable,nonBreak,$ca
  20913.     setByte    wordTable,letter,('A','Z'),('a','z')('Ä','ü')
  20914.     setByte    wordTable,letter,'Æ','Ø','æ','ø',('À','œ'),'ÿ'
  20915.     setByte    wordTable,midLetter,'-'
  20916.     setByte    wordTable,midLetNum,$27,'’'
  20917.     setByte    wordTable,number,('0','9')
  20918.     setByte    wordTable,preNum,'$','¢','£','¥'
  20919.     setByte    wordTable,postNum,'%'
  20920.     setByte    wordTable,midNum,','
  20921.     setByte    wordTable,preMidNum,'.'
  20922.     setByte    wordTable,blank,$00,' ',$09
  20923.     setByte    wordTable,cr,$0d
  20924.     endWith
  20925. ;============================================================
  20926.  
  20927. The character triple table is a coded representation of a list of continuation sequences.
  20928. It consists of a list of packed one word triples, preceded by a length word. This
  20929. length word contains the number of triples minus one. Each triple contains three
  20930. character types, either as derived from the charType table or the special type wild
  20931. (= zero). The three types in a triple are packed into fields five bits apiece, with
  20932. the most significant bit in the word cleared.  The first type in the triple is the
  20933. leftmost. 
  20934.  
  20935. A continuation sequence of length three (xyz) is represented by entering three triples
  20936. into the triple list: xyz, *xy, and yz* (where ‘*’ stands for the type wild, which is
  20937. always zero). 
  20938.  
  20939. •••Click on the Illustration button below, and refer to Figure 2.••• 
  20940.  
  20941. A continuation sequence of length two (xy) is represented by entering two triples
  20942. into this list: *xy, and xy*. A continuation sequence of length one has no entry in
  20943. the triple list: the character type is simply nonBreaking.
  20944.  
  20945. •••Click on the Illustration button below, and refer to Figure 3.••• 
  20946.  
  20947. Note that the type wild cannot appear as the middle element of a triple. The words in
  20948. the triple table must be sorted in ascending numerical order for future compatibility.
  20949.  
  20950. The following is an example of how a character triple table could be coded. The defSeq
  20951. macro takes a continuation sequence as a parameter, and enters a set of triples into
  20952. an internal array. The dumpSeq macro sorts the triples, and stores them in the proper
  20953. order with dc.w commands.  Once again, writing the macros defSeq and dumpSeq is left
  20954. as an exercise for the reader.
  20955.  
  20956. ;============================================================
  20957.     with      charWordRec
  20958.     defSeq    letter,letter
  20959.     defSeq    letter,preMidNum,letter
  20960.     defSeq    letter,midLetter,letter
  20961.     defSeq    letter,midLetNum,letter
  20962.         
  20963.     defSeq    number,number
  20964.     defSeq    number,letter
  20965.     defSeq    number,midNum,number
  20966.     defSeq    number,midLetNum,number
  20967.     defSeq    number,preMidNum,number
  20968.     defSeq    number,postNum
  20969.     defSeq    preNum,number
  20970.     defSeq    preMidNum,number
  20971.  
  20972.     defSeq    blank,blank
  20973.     defSeq    blank,cr
  20974.     endWith
  20975.  
  20976. ;============================================================
  20977.     dc.w    ((wordEnd-wordBegin)/2)-1    ; length word.
  20978. wordBegin
  20979.     dumpSeq
  20980. wordEnd
  20981. ;============================================================
  20982.  
  20983. A series of blanks should generally select as a single word. Make certain, however,
  20984. that a carriage return does not continue a word to the right (note how it has a separate
  20985. character type from blank for this reason), otherwise word selection and wrapping do
  20986. not work properly across paragraphs.
  20987.  
  20988.  
  20989. Extensions
  20990.  
  20991. The values 16-31 in the character type table entry for null ($00) (the first byte in
  20992. the character type table) are reserved by Apple for future expansion. The use of one
  20993. of these values indicates the presence of a supplementary table after the triple
  20994. table.
  20995.  
  20996.  
  20997.  
  20998. æKY 183
  20999. æC #183: Position-Independent PostScript
  21000.  
  21001. See also:     The Printing Manager
  21002.               QuickDraw
  21003.               LaserWriter Reference Manual
  21004.               Technical Note #91 — Optimizing for the LaserWriter—PicComments
  21005.               PostScript Language Reference Manual, Adobe Systems
  21006.               PostScript Language Cookbook and Tutorial, Adobe Systems
  21007.  
  21008. Written by:    Scott “ZZ” Zimmerman     November 2, 1987
  21009. Updated:                                March 1, 1988
  21010. _______________________________________________________________________________
  21011.  
  21012. This technical note describes a method for inserting position-independent PostScript
  21013. into QuickDraw pictures.
  21014. _______________________________________________________________________________
  21015.  
  21016. There is a problem with pictures that contain PostScript code. Sometimes the PostScript
  21017. code that is inserted into the picture is dependent on the position of the picture on
  21018. the page. The problem arises when these pictures are cut or copied from their original
  21019. position, and pasted into another position or even into another document. The PostScript
  21020. code will not know the new location of the picture, and will not execute correctly.
  21021.  
  21022. The solution for this problem, is to provide some way for the PostScript code to
  21023. determine the current location of the picture relative to the page that it is being
  21024. printed on. This is done by inserting QuickDraw calls to position the LaserWriter’s
  21025. pen before inserting the position dependent PostScript code. When the PostScript in
  21026. the picture is executed, the LaserWriter’s pen location will be in a location relative
  21027. to the position of the picture on the page.
  21028.  
  21029. The following example illustrates a method for positioning the LaserWriter’s pen
  21030. before inserting any PostScript code into the picture. The method uses QuickDraw
  21031. calls to position the LaserWriter’s pen, and will work with any application that
  21032. supports QuickDraw pictures. Applications do not have to be changed to be able to
  21033. print pictures which use this technique; normal calls to DrawPicture will work.
  21034.  
  21035. The following code fragment will create a picture that contains PostScript to draw a
  21036. rectangle around the picture’s frame:
  21037.  
  21038. FUNCTION CreatePicture(pictureRect: Rect): PicHandle; 
  21039.     CONST         
  21040.         PostScriptBegin = 190;         
  21041.         PostScriptEnd = 191;         
  21042.         PostScriptHandle = 192; 
  21043.     VAR         
  21044.         PSString     : Str255;         
  21045.         PSHandle     : Handle;         
  21046.         theError     : OSErr; 
  21047.     BEGIN         
  21048.         (* Create a new Picture. *)         
  21049.         CreatePicture := OpenPicture(pictureRect);        ClipRect(pictureRect);
  21050.  
  21051.  
  21052.         (* Set the pen size to 0,0 so the following Line will not *)
  21053.         (* be shown, it is only sent to position the pen.*)        PenSize(0,0);
  21054.  
  21055.  
  21056.         (* Move the QuickDraw pen to the first pixel inside the *)
  21057.         (* the picture’s frame.  This by itself will not *)
  21058.         (* change the LaserWriter’s pen location! *)     
  21059.         MoveTo(pictureRect.left, pictureRect.top);
  21060.  
  21061.         (* Force the LaserWriter’s pen location to match the  *)
  21062.         (* QuickDraw pen location.  Actually any drawing command, *)
  21063.         (* such as LineTo or even DrawString will cause the *)
  21064.         (* LaserWriter’s pen location to change.  This call to *)
  21065.         (* the Line procedure only causes the coordinates of *)
  21066.         (* the above MoveTo to be flushed to the LaserWriter. *)
  21067.         (* Because of the PenSize call above, no Line is drawn. *)
  21068.          Line(0,0); 
  21069.  
  21070.         (* Reset the pen to its default size. *)         
  21071.         PenSize(1,1); 
  21072.  
  21073.         (* The LaserWriter’s pen location has now been changed.  The *)
  21074.         (* PostScript currentpoint operator will now return the *)
  21075.         (* location of the center pixel of the Picture. *) 
  21076.         (* Get the PostScript ready to be sent. *)
  21077.         (* currentpoint    - push the current Point onto the stack. *) 
  21078.         (* newpath         - Begin a new Line segment. *)
  21079.         (* MoveTo          - Move to the currentpoint we saved above. *)
  21080.         (* nn nn rlineto   - frame the Picture with lines. *)
  21081.         (* stroke          - Draw the frame. *)
  21082.          PSString := 
  21083.         '100 0 rlineto 0 100 rlineto -100 0 rlineto 0 -100 rlineto stroke '
  21084.                 ;
  21085.          PSString[length(PSString)] := CHR(13); (* Don’t forget CR. *)
  21086.              theError:=PtrToHand(Ptr(ORD4(@PSString)+1), 
  21087.                       PSHandle,length(PSString));
  21088.          IF theError <> noErr THEN HandleError; 
  21089.  
  21090.         (* Send the PostScript code to the LaserWriter. *)             PicComment(PostScriptBegin,0,nil);
  21091.  
  21092.             (* QuickDraw calls made between the PostScriptBegin *)
  21093.             (* and PostScriptEnd PicComments will be ignored by *)
  21094.             (* devices that support PostScript. *)
  21095.  
  21096.             PicComment(PostScriptHandle,GetHandleSize(PSHandle),PSHandle); 
  21097.  
  21098.         PicComment(PostScriptEnd,0,nil);
  21099.  
  21100.         (* Kill off the Handle we created and close the picture. *)
  21101.          DisposHandle(PSHandle); 
  21102.         ClosePicture;     
  21103.     END; (* CreatePicture *)
  21104.  
  21105. See the LaserWriter Reference Manual for more information about PicComments. See the
  21106. PostScript Language Reference Manual for more information about the currentpoint
  21107. operator.
  21108.  
  21109. There are some important guidelines to follow when sending PostScript directly to the
  21110. LaserWriter. See the PostScript Commands section of Technical Note #91, for a complete
  21111. description of these guidelines.
  21112.  
  21113.  
  21114.  
  21115. æKY 184
  21116. æC #184:    Notification Manager
  21117.  
  21118. Revised by:    Rich Collyer                                        October 1989
  21119. Written by:    Darin Adler                                           April 1988
  21120.  
  21121. This Technical Note describes the Notification Manager, the part of the operating
  21122. system that lets an application, desk accessory, or driver alert the user.
  21123. Changes since June 1989:  Made minor changes to the code examples and clarified the
  21124. descriptions of what is needed when.
  21125. _______________________________________________________________________________
  21126.  
  21127. The Notification Manager, in System Software version 6.0 and later, provides the user
  21128. with an asynchronous “notification” service.  The Notification Manager is especially
  21129. useful for background applications; the PrintMonitor application and the system alarm
  21130. (set by the Alarm Clock desk accessory) both use its services.
  21131.  
  21132. Each application, desk accessory, or device driver can queue any number of notifications.
  21133.  For this reason, you should try to avoid posting multiple notifications since each
  21134. one will be presented separately to the user (i.e.,
  21135. “you have mail,” “you have mail,” etc.).
  21136.  
  21137. The Notification Manager queue contains information describing each notification
  21138. request; you supply a pointer to a queue element describing the type of notification
  21139. you desire.  The Notification Manager queue is a standard Macintosh queue, as described
  21140. in the Operating System Utilities chapter of Inside Macintosh, Volume II-367.  Each
  21141. entry in the Notification Manager queue has the following structure:
  21142.  
  21143.     TYPE NMRec = RECORD
  21144.                   qLink:         QElemPtr;   {next queue entry}
  21145.                   qType:         INTEGER;    {queue type -- ORD(nmType) = 8}
  21146.                   nmFlags:       INTEGER;    {reserved}
  21147.                   nmPrivate:     LONGINT;    {reserved}
  21148.                   nmReserved:    INTEGER;    {reserved}
  21149.                   nmMark:        INTEGER;    {item to mark in Apple menu}
  21150.                   nmSIcon:       Handle;     {handle to small icon}
  21151.                   nmSound:       Handle;     {handle to sound record}
  21152.                   nmStr:         StringPtr;  {string to appear in alert}
  21153.                   nmResp:        ProcPtr;    {pointer to response routine}
  21154.                   nmRefCon:      LONGINT;    {for application use}
  21155.                  END;
  21156.  
  21157. To use the Notification Manager, you must also use _SysEnvirons (discussed in Inside
  21158. Macintosh, Volume V and Technical Note #129) to test the System Software version.  If
  21159. the System Software is not current and the Notification Manager routines are not
  21160. present, display an alert to inform the user that your application requires System
  21161. Software version 6.0 or newer, then exit.
  21162.  
  21163.  
  21164. Using the Notification Manager
  21165.  
  21166. Your program can request three types of notification:
  21167.  
  21168.   •  polite notification:  a small icon that periodically appears in
  21169.      rotation with the Apple in the menu bar;
  21170.   •  audible notification:  a sound to be played by the Sound Manager;
  21171.   •  alert notification:  an dialog box containing a short message.
  21172.  
  21173. In addition, you can place a diamond mark next to the name of the requesting desk
  21174. accessory or application in the Apple menu.
  21175.  
  21176. After you have notified the user as you feel necessary (placed a diamond mark in the
  21177. Apple menu, added a small icon to the list of icons which rotate with the Apple in
  21178. the menu bar, played a sound, and presented the user with a dialog box to acknowledge),
  21179. you should call a response procedure.
  21180.  
  21181. How the Notification Manager Handles Notifications
  21182.  
  21183. When the Notification Manager handles a notification, it does one or more of the
  21184. following (in this order):
  21185.  
  21186.   •  puts a diamond mark next to the requesting application or desk
  21187.      accessory in the Apple menu
  21188.   •  adds a small icon to the list of icons which rotate with the
  21189.      Apple in the menu bar
  21190.   •  plays a specified sound
  21191.   •  presents a dialog box for the user to acknowledge and dismiss
  21192.   •  calls the response procedure
  21193.  
  21194. At this point, the diamond mark in the Apple menu and the icon rotating with the
  21195. Apple in the menu bar remain until your application removes the notification request
  21196. from the queue.  The Notification Manager only presents the sound and dialog box
  21197. once.
  21198.  
  21199. Creating a Notification Request
  21200.  
  21201. To create a notification request, you must set up an NMRec with all the information
  21202. about the notification you want:
  21203.  
  21204. nmMark     contains 0 to not place a mark in the Apple menu, 1 to mark
  21205.            the current application, or the refNum of a desk accessory
  21206.            to mark that desk accessory.  An application should pass 1,
  21207.            a desk accessory should pass its own refNum, and a VBL task
  21208.            should pass 0.
  21209.  
  21210. nmSIcon    contains NIL for no icon in the menu bar, or a handle to a
  21211.            small icon to rotate with the Apple.  (A small icon is a
  21212.            16 x 16 bitmap, often stored in an 'SICN' resource.)  This
  21213.            handle does not need to be locked, but it must be non-purgeable.
  21214.  
  21215. nmSound    contains NIL to use no sound, –1 to use the system beep sound,
  21216.            or a handle to a sound record which can be played with _SndPlay.
  21217.            This handle does not need to be locked, but it must be non-purgeable.
  21218.  
  21219. nmStr      contains NIL for no alert, or a pointer to the string to appear
  21220.            in the alert.
  21221.  
  21222. nmResp     contains NIL for no response procedure, –1 for a predefined
  21223.            procedure that removes the request immediately after it is
  21224.            completed, or a pointer to a procedure which takes one parameter,
  21225.            a pointer to your queue element.  For example, the following is
  21226.            how you would declare it if it were named MyResponse:
  21227.  
  21228.                PROCEDURE MyResponse (nmReqPtr: QElemPtr);
  21229.                pascal void MyResponse (QElemPtr nmReqPtr);
  21230.  
  21231. Note that when the Notification Manager calls your response procedure, it does not
  21232. set up A5 and low-memory globals for you.  If you need to access your application’s
  21233. globals, you should save your application’s A5 in the nmRefCon field as discussed
  21234. later in this Note.
  21235.  
  21236. Response procedures should never draw or do “user interface” things.  You should wait
  21237. until the user brings the application or desk accessory to the front before responding
  21238. to the user.  Some good ways to use the response procedure are to dequeue and deallocate
  21239. your Notification Manager queue element or to set an application global (being careful
  21240. about A5), so the application knows when the user receives the notification.
  21241.  
  21242. You should probably use an nmResp of –1 with audible and alert notifications to remove
  21243. the notification as soon as the sound has finished or the user has dismissed the
  21244. dialog.  You should not use an nmResp of –1 with an nmMark or an nmSIcon, because the
  21245. Notification Manager would remove the diamond mark or small icon before the user
  21246. could see it.  Note that an nmResp of –1 does not deallocate the memory block containing
  21247. the queue element, it only removes it from the notification queue.
  21248.  
  21249. You can also use nmRefCon; one convenient use is putting your application’s A5 in
  21250. this field so the response procedure can access application globals.  For more information
  21251. about this technique, refer to the section about VBL tasks in Technical Note #180.
  21252.  
  21253.  
  21254. Notification Manager Routines
  21255.  
  21256. The system automatically initializes the Notification Manager when it boots.  To add
  21257. a notification request to the notification queue, call _NMInstall.  When your application
  21258. no longer wants a notification to continue, it can remove the request by calling
  21259. _NMRemove.  Neither _NMInstall nor _NMRemove move or purge memory, and you can call
  21260. either of them from completion routines or interrupt handlers, as well as from the
  21261. main body of an application and the response procedure of a notification request.
  21262.  
  21263. FUNCTION NMInstall (nmReqPtr: QElemPtr) : OSErr;
  21264.  
  21265.     Trap macro    _NMInstall ($A05E)
  21266.     On entry      A0: theNMRec (pointer)
  21267.     On exit       D0: result code (word)
  21268.  
  21269. _NMInstall adds the notification request specified by nmReqPtr to the notification
  21270. queue and returns one of the following result codes:
  21271.  
  21272.     Result codes    noErr                No error
  21273.                     nmTypErr (–299)      qType field is not ORD(nmType)
  21274.  
  21275.  
  21276. FUNCTION NMRemove (nmReqPtr: QElemPtr) : OSErr;
  21277.  
  21278.     Trap macro    _NMRemove ($A05F)
  21279.     On entry      A0: theNMRec (pointer)
  21280.     On exit       D0: result code (word)
  21281.  
  21282. _NMRemove removes the notification identified by nmReqPtr from the notification queue
  21283. and returns one of the following result codes:
  21284.  
  21285.     Result codes    noErr                No error
  21286.                     qErr                 Not in queue
  21287.                     nmTypErr (–299)      qType field is not ORD(nmType)
  21288.  
  21289.  
  21290. How to Call _NMInstall and _NMRemove
  21291.  
  21292. If you do not yet have glue for _NMInstall and _NMRemove, you can use the following
  21293. from MPW (these are in the include files for MPW 3.0):
  21294.  
  21295. Pascal
  21296.  
  21297.     FUNCTION NMInstall (nmReqPtr: QElemPtr) : OSErr;
  21298.     INLINE $205F, $A05E, $3E80;
  21299.  
  21300.     FUNCTION NMRemove (nmReqPtr: QElemPtr) : OSErr;
  21301.     INLINE $205F, $A05F, $3E80;
  21302.  
  21303. C
  21304.  
  21305.     pascal OSErr NMInstall (QElemPtr nmReqPtr)
  21306.         = {0x205F, 0xA05E, 0x3E80};
  21307.  
  21308.     pascal OSErr NMRemove (QElemPtr nmReqPtr)
  21309.         = {0x205F, 0xA05F, 0x3E80};
  21310.  
  21311. Also note that qType must be set to ORD(nmType), which is 8.
  21312.  
  21313. The following short code segments demonstrate the use of the Notification Manager in
  21314. MPW C:
  21315.  
  21316.     #include <OSUtils.h>
  21317.     #include <Notification.h>
  21318.  
  21319.     struct NMRec    myNote;       /* declare your NMRec */
  21320.     Handle          ManDoneS;     /* declare a handle for the sound */
  21321.     OSErr           err;          /* declare for err handling */
  21322.  
  21323.     myNote.qType = nmType;        /* queue type -- nmType = 8 */
  21324.     myNote.nmMark = 1;            /* get mark in Apple menu */
  21325.     myNote.nmSIcon = nil;         /* no flashing Icon */
  21326.  
  21327.     /* get the sound you want out of your resources */
  21328.     ManDoneS = GetResource('snd ', soundID);
  21329.  
  21330.     myNote.nmSound = ManDoneS;    /* set the sound to be played */
  21331.     myNote.nmStr = nil;           /* no alert box */
  21332.     myNote.nmResp = nil;          /* no response procedure */
  21333.     myNote.nmRefCon = nil;        /* set to nil since I don't need my A5 */
  21334.  
  21335.  
  21336. Before calling _NMInstall, you need to see if your application is running in the
  21337. background.  If your application is in the foreground, you really do not need to
  21338. notify the user, but if your application is in the background, you should make the
  21339. following call to install the notification event:
  21340.  
  21341.     err = NMInstall ((QElemPtr) &myNote);
  21342.  
  21343. Before continuing, you should handle any errors.  If your application is running in
  21344. the background, you should wait until it switches to the foreground before proceeding
  21345. with anything else.  While you are waiting for a resume event, you should take care
  21346. of other events, such as updates.  You want to make sure that when you are switched
  21347. back into the foreground you remove the notification, and the following code does
  21348. just that:
  21349.  
  21350.     err = NMRemove ((QElemPtr) &myNote);
  21351.  
  21352. Once again, you should be sure to handle any errors.
  21353.  
  21354.  
  21355. Further Reference:
  21356. _______________________________________________________________________________
  21357.   •  Inside Macintosh, Volume II-367, V-591, The Operating System Utilities
  21358.   •  Technical Note #129, _SysEnvirons:  System 6.0 and Beyond
  21359.   •  Technical Note #180, MultiFinder Miscellanea
  21360.  
  21361.  
  21362. æKY 185
  21363. æC #185: OpenRFPerm: What your mother never told you.
  21364.  
  21365. See also:    The Resource Manager
  21366.  
  21367. Written by:    Dave Burnard    April 2, 1988
  21368. _______________________________________________________________________________
  21369.  
  21370. This note corrects an error in the description of the Resource Manager routine OpenRFPerm
  21371. found in Inside Macintosh Volume IV.
  21372. _______________________________________________________________________________
  21373.  
  21374. On page IV-17 in the Resource Manager chapter of Inside Macintosh Volume IV it states,
  21375.  
  21376.  
  21377.   “OpenRFPerm, like OpenResFile, will not open the specified file twice; it     
  21378.    simply returns the reference number already assigned to the file. In other    
  21379.  
  21380.    words, OpenRFPerm cannot be used to open a second access path to a resource 
  21381.    file…”
  21382.  
  21383. This statement is incorrect. OpenRFPerm behaves exactly as described if an application
  21384. attempts to open a second access path, with write access, to a resource file without
  21385. MultiFinder. With MultiFinder, however, if the second attempt comes from a different
  21386. application than the one that originally opened the file OpenRFPerm will not return
  21387. the reference number already assigned to the file, instead it will return –1 and
  21388. ResError will be set to opWrErr (–49). In fact in similar circumstances with MultiFinder,
  21389. OpenResFile behaves the same way, returning –1 and setting ResError to –49.
  21390.  
  21391. Note, however, that with or without MultiFinder, OpenRFPerm will create multiple,
  21392. unique, read-only access paths to a resource file. Using this feature is not safe and
  21393. should be avoided since if a resource file is opened twice, once with read-write
  21394. permission and once with read-only permission then two copies of the resource map
  21395. will exist in memory. If one of the resources in the file is changed and written to
  21396. disk then the two maps will become inconsistent and the second access path will see
  21397. what it thinks is a trashed resource file.
  21398.  
  21399. If you must use this technique for read-only access, only call OpenRFPerm when your
  21400. application is ready to read information from the file and close the file immediately
  21401. afterwards. Otherwise you risk having the resource file change out from under your
  21402. application.
  21403.  
  21404.  
  21405.  
  21406. æKY 186
  21407. æC #186: PBLock/UnlockRange 
  21408.  
  21409. See also:    The File Manager
  21410.  
  21411. Written by:    Dave Burnard    April 2, 1988
  21412. _______________________________________________________________________________
  21413.  
  21414. The File Manager chapter of Inside Macintosh Volume IV discusses the file range locking
  21415. calls PBLockRange and PBUnlockRange. Unfortunately, it does not mention the fact that
  21416. these calls are currently only properly implemented for shared volumes (i.e. AppleShare,
  21417. TOPS). Calling PBLockRange or PBUnlockRange currently has absolutely no effect for
  21418. local HFS (or MFS) volumes.
  21419.  
  21420. As an aside, the File Manager chapter of Inside Macintosh Volume V discusses several
  21421. other calls which are available for accessing (or restricting access to) files on
  21422. shared volumes.
  21423.  
  21424. æKY 187
  21425. æC #187: Don’t Look at ioPosOffset
  21426.  
  21427. See also:    The Device Manager
  21428.  
  21429. Written by:    Darin Adler    April 2, 1988
  21430. _______________________________________________________________________________
  21431.  
  21432. The Device Manager chapter of Inside Macintosh Volume II says that ioPosOffset is
  21433. passed to and returned by Read and Write calls. It also says that “After the read [or
  21434. write] is completed, the position is returned in ioPosOffset…” Actually, ioPosOffset
  21435. is not changed by either call.
  21436.  
  21437. Also note that device drivers should only look at the dCtlPosition field of the DCE,
  21438. and should not look directly at the ioPosOffset field of the parameter block. The
  21439. Device Manager sets up dCtlPosition for the driver, taking into account both the
  21440. ioPosMode and the ioPosOffset.
  21441.  
  21442.  
  21443. æKY 188
  21444. æC #188: ChangedResource: Too much of a good thing.
  21445.  
  21446. See also:    The Resource Manager
  21447.  
  21448. Written by:    Dave Burnard    April 2, 1988
  21449. _______________________________________________________________________________
  21450.  
  21451. The toolbox trap ChangedResource is used to inform the Resource Manager that the
  21452. contents of a resource have changed and should be written to disk. The actual write
  21453. occurs on the next call to WriteResource (for the specific resource) or UpdateResFile
  21454. (for the resource file containing the specified resource). When called, ChangedResource
  21455. reserves enough disk space to contain the changed resource. A little-known “feature”
  21456. of ChangedResource is that it reserves disk space every time it is called. Thus if
  21457. you call ChangedResource ten times on a large resource before the resource is actually
  21458. written out, you may unexpectedly run out of disk space since ten times the amount of
  21459. disk space actually needed will be reserved. 
  21460.  
  21461. If your program frequently changes the contents of resources, especially large ones,
  21462. then you should call WriteResource or UpdateResFile immediately after calling ChangedResource.
  21463. Once the resource is actually written, the file’s EOF will be set correctly, and the
  21464. first subsequent call to ChangedResource will work correctly.
  21465.  
  21466.  
  21467.  
  21468. æKY 189
  21469. æC #189:    Version Territory
  21470.  
  21471. Revised by:    Rich Collyer                                          April 1989
  21472. Written by:    Darin Adler                                           April 1988
  21473.  
  21474. This Technical Note describes the 'vers' resource supported by Finder 6.1 and later.
  21475. Changes since October 1988:  Changed MPW C code to reflect the changes in
  21476. MPW C 3.0.
  21477. _______________________________________________________________________________
  21478.  
  21479. Finder 6.1 introduced a feature which allows the creator of a file to identify the
  21480. version of that file as well as the version of a set of files which includes that
  21481. file.  These version numbers are stored in 'vers' resources, and each contains a BCD
  21482. form of the version number and a longer version message
  21483. (which the Finder displays in the Get Info window for each file).
  21484.  
  21485. Apple’s Version Numbering Scheme
  21486.  
  21487. Apple uses a version numbering scheme for its software products which you might want
  21488. to adopt.  Table 1 summarizes the scheme, which involves three numbers, each separated
  21489. by periods.
  21490.  
  21491.                 Event                                  Version
  21492.                 ______________________________________________
  21493.                 First released version                 1.0
  21494.                 First revision                         1.1
  21495.                 First bug fix to the first revision    1.1.1
  21496.                 First major revision or rewrite        2.0
  21497.                 ______________________________________________
  21498.  
  21499.                    Table 1–Apple’s Version Numbering Scheme
  21500.  
  21501. Note that Apple increments the first number when it releases a major revision, the
  21502. second number when it releases a minor revision, and the third number when it releases
  21503. a version to address bugs (the third number is omitted if it is zero).
  21504.  
  21505. During product development, Apple uses a version number followed by a suffix which
  21506. indicates the stage of development.  Table 2 presents a few examples.
  21507.  
  21508.     Event                                       Version          Stage
  21509.     ________________________________________________________________________
  21510.     First versions                              1.0d1, 1.0d2…    development
  21511.     Product features defined (begin testing)    1.0a1, 1.0a2…    alpha
  21512.     Product is stable (begin final testing)     1.0b1, 1.0b2…    beta
  21513.     First released version                      1.0              release
  21514.     First revision                              1.1d1…1.1
  21515.     First bug fix to the first revision         1.1.1d1…1.1.1
  21516.     First major revision                        2.0d1…2.0
  21517.     ________________________________________________________________________
  21518.  
  21519.                       Table 2–Development Version Numbering
  21520.  
  21521.  
  21522. Version Resources
  21523.  
  21524. Each 'vers' resource has the following format (described with a Rez template):
  21525.  
  21526.     #include "SysTypes.r" /* for country codes */
  21527.  
  21528.     type 'vers' {
  21529.         byte;             /* first part of version number in BCD */
  21530.         byte;             /* second and third parts of version number */
  21531.         byte development=0x20, alpha=0x40, beta=0x60, release=0x80;
  21532.         byte;             /* stage of non-release version */
  21533.         integer Country;  /* country code as in international utilities */
  21534.         pstring;          /* short version number */
  21535.         pstring;          /* long version message */
  21536.     };
  21537.  
  21538. The short version number is a string which only contains the version number 
  21539. (e.g., “1.0”).  The long version message can also include a copyright notice, a release
  21540. date, or other information, but it should not include the name of the program.  The
  21541. following examples illustrate the proper use of the Rez template to create version
  21542. resources:
  21543.  
  21544.     resource 'vers' (1) {
  21545.         0x01, 0x00, release, 0x00, verUS,
  21546.         "1.0",
  21547.         "1.0 (US), ©1989 Inside Joke"
  21548.     };
  21549.     resource 'vers' (2) {
  21550.         0x12, 0x00, release, 0x00, verUS,
  21551.         "12.0",
  21552.         "Watt-R-Utilities Disk 12.0"
  21553.     };
  21554.  
  21555.     resource 'vers' (1) {
  21556.         0x23, 0x45, beta, 0x67, verFinland,
  21557.         "23.4.5b67",
  21558.         "23.4.5b67 (Finland), ©1989 Squid, Inc."
  21559.     };
  21560.     resource 'vers' (2) {
  21561.         0x55, 0x00, development, 0x67, verFinland,
  21562.         "55.0d67",
  21563.         "Friends of Skippy White 55.0d67"
  21564.     };
  21565.  
  21566. The following is a type definition for 'vers' resources in MPW Pascal:
  21567.  
  21568.     NumVersion = PACKED RECORD
  21569.         CASE INTEGER OF
  21570.           0:
  21571.             (majorRev: SignedByte;  {1st part of version number in BCD}
  21572.             minorRev: 0..9;         {2nd part is 1 nibble in BCD}
  21573.             bugFixRev: 0..9;        {3rd part is 1 nibble in BCD}
  21574.             stage: SignedByte;      {stage code: dev, alpha, beta, final}
  21575.             nonRelRev: SignedByte); {revision level of non-released version}
  21576.           1:
  21577.             (version: LONGINT);     {to use all 4 fields at one time}
  21578.         END;
  21579.  
  21580.  
  21581.     { Numeric version part of 'vers' resource }
  21582.     VersRecPtr = ^VersRec;
  21583.     VersRecHndl = ^VersRecPtr;
  21584.     VersRec = RECORD
  21585.         numericVersion: NumVersion; {encoded version number}
  21586.         countryCode: INTEGER;       {country code from intl utilities}
  21587.         shortVersion: Str255;       {version number string - worst case}
  21588.         reserved: Str255;           {longMessage string packed after
  21589.         END;                         shortVersion}
  21590.  
  21591.  
  21592. The type definition in MPW C is as follows (these structures are not needed in your
  21593. code since they are included in the header file Files.h):
  21594.  
  21595.     struct NumVersion {
  21596.         unsigned char majorRev;     /*1st part of version number in BCD*/
  21597.         unsigned int minorRev : 4;  /*2nd part is 1 nibble in BCD*/
  21598.         unsigned int bugFixRev : 4; /*3rd part is 1 nibble in BCD*/
  21599.         unsigned char stage;        /*stage code: dev, alpha, beta, final*/
  21600.         unsigned char nonRelRev;    /*revision level of non-released version*/
  21601.     };
  21602.     
  21603.     /* Numeric version part of 'vers' resource */
  21604.     struct VersRec {
  21605.         NumVersion numericVersion;  /*encoded version number*/
  21606.         short countryCode;          /*country code from intl utilities*/
  21607.         Str255 shortVersion;        /*version number string - worst case*/
  21608.         Str255 reserved;            /*longMessage string packed after
  21609.     };                                shortVersion*/
  21610.  
  21611.     typedef VersRec *VersRecPtr, **VersRecHndl;
  21612.  
  21613. The longMessage string is not necessarily word-aligned due to the way the resource is
  21614. formatted, so you should use _BlockMove to extract it from the record.  Following are
  21615. examples of this technique:
  21616.  
  21617. MPW Pascal
  21618.  
  21619.     VAR
  21620.         version: VersRecHandle;
  21621.         messagePtr: StringPtr;
  21622.         longMessage: Str255;
  21623.     ...
  21624.     WITH version^^ DO
  21625.         BEGIN
  21626.         {calculate a pointer to the long message}
  21627.         messagePtr := StringPtr(Ord(@shortVersion)+Length(shortVersion)+1);
  21628.         {move the long message into a string}
  21629.         BlockMove(Ptr(messagePtr), @longMessage, Length(messagePtr^)+1);
  21630.         END;
  21631.  
  21632. MPW C
  21633.  
  21634.     VersRecHndl version; /* don't forget to get the 'vers' resource
  21635.     StringPtr messagePtr;   you want to look at */
  21636.     Str255 longMessage;
  21637.  
  21638.     /* calculate a pointer to the long message */
  21639.     messagePtr = (StringPtr) (((unsigned long) &(**version).shortVersion[1]) +
  21640.                  (**version).shortVersion[0] + 1);
  21641.     /* copy the long message into a string */
  21642.     BlockMove(messagePtr, &longMessage, ((unsigned char) &messagePtr) + 1);
  21643.  
  21644. A file can contain either one, two, or no 'vers' resources.  If present, a 
  21645. 'vers' (1) resource identifies the file version while a 'vers' (2) resource identifies
  21646. the version (and name) of a set of files which includes that file, thus linking all
  21647. the files which make up the set.  Apple uses this mechanism to identify System Software
  21648. versions.  All files on System Tools disks have a 
  21649. 'vers' (2) resource that identifies the version of System Tools with which they were
  21650. released.  In addition, each file has a 'vers' (1) resource that identifies the version
  21651. of the particular file.
  21652.  
  21653.  
  21654. Version Resources and the Finder
  21655.  
  21656. The Finder displays the long message from 'vers' (1) and 'vers' (2) resources, if
  21657. they are present, in the Get Info window of a file; it ignores the rest of the 'vers'
  21658. resource.  Following is an example of the 'vers' resources from Finder 6.1 with a Get
  21659. Info window for the Finder file in Figure 1.
  21660.  
  21661.     resource 'vers' (1) {
  21662.         0x06, 0x10, release, 0x00, verUS,
  21663.         "6.1",
  21664.         "6.1, Copyright Apple Computer, Inc. 1983-88"
  21665.     };
  21666.  
  21667.     resource 'vers' (2) {
  21668.         0x06, 0x03, release, 0x00, verUS,
  21669.         "6.0.3",
  21670.         "System Software Version 6.0.3"
  21671.     };
  21672.  
  21673. •••Click on the Illustration button below, and refer to Figure 1.••• 
  21674.  
  21675. Figure 1–Get Info Window for the Finder File
  21676.  
  21677. The other fields (besides the long message) are often useful to applications other
  21678. than the Finder.  The short version number is good for displaying the version of a
  21679. particular file, as the Finder does for the System and Finder in the About the Macintosh™
  21680. Finder window.  The BCD version number is well suited for checking for a desired
  21681. version number or comparing two versions.  Note that this BCD numbering scheme represents
  21682. a more recent version with a number greater than an older version, so a numeric comparison
  21683. between two four-byte values is all that is necessary to determine which value is the
  21684. most recent.
  21685.  
  21686.  
  21687. Final Note
  21688.  
  21689. The Finder Interface chapter of Inside Macintosh, Volume III-7 describes a resource
  21690. (part of the bundle) that contains the version data of an application.  This version
  21691. data is typically a string that gives the name, version number, and date of the application.
  21692.  The Finder displays the version data (treating it as a string) in the Get Info window
  21693. if there is no 'vers' (1) resource in the application.  Unlike this version data in
  21694. an application, any type of file can contain 'vers' resources, not just those files
  21695. which contain bundles.
  21696.  
  21697.  
  21698. Further Reference:
  21699. _______________________________________________________________________________
  21700.     •    Inside Macintosh, Volume III-7, The Finder Interface
  21701.     •    Technical Note #48, Bundles
  21702.  
  21703.  
  21704. æKY 190
  21705. æC #190: Working Directories and MultiFinder
  21706.  
  21707. See also:     The File Manager
  21708.               Technical Note #77 — HFS Ruminations
  21709.  
  21710. Written by:    Darin Adler    April 2, 1988
  21711. _______________________________________________________________________________
  21712.  
  21713. This technical note describes the way that working directories are handled under
  21714. MultiFinder.
  21715. _______________________________________________________________________________
  21716.  
  21717. Some versions of Technical Note #77 claim that you can open working directories with
  21718. a unique ioWDProcID and that they will only be deallocated when “the system is rebooted.”
  21719.  
  21720. With MultiFinder, this has changed. When you call PBOpenWD, the ioWDProcID that you
  21721. pass in is ignored. MultiFinder overrides your ioWDProcID with a unique process ID
  21722. for your application, and deallocates all working directories that you allocated when
  21723. your application terminates. Thus, you cannot use the ioWDProcID to identify your
  21724. working directories when running under MultiFinder.
  21725.  
  21726. Indexing through working directories with PBGetWDInfo and a nonzero value of ioWDProcID
  21727. is now a bad idea. This is because the working directories will have ioWDProcIDs that
  21728. MultiFinder has assigned, rather than the ones you specified.
  21729.  
  21730. Whenever you open a working directory with PBOpenWD, you should pass your application’s
  21731. signature as the ioWDProcID and close the working directory as soon as possible with
  21732. PBCloseWD. You should only close working directories that you open, not ones that are
  21733. returned to you from Standard File or SysEnvirons. It is best to keep working directories
  21734. open for the minimum time necessary, and to avoid using them when possible. Note that
  21735. working directories are implemented mostly for the benefit of old (pre-HFS) applications,
  21736. and rarely need to be used.
  21737.  
  21738.  
  21739.  
  21740. æKY 191
  21741. æC #191: Font Names
  21742.  
  21743. See also:     The Font Manager
  21744.  
  21745. Written by:   Darin Adler       April 2, 1988
  21746. Revised by:   Bryan Stearns     August 1, 1988
  21747. ________________________________________________________________________________
  21748.  
  21749. This note recommends the use of font names rather than font numbers.
  21750. ________________________________________________________________________________
  21751.  
  21752. The Font Manager chapter of Inside Macintosh Volume IV claims that font family numbers
  21753. 0 through 127 are reserved for use by Apple, and numbers 128 through 255 are assigned
  21754. by Apple for fonts created by software developers. This is no longer true. Developer
  21755. Technical Support does not assign font family numbers. You should only use font numbers
  21756. to reference the system font (font 0) and application default font (font 1). All
  21757. other fonts should be identified by name. The Font/DA Mover will renumber a font when
  21758. moving it into a file containing a conflicting font family.
  21759.  
  21760. The Font Manager routines GetFontName and GetFNum map font names to numbers and vice
  21761. versa. This makes it simple to store a font’s name in a document and turn it back
  21762. into a number when reading the document. Unfortunately, GetFNum returns a 0 when a
  21763. font by that name doesn’t exist; this is the same as the font ID for the system font.
  21764. The following routine in MPW Pascal alleviates this problem:
  21765.  
  21766.      FUNCTION GetFontNumber(fontName: Str255; VAR fontNum: INTEGER) : BOOLEAN;
  21767.      {GetFontNumber returns in fontNum the number for the font having
  21768.       the given fontName. If there’s no such font, it returns FALSE.}
  21769.  
  21770.      VAR
  21771.          systemFontName: Str255;
  21772.  
  21773.      BEGIN
  21774.      GetFNum(fontName, theNum);
  21775.      IF fontNum = 0 THEN BEGIN
  21776.          {either the font was not found, or it is the system font}
  21777.          {if it was the system font, we got it, otherwise we didn't}
  21778.          GetFontName(0, systemFontName);
  21779.          GetFontNumber := EqualString(fontName, systemFontName, FALSE, FALSE);
  21780.      END ELSE
  21781.          {if theNum was not 0, we found the font}
  21782.          GetFontNumber := TRUE;
  21783.      END;
  21784.  
  21785. In MPW C:
  21786.  
  21787.      Boolean GETFONTNUMBER(fontName, fontNum)
  21788.      Str255* fontName;
  21789.      short* fontNum;
  21790.      /* GetFontNumber returns in fontNum the number for the font having
  21791.         the given fontName. If there’s no such font, it returns false. */
  21792.      {
  21793.          Str255* systemFontName;
  21794.  
  21795.          GETFNUM(fontName, fontNum);
  21796.          if (*fontNum == 0) {
  21797.              /* either the font was not found, or it is the system font */
  21798.              /* if it was the system font, we got it, otherwise we didn't */
  21799.              GETFONTNAME(0, systemFontName);
  21800.              return (EQUALSTRING(fontName, systemFontName, false, false));
  21801.          } else
  21802.              return true;
  21803.      }
  21804.  
  21805. This routine makes it easy to find out if a given font exists; if the font isn’t
  21806. available, you can present a dialog to the user, allowing an appropriate substitute
  21807. font to be chosen.
  21808.  
  21809.  
  21810. Handy Hint for Lists of Font Names
  21811.  
  21812. Most applications that offer the user a choice of fonts do so by creating a Fonts
  21813. menu. Some applications, however, present a list of fonts in some other way: for
  21814. example, word processors that use a dialog box to let the user pick a font family,
  21815. point size, and style often display the font family choices in a List Manager list.
  21816. You can get the Menu Manager to do most of the work by using AddResMenu to enumerate
  21817. and alphabetize the names, as follows:
  21818.  
  21819. PROCEDURE BuildMyFontList;
  21820. CONST
  21821.      AnUnusedMenuID = 150; {a menu ID not used by any of your menus}
  21822. VAR
  21823.      tempMenu: MenuHandle;
  21824.      thisItem: INTEGER;
  21825.      aFontName: Str255;
  21826. BEGIN
  21827.      {Get a menu; use the Menu Manager to fill it with font names}
  21828.      tempMenu := NewMenu(AnUnusedMenuID,'x');
  21829.      AddResMenu(tempMenu,'FONT');
  21830.  
  21831.      {Extract the names we got, one at a time}
  21832.      FOR thisItem := 1 TO CountMItems(tempMenu) DO
  21833.           BEGIN
  21834.                {Extract the next name from the menu}
  21835.                GetItem(tempMenu,thisItem,aFontName);
  21836.  
  21837.                {** Do something with this font name (add   **}
  21838.                 {** it to a List Manager list, or whatever) **}
  21839.           END;
  21840.  
  21841.      {We’re done with the menu; dispose of it}
  21842.      DisposeMenu(tempMenu);
  21843. END; {BuildMyFontList}
  21844.  
  21845. This approach will help to insulate your application from changes to the Font Manager.
  21846. Historically, AddResMenu was modified to notice FOND resources at the same time the
  21847. Font Manager began to support them, so applications that used this technique to build
  21848. their font lists didn’t need to be modified to work with FONDs.
  21849.  
  21850.  
  21851. Suggested Font Strategy for Applications and Documents
  21852.  
  21853. If your application offers the user a choice of a single font for use in the entire
  21854. document, it’s a simple matter to store the name of that font somewhere in the document
  21855. (perhaps in an ‘STR ’ resource). However, if your application lets the user use many
  21856. fonts within each document (as is the case with most word processors) a more complex
  21857. strategy is necessary:  the Font Name Mapping Table. Each entry might look like this:
  21858.  
  21859.      FontMapEntry : RECORD
  21860.           name:     Str255;     {The name of the font}
  21861.           localID:  INTEGER;    {a unique number for this entry}
  21862.           realID:   INTEGER;    {last time we checked, this font’s font number}
  21863.           useCount: INTEGER;    {How many times this font is used in this document}
  21864.      END;
  21865.  
  21866. In a new document, start out with no entries in the table. When the user changes a
  21867. selection of text to a new font (that is, one whose name is not in the table), add an
  21868. entry to the table. Set useCount to 1 (as this font is now referenced once within the
  21869. document), and pick a localID that is unique within the table. In the text, or wherever
  21870. you would normally keep the font number, store a copy of this localID instead of the
  21871. font number. Use GetFontNumber (the example above) to get the “real” font number, and
  21872. store it in the table as well, in realID.
  21873.  
  21874. Whenever you need to draw text, search through the table for the proper localID. When
  21875. you find it, use the realID that is stored in that entry in a call to TextFont, then
  21876. draw your text as usual.
  21877.  
  21878. Keep the useCount updated, so that you know when to get rid of a table entry. If the
  21879. user deletes a range of text, or changes it to another font, examine the text to see
  21880. if you should decrement any of the useCounts in your table. When a useCount for an
  21881. entry becomes zero, you’ll know that the font for that entry isn’t used anywhere
  21882. within the document, and you can remove the entry from the table.
  21883.  
  21884. When you save the document, save the table with it. When you open an existing document,
  21885. load the table. For each entry, call GetFontNumber using each name, and update the
  21886. realID field with the current font number for that name. If a font isn’t present, you
  21887. should warn the user: You could let the user choose an alternative font, or use the
  21888. default application font by storing (and using) the constant applFont as that font’s
  21889. realID; this way, the user could still edit the document, but the original font name
  21890. would remain, so that when the user adds the proper font to the System file, or moves
  21891. the document to a Macintosh whose System file contains it, the document would be
  21892. displayed as originally intended. 
  21893.  
  21894. The overhead of handling your documents’ fonts in this manner is rather small; as
  21895. more font families become available, and Font/DA Mover’s renumberings occur more
  21896. often, your customers will appreciate this extra effort.
  21897.  
  21898.  
  21899.  
  21900. æKY 192
  21901. æC #192: Surprises in LaserWriter 5.0 and newer
  21902.  
  21903. Revised by:    Mary Burke & Scott “Zz” Zimmerman                  February 1990
  21904. Written by:    Scott “Zz” Zimmerman                                  April 1988
  21905.  
  21906. This Technical Note describes some changes in version 5.0 and later LaserWriter
  21907. drivers. Changes since April 1988:  Described a bug in 5.x which is fixed in 6.0
  21908. and later, and reiterated a warning about storing fonts in an application.
  21909. _______________________________________________________________________________
  21910.  
  21911. With the release of LaserWriter 5.0 and background printing, a few changes had to
  21912. be made to the LaserWriter driver.  Although these changes were transparent to most
  21913. applications, some have had problems.  Most of these problems are related to use of
  21914. unsupported features.  This Note details a partial list of the changes.
  21915.  
  21916.  
  21917. No Mo’ Low
  21918.  
  21919. Because of the problems supporting both the high-level and low-level interfaces in
  21920. the background, the low-level interface is all but removed.  Instead of the low-level
  21921. calls being executed by the device driver, the _PrCtlCall procedure converts the call
  21922. into its high-level equivalent before execution.  This way, the LaserWriter driver has
  21923. a common entry point for both the low-level and high-level interfaces.  Because of
  21924. this conversion, the low-level calls may not be faster than using the high-level
  21925. equivalents.  In some cases, they may even be slower.
  21926.  
  21927. Version 5.x of the LaserWriter driver also contains a bug with the low-level interface.
  21928. If an application which uses the low-level Printing Manager interface encounters an error
  21929. during the course of the print job, the LaserWriter driver crashes before the application
  21930. has a chance to see the error.  Because the error occurs inside the driver, there is no
  21931. way for an application to predict or work around this problem.  The only solution to this
  21932. problem is to use the high-level Printing Manager interface or to upgrade to version 6.0 or
  21933. later of the LaserWriter driver which fixes this bug.
  21934.  
  21935.  
  21936. Are You Convertible?
  21937.  
  21938. Whereas the conversion of the low-level calls should be transparent, the conversion
  21939. routines make some assumptions.  The conversion routines require a context in which
  21940. to operate; the Printing Manager maintains a certain state while executing commands,
  21941. and the conversion routines need access to this state to perform the conversion.  To
  21942. provide this context, an application must have opened a document and a page.  This
  21943. requirement means that the original method of using the low-level interface, which
  21944. is documented in Inside Macintosh, Volume II-164, no longer works, as in the
  21945. following example:
  21946.  
  21947.     PrDrvrOpen;
  21948.     PrCtlCall(iPrDevCtl, lPrReset, 0, 0);
  21949.     { Send data to be printed. }
  21950.     PrCtlCall(iPrDevCtl, lPrPageEnd, 0, 0);
  21951.     PrDrvrClose;
  21952.  
  21953. Instead, an application should use the following:
  21954.  
  21955.     PrDrvrOpen;
  21956.     PrCtlCall(iPrDevCtl, lPrDocOpen, 0, 0);
  21957.     PrCtlCall(iPrDevCtl, lPrPageOpen, 0, 0);
  21958.     { Send data to be printed. }
  21959.     PrCtlCall(iPrDevCtl, lPrPageClose, 0, 0);
  21960.     PrCtlCall(iPrDevCtl, lPrDocClose, 0, 0);
  21961.     PrDrvrClose;
  21962.  
  21963. This method provides the Printing Manager with the context it needs to convert the calls.
  21964.  
  21965.  
  21966. Really Unsupported Features
  21967.  
  21968. Sending data to the printer between the _PrOpenDoc or lPrDocOpen and the
  21969. _PrOpenPage or lPrPageOpen calls is not currently, and has never been supported.
  21970. LaserWriter drivers prior to 5.0 interpreted this data, but 5.0 and later drivers
  21971. ignore it.  To download an application-specific PostScript® dictionary as a header
  21972. with each document, Apple recommends that the application provide a 'PREC' resource 
  21973. of ID = 103, as is described in the LaserWriter Reference.
  21974.  
  21975.  
  21976. A Little Less Control
  21977.  
  21978. Four of the six printer control calls originally supported by the LaserWriter driver
  21979. have been discontinued due to lack of use and difficulty supporting with background
  21980. printing.  The four calls which follow were only supported by the LaserWriter driver
  21981. and only documented in the LaserWriter Reference Manual:
  21982.  
  21983.         °•° fill    °•° hexBuf    °•° printR    °•° printF
  21984.  
  21985. In addition to these calls, the stdBuf call is also affected.  There are two versions
  21986. of the stdBuf call depending upon the sign of the bytes parameter.  If bytes is
  21987. negative, the text passed to the stdBuf call is converted to PostScript text before
  21988. being sent to the LaserWriter.  This conversion means that special PostScript characters
  21989. in the text are preceded by a PostScript escape character.  In addition, characters
  21990. with an ASCII value greater than 128 are converted to octal before being sent to the
  21991. LaserWriter.  This version of the call is no longer supported.
  21992.  
  21993. If the bytes parameter is positive, the text passed to the call is sent directly to
  21994. the LaserWriter without conversion and interpreted as PostScript instructions.  This
  21995. version of the call is still supported, but there is one more problem.  When an 
  21996. application first opens the low-level driver (via_PrDrvrOpen) with background printing
  21997. enabled, no clip region is defined.  If the application then begins sending PostScript
  21998. to the driver via the stdBuf call, all of the output is clipped, and only a blank page
  21999. is printed.
  22000.  
  22001. To prevent this problem, the application must force a clip region to be sent to the
  22002. LaserWriter.  The region is sent by the driver when it receives its first drawing
  22003. command.  Unfortunately, the driver does not consider the stdBuf call to be a drawing
  22004. command.  To force the clip region on the printer, the application can use the
  22005. iPrBitsCtl call to print a small bitmap outside the printable area of the page.
  22006. This call does not have any effect on the document, but it fires the bottleneck
  22007. routine and causes a definition of the clip region.  Since the clip region is
  22008. reinitialized at each call to lPrPageOpen, the application should send the bitmap
  22009. once at the start of each page.  If any other printer control calls precede the stdBuf
  22010. call, the application does not need to send the bitmap.
  22011.  
  22012.  
  22013. Background Preparations
  22014.  
  22015. The 'PREC' ID = 201 mechanism only works when background printing is disabled.  This
  22016. limitation is because of difficulties finding the resource under MultiFinder.  Since
  22017. the option only works in the foreground, and since there is no way for an application
  22018. to know if background printing is enabled, an application should avoid using this feature.
  22019.  
  22020.  
  22021. Fonts In An Application?
  22022.  
  22023. There are two problems when printing application fonts with the LaserWriter driver.
  22024. Application fonts are fonts that are stored in the resource fork of the application’s 
  22025. resource file rather than being stored in the System file.  The first problem occurs
  22026. when the application font has the same name as the application’s resource file.  If this
  22027. is the true, the LaserWriter driver incorrectly assumes that the application’s resource
  22028. file is a font file, and closes it after using the font.  To solve this problem, developers
  22029. should make sure the name of the application font (i.e., the 'FOND' resource) is different
  22030. from the name of the application’s resource file.  Since the application can still be
  22031. renamed by the user, developers should try to select a unique font name.  If the font
  22032. name does not appear in a font menu, developers can simply append the application’s 
  22033. creator string onto the desired font name (i.e.,'MyApplicationFont ZZAP').
  22034.  
  22035. The second problem with application fonts only occurs when background printing is enabled.
  22036. When a print job is performed in the background, the LaserWriter driver writes all of the
  22037. data to be printed into a file (called a spool file) for printing at a later time by Print
  22038. Monitor.  Since the LaserWriter driver has no way of knowing when the file will actually
  22039. be printed, it cannot assume that the application will still be open when the job is printed.
  22040. This is a problem if the application contains application fonts that Print Monitor needs
  22041. at print time.  To solve this problem, the LaserWriter driver must determine whether the
  22042. fonts needed by the application are resident in the System file or in the application file
  22043. .  If they are in the application file, the driver must copy them into the spool file so 
  22044. they are available to Print Monitor.  This practice can lead to very large spool files, 
  22045. as well as a significant loss of performance when background printing is enabled.  To solve 
  22046. these and other user interface problems related to application fonts, Apple strongly 
  22047. recommends that developers ship custom application fonts as suitcase files for the user
  22048. to install in the System file.
  22049.  
  22050.  
  22051. Headin’ For Trouble
  22052.  
  22053. There is a minor bug in version 5.0 of the LaserWriter driver that only affects applications
  22054. that parse the PostScript header downloaded by the driver with each document.  This header
  22055. contains some PostScript comments that provide information about the current job.  One
  22056. of these comments is IncludeProcSet.  This comment takes three arguments:  a PostScript
  22057. dictionary name, a major version number, and a minor version number.  In version 4.0 of
  22058. the LaserWriter driver, the comment line looked like the following:
  22059.  
  22060.     %% IncludeProcSet: (Appledict md) 65 0
  22061.  
  22062. Unfortunately, in version 5.0 of the LaserWriter driver, the last argument was removed.
  22063. This caused the comment line to look like the following:
  22064.  
  22065.     %% IncludeProcSet (Appledict md) 66
  22066.  
  22067. Since Adobe defined the comment to take three arguments, it is reasonable for applications
  22068. that parse the comments to expect three arguments; therefore, version 5.1 and later of the
  22069. LaserWriter driver contain the correct version of the comment:
  22070.  
  22071.     %% IncludeProcSet (Appledict md) 67 0
  22072.  
  22073.  
  22074. No Go With Zero
  22075.  
  22076. Some applications want to force a font to be downloaded to the LaserWriter without
  22077. actually printing characters with the font.  This can be done in three easy steps:
  22078.  
  22079.   1.  Save the current pen position.
  22080.   2.  Use any text drawing routine to draw a space character.
  22081.   3.  Move the pen back to the saved position.
  22082.  
  22083. Some applications use _DrawString with a empty string (e.g., DrawString('')) to force
  22084. the font downloading. Although this worked in LaserWriter drivers up to 5.0, these
  22085. calls are ignored by the 5.1 and later drivers.  The main reasons for this change
  22086. were optimization of performance and a reduction in the size of spool files.
  22087.  
  22088.  
  22089. Further Reference:
  22090. _______________________________________________________________________________
  22091.   •  Inside Macintosh, Volumes II & V, The Printing Manager
  22092.   •  LaserWriter Reference Manual
  22093.  
  22094. PostScript is a registered trademark of Adobe Systems Incorporated.
  22095.  
  22096.  
  22097.  
  22098. æKY 193
  22099. æC #193:    So Many Bitmaps, So Little Time
  22100.  
  22101. Revised by:    Rich Collyer                                        October 1989
  22102. Written by:    Rick Blair                                            April 1988
  22103.  
  22104. This Technical Note discusses the routine BitMapToRegion, which converts a bitmap to
  22105. a region, and is available in the 32-Bit QuickDraw INIT and from Apple Software Licensing.
  22106. Changes since June 1989:  Changed references of BitMapRgn to BitMapToRegion to match
  22107. the updated MPW and 32-Bit QuickDraw usage, added the trap number, and documented how
  22108. and when to use the 32-Bit QuickDraw version versus the object file version.
  22109. _______________________________________________________________________________
  22110.  
  22111. The following routine is now available to convert a bitmap to a region:
  22112.  
  22113. FUNCTION BitMapToRegion(region:RgnHandle; bMap:BitMap): OSErr;
  22114.  
  22115. in C:
  22116.  
  22117. pascal OSErr BitMapToRegion(RgnHandle region, BitMap bMap);
  22118.  
  22119. The region will be built so that all one bits in bMap are inside the region and all
  22120. zero bits are outside of it.
  22121.  
  22122. As with all QuickDraw calls which change a region, BitMapToRegion requires you to
  22123. pass an existing region (originally created by _NewRgn).  If the region cannot be
  22124. built due to an insufficient heap space or a size greater than 32K, then the routine
  22125. will return an appropriate error code and the region will be empty.  If the region
  22126. would have exceeded 32K, the error will be rgnTooBigErr
  22127. (-500).
  22128.  
  22129. This function is useful for a number of situations where you have (or can produce) a
  22130. bitmap representing an area.  You can use _CalcMask to produce such a bitmap.  Once
  22131. you have a region, you can perform region operations (i.e.,
  22132. _PtInRgn, _UnionRgn, or _InsetRgn) or call _DragGrayRgn, for example
  22133.  
  22134. This call is part of the 32-Bit QuickDraw INIT ($A8D7).  If you do not wish to depend
  22135. on 32-Bit QuickDraw, then you can obtain a version of BitMapToRegion in MPW object
  22136. format which can be linked into an MPW program, by contacting Apple Software Licensing:
  22137.  
  22138.             Apple Software Licensing
  22139.             Apple Computer, Inc.,
  22140.             20525 Mariani Avenue, M/S 38-I
  22141.             Cupertino, CA, 95014
  22142.             (408) 974-4667
  22143.             AppleLink:  SW.LICENSE
  22144.  
  22145. If you licensed the older version of this routine, BitMapRgn, contact Software Licensing
  22146. about receiving an updated version.  We recommend you update your application to use
  22147. the new version as soon as possible.
  22148.  
  22149. The new version is now named BitMapToRegion to be consistent with the version in
  22150. 32-Bit QuickDraw and the MPW interfaces.  In addition, BitMapToRegion offers new
  22151. features.  You can now pass a one-bit pixelmap which has been coerced to a bitmap. 
  22152. If you pass a pixelmap which is too large, then you will get a pixmapTooDeepErr (-148)
  22153. error.  You can also pass the portBits of a window, much like you would do with a
  22154. call to _CopyBits.
  22155.  
  22156. There is a potential problem with this routine, since MPW 3.1 include files contain
  22157. information about 32-Bit QuickDraw.  If you want BitMapToRegion to be available on
  22158. all machines, then you must use the object file from Software Licensing.  The problem
  22159. is that when you compile your application with MPW 3.1 or later, the 32-Bit QuickDraw
  22160. version gets preference over the object file.  You must comment out the routine in
  22161. the include files if you want to use the object file.  If you only care about using
  22162. BitMapToRegion on machines running 32-Bit QuickDraw, then you need not do anything.
  22163.  
  22164.  
  22165.  
  22166. æKY 194
  22167. æC #194: WMgrPortability
  22168.  
  22169. See also:     The Window Manager
  22170.               QuickDraw
  22171.               MultiFinder Development Package
  22172.  
  22173. Written by:   Rick Blair        March 1, 1988
  22174. _______________________________________________________________________________
  22175.  
  22176. Where WMgrPort (the Window Manager’s port), MultiFinder, and drawing outside of one’s
  22177. windows will be reconciled.
  22178. _______________________________________________________________________________
  22179.  
  22180.  
  22181. Beware
  22182.  
  22183. Drawing outside of windows from within an application is guaranteed to make that
  22184. application less compatible with future systems. In order to be as MultiFinder compatible
  22185. as possible, draw only in response to an update event or as part of the feedback for
  22186. a user action, i.e. while tracking the mouse. MultiFinder compatibility is just as
  22187. important as HFS compatibility!
  22188.  
  22189. MultiFinder documentation warns against drawing in WMgrPort since the system “owns”
  22190. the desktop and windows of other applications besides your own are drawn within it.
  22191. This note will tell you how and when to draw outside the confines of your own windows
  22192. if you feel that you must.
  22193.  
  22194. In the future the system may provide calls for drawing outside your windows safely.
  22195. When that occurs, the techniques described here may no longer be valid. Nevertheless…
  22196.  
  22197.  
  22198. WMgrPort and GrayRgn
  22199.  
  22200. WMgrPort has its visRgn set to include all active screens. Its clipRgn is initially
  22201. set to “wide open” (the rectangle –32767, –32767, 32767, 32767), although Window
  22202. Manager routines like ClipAbove, etc. will change it. Consider this GrafPort read-only.
  22203. The global variable GrayRgn is a region which is equal to the WMgrPort’s visRgn minus
  22204. the menu bar area.
  22205.  
  22206. Note that you should use GrayRgn, which is the best way to find out the shape, size,
  22207. and coordinates of the screens. You will never have to use the WMgrPort directly, and
  22208. should not call GetWMgrPort under any circumstances.
  22209.  
  22210.  
  22211. Rules
  22212.  
  22213. Only draw to the whole screen/desktop in a “modal” way. This can take the form of a
  22214. brief animation across windows or the visual feedback for dragging from one window to
  22215. another. It is important to know that no other application (including the Finder)
  22216. will draw until you have finished. To guarantee this, you must follow some rules:
  22217.  
  22218. In the case of a drag, you should only draw while the mouse button is down. In the
  22219. case of an animation effect, the drawing should be of brief duration.  All operations
  22220. should conclude with nothing left drawn outside your windows. Under MultiFinder (version
  22221. 1.0 and 6.0 at least) you will be OK if you don’t call GetNextEvent, EventAvail, or
  22222. WaitNextEvent while drawing outside your windows. Use the StillDown function (or the
  22223. WaitMouseUp function) for loops that wait for the mouse button to go up. Remember,
  22224. however, it is only through possible future system-provided calls that you can be
  22225. completely safe from others drawing underneath you.
  22226.  
  22227. Never draw something on the desktop and leave it there. There is no way to tell the
  22228. system that you have drawn on that bit of desktop, so the Finder will draw right over
  22229. you.
  22230.  
  22231.  
  22232. Examples
  22233.  
  22234. The most famous animation effect is the ZoomRect routine. It is used by the Finder to
  22235. draw a series of nested rectangles around an icon that is being opened. The rectangles
  22236. form a progression (zoom) out to where the window for the icon will be placed.
  22237.  
  22238. Another, potentially more interesting, case is where you want to drag something from
  22239. one window to another, perhaps to copy it. This is often done with DragGrayRgn, which
  22240. for this purpose will do the right thing (not call GetNextEvent, etc.).
  22241.  
  22242.  
  22243. How to do these effects
  22244.  
  22245. Use a GrafPort (not a window or the WMgrPort) that covers all the screens. OpenPort
  22246. will set up most of the fields of the GrafPort properly. All you have to do is change
  22247. the visRgn of your port to a copy of GrayRgn and put the GrayRgn’s rgnBBox into your
  22248. portRect. Directly manipulating the visRgn of a window is a no-no under MultiFinder.
  22249.  
  22250. Draw using srcXor mode. This will allow you to erase as you go, by drawing each object
  22251. a second time, also in srcXor mode. You must leave all areas outside your windows
  22252. exactly as you found them.
  22253.  
  22254.  
  22255. WDEFs and MDEFs
  22256.  
  22257. Window and menu definition procedures draw in the current port, which is set to the
  22258. WMgrPort by the Window Manager and the Menu Manager. Note that this means that you do
  22259. not ever have to call GetWMgrPort, as mentioned above. We recommend that you never
  22260. draw into it except from one of these procedures.
  22261.  
  22262.  
  22263.  
  22264. æKY 195
  22265. æC #195: ASP and AFP Description Discrepancies
  22266.  
  22267. See also:     The AppleTalk Manager
  22268.  
  22269. Written by:   Mark Bennett     August 1, 1988
  22270. ________________________________________________________________________________
  22271.  
  22272. The descriptions of the AppleTalk Session Protocol and AppleTalk Filing Protocol
  22273. functions within the body of the AppleTalk Manager chapter are incorrect and conflict
  22274. with those in the Summary of the AppleTalk Manager. This technical note resolves the
  22275. discrepancy.
  22276. ________________________________________________________________________________
  22277.  
  22278. The descriptions of the AppleTalk Session Protocol and AppleTalk Filing Protocol
  22279. functions which are described on pages 534 through 548 of Inside Macintosh Volume V
  22280. conflict with the descriptions in the Summary of the AppleTalk Manager section, pages
  22281. 554 through 559. The descriptions in the Summary of the AppleTalk Manager section are
  22282. correct and should be followed.
  22283.  
  22284. The Summary of the AppleTalk Manager does not, however, present a description of the
  22285. correct meaning of the arrows next to the parameter names in the function descriptions.
  22286. The meaning of the arrows is equivalent to the one given to them in the descriptions
  22287. of other Operating System calls, i.e.:
  22288.  
  22289.    Arrow     Meaning
  22290.  
  22291.      ->      Parameter is passed
  22292.     <-       Parameter is returned
  22293.     <->      Parameter is passed and returned
  22294.  
  22295.  
  22296.  
  22297. æKY 196
  22298. æC #196:    'CDEF' Parameters and Bugs
  22299.  
  22300. Revised by:    David Shayer                                        October 1989
  22301. Written by:    Mark Bennett                                         August 1988
  22302.  
  22303. This Technical Note describes known bugs in the Control Manager which affect control
  22304. definition functions ('CDEF' resources).
  22305. Changes since August 1988:  Updated to reflect known bugs in the posCntl and thumbCntl
  22306. messages and the Control Manager _TrackControl call.
  22307. _______________________________________________________________________________
  22308.  
  22309. The Control Manager chapter of Inside Macintosh, Volume I-309, describes how to write
  22310. a control definition function ('CDEF' resource).  This Note assumes a basic understanding
  22311. of this chapter, specifically of the various messages which are sent in the message
  22312. parameter.
  22313.  
  22314. drawCntl (0) and autoTrack (8)
  22315.  
  22316. When a 'CDEF' is called with either the message drawCntl or autoTrack, it is possible
  22317. for the high word of the param parameter to contain undefined data which could result
  22318. in the failure of routines that rely upon all 32 bits of param being defined.  'CDEF'
  22319. resources should only consider the low word of the param parameter when dealing with
  22320. the drawCntl and autoTrack messages.
  22321.  
  22322. posCntl (5) and thumbCntl (6)
  22323.  
  22324. According to Inside Macintosh, the Control Manager calls a 'CDEF' with the posCntl
  22325. message and the thumbCntl message if an application does custom dragging of an indicator
  22326. (a thumb), but not if it does default dragging.  This is not true.  The Control Manager
  22327. calls a 'CDEF' with the posCntl message if an application does default dragging,
  22328. which is exactly the opposite of the way it is documented.  The 'CDEF' receives the
  22329. thumbCntl message regardless of which type of dragging an application does, however,
  22330. the results are used only for default dragging (they are ignored for custom dragging).
  22331.  
  22332. _TrackControl
  22333.  
  22334. When a user clicks on your control, you normally call _TrackControl, which is supposed
  22335. to return zero if the user does not change the control’s setting or the part code if
  22336. the user does change the setting.  For 'CDEF' resources that implement custom dragging,
  22337. _TrackControl returns zero whether or not the user changes the control’s setting.  To
  22338. work around this problem, you must use another method to find out if the user has
  22339. changed the control’s setting, such as comparing the control’s value before and after
  22340. the call to _TrackControl.
  22341.  
  22342.  
  22343. Further Reference:
  22344. _______________________________________________________________________________
  22345.   •  Inside Macintosh, Volume I-309, The Control Manager
  22346.  
  22347.  
  22348. æKY 197
  22349. æC #197: Chooser Enhancements
  22350.  
  22351. See also:     The Device Manager (Volumes II, IV, and V)
  22352.               The Control Panel (Volume V)
  22353.  
  22354. Written by:   Chris Knepper     August 1, 1988
  22355. ________________________________________________________________________________
  22356.  
  22357. Beginning with version 3.2, the Chooser has been enhanced to provide support for
  22358. additional controls.
  22359. ________________________________________________________________________________
  22360.  
  22361. As stated in Inside Macintosh IV-217, the Chooser communicates with device packages
  22362. as if they were the following function:
  22363.  
  22364.      FUNCTION Device (message, caller: INTEGER;
  22365.           objName, zoneName: StringPtr;
  22366.           p1, p2: LONGINT):  OSErr;
  22367.  
  22368. This function is contained in the device package’s 'PACK' -4096 resource. If bit 17
  22369. in the flags field of this 'PACK' is set, the Device function will receive an initialization
  22370. message when the user selects that device resource file from the Chooser’s window – 
  22371. Device will be called with message = initMsg. This is the first message that device
  22372. packages receive and may be used to set up default configurations. The MPW assembler
  22373. interface for this new message is:
  22374.  
  22375.      initMsg EQU 11
  22376.  
  22377. When the Device function receives initMsg, the objName parameter contains a pointer
  22378. to an array of ControlHandles. For this message (and for the buttonMsg described
  22379. below) the objName parameter is a pointer to a structure as follows:  it begins with
  22380. a size word and is followed by at least 4 ControlHandles. The size is at least 18
  22381. bytes (2 bytes for the size word and 4 bytes each for the handles). More handles may
  22382. be added in the future. The four ControlHandles that have been defined thus far are
  22383. the left and right buttons and the “on” and “off” radio buttons. Their handles appear
  22384. in this order:
  22385.  
  22386.   size   word
  22387.   left   ControlHandle
  22388.   right  ControlHandle
  22389.   on     ControlHandle
  22390.   off    ControlHandle
  22391.   …?
  22392.  
  22393. The Flags bits of the 'PACK' control which buttons are used. Bits 26 and 27 are used
  22394. to indicate whether you use the left and right buttons and are described in the Device
  22395. Manager chapters in Inside Macintosh volumes IV and V.
  22396.  
  22397. Bit 20 tells the Chooser that you are employing the on and off radio buttons. Their
  22398. titles can be something other than “on” and “off”, of course, but we’ll continue to
  22399. use those names since that is what the LaserWriter driver (5.0 or later) calls them.
  22400. In addition to the controls and their titles, a static label (“Background Printing:”
  22401. for the LaserWriter) will be displayed.
  22402.  
  22403. The title strings for these radio buttons are contained in the 'STR ' resources with
  22404. IDs –4089 and –4088 in the device resource file. The string for the label is contained
  22405. in 'STR ' resource –4087.
  22406.  
  22407. The rectangles for these items are defined in the 'nrct' –4096 resource in the device
  22408. resource file.  The Device Manager chapter of Inside Macintosh V-430 describes the
  22409. 'nrct' resources. The third and fourth rectangles position the radio buttons while
  22410. the fifth rectangle positions the label.
  22411.  
  22412. For example,  the Chooser interface pictured below corresponds to the following Rez
  22413. input.
  22414.  
  22415. •••Click on the Illustration button below, and refer to Figure 1.••• 
  22416.  
  22417.      /* label for radio buttons */
  22418.      resource 'STR ' (-4087) {
  22419.           "Turn me:"
  22420.      };
  22421.  
  22422.      /* “off” radio button */
  22423.      resource 'STR ' (-4088) {
  22424.           "Off"
  22425.      };
  22426.  
  22427.      /* “on” radio button */
  22428.      resource 'STR ' (-4089) {
  22429.           "On"
  22430.      };
  22431.  
  22432.      resource 'nrct' (-4096) {
  22433.           { 
  22434.           /* [1] Left Button */
  22435.           {0, 0, 0, 0},
  22436.           /* [2] Right Button */
  22437.           {0, 0, 0, 0},
  22438.           /* [3] on Radio Button */
  22439.           {114, 260, 130, 320},
  22440.           /* [4] off Radio Button */
  22441.           {114, 330, 130, 390},
  22442.           /* [5] Label for Radio Buttons */
  22443.           {114, 177, 130, 250}
  22444.           }
  22445.      };
  22446.  
  22447.  
  22448. If the rectangles for these items are not specified in the 'nrct' resource, then the
  22449. Chooser uses default positions for these items from the Chooser’s private 'nrct'
  22450. resource. These default positions are:
  22451.  
  22452.           /* [3] on Radio Button */
  22453.           {114, 272, 130, 332},
  22454.           /* [4] off Radio Button */
  22455.           {114, 335, 130, 395},
  22456.           /* [5] Label for Radio Buttons */
  22457.           {114, 170, 130, 267},
  22458.  
  22459. In order for the radio buttons to be displayed, the 'PACK' must call ShowControl for
  22460. each of them, and pass in the corresponding ControlHandle. To turn one of the radio
  22461. buttons “on” and the other “off,” the 'PACK' must call SetCtlValue. The best time to
  22462. call these routines to set up the radio buttons is when the 'PACK' receives the initMsg.
  22463. By showing the buttons after you have set their values, you can avoid unnecessary
  22464. redrawing.
  22465.  
  22466. When the user clicks either of the radio buttons, the 'PACK' receives buttonMsg and
  22467. objName contains a pointer to an array of ControlHandles, as explained above. The
  22468. 'PACK' knows which of the radio buttons was clicked through the low-order byte of the
  22469. p2 parameter. This byte contains 3 if the “on” radio button was clicked, or 4 if the
  22470. “off” radio button was clicked. It is the device package’s responsibility to set or
  22471. clear the radio buttons in response to the user’s action.
  22472.  
  22473.  
  22474.  
  22475. æKY 198
  22476. æC #198: Font/DA Mover, Styled Fonts, and 'NFNT's
  22477.  
  22478. See also:     The Font Manager
  22479.               QuickDraw
  22480.               Technical Note #191—Font Names
  22481.  
  22482. Written by:   Bryan Stearns     August 1, 1988
  22483. ________________________________________________________________________________
  22484.  
  22485. Font/DA Mover version 3.8 (shipped with System 6.0) incorporates support for 
  22486. 'NFNT' resources and styled font family members. This note discusses several issues
  22487. concerning the arrangement of font-related resources and their IDs. It also documents
  22488. the proper arrangement of font resources within the font files that Font/DA Mover
  22489. uses. 
  22490. ________________________________________________________________________________
  22491.  
  22492. Most applications will not be affected by the existence of 'NFNT' resources. The
  22493. remainder of this note should be of interest to you only if you are a producer of
  22494. screen fonts, or if your application deals with font resources directly using the
  22495. Resource Manager (for instance, a font editor). For compatibility with future changes
  22496. to the Font Manager, applications should avoid directly referencing 'FONT', 'FOND',
  22497. or 'NFNT' resources with Resource Manager calls; instead, QuickDraw and Font Manager
  22498. calls should be used. The techniques & warnings given in Technical Note #191 still
  22499. apply. 
  22500.  
  22501.  
  22502. Font-related resources
  22503.  
  22504. The Font Manager uses several different kinds of resources:
  22505.  
  22506. Font-family description resources (stored as type 'FOND') contain information generic
  22507. to all point sizes and styles belonging to a particular font family, as well as references
  22508. to each member of that family present in its resource file. These references make up
  22509. the font-association table within the 'FOND'; each entry consists of a resource ID, a
  22510. point size, and style & depth information. Only those resources present in the same
  22511. file as an 'FOND' should be referenced from that 'FOND's font-association table. The
  22512. 'FOND's ID is its family number, and its resource name is the family name.
  22513.  
  22514. Font strike resources (which contain a bit image for a particular point size and
  22515. style of a font) come in two flavors, 'FONT's and 'NFNT's. Font strike resources
  22516. never have resource names.
  22517.  
  22518. 'FONT' resources were the original strike resources. Their IDs are encoded from their
  22519. font family number and point size [the actual equation is font_ID := (128 * family) +
  22520. point_size]. Only families 0 through 255 can be encoded this way: families 256 through
  22521. 511, though encodable, cause the resource ID (a 16-bit signed integer) to become
  22522. negative; this runs the risk of confusion with owned resources such as those owned by
  22523. desk accessories (Apple has reserved these unencodable family numbers for application-based
  22524. fonts; see below.)
  22525.  
  22526. 'NFNT' resources were first supported by the 128K ROM; their internal form is nearly
  22527. identical to that of 'FONT' resources, without the limitations of the resource ID
  22528. encoding scheme: their IDs can be chosen nearly at random (any positive integer greater
  22529. than 1023, to prevent collision with ROM-based NFNTs; also, the ID cannot be used by
  22530. any 'FONT' resource in that resource file). They can also contain multi-bit fonts for
  22531. use with Macintosh II; however, Font/DA Mover version 3.8 does not support the associated
  22532. 'fctb' (font color table) resource type.
  22533.  
  22534. Font family-name resources are also stored as resource type 'FONT', however, the
  22535. resource ID encoding appears as point size zero for that family. Family name resources
  22536. contain no data, but have the family name as the resource name.
  22537.  
  22538. They are not referenced from the family’s 'FOND's font-association table.
  22539.  
  22540. Family-name resources are no longer used if an 'FOND' is present for its family (the
  22541. Font Manager will get the family name from the 'FOND' instead), but were required for
  22542. use with the 64K ROM Font Manager, so we tolerate their presence for the time being;
  22543. a future version of Font/DA Mover may do away with them entirely.
  22544.  
  22545.  
  22546. 'NFNT' and 'FONT' priorities
  22547.  
  22548. When QuickDraw asks the Font Manager for a specific family, point size, style, and
  22549. depth, the Font Manager looks for an 'FOND' for that family. If one is found, the
  22550. Font Manager looks for a font-association table entry for the right point size and
  22551. style (and depth, on Macintosh II). Finding one, It looks for an 'NFNT' with the ID
  22552. stored in that entry. If an 'NFNT' isn’t found, it searches for an 'FONT' instead.
  22553.  
  22554.  
  22555. Thus, no 'NFNT' can ever have the same ID as an 'FONT' in the same resource file.
  22556. Whenever Font/DA Mover must find a number for a new 'NFNT', it calls Unique1ID repeatedly
  22557. until a number is found that is greater than 1023 and that isn’t used by either an
  22558. existing 'FONT' or 'NFNT' in that file. Also, if an 'FONT' is moved into a file that
  22559. happens to contain an 'NFNT' with the 'FONT's intended encoded ID, that 'FONT' will
  22560. be converted to an 'NFNT' and given a new unencoded number as described above.
  22561.  
  22562.  
  22563. Styled fonts
  22564.  
  22565. Normally, when you specify that a style be applied to a font you’ve selected (such as
  22566. Bold with Times), QuickDraw applies an algorithm when drawing characters to give you
  22567. the style. For example, Bold is merely a horizontal bit-doubling effect, which gives
  22568. the font a thicker (but slightly smeared) look. Italic appears as a successively-increasing
  22569. horizontal offset for each scan line of each character.
  22570.  
  22571. On our screens, while these algorithmic effects are not perfect, they are fast, so we
  22572. tolerate their rough edges. Typographically, however, they are horrible; this is
  22573. evidenced when printing on devices capable of higher resolution (like the LaserWriter).
  22574. That’s why we provide separate fonts in the LaserWriter for the common stylistic
  22575. variations.
  22576.  
  22577. Though the printer can represent a particular stylistic variation well, the screen
  22578. cannot. The Font Manager supports the concept of a styled font: a font designed to
  22579. represent a particular style, so that “what you see” will be more like “what you
  22580. get.” 
  22581.  
  22582. How does this work in practice? When QuickDraw asks for Times, 12 point, bold, the
  22583. Font Manager looks through the Times 'FOND's font-association table. It finds the
  22584. entry for a Times 12 plain font resource, but before giving up and telling QuickDraw
  22585. to apply the smearing effect, it looks to see if there is another 12-point entry with
  22586. the style “bold” attribute set. If so, it will return this entry to QuickDraw instead,
  22587. and the algorithmic effect will not be applied.
  22588.  
  22589. This “automatic style substitution” results in more accurate screen rendering, as
  22590. well as improved printer representation (since full justification is usually done by
  22591. applications, which use screen representations for deciding line breaks).
  22592.  
  22593. There’s a catch, though.  How do we store styled fonts? 'FONT' resources use an encoding
  22594. scheme for their IDs that doesn’t include style information; thus, the ID of a Times
  22595. 12 'FONT' would be the same as that of any styled 12-point Times 'FONT'. We must find
  22596. an alternative; as usual, there’s a wrong way and a right way. Read on.
  22597.  
  22598.  
  22599. Fonts that belong to more than one family
  22600. (Styled fonts: the wrong way)
  22601.  
  22602. Because Font/DA Mover hasn’t (until now) supported NFNTs, a less-than-perfect approach
  22603. has been used: the styled members of a font family were given their own family name
  22604. (and hence, their own 'FOND'), but both their own 'FOND' and the “plain” 'FOND' referenced
  22605. the styled members. Thus, one of these “styled 'FOND's” (which often had names prefixed
  22606. with style initials like “BI Helvetica Bold Italic”) contains entries to their members
  22607. marked as “plain”, while the 'FOND' with the plain family name (like “Helvetica”) had
  22608. references to the styled members with the appropriate style, as well as references to
  22609. the other plain members (marked as plain, of course).
  22610.  
  22611. The drawback to this approach is that though the automatic style-substitution did
  22612. work, the styled family name still shows up in Font menus. In some cases, this was
  22613. desirable: the LaserWriter includes a family called “N Helvetica Narrow,” whose members
  22614. are all also members of the “Helvetica” family with the “condensed” QuickDraw style.
  22615. Most applications don’t include the choice of Condensed (or Extended, for that matter)
  22616. in their Style menus, so users have no way of choosing this font indirectly. For this
  22617. reason, Font/DA Mover 3.8 still supports 'FONT's that belong to more than one family;
  22618. however, 'NFNT's are not supported in this manner.
  22619.  
  22620.  
  22621. Styled fonts stored as 'NFNT' resources (the right way)
  22622.  
  22623. Styled fonts should be stored as 'NFNT' resources now that Font/DA Mover supports
  22624. 'NFNT's. There should be one family resource without style information in its name
  22625. (no “B Times Bold”, just “Times”). Along with the other entries for plain (un-styled)
  22626. font resources, its font-association table should contain entries with the appropriate
  22627. QuickDraw style for the styled fonts.
  22628.  
  22629. The style substitution will happen automatically, and Font menus will not be bogged
  22630. down with the alternate styled family names.
  22631.  
  22632.  
  22633. Summary: the correct arrangement of font resources
  22634.  
  22635. If you ship your own font files (either because your product is a font editor or the
  22636. fonts themselves), here are the basic rules that you’ll need to follow to avoid confusing
  22637. Font/DA Mover or the Font Manager. They’ll also help avoid compatibility problems
  22638. with future versions of our system software. 
  22639.  
  22640.  • Each font file should contain one 'FOND' resource per family in that file.
  22641.    Its font-association table should reference each other 'FONT' or 'NFNT'
  22642.    resource belonging to that family in that resource file;  all referenced
  22643.    resources must be present in that file except for system-file 'FOND'
  22644.    references to font resources in ROM.
  22645.  
  22646.  • The font family number should be between 128 and 255 (inclusive) if any
  22647.    members of the family are 'FONT's; if not (that is, all members are 'NFNT's),
  22648.    the range can also include 512 through 16383. Note: These numbers apply to
  22649.    fonts for use with the roman script system; fonts to be used with other
  22650.    script systems (such as Kanji or Arabic) use subranges above 16383. See the
  22651.    Script Manager for more information.
  22652.  
  22653.  • If the family number is 255 or less, a font family-name resource should be
  22654.    present.
  22655.  
  22656.  • Each 'FONT' resource should have its ID encoded using its family number and
  22657.    point size (as described above). 'NFNT' resources must have positive IDs
  22658.    greater than 1023 that do not conflict with any 'FONT' IDs. All styled font
  22659.    resources should be stored as 'NFNT's; of course, plain fonts can also be
  22660.    stored as 'NFNT's.
  22661.  
  22662.  • All of these resources should be marked purgeable (except for those
  22663.    associated with the system font, but Apple will worry about those).
  22664.  
  22665.  • It’s very important to use a Macintosh Plus (or later) system and the latest
  22666.    system software when creating font files for shipment; whatever tools you
  22667.    use should use the Resource Manager to add font resources to your files (as
  22668.    Font/DA Mover does). We have seen cases where a font-producing company
  22669.    shipped resource files that were created on a minicomputer and downloaded to
  22670.    a Macintosh disk; their custom minicomputer tools created corrupt resource
  22671.    maps, and they cannot be correctly modified by the Resource Manager without
  22672.    corrupting them further (Font/DA Mover is able to successfully copy
  22673.    resources from these files to other font files, but if you remove or copy
  22674.    into the files, they become trashed). To be safe, use the latest Font/DA
  22675.    Mover (currently version 3.8) to create a new font file and copy your fonts
  22676.    into it before shipping this new file.
  22677.  
  22678. Also, a few notes about Font/DA Mover itself: 
  22679.  
  22680.  • Font/DA Mover will still create a vanilla 'FOND' whenever it moves an 'FONT'
  22681.    that doesn’t have one; however, be aware that it does this only for families
  22682.    that consist solely of 'FONT's and have a family-name resource; if you want
  22683.    your font editor to create fonts in 'NFNT' resources, you’ll have to provide
  22684.    a 'FOND' for them! Likewise, if you’re a provider of finished fonts, and you
  22685.    want to ship them as 'NFNT's, you must provide a 'FOND' for each family with
  22686.    an 'NFNT' in it (or Font/DA Mover and the Font Manager won’t be able to find
  22687.    it!)
  22688.  
  22689.  • Older versions of Font/DA Mover were less reliable than version 3.8;
  22690.    likewise, the next version will probably be more reliable than this one.
  22691.    Always use the latest Font/DA Mover (in fact, the latest system software is
  22692.    generally the safest!): stress this to your users in the documentation
  22693.    accompanying your font files or font editor. Of course, if you ship 'NFNT'
  22694.    fonts, Font/DA Mover 3.8 (or later) is required!
  22695.    Contact Apple’s Software Licensing department (AppleLink: SW.LICENSE,
  22696.    MCI: 312-5360, or (408) 973-4667) for information on licensing Font/DA Mover.
  22697.  
  22698.  
  22699.  • Up to (and including) version 3.8, Font/DA Mover has never tampered with
  22700.    files without the user explicitly choosing to “Copy” into them or “Remove”
  22701.    from them; that is, all of the conversions, renumbering, etc, are done in
  22702.    the process of moving from one file to another. In the future, there may be
  22703.    commands for fixing or optimizing font files in place. If you want to update
  22704.    your old font files, move them into a new file with the latest Font/DA Mover.
  22705.  
  22706.  
  22707. The future
  22708.  
  22709. A future version of Font/DA Mover may convert 'FONT's to 'NFNT's as it moves them,
  22710. and ignore family-name resources altogether. There are two good reasons to do this:
  22711. there’s a performance advantage in only having 'FOND's and 'NFNT's, because AddResMenu
  22712. (used to build Font menus) must currently index through all 'FONT's looking for those
  22713. that might be family-name resources, and this is slow. Also, the limited range of
  22714. family numbers that can be encoded (128-255) doesn’t apply to fonts consisting only
  22715. of 'NFNT's, which can use the range 512 through 16383.
  22716.  
  22717.  
  22718.  
  22719. æKY 199
  22720. æC #199: KillNBP Clarification
  22721.  
  22722. See also:     The AppleTalk Manager
  22723.  
  22724. Written by:   Mark Bennett     August 1, 1988
  22725. ________________________________________________________________________________
  22726.  
  22727. This technical note clears up some confusion regarding the Name Binding Protocol
  22728. KillNBP function.
  22729. ________________________________________________________________________________
  22730.  
  22731. The description of the PKillNBP function on page 519 of Inside Macintosh Volume V is
  22732. somewhat confusing. The data type of the parameter thePBptr is incorrectly given as
  22733. ATPPBPtr and the pointer to the queue element from the NBP call to be aborted is
  22734. incorrectly given as being passed in aKillQEl. The following is a correct description
  22735. of the KillNBP function:
  22736.  
  22737.  
  22738. KillNBP function
  22739.  
  22740. FUNCTION PKillNBP (thePBptr: MPPPBPtr; async: BOOLEAN) : OSErr;
  22741.  
  22742.    Parameter block
  22743.  
  22744.       -->    26   csCode      word      Always PKillNBP
  22745.       -->    28   nKillQEl    pointer   Pointer to queue element
  22746.  
  22747.  
  22748.  
  22749. æKY 200
  22750. æC #200: MPW 2.0.2 Bugs
  22751.  
  22752. Written by:     Dave Burnard      August 1, 1988
  22753. Modified by:    Andy Shebanow     October 1, 1988
  22754. _______________________________________________________________________________
  22755.  
  22756. This Technical Note describes latest information about bugs or unexpected 
  22757. “features” in the MPW C, Pascal, and Assembler products and the Toolbox and OS Interface
  22758. Libraries.  We intend this Note to be a complete list of all known bugs in these
  22759. products, which will be updated as old bugs are fixed, or new ones appear.  If you
  22760. have encountered a bug or unexpected feature which is not described here, be sure to
  22761. let us know.  Specific code examples are useful.
  22762.  
  22763. The bugs described in the October 1 revision of this Note will be fixed in the 3.0
  22764. release of MPW scheduled for Fall 1988.
  22765.  
  22766. Changes since August 1, 1988:  Corrected the description of “bug” #3 under MPW C as
  22767. it is not a bug according to the definition of the C language and corrected an error
  22768. in bug #2 of the Interface Libraries concerning the glue for _SlotVInstall and _SlotVRemove.
  22769. _______________________________________________________________________________
  22770.  
  22771.  
  22772. C Language
  22773.  
  22774. The following information applies to the C compiler and its associated libraries
  22775. shipped with the 2.0.2 version of MPW.
  22776.  
  22777. 1) A series of bugs involving floating point array elements and the +=, *=, and =
  22778. operators.  A similar bug was reported as fixed in MPW 2.0.2, unfortunately the fix
  22779. did not apply to array elements.  This bug ONLY occurs when using SANE in combination
  22780. with float or double variables, it does not occur if the -mc68881 compiler option is
  22781. specified or if extended variables are used.  The following fragment illustrates the
  22782. bugs:
  22783.  
  22784.    main()
  22785.    {
  22786.        double x[2],y;  /* Also fails if x,y are declared float
  22787.                       but succeeds if declared extended */
  22788.        x[0] = 0.5;
  22789.        y = 5.0;
  22790.        x[0] += 2.0*y;
  22791.        printf("x[0] = %f\n", x[0]);
  22792.   
  22793.        x[0] = 0.5;
  22794.        y = 5.0;
  22795.        x[0] = x[0] + 2.0*y;
  22796.        printf("x[0] = %f\n", x[0]);
  22797.   
  22798.        x[0] = 0.5;
  22799.        y = 5.0;
  22800.        x[0] *= 2.0*y;
  22801.        printf("x[0] = %f\n", x[0]);
  22802.   
  22803.        x[0] = 0.5;
  22804.        y = 5.0;
  22805.        x[0] = x[0]*(2.0*y);     /* Succeeds if parenthesis are removed */
  22806.        printf("x[0] = %f\n", x[0]);
  22807.   
  22808.        exit(0);
  22809.  
  22810.  
  22811. This code fragment returns the erroneous values 0.5, 0.5, 0.5, 0.5 (the correct values
  22812. are 10.5,10.5, 5.0 and 5.0).
  22813.  
  22814. WORKAROUND:  If using SANE, use extended variables in these situations.
  22815.  
  22816. 2) Taking the address of a floating point formal parameter (function argument) to a
  22817. function fails.  This bug occurs when using either SANE or the -mc68881 compiler
  22818. option in combination with float or double function arguments, it does not occur if
  22819. the function arguments are declared extended. The following fragment illustrates two
  22820. instances of this bug:
  22821.  
  22822.      #include <Types.h>
  22823.      #include <Math.h>
  22824.      #include <stdio.h>
  22825.      
  22826.      #define real float     /* Fails with either float or double */
  22827.      
  22828.      main()
  22829.      {
  22830.           Bug1(1.0, 2.0, 3.0);
  22831.           Bug2(1.0, 2.0, 3.0);
  22832.      }
  22833.      
  22834.      Bug1 (x, y, z)
  22835.           real x, y, z;
  22836.      {
  22837.           real *p, *q, *r;
  22838.      
  22839.           /* Take address of arguments, assign directly */
  22840.           p = &x; q = &y; r = &z;
  22841.      
  22842.           fprintf(stderr, "Example 1: Before: %g, %g, %g\n", x, y, z);
  22843.           *p = 11.0;
  22844.           *q = 12.0;
  22845.           *r = 13.0;
  22846.           fprintf(stderr, "Example 1: After:  %g, %g, %g\n", x, y, z);
  22847.      }
  22848.      
  22849.      Bug2(x, y, z)
  22850.           real x, y, z;
  22851.      {
  22852.           fprintf(stderr, "Example 2: Bug2 Before: %g, %g, %g\n", x, y, z);
  22853.      
  22854.           /* Take address of arguments, assign indirectly */
  22855.           foo(&x, &y, &z);
  22856.           fprintf(stderr, "Example 2: Bug2 After:  %g, %g, %g\n", x, y, z);
  22857.      }
  22858.      foo(x, y, z)
  22859.           real *x, *y, *z;
  22860.      {
  22861.           fprintf(stderr, "Example 2: foo Before: %g, %g, %g\n", *x, *y, *z);
  22862.           *x = 11.0;
  22863.           *y = 12.0;
  22864.           *z = 13.0;
  22865.           fprintf(stderr, "Example 2: foo After:  %g, %g, %g\n", *x, *y, *z);
  22866.      }
  22867.  
  22868. This is, in fact, a general problem with C compilers.  The underlying reason for this
  22869. problem is related to the automatic widening and narrowing of basic types performed
  22870. by C compilers. For instance in C, as defined by K&R (The C Programming Language,
  22871. Kernighan & Ritchie, 1978, Appendix A Sec. 7.1, p186 and Sec. 10.1, p206), variables
  22872. of type char and short are widened to int before being passed as function arguments
  22873. and narrowed before use inside the function.  Similarly, the floating point type
  22874. float is automatically widened to double before being passed.  K&R notes, however,
  22875. that “C converts all float actual parameters to double, so formal parameters declared
  22876. float have their declarations adjusted to read double.”  The value of such a formal
  22877. parameter is not narrowed to the declared type of the parameter, instead the declared
  22878. type is adjusted to match that of the widened value.  So, in fact, the sample code
  22879. above will fail if real is defined as float, even on a bug free K&R conforming compiler.
  22880.  
  22881. In MPW C, where float and double are widened to extended, the sample code fails for
  22882. either float or double formal parameters.  This can, of course, lead to additional
  22883. problems if you are porting code from an environment where double was the widened
  22884. floating point type (where taking the address of a double formal parameter would work
  22885. as expected).
  22886.  
  22887. WORKAROUND:  Taking the address of a function argument is not recommended; you should
  22888. make a local copy in a temporary variable and take the address of the copy. If you
  22889. must take the address of a floating point function argument, make sure it is declared
  22890. as type extended, and the pointer is of type extended*.
  22891.  
  22892.  
  22893. 3) The shift operators >> and << can sometimes produce unexpected results when applied
  22894. to unsigned short and unsigned char operands.  The anomaly lies in the fact that the
  22895. 680x0 LSR.L and LSL.L instructions are used instead of the LSR.W and LSL.W or the
  22896. LSR.B and LSL.B instructions.  The following example illustrates this anomaly:
  22897.  
  22898.      main()
  22899.      {
  22900.           unsigned short u, c;
  22901.           short i, k;
  22902.           
  22903.           u = 0xFFFF;
  22904.           k = 8;
  22905.           
  22906.           i = (u << k)/256;
  22907.           printf("unsigned short: i = %d, %#x\n", i, i);
  22908.      
  22909.           c = 256;
  22910.           i = (u << k)/c;
  22911.           printf("unsigned short: i = %d, %#x\n", i, i);
  22912.      }
  22913.  
  22914. This code fragment returns the values -1, 0xFFFFFFFF and -1, 0xFFFFFFFF, which are
  22915. the correct values as defined by the C language, however, you might be expecting 255,
  22916. 0xFF and 255, 0xFF.
  22917.  
  22918.  
  22919. 4) The compiler optimization flags -q (sacrifice code size for speed) and -q2 (memory
  22920. locations won’t change except by explicit stores) can produce incorrect code. We have
  22921. several vague descriptions of problems, and are looking for more specific examples. 
  22922. The following example illustrates a specific bug that occurs when using enumerated
  22923. types:
  22924.  
  22925.     badfunc(bool, i)
  22926.          Boolean *bool;
  22927.          int i;
  22928.     {
  22929.          while (true) {
  22930.               if(i < 3) {
  22931.                    *bool  = true;
  22932.                    if (func(1)) return;
  22933.               }
  22934.                if (func(i)) return;
  22935.          }
  22936.     }
  22937.  
  22938. The enumerated type here is the type used to define the values true and false in the
  22939. header file Types.h.  The optimizer is apparently confused by the fact that true has
  22940. the char value 1, which it thinks is the same as the int value 1 to be passed to the
  22941. function func(). The object code produced for the two calls to the function func()
  22942. is:
  22943.  
  22944.     ...
  22945.     MOVEQ     #$01,D3    ; Move the constant 1 into a register
  22946.     ...
  22947.     MOVE.B    D3,(A2)    ; Assign "true" to variable bool
  22948.     MOVE.B    D3,-(A7)   ; Attempt to push integer 1 onto stack
  22949.                          ; ERROR should be MOVE.L !!!!!
  22950.     JSR       *+$0002    ; JSR to func
  22951.     ...
  22952.     MOVE.L    D4,-(A7)   ; Correctly push integer variable i onto stack
  22953.     JSR       *+$0002    ; JSR to func
  22954.     ...
  22955.  
  22956. In the first function call, the int constant 1 is passed as a byte value! Since the
  22957. stack is correctly adjusted after the first call this error may go undetected (except
  22958. that the called function may spot the resulting nonsensical parameter).  This problem
  22959. is, of course, not limited to the enumerated type defining true and false, but can
  22960. occur as a side effect of any of the many enumerated types defined in the Toolbox
  22961. header files.
  22962.  
  22963. WORKAROUND: The best solution, for now, is to avoid using the optimization flags -q
  22964. and -q2 altogether.
  22965.  
  22966. 5) The compiler flag -mc68020 (generate MC68020 instructions) generates inconsistent
  22967. code when passing structures by value.  Specifically, structures larger than 4 bytes
  22968. that are not a multiple of 4 bytes are padded out to the nearest 4 byte multiple
  22969. before being pushed onto the stack by the calling routine.  Unfortunately, the called
  22970. routine does not take this padding into account when accessing its function arguments.
  22971. The following example illustrates this bug:
  22972.  
  22973.   #include <Types.h>
  22974.   #include <strings.h>
  22975.   
  22976.   typedef struct         /* 6 byte long structure */
  22977.   {
  22978.     short Temp1;
  22979.     short Temp2;
  22980.     short Temp3;
  22981.   } TestStruct;
  22982.   
  22983.   main()
  22984.   {
  22985.     TestStruct reply;
  22986.   
  22987.     foo(reply,"Hello world.\n");
  22988.   }
  22989.   
  22990.   
  22991.   foo(tst ,str)
  22992.     TestStruct tst;
  22993.     char *str;
  22994.   {
  22995.     Debugger();     /* So we can look, before stepping off the cliff */
  22996.     printf("%s",str);
  22997.   }
  22998.  
  22999. Since function arguments are pushed onto the stack in left to right order in C, the
  23000. pointer to the string constant "Hello world.\n" is pushed onto the stack before the
  23001. padded contents of the reply structure.  Thus when the called function, foo(), goes
  23002. to fetch the argument str, it looks at the location just beyond where the argument
  23003. tst is located. Unfortunately, since the called function does not know the structure
  23004. argument was padded, it will not find the correct value for the second argument (or
  23005. in the general case, any arguments following the structure).
  23006.  
  23007. WORKAROUND: When using the -mc68020 compiler option, either don’t pass structures
  23008. larger than 4 bytes by value, or make sure all structures larger than 4 bytes are
  23009. padded out to a multiple of 4 bytes.
  23010.  
  23011.  
  23012. 6) Switch statements with non-integer controlling expressions can fail.  The following
  23013. example illustrates the problem:
  23014.  
  23015.    #include <stdio.h>
  23016.    #include <strings.h>
  23017.    main()
  23018.    {
  23019.        unsigned short w = 1;
  23020.        
  23021.        printf("The following test fails in MPW\n");
  23022.        printf("Should switch to 1; actually got to ");
  23023.        
  23024.        switch (w) {
  23025.            case 1:
  23026.                printf("1\n");
  23027.                break;
  23028.            case 5:
  23029.                printf("5\n");
  23030.                break;
  23031.            case 32771:
  23032.                printf("32771\n");
  23033.                break;
  23034.            case 32773:
  23035.                printf("32773\n");
  23036.                break;
  23037.            default:
  23038.                printf("default\n");
  23039.                break;
  23040.        }
  23041.    }
  23042.  
  23043. In this example, instead of reaching case 1: in the switch statement, the default:
  23044. case is reached due to the fact that the compiler generates comparison instructions
  23045. based on word length values. The 1st Edition of K&R (The C Programming Language,
  23046. Kernighan & Ritchie, 1978, Appendix A Sec. 9.7, p202) requires all case constants to
  23047. have integer type and the controlling expression to be coerced to integer type.  The 
  23048. 2nd Edition (ANSI) of K&R (The C Programming Language, Kernighan & Ritchie, 1988,
  23049. Appendix A Sec. 9.4, p223) requires that the controlling expression and all case
  23050. constants undergo “integral promotion”—promotion to int (or to unsigned int if necessary).
  23051.  MPW 2.0.2 C fails to promote either the controlling expression or the case constants
  23052. to type int.
  23053.      
  23054. WORKAROUND: If the controlling expression is manually coerced to type int this example
  23055. functions correctly.
  23056.  
  23057.  
  23058. 7) Variable declarations inside the body of switch statements, when combined with the
  23059. -g compiler flag, can cause the compiler’s code generator to abort with the message:
  23060. “Code generator abort code 615.”  The following example illustrates the problem:
  23061.  
  23062.    foo(i)
  23063.        int i;
  23064.   {
  23065.        switch (i) {
  23066.            int j;   /* VARIABLE DECLARATION INSIDE BODY OF SWITCH */
  23067.             
  23068.             
  23069.            case 0:
  23070.                j = 22 +i;
  23071.                printf("INSIDE: i=%d, j=%d\n", i, j);
  23072.                break;
  23073.            case 1:
  23074.                j = 57 -i;
  23075.                printf("INSIDE: i=%d, j=%d\n", i, j);
  23076.                break;
  23077.            default:
  23078.                j = i;
  23079.                printf("INSIDE: i=%d, j=%d\n", i, j);
  23080.                break;
  23081.        }
  23082.    }
  23083.  
  23084. While such a declaration is perfectly legal C, it is a bad practice (The C Programming
  23085. Language, Kernighan & Ritchie, 1978, Appendix A Sec. 9.2, p201). K&R go on to point
  23086. out that if the declaration of the variable contains an initializer, the initialization
  23087. will NOT take place.  This is also the ANSI draft C standard’s interpretation.
  23088.  
  23089. WORKAROUND: Since the -g option is a very useful debugging option, move the variable
  23090. declaration outside the body of the switch statement.
  23091.  
  23092. 8) Compatibility Note:  Local variable declarations of the form char s[]; (as an
  23093. array of unspecified length), may not behave as expected. Pre-ANSI compilers often
  23094. expand such declarations into the equivalent of an extern declaration. MPW 2.0.2 C,
  23095. and most modern compilers, treat this construct as the declaration of a new array,
  23096. thus overriding any global declaration with the same name. In the following example
  23097.  
  23098.      /* From file foo.c */
  23099.      char s[255];
  23100.      
  23101.      main()
  23102.      {
  23103.           strcpy(s, "This is one heck of a mess");
  23104.           otherfunc();
  23105.      }
  23106.      
  23107.      
  23108.      /* From file bar.c */
  23109.      otherfunc()
  23110.      {
  23111.           char s[];
  23112.           
  23113.           printf("%s\n", s);
  23114.      }
  23115.  
  23116. garbage is printed.  As a local variable declaration, this declaration is incomplete
  23117. since no length is specified or implied, so an ANSI C compiler will of course fail. 
  23118. This is obviously not a recommended programming practice, but if you are porting old
  23119. C code you may encounter this usage.
  23120.  
  23121. WORKAROUND: ALWAYS use the declaration extern char s[]; instead.
  23122.  
  23123. 9) Another instance where declarations of the form type s[]; (as an array of unspecified
  23124. length) may not behave as expected, is as a member of a struct or union. Pre ANSI
  23125. compilers often expand such declarations into the equivalent of a pointer declaration.
  23126. This construct is explicitly prohibited in ANSI C structures and unions. MPW 2.0.2 C
  23127. on the other hand, issues no warning and treats this construct as the declaration of
  23128. an array of length zero, which occupies no space in the struct. In the following
  23129. example
  23130.  
  23131.      typedef struct  ST1  {
  23132.              int array1[];     /* Zero length array or Ptr to array? */
  23133.              int array2[];
  23134.              int array3[];
  23135.      }ST1;
  23136.      
  23137.      main()
  23138.      {
  23139.           ST1 s1;
  23140.           int i1, i2, i3;
  23141.           
  23142.           i1 = s1.array1[0];
  23143.           i2 = s1.array2[0];
  23144.           i3 = s1.array3[0];
  23145.      }
  23146.  
  23147. the three fields of the struct ST1 are located at the same memory location, and the
  23148. assignments shown will actually copy garbage into the integers, since no space was
  23149. allocated for even the first element of the arrays.  Unfortunately, structures containing
  23150. an array of unspecified or zero length as the final member are sometimes used to
  23151. indicate a structure containing a variable length array. While this may be useful, it
  23152. is not tolerated  by ANSI C and thus is not a recommended programming practice. However,
  23153. if you are porting old C code you may encounter this usage.
  23154.  
  23155. WORKAROUND: ALWAYS use the declarations of the form type *s;, type (*s)[];, or type
  23156. s[1] (depending on the intended meaning) in structures and unions instead.
  23157.  
  23158. 10) The routines _SetUpA5 and _RestoreA5 described in the OS Utilities Chapter of
  23159. Inside Macintosh, Volume II, are missing.  Refer to Technical Note #208:  Setting and
  23160. Restoring A5 for two new routines which solve this problem.
  23161.  
  23162. 11) Hint: Switch statements with large numbers of cases (over 100 or so) can trigger
  23163. the appearance of the MPW Bulldozer cursor (signaling heap purging and compacting in
  23164. progress), can cause “Out of memory” errors from the compiler, or at least take a
  23165. very long time to compile.
  23166.  
  23167. WORKAROUND: Large switch statements can be split up into smaller ones to avoid these
  23168. symptoms.
  23169.  
  23170.  
  23171. Pascal Language
  23172.  
  23173. The following information applies to the Pascal compiler and its associated libraries
  23174. shipped with the 2.0.2 version of MPW.
  23175.  
  23176. 1) The Pascal compiler generates incorrect code when accessing the elements of packed
  23177. Boolean arrays with more than 32767 elements. The generated code contains an ASR.W
  23178. instruction to compute the byte offset into the array, this of course fails for numbers
  23179. larger than 32767 ($7FFF). The following example, which zeroes bits far beyond the
  23180. end of the array itself, illustrates the error:
  23181.  
  23182.   PROGRAM PackArrayBug;
  23183.   
  23184.        VAR
  23185.          MyBits:     packed array[0..33000] of Boolean;
  23186.   
  23187.          PROCEDURE BadCode;
  23188.          VAR
  23189.             i: longint;
  23190.          BEGIN
  23191.             for i := 0 to 33000 do
  23192.               MyBits[i] := false;
  23193.          END;
  23194.   
  23195.        BEGIN
  23196.             BadCode
  23197.        END.
  23198.  
  23199. WORKAROUND: Don’t use packed Boolean arrays with more than 32767 elements.
  23200.  
  23201. 2) The Pascal compiler fails to detect the situation where a procedure name is passed
  23202. as an argument to a routine expecting a function as an argument. The following example
  23203.  illustrates the error:
  23204.  
  23205.    PROGRAM FuncArgBug;
  23206.   
  23207.         PROCEDURE ExpectsFunc(x: integer; function Visit(y1: longint;
  23208.                 y2: char;): Boolean);
  23209.         VAR
  23210.            result: Boolean;
  23211.            yc: char;
  23212.         BEGIN
  23213.            yc := 'A';
  23214.            result := Visit(x, yc);
  23215.         END;
  23216.   
  23217.         PROCEDURE FormalProc(y1: longint; y2: char;);
  23218.         BEGIN
  23219.            writeln(y1: 1, ' ', y2);
  23220.         END;
  23221.   
  23222.       BEGIN
  23223.            ExpectsFunc(5, FormalProc);
  23224.       END.
  23225.  
  23226. This type of problem typically leads to stack misalignment and spectacular crashes.
  23227.  
  23228. WORKAROUND: Make certain that Pascal routines expecting functions as arguments are
  23229. indeed passed functions.
  23230.  
  23231. 3) The -mc68881 option causes the Pascal compiler to generate incorrect code when
  23232. calling external C language routines with floating point extended arguments. Apparently
  23233. the compiler miscalculates the size of the extended argument so that it incorrectly
  23234. removes the arguments from the stack. The following example, which can corrupt the
  23235. local variables of its caller, illustrates the error:
  23236.  
  23237.   FUNCTION E2S(f: Str255; x: extended): Str255;
  23238.      VAR
  23239.        dummy: integer;
  23240.        i:      integer;
  23241.        t:      Str255;
  23242.                  
  23243.     FUNCTION sprintf(str: Ptr; fmt: Ptr; x: extended): integer; C; EXTERNAL;    
  23244.  
  23245.   
  23246.     BEGIN
  23247.        t[0] := chr(0);
  23248.        f[ord(f[0]) +1] := chr(0);
  23249.   
  23250.        dummy := sprintf(@t[1], @f[1], x);
  23251.   
  23252.        i := 0;
  23253.        repeat
  23254.          i := i+1;
  23255.        until ((t[i] = chr(0)) or (i > 254));
  23256.        t[0] := chr(i);
  23257.   
  23258.        E2S := t;
  23259.     END.
  23260.  
  23261. The relevant portions of the generated code are:
  23262.  
  23263.   LINK       A6,#$FDF0     ; LINK for local vars
  23264.   MOVEM.L    D6/D7,-(A7)   ; Save Registers used here
  23265.   ...
  23266.   ...
  23267.   LEA        $FF00(A6),A0  ; Get address of extended var
  23268.   MOVE.L     -(A0),-(A7)   ; Push extended onto stack
  23269.   MOVE.L     -(A0),-(A7)
  23270.   MOVE.L     -(A0),-(A7)
  23271.   PEA        $FF01(A6)     ; Push address of format str
  23272.   PEA        $FDF1(A6)     ; Push address of target str
  23273.   JSR        *+$0002       ; JSR to sprintf
  23274.   LEA        $0012(A7),A7  ; Pop args off stack 
  23275.                            ; (SHOULD pop off $14 bytes!)
  23276.   MOVE.W     D0,D6         ; Save function result
  23277.   ...
  23278.   ...
  23279.   MOVEM.L    (A7)+,D6/D7   ; Restore registers used here
  23280.                            ;  (Now they've been corrupted!)
  23281.   UNLK       A6            ; Correctly restores A7
  23282.   MOVEA.L    (A7)+,A0
  23283.   ADDQ.W     #$8,A7
  23284.   JMP        (A0)          ; JUMP back to caller
  23285.  
  23286. Notice that this code would have succeeded if the routine had not used the D6 and D7
  23287. registers for storage, and then restored them (incorrectly) before returning.
  23288.  
  23289. WORKAROUND: When calling such a routine with the -mc68881 option, isolate the call in
  23290. a small subroutine or function that has no local variables, so that registers will
  23291. not need to be saved and restored.
  23292.  
  23293. 4) The {$SC+} compiler flag for short circuiting AND and OR operators can sometimes
  23294. produce incorrect code. The following example does not work if {$SC+} has been used:
  23295.  
  23296.   USES
  23297.     Memtypes,QuickDraw,OSIntf,ToolIntf,PackIntf;
  23298.      
  23299.   VAR
  23300.     b1,b2,b3 : BOOLEAN;
  23301.   
  23302.   Begin
  23303.     b1 := false;
  23304.     b2 := true;
  23305.     b3 := true;
  23306.   
  23307.     if not (b1 and b2) and b3 then
  23308.         SysBeep(40);
  23309.   End.
  23310.  
  23311. WORKAROUND: Don’t use the {$SC+} compiler flag.
  23312.  
  23313. 5) The Pascal compiler generates incorrect overflow checking code for longint valued
  23314. arguments to the Odd function. The generated code contains a signed divide (DIVS) by
  23315. 1 followed by a TRAPV, thus the overflow flag is set for values greater than $7FFF.
  23316. The following example will fail with a CHK exception, unless the {$OV+} directive is
  23317. removed:
  23318.  
  23319.   {$OV+}
  23320.   PROGRAM PascalOdd;
  23321.   
  23322.        VAR
  23323.             IsOdd:     Boolean;
  23324.             longval:   LONGINT;
  23325.   
  23326.        BEGIN
  23327.             longval := 123456;
  23328.             IsOdd := Odd(longval);
  23329.        END.
  23330.  
  23331. Workaround: Don’t use the {$OV+} compiler flag if you pass longint values to the Odd
  23332. function.
  23333.  
  23334. 6) The Pascal compiler generates incorrect code when functions with RECORD type are
  23335. used as the object of a WITH statement.  This will only occur if the function is
  23336. called at a level below the main program, and if the length of the RECORD type is 4
  23337. bytes or less.  The generated code often contains an LEA (A7)+,an instruction, which
  23338. is of course illegal. The following example demonstrates this unusual situation:
  23339.  
  23340.   PROGRAM PasRecordValFuncBug;
  23341.   
  23342.     TYPE
  23343.       OurRecord   =
  23344.         RECORD
  23345.           a:    Integer;
  23346.         END;
  23347.   
  23348.     FUNCTION RecordValuedFunction: OurRecord;
  23349.     BEGIN
  23350.     END;
  23351.   
  23352.     PROCEDURE ContainsBadCode;
  23353.   
  23354.     BEGIN
  23355.       WITH RecordValuedFunction DO BEGIN  { This usage bad code. }
  23356.         a:=a;
  23357.       END;
  23358.     END;
  23359.   
  23360.   BEGIN { PasRecordValFuncBug }
  23361.     ContainsBadCode;
  23362.     WITH RecordValuedFunction DO BEGIN  { This usage is okay. }
  23363.       a:=a;
  23364.     END;
  23365.   END. { PasRecordValFuncBug }
  23366.  
  23367. WORKAROUND: Don’t use RECORD valued functions as the object of WITH statements.
  23368.  
  23369.  
  23370. Assembly Language
  23371.  
  23372. The following information applies to the Assembler and its associated libraries shipped
  23373. with the 2.0.2 version of MPW.
  23374.  
  23375. 1) There are no known outstanding bugs in the MPW Assembler.
  23376.  
  23377.  
  23378. Interface Libraries
  23379.  
  23380. The following information applies to the Toolbox and OS interface libraries shipped
  23381. with the 2.0.2 version of MPW.
  23382.  
  23383. 1) The glue for the Device Manager call GetDCtlValue [Not in ROM] described in Inside
  23384. Macintosh, Volume III, is incorrect and will return an incorrect value for the handle
  23385. to the driver’s device control entry.  The following is a corrected version of the
  23386. erroneous glue found in the library file Interface.o:
  23387.  
  23388.   ;-----------------------------------------------------
  23389.   ;FUNCTION GetDCtlEntry(refNum: Integer) : DCtlHandle;
  23390.   ;-----------------------------------------------------
  23391.   GetDCtlEntry PROC EXPORT
  23392.       MOVEA.L   (SP)+,A0        ;Get the return address
  23393.       MOVE.W    (SP)+,D0        ;Get the refNum
  23394.       ADDQ.W    #$1,D0          ;Change to a
  23395.       NEG.W     D0              ;  Unit Number
  23396.   ;===>     LSR.W     #$2,D0    ;==Shift in wrong direction!
  23397.       LSL.W    #$2,D0           ;Times 4 bytes/entry
  23398.       MOVEA.L   UTableBase,A1   ;Get address of unit table
  23399.       MOVE.L    (A1,D0.W),(SP)  ;Get the DCtlHandle
  23400.       JMP       (A0)            ;And go home
  23401.  
  23402. This error will affect C, Pascal, and assembly language users. 
  23403.  
  23404. WORKAROUND: Use the corrected glue for GetDCtlValue.
  23405.  
  23406. 2) The glue for the register-based Vertical Retrace Manager calls _SlotVInstall and
  23407. _SlotVRemove described in Inside Macintosh, Volume V, is incorrect. The following are
  23408. corrected versions of the erroneous glue for these routines found in the library file
  23409. Interface.o:
  23410.  
  23411.   ;-----------------------------------------------------------------------
  23412.   ;FUNCTION SlotVInstall(vblTaskPtr: QElemPtr; theSlot: Integer): OSErr;
  23413.   ;-----------------------------------------------------------------------
  23414.   SlotVInstall PROC EXPORT
  23415.       MOVEA.L    (A7)+,A1    ; save return address
  23416.       MOVE.W     (A7)+,D0    ; the slot number
  23417.       MOVEA.L    (A7)+,A0    ; the VBL task ptr
  23418.       _SlotVInstall
  23419.       MOVE.W     D0,(A7)     ; save result code on stack
  23420.       JMP        (A1)        ; return to caller
  23421.       ENDPROC
  23422.  
  23423.   ;----------------------------------------------------------------------
  23424.   ;FUNCTION SlotVRemove(vblTaskPtr: QElemPtr; theSlot: Integer): OSErr;
  23425.   ;----------------------------------------------------------------------
  23426.   SlotVRemove PROC EXPORT
  23427.       MOVEA.L    (A7)+,A1    ; save return address
  23428.       MOVE.W     (A7)+,D0    ; the slot number
  23429.       MOVEA.L    (A7)+,A0    ; the VBL task ptr
  23430.       _SlotVRemove
  23431.       MOVE.W     D0,(A7)     ; save result code on stack
  23432.       JMP        (A1)        ; return to caller
  23433.       ENDPROC
  23434.  
  23435.   These errors will affect C, Pascal, and assembly language users. 
  23436.  
  23437.   Workaround:  Use the corrected glue for _SlotVInstall and _SlotVRemove.
  23438.  
  23439. 3)     The glue for the register based Start Manager calls _GetTimeout and _SetTimeout
  23440. described in Inside Macintosh, Volume V, is incorrect .  The following are corrected
  23441. versions of the erroneous glue for these routines found in the library file Interface.o:
  23442.  
  23443.   ;-----------------------------------------------------------------------
  23444.   ;PROCEDURE GetTimeout(VAR count: INTEGER);
  23445.   ;-----------------------------------------------------------------------
  23446.   GetTimeout PROC EXPORT
  23447.   ;===>     CLR.W      -(A7)      ;===OOPS, selector in A0 not on stack
  23448.        SUBA.L     A0,A0       ;Put selector in A0, i.e. 0
  23449.        _InternalWait
  23450.        MOVEA.L    (A7)+,A1    ;Pop return address into A1
  23451.        MOVEA.L    (A7)+,A0    ;Pop location for VAR count
  23452.        MOVE.W     D0,(A0)     ;Stuff returned value into count
  23453.        JMP        (A1)        ;And go home
  23454.   
  23455.   ;----------------------------------------------------------------------
  23456.   ; PROCEDURE SetTimeout(count: INTEGER);
  23457.   ;----------------------------------------------------------------------
  23458.   SetTimeout PROC EXPORT
  23459.       MOVEA.L    (A7)+,A1    ;Pop return address into A1
  23460.       MOVE.W     (A7),D0     ;Move count parameter into D0
  23461.  ;===>     MOVE.W    #$0001,(A7)      ;===OOPS, selector in A0 not on stack
  23462.       MOVEA.W    #$0001,A0   ;Put selector in A0
  23463.       _InternalWait
  23464.       JMP        (A1)        ;And go home
  23465.  
  23466. These errors will affect C, Pascal, and assembly language users.
  23467.  
  23468. WORKAROUND: Use the corrected glue for _GetTimeout and _SetTimeout.
  23469.  
  23470.  
  23471.  
  23472. æKY 201
  23473. æC #201: ReadPacket Clarification
  23474.  
  23475. See also:     The AppleTalk Manager
  23476.  
  23477. Written by:   Mark Bennett     August 1, 1988
  23478. ________________________________________________________________________________
  23479.  
  23480. This technical note clears up some confusion concerning the low-level function ReadPacket.
  23481. This function is called by protocol handlers and socket listeners.
  23482. ________________________________________________________________________________
  23483.  
  23484. The documentation for ReadPacket on page 327 of Inside Macintosh Volume II states
  23485. that MC680X0 register D3 should be tested to determine if there was an error condition.
  23486. This is incorrect. D3 merely reflects the number of bytes left to be read and could
  23487. be zero even though an error occurred. The correct test for an error condition after
  23488. calling either ReadPacket and ReadRest is the Z
  23489. (Zero) bit, which will be set if no error was detected and clear otherwise.
  23490.  
  23491.  
  23492. æKY 202
  23493. æC #202:   Resetting the Event Mask
  23494.  
  23495. See also:   Inside Macintosh, Volume II, The OS Event Manager
  23496.  
  23497. Written by:   Chris Knepper      August 1, 1988
  23498. Revised by:   Chris Knepper      December 1988
  23499. ________________________________________________________________________________
  23500.  
  23501. As of System 4.2 and Finder 6.0, applications which alter the event mask must now
  23502. restore the original event mask when quitting.
  23503. Changes since August 1, 1988:  Added a related MultiFinder anomaly.
  23504. ________________________________________________________________________________
  23505.  
  23506. In most cases, applications should not modify the system event mask, which means they
  23507. should avoid calling SetEventMask and not alter the low-memory global SysEvtMask. 
  23508. Modifying the event mask is of no use to most applications, and the only situation in
  23509. which an application might need to modify it is to detect key-up events.  Only those
  23510. developers creating applications which must detect key-up events need to know the
  23511. information presented in this Technical Note.  Other developers should avoid altering
  23512. the system event mask at all costs.
  23513.  
  23514. Since the system event mask normally prevents key-up events from being posted, those
  23515. applications which need to detect key-up events call SetEventMask during initialization
  23516. to enable key-up events.  This process might be as follows:
  23517.  
  23518.       myMask := EveryEvent;
  23519.       SetEventMask(myMask);
  23520.  
  23521. Applications which make this call during initialization, must save the event mask
  23522. prior to calling SetEventMask and restore the event mask when quitting.  Given the
  23523. following definitions and declarations in MPW Pascal:
  23524.  
  23525.    CONST   SysEvtMask = $144;
  23526.    TYPE    IntPtr = ^INTEGER;
  23527.    VAR     saveMask: INTEGER;
  23528.            MaskPtr: IntPtr;
  23529.  
  23530. you save the event mask as follows:
  23531.  
  23532.       { set up our event mask pointer }
  23533.       MaskPtr := IntPtr(SysEvtMask);
  23534.       { save the event mask }
  23535.       saveMask := MaskPtr^;
  23536.  
  23537. and restore the event mask as follows:
  23538.  
  23539.       { restore the event mask }
  23540.       MaskPtr^ := saveMask;
  23541.  
  23542.  
  23543. Finder Anomaly
  23544.  
  23545. When MultiFinder is disabled, users will notice a strange behavior in the Finder
  23546. (versions 6.0 and later) after quitting applications which fail to restore the event
  23547. mask.  If an application failed to restore the event mask when quitting and had set
  23548. the event mask to mask out mouse-up events, all mouse-up events would continue to be
  23549. masked out, and the user would notice that the Finder no longer recognizes double
  23550. clicks.
  23551.  
  23552.  
  23553. MultiFinder Anomaly
  23554.  
  23555. With the current Macintosh architecture, the interrupt handlers which service hardware
  23556. interrupts call _PostEvent to post events in the event queue, so events are inserted
  23557. in the queue unless they are masked out by SysEvtMask.  If an event is masked out by
  23558. SysEvtMask, then _PostEvent returns a evtNotEnb error and does not insert the event
  23559. in the queue.
  23560.  
  23561. Applications normally retrieve events which have been successfully posted to the
  23562. event queue by calling _GetNextEvent or _WaitNextEvent.  If the event being retrieved
  23563. is not masked out by the event mask (mask) which is supplied to these routines, then
  23564. the routines will return TRUE.
  23565.  
  23566. Under MultiFinder, SysEvtMask is an application-specific global variable which is
  23567. switched during context switches; therefore, under MultiFinder, if an application
  23568. must alter SysEvtMask, it must also be prepared to handle all events, even those
  23569. which normally would be masked out by SysEvtMask.
  23570.  
  23571. This anomaly occurs when MultiFinder switches out SysEvtMask during a “minor switch”
  23572. (i.e., switching from foreground to background to service background applications). 
  23573. Interrupt service routines operating at interrupt time call _PostEvent to post events,
  23574. and _PostEvent masks out events using the mask in SysEvtMask to determine whether or
  23575. not to post the event.  This scheme means that events are posted as they occur, regardless
  23576. of whether the current value of SysEvtMask belongs to the foreground application or a
  23577. background application; therefore, it is possible for the event queue to not contain
  23578. key-up events, even though the foreground application enabled them, because the background
  23579. application could mask them out.
  23580.  
  23581. A future version of the System Software will account for this problem by using the
  23582. foreground application’s event mask when events are posted (even if a background
  23583. application is running when the interrupt service routine is called).
  23584.  
  23585.  
  23586.  
  23587. æKY 203
  23588. æC #203: Don’t Abuse the Managers
  23589.  
  23590. See also:     The Resource Manager
  23591.               TextEdit
  23592.               The List Manager
  23593.               The Dialog Manager
  23594.               Technical Note #141–Maximum Number of Resources in a File
  23595.               Technical Note #34—User Items in Dialogs 
  23596.  
  23597. Written by:   Bo3b Johnson     August 1, 1988
  23598. ________________________________________________________________________________
  23599.  
  23600. When using the various pieces of the Macintosh operating system there is a temptation
  23601. to try to stretch the built-in Managers too far. Developers should be aware of the
  23602. intended purpose of the various Managers and beware of using them for things that
  23603. they were not designed to handle. If extended beyond their design goals, they will
  23604. become slow and unwieldy.
  23605. ________________________________________________________________________________
  23606.  
  23607. Managers to avoid abusing, and the type of abuse:
  23608.  
  23609. 1) The Resource Manager is not a database.
  23610. 2) The TextEdit package is not a word processor.
  23611. 3) The List Manager is not a spreadsheet.
  23612. 4) The Dialog Manager is not a user interface.
  23613.  
  23614.  
  23615. 1) No free database
  23616.  
  23617. After using the Resource Manager for a short time, its virtues become apparent: it is
  23618. very flexible, it is easy to use, it gives disk based I/O with no extra calls, data
  23619. can be extracted by either name or ID number, and the data is stored transparently so
  23620. the caller can pretend the data is always available in a virtual memory fashion. With
  23621. such wide ranging advantages, it would seem that the Resource Manager should be used
  23622. for everything. It should be apparent that the TANSTAAFL (There Ain’t No Such Thing
  23623. As A Free Lunch) philosophy applies to the Resource Manager as well. If overextended,
  23624. the Resource Manager will become slow and unusable.
  23625.  
  23626. The Resource Manager is not a database, nor is it a good way to store user data. 
  23627. Although it can be used to store very small amounts of data, such as configuration
  23628. data, and features some of the same characteristics of databases in general, the
  23629. Resource Manager is a specialized tool designed specifically for the types of things
  23630. that the Macintosh System needs. Its main virtue for system use is that a large variety
  23631. of data can be stored on disk, and accessed when needed. This is a primitive form of
  23632. virtual memory which extends the power of the system beyond what the RAM supplies.
  23633. Remembering that the Resource Manager was written in an era of 128K RAM, it should be
  23634. apparent that it is optimized to use as little RAM as possible.
  23635.  
  23636. The Resource Manager uses a simple data structure for accessing the data in the file.
  23637. Examining the Resource Manager file format can show some of the tradeoffs expected.
  23638. For instance, there is a linearly accessed table which describes all of the possible
  23639. resource types that are in the current file. Without too much thought it should be
  23640. apparent that if a file is created with thousands of different resource types then
  23641. access to those resources will be slow. The reason?  Each access requires scanning a
  23642. linear array.  There is no hashing technique used on the resource types.
  23643.  
  23644. There is a similar linear table for the resource IDs themselves. Based on the previous
  23645. discussion it should also be apparent that if there are thousands of resources of a
  23646. specific type that the access time will become much larger. It will be imperceptible
  23647. on a single access of a resource, but for thousands of accesses to the resource file
  23648. the time spent traversing the linear list will impact the overall speed of the program.
  23649. The user will not be pleased.
  23650.  
  23651. Increasing the slowness by having too many resources as well as too many types will
  23652. encourage the user to file the program in a ground based circular storage facility.
  23653.  
  23654. As stated in Technical Note #141, there is a limit of about 2700 resources in a given
  23655. file due to the way the resources are stored. The performance penalty will arrive
  23656. sooner, and the dividing line for where it is “too slow” is a personal preference. As
  23657. a rule of thumb, if the program has the ability to store more than about 500 resources
  23658. total (both IDs and types), then consideration should be given to using the Data Fork
  23659. instead. In particular, if the program allows the user to create data files, do not
  23660. use the Resource Manager to store the user data. The users will always overextend the
  23661. use of a program. Plan for it, and avoid making obviously bad decisions. For large
  23662. amounts of data, the File Manager is the place to look. If the program wants to allow
  23663. simultaneous (multi-user) access with read and write privileges to data files, then
  23664. do not use the Resource Manager.  Because it caches data, the Resource Manager cannot
  23665. be relied upon as a multi-user database – even for small amounts of data. This is
  23666. because there is no way to tell the Resource Manager its cache is invalid. 
  23667.  
  23668. Don’t be fooled by a convenient interface. The Resource Manager is not a database,
  23669. nor is it a file system.
  23670.  
  23671.  
  23672. 2) Words to live by
  23673.  
  23674. Looking at the TextEdit package can give the impression that there is a full featured
  23675. word processing system built in. This is even more true now that TextEdit has been
  23676. extended to support various styles and fonts. Unfortunately, appearances are deceiving,
  23677. and TextEdit is not up to the job of being a word processor. Looking through the
  23678. documentation shows that there is a 32,767 character limit on the text in a TextEdit
  23679. record. The teLength is defined as an Integer. Another more subtle limit is the drawing
  23680. limit of the rectangles surrounding the text. The destRect and viewRect both surround
  23681. the complete TextEdit record. Using some rather rough approximations, there is an
  23682. upper limit of about 40 pages of text that can be supported in the QuickDraw rectangle.
  23683. This is quite a lot for some applications, but is not very many when looking at the
  23684. job typically required of a word processor. Users do not enjoy breaking their documents
  23685. into multiple pieces. 
  23686.  
  23687. There are some other programmatic limitations, not the least of which is performance.
  23688. TextEdit will become quite sluggish with large blocks of data. After 2,000-4,000
  23689. characters have been stored in a TextEdit record, the performance will have slowed to
  23690. an unacceptable level. It is notable that the lineStarts array is a linear array of
  23691. offsets into the edit record. If the data towards the end of the data record (high in
  23692. the record) changes, the offsets have to be changed. This can involve updating thousands
  23693. of Integer offsets for every character typed. If the different font, size and style
  23694. information is tacked on top of all that, the performance can be expected to suffer
  23695. with large blocks of text. Make no mistake about it, a full Macintosh style word
  23696. processor is not an easy thing to write. TextEdit was not designed to handle large
  23697. documents. It was designed as a simple field editor for the Dialog Manager, and extended
  23698. from there. It was never intended to handle the large jobs expected of a word processor.
  23699.  
  23700. In order to perform the operations required of a word processor it is necessary to
  23701. use QuickDraw extensively. The expected Macintosh selection approach with autoscrolling,
  23702. typing over selected text, cut/copy/paste, and so on are best implemented using QuickDraw
  23703. directly. How the text is stored internally is the primary determining factor on how
  23704. the word processor will perform.
  23705.  
  23706. Don’t be fooled by how easy it is to implement simple editing in an application.
  23707. TextEdit is not a word processor.
  23708.  
  23709.  
  23710. 3) Checking lists twice
  23711.  
  23712. The List Manager appears to be a cell oriented display tool, allowing the easy creation
  23713. of a spreadsheet interface using system calls. The rich interface to the manager
  23714. makes it easy to handle arbitrary lists of data. Or does it? Although the List Manager
  23715. is very flexible, easy to use, and general enough to handle graphic elements, its
  23716. performance becomes unacceptable with relatively modest amounts of data. A one-dimensional
  23717. list (like the files list in StdFile) can be done very well using the List Manager,
  23718. but with several thousand items in the list, the performance may not be sufficient.
  23719. This rarely happens in StdFile of course, and StdFile was the father of the List
  23720. Manager. Here again, the tool was designed with a specific concept in mind, not to be
  23721. the ultimate tool for handling any possible arbitrary data. A two-dimensional list of
  23722. data will become too slow to use with an array as small as 10x100. This can hardly be
  23723. expected to satisfy the user of a spreadsheet, since one “power” criteria is always
  23724. the number of cells available. 
  23725.  
  23726. Why so slow? As above, examining the data structures used by the List Manager can
  23727. tell a lot about the expected performance and limitations. Notably the cellArray used
  23728. to offset to each cell’s data is an old friend, a linear array of Integer offsets. It
  23729. should come as no surprise that inserting or deleting