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