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