summaryrefslogtreecommitdiffstats
path: root/lib/tty/tty.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tty/tty.c')
-rw-r--r--lib/tty/tty.c413
1 files changed, 413 insertions, 0 deletions
diff --git a/lib/tty/tty.c b/lib/tty/tty.c
new file mode 100644
index 0000000..60b138e
--- /dev/null
+++ b/lib/tty/tty.c
@@ -0,0 +1,413 @@
+/*
+ Interface to the terminal controlling library.
+
+ Copyright (C) 2005-2022
+ Free Software Foundation, Inc.
+
+ Written by:
+ Roland Illig <roland.illig@gmx.de>, 2005.
+ Andrew Borodin <aborodin@vmail.ru>, 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 tty.c
+ * \brief Source: %interface to the terminal controlling library
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h> /* memset() */
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#else
+#include <sys/time.h>
+#include <sys/types.h>
+#endif
+#include <unistd.h> /* exit() */
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+/* In some systems (like Solaris 11.4 SPARC), TIOCSWINSZ is defined in termios.h */
+#include <termios.h>
+
+#include "lib/global.h"
+#include "lib/strutil.h"
+
+#include "tty.h"
+#include "tty-internal.h"
+#include "color.h" /* tty_set_normal_attrs() */
+#include "mouse.h" /* use_mouse_p */
+#include "win.h"
+
+/*** global variables ****************************************************************************/
+
+int mc_tty_frm[MC_TTY_FRM_MAX];
+
+/*** file scope macro definitions ****************************************************************/
+
+/*** file scope type declarations ****************************************************************/
+
+/*** file scope variables ************************************************************************/
+
+static SIG_ATOMIC_VOLATILE_T got_interrupt = 0;
+
+/*** file scope functions ************************************************************************/
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+sigintr_handler (int signo)
+{
+ (void) &signo;
+ got_interrupt = 1;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/*** public functions ****************************************************************************/
+/* --------------------------------------------------------------------------------------------- */
+
+/**
+ * Check terminal type. If $TERM is not set or value is empty, mc finishes with EXIT_FAILURE.
+ *
+ * @param force_xterm Set forced the XTerm type
+ *
+ * @return true if @param force_xterm is true or value of $TERM is one of following:
+ * term*
+ * konsole*
+ * rxvt*
+ * Eterm
+ * dtterm
+ * alacritty*
+ * foot*
+ * screen*
+ * tmux*
+ * contour*
+ */
+gboolean
+tty_check_term (gboolean force_xterm)
+{
+ const char *termvalue;
+
+ termvalue = getenv ("TERM");
+ if (termvalue == NULL || *termvalue == '\0')
+ {
+ fputs (_("The TERM environment variable is unset!\n"), stderr);
+ exit (EXIT_FAILURE);
+ }
+
+ /* *INDENT-OFF* */
+ return force_xterm
+ || strncmp (termvalue, "xterm", 5) == 0
+ || strncmp (termvalue, "konsole", 7) == 0
+ || strncmp (termvalue, "rxvt", 4) == 0
+ || strcmp (termvalue, "Eterm") == 0
+ || strcmp (termvalue, "dtterm") == 0
+ || strncmp (termvalue, "alacritty", 9) == 0
+ || strncmp (termvalue, "foot", 4) == 0
+ || strncmp (termvalue, "screen", 6) == 0
+ || strncmp (termvalue, "tmux", 4) == 0
+ || strncmp (termvalue, "contour", 7) == 0;
+ /* *INDENT-ON* */
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+extern void
+tty_start_interrupt_key (void)
+{
+ struct sigaction act;
+
+ memset (&act, 0, sizeof (act));
+ act.sa_handler = sigintr_handler;
+ sigemptyset (&act.sa_mask);
+#ifdef SA_RESTART
+ act.sa_flags = SA_RESTART;
+#endif /* SA_RESTART */
+ sigaction (SIGINT, &act, NULL);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+extern void
+tty_enable_interrupt_key (void)
+{
+ struct sigaction act;
+
+ memset (&act, 0, sizeof (act));
+ act.sa_handler = sigintr_handler;
+ sigemptyset (&act.sa_mask);
+ sigaction (SIGINT, &act, NULL);
+ got_interrupt = 0;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+extern void
+tty_disable_interrupt_key (void)
+{
+ struct sigaction act;
+
+ memset (&act, 0, sizeof (act));
+ act.sa_handler = SIG_IGN;
+ sigemptyset (&act.sa_mask);
+ sigaction (SIGINT, &act, NULL);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+extern gboolean
+tty_got_interrupt (void)
+{
+ gboolean rv;
+
+ rv = (got_interrupt != 0);
+ got_interrupt = 0;
+ return rv;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+gboolean
+tty_got_winch (void)
+{
+ fd_set fdset;
+ /* *INDENT-OFF* */
+ /* instant timeout */
+ struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 };
+ /* *INDENT-ON* */
+ int ok;
+
+ FD_ZERO (&fdset);
+ FD_SET (sigwinch_pipe[0], &fdset);
+
+ while ((ok = select (sigwinch_pipe[0] + 1, &fdset, NULL, NULL, &timeout)) < 0)
+ if (errno != EINTR)
+ {
+ perror (_("Cannot check SIGWINCH pipe"));
+ exit (EXIT_FAILURE);
+ }
+
+ return (ok != 0 && FD_ISSET (sigwinch_pipe[0], &fdset));
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_flush_winch (void)
+{
+ ssize_t n;
+
+ /* merge all SIGWINCH events raised to this moment */
+ do
+ {
+ char x[16];
+
+ /* read multiple events at a time */
+ n = read (sigwinch_pipe[0], &x, sizeof (x));
+ }
+ while (n > 0 || (n == -1 && errno == EINTR));
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_print_one_hline (gboolean single)
+{
+ tty_print_alt_char (ACS_HLINE, single);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_print_one_vline (gboolean single)
+{
+ tty_print_alt_char (ACS_VLINE, single);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_draw_box (int y, int x, int ys, int xs, gboolean single)
+{
+ int y2, x2;
+
+ if (ys <= 0 || xs <= 0)
+ return;
+
+ ys--;
+ xs--;
+
+ y2 = y + ys;
+ x2 = x + xs;
+
+ tty_draw_vline (y, x, mc_tty_frm[single ? MC_TTY_FRM_VERT : MC_TTY_FRM_DVERT], ys);
+ tty_draw_vline (y, x2, mc_tty_frm[single ? MC_TTY_FRM_VERT : MC_TTY_FRM_DVERT], ys);
+ tty_draw_hline (y, x, mc_tty_frm[single ? MC_TTY_FRM_HORIZ : MC_TTY_FRM_DHORIZ], xs);
+ tty_draw_hline (y2, x, mc_tty_frm[single ? MC_TTY_FRM_HORIZ : MC_TTY_FRM_DHORIZ], xs);
+ tty_gotoyx (y, x);
+ tty_print_alt_char (ACS_ULCORNER, single);
+ tty_gotoyx (y2, x);
+ tty_print_alt_char (ACS_LLCORNER, single);
+ tty_gotoyx (y, x2);
+ tty_print_alt_char (ACS_URCORNER, single);
+ tty_gotoyx (y2, x2);
+ tty_print_alt_char (ACS_LRCORNER, single);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_draw_box_shadow (int y, int x, int rows, int cols, int shadow_color)
+{
+ /* draw right shadow */
+ tty_colorize_area (y + 1, x + cols, rows - 1, 2, shadow_color);
+ /* draw bottom shadow */
+ tty_colorize_area (y + rows, x + 2, 1, cols, shadow_color);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+char *
+mc_tty_normalize_from_utf8 (const char *str)
+{
+ GIConv conv;
+ GString *buffer;
+ const char *_system_codepage = str_detect_termencoding ();
+
+ if (str_isutf8 (_system_codepage))
+ return g_strdup (str);
+
+ conv = g_iconv_open (_system_codepage, "UTF-8");
+ if (conv == INVALID_CONV)
+ return g_strdup (str);
+
+ buffer = g_string_new ("");
+
+ if (str_convert (conv, str, buffer) == ESTR_FAILURE)
+ {
+ g_string_free (buffer, TRUE);
+ str_close_conv (conv);
+ return g_strdup (str);
+ }
+ str_close_conv (conv);
+
+ return g_string_free (buffer, FALSE);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+/** Resize given terminal using TIOCSWINSZ, return ioctl() result */
+int
+tty_resize (int fd)
+{
+#if defined TIOCSWINSZ
+ struct winsize tty_size;
+
+ tty_size.ws_row = LINES;
+ tty_size.ws_col = COLS;
+ tty_size.ws_xpixel = tty_size.ws_ypixel = 0;
+
+ return ioctl (fd, TIOCSWINSZ, &tty_size);
+#else
+ return 0;
+#endif
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+/** Clear screen */
+void
+tty_clear_screen (void)
+{
+ tty_set_normal_attrs ();
+ tty_fill_region (0, 0, LINES, COLS, ' ');
+ tty_refresh ();
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+tty_init_xterm_support (gboolean is_xterm)
+{
+ const char *termvalue;
+
+ termvalue = getenv ("TERM");
+
+ /* Check mouse and ca capabilities */
+ /* terminfo/termcap structures have been already initialized,
+ in slang_init() or/and init_curses() */
+ /* Check terminfo at first, then check termcap */
+ xmouse_seq = tty_tgetstr ("kmous");
+ if (xmouse_seq == NULL)
+ xmouse_seq = tty_tgetstr ("Km");
+ smcup = tty_tgetstr ("smcup");
+ if (smcup == NULL)
+ smcup = tty_tgetstr ("ti");
+ rmcup = tty_tgetstr ("rmcup");
+ if (rmcup == NULL)
+ rmcup = tty_tgetstr ("te");
+
+ if (strcmp (termvalue, "cygwin") == 0)
+ {
+ is_xterm = TRUE;
+ use_mouse_p = MOUSE_DISABLED;
+ }
+
+ if (is_xterm)
+ {
+ /* Default to the standard xterm sequence */
+ if (xmouse_seq == NULL)
+ xmouse_seq = ESC_STR "[M";
+
+ /* Enable mouse unless explicitly disabled by --nomouse */
+ if (use_mouse_p != MOUSE_DISABLED)
+ {
+ if (mc_global.tty.old_mouse)
+ use_mouse_p = MOUSE_XTERM_NORMAL_TRACKING;
+ else
+ {
+ /* FIXME: this dirty hack to set supported type of tracking the mouse */
+ const char *color_term = getenv ("COLORTERM");
+ if (strncmp (termvalue, "rxvt", 4) == 0 ||
+ (color_term != NULL && strncmp (color_term, "rxvt", 4) == 0) ||
+ strcmp (termvalue, "Eterm") == 0)
+ use_mouse_p = MOUSE_XTERM_NORMAL_TRACKING;
+ else
+ use_mouse_p = MOUSE_XTERM_BUTTON_EVENT_TRACKING;
+ }
+ }
+ }
+
+ /* There's only one termcap entry "kmous", typically containing "\E[M" or "\E[<".
+ * We need the former in xmouse_seq, the latter in xmouse_extended_seq.
+ * See tickets 2956, 3954, and 4063 for details. */
+ if (xmouse_seq != NULL)
+ {
+ if (strcmp (xmouse_seq, ESC_STR "[<") == 0)
+ xmouse_seq = ESC_STR "[M";
+
+ xmouse_extended_seq = ESC_STR "[<";
+ }
+}
+
+/* --------------------------------------------------------------------------------------------- */