diff options
Diffstat (limited to '')
-rw-r--r-- | lib/tty/tty-ncurses.c | 772 |
1 files changed, 772 insertions, 0 deletions
diff --git a/lib/tty/tty-ncurses.c b/lib/tty/tty-ncurses.c new file mode 100644 index 0000000..08f663d --- /dev/null +++ b/lib/tty/tty-ncurses.c @@ -0,0 +1,772 @@ +/* + Interface to the terminal controlling library. + Ncurses wrapper. + + Copyright (C) 2005-2023 + Free Software Foundation, Inc. + + Written by: + Andrew Borodin <aborodin@vmail.ru>, 2009. + Ilia Maslakov <il.smind@gmail.com>, 2009. + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file + * \brief Source: NCurses-based tty layer of Midnight-commander + */ + +#include <config.h> + +#include <stdlib.h> +#include <stdarg.h> +#include <signal.h> +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#include <termios.h> + +#include "lib/global.h" +#include "lib/strutil.h" /* str_term_form */ + +#ifndef WANT_TERM_H +#define WANT_TERM_H +#endif + +#include "tty-internal.h" /* mc_tty_normalize_from_utf8() */ +#include "tty.h" +#include "color.h" /* tty_setcolor */ +#include "color-internal.h" +#include "key.h" +#include "mouse.h" +#include "win.h" + +/* include at last !!! */ +#ifdef WANT_TERM_H +#ifdef HAVE_NCURSES_TERM_H +#include <ncurses/term.h> +#else +#include <term.h> +#endif /* HAVE_NCURSES_TERM_H */ +#endif /* WANT_TERM_H */ + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +#if !defined(CTRL) +#define CTRL(x) ((x) & 0x1f) +#endif + +#define yx_in_screen(y, x) \ + (y >= 0 && y < LINES && x >= 0 && x < COLS) + +/*** global variables ****************************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +/* ncurses supports cursor positions only within window */ +/* We use our own cursor coordinates to support partially visible widgets */ +static int mc_curs_row, mc_curs_col; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static void +tty_setup_sigwinch (void (*handler) (int)) +{ +#if (NCURSES_VERSION_MAJOR >= 4) && defined (SIGWINCH) + struct sigaction act, oact; + + memset (&act, 0, sizeof (act)); + act.sa_handler = handler; + sigemptyset (&act.sa_mask); +#ifdef SA_RESTART + act.sa_flags = SA_RESTART; +#endif /* SA_RESTART */ + sigaction (SIGWINCH, &act, &oact); +#endif /* SIGWINCH */ + + tty_create_winch_pipe (); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +sigwinch_handler (int dummy) +{ + ssize_t n = 0; + + (void) dummy; + + n = write (sigwinch_pipe[1], "", 1); + (void) n; +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Get visible part of area. + * + * @returns TRUE if any part of area is in screen bounds, FALSE otherwise. + */ +static gboolean +tty_clip (int *y, int *x, int *rows, int *cols) +{ + if (*y < 0) + { + *rows += *y; + + if (*rows <= 0) + return FALSE; + + *y = 0; + } + + if (*x < 0) + { + *cols += *x; + + if (*cols <= 0) + return FALSE; + + *x = 0; + } + + if (*y + *rows > LINES) + *rows = LINES - *y; + + if (*rows <= 0) + return FALSE; + + if (*x + *cols > COLS) + *cols = COLS - *x; + + if (*cols <= 0) + return FALSE; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +int +mc_tty_normalize_lines_char (const char *ch) +{ + char *str2; + int res; + + struct mc_tty_lines_struct + { + const char *line; + int line_code; + } const lines_codes[] = { + {"\342\224\230", ACS_LRCORNER}, /* ┌ */ + {"\342\224\224", ACS_LLCORNER}, /* └ */ + {"\342\224\220", ACS_URCORNER}, /* ┐ */ + {"\342\224\214", ACS_ULCORNER}, /* ┘ */ + {"\342\224\234", ACS_LTEE}, /* ├ */ + {"\342\224\244", ACS_RTEE}, /* ┤ */ + {"\342\224\254", ACS_TTEE}, /* ┬ */ + {"\342\224\264", ACS_BTEE}, /* ┴ */ + {"\342\224\200", ACS_HLINE}, /* ─ */ + {"\342\224\202", ACS_VLINE}, /* │ */ + {"\342\224\274", ACS_PLUS}, /* ┼ */ + + {"\342\225\235", ACS_LRCORNER | A_BOLD}, /* ╔ */ + {"\342\225\232", ACS_LLCORNER | A_BOLD}, /* ╚ */ + {"\342\225\227", ACS_URCORNER | A_BOLD}, /* ╗ */ + {"\342\225\224", ACS_ULCORNER | A_BOLD}, /* ╝ */ + {"\342\225\237", ACS_LTEE | A_BOLD}, /* ╟ */ + {"\342\225\242", ACS_RTEE | A_BOLD}, /* ╢ */ + {"\342\225\244", ACS_TTEE | A_BOLD}, /* ╤ */ + {"\342\225\247", ACS_BTEE | A_BOLD}, /* ╧ */ + {"\342\225\220", ACS_HLINE | A_BOLD}, /* ═ */ + {"\342\225\221", ACS_VLINE | A_BOLD}, /* ║ */ + + {NULL, 0} + }; + + if (ch == NULL) + return (int) ' '; + + for (res = 0; lines_codes[res].line; res++) + { + if (strcmp (ch, lines_codes[res].line) == 0) + return lines_codes[res].line_code; + } + + str2 = mc_tty_normalize_from_utf8 (ch); + res = g_utf8_get_char_validated (str2, -1); + + if (res < 0) + res = (unsigned char) str2[0]; + g_free (str2); + + return res; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_init (gboolean mouse_enable, gboolean is_xterm) +{ + struct termios mode; + + initscr (); + +#ifdef HAVE_ESCDELAY + /* + * If ncurses exports the ESCDELAY variable, it should be set to + * a low value, or you'll experience a delay in processing escape + * sequences that are recognized by mc (e.g. Esc-Esc). On the other + * hand, making ESCDELAY too small can result in some sequences + * (e.g. cursor arrows) being reported as separate keys under heavy + * processor load, and this can be a problem if mc hasn't learned + * them in the "Learn Keys" dialog. The value is in milliseconds. + */ + ESCDELAY = 200; +#endif /* HAVE_ESCDELAY */ + + tcgetattr (STDIN_FILENO, &mode); + /* use Ctrl-g to generate SIGINT */ + mode.c_cc[VINTR] = CTRL ('g'); /* ^g */ + /* disable SIGQUIT to allow use Ctrl-\ key */ + mode.c_cc[VQUIT] = NULL_VALUE; + tcsetattr (STDIN_FILENO, TCSANOW, &mode); + + /* curses remembers the "in-program" modes after this call */ + def_prog_mode (); + + tty_start_interrupt_key (); + + if (!mouse_enable) + use_mouse_p = MOUSE_DISABLED; + tty_init_xterm_support (is_xterm); /* do it before tty_enter_ca_mode() call */ + tty_enter_ca_mode (); + tty_raw_mode (); + noecho (); + keypad (stdscr, TRUE); + nodelay (stdscr, FALSE); + + tty_setup_sigwinch (sigwinch_handler); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_shutdown (void) +{ + tty_destroy_winch_pipe (); + tty_reset_shell_mode (); + tty_noraw_mode (); + tty_keypad (FALSE); + tty_reset_screen (); + tty_exit_ca_mode (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_enter_ca_mode (void) +{ + if (mc_global.tty.xterm_flag && smcup != NULL) + { + fprintf (stdout, /* ESC_STR ")0" */ ESC_STR "7" ESC_STR "[?47h"); + fflush (stdout); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_exit_ca_mode (void) +{ + if (mc_global.tty.xterm_flag && rmcup != NULL) + { + fprintf (stdout, ESC_STR "[?47l" ESC_STR "8" ESC_STR "[m"); + fflush (stdout); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_change_screen_size (void) +{ +#if defined(TIOCGWINSZ) && NCURSES_VERSION_MAJOR >= 4 + struct winsize winsz; + + winsz.ws_col = winsz.ws_row = 0; + +#ifndef NCURSES_VERSION + tty_noraw_mode (); + tty_reset_screen (); +#endif + + /* Ioctl on the STDIN_FILENO */ + ioctl (fileno (stdout), TIOCGWINSZ, &winsz); + if (winsz.ws_col != 0 && winsz.ws_row != 0) + { +#if defined(NCURSES_VERSION) && defined(HAVE_RESIZETERM) + resizeterm (winsz.ws_row, winsz.ws_col); + clearok (stdscr, TRUE); /* sigwinch's should use a semaphore! */ +#else + COLS = winsz.ws_col; + LINES = winsz.ws_row; +#endif + } +#endif /* defined(TIOCGWINSZ) || NCURSES_VERSION_MAJOR >= 4 */ + +#ifdef ENABLE_SUBSHELL + if (mc_global.tty.use_subshell) + tty_resize (mc_global.tty.subshell_pty); +#endif +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_reset_prog_mode (void) +{ + reset_prog_mode (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_reset_shell_mode (void) +{ + reset_shell_mode (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_raw_mode (void) +{ + raw (); /* FIXME: unneeded? */ + cbreak (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_noraw_mode (void) +{ + nocbreak (); /* FIXME: unneeded? */ + noraw (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_noecho (void) +{ + noecho (); +} + +/* --------------------------------------------------------------------------------------------- */ + +int +tty_flush_input (void) +{ + return flushinp (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_keypad (gboolean set) +{ + keypad (stdscr, (bool) set); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_nodelay (gboolean set) +{ + nodelay (stdscr, (bool) set); +} + +/* --------------------------------------------------------------------------------------------- */ + +int +tty_baudrate (void) +{ + return baudrate (); +} + +/* --------------------------------------------------------------------------------------------- */ + +int +tty_lowlevel_getch (void) +{ + return getch (); +} + +/* --------------------------------------------------------------------------------------------- */ + +int +tty_reset_screen (void) +{ + return endwin (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_touch_screen (void) +{ + touchwin (stdscr); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_gotoyx (int y, int x) +{ + mc_curs_row = y; + mc_curs_col = x; + + if (y < 0) + y = 0; + if (y >= LINES) + y = LINES - 1; + + if (x < 0) + x = 0; + if (x >= COLS) + x = COLS - 1; + + move (y, x); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_getyx (int *py, int *px) +{ + *py = mc_curs_row; + *px = mc_curs_col; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_draw_hline (int y, int x, int ch, int len) +{ + int x1; + + if (y < 0 || y >= LINES || x >= COLS) + return; + + x1 = x; + + if (x < 0) + { + len += x; + if (len <= 0) + return; + x = 0; + } + + if ((chtype) ch == ACS_HLINE) + ch = mc_tty_frm[MC_TTY_FRM_HORIZ]; + + move (y, x); + hline (ch, len); + move (y, x1); + + mc_curs_row = y; + mc_curs_col = x1; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_draw_vline (int y, int x, int ch, int len) +{ + int y1; + + if (x < 0 || x >= COLS || y >= LINES) + return; + + y1 = y; + + if (y < 0) + { + len += y; + if (len <= 0) + return; + y = 0; + } + + if ((chtype) ch == ACS_VLINE) + ch = mc_tty_frm[MC_TTY_FRM_VERT]; + + move (y, x); + vline (ch, len); + move (y1, x); + + mc_curs_row = y1; + mc_curs_col = x; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_fill_region (int y, int x, int rows, int cols, unsigned char ch) +{ + int i; + + if (!tty_clip (&y, &x, &rows, &cols)) + return; + + for (i = 0; i < rows; i++) + { + move (y + i, x); + hline (ch, cols); + } + + move (y, x); + + mc_curs_row = y; + mc_curs_col = x; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_colorize_area (int y, int x, int rows, int cols, int color) +{ +#ifdef ENABLE_SHADOWS + cchar_t *ctext; + wchar_t wch[10]; /* TODO not sure if the length is correct */ + attr_t attrs; + short color_pair; + + if (!use_colors || !tty_clip (&y, &x, &rows, &cols)) + return; + + tty_setcolor (color); + ctext = g_malloc (sizeof (cchar_t) * (cols + 1)); + + for (int row = 0; row < rows; row++) + { + mvin_wchnstr (y + row, x, ctext, cols); + + for (int col = 0; col < cols; col++) + { + getcchar (&ctext[col], wch, &attrs, &color_pair, NULL); + setcchar (&ctext[col], wch, attrs, color, NULL); + } + + mvadd_wchnstr (y + row, x, ctext, cols); + } + + g_free (ctext); +#else + (void) y; + (void) x; + (void) rows; + (void) cols; + (void) color; +#endif /* ENABLE_SHADOWS */ +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_set_alt_charset (gboolean alt_charset) +{ + (void) alt_charset; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_display_8bit (gboolean what) +{ + meta (stdscr, (int) what); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_print_char (int c) +{ + if (yx_in_screen (mc_curs_row, mc_curs_col)) + addch (c); + mc_curs_col++; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_print_anychar (int c) +{ + if (mc_global.utf8_display || c > 255) + { + int res; + unsigned char str[UTF8_CHAR_LEN + 1]; + + res = g_unichar_to_utf8 (c, (char *) str); + if (res == 0) + { + if (yx_in_screen (mc_curs_row, mc_curs_col)) + addch ('.'); + mc_curs_col++; + } + else + { + const char *s; + + str[res] = '\0'; + s = str_term_form ((char *) str); + + if (yx_in_screen (mc_curs_row, mc_curs_col)) + addstr (s); + + if (g_unichar_iswide (c)) + mc_curs_col += 2; + else if (!g_unichar_iszerowidth (c)) + mc_curs_col++; + } + } + else + { + if (yx_in_screen (mc_curs_row, mc_curs_col)) + addch (c); + mc_curs_col++; + } +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_print_alt_char (int c, gboolean single) +{ + if (yx_in_screen (mc_curs_row, mc_curs_col)) + { + if ((chtype) c == ACS_VLINE) + c = mc_tty_frm[single ? MC_TTY_FRM_VERT : MC_TTY_FRM_DVERT]; + else if ((chtype) c == ACS_HLINE) + c = mc_tty_frm[single ? MC_TTY_FRM_HORIZ : MC_TTY_FRM_DHORIZ]; + else if ((chtype) c == ACS_LTEE) + c = mc_tty_frm[single ? MC_TTY_FRM_LEFTMIDDLE : MC_TTY_FRM_DLEFTMIDDLE]; + else if ((chtype) c == ACS_RTEE) + c = mc_tty_frm[single ? MC_TTY_FRM_RIGHTMIDDLE : MC_TTY_FRM_DRIGHTMIDDLE]; + else if ((chtype) c == ACS_ULCORNER) + c = mc_tty_frm[single ? MC_TTY_FRM_LEFTTOP : MC_TTY_FRM_DLEFTTOP]; + else if ((chtype) c == ACS_LLCORNER) + c = mc_tty_frm[single ? MC_TTY_FRM_LEFTBOTTOM : MC_TTY_FRM_DLEFTBOTTOM]; + else if ((chtype) c == ACS_URCORNER) + c = mc_tty_frm[single ? MC_TTY_FRM_RIGHTTOP : MC_TTY_FRM_DRIGHTTOP]; + else if ((chtype) c == ACS_LRCORNER) + c = mc_tty_frm[single ? MC_TTY_FRM_RIGHTBOTTOM : MC_TTY_FRM_DRIGHTBOTTOM]; + else if ((chtype) c == ACS_PLUS) + c = mc_tty_frm[MC_TTY_FRM_CROSS]; + + addch (c); + } + + mc_curs_col++; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_print_string (const char *s) +{ + int len; + int start = 0; + + s = str_term_form (s); + len = str_term_width1 (s); + + /* line is upper or below the screen or entire line is before or after screen */ + if (mc_curs_row < 0 || mc_curs_row >= LINES || mc_curs_col + len <= 0 || mc_curs_col >= COLS) + { + mc_curs_col += len; + return; + } + + /* skip invisible left part */ + if (mc_curs_col < 0) + { + start = -mc_curs_col; + len += mc_curs_col; + mc_curs_col = 0; + } + + mc_curs_col += len; + if (mc_curs_col >= COLS) + len = COLS - (mc_curs_col - len); + + addstr (str_term_substring (s, start, len)); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_printf (const char *fmt, ...) +{ + va_list args; + char buf[BUF_1K]; /* FIXME: is it enough? */ + + va_start (args, fmt); + g_vsnprintf (buf, sizeof (buf), fmt, args); + va_end (args); + tty_print_string (buf); +} + +/* --------------------------------------------------------------------------------------------- */ + +char * +tty_tgetstr (const char *cap) +{ + char *unused = NULL; + + return tgetstr ((NCURSES_CONST char *) cap, &unused); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_refresh (void) +{ + refresh (); + doupdate (); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tty_beep (void) +{ + beep (); +} + +/* --------------------------------------------------------------------------------------------- */ |