1 /*	SCCS Id: @(#)mactty.c	3.1	93/03/01			*/
2 /* Copyright (c) Jon W{tte 1993.					*/
3 /* NetHack may be freely redistributed.  See license for details.	*/
4 
5 /*
6  * mactty.c
7  *
8  * This file contains the actual code for the tty library. For a
9  * description, see the file mactty.h, which contains all you
10  * need to know to use the library.
11  */
12 
13 #include "hack.h"	/* to get flags */
14 #include "mttypriv.h"
15 #if !TARGET_API_MAC_CARBON
16 #include <Resources.h>
17 #endif
18 
19 char game_active = 0;	/* flag to window rendering routines not to use ppat */
20 
21 /* these declarations are here because I can't include macwin.h without including the world */
22 extern void dprintf(char *, ...);	/* dprintf.c */
23 
24 /*
25  * Borrowed from the Mac tty port
26  */
27 extern WindowPtr _mt_window;
28 
29 static void select_onscreen_window (tty_record *record);
30 static void select_offscreen_port (tty_record *record);
31 
32 #define MEMORY_MARGIN 30000
33 
34 /*
35  * Convenience macro for most functions - put last in declaration
36  */
37 #define RECORD_EXISTS(record) \
38 	tty_record * record; \
39 	if (!window || !(record = (tty_record *) GetWRefCon (window))) \
40 		return general_failure;
41 
42 
43 /*
44  * Simple macro for deciding wether we draw at once or delay
45  */
46 #define DRAW_DIRECT (TA_ALWAYS_REFRESH & record->attribute [TTY_ATTRIB_FLAGS])
47 
48 
49 /*
50  * Table of special characters. Zero is ALWAYS special; it means
51  * end of string and would be MISSED if it was not included here.
52  */
53 #define COOKED_CONTROLS 0X00002581
54 #define RAW_CONTROLS 1
55 static unsigned long s_control = COOKED_CONTROLS;
56 
57 
58 /*
59  * Memory-related error
60  */
61 static short
mem_err(void)62 mem_err (void) {
63 	short ret_val = MemError();
64 	if (!ret_val) {
65 		ret_val = general_failure;
66 	}
67 	return ret_val;
68 }
69 
70 
71 /*
72  * Make a rectangle empty
73  */
74 static void
empty_rect(Rect * r)75 empty_rect (Rect *r) {
76 	r->right = -20000;
77 	r->left = 20000;
78 	r->top = 20000;
79 	r->bottom = -20000;
80 }
81 
82 
83 /*
84  * Union twp rect together
85  */
86 static void
union_rect(Rect * r1,Rect * r2,Rect * dest)87 union_rect (Rect *r1, Rect *r2, Rect *dest) {
88 	dest->left = min (r1->left, r2->left);
89 	dest->top = min (r1->top, r2 ->top);
90 	dest->bottom = max (r1->bottom, r2->bottom);
91 	dest->right = max (r1->right, r2->right);
92 }
93 
94 
95 /*
96  * Dispose a pointer using the set memory-allocator
97  */
98 static short
dispose_ptr(void * ptr)99 dispose_ptr (void *ptr) {
100 	if (!ptr) {
101 		return noErr; /* Silently accept disposing nulls */
102 	}
103 	DisposePtr (ptr);
104 	return MemError ();
105 }
106 
107 
108 #ifdef MAC_MPW	/* else use alloc.c instead */
109 /*
110  * Allocate a pointer using the set memory-allocator
111  */
112 static short
alloc_ptr(void ** ptr,long size)113 alloc_ptr (void **ptr, long size) {
114 	*ptr = NewPtr (size);
115 	return MemError ();
116 }
117 #endif
118 
119 
120 /*
121  * Set up a GWorld in the record
122  */
123 static short
allocate_offscreen_world(tty_record * record)124 allocate_offscreen_world (tty_record *record) {
125 GWorldPtr gw = (GWorldPtr)0;
126 GWorldFlags world_flags = 0;
127 long mem_here, mem_there, other, required_mem;
128 Point p = {0, 0};
129 Rect r_screen;
130 GDHandle gdh;
131 short s_err;
132 
133 	select_onscreen_window (record);
134 	LocalToGlobal (&p);
135 	r_screen = record->its_bits.bounds;
136 	OffsetRect (&r_screen, p.h, p.v);
137 
138 	gdh = GetMaxDevice (&r_screen);
139 	required_mem = (long) (*((*gdh)->gdPMap))->pixelSize *
140 		((long) record->its_bits.bounds.right *
141 		record->its_bits.bounds.bottom) >> 3;
142 
143 	PurgeSpace (&other, &mem_here);
144 	if (other < mem_here + MEMORY_MARGIN) {
145 		mem_here = other - MEMORY_MARGIN;
146 	}
147 	dprintf ("Heap %ld Required %ld", mem_here, required_mem);
148 	if (required_mem > mem_here) {
149 		mem_there = required_mem;
150 		if (required_mem > TempMaxMem (&mem_there)) {
151 			dprintf ("No memory");
152 			return memFullErr;
153 		}
154 		world_flags |= useTempMem;
155 	}
156 	s_err = NewGWorld (&gw, 0, &r_screen, (CTabHandle) 0, (GDHandle) 0, world_flags);
157 	if (!s_err) {
158 		record->offscreen_world = gw;
159 		select_offscreen_port (record);
160 		SetOrigin (0, 0);
161 		select_onscreen_window (record);
162 		dprintf ("New GWorld @ %lx;dm", gw);
163 	}
164 	return s_err;
165 }
166 
167 
168 /*
169  * Done with GWorld, release data
170  */
171 static short
deallocate_gworld(tty_record * record)172 deallocate_gworld (tty_record *record) {
173 	if (record->offscreen_world) {
174 		DisposeGWorld (record->offscreen_world);
175 		record->offscreen_world = (GWorldPtr) 0;
176 	}
177 	return noErr;
178 }
179 
180 
181 
182 
183 /*
184  * Get rid of offscreen bitmap
185  */
186 static short
free_bits(tty_record * record)187 free_bits (tty_record *record) {
188 	short s_err;
189 
190 	if (record->uses_gworld) {
191 		s_err = deallocate_gworld (record);
192 #if !TARGET_API_MAC_CARBON
193 	} else {
194 		s_err = dispose_ptr (record->its_bits.baseAddr);
195 		if (!s_err) {
196 			record->its_bits.baseAddr = (char *)0;
197 			if (record->offscreen_port) {
198 				ClosePort (record->offscreen_port);
199 				s_err = dispose_ptr (record->offscreen_port);
200 				if (!s_err) {
201 					record->offscreen_port = (GrafPtr) 0;
202 				}
203 			}
204 		}
205 #endif
206 	}
207 	return s_err;
208 }
209 
210 
211 /*
212  * Snatch a window from the resource fork. Create the record.
213  * Otherwise, do nothing.
214  */
215 
216 short
create_tty(WindowRef * window,short resource_id,Boolean in_color)217 create_tty (WindowRef *window, short resource_id, Boolean in_color)
218 {
219 	tty_record * record;
220 	Boolean was_allocated = !!*window;
221 
222 	if (in_color) {
223 		*window = GetNewCWindow (resource_id, (Ptr) *window, (WindowRef) -1L);
224 	} else {
225 		*window = GetNewWindow (resource_id, (Ptr) *window, (WindowRef) -1L);
226 	}
227 	if (!*window) {
228 		return mem_err ();
229 	}
230 
231 	record = (tty_record *) NewPtrClear (sizeof (tty_record));
232 	if (!record) {
233 #if !TARGET_API_MAC_CARBON
234 		if (was_allocated) {
235 			CloseWindow (*window);
236 		} else {
237 #endif
238 			DisposeWindow (*window);
239 #if !TARGET_API_MAC_CARBON
240 		}
241 #endif
242 		return mem_err ();
243 	}
244 	record->its_window = *window;
245 	SetWRefCon (*window, (long) record);
246 	record->was_allocated = was_allocated;
247 	record->its_bits.baseAddr = (char *)0;
248 	record->curs_state = TRUE;
249 
250 /*
251  * We need to keep the window world around if we switch worlds
252  */
253 	record->offscreen_world = (GWorldPtr) 0;
254 	record->uses_gworld = in_color;
255 	if (in_color) {
256 	GDHandle gh;
257 
258 		SetPortWindowPort(*window);
259 		GetGWorld(&(record->its_window_world), &gh);
260 	} else {
261 		record->its_window_world = (GWorldPtr)0;
262 	}
263 
264 #if CLIP_RECT_ONLY
265 	empty_rect (&(record->invalid_rect));
266 #else
267 	record->invalid_part = NewRgn ();
268 	if (!record->invalid_part) {
269 		return destroy_tty (*window);
270 	}
271 #endif
272 
273 	return noErr;
274 }
275 
276 
277 short
init_tty_number(WindowPtr window,short font_number,short font_size,short x_size,short y_size)278 init_tty_number (WindowPtr window, short font_number, short font_size,
279 	short x_size, short y_size) {
280 RECORD_EXISTS (record);
281 
282 	record->font_number = font_number;
283 	record->font_size = font_size;
284 	record->x_size = x_size;
285 	record->y_size = y_size;
286 
287 	return force_tty_coordinate_system_recalc (window);
288 }
289 
290 
291 /*
292  * Done with a window - destroy it. Release the memory only if
293  * it wasn't allocated when we got it!
294  */
295 short
destroy_tty(WindowPtr window)296 destroy_tty (WindowPtr window) {
297 short s_err;
298 RECORD_EXISTS (record);
299 
300 	s_err = free_bits (record);
301 	if (!s_err) {
302 #if !TARGET_API_MAC_CARBON
303 		if (record->was_allocated) {
304 			CloseWindow (window);
305 		} else {
306 #endif
307 			DisposeWindow (window);
308 #if !TARGET_API_MAC_CARBON
309 		}
310 #endif
311 		s_err = dispose_ptr (record);
312 	}
313 
314 	return s_err;
315 }
316 
317 
318 static void
do_set_port_font(tty_record * record)319 do_set_port_font (tty_record *record) {
320 
321 	PenNormal ();
322 	TextFont (record->font_number);
323 	TextSize (record->font_size);
324 	if (0L != (record->attribute [TTY_ATTRIB_FLAGS] & TA_OVERSTRIKE)) {
325 		TextMode (srcOr);
326 	} else {
327 		TextMode (srcCopy);
328 	}
329 }
330 
331 
332 /*
333  * Fill in some fields from some other fields that may have changed
334  */
335 static void
calc_font_sizes(tty_record * record)336 calc_font_sizes (tty_record *record) {
337 FontInfo font_info;
338 
339 	do_set_port_font (record);
340 
341 	GetFontInfo (&font_info);
342 	record->char_width = font_info.widMax;
343 	record->ascent_height = font_info.ascent + font_info.leading;
344 	record->row_height = record->ascent_height + font_info.descent;
345 }
346 
347 
348 /*
349  * Allocate memory for the bitmap holding the tty window
350  */
351 static short
alloc_bits(tty_record * record)352 alloc_bits (tty_record *record) {
353 short s_err;
354 
355 	SetRect (&record->its_bits.bounds, 0, 0,
356 		record->char_width * record->x_size,
357 		record->row_height * record->y_size);
358 
359 /*
360  * Clear two highest and lowest bit - not a color pixMap, and even in size
361  */
362 	record->its_bits.rowBytes = ((record->its_bits.bounds.right + 15)
363 		>> 3) & 0x1ffe;
364 
365 	if (record->uses_gworld) {
366 		s_err = allocate_offscreen_world (record);
367 #if !TARGET_API_MAC_CARBON
368 	} else {
369 		s_err = alloc_ptr ((void **) &(record->its_bits.baseAddr),
370 			record->its_bits.rowBytes * record->its_bits.bounds.bottom);
371 		if (!s_err) {
372 			s_err = alloc_ptr ((void **) &(record->offscreen_port),
373 				sizeof (GrafPort));
374 		}
375 		if (!s_err) {
376 			OpenPort (record->offscreen_port);
377 			SetPort (record->offscreen_port);
378 			ClipRect (&(record->its_bits.bounds));
379 			SetPortBits (&(record->its_bits));
380 		}
381 #endif
382 	}
383 	return s_err;
384 }
385 
386 
387 /*
388  * Save the current port/world in a safe place for later retrieval
389  */
390 static void
save_port(tty_record * record,void * save)391 save_port (tty_record *record, void *save) {
392 GWorldPtr gw;
393 GDHandle gh;
394 GrafPtr gp;
395 
396 	if (record->uses_gworld) {
397 		GetGWorld (&gw, &gh);
398 		*(GWorldPtr *) save = gw;
399 	} else {
400 		GetPort (&gp);
401 		*(GrafPtr *) save = gp;
402 	}
403 }
404 
405 
406 /*
407  * Restore current port/world after a save
408  */
409 static void
use_port(tty_record * record,void * port)410 use_port (tty_record *record, void *port) {
411 	if (record->uses_gworld) {
412 		PixMapHandle pix_map;
413 
414 		SetGWorld ((GWorldPtr) port, (GDHandle) 0);
415 		pix_map = GetGWorldPixMap (record->offscreen_world);
416 		if (pix_map) {
417 			if (port == record->offscreen_world)
418 				LockPixels (pix_map);
419 			else
420 				UnlockPixels (pix_map);
421 		}
422 	} else {
423 		SetPort ((GrafPtr) port);
424 	}
425 }
426 
427 
428 /*
429  * Use offscreen drawing - lock the pixels through use_port
430  */
431 static void
select_offscreen_port(tty_record * record)432 select_offscreen_port (tty_record *record) {
433 	if (record->uses_gworld) {
434 		use_port (record, record->offscreen_world);
435 	} else {
436 		use_port (record, record->offscreen_port);
437 	}
438 }
439 
440 
441 /*
442  * Use the window - unlock pixels
443  */
444 static void
select_onscreen_window(tty_record * record)445 select_onscreen_window (tty_record *record) {
446 	if (record->uses_gworld) {
447 		use_port (record, record->its_window_world);
448 		SetPortWindowPort(record->its_window);
449 	} else {
450 		use_port(record, record->its_window);
451 	}
452 }
453 
454 
455 /*
456  * Do bits copy depending on if we're using color or not
457  */
458 static void
copy_bits(tty_record * record,Rect * bounds,short xfer_mode,RgnHandle mask_rgn)459 copy_bits(tty_record *record, Rect *bounds, short xfer_mode, RgnHandle mask_rgn)
460 {
461 	GWorldFlags pix_state;
462 	BitMap * source;
463 
464 	if (record->uses_gworld) {
465 		pix_state = GetPixelsState (GetGWorldPixMap (record->offscreen_world));
466 		LockPixels (GetGWorldPixMap (record->offscreen_world));
467 		source = (BitMapPtr) *GetGWorldPixMap(record->offscreen_world);
468 	}
469 	else	source = &record->its_bits;
470 
471 #if !TARGET_API_MAC_CARBON
472 	CopyBits (source, &(record->its_window->portBits), bounds, bounds, xfer_mode, mask_rgn);
473 #else
474 	SetPortWindowPort(record->its_window);
475 	CopyBits(source, GetPortBitMapForCopyBits(GetWindowPort(record->its_window)),
476 		bounds, bounds, xfer_mode, mask_rgn);
477 #endif
478 
479 	if (record->uses_gworld) {
480 		SetPixelsState (GetGWorldPixMap (record->offscreen_world), pix_state);
481 	}
482 }
483 
484 
485 /*
486  * Fill an area with the background color
487  */
488 static void
erase_rect(tty_record * record,Rect * area)489 erase_rect (tty_record *record, Rect *area) {
490 	if (game_active && u.uhp > 0 && iflags.use_stone && record->its_window == _mt_window) {
491 		PixPatHandle ppat;
492 
493 		ppat = GetPixPat(iflags.use_stone + 127);	/* find which pat to get */
494 		if (ppat) {	/* in game window, using backgroung pattern, and have pattern */
495 			FillCRect (area, ppat);
496 			DisposePixPat (ppat);
497 			return;
498 		}
499 	}
500 	EraseRect (area);
501 }
502 
503 
504 /*
505  * Recalculate the window based on new size, font, extent values,
506  * and re-allocate the bitmap.
507  */
508 short
force_tty_coordinate_system_recalc(WindowPtr window)509 force_tty_coordinate_system_recalc (WindowPtr window) {
510 short s_err;
511 RECORD_EXISTS (record);
512 
513 	s_err = free_bits (record);
514 	if (s_err) {
515 		return s_err;
516 	}
517 	calc_font_sizes (record);
518 
519 	s_err = alloc_bits (record);
520 	if (s_err) {
521 /*
522  * Catastrophe! We could not allocate memory for the bitmap! Things may go very
523  * much downhill from here!
524  */
525  		dprintf ("alloc_bits returned null in force_tty_coordinate_system_recalc!");
526 		return s_err;
527 	}
528 	select_offscreen_port (record);
529 	do_set_port_font (record);
530 	return clear_tty (window);
531 }
532 
533 
534 #if 0
535 /*
536  * Update TTY according to new color environment for the window
537  */
538 static short
539 tty_environment_changed (tty_record *record) {
540 Point p = {0, 0};
541 Rect r_screen;
542 
543 	if (record->uses_gworld) {
544 		r_screen = record->its_bits.bounds;
545 		LocalToGlobal (&p);
546 		OffsetRect (&r_screen, p.h, p.v);
547 		UpdateGWorld (&(record->offscreen_world), 0, &r_screen,
548 			(CTabHandle) 0, (GDHandle) 0, stretchPix);
549 		select_offscreen_port (record);
550 		SetOrigin (0, 0);
551 		select_onscreen_window (record);
552 	}
553 	return 0;
554 }
555 #endif
556 
557 
558 /*
559  * Read a lot of interesting and useful information from the current tty
560  */
561 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)562 get_tty_metrics (WindowPtr window, short *x_size, short *y_size,
563 	short *x_size_pixels, short *y_size_pixels, short *font_number,
564 	short *font_size, short *char_width, short *row_height) {
565 RECORD_EXISTS (record);
566 
567 /*
568  * First, test that we actually have something to draw to...
569  */
570 	if ((((char *)0 == record->its_bits.baseAddr) && !record->uses_gworld) ||
571 		(((GWorldPtr)0 == record->offscreen_world) && record->uses_gworld)) {
572 		return general_failure;
573 	}
574 
575 	*x_size = record->x_size;
576 	*y_size = record->y_size;
577 	*x_size_pixels = record->its_bits.bounds.right;
578 	*y_size_pixels = record->its_bits.bounds.bottom;
579 	*font_number = record->font_number;
580 	*font_size = record->font_size;
581 	*char_width = record->char_width;
582 	*row_height = record->row_height;
583 
584 	return noErr;
585 }
586 
587 
588 /*
589  * Map a position on the map to screen coordinates
590  */
591 static void
pos_rect(tty_record * record,Rect * r,short x_pos,short y_pos,short x_end,short y_end)592 pos_rect (tty_record *record, Rect *r, short x_pos, short y_pos,
593 	short x_end, short y_end) {
594 
595 	SetRect (r, x_pos * (record->char_width), y_pos * (record->row_height),
596 		(1 + x_end) * (record->char_width) , (1 + y_end) *
597 		(record->row_height));
598 }
599 
600 
601 static void
accumulate_rect(tty_record * record,Rect * rect)602 accumulate_rect (tty_record *record, Rect *rect) {
603 #if CLIP_RECT_ONLY
604 	union_rect (rect, &(record->invalid_rect), &(record->invalid_rect));
605 #else
606 RgnHandle rh = NewRgn ();
607 
608 	RectRgn (rh, rect);
609 	UnionRgn (record->invalid_part, rh, record->invalid_part);
610 	DisposeRgn (rh);
611 #endif
612 }
613 
614 
615 /*
616  * get and set window invalid region.  exposed for HandleUpdateEvent in macwin.c
617  * to correct display problem
618  */
619 
get_invalid_region(WindowPtr window,Rect * inval_rect)620 short get_invalid_region (WindowPtr window, Rect *inval_rect) {
621 	RECORD_EXISTS (record);
622 #if CLIP_RECT_ONLY
623 	if (record->invalid_rect.right <= record->invalid_rect.left ||
624 		record->invalid_rect.bottom <= record->invalid_rect.top) {
625 		return general_failure;
626 	}
627 	*inval_rect = record->invalid_rect;
628 #else
629 	if (EmptyRgn (record->invalid_part)) {
630 		return general_failure;
631 	}
632 	*inval_rect = (*(record->invalid_part))->rgnBBox;
633 #endif
634 	return noErr;
635 }
636 
set_invalid_region(WindowPtr window,Rect * inval_rect)637 short set_invalid_region (WindowPtr window, Rect *inval_rect) {
638 	RECORD_EXISTS (record);
639 	accumulate_rect (record, inval_rect);
640 	return noErr;
641 }
642 
643 
644 /*
645  * Invert the specified position
646  */
647 static void
curs_pos(tty_record * record,short x_pos,short y_pos,short to_state)648 curs_pos (tty_record *record, short x_pos, short y_pos, short to_state) {
649 Rect r;
650 
651 	if (record->curs_state == to_state) {
652 		return;
653 	}
654 	record->curs_state = to_state;
655 	pos_rect (record, &r, x_pos, y_pos, x_pos, y_pos);
656 
657 	if (DRAW_DIRECT) {
658 	void *old_port;
659 
660 		save_port (record, &old_port);
661 		select_onscreen_window (record);
662 		InvertRect (&r);
663 		use_port (record, old_port);
664 	} else {
665 		accumulate_rect (record, &r);
666 	}
667 }
668 
669 
670 /*
671  * Move the cursor (both as displayed and where drawing goes)
672  * HOWEVER: The cursor is NOT stored in the bitmap!
673  */
674 short
move_tty_cursor(WindowPtr window,short x_pos,short y_pos)675 move_tty_cursor (WindowPtr window, short x_pos, short y_pos) {
676 RECORD_EXISTS (record);
677 
678 	if (record->x_curs == x_pos && record->y_curs == y_pos) {
679 		return noErr;
680 	}
681 	if (record->x_size <= x_pos || x_pos < 0 ||
682 		record->y_size <= y_pos || y_pos < 0) {
683 		return general_failure;
684 	}
685 	curs_pos (record, record->x_curs, record->y_curs, 0);
686 	record->x_curs = x_pos;
687 	record->y_curs = y_pos;
688 	curs_pos (record, x_pos, y_pos, 1);
689 
690 	return noErr;
691 }
692 
693 
694 /*
695  * Update the screen to match the current bitmap, after adding stuff
696  * with add_tty_char etc.
697  */
698 short
update_tty(WindowPtr window)699 update_tty (WindowPtr window) {
700 Rect r;
701 RECORD_EXISTS (record);
702 
703 #if CLIP_RECT_ONLY
704 	if (record->invalid_rect.right <= record->invalid_rect.left ||
705 		record->invalid_rect.bottom <= record->invalid_rect.top) {
706 		return noErr;
707 	}
708 	r = record->invalid_rect;
709 #else
710 	if (EmptyRgn (record->invalid_part)) {
711 		return noErr;
712 	}
713 	r = (*(record->invalid_part))->rgnBBox;
714 #endif
715 	select_onscreen_window (record);
716 	copy_bits (record, &r, srcCopy, (RgnHandle) 0);
717 #if CLIP_RECT_ONLY
718 	empty_rect (&(record->invalid_rect));
719 #else
720 	SetEmptyRgn (record->invalid_part);
721 #endif
722 	if (record->curs_state) {
723 		pos_rect (record, &r, record->x_curs, record->y_curs,
724 			record->x_curs, record->y_curs);
725 		InvertRect (&r);
726 	}
727 
728 	return noErr;
729 }
730 
731 
732 /*
733  * Low level add to screen
734  */
735 static void
do_add_string(tty_record * record,char * str,short len)736 do_add_string (tty_record *record, char *str, short len) {
737 Rect r;
738 
739 	if (len < 1) {
740 		return;
741 	}
742 	select_offscreen_port (record);
743 
744 	MoveTo (record->x_curs * record->char_width, record->y_curs *
745 		record->row_height + record->ascent_height);
746 	DrawText (str, 0, len);
747 
748 	pos_rect (record, &r, record->x_curs, record->y_curs,
749 		record->x_curs + len - 1, record->y_curs);
750 	select_onscreen_window (record);
751 	if (DRAW_DIRECT) {
752 		copy_bits (record, &r, srcCopy, (RgnHandle)0);
753 	} else {
754 		accumulate_rect (record, &r);
755 	}
756 }
757 
758 
759 /*
760  * Low-level cursor handling routine
761  */
762 static void
do_add_cursor(tty_record * record,short x_pos)763 do_add_cursor (tty_record *record, short x_pos) {
764 
765 	record->x_curs = x_pos;
766 	if (record->x_curs >= record->x_size) {
767 		if (0L != (record->attribute [TTY_ATTRIB_FLAGS] & TA_WRAP_AROUND)) {
768 			record->y_curs ++;
769 			record->x_curs = 0;
770 			if (record->y_curs >= record->y_size) {
771 				if (0L != (record->attribute [TTY_ATTRIB_FLAGS] &
772 					TA_INHIBIT_VERT_SCROLL)) {
773 					record->y_curs = record->y_size;
774 				} else {
775 					scroll_tty (record->its_window, 0, 1 + record->y_curs -
776 						record->y_size);
777 				}
778 			}
779 		} else {
780 			record->x_curs = record->x_size;
781 		}
782 	}
783 }
784 
785 
786 /*
787  * Do control character
788  */
789 static void
do_control(tty_record * record,short character)790 do_control (tty_record *record, short character) {
791 	int recurse = 0;
792 
793 /*
794  * Check recursion because nl_add_cr and cr_add_nl may both be set and invoke each other
795  */
796 	do {
797 		switch (character) {
798 		case '\r' :
799 			record->x_curs = 0;
800 			if (!recurse && (record->attribute [TTY_ATTRIB_CURSOR] & TA_CR_ADD_NL)) {
801 				recurse = 1;
802 			}
803 			else {
804 				recurse = 0;
805 				break;
806 			}	/* FALL-THROUGH: if CR-LF, don't bother with loop */
807 		case '\n' :
808 			record->y_curs++;
809 			if (record->y_curs >= record->y_size) {
810 				scroll_tty (record->its_window, 0, 1 + record->y_curs - record->y_size);
811 			}
812 			if (!recurse && (record->attribute [TTY_ATTRIB_CURSOR] & TA_NL_ADD_CR)) {
813 				character = '\r';
814 				recurse = 1;
815 			}
816 			else recurse = 0;
817 			break;
818 		case CHAR_BELL :
819 			tty_nhbell();
820 			break;
821 		case CHAR_BS :
822 			if (record->x_curs > 0)
823 				record->x_curs --;
824 		default :
825 			break;
826 		}
827 	} while (recurse);
828 }
829 
830 
831 /*
832  * Add a single character. It is drawn directly if the correct flag is set,
833  * else deferred to the next update event or call of update_tty()
834  */
835 short
add_tty_char(WindowPtr window,short character)836 add_tty_char (WindowPtr window, short character) {
837 register char is_control;
838 char ch;
839 RECORD_EXISTS (record);
840 
841 	if (!(record->attribute [TTY_ATTRIB_FLAGS] & TA_WRAP_AROUND) &&
842 		record->x_curs >= record->x_size)
843 		return noErr;		/* Optimize away drawing across border without wrap */
844 
845 	if (record->curs_state != 0)
846 		curs_pos (record, record->x_curs, record->y_curs, 0);
847 
848 	ch = character;
849 	is_control = (ch < sizeof(long) * 8) && ((s_control & (1 << ch)) != 0L);
850 	if (is_control)
851 		do_control (record, ch);
852 	else {
853 		do_add_string (record, (char *)&ch, 1);
854 		do_add_cursor (record, record->x_curs + 1);
855 	}
856 
857 	return noErr;
858 }
859 
860 
861 /*
862  * Add a null-terminated string of characters
863  */
864 short
add_tty_string(WindowPtr window,const char * string)865 add_tty_string(WindowPtr window, const char *string)
866 {
867 	register const unsigned char * start_c;
868 	register const unsigned char * the_c;
869 	register unsigned char ch, is_control = 0, tty_wrap;
870 	register short max_x, pos_x;
871 	RECORD_EXISTS (record);
872 
873 	if (record->curs_state != 0)
874 		curs_pos (record, record->x_curs, record->y_curs, 0);
875 
876 	the_c = (const unsigned char *) string;
877 	max_x = record->x_size;
878 	tty_wrap = (record->attribute [TTY_ATTRIB_FLAGS] & TA_WRAP_AROUND);
879 	for (;;) {
880 		pos_x = record->x_curs;
881 		if (!tty_wrap && pos_x >= max_x)
882 			break;		/* Optimize away drawing across border without wrap */
883 
884 		start_c = the_c;
885 		ch = *the_c;
886 		while (pos_x < max_x) {
887 			is_control = (ch < sizeof(long) * 8) && ((s_control & (1 << ch)) != 0L);
888 			if (is_control)
889 				break;
890 			the_c ++; ch = *the_c;
891 			pos_x ++;
892 		}
893 		do_add_string (record, (char *) start_c, the_c - start_c);
894 		do_add_cursor (record, pos_x);
895 		if (!ch)
896 			break;
897 
898 		if (is_control) {
899 			do_control (record, ch);
900 			the_c ++;
901 		}
902 	}
903 
904 	return noErr;
905 }
906 
907 
908 /*
909  * Read or change attributes for the tty. Note that some attribs may
910  * very well clear and reallocate the bitmap when changed, whereas
911  * others (color, highlight, ...) are guaranteed not to.
912  */
get_tty_attrib(WindowPtr window,tty_attrib attrib,long * value)913 short get_tty_attrib (WindowPtr window, tty_attrib attrib, long *value) {
914 RECORD_EXISTS (record);
915 
916 	if (attrib < 0 || attrib >= TTY_NUMBER_ATTRIBUTES) {
917 		return general_failure;
918 	}
919 	*value = record->attribute [attrib];
920 
921 	return noErr;
922 }
923 
924 
set_tty_attrib(WindowPtr window,tty_attrib attrib,long value)925 short set_tty_attrib (WindowPtr window, tty_attrib attrib, long value) {
926 RGBColor rgb_color;
927 RECORD_EXISTS (record);
928 
929 	if (attrib < 0 || attrib >= TTY_NUMBER_ATTRIBUTES) {
930 		return general_failure;
931 	}
932 	record->attribute [attrib] = value;
933 	/*
934 	 * Presently, no attributes generate a new bitmap.
935 	 */
936 	switch (attrib) {
937 	case TTY_ATTRIB_CURSOR :
938 /*
939  * Check if we should change tables
940  */
941 		if (0L != (value & TA_RAW_OUTPUT)) {
942 			s_control = RAW_CONTROLS;
943 		} else {
944 			s_control = COOKED_CONTROLS;
945 		}
946 		break;
947 	case TTY_ATTRIB_FLAGS :
948 /*
949  * Check if we should flush the output going from cached to draw-direct
950  */
951 		if (0L != (value & TA_ALWAYS_REFRESH)) {
952 			update_tty (window);
953 		}
954 		break;
955 	case TTY_ATTRIB_FOREGROUND :
956 /*
957  * Set foreground color
958  */
959  		TA_TO_RGB (value, rgb_color);
960 		select_offscreen_port (record);
961 		RGBForeColor (&rgb_color);
962 		select_onscreen_window (record);
963 		break;
964 	case TTY_ATTRIB_BACKGROUND :
965 /*
966  * Set background color
967  */
968  		TA_TO_RGB (value, rgb_color);
969 		select_offscreen_port (record);
970 		RGBBackColor (&rgb_color);
971 		select_onscreen_window (record);
972 		break;
973 	default :
974 		break;
975 	}
976 	return noErr;
977 }
978 
979 
980 /*
981  * Scroll the window. Positive is up/left. scroll_tty ( window, 0, 1 ) is a line feed.
982  * Scroll flushes the accumulated update area by calling update_tty().
983  */
984 
scroll_tty(WindowPtr window,short delta_x,short delta_y)985 short scroll_tty (WindowPtr window, short delta_x, short delta_y) {
986 RgnHandle rgn;
987 short s_err;
988 RECORD_EXISTS (record);
989 
990 	s_err = update_tty (window);
991 
992 	rgn = NewRgn ();
993 
994 	select_offscreen_port (record);
995 	ScrollRect (&(record->its_bits.bounds), -delta_x * record->char_width,
996 		-delta_y * record->row_height, rgn);
997 	EraseRgn (rgn);
998 	SetEmptyRgn (rgn);
999 
1000 	select_onscreen_window (record);
1001 	ScrollRect (&(record->its_bits.bounds), -delta_x * record->char_width,
1002 		-delta_y*record->row_height, rgn);
1003 	EraseRgn (rgn);
1004 	DisposeRgn (rgn);
1005 
1006 	record->y_curs -= delta_y;
1007 	record->x_curs -= delta_x;
1008 
1009 	return noErr;
1010 }
1011 
1012 
1013 /*
1014  * Clear the screen. Immediate.
1015  */
clear_tty(WindowPtr window)1016 short clear_tty (WindowPtr window) {
1017 RECORD_EXISTS (record);
1018 
1019 	record->curs_state = 0;
1020 	select_offscreen_port (record);
1021 	erase_rect (record, &(record->its_bits.bounds));
1022 	accumulate_rect (record, &(record->its_bits.bounds));
1023 	update_tty (window);
1024 
1025 	return noErr;
1026 }
1027 
1028 
1029 /*
1030  * Blink cursor on window if necessary
1031  */
blink_cursor(WindowPtr window,long when)1032 short blink_cursor (WindowPtr window, long when) {
1033 	RECORD_EXISTS (record);
1034 
1035 	if ((record->attribute [TTY_ATTRIB_CURSOR] & TA_BLINKING_CURSOR)) {
1036 		if (when > record->last_cursor + GetCaretTime ()) {
1037 			curs_pos (record, record->x_curs, record->y_curs, !record->curs_state);
1038 			record->last_cursor = when;
1039 			update_tty (window);
1040 		}
1041 	}
1042 	return 0;
1043 }
1044 
1045 
1046 /*
1047  * Draw an image of the tty - used for update events and can be called
1048  * for screen dumps.
1049  */
1050 short
image_tty(EventRecord * theEvent,WindowPtr window)1051 image_tty (EventRecord *theEvent, WindowPtr window) {
1052 #if defined(__SC__) || defined(__MRC__)
1053 # pragma unused(theEvent)
1054 #endif
1055 RECORD_EXISTS (record);
1056 
1057 #if CLIP_RECT_ONLY
1058 	record->invalid_rect = record->its_bits.bounds;
1059 #else
1060 RgnHandle rh = NewRgn ();
1061 
1062 	RectRgn (rh, record ->its_bits.bounds);
1063 	UnionRgn (record->invalid_part, rh, record->invalid_part);
1064 	DisposeRgn (rh);
1065 #endif
1066 	return update_tty (window);
1067 }
1068 
1069 
1070 
1071 /*
1072  * Clear an area
1073  */
clear_tty_window(WindowPtr window,short from_x,short from_y,short to_x,short to_y)1074 short clear_tty_window (WindowPtr window, short from_x, short from_y,
1075 	short to_x, short to_y) {
1076 Rect r;
1077 RECORD_EXISTS (record);
1078 
1079 	if (from_x > to_x || from_y > to_y) {
1080 		return general_failure;
1081 	}
1082 	pos_rect (record, &r, from_x, from_y, to_x, to_y);
1083 	select_offscreen_port (record);
1084 	erase_rect (record, &r);
1085 	accumulate_rect (record, &r);
1086 	if (DRAW_DIRECT) {
1087 		update_tty (window);
1088 	} else
1089 		select_onscreen_window (record);
1090 	return noErr;
1091 }
1092 
1093 
1094 #if EXTENDED_SUPPORT
1095 /*
1096  * Delete or insert operations used by many terminals can bottleneck through
1097  * here. Note that the order of executin for row/colum insertions is NOT
1098  * specified. Negative values for num_ mean delete, zero means no effect.
1099  */
mangle_tty_rows_columns(WindowPtr window,short from_row,short num_rows,short from_column,short num_columns)1100 short mangle_tty_rows_columns (WindowPtr window, short from_row, short num_rows,
1101 	short from_column, short num_columns) {
1102 Rect r;
1103 RgnHandle rh = NewRgn ();
1104 RECORD_EXISTS (record);
1105 
1106 	update_tty (window); /* Always make sure screen is OK */
1107 	curs_pos (record, record->x_curs, record->y_curs, 0);
1108 
1109 	if (num_rows) {
1110 		pos_rect (record, &r, 0, from_row, record->x_size - 1,
1111 			record->y_size - 1);
1112 		select_offscreen_port (record);
1113 		ScrollRect (&r, 0, num_rows * record->row_height, rh);
1114 		EraseRgn (rh);
1115 		SetEmptyRgn (rh);
1116 		select_onscreen_window (record);
1117 		ScrollRect (&r, 0, num_rows * record->row_height, rh);
1118 		EraseRgn (rh);
1119 		SetEmptyRgn (rh);
1120 	}
1121 	if (num_columns) {
1122 		pos_rect (record, &r, from_column, 0, record->x_size - 1,
1123 			record->y_size - 1);
1124 		select_offscreen_port (record);
1125 		ScrollRect (&r, num_columns * record->char_width, 0, rh);
1126 		EraseRgn (rh);
1127 		SetEmptyRgn (rh);
1128 		select_onscreen_window (record);
1129 		ScrollRect (&r, num_columns * record->char_width, 0, rh);
1130 		EraseRgn (rh);
1131 		SetEmptyRgn (rh);
1132 	}
1133 	DisposeRgn (rh);
1134 	if (record->x_curs >= from_column) {
1135 		record->x_curs += num_columns;
1136 	}
1137 	if (record->y_curs >= from_row) {
1138 		record->y_curs += num_rows;
1139 	}
1140 	curs_pos (record, record->x_curs, record->y_curs, 1);
1141 
1142 	return noErr;
1143 }
1144 
1145 /*
1146  * Frame an area in an aesthetically pleasing way.
1147  */
frame_tty_window(WindowPtr window,short from_x,short from_y,short to_x,short to_y,short frame_fatness)1148 short frame_tty_window (WindowPtr window, short from_x, short from_y ,
1149 	short to_x, short to_y, short frame_fatness) {
1150 Rect r;
1151 RECORD_EXISTS (record);
1152 
1153 	if (from_x > to_x || from_y > to_y) {
1154 		return general_failure;
1155 	}
1156 	pos_rect (record, & r, from_x, from_y, to_x, to_y);
1157 	select_offscreen_port (record);
1158 	PenSize (frame_fatness, frame_fatness);
1159 	FrameRect (&r);
1160 	PenNormal ();
1161 	accumulate_rect (record, &r);
1162 	if (DRAW_DIRECT) {
1163 		update_tty (window);
1164 	} else
1165 		select_onscreen_window (record);
1166 }
1167 
1168 
1169 /*
1170  * Highlighting a specific part of the tty window
1171  */
invert_tty_window(WindowPtr window,short from_x,short from_y,short to_x,short to_y)1172 short invert_tty_window (WindowPtr window, short from_x, short from_y ,
1173 	short to_x, short to_y) {
1174 Rect r;
1175 RECORD_EXISTS (record);
1176 
1177 	if (from_x > to_x || from_y > to_y) {
1178 		return general_failure;
1179 	}
1180 	pos_rect (record, &r, from_x, from_y, to_x, to_y);
1181 	select_offscreen_port (record);
1182 	InvertRect ( &r);
1183 	accumulate_rect (record, &r);
1184 	if (DRAW_DIRECT) {
1185 		update_tty (window);
1186 	} else
1187 		select_onscreen_window (record);
1188 }
1189 
1190 
1191 static void
canonical_rect(Rect * r,short x1,short y1,short x2,short y2)1192 canonical_rect (Rect * r, short x1, short y1, short x2, short y2) {
1193 	if (x1 < x2) {
1194 		if (y1 < y2) {
1195 			SetRect (r, x1, x2, y1, y2);
1196 		} else {
1197 			SetRect (r, x1, x2, y2, y1);
1198 		}
1199 	} else {
1200 		if (y1 < y2) {
1201 			SetRect (r, x2, x1, y1, y2);
1202 		} else {
1203 			SetRect (r, x2, x1, y2, y1);
1204 		}
1205 	}
1206 }
1207 
1208 
1209 /*
1210  * Line drawing - very device dependent
1211  */
draw_tty_line(WindowPtr window,short from_x,short from_y,short to_x,short to_y)1212 short draw_tty_line (WindowPtr window, short from_x, short from_y ,
1213 	short to_x, short to_y) {
1214 Rect r;
1215 RECORD_EXISTS (record);
1216 
1217 	select_offscreen_port (record);
1218 	MoveTo (from_x, from_y);
1219 	LineTo (to_x, to_y);
1220 	canonical_rect (&r, from_x, from_y, to_x, to_y);
1221 	accumulate_rect (record, &r);
1222 	if (DRAW_DIRECT) {
1223 		update_tty (window);
1224 	} else
1225 		select_onscreen_window (record);
1226 }
1227 
1228 
1229 #endif /* EXTENDED_SUPPORT */
1230