/* NetHack 3.6 mactty.c $NHDT-Date: 1432512797 2015/05/25 00:13:17 $ $NHDT-Branch: master $:$NHDT-Revision: 1.9 $ */ /* Copyright (c) Jon W{tte 1993. */ /* NetHack may be freely redistributed. See license for details. */ /* * mactty.c * * This file contains the actual code for the tty library. For a * description, see the file mactty.h, which contains all you * need to know to use the library. */ #include "hack.h" /* to get flags */ #include "mttypriv.h" #if !TARGET_API_MAC_CARBON #include #include #endif char game_active = 0; /* flag to window rendering routines not to use ppat */ /* these declarations are here because I can't include macwin.h without * including the world */ extern void dprintf(char *, ...); /* dprintf.c */ /* * Borrowed from the Mac tty port */ extern WindowPtr _mt_window; static void select_onscreen_window(tty_record *record); static void select_offscreen_port(tty_record *record); #define MEMORY_MARGIN 30000 /* * Convenience macro for most functions - put last in declaration */ #define RECORD_EXISTS(record) \ tty_record *record; \ if (!window || !(record = (tty_record *) GetWRefCon(window))) \ return general_failure; /* * Simple macro for deciding whether we draw at once or delay */ #define DRAW_DIRECT (TA_ALWAYS_REFRESH & record->attribute[TTY_ATTRIB_FLAGS]) /* * Table of special characters. Zero is ALWAYS special; it means * end of string and would be MISSED if it was not included here. */ #define COOKED_CONTROLS 0X00002581 #define RAW_CONTROLS 1 static unsigned long s_control = COOKED_CONTROLS; /* * Memory-related error */ static short mem_err(void) { short ret_val = MemError(); if (!ret_val) { ret_val = general_failure; } return ret_val; } /* * Make a rectangle empty */ static void empty_rect(Rect *r) { r->right = -20000; r->left = 20000; r->top = 20000; r->bottom = -20000; } /* * Union twp rect together */ static void union_rect(Rect *r1, Rect *r2, Rect *dest) { dest->left = min(r1->left, r2->left); dest->top = min(r1->top, r2->top); dest->bottom = max(r1->bottom, r2->bottom); dest->right = max(r1->right, r2->right); } /* * Dispose a pointer using the set memory-allocator */ static short dispose_ptr(void *ptr) { if (!ptr) { return noErr; /* Silently accept disposing nulls */ } DisposePtr(ptr); return MemError(); } #if 0 /* Use alloc.c instead */ /* * Allocate a pointer using the set memory-allocator */ static short alloc_ptr (void **ptr, long size) { *ptr = NewPtr (size); return MemError (); } #endif /* * Set up a GWorld in the record */ static short allocate_offscreen_world(tty_record *record) { GWorldPtr gw = (GWorldPtr) 0; GWorldFlags world_flags = 0; long mem_here, mem_there, other, required_mem; Point p = { 0, 0 }; Rect r_screen; GDHandle gdh; short s_err; select_onscreen_window(record); LocalToGlobal(&p); r_screen = record->its_bits.bounds; OffsetRect(&r_screen, p.h, p.v); gdh = GetMaxDevice(&r_screen); required_mem = (long) (*((*gdh)->gdPMap))->pixelSize * ((long) record->its_bits.bounds.right * record->its_bits.bounds.bottom) >> 3; PurgeSpace(&other, &mem_here); if (other < mem_here + MEMORY_MARGIN) { mem_here = other - MEMORY_MARGIN; } dprintf("Heap %ld Required %ld", mem_here, required_mem); if (required_mem > mem_here) { mem_there = required_mem; if (required_mem > TempMaxMem(&mem_there)) { dprintf("No memory"); return memFullErr; } world_flags |= useTempMem; } s_err = NewGWorld(&gw, 0, &r_screen, (CTabHandle) 0, (GDHandle) 0, world_flags); if (!s_err) { record->offscreen_world = gw; select_offscreen_port(record); SetOrigin(0, 0); select_onscreen_window(record); dprintf("New GWorld @ %lx;dm", gw); } return s_err; } /* * Done with GWorld, release data */ static short deallocate_gworld(tty_record *record) { if (record->offscreen_world) { DisposeGWorld(record->offscreen_world); record->offscreen_world = (GWorldPtr) 0; } return noErr; } /* * Get rid of offscreen bitmap */ static short free_bits(tty_record *record) { short s_err; if (record->uses_gworld) { s_err = deallocate_gworld(record); #if !TARGET_API_MAC_CARBON } else { s_err = dispose_ptr(record->its_bits.baseAddr); if (!s_err) { record->its_bits.baseAddr = (char *) 0; if (record->offscreen_port) { ClosePort(record->offscreen_port); s_err = dispose_ptr(record->offscreen_port); if (!s_err) { record->offscreen_port = (GrafPtr) 0; } } } #endif } return s_err; } /* * Snatch a window from the resource fork. Create the record. * Otherwise, do nothing. */ short create_tty(WindowRef *window, short resource_id, Boolean in_color) { tty_record *record; Boolean was_allocated = !!*window; if (in_color) { *window = GetNewCWindow(resource_id, (Ptr) *window, (WindowRef) -1L); } else { *window = GetNewWindow(resource_id, (Ptr) *window, (WindowRef) -1L); } if (!*window) { return mem_err(); } record = (tty_record *) NewPtrClear(sizeof(tty_record)); if (!record) { #if !TARGET_API_MAC_CARBON if (was_allocated) { CloseWindow(*window); } else { #endif DisposeWindow(*window); #if !TARGET_API_MAC_CARBON } #endif return mem_err(); } record->its_window = *window; SetWRefCon(*window, (long) record); record->was_allocated = was_allocated; record->its_bits.baseAddr = (char *) 0; record->curs_state = TRUE; /* * We need to keep the window world around if we switch worlds */ record->offscreen_world = (GWorldPtr) 0; record->uses_gworld = in_color; if (in_color) { GDHandle gh; SetPortWindowPort(*window); GetGWorld(&(record->its_window_world), &gh); } else { record->its_window_world = (GWorldPtr) 0; } #if CLIP_RECT_ONLY empty_rect(&(record->invalid_rect)); #else record->invalid_part = NewRgn(); if (!record->invalid_part) { return destroy_tty(*window); } #endif return noErr; } short init_tty_number(WindowPtr window, short font_number, short font_size, short x_size, short y_size) { RECORD_EXISTS(record); record->font_number = font_number; record->font_size = font_size; record->x_size = x_size; record->y_size = y_size; return force_tty_coordinate_system_recalc(window); } /* * Done with a window - destroy it. Release the memory only if * it wasn't allocated when we got it! */ short destroy_tty(WindowPtr window) { short s_err; RECORD_EXISTS(record); s_err = free_bits(record); if (!s_err) { #if !TARGET_API_MAC_CARBON if (record->was_allocated) { CloseWindow(window); } else { #endif DisposeWindow(window); #if !TARGET_API_MAC_CARBON } #endif s_err = dispose_ptr(record); } return s_err; } static void do_set_port_font(tty_record *record) { PenNormal(); TextFont(record->font_number); TextSize(record->font_size); if (0L != (record->attribute[TTY_ATTRIB_FLAGS] & TA_OVERSTRIKE)) { TextMode(srcOr); } else { TextMode(srcCopy); } } void tty_nhbell(void) { Handle h = GetNamedResource('snd ', "\pNetHack Bell"); if (h) { HLock(h); SndPlay((SndChannelPtr) 0, (SndListHandle) h, 0); ReleaseResource(h); } else SysBeep(30); } /* * Fill in some fields from some other fields that may have changed */ static void calc_font_sizes(tty_record *record) { FontInfo font_info; do_set_port_font(record); GetFontInfo(&font_info); record->char_width = font_info.widMax; record->ascent_height = font_info.ascent + font_info.leading; record->row_height = record->ascent_height + font_info.descent; } /* * Allocate memory for the bitmap holding the tty window */ static short alloc_bits(tty_record *record) { short s_err; SetRect(&record->its_bits.bounds, 0, 0, record->char_width * record->x_size, record->row_height * record->y_size); /* * Clear two highest and lowest bit - not a color pixMap, and even in size */ record->its_bits.rowBytes = ((record->its_bits.bounds.right + 15) >> 3) & 0x1ffe; if (record->uses_gworld) { s_err = allocate_offscreen_world(record); #if !TARGET_API_MAC_CARBON } else { s_err = alloc_ptr((void **) &(record->its_bits.baseAddr), record->its_bits.rowBytes * record->its_bits.bounds.bottom); if (!s_err) { s_err = alloc_ptr((void **) &(record->offscreen_port), sizeof(GrafPort)); } if (!s_err) { OpenPort(record->offscreen_port); SetPort(record->offscreen_port); ClipRect(&(record->its_bits.bounds)); SetPortBits(&(record->its_bits)); } #endif } return s_err; } /* * Save the current port/world in a safe place for later retrieval */ static void save_port(tty_record *record, void *save) { GWorldPtr gw; GDHandle gh; GrafPtr gp; if (record->uses_gworld) { GetGWorld(&gw, &gh); *(GWorldPtr *) save = gw; } else { GetPort(&gp); *(GrafPtr *) save = gp; } } /* * Restore current port/world after a save */ static void use_port(tty_record *record, void *port) { if (record->uses_gworld) { PixMapHandle pix_map; SetGWorld((GWorldPtr) port, (GDHandle) 0); pix_map = GetGWorldPixMap(record->offscreen_world); if (pix_map) { if (port == record->offscreen_world) LockPixels(pix_map); else UnlockPixels(pix_map); } } else { SetPort((GrafPtr) port); } } /* * Use offscreen drawing - lock the pixels through use_port */ static void select_offscreen_port(tty_record *record) { if (record->uses_gworld) { use_port(record, record->offscreen_world); } else { use_port(record, record->offscreen_port); } } /* * Use the window - unlock pixels */ static void select_onscreen_window(tty_record *record) { if (record->uses_gworld) { use_port(record, record->its_window_world); SetPortWindowPort(record->its_window); } else { use_port(record, record->its_window); } } /* * Do bits copy depending on if we're using color or not */ static void copy_bits(tty_record *record, Rect *bounds, short xfer_mode, RgnHandle mask_rgn) { GWorldFlags pix_state; BitMap *source; if (record->uses_gworld) { pix_state = GetPixelsState(GetGWorldPixMap(record->offscreen_world)); LockPixels(GetGWorldPixMap(record->offscreen_world)); source = (BitMapPtr) *GetGWorldPixMap(record->offscreen_world); } else source = &record->its_bits; SetPortWindowPort(record->its_window); CopyBits(source, GetPortBitMapForCopyBits(GetWindowPort(record->its_window)), bounds, bounds, xfer_mode, mask_rgn); if (record->uses_gworld) { SetPixelsState(GetGWorldPixMap(record->offscreen_world), pix_state); } } /* * Fill an area with the background color */ static void erase_rect(tty_record *record, Rect *area) { if (game_active && u.uhp > 0 && iflags.use_stone && record->its_window == _mt_window) { PixPatHandle ppat; ppat = GetPixPat(iflags.use_stone + 127); /* find which pat to get */ if (ppat) { /* in game window, using backgroung pattern, and have pattern */ FillCRect(area, ppat); DisposePixPat(ppat); return; } } EraseRect(area); } /* * Recalculate the window based on new size, font, extent values, * and re-allocate the bitmap. */ short force_tty_coordinate_system_recalc(WindowPtr window) { short s_err; RECORD_EXISTS(record); s_err = free_bits(record); if (s_err) { return s_err; } calc_font_sizes(record); s_err = alloc_bits(record); if (s_err) { /* * Catastrophe! We could not allocate memory for the bitmap! Things * may go very * much downhill from here! */ dprintf("alloc_bits returned null in " "force_tty_coordinate_system_recalc!"); return s_err; } select_offscreen_port(record); do_set_port_font(record); return clear_tty(window); } #if 0 /* * Update TTY according to new color environment for the window */ static short tty_environment_changed (tty_record *record) { Point p = {0, 0}; Rect r_screen; if (record->uses_gworld) { r_screen = record->its_bits.bounds; LocalToGlobal (&p); OffsetRect (&r_screen, p.h, p.v); UpdateGWorld (&(record->offscreen_world), 0, &r_screen, (CTabHandle) 0, (GDHandle) 0, stretchPix); select_offscreen_port (record); SetOrigin (0, 0); select_onscreen_window (record); } return 0; } #endif /* * Read a lot of interesting and useful information from the current tty */ short get_tty_metrics(WindowPtr window, short *x_size, short *y_size, short *x_size_pixels, short *y_size_pixels, short *font_number, short *font_size, short *char_width, short *row_height) { RECORD_EXISTS(record); /* * First, test that we actually have something to draw to... */ if ((((char *) 0 == record->its_bits.baseAddr) && !record->uses_gworld) || (((GWorldPtr) 0 == record->offscreen_world) && record->uses_gworld)) { return general_failure; } *x_size = record->x_size; *y_size = record->y_size; *x_size_pixels = record->its_bits.bounds.right; *y_size_pixels = record->its_bits.bounds.bottom; *font_number = record->font_number; *font_size = record->font_size; *char_width = record->char_width; *row_height = record->row_height; return noErr; } /* * Map a position on the map to screen coordinates */ static void pos_rect(tty_record *record, Rect *r, short x_pos, short y_pos, short x_end, short y_end) { SetRect(r, x_pos * (record->char_width), y_pos * (record->row_height), (1 + x_end) * (record->char_width), (1 + y_end) * (record->row_height)); } static void accumulate_rect(tty_record *record, Rect *rect) { #if CLIP_RECT_ONLY union_rect(rect, &(record->invalid_rect), &(record->invalid_rect)); #else RgnHandle rh = NewRgn(); RectRgn(rh, rect); UnionRgn(record->invalid_part, rh, record->invalid_part); DisposeRgn(rh); #endif } /* * get and set window invalid region. exposed for HandleUpdateEvent in * macwin.c * to correct display problem */ short get_invalid_region(WindowPtr window, Rect *inval_rect) { RECORD_EXISTS(record); #if CLIP_RECT_ONLY if (record->invalid_rect.right <= record->invalid_rect.left || record->invalid_rect.bottom <= record->invalid_rect.top) { return general_failure; } *inval_rect = record->invalid_rect; #else if (EmptyRgn(record->invalid_part)) { return general_failure; } *inval_rect = (*(record->invalid_part))->rgnBBox; #endif return noErr; } short set_invalid_region(WindowPtr window, Rect *inval_rect) { RECORD_EXISTS(record); accumulate_rect(record, inval_rect); return noErr; } /* * Invert the specified position */ static void curs_pos(tty_record *record, short x_pos, short y_pos, short to_state) { Rect r; if (record->curs_state == to_state) { return; } record->curs_state = to_state; pos_rect(record, &r, x_pos, y_pos, x_pos, y_pos); if (DRAW_DIRECT) { void *old_port; save_port(record, &old_port); select_onscreen_window(record); InvertRect(&r); use_port(record, old_port); } else { accumulate_rect(record, &r); } } /* * Move the cursor (both as displayed and where drawing goes) * HOWEVER: The cursor is NOT stored in the bitmap! */ short move_tty_cursor(WindowPtr window, short x_pos, short y_pos) { RECORD_EXISTS(record); if (record->x_curs == x_pos && record->y_curs == y_pos) { return noErr; } if (record->x_size <= x_pos || x_pos < 0 || record->y_size <= y_pos || y_pos < 0) { return general_failure; } curs_pos(record, record->x_curs, record->y_curs, 0); record->x_curs = x_pos; record->y_curs = y_pos; curs_pos(record, x_pos, y_pos, 1); return noErr; } /* * Update the screen to match the current bitmap, after adding stuff * with add_tty_char etc. */ short update_tty(WindowPtr window) { Rect r; RECORD_EXISTS(record); #if CLIP_RECT_ONLY if (record->invalid_rect.right <= record->invalid_rect.left || record->invalid_rect.bottom <= record->invalid_rect.top) { return noErr; } r = record->invalid_rect; #else if (EmptyRgn(record->invalid_part)) { return noErr; } r = (*(record->invalid_part))->rgnBBox; #endif select_onscreen_window(record); copy_bits(record, &r, srcCopy, (RgnHandle) 0); #if CLIP_RECT_ONLY empty_rect(&(record->invalid_rect)); #else SetEmptyRgn(record->invalid_part); #endif if (record->curs_state) { pos_rect(record, &r, record->x_curs, record->y_curs, record->x_curs, record->y_curs); InvertRect(&r); } return noErr; } /* * Low level add to screen */ static void do_add_string(tty_record *record, char *str, short len) { Rect r; if (len < 1) { return; } select_offscreen_port(record); MoveTo(record->x_curs * record->char_width, record->y_curs * record->row_height + record->ascent_height); DrawText(str, 0, len); pos_rect(record, &r, record->x_curs, record->y_curs, record->x_curs + len - 1, record->y_curs); select_onscreen_window(record); if (DRAW_DIRECT) { copy_bits(record, &r, srcCopy, (RgnHandle) 0); } else { accumulate_rect(record, &r); } } /* * Low-level cursor handling routine */ static void do_add_cursor(tty_record *record, short x_pos) { record->x_curs = x_pos; if (record->x_curs >= record->x_size) { if (0L != (record->attribute[TTY_ATTRIB_FLAGS] & TA_WRAP_AROUND)) { record->y_curs++; record->x_curs = 0; if (record->y_curs >= record->y_size) { if (0L != (record->attribute[TTY_ATTRIB_FLAGS] & TA_INHIBIT_VERT_SCROLL)) { record->y_curs = record->y_size; } else { scroll_tty(record->its_window, 0, 1 + record->y_curs - record->y_size); } } } else { record->x_curs = record->x_size; } } } /* * Do control character */ static void do_control(tty_record *record, short character) { int recurse = 0; /* * Check recursion because nl_add_cr and cr_add_nl may both be set and * invoke each other */ do { switch (character) { case CHAR_CR: record->x_curs = 0; if (!recurse && (record->attribute[TTY_ATTRIB_CURSOR] & TA_CR_ADD_NL)) { recurse = 1; } else { recurse = 0; break; } /* FALL-THROUGH: if CR-LF, don't bother with loop */ case CHAR_LF: record->y_curs++; if (record->y_curs >= record->y_size) { scroll_tty(record->its_window, 0, 1 + record->y_curs - record->y_size); } if (!recurse && (record->attribute[TTY_ATTRIB_CURSOR] & TA_NL_ADD_CR)) { character = CHAR_CR; recurse = 1; } else recurse = 0; break; case CHAR_BELL: tty_nhbell(); break; case CHAR_BS: if (record->x_curs > 0) record->x_curs--; default: break; } } while (recurse); } /* * Add a single character. It is drawn directly if the correct flag is set, * else deferred to the next update event or call of update_tty() */ short add_tty_char(WindowPtr window, short character) { register char is_control; char ch; RECORD_EXISTS(record); if (!(record->attribute[TTY_ATTRIB_FLAGS] & TA_WRAP_AROUND) && record->x_curs >= record->x_size) return noErr; /* Optimize away drawing across border without wrap */ if (record->curs_state != 0) curs_pos(record, record->x_curs, record->y_curs, 0); ch = character; is_control = (ch < sizeof(long) * 8) && ((s_control & (1 << ch)) != 0L); if (is_control) do_control(record, ch); else { do_add_string(record, (char *) &ch, 1); do_add_cursor(record, record->x_curs + 1); } return noErr; } /* * Add a null-terminated string of characters */ short add_tty_string(WindowPtr window, const char *string) { register const unsigned char *start_c; register const unsigned char *the_c; register unsigned char ch, is_control = 0, tty_wrap; register short max_x, pos_x; RECORD_EXISTS(record); if (record->curs_state != 0) curs_pos(record, record->x_curs, record->y_curs, 0); the_c = (const unsigned char *) string; max_x = record->x_size; tty_wrap = (record->attribute[TTY_ATTRIB_FLAGS] & TA_WRAP_AROUND); for (;;) { pos_x = record->x_curs; if (!tty_wrap && pos_x >= max_x) break; /* Optimize away drawing across border without wrap */ start_c = the_c; ch = *the_c; while (pos_x < max_x) { is_control = (ch < sizeof(long) * 8) && ((s_control & (1 << ch)) != 0L); if (is_control) break; the_c++; ch = *the_c; pos_x++; } do_add_string(record, (char *) start_c, the_c - start_c); do_add_cursor(record, pos_x); if (!ch) break; if (is_control) { do_control(record, ch); the_c++; } } return noErr; } /* * Read or change attributes for the tty. Note that some attribs may * very well clear and reallocate the bitmap when changed, whereas * others (color, highlight, ...) are guaranteed not to. */ short get_tty_attrib(WindowPtr window, tty_attrib attrib, long *value) { RECORD_EXISTS(record); if (attrib < 0 || attrib >= TTY_NUMBER_ATTRIBUTES) { return general_failure; } *value = record->attribute[attrib]; return noErr; } short set_tty_attrib(WindowPtr window, tty_attrib attrib, long value) { RGBColor rgb_color; RECORD_EXISTS(record); if (attrib < 0 || attrib >= TTY_NUMBER_ATTRIBUTES) { return general_failure; } record->attribute[attrib] = value; /* * Presently, no attributes generate a new bitmap. */ switch (attrib) { case TTY_ATTRIB_CURSOR: /* * Check if we should change tables */ if (0L != (value & TA_RAW_OUTPUT)) { s_control = RAW_CONTROLS; } else { s_control = COOKED_CONTROLS; } break; case TTY_ATTRIB_FLAGS: /* * Check if we should flush the output going from cached to * draw-direct */ if (0L != (value & TA_ALWAYS_REFRESH)) { update_tty(window); } break; case TTY_ATTRIB_FOREGROUND: /* * Set foreground color */ TA_TO_RGB(value, rgb_color); select_offscreen_port(record); RGBForeColor(&rgb_color); select_onscreen_window(record); break; case TTY_ATTRIB_BACKGROUND: /* * Set background color */ TA_TO_RGB(value, rgb_color); select_offscreen_port(record); RGBBackColor(&rgb_color); select_onscreen_window(record); break; default: break; } return noErr; } /* * Scroll the window. Positive is up/left. scroll_tty ( window, 0, 1 ) is a * line feed. * Scroll flushes the accumulated update area by calling update_tty(). */ short scroll_tty(WindowPtr window, short delta_x, short delta_y) { RgnHandle rgn; short s_err; RECORD_EXISTS(record); s_err = update_tty(window); rgn = NewRgn(); select_offscreen_port(record); ScrollRect(&(record->its_bits.bounds), -delta_x * record->char_width, -delta_y * record->row_height, rgn); EraseRgn(rgn); SetEmptyRgn(rgn); select_onscreen_window(record); ScrollRect(&(record->its_bits.bounds), -delta_x * record->char_width, -delta_y * record->row_height, rgn); EraseRgn(rgn); DisposeRgn(rgn); record->y_curs -= delta_y; record->x_curs -= delta_x; return noErr; } /* * Clear the screen. Immediate. */ short clear_tty(WindowPtr window) { RECORD_EXISTS(record); record->curs_state = 0; select_offscreen_port(record); erase_rect(record, &(record->its_bits.bounds)); accumulate_rect(record, &(record->its_bits.bounds)); update_tty(window); return noErr; } /* * Blink cursor on window if necessary */ short blink_cursor(WindowPtr window, long when) { RECORD_EXISTS(record); if ((record->attribute[TTY_ATTRIB_CURSOR] & TA_BLINKING_CURSOR)) { if (when > record->last_cursor + GetCaretTime()) { curs_pos(record, record->x_curs, record->y_curs, !record->curs_state); record->last_cursor = when; update_tty(window); } } return 0; } /* * Draw an image of the tty - used for update events and can be called * for screen dumps. */ short image_tty(EventRecord *theEvent, WindowPtr window) { #if defined(__SC__) || defined(__MRC__) #pragma unused(theEvent) #endif RECORD_EXISTS(record); #if CLIP_RECT_ONLY record->invalid_rect = record->its_bits.bounds; #else RgnHandle rh = NewRgn(); RectRgn(rh, record->its_bits.bounds); UnionRgn(record->invalid_part, rh, record->invalid_part); DisposeRgn(rh); #endif return update_tty(window); } /* * Clear an area */ short clear_tty_window(WindowPtr window, short from_x, short from_y, short to_x, short to_y) { Rect r; RECORD_EXISTS(record); if (from_x > to_x || from_y > to_y) { return general_failure; } pos_rect(record, &r, from_x, from_y, to_x, to_y); select_offscreen_port(record); erase_rect(record, &r); accumulate_rect(record, &r); if (DRAW_DIRECT) { update_tty(window); } else select_onscreen_window(record); return noErr; } #if EXTENDED_SUPPORT /* * Delete or insert operations used by many terminals can bottleneck through * here. Note that the order of executin for row/colum insertions is NOT * specified. Negative values for num_ mean delete, zero means no effect. */ short mangle_tty_rows_columns(WindowPtr window, short from_row, short num_rows, short from_column, short num_columns) { Rect r; RgnHandle rh = NewRgn(); RECORD_EXISTS(record); update_tty(window); /* Always make sure screen is OK */ curs_pos(record, record->x_curs, record->y_curs, 0); if (num_rows) { pos_rect(record, &r, 0, from_row, record->x_size - 1, record->y_size - 1); select_offscreen_port(record); ScrollRect(&r, 0, num_rows * record->row_height, rh); EraseRgn(rh); SetEmptyRgn(rh); select_onscreen_window(record); ScrollRect(&r, 0, num_rows * record->row_height, rh); EraseRgn(rh); SetEmptyRgn(rh); } if (num_columns) { pos_rect(record, &r, from_column, 0, record->x_size - 1, record->y_size - 1); select_offscreen_port(record); ScrollRect(&r, num_columns * record->char_width, 0, rh); EraseRgn(rh); SetEmptyRgn(rh); select_onscreen_window(record); ScrollRect(&r, num_columns * record->char_width, 0, rh); EraseRgn(rh); SetEmptyRgn(rh); } DisposeRgn(rh); if (record->x_curs >= from_column) { record->x_curs += num_columns; } if (record->y_curs >= from_row) { record->y_curs += num_rows; } curs_pos(record, record->x_curs, record->y_curs, 1); return noErr; } /* * Frame an area in an aesthetically pleasing way. */ short frame_tty_window(WindowPtr window, short from_x, short from_y, short to_x, short to_y, short frame_fatness) { Rect r; RECORD_EXISTS(record); if (from_x > to_x || from_y > to_y) { return general_failure; } pos_rect(record, &r, from_x, from_y, to_x, to_y); select_offscreen_port(record); PenSize(frame_fatness, frame_fatness); FrameRect(&r); PenNormal(); accumulate_rect(record, &r); if (DRAW_DIRECT) { update_tty(window); } else select_onscreen_window(record); } /* * Highlighting a specific part of the tty window */ short invert_tty_window(WindowPtr window, short from_x, short from_y, short to_x, short to_y) { Rect r; RECORD_EXISTS(record); if (from_x > to_x || from_y > to_y) { return general_failure; } pos_rect(record, &r, from_x, from_y, to_x, to_y); select_offscreen_port(record); InvertRect(&r); accumulate_rect(record, &r); if (DRAW_DIRECT) { update_tty(window); } else select_onscreen_window(record); } static void canonical_rect(Rect *r, short x1, short y1, short x2, short y2) { if (x1 < x2) { if (y1 < y2) { SetRect(r, x1, x2, y1, y2); } else { SetRect(r, x1, x2, y2, y1); } } else { if (y1 < y2) { SetRect(r, x2, x1, y1, y2); } else { SetRect(r, x2, x1, y2, y1); } } } /* * Line drawing - very device dependent */ short draw_tty_line(WindowPtr window, short from_x, short from_y, short to_x, short to_y) { Rect r; RECORD_EXISTS(record); select_offscreen_port(record); MoveTo(from_x, from_y); LineTo(to_x, to_y); canonical_rect(&r, from_x, from_y, to_x, to_y); accumulate_rect(record, &r); if (DRAW_DIRECT) { update_tty(window); } else select_onscreen_window(record); } #endif /* EXTENDED_SUPPORT */