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, ¤tGDH);
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