1 /* File: main-crb.c */
2 
3 /*
4  * Copyright (c) 1997 Ben Harrison, Keith Randall, Peter Ammon, Ron Anderson
5  * and others
6  *
7  * This software may be copied and distributed for educational, research,
8  * and not for profit purposes provided that this copyright and statement
9  * are included in all such copies.
10  */
11 
12 
13 /*
14  * This file helps Angband work with Macintosh computers running OS X,
15  * or OS 8/9 with CarbonLib system extention.
16  *
17  * To use this file, use an appropriate "Makefile" or "Project File", which
18  * should define "MACINTOSH" in case of PEF Carbon compilation on CodeWarrior
19  * or MPW, and "MACH_O_CARBON" for OS X Developer CD gcc.  Please note that
20  * defining "MACINTOSH" for the latter will result in completely broken
21  * binary, mostly because of different pathname conventions.
22  *
23  * The official compilation uses the CodeWarrior Pro compiler.
24  *
25  * If you are never going to use "graphics" (especially if you are not
26  * compiling support for graphics anyway) then you can delete the "pict"
27  * resources with id "1001", "1002", "1003" and "1004" with no dangerous
28  * side effects.
29  *
30  *
31  * This file assumes that you will be using a PPC Mac running OS X
32  * or OS 8/9 (8.6 or greater) with CarbonLib system extention enabled.
33  * In fact, the game will refuse to run unless these features are available.
34  *
35  * MACH_O_CARBON code pushes the system requirement a bit further, and
36  * I don't think it works on System 8, even with CarbonLib, because it uses
37  * the Bundle services, but I may be wrong.
38  *
39  * Note that the "preference" file is now a simple XML text file
40  * called "<program name>.plist" in case of PEF Carbon, and "<Java-style
41  * program id defined in Info.plist>.plist" for Mach-O Carbon, which contains
42  * key-value paris, so it no longer has to check version stamp to validate
43  * its contents.
44  *
45  *
46  * Note that "init1.c", "init2.c", "load1.c", "load2.c", and "birth.c"
47  * should probably be "unloaded" as soon as they are no longer needed,
48  * to save space, but I do not know how to do this.  XXX XXX XXX
49  *
50  * Stange bug -- The first "ClipRect()" call crashes if the user closes
51  * all the windows, switches to another application, switches back, and
52  * re-opens the main window, for example, using "command-a".  XXX XXX XXX
53  *
54  *
55  * Initial framework (and most code) by Ben Harrison (benh@phial.com).
56  *
57  * Some code adapted from "MacAngband 2.6.1" by Keith Randall
58  *
59  * Initial PowerMac port by Maarten Hazewinkel (mmhazewi@cs.ruu.nl).
60  *
61  * Most Apple Event code provided by Steve Linberg (slinberg@crocker.com).
62  *
63  * Most of the graphics code is adapted from an extremely minimal subset of
64  * the "Sprite World II" package, an amazing (and free) animation package.
65  *
66  * Carbon code adapted from works by Peter Ammon and Ron Anderson.
67  *
68  * (List of changes made by "pelpel" follow)
69  * Some API calls are updated to OS 8.x-- ones.
70  *
71  * Pixmap locking code in Term_pict_mac() follows Carbon Porting Guide
72  * by Apple.
73  *
74  * The idle loop in TERM_XTRA_DELAY is rewritten to sleep on WaitNextEvent
75  * for a couple of reasons.
76  *
77  * CheckEvent now really blocks whenever asked to wait.
78  *
79  * The unused buffer GWorld is completely removed. It has long been pure waste
80  * of memory.
81  *
82  * The default font-size combination was changed because the old one, Monaco
83  * at 12 points causes the redraw artefact problem on OS X.
84  *
85  * Characters in the ASCII mode are clipped by their bounding rects to reduce
86  * redraw artefacts that were quite annoying in certain font-point combos.
87  *
88  * Transparency effect now avoids double bitblts whenever possible.
89  *
90  * Old tiles were drawn in a wrong fashion by the USE_TRANSPARENCY code.
91  *
92  * ASCII and the two graphics modes are now controlled by single graf_mode
93  * variable.  arg_* and use_* variables are set when requested mode is
94  * successfully initialised.
95  *
96  * Most of the menus are now loaded from resources.
97  *
98  * Moved TileWidth and TileHeight menus into Special. There were too many menus.
99  *
100  * Added support for 32x32 tiles, now for [V] only.
101  *
102  * Related to the above, globe_init no longer loads tile images twice if
103  * a tileset doesn't have corresponding masks.
104  *
105  * Added support for POSIX-style pathnames, for Mach-O Carbon (gcc, CW >= 7).
106  * We can finally live without Pascal strings to handle files this way.
107  *
108  * (Mach-O Carbon) Graphics tiles are moved out of the resource fork into
109  * bundle-based data fork files.
110  *
111  * Changed size-related menu code, because they no longer function because
112  * some APIs have been changed to return Unicode in some cases.
113  *
114  * Changed the transparency code again, this time using Ron Anderson's code,
115  * which makes more sound assumption about background colour and is more
116  * efficient.
117  *
118  * The old asynchronous sound player could try to lock the same handle more
119  * than once, load same sound resource already in use, or unlock and release
120  * currently playing sound.
121  *
122  * hook_quit() now releases memory-related resources dynamically allocated by
123  * the graphics and sound code.
124  *
125  * Added support for yet another required Apple Event, reopen application.
126  *
127  * Changed the way the main game code is called from the file.  It's now
128  * entered from a single point in main().  This makes do_menu_file_new,
129  * do_menu_file_open and handle_open_when_ready much less hackish, but
130  * slightly complicates main().
131  *
132  * Introduced some "common sense" practices of Macintosh programs:
133  * - the update event handler now respects update regions at last;
134  * - removed validating/invalidating that became superfluous; and
135  * - the event handlers makes use of Window's refcon to find term_data.
136  *
137  * Important Resources in the resource file:
138  *
139  *   FREF 130 = ANGBAND_CREATOR / 'APPL' (application)
140  *   FREF 129 = ANGBAND_CREATOR / 'SAVE' (save file)
141  *   FREF 130 = ANGBAND_CREATOR / 'TEXT' (bone file, generic text file)
142  *   FREF 131 = ANGBAND_CREATOR / 'DATA' (binary image file, score file)
143  *
144  *   DLOG 128 = "About Angband..."
145  *
146  *   ALRT 128 = unused (?)
147  *   ALRT 129 = "Warning..."
148  *
149  *   DITL 128 = body for DLOG 128
150  *   DITL 129 = body for ALRT 129
151  *   DITL 130 = body for ALRT 130
152  *
153  *   ICON 128 = "warning" icon
154  *
155  *   MBAR 128 = array of MENU id's (128, 129, 130, 131, 132, 133, 134)
156  *   MENU 128 = apple (about, -, ...)
157  *   MENU 129 = File (new, open, close, save, -, score, quit)
158  *   MENU 130 = Edit (undo, -, cut, copy, paste, clear)
159  *   MENU 131 = Font (bold, wide, -)
160  *   MENU 132 = Size ()
161  *   MENU 133 = Windows ()
162  *   MENU 134 = Special (Sound, Graphics, TileWidth, TileHeight, -, Fiddle,
163  *                       Wizard)
164  *              Graphics have following submenu attached:
165  *   MENU 144 = Graphics (None, 8x8, 16x16, 32x32, enlarge tiles)
166  *              TileWidth and TileHeight submenus are filled in by this program.
167  *   MENU 145 = TileWidth ()
168  *   MENU 146 = TileHeight ()
169  *
170  *   On CFM(PEF) Carbon only:
171  *   PICT 1001 = Graphics tile set (8x8)
172  *   PICT 1002 = Graphics tile set (16x16 images)
173  *   PICT 1004 = Graphics tile set (32x32)
174  *
175  *   Mach-O Carbon now uses data fork resources:
176  *   8x8.png   = Graphics tile set (8x8)
177  *   16x16.png = Graphics tile set (16x16 images)
178  *   32x32.png = Graphics tile set (32x32)
179  *   These files should go into the Resources subdirectory of an application
180  *   bundle.
181  *
182  *   STR# 128 = "Please select the "lib" folder"
183  *
184  *   plst 0   can be empty, but required for single binary Carbon apps on OS X
185  *            Isn't necessary for Mach-O Carbon.
186  *
187  *
188  * File name patterns:
189  *   all 'APEX' files have a filename of the form "*:apex:*" (?)
190  *   all 'BONE' files have a filename of the form "*:bone:*" (?)
191  *   all 'DATA' files have a filename of the form "*:data:*"
192  *   all 'SAVE' files have a filename of the form "*:save:*"
193  *   all 'USER' files have a filename of the form "*:user:*" (?)
194  *
195  * Perhaps we should attempt to set the "_ftype" flag inside this file,
196  * to avoid nasty file type information being spread all through the
197  * rest of the code.  (?)  This might require adding hooks into the
198  * "fd_open()" and "my_fopen()" functions in "util.c".  XXX XXX XXX
199  *
200  *
201  * Reasons for each header file:
202  *
203  *   angband.h = Angband header file
204  *
205  *   Types.h = (included anyway)
206  *   Gestalt.h = gestalt code
207  *   QuickDraw.h = (included anyway)
208  *   OSUtils.h = (included anyway)
209  *   Files.h = file code
210  *   Fonts.h = font code
211  *   Menus.h = menu code
212  *   Dialogs.h = dialog code
213  *   Windows.h = (included anyway)
214  *   Palettes.h = palette code
215  *   ToolUtils.h = HiWord() / LoWord()
216  *   Events.h = event code
217  *   Resources.h = resource code
218  *   Controls.h = button code
219  *   Processes.h = GetProcessInformation(), ExitToShell()
220  *   Memory.h = NewPtr(), etc
221  *   QDOffscreen.h = GWorld code
222  *   Sound.h = Sound code
223  *   Navigation.h = save file / lib locating dialogues
224  *   CFPreferences.h = Preferences
225  *   CFNumber.h = read/write short values from/to preferences
226  */
227 
228 /*
229  * Yet another main-xxx.c for Carbon (pelpel) - revision 12
230  *
231  * Since I'm using CodeWarrior, the traditional header files are
232  * #include'd below.
233  *
234  * I also compiled Angband 3.0.2 successfully with OS X's gcc.
235  * Please follow these instructions if you are interested.
236  *
237  * ---(developer CD gcc + makefile porting notes, for Angband 3.0.2)-------
238  * 1. Compiling the binary
239  *
240  * If you try this on OS X + gcc, please use makefile.std, replacing
241  * main.c and main.o with main-crb.c and main-crb.o, removing all main-xxx.c
242  * and main-xxx.o from SRCS and OBJS, and, and use these settings:
243  *
244  * COPTS = -Wall -O1 -g -fpascal-strings
245  * INCLUDES =
246  * DEFINES = -DMACH_O_CARBON -DANGBAND30X
247  * LIBS = -framework CoreFoundation -framework QuickTime -framework Carbon
248  *
249  * -DANGBAND30X only affects main-crb.c. This is because I'm also compiling
250  * a couple of variants, and this arrangement makes my life easier.
251  *
252  * Never, ever #define MACINTOSH.  It'll wreck havoc in system interface
253  * (mostly because of totally different pathname convention).
254  *
255  * You might wish to disable some SET_UID features for various reasons:
256  * to have user folder within the lib folder, savefile names etc.
257  *
258  * For the best compatibility with the Classic ports and my PEF Carbon
259  * ports, my_fopen, fd_make and fd_open [in util.c] should call
260  *   (void)fsetfileinfo(buf, _fcreator, _ftype);
261  * when a file is successfully opened.  Or you'll see odd icons for some files
262  * in the lib folder.  In order to do so, extern.h should contain these lines,
263  * within #ifdef MACH_O_CARBON:
264  *   extern int fsetfileinfo(char *path, u32b fcreator, u32b ftype);
265  *   extern u32b _fcreator;
266  *   extern u32b _ftype;
267  * And enable the four FILE_TYPE macros in h-config.h for defined(MACH_O_CARBON)
268  * in addition to defined(MACINTOSH) && !defined(applec), i.e.
269  *  #if defined(MACINTOSH) && !defined(applec) || defined(MACH_O_CARBON)
270  *
271  * This is a very good way to spot bugs in use of these macros, btw.
272  *
273  * 2. Installation
274  *
275  * The "angband" binary must be arranged this way for it to work:
276  *
277  * lib/ <- the lib folder
278  * Angband (OS X).app/
279  *   Contents/
280  *     MacOS/
281  *       angband <- the binary you've just compiled
282  *     Info.plist <- to be explained below
283  *     Resources/
284  *       Angband.icns
285  *       Data.icns
286  *       Edit.icns
287  *       Save.icns
288  *       8x8.png <- 8x8 tiles
289  *       16x16.png <- 16x16 tiles
290  *       angband.rsrc <- see below
291  *
292  * 3. Preparing Info.plist
293  *
294  * Info.plist is an XML file describing some attributes of an application,
295  * and this is appropriate for Angband:
296  *
297  * <?xml version="1.0" encoding="UTF-8"?>
298  * <plist version="1.0">
299  *   <dict>
300  *     <key>CFBundleName</key><string>Angband</string>
301  *     <key>CFBundleDisplayName</key><string>Angband (OS X)</string>
302  *     <key>CFBundleExecutable</key><string>angband</string>
303  *     <key>CFBundlePackageType</key><string>APPL</string>
304  *     <key>CFBundleSignature</key><string>A271</string>
305  *     <key>CFBundleVersion</key><string>3.0.2</string>
306  *     <key>CFBundleShortVersionString</key><string>3.0.2</string>
307  *     <key>CFBundleIconFile</key><string>Angband</string>
308  *     <key>CFBundleIdentifier</key><string>net.thangorodrim.Angband</string>
309  *     <key>CFBundleInfoDictionaryVersion</key><string>6.0</string>
310  *     <key>CFBundleDocumentTypes</key>
311  *       <array>
312  *         <dict>
313  *           <key>CFBundleTypeExtentions</key><array><string>*</string></array>
314  *           <key>CFBundleTypeIconFile</key><string>Save</string>
315  *           <key>CFBundleTypeName</key><string>Angband saved game</string>
316  *           <key>CFBundleTypeOSTypes</key><array><string>SAVE</string></array>
317  *           <key>CFBundleTypeRole</key><string>Editor</string>
318  *         </dict>
319  *         <dict>
320  *           <key>CFBundleTypeExtentions</key><array><string>*</string></array>
321  *           <key>CFBundleTypeIconFile</key><string>Edit</string>
322  *           <key>CFBundleTypeName</key><string>Angband game data</string>
323  *           <key>CFBundleTypeOSTypes</key><array><string>TEXT</string></array>
324  *           <key>CFBundleTypeRole</key><string>Editor</string>
325  *         </dict>
326  *         <dict>
327  *           <key>CFBundleTypeExtentions</key><array><string>raw</string></array>
328  *           <key>CFBundleTypeIconFile</key><string>Data</string>
329  *           <key>CFBundleTypeName</key><string>Angband game data</string>
330  *           <key>CFBundleTypeOSTypes</key><array><string>DATA</string></array>
331  *           <key>CFBundleTypeRole</key><string>Editor</string>
332  *         </dict>
333  * 	</array>
334  *   </dict>
335  * </plist>
336  *
337  * 4. Menu, diaglogue and gfx resources
338  *
339  * The binary assumes angband.rsrc should be in the traditional resource
340  * mangager format.  Please run this command to create it from its textual
341  * description:
342  *
343  * Rez -i /Developer/Headers/FlatCarbon -d MACH_O -o angband.rsrc Angband.r
344  *
345  * The command is in /Developer/Tools.  You might wish to include it in your
346  * PATH.
347  *
348  * It's better to comment out the definitions of BNDL and plst resources
349  * before you do that.  I think you can DeRez the resulting angband.rsrc and
350  * feed it to the Interface Builder to produce a set of compatible .nib files,
351  * but this file also needs to be updated to understand .nib...  On the other
352  * hand, I really don't like to hardcode UI definitions in C.
353  *
354  * Graphics resources are moved out of the resource fork and become ordinary
355  * PNG files.  Make sure to set its resolution to 72 dpi (<- VERY important)
356  * while keeping vertical and horizontal scaling factor to 100% (<- VERY
357  * important), when you convert tiles in any formats to PNG.  This means
358  * that the real size of an image must shrink or grow when you change it's dpi.
359  *
360  * Sound resources are a bit more complicated.
361  * The easiest way is:
362  * 1) Grab recent Mac Angband binary.
363  * 2) Run this command:
364  *   DeRez -only 'snd ' (Angband binary) > sound.r
365  * 3) And specify sound.r files in addition to Angband.r when you run Rez.
366  *
367  * ---(end of OS X + gcc porting note)--------------------------------------
368  *
369  * Code adapted from Peter Ammon's work on 2.8.3 and some modifications
370  * are made when Apple's Carbon Porting Guide says they are absolutely
371  * necessary. Other arbirary changes are mostly because of my hatred
372  * of deep nestings and indentations. The code for controlling graphics modes
373  * have been thoroughly revised simply because I didn't like it (^ ^;).
374  * A bonus of this is that graphics settings can be loaded from Preferences
375  * quite easily.
376  *
377  * I also took Ron Anderson's (minimising the use of local-global coordinate
378  * conversions). Some might say his QuickTime multimedia is the most
379  * significant achievement... Play your favourite CD instead, if you really
380  * miss that (^ ^;) I might consider incorporating it if it makes use of
381  * event notification.
382  *
383  * I replaced some old API calls with new (OS 8.x--) ones, especially
384  * when I felt Apple is strongly against their continued usage.
385  *
386  * Similarly, USE_SFL_CODE (AppleEvent code by Steve Linberg) should be
387  * always active, so I removed ifdef's just to prevent accidents, as well as
388  * to make the code a bit cleaner.
389  *
390  * On the contrary, I deliberately left traditional resource interfaces.
391  * Whatever Apple might say, I abhor file name extentions. And keeping two
392  * different sets of resources for Classic and Carbon is just too much for
393  * a personal project XXX
394  *
395  * Because Carbon forbids the use of 68K code, ANGBAND_LITE_MAC sections
396  * are removed.
397  *
398  * Because the default font-size combination causes redraw artefact problem
399  * (some characters, even in monospace fonts, have negative left bearings),
400  * I introduced rather crude hack to clip all character drawings within
401  * their bounding rects. If you don't like this, please comment out the line
402  * #define CLIP_HACK
403  * below. The alternative, #define OVERWRITE_HACK, is based on Julian Lighton's
404  * brilliant suggestion, but it doesn't work as expected. This is because
405  * DrawText can render the same character with _different_ pixel width,
406  * depending on relative position of a character to the pen. Fonts do look
407  * very nice on the Mac, but too nice I'd say, in case of Angband.
408  *
409  * The check for return values of AEProcessAppleEvent is removed,
410  * because it results in annoying dialogues on OS X, also because
411  * Apple says it isn't usually necessary.
412  *
413  * Because the always_pict code is *so* slow, I changed the graphics
414  * mode selection a bit to use higher_pict when a user chooses a fixed
415  * width font and doesn't changes tile width / height.
416  *
417  * Added support for David Gervais' 32x32 tiles.
418  *
419  * Replaced transparency effect code by Ron Anderson's.
420  *
421  * Added support for gcc & make compilation. They come free with OS X
422  * (on the developer CD).  This means that it can be compiled as a bundle.
423  *
424  * For Mach-O Carbon binary, moved graphics tiles out of the
425  * resource fork and made them plain PNG files, to be stored in the application
426  * bundle's "Resources" subdirectory.
427  *
428  * For Mach-O Carbon binary, provided a compile-time option (USE_QT_SOUND) to
429  * move sound effect samples out of the resource fork and use *.wav files in
430  * the bundle's "Resources" subdirectory. The "*" part must match the names in
431  * angband_sound_name (variable.c) exactly. This doesn't hurt performance
432  * a lot in [V] (it's got ~25 sound events), but problematic in [Z]-based
433  * ones (they have somewhere around 70 sound events).
434  *
435  * You still can use the resource file that comes with the ext-mac archive
436  * on the Angband FTP server, with these additions:
437  * - MENUs 131--134 and 144--146, as described above
438  * - MBAR 128: just a array of 128 through 134
439  * - plst 0 : can be empty, although Apple recommends us to fill it in.
440  * - STR# 128 : something like "Please select your lib folder"
441  *
442  * Since this involves considerable amount of work, I attached
443  * a plain text resource definition (= Rez format) .
444  * I heavily commented on the file, hoping it could be easily adapted
445  * to future versions of Angband as well as variants.
446  * I omitted sound effects and graphic tiles to make it reasonably small.
447  * Please copy them from any recent Mac binaries - you can use, say ZAngband
448  * ones for Vanilla or [V]-based variants quite safely. T.o.M.E. uses fairly
449  * extended 16x16 tileset and it is maintained actively. IIRC Thangorodrim
450  * compile page has an intruction explaining how to convert tiles for
451  * use on the Mac... It can be tricky, depending on your choice of
452  * graphics utility. Remember setting resolution to 72 pixels per inch,
453  * while keeping vertical/horizontal scale factor to 100% and dump the
454  * result as a PICT from resource.
455  *
456  * To build Carbonised Angband with CodeWarrior, copy your PPC project
457  * and
458  * - replace main-mac.c in the project with this file (in the link order tab)
459  * - remove InterfaceLib and MathLib
460  * - add CarbonLib (found in Carbon SDK or CW's UniversalInterfaces) --
461  *   if you have compiler/linker errors, you'll need Carbon SDK 1.1 or greater
462  * - replace MSL C.PPC.Lib with MSL C.Carbon.Lib (both found in
463  *   MSL:MSL_C:MSL_MacOS:Lib:PPC)
464  * - leave MSL RuntimePPC.Lib as it is
465  * - don't forget to update resource file, as described above
466  * - as in Classic targets, you may have to include <unistd.h> and
467  *   <fcntl.h>. The most convinient place for them is the first
468  *   #ifdef MACINTOSH in h-system.h
469  * - check variant dependent ifdef's explained below, and add
470  *   appropriate one(s) in your A-mac-h.pch.
471  */
472 
473 
474 /*
475  * Force Carbon-compatible APIs
476  */
477 #ifndef MACH_O_CARBON
478 
479 # ifndef TARGET_API_MAC_CARBON
480 /* Can be CodeWarrior or MPW */
481 #  define TARGET_API_MAC_CARBON 1
482 
483 # endif
484 
485 #else
486 
487 /*
488  * Must be Mach-O Carbon target with OS X gcc.
489  * No need to set TARGET_API_MAC_CARBON to 1 here, but I assume it should
490  * be able to make efficient use of BSD functions, hence:
491  */
492 # define USE_MALLOC
493 /* Not yet */
494 /* # define USE_NIB */
495 
496 #endif /* !MACH_O_CARBON */
497 
498 
499 #include "angband.h"
500 
501 
502 #if defined(MACINTOSH) || defined(MACH_O_CARBON)
503 
504 /*
505  * Variant-dependent features:
506  *
507  * #define ALLOW_BIG_SCREEN (V, Ey, O, and Z.  Dr's big screen needs
508  * more work.  New S one is too idiosyncratic...)
509  * #define ANG281_RESET_VISUALS (Cth, Gum, Z)
510  * #define ZANG_AUTO_SAVE (O and Z)
511  * #define HAS_SCORE_MENU (V and maybe more)
512  * #define ANGBAND_CREATOR four letter code for your variant, if any.
513  * or use the default one.
514  *
515  * For [Z], you also have to say -- #define inkey_flag (p_ptr->inkey_flag)
516  * but before that, please, please consider using main-mac-carbon.c in [Z],
517  * that has some interesting features.
518  */
519 
520 /* ZAngband 2.7.x characteristics */
521 #define ALLOW_BIG_SCREEN
522 #define NEW_ZVIRT_HOOKS
523 #define ANG281_RESET_VISUALS
524 #define ZANG_AUTO_SAVE
525 
526 /* I can't ditch these, yet, because there are many variants */
527 #define USE_TRANSPARENCY
528 
529 #undef DEBUG
530 
531 
532 /* Default creator signature */
533 #ifndef ANGBAND_CREATOR
534 # define ANGBAND_CREATOR 'A271'
535 #endif
536 
537 
538 /*
539  * Use rewritten asynchronous sound player
540  */
541 #define USE_ASYNC_SOUND
542 
543 
544 /*
545  * A rather crude fix to reduce amount of redraw artefacts.
546  * Some fixed width fonts (i.e. Monaco) has characters with negative
547  * left bearings, so Term_wipe_mac or overwriting cannot completely
548  * erase them. This could be introduced to Classic Mac OS ports too,
549  * but since I've never heard any complaints and I don't like to
550  * make 68K ports even slower, I won't do so there.
551  */
552 #define CLIP_HACK /* */
553 
554 /*
555  * Another redraw artifact killer, based on suggestion by Julian Lighton
556  */
557 /* #define OVERWRITE_HACK */
558 
559 /* These hacks can co-exist, but I don't like overkill */
560 #ifdef OVERWRITE_HACK
561 # ifdef CLIP_HACK
562 #  undef CLIP_HACK
563 # endif
564 #endif
565 
566 
567 /*
568  * In OS X + gcc, use <Carbon/Carbon.h>, <CoreServices/CoreServices.h> and
569  * <CoreFoundation/CoreFoundation.h> for ALL of these, including the Apple
570  * Event ones.  <QuickTime/QuickTime.h> is used by the tile loading code.
571  */
572 #ifdef MACH_O_CARBON
573 
574 #include <Carbon/Carbon.h>
575 #include <QuickTime/QuickTime.h>
576 #include <CoreServices/CoreServices.h>
577 #include <CoreFoundation/CoreFoundation.h>
578 
579 #else /* MACH_O_CARBON */
580 
581 #include <MacTypes.h>
582 #include <Gestalt.h>
583 #include <QuickDraw.h>
584 #include <Files.h>
585 #include <Fonts.h>
586 #include <Menus.h>
587 #include <Dialogs.h>
588 #include <Windows.h>
589 #include <Palettes.h>
590 #include <ToolUtils.h>
591 #include <Events.h>
592 #include <Processes.h>
593 #include <Resources.h>
594 #include <Controls.h>
595 #include <MacMemory.h>
596 #include <QDOffscreen.h>
597 #include <Sound.h>
598 #include <Navigation.h>
599 #include <CFPreferences.h>
600 #include <CFNumber.h>
601 #include <AppleEvents.h>
602 #include <EPPC.h>
603 #include <Folders.h>
604 
605 # ifdef MAC_MPW
606 #  include <CarbonStdCLib.h>
607 # endif
608 
609 #endif /* MACH_O_CARBON */
610 
611 
612 
613 /*
614  * Use "malloc()" instead of "NewPtr()"
615  */
616 /* #define USE_MALLOC */
617 
618 
619 /*
620  * Information about each of the 256 available colors
621  */
622 static RGBColor color_info[256];
623 
624 
625 #if defined(MACH_O_CARBON) || defined(MAC_MPW)
626 
627 /*
628  * Creator signature and file type - Didn't I say that I abhor file name
629  * extentions?  Names and metadata are entirely different set of notions.
630  */
631 OSType _fcreator;
632 OSType _ftype;
633 
634 #endif /* MACH_O_CARBON || MAC_MPW */
635 
636 
637 /*
638  * Forward declare
639  */
640 typedef struct term_data term_data;
641 
642 /*
643  * Extra "term" data
644  */
645 struct term_data
646 {
647 	term *t;
648 
649 	Rect r;
650 
651 	WindowPtr w;
652 
653 
654 	short padding;
655 
656 	short pixelDepth;
657 
658 	/* GWorldPtr theGWorld;	*/
659 
660 	GDHandle theGDH;
661 
662 	/* GDHandle mainSWGDH; */
663 
664 	Str15 title;
665 
666 	s16b oops;
667 
668 	s16b keys;
669 
670 	s16b last;
671 
672 	s16b mapped;
673 
674 	s16b rows;
675 	s16b cols;
676 
677 	s16b font_id;
678 	s16b font_size;
679 	s16b font_face;
680 	s16b font_mono;
681 
682 	s16b font_o_x;
683 	s16b font_o_y;
684 	s16b font_wid;
685 	s16b font_hgt;
686 
687 	s16b tile_o_x;
688 	s16b tile_o_y;
689 	s16b tile_wid;
690 	s16b tile_hgt;
691 
692 	s16b size_wid;
693 	s16b size_hgt;
694 
695 	s16b size_ow1;
696 	s16b size_oh1;
697 	s16b size_ow2;
698 	s16b size_oh2;
699 };
700 
701 
702 
703 
704 /*
705  * Forward declare -- see below
706  */
707 static bool CheckEvents(int wait);
708 
709 /*
710  * Available values for 'wait'
711  */
712 #define CHECK_EVENTS_DRAIN -1
713 #define CHECK_EVENTS_NO_WAIT	0
714 #define CHECK_EVENTS_WAIT 1
715 
716 
717 #ifndef MACH_O_CARBON
718 
719 /*
720  * Hack -- location of the main directory
721  */
722 static short app_vol;
723 static long  app_dir;
724 
725 #endif /* !MACH_O_CARBON */
726 
727 
728 /*
729  * Delay handling of double-clicked savefiles
730  */
731 Boolean open_when_ready = FALSE;
732 
733 /*
734  * Delay handling of pre-emptive "quit" event
735  */
736 Boolean quit_when_ready = FALSE;
737 
738 
739 /*
740  * Aqua automatically supplies the Quit menu.
741  */
742 static Boolean is_aqua = FALSE;
743 
744 /*
745  * Version of Mac OS - for version specific bug workarounds (; ;)
746  */
747 static long mac_os_version;
748 
749 
750 /*
751  * Hack -- game in progress
752  */
753 static Boolean game_in_progress = FALSE;
754 
755 
756 /*
757  * Indicate if the user chooses "new" to start a game
758  */
759 static Boolean new_game = FALSE;
760 
761 
762 /*
763  * Only do "SetPort()" when needed
764  */
765 static WindowPtr active = NULL;
766 
767 
768 /*
769  * Maximum number of terms
770  */
771 #define MAX_TERM_DATA 8
772 
773 
774 /*
775  * An array of term_data's
776  */
777 static term_data data[MAX_TERM_DATA];
778 
779 
780 /*
781  * Note when "open"/"new" become valid
782  */
783 static bool initialized = FALSE;
784 
785 
786 
787 
788 #ifdef MACH_O_CARBON
789 
790 /* Carbon File Manager utilities by pelpel */
791 
792 /*
793  * (Carbon)
794  * Convert a pathname to a corresponding FSSpec.
795  * Returns noErr on success.
796  */
path_to_spec(const char * path,FSSpec * spec)797 static OSErr path_to_spec(const char *path, FSSpec *spec)
798 {
799 	OSErr err;
800 	FSRef ref;
801 
802 	/* Convert pathname to FSRef ... */
803 	err = FSPathMakeRef(path, &ref, NULL);
804 	if (err != noErr) return (err);
805 
806 	/* ... then FSRef to FSSpec */
807 	err = FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL);
808 
809 	/* Inform caller of success or failure */
810 	return (err);
811 }
812 
813 
814 /*
815  * (Carbon)
816  * Convert a FSSpec to a corresponding pathname.
817  * Returns noErr on success.
818  */
spec_to_path(const FSSpec * spec,char * buf,size_t size)819 static OSErr spec_to_path(const FSSpec *spec, char *buf, size_t size)
820 {
821 	OSErr err;
822 	FSRef ref;
823 
824 	/* Convert FSSpec to FSRef ... */
825 	err = FSpMakeFSRef(spec, &ref);
826 	if (err != noErr) return (err);
827 
828 	/* ... then FSRef to pathname */
829 	err = FSRefMakePath(&ref, buf, size);
830 
831 	/* Inform caller of success or failure */
832 	return (err);
833 }
834 
835 
836 /*
837  * (Carbon) [via path_to_spec]
838  * Set creator and filetype of a file specified by POSIX-style pathname.
839  * Returns 0 on success, -1 in case of errors.
840  */
fsetfileinfo(cptr pathname,OSType fcreator,OSType ftype)841 void fsetfileinfo(cptr pathname, OSType fcreator, OSType ftype)
842 {
843 	OSErr err;
844 	FSSpec spec;
845 	FInfo info;
846 
847 	/* Convert pathname to FSSpec */
848 	if (path_to_spec(pathname, &spec) != noErr) return;
849 
850 	/* Obtain current finder info of the file */
851 	if (FSpGetFInfo(&spec, &info) != noErr) return;
852 
853 	/* Overwrite creator and type */
854 	info.fdCreator = fcreator;
855 	info.fdType = ftype;
856 	err = FSpSetFInfo(&spec, &info);
857 
858 	/* Done */
859 	return;
860 }
861 
862 
863 #else /* MACH_O_CARBON */
864 
865 
866 /*
867  * Convert a pascal string in place
868  *
869  * This function may be defined elsewhere, but since it is so
870  * small, it is not worth finding the proper function name for
871  * all the different platforms.
872  */
ptocstr(StringPtr src)873 static void ptocstr(StringPtr src)
874 {
875 	int i;
876 
877 	/* Hack -- pointer */
878 	char *s = (char*)(src);
879 
880 	/* Hack -- convert the string */
881 	for (i = s[0]; i; i--, s++) s[0] = s[1];
882 
883 	/* Hack -- terminate the string */
884 	s[0] = '\0';
885 }
886 
887 
888 
889 /*
890  * Utility routines by Steve Linberg
891  *
892  * The following three routines (pstrcat, pstrinsert, and PathNameFromDirID)
893  * were taken from the Think Reference section called "Getting a Full Pathname"
894  * (under the File Manager section).  We need PathNameFromDirID to get the
895  * full pathname of the opened savefile, making no assumptions about where it
896  * is.
897  *
898  * I had to hack PathNameFromDirID a little for MetroWerks, but it's awfully
899  * nice.
900  */
pstrcat(StringPtr dst,StringPtr src)901 static void pstrcat(StringPtr dst, StringPtr src)
902 {
903 	/* copy string in */
904 	BlockMove(src + 1, dst + *dst + 1, *src);
905 
906 	/* adjust length byte */
907 	*dst += *src;
908 }
909 
910 
911 /*
912  * pstrinsert - insert string 'src' at beginning of string 'dst'
913  */
pstrinsert(StringPtr dst,StringPtr src)914 static void pstrinsert(StringPtr dst, StringPtr src)
915 {
916 	/* make room for new string */
917 	BlockMove(dst + 1, dst + *src + 1, *dst);
918 
919 	/* copy new string in */
920 	BlockMove(src + 1, dst + 1, *src);
921 
922 	/* adjust length byte */
923 	*dst += *src;
924 }
925 
926 
PathNameFromDirID(long dirID,short vRefNum,StringPtr fullPathName)927 static void PathNameFromDirID(long dirID, short vRefNum, StringPtr fullPathName)
928 {
929 	CInfoPBRec block;
930 	Str255 directoryName;
931 	OSErr err;
932 
933 	fullPathName[0] = '\0';
934 
935 	block.dirInfo.ioDrParID = dirID;
936 	block.dirInfo.ioNamePtr = directoryName;
937 
938 	while (1)
939 	{
940 		block.dirInfo.ioVRefNum = vRefNum;
941 		block.dirInfo.ioFDirIndex = -1;
942 		block.dirInfo.ioDrDirID = block.dirInfo.ioDrParID;
943 		err = PBGetCatInfoSync(&block);
944 		pstrcat(directoryName, (StringPtr)"\p:");
945 		pstrinsert(fullPathName, directoryName);
946 		if (block.dirInfo.ioDrDirID == 2) break;
947 	}
948 }
949 
950 
951 /*
952  * Rewritten to use PathNameFromDirID -- pelpel
953  *
954  * Convert refnum+vrefnum+fname into a full file name
955  * Store this filename in 'buf' (make sure it is long enough)
956  * Note that 'fname' looks to be a "pascal" string
957  */
refnum_to_name(char * buf,long refnum,short vrefnum,char * fname)958 static void refnum_to_name(char *buf, long refnum, short vrefnum, char *fname)
959 {
960 	/* Convert directory & volume reference numbers to an absolute path */
961 	PathNameFromDirID(refnum, vrefnum, (unsigned char *)buf);
962 
963 	/* Append file name to the path */
964 	pstrcat((unsigned char *)buf, (unsigned char *)fname);
965 
966 	/* Convert the result into a C string */
967 	ptocstr((unsigned char *)buf);
968 }
969 
970 #endif /* MACH_O_CARBON */
971 
972 
973 #ifdef MAC_MPW
974 
975 /*
976  * Convert pathname to an appropriate format, because MPW's
977  * CarbonStdCLib chose to use system's native path format,
978  * making our lives harder to create binaries that run on
979  * OS 8/9 and OS X :( -- pelpel
980  */
convert_pathname(char * path)981 void convert_pathname(char* path)
982 {
983 	char buf[1024];
984 
985 	/* Nothing has to be done for CarbonLib on Classic */
986 	if (mac_os_version >= 0x1000)
987 	{
988 		/* Convert to POSIX style */
989 		ConvertHFSPathToUnixPath(path, buf);
990 
991 		/* Copy the result back */
992 		strcpy(path, buf);
993 	}
994 
995 	/* Done. */
996 	return;
997 }
998 
999 # ifdef CHECK_MODIFICATION_TIME
1000 
1001 /*
1002  * Although there is no easy way to emulate fstat in the old interface,
1003  * we still can do stat-like things, because Mac OS is an OS.
1004  */
get_modification_time(cptr path,u32b * mod_time)1005 static int get_modification_time(cptr path, u32b *mod_time)
1006 {
1007 	CInfoPBRec pb;
1008 	Str255 pathname;
1009 	int i;
1010 
1011 	/* Paranoia - make sure the pathname fits in Str255 */
1012 	i = strlen(path);
1013 	if (i > 255) return (-1);
1014 
1015 	/* Convert pathname to a Pascal string */
1016 	strncpy((char *)pathname + 1, path, 255);
1017 	pathname[0] = i;
1018 
1019 	/* Set up parameter block */
1020 	pb.hFileInfo.ioNamePtr = pathname;
1021 	pb.hFileInfo.ioFDirIndex = 0;
1022 	pb.hFileInfo.ioVRefNum = app_vol;
1023 	pb.hFileInfo.ioDirID = 0;
1024 
1025 	/* Get catalog information of the file */
1026 	if (PBGetCatInfoSync(&pb) != noErr) return (-1);
1027 
1028 	/* Set modification date and time */
1029 	*mod_time = pb.hFileInfo.ioFlMdDat;
1030 
1031 	/* Success */
1032 	return (0);
1033 }
1034 
1035 
1036 /*
1037  * A (non-Mach-O) Mac OS version of check_modification_time, for those
1038  * compilers without good enough POSIX-compatibility libraries XXX XXX
1039  */
check_modification_date(int fd,cptr template_file)1040 errr check_modification_date(int fd, cptr template_file)
1041 {
1042 #pragma unused(fd)
1043 	u32b txt_stat, raw_stat;
1044 	char *p;
1045 	char fname[32];
1046 	char buf[1024];
1047 
1048 	/* Build the file name */
1049 	path_build(buf, sizeof(buf), ANGBAND_DIR_EDIT, template_file);
1050 
1051 	/* XXX XXX XXX */
1052 	convert_pathname(buf);
1053 
1054 	/* Obtain modification time */
1055 	if (get_modification_time(buf, &txt_stat)) return (-1);
1056 
1057 	/* XXX Build filename of the corresponding *.raw file */
1058 	strnfmt(fname, sizeof(fname), "%s", template_file);
1059 
1060 	/* Find last '.' */
1061 	p = strrchr(fname, '.');
1062 
1063 	/* Can't happen */
1064 	if (p == NULL) return (-1);
1065 
1066 	/* Substitute ".raw" for ".txt" */
1067 	strcpy(p, ".raw");
1068 
1069 	/* Build the file name of the raw file */
1070 	path_build(buf, sizeof(buf), ANGBAND_DIR_DATA, fname);
1071 
1072 	/* XXX XXX XXX */
1073 	convert_pathname(buf);
1074 
1075 	/* Obtain modification time */
1076 	if (get_modification_time(buf, &raw_stat)) return (-1);
1077 
1078 	/* Ensure the text file is not newer than the raw file */
1079 	if (txt_stat > raw_stat) return (-1);
1080 
1081 	/* Keep using the current .raw file */
1082 	return (0);
1083 }
1084 
1085 # endif /* CHECK_MODIFICATION_TIME */
1086 
1087 #endif /* MAC_MPW */
1088 
1089 
1090 
1091 /*
1092  * Center a rectangle inside another rectangle
1093  *
1094  * Consider using RepositionWindow() whenever possible
1095  */
center_rect(Rect * r,Rect * s)1096 static void center_rect(Rect *r, Rect *s)
1097 {
1098 	int centerx = (s->left + s->right)/2;
1099 	int centery = (2*s->top + s->bottom)/3;
1100 	int dx = centerx - (r->right - r->left)/2 - r->left;
1101 	int dy = centery - (r->bottom - r->top)/2 - r->top;
1102 	r->left += dx;
1103 	r->right += dx;
1104 	r->top += dy;
1105 	r->bottom += dy;
1106 }
1107 
1108 
1109 /*
1110  * Activate a given window, if necessary
1111  */
activate(WindowPtr w)1112 static void activate(WindowPtr w)
1113 {
1114 	/* Activate */
1115 	if (active != w)
1116 	{
1117 		/* Activate */
1118 		if (w) SetPort(GetWindowPort(w));
1119 
1120 		/* Remember */
1121 		active = w;
1122 	}
1123 }
1124 
1125 
1126 /*
1127  * Display a warning message
1128  */
mac_warning(cptr warning)1129 static void mac_warning(cptr warning)
1130 {
1131 	Str255 text;
1132 	int len, i;
1133 
1134 	/* Limit of 250 chars */
1135 	len = strlen(warning);
1136 	if (len > 250) len = 250;
1137 
1138 	/* Make a "Pascal" string */
1139 	text[0] = len;
1140 	for (i=0; i<len; i++) text[i+1] = warning[i];
1141 
1142 	/* Prepare the dialog box values */
1143 	ParamText(text, "\p", "\p", "\p");
1144 
1145 	/* Display the Alert, wait for Okay */
1146 	Alert(129, 0L);
1147 }
1148 
1149 
1150 /*
1151  * Notice fully up-to-date status of the main window
1152  */
validate_main_window(void)1153 static void validate_main_window(void)
1154 {
1155 	WindowRef w;
1156 	Rect r;
1157 
1158 	/* Get the main window */
1159 	w = data[0].w;
1160 
1161 	/* Get its rectangle */
1162 	GetPortBounds(GetWindowPort(w), &r);
1163 
1164 	/* Validate it */
1165 	ValidWindowRect(w, &r);
1166 }
1167 
1168 
1169 
1170 /*** Some generic functions ***/
1171 
1172 /*
1173  * Update color_info with the current values in angband_color_table
1174  */
update_colour_info(void)1175 static void update_colour_info(void)
1176 {
1177 	int i;
1178 
1179 	/* Update colors */
1180 	for (i = 0; i < 256; i++)
1181 	{
1182 		u16b rv, gv, bv;
1183 
1184 		/* Extract the R,G,B data */
1185 		rv = angband_color_table[i][1];
1186 		gv = angband_color_table[i][2];
1187 		bv = angband_color_table[i][3];
1188 
1189 		/* Save the actual color */
1190 		color_info[i].red = (rv | (rv << 8));
1191 		color_info[i].green = (gv | (gv << 8));
1192 		color_info[i].blue = (bv | (bv << 8));
1193 	}
1194 }
1195 
1196 
1197 /*
1198  * Hack -- activate a color (0 to 255)
1199  */
term_data_color(term_data * td,int a)1200 static void term_data_color(term_data *td, int a)
1201 {
1202 	/* Activate the color */
1203 	if (td->last != a)
1204 	{
1205 		/* Activate the color */
1206 		RGBForeColor(&color_info[a]);
1207 
1208 		/* Memorize color */
1209 		td->last = a;
1210 	}
1211 }
1212 
1213 
1214 /*
1215  * Hack -- Apply and Verify the "font" info
1216  *
1217  * This should usually be followed by "term_data_check_size()"
1218  *
1219  * XXX XXX To force (re)initialisation of td->tile_wid and td->tile_hgt
1220  * you have to reset them to zero before this function is called.
1221  * XXX XXX This is automatic when the program starts because the term_data
1222  * array is WIPE'd by term_data_hack, but isn't in the other cases, i.e.
1223  * font, font style and size changes.
1224  */
term_data_check_font(term_data * td)1225 static void term_data_check_font(term_data *td)
1226 {
1227 	int i;
1228 
1229 	FontInfo info;
1230 
1231 	WindowPtr old = active;
1232 
1233 
1234 	/* Activate */
1235 	activate(td->w);
1236 
1237 	/* Instantiate font */
1238 	TextFont(td->font_id);
1239 	TextSize(td->font_size);
1240 	TextFace(td->font_face);
1241 
1242 	/* Extract the font info */
1243 	GetFontInfo(&info);
1244 
1245 	/* Assume monospaced */
1246 	td->font_mono = TRUE;
1247 
1248 	/* Extract the font sizing values XXX XXX XXX */
1249 	td->font_wid = CharWidth('@'); /* info.widMax; */
1250 	td->font_hgt = info.ascent + info.descent;
1251 	td->font_o_x = 0;
1252 	td->font_o_y = info.ascent;
1253 
1254 	/* Check important characters */
1255 	for (i = 33; i < 127; i++)
1256 	{
1257 		/* Hack -- notice non-mono-space */
1258 		if (td->font_wid != CharWidth(i)) td->font_mono = FALSE;
1259 
1260 		/* Hack -- collect largest width */
1261 		if (td->font_wid < CharWidth(i)) td->font_wid = CharWidth(i);
1262 	}
1263 
1264 	/* Set default offsets */
1265 	td->tile_o_x = td->font_o_x;
1266 	td->tile_o_y = td->font_o_y;
1267 
1268 	/* Set default tile size */
1269 	if (td->tile_wid == 0) td->tile_wid = td->font_wid;
1270 	if (td->tile_hgt == 0) td->tile_hgt = td->font_hgt;
1271 
1272 	/* Re-activate the old window */
1273 	activate(old);
1274 }
1275 
1276 
1277 /*
1278  * Hack -- Apply and Verify the "size" info
1279  */
term_data_check_size(term_data * td)1280 static void term_data_check_size(term_data *td)
1281 {
1282 	if (td == &data[0])
1283 	{
1284 #ifndef ALLOW_BIG_SCREEN
1285 
1286 		/* Forbid resizing of the Angband window */
1287 		td->cols = 80;
1288 		td->rows = 24;
1289 
1290 #else
1291 
1292 		/* Enforce minimal size */
1293 		if (td->cols < 80) td->cols = 80;
1294 		if (td->rows < 24) td->rows = 24;
1295 
1296 #endif /* !ALLOW_BIG_SCREEN */
1297 	}
1298 
1299 	/* Information windows can be much smaller */
1300 	else
1301 	{
1302 		if (td->cols < 1) td->cols = 1;
1303 		if (td->rows < 1) td->rows = 1;
1304 	}
1305 
1306 	/* Enforce maximal sizes */
1307 	if (td->cols > 255) td->cols = 255;
1308 	if (td->rows > 255) td->rows = 255;
1309 
1310 	/* Minimal tile size */
1311 	if (td->tile_wid < td->font_wid) td->tile_wid = td->font_wid;
1312 	if (td->tile_hgt < td->font_hgt) td->tile_hgt = td->font_hgt;
1313 
1314 	/* Default tile offsets */
1315 	td->tile_o_x = (td->tile_wid - td->font_wid) / 2;
1316 	td->tile_o_y = (td->tile_hgt - td->font_hgt) / 2;
1317 
1318 	/* Minimal tile offsets */
1319 	if (td->tile_o_x < 0) td->tile_o_x = 0;
1320 	if (td->tile_o_y < 0) td->tile_o_y = 0;
1321 
1322 	/* Apply font offsets */
1323 	td->tile_o_x += td->font_o_x;
1324 	td->tile_o_y += td->font_o_y;
1325 
1326 	/* Calculate full window size */
1327 	td->size_wid = td->cols * td->tile_wid + td->size_ow1 + td->size_ow2;
1328 	td->size_hgt = td->rows * td->tile_hgt + td->size_oh1 + td->size_oh2;
1329 
1330 	{
1331 		BitMap tScreen;
1332 
1333 		/* Get current screen */
1334 		(void)GetQDGlobalsScreenBits(&tScreen);
1335 
1336 		/* Verify the bottom */
1337 		if (td->r.top > tScreen.bounds.bottom - td->size_hgt)
1338 		{
1339 			td->r.top = tScreen.bounds.bottom - td->size_hgt;
1340 		}
1341 
1342 		/* Verify the top */
1343 		if (td->r.top < tScreen.bounds.top + GetMBarHeight())
1344 		{
1345 			td->r.top = tScreen.bounds.top + GetMBarHeight();
1346 		}
1347 
1348 		/* Verify the right */
1349 		if (td->r.left > tScreen.bounds.right - td->size_wid)
1350 		{
1351 			td->r.left = tScreen.bounds.right - td->size_wid;
1352 		}
1353 
1354 		/* Verify the left */
1355 		if (td->r.left < tScreen.bounds.left)
1356 		{
1357 			td->r.left = tScreen.bounds.left;
1358 		}
1359 	}
1360 
1361 	/* Calculate bottom right corner */
1362 	td->r.right = td->r.left + td->size_wid;
1363 	td->r.bottom = td->r.top + td->size_hgt;
1364 
1365 	/* Assume no graphics */
1366 	td->t->higher_pict = FALSE;
1367 	td->t->always_pict = FALSE;
1368 
1369 
1370 	/* Handle graphics */
1371 	if (use_graphics)
1372 	{
1373 		/* Use higher pict whenever possible */
1374 		if (td->font_mono) td->t->higher_pict = TRUE;
1375 
1376 		/* Use always_pict only when necessary */
1377 		else td->t->always_pict = TRUE;
1378 	}
1379 
1380 	/* Fake mono-space */
1381 	if (!td->font_mono ||
1382 	    (td->font_wid != td->tile_wid) ||
1383 		(td->font_hgt != td->tile_hgt))
1384 	{
1385 		/*
1386 		 * Handle fake monospace
1387 		 *
1388 		 * pelpel: This is SLOW. Couldn't we use CharExtra
1389 		 * and SpaceExtra for monospaced fonts?
1390 		 */
1391 		if (td->t->higher_pict) td->t->higher_pict = FALSE;
1392 		td->t->always_pict = TRUE;
1393 	}
1394 }
1395 
1396 
1397 /*
1398  * Hack -- resize a term_data
1399  *
1400  * This should normally be followed by "term_data_redraw()"
1401  */
term_data_resize(term_data * td)1402 static void term_data_resize(term_data *td)
1403 {
1404 	/*
1405 	 * Actually resize the window
1406 	 *
1407 	 * ResizeWindow is the preferred API call, but it cannot
1408 	 * be used here.
1409 	 */
1410 	SizeWindow(td->w, td->size_wid, td->size_hgt, 0);
1411 }
1412 
1413 
1414 
1415 /*
1416  * Hack -- redraw a term_data
1417  *
1418  * Note that "Term_redraw()" calls "TERM_XTRA_CLEAR"
1419  */
term_data_redraw(term_data * td)1420 static void term_data_redraw(term_data *td)
1421 {
1422 	term *old = Term;
1423 
1424 	/* Activate the term */
1425 	Term_activate(td->t);
1426 
1427 	/* Redraw the contents */
1428 	Term_redraw();
1429 
1430 	/* Flush the output */
1431 	Term_fresh();
1432 
1433 	/* Restore the old term */
1434 	Term_activate(old);
1435 }
1436 
1437 
1438 /*
1439  * Graphics support
1440  */
1441 
1442 /*
1443  * PICT id / file name of image tiles
1444  */
1445 #ifdef MACH_O_CARBON
1446 static CFStringRef pict_id = NULL;
1447 #else
1448 static int pict_id = 0;
1449 #endif /* MACH_O_CARBON */
1450 
1451 /*
1452  * Width and height of a tile in pixels
1453  */
1454 static int graf_width = 0;
1455 static int graf_height = 0;
1456 
1457 /*
1458  * Numbers of rows and columns in a tileset,
1459  * calculated by the PICT/PNG loading code
1460  */
1461 static int pict_cols = 0;
1462 static int pict_rows = 0;
1463 
1464 /*
1465  * Available graphics modes
1466  */
1467 #define GRAF_MODE_NONE	0	/* plain ASCII */
1468 #define GRAF_MODE_8X8	1	/* 8x8 tiles */
1469 #define GRAF_MODE_16X16	2	/* 16x16 tiles */
1470 #define GRAF_MODE_32X32	3	/* 32x32 tiles */
1471 
1472 /*
1473  * Current and requested graphics modes
1474  */
1475 static int graf_mode = GRAF_MODE_NONE;
1476 static int graf_mode_req = GRAF_MODE_NONE;
1477 
1478 /*
1479  * Available transparency effect modes:
1480  *   TR_NONE - no transparency effects
1481  *   TR_OVER - Overwriting with transparent black pixels
1482  * TR_OVER only works with 256 colour (8-bit) images if this file
1483  * is compiled to produce a PEF Carbon binary, while on Mach-O Carbon
1484  * it can handle much deeper pixels (verified with 16-bit and
1485  * 24-bit ones).
1486  */
1487 #define TR_NONE	0
1488 #define TR_OVER 1
1489 
1490 /*
1491  * Current transparency effect mode
1492  */
1493 static int transparency_mode = TR_NONE;
1494 
1495 
1496 /*
1497  * Forward Declare
1498  */
1499 typedef struct FrameRec FrameRec;
1500 
1501 /*
1502  * Frame
1503  *
1504  *	- GWorld for the frame image
1505  *	- Handle to pix map (saved for unlocking/locking)
1506  *	- Pointer to color pix map (valid only while locked)
1507  */
1508 struct FrameRec
1509 {
1510 	GWorldPtr 		framePort;
1511 	PixMapHandle 	framePixHndl;
1512 	PixMapPtr 		framePix;
1513 };
1514 
1515 
1516 /*
1517  * The global picture data
1518  */
1519 static FrameRec *frameP = NULL;
1520 
1521 
1522 /*
1523  * Lock a frame
1524  */
BenSWLockFrame(FrameRec * srcFrameP)1525 static void BenSWLockFrame(FrameRec *srcFrameP)
1526 {
1527 	PixMapHandle pixMapH;
1528 
1529 	pixMapH = GetGWorldPixMap(srcFrameP->framePort);
1530 	(void)LockPixels(pixMapH);
1531 	HLockHi((Handle)pixMapH);
1532 	srcFrameP->framePixHndl = pixMapH;
1533 	srcFrameP->framePix = (PixMapPtr)(*(Handle)pixMapH);
1534 }
1535 
1536 
1537 /*
1538  * Unlock a frame
1539  */
BenSWUnlockFrame(FrameRec * srcFrameP)1540 static void BenSWUnlockFrame(FrameRec *srcFrameP)
1541 {
1542 	if (srcFrameP->framePort != NULL)
1543 	{
1544 		HUnlock((Handle)srcFrameP->framePixHndl);
1545 		UnlockPixels(srcFrameP->framePixHndl);
1546 	}
1547 
1548 	srcFrameP->framePix = NULL;
1549 }
1550 
1551 
1552 
1553 #ifdef MACH_O_CARBON
1554 
1555 /*
1556  * Moving graphics resources into data fork -- pelpel
1557  *
1558  * (Carbon, Bundle)
1559  * Given base and type names of a resource, find a file in the
1560  * current application bundle and return its FSSpec in the third argument.
1561  * Returns true on success, false otherwise.
1562  * e.g. get_resource_spec(CFSTR("8x8"), CFSTR("png"), &spec);
1563  */
get_resource_spec(CFStringRef base_name,CFStringRef type_name,FSSpec * spec)1564 static Boolean get_resource_spec(
1565 	CFStringRef base_name, CFStringRef type_name, FSSpec *spec)
1566 {
1567 	CFURLRef res_url;
1568 	FSRef ref;
1569 
1570 	/* Find resource (=file) in the current bundle */
1571 	res_url = CFBundleCopyResourceURL(
1572 		CFBundleGetMainBundle(), base_name, type_name, NULL);
1573 
1574 	/* Oops */
1575 	if (res_url == NULL) return (false);
1576 
1577 	/* Convert CFURL to FSRef */
1578 	(void)CFURLGetFSRef(res_url, &ref);
1579 
1580 	/* Convert FSRef to FSSpec */
1581 	(void)FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL);
1582 
1583 	/* Free allocated CF data */
1584 	CFRelease(res_url);
1585 
1586 	/* Success */
1587 	return (true);
1588 }
1589 
1590 
1591 /*
1592  * (QuickTime)
1593  * Create an off-screen GWorld from contents of a file specified by a FSSpec.
1594  * Based on BenSWCreateGWorldFromPict.
1595  *
1596  * Globals referenced: data[0], graf_height, graf_width
1597  * Globals updated: pict_rows, pict_cols.
1598  */
create_gworld_from_spec(GWorldPtr * tile_gw,FSSpec * tile_spec)1599 static OSErr create_gworld_from_spec(
1600 	GWorldPtr *tile_gw, FSSpec *tile_spec)
1601 {
1602 	OSErr err;
1603 	GraphicsImportComponent gi;
1604 	GWorldPtr gw, tmp_gw;
1605 	GDHandle gdh, tmp_gdh;
1606 	Rect r;
1607 	SInt16 depth;
1608 
1609 	/* See if QuickTime understands the file format */
1610 	err = GetGraphicsImporterForFile(tile_spec, &gi);
1611 
1612 	/* Oops */
1613 	if (err != noErr) return (err);
1614 
1615 	/* Get depth */
1616 	depth = data[0].pixelDepth;
1617 
1618 	/* Get GDH */
1619 	gdh = data[0].theGDH;
1620 
1621 	/* Retrieve the rect of the image */
1622 	err = GraphicsImportGetNaturalBounds(gi, &r);
1623 
1624 	/* Adjust it, so that the upper left corner becomes (0, 0) */
1625 	OffsetRect(&r, -r.left, -r.top);
1626 
1627 	/* Calculate and set numbers of rows and columns */
1628 	pict_rows = r.bottom / graf_height;
1629 	pict_cols = r.right / graf_width;
1630 
1631 	/* Create a GWorld */
1632 	err = NewGWorld(&gw, depth, &r, NULL, gdh, noNewDevice);
1633 
1634 	/* Oops */
1635 	if (err != noErr) return (err);
1636 
1637 	/* Save the pointer to the GWorld */
1638 	*tile_gw = gw;
1639 
1640 	/* Save the current GWorld */
1641 	GetGWorld(&tmp_gw, &tmp_gdh);
1642 
1643 	/* Activate the newly created GWorld */
1644 	(void)GraphicsImportSetGWorld(gi, gw, NULL);
1645 
1646 	/* Prevent pixmap from moving while drawing */
1647 	(void)LockPixels(GetGWorldPixMap(gw));
1648 
1649 	/* Clear the pixels */
1650 	EraseRect(&r);
1651 
1652 	/* Draw the image into it */
1653 	(void)GraphicsImportDraw(gi);
1654 
1655 	/* Release the lock*/
1656 	UnlockPixels(GetGWorldPixMap(gw));
1657 
1658 	/* Restore GWorld */
1659 	SetGWorld(tmp_gw, tmp_gdh);
1660 
1661 	/* Close the image importer */
1662 	CloseComponent(gi);
1663 
1664 	/* Success */
1665 	return (noErr);
1666 }
1667 
1668 #else /* MACH_O_CARBON */
1669 
BenSWCreateGWorldFromPict(GWorldPtr * pictGWorld,PicHandle pictH)1670 static OSErr BenSWCreateGWorldFromPict(
1671 	GWorldPtr *pictGWorld, PicHandle pictH)
1672 {
1673 	OSErr err;
1674 	GWorldPtr saveGWorld;
1675 	GDHandle saveGDevice;
1676 	GWorldPtr tempGWorld;
1677 	Rect pictRect;
1678 	short depth;
1679 	GDHandle theGDH;
1680 
1681 	tempGWorld = NULL;
1682 
1683 	/* Reset */
1684 	*pictGWorld = NULL;
1685 
1686 	/* Get depth */
1687 	depth = data[0].pixelDepth;
1688 
1689 	/* Get GDH */
1690 	theGDH = data[0].theGDH;
1691 
1692 	/* Obtain size rectangle */
1693 	pictRect = (**pictH).picFrame;
1694 	OffsetRect(&pictRect, -pictRect.left, -pictRect.top);
1695 
1696 	/* Calculate and set numbers of rows and columns */
1697 	pict_rows = pictRect.bottom / graf_height;
1698 	pict_cols = pictRect.right / graf_width;
1699 
1700 	/* Create a GWorld */
1701 	err = NewGWorld(&tempGWorld, depth, &pictRect, nil, theGDH, noNewDevice);
1702 
1703 	/* Oops */
1704 	if (err != noErr) return (err);
1705 
1706 	/* Save pointer */
1707 	*pictGWorld = tempGWorld;
1708 
1709 	/* Save GWorld */
1710 	GetGWorld(&saveGWorld, &saveGDevice);
1711 
1712 	/* Activate */
1713 	SetGWorld(tempGWorld, nil);
1714 
1715 	/* Dump the pict into the GWorld */
1716 	(void)LockPixels(GetGWorldPixMap(tempGWorld));
1717 	EraseRect(&pictRect);
1718 	DrawPicture(pictH, &pictRect);
1719 	UnlockPixels(GetGWorldPixMap(tempGWorld));
1720 
1721 	/* Restore GWorld */
1722 	SetGWorld(saveGWorld, saveGDevice);
1723 
1724 	/* Success */
1725 	return (0);
1726 }
1727 
1728 #endif /* MACH_O_CARBON */
1729 
1730 
1731 /*
1732  * Init the global "frameP"
1733  */
globe_init(void)1734 static errr globe_init(void)
1735 {
1736 	OSErr err;
1737 
1738 	GWorldPtr tempPictGWorldP;
1739 
1740 #ifdef MACH_O_CARBON
1741 	FSSpec pict_spec;
1742 #else
1743 	PicHandle newPictH;
1744 #endif /* MACH_O_CARBON */
1745 
1746 
1747 	/* Use window XXX XXX XXX */
1748 	SetPort(GetWindowPort(data[0].w));
1749 
1750 
1751 #ifdef MACH_O_CARBON
1752 
1753 	/* Get the tile resources */
1754 	if (!get_resource_spec(pict_id, CFSTR("png"), &pict_spec)) return (-1);
1755 
1756 	/* Create GWorld */
1757 	err = create_gworld_from_spec(&tempPictGWorldP, &pict_spec);
1758 
1759 #else /* MACH_O_CARBON */
1760 
1761 	/* Get the pict resource */
1762 	if ((newPictH = GetPicture(pict_id)) == 0) return (-1);
1763 
1764 	/* Create GWorld */
1765 	err = BenSWCreateGWorldFromPict(&tempPictGWorldP, newPictH);
1766 
1767 	/* Release resource */
1768 	ReleaseResource((Handle)newPictH);
1769 
1770 #endif /* MACH_O_CARBON */
1771 
1772 	/* Error */
1773 	if (err != noErr) return (err);
1774 
1775 	/* Create the frame */
1776 	frameP = (FrameRec*)NewPtrClear((Size)sizeof(FrameRec));
1777 
1778 	/* Analyze result */
1779 	if (frameP == NULL)
1780 	{
1781 		/* Dispose of image GWorld */
1782 		DisposeGWorld(tempPictGWorldP);
1783 
1784 		/* Fake error code */
1785 		return (-1);
1786 	}
1787 
1788 	/* Save GWorld */
1789 	frameP->framePort = tempPictGWorldP;
1790 
1791 	/* Lock it */
1792 	BenSWLockFrame(frameP);
1793 
1794 	/* Success */
1795 	return (noErr);
1796 }
1797 
1798 
1799 /*
1800  * Nuke the global "frameP"
1801  */
globe_nuke(void)1802 static errr globe_nuke(void)
1803 {
1804 	/* Dispose */
1805 	if (frameP)
1806 	{
1807 		/* Unlock */
1808 		BenSWUnlockFrame(frameP);
1809 
1810 		/* Dispose of the GWorld */
1811 		DisposeGWorld(frameP->framePort);
1812 
1813 		/* Dispose of the memory */
1814 		DisposePtr((Ptr)frameP);
1815 
1816 		/* Forget */
1817 		frameP = NULL;
1818 	}
1819 
1820 	/* Flush events */
1821 	FlushEvents(everyEvent, 0);
1822 
1823 	/* Success */
1824 	return (0);
1825 }
1826 
1827 
1828 #ifdef USE_ASYNC_SOUND
1829 
1830 /*
1831  * Asynchronous sound player revised
1832  */
1833 #if defined(USE_QT_SOUND) && !defined(MACH_O_CARBON)
1834 # undef USE_QT_SOUND
1835 #endif /* USE_QT_SOUND && !MACH_O_CARBON */
1836 
1837 /*
1838  * How many sound channels will be pooled
1839  *
1840  * Was: 20, but I don't think we need 20 sound effects playing
1841  * simultaneously :) -- pelpel
1842  */
1843 #define MAX_CHANNELS		8
1844 
1845 /*
1846  * A pool of sound channels
1847  */
1848 static SndChannelPtr channels[MAX_CHANNELS];
1849 
1850 /*
1851  * Status of the channel pool
1852  */
1853 static Boolean channel_initialised = FALSE;
1854 
1855 /*
1856  * Data handles containing sound samples
1857  */
1858 static SndListHandle samples[MSG_MAX];
1859 
1860 /*
1861  * Reference counts of sound samples
1862  */
1863 static SInt16 sample_refs[MSG_MAX];
1864 
1865 #define SOUND_VOLUME_MIN	0	/* Default minimum sound volume */
1866 #define SOUND_VOLUME_MAX	255	/* Default maximum sound volume */
1867 #define VOLUME_MIN			0	/* Minimum sound volume in % */
1868 #define VOLUME_MAX			100	/* Maximum sound volume in % */
1869 #define VOLUME_INC			5	/* Increment sound volume in % */
1870 
1871 /*
1872  * I'm just too lazy to write a panel for this XXX XXX
1873  */
1874 static SInt16 sound_volume = SOUND_VOLUME_MAX;
1875 
1876 
1877 #ifdef USE_QT_SOUND
1878 
1879 /*
1880  * QuickTime sound, by Ron Anderson
1881  *
1882  * I didn't choose to use Windows-style .ini files (Ron wrote a parser
1883  * for it, but...), nor did I use lib/xtra directory, hoping someone
1884  * would code plist-based configuration code in the future -- pelpel
1885  */
1886 
1887 /*
1888  * (QuickTime)
1889  * Load sound effects from data-fork resources.  They are wav files
1890  * with the same names as angband_sound_name[] (variable.c)
1891  *
1892  * Globals referenced: angband_sound_name[]
1893  * Globals updated: samples[] (they can be *huge*)
1894  */
load_sounds(void)1895 static void load_sounds(void)
1896 {
1897 	OSErr err;
1898 	int i;
1899 
1900 	/* Start QuickTime */
1901 	err = EnterMovies();
1902 
1903 	/* Error */
1904 	if (err != noErr) return;
1905 
1906 	/*
1907 	 * This loop may take a while depending on the count and size of samples
1908 	 * to load.
1909 	 *
1910 	 * We should use a progress dialog for this.
1911 	 */
1912 	for (i = 1; i < MSG_MAX; i++)
1913 	{
1914 		/* Apple APIs always give me headacke :( */
1915 		CFStringRef name;
1916 		FSSpec spec;
1917 		SInt16 file_id;
1918 		SInt16 res_id;
1919 		Str255 movie_name;
1920 		Movie movie;
1921 		Track track;
1922 		Handle h;
1923 		Boolean res;
1924 
1925 		/* Allocate CFString with the name of sound event to be processed */
1926 		name = CFStringCreateWithCString(NULL, angband_sound_name[i],
1927 			kTextEncodingUS_ASCII);
1928 
1929 		/* Error */
1930 		if (name == NULL) continue;
1931 
1932 		/* Find sound sample resource with the same name */
1933 		res = get_resource_spec(name, CFSTR("wav"), &spec);
1934 
1935 		/* Free the reference to CFString */
1936 		CFRelease(name);
1937 
1938 		/* Error */
1939 		if (!res) continue;
1940 
1941 		/* Open the sound file */
1942 		err = OpenMovieFile(&spec, &file_id, fsRdPerm);
1943 
1944 		/* Error */
1945 		if (err != noErr) continue;
1946 
1947 		/* Create Movie from the file */
1948 		err = NewMovieFromFile(&movie, file_id, &res_id, movie_name,
1949 			newMovieActive, NULL);
1950 
1951 		/* Error */
1952 		if (err != noErr) goto close_file;
1953 
1954 		/* Get the first track of the movie */
1955 		track = GetMovieIndTrackType(movie, 1, AudioMediaCharacteristic,
1956 			movieTrackCharacteristic | movieTrackEnabledOnly );
1957 
1958 		/* Error */
1959 		if (track == NULL) goto close_movie;
1960 
1961 		/* Allocate a handle to store sample */
1962 		h = NewHandle(0);
1963 
1964 		/* Error */
1965 		if (h == NULL) goto close_track;
1966 
1967 		/* Dump the sample into the handle */
1968 		err = PutMovieIntoTypedHandle(movie, track, soundListRsrc, h, 0,
1969 			GetTrackDuration(track), 0L, NULL);
1970 
1971 		/* Success */
1972 		if (err == noErr)
1973 		{
1974 			/* Store the handle in the sample list */
1975 			samples[i] = (SndListHandle)h;
1976 		}
1977 
1978 		/* Failure */
1979 		else
1980 		{
1981 			/* Free unused handle */
1982 			DisposeHandle(h);
1983 		}
1984 
1985 		/* Free the track */
1986 close_track: DisposeMovieTrack(track);
1987 
1988 		/* Free the movie */
1989 close_movie: DisposeMovie(movie);
1990 
1991 		/* Close the movie file */
1992 close_file: CloseMovieFile(file_id);
1993 	}
1994 
1995 	/* Stop QuickTime */
1996 	ExitMovies();
1997 }
1998 
1999 #else /* USE_QT_SOUND */
2000 
2001 /*
2002  * Return a handle of 'snd ' resource given Angband sound event number,
2003  * or NULL if it isn't found.
2004  *
2005  * Globals referenced: angband_sound_name[] (variable.c)
2006  */
find_sound(int num)2007 static SndListHandle find_sound(int num)
2008 {
2009 	Str255 sound;
2010 
2011 	/* Get the proper sound name */
2012 	strnfmt((char*)sound + 1, 255, "%.16s.wav", angband_sound_name[num]);
2013 	sound[0] = strlen((char*)sound + 1);
2014 
2015 	/* Obtain resource XXX XXX XXX */
2016 	return ((SndListHandle)GetNamedResource('snd ', sound));
2017 }
2018 
2019 #endif /* USE_QT_SOUND */
2020 
2021 
2022 /*
2023  * Clean up sound support - to be called when the game exits.
2024  *
2025  * Globals referenced: channels[], samples[], sample_refs[].
2026  */
cleanup_sound(void)2027 static void cleanup_sound(void)
2028 {
2029 	int i;
2030 
2031 	/* No need to clean it up */
2032 	if (!channel_initialised) return;
2033 
2034 	/* Dispose channels */
2035 	for (i = 0; i < MAX_CHANNELS; i++)
2036 	{
2037 		/* Drain sound commands and free the channel */
2038 		SndDisposeChannel(channels[i], TRUE);
2039 	}
2040 
2041 	/* Free sound data */
2042 	for (i = 1; i < MSG_MAX; i++)
2043 	{
2044 		/* Still locked */
2045 		if ((sample_refs[i] > 0) && (samples[i] != NULL))
2046 		{
2047 			/* Unlock it */
2048 			HUnlock((Handle)samples[i]);
2049 		}
2050 
2051 #ifndef USE_QT_SOUND
2052 
2053 		/* Release it */
2054 		if (samples[i]) ReleaseResource((Handle)samples[i]);
2055 
2056 #else
2057 		/* Free handle */
2058 		if (samples[i]) DisposeHandle((Handle)samples[i]);
2059 
2060 #endif /* !USE_QT_SOUND */
2061 	}
2062 }
2063 
2064 
2065 /*
2066  * Play sound effects asynchronously -- pelpel
2067  *
2068  * I don't believe those who first started using the previous implementations
2069  * imagined this is *much* more complicated as it may seem.  Anyway,
2070  * introduced round-robin scheduling of channels and made it much more
2071  * paranoid about HLock/HUnlock.
2072  *
2073  * XXX XXX de-refcounting, HUnlock and ReleaseResource should be done
2074  * using channel's callback procedures, which set global flags, and
2075  * a procedure hooked into CheckEvents does housekeeping.  On the other
2076  * hand, this lazy reclaiming strategy keeps things simple (no interrupt
2077  * time code) and provides a sort of cache for sound data.
2078  *
2079  * Globals referenced: channel_initialised, channels[], samples[],
2080  *   sample_refs[].
2081  * Globals updated: channel_initialised, channels[], sample_refs[].
2082  *   Only in !USE_QT_SOUND, samples[].
2083  */
play_sound(int num,SInt16 vol)2084 static void play_sound(int num, SInt16 vol)
2085 {
2086 	OSErr err;
2087 	int i;
2088 	int prev_num;
2089 	SndListHandle h;
2090 	SndChannelPtr chan;
2091 	SCStatus status;
2092 
2093 	static int next_chan;
2094 	static SInt16 channel_occupants[MAX_CHANNELS];
2095 	static SndCommand volume_cmd, quiet_cmd;
2096 
2097 
2098 	/* Initialise sound channels */
2099 	if (!channel_initialised)
2100 	{
2101 		for (i = 0; i < MAX_CHANNELS; i++)
2102 		{
2103 			/* Paranoia - Clear occupant table */
2104 			/* channel_occupants[i] = 0; */
2105 
2106 			/* Create sound channel for all sounds to play from */
2107 			err = SndNewChannel(&channels[i], sampledSynth, initMono, NULL);
2108 
2109 			/* Error */
2110 			if (err != noErr)
2111 			{
2112 				/* Free channels */
2113 				while (--i >= 0)
2114 				{
2115 					SndDisposeChannel(channels[i], TRUE);
2116 				}
2117 
2118 				/* Notify error */
2119 				plog("Cannot initialise sound channels!");
2120 
2121 				/* Cancel request */
2122 				use_sound = arg_sound = FALSE;
2123 
2124 				/* Failure */
2125 				return;
2126 			}
2127 		}
2128 
2129 		/* First channel to use */
2130 		next_chan = 0;
2131 
2132 		/* Prepare volume command */
2133 		volume_cmd.cmd = volumeCmd;
2134 		volume_cmd.param1 = 0;
2135 		volume_cmd.param2 = 0;
2136 
2137 		/* Prepare quiet command */
2138 		quiet_cmd.cmd = quietCmd;
2139 		quiet_cmd.param1 = 0;
2140 		quiet_cmd.param2 = 0;
2141 
2142 		/* Initialisation complete */
2143 		channel_initialised = TRUE;
2144 	}
2145 
2146 	/* Paranoia */
2147 	if ((num <= 0) || (num >= MSG_MAX)) return;
2148 
2149 	/* Prepare volume command */
2150 	volume_cmd.param2 = ((SInt32)vol << 16) | vol;
2151 
2152 	/* Channel to use (round robin) */
2153 	chan = channels[next_chan];
2154 
2155 	/* See if the resource is already in use */
2156 	if (sample_refs[num] > 0)
2157 	{
2158 		/* Resource in use */
2159 		h = samples[num];
2160 
2161 		/* Increase the refcount */
2162 		sample_refs[num]++;
2163 	}
2164 
2165 	/* Sound is not currently in use */
2166 	else
2167 	{
2168 		/* Get handle for the sound */
2169 #ifdef USE_QT_SOUND
2170 		h = samples[num];
2171 #else
2172 		h = find_sound(num);
2173 #endif /* USE_QT_SOUND */
2174 
2175 		/* Sample not available */
2176 		if (h == NULL) return;
2177 
2178 #ifndef USE_QT_SOUND
2179 
2180 		/* Load resource */
2181 		LoadResource((Handle)h);
2182 
2183 		/* Remember it */
2184 		samples[num] = h;
2185 
2186 #endif /* !USE_QT_SOUND */
2187 
2188 		/* Lock the handle */
2189 		HLockHi((Handle)h);
2190 
2191 		/* Initialise refcount */
2192 		sample_refs[num] = 1;
2193 	}
2194 
2195 	/* Poll the channel */
2196 	err = SndChannelStatus(chan, sizeof(SCStatus), &status);
2197 
2198 	/* It isn't available */
2199 	if ((err != noErr) || status.scChannelBusy)
2200 	{
2201 		/* Shut it down */
2202 		SndDoImmediate(chan, &quiet_cmd);
2203 	}
2204 
2205 	/* Previously played sound on this channel */
2206 	prev_num = channel_occupants[next_chan];
2207 
2208 	/* Process previously played sound */
2209 	if (prev_num != 0)
2210 	{
2211 		/* Decrease refcount */
2212 		sample_refs[prev_num]--;
2213 
2214 		/* We can free it now */
2215 		if (sample_refs[prev_num] <= 0)
2216 		{
2217 			/* Unlock */
2218 			HUnlock((Handle)samples[prev_num]);
2219 
2220 #ifndef USE_QT_SOUND
2221 
2222 			/* Release */
2223 			ReleaseResource((Handle)samples[prev_num]);
2224 
2225 			/* Forget handle */
2226 			samples[prev_num] = NULL;
2227 
2228 #endif /* !USE_QT_SOUND */
2229 
2230 			/* Paranoia */
2231 			sample_refs[prev_num] = 0;
2232 		}
2233 	}
2234 
2235 	/* Remember this sound as the current occupant of the channel */
2236 	channel_occupants[next_chan] = num;
2237 
2238 	/* Set up volume for channel */
2239 	SndDoImmediate(chan, &volume_cmd);
2240 
2241 	/* Play new sound asynchronously */
2242 	SndPlay(chan, h, TRUE);
2243 
2244 	/* Schedule next channel (round robin) */
2245 	next_chan++;
2246 	if (next_chan >= MAX_CHANNELS) next_chan = 0;
2247 }
2248 
2249 #else /* USE_ASYNC_SOUND */
2250 
2251 /*
2252  * Synchronous sound effect player
2253  *
2254  * This may not be your choice, but much safer and much less
2255  * resource hungry.
2256  */
play_sound(int num,SInt16 vol)2257 static void play_sound(int num, SInt16 vol)
2258 {
2259 #pragma unused(vol)
2260 	Handle handle;
2261 	Str255 sound;
2262 
2263 	/* Get the proper sound name */
2264 	strnfmt((char*)sound + 1, 255, "%.16s.wav", angband_sound_name[num]);
2265 	sound[0] = strlen((char*)sound + 1);
2266 
2267 	/* Obtain resource XXX XXX XXX */
2268 	handle = GetNamedResource('snd ', sound);
2269 
2270 	/* Oops -- it is a failure, but we return 0 anyway */
2271 	if (handle == NULL) return;
2272 
2273 	/* Load and Lock */
2274 	LoadResource(handle);
2275 	HLockHi(handle);
2276 
2277 	/* Play sound (wait for completion) */
2278 	SndPlay(NULL, (SndListHandle)handle, FALSE);
2279 
2280 	/* Unlock and release */
2281 	HUnlock(handle);
2282 	ReleaseResource(handle);
2283 }
2284 
2285 #endif /* USE_ASYNC_SOUND */
2286 
2287 
2288 
2289 
2290 /*** Support for the "z-term.c" package ***/
2291 
2292 
2293 /*
2294  * Initialize a new Term
2295  *
2296  * Note also the "window type" called "noGrowDocProc", which might be more
2297  * appropriate for the main "screen" window.
2298  *
2299  * Note the use of "srcCopy" mode for optimized screen writes.
2300  */
Term_init_mac(term * t)2301 static void Term_init_mac(term *t)
2302 {
2303 	term_data *td = (term_data*)(t->data);
2304 	WindowAttributes wattrs;
2305 	OSStatus err;
2306 
2307 	static RGBColor black = {0x0000,0x0000,0x0000};
2308 	static RGBColor white = {0xFFFF,0xFFFF,0xFFFF};
2309 
2310 #ifndef ALLOW_BIG_SCREEN
2311 
2312 	/* Every window has close and collapse boxes */
2313 	wattrs = kWindowCloseBoxAttribute | kWindowCollapseBoxAttribute;
2314 
2315 	/* Information windows are resizable */
2316 	if (td != &data[0]) wattrs |= kWindowResizableAttribute;
2317 
2318 #else
2319 
2320 	/* Big screen - every window has close, collapse and resize boxes */
2321 	wattrs = kWindowCloseBoxAttribute |
2322 		kWindowCollapseBoxAttribute |
2323 		kWindowResizableAttribute;
2324 
2325 #endif /* !ALLOW_BIG_SCREEN */
2326 
2327 	/* Make the window  */
2328 	err = CreateNewWindow(
2329 			kDocumentWindowClass,
2330 			wattrs,
2331 			&td->r,
2332 			&td->w);
2333 
2334 	/* Fatal error */
2335 	if (err != noErr) ExitToShell();
2336 
2337 	/* Set refcon */
2338 	SetWRefCon(td->w, (long)td);
2339 
2340 	/* Set window title */
2341 	SetWTitle(td->w, td->title);
2342 
2343 	/* Activate the window */
2344 	activate(td->w);
2345 
2346 	/* Erase behind words */
2347 	TextMode(srcCopy);
2348 
2349 	/* Apply and Verify */
2350 	term_data_check_font(td);
2351 	term_data_check_size(td);
2352 
2353 	/* Resize the window */
2354 	term_data_resize(td);
2355 
2356 
2357 	/* Prepare the colors (real colors) */
2358 	RGBBackColor(&black);
2359 	RGBForeColor(&white);
2360 
2361 	/* Block */
2362 	{
2363 		Rect globalRect;
2364 		GDHandle mainGDH;
2365 		GDHandle currentGDH;
2366 		GWorldPtr windowGWorld;
2367 		PixMapHandle basePixMap;
2368 
2369 		/* Obtain the global rect */
2370 		GetWindowBounds((WindowRef)td->w, kWindowContentRgn, &globalRect);
2371 
2372 		/* Obtain the proper GDH */
2373 		mainGDH = GetMaxDevice(&globalRect);
2374 
2375 		/* Extract GWorld and GDH */
2376 		GetGWorld(&windowGWorld, &currentGDH);
2377 
2378 		/* Obtain base pixmap */
2379 		basePixMap = (**mainGDH).gdPMap;
2380 
2381 		/* Save pixel depth */
2382 		td->pixelDepth = (**basePixMap).pixelSize;
2383 
2384 		/* Save Window GWorld - unused */
2385 		/* td->theGWorld = windowGWorld; */
2386 
2387 		/* Save Window GDH */
2388 		td->theGDH = currentGDH;
2389 
2390 		/* Save main GDH - unused */
2391 		/* td->mainSWGDH = mainGDH; */
2392 	}
2393 
2394 	{
2395 		Rect portRect;
2396 
2397 		/* Get current Rect */
2398 		GetPortBounds(GetWindowPort(td->w), &portRect);
2399 
2400 		/* Clip to the window */
2401 		ClipRect(&portRect);
2402 
2403 		/* Erase the window */
2404 		EraseRect(&portRect);
2405 	}
2406 
2407 	/*
2408 	 * A certain release of OS X fails to display windows at proper
2409 	 * locations (_ _#)
2410 	 */
2411 	if ((mac_os_version >= 0x1000) && (mac_os_version < 0x1010))
2412 	{
2413 		/* Hack - Make sure the window is displayed at (r.left,r.top) */
2414 		MoveWindow(td->w, td->r.left, td->r.top, 1);
2415 	}
2416 
2417 	/* Display the window if needed */
2418 	if (td->mapped)
2419 	{
2420 		TransitionWindow(td->w,
2421 			kWindowZoomTransitionEffect, kWindowShowTransitionAction, NULL);
2422 	}
2423 
2424 	/* Hack -- set "mapped" flag */
2425 	t->mapped_flag = td->mapped;
2426 
2427 	/* Forget color */
2428 	td->last = -1;
2429 }
2430 
2431 
2432 
2433 /*
2434  * Nuke an old Term
2435  */
Term_nuke_mac(term * t)2436 static void Term_nuke_mac(term *t)
2437 {
2438 #pragma unused(t)
2439 	/* XXX */
2440 }
2441 
2442 
2443 
2444 /*
2445  * Unused
2446  */
Term_user_mac(int n)2447 static errr Term_user_mac(int n)
2448 {
2449 #pragma unused(n)
2450 	/* Success */
2451 	return (0);
2452 }
2453 
2454 
2455 
2456 /*
2457  * React to changes
2458  */
Term_xtra_mac_react(void)2459 static errr Term_xtra_mac_react(void)
2460 {
2461 	term_data *td = (term_data*)(Term->data);
2462 
2463 
2464 	/* Reset color */
2465 	td->last = -1;
2466 
2467 	/* Update colors */
2468 	update_colour_info();
2469 
2470 
2471 	/* Handle sound */
2472 	if (use_sound != arg_sound)
2473 	{
2474 		/* Apply request */
2475 		use_sound = arg_sound;
2476 	}
2477 
2478 	/* Don't actually switch graphics until the game is running */
2479 	if (!initialized || !game_in_progress) return (-1);
2480 
2481 	/* Handle graphics */
2482 	if (graf_mode_req != graf_mode)
2483 	{
2484 		/* dispose old GWorld's if present */
2485 		globe_nuke();
2486 
2487 		/*
2488 		 * Setup parameters according to request
2489 		 */
2490 		switch (graf_mode_req)
2491 		{
2492 			/* ASCII - no graphics whatsoever */
2493 			case GRAF_MODE_NONE:
2494 			{
2495 				use_graphics = arg_graphics = GRAPHICS_NONE;
2496 				transparency_mode = TR_NONE;
2497 				break;
2498 			}
2499 
2500 			/*
2501 			 * 8x8 tiles (PICT id 1001)
2502 			 * no transparency effect
2503 			 * "old" graphics definitions
2504 			 */
2505 			case GRAF_MODE_8X8:
2506 			{
2507 				use_graphics = arg_graphics = GRAPHICS_ORIGINAL;
2508 				transparency_mode = TR_NONE;
2509 #ifdef MACH_O_CARBON
2510 				pict_id = CFSTR("8x8");
2511 #else
2512 				pict_id = 1001;
2513 #endif /* MACH_O_CARBON */
2514 				graf_width = graf_height = 8;
2515 				break;
2516 			}
2517 
2518 			/*
2519 			 * 16x16 tiles (images: PICT id 1002, masks: PICT id 1003)
2520 			 * with transparency effect
2521 			 * "new" graphics definitions
2522 			 */
2523 			case GRAF_MODE_16X16:
2524 			{
2525 				use_graphics = arg_graphics = GRAPHICS_ADAM_BOLT;
2526 				transparency_mode = TR_OVER;
2527 #ifdef MACH_O_CARBON
2528 				pict_id = CFSTR("16x16");
2529 #else
2530 				pict_id = 1002;
2531 #endif /* MACH_O_CARBON */
2532 				graf_width = graf_height = 16;
2533 				break;
2534 			}
2535 
2536 			/*
2537 			 * 32x32 tiles (images: PICT id 1004)
2538 			 * with transparency effect
2539 			 * "david" graphics definitions
2540 			 * Vanilla-specific
2541 			 */
2542 			case GRAF_MODE_32X32:
2543 			{
2544 				use_graphics = arg_graphics = GRAPHICS_DAVID_GERVAIS;
2545 				transparency_mode = TR_OVER;
2546 #ifdef MACH_O_CARBON
2547 				pict_id = CFSTR("32x32");
2548 #else
2549 				pict_id = 1004;
2550 #endif /* MACH_O_CARBON */
2551 				graf_width = graf_height = 32;
2552 				break;
2553 			}
2554 		}
2555 
2556 		/* load tiles and setup GWorlds if tiles are requested */
2557 		if ((graf_mode_req != GRAF_MODE_NONE) && (globe_init() != 0))
2558 		{
2559 			/* Oops */
2560 			plog("Cannot initialize graphics!");
2561 
2562 			/* reject request */
2563 			graf_mode_req = GRAF_MODE_NONE;
2564 
2565 			/* reset graphics flags */
2566 			use_graphics = arg_graphics = GRAPHICS_NONE;
2567 
2568 			/* reset transparency mode */
2569 			transparency_mode = TR_NONE;
2570 		}
2571 
2572 		/* update current graphics mode */
2573 		graf_mode = graf_mode_req;
2574 
2575 		/* Apply and Verify */
2576 		term_data_check_size(td);
2577 
2578 		/* Resize the window */
2579 		term_data_resize(td);
2580 
2581 		/* Reset visuals */
2582 		if (initialized && game_in_progress)
2583 		{
2584 #ifndef ANG281_RESET_VISUALS
2585 			reset_visuals(TRUE);
2586 #else
2587 			reset_visuals();
2588 #endif /* !ANG281_RESET_VISUALS */
2589 		}
2590 	}
2591 
2592 	/* Success */
2593 	return (0);
2594 }
2595 
2596 
2597 /*
2598  * Do a "special thing"
2599  */
Term_xtra_mac(int n,int v)2600 static errr Term_xtra_mac(int n, int v)
2601 {
2602 	term_data *td = (term_data*)(Term->data);
2603 
2604 	Rect r;
2605 
2606 	/* Analyze */
2607 	switch (n)
2608 	{
2609 		/* Make a noise */
2610 		case TERM_XTRA_NOISE:
2611 		{
2612 			/* Make a noise */
2613 			SysBeep(1);
2614 
2615 			/* Success */
2616 			return (0);
2617 		}
2618 
2619 		/* Make a sound */
2620 		case TERM_XTRA_SOUND:
2621 		{
2622 			/* Play sound */
2623 			play_sound(v, sound_volume);
2624 
2625 			/* Success */
2626 			return (0);
2627 		}
2628 
2629 		/* Process random events */
2630 		case TERM_XTRA_BORED:
2631 		{
2632 			/* Process an event */
2633 			(void)CheckEvents(CHECK_EVENTS_NO_WAIT);
2634 
2635 			/* Success */
2636 			return (0);
2637 		}
2638 
2639 		/* Process pending events */
2640 		case TERM_XTRA_EVENT:
2641 		{
2642 			/* Process an event */
2643 			(void)CheckEvents(v);
2644 
2645 			/* Success */
2646 			return (0);
2647 		}
2648 
2649 		/* Flush all pending events (if any) */
2650 		case TERM_XTRA_FLUSH:
2651 		{
2652 			/* Hack -- flush all events */
2653 			while (CheckEvents(CHECK_EVENTS_DRAIN)) /* loop */;
2654 
2655 			/* Success */
2656 			return (0);
2657 		}
2658 
2659 		/* Hack -- Change the "soft level" */
2660 		case TERM_XTRA_LEVEL:
2661 		{
2662 			/* Activate if requested */
2663 			if (v) activate(td->w);
2664 
2665 			/* Success */
2666 			return (0);
2667 		}
2668 
2669 #if 0
2670 		/* Clear the screen */
2671 		case TERM_XTRA_CLEAR:
2672 		{
2673 			Rect portRect;
2674 
2675 			/* Get current Rect */
2676 			GetPortBounds(GetWindowPort(td->w), &portRect);
2677 
2678 			/* No clipping XXX XXX XXX */
2679 			ClipRect(&portRect);
2680 
2681 			/* Erase the window */
2682 			EraseRect(&portRect);
2683 
2684 			/* Set the color */
2685 			term_data_color(td, TERM_WHITE);
2686 
2687 			/* Frame the window in white */
2688 			MoveTo(0, 0);
2689 			LineTo(0, td->size_hgt-1);
2690 			LineTo(td->size_wid-1, td->size_hgt-1);
2691 			LineTo(td->size_wid-1, 0);
2692 
2693 			/* Clip to the new size */
2694 			r.left = portRect.left + td->size_ow1;
2695 			r.top = portRect.top + td->size_oh1;
2696 			r.right = portRect.right - td->size_ow2;
2697 			r.bottom = portRect.bottom - td->size_oh2;
2698 			ClipRect(&r);
2699 
2700 			/* Success */
2701 			return (0);
2702 		}
2703 #endif /* 0 */
2704 
2705 		/* React to changes */
2706 		case TERM_XTRA_REACT:
2707 		{
2708 			/* React to changes */
2709 			return (Term_xtra_mac_react());
2710 		}
2711 
2712 		/* Delay (milliseconds) */
2713 		case TERM_XTRA_DELAY:
2714 		{
2715 			/*
2716 			 * WaitNextEvent relinquishes CPU as well as
2717 			 * induces a screen refresh on OS X
2718 			 */
2719 
2720 			/* If needed */
2721 			if (v > 0)
2722 			{
2723 				EventRecord tmp;
2724 				UInt32 ticks;
2725 
2726 				/* Convert millisecs to ticks */
2727 				ticks = (v * 60L) / 1000;
2728 
2729 				/*
2730 				 * Hack? - Put the programme into sleep.
2731 				 * No events match ~everyEvent, so nothing
2732 				 * should be lost in Angband's event queue.
2733 				 * Even if ticks are 0, it's worth calling for
2734 				 * the above mentioned reasons.
2735 				 */
2736 				WaitNextEvent((EventMask)~everyEvent, &tmp, ticks, nil);
2737 			}
2738 
2739 			/* Success */
2740 			return (0);
2741 		}
2742 	}
2743 
2744 	/* Oops */
2745 	return (1);
2746 }
2747 
2748 
2749 
2750 /*
2751  * Low level graphics (Assumes valid input).
2752  * Draw a "cursor" at (x,y), using a "yellow box".
2753  * We are allowed to use "Term_what()" to determine
2754  * the current screen contents (for inverting, etc).
2755  */
Term_curs_mac(int x,int y)2756 static errr Term_curs_mac(int x, int y)
2757 {
2758 	Rect r;
2759 
2760 	term_data *td = (term_data*)(Term->data);
2761 
2762 	/* Set the color */
2763 	term_data_color(td, TERM_YELLOW);
2764 
2765 	/* Frame the grid */
2766 	r.left = x * td->tile_wid + td->size_ow1;
2767 	r.right = r.left + td->tile_wid;
2768 	r.top = y * td->tile_hgt + td->size_oh1;
2769 	r.bottom = r.top + td->tile_hgt;
2770 	FrameRect(&r);
2771 
2772 	/* Success */
2773 	return (0);
2774 }
2775 
2776 
2777 #ifdef USE_DOUBLE_TILES
2778 
2779 /*
2780  * Low level graphics (Assumes valid input).
2781  * Draw a "cursor" at (x,y), using a "yellow box", twice the width of
2782  * the current font.
2783  * We are allowed to use "Term_what()" to determine
2784  * the current screen contents (for inverting, etc).
2785  */
Term_bigcurs_mac(int x,int y)2786 static errr Term_bigcurs_mac(int x, int y)
2787 {
2788 	Rect r;
2789 
2790 	term_data *td = (term_data*)(Term->data);
2791 
2792 	/* Set the color */
2793 	term_data_color(td, TERM_YELLOW);
2794 
2795 	/* Frame the grid */
2796 	r.left = x * td->tile_wid + td->size_ow1;
2797 	r.right = r.left + 2 * td->tile_wid;
2798 	r.top = y * td->tile_hgt + td->size_oh1;
2799 	r.bottom = r.top + td->tile_hgt;
2800 	FrameRect(&r);
2801 
2802 	/* Success */
2803 	return (0);
2804 }
2805 
2806 #endif /* USE_DOUBLE_TILES */
2807 
2808 
2809 #ifdef OVERWRITE_HACK
2810 
2811 /*
2812  * Low level graphics helper (Assumes valid input)
2813  *
2814  * Based on suggestion by Julian Lighton
2815  *
2816  * Overwrite "n" old characters starting at	(x,y)
2817  * with the same ones in the background colour
2818  */
Term_wipe_mac_aux(int x,int y,int n)2819 static void Term_wipe_mac_aux(int x, int y, int n)
2820 {
2821 	term_data *td = (term_data*)(Term->data);
2822 
2823 	int xp, yp;
2824 
2825 	const char *cp;
2826 
2827 	static RGBColor black = {0x0000,0x0000,0x0000};
2828 
2829 
2830 	/* Hack - Black, blacker, blackest-- */
2831 	if (td->last != -2) RGBForeColor(&black);
2832 
2833 	/* Hack - force later RGBForeColor switching */
2834 	td->last = -2;
2835 
2836 	/* Mega-Hack - use old screen image kept inside the term package */
2837 	cp = &(Term->old->c[y][x]);
2838 
2839 	/* Starting pixel */
2840 	xp = x * td->tile_wid + td->tile_o_x + td->size_ow1;
2841 	yp = y * td->tile_hgt + td->tile_o_y + td->size_oh1;
2842 
2843 	/* Move to the correct location */
2844 	MoveTo(xp, yp);
2845 
2846 	/* Draw the character */
2847 	if (n == 1) DrawChar(*cp);
2848 
2849 	/* Draw the string */
2850 	else DrawText(cp, 0, n);
2851 }
2852 
2853 #endif /* OVERWRITE_HACK */
2854 
2855 
2856 /*
2857  * Low level graphics (Assumes valid input)
2858  *
2859  * Erase "n" characters starting at (x,y)
2860  */
Term_wipe_mac(int x,int y,int n)2861 static errr Term_wipe_mac(int x, int y, int n)
2862 {
2863 	Rect r;
2864 
2865 	term_data *td = (term_data*)(Term->data);
2866 
2867 
2868 #ifdef OVERWRITE_HACK
2869 
2870 	/*
2871 	 * Hack - overstrike the leftmost character with
2872 	 * the background colour. This doesn't interfere with
2873 	 * the graphics modes, because they set always_pict.
2874 	 */
2875 	Term_wipe_mac_aux(x, y, 1);
2876 
2877 #endif /* OVERWRITE_HACK */
2878 
2879 	/* Erase the block of characters */
2880 	r.left = x * td->tile_wid + td->size_ow1;
2881 	r.right = r.left + n * td->tile_wid;
2882 	r.top = y * td->tile_hgt + td->size_oh1;
2883 	r.bottom = r.top + td->tile_hgt;
2884 	EraseRect(&r);
2885 
2886 	/* Success */
2887 	return (0);
2888 }
2889 
2890 
2891 /*
2892  * Low level graphics.  Assumes valid input.
2893  *
2894  * Draw several ("n") chars, with an attr, at a given location.
2895  */
Term_text_mac(int x,int y,int n,byte a,const char * cp)2896 static errr Term_text_mac(int x, int y, int n, byte a, const char *cp)
2897 {
2898 	int xp, yp;
2899 
2900 #ifdef CLIP_HACK
2901 	Rect r;
2902 #endif /* CLIP_HACK */
2903 
2904 	term_data *td = (term_data*)(Term->data);
2905 
2906 
2907 #ifdef OVERWRITE_HACK
2908 	/* Hack - overstrike with background colour. Is 1 enough? */
2909 	Term_wipe_mac_aux(x, y, n);
2910 #endif /* OVERWRITE_HACK */
2911 
2912 	/* Set the color */
2913 	term_data_color(td, a);
2914 
2915 #ifdef CLIP_HACK
2916 	/* Hack - only draw within the bounding rect */
2917 	r.left = x * td->tile_wid + td->size_ow1;
2918 	r.right = r.left + n * td->tile_wid;
2919 	r.top = y * td->tile_hgt + td->size_oh1;
2920 	r.bottom = r.top + td->tile_hgt;
2921 	ClipRect(&r);
2922 
2923 	/* Hack - clear the content of the bounding rect */
2924 	EraseRect(&r);
2925 #endif /* CLIP_HACK */
2926 
2927 	/* Starting pixel */
2928 	xp = x * td->tile_wid + td->tile_o_x + td->size_ow1;
2929 	yp = y * td->tile_hgt + td->tile_o_y + td->size_oh1;
2930 
2931 	/* Move to the correct location */
2932 	MoveTo(xp, yp);
2933 
2934 	/* Draw the character */
2935 	if (n == 1) DrawChar(*cp);
2936 
2937 	/* Draw the string */
2938 	else DrawText(cp, 0, n);
2939 
2940 #ifdef CLIP_HACK
2941 	/* Obtain current window's rect */
2942 	GetPortBounds(GetWindowPort(td->w), &r);
2943 
2944 	/* Clip to the window again */
2945 	ClipRect(&r);
2946 #endif /* CLIP_HACK */
2947 
2948 	/* Success */
2949 	return (0);
2950 }
2951 
2952 
2953 /*
2954  * Low level graphics (Assumes valid input)
2955  *
2956  * Erase "n" characters starting at (x,y)
2957  */
2958 #ifdef USE_TRANSPARENCY
Term_pict_mac(int x,int y,int n,const byte * ap,const char * cp,const byte * tap,const char * tcp)2959 static errr Term_pict_mac(int x, int y, int n, const byte *ap, const char *cp,
2960 			  const byte *tap, const char *tcp)
2961 #else /* USE_TRANSPARENCY */
2962 static errr Term_pict_mac(int x, int y, int n, const byte *ap, const char *cp)
2963 #endif /* USE_TRANSPARENCY */
2964 {
2965 	int i;
2966 	Rect dst_r;
2967 	GrafPtr port;
2968 	PixMapHandle pixmap_h;
2969 
2970 #ifdef CLIP_HACK
2971 	Rect portRect;
2972 #endif /* CLIP_HACK */
2973 
2974 	term_data *td = (term_data*)(Term->data);
2975 
2976 	static RGBColor black = {0x0000,0x0000,0x0000};
2977 	static RGBColor white = {0xFFFF,0xFFFF,0xFFFF};
2978 
2979 
2980 #ifdef CLIP_HACK
2981 	/* Remember current window's rect */
2982 	GetPortBounds(GetWindowPort(td->w), &portRect);
2983 #endif /* CLIP_HACK */
2984 
2985 	/* Destination rectangle */
2986 	dst_r.left = x * td->tile_wid + td->size_ow1;
2987 #ifndef USE_DOUBLE_TILES
2988 	dst_r.right = dst_r.left + td->tile_wid;
2989 #endif /* !USE_DOUBLE_TILES */
2990 	dst_r.top = y * td->tile_hgt + td->size_oh1;
2991 	dst_r.bottom = dst_r.top + td->tile_hgt;
2992 
2993 	/* Scan the input */
2994 	for (i = 0; i < n; i++)
2995 	{
2996 		byte a = *ap++;
2997 		char c = *cp++;
2998 
2999 #ifdef USE_TRANSPARENCY
3000 		byte ta = *tap++;
3001 		char tc = *tcp++;
3002 #endif
3003 
3004 
3005 #ifdef USE_DOUBLE_TILES
3006 
3007 		/* Hack -- a filler for double-width tile */
3008 		if (use_bigtile && (a == 255))
3009 		{
3010 			/* Advance */
3011 			dst_r.left += td->tile_wid;
3012 
3013 			/* Ignore */
3014 			continue;
3015 		}
3016 
3017 		/* Prepare right side of rectagle now */
3018 		dst_r.right = dst_r.left + td->tile_wid;
3019 
3020 #endif /* USE_DOUBLE_TILES */
3021 
3022 		/* Graphics -- if Available and Needed */
3023 		if (use_graphics && ((byte)a & 0x80) && ((byte)c & 0x80))
3024 		{
3025 			int col, row;
3026 			Rect src_r;
3027 #ifdef USE_TRANSPARENCY
3028 			int t_col, t_row;
3029 			Rect terrain_r;
3030 #endif /* USE_TRANSPARENCY */
3031 
3032 			/* Row and Col */
3033 			row = ((byte)a & 0x7F) % pict_rows;
3034 			col = ((byte)c & 0x7F) % pict_cols;
3035 
3036 			/* Source rectangle */
3037 			src_r.left = col * graf_width;
3038 			src_r.top = row * graf_height;
3039 			src_r.right = src_r.left + graf_width;
3040 			src_r.bottom = src_r.top + graf_height;
3041 
3042 #ifdef USE_TRANSPARENCY
3043 			/* Row and Col */
3044 			t_row = ((byte)ta & 0x7F) % pict_rows;
3045 			t_col = ((byte)tc & 0x7F) % pict_cols;
3046 
3047 			/* Source rectangle */
3048 			terrain_r.left = t_col * graf_width;
3049 			terrain_r.top = t_row * graf_height;
3050 			terrain_r.right = terrain_r.left + graf_width;
3051 			terrain_r.bottom = terrain_r.top + graf_height;
3052 #endif /* USE_TRANSPARENCY */
3053 
3054 			/* Hardwire CopyBits */
3055 			RGBBackColor(&white);
3056 			RGBForeColor(&black);
3057 
3058 #ifdef USE_DOUBLE_TILES
3059 
3060 			/* Double width tiles */
3061 			if (use_bigtile) dst_r.right += td->tile_wid;
3062 
3063 #endif /* USE_DOUBLE_TILES */
3064 
3065 			/*
3066 			 * OS X requires locking and unlocking of window port
3067 			 * when we draw directly to its pixmap.
3068 			 * The Lock/Unlock protocol is described in the Carbon
3069 			 * Porting Guide.
3070 			 */
3071 
3072 			/* Obtain current window's graphic port */
3073 			port = GetWindowPort(td->w);
3074 
3075 			/* Lock pixels, so we can use handle safely */
3076 			LockPortBits(port);
3077 
3078 			/* Get Pixmap handle */
3079 			pixmap_h = GetPortPixMap(port);
3080 
3081 #ifdef USE_TRANSPARENCY
3082 
3083 			/* Transparency effect */
3084 			switch (transparency_mode)
3085 			{
3086 				/* No transparency effects */
3087 				case TR_NONE:
3088 				default:
3089 				{
3090 					/* Draw the picture */
3091 					CopyBits((BitMap*)frameP->framePix,
3092 						(BitMap*)*pixmap_h,
3093 						&src_r, &dst_r, srcCopy, NULL);
3094 
3095 					break;
3096 				}
3097 
3098 				/* Overwriting with transparent black pixels */
3099 				case TR_OVER:
3100 				{
3101 					/* Draw the terrain */
3102 					CopyBits((BitMap*)frameP->framePix,
3103 						 (BitMap*)*pixmap_h,
3104 						 &terrain_r, &dst_r, srcCopy, NULL);
3105 
3106 					/* There's something on the terrain */
3107 					if ((row != t_row) || (col != t_col))
3108 					{
3109 						/* Make black pixels transparent */
3110 						RGBBackColor(&black);
3111 
3112 						/* Draw monster/object/player */
3113 						CopyBits((BitMap*)frameP->framePix,
3114 							(BitMap*)*pixmap_h,
3115 							&src_r, &dst_r, transparent, NULL);
3116 					}
3117 
3118 					break;
3119 				}
3120 			}
3121 
3122 #else /* USE_TRANSPARENCY */
3123 
3124 			/* Draw the picture */
3125 			CopyBits((BitMap*)frameP->framePix,
3126 				(BitMap*)*pixmap_h,
3127 				&src_r, &dst_r, srcCopy, NULL);
3128 
3129 #endif /* USE_TRANSPARENCY */
3130 
3131 			/* Release the lock and dispose the PixMap handle */
3132 			UnlockPortBits(port);
3133 
3134 			/* Restore colors */
3135 			RGBBackColor(&black);
3136 			RGBForeColor(&white);
3137 
3138 			/* Forget color */
3139 			td->last = -1;
3140 		}
3141 
3142 		/*
3143 		 * Deal with these cases:
3144 		 * (1) the player changed tile width / height, or
3145 		 * (2) fake fixed-width for proportional font
3146 		 */
3147 		else
3148 		{
3149 			int xp, yp;
3150 
3151 #ifdef OVERWRITE_HACK
3152 			/*
3153 			 * Hack - overstrike with the background colour
3154 			 * Utterly useless in the graphics mode, but it only affects
3155 			 * performance slightly...
3156 			 */
3157 			Term_wipe_mac_aux(x+i, y, 1);
3158 #endif /* OVERWRITE_HACK */
3159 
3160 #ifdef CLIP_HACK
3161 			/* Hack - avoid writing outside of dst_r */
3162 			ClipRect(&dst_r);
3163 			/* Some characters do not match dst_r, therefore we have to... */
3164 #endif /* CLIP_HACK */
3165 
3166 			/* Erase */
3167 			EraseRect(&dst_r);
3168 
3169 			/* Set the color */
3170 			term_data_color(td, a);
3171 
3172 			/* Starting pixel */
3173 			xp = dst_r.left + td->tile_o_x;
3174 			yp = dst_r.top + td->tile_o_y;
3175 
3176 			/* Move to the correct location */
3177 			MoveTo(xp, yp);
3178 
3179 			/* Draw the character */
3180 			DrawChar(c);
3181 
3182 #ifdef CLIP_HACK
3183 			/* Clip to the window - inefficient (; ;) XXX XXX */
3184 			ClipRect(&portRect);
3185 #endif /* CLIP_HACK */
3186 		}
3187 
3188 		/* Advance */
3189 		dst_r.left += td->tile_wid;
3190 #ifndef USE_DOUBLE_TILES
3191 		dst_r.right += td->tile_wid;
3192 #endif /* !USE_DOUBLE_TILES */
3193 	}
3194 
3195 	/* Success */
3196 	return (0);
3197 }
3198 
3199 
3200 
3201 
3202 
3203 /*
3204  * Create and initialize window number "i"
3205  */
term_data_link(int i)3206 static void term_data_link(int i)
3207 {
3208 	term *old = Term;
3209 
3210 	term_data *td = &data[i];
3211 
3212 	/* Only once */
3213 	if (td->t) return;
3214 
3215 	/* Require mapped */
3216 	if (!td->mapped) return;
3217 
3218 	/* Allocate */
3219 	MAKE(td->t, term);
3220 
3221 	/* Initialize the term */
3222 	term_init(td->t, td->cols, td->rows, td->keys);
3223 
3224 	/* Use a "software" cursor */
3225 	td->t->soft_cursor = TRUE;
3226 
3227 	/*
3228 	 * HACK - We have an "icky" lower right corner, since
3229 	 * the window resize control is placed there
3230 	 */
3231 	td->t->icky_corner = TRUE;
3232 
3233 	/* Erase with "white space" */
3234 	td->t->attr_blank = TERM_WHITE;
3235 	td->t->char_blank = ' ';
3236 
3237 	/* Prepare the init/nuke hooks */
3238 	td->t->init_hook = Term_init_mac;
3239 	td->t->nuke_hook = Term_nuke_mac;
3240 
3241 	/* Prepare the function hooks */
3242 	td->t->user_hook = Term_user_mac;
3243 	td->t->xtra_hook = Term_xtra_mac;
3244 	td->t->wipe_hook = Term_wipe_mac;
3245 	td->t->curs_hook = Term_curs_mac;
3246 #ifdef USE_DOUBLE_TILES
3247 	td->t->bigcurs_hook = Term_bigcurs_mac;
3248 #endif /* USE_DOUBLE_TILES */
3249 	td->t->text_hook = Term_text_mac;
3250 	td->t->pict_hook = Term_pict_mac;
3251 
3252 #if 0
3253 
3254 	/* Doesn't make big difference? */
3255 	td->t->never_bored = TRUE;
3256 
3257 #endif
3258 
3259 	/* Link the local structure */
3260 	td->t->data = (void *)(td);
3261 
3262 	/* Activate it */
3263 	Term_activate(td->t);
3264 
3265 	/* Global pointer */
3266 	angband_term[i] = td->t;
3267 
3268 	/* Activate old */
3269 	Term_activate(old);
3270 }
3271 
3272 
3273 
3274 
3275 #ifdef MACH_O_CARBON
3276 
3277 /*
3278  * (Carbon, Bundle)
3279  * Return a POSIX pathname of the lib directory, or NULL if it can't be
3280  * located.  Caller must supply a buffer along with its size in bytes,
3281  * where returned pathname will be stored.
3282  */
locate_lib(char * buf,size_t size)3283 static char *locate_lib(char *buf, size_t size)
3284 {
3285 	CFBundleRef main_bundle;
3286 	CFURLRef main_url;
3287 	bool success;
3288 
3289 	/* Get the application bundle (Angband.app) */
3290 	main_bundle = CFBundleGetMainBundle();
3291 
3292 	/* Oops */
3293 	if (!main_bundle) return (NULL);
3294 
3295 	/* Obtain the URL of the main bundle */
3296 	main_url = CFBundleCopyBundleURL(main_bundle);
3297 
3298 	/* Oops */
3299 	if (!main_url) return (NULL);
3300 
3301 	/* Get the URL in the file system's native string representation */
3302 	success = CFURLGetFileSystemRepresentation(main_url, TRUE, buf, size);
3303 
3304 	/* Free the url */
3305 	CFRelease(main_url);
3306 
3307 	/* Oops */
3308 	if (!success) return (NULL);
3309 
3310 	/* Append "/Contents/Resources/lib/" */
3311 	my_strcat(buf, "/Contents/Resources/lib/", size);
3312 
3313 	return (buf);
3314 }
3315 
3316 
3317 #else /* MACH_O_CARBON */
3318 
3319 /*
3320  * Set the "current working directory" (also known as the "default"
3321  * volume/directory) to the location of the current application.
3322  *
3323  * Original code by: Maarten Hazewinkel (mmhazewi@cs.ruu.nl)
3324  *
3325  * Completely rewritten to use Carbon Process Manager.  It retrieves the
3326  * volume and directory of the current application and simply stores it
3327  * in the (static) global variables app_vol and app_dir, but doesn't
3328  * mess with the "current working directory", because it has long been
3329  * an obsolete (and arcane!) feature.
3330  */
SetupAppDir(void)3331 static void SetupAppDir(void)
3332 {
3333 	OSErr err;
3334 	ProcessSerialNumber curPSN;
3335 	ProcessInfoRec procInfo;
3336 	FSSpec cwdSpec;
3337 
3338 	/* Initialise PSN info for the current process */
3339 	curPSN.highLongOfPSN = 0;
3340 	curPSN.lowLongOfPSN = kCurrentProcess;
3341 
3342 	/* Fill in mandatory fields */
3343 	procInfo.processInfoLength = sizeof(ProcessInfoRec);
3344 	procInfo.processName = nil;
3345 	procInfo.processAppSpec = &cwdSpec;
3346 
3347 	/* Obtain current process information */
3348 	err = GetProcessInformation(&curPSN, &procInfo);
3349 
3350 	/* Oops */
3351 	if (err != noErr)
3352 	{
3353 		mac_warning("Unable to get process information");
3354 
3355 		/* Quit without writing anything */
3356 		ExitToShell();
3357 	}
3358 
3359 	/* Extract and save the Vol and Dir */
3360 	app_vol = cwdSpec.vRefNum;
3361 	app_dir = cwdSpec.parID;
3362 }
3363 
3364 #endif /* MACH_O_CARBON */
3365 
3366 
3367 
3368 
3369 /*
3370  * Using Core Foundation's Preferences services -- pelpel
3371  *
3372  * Requires OS 8.6 or greater with CarbonLib 1.1 or greater. Or OS X,
3373  * of course.
3374  *
3375  * Without this, we can support older versions of OS 8 as well
3376  * (with CarbonLib 1.0.4).
3377  *
3378  * Frequent allocation/deallocation of small chunks of data is
3379  * far from my liking, but since this is only called at the
3380  * beginning and the end of a session, I hope this hardly matters.
3381  */
3382 
3383 
3384 /*
3385  * Store "value" as the value for preferences item name
3386  * pointed by key
3387  */
save_pref_short(const char * key,short value)3388 static void save_pref_short(const char *key, short value)
3389 {
3390 	CFStringRef cf_key;
3391 	CFNumberRef cf_value;
3392 
3393 	/* allocate and initialise the key */
3394 	cf_key = CFStringCreateWithCString(NULL, key, kTextEncodingUS_ASCII);
3395 
3396 	/* allocate and initialise the value */
3397 	cf_value = CFNumberCreate(NULL, kCFNumberShortType, &value);
3398 
3399 	if ((cf_key != NULL) && (cf_value != NULL))
3400 	{
3401 		/* Store the key-value pair in the applications preferences */
3402 		CFPreferencesSetAppValue(
3403 			cf_key,
3404 			cf_value,
3405 			kCFPreferencesCurrentApplication);
3406 	}
3407 
3408 	/*
3409 	 * Free CF data - the reverse order is a vain attempt to
3410 	 * minimise memory fragmentation.
3411 	 */
3412 	if (cf_value) CFRelease(cf_value);
3413 	if (cf_key) CFRelease(cf_key);
3414 }
3415 
3416 
3417 /*
3418  * Load preference value for key, returns TRUE if it succeeds with
3419  * vptr updated appropriately, FALSE otherwise.
3420  */
query_load_pref_short(const char * key,short * vptr)3421 static bool query_load_pref_short(const char *key, short *vptr)
3422 {
3423 	CFStringRef cf_key;
3424 	CFNumberRef cf_value;
3425 
3426 	/* allocate and initialise the key */
3427 	cf_key = CFStringCreateWithCString(NULL, key, kTextEncodingUS_ASCII);
3428 
3429 	/* Oops */
3430 	if (cf_key == NULL) return (FALSE);
3431 
3432 	/* Retrieve value for the key */
3433 	cf_value = CFPreferencesCopyAppValue(
3434 		cf_key,
3435 		kCFPreferencesCurrentApplication);
3436 
3437 	/* Value not found */
3438 	if (cf_value == NULL)
3439 	{
3440 		CFRelease(cf_key);
3441 		return (FALSE);
3442 	}
3443 
3444 	/* Convert the value to short */
3445 	CFNumberGetValue(
3446 		cf_value,
3447 		kCFNumberShortType,
3448 		vptr);
3449 
3450 	/* Free CF data */
3451 	CFRelease(cf_value);
3452 	CFRelease(cf_key);
3453 
3454 	/* Success */
3455 	return (TRUE);
3456 }
3457 
3458 
3459 /*
3460  * Update short data pointed by vptr only if preferences
3461  * value for key is located.
3462  */
load_pref_short(const char * key,short * vptr)3463 static void load_pref_short(const char *key, short *vptr)
3464 {
3465 	short tmp;
3466 
3467 	if (query_load_pref_short(key, &tmp)) *vptr = tmp;
3468 }
3469 
3470 
3471 /*
3472  * Save preferences to preferences file for current host+current user+
3473  * current application.
3474  */
cf_save_prefs()3475 static void cf_save_prefs()
3476 {
3477 	int i;
3478 
3479 	/* Version stamp */
3480 	save_pref_short("version.major", VER_MAJOR);
3481 	save_pref_short("version.minor", VER_MINOR);
3482 	save_pref_short("version.patch", VER_PATCH);
3483 	save_pref_short("version.extra", VER_EXTRA);
3484 
3485 	/* Gfx settings */
3486 	save_pref_short("arg.arg_sound", arg_sound);
3487 	save_pref_short("arg.graf_mode", graf_mode);
3488 #ifdef USE_DOUBLE_TILES
3489 	save_pref_short("arg.big_tile", use_bigtile);
3490 #endif /* USE_DOUBLE_TILES */
3491 
3492 	/* Windows */
3493 	for (i = 0; i < MAX_TERM_DATA; i++)
3494 	{
3495 		term_data *td = &data[i];
3496 
3497 		save_pref_short(format("term%d.mapped", i), td->mapped);
3498 
3499 		save_pref_short(format("term%d.font_id", i), td->font_id);
3500 		save_pref_short(format("term%d.font_size", i), td->font_size);
3501 		save_pref_short(format("term%d.font_face", i), td->font_face);
3502 
3503 		save_pref_short(format("term%d.tile_wid", i), td->tile_wid);
3504 		save_pref_short(format("term%d.tile_hgt", i), td->tile_hgt);
3505 
3506 		save_pref_short(format("term%d.cols", i), td->cols);
3507 		save_pref_short(format("term%d.rows", i), td->rows);
3508 		save_pref_short(format("term%d.left", i), td->r.left);
3509 		save_pref_short(format("term%d.top", i), td->r.top);
3510 	}
3511 
3512 	/*
3513 	 * Make sure preferences are persistent
3514 	 */
3515 	CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
3516 }
3517 
3518 
3519 /*
3520  * Load preferences from preferences file for current host+current user+
3521  * current application.
3522  */
cf_load_prefs()3523 static void cf_load_prefs()
3524 {
3525 	bool ok;
3526 	short pref_major, pref_minor, pref_patch, pref_extra;
3527 	short valid;
3528 	int i;
3529 
3530 	/* Assume nothing is wrong, yet */
3531 	ok = TRUE;
3532 
3533 	/* Load version information */
3534 	ok &= query_load_pref_short("version.major", &pref_major);
3535 	ok &= query_load_pref_short("version.minor", &pref_minor);
3536 	ok &= query_load_pref_short("version.patch", &pref_patch);
3537 	ok &= query_load_pref_short("version.extra", &pref_extra);
3538 
3539 	/* Any of the above failed */
3540 	if (!ok)
3541 	{
3542 #if 0
3543 		/* This may be the first run */
3544 		mac_warning("Preferences are not found.");
3545 #endif /* 0 */
3546 
3547 		/* Ignore the rest */
3548 		return;
3549 	}
3550 
3551 #if 0
3552 
3553 	/* Check version */
3554 	if ((pref_major != VERSION_MAJOR) ||
3555 		(pref_minor != VERSION_MINOR) ||
3556 		(pref_patch != VERSION_PATCH) ||
3557 		(pref_extra != VERSION_EXTRA))
3558 	{
3559 		/* Message */
3560 		mac_warning(
3561 			format("Ignoring %d.%d.%d.%d preferences.",
3562 				pref_major, pref_minor, pref_patch, pref_extra));
3563 
3564 		/* Ignore */
3565 		return;
3566 	}
3567 
3568 #endif
3569 
3570 	/* HACK - Check for broken preferences */
3571 	load_pref_short("term0.mapped", &valid);
3572 
3573 	/* Ignore broken preferences */
3574 	if (!valid)
3575 	{
3576 		mac_warning("Ignoring broken preferences.");
3577 
3578 		/* Ignore */
3579 		return;
3580 	}
3581 
3582 	/* Gfx settings */
3583 	{
3584 		short pref_tmp;
3585 
3586 		/* sound */
3587 		if (query_load_pref_short("arg.arg_sound", &pref_tmp))
3588 			arg_sound = pref_tmp;
3589 
3590 		/* graphics */
3591 		if (query_load_pref_short("arg.graf_mode", &pref_tmp))
3592 			graf_mode_req = pref_tmp;
3593 
3594 #ifdef USE_DOUBLE_TILES
3595 
3596 		/* double-width tiles */
3597 		if (query_load_pref_short("arg.big_tile", &pref_tmp))
3598 		{
3599 			use_bigtile = pref_tmp;
3600 		}
3601 
3602 #endif /* USE_DOUBLE_TILES */
3603 
3604 	}
3605 
3606 	/* Windows */
3607 	for (i = 0; i < MAX_TERM_DATA; i++)
3608 	{
3609 		term_data *td = &data[i];
3610 
3611 		load_pref_short(format("term%d.mapped", i), &td->mapped);
3612 
3613 		load_pref_short(format("term%d.font_id", i), &td->font_id);
3614 		load_pref_short(format("term%d.font_size", i), &td->font_size);
3615 		load_pref_short(format("term%d.font_face", i), &td->font_face);
3616 
3617 		load_pref_short(format("term%d.tile_wid", i), &td->tile_wid);
3618 		load_pref_short(format("term%d.tile_hgt", i), &td->tile_hgt);
3619 
3620 		load_pref_short(format("term%d.cols", i), &td->cols);
3621 		load_pref_short(format("term%d.rows", i), &td->rows);
3622 		load_pref_short(format("term%d.left", i), &td->r.left);
3623 		load_pref_short(format("term%d.top", i), &td->r.top);
3624 	}
3625 }
3626 
3627 
3628 
3629 
3630 /*
3631  * Hack -- default data for a window
3632  */
term_data_hack(term_data * td)3633 static void term_data_hack(term_data *td)
3634 {
3635 	short fid;
3636 
3637 	/* Default to Monaco font */
3638 	GetFNum("\pmonaco", &fid);
3639 
3640 	/* Wipe it */
3641 	WIPE(td, term_data);
3642 
3643 	/* No color */
3644 	td->last = -1;
3645 
3646 	/* Default borders */
3647 	td->size_ow1 = 2;
3648 	td->size_ow2 = 2;
3649 	td->size_oh2 = 2;
3650 
3651 	/* Start hidden */
3652 	td->mapped = FALSE;
3653 
3654 	/* Default font */
3655 	td->font_id = fid;
3656 
3657 	/* Default font size - was 12 */
3658 	td->font_size = 14;
3659 
3660 	/* Default font face */
3661 	td->font_face = 0;
3662 
3663 	/* Default size */
3664 	td->rows = 24;
3665 	td->cols = 80;
3666 
3667 	/* Default position */
3668 	td->r.left = 10;
3669 	td->r.top = 40;
3670 
3671 	/* Minimal keys */
3672 	td->keys = 16;
3673 }
3674 
3675 
3676 /*
3677  * Read the preference file, Create the windows.
3678  *
3679  * We attempt to use "FindFolder()" to track down the preference file.
3680  */
init_windows(void)3681 static void init_windows(void)
3682 {
3683 	int i, b = 0;
3684 
3685 	term_data *td;
3686 
3687 
3688 	/*** Default values ***/
3689 
3690 	/* Initialize (backwards) */
3691 	for (i = MAX_TERM_DATA; i-- > 0; )
3692 	{
3693 		int n;
3694 
3695 		cptr s;
3696 
3697 		/* Obtain */
3698 		td = &data[i];
3699 
3700 		/* Defaults */
3701 		term_data_hack(td);
3702 
3703 		/* Obtain title */
3704 		s = angband_term_name[i];
3705 
3706 		/* Get length */
3707 		n = strlen(s);
3708 
3709 		/* Maximal length */
3710 		if (n > 15) n = 15;
3711 
3712 		/* Copy the title */
3713 		strncpy((char*)(td->title) + 1, s, n);
3714 
3715 		/* Save the length */
3716 		td->title[0] = n;
3717 
3718 		/* Tile the windows */
3719 		td->r.left += (b * 30);
3720 		td->r.top += (b * 30);
3721 
3722 		/* Tile */
3723 		b++;
3724 	}
3725 
3726 
3727 	/*** Load preferences ***/
3728 
3729 	cf_load_prefs();
3730 
3731 
3732 	/*** Instantiate ***/
3733 
3734 	/* Main window */
3735 	td = &data[0];
3736 
3737 	/* Many keys */
3738 	td->keys = 1024;
3739 
3740 	/* Start visible */
3741 	td->mapped = TRUE;
3742 
3743 	/* Link (backwards, for stacking order) */
3744 	for (i = MAX_TERM_DATA; i-- > 0; )
3745 	{
3746 		term_data_link(i);
3747 	}
3748 
3749 	/* Main window */
3750 	td = &data[0];
3751 
3752 	/* Main window */
3753 	Term_activate(td->t);
3754 }
3755 
3756 
3757 /*
3758  * Save preferences
3759  */
save_pref_file(void)3760 static void save_pref_file(void)
3761 {
3762 	cf_save_prefs();
3763 }
3764 
3765 
3766 
3767 
3768 /*
3769  * Prepare savefile dialogue and set the variable
3770  * savefile accordingly. Returns true if it succeeds, false (or
3771  * aborts) otherwise. If all is false, only allow files whose type
3772  * is 'SAVE'.
3773  * Originally written by Peter Ammon
3774  */
select_savefile(bool all)3775 static bool select_savefile(bool all)
3776 {
3777 	OSErr err;
3778 	FSSpec theFolderSpec;
3779 	FSSpec savedGameSpec;
3780 	NavDialogOptions dialogOptions;
3781 	NavReplyRecord reply;
3782 	/* Used only when 'all' is true */
3783 	NavTypeList types = {ANGBAND_CREATOR, 1, 1, {'SAVE'}};
3784 	NavTypeListHandle myTypeList;
3785 	AEDesc defaultLocation;
3786 
3787 #ifdef MACH_O_CARBON
3788 
3789 	short foundVRefNum;
3790 	long foundDirID;
3791 
3792 	/* Find the preferences folder */
3793 	err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder,
3794 	                 &foundVRefNum, &foundDirID);
3795 
3796 	if (err != noErr) quit("Couldn't find the preferences folder!");
3797 
3798 	/* Look for the "ZAngband/save/" sub-folder */
3799 	err = FSMakeFSSpec(foundVRefNum, foundDirID, "\p:ZAngband:save:", &theFolderSpec);
3800 
3801 	/* Oops */
3802 	if (err != noErr) quit_fmt("Unable to find the savefile folder! (Error %d)", err);
3803 
3804 #else
3805 
3806 	/* Find :lib:save: folder */
3807 	err = FSMakeFSSpec(app_vol, app_dir, "\p:lib:save:", &theFolderSpec);
3808 
3809 
3810 	/* Oops */
3811 	if (err != noErr) quit("Unable to find the folder :lib:save:");
3812 
3813 #endif
3814 
3815 	/* Get default Navigator dialog options */
3816 	err = NavGetDefaultDialogOptions(&dialogOptions);
3817 
3818 	/* Clear preview option */
3819 	dialogOptions.dialogOptionFlags &= ~kNavAllowPreviews;
3820 
3821 	/* Disable multiple file selection */
3822 	dialogOptions.dialogOptionFlags &= ~kNavAllowMultipleFiles;
3823 
3824 	/* Make descriptor for default location */
3825 	err = AECreateDesc(typeFSS, &theFolderSpec, sizeof(FSSpec),
3826 		&defaultLocation);
3827 
3828 	/* Oops */
3829 	if (err != noErr) quit("Unable to allocate descriptor");
3830 
3831 	/* We are indifferent to signature and file types */
3832 	if (all)
3833 	{
3834 		myTypeList = (NavTypeListHandle)nil;
3835 	}
3836 
3837 	/* Set up type handle */
3838 	else
3839 	{
3840 		err = PtrToHand(&types, (Handle *)&myTypeList, sizeof(NavTypeList));
3841 
3842 		/* Oops */
3843 		if (err != noErr) quit("Error in PtrToHand. Try enlarging heap");
3844 
3845 	}
3846 
3847 	/* Call NavGetFile() with the types list */
3848 	err = NavChooseFile(&defaultLocation, &reply, &dialogOptions, NULL,
3849 		NULL, NULL, myTypeList, NULL);
3850 
3851 	/* Free type list */
3852 	if (!all) DisposeHandle((Handle)myTypeList);
3853 
3854 	/* Error */
3855 	if (err != noErr)
3856 	{
3857 		/* Nothing */
3858 	}
3859 
3860 	/* Invalid response -- allow the user to cancel */
3861 	else if (!reply.validRecord)
3862 	{
3863 		/* Hack -- Fake error */
3864 		err = -1;
3865 	}
3866 
3867 	/* Retrieve FSSpec from the reply */
3868 	else
3869 	{
3870 		AEKeyword theKeyword;
3871 		DescType actualType;
3872 		Size actualSize;
3873 
3874 		/* Get a pointer to selected file */
3875 		(void)AEGetNthPtr(&reply.selection, 1, typeFSS, &theKeyword,
3876 			&actualType, &savedGameSpec, sizeof(FSSpec), &actualSize);
3877 
3878 		/* Dispose NavReplyRecord, resources and descriptors */
3879 		(void)NavDisposeReply(&reply);
3880 	}
3881 
3882 	/* Dispose location info */
3883 	AEDisposeDesc(&defaultLocation);
3884 
3885 	/* Error */
3886 	if (err != noErr) return (FALSE);
3887 
3888 #ifdef MACH_O_CARBON
3889 
3890 	/* Convert FSSpec to pathname and store it in variable savefile */
3891 	(void)spec_to_path(&savedGameSpec, savefile, sizeof(savefile));
3892 
3893 #else
3894 
3895 	/* Convert FSSpec to pathname and store it in variable savefile */
3896 	refnum_to_name(
3897 		savefile,
3898 		savedGameSpec.parID,
3899 		savedGameSpec.vRefNum,
3900 		(char *)savedGameSpec.name);
3901 
3902 #endif
3903 
3904 	/* Success */
3905 	return (TRUE);
3906 }
3907 
3908 
3909 /*
3910  * Handle menu: "File" + "New"
3911  */
do_menu_file_new(void)3912 static void do_menu_file_new(void)
3913 {
3914 	/* Game is in progress */
3915 	game_in_progress = TRUE;
3916 
3917 	/* Start a new game */
3918 	new_game = TRUE;
3919 }
3920 
3921 
3922 /*
3923  * Handle menu: "File" + "Open" /  "Import"
3924  */
do_menu_file_open(bool all)3925 static void do_menu_file_open(bool all)
3926 {
3927 	/* Let the player to choose savefile */
3928 	if (!select_savefile(all)) return;
3929 
3930 	/* Game is in progress */
3931 	game_in_progress = TRUE;
3932 
3933 	/* Use an existing savefile */
3934 	new_game = FALSE;
3935 }
3936 
3937 
3938 /*
3939  * Handle the "open_when_ready" flag
3940  */
handle_open_when_ready(void)3941 static void handle_open_when_ready(void)
3942 {
3943 	/* Check the flag XXX XXX XXX make a function for this */
3944 	if (open_when_ready && initialized && !game_in_progress)
3945 	{
3946 		/* Forget */
3947 		open_when_ready = FALSE;
3948 
3949 		/* Game is in progress */
3950 		game_in_progress = TRUE;
3951 
3952 		/* Use an existing savefile */
3953 		new_game = FALSE;
3954 
3955 		/* Wait for a keypress */
3956 		pause_line(Term->hgt - 1);
3957 	}
3958 }
3959 
3960 
3961 
3962 
3963 /*
3964  * Menus
3965  *
3966  * The standard menus are:
3967  *
3968  *   Apple (128) =   { About, -, ... }
3969  *   File (129) =    { New,Open,Import,Close,Save,-,Score,Quit }
3970  *   Edit (130) =    { Cut, Copy, Paste, Clear }   (?)
3971  *   Font (131) =    { Bold, Extend, -, Monaco, ..., -, ... }
3972  *   Size (132) =    { ... }
3973  *   Window (133) =  { Angband, Term-1/Mirror, Term-2/Recall, Term-3/Choice,
3974  *                     Term-4, Term-5, Term-6, Term-7 }
3975  *   Special (134) = { Sound, Graphics, TileWidth, TileHeight, -,
3976  *                     Fiddle, Wizard }
3977  */
3978 
3979 /* Apple menu */
3980 #define MENU_APPLE	128
3981 #define ITEM_ABOUT	1
3982 
3983 /* File menu */
3984 #define MENU_FILE	129
3985 # define ITEM_NEW	1
3986 # define ITEM_OPEN	2
3987 # define ITEM_IMPORT	3
3988 # define ITEM_CLOSE	4
3989 # define ITEM_SAVE	5
3990 # ifdef HAS_SCORE_MENU
3991 #  define ITEM_SCORE 7
3992 #  define ITEM_QUIT	8
3993 # else
3994 #  define ITEM_QUIT	7
3995 # endif /* HAS_SCORE_MENU */
3996 
3997 /* Edit menu */
3998 #define MENU_EDIT	130
3999 # define ITEM_UNDO	1
4000 # define ITEM_CUT	3
4001 # define ITEM_COPY	4
4002 # define ITEM_PASTE	5
4003 # define ITEM_CLEAR	6
4004 
4005 /* Font menu */
4006 #define MENU_FONT	131
4007 # define ITEM_BOLD	1
4008 # define ITEM_WIDE	2
4009 
4010 /* Size menu */
4011 #define MENU_SIZE	132
4012 
4013 /* Windows menu */
4014 #define MENU_WINDOWS	133
4015 
4016 /* Special menu */
4017 #define MENU_SPECIAL	134
4018 # define ITEM_SOUND	1
4019 # define ITEM_GRAPH	2
4020 # define SUBMENU_GRAPH	144
4021 #  define ITEM_NONE	1
4022 #  define ITEM_8X8	2
4023 #  define ITEM_16X16	3
4024 #  define ITEM_32X32	4
4025 #  define ITEM_BIGTILE 6
4026 # define ITEM_TILEWIDTH 3
4027 # define SUBMENU_TILEWIDTH 145
4028 # define ITEM_TILEHEIGHT 4
4029 # define SUBMENU_TILEHEIGHT 146
4030 # define ITEM_FIDDLE	6
4031 # define ITEM_WIZARD	7
4032 
4033 
4034 /*
4035  * I HATE UNICODE!  We've never wanted it.  Some multi-national companies
4036  * made it up as their internationalisation "solution".  So I won't use
4037  * any such API's -- pelpel
4038  */
4039 #define NSIZES 32
4040 static byte menu_size_values[NSIZES];
4041 static byte menu_tilewidth_values[NSIZES];
4042 static byte menu_tileheight_values[NSIZES];
4043 
4044 /*
4045  * Initialize the menus
4046  *
4047  * Fixed top level menus are now loaded all at once by GetNewMBar().
4048  * Although this simplifies the function a bit, we have to make sure
4049  * that resources have all the expected entries defined XXX XXX
4050  */
init_menubar(void)4051 static void init_menubar(void)
4052 {
4053 	int i, n;
4054 
4055 	Rect r;
4056 
4057 	WindowPtr tmpw;
4058 
4059 	MenuRef m;
4060 
4061 #ifdef USE_NIB
4062 
4063 	/* The new way - loading main menu using Interface Builder services */
4064 	{
4065 		IBNibRef nib;
4066 		OSStatus err;
4067 
4068 		/* Create a nib reference to the main nib file */
4069 		err = CreateNibReference(CFSTR("main"), &nib);
4070 
4071 		/* Fatal error - missing Main.nib */
4072 		if (err != noErr) quit("Cannot find Main.nib in the bundle!");
4073 
4074 		/* Unarchive the menu bar and make it ready to use */
4075 		err = SetMenuBarFromNib(nib, CFSTR("MainMenu"));
4076 
4077 		/* Fatal error - couldn't insert menu bar */
4078 		if (err != noErr) quit("Cannot prepare menu bar!");
4079 
4080 		/* Dispose of the nib reference because we don't need it any longer */
4081 		DisposeNibReference(nib);
4082 	}
4083 
4084 #else /* USE_NIB */
4085 
4086 	/* The old way - loading main menu from Resource Manager resource */
4087 	{
4088 		Handle mbar;
4089 
4090 		/* Load menubar from resources */
4091 		mbar = GetNewMBar(128);
4092 
4093 		/* Whoops! */
4094 		if (mbar == nil) quit("Cannot find menubar('MBAR') id 128!");
4095 
4096 		/* Insert them into the current menu list */
4097 		SetMenuBar(mbar);
4098 
4099 		/* Free handle */
4100 		DisposeHandle(mbar);
4101 	}
4102 
4103 #endif /* USE_NIB */
4104 
4105 
4106 	/* Apple menu (id 128) - we don't have to do anything */
4107 
4108 
4109 #ifndef USE_NIB
4110 
4111 	/* File menu (id 129) - Aqua provides Quit menu for us */
4112 	if (is_aqua)
4113 	{
4114 		/* Get a handle to the file menu */
4115 		m = GetMenuHandle(MENU_FILE);
4116 
4117 		/* Nuke the quit menu since Aqua does that for us */
4118 		DeleteMenuItem(m, ITEM_QUIT);
4119 
4120 #ifndef HAS_SCORE_MENU
4121 
4122 		/* Hack - because the above leaves a separator as the last item */
4123 		DeleteMenuItem(m, ITEM_QUIT - 1);
4124 
4125 #endif /* !HAS_SCORE_MENU */
4126 	}
4127 
4128 #endif /* !USE_NIB */
4129 
4130 
4131 	/* Edit menu (id 130) - we don't have to do anything */
4132 
4133 
4134 	/*
4135 	 * Font menu (id 131) - append names of mono-spaced fonts
4136 	 * followed by all available ones
4137 	 */
4138 	m = GetMenuHandle(MENU_FONT);
4139 
4140 	/* Fake window */
4141 	r.left = r.right = r.top = r.bottom = 0;
4142 
4143 	/* Make the fake window so that we can retrieve font info */
4144 	(void)CreateNewWindow(
4145 		kDocumentWindowClass,
4146 		kWindowNoAttributes,
4147 		&r,
4148 		&tmpw);
4149 
4150 	/* Activate the "fake" window */
4151 	SetPort(GetWindowPort(tmpw));
4152 
4153 	/* Default mode */
4154 	TextMode(0);
4155 
4156 	/* Default size */
4157 	TextSize(12);
4158 
4159 	/* Add the fonts to the menu */
4160 	AppendResMenu(m, 'FONT');
4161 
4162 	/* Size of menu */
4163 	n = CountMenuItems(m);
4164 
4165 	/* Scan the menu */
4166 	for (i = n; i >= 4; i--)
4167 	{
4168 		Str255 tmpName;
4169 		short fontNum;
4170 
4171 		/* Acquire the font name */
4172 		GetMenuItemText(m, i, tmpName);
4173 
4174 		/* Acquire the font index */
4175 		GetFNum(tmpName, &fontNum);
4176 
4177 		/* Apply the font index */
4178 		TextFont(fontNum);
4179 
4180 		/* Remove non-mono-spaced fonts */
4181 		if ((CharWidth('i') != CharWidth('W')) || (CharWidth('W') == 0))
4182 		{
4183 			/* Delete the menu item */
4184 			DeleteMenuItem(m, i);
4185 		}
4186 	}
4187 
4188 	/* Destroy the fake window */
4189 	DisposeWindow(tmpw);
4190 
4191 	/* Add a separator */
4192 	AppendMenu(m, "\p-");
4193 
4194 	/* Add the fonts to the menu */
4195 	AppendResMenu(m, 'FONT');
4196 
4197 
4198 #ifndef USE_NIB
4199 
4200 	/* Size menu (id 132) */
4201 	m = GetMenuHandle(MENU_SIZE);
4202 
4203 	/* Add some sizes (stagger choices) */
4204 	for (i = 8, n = 1; i <= 32; i += ((i / 16) + 1), n++)
4205 	{
4206 		Str15 buf;
4207 
4208 		/* Textual size */
4209 		strnfmt((char*)buf + 1, 15, "%d", i);
4210 		buf[0] = strlen((char*)buf + 1);
4211 
4212 		/* Add the item */
4213 		AppendMenu(m, buf);
4214 
4215 		/* Remember its value, for we can't be sure it's in ASCII */
4216 		menu_size_values[n] = i;
4217 	}
4218 
4219 #endif /* !USE_NIB */
4220 
4221 
4222 	/* Windows menu (id 133) */
4223 	m = GetMenuHandle(MENU_WINDOWS);
4224 
4225 	/* Default choices */
4226 	for (i = 0; i < MAX_TERM_DATA; i++)
4227 	{
4228 		Str15 buf;
4229 
4230 		/* Describe the item */
4231 		strnfmt((char*)buf + 1, 15, "%.15s", angband_term_name[i]);
4232 		buf[0] = strlen((char*)buf + 1);
4233 
4234 		/* Add the item */
4235 		AppendMenu(m, buf);
4236 
4237 		/* Command-Key shortcuts */
4238 		if (i < 8) SetItemCmd(m, i + 1, I2D(i));
4239 	}
4240 
4241 
4242 #ifndef USE_NIB
4243 
4244 # ifndef MAC_MPW
4245 
4246 	/* CW or gcc -- Use recommended interface for hierarchical menus */
4247 
4248 	/* Special menu (id 134) */
4249 	m = GetMenuHandle(MENU_SPECIAL);
4250 
4251 	/* Insert Graphics submenu (id 144) */
4252 	{
4253 		MenuHandle submenu;
4254 
4255 		/* Get the submenu */
4256 		submenu = GetMenu(SUBMENU_GRAPH);
4257 
4258 		/* Insert it */
4259 		SetMenuItemHierarchicalMenu(m, ITEM_GRAPH, submenu);
4260 	}
4261 
4262 	/* Insert TileWidth submenu (id 145) */
4263 	{
4264 		MenuHandle submenu;
4265 
4266 		/* Get the submenu */
4267 		submenu = GetMenu(SUBMENU_TILEWIDTH);
4268 
4269 		/* Add some sizes */
4270 		for (i = 4, n = 1; i <= 32; i++, n++)
4271 		{
4272 			Str15 buf;
4273 
4274 			/* Textual size */
4275 			strnfmt((char*)buf + 1, 15, "%d", i);
4276 			buf[0] = strlen((char*)buf + 1);
4277 
4278 			/* Append item */
4279 			AppendMenu(submenu, buf);
4280 
4281 			/* Remember its value, for we can't be sure it's in ASCII */
4282 			menu_tilewidth_values[n] = i;
4283 		}
4284 
4285 		/* Insert it */
4286 		SetMenuItemHierarchicalMenu(m, ITEM_TILEWIDTH, submenu);
4287 	}
4288 
4289 	/* Insert TileHeight submenu (id 146) */
4290 	{
4291 		MenuHandle submenu;
4292 
4293 		/* Get the submenu */
4294 		submenu = GetMenu(SUBMENU_TILEHEIGHT);
4295 
4296 
4297 		/* Add some sizes */
4298 		for (i = 4, n = 1; i <= 32; i++, n++)
4299 		{
4300 			Str15 buf;
4301 
4302 			/* Textual size */
4303 			strnfmt((char*)buf + 1, 15, "%d", i);
4304 			buf[0] = strlen((char*)buf + 1);
4305 
4306 			/* Append item */
4307 			AppendMenu(submenu, buf);
4308 
4309 			/* Remember its value, for we can't be sure it's in ASCII */
4310 			menu_tileheight_values[n] = i;
4311 		}
4312 
4313 		/* Insert it */
4314 		SetMenuItemHierarchicalMenu(m, ITEM_TILEHEIGHT, submenu);
4315 	}
4316 
4317 # else /* !MAC_MPW */
4318 
4319 	/* XXX XXX */
4320 
4321 	/* MPW's Universal Interface doesn't understand some newer Carbon APIs */
4322 
4323 	/* Special menu (id 134) */
4324 
4325 	/* Get graphics (sub)menu (id 144) */
4326 	m = GetMenu(SUBMENU_GRAPH);
4327 
4328 	/* Insert it as a submenu */
4329 	InsertMenu(m, hierMenu);
4330 
4331 
4332 	/* Get TileWidth (sub)menu (id 145) */
4333 	m = GetMenu(SUBMENU_TILEWIDTH);
4334 
4335 	/* Add some sizes */
4336 	for (i = 4, n = 1; i <= 32; i++, n++)
4337 	{
4338 		Str15 buf;
4339 
4340 		/* Textual size */
4341 		strnfmt((char*)buf + 1, 15, "%d", i);
4342 		buf[0] = strlen((char*)buf + 1);
4343 
4344 		/* Append item */
4345 		AppendMenu(m, buf);
4346 
4347 		/* Remember its value, for we can't be sure it's in ASCII */
4348 		menu_tilewidth_values[n] = i;
4349 	}
4350 
4351 	/* Insert it as a submenu */
4352 	InsertMenu(m, hierMenu);
4353 
4354 	/* Get TileHeight (sub)menu (id 146) */
4355 	m = GetMenu(SUBMENU_TILEHEIGHT);
4356 
4357 	/* Add some sizes */
4358 	for (i = 4, n = 1; i <= 32; i++, n++)
4359 	{
4360 		Str15 buf;
4361 
4362 		/* Textual size */
4363 		strnfmt((char*)buf + 1, 15, "%d", i);
4364 		buf[0] = strlen((char*)buf + 1);
4365 
4366 		/* Append item */
4367 		AppendMenu(m, buf);
4368 
4369 		/* Remember its value, for we can't be sure it's in ASCII */
4370 		menu_tileheight_values[n] = i;
4371 	}
4372 
4373 	/* Insert it as a submenu */
4374 	InsertMenu(m, hierMenu);
4375 
4376 # endif /* MAC_MPW */
4377 
4378 #endif /* !USE_NIB */
4379 
4380 
4381 	/* Update the menu bar */
4382 	DrawMenuBar();
4383 }
4384 
4385 
4386 /*
4387  * Prepare the menus
4388  *
4389  * It is very important that the player not be allowed to "save" the game
4390  * unless the "inkey_flag" variable is set, indicating that the game is
4391  * waiting for a new command.  XXX XXX XXX
4392  */
4393 
setup_menus(void)4394 static void setup_menus(void)
4395 {
4396 	int i, n;
4397 
4398 	short value;
4399 
4400 	Str255 s;
4401 
4402 	MenuHandle m;
4403 
4404 	WindowRef w;
4405 
4406 	term_data *td = NULL;
4407 
4408 
4409 	/* Find window */
4410 	w = FrontWindow();
4411 
4412 	/* Relevant "term_data" */
4413 	if (w != NULL) td = (term_data *)GetWRefCon(w);
4414 
4415 
4416 	/* File menu */
4417 	m = GetMenuHandle(MENU_FILE);
4418 
4419 	/* Get menu size */
4420 	n = CountMenuItems(m);
4421 
4422 	/* Reset menu */
4423 	for (i = 1; i <= n; i++)
4424 	{
4425 		/* Reset */
4426 		DisableMenuItem(m, i);
4427 		CheckMenuItem(m, i, FALSE);
4428 	}
4429 
4430 	/* Enable "new"/"open..."/"import..." */
4431 	if (initialized && !game_in_progress)
4432 	{
4433 		EnableMenuItem(m, ITEM_NEW);
4434 		EnableMenuItem(m, ITEM_OPEN);
4435 		EnableMenuItem(m, ITEM_IMPORT);
4436 	}
4437 
4438 	/* Enable "close" */
4439 	if (initialized)
4440 	{
4441 		EnableMenuItem(m, ITEM_CLOSE);
4442 	}
4443 
4444 	/* Enable "save" */
4445 	if (initialized && character_generated && p_ptr->cmd.inkey_flag)
4446 	{
4447 		EnableMenuItem(m, ITEM_SAVE);
4448 	}
4449 
4450 #ifdef HAS_SCORE_MENU
4451 
4452 	/* Enable "score" */
4453 	if (initialized && character_generated && !character_icky)
4454 	{
4455 		EnableMenuItem(m, ITEM_SCORE);
4456 	}
4457 
4458 #endif /* HAS_SCORE_MENU */
4459 
4460 	/* Enable "quit" */
4461 	if (!is_aqua)
4462 	{
4463 		if (!initialized || !character_generated || p_ptr->cmd.inkey_flag)
4464 		{
4465 			EnableMenuItem(m, ITEM_QUIT);
4466 		}
4467 	}
4468 
4469 
4470 	/* Edit menu */
4471 	m = GetMenuHandle(MENU_EDIT);
4472 
4473 	/* Get menu size */
4474 	n = CountMenuItems(m);
4475 
4476 	/* Reset menu */
4477 	for (i = 1; i <= n; i++)
4478 	{
4479 		/* Reset */
4480 		DisableMenuItem(m, i);
4481 		CheckMenuItem(m, i, FALSE);
4482 	}
4483 
4484 	/* Enable "edit" options if "needed" */
4485 	if (!td)
4486 	{
4487 		EnableMenuItem(m, ITEM_UNDO);
4488 		EnableMenuItem(m, ITEM_CUT);
4489 		EnableMenuItem(m, ITEM_COPY);
4490 		EnableMenuItem(m, ITEM_PASTE);
4491 		EnableMenuItem(m, ITEM_CLEAR);
4492 	}
4493 
4494 
4495 	/* Font menu */
4496 	m = GetMenuHandle(MENU_FONT);
4497 
4498 	/* Get menu size */
4499 	n = CountMenuItems(m);
4500 
4501 	/* Reset menu */
4502 	for (i = 1; i <= n; i++)
4503 	{
4504 		/* Reset */
4505 		DisableMenuItem(m, i);
4506 		CheckMenuItem(m, i, FALSE);
4507 	}
4508 
4509 	/* Hack -- look cute XXX XXX */
4510 	/* SetItemStyle(m, ITEM_BOLD, bold); */
4511 
4512 	/* Hack -- look cute XXX XXX */
4513 	/* SetItemStyle(m, ITEM_WIDE, extend); */
4514 
4515 	/* Active window */
4516 	if (initialized && td)
4517 	{
4518 		/* Enable "bold" */
4519 		EnableMenuItem(m, ITEM_BOLD);
4520 
4521 		/* Enable "extend" */
4522 		EnableMenuItem(m, ITEM_WIDE);
4523 
4524 		/* Check the appropriate "bold-ness" */
4525 		if (td->font_face & bold) CheckMenuItem(m, ITEM_BOLD, TRUE);
4526 
4527 		/* Check the appropriate "wide-ness" */
4528 		if (td->font_face & extend) CheckMenuItem(m, ITEM_WIDE, TRUE);
4529 
4530 		/* Analyze fonts */
4531 		for (i = 4; i <= n; i++)
4532 		{
4533 			/* Enable it */
4534 			EnableMenuItem(m, i);
4535 
4536 			/* Analyze font */
4537 			GetMenuItemText(m, i, s);
4538 			GetFNum(s, &value);
4539 
4540 			/* Check active font */
4541 			if (td->font_id == value) CheckMenuItem(m, i, TRUE);
4542 		}
4543 	}
4544 
4545 
4546 	/* Size menu */
4547 	m = GetMenuHandle(MENU_SIZE);
4548 
4549 	/* Get menu size */
4550 	n = CountMenuItems(m);
4551 
4552 	/* Reset menu */
4553 	for (i = 1; i <= n; i++)
4554 	{
4555 		/* Reset */
4556 		DisableMenuItem(m, i);
4557 		CheckMenuItem(m, i, FALSE);
4558 	}
4559 
4560 	/* Active window */
4561 	if (initialized && td)
4562 	{
4563 		/* Analyze sizes */
4564 		for (i = 1; i <= n; i++)
4565 		{
4566 			/* Analyze size */
4567 			value = menu_size_values[i];
4568 
4569 			/* Enable the "real" sizes */
4570 			if (RealFont(td->font_id, value)) EnableMenuItem(m, i);
4571 
4572 			/* Check the current size */
4573 			if (td->font_size == value) CheckMenuItem(m, i, TRUE);
4574 		}
4575 	}
4576 
4577 
4578 	/* Windows menu */
4579 	m = GetMenuHandle(MENU_WINDOWS);
4580 
4581 	/* Get menu size */
4582 	n = CountMenuItems(m);
4583 
4584 	/* Check active windows */
4585 	for (i = 1; i <= n; i++)
4586 	{
4587 		/* Check if needed */
4588 		CheckMenuItem(m, i, data[i-1].mapped);
4589 	}
4590 
4591 
4592 	/* Special menu */
4593 	m = GetMenuHandle(MENU_SPECIAL);
4594 
4595 	/* Get menu size */
4596 	n = CountMenuItems(m);
4597 
4598 	/* Reset menu */
4599 	for (i = 1; i <= n; i++)
4600 	{
4601 		/* Reset */
4602 		DisableMenuItem(m, i);
4603 
4604 #ifdef MAC_MPW
4605 
4606 		/* MPW's Universal Interface is a bit out of date */
4607 
4608 		/* XXX Oh no, this removes submenu... */
4609 		if ((i != ITEM_GRAPH) &&
4610 		    (i != ITEM_TILEWIDTH) &&
4611 		    (i != ITEM_TILEHEIGHT)) CheckMenuItem(m, i, FALSE);
4612 
4613 #else
4614 
4615 		CheckMenuItem(m, i, FALSE);
4616 #endif
4617 	}
4618 
4619 	/* Item "arg_sound" */
4620 	EnableMenuItem(m, ITEM_SOUND);
4621 	CheckMenuItem(m, ITEM_SOUND, arg_sound);
4622 
4623 	/* Item "Graphics" */
4624 	EnableMenuItem(m, ITEM_GRAPH);
4625 	{
4626 		MenuRef submenu;
4627 
4628 #ifdef MAC_MPW
4629 
4630 		/* MPW's Universal Interface is a bit out of date */
4631 
4632 		/* Graphics submenu */
4633 		submenu = GetMenuHandle(SUBMENU_GRAPH);
4634 
4635 #else
4636 
4637 		/* Graphics submenu */
4638 		(void)GetMenuItemHierarchicalMenu(m, ITEM_GRAPH, &submenu);
4639 
4640 #endif
4641 
4642 		/* Get menu size */
4643 		n = CountMenuItems(submenu);
4644 
4645 		/* Reset menu */
4646 		for (i = 1; i <= n; i++)
4647 		{
4648 			/* Reset */
4649 			DisableMenuItem(submenu, i);
4650 			CheckMenuItem(submenu, i, FALSE);
4651 		}
4652 
4653 		/* Item "None" */
4654 		EnableMenuItem(submenu, ITEM_NONE);
4655 		CheckMenuItem(submenu, ITEM_NONE, (graf_mode_req == GRAF_MODE_NONE));
4656 
4657 		/* Item "8x8" */
4658 		EnableMenuItem(submenu, ITEM_8X8);
4659 		CheckMenuItem(submenu, ITEM_8X8, (graf_mode_req == GRAF_MODE_8X8));
4660 
4661 		/* Item "16x16" */
4662 		EnableMenuItem(submenu, ITEM_16X16);
4663 		CheckMenuItem(submenu, ITEM_16X16, (graf_mode_req == GRAF_MODE_16X16));
4664 
4665 		/* Item "32x32" */
4666 		EnableMenuItem(submenu, ITEM_32X32);
4667 		CheckMenuItem(submenu, ITEM_32X32, (graf_mode_req == GRAF_MODE_32X32));
4668 
4669 #ifdef USE_DOUBLE_TILES
4670 
4671 		/* Item "Big tiles" */
4672 		if (inkey_flag) EnableMenuItem(submenu, ITEM_BIGTILE);
4673 		CheckMenuItem(submenu, ITEM_BIGTILE, use_bigtile);
4674 
4675 #endif /* USE_DOUBLE_TILES */
4676 
4677 	}
4678 
4679 	/* Item "TileWidth" */
4680 	EnableMenuItem(m, ITEM_TILEWIDTH);
4681 	{
4682 		MenuRef submenu;
4683 
4684 #ifdef MAC_MPW
4685 
4686 		/* MPW's Universal Interface is a bit out of date */
4687 
4688 		/* TIleWidth submenu */
4689 		submenu = GetMenuHandle(SUBMENU_TILEWIDTH);
4690 
4691 #else
4692 
4693 		/* TileWidth submenu */
4694 		(void)GetMenuItemHierarchicalMenu(m, ITEM_TILEWIDTH, &submenu);
4695 
4696 #endif
4697 
4698 		/* Get menu size */
4699 		n = CountMenuItems(submenu);
4700 
4701 		/* Reset menu */
4702 		for (i = 1; i <= n; i++)
4703 		{
4704 			/* Reset */
4705 			DisableMenuItem(submenu, i);
4706 			CheckMenuItem(submenu, i, FALSE);
4707 		}
4708 
4709 		/* Active window */
4710 		if (initialized && td)
4711 		{
4712 			/* Analyze sizes */
4713 			for (i = 1; i <= n; i++)
4714 			{
4715 				/* Analyze size */
4716 				value = menu_tilewidth_values[i];
4717 
4718 				/* Enable */
4719 				if (value >= td->font_wid) EnableMenuItem(submenu, i);
4720 
4721 				/* Check the current size */
4722 				if (td->tile_wid == value) CheckMenuItem(submenu, i, TRUE);
4723 			}
4724 		}
4725 	}
4726 
4727 	/* Item "TileHeight" */
4728 	EnableMenuItem(m, ITEM_TILEHEIGHT);
4729 	{
4730 		MenuRef submenu;
4731 
4732 #ifdef MAC_MPW
4733 
4734 		/* MPW's Universal Interface is a bit out of date */
4735 
4736 		/* TileHeight submenu */
4737 		submenu = GetMenuHandle(SUBMENU_TILEHEIGHT);
4738 
4739 #else
4740 
4741 		/* TileWidth submenu */
4742 		(void)GetMenuItemHierarchicalMenu(m, ITEM_TILEHEIGHT, &submenu);
4743 
4744 #endif
4745 
4746 		/* Get menu size */
4747 		n = CountMenuItems(submenu);
4748 
4749 		/* Reset menu */
4750 		for (i = 1; i <= n; i++)
4751 		{
4752 			/* Reset */
4753 			DisableMenuItem(submenu, i);
4754 			CheckMenuItem(submenu, i, FALSE);
4755 		}
4756 
4757 		/* Active window */
4758 		if (initialized && td)
4759 		{
4760 			/* Analyze sizes */
4761 			for (i = 1; i <= n; i++)
4762 			{
4763 				/* Analyze size */
4764 				value = menu_tileheight_values[i];
4765 
4766 				/* Enable */
4767 				if (value >= td->font_hgt) EnableMenuItem(submenu, i);
4768 
4769 				/* Check the current size */
4770 				if (td->tile_hgt == value) CheckMenuItem(submenu, i, TRUE);
4771 			}
4772 		}
4773 	}
4774 
4775 	/* Item "arg_fiddle" */
4776 	EnableMenuItem(m, ITEM_FIDDLE);
4777 	CheckMenuItem(m, ITEM_FIDDLE, arg_fiddle);
4778 
4779 	/* Item "arg_wizard" */
4780 	EnableMenuItem(m, ITEM_WIZARD);
4781 	CheckMenuItem(m, ITEM_WIZARD, arg_wizard);
4782 }
4783 
4784 
4785 /*
4786  * Process a menu selection (see above)
4787  *
4788  * Hack -- assume that invalid menu selections are disabled above,
4789  * which I have been informed may not be reliable.  XXX XXX XXX
4790  */
menu(long mc)4791 static void menu(long mc)
4792 {
4793 	int i;
4794 
4795 	int menuid, selection;
4796 
4797 	static unsigned char s[1000];
4798 
4799 	short fid;
4800 
4801 	term_data *td = NULL;
4802 
4803 	WindowPtr old_win;
4804 
4805 
4806 	/* Analyze the menu command */
4807 	menuid = HiWord(mc);
4808 	selection = LoWord(mc);
4809 
4810 
4811 	/* Find the window XXX XXX Declare another variable */
4812 	old_win = FrontWindow();
4813 
4814 	/* Relevant "term_data" */
4815 	if (old_win) td = (term_data *)GetWRefCon(old_win);
4816 
4817 
4818 	/* Branch on the menu */
4819 	switch (menuid)
4820 	{
4821 		/* Apple Menu */
4822 		case MENU_APPLE:
4823 		{
4824 			/* About Angband... */
4825 			if (selection == ITEM_ABOUT)
4826 			{
4827 				DialogPtr dialog;
4828 				short item_hit;
4829 
4830 				/* Get the about dialogue */
4831 				dialog = GetNewDialog(128, 0, (WindowPtr)-1);
4832 
4833 				/* Move it to the middle of the screen */
4834 				RepositionWindow(
4835 					GetDialogWindow(dialog),
4836 					NULL,
4837 					kWindowCenterOnMainScreen);
4838 
4839 				/* Show the dialog */
4840 				TransitionWindow(GetDialogWindow(dialog),
4841 					kWindowZoomTransitionEffect,
4842 					kWindowShowTransitionAction,
4843 					NULL);
4844 
4845 				/* Wait for user to click on it */
4846 				ModalDialog(0, &item_hit);
4847 
4848 				/* Free the dialogue */
4849 				DisposeDialog(dialog);
4850 				break;
4851 			}
4852 
4853 			break;
4854 		}
4855 
4856 		/* File Menu */
4857 		case MENU_FILE:
4858 		{
4859 			switch (selection)
4860 			{
4861 				/* New */
4862 				case ITEM_NEW:
4863 				{
4864 					do_menu_file_new();
4865 					break;
4866 				}
4867 
4868 				/* Open... */
4869 				case ITEM_OPEN:
4870 				{
4871 					do_menu_file_open(FALSE);
4872 					break;
4873 				}
4874 
4875 				/* Import... */
4876 				case ITEM_IMPORT:
4877 				{
4878 					do_menu_file_open(TRUE);
4879 					break;
4880 				}
4881 
4882 				/* Close */
4883 				case ITEM_CLOSE:
4884 				{
4885 					/* No window */
4886 					if (!td) break;
4887 
4888 					/* Not Mapped */
4889 					td->mapped = FALSE;
4890 
4891 					/* Not Mapped */
4892 					td->t->mapped_flag = FALSE;
4893 
4894 					/* Hide the window */
4895 					TransitionWindow(td->w,
4896 						kWindowZoomTransitionEffect,
4897 						kWindowHideTransitionAction,
4898 						NULL);
4899 
4900 					break;
4901 				}
4902 
4903 				/* Save */
4904 				case ITEM_SAVE:
4905 				{
4906 					/* Hack -- Forget messages */
4907 					msg_flag = FALSE;
4908 
4909 					/* Hack -- Save the game */
4910 #ifndef ZANG_AUTO_SAVE
4911 					do_cmd_save_game();
4912 #else
4913 					do_cmd_save_game(FALSE);
4914 #endif /* !ZANG_AUTO_SAVE */
4915 
4916 					break;
4917 				}
4918 
4919 #ifdef HAS_SCORE_MENU
4920 
4921 				/* Show score */
4922 				case ITEM_SCORE:
4923 				{
4924 					char buf[1024];
4925 
4926 					/* Paranoia */
4927 					if (!initialized || character_icky ||
4928 					    !game_in_progress || !character_generated)
4929 					{
4930 						/* Can't happen but just in case */
4931 						plog("You may not do that right now.");
4932 
4933 						break;
4934 					}
4935 
4936 					/* Build the pathname of the score file */
4937 					path_build(buf, sizeof(buf), ANGBAND_DIR_APEX,
4938 						"scores.raw");
4939 
4940 					/* Hack - open the score file for reading */
4941 					highscore_fd = fd_open(buf, O_RDONLY);
4942 
4943 					/* Paranoia - No score file */
4944 					if (highscore_fd < 0)
4945 					{
4946 						msg_print("Score file is not available.");
4947 
4948 						break;
4949 					}
4950 
4951 					/* Mega-Hack - prevent various functions XXX XXX XXX */
4952 					initialized = FALSE;
4953 
4954 					/* Save screen */
4955 					screen_save();
4956 
4957 					/* Clear screen */
4958 					Term_clear();
4959 
4960 					/* Prepare scores */
4961 					if (game_in_progress && character_generated)
4962 					{
4963 						predict_score();
4964 					}
4965 
4966 #if 0 /* I don't like this - pelpel */
4967 
4968 					/* Mega-Hack - No current player XXX XXX XXX XXX */
4969 					else
4970 					{
4971 						display_scores_aux(0, MAX_HISCORES, -1, NULL);
4972 					}
4973 
4974 #endif
4975 
4976 					/* Close the high score file */
4977 					(void)fd_close(highscore_fd);
4978 
4979 					/* Forget the fd */
4980 					highscore_fd = -1;
4981 
4982 					/* Restore screen */
4983 					screen_load();
4984 
4985 					/* Hack - Flush it */
4986 					Term_fresh();
4987 
4988 					/* Mega-Hack - We are ready again */
4989 					initialized = TRUE;
4990 
4991 					/* Done */
4992 					break;
4993 				}
4994 
4995 #endif /* HAS_SCORE_MENU */
4996 
4997 				/* Quit (with save) */
4998 				case ITEM_QUIT:
4999 				{
5000 					/* Save the game (if necessary) */
5001 					if (game_in_progress && character_generated)
5002 					{
5003 						/* Hack -- Forget messages */
5004 						msg_flag = FALSE;
5005 
5006 						/* Save the game */
5007 #ifndef ZANG_AUTO_SAVE
5008 						do_cmd_save_game();
5009 #else
5010 						do_cmd_save_game(FALSE);
5011 #endif /* !ZANG_AUTO_SAVE */
5012 					}
5013 
5014 					/* Quit */
5015 					quit(NULL);
5016 					break;
5017 				}
5018 			}
5019 			break;
5020 		}
5021 
5022 		/* Edit menu */
5023 		case MENU_EDIT:
5024 		{
5025 			/* Unused */
5026 			break;
5027 		}
5028 
5029 		/* Font menu */
5030 		case MENU_FONT:
5031 		{
5032 			/* Require a window */
5033 			if (!td) break;
5034 
5035 			/* Memorize old */
5036 			old_win = active;
5037 
5038 			/* Activate */
5039 			activate(td->w);
5040 
5041 			/* Toggle the "bold" setting */
5042 			if (selection == ITEM_BOLD)
5043 			{
5044 				/* Toggle the setting */
5045 				if (td->font_face & bold)
5046 				{
5047 					td->font_face &= ~bold;
5048 				}
5049 				else
5050 				{
5051 					td->font_face |= bold;
5052 				}
5053 
5054 				/* Hack - clear tile size info XXX XXX */
5055 				td->tile_wid = td->tile_hgt = 0;
5056 
5057 				/* Apply and Verify */
5058 				term_data_check_font(td);
5059 				term_data_check_size(td);
5060 
5061 				/* Resize and Redraw */
5062 				term_data_resize(td);
5063 				term_data_redraw(td);
5064 
5065 				break;
5066 			}
5067 
5068 			/* Toggle the "wide" setting */
5069 			if (selection == ITEM_WIDE)
5070 			{
5071 				/* Toggle the setting */
5072 				if (td->font_face & extend)
5073 				{
5074 					td->font_face &= ~extend;
5075 				}
5076 				else
5077 				{
5078 					td->font_face |= extend;
5079 				}
5080 
5081 				/* Hack - clear tile size info XXX XXX */
5082 				td->tile_wid = td->tile_hgt = 0;
5083 
5084 				/* Apply and Verify */
5085 				term_data_check_font(td);
5086 				term_data_check_size(td);
5087 
5088 				/* Resize and Redraw */
5089 				term_data_resize(td);
5090 				term_data_redraw(td);
5091 
5092 				break;
5093 			}
5094 
5095 			/* Get a new font name */
5096 			GetMenuItemText(GetMenuHandle(MENU_FONT), selection, s);
5097 			GetFNum(s, &fid);
5098 
5099 			/* Save the new font id */
5100 			td->font_id = fid;
5101 
5102 			/* Current size is bad for new font */
5103 			if (!RealFont(td->font_id, td->font_size))
5104 			{
5105 				/* Find similar size */
5106 				for (i = 1; i <= 32; i++)
5107 				{
5108 					/* Adjust smaller */
5109 					if (td->font_size - i >= 8)
5110 					{
5111 						if (RealFont(td->font_id, td->font_size - i))
5112 						{
5113 							td->font_size -= i;
5114 							break;
5115 						}
5116 					}
5117 
5118 					/* Adjust larger */
5119 					if (td->font_size + i <= 128)
5120 					{
5121 						if (RealFont(td->font_id, td->font_size + i))
5122 						{
5123 							td->font_size += i;
5124 							break;
5125 						}
5126 					}
5127 				}
5128 			}
5129 
5130 			/* Hack - clear tile size info XXX XXX */
5131 			td->tile_wid = td->tile_hgt = 0;
5132 
5133 			/* Apply and Verify */
5134 			term_data_check_font(td);
5135 			term_data_check_size(td);
5136 
5137 			/* Resize and Redraw */
5138 			term_data_resize(td);
5139 			term_data_redraw(td);
5140 
5141 			/* Restore the window */
5142 			activate(old_win);
5143 
5144 			break;
5145 		}
5146 
5147 		/* Size menu */
5148 		case MENU_SIZE:
5149 		{
5150 			if (!td) break;
5151 
5152 			/* Save old */
5153 			old_win = active;
5154 
5155 			/* Activate */
5156 			activate(td->w);
5157 
5158 			td->font_size = menu_size_values[selection];
5159 
5160 			/* Hack - clear tile size info XXX XXX */
5161 			td->tile_wid = td->tile_hgt = 0;
5162 
5163 			/* Apply and Verify */
5164 			term_data_check_font(td);
5165 			term_data_check_size(td);
5166 
5167 			/* Resize and Redraw */
5168 			term_data_resize(td);
5169 			term_data_redraw(td);
5170 
5171 			/* Restore */
5172 			activate(old_win);
5173 
5174 			break;
5175 		}
5176 
5177 		/* Window menu */
5178 		case MENU_WINDOWS:
5179 		{
5180 			/* Parse */
5181 			i = selection - 1;
5182 
5183 			/* Check legality of choice */
5184 			if ((i < 0) || (i >= MAX_TERM_DATA)) break;
5185 
5186 			/* Obtain the window */
5187 			td = &data[i];
5188 
5189 			/* Mapped */
5190 			td->mapped = TRUE;
5191 
5192 			/* Link */
5193 			term_data_link(i);
5194 
5195 			/* Mapped (?) */
5196 			td->t->mapped_flag = TRUE;
5197 
5198 			/* Show the window */
5199 			TransitionWindow(td->w,
5200 				kWindowZoomTransitionEffect,
5201 				kWindowShowTransitionAction,
5202 				NULL);
5203 
5204 			/* Bring to the front */
5205 			SelectWindow(td->w);
5206 
5207 			break;
5208 		}
5209 
5210 		/* Special menu */
5211 		case MENU_SPECIAL:
5212 		{
5213 			switch (selection)
5214 			{
5215 				case ITEM_SOUND:
5216 				{
5217 					/* Toggle arg_sound */
5218 					arg_sound = !arg_sound;
5219 
5220 					/* React to changes */
5221 					Term_xtra(TERM_XTRA_REACT, 0);
5222 
5223 					break;
5224 				}
5225 
5226 				case ITEM_FIDDLE:
5227 				{
5228 					arg_fiddle = !arg_fiddle;
5229 
5230 					break;
5231 				}
5232 
5233 				case ITEM_WIZARD:
5234 				{
5235 					arg_wizard = !arg_wizard;
5236 
5237 					break;
5238 				}
5239 			}
5240 
5241 			break;
5242 		}
5243 
5244 		/* Graphics submenu */
5245 		case SUBMENU_GRAPH:
5246 		{
5247 			switch (selection)
5248 			{
5249 				case ITEM_NONE:
5250 				{
5251 					graf_mode_req = GRAF_MODE_NONE;
5252 
5253 					break;
5254 				}
5255 
5256 				case ITEM_8X8:
5257 				{
5258 					graf_mode_req = GRAF_MODE_8X8;
5259 
5260 					break;
5261 				}
5262 
5263 				case ITEM_16X16:
5264 				{
5265 					graf_mode_req = GRAF_MODE_16X16;
5266 
5267 					break;
5268 				}
5269 
5270 				case ITEM_32X32:
5271 				{
5272 					graf_mode_req = GRAF_MODE_32X32;
5273 
5274 					break;
5275 				}
5276 
5277 #ifdef USE_DOUBLE_TILES
5278 
5279 				case ITEM_BIGTILE:
5280 				{
5281 					term *old = Term;
5282 					term_data *td = &data[0];
5283 
5284 					/* Toggle "use_bigtile" */
5285 					use_bigtile = !use_bigtile;
5286 
5287 					/* Activate */
5288 					Term_activate(td->t);
5289 
5290 					/* Resize the term */
5291 					Term_resize(td->cols, td->rows);
5292 
5293 					/* Activate old */
5294 					Term_activate(old);
5295 
5296 					break;
5297 				}
5298 
5299 #endif /* USE_DOUBLE_TILES */
5300 
5301 			}
5302 
5303 			/* Hack -- Force redraw */
5304 			Term_key_push(KTRL('R'));
5305 
5306 			break;
5307 		}
5308 
5309 		/* TileWidth menu */
5310 		case SUBMENU_TILEWIDTH:
5311 		{
5312 			if (!td) break;
5313 
5314 			/* Save old */
5315 			old_win = active;
5316 
5317 			/* Activate */
5318 			activate(td->w);
5319 
5320 			/* Analyse value */
5321 			td->tile_wid = menu_tilewidth_values[selection];
5322 
5323 			/* Apply and Verify */
5324 			term_data_check_size(td);
5325 
5326 			/* Resize and Redraw */
5327 			term_data_resize(td);
5328 			term_data_redraw(td);
5329 
5330 			/* Restore */
5331 			activate(old_win);
5332 
5333 			break;
5334 		}
5335 
5336 		/* TileHeight menu */
5337 		case SUBMENU_TILEHEIGHT:
5338 		{
5339 			if (!td) break;
5340 
5341 			/* Save old */
5342 			old_win = active;
5343 
5344 			/* Activate */
5345 			activate(td->w);
5346 
5347 			/* Analyse value */
5348 			td->tile_hgt = menu_tileheight_values[selection];
5349 
5350 			/* Apply and Verify */
5351 			term_data_check_size(td);
5352 
5353 			/* Resize and Redraw */
5354 			term_data_resize(td);
5355 			term_data_redraw(td);
5356 
5357 			/* Restore */
5358 			activate(old_win);
5359 
5360 			break;
5361 		}
5362 	}
5363 
5364 
5365 	/* Clean the menu */
5366 	HiliteMenu(0);
5367 }
5368 
5369 
5370 /*
5371  * Check for extra required parameters -- From "Maarten Hazewinkel"
5372  */
CheckRequiredAEParams(const AppleEvent * theAppleEvent)5373 static OSErr CheckRequiredAEParams(const AppleEvent *theAppleEvent)
5374 {
5375 	OSErr aeError;
5376 	DescType returnedType;
5377 	Size actualSize;
5378 
5379 	aeError = AEGetAttributePtr(
5380 		theAppleEvent, keyMissedKeywordAttr, typeWildCard,
5381 		&returnedType, NULL, 0, &actualSize);
5382 
5383 	if (aeError == errAEDescNotFound) return (noErr);
5384 
5385 	if (aeError == noErr) return (errAEParamMissed);
5386 
5387 	return (aeError);
5388 }
5389 
5390 
5391 /*
5392  * Apple Event Handler -- Open Application
5393  */
AEH_Start(const AppleEvent * theAppleEvent,AppleEvent * reply,SInt32 handlerRefCon)5394 static pascal OSErr AEH_Start(const AppleEvent *theAppleEvent, AppleEvent *reply,
5395 	SInt32 handlerRefCon)
5396 {
5397 #pragma unused(reply)
5398 #pragma unused(handlerRefCon)
5399 
5400 	return (CheckRequiredAEParams(theAppleEvent));
5401 }
5402 
5403 
5404 /*
5405  * Apple Event Handler -- Quit Application
5406  */
AEH_Quit(const AppleEvent * theAppleEvent,AppleEvent * reply,SInt32 handlerRefCon)5407 static pascal OSErr AEH_Quit(const AppleEvent *theAppleEvent, AppleEvent *reply,
5408 	SInt32 handlerRefCon)
5409 {
5410 #pragma unused(reply)
5411 #pragma unused(handlerRefCon)
5412 
5413 	/* Quit later */
5414 	quit_when_ready = TRUE;
5415 
5416 	/* Check arguments */
5417 	return (CheckRequiredAEParams(theAppleEvent));
5418 }
5419 
5420 
5421 /*
5422  * Apple Event Handler -- Print Documents
5423  */
AEH_Print(const AppleEvent * theAppleEvent,AppleEvent * reply,SInt32 handlerRefCon)5424 static pascal OSErr AEH_Print(const AppleEvent *theAppleEvent, AppleEvent *reply,
5425 	SInt32 handlerRefCon)
5426 {
5427 #pragma unused(theAppleEvent)
5428 #pragma unused(reply)
5429 #pragma unused(handlerRefCon)
5430 
5431 	return (errAEEventNotHandled);
5432 }
5433 
5434 
5435 /*
5436  * Apple Event Handler by Steve Linberg (slinberg@crocker.com).
5437  *
5438  * The old method of opening savefiles from the finder does not work
5439  * on the Power Macintosh, because CountAppFiles and GetAppFiles,
5440  * used to return information about the selected document files when
5441  * an application is launched, are part of the Segment Loader, which
5442  * is not present in the RISC OS due to the new memory architecture.
5443  *
5444  * The "correct" way to do this is with AppleEvents.  The following
5445  * code is modeled on the "Getting Files Selected from the Finder"
5446  * snippet from Think Reference 2.0.  (The prior sentence could read
5447  * "shamelessly swiped & hacked")
5448  */
AEH_Open(const AppleEvent * theAppleEvent,AppleEvent * reply,SInt32 handlerRefCon)5449 static pascal OSErr AEH_Open(const AppleEvent *theAppleEvent, AppleEvent* reply,
5450 	SInt32 handlerRefCon)
5451 {
5452 	FSSpec myFSS;
5453 	AEDescList docList;
5454 	OSErr err;
5455 	Size actualSize;
5456 	AEKeyword keywd;
5457 	DescType returnedType;
5458 	char msg[128];
5459 	FInfo myFileInfo;
5460 
5461 #pragma unused(reply)
5462 #pragma unused(handlerRefCon)
5463 
5464 	/* Put the direct parameter (a descriptor list) into a docList */
5465 	err = AEGetParamDesc(
5466 		theAppleEvent, keyDirectObject, typeAEList, &docList);
5467 	if (err) return err;
5468 
5469 	/*
5470 	 * We ignore the validity check, because we trust the FInder, and we only
5471 	 * allow one savefile to be opened, so we ignore the depth of the list.
5472 	 */
5473 	err = AEGetNthPtr(
5474 		&docList, 1L, typeFSS, &keywd, &returnedType,
5475 		(Ptr) &myFSS, sizeof(myFSS), &actualSize);
5476 	if (err) return err;
5477 
5478 	/* Only needed to check savefile type below */
5479 	err = FSpGetFInfo(&myFSS, &myFileInfo);
5480 	if (err)
5481 	{
5482 		strnfmt(msg, sizeof(msg), "Argh!  FSpGetFInfo failed with code %d", err);
5483 		mac_warning(msg);
5484 		return err;
5485 	}
5486 
5487 	/* Ignore non 'SAVE' files */
5488 	if (myFileInfo.fdType != 'SAVE') return noErr;
5489 
5490 #ifdef MACH_O_CARBON
5491 
5492 	/* Extract a file name */
5493 	(void)spec_to_path(&myFSS, savefile, sizeof(savefile));
5494 
5495 #else
5496 
5497 	/* XXX XXX XXX Extract a file name */
5498 	PathNameFromDirID(myFSS.parID, myFSS.vRefNum, (StringPtr)savefile);
5499 	pstrcat((StringPtr)savefile, (StringPtr)&myFSS.name);
5500 
5501 	/* Convert the string */
5502 	ptocstr((StringPtr)savefile);
5503 
5504 #endif /* MACH_O_CARBON */
5505 
5506 	/* Delay actual open */
5507 	open_when_ready = TRUE;
5508 
5509 	/* Dispose */
5510 	err = AEDisposeDesc(&docList);
5511 
5512 	/* Success */
5513 	return noErr;
5514 }
5515 
5516 
5517 /*
5518  * Apple Event Handler -- Re-open Application
5519  *
5520  * If no windows are currently open, show the Angband window.
5521  * This required AppleEvent was introduced by System 8 -- pelpel
5522  */
AEH_Reopen(const AppleEvent * theAppleEvent,AppleEvent * reply,long handlerRefCon)5523 static pascal OSErr AEH_Reopen(const AppleEvent *theAppleEvent,
5524 			     AppleEvent* reply, long handlerRefCon)
5525 {
5526 #pragma unused(theAppleEvent, reply, handlerRefCon)
5527 
5528 	term_data *td = NULL;
5529 
5530 	/* No open windows */
5531 	if (NULL == FrontWindow())
5532 	{
5533 		/* Obtain the Angband window */
5534 		td = &data[0];
5535 
5536 		/* Mapped */
5537 		td->mapped = TRUE;
5538 
5539 		/* Link */
5540 		term_data_link(0);
5541 
5542 		/* Mapped (?) */
5543 		td->t->mapped_flag = TRUE;
5544 
5545 		/* Show the window */
5546 		ShowWindow(td->w);
5547 
5548 		/* Bring to the front */
5549 		SelectWindow(td->w);
5550 
5551 		/* Make it active */
5552 		activate(td->w);
5553 	}
5554 
5555 	/* Event handled */
5556 	return (noErr);
5557 }
5558 
5559 
5560 /*
5561  * Handle quit_when_ready, by Peter Ammon,
5562  * slightly modified to check inkey_flag.
5563  */
quit_calmly(void)5564 static void quit_calmly(void)
5565 {
5566 	/* Quit immediately if game's not started */
5567 	if (!game_in_progress || !character_generated) quit(NULL);
5568 
5569 	/* Save the game and Quit (if it's safe) */
5570 	if (p_ptr->cmd.inkey_flag)
5571 	{
5572 		/* Hack -- Forget messages */
5573 		msg_flag = FALSE;
5574 
5575 		/* Save the game */
5576 #ifndef ZANG_AUTO_SAVE
5577 		do_cmd_save_game();
5578 #else
5579 		do_cmd_save_game(FALSE);
5580 #endif /* !ZANG_AUTO_SAVE */
5581 
5582 		/* Quit */
5583 		quit(NULL);
5584 	}
5585 
5586 	/* Wait until inkey_flag is set */
5587 }
5588 
5589 
5590 /*
5591  * Macintosh modifiers (event.modifier & ccc):
5592  *   cmdKey, optionKey, shiftKey, alphaLock, controlKey
5593  *
5594  *
5595  * Macintosh Keycodes (0-63 normal, 64-95 keypad, 96-127 extra):
5596  *
5597  * Return:36
5598  * Delete:51
5599  *
5600  * Period:65
5601  * Star:67
5602  * Plus:69
5603  * Clear:71
5604  * Slash:75
5605  * Enter:76
5606  * Minus:78
5607  * Equal:81
5608  * 0-7:82-89
5609  * 8-9:91-92
5610  *
5611  * backslash/vertical bar (Japanese keyboard):93
5612  *
5613  * F5: 96
5614  * F6: 97
5615  * F7: 98
5616  * F3:99
5617  * F8:100
5618  * F10:101
5619  * F11:103
5620  * F13:105
5621  * F14:107
5622  * F9:109
5623  * F12:111
5624  * F15:113
5625  * Help:114
5626  * Home:115
5627  * PgUp:116
5628  * Del:117
5629  * F4: 118
5630  * End:119
5631  * F2:120
5632  * PgDn:121
5633  * F1:122
5634  * Lt:123
5635  * Rt:124
5636  * Dn:125
5637  * Up:126
5638  */
5639 
5640 
5641 /*
5642  * Optimize non-blocking calls to "CheckEvents()"
5643  * Idea from "Maarten Hazewinkel <mmhazewi@cs.ruu.nl>"
5644  *
5645  * WAS: 6. The value of one (~ 60 FPS) seems to work better with the Borg,
5646  * and so should be for other CPU-intensive features like the autoroller.
5647  */
5648 #define EVENT_TICKS 1
5649 
5650 
5651 /*
5652  * Check for Events, return TRUE if we process any
5653  */
CheckEvents(int wait)5654 static bool CheckEvents(int wait)
5655 {
5656 	EventRecord event;
5657 
5658 	WindowPtr w;
5659 
5660 	Rect r;
5661 
5662 	UInt32 sleep_ticks;
5663 
5664 	int ch, ck;
5665 
5666 	int mc, ms, mo, mx;
5667 
5668 	term_data *td = NULL;
5669 
5670 	UInt32 curTicks;
5671 
5672 	static UInt32 lastTicks = 0L;
5673 
5674 
5675 	/* Access the clock */
5676 	curTicks = TickCount();
5677 
5678 	/* Hack -- Allow efficient checking for non-pending events */
5679 	if ((wait == CHECK_EVENTS_NO_WAIT) &&
5680 		(curTicks < lastTicks + EVENT_TICKS)) return (FALSE);
5681 
5682 	/* Timestamp last check */
5683 	lastTicks = curTicks;
5684 
5685 
5686 	/* Handles the quit_when_ready flag */
5687 	if (quit_when_ready) quit_calmly();
5688 
5689 	/* Blocking call to WaitNextEvent - should use MAX_INT XXX XXX */
5690 	if (wait == CHECK_EVENTS_WAIT) sleep_ticks = 0x7FFFFFFFL;
5691 
5692 	/* Non-blocking */
5693 	else sleep_ticks = 0L;
5694 
5695 	/* Get an event (or null)  */
5696 	WaitNextEvent(everyEvent, &event, sleep_ticks, nil);
5697 
5698 	/* Hack -- Nothing is ready yet */
5699 	if (event.what == nullEvent) return (FALSE);
5700 
5701 
5702 	/* Analyze the event */
5703 	switch (event.what)
5704 	{
5705 
5706 #if 0
5707 
5708 		case activateEvt:
5709 		{
5710 			w = (WindowPtr)event.message;
5711 
5712 			activate(w);
5713 
5714 			break;
5715 		}
5716 
5717 #endif
5718 
5719 		case updateEvt:
5720 		{
5721 			/* Extract the window */
5722 			w = (WindowPtr)event.message;
5723 
5724 			/* Relevant "term_data" */
5725 			td = (term_data *)GetWRefCon(w);
5726 
5727 			/* Clear window's update region and clip drawings with it */
5728 			BeginUpdate(w);
5729 
5730 			/* Redraw the window */
5731 			if (td) term_data_redraw(td);
5732 
5733 			/* Restore window's clipping region */
5734 			EndUpdate(w);
5735 
5736 			break;
5737 		}
5738 
5739 		case keyDown:
5740 		case autoKey:
5741 		{
5742 			/* Extract some modifiers */
5743 			mc = (event.modifiers & controlKey) ? TRUE : FALSE;
5744 			ms = (event.modifiers & shiftKey) ? TRUE : FALSE;
5745 			mo = (event.modifiers & optionKey) ? TRUE : FALSE;
5746 			mx = (event.modifiers & cmdKey) ? TRUE : FALSE;
5747 
5748 			/* Keypress: (only "valid" if ck < 96) */
5749 			ch = (event.message & charCodeMask) & 255;
5750 
5751 			/* Keycode: see table above */
5752 			ck = ((event.message & keyCodeMask) >> 8) & 255;
5753 
5754 			/* Command + "normal key" -> menu action */
5755 			if (mx && (ck < 64))
5756 			{
5757 				/* Hack -- Prepare the menus */
5758 				setup_menus();
5759 
5760 				/* Run the Menu-Handler */
5761 				menu(MenuKey(ch));
5762 
5763 				/* Turn off the menus */
5764 				HiliteMenu(0);
5765 
5766 				/* Done */
5767 				break;
5768 			}
5769 
5770 
5771 			/* Hide the mouse pointer */
5772 			ObscureCursor();
5773 
5774 			/* Normal key -> simple keypress */
5775 			if ((ck < 64) || (ck == 93))
5776 			{
5777 				/* Enqueue the keypress */
5778 				Term_keypress(ch);
5779 			}
5780 
5781 			/* Keypad keys -> trigger plus simple keypress */
5782 			else if (!mc && !ms && !mo && !mx && (ck < 96))
5783 			{
5784 				/* Hack -- "enter" is confused */
5785 				if (ck == 76) ch = '\n';
5786 
5787 				/* Begin special trigger */
5788 				Term_keypress(31);
5789 
5790 				/* Send the "keypad" modifier */
5791 				Term_keypress('K');
5792 
5793 				/* Terminate the trigger */
5794 				Term_keypress(13);
5795 
5796 				/* Send the "ascii" keypress */
5797 				Term_keypress(ch);
5798 			}
5799 
5800 			/* Bizarre key -> encoded keypress */
5801 			else if (ck <= 127)
5802 			{
5803 				/* Begin special trigger */
5804 				Term_keypress(31);
5805 
5806 				/* Send some modifier keys */
5807 				if (mc) Term_keypress('C');
5808 				if (ms) Term_keypress('S');
5809 				if (mo) Term_keypress('O');
5810 				if (mx) Term_keypress('X');
5811 
5812 				/* Downshift and encode the keycode */
5813 				Term_keypress(I2D((ck - 64) / 10));
5814 				Term_keypress(I2D((ck - 64) % 10));
5815 
5816 				/* Terminate the trigger */
5817 				Term_keypress(13);
5818 			}
5819 
5820 			break;
5821 		}
5822 
5823 		case mouseDown:
5824 		{
5825 			int code;
5826 
5827 			/* Analyze click location */
5828 			code = FindWindow(event.where, &w);
5829 
5830 			/* Relevant "term_data" */
5831 			td = (term_data *)GetWRefCon(w);
5832 
5833 			/* Analyze */
5834 			switch (code)
5835 			{
5836 				case inMenuBar:
5837 				{
5838 					setup_menus();
5839 					menu(MenuSelect(event.where));
5840 					HiliteMenu(0);
5841 					break;
5842 				}
5843 
5844 				case inDrag:
5845 				{
5846 					WindowPtr old_win;
5847 					BitMap tBitMap;
5848 					Rect pRect;
5849 
5850 					r = GetQDGlobalsScreenBits(&tBitMap)->bounds;
5851 					r.top += 20; /* GetMBarHeight() XXX XXX XXX */
5852 					InsetRect(&r, 4, 4);
5853 					DragWindow(w, event.where, &r);
5854 
5855 					/* Oops */
5856 					if (!td) break;
5857 
5858 					/* Save */
5859 					old_win = active;
5860 
5861 					/* Activate */
5862 					activate(td->w);
5863 
5864 					/* Analyze */
5865 					GetWindowBounds(
5866 						(WindowRef)td->w,
5867 						kWindowContentRgn,
5868 						&pRect);
5869 					td->r.left = pRect.left;
5870 					td->r.top = pRect.top;
5871 
5872 					/* Apply and Verify */
5873 					term_data_check_size(td);
5874 
5875 					/* Restore */
5876 					activate(old_win);
5877 
5878 					break;
5879 				}
5880 
5881 				case inGoAway:
5882 				{
5883 					/* Oops */
5884 					if (!td) break;
5885 
5886 					/* Track the go-away box */
5887 					if (TrackGoAway(w, event.where))
5888 					{
5889 						/* Not Mapped */
5890 						td->mapped = FALSE;
5891 
5892 						/* Not Mapped */
5893 						td->t->mapped_flag = FALSE;
5894 
5895 						/* Hide the window */
5896 						TransitionWindow(td->w,
5897 							kWindowZoomTransitionEffect,
5898 							kWindowHideTransitionAction,
5899 							NULL);
5900 					}
5901 
5902 					break;
5903 				}
5904 
5905 				case inGrow:
5906 				{
5907 					int x, y;
5908 
5909 					Rect nr;
5910 
5911 					term *old = Term;
5912 
5913 					/* Oops */
5914 					if (!td) break;
5915 
5916 #ifndef ALLOW_BIG_SCREEN
5917 
5918 					/* Minimum and maximum sizes */
5919 					r.left = 20 * td->tile_wid + td->size_ow1;
5920 					r.right = 80 * td->tile_wid + td->size_ow1 + td->size_ow2 + 1;
5921 					r.top = 1 * td->tile_hgt + td->size_oh1;
5922 					r.bottom = 24 * td->tile_hgt + td->size_oh1 + td->size_oh2 + 1;
5923 
5924 					/* Grow the rectangle */
5925 					if (!ResizeWindow(w, event.where, &r, NULL)) break;
5926 #else
5927 
5928 					/* Grow the rectangle */
5929 					if (!ResizeWindow(w, event.where, NULL, NULL)) break;
5930 
5931 #endif /* !ALLOW_BIG_SCREEN */
5932 
5933 
5934 					/* Obtain geometry of resized window */
5935 					GetWindowBounds(w, kWindowContentRgn, &nr);
5936 
5937 					/* Extract the new size in pixels */
5938 					y = nr.bottom - nr.top - td->size_oh1 - td->size_oh2;
5939 					x = nr.right - nr.left - td->size_ow1 - td->size_ow2;
5940 
5941 					/* Extract a "close" approximation */
5942 					td->rows = y / td->tile_hgt;
5943 					td->cols = x / td->tile_wid;
5944 
5945 					/* Apply and Verify */
5946 					term_data_check_size(td);
5947 
5948 					/* Activate */
5949 					Term_activate(td->t);
5950 
5951 					/* Hack -- Resize the term */
5952 					Term_resize(td->cols, td->rows);
5953 
5954 					/* Resize and Redraw */
5955 					term_data_resize(td);
5956 					term_data_redraw(td);
5957 
5958 					/* Restore */
5959 					Term_activate(old);
5960 
5961 					break;
5962 				}
5963 
5964 				case inContent:
5965 				{
5966 					SelectWindow(w);
5967 
5968 					break;
5969 				}
5970 			}
5971 
5972 			break;
5973 		}
5974 
5975 		/* OS Event -- From "Maarten Hazewinkel" */
5976 		case osEvt:
5977 		{
5978 			switch ((event.message >> 24) & 0x000000FF)
5979 			{
5980 				case suspendResumeMessage:
5981 
5982 				/* Resuming: activate the front window */
5983 				if (event.message & resumeFlag)
5984 				{
5985 					Cursor tempCursor;
5986 					WindowRef w;
5987 
5988 					/* Find the window */
5989 					w = FrontWindow();
5990 
5991 					if (w != NULL)
5992 					{
5993 						/* Relevant "term_data" */
5994 						td = (term_data *)GetWRefCon(FrontWindow());
5995 
5996 						/* Activate the window */
5997 						SetPort(GetWindowPort(td->w));
5998 
5999 						/* Mega-Hack -- Synchronise 'active' */
6000 						active = td->w;
6001 
6002 						SetCursor(GetQDGlobalsArrow(&tempCursor));
6003 
6004 						/* Synchronise term */
6005 						Term_activate(td->t);
6006 					}
6007 				}
6008 
6009 				/* Suspend: deactivate the front window */
6010 				else
6011 				{
6012 					/* Nothing */
6013 				}
6014 
6015 				break;
6016 			}
6017 
6018 			break;
6019 		}
6020 
6021 		/* From "Steve Linberg" and "Maarten Hazewinkel" */
6022 		case kHighLevelEvent:
6023 		{
6024 			/* Process apple events */
6025 			(void)AEProcessAppleEvent(&event);
6026 
6027 			/* Handle "quit_when_ready" */
6028 			if (quit_when_ready)
6029 			{
6030 #if 0 /* Doesn't work with Aqua well */
6031 				/* Forget */
6032 				quit_when_ready = FALSE;
6033 
6034 				/* Do the menu key */
6035 				menu(MenuKey('q'));
6036 #endif
6037 				/* Turn off the menus */
6038 				HiliteMenu(0);
6039 			}
6040 
6041 			/* Handle "open_when_ready" */
6042 			else if (open_when_ready)
6043 			{
6044 				handle_open_when_ready();
6045 			}
6046 
6047 			break;
6048 		}
6049 
6050 	}
6051 
6052 
6053 	/* Something happened */
6054 	return (TRUE);
6055 }
6056 
6057 
6058 /*** Some Hooks for various routines ***/
6059 
6060 
6061 /*
6062  * Mega-Hack -- emergency lifeboat
6063  */
6064 static void *lifeboat = NULL;
6065 
6066 
6067 /*
6068  * Hook to "release" memory
6069  */
6070 #ifdef NEW_ZVIRT_HOOKS /* [V] removed the unused 'size' argument. */
hook_rnfree(void * v)6071 static void *hook_rnfree(void *v)
6072 #else
6073 static void *hook_rnfree(void *v, huge size)
6074 #endif /* NEW_ZVIRT_HOOKS */
6075 {
6076 
6077 #ifdef USE_MALLOC
6078 
6079 	/* Alternative method */
6080 	free(v);
6081 
6082 #else
6083 
6084 	/* Dispose */
6085 	DisposePtr(v);
6086 
6087 #endif
6088 
6089 	/* Success */
6090 	return (NULL);
6091 }
6092 
6093 /*
6094  * Hook to "allocate" memory
6095  */
hook_ralloc(huge size)6096 static void *hook_ralloc(huge size)
6097 {
6098 
6099 #ifdef USE_MALLOC
6100 
6101 	/* Make a new pointer */
6102 	return (malloc(size));
6103 
6104 #else
6105 
6106 	/* Make a new pointer */
6107 	return (NewPtr(size));
6108 
6109 #endif
6110 
6111 }
6112 
6113 /*
6114  * Hook to handle "out of memory" errors
6115  */
hook_rpanic(huge size)6116 static void *hook_rpanic(huge size)
6117 {
6118 #pragma unused(size)
6119 
6120 	/* Free the lifeboat */
6121 	if (lifeboat)
6122 	{
6123 		/* Free the lifeboat */
6124 		DisposePtr(lifeboat);
6125 
6126 		/* Forget the lifeboat */
6127 		lifeboat = NULL;
6128 
6129 		/* Mega-Hack -- Warning */
6130 		mac_warning("Running out of Memory!\rAbort this process now!");
6131 
6132 		/* Mega-Hack -- Never leave this function */
6133 		while (TRUE) CheckEvents(CHECK_EVENTS_WAIT);
6134 	}
6135 
6136 	/* Mega-Hack -- Crash */
6137 	return (NULL);
6138 }
6139 
6140 
6141 /*
6142  * Hook to tell the user something important
6143  */
hook_plog(cptr str)6144 static void hook_plog(cptr str)
6145 {
6146 	/* Warning message */
6147 	mac_warning(str);
6148 }
6149 
6150 
6151 /*
6152  * Hook to tell the user something, and then quit
6153  */
hook_quit(cptr str)6154 static void hook_quit(cptr str)
6155 {
6156 	/* Warning if needed */
6157 	if (str) mac_warning(str);
6158 
6159 #ifdef USE_ASYNC_SOUND
6160 
6161 	/* Clean up sound support */
6162 	cleanup_sound();
6163 
6164 #endif /* USE_ASYNC_SOUND */
6165 
6166 	/* Dispose of graphic tiles */
6167 	if (frameP)
6168 	{
6169 		/* Unlock */
6170 		BenSWUnlockFrame(frameP);
6171 
6172 		/* Dispose of the GWorld */
6173 		DisposeGWorld(frameP->framePort);
6174 
6175 		/* Dispose of the memory */
6176 		DisposePtr((Ptr)frameP);
6177 	}
6178 
6179 	/* Write a preference file */
6180 	if (initialized) save_pref_file();
6181 
6182 	/* All done */
6183 	ExitToShell();
6184 }
6185 
6186 
6187 /*
6188  * Hook to tell the user something, and then crash
6189  */
hook_core(cptr str)6190 static void hook_core(cptr str)
6191 {
6192 	/* XXX Use the debugger */
6193 	/* DebugStr(str); */
6194 
6195 	/* Warning */
6196 	if (str) mac_warning(str);
6197 
6198 	/* Warn, then save player */
6199 	mac_warning("Fatal error.\rI will now attempt to save and quit.");
6200 
6201 	/* Attempt to save */
6202 	if (!save_player()) mac_warning("Warning -- save failed!");
6203 
6204 	/* Quit */
6205 	quit(NULL);
6206 }
6207 
6208 
6209 
6210 /*** Main program ***/
6211 
6212 
6213 /*
6214  * Init some stuff
6215  *
6216  * XXX XXX XXX Hack -- This function attempts to "fix" the nasty
6217  * "Macintosh Save Bug" by using "absolute" path names, since on
6218  * System 7 machines anyway, the "current working directory" often
6219  * "changes" due to background processes, invalidating any "relative"
6220  * path names.  Note that the Macintosh is limited to 255 character
6221  * path names, so be careful about deeply embedded directories...
6222  *
6223  * XXX XXX XXX Hack -- This function attempts to "fix" the nasty
6224  * "missing lib folder bug" by allowing the user to help find the
6225  * "lib" folder by hand if the "application folder" code fails...
6226  *
6227  *
6228  * The problem description above no longer applies, but I left it here,
6229  * modified for Carbon, to allow the game proceeds when a user doesn't
6230  * placed the Angband binary and the lib folder in the same place for
6231  * whatever reasons. -- pelpel
6232  */
init_stuff(void)6233 static void init_stuff(void)
6234 {
6235 	Rect r;
6236 	BitMap tBitMap;
6237 	Rect screenRect;
6238 	Point topleft;
6239 
6240 	char path[1024];
6241 
6242 	OSErr err = noErr;
6243 	NavDialogOptions dialogOptions;
6244 	FSSpec theFolderSpec;
6245 	NavReplyRecord theReply;
6246 
6247 
6248 	/* Fake rectangle */
6249 	r.left = 0;
6250 	r.top = 0;
6251 	r.right = 344;
6252 	r.bottom = 188;
6253 
6254 	/* Center it */
6255 	screenRect = GetQDGlobalsScreenBits(&tBitMap)->bounds;
6256 	center_rect(&r, &screenRect);
6257 
6258 	/* Extract corner */
6259 	topleft.v = r.top;
6260 	topleft.h = r.left;
6261 
6262 
6263 	/* Default to the "lib" folder with the application */
6264 #ifdef MACH_O_CARBON
6265 	if (locate_lib(path, sizeof(path)) == NULL) quit(NULL);
6266 #else
6267 	/* Metrowerks uses colon-separated path */
6268 	refnum_to_name(path, app_dir, app_vol, (char*)("\plib:"));
6269 #endif
6270 
6271 
6272 	/* Check until done */
6273 	while (1)
6274 	{
6275 		/* Create directories for the users files */
6276 		create_user_dirs();
6277 
6278 		/* Prepare the paths */
6279 		init_file_paths(path);
6280 
6281 		/* Build the filename */
6282 		path_build(path, sizeof(path), ANGBAND_DIR_FILE, "news.txt");
6283 
6284 		/* Attempt to open and close that file */
6285 		if (0 == fd_close(fd_open(path, O_RDONLY))) break;
6286 
6287 		/* Warning */
6288 		plog_fmt("Unable to open the '%s' file.", path);
6289 
6290 		/* Warning */
6291 		plog("The ZAngband 'lib' folder is probably missing or misplaced.");
6292 
6293 		/* Ask the user to choose the lib folder */
6294 		err = NavGetDefaultDialogOptions(&dialogOptions);
6295 
6296 		/* Paranoia */
6297 		if (err != noErr) quit(NULL);
6298 
6299 		/* Set default location option */
6300 		dialogOptions.dialogOptionFlags |= kNavSelectDefaultLocation;
6301 
6302 		/* Clear preview option */
6303 		dialogOptions.dialogOptionFlags &= ~(kNavAllowPreviews);
6304 
6305 		/* Forbit selection of multiple files */
6306 		dialogOptions.dialogOptionFlags &= ~(kNavAllowMultipleFiles);
6307 
6308 		/* Display location */
6309 		dialogOptions.location = topleft;
6310 
6311 #if 0
6312 
6313 		/* Load the message for the missing folder from the resource fork */
6314 		/* GetIndString(dialogOptions.message, 128, 1); */
6315 
6316 #else
6317 
6318 		/* Set the message for the missing folder XXX XXX */
6319 		strcpy((char *)dialogOptions.message + 1,
6320 			"Please select the \"lib\" folder");
6321 		dialogOptions.message[0] = strlen((char *)dialogOptions.message + 1);
6322 
6323 #endif
6324 
6325 		/* Wait for the user to choose a folder */
6326 		err = NavChooseFolder(
6327 			nil, &theReply, &dialogOptions, nil, nil, nil);
6328 
6329 		/* Assume the player doesn't want to go on */
6330 		if ((err != noErr) || !theReply.validRecord) quit(NULL);
6331 
6332 		/* Retrieve FSSpec from the reply */
6333 		{
6334 			AEKeyword theKeyword;
6335 			DescType actualType;
6336 			Size actualSize;
6337 
6338 			/* Get a pointer to selected folder */
6339 			err = AEGetNthPtr(
6340 				&(theReply.selection), 1, typeFSS, &theKeyword,
6341 				&actualType, &theFolderSpec, sizeof(FSSpec), &actualSize);
6342 
6343 			/* Paranoia */
6344 			if (err != noErr) quit(NULL);
6345 		}
6346 
6347 		/* Free navitagor reply */
6348 		err = NavDisposeReply(&theReply);
6349 
6350 		/* Paranoia */
6351 		if (err != noErr) quit(NULL);
6352 
6353 #ifdef MACH_O_CARBON
6354 
6355 		/* Extract textual file name for given file */
6356 		if (spec_to_path(&theFolderSpec, path, sizeof(path)) != noErr)
6357 		{
6358 			quit(NULL);
6359 		}
6360 
6361 #else /* MACH_O_CARBON */
6362 
6363 		/* Extract textual file name for given file */
6364 		refnum_to_name(
6365 			path,
6366 			theFolderSpec.parID,
6367 			theFolderSpec.vRefNum,
6368 			(char *)theFolderSpec.name);
6369 
6370 #endif /* MACH_O_CARBON */
6371 
6372 	}
6373 }
6374 
6375 
6376 /*
6377  * Macintosh Main loop
6378  */
main(void)6379 int main(void)
6380 {
6381 	long response;
6382 	OSStatus err;
6383 	UInt32 numberOfMasters = 10;
6384 
6385 
6386 	/* Get more Masters -- it is not recommended by Apple, should go away */
6387 	MoreMasterPointers(numberOfMasters);
6388 
6389 	/* Flush events */
6390 	FlushEvents(everyEvent, 0);
6391 
6392 	/* Initialise the cursor and turn it into an "arrow" */
6393 	InitCursor();
6394 
6395 
6396 	/* Check for existence of Carbon */
6397 	err = Gestalt(gestaltCarbonVersion, &response);
6398 
6399 	if (err != noErr) quit("This program requires Carbon API");
6400 
6401 	/* See if we are running on Aqua */
6402 	err = Gestalt(gestaltMenuMgrAttr, &response);
6403 
6404 	/* Cache the result */
6405 	if ((err == noErr) &&
6406 	    (response & gestaltMenuMgrAquaLayoutMask)) is_aqua = TRUE;
6407 
6408 	/*
6409 	 * Remember Mac OS version, in case we have to cope with version-specific
6410 	 * problems
6411 	 */
6412 	(void)Gestalt(gestaltSystemVersion, &mac_os_version);
6413 
6414 
6415 	/* Install the start event hook (ignore error codes) */
6416 	(void)AEInstallEventHandler(
6417 		kCoreEventClass,
6418 		kAEOpenApplication,
6419 		NewAEEventHandlerUPP(AEH_Start),
6420 		0L,
6421 		FALSE);
6422 
6423 	/* Install the quit event hook (ignore error codes) */
6424 	(void)AEInstallEventHandler(
6425 		kCoreEventClass,
6426 		kAEQuitApplication,
6427 		NewAEEventHandlerUPP(AEH_Quit),
6428 		0L,
6429 		FALSE);
6430 
6431 	/* Install the print event hook (ignore error codes) */
6432 	(void)AEInstallEventHandler(
6433 		kCoreEventClass,
6434 		kAEPrintDocuments,
6435 		NewAEEventHandlerUPP(AEH_Print),
6436 		0L,
6437 		FALSE);
6438 
6439 	/* Install the open event hook (ignore error codes) */
6440 	(void)AEInstallEventHandler(
6441 		kCoreEventClass,
6442 		kAEOpenDocuments,
6443 		NewAEEventHandlerUPP(AEH_Open),
6444 		0L,
6445 		FALSE);
6446 
6447 	/* Install the Re-open event hook (ignore error codes) */
6448 	(void)AEInstallEventHandler(
6449 		kCoreEventClass,
6450 		kAEReopenApplication,
6451 		NewAEEventHandlerUPP(AEH_Reopen),
6452 		0L,
6453 		FALSE);
6454 
6455 
6456 #ifndef MACH_O_CARBON
6457 
6458 	/* Find the current application */
6459 	SetupAppDir();
6460 
6461 #endif /* !MACH_O_CARBON */
6462 
6463 
6464 	/* Mark ourself as the file creator */
6465 	_fcreator = ANGBAND_CREATOR;
6466 
6467 	/* Default to saving a "text" file */
6468 	_ftype = 'TEXT';
6469 
6470 
6471 	/* Hook in some "z-virt.c" hooks */
6472 	rnfree_aux = hook_rnfree;
6473 	ralloc_aux = hook_ralloc;
6474 	rpanic_aux = hook_rpanic;
6475 
6476 	/* Hooks in some "z-util.c" hooks */
6477 	plog_aux = hook_plog;
6478 	quit_aux = hook_quit;
6479 	core_aux = hook_core;
6480 
6481 
6482 	/* Initialize colors */
6483 	update_colour_info();
6484 
6485 
6486 	/* Show the "watch" cursor */
6487 	SetCursor(*(GetCursor(watchCursor)));
6488 
6489 	/* Prepare the menubar */
6490 	init_menubar();
6491 
6492 	/* Prepare the windows */
6493 	init_windows();
6494 
6495 	/* Hack -- process all events */
6496 	while (CheckEvents(CHECK_EVENTS_DRAIN)) /* loop */;
6497 
6498 	/* Reset the cursor */
6499 	{
6500 		Cursor tempCursor;
6501 
6502 		SetCursor(GetQDGlobalsArrow(&tempCursor));
6503 	}
6504 
6505 
6506 	/* Mega-Hack -- Allocate a "lifeboat" */
6507 	lifeboat = NewPtr(16384);
6508 
6509 #ifdef USE_QT_SOUND
6510 
6511 	/* Load sound effect resources */
6512 	load_sounds();
6513 
6514 #endif /* USE_QT_SOUND */
6515 
6516 	/* Note the "system" */
6517 	ANGBAND_SYS = "mac";
6518 
6519 
6520 	/* Initialize */
6521 	init_stuff();
6522 
6523 	/* Initialize */
6524 	init_angband();
6525 
6526 	/* Validate the contents of the main window */
6527 	validate_main_window();
6528 
6529 	/* Hack -- process all events */
6530 	while (CheckEvents(CHECK_EVENTS_DRAIN)) /* loop */;
6531 
6532 
6533 	/* We are now initialized */
6534 	initialized = TRUE;
6535 
6536 
6537 	/* Handle "open_when_ready" */
6538 	handle_open_when_ready();
6539 
6540 	/* Let the player choose a savefile or start a new game */
6541 	if (!game_in_progress)
6542 	{
6543 		/* Prompt the user - You may have to change this for some variants */
6544 		prtf(15, 23, "[Choose 'New' or 'Open' from the 'File' menu]");
6545 
6546 		/* Flush the prompt */
6547 		Term_fresh();
6548 
6549 		/* Hack -- Process Events until "new" or "open" is selected */
6550 		while (!game_in_progress) CheckEvents(CHECK_EVENTS_WAIT);
6551 	}
6552 
6553 	/* Handle pending events (most notably update) and flush input */
6554 	Term_flush();
6555 
6556 	/*
6557 	 * Play a game -- "new_game" is set by "new", "open" or the open document
6558 	 * even handler as appropriate
6559 	 */
6560 	play_game(new_game);
6561 
6562 	/* Quit */
6563 	quit(NULL);
6564 
6565 	/* Since it's an int function */
6566 	return (0);
6567 }
6568 
6569 #endif /* MACINTOSH || MACH_O_CARBON */
6570