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