/* File: main-mac.c */ /* * Copyright (c) 1997 Ben Harrison, Keith Randall, and others * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ /* * This file helps Angband work with Macintosh computers. * * To use this file, use an appropriate "Makefile" or "Project File", which * should define "MACINTOSH". * * The official compilation uses the CodeWarrior Pro compiler. * * If you are never going to use "graphics" (especially if you are not * compiling support for graphics anyway) then you can delete the "pict" * resource with id "1001" with no dangerous side effects. * * * By default, this file assumes that you will be using a 68020 or better * machine, running System 7 and Color Quickdraw. In fact, the game will * refuse to run unless these features are available. This allows the use * of a variety of interesting features such as graphics and sound. * * To create a version which can be used on 68000 machines, or on machines * which are not running System 7 or Color Quickdraw, simply activate the * "ANGBAND_LITE_MAC" compilation flag in the proper header file. This * will disable all "modern" features used in this file, including support * for multiple sub-windows, color, graphics, and sound. * * When compiling with the "ANGBAND_LITE_MAC" flag, the "ANGBAND_LITE" * flag will be automatically defined, which will disable many of the * advanced features of the game itself, reducing the total memory usage. * * * Note that the "preference" file is now a simple text file called * "Angband Preferences", which contains a version stamp, so that * obsolete preference files can be ignored. This should probably * be replaced with a "structured" preference file of some kind. * * Note that "init1.c", "init2.c", "load1.c", "load2.c", and "birth.c" * should probably be "unloaded" as soon as they are no longer needed, * to save space, but I do not know how to do this. XXX XXX XXX * * Stange bug -- The first "ClipRect()" call crashes if the user closes * all the windows, switches to another application, switches back, and * re-opens the main window, for example, using "command-a". XXX XXX XXX * * * Initial framework (and most code) by Ben Harrison (benh@phial.com). * * Some code adapted from "MacAngband 2.6.1" by Keith Randall * * Initial PowerMac port by Maarten Hazewinkel (mmhazewi@cs.ruu.nl). * * Most "USE_SFL_CODE" code provided by Steve Linberg (slinberg@crocker.com). * * Most of the graphics code is adapted from an extremely minimal subset of * the "Sprite World II" package, an amazing (and free) animation package. * * * Important Resources in the resource file: * * FREF 130 = ANGBAND_CREATOR / 'APPL' (application) * FREF 129 = ANGBAND_CREATOR / 'SAVE' (save file) * FREF 130 = ANGBAND_CREATOR / 'TEXT' (bone file, generic text file) * FREF 131 = ANGBAND_CREATOR / 'DATA' (binary image file, score file) * * DLOG 128 = "About Angband..." * * ALRT 128 = unused (?) * ALRT 129 = "Warning..." * ALRT 130 = "Are you sure you want to quit without saving?" * * DITL 128 = body for DLOG 128 * DITL 129 = body for ALRT 129 * DITL 130 = body for ALRT 130 * * ICON 128 = "warning" icon * * MENU 128 = apple (about, -, ...) * MENU 129 = File (new, open, close, save, -, score, exit, quit) * In T.o.M.E. * MENU 129 = File (close, save, -, score, exit, quit) * MENU 130 = Edit (undo, -, cut, copy, paste, clear) * MENU 131 = Font (bold, wide, -) * MENU 132 = Size () * MENU 133 = Windows () * MENU 134 = Special (Sound, Graphics, TileWidth, TileHeight, -, Fiddle, Wizard) * Graphics have following submenu attached: * MENU 144 = Graphics (None, 8x8, 16x16) * TileWidth and TileHeight submenus are filled in by this program * MENU 145 = TileWidth () * MENU 146 = TileHeight () * * PICT 1001 = Graphics tile set (8x8) * PICT 1002 = Graphics tile set (16x16 images) * * Note: You can no longer use the exit menu unless you build the programme * with an appropriate compile-time option. * * * File name patterns: * all 'APEX' files have a filename of the form "*:apex:*" (?) * all 'BONE' files have a filename of the form "*:bone:*" (?) * all 'DATA' files have a filename of the form "*:data:*" * all 'SAVE' files have a filename of the form "*:save:*" * all 'USER' files have a filename of the form "*:user:*" (?) * * Perhaps we should attempt to set the "_ftype" flag inside this file, * to avoid nasty file type information being spread all through the * rest of the code. (?) This might require adding hooks into the * "fd_open()" and "my_fopen()" functions in "util.c". XXX XXX XXX * * * Reasons for each header file: * * angband.h = Angband header file * * Types.h = (included anyway) * Gestalt.h = gestalt code * QuickDraw.h = (included anyway) * OSUtils.h = (included anyway) * Files.h = file code * Fonts.h = font code * Menus.h = menu code * Dialogs.h = dialog code * Windows.h = (included anyway) * Palettes.h = palette code * StandardFile.h = file dialog box * DiskInit.h = disk initialization * ToolUtils.h = HiWord() / LoWord() * Desk.h = OpenDeskAcc() * Devices.h = OpenDeskAcc() * Events.h = event code * Resources.h = resource code * Controls.h = button code * SegLoad.h = ExitToShell(), AppFile, etc * Memory.h = SetApplLimit(), NewPtr(), etc * QDOffscreen.h = GWorld code * Sound.h = Sound code * * For backwards compatibility: * Use GestaltEqu.h instead of Gestalt.h * Add Desk.h to include simply includes Menus.h, Devices.h, Events.h */ #include "angband.h" #ifdef MACINTOSH /* * Variant-dependent features: * * #define ALLOW_BIG_SCREEN (V, Ey, O, T.o.M.E. and Z. * Dr's big screen needs more work. New S one needs some thought) * #define ANG281_RESET_VISUALS (Cth, Gum, T.o.M.E., Z) * #define SAVEFILE_SCREEN (T.o.M.E.) * #define USE_DOUBLE_TILES ("bigtile" patch, V and T.o.M.E. * T requires #define TOME in addition to this) * #define ZANG_AUTO_SAVE (O and Z) * #define HAS_SCORE_MENU (V and T.o.M.E.) * #define ANGBAND_PREFERENCES "_your_variant_name_ Preferences" * #define ANGBAND_CREATOR four letter code for your variant, if any. * or use the default one. * * In [Z], please replace inkey_flag with p_ptr->inkey_flag as well. */ /* Some porting examples */ #ifdef ANGBAND30X # define USE_DOUBLE_TILES # define ALLOW_BIG_SCREEN # define HAS_SCORE_MENU # define NEW_ZVIRT_HOOKS /* I can't ditch this, yet, because there are many variants */ # define USE_TRANSPARENCY #endif /* ANGBAND30X */ #ifdef TOME # define USE_DOUBLE_TILES # define SAVEFILE_SCREEN # define ANG281_RESET_VISUALS # define ALLOW_BIG_SCREEN # define HAS_SCORE_MENU # define ANGBAND_CREATOR 'PrnA' # define ANGBAND_PREFERENCES "T.o.M.E. Preferences" #endif /* TOME */ /* Default creator signature */ # ifndef ANGBAND_CREATOR # define ANGBAND_CREATOR 'A271' # endif /* Default preferences file name */ # ifndef ANGBAND_PREFERENCES # define ANGBAND_PREFERENCES "Angband Preferences" # endif /* * To cope with pref file related problems * * Please note that some variants don't set them to "real" version number * and uses other defines for that purpose. * * This is *very* important for Classic ports, because wrong pref file formats * can crash the system. */ #ifndef PREF_VER_MAJOR # define PREF_VER_MAJOR VERSION_MAJOR #endif #ifndef PREF_VER_MINOR # define PREF_VER_MINOR VERSION_MINOR #endif #ifndef PREF_VER_PATCH # define PREF_VER_PATCH VERSION_PATCH #endif #ifndef PREF_VER_EXTRA # define PREF_VER_EXTRA VERSION_EXTRA #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Use "malloc()" instead of "NewPtr()" */ /* #define USE_MALLOC */ #if defined(powerc) || defined(__powerc) /* * Disable "LITE" version */ # undef ANGBAND_LITE_MAC #endif #ifndef ANGBAND_LITE_MAC /* * Activate some special code */ # define USE_SFL_CODE #endif /* ANGBAND_LITE_MAC */ #ifdef USE_SFL_CODE /* * Include the necessary header files */ #include #include #include #endif #ifdef ANGBAND_LITE_MAC /* * Everything in drawn as white on black */ #else /* ANGBAND_LITE_MAC */ /* * Information about each of the 256 available colors */ static RGBColor color_info[256]; #endif /* ANGBAND_LITE_MAC */ /* * Forward declare */ typedef struct term_data term_data; /* * Extra "term" data */ struct term_data { term *t; Rect r; WindowPtr w; #ifdef ANGBAND_LITE_MAC /* Nothing */ #else /* ANGBAND_LITE_MAC */ short padding; short pixelDepth; GWorldPtr theGWorld; GDHandle theGDH; GDHandle mainSWGDH; #endif /* ANGBAND_LITE_MAC */ Str15 title; s16b oops; s16b keys; s16b last; s16b mapped; s16b rows; s16b cols; s16b font_id; s16b font_size; s16b font_face; s16b font_mono; s16b font_o_x; s16b font_o_y; s16b font_wid; s16b font_hgt; s16b tile_o_x; s16b tile_o_y; s16b tile_wid; s16b tile_hgt; s16b size_wid; s16b size_hgt; s16b size_ow1; s16b size_oh1; s16b size_ow2; s16b size_oh2; }; #ifdef MAC_MPW /* * MPW 68K compiler cannot process ToME's variable.c correctly... * but support for it is here, for your reference. I have tried * this with SC and MrC to compile Vanilla successfully. */ QDGlobals qd; /* * File type assigner - declare them in externs.h as well. * * You still have to call * fsetfileinfo(buf, _fcreator, _ftype); * in fd_make() and my_fopen() to assign creator and type * to a file. */ u32b _ftype; u32b _fcreator; /* * Since MPW's C library doesn't have stat or fstat, you have to * disable CHECK_MODIFICATION_TIME in config.h * * Another source code change required for MPW compilation is * to #define MACINTOSH and #undef __STDC__ * This can be done conveniently in h-system.h * * You may have to cast some pointers to non-offending types e.g. (void *). * This typically occurs when passing a const pointer to a library * function whose prototype doesn't have const in corresponding * parameter. */ #endif /* MAC_MPW */ /* * Forward declare -- see below */ static bool CheckEvents(bool wait); /* * Hack -- location of the main directory */ static short app_vol; static long app_dir; /* * Delay handling of double-clicked savefiles */ Boolean open_when_ready = FALSE; /* * Delay handling of pre-emptive "quit" event */ Boolean quit_when_ready = FALSE; /* * Hack -- game in progress */ static int game_in_progress = 0; /* * Only do "SetPort()" when needed */ static WindowPtr active = NULL; /* * Maximum number of terms */ #define MAX_TERM_DATA 8 /* * An array of term_data's */ static term_data data[MAX_TERM_DATA]; /* * Note when "open"/"new" become valid */ static bool initialized = FALSE; #ifdef ALLOW_NO_SAVE_QUITS /* * CodeWarrior uses Universal Procedure Pointers */ static ModalFilterUPP ynfilterUPP; #endif /* ALLOW_NO_SAVE_QUITS */ #ifdef USE_SFL_CODE /* * Apple Event Hooks */ AEEventHandlerUPP AEH_Start_UPP; AEEventHandlerUPP AEH_Quit_UPP; AEEventHandlerUPP AEH_Print_UPP; AEEventHandlerUPP AEH_Open_UPP; #endif /* * Convert a C string to a pascal string in place * * This function may be defined elsewhere, but since it is so * small, it is not worth finding the proper function name for * all the different platforms. */ static void ctopstr(StringPtr src) { int i; byte len; /* Hack -- pointer */ char *s = (char*)(src); len = strlen(s); /* Hack -- convert the string */ for (i = len; i > 1; i--) s[i] = s[i - 1]; /* Hack -- terminate the string */ s[0] = len; } /* * Convert refnum+vrefnum+fname into a full file name * Store this filename in 'buf' (make sure it is long enough) * Note that 'fname' looks to be a "pascal" string */ static void refnum_to_name(char *buf, long refnum, short vrefnum, char *fname) { DirInfo pb; Str255 name; int err; int i, j; char res[1000]; i = 999; res[i] = 0; i--; for (j = 1; j <= fname[0]; j++) { res[i - fname[0] + j] = fname[j]; } i -= fname[0]; pb.ioCompletion = NULL; pb.ioNamePtr = name; pb.ioVRefNum = vrefnum; pb.ioDrParID = refnum; pb.ioFDirIndex = -1; while (1) { pb.ioDrDirID = pb.ioDrParID; err = PBGetCatInfoSync((CInfoPBPtr) & pb); res[i] = ':'; i--; for (j = 1; j <= name[0]; j++) { res[i - name[0] + j] = name[j]; } i -= name[0]; if (pb.ioDrDirID == fsRtDirID) break; } /* Extract the result */ for (j = 0, i++; res[i]; j++, i++) buf[j] = res[i]; buf[j] = 0; } #if 0 /* * XXX XXX XXX Allow the system to ask us for a filename */ static bool askfor_file(char *buf, int len) { SFReply reply; Str255 dflt; Point topleft; short vrefnum; long drefnum, junk; /* Default file name */ strnfmt((char*)dflt + 1, 255, "%s's description", buf); dflt[0] = strlen((char*)dflt + 1); /* Ask for a file name */ topleft.h = (qd.screenBits.bounds.left + qd.screenBits.bounds.right) / 2 - 344 / 2; topleft.v = (2 * qd.screenBits.bounds.top + qd.screenBits.bounds.bottom) / 3 - 188 / 2; SFPutFile(topleft, "\pSelect a filename:", dflt, NULL, &reply); /* StandardPutFile("\pSelect a filename:", dflt, &reply); */ /* Process */ if (reply.good) { int fc; /* Get info */ GetWDInfo(reply.vRefNum, &vrefnum, &drefnum, &junk); /* Extract the name */ refnum_to_name(buf, drefnum, vrefnum, (char*)reply.fName); /* Success */ return (TRUE); } /* Failure */ return (FALSE); } #endif /* * Center a rectangle inside another rectangle */ static void center_rect(Rect *r, Rect *s) { int centerx = (s->left + s->right) / 2; int centery = (2 * s->top + s->bottom) / 3; int dx = centerx - (r->right - r->left) / 2 - r->left; int dy = centery - (r->bottom - r->top) / 2 - r->top; r->left += dx; r->right += dx; r->top += dy; r->bottom += dy; } /* * Convert a pascal string in place * * This function may be defined elsewhere, but since it is so * small, it is not worth finding the proper function name for * all the different platforms. */ static void ptocstr(StringPtr src) { int i; /* Hack -- pointer */ char *s = (char*)(src); /* Hack -- convert the string */ for (i = s[0]; i; i--, s++) s[0] = s[1]; /* Hack -- terminate the string */ s[0] = '\0'; } #if defined(USE_SFL_CODE) /* * The following three routines (pstrcat, pstrinsert, and PathNameFromDirID) * were taken from the Think Reference section called "Getting a Full Pathname" * (under the File Manager section). We need PathNameFromDirID to get the * full pathname of the opened savefile, making no assumptions about where it * is. * * I had to hack PathNameFromDirID a little for MetroWerks, but it's awfully * nice. */ static void pstrcat(StringPtr dst, StringPtr src) { /* copy string in */ BlockMove(src + 1, dst + *dst + 1, *src); /* adjust length byte */ *dst += *src; } /* * pstrinsert - insert string 'src' at beginning of string 'dst' */ static void pstrinsert(StringPtr dst, StringPtr src) { /* make room for new string */ BlockMove(dst + 1, dst + *src + 1, *dst); /* copy new string in */ BlockMove(src + 1, dst + 1, *src); /* adjust length byte */ *dst += *src; } static void PathNameFromDirID(long dirID, short vRefNum, StringPtr fullPathName) { CInfoPBRec block; Str255 directoryName; OSErr err; fullPathName[0] = '\0'; block.dirInfo.ioDrParID = dirID; block.dirInfo.ioNamePtr = directoryName; while (1) { block.dirInfo.ioVRefNum = vRefNum; block.dirInfo.ioFDirIndex = -1; block.dirInfo.ioDrDirID = block.dirInfo.ioDrParID; err = PBGetCatInfoSync(&block); pstrcat(directoryName, (StringPtr)"\p:"); pstrinsert(fullPathName, directoryName); if (block.dirInfo.ioDrDirID == 2) break; } } #endif /* * Activate a given window, if necessary */ static void activate(WindowPtr w) { /* Activate */ if (active != w) { /* Activate */ if (w) SetPort(w); /* Remember */ active = w; } } /* * Display a warning message */ static void mac_warning(cptr warning) { Str255 text; int len, i; /* Limit of 250 chars */ len = strlen(warning); if (len > 250) len = 250; /* Make a "Pascal" string */ text[0] = len; for (i = 0; i < len; i++) text[i + 1] = warning[i]; /* Prepare the dialog box values */ ParamText(text, "\p", "\p", "\p"); /* Display the Alert, wait for Okay */ Alert(129, 0L); } /*** Some generic functions ***/ #ifdef ANGBAND_LITE_MAC /* * Hack -- activate a color (0 to 255) */ #define term_data_color(TD,A) /* Nothing */ #else /* ANGBAND_LITE_MAC */ /* * Hack -- activate a color (0 to 255) */ static void term_data_color(term_data *td, int a) { /* Activate the color */ if (td->last != a) { /* Activate the color */ RGBForeColor(&color_info[a]); /* Memorize color */ td->last = a; } } #endif /* ANGBAND_LITE_MAC */ /* * Hack -- Apply and Verify the "font" info * * This should usually be followed by "term_data_check_size()" * * XXX XXX To force (re)initialisation of td->tile_wid and td->tile_hgt * you have to reset them to zero before this function is called. * XXX XXX This is automatic when the program starts because the term_data * array is WIPE'd by term_data_hack, but isn't in the other cases, i.e. * font, font style and size changes. */ static void term_data_check_font(term_data *td) { int i; FontInfo info; WindowPtr old = active; /* Activate */ activate(td->w); /* Instantiate font */ TextFont(td->font_id); TextSize(td->font_size); TextFace(td->font_face); /* Extract the font info */ GetFontInfo(&info); /* Assume monospaced */ td->font_mono = TRUE; /* Extract the font sizing values XXX XXX XXX */ td->font_wid = CharWidth('@'); /* info.widMax; */ td->font_hgt = info.ascent + info.descent; td->font_o_x = 0; td->font_o_y = info.ascent; /* Check important characters */ for (i = 33; i < 127; i++) { /* Hack -- notice non-mono-space */ if (td->font_wid != CharWidth(i)) td->font_mono = FALSE; /* Hack -- collect largest width */ if (td->font_wid < CharWidth(i)) td->font_wid = CharWidth(i); } /* Set default offsets */ td->tile_o_x = td->font_o_x; td->tile_o_y = td->font_o_y; /* Set default tile size */ if (td->tile_wid == 0) td->tile_wid = td->font_wid; if (td->tile_hgt == 0) td->tile_hgt = td->font_hgt; /* Re-activate the old window */ activate(old); } /* * Hack -- Apply and Verify the "size" info */ static void term_data_check_size(term_data *td) { /* Minimal window size for the Angband window */ if (td == &data[0]) { #ifdef ALLOW_BIG_SCREEN /* Enforce minimal size */ if (td->cols < 80) td->cols = 80; if (td->rows < 24) td->rows = 24; #else /* Enforce the traditional size */ if (td->cols != 80) td->cols = 80; if (td->rows != 24) td->rows = 24; #endif /* ALLOW_BIG_SCREEN */ } /* Allow small windows for the rest */ else { if (td->cols < 1) td->cols = 1; if (td->rows < 1) td->rows = 1; } /* Enforce maximal sizes */ if (td->cols > 255) td->cols = 255; if (td->rows > 255) td->rows = 255; /* Minimal tile size */ if (td->tile_wid < td->font_wid) td->tile_wid = td->font_wid; if (td->tile_hgt < td->font_hgt) td->tile_hgt = td->font_hgt; /* Default tile offsets */ td->tile_o_x = (td->tile_wid - td->font_wid) / 2; td->tile_o_y = (td->tile_hgt - td->font_hgt) / 2; /* Minimal tile offsets */ if (td->tile_o_x < 0) td->tile_o_x = 0; if (td->tile_o_y < 0) td->tile_o_y = 0; /* Apply font offsets */ td->tile_o_x += td->font_o_x; td->tile_o_y += td->font_o_y; /* Calculate full window size */ td->size_wid = td->cols * td->tile_wid + td->size_ow1 + td->size_ow2; td->size_hgt = td->rows * td->tile_hgt + td->size_oh1 + td->size_oh2; /* Verify the top */ if (td->r.top > qd.screenBits.bounds.bottom - td->size_hgt) { td->r.top = qd.screenBits.bounds.bottom - td->size_hgt; } /* Verify the top */ if (td->r.top < qd.screenBits.bounds.top + 30) { td->r.top = qd.screenBits.bounds.top + 30; } /* Verify the left */ if (td->r.left > qd.screenBits.bounds.right - td->size_wid) { td->r.left = qd.screenBits.bounds.right - td->size_wid; } /* Verify the left */ if (td->r.left < qd.screenBits.bounds.left) { td->r.left = qd.screenBits.bounds.left; } /* Calculate bottom right corner */ td->r.right = td->r.left + td->size_wid; td->r.bottom = td->r.top + td->size_hgt; /* Assume no graphics */ td->t->higher_pict = FALSE; td->t->always_pict = FALSE; #ifdef ANGBAND_LITE_MAC /* No graphics */ #else /* ANGBAND_LITE_MAC */ /* Handle graphics */ if (use_graphics) { /* Use higher_pict whenever possible */ if (td->font_mono) td->t->higher_pict = TRUE; /* Use always_pict only when necessary */ else td->t->always_pict = TRUE; } #endif /* ANGBAND_LITE_MAC */ /* Fake mono-space */ if (!td->font_mono || (td->font_wid != td->tile_wid) || (td->font_hgt != td->tile_hgt)) { /* Handle fake monospace -- this is SLOW */ if (td->t->higher_pict) td->t->higher_pict = FALSE; td->t->always_pict = TRUE; } } /* * Hack -- resize a term_data * * This should normally be followed by "term_data_resize()" */ static void term_data_resize(term_data *td) { /* Actually resize the window */ SizeWindow(td->w, td->size_wid, td->size_hgt, 0); } /* * Hack -- redraw a term_data * * Note that "Term_redraw()" calls "TERM_XTRA_CLEAR" */ static void term_data_redraw(term_data *td) { term *old = Term; /* Activate the term */ Term_activate(td->t); /* Redraw the contents */ Term_redraw(); /* Flush the output */ Term_fresh(); /* Restore the old term */ Term_activate(old); /* No need to redraw */ ValidRect(&td->w->portRect); } #ifdef ANGBAND_LITE_MAC /* No graphics */ #else /* ANGBAND_LITE_MAC */ /* * Graphics support */ /* Set by Term_xtra_mac_react */ static int pictID; /* PICT id of image tiles */ static int grafWidth; /* Width of a tile in pixels */ static int grafHeight; /* Height of a tile in pixels */ /* Calculated by PICT loading code */ static int pictCols; /* Number of columns in tiles */ static int pictRows; /* Number of rows in tiles */ /* Available graphics modes */ #define GRAF_MODE_NONE 0 /* plain ASCII */ #define GRAF_MODE_8X8 1 /* 8x8 tiles, no transparency effect */ #define GRAF_MODE_16X16 2 /* 16x16 tiles, transparency effect */ /* This doesn't work... */ #define GRAF_MODE_32X32 3 /* 32x32 tiles, transparency effect */ static int graf_mode = GRAF_MODE_NONE; /* current graphics mode */ static int graf_mode_req = GRAF_MODE_NONE; /* requested graphics mode */ #define TR_NONE 0 /* No transparency effect */ #define TR_OVER 1 /* Overwriting with transparent black pixels */ static int transparency_mode = TR_NONE; /* * Forward Declare */ typedef struct FrameRec FrameRec; /* * Frame * * - GWorld for the frame image * - Handle to pix map (saved for unlocking/locking) * - Pointer to color pix map (valid only while locked) */ struct FrameRec { GWorldPtr framePort; PixMapHandle framePixHndl; PixMapPtr framePix; }; /* * The global picture data */ static FrameRec *frameP = NULL; /* * Lock a frame */ static void BenSWLockFrame(FrameRec *srcFrameP) { PixMapHandle pixMapH; pixMapH = GetGWorldPixMap(srcFrameP->framePort); (void)LockPixels(pixMapH); HLockHi((Handle)pixMapH); srcFrameP->framePixHndl = pixMapH; srcFrameP->framePix = (PixMapPtr)StripAddress(*(Handle)pixMapH); } /* * Unlock a frame */ static void BenSWUnlockFrame(FrameRec *srcFrameP) { if (srcFrameP->framePort != NULL) { HUnlock((Handle)srcFrameP->framePixHndl); UnlockPixels(srcFrameP->framePixHndl); } srcFrameP->framePix = NULL; } static OSErr BenSWCreateGWorldFromPict( GWorldPtr *pictGWorld, PicHandle pictH) { OSErr err; GWorldPtr saveGWorld; GDHandle saveGDevice; GWorldPtr tempGWorld; Rect pictRect; short depth; GDHandle theGDH; tempGWorld = NULL; /* Reset */ *pictGWorld = NULL; /* Get depth */ depth = data[0].pixelDepth; /* Get GDH */ theGDH = data[0].theGDH; /* Obtain size rectangle */ pictRect = (**pictH).picFrame; OffsetRect(&pictRect, -pictRect.left, -pictRect.top); /* Calculate and set numbers of rows and columns */ pictRows = pictRect.bottom / grafHeight; pictCols = pictRect.right / grafWidth; /* Create a GWorld */ err = NewGWorld(&tempGWorld, depth, &pictRect, nil, theGDH, noNewDevice); /* Error */ if (err != noErr) return (err); /* Save pointer */ *pictGWorld = tempGWorld; /* Save GWorld */ GetGWorld(&saveGWorld, &saveGDevice); /* Activate */ SetGWorld(tempGWorld, nil); /* Dump the pict into the GWorld */ (void)LockPixels(GetGWorldPixMap(tempGWorld)); EraseRect(&pictRect); DrawPicture(pictH, &pictRect); UnlockPixels(GetGWorldPixMap(tempGWorld)); /* Restore GWorld */ SetGWorld(saveGWorld, saveGDevice); /* Success */ return (0); } /* * Init the global "frameP" */ static OSErr globe_init(void) { OSErr err; GWorldPtr tempPictGWorldP; PicHandle newPictH; /* Use window XXX XXX XXX */ SetPort(data[0].w); /* Get the pict resource */ newPictH = GetPicture(pictID); /* Error */ if (newPictH == NULL) return ( -1); /* Create GWorld */ err = BenSWCreateGWorldFromPict(&tempPictGWorldP, newPictH); /* Release resource */ ReleaseResource((Handle)newPictH); /* Error */ if (err != noErr) return (err); /* Create the frame */ frameP = (FrameRec*)NewPtrClear((Size)sizeof(FrameRec)); /* Error */ if (frameP == NULL) return ( -1); /* Save GWorld */ frameP->framePort = tempPictGWorldP; /* Lock it */ BenSWLockFrame(frameP); /* Success */ return (noErr); } /* * Nuke the global "frameP" */ static errr globe_nuke(void) { /* Dispose */ if (frameP) { /* Unlock */ BenSWUnlockFrame(frameP); /* Dispose of the GWorld */ DisposeGWorld(frameP->framePort); /* Dispose of the memory */ DisposePtr((Ptr)frameP); /* Forget */ frameP = NULL; } /* Flush events */ FlushEvents(everyEvent, 0); /* Success */ return (0); } #endif /* ANGBAND_LITE_MAC */ #ifdef USE_ASYNC_SOUND /* * Asynchronous sound player - completely revised (beta) */ /* * Number of channels in the channel pool */ #define MAX_CHANNELS 4 /* * A pool of sound channels */ static SndChannelPtr channels[MAX_CHANNELS]; /* * Status of the channel pool */ static Boolean channel_initialised = FALSE; /* * Data handles containing sound samples */ static SndListHandle samples[SOUND_MAX]; /* * Reference counts of sound samples */ static SInt16 sample_refs[SOUND_MAX]; #define SOUND_VOLUME_MIN 0 /* Default minimum sound volume */ #define SOUND_VOLUME_MAX 255 /* Default maximum sound volume */ #define VOLUME_MIN 0 /* Minimum sound volume in % */ #define VOLUME_MAX 100 /* Maximum sound volume in % */ #define VOLUME_INC 5 /* Increment sound volume in % */ /* I'm just too lazy to write a panel for this XXX XXX */ static int sound_volume = SOUND_VOLUME_MAX; /* * Return a handle of 'snd ' resource given Angband sound event number, * or NULL if it isn't found. * * Globals referenced: angband_sound_name[] (variable.c) */ static SndListHandle find_sound(int num) { Str255 sound; /* Get the proper sound name */ strnfmt((char*)sound + 1, 255, "%.16s.wav", angband_sound_name[num]); sound[0] = strlen((char*)sound + 1); /* Obtain resource XXX XXX XXX */ return ((SndListHandle)GetNamedResource('snd ', sound)); } /* * Clean up sound support - to be called when the game exits. * * Globals referenced: channels[], samples[], sample_refs[]. */ static void cleanup_sound(void) { int i; /* No need to clean it up */ if (!channel_initialised) return; /* Dispose channels */ for (i = 0; i < MAX_CHANNELS; i++) { /* Drain sound commands and free the channel */ SndDisposeChannel(channels[i], TRUE); } /* Free sound data */ for (i = 1; i < SOUND_MAX; i++) { /* Still locked */ if ((sample_refs[i] > 0) && (samples[i] != NULL)) { /* Unlock it */ HUnlock((Handle)samples[i]); } /* Release it */ if (samples[i]) ReleaseResource((Handle)samples[i]); } } /* * Play sound effects asynchronously -- pelpel * * I don't believe those who first started using the previous implementations * imagined this is *much* more complicated as it may seem. Anyway, * introduced round-robin scheduling of channels and made it much more * paranoid about HLock/HUnlock. * * XXX XXX de-refcounting, HUnlock and ReleaseResource should be done * using channel's callback procedures, which set global flags, and * a procedure hooked into CheckEvents does housekeeping. On the other * hand, this lazy reclaiming strategy keeps things simple (no interrupt * time code) and provides a sort of cache for sound data. * * Globals referenced: channel_initialised, channels[], samples[], * sample_refs[]. * Globals updated: ditto. */ static void play_sound(int num, int vol) { OSErr err; int i; int prev_num; SndListHandle h; SndChannelPtr chan; SCStatus status; static int next_chan; static SInt16 channel_occupants[MAX_CHANNELS]; static SndCommand volume_cmd, quiet_cmd; /* Initialise sound channels */ if (!channel_initialised) { for (i = 0; i < MAX_CHANNELS; i++) { /* Paranoia - Clear occupant table */ /* channel_occupants[i] = 0; */ /* Create sound channel for all sounds to play from */ err = SndNewChannel(&channels[i], sampledSynth, initMono, 0L); /* Error */ if (err != noErr) { /* Free channels */ while (--i >= 0) { SndDisposeChannel(channels[i], TRUE); } /* Notify error */ plog("Cannot initialise sound channels!"); /* Cancel request */ use_sound = arg_sound = FALSE; /* Failure */ return; } } /* First channel to use */ next_chan = 0; /* Prepare volume command */ volume_cmd.cmd = volumeCmd; volume_cmd.param1 = 0; volume_cmd.param2 = 0; /* Prepare quiet command */ quiet_cmd.cmd = quietCmd; quiet_cmd.param1 = 0; quiet_cmd.param2 = 0; /* Initialisation complete */ channel_initialised = TRUE; } /* Paranoia */ if ((num <= 0) || (num >= SOUND_MAX)) return; /* Prepare volume command */ volume_cmd.param2 = (SInt16)((vol << 4) | vol); /* Channel to use (round robin) */ chan = channels[next_chan]; /* See if the resource is already in use */ if (sample_refs[num] > 0) { /* Resource in use */ h = samples[num]; /* Increase the refcount */ sample_refs[num]++; } /* Sound is not currently in use */ else { /* Get handle for the sound */ h = find_sound(num); /* Sample not available */ if (h == NULL) return; /* Load resource */ LoadResource((Handle)h); /* Lock the handle */ HLock((Handle)h); /* Remember it */ samples[num] = h; /* Initialise refcount */ sample_refs[num] = 1; } /* Poll the channel */ err = SndChannelStatus(chan, sizeof(SCStatus), &status); /* It isn't available */ if ((err != noErr) || status.scChannelBusy) { /* Shut it down */ SndDoImmediate(chan, &quiet_cmd); } /* Previously played sound on this channel */ prev_num = channel_occupants[next_chan]; /* Process previously played sound */ if (prev_num != 0) { /* Decrease refcount */ sample_refs[prev_num]--; /* We can free it now */ if (sample_refs[prev_num] <= 0) { /* Unlock */ HUnlock((Handle)samples[prev_num]); /* Release */ ReleaseResource((Handle)samples[prev_num]); /* Forget handle */ samples[prev_num] = NULL; /* Paranoia */ sample_refs[prev_num] = 0; } } /* Remember this sound as the current occupant of the channel */ channel_occupants[next_chan] = num; /* Set up volume for channel */ SndDoImmediate(chan, &volume_cmd); /* Play new sound asynchronously */ SndPlay(chan, h, TRUE); /* Schedule next channel (round robin) */ next_chan++; if (next_chan >= MAX_CHANNELS) next_chan = 0; } #endif /* USE_ASYNC_SOUND */ /*** Support for the "z-term.c" package ***/ /* * Initialize a new Term * * Note also the "window type" called "noGrowDocProc", which might be more * appropriate for the main "screen" window. * * Note the use of "srcCopy" mode for optimized screen writes. */ static void Term_init_mac(term *t) { term_data *td = (term_data*)(t->data); static RGBColor black = {0x0000, 0x0000, 0x0000}; static RGBColor white = {0xFFFF, 0xFFFF, 0xFFFF}; #ifdef ANGBAND_LITE_MAC /* Make the window */ td->w = NewWindow(0, &td->r, td->title, 0, noGrowDocProc, (WindowPtr) - 1, 1, 0L); #else /* ANGBAND_LITE_MAC */ /* Make the window */ td->w = NewCWindow(0, &td->r, td->title, 0, documentProc, (WindowPtr) - 1, 1, 0L); #endif /* ANGBAND_LITE_MAC */ /* Activate the window */ activate(td->w); /* Erase behind words */ TextMode(srcCopy); /* Apply and Verify */ term_data_check_font(td); term_data_check_size(td); /* Resize the window */ term_data_resize(td); #ifdef ANGBAND_LITE_MAC /* Prepare the colors (base colors) */ BackColor(blackColor); ForeColor(whiteColor); #else /* ANGBAND_LITE_MAC */ /* Prepare the colors (real colors) */ RGBBackColor(&black); RGBForeColor(&white); /* Block */ { Rect tempRect; Rect globalRect; GDHandle mainGDH; GDHandle currentGDH; GWorldPtr windowGWorld; PixMapHandle basePixMap; /* Obtain the rect */ tempRect = td->w->portRect; /* Obtain the global rect */ globalRect = tempRect; LocalToGlobal((Point*)&globalRect.top); LocalToGlobal((Point*)&globalRect.bottom); /* Obtain the proper GDH */ mainGDH = GetMaxDevice(&globalRect); /* Extract GWorld and GDH */ GetGWorld(&windowGWorld, ¤tGDH); /* Obtain base pixmap */ basePixMap = (**mainGDH).gdPMap; /* Save pixel depth */ td->pixelDepth = (**basePixMap).pixelSize; /* Save Window GWorld */ td->theGWorld = windowGWorld; /* Save Window GDH */ td->theGDH = currentGDH; /* Save main GDH */ td->mainSWGDH = mainGDH; } #endif /* ANGBAND_LITE_MAC */ /* Clip to the window */ ClipRect(&td->w->portRect); /* Erase the window */ EraseRect(&td->w->portRect); /* Invalidate the window */ InvalRect(&td->w->portRect); /* Display the window if needed */ if (td->mapped) ShowWindow(td->w); /* Hack -- set "mapped" flag */ t->mapped_flag = td->mapped; /* Forget color */ td->last = -1; } /* * Nuke an old Term */ static void Term_nuke_mac(term *t) { #pragma unused (t) /* XXX */ } /* * Unused */ static errr Term_user_mac(int n) { #pragma unused (n) /* Success */ return (0); } /* * React to changes */ static errr Term_xtra_mac_react(void) { term_data *td = (term_data*)(Term->data); int i; #ifdef ANGBAND_LITE_MAC /* Nothing */ #else /* ANGBAND_LITE_MAC */ /* Reset color */ td->last = -1; /* Update colors */ for (i = 0; i < 256; i++) { u16b rv, gv, bv; /* Extract the R,G,B data */ rv = angband_color_table[i][1]; gv = angband_color_table[i][2]; bv = angband_color_table[i][3]; /* Save the actual color */ color_info[i].red = (rv | (rv << 8)); color_info[i].green = (gv | (gv << 8)); color_info[i].blue = (bv | (bv << 8)); } /* Handle sound */ if (use_sound != arg_sound) { /* Apply request */ use_sound = arg_sound; } /* Handle graphics */ if (graf_mode_req != graf_mode) { /* dispose old GWorld's if present */ globe_nuke(); /* Setup parameters according to request */ switch (graf_mode_req) { /* ASCII - no graphics whatsoever */ case GRAF_MODE_NONE: { #ifndef ZANGBAND use_graphics = arg_graphics = FALSE; #else use_graphics = arg_graphics = GRAPHICS_NONE; #endif /* !ZANGBAND */ transparency_mode = TR_NONE; break; } /* * 8x8 tiles (PICT id 1001) * no transparency effect * "old" graphics definitions */ case GRAF_MODE_8X8: { #ifndef ZANGBAND use_graphics = arg_graphics = TRUE; ANGBAND_GRAF = "old"; #else use_graphics = arg_graphics = GRAPHICS_ORIGINAL; #endif /* !ZANGBAND */ pictID = 1001; transparency_mode = TR_NONE; grafWidth = grafHeight = 8; break; } /* * 16x16 tiles (images: PICT id 1002) * with transparency effect * "new" graphics definitions */ case GRAF_MODE_16X16: { #ifndef ZANGBAND use_graphics = arg_graphics = TRUE; ANGBAND_GRAF = "new"; #else use_graphics = arg_graphics = GRAPHICS_ADAM_BOLT; #endif /* !ZANGBAND */ pictID = 1002; transparency_mode = TR_OVER; grafWidth = grafHeight = 16; break; } #if 0 /* Good Lord! It doesn't work... Is it too large? */ /* * 32x32 tiles (images: PICT id 1004) * no transparency effect * "david" graphics definitions */ case GRAF_MODE_32X32: { use_graphics = arg_graphics = TRUE; ANGBAND_GRAF = "david"; pictID = 1004; transparency_mode = TR_OVER; grafWidth = grafHeight = 32; break; } #endif /* sigh */ } /* load tiles and setup GWorlds if tiles are requested */ if ((graf_mode_req != GRAF_MODE_NONE) && (globe_init() != 0)) { /* Oops */ plog("Cannot initialize graphics!"); /* reject request */ graf_mode_req = GRAF_MODE_NONE; /* reset graphics flags */ use_graphics = arg_graphics = FALSE; /* reset transparency mode */ transparency_mode = TR_NONE; } /* update current graphics mode */ graf_mode = graf_mode_req; /* Apply and Verify */ term_data_check_size(td); /* Resize the window */ term_data_resize(td); /* Reset visuals */ #ifndef ANG281_RESET_VISUALS reset_visuals(TRUE); #else reset_visuals(); #endif /* !ANG281_RESET_VISUALS */ } #endif /* ANGBAND_LITE_MAC */ /* Success */ return (0); } /* * Do a "special thing" */ static errr Term_xtra_mac(int n, int v) { term_data *td = (term_data*)(Term->data); Rect r; /* Analyze */ switch (n) { /* Make a noise */ case TERM_XTRA_NOISE: { /* Make a noise */ SysBeep(1); /* Success */ return (0); } #ifdef ANGBAND_LITE_MAC /* Nothing */ #else /* ANGBAND_LITE_MAC */ /* Make a sound */ case TERM_XTRA_SOUND: { #ifndef USE_ASYNC_SOUND /* * This may not be your choice, but much safer and much less * resource hungry. Existing implementations can quite easily * crash, by starting asynchronous playing and immediately * unlocking and releasing the sound data just started playing... * -- pelpel */ Handle handle; Str255 sound; /* Get the proper sound name */ strnfmt((char*)sound + 1, 255, "%.16s.wav", angband_sound_name[v]); sound[0] = strlen((char*)sound + 1); /* Obtain resource XXX XXX XXX */ handle = GetNamedResource('snd ', sound); /* Oops -- it is a failure, but we return 0 anyway */ if (handle == NULL) return (0); /* Load and Lock */ LoadResource(handle); HLock(handle); /* Play sound (wait for completion) */ SndPlay(nil, (SndListHandle)handle, false); /* Unlock and release */ HUnlock(handle); ReleaseResource(handle); #else /* !USE_ASYNC_SOUND */ /* If you really want async player, please try this one */ play_sound(v, sound_volume); #endif /* !USE_ASYNC_SOUND */ /* Success */ return (0); } #endif /* ANGBAND_LITE_MAC */ /* Process random events */ case TERM_XTRA_BORED: { /* Process an event */ (void)CheckEvents(FALSE); /* Success */ return (0); } /* Process pending events */ case TERM_XTRA_EVENT: { /* Process an event */ (void)CheckEvents(v); /* Success */ return (0); } /* Flush all pending events (if any) */ case TERM_XTRA_FLUSH: { /* Hack -- flush all events */ while (CheckEvents(TRUE)) /* loop */; /* Success */ return (0); } /* Hack -- Change the "soft level" */ case TERM_XTRA_LEVEL: { /* Activate if requested */ if (v) activate(td->w); /* Success */ return (0); } /* Clear the screen */ case TERM_XTRA_CLEAR: { /* No clipping XXX XXX XXX */ ClipRect(&td->w->portRect); /* Erase the window */ EraseRect(&td->w->portRect); /* Set the color */ term_data_color(td, TERM_WHITE); /* Frame the window in white */ MoveTo(0, 0); LineTo(0, td->size_hgt - 1); LineTo(td->size_wid - 1, td->size_hgt - 1); LineTo(td->size_wid - 1, 0); /* Clip to the new size */ r.left = td->w->portRect.left + td->size_ow1; r.top = td->w->portRect.top + td->size_oh1; r.right = td->w->portRect.right - td->size_ow2; r.bottom = td->w->portRect.bottom - td->size_oh2; ClipRect(&r); /* Success */ return (0); } /* React to changes */ case TERM_XTRA_REACT: { /* React to changes */ return (Term_xtra_mac_react()); } /* Delay (milliseconds) */ case TERM_XTRA_DELAY: { /* If needed */ if (v > 0) { long m = TickCount() + (v * 60L) / 1000; /* Wait for it */ while (TickCount() < m) /* loop */; } /* Success */ return (0); } /* Rename main window */ case TERM_XTRA_RENAME_MAIN_WIN: { char *s = strdup(angband_term_name[0]); ctopstr((StringPtr)s); SetWTitle(data[0].w, (StringPtr)s); free(s); return (0); } } /* Oops */ return (1); } /* * Low level graphics (Assumes valid input). * Draw a "cursor" at (x,y), using a "yellow box". * We are allowed to use "Term_what()" to determine * the current screen contents (for inverting, etc). */ static errr Term_curs_mac(int x, int y) { Rect r; term_data *td = (term_data*)(Term->data); /* Set the color */ term_data_color(td, TERM_YELLOW); /* Frame the grid */ r.left = x * td->tile_wid + td->size_ow1; r.right = r.left + td->tile_wid; r.top = y * td->tile_hgt + td->size_oh1; r.bottom = r.top + td->tile_hgt; #ifdef USE_DOUBLE_TILES /* Mogami's double width tile patch */ if (use_bigtile && (x + 1 < Term->wid) && (Term->old->a[y][x + 1] == 255)) { r.right += td->tile_wid; } #endif /* USE_DOUBLE_TILES */ FrameRect(&r); /* Success */ return (0); } /* * Low level graphics (Assumes valid input) * * Erase "n" characters starting at (x,y) */ static errr Term_wipe_mac(int x, int y, int n) { Rect r; term_data *td = (term_data*)(Term->data); /* Erase the block of characters */ r.left = x * td->tile_wid + td->size_ow1; r.right = r.left + n * td->tile_wid; r.top = y * td->tile_hgt + td->size_oh1; r.bottom = r.top + td->tile_hgt; EraseRect(&r); /* Success */ return (0); } /* * Low level graphics. Assumes valid input. * * Draw several ("n") chars, with an attr, at a given location. */ static errr Term_text_mac(int x, int y, int n, byte a, const char *cp) { int xp, yp; term_data *td = (term_data*)(Term->data); /* Set the color */ term_data_color(td, a); /* Starting pixel */ xp = x * td->tile_wid + td->tile_o_x + td->size_ow1; yp = y * td->tile_hgt + td->tile_o_y + td->size_oh1; /* Move to the correct location */ MoveTo(xp, yp); /* Draw the character */ if (n == 1) DrawChar(*cp); /* Draw the string */ else DrawText(cp, 0, n); /* Success */ return (0); } /* * Low level graphics (Assumes valid input) * * Erase "n" characters starting at (x,y) */ #ifdef USE_TRANSPARENCY # ifdef USE_EGO_GRAPHICS static errr Term_pict_mac(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp, const byte *eap, const char *ecp) # else /* USE_EGO_GRAPHICS */ static errr Term_pict_mac(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp) # endif /* USE_EGO_GRAPHICS */ #else /* USE_TRANSPARENCY */ static errr Term_pict_mac(int x, int y, int n, const byte *ap, const char *cp) #endif /* USE_TRANSPARENCY */ { int i; Rect dst_r; static RGBColor black = {0x0000, 0x0000, 0x0000}; static RGBColor white = {0xFFFF, 0xFFFF, 0xFFFF}; term_data *td = (term_data*)(Term->data); /* Destination rectangle */ dst_r.left = x * td->tile_wid + td->size_ow1; #ifndef USE_DOUBLE_TILES dst_r.right = dst_r.left + td->tile_wid; #endif /* !USE_DOUBLE_TILES */ dst_r.top = y * td->tile_hgt + td->size_oh1; dst_r.bottom = dst_r.top + td->tile_hgt; /* Scan the input */ for (i = 0; i < n; i++) { byte a = *ap++; char c = *cp++; #ifdef USE_TRANSPARENCY byte ta = *tap++; char tc = *tcp++; # ifdef USE_EGO_GRAPHICS byte ea = *eap++; char ec = *ecp++; bool has_overlay = (ea && ec); # endif /* USE_EGO_GRAPHICS */ #endif /* USE_TRANSPARENCY */ #ifdef USE_DOUBLE_TILES /* Second byte of bigtile */ if (use_bigtile && (a == 255)) { /* Advance */ dst_r.left += td->tile_wid; /* Ignore it */ continue; } /* Prepare right side of rectangle */ dst_r.right = dst_r.left + td->tile_wid; #endif /* USE_DOUBLE_TILES */ #ifdef ANGBAND_LITE_MAC /* Nothing */ #else /* ANGBAND_LITE_MAC */ /* Graphics -- if Available and Needed */ if (use_graphics && ((byte)a & 0x80) && ((byte)c & 0x80)) { #ifdef USE_TRANSPARENCY int t_col, t_row; Rect terrain_r; # ifdef USE_EGO_GRAPHICS int e_col, e_row; Rect ego_r; # endif /* USE_EGO_GRAPHICS */ #endif /* USE_TRANSPARENCY */ int col, row; Rect src_r; /* Row and Col */ row = ((byte)a & 0x7F) % pictRows; col = ((byte)c & 0x7F) % pictCols; /* Source rectangle */ src_r.left = col * grafWidth; src_r.top = row * grafHeight; src_r.right = src_r.left + grafWidth; src_r.bottom = src_r.top + grafHeight; #ifdef USE_TRANSPARENCY /* Terrain Row and Col */ t_row = ((byte)ta & 0x7F) % pictRows; t_col = ((byte)tc & 0x7F) % pictCols; /* Terrain Source rectangle */ terrain_r.left = t_col * grafWidth; terrain_r.top = t_row * grafHeight; terrain_r.right = terrain_r.left + grafWidth; terrain_r.bottom = terrain_r.top + grafHeight; # ifdef USE_EGO_GRAPHICS /* If and only if there's overlay */ if (has_overlay) { /* Overlay Row and Col */ e_row = ((byte)ea & 0x7F) % pictRows; e_col = ((byte)ec & 0x7F) % pictCols; /* Overlay Source rectangle */ ego_r.left = e_col * grafWidth; ego_r.top = e_row * grafHeight; ego_r.right = ego_r.left + grafWidth; ego_r.bottom = ego_r.top + grafHeight; } # endif /* USE_EGO_GRAPHICS */ #endif /* USE_TRANSPARENCY */ /* Hardwire CopyBits */ RGBBackColor(&white); RGBForeColor(&black); #ifdef USE_DOUBLE_TILES /* Double width tiles */ if (use_bigtile) dst_r.right += td->tile_wid; #endif /* USE_DOUBLE_TILES */ #ifdef USE_TRANSPARENCY /* Transparency effect */ switch (transparency_mode) { /* No transparency effect */ case TR_NONE: default: { /* Draw the picture */ CopyBits((BitMap*)frameP->framePix, &(td->w->portBits), &src_r, &dst_r, srcCopy, NULL); break; } /* Overwriting with transparent black pixels */ case TR_OVER: { /* Draw the terrain */ CopyBits((BitMap*)frameP->framePix, &(td->w->portBits), &terrain_r, &dst_r, srcCopy, NULL); /* Make black pixels transparent */ RGBBackColor(&black); /* Draw mon/obj if there's one */ if ((row != t_row) || (col != t_col)) CopyBits((BitMap*)frameP->framePix, &(td->w->portBits), &src_r, &dst_r, transparent, NULL); # ifdef USE_EGO_GRAPHICS /* Draw overlay if there's one */ if (has_overlay) CopyBits((BitMap*)frameP->framePix, &(td->w->portBits), &ego_r, &dst_r, transparent, NULL); # endif /* USE_EGO_GRAPHICS */ break; } } #else /* USE_TRANSPARENCY */ /* Draw the picture */ CopyBits((BitMap*)frameP->framePix, &(td->w->portBits), &src_r, &dst_r, srcCopy, NULL); #endif /* USE_TRANSPARENCY */ /* Restore colors */ RGBBackColor(&black); RGBForeColor(&white); /* Forget color */ td->last = -1; } #endif /* ANGBAND_LITE_MAC */ /* Normal */ else { int xp, yp; /* Erase */ EraseRect(&dst_r); /* Set the color */ term_data_color(td, a); /* Starting pixel */ xp = dst_r.left + td->tile_o_x; yp = dst_r.top + td->tile_o_y; /* Move to the correct location */ MoveTo(xp, yp); /* Draw the character */ DrawChar(c); } /* Advance */ dst_r.left += td->tile_wid; #ifndef USE_DOUBLE_TILES dst_r.right += td->tile_wid; #endif /* USE_DOUBLE_TILES */ } /* Success */ return (0); } /* * Create and initialize window number "i" */ static void term_data_link(int i) { term *old = Term; term_data *td = &data[i]; /* Only once */ if (td->t) return; /* Require mapped */ if (!td->mapped) return; /* Allocate */ MAKE(td->t, term); /* Initialize the term */ term_init(td->t, td->cols, td->rows, td->keys); /* Use a "software" cursor */ td->t->soft_cursor = TRUE; /* Erase with "white space" */ td->t->attr_blank = TERM_WHITE; td->t->char_blank = ' '; /* Prepare the init/nuke hooks */ td->t->init_hook = Term_init_mac; td->t->nuke_hook = Term_nuke_mac; /* Prepare the function hooks */ td->t->user_hook = Term_user_mac; td->t->xtra_hook = Term_xtra_mac; td->t->wipe_hook = Term_wipe_mac; td->t->curs_hook = Term_curs_mac; td->t->text_hook = Term_text_mac; td->t->pict_hook = Term_pict_mac; /* Link the local structure */ td->t->data = (void *)(td); /* Activate it */ Term_activate(td->t); /* Global pointer */ angband_term[i] = td->t; /* Activate old */ Term_activate(old); } /* * Set the "current working directory" (also known as the "default" * volume/directory) to the location of the current application. * * Code by: Maarten Hazewinkel (mmhazewi@cs.ruu.nl) * * This function does not appear to work correctly with System 6. */ static void SetupAppDir(void) { FCBPBRec fcbBlock; OSErr err = noErr; char errString[100]; /* Get the location of the Angband executable */ fcbBlock.ioCompletion = NULL; fcbBlock.ioNamePtr = NULL; fcbBlock.ioVRefNum = 0; fcbBlock.ioRefNum = CurResFile(); fcbBlock.ioFCBIndx = 0; err = PBGetFCBInfoSync(&fcbBlock); if (err != noErr) { strnfmt(errString, 100, "Fatal PBGetFCBInfo Error #%d.\r Exiting.", err); mac_warning(errString); ExitToShell(); } /* Extract the Vol and Dir */ app_vol = fcbBlock.ioFCBVRefNum; app_dir = fcbBlock.ioFCBParID; /* Set the current working directory to that location */ err = HSetVol(NULL, app_vol, app_dir); if (err != noErr) { strnfmt(errString, 100, "Fatal HSetVol Error #%d.\r Exiting.", err); mac_warning(errString); ExitToShell(); } } /* * Global "preference" file pointer */ static FILE *fff; /* * Read a "short" from the file */ static int getshort(void) { int x = 0; char buf[256]; if (0 == my_fgets(fff, buf, 256)) x = atoi(buf); return (x); } /* * Dump a "short" to the file */ static void putshort(int x) { fprintf(fff, "%d\n", x); } /* * Write the "preference" data to the current "file" */ static void save_prefs(void) { int i; term_data *td; /*** The current version ***/ putshort(PREF_VER_MAJOR); putshort(PREF_VER_MINOR); putshort(PREF_VER_PATCH); putshort(PREF_VER_EXTRA); /* Gfx settings */ putshort(arg_sound); putshort(graf_mode); #ifdef USE_DOUBLE_TILES putshort(use_bigtile); #endif /* USE_DOUBLE_TILES */ /* Dump */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Access */ td = &data[i]; putshort(td->mapped); putshort(td->font_id); putshort(td->font_size); putshort(td->font_face); putshort(td->tile_wid); putshort(td->tile_hgt); putshort(td->cols); putshort(td->rows); putshort(td->r.left); putshort(td->r.top); } } /* * Load the preferences from the current "file" * * XXX XXX XXX Being able to undefine various windows is * slightly bizarre, and may cause problems. */ static void load_prefs(void) { int i; int old_major, old_minor, old_patch, old_extra; term_data *td; /*** Version information ***/ /* Preferences version */ old_major = getshort(); old_minor = getshort(); old_patch = getshort(); old_extra = getshort(); /* Hack -- Verify or ignore */ if ((old_major != PREF_VER_MAJOR) || (old_minor != PREF_VER_MINOR) || (old_patch != PREF_VER_PATCH) || (old_extra != PREF_VER_EXTRA)) { /* Message */ mac_warning("Ignoring old preferences."); /* Ignore */ return; } /* Gfx mode */ arg_sound = getshort(); graf_mode_req = getshort(); #ifdef USE_DOUBLE_TILES use_bigtile = getshort(); #endif /* USE_DOUBLE_TILES */ /* Windows */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Access */ td = &data[i]; td->mapped = getshort(); td->font_id = getshort(); td->font_size = getshort(); td->font_face = getshort(); td->tile_wid = getshort(); td->tile_hgt = getshort(); td->cols = getshort(); td->rows = getshort(); td->r.left = getshort(); td->r.top = getshort(); /* Done */ if (feof(fff)) break; } } /* * Hack -- default data for a window */ static void term_data_hack(term_data *td) { short fid; /* Default to Monaco font */ GetFNum("\pmonaco", &fid); /* Wipe it */ WIPE(td, term_data); /* No color */ td->last = -1; /* Default borders */ td->size_ow1 = 2; td->size_ow2 = 2; td->size_oh2 = 2; /* Start hidden */ td->mapped = FALSE; /* Default font */ td->font_id = fid; /* Default font size */ td->font_size = 12; /* Default font face */ td->font_face = 0; /* Default size */ td->rows = 24; td->cols = 80; /* Default position */ td->r.left = 10; td->r.top = 40; /* Minimal keys */ td->keys = 16; } /* * Read the preference file, Create the windows. * * We attempt to use "FindFolder()" to track down the preference file, * but if this fails, for any reason, we will try the "SysEnvirons()" * method, which may work better with System 6. */ static void init_windows(void) { int i, b = 0; term_data *td; bool oops; /*** Default values ***/ /* Initialize (backwards) */ for (i = MAX_TERM_DATA; i-- > 0; ) { int n; cptr s; /* Obtain */ td = &data[i]; /* Defaults */ term_data_hack(td); /* Obtain title */ s = angband_term_name[i]; /* Get length */ n = strlen(s); /* Maximal length */ if (n > 15) n = 15; /* Copy the title */ strncpy((char*)(td->title) + 1, s, n); /* Save the length */ td->title[0] = n; /* Tile the windows */ td->r.left += (b * 30); td->r.top += (b * 30); /* Tile */ b++; } /*** Load preferences ***/ /* Assume failure */ oops = TRUE; /* Assume failure */ fff = NULL; #ifdef USE_SFL_CODE /* Block */ if (TRUE) { OSErr err; short vref; long dirID; char foo[128]; /* Find the folder */ err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &vref, &dirID); /* Success */ if (!err) { /* Extract a path name */ PathNameFromDirID(dirID, vref, (StringPtr)foo); /* Convert the string */ ptocstr((StringPtr)foo); /* Append the preference file name */ strcat(foo, ANGBAND_PREFERENCES); /* Open the preference file */ fff = fopen(foo, "r"); /* Success */ oops = FALSE; } } #endif /* USE_SFL_CODE */ #if 0 /* SysEnvirons? SetVol? I'd say no */ /* Oops */ if (oops) { SysEnvRec env; short savev; long saved; /* Save */ HGetVol(0, &savev, &saved); /* Go to the "system" folder */ SysEnvirons(curSysEnvVers, &env); SetVol(0, env.sysVRefNum); /* Open the file */ fff = fopen(":Preferences:" ANGBAND_PREFERENCES, "r"); if (!fff) fff = fopen(":" ANGBAND_PREFERENCES, "r"); /* Restore */ HSetVol(0, savev, saved); } #endif /* relic */ /* Load preferences */ if (fff) { /* Load a real preference file */ load_prefs(); /* Close the file */ my_fclose(fff); } /*** Instantiate ***/ /* Main window */ td = &data[0]; /* Many keys */ td->keys = 1024; /* Start visible */ td->mapped = TRUE; /* Link (backwards, for stacking order) */ for (i = MAX_TERM_DATA; i-- > 0; ) { term_data_link(i); } /* Main window */ td = &data[0]; /* Main window */ Term_activate(td->t); } /* * Exit the program */ static void save_pref_file(void) { bool oops; /* Assume failure */ oops = TRUE; /* Assume failure */ fff = NULL; /* Text file */ _ftype = 'TEXT'; #ifdef USE_SFL_CODE /* Block */ if (TRUE) { OSErr err; short vref; long dirID; char foo[128]; /* Find the folder */ err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &vref, &dirID); /* Success */ if (!err) { /* Extract a path name */ PathNameFromDirID(dirID, vref, (StringPtr)foo); /* Convert the string */ ptocstr((StringPtr)foo); /* Append the preference file name */ strcat(foo, ANGBAND_PREFERENCES); /* Open the preference file */ fff = fopen(foo, "w"); /* Success */ oops = FALSE; } } #endif /* USE_SFL_CODE */ #if 0 /* I don't believe we need SysEnvirons any longer */ /* Oops */ if (oops) { SysEnvRec env; short savev; long saved; /* Save */ HGetVol(0, &savev, &saved); /* Go to "system" folder */ SysEnvirons(curSysEnvVers, &env); SetVol(0, env.sysVRefNum); /* Open the preference file */ fff = fopen(":Preferences:" ANGBAND_PREFERENCES, "w"); if (!fff) fff = fopen(":" ANGBAND_PREFERENCES, "w"); /* Restore */ HSetVol(0, savev, saved); } #endif /* relic */ /* Save preferences */ if (fff) { /* Write the preferences */ save_prefs(); /* Close it */ my_fclose(fff); } } #ifdef ALLOW_NO_SAVE_QUITS /* * A simple "Yes/No" filter to parse "key press" events in dialog windows */ static pascal Boolean ynfilter(DialogPtr dialog, EventRecord *event, short *ip) { /* Parse key press events */ if (event->what == keyDown) { int i = 0; char c; /* Extract the pressed key */ c = (event->message & charCodeMask); /* Accept "no" and and */ if ((c == 'n') || (c == 'N') || (c == 13) || (c == 3)) i = 1; /* Accept "yes" */ else if ((c == 'y') || (c == 'Y')) i = 2; /* Handle "yes" or "no" */ if (i) { short type; ControlHandle control; Rect r; /* Get the button */ GetDialogItem(dialog, i, &type, (Handle*)&control, &r); /* Blink button for 1/10 second */ HiliteControl(control, 1); Term_xtra_mac(TERM_XTRA_DELAY, 100); HiliteControl(control, 0); /* Result */ *ip = i; return (1); } } /* Ignore */ return (0); } #endif /* ALLOW_NO_SAVE_QUITS */ #ifndef SAVEFILE_SCREEN /* * Handle menu: "File" + "New" */ static void do_menu_file_new(void) { /* Hack */ HiliteMenu(0); /* Game is in progress */ game_in_progress = 1; /* Flush input */ Term_flush(); /* Play a game */ play_game(TRUE); /* Hack -- quit */ quit(NULL); } /* * Handle menu: "File" + "Open" */ static void do_menu_file_open(bool all) { int err; short vrefnum; long drefnum; long junk; DirInfo pb; SFTypeList types; SFReply reply; Point topleft; /* XXX XXX XXX */ /* vrefnum = GetSFCurVol(); */ vrefnum = -*((short*)0x214); /* drefnum = GetSFCurDir(); */ drefnum = *((long*)0x398); /* Descend into "lib" folder */ pb.ioCompletion = NULL; pb.ioNamePtr = "\plib"; pb.ioVRefNum = vrefnum; pb.ioDrDirID = drefnum; pb.ioFDirIndex = 0; /* Check for errors */ err = PBGetCatInfoSync((CInfoPBPtr) & pb); /* Success */ if ((err == noErr) && (pb.ioFlAttrib & 0x10)) { /* Descend into "lib/save" folder */ pb.ioCompletion = NULL; pb.ioNamePtr = "\psave"; pb.ioVRefNum = vrefnum; pb.ioDrDirID = pb.ioDrDirID; pb.ioFDirIndex = 0; /* Check for errors */ err = PBGetCatInfoSync((CInfoPBPtr) & pb); /* Success */ if ((err == noErr) && (pb.ioFlAttrib & 0x10)) { /* SetSFCurDir(pb.ioDrDirID); */ *((long*)0x398) = pb.ioDrDirID; } } /* Window location */ topleft.h = (qd.screenBits.bounds.left + qd.screenBits.bounds.right) / 2 - 344 / 2; topleft.v = (2 * qd.screenBits.bounds.top + qd.screenBits.bounds.bottom) / 3 - 188 / 2; /* Allow "all" files */ if (all) { /* Get any file */ SFGetFile(topleft, "\p", NULL, -1, types, NULL, &reply); } /* Allow "save" files */ else { /* Legal types */ types[0] = 'SAVE'; /* Get a file */ SFGetFile(topleft, "\p", NULL, 1, types, NULL, &reply); } /* Allow cancel */ if (!reply.good) return; /* Extract textual file name for save file */ GetWDInfo(reply.vRefNum, &vrefnum, &drefnum, &junk); refnum_to_name(savefile, drefnum, vrefnum, (char*)reply.fName); /* Hack */ HiliteMenu(0); /* Game is in progress */ game_in_progress = 1; /* Flush input */ flush(); /* Play a game */ play_game(FALSE); /* Hack -- quit */ quit(NULL); } #endif /* !SAVEFILE_SCREEN */ /* * Handle the "open_when_ready" flag */ static void handle_open_when_ready(void) { /* Check the flag XXX XXX XXX make a function for this */ if (open_when_ready && initialized && !game_in_progress) { /* Forget */ open_when_ready = FALSE; /* Game is in progress */ game_in_progress = 1; /* Wait for it */ pause_line(23); /* Flush input */ flush(); #ifdef SAVEFILE_SCREEN /* User double-clicked savefile; no savefile screen */ no_begin_screen = TRUE; #endif /* SAVEFILE_SCREEN */ /* Play a game */ play_game(FALSE); /* Quit */ quit(NULL); } } /* * The standard menus are: * * Apple (128) = { About, -, ... } * File (129) = { New,Open,Import,Close,Save,-,Score,Exit,Quit } * In ToME, this becomes * File (129) = { Close,Save,-,Score,Exit,Quit } * Edit (130) = { Cut, Copy, Paste, Clear } (?) * Font (131) = { Bold, Extend, -, Monaco, ..., -, ... } * Size (132) = { ... } * Window (133) = { Angband, Term-1/Mirror, Term-2/Recall, Term-3/Choice, * Term-4, Term-5, Term-6, Term-7 } * Special (134) = { Sound, Graphics, TileWidth, TileHeight, -, * Fiddle, Wizard } */ /* Apple menu */ #define MENU_APPLE 128 #define ITEM_ABOUT 1 /* File menu */ #define MENU_FILE 129 #ifndef SAVEFILE_SCREEN # define ITEM_NEW 1 # define ITEM_OPEN 2 # define ITEM_IMPORT 3 # define ITEM_CLOSE 4 # define ITEM_SAVE 5 # ifdef HAS_SCORE_MENU # define ITEM_SCORE 7 # define ITEM_EXIT 8 # define ITEM_QUIT 9 # else # define ITEM_EXIT 7 # define ITEM_QUIT 8 # endif /* HAS_SCORE_MENU */ #else /* !SAVEFILE_SCREEN - in-game savefile menu */ # define ITEM_CLOSE 1 # define ITEM_SAVE 2 # ifdef HAS_SCORE_MENU # define ITEM_SCORE 4 # define ITEM_EXIT 5 # define ITEM_QUIT 6 # else # define ITEM_EXIT 4 # define ITEM_QUIT 5 # endif /* HAS_SCORE_MENU */ #endif /* !SAVEFILE_SCREEN */ /* Edit menu */ #define MENU_EDIT 130 #define ITEM_UNDO 1 #define ITEM_CUT 3 #define ITEM_COPY 4 #define ITEM_PASTE 5 #define ITEM_CLEAR 6 /* Font menu */ #define MENU_FONT 131 #define ITEM_BOLD 1 #define ITEM_WIDE 2 /* Size menu */ #define MENU_SIZE 132 /* Windows menu */ #define MENU_WINDOWS 133 /* Special menu */ #define MENU_SPECIAL 134 #define ITEM_SOUND 1 #define ITEM_GRAPH 2 #define ITEM_TILEWIDTH 3 #define ITEM_TILEHEIGHT 4 #define ITEM_FIDDLE 6 #define ITEM_WIZARD 7 /* Graphics submenu */ #define SUBMENU_GRAPH 144 #define ITEM_NONE 1 #define ITEM_8X8 2 #define ITEM_16X16 3 #define ITEM_BIGTILE 5 /* TileWidth submenu */ #define SUBMENU_TILEWIDTH 145 /* TileHeight submenu */ #define SUBMENU_TILEHEIGHT 146 /* * Initialize the menus */ static void init_menubar(void) { int i, n; Rect r; WindowPtr tmpw; MenuHandle m; Handle mbar; /* Load menubar from resources */ mbar = GetNewMBar(128); /* Whoops! */ if (mbar == nil) quit("Cannot find menubar('MBAR') id 128!"); /* Insert them into the current menu list */ SetMenuBar(mbar); /* Free handle */ DisposeHandle(mbar); /* Apple menu (id 128) */ m = GetMenuHandle(MENU_APPLE); /* Add the DA's to the "apple" menu */ AppendResMenu(m, 'DRVR'); /* File menu (id 129) - we don't have to do anything */ /* Edit menu (id 130) - we don't have to do anything */ /* * Font menu (id 131) - append names of mono-spaced fonts * followed by all available ones */ m = GetMenuHandle(MENU_FONT); /* Fake window */ r.left = r.right = r.top = r.bottom = 0; /* Make the fake window so that we can retrive font info */ tmpw = NewWindow(0, &r, "\p", false, documentProc, 0, 0, 0); /* Activate the "fake" window */ SetPort(tmpw); /* Default mode */ TextMode(0); /* Default size */ TextSize(12); /* Add the fonts to the menu */ AppendResMenu(m, 'FONT'); /* Size of menu */ n = CountMItems(m); /* Scan the menu */ for (i = n; i >= 4; i--) { Str255 tmpName; short fontNum; /* Acquire the font name */ GetMenuItemText(m, i, tmpName); /* Acquire the font index */ GetFNum(tmpName, &fontNum); /* Apply the font index */ TextFont(fontNum); /* Remove non-mono-spaced fonts */ if ((CharWidth('i') != CharWidth('W')) || (CharWidth('W') == 0)) { /* Delete the menu item */ DeleteMenuItem(m, i); } } /* Destroy the old window */ DisposeWindow(tmpw); /* Add a separator */ AppendMenu(m, "\p-"); /* Add the fonts to the menu */ AppendResMenu(m, 'FONT'); /* Size menu (id 132) */ m = GetMenuHandle(MENU_SIZE); /* Add some sizes (stagger choices) */ for (i = 8; i <= 32; i += ((i / 16) + 1)) { Str15 buf; /* Textual size */ strnfmt((char*)buf + 1, 15, "%d", i); buf[0] = strlen((char*)buf + 1); /* Add the item */ AppendMenu(m, buf); } /* Windows menu (id 133) */ m = GetMenuHandle(MENU_WINDOWS); /* Default choices */ for (i = 0; i < MAX_TERM_DATA; i++) { Str15 buf; /* Describe the item */ strnfmt((char*)buf + 1, 15, "%.15s", angband_term_name[i]); buf[0] = strlen((char*)buf + 1); /* Add the item */ AppendMenu(m, buf); /* Command-Key shortcuts */ if (i < 8) SetItemCmd(m, i + 1, I2D(i)); } /* Special menu (id 134) */ /* Get graphics (sub)menu (id 144) */ m = GetMenu(SUBMENU_GRAPH); /* Insert it as a submenu */ InsertMenu(m, hierMenu); /* Get TileWidth (sub)menu (id 145) */ m = GetMenu(SUBMENU_TILEWIDTH); /* Add some sizes */ for (i = 4; i <= 32; i++) { Str15 buf; /* Textual size */ strnfmt((char*)buf + 1, 15, "%d", i); buf[0] = strlen((char*)buf + 1); /* Append item */ AppendMenu(m, buf); } /* Insert it as a submenu */ InsertMenu(m, hierMenu); /* Get TileHeight (sub)menu (id 146) */ m = GetMenu(SUBMENU_TILEHEIGHT); /* Add some sizes */ for (i = 4; i <= 32; i++) { Str15 buf; /* Textual size */ strnfmt((char*)buf + 1, 15, "%d", i); buf[0] = strlen((char*)buf + 1); /* Append item */ AppendMenu(m, buf); } /* Insert it as a submenu */ InsertMenu(m, hierMenu); /* Update the menu bar */ DrawMenuBar(); } /* * Prepare the menus * * It is very important that the player not be allowed to "save" the game * unless the "inkey_flag" variable is set, indicating that the game is * waiting for a new command. XXX XXX XXX */ static void setup_menus(void) { int i, n; short value; Str255 s; MenuHandle m; term_data *td = NULL; /* Relevant "term_data" */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Unused */ if (!data[i].t) continue; /* Notice the matching window */ if (data[i].w == FrontWindow()) td = &data[i]; } /* File menu */ m = GetMenuHandle(MENU_FILE); /* Get menu size */ n = CountMItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableItem(m, i); CheckItem(m, i, FALSE); } #ifndef SAVEFILE_SCREEN /* Enable "new"/"open..."/"import..." */ if (initialized && !game_in_progress) { EnableItem(m, ITEM_NEW); EnableItem(m, ITEM_OPEN); EnableItem(m, ITEM_IMPORT); } #endif /* !SAVEFILE_SCREEN */ /* Enable "close" */ if (initialized) { EnableItem(m, ITEM_CLOSE); } /* Enable "save" */ if (initialized && character_generated && inkey_flag) { EnableItem(m, ITEM_SAVE); } #ifdef HAS_SCORE_MENU /* Enable "score" */ if (initialized && character_generated && !character_icky) { EnableItem(m, ITEM_SCORE); } #endif /* HAS_SCORE_MENU */ #ifdef ALLOW_NO_SAVE_QUITS /* Enable "exit" */ if (TRUE) { EnableItem(m, ITEM_EXIT); } #endif /* ALLOW_NO_SAVE_QUITS */ /* Enable "quit" */ if (!initialized || !character_generated || inkey_flag) { EnableItem(m, ITEM_QUIT); } /* Edit menu */ m = GetMenuHandle(MENU_EDIT); /* Get menu size */ n = CountMItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableItem(m, i); CheckItem(m, i, FALSE); } /* Enable "edit" options if "needed" */ if (!td) { EnableItem(m, ITEM_UNDO); EnableItem(m, ITEM_CUT); EnableItem(m, ITEM_COPY); EnableItem(m, ITEM_PASTE); EnableItem(m, ITEM_CLEAR); } /* Font menu */ m = GetMenuHandle(MENU_FONT); /* Get menu size */ n = CountMItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableItem(m, i); CheckItem(m, i, FALSE); } /* Hack -- look cute XXX XXX */ /* SetItemStyle(m, ITEM_BOLD, bold); */ /* Hack -- look cute XXX XXX */ /* SetItemStyle(m, ITEM_WIDE, extend); */ /* Active window */ if (initialized && td) { /* Enable "bold" */ EnableItem(m, ITEM_BOLD); /* Enable "extend" */ EnableItem(m, ITEM_WIDE); /* Check the appropriate "bold-ness" */ if (td->font_face & bold) CheckItem(m, ITEM_BOLD, TRUE); /* Check the appropriate "wide-ness" */ if (td->font_face & extend) CheckItem(m, ITEM_WIDE, TRUE); /* Analyze fonts */ for (i = 4; i <= n; i++) { /* Enable it */ EnableItem(m, i); /* Analyze font */ GetMenuItemText(m, i, s); GetFNum(s, &value); /* Check active font */ if (td->font_id == value) CheckItem(m, i, TRUE); } } /* Size menu */ m = GetMenuHandle(MENU_SIZE); /* Get menu size */ n = CountMItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableItem(m, i); CheckItem(m, i, FALSE); } /* Active window */ if (initialized && td) { /* Analyze sizes */ for (i = 1; i <= n; i++) { /* Analyze size */ GetMenuItemText(m, i, s); s[s[0] + 1] = '\0'; value = atoi((char*)(s + 1)); /* Enable the "real" sizes */ if (RealFont(td->font_id, value)) EnableItem(m, i); /* Check the current size */ if (td->font_size == value) CheckItem(m, i, TRUE); } } /* Windows menu */ m = GetMenuHandle(MENU_WINDOWS); /* Get menu size */ n = CountMItems(m); /* Check active windows */ for (i = 1; i <= n; i++) { /* Check if needed */ CheckItem(m, i, data[i - 1].mapped); } /* Special menu */ m = GetMenuHandle(MENU_SPECIAL); /* Get menu size */ n = CountMItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableItem(m, i); /* XXX Oh no, this removes submenu... */ if ((i != ITEM_GRAPH) && (i != ITEM_TILEWIDTH) && (i != ITEM_TILEHEIGHT)) CheckItem(m, i, FALSE); } /* Item "Sound" */ EnableItem(m, ITEM_SOUND); CheckItem(m, ITEM_SOUND, arg_sound); /* Item "Graphics" */ EnableItem(m, ITEM_GRAPH); /* Item "TileWidth" */ EnableItem(m, ITEM_TILEWIDTH); /* Item "TileHeight" */ EnableItem(m, ITEM_TILEHEIGHT); /* Item "Fiddle" */ EnableItem(m, ITEM_FIDDLE); CheckItem(m, ITEM_FIDDLE, arg_fiddle); /* Item "Wizard" */ EnableItem(m, ITEM_WIZARD); CheckItem(m, ITEM_WIZARD, arg_wizard); /* Graphics submenu */ m = GetMenuHandle(SUBMENU_GRAPH); /* Get menu size */ n = CountMItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableItem(m, i); CheckItem(m, i, FALSE); } /* Item "None" */ EnableItem(m, ITEM_NONE); CheckItem(m, ITEM_NONE, (graf_mode == GRAF_MODE_NONE)); /* Item "8x8" */ EnableItem(m, ITEM_8X8); CheckItem(m, ITEM_8X8, (graf_mode == GRAF_MODE_8X8)); /* Item "16x16" */ EnableItem(m, ITEM_16X16); CheckItem(m, ITEM_16X16, (graf_mode == GRAF_MODE_16X16)); #ifdef USE_DOUBLE_TILES /* Item "Bigtile" */ if (inkey_flag) EnableItem(m, ITEM_BIGTILE); CheckItem(m, ITEM_BIGTILE, use_bigtile); #endif /* USE_DOUBLE_TILES */ /* TIleWidth submenu */ m = GetMenuHandle(SUBMENU_TILEWIDTH); /* Get menu size */ n = CountMItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableItem(m, i); CheckItem(m, i, FALSE); } /* Active window */ if (initialized && td) { /* Analyze sizes */ for (i = 1; i <= n; i++) { /* Analyze size */ GetMenuItemText(m, i, s); s[s[0] + 1] = '\0'; value = atoi((char*)(s + 1)); /* Enable */ if (value >= td->font_wid) EnableItem(m, i); /* Check the current size */ if (td->tile_wid == value) CheckItem(m, i, TRUE); } } /* TileHeight submenu */ m = GetMenuHandle(SUBMENU_TILEHEIGHT); /* Get menu size */ n = CountMItems(m); /* Reset menu */ for (i = 1; i <= n; i++) { /* Reset */ DisableItem(m, i); CheckItem(m, i, FALSE); } /* Active window */ if (initialized && td) { /* Analyze sizes */ for (i = 1; i <= n; i++) { /* Analyze size */ GetMenuItemText(m, i, s); s[s[0] + 1] = '\0'; value = atoi((char*)(s + 1)); /* Enable */ if (value >= td->font_hgt) EnableItem(m, i); /* Check the current size */ if (td->tile_hgt == value) CheckItem(m, i, TRUE); } } } /* * Process a menu selection (see above) * * Hack -- assume that invalid menu selections are disabled above, * which I have been informed may not be reliable. XXX XXX XXX */ static void menu(long mc) { int i; int menuid, selection; static unsigned char s[1000]; short fid; term_data *td = NULL; WindowPtr old_win; /* Analyze the menu command */ menuid = HiWord(mc); selection = LoWord(mc); /* Find the window */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Skip dead windows */ if (!data[i].t) continue; /* Notice matches */ if (data[i].w == FrontWindow()) td = &data[i]; } /* Branch on the menu */ switch (menuid) { /* Apple Menu */ case MENU_APPLE: { /* About Angband... */ if (selection == ITEM_ABOUT) { DialogPtr dialog; Rect r; short item_hit; dialog = GetNewDialog(128, 0, (WindowPtr) - 1); r = dialog->portRect; center_rect(&r, &qd.screenBits.bounds); MoveWindow(dialog, r.left, r.top, 1); ShowWindow(dialog); ModalDialog(0, &item_hit); DisposeDialog(dialog); break; } /* Desk accessory */ GetMenuItemText(GetMenuHandle(MENU_APPLE), selection, s); OpenDeskAcc(s); break; } /* File Menu */ case MENU_FILE: { switch (selection) { #ifndef SAVEFILE_SCREEN /* New */ case ITEM_NEW: { do_menu_file_new(); break; } /* Open... */ case ITEM_OPEN: { do_menu_file_open(FALSE); break; } /* Import... */ case ITEM_IMPORT: { do_menu_file_open(TRUE); break; } #endif /* !SAVEFILE_SCREEN */ /* Close */ case ITEM_CLOSE: { /* No window */ if (!td) break; /* Not Mapped */ td->mapped = FALSE; /* Not Mapped */ td->t->mapped_flag = FALSE; /* Hide the window */ HideWindow(td->w); break; } /* Save */ case ITEM_SAVE: { /* Hack -- Forget messages */ msg_flag = FALSE; /* Hack -- Save the game */ #ifndef ZANG_AUTO_SAVE do_cmd_save_game(); #else do_cmd_save_game(FALSE); #endif /* !ZANG_AUTO_SAVE */ break; } #ifdef HAS_SCORE_MENU /* Show score */ case ITEM_SCORE: { char buf[1024]; /* Paranoia */ if (!initialized || character_icky || !game_in_progress || !character_generated) { /* Can't happen but just in case */ plog("You may not do that right now."); break; } /* Build the pathname of the score file */ path_build(buf, sizeof(buf), ANGBAND_DIR_APEX, "scores.raw"); /* Hack - open the score file for reading */ highscore_fd = fd_open(buf, O_RDONLY); /* Paranoia - No score file */ if (highscore_fd < 0) { msg_print("Score file is not available."); break; } /* Mega-Hack - prevent various functions XXX XXX XXX */ initialized = FALSE; /* Save screen */ screen_save(); /* Clear screen */ Term_clear(); /* Prepare scores */ if (game_in_progress && character_generated) { predict_score(); } #if 0 /* I don't like this - pelpel */ /* Mega-Hack - No current player XXX XXX XXX XXX */ else { display_scores_aux(0, MAX_HISCORES, -1, NULL); } #endif /* Close the high score file */ (void)fd_close(highscore_fd); /* Forget the fd */ highscore_fd = -1; /* Restore screen */ screen_load(); /* Hack - Flush it */ Term_fresh(); /* Mega-Hack - We are ready again */ initialized = TRUE; /* Done */ break; } #endif /* HAS_SCORE_MENU */ #ifdef ALLOW_NO_SAVE_QUITS /* Exit (without save) */ case ITEM_EXIT: { /* Allow user to cancel "dangerous" exit */ if (game_in_progress && character_generated) { AlertTHndl alert; short item_hit; /* Get the "alert" info */ alert = (AlertTHndl)GetResource('ALRT', 130); /* Center the "alert" rectangle */ center_rect(&(*alert)->boundsRect, &qd.screenBits.bounds); /* Display the Alert, get "No" or "Yes" */ item_hit = Alert(130, ynfilterUPP); /* Require "yes" button */ if (item_hit != 2) break; } /* Quit */ quit(NULL); break; } #endif /* ALLOW_NO_SAVE_QUITS */ /* Quit (with save) */ case ITEM_QUIT: { /* Save the game (if necessary) */ if (game_in_progress && character_generated) { /* Hack -- Forget messages */ msg_flag = FALSE; /* Save the game */ #ifndef ZANG_AUTO_SAVE do_cmd_save_game(); #else do_cmd_save_game(FALSE); #endif /* !ZANG_AUTO_SAVE */ } /* Quit */ quit(NULL); break; } } break; } /* Edit menu */ case MENU_EDIT: { /* Unused */ break; } /* Font menu */ case MENU_FONT: { /* Require a window */ if (!td) break; /* Memorize old */ old_win = active; /* Activate */ activate(td->w); /* Toggle the "bold" setting */ if (selection == ITEM_BOLD) { /* Toggle the setting */ if (td->font_face & bold) { td->font_face &= ~bold; } else { td->font_face |= bold; } /* Hack - clear tile size info XXX XXX */ td->tile_wid = td->tile_hgt = 0; /* Apply and Verify */ term_data_check_font(td); term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); break; } /* Toggle the "wide" setting */ if (selection == ITEM_WIDE) { /* Toggle the setting */ if (td->font_face & extend) { td->font_face &= ~extend; } else { td->font_face |= extend; } /* Hack - clear tile size info XXX XXX */ td->tile_wid = td->tile_hgt = 0; /* Apply and Verify */ term_data_check_font(td); term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); break; } /* Get a new font name */ GetMenuItemText(GetMenuHandle(MENU_FONT), selection, s); GetFNum(s, &fid); /* Save the new font id */ td->font_id = fid; /* Current size is bad for new font */ if (!RealFont(td->font_id, td->font_size)) { /* Find similar size */ for (i = 1; i <= 32; i++) { /* Adjust smaller */ if (td->font_size - i >= 8) { if (RealFont(td->font_id, td->font_size - i)) { td->font_size -= i; break; } } /* Adjust larger */ if (td->font_size + i <= 128) { if (RealFont(td->font_id, td->font_size + i)) { td->font_size += i; break; } } } } /* Hack - clear tile size info XXX XXX */ td->tile_wid = td->tile_hgt = 0; /* Apply and Verify */ term_data_check_font(td); term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); /* Restore the window */ activate(old_win); break; } /* Size menu */ case MENU_SIZE: { if (!td) break; /* Save old */ old_win = active; /* Activate */ activate(td->w); GetMenuItemText(GetMenuHandle(MENU_SIZE), selection, s); s[s[0] + 1] = 0; td->font_size = atoi((char*)(s + 1)); /* Hack - clear tile size info XXX XXX */ td->tile_wid = td->tile_hgt = 0; /* Apply and Verify */ term_data_check_font(td); term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); /* Restore */ activate(old_win); break; } /* Window menu */ case MENU_WINDOWS: { /* Parse */ i = selection - 1; /* Check legality of choice */ if ((i < 0) || (i >= MAX_TERM_DATA)) break; /* Obtain the window */ td = &data[i]; /* Mapped */ td->mapped = TRUE; /* Link */ term_data_link(i); /* Mapped (?) */ td->t->mapped_flag = TRUE; /* Show the window */ ShowWindow(td->w); /* Bring to the front */ SelectWindow(td->w); break; } /* Special menu */ case MENU_SPECIAL: { switch (selection) { case ITEM_SOUND: { /* Toggle arg_sound */ arg_sound = !arg_sound; /* React to changes */ Term_xtra(TERM_XTRA_REACT, 0); break; } case ITEM_FIDDLE: { arg_fiddle = !arg_fiddle; break; } case ITEM_WIZARD: { arg_wizard = !arg_wizard; break; } } break; } /* Graphics submenu */ case SUBMENU_GRAPH: { switch (selection) { case ITEM_NONE: { graf_mode_req = GRAF_MODE_NONE; break; } case ITEM_8X8: { graf_mode_req = GRAF_MODE_8X8; break; } case ITEM_16X16: { graf_mode_req = GRAF_MODE_16X16; break; } #ifdef USE_DOUBLE_TILES case ITEM_BIGTILE: { term *old = Term; term_data *td = &data[0]; /* Toggle "arg_bigtile" */ use_bigtile = !use_bigtile; # ifdef TOME arg_bigtile = use_bigtile; # endif /* TOME */ /* Activate */ Term_activate(td->t); /* Resize the term */ Term_resize(td->cols, td->rows); /* Activate old */ Term_activate(old); break; } #endif /* USE_DOUBLE_TILES */ } /* Hack -- Force redraw */ Term_key_push(KTRL('R')); break; } /* TileWidth submenu */ case SUBMENU_TILEWIDTH: { if (!td) break; /* Save old */ old_win = active; /* Activate */ activate(td->w); GetMenuItemText(GetMenuHandle(SUBMENU_TILEWIDTH), selection, s); s[s[0] + 1] = 0; td->tile_wid = atoi((char*)(s + 1)); /* Apply and Verify */ term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); /* Restore */ activate(old_win); break; } /* TileHeight submenu */ case SUBMENU_TILEHEIGHT: { if (!td) break; /* Save old */ old_win = active; /* Activate */ activate(td->w); GetMenuItemText(GetMenuHandle(SUBMENU_TILEHEIGHT), selection, s); s[s[0] + 1] = 0; td->tile_hgt = atoi((char*)(s + 1)); /* Apply and Verify */ term_data_check_size(td); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); /* Restore */ activate(old_win); break; } } /* Clean the menu */ HiliteMenu(0); } #ifdef USE_SFL_CODE /* * Check for extra required parameters -- From "Maarten Hazewinkel" */ static OSErr CheckRequiredAEParams(const AppleEvent *theAppleEvent) { OSErr aeError; DescType returnedType; Size actualSize; aeError = AEGetAttributePtr(theAppleEvent, keyMissedKeywordAttr, typeWildCard, &returnedType, NULL, 0, &actualSize); if (aeError == errAEDescNotFound) return (noErr); if (aeError == noErr) return (errAEParamMissed); return (aeError); } /* * Apple Event Handler -- Open Application */ static OSErr AEH_Start(const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefCon) { #pragma unused(reply, handlerRefCon) return (CheckRequiredAEParams(theAppleEvent)); } /* * Apple Event Handler -- Quit Application */ static OSErr AEH_Quit(const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefCon) { #pragma unused(reply, handlerRefCon) /* Quit later */ quit_when_ready = TRUE; /* Check arguments */ return (CheckRequiredAEParams(theAppleEvent)); } /* * Apple Event Handler -- Print Documents */ static OSErr AEH_Print(const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefCon) { #pragma unused(theAppleEvent, reply, handlerRefCon) return (errAEEventNotHandled); } /* * Apple Event Handler by Steve Linberg (slinberg@crocker.com). * * The old method of opening savefiles from the finder does not work * on the Power Macintosh, because CountAppFiles and GetAppFiles, * used to return information about the selected document files when * an application is launched, are part of the Segment Loader, which * is not present in the RISC OS due to the new memory architecture. * * The "correct" way to do this is with AppleEvents. The following * code is modeled on the "Getting Files Selected from the Finder" * snippet from Think Reference 2.0. (The prior sentence could read * "shamelessly swiped & hacked") */ static OSErr AEH_Open(AppleEvent *theAppleEvent, AppleEvent* reply, long handlerRefCon) { #pragma unused(reply, handlerRefCon) FSSpec myFSS; AEDescList docList; OSErr err; Size actualSize; AEKeyword keywd; DescType returnedType; char msg[128]; FInfo myFileInfo; /* Put the direct parameter (a descriptor list) into a docList */ err = AEGetParamDesc(theAppleEvent, keyDirectObject, typeAEList, &docList); if (err) return err; /* * We ignore the validity check, because we trust the FInder, and we only * allow one savefile to be opened, so we ignore the depth of the list. */ err = AEGetNthPtr(&docList, 1L, typeFSS, &keywd, &returnedType, (Ptr) & myFSS, sizeof(myFSS), &actualSize); if (err) return err; /* Only needed to check savefile type below */ err = FSpGetFInfo(&myFSS, &myFileInfo); if (err) { strnfmt(msg, 128, "Argh! FSpGetFInfo failed with code %d", err); mac_warning(msg); return err; } /* Ignore non 'SAVE' files */ if (myFileInfo.fdType != 'SAVE') return noErr; /* XXX XXX XXX Extract a file name */ PathNameFromDirID(myFSS.parID, myFSS.vRefNum, (StringPtr)savefile); pstrcat((StringPtr)savefile, (StringPtr)&myFSS.name); /* Convert the string */ ptocstr((StringPtr)savefile); /* Delay actual open */ open_when_ready = TRUE; /* Dispose */ err = AEDisposeDesc(&docList); /* Success */ return noErr; } #endif /* * Macintosh modifiers (event.modifier & ccc): * cmdKey, optionKey, shiftKey, alphaLock, controlKey * * * Macintosh Keycodes (0-63 normal, 64-95 keypad, 96-127 extra): * * Return:36 * Delete:51 * * Period:65 * Star:67 * Plus:69 * Clear:71 * Slash:75 * Enter:76 * Minus:78 * Equal:81 * 0-7:82-89 * 8-9:91-92 * * backslash/vertical bar (Japanese keyboard):93 * * F5: 96 * F6: 97 * F7: 98 * F3:99 * F8:100 * F10:101 * F11:103 * F13:105 * F14:107 * F9:109 * F12:111 * F15:113 * Help:114 * Home:115 * PgUp:116 * Del:117 * F4: 118 * End:119 * F2:120 * PgDn:121 * F1:122 * Lt:123 * Rt:124 * Dn:125 * Up:126 */ /* * Optimize non-blocking calls to "CheckEvents()" * Idea from "Maarten Hazewinkel " */ #define EVENT_TICKS 6 /* * Check for Events, return TRUE if we process any * * Hack -- Handle AppleEvents if appropriate (ignore result code). */ static bool CheckEvents(bool wait) { EventRecord event; WindowPtr w; Rect r; long newsize; int ch, ck; int mc, ms, mo, mx; int i; term_data *td = NULL; UInt32 curTicks; static UInt32 lastTicks = 0L; /* Access the clock */ curTicks = TickCount(); /* Hack -- Allow efficient checking for non-pending events */ if (!wait && (curTicks < lastTicks + EVENT_TICKS)) return (FALSE); /* Timestamp last check */ lastTicks = curTicks; /* Let the "system" run */ SystemTask(); /* Get an event (or null) */ WaitNextEvent(everyEvent, &event, 0L, nil); /* Hack -- Nothing is ready yet */ if (event.what == nullEvent) return (FALSE); /* Analyze the event */ switch (event.what) { #if 0 case activateEvt: { w = (WindowPtr)event.message; activate(w); break; } #endif case updateEvt: { /* Extract the window */ w = (WindowPtr)event.message; /* Find the window */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Skip dead windows */ if (!data[i].t) continue; /* Notice matches */ if (data[i].w == w) td = &data[i]; } /* Hack XXX XXX XXX */ BeginUpdate(w); EndUpdate(w); /* Redraw the window */ if (td) term_data_redraw(td); break; } case keyDown: case autoKey: { /* Extract some modifiers */ mc = (event.modifiers & controlKey) ? TRUE : FALSE; ms = (event.modifiers & shiftKey) ? TRUE : FALSE; mo = (event.modifiers & optionKey) ? TRUE : FALSE; mx = (event.modifiers & cmdKey) ? TRUE : FALSE; /* Keypress: (only "valid" if ck < 96) */ ch = (event.message & charCodeMask) & 255; /* Keycode: see table above */ ck = ((event.message & keyCodeMask) >> 8) & 255; /* Command + "normal key" -> menu action */ if (mx && (ck < 64)) { /* Hack -- Prepare the menus */ setup_menus(); /* Mega-Hack -- allow easy exit if nothing to save */ /* if (!character_generated && (ch=='Q' || ch=='q')) ch = 'e'; */ /* Run the Menu-Handler */ menu(MenuKey(ch)); /* Turn off the menus */ HiliteMenu(0); /* Done */ break; } /* Hide the mouse pointer */ ObscureCursor(); /* Normal key -> simple keypress */ if ((ck < 64) || (ck == 93)) { /* Enqueue the keypress */ Term_keypress(ch); } /* Keypad keys -> trigger plus simple keypress */ else if (!mc && !ms && !mo && !mx && (ck < 96)) { /* Hack -- "enter" is confused */ if (ck == 76) ch = '\n'; /* Begin special trigger */ Term_keypress(31); /* Send the "keypad" modifier */ Term_keypress('K'); /* Send the "ascii" keypress */ Term_keypress(ch); /* Terminate the trigger */ Term_keypress(13); } /* Bizarre key -> encoded keypress */ else if (ck <= 127) { /* Begin special trigger */ Term_keypress(31); /* Send some modifier keys */ if (mc) Term_keypress('C'); if (ms) Term_keypress('S'); if (mo) Term_keypress('O'); if (mx) Term_keypress('X'); /* Downshift and encode the keycode */ Term_keypress(I2D((ck - 64) / 10)); Term_keypress(I2D((ck - 64) % 10)); /* Terminate the trigger */ Term_keypress(13); } break; } case mouseDown: { int code; /* Analyze click location */ code = FindWindow(event.where, &w); /* Find the window */ for (i = 0; i < MAX_TERM_DATA; i++) { /* Skip dead windows */ if (!data[i].t) continue; /* Notice matches */ if (data[i].w == w) td = &data[i]; } /* Analyze */ switch (code) { case inMenuBar: { setup_menus(); menu(MenuSelect(event.where)); HiliteMenu(0); break; } case inSysWindow: { SystemClick(&event, w); break; } case inDrag: { Point p; WindowPtr old_win; r = qd.screenBits.bounds; r.top += 20; /* GetMBarHeight() XXX XXX XXX */ InsetRect(&r, 4, 4); DragWindow(w, event.where, &r); /* Oops */ if (!td) break; /* Save */ old_win = active; /* Activate */ activate(td->w); /* Analyze */ p.h = td->w->portRect.left; p.v = td->w->portRect.top; LocalToGlobal(&p); td->r.left = p.h; td->r.top = p.v; /* Restore */ activate(old_win); /* Apply and Verify */ term_data_check_size(td); break; } case inGoAway: { /* Oops */ if (!td) break; /* Track the go-away box */ if (TrackGoAway(w, event.where)) { /* Not Mapped */ td->mapped = FALSE; /* Not Mapped */ td->t->mapped_flag = FALSE; /* Hide the window */ HideWindow(td->w); } break; } case inGrow: { int x, y; term *old = Term; /* Oops */ if (!td) break; #ifndef ALLOW_BIG_SCREEN /* Fake rectangle */ r.left = 20 * td->tile_wid + td->size_ow1; r.right = 80 * td->tile_wid + td->size_ow1 + td->size_ow2 + 1; r.top = 1 * td->tile_hgt + td->size_oh1; r.bottom = 24 * td->tile_hgt + td->size_oh1 + td->size_oh2 + 1; #else /* ALLOW_BIG_SCREEN */ /* Fake rectangle */ r.left = 20 * td->tile_wid + td->size_ow1; r.right = qd.screenBits.bounds.right; r.top = 1 * td->tile_hgt + td->size_oh1; r.bottom = qd.screenBits.bounds.bottom; #endif /* ALLOW_BIG_SCREEN */ /* Grow the rectangle */ newsize = GrowWindow(w, event.where, &r); /* Handle abort */ if (!newsize) break; /* Extract the new size in pixels */ y = HiWord(newsize) - td->size_oh1 - td->size_oh2; x = LoWord(newsize) - td->size_ow1 - td->size_ow2; /* Extract a "close" approximation */ td->rows = y / td->tile_hgt; td->cols = x / td->tile_wid; /* Apply and Verify */ term_data_check_size(td); /* Activate */ Term_activate(td->t); /* Hack -- Resize the term */ Term_resize(td->cols, td->rows); /* Resize and Redraw */ term_data_resize(td); term_data_redraw(td); /* Restore */ Term_activate(old); break; } case inContent: { SelectWindow(w); break; } } break; } /* Disk Event -- From "Maarten Hazewinkel" */ case diskEvt: { /* check for error when mounting the disk */ if (HiWord(event.message) != noErr) { Point p = {120, 120}; DILoad(); DIBadMount(p, event.message); DIUnload(); } break; } /* OS Event -- From "Maarten Hazewinkel" */ case osEvt: { switch ((event.message >> 24) & 0x000000FF) { case suspendResumeMessage: /* Resuming: activate the front window */ if (event.message & resumeFlag) { SetPort(FrontWindow()); SetCursor(&qd.arrow); } /* Suspend: deactivate the front window */ else { /* Nothing */ } break; } break; } #ifdef USE_SFL_CODE /* From "Steve Linberg" and "Maarten Hazewinkel" */ case kHighLevelEvent: { /* Process apple events */ if (AEProcessAppleEvent(&event) != noErr) { plog("Error in Apple Event Handler!"); } /* Handle "quit_when_ready" */ if (quit_when_ready) { /* Forget */ quit_when_ready = FALSE; /* Do the menu key */ menu(MenuKey('q')); /* Turn off the menus */ HiliteMenu(0); } /* Handle "open_when_ready" */ handle_open_when_ready(); break; } #endif } /* Something happened */ return (TRUE); } /*** Some Hooks for various routines ***/ /* * Mega-Hack -- emergency lifeboat */ static void *lifeboat = NULL; /* * Hook to "release" memory */ #ifdef NEW_ZVIRT_HOOKS /* [V] removed the unused 'size' argument. */ static void *hook_rnfree(void *v) #else static void *hook_rnfree(void *v, size_t size) #endif /* NEW_ZVIRT_HOOKS */ { #ifndef NEW_ZVIRT_HOOKS /* Oh, no. */ #pragma unused (size) #endif #ifdef USE_MALLOC /* Alternative method */ free(v); #else /* Dispose */ DisposePtr(v); #endif /* Success */ return (NULL); } /* * Hook to "allocate" memory */ static void *hook_ralloc(size_t size) { #ifdef USE_MALLOC /* Make a new pointer */ return (malloc(size)); #else /* Make a new pointer */ return (NewPtr(size)); #endif } /* * Hook to handle "out of memory" errors */ static void *hook_rpanic(size_t size) { #pragma unused (size) /* void *mem = NULL; */ /* Free the lifeboat */ if (lifeboat) { /* Free the lifeboat */ DisposePtr(lifeboat); /* Forget the lifeboat */ lifeboat = NULL; /* Mega-Hack -- Warning */ mac_warning("Running out of Memory!\rAbort this process now!"); /* Mega-Hack -- Never leave this function */ while (TRUE) CheckEvents(TRUE); } /* Mega-Hack -- Crash */ return (NULL); } /* * Hook to tell the user something important */ static void hook_plog(cptr str) { /* Warning message */ mac_warning(str); } /* * Hook to tell the user something, and then quit */ static void hook_quit(cptr str) { /* Warning if needed */ if (str) mac_warning(str); #ifdef USE_ASYNC_SOUND /* Cleanup sound player */ cleanup_sound(); #endif /* USE_ASYNC_SOUND */ /* Dispose of graphic tiles */ if (frameP) { /* Unlock */ BenSWUnlockFrame(frameP); /* Dispose of the GWorld */ DisposeGWorld(frameP->framePort); /* Dispose of the memory */ DisposePtr((Ptr)frameP); } /* Write a preference file */ save_pref_file(); /* All done */ ExitToShell(); } /* * Hook to tell the user something, and then crash */ static void hook_core(cptr str) { /* XXX Use the debugger */ /* DebugStr(str); */ /* Warning */ if (str) mac_warning(str); /* Warn, then save player */ mac_warning("Fatal error.\rI will now attempt to save and quit."); /* Attempt to save */ if (!save_player()) mac_warning("Warning -- save failed!"); /* Quit */ quit(NULL); } /*** Main program ***/ /* * Init some stuff * * XXX XXX XXX Hack -- This function attempts to "fix" the nasty * "Macintosh Save Bug" by using "absolute" path names, since on * System 7 machines anyway, the "current working directory" often * "changes" due to background processes, invalidating any "relative" * path names. Note that the Macintosh is limited to 255 character * path names, so be careful about deeply embedded directories... * * XXX XXX XXX Hack -- This function attempts to "fix" the nasty * "missing lib folder bug" by allowing the user to help find the * "lib" folder by hand if the "application folder" code fails... */ static void init_stuff(void) { int i; short vrefnum; long drefnum; long junk; SFTypeList types; SFReply reply; Rect r; Point topleft; char path[1024]; /* Fake rectangle */ r.left = 0; r.top = 0; r.right = 344; r.bottom = 188; /* Center it */ center_rect(&r, &qd.screenBits.bounds); /* Extract corner */ topleft.v = r.top; topleft.h = r.left; /* Default to the "lib" folder with the application */ refnum_to_name(path, app_dir, app_vol, (char*)("\plib:")); /* Check until done */ while (1) { /* Prepare the paths */ init_file_paths(path); /* Build the filename */ path_build(path, 1024, ANGBAND_DIR_FILE, "news.txt"); /* Attempt to open and close that file */ if (0 == fd_close(fd_open(path, O_RDONLY))) break; /* Warning */ plog_fmt("Unable to open the '%s' file.", path); /* Warning */ plog("The Angband 'lib' folder is probably missing or misplaced."); /* Warning */ plog("Please 'open' any file in any sub-folder of the 'lib' folder."); /* Allow "text" files */ types[0] = 'TEXT'; /* Allow "save" files */ types[1] = 'SAVE'; /* Allow "data" files */ types[2] = 'DATA'; /* Get any file */ SFGetFile(topleft, "\p", NULL, 3, types, NULL, &reply); /* Allow cancel */ if (!reply.good) quit(NULL); /* Extract textual file name for given file */ GetWDInfo(reply.vRefNum, &vrefnum, &drefnum, &junk); refnum_to_name(path, drefnum, vrefnum, (char*)reply.fName); /* Hack -- Remove the "filename" */ i = strlen(path) - 1; while ((i > 0) && (path[i] != ':')) i--; if (path[i] == ':') path[i + 1] = '\0'; /* Hack -- allow "lib" folders */ if (suffix(path, "lib:")) continue; /* Hack -- Remove the "sub-folder" */ i = i - 1; while ((i > 1) && (path[i] != ':')) i--; if (path[i] == ':') path[i + 1] = '\0'; } } /* * Macintosh Main loop */ int main(void) { int i; EventRecord tempEvent; int numberOfMasters = 10; /* Increase stack space by 64K */ SetApplLimit(GetApplLimit() - 65536L); /* Stretch out the heap to full size */ MaxApplZone(); /* Get more Masters */ while (numberOfMasters--) MoreMasters(); /* Set up the Macintosh */ InitGraf(&qd.thePort); InitFonts(); InitWindows(); InitMenus(); /* TEInit(); */ InitDialogs(NULL); InitCursor(); /* Flush events */ FlushEvents(everyEvent, 0); /* Flush events some more (?) */ if (EventAvail(everyEvent, &tempEvent)) FlushEvents(everyEvent, 0); #ifdef ANGBAND_LITE_MAC /* Nothing */ #else /* ANGBAND_LITE_MAC */ # if defined(powerc) || defined(__powerc) /* Assume System 7 */ /* Assume Color Quickdraw */ # else /* Block */ if (TRUE) { OSErr err; long versionNumber; /* Check the Gestalt */ err = Gestalt(gestaltSystemVersion, &versionNumber); /* Check the version */ if ((err != noErr) || (versionNumber < 0x0700)) { quit("You must have System 7 to use this program."); } } /* Block */ if (TRUE) { SysEnvRec env; /* Check the environs */ if (SysEnvirons(1, &env) != noErr) { quit("The SysEnvirons call failed!"); } /* Check for System Seven Stuff */ if (env.systemVersion < 0x0700) { quit("You must have System 7 to use this program."); } /* Check for Color Quickdraw */ if (!env.hasColorQD) { quit("You must have Color Quickdraw to use this program."); } } # endif #endif /* ANGBAND_LITE_MAC */ #ifdef USE_SFL_CODE /* Obtain a "Universal Procedure Pointer" */ AEH_Start_UPP = NewAEEventHandlerProc(AEH_Start); /* Install the hook (ignore error codes) */ AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, AEH_Start_UPP, 0L, FALSE); /* Obtain a "Universal Procedure Pointer" */ AEH_Quit_UPP = NewAEEventHandlerProc(AEH_Quit); /* Install the hook (ignore error codes) */ AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, AEH_Quit_UPP, 0L, FALSE); /* Obtain a "Universal Procedure Pointer" */ AEH_Print_UPP = NewAEEventHandlerProc(AEH_Print); /* Install the hook (ignore error codes) */ AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, AEH_Print_UPP, 0L, FALSE); /* Obtain a "Universal Procedure Pointer" */ AEH_Open_UPP = NewAEEventHandlerProc(AEH_Open); /* Install the hook (ignore error codes) */ AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, AEH_Open_UPP, 0L, FALSE); #endif /* Find the current application */ SetupAppDir(); /* Mark ourself as the file creator */ _fcreator = ANGBAND_CREATOR; /* Default to saving a "text" file */ _ftype = 'TEXT'; #if defined(ALLOW_NO_SAVE_QUITS) && defined(__MWERKS__) /* Obtian a "Universal Procedure Pointer" */ ynfilterUPP = NewModalFilterProc(ynfilter); #endif /* ALLOW_NO_SAVE_QUITS && __MWERKS__ */ /* Hook in some "z-virt.c" hooks */ rnfree_aux = hook_rnfree; ralloc_aux = hook_ralloc; rpanic_aux = hook_rpanic; /* Hooks in some "z-util.c" hooks */ plog_aux = hook_plog; quit_aux = hook_quit; core_aux = hook_core; /* Initialize colors */ for (i = 0; i < 256; i++) { u16b rv, gv, bv; /* Extract the R,G,B data */ rv = angband_color_table[i][1]; gv = angband_color_table[i][2]; bv = angband_color_table[i][3]; /* Save the actual color */ color_info[i].red = (rv | (rv << 8)); color_info[i].green = (gv | (gv << 8)); color_info[i].blue = (bv | (bv << 8)); } /* Show the "watch" cursor */ SetCursor(*(GetCursor(watchCursor))); /* Prepare the menubar */ init_menubar(); /* Prepare the windows */ init_windows(); /* Hack -- process all events */ while (CheckEvents(TRUE)) /* loop */; /* Reset the cursor */ SetCursor(&qd.arrow); /* Mega-Hack -- Allocate a "lifeboat" */ lifeboat = NewPtr(16384); /* Note the "system" */ ANGBAND_SYS = "mac"; /* Initialize */ init_stuff(); /* Initialize */ init_angband(); /* Hack -- process all events */ while (CheckEvents(TRUE)) /* loop */; /* We are now initialized */ initialized = TRUE; /* Handle "open_when_ready" */ handle_open_when_ready(); #ifndef SAVEFILE_SCREEN /* Prompt the user */ /* In [Z], it's currently prtf(17, 23, ); */ prt("[Choose 'New' or 'Open' from the 'File' menu]", 23, 15); /* Flush the prompt */ Term_fresh(); /* Hack -- Process Events Forever */ while (TRUE) CheckEvents(TRUE); #else /* Game is in progress */ game_in_progress = 1; /* Wait for keypress */ pause_line(23); /* flush input - Warning: without this, _system_ would hang */ flush(); /* Play the game - note the value of the argument */ play_game(FALSE); /* Quit */ quit(NULL); #endif /* !SAVEFILE_SCREEN */ } #endif /* MACINTOSH */