Ticket #3710: don't parse "window-state-char" and "window-close-char" as colors.
[midnight-commander.git] / lib / tty / tty-ncurses.c
blob9b01ce6fa9de705173314396460fb3c0625dc186
1 /*
2 Interface to the terminal controlling library.
3 Ncurses wrapper.
5 Copyright (C) 2005-2016
6 Free Software Foundation, Inc.
8 Written by:
9 Andrew Borodin <aborodin@vmail.ru>, 2009.
10 Ilia Maslakov <il.smind@gmail.com>, 2009.
12 This file is part of the Midnight Commander.
14 The Midnight Commander is free software: you can redistribute it
15 and/or modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation, either version 3 of the License,
17 or (at your option) any later version.
19 The Midnight Commander is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
24 You should have received a copy of the GNU General Public License
25 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 /** \file
29 * \brief Source: NCurses-based tty layer of Midnight-commander
32 #include <config.h>
34 #include <stdlib.h>
35 #include <stdarg.h>
36 #include <signal.h>
37 #ifdef HAVE_SYS_IOCTL_H
38 #include <sys/ioctl.h>
39 #endif
40 #include <termios.h>
42 #include "lib/global.h"
43 #include "lib/strutil.h" /* str_term_form */
45 #ifndef WANT_TERM_H
46 #define WANT_TERM_H
47 #endif
49 #include "tty-internal.h" /* mc_tty_normalize_from_utf8() */
50 #include "tty.h"
51 #include "color-internal.h"
52 #include "key.h"
53 #include "mouse.h"
54 #include "win.h"
56 /* include at last !!! */
57 #ifdef WANT_TERM_H
58 #ifdef HAVE_NCURSES_TERM_H
59 #include <ncurses/term.h>
60 #else
61 #include <term.h>
62 #endif /* HAVE_NCURSES_TERM_H */
63 #endif /* WANT_TERM_H */
65 /*** global variables ****************************************************************************/
67 /*** file scope macro definitions ****************************************************************/
69 #if !defined(CTRL)
70 #define CTRL(x) ((x) & 0x1f)
71 #endif
73 #define yx_in_screen(y, x) \
74 (y >= 0 && y < LINES && x >= 0 && x < COLS)
76 /*** global variables ****************************************************************************/
78 /*** file scope type declarations ****************************************************************/
80 /*** file scope variables ************************************************************************/
82 /* ncurses supports cursor positions only within window */
83 /* We use our own cursor coordinates to support partially visible widgets */
84 static int mc_curs_row, mc_curs_col;
86 /*** file scope functions ************************************************************************/
87 /* --------------------------------------------------------------------------------------------- */
89 /* --------------------------------------------------------------------------------------------- */
91 static void
92 tty_setup_sigwinch (void (*handler) (int))
94 #if (NCURSES_VERSION_MAJOR >= 4) && defined (SIGWINCH)
95 struct sigaction act, oact;
97 memset (&act, 0, sizeof (act));
98 act.sa_handler = handler;
99 sigemptyset (&act.sa_mask);
100 #ifdef SA_RESTART
101 act.sa_flags = SA_RESTART;
102 #endif /* SA_RESTART */
103 sigaction (SIGWINCH, &act, &oact);
104 #endif /* SIGWINCH */
107 /* --------------------------------------------------------------------------------------------- */
109 static void
110 sigwinch_handler (int dummy)
112 (void) dummy;
114 mc_global.tty.winch_flag = 1;
117 /* --------------------------------------------------------------------------------------------- */
118 /*** public functions ****************************************************************************/
119 /* --------------------------------------------------------------------------------------------- */
122 mc_tty_normalize_lines_char (const char *ch)
124 char *str2;
125 int res;
127 struct mc_tty_lines_struct
129 const char *line;
130 int line_code;
131 } const lines_codes[] = {
132 {"\342\224\230", ACS_LRCORNER}, /* ┌ */
133 {"\342\224\224", ACS_LLCORNER}, /* └ */
134 {"\342\224\220", ACS_URCORNER}, /* ┐ */
135 {"\342\224\214", ACS_ULCORNER}, /* ┘ */
136 {"\342\224\234", ACS_LTEE}, /* ├ */
137 {"\342\224\244", ACS_RTEE}, /* ┤ */
138 {"\342\224\254", ACS_TTEE}, /* ┬ */
139 {"\342\224\264", ACS_BTEE}, /* ┴ */
140 {"\342\224\200", ACS_HLINE}, /* ─ */
141 {"\342\224\202", ACS_VLINE}, /* │ */
142 {"\342\224\274", ACS_PLUS}, /* ┼ */
144 {"\342\225\235", ACS_LRCORNER | A_BOLD}, /* ╔ */
145 {"\342\225\232", ACS_LLCORNER | A_BOLD}, /* ╚ */
146 {"\342\225\227", ACS_URCORNER | A_BOLD}, /* ╗ */
147 {"\342\225\224", ACS_ULCORNER | A_BOLD}, /* ╝ */
148 {"\342\225\237", ACS_LTEE | A_BOLD}, /* ╟ */
149 {"\342\225\242", ACS_RTEE | A_BOLD}, /* ╢ */
150 {"\342\225\244", ACS_TTEE | A_BOLD}, /* ╤ */
151 {"\342\225\247", ACS_BTEE | A_BOLD}, /* ╧ */
152 {"\342\225\220", ACS_HLINE | A_BOLD}, /* ═ */
153 {"\342\225\221", ACS_VLINE | A_BOLD}, /* ║ */
155 {NULL, 0}
158 if (ch == NULL)
159 return (int) ' ';
161 for (res = 0; lines_codes[res].line; res++)
163 if (strcmp (ch, lines_codes[res].line) == 0)
164 return lines_codes[res].line_code;
167 str2 = mc_tty_normalize_from_utf8 (ch);
168 res = g_utf8_get_char_validated (str2, -1);
170 if (res < 0)
171 res = (unsigned char) str2[0];
172 g_free (str2);
174 return res;
177 /* --------------------------------------------------------------------------------------------- */
179 void
180 tty_init (gboolean mouse_enable, gboolean is_xterm)
182 initscr ();
184 #ifdef HAVE_ESCDELAY
186 * If ncurses exports the ESCDELAY variable, it should be set to
187 * a low value, or you'll experience a delay in processing escape
188 * sequences that are recognized by mc (e.g. Esc-Esc). On the other
189 * hand, making ESCDELAY too small can result in some sequences
190 * (e.g. cursor arrows) being reported as separate keys under heavy
191 * processor load, and this can be a problem if mc hasn't learned
192 * them in the "Learn Keys" dialog. The value is in milliseconds.
194 ESCDELAY = 200;
195 #endif /* HAVE_ESCDELAY */
197 #ifdef NCURSES_VERSION
198 /* use Ctrl-g to generate SIGINT */
199 cur_term->Nttyb.c_cc[VINTR] = CTRL ('g'); /* ^g */
200 /* disable SIGQUIT to allow use Ctrl-\ key */
201 cur_term->Nttyb.c_cc[VQUIT] = NULL_VALUE;
202 tcsetattr (cur_term->Filedes, TCSANOW, &cur_term->Nttyb);
203 #else
204 /* other curses implementation (bsd curses, ...) */
206 struct termios mode;
208 tcgetattr (STDIN_FILENO, &mode);
209 /* use Ctrl-g to generate SIGINT */
210 mode.c_cc[VINTR] = CTRL ('g'); /* ^g */
211 /* disable SIGQUIT to allow use Ctrl-\ key */
212 mode.c_cc[VQUIT] = NULL_VALUE;
213 tcsetattr (STDIN_FILENO, TCSANOW, &mode);
215 #endif /* NCURSES_VERSION */
217 tty_start_interrupt_key ();
219 if (!mouse_enable)
220 use_mouse_p = MOUSE_DISABLED;
221 tty_init_xterm_support (is_xterm); /* do it before tty_enter_ca_mode() call */
222 tty_enter_ca_mode ();
223 tty_raw_mode ();
224 noecho ();
225 keypad (stdscr, TRUE);
226 nodelay (stdscr, FALSE);
228 tty_setup_sigwinch (sigwinch_handler);
231 /* --------------------------------------------------------------------------------------------- */
233 void
234 tty_shutdown (void)
236 tty_reset_shell_mode ();
237 tty_noraw_mode ();
238 tty_keypad (FALSE);
239 tty_reset_screen ();
240 tty_exit_ca_mode ();
243 /* --------------------------------------------------------------------------------------------- */
245 void
246 tty_enter_ca_mode (void)
248 if (mc_global.tty.xterm_flag && smcup != NULL)
250 fprintf (stdout, /* ESC_STR ")0" */ ESC_STR "7" ESC_STR "[?47h");
251 fflush (stdout);
255 /* --------------------------------------------------------------------------------------------- */
257 void
258 tty_exit_ca_mode (void)
260 if (mc_global.tty.xterm_flag && rmcup != NULL)
262 fprintf (stdout, ESC_STR "[?47l" ESC_STR "8" ESC_STR "[m");
263 fflush (stdout);
267 /* --------------------------------------------------------------------------------------------- */
269 void
270 tty_change_screen_size (void)
272 #if defined(TIOCGWINSZ) && NCURSES_VERSION_MAJOR >= 4
273 struct winsize winsz;
275 winsz.ws_col = winsz.ws_row = 0;
277 #ifndef NCURSES_VERSION
278 tty_noraw_mode ();
279 tty_reset_screen ();
280 #endif
282 /* Ioctl on the STDIN_FILENO */
283 ioctl (fileno (stdout), TIOCGWINSZ, &winsz);
284 if (winsz.ws_col != 0 && winsz.ws_row != 0)
286 #if defined(NCURSES_VERSION) && defined(HAVE_RESIZETERM)
287 resizeterm (winsz.ws_row, winsz.ws_col);
288 clearok (stdscr, TRUE); /* sigwinch's should use a semaphore! */
289 #else
290 COLS = winsz.ws_col;
291 LINES = winsz.ws_row;
292 #endif
294 #endif /* defined(TIOCGWINSZ) || NCURSES_VERSION_MAJOR >= 4 */
296 #ifdef ENABLE_SUBSHELL
297 if (mc_global.tty.use_subshell)
298 tty_resize (mc_global.tty.subshell_pty);
299 #endif
302 /* --------------------------------------------------------------------------------------------- */
304 void
305 tty_reset_prog_mode (void)
307 reset_prog_mode ();
310 /* --------------------------------------------------------------------------------------------- */
312 void
313 tty_reset_shell_mode (void)
315 reset_shell_mode ();
318 /* --------------------------------------------------------------------------------------------- */
320 void
321 tty_raw_mode (void)
323 raw (); /* FIXME: uneeded? */
324 cbreak ();
327 /* --------------------------------------------------------------------------------------------- */
329 void
330 tty_noraw_mode (void)
332 nocbreak (); /* FIXME: unneeded? */
333 noraw ();
336 /* --------------------------------------------------------------------------------------------- */
338 void
339 tty_noecho (void)
341 noecho ();
344 /* --------------------------------------------------------------------------------------------- */
347 tty_flush_input (void)
349 return flushinp ();
352 /* --------------------------------------------------------------------------------------------- */
354 void
355 tty_keypad (gboolean set)
357 keypad (stdscr, (bool) set);
360 /* --------------------------------------------------------------------------------------------- */
362 void
363 tty_nodelay (gboolean set)
365 nodelay (stdscr, (bool) set);
368 /* --------------------------------------------------------------------------------------------- */
371 tty_baudrate (void)
373 return baudrate ();
376 /* --------------------------------------------------------------------------------------------- */
379 tty_lowlevel_getch (void)
381 return getch ();
384 /* --------------------------------------------------------------------------------------------- */
387 tty_reset_screen (void)
389 return endwin ();
392 /* --------------------------------------------------------------------------------------------- */
394 void
395 tty_touch_screen (void)
397 touchwin (stdscr);
400 /* --------------------------------------------------------------------------------------------- */
402 void
403 tty_gotoyx (int y, int x)
405 mc_curs_row = y;
406 mc_curs_col = x;
408 if (y < 0)
409 y = 0;
410 if (y >= LINES)
411 y = LINES - 1;
413 if (x < 0)
414 x = 0;
415 if (x >= COLS)
416 x = COLS - 1;
418 move (y, x);
421 /* --------------------------------------------------------------------------------------------- */
423 void
424 tty_getyx (int *py, int *px)
426 *py = mc_curs_row;
427 *px = mc_curs_col;
430 /* --------------------------------------------------------------------------------------------- */
432 void
433 tty_draw_hline (int y, int x, int ch, int len)
435 int x1;
437 if (y < 0 || y >= LINES || x >= COLS)
438 return;
440 x1 = x;
442 if (x < 0)
444 len += x;
445 if (len <= 0)
446 return;
447 x = 0;
450 if ((chtype) ch == ACS_HLINE)
451 ch = mc_tty_frm[MC_TTY_FRM_HORIZ];
453 move (y, x);
454 hline (ch, len);
455 move (y, x1);
457 mc_curs_row = y;
458 mc_curs_col = x1;
461 /* --------------------------------------------------------------------------------------------- */
463 void
464 tty_draw_vline (int y, int x, int ch, int len)
466 int y1;
468 if (x < 0 || x >= COLS || y >= LINES)
469 return;
471 y1 = y;
473 if (y < 0)
475 len += y;
476 if (len <= 0)
477 return;
478 y = 0;
481 if ((chtype) ch == ACS_VLINE)
482 ch = mc_tty_frm[MC_TTY_FRM_VERT];
484 move (y, x);
485 vline (ch, len);
486 move (y1, x);
488 mc_curs_row = y1;
489 mc_curs_col = x;
492 /* --------------------------------------------------------------------------------------------- */
494 void
495 tty_fill_region (int y, int x, int rows, int cols, unsigned char ch)
497 int i;
499 if (y < 0)
501 rows += y;
503 if (rows <= 0)
504 return;
506 y = 0;
509 if (x < 0)
511 cols += x;
513 if (cols <= 0)
514 return;
516 x = 0;
519 if (y + rows > LINES)
520 rows = LINES - y;
521 if (x + cols > COLS)
522 cols = COLS - x;
524 for (i = 0; i < rows; i++)
526 move (y + i, x);
527 hline (ch, cols);
530 move (y, x);
532 mc_curs_row = y;
533 mc_curs_col = x;
536 /* --------------------------------------------------------------------------------------------- */
538 void
539 tty_set_alt_charset (gboolean alt_charset)
541 (void) alt_charset;
544 /* --------------------------------------------------------------------------------------------- */
546 void
547 tty_display_8bit (gboolean what)
549 meta (stdscr, (int) what);
552 /* --------------------------------------------------------------------------------------------- */
554 void
555 tty_print_char (int c)
557 if (yx_in_screen (mc_curs_row, mc_curs_col))
558 addch (c);
559 mc_curs_col++;
562 /* --------------------------------------------------------------------------------------------- */
564 void
565 tty_print_anychar (int c)
567 if (mc_global.utf8_display || c > 255)
569 int res;
570 unsigned char str[UTF8_CHAR_LEN + 1];
572 res = g_unichar_to_utf8 (c, (char *) str);
573 if (res == 0)
575 if (yx_in_screen (mc_curs_row, mc_curs_col))
576 addch ('.');
577 mc_curs_col++;
579 else
581 const char *s;
583 str[res] = '\0';
584 s = str_term_form ((char *) str);
586 if (yx_in_screen (mc_curs_row, mc_curs_col))
587 addstr (s);
589 if (g_unichar_iswide (c))
590 mc_curs_col += 2;
591 else if (!g_unichar_iszerowidth (c))
592 mc_curs_col++;
595 else
597 if (yx_in_screen (mc_curs_row, mc_curs_col))
598 addch (c);
599 mc_curs_col++;
603 /* --------------------------------------------------------------------------------------------- */
605 void
606 tty_print_alt_char (int c, gboolean single)
608 if (yx_in_screen (mc_curs_row, mc_curs_col))
610 if ((chtype) c == ACS_VLINE)
611 c = mc_tty_frm[single ? MC_TTY_FRM_VERT : MC_TTY_FRM_DVERT];
612 else if ((chtype) c == ACS_HLINE)
613 c = mc_tty_frm[single ? MC_TTY_FRM_HORIZ : MC_TTY_FRM_DHORIZ];
614 else if ((chtype) c == ACS_LTEE)
615 c = mc_tty_frm[single ? MC_TTY_FRM_LEFTMIDDLE : MC_TTY_FRM_DLEFTMIDDLE];
616 else if ((chtype) c == ACS_RTEE)
617 c = mc_tty_frm[single ? MC_TTY_FRM_RIGHTMIDDLE : MC_TTY_FRM_DRIGHTMIDDLE];
618 else if ((chtype) c == ACS_ULCORNER)
619 c = mc_tty_frm[single ? MC_TTY_FRM_LEFTTOP : MC_TTY_FRM_DLEFTTOP];
620 else if ((chtype) c == ACS_LLCORNER)
621 c = mc_tty_frm[single ? MC_TTY_FRM_LEFTBOTTOM : MC_TTY_FRM_DLEFTBOTTOM];
622 else if ((chtype) c == ACS_URCORNER)
623 c = mc_tty_frm[single ? MC_TTY_FRM_RIGHTTOP : MC_TTY_FRM_DRIGHTTOP];
624 else if ((chtype) c == ACS_LRCORNER)
625 c = mc_tty_frm[single ? MC_TTY_FRM_RIGHTBOTTOM : MC_TTY_FRM_DRIGHTBOTTOM];
626 else if ((chtype) c == ACS_PLUS)
627 c = mc_tty_frm[MC_TTY_FRM_CROSS];
629 addch (c);
632 mc_curs_col++;
635 /* --------------------------------------------------------------------------------------------- */
637 void
638 tty_print_string (const char *s)
640 int len;
641 int start = 0;
643 s = str_term_form (s);
644 len = str_term_width1 (s);
646 /* line is upper or below the screen or entire line is before or after screen */
647 if (mc_curs_row < 0 || mc_curs_row >= LINES || mc_curs_col + len <= 0 || mc_curs_col >= COLS)
649 mc_curs_col += len;
650 return;
653 /* skip invisible left part */
654 if (mc_curs_col < 0)
656 start = -mc_curs_col;
657 len += mc_curs_col;
658 mc_curs_col = 0;
661 mc_curs_col += len;
662 if (mc_curs_col >= COLS)
663 len = COLS - (mc_curs_col - len);
665 addstr (str_term_substring (s, start, len));
668 /* --------------------------------------------------------------------------------------------- */
670 void
671 tty_printf (const char *fmt, ...)
673 va_list args;
674 char buf[BUF_1K]; /* FIXME: is it enough? */
676 va_start (args, fmt);
677 g_vsnprintf (buf, sizeof (buf), fmt, args);
678 va_end (args);
679 tty_print_string (buf);
682 /* --------------------------------------------------------------------------------------------- */
684 char *
685 tty_tgetstr (const char *cap)
687 char *unused = NULL;
689 return tgetstr ((NCURSES_CONST char *) cap, &unused);
692 /* --------------------------------------------------------------------------------------------- */
694 void
695 tty_refresh (void)
697 refresh ();
698 doupdate ();
701 /* --------------------------------------------------------------------------------------------- */
703 void
704 tty_beep (void)
706 beep ();
709 /* --------------------------------------------------------------------------------------------- */