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