Manual pages: mcmcdiffmceditmcview

root/lib/tty/tty.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. sigintr_handler
  2. tty_check_xterm_compat
  3. tty_start_interrupt_key
  4. tty_enable_interrupt_key
  5. tty_disable_interrupt_key
  6. tty_got_interrupt
  7. tty_got_winch
  8. tty_flush_winch
  9. tty_print_one_hline
  10. tty_print_one_vline
  11. tty_draw_box
  12. tty_draw_box_shadow
  13. mc_tty_normalize_from_utf8
  14. tty_resize
  15. tty_clear_screen
  16. tty_init_xterm_support

   1 /*
   2    Interface to the terminal controlling library.
   3 
   4    Copyright (C) 2005-2026
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Roland Illig <roland.illig@gmx.de>, 2005.
   9    Andrew Borodin <aborodin@vmail.ru>, 2009.
  10 
  11    This file is part of the Midnight Commander.
  12 
  13    The Midnight Commander is free software: you can redistribute it
  14    and/or modify it under the terms of the GNU General Public License as
  15    published by the Free Software Foundation, either version 3 of the License,
  16    or (at your option) any later version.
  17 
  18    The Midnight Commander is distributed in the hope that it will be useful,
  19    but WITHOUT ANY WARRANTY; without even the implied warranty of
  20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21    GNU General Public License for more details.
  22 
  23    You should have received a copy of the GNU General Public License
  24    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  25  */
  26 
  27 /** \file tty.c
  28  *  \brief Source: %interface to the terminal controlling library
  29  */
  30 
  31 #include <config.h>
  32 
  33 #include <errno.h>
  34 #include <signal.h>
  35 #include <stdarg.h>
  36 #include <stdlib.h>
  37 #include <string.h>  // memset()
  38 
  39 #ifdef HAVE_SYS_SELECT_H
  40 #include <sys/select.h>
  41 #else
  42 #include <sys/time.h>
  43 #include <sys/types.h>
  44 #endif
  45 #include <unistd.h>  // exit()
  46 
  47 #ifdef HAVE_SYS_IOCTL_H
  48 #include <sys/ioctl.h>
  49 #endif
  50 
  51 /* In some systems (like Solaris 11.4 SPARC), TIOCSWINSZ is defined in termios.h */
  52 #include <termios.h>
  53 
  54 #include "lib/global.h"
  55 #include "lib/strutil.h"
  56 #include "lib/util.h"
  57 
  58 #include "tty.h"
  59 #include "tty-internal.h"
  60 #include "color-internal.h"
  61 #include "color.h"  // tty_set_normal_attrs()
  62 #include "mouse.h"  // use_mouse_p
  63 #include "win.h"
  64 
  65 /*** global variables ****************************************************************************/
  66 
  67 mc_tty_char_t mc_tty_frm[MC_TTY_FRM_MAX];
  68 
  69 /*** file scope macro definitions ****************************************************************/
  70 
  71 /*** file scope type declarations ****************************************************************/
  72 
  73 /*** forward declarations (file scope functions) *************************************************/
  74 
  75 /*** file scope variables ************************************************************************/
  76 
  77 static SIG_ATOMIC_VOLATILE_T got_interrupt = 0;
  78 
  79 /* --------------------------------------------------------------------------------------------- */
  80 /*** file scope functions ************************************************************************/
  81 /* --------------------------------------------------------------------------------------------- */
  82 
  83 static void
  84 sigintr_handler (int signo)
     /* [previous][next][first][last][top][bottom][index][help]  */
  85 {
  86     (void) &signo;
  87     got_interrupt = 1;
  88 }
  89 
  90 /* --------------------------------------------------------------------------------------------- */
  91 /*** public functions ****************************************************************************/
  92 /* --------------------------------------------------------------------------------------------- */
  93 
  94 /**
  95  * Check terminal type. If $TERM is not set or value is empty, mc finishes with EXIT_FAILURE.
  96  *
  97  * @param force_xterm Set forced the XTerm type
  98  *
  99  * @return true if @param force_xterm is true or value of $TERM is one of following:
 100  *         alacritty*
 101  *         contour*
 102  *         dtterm
 103  *         Eterm
 104  *         foot*
 105  *         konsole*
 106  *         rxvt*
 107  *         screen*
 108  *         term*
 109  *         tmux*
 110  */
 111 gboolean
 112 tty_check_xterm_compat (const gboolean force_xterm)
     /* [previous][next][first][last][top][bottom][index][help]  */
 113 {
 114     static const char *xterm_compatible_terminals[] = {
 115         "alacritty",  //
 116         "contour",    //
 117         "dtterm",     //
 118         "Eterm",      //
 119         "foot",       //
 120         "konsole",    //
 121         "rxvt",       //
 122         "screen",     //
 123         "tmux",       //
 124         "xterm",      //
 125         NULL,
 126     };
 127 
 128     const char *termvalue = getenv ("TERM");
 129     if (termvalue == NULL || *termvalue == '\0')
 130     {
 131         fputs (_ ("The TERM environment variable is unset!\n"), stderr);
 132         my_exit (EXIT_FAILURE);
 133         // my_exit() must terminate this function. Required for test_tty_check_term_unset().
 134         return FALSE;
 135     }
 136 
 137     if (force_xterm)
 138         return TRUE;
 139 
 140     for (const char **p = xterm_compatible_terminals; *p != NULL; p++)
 141         if (strncmp (termvalue, *p, strlen (*p)) == 0)
 142             return TRUE;
 143 
 144     return FALSE;
 145 }
 146 /* --------------------------------------------------------------------------------------------- */
 147 
 148 extern void
 149 tty_start_interrupt_key (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 150 {
 151     struct sigaction act;
 152 
 153     memset (&act, 0, sizeof (act));
 154     act.sa_handler = sigintr_handler;
 155     sigemptyset (&act.sa_mask);
 156 #ifdef SA_RESTART
 157     act.sa_flags = SA_RESTART;
 158 #endif
 159     my_sigaction (SIGINT, &act, NULL);
 160 }
 161 
 162 /* --------------------------------------------------------------------------------------------- */
 163 
 164 extern void
 165 tty_enable_interrupt_key (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 166 {
 167     struct sigaction act;
 168 
 169     memset (&act, 0, sizeof (act));
 170     act.sa_handler = sigintr_handler;
 171     sigemptyset (&act.sa_mask);
 172     my_sigaction (SIGINT, &act, NULL);
 173     got_interrupt = 0;
 174 }
 175 
 176 /* --------------------------------------------------------------------------------------------- */
 177 
 178 extern void
 179 tty_disable_interrupt_key (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 180 {
 181     struct sigaction act;
 182 
 183     memset (&act, 0, sizeof (act));
 184     act.sa_handler = SIG_IGN;
 185     sigemptyset (&act.sa_mask);
 186     my_sigaction (SIGINT, &act, NULL);
 187 }
 188 
 189 /* --------------------------------------------------------------------------------------------- */
 190 
 191 extern gboolean
 192 tty_got_interrupt (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 193 {
 194     gboolean rv;
 195 
 196     rv = (got_interrupt != 0);
 197     got_interrupt = 0;
 198     return rv;
 199 }
 200 
 201 /* --------------------------------------------------------------------------------------------- */
 202 
 203 gboolean
 204 tty_got_winch (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 205 {
 206     fd_set fdset;
 207     // instant timeout
 208     struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 };
 209     int ok;
 210 
 211     FD_ZERO (&fdset);
 212     FD_SET (sigwinch_pipe[0], &fdset);
 213 
 214     while ((ok = select (sigwinch_pipe[0] + 1, &fdset, NULL, NULL, &timeout)) < 0)
 215         if (errno != EINTR)
 216         {
 217             perror (_ ("Cannot check SIGWINCH pipe"));
 218             exit (EXIT_FAILURE);
 219         }
 220 
 221     return (ok != 0 && FD_ISSET (sigwinch_pipe[0], &fdset));
 222 }
 223 
 224 /* --------------------------------------------------------------------------------------------- */
 225 
 226 gboolean
 227 tty_flush_winch (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 228 {
 229     ssize_t n;
 230     gboolean ret = FALSE;
 231 
 232     // merge all SIGWINCH events raised to this moment
 233     do
 234     {
 235         char x[16];
 236 
 237         // read multiple events at a time
 238         n = read (sigwinch_pipe[0], &x, sizeof (x));
 239 
 240         // at least one SIGWINCH came
 241         if (n > 0)
 242             ret = TRUE;
 243     }
 244     while (n > 0 || (n == -1 && errno == EINTR));
 245 
 246     return ret;
 247 }
 248 
 249 /* --------------------------------------------------------------------------------------------- */
 250 
 251 void
 252 tty_print_one_hline (gboolean single)
     /* [previous][next][first][last][top][bottom][index][help]  */
 253 {
 254     tty_print_char (mc_tty_frm[single ? MC_TTY_FRM_HORIZ : MC_TTY_FRM_DHORIZ]);
 255 }
 256 
 257 /* --------------------------------------------------------------------------------------------- */
 258 
 259 void
 260 tty_print_one_vline (gboolean single)
     /* [previous][next][first][last][top][bottom][index][help]  */
 261 {
 262     tty_print_char (mc_tty_frm[single ? MC_TTY_FRM_VERT : MC_TTY_FRM_DVERT]);
 263 }
 264 
 265 /* --------------------------------------------------------------------------------------------- */
 266 
 267 void
 268 tty_draw_box (int y, int x, int ys, int xs, gboolean single)
     /* [previous][next][first][last][top][bottom][index][help]  */
 269 {
 270     int y2, x2;
 271 
 272     if (ys <= 0 || xs <= 0)
 273         return;
 274 
 275     ys--;
 276     xs--;
 277 
 278     y2 = y + ys;
 279     x2 = x + xs;
 280 
 281     tty_draw_vline (y, x, mc_tty_frm[single ? MC_TTY_FRM_VERT : MC_TTY_FRM_DVERT], ys);
 282     tty_draw_vline (y, x2, mc_tty_frm[single ? MC_TTY_FRM_VERT : MC_TTY_FRM_DVERT], ys);
 283     tty_draw_hline (y, x, mc_tty_frm[single ? MC_TTY_FRM_HORIZ : MC_TTY_FRM_DHORIZ], xs);
 284     tty_draw_hline (y2, x, mc_tty_frm[single ? MC_TTY_FRM_HORIZ : MC_TTY_FRM_DHORIZ], xs);
 285     tty_gotoyx (y, x);
 286     tty_print_char (mc_tty_frm[single ? MC_TTY_FRM_LEFTTOP : MC_TTY_FRM_DLEFTTOP]);
 287     tty_gotoyx (y2, x);
 288     tty_print_char (mc_tty_frm[single ? MC_TTY_FRM_LEFTBOTTOM : MC_TTY_FRM_DLEFTBOTTOM]);
 289     tty_gotoyx (y, x2);
 290     tty_print_char (mc_tty_frm[single ? MC_TTY_FRM_RIGHTTOP : MC_TTY_FRM_DRIGHTTOP]);
 291     tty_gotoyx (y2, x2);
 292     tty_print_char (mc_tty_frm[single ? MC_TTY_FRM_RIGHTBOTTOM : MC_TTY_FRM_DRIGHTBOTTOM]);
 293 }
 294 
 295 /* --------------------------------------------------------------------------------------------- */
 296 
 297 void
 298 tty_draw_box_shadow (int y, int x, int rows, int cols, int shadow_color)
     /* [previous][next][first][last][top][bottom][index][help]  */
 299 {
 300     // draw right shadow
 301     tty_colorize_area (y + 1, x + cols, rows - 1, 2, shadow_color);
 302     // draw bottom shadow
 303     tty_colorize_area (y + rows, x + 2, 1, cols, shadow_color);
 304 }
 305 
 306 /* --------------------------------------------------------------------------------------------- */
 307 
 308 char *
 309 mc_tty_normalize_from_utf8 (const char *str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 310 {
 311     GIConv conv;
 312     GString *buffer;
 313     const char *_system_codepage = str_detect_termencoding ();
 314 
 315     if (str_isutf8 (_system_codepage))
 316         return g_strdup (str);
 317 
 318     conv = g_iconv_open (_system_codepage, "UTF-8");
 319     if (conv == INVALID_CONV)
 320         return g_strdup (str);
 321 
 322     buffer = g_string_new ("");
 323 
 324     if (str_convert (conv, str, buffer) == ESTR_FAILURE)
 325     {
 326         g_string_free (buffer, TRUE);
 327         str_close_conv (conv);
 328         return g_strdup (str);
 329     }
 330     str_close_conv (conv);
 331 
 332     return g_string_free (buffer, FALSE);
 333 }
 334 
 335 /* --------------------------------------------------------------------------------------------- */
 336 
 337 /** Resize given terminal using TIOCSWINSZ, return ioctl() result */
 338 int
 339 tty_resize (int fd)
     /* [previous][next][first][last][top][bottom][index][help]  */
 340 {
 341 #if defined TIOCSWINSZ
 342     struct winsize tty_size;
 343 
 344     /* Make sure to copy to the inner terminal all the fields, even the ones we don't care about,
 345      * including ws_xpixel and ws_ypixel. */
 346     if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &tty_size) == -1)
 347         return -1;
 348     return ioctl (fd, TIOCSWINSZ, &tty_size);
 349 #else
 350     return 0;
 351 #endif
 352 }
 353 
 354 /* --------------------------------------------------------------------------------------------- */
 355 
 356 /** Clear screen */
 357 void
 358 tty_clear_screen (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 359 {
 360     tty_set_normal_attrs ();
 361     tty_fill_region (0, 0, LINES, COLS, ' ');
 362     tty_refresh ();
 363 }
 364 
 365 /* --------------------------------------------------------------------------------------------- */
 366 
 367 void
 368 tty_init_xterm_support (gboolean is_xterm)
     /* [previous][next][first][last][top][bottom][index][help]  */
 369 {
 370     const char *termvalue;
 371 
 372     termvalue = getenv ("TERM");
 373 
 374     // Check mouse and ca capabilities
 375     /* terminfo/termcap structures have been already initialized,
 376        in slang_init() or/and init_curses()  */
 377     xmouse_seq = tty_tigetstr ("kmous", "Km");
 378     smcup = tty_tigetstr ("smcup", "ti");
 379     rmcup = tty_tigetstr ("rmcup", "te");
 380 
 381     if (strcmp (termvalue, "cygwin") == 0)
 382     {
 383         is_xterm = TRUE;
 384         use_mouse_p = MOUSE_DISABLED;
 385     }
 386 
 387     if (is_xterm)
 388     {
 389         // Default to the standard xterm sequence
 390         if (xmouse_seq == NULL)
 391             xmouse_seq = ESC_STR "[M";
 392 
 393         // Enable mouse unless explicitly disabled by --nomouse
 394         if (use_mouse_p != MOUSE_DISABLED)
 395         {
 396             if (mc_global.tty.old_mouse)
 397                 use_mouse_p = MOUSE_XTERM_NORMAL_TRACKING;
 398             else
 399             {
 400                 // FIXME: this dirty hack to set supported type of tracking the mouse
 401                 const char *color_term = getenv ("COLORTERM");
 402                 if (strncmp (termvalue, "rxvt", 4) == 0
 403                     || (color_term != NULL && strncmp (color_term, "rxvt", 4) == 0)
 404                     || strcmp (termvalue, "Eterm") == 0)
 405                     use_mouse_p = MOUSE_XTERM_NORMAL_TRACKING;
 406                 else
 407                     use_mouse_p = MOUSE_XTERM_BUTTON_EVENT_TRACKING;
 408             }
 409         }
 410     }
 411 
 412     /* There's only one termcap entry "kmous", typically containing "\E[M" or "\E[<".
 413      * We need the former in xmouse_seq, the latter in xmouse_extended_seq.
 414      * See tickets 2956, 3954, and 4063 for details. */
 415     if (xmouse_seq != NULL)
 416     {
 417         if (strcmp (xmouse_seq, ESC_STR "[<") == 0)
 418         {
 419             xmouse_seq = ESC_STR "[M";
 420             ncurses_key_mouse_means_extended = TRUE;
 421         }
 422 
 423         xmouse_extended_seq = ESC_STR "[<";
 424     }
 425 }
 426 
 427 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */