1 /* File: main-cap.c */
3 /* Purpose: Support for "term.c" using "termcap" calls */
11 * This file is a total hack, but is often very helpful. :-)
13 * This file allows use of the terminal without requiring the
14 * "curses" routines. In fact, if "USE_HARDCODE" is defined,
15 * this file will attempt to use various hard-coded "vt100"
16 * escape sequences to also avoid the use of the "termcap"
17 * routines. I do not know if this will work on System V.
19 * This file is intended for use only on those machines which are
20 * unable, for whatever reason, to compile the "main-gcu.c" file,
21 * but which seem to be able to support the "termcap" library, or
22 * which at least seem able to support "vt100" terminals.
24 * Large portions of this file were stolen from "main-gcu.c"
26 * This file incorrectly handles output to column 80, I think.
33 #if !defined(USE_TERMCAP) && !defined(USE_HARDCODE)
38 * Hack -- try to guess which systems use what commands
39 * Hack -- allow one of the "USE_Txxxxx" flags to be pre-set.
40 * Mega-Hack -- try to guess when "POSIX" is available.
41 * If the user defines two of these, we will probably crash.
43 # if !defined(USE_TERMIO) && !defined(USE_TCHARS)
44 # if defined(_POSIX_VERSION)
60 # include <sys/ioctl.h>
65 * One version needs these files
68 # include <sys/ioctl.h>
73 * The other needs these files
76 # include <sys/ioctl.h>
77 # include <sys/resource.h>
78 # include <sys/param.h>
79 # include <sys/file.h>
80 # include <sys/types.h>
85 * XXX XXX Hack -- POSIX uses "O_NONBLOCK" instead of "O_NDELAY"
87 * They should both work due to the "(i != 1)" test in the code
88 * which checks for the result of the "read()" command.
91 # define O_NDELAY O_NONBLOCK
100 * Termcap string information
103 static char blob[1024]; /* The "termcap" entry */
104 static char area[1024]; /* The string extraction buffer */
105 static char *next = area; /* The current "index" into "area" */
106 static char *desc; /* The terminal name */
112 * Pointers into the "area"
115 static char *cm; /* Move cursor */
116 static char *ch; /* Move cursor to horizontal location */
117 static char *cv; /* Move cursor to vertical location */
118 static char *ho; /* Move cursor to top left */
119 static char *ll; /* Move cursor to bottom left */
120 static char *cs; /* Set scroll area */
121 static char *cl; /* Clear screen */
122 static char *cd; /* Clear to end of display */
123 static char *ce; /* Clear to end of line */
124 static char *cr; /* Move to start of line */
125 static char *so; /* Turn on standout */
126 static char *se; /* Turn off standout */
127 static char *md; /* Turn on bold */
128 static char *me; /* Turn off bold */
129 static char *vi; /* Cursor - invisible */
130 static char *ve; /* Cursor - normal */
131 static char *vs; /* Cursor - bright */
138 static int rows; /* Screen size (Y) */
139 static int cols; /* Screen size (X) */
140 static int curx; /* Cursor location (X) */
141 static int cury; /* Cursor location (Y) */
142 static int curv; /* Cursor visibility */
148 extern char *getenv();
149 extern char *tgoto();
150 extern char *tgetstr();
154 * Write some chars to the terminal
156 static void ewrite(char *str)
158 int numtowrite, numwritten;
160 /* See how much work we have */
161 numtowrite = strlen(str);
163 /* Write until done */
164 while (numtowrite > 0)
166 /* Try to write the chars */
167 numwritten = write(1, str, numtowrite);
169 /* Handle FIFOs and EINTR */
170 if (numwritten < 0) numwritten = 0;
172 /* See what we completed */
173 numtowrite -= numwritten;
176 /* Hack -- sleep if not done */
177 if (numtowrite > 0) sleep(1);
185 static char write_buffer[128];
186 static char *write_buffer_ptr;
188 static void output_one(char c)
190 *write_buffer_ptr++ = c;
193 static void tp(char *s)
195 /* Dump the string into us */
196 write_buffer_ptr = write_buffer;
198 /* Write the string with padding */
199 tputs (s, 1, output_one);
201 /* Finish the string */
202 *write_buffer_ptr = '\0';
204 /* Dump the recorded buffer */
205 ewrite (write_buffer);
212 static void tp(char *s)
228 static void do_cl(void)
234 * Clear to the end of the line
236 static void do_ce(void)
243 * Set the cursor visibility (0 = invis, 1 = normal, 2 = bright)
245 static void curs_set(int vis)
268 * Restrict scrolling to within these rows
270 static void do_cs(int y1, int y2)
274 if (cs) tp(tgoto(cs, y2, y1));
279 sprintf(temp, cs, y1, y2);
288 * Go to the given screen location directly
290 static void do_cm(int x, int y)
294 if (cm) tp(tgoto(cm, x, y));
299 sprintf(temp, cm, y+1, x+1);
307 * Go to the given screen location in a "clever" manner
309 * XXX XXX XXX This function could use some work!
311 static void do_move(int x1, int y1, int x2, int y2)
313 /* Hack -- unknown start location */
314 if ((x1 == x2) && (y1 == y2)) do_cm(x2, y2);
319 if ((y2 <= 0) && ho) tp(ho);
320 else if ((y2 >= rows-1) && ll) tp(ll);
321 else if ((y2 == y1) && cr) tp(cr);
325 /* Default -- go directly there */
333 * Help initialize this file (see below)
335 errr init_cap_aux(void)
340 /* Get the terminal name (if possible) */
341 desc = getenv("TERM");
342 if (!desc) return (1);
344 /* Get the terminal info */
345 if (tgetent(blob, desc) != 1) return (2);
347 /* Get the (initial) columns and rows, or default */
348 if ((cols = tgetnum("co")) == -1) cols = 80;
349 if ((rows = tgetnum("li")) == -1) rows = 24;
351 /* Find out how to move the cursor to a given location */
352 cm = tgetstr("cm", &next);
353 if (!cm) return (10);
355 /* Find out how to move the cursor to a given position */
356 ch = tgetstr("ch", &next);
357 cv = tgetstr("cv", &next);
359 /* Find out how to "home" the screen */
360 ho = tgetstr("ho", &next);
362 /* Find out how to "last-line" the screen */
363 ll = tgetstr("ll", &next);
365 /* Find out how to do a "carriage return" */
366 cr = tgetstr("cr", &next);
369 /* Find out how to clear the screen */
370 cl = tgetstr("cl", &next);
371 if (!cl) return (11);
373 /* Find out how to clear to the end of display */
374 cd = tgetstr("cd", &next);
376 /* Find out how to clear to the end of the line */
377 ce = tgetstr("ce", &next);
379 /* Find out how to scroll (set the scroll region) */
380 cs = tgetstr("cs", &next);
382 /* Find out how to hilite */
383 so = tgetstr("so", &next);
384 se = tgetstr("se", &next);
385 if (!so || !se) so = se = NULL;
387 /* Find out how to bold */
388 md = tgetstr("md", &next);
389 me = tgetstr("me", &next);
390 if (!md || !me) md = me = NULL;
392 /* Check the cursor visibility stuff */
393 vi = tgetstr("vi", &next);
394 vs = tgetstr("vs", &next);
395 ve = tgetstr("ve", &next);
401 /* Assume some defualt information */
406 cl = "\033[2J\033[H"; /* --]--]-- */
408 /* Clear to end of line */
409 ce = "\033[K"; /* --]-- */
412 so = "\033[7m"; /* --]-- */
413 se = "\033[m"; /* --]-- */
416 cs = "\033[%d;%dr"; /* --]-- */
419 cm = "\033[%d;%dH"; /* --]-- */
434 * Save the "normal" and "angband" terminal settings
439 static struct termios norm_termios;
441 static struct termios game_termios;
447 static struct termio norm_termio;
449 static struct termio game_termio;
455 static struct sgttyb norm_ttyb;
456 static struct tchars norm_tchars;
457 static struct ltchars norm_ltchars;
458 static int norm_local_chars;
460 static struct sgttyb game_ttyb;
461 static struct tchars game_tchars;
462 static struct ltchars game_ltchars;
463 static int game_local_chars;
470 * Are we active? Not really needed.
472 static int active = FALSE;
476 * The main screen (no sub-screens)
478 static term term_screen_body;
483 * Place the "keymap" into its "normal" state
485 static void keymap_norm(void)
490 /* restore the saved values of the special chars */
491 (void)tcsetattr(0, TCSAFLUSH, &norm_termios);
497 /* restore the saved values of the special chars */
498 (void)ioctl(0, TCSETA, (char *)&norm_termio);
504 /* restore the saved values of the special chars */
505 (void)ioctl(0, TIOCSETP, (char *)&norm_ttyb);
506 (void)ioctl(0, TIOCSETC, (char *)&norm_tchars);
507 (void)ioctl(0, TIOCSLTC, (char *)&norm_ltchars);
508 (void)ioctl(0, TIOCLSET, (char *)&norm_local_chars);
516 * Place the "keymap" into the "game" state
518 static void keymap_game(void)
523 /* restore the saved values of the special chars */
524 (void)tcsetattr(0, TCSAFLUSH, &game_termios);
530 /* restore the saved values of the special chars */
531 (void)ioctl(0, TCSETA, (char *)&game_termio);
537 /* restore the saved values of the special chars */
538 (void)ioctl(0, TIOCSETP, (char *)&game_ttyb);
539 (void)ioctl(0, TIOCSETC, (char *)&game_tchars);
540 (void)ioctl(0, TIOCSLTC, (char *)&game_ltchars);
541 (void)ioctl(0, TIOCLSET, (char *)&game_local_chars);
549 * Save the normal keymap
551 static void keymap_norm_prepare(void)
556 /* Get the normal keymap */
557 tcgetattr(0, &norm_termios);
563 /* Get the normal keymap */
564 (void)ioctl(0, TCGETA, (char *)&norm_termio);
570 /* Get the normal keymap */
571 (void)ioctl(0, TIOCGETP, (char *)&norm_ttyb);
572 (void)ioctl(0, TIOCGETC, (char *)&norm_tchars);
573 (void)ioctl(0, TIOCGLTC, (char *)&norm_ltchars);
574 (void)ioctl(0, TIOCLGET, (char *)&norm_local_chars);
582 * Save the keymaps (normal and game)
584 static void keymap_game_prepare(void)
589 /* Acquire the current mapping */
590 tcgetattr(0, &game_termios);
592 /* Force "Ctrl-C" to interupt */
593 game_termios.c_cc[VINTR] = (char)3;
595 /* Force "Ctrl-Z" to suspend */
596 game_termios.c_cc[VSUSP] = (char)26;
598 /* Hack -- Leave "VSTART/VSTOP" alone */
600 /* Disable the standard control characters */
601 game_termios.c_cc[VQUIT] = (char)-1;
602 game_termios.c_cc[VERASE] = (char)-1;
603 game_termios.c_cc[VKILL] = (char)-1;
604 game_termios.c_cc[VEOF] = (char)-1;
605 game_termios.c_cc[VEOL] = (char)-1;
607 /* Normally, block until a character is read */
608 game_termios.c_cc[VMIN] = 1;
609 game_termios.c_cc[VTIME] = 0;
611 /* Hack -- Turn off "echo" and "canonical" mode */
612 game_termios.c_lflag &= ~(ECHO | ICANON);
614 /* Turn off flow control */
615 game_termios.c_iflag &= ~IXON;
621 /* Acquire the current mapping */
622 (void)ioctl(0, TCGETA, (char *)&game_termio);
624 /* Force "Ctrl-C" to interupt */
625 game_termio.c_cc[VINTR] = (char)3;
627 /* Force "Ctrl-Z" to suspend */
628 game_termio.c_cc[VSUSP] = (char)26;
630 /* Hack -- Leave "VSTART/VSTOP" alone */
632 /* Disable the standard control characters */
633 game_termio.c_cc[VQUIT] = (char)-1;
634 game_termio.c_cc[VERASE] = (char)-1;
635 game_termio.c_cc[VKILL] = (char)-1;
636 game_termio.c_cc[VEOF] = (char)-1;
637 game_termio.c_cc[VEOL] = (char)-1;
639 /* Normally, block until a character is read */
640 game_termio.c_cc[VMIN] = 1;
641 game_termio.c_cc[VTIME] = 0;
643 /* Hack -- Turn off "echo" and "canonical" mode */
644 game_termio.c_lflag &= ~(ECHO | ICANON);
646 /* Turn off flow control */
647 game_termio.c_iflag &= ~IXON;
653 /* Get the default game characters */
654 (void)ioctl(0, TIOCGETP, (char *)&game_ttyb);
655 (void)ioctl(0, TIOCGETC, (char *)&game_tchars);
656 (void)ioctl(0, TIOCGLTC, (char *)&game_ltchars);
657 (void)ioctl(0, TIOCLGET, (char *)&game_local_chars);
659 /* Force interupt (^C) */
660 game_tchars.t_intrc = (char)3;
662 /* Force start/stop (^Q, ^S) */
663 game_tchars.t_startc = (char)17;
664 game_tchars.t_stopc = (char)19;
666 /* Cancel some things */
667 game_tchars.t_quitc = (char)-1;
668 game_tchars.t_eofc = (char)-1;
669 game_tchars.t_brkc = (char)-1;
671 /* Force suspend (^Z) */
672 game_ltchars.t_suspc = (char)26;
674 /* Cancel some things */
675 game_ltchars.t_dsuspc = (char)-1;
676 game_ltchars.t_rprntc = (char)-1;
677 game_ltchars.t_flushc = (char)-1;
678 game_ltchars.t_werasc = (char)-1;
679 game_ltchars.t_lnextc = (char)-1;
681 /* XXX XXX XXX XXX Verify this before use */
682 /* Hack -- Turn off "echo" and "canonical" mode */
683 /* game_termios.c_lflag &= ~(ECHO | ICANON); */
684 game_ttyb.flag &= ~(ECHO | ICANON);
686 /* XXX XXX XXX Should maybe turn off flow control too. How? */
702 static errr Term_xtra_cap_alive(int v)
707 if (!active) return (1);
709 /* Hack -- make sure the cursor is visible */
712 /* Move to bottom right */
713 do_move(0, rows - 1, 0, rows - 1);
715 /* Go to normal keymap mode */
718 /* No longer active */
725 if (active) return (1);
727 /* Hack -- restore the cursor location */
728 do_move(curx, cury, curx, cury);
730 /* Hack -- restore the cursor visibility */
733 /* Go to angband keymap mode */
736 /* Now we are active */
749 static errr Term_xtra_cap_event(int v)
757 /* Wait for one byte */
760 /* Hack -- Handle "errors" */
761 if ((i <= 0) && (errno != EINTR)) exit_game_panic(p_ptr);
767 /* Get the current flags for stdin */
768 if ((arg = fcntl(0, F_GETFL, 0)) < 1) return (1);
770 /* Tell stdin not to block */
771 if (fcntl(0, F_SETFL, arg | O_NDELAY) < 0) return (1);
773 /* Read one byte, if possible */
776 /* Replace the flags for stdin */
777 if (fcntl(0, F_SETFL, arg)) return (1);
781 if ((i != 1) || (!buf[0])) return (1);
783 /* Enqueue the keypress */
784 Term_keypress(buf[0]);
794 * Actually move the hardware cursor
796 static errr Term_curs_cap(int x, int y)
798 /* Literally move the cursor */
799 do_move(curx, cury, x, y);
801 /* Save the cursor location */
811 * Erase a grid of space
813 * XXX XXX XXX Note that we will never be asked to clear the
814 * bottom line all the way to the bottom right edge, since we
815 * have set the "avoid the bottom right corner" flag.
817 static errr Term_wipe_cap(int x, int y, int n)
821 /* Place the cursor */
824 /* Wipe to end of line */
833 for (dx = 0; dx < n; ++dx)
846 * Place some text on the screen using an attribute
848 static errr Term_text_cap(int x, int y, int n, byte a, concptr s)
852 /* Move the cursor */
855 /* Dump the text, advance the cursor */
856 for (i = 0; s[i]; i++)
861 /* Advance cursor 'X', and wrap */
864 /* Reset cursor 'X' */
867 /* Hack -- Advance cursor 'Y', and wrap */
868 if (++cury == rows) cury = 0;
878 * Handle a "special request"
880 static errr Term_xtra_cap(int n, int v)
882 /* Analyze the request */
885 /* Clear the screen */
886 case TERM_XTRA_CLEAR:
892 case TERM_XTRA_NOISE:
893 (void)write(1, "\007", 1);
896 /* Change the cursor visibility */
897 case TERM_XTRA_SHAPE:
903 case TERM_XTRA_ALIVE:
904 return (Term_xtra_cap_alive(v));
907 case TERM_XTRA_EVENT:
908 return (Term_xtra_cap_event(v));
911 case TERM_XTRA_FLUSH:
912 while (!Term_xtra_cap_event(FALSE));
916 case TERM_XTRA_DELAY:
929 * Init a "term" for this file
931 static void Term_init_cap(term *t)
935 /* Assume cursor at top left */
939 /* Assume visible cursor */
942 /* Clear the screen */
945 /* Hack -- visible cursor */
954 * Nuke a "term" for this file
956 static void Term_nuke_cap(term *t)
960 /* Hack -- make sure the cursor is visible */
963 /* Move to bottom right */
964 do_move(0, rows - 1, 0, rows - 1);
969 /* No longer active */
983 * Prepare this file for Angband usage
987 term *t = &term_screen_body;
992 /* Initialize the screen */
993 if (init_cap_aux()) return (-1);
995 /* Hack -- Require large screen, or Quit with message */
996 if ((rows < 24) || (cols < 80)) quit("Screen too small!");
999 /*** Prepare to play ***/
1001 /* Extract the normal keymap */
1002 keymap_norm_prepare();
1004 /* Extract the game keymap */
1005 keymap_game_prepare();
1007 /* Hack -- activate the game keymap */
1010 /* Hack -- Do NOT buffer stdout */
1011 setbuf(stdout, NULL);
1014 /*** Now prepare the term ***/
1016 /* Initialize the term */
1017 term_init(t, 80, 24, 256);
1019 /* Avoid the bottom right corner */
1020 t->icky_corner = TRUE;
1022 /* Erase with "white space" */
1023 t->attr_blank = TERM_WHITE;
1024 t->char_blank = ' ';
1026 /* Set some hooks */
1027 t->init_hook = Term_init_cap;
1028 t->nuke_hook = Term_nuke_cap;
1030 /* Set some more hooks */
1031 t->text_hook = Term_text_cap;
1032 t->wipe_hook = Term_wipe_cap;
1033 t->curs_hook = Term_curs_cap;
1034 t->xtra_hook = Term_xtra_cap;
1040 Term_activate(term_screen);
1047 #endif /* USE_CAP */