1 /* File: main-cap.c */
3 /* Purpose: Support for "term.c" using "termcap" calls */
5 #include "io/exit-panic.h"
6 #include "system/angband.h"
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.
32 #if !defined(USE_TERMCAP) && !defined(USE_HARDCODE)
37 * Hack -- try to guess which systems use what commands
38 * Hack -- allow one of the "USE_Txxxxx" flags to be pre-set.
39 * Mega-Hack -- try to guess when "POSIX" is available.
40 * If the user defines two of these, we will probably crash.
52 #include <sys/ioctl.h>
57 * One version needs these files
60 #include <sys/ioctl.h>
65 * The other needs these files
69 #include <sys/ioctl.h>
70 #include <sys/param.h>
71 #include <sys/resource.h>
72 #include <sys/types.h>
76 * XXX XXX Hack -- POSIX uses "O_NONBLOCK" instead of "O_NDELAY"
78 * They should both work due to the "(i != 1)" test in the code
79 * which checks for the result of the "read()" command.
82 #define O_NDELAY O_NONBLOCK
88 * Termcap string information
91 static char blob[1024]; /* The "termcap" entry */
92 static char area[1024]; /* The string extraction buffer */
93 static char *next = area; /* The current "index" into "area" */
94 static char *desc; /* The terminal name */
99 * Pointers into the "area"
102 static char *cm; /* Move cursor */
103 static char *ch; /* Move cursor to horizontal location */
104 static char *cv; /* Move cursor to vertical location */
105 static char *ho; /* Move cursor to top left */
106 static char *ll; /* Move cursor to bottom left */
107 static char *cs; /* Set scroll area */
108 static char *cl; /* Clear screen */
109 static char *cd; /* Clear to end of display */
110 static char *ce; /* Clear to end of line */
111 static char *cr; /* Move to start of line */
112 static char *so; /* Turn on standout */
113 static char *se; /* Turn off standout */
114 static char *md; /* Turn on bold */
115 static char *me; /* Turn off bold */
116 static char *vi; /* Cursor - invisible */
117 static char *ve; /* Cursor - normal */
118 static char *vs; /* Cursor - bright */
124 static int rows; /* Screen size (Y) */
125 static int cols; /* Screen size (X) */
126 static int curx; /* Cursor location (X) */
127 static int cury; /* Cursor location (Y) */
128 static int curv; /* Cursor visibility */
133 extern char *getenv();
134 extern char *tgoto();
135 extern char *tgetstr();
138 * Write some chars to the terminal
140 static void ewrite(char *str)
142 int numtowrite, numwritten;
144 /* See how much work we have */
145 numtowrite = strlen(str);
147 /* Write until done */
148 while (numtowrite > 0) {
149 /* Try to write the chars */
150 numwritten = write(1, str, numtowrite);
152 /* Handle FIFOs and EINTR */
156 /* See what we completed */
157 numtowrite -= numwritten;
160 /* Hack -- sleep if not done */
168 static char write_buffer[128];
169 static char *write_buffer_ptr;
171 static void output_one(char c)
173 *write_buffer_ptr++ = c;
176 static void tp(char *s)
178 /* Dump the string into us */
179 write_buffer_ptr = write_buffer;
181 /* Write the string with padding */
182 tputs(s, 1, output_one);
184 /* Finish the string */
185 *write_buffer_ptr = '\0';
187 /* Dump the recorded buffer */
188 ewrite(write_buffer);
195 static void tp(char *s)
205 static void do_cl(void)
212 * Clear to the end of the line
214 static void do_ce(void)
221 * Set the cursor visibility (0 = invis, 1 = normal, 2 = bright)
223 static void curs_set(int vis)
229 } else if (vis > 1) {
240 * Restrict scrolling to within these rows
242 static void do_cs(int y1, int y2)
247 tp(tgoto(cs, y2, y1));
252 sprintf(temp, cs, y1, y2);
258 * Go to the given screen location directly
260 static void do_cm(int x, int y)
270 sprintf(temp, cm, y + 1, x + 1);
276 * Go to the given screen location in a "clever" manner
278 * XXX XXX XXX This function could use some work!
280 static void do_move(int x1, int y1, int x2, int y2)
282 /* Hack -- unknown start location */
283 if ((x1 == x2) && (y1 == y2))
290 else if ((y2 >= rows - 1) && ll)
292 else if ((y2 == y1) && cr)
298 /* Default -- go directly there */
304 * Help initialize this file (see below)
306 errr init_cap_aux(void)
311 /* Get the terminal name (if possible) */
312 desc = getenv("TERM");
316 /* Get the terminal info */
317 if (tgetent(blob, desc) != 1)
320 /* Get the (initial) columns and rows, or default */
321 if ((cols = tgetnum("co")) == -1)
323 if ((rows = tgetnum("li")) == -1)
326 /* Find out how to move the cursor to a given location */
327 cm = tgetstr("cm", &next);
331 /* Find out how to move the cursor to a given position */
332 ch = tgetstr("ch", &next);
333 cv = tgetstr("cv", &next);
335 /* Find out how to "home" the screen */
336 ho = tgetstr("ho", &next);
338 /* Find out how to "last-line" the screen */
339 ll = tgetstr("ll", &next);
341 /* Find out how to do a "carriage return" */
342 cr = tgetstr("cr", &next);
346 /* Find out how to clear the screen */
347 cl = tgetstr("cl", &next);
351 /* Find out how to clear to the end of display */
352 cd = tgetstr("cd", &next);
354 /* Find out how to clear to the end of the line */
355 ce = tgetstr("ce", &next);
357 /* Find out how to scroll (set the scroll region) */
358 cs = tgetstr("cs", &next);
360 /* Find out how to hilite */
361 so = tgetstr("so", &next);
362 se = tgetstr("se", &next);
366 /* Find out how to bold */
367 md = tgetstr("md", &next);
368 me = tgetstr("me", &next);
372 /* Check the cursor visibility stuff */
373 vi = tgetstr("vi", &next);
374 vs = tgetstr("vs", &next);
375 ve = tgetstr("ve", &next);
381 /* Assume some defualt information */
386 cl = "\033[2J\033[H"; /* --]--]-- */
388 /* Clear to end of line */
389 ce = "\033[K"; /* --]-- */
392 so = "\033[7m"; /* --]-- */
393 se = "\033[m"; /* --]-- */
396 cs = "\033[%d;%dr"; /* --]-- */
399 cm = "\033[%d;%dH"; /* --]-- */
408 * Save the "normal" and "angband" terminal settings
413 static struct termios norm_termios;
415 static struct termios game_termios;
421 static struct termio norm_termio;
423 static struct termio game_termio;
429 static struct sgttyb norm_ttyb;
430 static struct tchars norm_tchars;
431 static struct ltchars norm_ltchars;
432 static int norm_local_chars;
434 static struct sgttyb game_ttyb;
435 static struct tchars game_tchars;
436 static struct ltchars game_ltchars;
437 static int game_local_chars;
442 * Are we active? Not really needed.
444 static int active = false;
447 * The main screen (no sub-screens)
449 static term term_screen_body;
452 * Place the "keymap" into its "normal" state
454 static void keymap_norm(void)
459 /* restore the saved values of the special chars */
460 (void)tcsetattr(0, TCSAFLUSH, &norm_termios);
466 /* restore the saved values of the special chars */
467 (void)ioctl(0, TCSETA, (char *)&norm_termio);
473 /* restore the saved values of the special chars */
474 (void)ioctl(0, TIOCSETP, (char *)&norm_ttyb);
475 (void)ioctl(0, TIOCSETC, (char *)&norm_tchars);
476 (void)ioctl(0, TIOCSLTC, (char *)&norm_ltchars);
477 (void)ioctl(0, TIOCLSET, (char *)&norm_local_chars);
483 * Place the "keymap" into the "game" state
485 static void keymap_game(void)
490 /* restore the saved values of the special chars */
491 (void)tcsetattr(0, TCSAFLUSH, &game_termios);
497 /* restore the saved values of the special chars */
498 (void)ioctl(0, TCSETA, (char *)&game_termio);
504 /* restore the saved values of the special chars */
505 (void)ioctl(0, TIOCSETP, (char *)&game_ttyb);
506 (void)ioctl(0, TIOCSETC, (char *)&game_tchars);
507 (void)ioctl(0, TIOCSLTC, (char *)&game_ltchars);
508 (void)ioctl(0, TIOCLSET, (char *)&game_local_chars);
514 * Save the normal keymap
516 static void keymap_norm_prepare(void)
521 /* Get the normal keymap */
522 tcgetattr(0, &norm_termios);
528 /* Get the normal keymap */
529 (void)ioctl(0, TCGETA, (char *)&norm_termio);
535 /* Get the normal keymap */
536 (void)ioctl(0, TIOCGETP, (char *)&norm_ttyb);
537 (void)ioctl(0, TIOCGETC, (char *)&norm_tchars);
538 (void)ioctl(0, TIOCGLTC, (char *)&norm_ltchars);
539 (void)ioctl(0, TIOCLGET, (char *)&norm_local_chars);
545 * Save the keymaps (normal and game)
547 static void keymap_game_prepare(void)
552 /* Acquire the current mapping */
553 tcgetattr(0, &game_termios);
555 /* Force "Ctrl-C" to interupt */
556 game_termios.c_cc[VINTR] = (char)3;
558 /* Force "Ctrl-Z" to suspend */
559 game_termios.c_cc[VSUSP] = (char)26;
561 /* Hack -- Leave "VSTART/VSTOP" alone */
563 /* Disable the standard control characters */
564 game_termios.c_cc[VQUIT] = (char)-1;
565 game_termios.c_cc[VERASE] = (char)-1;
566 game_termios.c_cc[VKILL] = (char)-1;
567 game_termios.c_cc[VEOF] = (char)-1;
568 game_termios.c_cc[VEOL] = (char)-1;
570 /* Normally, block until a character is read */
571 game_termios.c_cc[VMIN] = 1;
572 game_termios.c_cc[VTIME] = 0;
574 /* Hack -- Turn off "echo" and "canonical" mode */
575 game_termios.c_lflag &= ~(ECHO | ICANON);
577 /* Turn off flow control */
578 game_termios.c_iflag &= ~IXON;
584 /* Acquire the current mapping */
585 (void)ioctl(0, TCGETA, (char *)&game_termio);
587 /* Force "Ctrl-C" to interupt */
588 game_termio.c_cc[VINTR] = (char)3;
590 /* Force "Ctrl-Z" to suspend */
591 game_termio.c_cc[VSUSP] = (char)26;
593 /* Hack -- Leave "VSTART/VSTOP" alone */
595 /* Disable the standard control characters */
596 game_termio.c_cc[VQUIT] = (char)-1;
597 game_termio.c_cc[VERASE] = (char)-1;
598 game_termio.c_cc[VKILL] = (char)-1;
599 game_termio.c_cc[VEOF] = (char)-1;
600 game_termio.c_cc[VEOL] = (char)-1;
602 /* Normally, block until a character is read */
603 game_termio.c_cc[VMIN] = 1;
604 game_termio.c_cc[VTIME] = 0;
606 /* Hack -- Turn off "echo" and "canonical" mode */
607 game_termio.c_lflag &= ~(ECHO | ICANON);
609 /* Turn off flow control */
610 game_termio.c_iflag &= ~IXON;
616 /* Get the default game characters */
617 (void)ioctl(0, TIOCGETP, (char *)&game_ttyb);
618 (void)ioctl(0, TIOCGETC, (char *)&game_tchars);
619 (void)ioctl(0, TIOCGLTC, (char *)&game_ltchars);
620 (void)ioctl(0, TIOCLGET, (char *)&game_local_chars);
622 /* Force interupt (^C) */
623 game_tchars.t_intrc = (char)3;
625 /* Force start/stop (^Q, ^S) */
626 game_tchars.t_startc = (char)17;
627 game_tchars.t_stopc = (char)19;
629 /* Cancel some things */
630 game_tchars.t_quitc = (char)-1;
631 game_tchars.t_eofc = (char)-1;
632 game_tchars.t_brkc = (char)-1;
634 /* Force suspend (^Z) */
635 game_ltchars.t_suspc = (char)26;
637 /* Cancel some things */
638 game_ltchars.t_dsuspc = (char)-1;
639 game_ltchars.t_rprntc = (char)-1;
640 game_ltchars.t_flushc = (char)-1;
641 game_ltchars.t_werasc = (char)-1;
642 game_ltchars.t_lnextc = (char)-1;
644 /* XXX XXX XXX XXX Verify this before use */
645 /* Hack -- Turn off "echo" and "canonical" mode */
646 /* game_termios.c_lflag &= ~(ECHO | ICANON); */
647 game_ttyb.flag &= ~(ECHO | ICANON);
649 /* XXX XXX XXX Should maybe turn off flow control too. How? */
657 static errr Term_xtra_cap_alive(int v)
664 /* Hack -- make sure the cursor is visible */
667 /* Move to bottom right */
668 do_move(0, rows - 1, 0, rows - 1);
670 /* Go to normal keymap mode */
673 /* No longer active */
682 /* Hack -- restore the cursor location */
683 do_move(curx, cury, curx, cury);
685 /* Hack -- restore the cursor visibility */
688 /* Go to angband keymap mode */
691 /* Now we are active */
702 static errr Term_xtra_cap_event(int v)
709 /* Wait for one byte */
712 /* Hack -- Handle "errors" */
713 if ((i <= 0) && (errno != EINTR))
714 exit_game_panic(p_ptr);
719 /* Get the current flags for stdin */
720 if ((arg = fcntl(0, F_GETFL, 0)) < 1)
723 /* Tell stdin not to block */
724 if (fcntl(0, F_SETFL, arg | O_NDELAY) < 0)
727 /* Read one byte, if possible */
730 /* Replace the flags for stdin */
731 if (fcntl(0, F_SETFL, arg))
736 if ((i != 1) || (!buf[0]))
739 /* Enqueue the keypress */
740 Term_keypress(buf[0]);
747 * Actually move the hardware cursor
749 static errr Term_curs_cap(int x, int y)
751 /* Literally move the cursor */
752 do_move(curx, cury, x, y);
754 /* Save the cursor location */
763 * Erase a grid of space
765 * XXX XXX XXX Note that we will never be asked to clear the
766 * bottom line all the way to the bottom right edge, since we
767 * have set the "avoid the bottom right corner" flag.
769 static errr Term_wipe_cap(int x, int y, int n)
773 /* Place the cursor */
776 /* Wipe to end of line */
783 for (dx = 0; dx < n; ++dx) {
794 * Place some text on the screen using an attribute
796 static errr Term_text_cap(int x, int y, int n, byte a, concptr s)
800 /* Move the cursor */
803 /* Dump the text, advance the cursor */
804 for (i = 0; s[i]; i++) {
808 /* Advance cursor 'X', and wrap */
809 if (++curx >= cols) {
810 /* Reset cursor 'X' */
813 /* Hack -- Advance cursor 'Y', and wrap */
824 * Handle a "special request"
826 static errr Term_xtra_cap(int n, int v)
828 /* Analyze the request */
830 /* Clear the screen */
831 case TERM_XTRA_CLEAR:
837 case TERM_XTRA_NOISE:
838 (void)write(1, "\007", 1);
841 /* Change the cursor visibility */
842 case TERM_XTRA_SHAPE:
848 case TERM_XTRA_ALIVE:
849 return (Term_xtra_cap_alive(v));
852 case TERM_XTRA_EVENT:
853 return (Term_xtra_cap_event(v));
856 case TERM_XTRA_FLUSH:
857 while (!Term_xtra_cap_event(false))
862 case TERM_XTRA_DELAY:
872 * Init a "term" for this file
874 static void Term_init_cap(term *t)
879 /* Assume cursor at top left */
883 /* Assume visible cursor */
886 /* Clear the screen */
889 /* Hack -- visible cursor */
897 * Nuke a "term" for this file
899 static void Term_nuke_cap(term *t)
904 /* Hack -- make sure the cursor is visible */
907 /* Move to bottom right */
908 do_move(0, rows - 1, 0, rows - 1);
913 /* No longer active */
918 * Prepare this file for Angband usage
922 term *t = &term_screen_body;
926 /* Initialize the screen */
930 /* Hack -- Require large screen, or Quit with message */
931 if ((rows < 24) || (cols < 80))
932 quit("Screen too small!");
934 /*** Prepare to play ***/
936 /* Extract the normal keymap */
937 keymap_norm_prepare();
939 /* Extract the game keymap */
940 keymap_game_prepare();
942 /* Hack -- activate the game keymap */
945 /* Hack -- Do NOT buffer stdout */
946 setbuf(stdout, nullptr);
948 /*** Now prepare the term ***/
950 /* Initialize the term */
951 term_init(t, 80, 24, 256);
953 /* Avoid the bottom right corner */
954 t->icky_corner = true;
956 /* Erase with "white space" */
957 t->attr_blank = TERM_WHITE;
961 t->init_hook = Term_init_cap;
962 t->nuke_hook = Term_nuke_cap;
964 /* Set some more hooks */
965 t->text_hook = Term_text_cap;
966 t->wipe_hook = Term_wipe_cap;
967 t->curs_hook = Term_curs_cap;
968 t->xtra_hook = Term_xtra_cap;
974 Term_activate(term_screen);