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 #if 0 /* Use alloc.c instead */
109 /*
110 * Allocate a pointer using the set memory-allocator
111 */
112 static short
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 SetPortWindowPort(record->its_window);
472 CopyBits(source, GetPortBitMapForCopyBits(GetWindowPort(record->its_window)),
473 bounds, bounds, xfer_mode, mask_rgn);
474
475 if (record->uses_gworld) {
476 SetPixelsState (GetGWorldPixMap (record->offscreen_world), pix_state);
477 }
478 }
479
480
481 /*
482 * Fill an area with the background color
483 */
484 static void
erase_rect(tty_record * record,Rect * area)485 erase_rect (tty_record *record, Rect *area) {
486 if (game_active && u.uhp > 0 && iflags.use_stone && record->its_window == _mt_window) {
487 PixPatHandle ppat;
488
489 ppat = GetPixPat(iflags.use_stone + 127); /* find which pat to get */
490 if (ppat) { /* in game window, using backgroung pattern, and have pattern */
491 FillCRect (area, ppat);
492 DisposePixPat (ppat);
493 return;
494 }
495 }
496 EraseRect (area);
497 }
498
499
500 /*
501 * Recalculate the window based on new size, font, extent values,
502 * and re-allocate the bitmap.
503 */
504 short
force_tty_coordinate_system_recalc(WindowPtr window)505 force_tty_coordinate_system_recalc (WindowPtr window) {
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 may go very
519 * much downhill from here!
520 */
521 dprintf ("alloc_bits returned null in force_tty_coordinate_system_recalc!");
522 return s_err;
523 }
524 select_offscreen_port (record);
525 do_set_port_font (record);
526 return clear_tty (window);
527 }
528
529
530 #if 0
531 /*
532 * Update TTY according to new color environment for the window
533 */
534 static short
535 tty_environment_changed (tty_record *record) {
536 Point p = {0, 0};
537 Rect r_screen;
538
539 if (record->uses_gworld) {
540 r_screen = record->its_bits.bounds;
541 LocalToGlobal (&p);
542 OffsetRect (&r_screen, p.h, p.v);
543 UpdateGWorld (&(record->offscreen_world), 0, &r_screen,
544 (CTabHandle) 0, (GDHandle) 0, stretchPix);
545 select_offscreen_port (record);
546 SetOrigin (0, 0);
547 select_onscreen_window (record);
548 }
549 return 0;
550 }
551 #endif
552
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, short *font_number,
560 short *font_size, short *char_width, short *row_height) {
561 RECORD_EXISTS (record);
562
563 /*
564 * First, test that we actually have something to draw to...
565 */
566 if ((((char *)0 == record->its_bits.baseAddr) && !record->uses_gworld) ||
567 (((GWorldPtr)0 == record->offscreen_world) && record->uses_gworld)) {
568 return general_failure;
569 }
570
571 *x_size = record->x_size;
572 *y_size = record->y_size;
573 *x_size_pixels = record->its_bits.bounds.right;
574 *y_size_pixels = record->its_bits.bounds.bottom;
575 *font_number = record->font_number;
576 *font_size = record->font_size;
577 *char_width = record->char_width;
578 *row_height = record->row_height;
579
580 return noErr;
581 }
582
583
584 /*
585 * Map a position on the map to screen coordinates
586 */
587 static void
pos_rect(tty_record * record,Rect * r,short x_pos,short y_pos,short x_end,short y_end)588 pos_rect (tty_record *record, Rect *r, short x_pos, short y_pos,
589 short x_end, short y_end) {
590
591 SetRect (r, x_pos * (record->char_width), y_pos * (record->row_height),
592 (1 + x_end) * (record->char_width) , (1 + y_end) *
593 (record->row_height));
594 }
595
596
597 static void
accumulate_rect(tty_record * record,Rect * rect)598 accumulate_rect (tty_record *record, Rect *rect) {
599 #if CLIP_RECT_ONLY
600 union_rect (rect, &(record->invalid_rect), &(record->invalid_rect));
601 #else
602 RgnHandle rh = NewRgn ();
603
604 RectRgn (rh, rect);
605 UnionRgn (record->invalid_part, rh, record->invalid_part);
606 DisposeRgn (rh);
607 #endif
608 }
609
610
611 /*
612 * get and set window invalid region. exposed for HandleUpdateEvent in macwin.c
613 * to correct display problem
614 */
615
get_invalid_region(WindowPtr window,Rect * inval_rect)616 short get_invalid_region (WindowPtr window, Rect *inval_rect) {
617 RECORD_EXISTS (record);
618 #if CLIP_RECT_ONLY
619 if (record->invalid_rect.right <= record->invalid_rect.left ||
620 record->invalid_rect.bottom <= record->invalid_rect.top) {
621 return general_failure;
622 }
623 *inval_rect = record->invalid_rect;
624 #else
625 if (EmptyRgn (record->invalid_part)) {
626 return general_failure;
627 }
628 *inval_rect = (*(record->invalid_part))->rgnBBox;
629 #endif
630 return noErr;
631 }
632
set_invalid_region(WindowPtr window,Rect * inval_rect)633 short set_invalid_region (WindowPtr window, Rect *inval_rect) {
634 RECORD_EXISTS (record);
635 accumulate_rect (record, inval_rect);
636 return noErr;
637 }
638
639
640 /*
641 * Invert the specified position
642 */
643 static void
curs_pos(tty_record * record,short x_pos,short y_pos,short to_state)644 curs_pos (tty_record *record, short x_pos, short y_pos, short to_state) {
645 Rect r;
646
647 if (record->curs_state == to_state) {
648 return;
649 }
650 record->curs_state = to_state;
651 pos_rect (record, &r, x_pos, y_pos, x_pos, y_pos);
652
653 if (DRAW_DIRECT) {
654 void *old_port;
655
656 save_port (record, &old_port);
657 select_onscreen_window (record);
658 InvertRect (&r);
659 use_port (record, old_port);
660 } else {
661 accumulate_rect (record, &r);
662 }
663 }
664
665
666 /*
667 * Move the cursor (both as displayed and where drawing goes)
668 * HOWEVER: The cursor is NOT stored in the bitmap!
669 */
670 short
move_tty_cursor(WindowPtr window,short x_pos,short y_pos)671 move_tty_cursor (WindowPtr window, short x_pos, short y_pos) {
672 RECORD_EXISTS (record);
673
674 if (record->x_curs == x_pos && record->y_curs == y_pos) {
675 return noErr;
676 }
677 if (record->x_size <= x_pos || x_pos < 0 ||
678 record->y_size <= y_pos || y_pos < 0) {
679 return general_failure;
680 }
681 curs_pos (record, record->x_curs, record->y_curs, 0);
682 record->x_curs = x_pos;
683 record->y_curs = y_pos;
684 curs_pos (record, x_pos, y_pos, 1);
685
686 return noErr;
687 }
688
689
690 /*
691 * Update the screen to match the current bitmap, after adding stuff
692 * with add_tty_char etc.
693 */
694 short
update_tty(WindowPtr window)695 update_tty (WindowPtr window) {
696 Rect r;
697 RECORD_EXISTS (record);
698
699 #if CLIP_RECT_ONLY
700 if (record->invalid_rect.right <= record->invalid_rect.left ||
701 record->invalid_rect.bottom <= record->invalid_rect.top) {
702 return noErr;
703 }
704 r = record->invalid_rect;
705 #else
706 if (EmptyRgn (record->invalid_part)) {
707 return noErr;
708 }
709 r = (*(record->invalid_part))->rgnBBox;
710 #endif
711 select_onscreen_window (record);
712 copy_bits (record, &r, srcCopy, (RgnHandle) 0);
713 #if CLIP_RECT_ONLY
714 empty_rect (&(record->invalid_rect));
715 #else
716 SetEmptyRgn (record->invalid_part);
717 #endif
718 if (record->curs_state) {
719 pos_rect (record, &r, record->x_curs, record->y_curs,
720 record->x_curs, record->y_curs);
721 InvertRect (&r);
722 }
723
724 return noErr;
725 }
726
727
728 /*
729 * Low level add to screen
730 */
731 static void
do_add_string(tty_record * record,char * str,short len)732 do_add_string (tty_record *record, char *str, short len) {
733 Rect r;
734
735 if (len < 1) {
736 return;
737 }
738 select_offscreen_port (record);
739
740 MoveTo (record->x_curs * record->char_width, record->y_curs *
741 record->row_height + record->ascent_height);
742 DrawText (str, 0, len);
743
744 pos_rect (record, &r, record->x_curs, record->y_curs,
745 record->x_curs + len - 1, record->y_curs);
746 select_onscreen_window (record);
747 if (DRAW_DIRECT) {
748 copy_bits (record, &r, srcCopy, (RgnHandle)0);
749 } else {
750 accumulate_rect (record, &r);
751 }
752 }
753
754
755 /*
756 * Low-level cursor handling routine
757 */
758 static void
do_add_cursor(tty_record * record,short x_pos)759 do_add_cursor (tty_record *record, short x_pos) {
760
761 record->x_curs = x_pos;
762 if (record->x_curs >= record->x_size) {
763 if (0L != (record->attribute [TTY_ATTRIB_FLAGS] & TA_WRAP_AROUND)) {
764 record->y_curs ++;
765 record->x_curs = 0;
766 if (record->y_curs >= record->y_size) {
767 if (0L != (record->attribute [TTY_ATTRIB_FLAGS] &
768 TA_INHIBIT_VERT_SCROLL)) {
769 record->y_curs = record->y_size;
770 } else {
771 scroll_tty (record->its_window, 0, 1 + record->y_curs -
772 record->y_size);
773 }
774 }
775 } else {
776 record->x_curs = record->x_size;
777 }
778 }
779 }
780
781
782 /*
783 * Do control character
784 */
785 static void
do_control(tty_record * record,short character)786 do_control (tty_record *record, short character) {
787 int recurse = 0;
788
789 /*
790 * Check recursion because nl_add_cr and cr_add_nl may both be set and invoke each other
791 */
792 do {
793 switch (character) {
794 case CHAR_CR :
795 record->x_curs = 0;
796 if (!recurse && (record->attribute [TTY_ATTRIB_CURSOR] & TA_CR_ADD_NL)) {
797 recurse = 1;
798 }
799 else {
800 recurse = 0;
801 break;
802 } /* FALL-THROUGH: if CR-LF, don't bother with loop */
803 case CHAR_LF :
804 record->y_curs++;
805 if (record->y_curs >= record->y_size) {
806 scroll_tty (record->its_window, 0, 1 + record->y_curs - record->y_size);
807 }
808 if (!recurse && (record->attribute [TTY_ATTRIB_CURSOR] & TA_NL_ADD_CR)) {
809 character = CHAR_CR;
810 recurse = 1;
811 }
812 else recurse = 0;
813 break;
814 case CHAR_BELL :
815 tty_nhbell();
816 break;
817 case CHAR_BS :
818 if (record->x_curs > 0)
819 record->x_curs --;
820 default :
821 break;
822 }
823 } while (recurse);
824 }
825
826
827 /*
828 * Add a single character. It is drawn directly if the correct flag is set,
829 * else deferred to the next update event or call of update_tty()
830 */
831 short
add_tty_char(WindowPtr window,short character)832 add_tty_char (WindowPtr window, short character) {
833 register char is_control;
834 char ch;
835 RECORD_EXISTS (record);
836
837 if (!(record->attribute [TTY_ATTRIB_FLAGS] & TA_WRAP_AROUND) &&
838 record->x_curs >= record->x_size)
839 return noErr; /* Optimize away drawing across border without wrap */
840
841 if (record->curs_state != 0)
842 curs_pos (record, record->x_curs, record->y_curs, 0);
843
844 ch = character;
845 is_control = (ch < sizeof(long) * 8) && ((s_control & (1 << ch)) != 0L);
846 if (is_control)
847 do_control (record, ch);
848 else {
849 do_add_string (record, (char *)&ch, 1);
850 do_add_cursor (record, record->x_curs + 1);
851 }
852
853 return noErr;
854 }
855
856
857 /*
858 * Add a null-terminated string of characters
859 */
860 short
add_tty_string(WindowPtr window,const char * string)861 add_tty_string(WindowPtr window, const char *string)
862 {
863 register const unsigned char * start_c;
864 register const unsigned char * the_c;
865 register unsigned char ch, is_control = 0, tty_wrap;
866 register short max_x, pos_x;
867 RECORD_EXISTS (record);
868
869 if (record->curs_state != 0)
870 curs_pos (record, record->x_curs, record->y_curs, 0);
871
872 the_c = (const unsigned char *) string;
873 max_x = record->x_size;
874 tty_wrap = (record->attribute [TTY_ATTRIB_FLAGS] & TA_WRAP_AROUND);
875 for (;;) {
876 pos_x = record->x_curs;
877 if (!tty_wrap && pos_x >= max_x)
878 break; /* Optimize away drawing across border without wrap */
879
880 start_c = the_c;
881 ch = *the_c;
882 while (pos_x < max_x) {
883 is_control = (ch < sizeof(long) * 8) && ((s_control & (1 << ch)) != 0L);
884 if (is_control)
885 break;
886 the_c ++; ch = *the_c;
887 pos_x ++;
888 }
889 do_add_string (record, (char *) start_c, the_c - start_c);
890 do_add_cursor (record, pos_x);
891 if (!ch)
892 break;
893
894 if (is_control) {
895 do_control (record, ch);
896 the_c ++;
897 }
898 }
899
900 return noErr;
901 }
902
903
904 /*
905 * Read or change attributes for the tty. Note that some attribs may
906 * very well clear and reallocate the bitmap when changed, whereas
907 * others (color, highlight, ...) are guaranteed not to.
908 */
get_tty_attrib(WindowPtr window,tty_attrib attrib,long * value)909 short get_tty_attrib (WindowPtr window, tty_attrib attrib, long *value) {
910 RECORD_EXISTS (record);
911
912 if (attrib < 0 || attrib >= TTY_NUMBER_ATTRIBUTES) {
913 return general_failure;
914 }
915 *value = record->attribute [attrib];
916
917 return noErr;
918 }
919
920
set_tty_attrib(WindowPtr window,tty_attrib attrib,long value)921 short set_tty_attrib (WindowPtr window, tty_attrib attrib, long value) {
922 RGBColor rgb_color;
923 RECORD_EXISTS (record);
924
925 if (attrib < 0 || attrib >= TTY_NUMBER_ATTRIBUTES) {
926 return general_failure;
927 }
928 record->attribute [attrib] = value;
929 /*
930 * Presently, no attributes generate a new bitmap.
931 */
932 switch (attrib) {
933 case TTY_ATTRIB_CURSOR :
934 /*
935 * Check if we should change tables
936 */
937 if (0L != (value & TA_RAW_OUTPUT)) {
938 s_control = RAW_CONTROLS;
939 } else {
940 s_control = COOKED_CONTROLS;
941 }
942 break;
943 case TTY_ATTRIB_FLAGS :
944 /*
945 * Check if we should flush the output going from cached to draw-direct
946 */
947 if (0L != (value & TA_ALWAYS_REFRESH)) {
948 update_tty (window);
949 }
950 break;
951 case TTY_ATTRIB_FOREGROUND :
952 /*
953 * Set foreground color
954 */
955 TA_TO_RGB (value, rgb_color);
956 select_offscreen_port (record);
957 RGBForeColor (&rgb_color);
958 select_onscreen_window (record);
959 break;
960 case TTY_ATTRIB_BACKGROUND :
961 /*
962 * Set background color
963 */
964 TA_TO_RGB (value, rgb_color);
965 select_offscreen_port (record);
966 RGBBackColor (&rgb_color);
967 select_onscreen_window (record);
968 break;
969 default :
970 break;
971 }
972 return noErr;
973 }
974
975
976 /*
977 * Scroll the window. Positive is up/left. scroll_tty ( window, 0, 1 ) is a line feed.
978 * Scroll flushes the accumulated update area by calling update_tty().
979 */
980
scroll_tty(WindowPtr window,short delta_x,short delta_y)981 short scroll_tty (WindowPtr window, short delta_x, short delta_y) {
982 RgnHandle rgn;
983 short s_err;
984 RECORD_EXISTS (record);
985
986 s_err = update_tty (window);
987
988 rgn = NewRgn ();
989
990 select_offscreen_port (record);
991 ScrollRect (&(record->its_bits.bounds), -delta_x * record->char_width,
992 -delta_y * record->row_height, rgn);
993 EraseRgn (rgn);
994 SetEmptyRgn (rgn);
995
996 select_onscreen_window (record);
997 ScrollRect (&(record->its_bits.bounds), -delta_x * record->char_width,
998 -delta_y*record->row_height, rgn);
999 EraseRgn (rgn);
1000 DisposeRgn (rgn);
1001
1002 record->y_curs -= delta_y;
1003 record->x_curs -= delta_x;
1004
1005 return noErr;
1006 }
1007
1008
1009 /*
1010 * Clear the screen. Immediate.
1011 */
clear_tty(WindowPtr window)1012 short clear_tty (WindowPtr window) {
1013 RECORD_EXISTS (record);
1014
1015 record->curs_state = 0;
1016 select_offscreen_port (record);
1017 erase_rect (record, &(record->its_bits.bounds));
1018 accumulate_rect (record, &(record->its_bits.bounds));
1019 update_tty (window);
1020
1021 return noErr;
1022 }
1023
1024
1025 /*
1026 * Blink cursor on window if necessary
1027 */
blink_cursor(WindowPtr window,long when)1028 short blink_cursor (WindowPtr window, long when) {
1029 RECORD_EXISTS (record);
1030
1031 if ((record->attribute [TTY_ATTRIB_CURSOR] & TA_BLINKING_CURSOR)) {
1032 if (when > record->last_cursor + GetCaretTime ()) {
1033 curs_pos (record, record->x_curs, record->y_curs, !record->curs_state);
1034 record->last_cursor = when;
1035 update_tty (window);
1036 }
1037 }
1038 return 0;
1039 }
1040
1041
1042 /*
1043 * Draw an image of the tty - used for update events and can be called
1044 * for screen dumps.
1045 */
1046 short
image_tty(EventRecord * theEvent,WindowPtr window)1047 image_tty (EventRecord *theEvent, WindowPtr window) {
1048 #if defined(__SC__) || defined(__MRC__)
1049 # pragma unused(theEvent)
1050 #endif
1051 RECORD_EXISTS (record);
1052
1053 #if CLIP_RECT_ONLY
1054 record->invalid_rect = record->its_bits.bounds;
1055 #else
1056 RgnHandle rh = NewRgn ();
1057
1058 RectRgn (rh, record ->its_bits.bounds);
1059 UnionRgn (record->invalid_part, rh, record->invalid_part);
1060 DisposeRgn (rh);
1061 #endif
1062 return update_tty (window);
1063 }
1064
1065
1066
1067 /*
1068 * Clear an area
1069 */
clear_tty_window(WindowPtr window,short from_x,short from_y,short to_x,short to_y)1070 short clear_tty_window (WindowPtr window, short from_x, short from_y,
1071 short to_x, short to_y) {
1072 Rect r;
1073 RECORD_EXISTS (record);
1074
1075 if (from_x > to_x || from_y > to_y) {
1076 return general_failure;
1077 }
1078 pos_rect (record, &r, from_x, from_y, to_x, to_y);
1079 select_offscreen_port (record);
1080 erase_rect (record, &r);
1081 accumulate_rect (record, &r);
1082 if (DRAW_DIRECT) {
1083 update_tty (window);
1084 } else
1085 select_onscreen_window (record);
1086 return noErr;
1087 }
1088
1089
1090 #if EXTENDED_SUPPORT
1091 /*
1092 * Delete or insert operations used by many terminals can bottleneck through
1093 * here. Note that the order of executin for row/colum insertions is NOT
1094 * specified. Negative values for num_ mean delete, zero means no effect.
1095 */
mangle_tty_rows_columns(WindowPtr window,short from_row,short num_rows,short from_column,short num_columns)1096 short mangle_tty_rows_columns (WindowPtr window, short from_row, short num_rows,
1097 short from_column, short num_columns) {
1098 Rect r;
1099 RgnHandle rh = NewRgn ();
1100 RECORD_EXISTS (record);
1101
1102 update_tty (window); /* Always make sure screen is OK */
1103 curs_pos (record, record->x_curs, record->y_curs, 0);
1104
1105 if (num_rows) {
1106 pos_rect (record, &r, 0, from_row, record->x_size - 1,
1107 record->y_size - 1);
1108 select_offscreen_port (record);
1109 ScrollRect (&r, 0, num_rows * record->row_height, rh);
1110 EraseRgn (rh);
1111 SetEmptyRgn (rh);
1112 select_onscreen_window (record);
1113 ScrollRect (&r, 0, num_rows * record->row_height, rh);
1114 EraseRgn (rh);
1115 SetEmptyRgn (rh);
1116 }
1117 if (num_columns) {
1118 pos_rect (record, &r, from_column, 0, record->x_size - 1,
1119 record->y_size - 1);
1120 select_offscreen_port (record);
1121 ScrollRect (&r, num_columns * record->char_width, 0, rh);
1122 EraseRgn (rh);
1123 SetEmptyRgn (rh);
1124 select_onscreen_window (record);
1125 ScrollRect (&r, num_columns * record->char_width, 0, rh);
1126 EraseRgn (rh);
1127 SetEmptyRgn (rh);
1128 }
1129 DisposeRgn (rh);
1130 if (record->x_curs >= from_column) {
1131 record->x_curs += num_columns;
1132 }
1133 if (record->y_curs >= from_row) {
1134 record->y_curs += num_rows;
1135 }
1136 curs_pos (record, record->x_curs, record->y_curs, 1);
1137
1138 return noErr;
1139 }
1140
1141 /*
1142 * Frame an area in an aesthetically pleasing way.
1143 */
frame_tty_window(WindowPtr window,short from_x,short from_y,short to_x,short to_y,short frame_fatness)1144 short frame_tty_window (WindowPtr window, short from_x, short from_y ,
1145 short to_x, short to_y, short frame_fatness) {
1146 Rect r;
1147 RECORD_EXISTS (record);
1148
1149 if (from_x > to_x || from_y > to_y) {
1150 return general_failure;
1151 }
1152 pos_rect (record, & r, from_x, from_y, to_x, to_y);
1153 select_offscreen_port (record);
1154 PenSize (frame_fatness, frame_fatness);
1155 FrameRect (&r);
1156 PenNormal ();
1157 accumulate_rect (record, &r);
1158 if (DRAW_DIRECT) {
1159 update_tty (window);
1160 } else
1161 select_onscreen_window (record);
1162 }
1163
1164
1165 /*
1166 * Highlighting a specific part of the tty window
1167 */
invert_tty_window(WindowPtr window,short from_x,short from_y,short to_x,short to_y)1168 short invert_tty_window (WindowPtr window, short from_x, short from_y ,
1169 short to_x, short to_y) {
1170 Rect r;
1171 RECORD_EXISTS (record);
1172
1173 if (from_x > to_x || from_y > to_y) {
1174 return general_failure;
1175 }
1176 pos_rect (record, &r, from_x, from_y, to_x, to_y);
1177 select_offscreen_port (record);
1178 InvertRect ( &r);
1179 accumulate_rect (record, &r);
1180 if (DRAW_DIRECT) {
1181 update_tty (window);
1182 } else
1183 select_onscreen_window (record);
1184 }
1185
1186
1187 static void
canonical_rect(Rect * r,short x1,short y1,short x2,short y2)1188 canonical_rect (Rect * r, short x1, short y1, short x2, short y2) {
1189 if (x1 < x2) {
1190 if (y1 < y2) {
1191 SetRect (r, x1, x2, y1, y2);
1192 } else {
1193 SetRect (r, x1, x2, y2, y1);
1194 }
1195 } else {
1196 if (y1 < y2) {
1197 SetRect (r, x2, x1, y1, y2);
1198 } else {
1199 SetRect (r, x2, x1, y2, y1);
1200 }
1201 }
1202 }
1203
1204
1205 /*
1206 * Line drawing - very device dependent
1207 */
draw_tty_line(WindowPtr window,short from_x,short from_y,short to_x,short to_y)1208 short draw_tty_line (WindowPtr window, short from_x, short from_y ,
1209 short to_x, short to_y) {
1210 Rect r;
1211 RECORD_EXISTS (record);
1212
1213 select_offscreen_port (record);
1214 MoveTo (from_x, from_y);
1215 LineTo (to_x, to_y);
1216 canonical_rect (&r, from_x, from_y, to_x, to_y);
1217 accumulate_rect (record, &r);
1218 if (DRAW_DIRECT) {
1219 update_tty (window);
1220 } else
1221 select_onscreen_window (record);
1222 }
1223
1224
1225 #endif /* EXTENDED_SUPPORT */
1226