2 Interface to the terminal controlling library.
5 Copyright (C) 2005-2024
6 Free Software Foundation, Inc.
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/>.
29 * \brief Source: NCurses-based tty layer of Midnight-commander
37 #ifdef HAVE_SYS_IOCTL_H
38 #include <sys/ioctl.h>
42 #include "lib/global.h"
43 #include "lib/strutil.h" /* str_term_form */
50 #include "tty-internal.h" /* mc_tty_normalize_from_utf8() */
52 #include "color.h" /* tty_setcolor */
53 #include "color-internal.h"
58 /* include at last !!! */
60 #ifdef HAVE_NCURSES_TERM_H
61 #include <ncurses/term.h>
64 #endif /* HAVE_NCURSES_TERM_H */
65 #endif /* WANT_TERM_H */
67 /*** global variables ****************************************************************************/
69 /*** file scope macro definitions ****************************************************************/
72 #define CTRL(x) ((x) & 0x1f)
75 #define yx_in_screen(y, x) \
76 (y >= 0 && y < LINES && x >= 0 && x < COLS)
78 /*** global variables ****************************************************************************/
80 /*** file scope type declarations ****************************************************************/
82 /*** forward declarations (file scope functions) *************************************************/
84 /*** file scope variables ************************************************************************/
86 /* ncurses supports cursor positions only within window */
87 /* We use our own cursor coordinates to support partially visible widgets */
88 static int mc_curs_row
, mc_curs_col
;
90 /* --------------------------------------------------------------------------------------------- */
91 /*** file scope functions ************************************************************************/
92 /* --------------------------------------------------------------------------------------------- */
95 tty_setup_sigwinch (void (*handler
) (int))
97 #if (NCURSES_VERSION_MAJOR >= 4) && defined (SIGWINCH)
98 struct sigaction act
, oact
;
100 memset (&act
, 0, sizeof (act
));
101 act
.sa_handler
= handler
;
102 sigemptyset (&act
.sa_mask
);
104 act
.sa_flags
= SA_RESTART
;
105 #endif /* SA_RESTART */
106 my_sigaction (SIGWINCH
, &act
, &oact
);
107 #endif /* SIGWINCH */
109 tty_create_winch_pipe ();
112 /* --------------------------------------------------------------------------------------------- */
115 sigwinch_handler (int dummy
)
121 n
= write (sigwinch_pipe
[1], "", 1);
125 /* --------------------------------------------------------------------------------------------- */
128 * Get visible part of area.
130 * @returns TRUE if any part of area is in screen bounds, FALSE otherwise.
133 tty_clip (int *y
, int *x
, int *rows
, int *cols
)
155 if (*y
+ *rows
> LINES
)
161 if (*x
+ *cols
> COLS
)
170 /* --------------------------------------------------------------------------------------------- */
171 /*** public functions ****************************************************************************/
172 /* --------------------------------------------------------------------------------------------- */
175 mc_tty_normalize_lines_char (const char *ch
)
180 struct mc_tty_lines_struct
184 } const lines_codes
[] = {
185 {"\342\224\230", ACS_LRCORNER
}, /* ┌ */
186 {"\342\224\224", ACS_LLCORNER
}, /* └ */
187 {"\342\224\220", ACS_URCORNER
}, /* ┐ */
188 {"\342\224\214", ACS_ULCORNER
}, /* ┘ */
189 {"\342\224\234", ACS_LTEE
}, /* ├ */
190 {"\342\224\244", ACS_RTEE
}, /* ┤ */
191 {"\342\224\254", ACS_TTEE
}, /* ┬ */
192 {"\342\224\264", ACS_BTEE
}, /* ┴ */
193 {"\342\224\200", ACS_HLINE
}, /* ─ */
194 {"\342\224\202", ACS_VLINE
}, /* │ */
195 {"\342\224\274", ACS_PLUS
}, /* ┼ */
197 {"\342\225\235", ACS_LRCORNER
| A_BOLD
}, /* ╔ */
198 {"\342\225\232", ACS_LLCORNER
| A_BOLD
}, /* ╚ */
199 {"\342\225\227", ACS_URCORNER
| A_BOLD
}, /* ╗ */
200 {"\342\225\224", ACS_ULCORNER
| A_BOLD
}, /* ╝ */
201 {"\342\225\237", ACS_LTEE
| A_BOLD
}, /* ╟ */
202 {"\342\225\242", ACS_RTEE
| A_BOLD
}, /* ╢ */
203 {"\342\225\244", ACS_TTEE
| A_BOLD
}, /* ╤ */
204 {"\342\225\247", ACS_BTEE
| A_BOLD
}, /* ╧ */
205 {"\342\225\220", ACS_HLINE
| A_BOLD
}, /* ═ */
206 {"\342\225\221", ACS_VLINE
| A_BOLD
}, /* ║ */
214 for (res
= 0; lines_codes
[res
].line
; res
++)
216 if (strcmp (ch
, lines_codes
[res
].line
) == 0)
217 return lines_codes
[res
].line_code
;
220 str2
= mc_tty_normalize_from_utf8 (ch
);
221 res
= g_utf8_get_char_validated (str2
, -1);
224 res
= (unsigned char) str2
[0];
230 /* --------------------------------------------------------------------------------------------- */
233 tty_init (gboolean mouse_enable
, gboolean is_xterm
)
241 * If ncurses exports the ESCDELAY variable, it should be set to
242 * a low value, or you'll experience a delay in processing escape
243 * sequences that are recognized by mc (e.g. Esc-Esc). On the other
244 * hand, making ESCDELAY too small can result in some sequences
245 * (e.g. cursor arrows) being reported as separate keys under heavy
246 * processor load, and this can be a problem if mc hasn't learned
247 * them in the "Learn Keys" dialog. The value is in milliseconds.
250 #endif /* HAVE_ESCDELAY */
252 tcgetattr (STDIN_FILENO
, &mode
);
253 /* use Ctrl-g to generate SIGINT */
254 mode
.c_cc
[VINTR
] = CTRL ('g'); /* ^g */
255 /* disable SIGQUIT to allow use Ctrl-\ key */
256 mode
.c_cc
[VQUIT
] = NULL_VALUE
;
257 tcsetattr (STDIN_FILENO
, TCSANOW
, &mode
);
259 /* curses remembers the "in-program" modes after this call */
262 tty_start_interrupt_key ();
265 use_mouse_p
= MOUSE_DISABLED
;
266 tty_init_xterm_support (is_xterm
); /* do it before tty_enter_ca_mode() call */
267 tty_enter_ca_mode ();
270 keypad (stdscr
, TRUE
);
271 nodelay (stdscr
, FALSE
);
273 tty_setup_sigwinch (sigwinch_handler
);
276 /* --------------------------------------------------------------------------------------------- */
281 tty_destroy_winch_pipe ();
282 tty_reset_shell_mode ();
289 /* --------------------------------------------------------------------------------------------- */
292 tty_enter_ca_mode (void)
294 if (mc_global
.tty
.xterm_flag
&& smcup
!= NULL
)
296 fprintf (stdout
, /* ESC_STR ")0" */ ESC_STR
"7" ESC_STR
"[?47h");
301 /* --------------------------------------------------------------------------------------------- */
304 tty_exit_ca_mode (void)
306 if (mc_global
.tty
.xterm_flag
&& rmcup
!= NULL
)
308 fprintf (stdout
, ESC_STR
"[?47l" ESC_STR
"8" ESC_STR
"[m");
313 /* --------------------------------------------------------------------------------------------- */
316 tty_change_screen_size (void)
318 #if defined(TIOCGWINSZ) && NCURSES_VERSION_MAJOR >= 4
319 struct winsize winsz
;
321 winsz
.ws_col
= winsz
.ws_row
= 0;
323 #ifndef NCURSES_VERSION
328 /* Ioctl on the STDIN_FILENO */
329 ioctl (fileno (stdout
), TIOCGWINSZ
, &winsz
);
330 if (winsz
.ws_col
!= 0 && winsz
.ws_row
!= 0)
332 #if defined(NCURSES_VERSION) && defined(HAVE_RESIZETERM)
333 resizeterm (winsz
.ws_row
, winsz
.ws_col
);
334 clearok (stdscr
, TRUE
); /* sigwinch's should use a semaphore! */
337 LINES
= winsz
.ws_row
;
340 #endif /* defined(TIOCGWINSZ) || NCURSES_VERSION_MAJOR >= 4 */
342 #ifdef ENABLE_SUBSHELL
343 if (mc_global
.tty
.use_subshell
)
344 tty_resize (mc_global
.tty
.subshell_pty
);
348 /* --------------------------------------------------------------------------------------------- */
351 tty_reset_prog_mode (void)
356 /* --------------------------------------------------------------------------------------------- */
359 tty_reset_shell_mode (void)
364 /* --------------------------------------------------------------------------------------------- */
369 raw (); /* FIXME: unneeded? */
373 /* --------------------------------------------------------------------------------------------- */
376 tty_noraw_mode (void)
378 nocbreak (); /* FIXME: unneeded? */
382 /* --------------------------------------------------------------------------------------------- */
390 /* --------------------------------------------------------------------------------------------- */
393 tty_flush_input (void)
398 /* --------------------------------------------------------------------------------------------- */
401 tty_keypad (gboolean set
)
403 keypad (stdscr
, (bool) set
);
406 /* --------------------------------------------------------------------------------------------- */
409 tty_nodelay (gboolean set
)
411 nodelay (stdscr
, (bool) set
);
414 /* --------------------------------------------------------------------------------------------- */
422 /* --------------------------------------------------------------------------------------------- */
425 tty_lowlevel_getch (void)
430 /* --------------------------------------------------------------------------------------------- */
433 tty_reset_screen (void)
438 /* --------------------------------------------------------------------------------------------- */
441 tty_touch_screen (void)
446 /* --------------------------------------------------------------------------------------------- */
449 tty_gotoyx (int y
, int x
)
467 /* --------------------------------------------------------------------------------------------- */
470 tty_getyx (int *py
, int *px
)
476 /* --------------------------------------------------------------------------------------------- */
479 tty_draw_hline (int y
, int x
, int ch
, int len
)
483 if (y
< 0 || y
>= LINES
|| x
>= COLS
)
496 if ((chtype
) ch
== ACS_HLINE
)
497 ch
= mc_tty_frm
[MC_TTY_FRM_HORIZ
];
507 /* --------------------------------------------------------------------------------------------- */
510 tty_draw_vline (int y
, int x
, int ch
, int len
)
514 if (x
< 0 || x
>= COLS
|| y
>= LINES
)
527 if ((chtype
) ch
== ACS_VLINE
)
528 ch
= mc_tty_frm
[MC_TTY_FRM_VERT
];
538 /* --------------------------------------------------------------------------------------------- */
541 tty_fill_region (int y
, int x
, int rows
, int cols
, unsigned char ch
)
545 if (!tty_clip (&y
, &x
, &rows
, &cols
))
548 for (i
= 0; i
< rows
; i
++)
560 /* --------------------------------------------------------------------------------------------- */
563 tty_colorize_area (int y
, int x
, int rows
, int cols
, int color
)
565 #ifdef ENABLE_SHADOWS
567 wchar_t wch
[10]; /* TODO not sure if the length is correct */
571 if (!use_colors
|| !tty_clip (&y
, &x
, &rows
, &cols
))
574 tty_setcolor (color
);
575 ctext
= g_malloc (sizeof (cchar_t
) * (cols
+ 1));
577 for (int row
= 0; row
< rows
; row
++)
579 mvin_wchnstr (y
+ row
, x
, ctext
, cols
);
581 for (int col
= 0; col
< cols
; col
++)
583 getcchar (&ctext
[col
], wch
, &attrs
, &color_pair
, NULL
);
584 setcchar (&ctext
[col
], wch
, attrs
, color
, NULL
);
587 mvadd_wchnstr (y
+ row
, x
, ctext
, cols
);
597 #endif /* ENABLE_SHADOWS */
600 /* --------------------------------------------------------------------------------------------- */
603 tty_set_alt_charset (gboolean alt_charset
)
608 /* --------------------------------------------------------------------------------------------- */
611 tty_display_8bit (gboolean what
)
613 meta (stdscr
, (int) what
);
616 /* --------------------------------------------------------------------------------------------- */
619 tty_print_char (int c
)
621 if (yx_in_screen (mc_curs_row
, mc_curs_col
))
626 /* --------------------------------------------------------------------------------------------- */
629 tty_print_anychar (int c
)
631 if (mc_global
.utf8_display
|| c
> 255)
634 unsigned char str
[UTF8_CHAR_LEN
+ 1];
636 res
= g_unichar_to_utf8 (c
, (char *) str
);
639 if (yx_in_screen (mc_curs_row
, mc_curs_col
))
648 s
= str_term_form ((char *) str
);
650 if (yx_in_screen (mc_curs_row
, mc_curs_col
))
653 if (g_unichar_iswide (c
))
655 else if (!g_unichar_iszerowidth (c
))
661 if (yx_in_screen (mc_curs_row
, mc_curs_col
))
667 /* --------------------------------------------------------------------------------------------- */
670 tty_print_alt_char (int c
, gboolean single
)
672 if (yx_in_screen (mc_curs_row
, mc_curs_col
))
674 if ((chtype
) c
== ACS_VLINE
)
675 c
= mc_tty_frm
[single
? MC_TTY_FRM_VERT
: MC_TTY_FRM_DVERT
];
676 else if ((chtype
) c
== ACS_HLINE
)
677 c
= mc_tty_frm
[single
? MC_TTY_FRM_HORIZ
: MC_TTY_FRM_DHORIZ
];
678 else if ((chtype
) c
== ACS_LTEE
)
679 c
= mc_tty_frm
[single
? MC_TTY_FRM_LEFTMIDDLE
: MC_TTY_FRM_DLEFTMIDDLE
];
680 else if ((chtype
) c
== ACS_RTEE
)
681 c
= mc_tty_frm
[single
? MC_TTY_FRM_RIGHTMIDDLE
: MC_TTY_FRM_DRIGHTMIDDLE
];
682 else if ((chtype
) c
== ACS_ULCORNER
)
683 c
= mc_tty_frm
[single
? MC_TTY_FRM_LEFTTOP
: MC_TTY_FRM_DLEFTTOP
];
684 else if ((chtype
) c
== ACS_LLCORNER
)
685 c
= mc_tty_frm
[single
? MC_TTY_FRM_LEFTBOTTOM
: MC_TTY_FRM_DLEFTBOTTOM
];
686 else if ((chtype
) c
== ACS_URCORNER
)
687 c
= mc_tty_frm
[single
? MC_TTY_FRM_RIGHTTOP
: MC_TTY_FRM_DRIGHTTOP
];
688 else if ((chtype
) c
== ACS_LRCORNER
)
689 c
= mc_tty_frm
[single
? MC_TTY_FRM_RIGHTBOTTOM
: MC_TTY_FRM_DRIGHTBOTTOM
];
690 else if ((chtype
) c
== ACS_PLUS
)
691 c
= mc_tty_frm
[MC_TTY_FRM_CROSS
];
699 /* --------------------------------------------------------------------------------------------- */
702 tty_print_string (const char *s
)
707 s
= str_term_form (s
);
708 len
= str_term_width1 (s
);
710 /* line is upper or below the screen or entire line is before or after screen */
711 if (mc_curs_row
< 0 || mc_curs_row
>= LINES
|| mc_curs_col
+ len
<= 0 || mc_curs_col
>= COLS
)
717 /* skip invisible left part */
720 start
= -mc_curs_col
;
726 if (mc_curs_col
>= COLS
)
727 len
= COLS
- (mc_curs_col
- len
);
729 addstr (str_term_substring (s
, start
, len
));
732 /* --------------------------------------------------------------------------------------------- */
735 tty_printf (const char *fmt
, ...)
738 char buf
[BUF_1K
]; /* FIXME: is it enough? */
740 va_start (args
, fmt
);
741 g_vsnprintf (buf
, sizeof (buf
), fmt
, args
);
743 tty_print_string (buf
);
746 /* --------------------------------------------------------------------------------------------- */
749 tty_tgetstr (const char *cap
)
753 return tgetstr ((NCURSES_CONST
char *) cap
, &unused
);
756 /* --------------------------------------------------------------------------------------------- */
765 /* --------------------------------------------------------------------------------------------- */
773 /* --------------------------------------------------------------------------------------------- */