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-2025
   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.h"  // tty_set_normal_attrs()
  61 #include "mouse.h"  // use_mouse_p
  62 #include "win.h"
  63 
  64 /*** global variables ****************************************************************************/
  65 
  66 int mc_tty_frm[MC_TTY_FRM_MAX];
  67 
  68 /*** file scope macro definitions ****************************************************************/
  69 
  70 /*** file scope type declarations ****************************************************************/
  71 
  72 /*** forward declarations (file scope functions) *************************************************/
  73 
  74 /*** file scope variables ************************************************************************/
  75 
  76 static SIG_ATOMIC_VOLATILE_T got_interrupt = 0;
  77 
  78 /* --------------------------------------------------------------------------------------------- */
  79 /*** file scope functions ************************************************************************/
  80 /* --------------------------------------------------------------------------------------------- */
  81 
  82 static void
  83 sigintr_handler (int signo)
     /* [previous][next][first][last][top][bottom][index][help]  */
  84 {
  85     (void) &signo;
  86     got_interrupt = 1;
  87 }
  88 
  89 /* --------------------------------------------------------------------------------------------- */
  90 /*** public functions ****************************************************************************/
  91 /* --------------------------------------------------------------------------------------------- */
  92 
  93 /**
  94  * Check terminal type. If $TERM is not set or value is empty, mc finishes with EXIT_FAILURE.
  95  *
  96  * @param force_xterm Set forced the XTerm type
  97  *
  98  * @return true if @param force_xterm is true or value of $TERM is one of following:
  99  *         alacritty*
 100  *         contour*
 101  *         dtterm
 102  *         Eterm
 103  *         foot*
 104  *         konsole*
 105  *         rxvt*
 106  *         screen*
 107  *         term*
 108  *         tmux*
 109  */
 110 gboolean
 111 tty_check_xterm_compat (const gboolean force_xterm)
     /* [previous][next][first][last][top][bottom][index][help]  */
 112 {
 113     static const char *xterm_compatible_terminals[] = {
 114         "alacritty",  //
 115         "contour",    //
 116         "dtterm",     //
 117         "Eterm",      //
 118         "foot",       //
 119         "konsole",    //
 120         "rxvt",       //
 121         "screen",     //
 122         "tmux",       //
 123         "xterm",      //
 124         NULL,
 125     };
 126 
 127     const char *termvalue = getenv ("TERM");
 128     if (termvalue == NULL || *termvalue == '\0')
 129     {
 130         fputs (_ ("The TERM environment variable is unset!\n"), stderr);
 131         my_exit (EXIT_FAILURE);
 132     }
 133 
 134     if (force_xterm)
 135         return TRUE;
 136 
 137     for (const char **p = xterm_compatible_terminals; *p != NULL; p++)
 138         if (strncmp (termvalue, *p, strlen (*p)) == 0)
 139             return TRUE;
 140 
 141     return FALSE;
 142 }
 143 /* --------------------------------------------------------------------------------------------- */
 144 
 145 extern void
 146 tty_start_interrupt_key (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 147 {
 148     struct sigaction act;
 149 
 150     memset (&act, 0, sizeof (act));
 151     act.sa_handler = sigintr_handler;
 152     sigemptyset (&act.sa_mask);
 153 #ifdef SA_RESTART
 154     act.sa_flags = SA_RESTART;
 155 #endif
 156     my_sigaction (SIGINT, &act, NULL);
 157 }
 158 
 159 /* --------------------------------------------------------------------------------------------- */
 160 
 161 extern void
 162 tty_enable_interrupt_key (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 163 {
 164     struct sigaction act;
 165 
 166     memset (&act, 0, sizeof (act));
 167     act.sa_handler = sigintr_handler;
 168     sigemptyset (&act.sa_mask);
 169     my_sigaction (SIGINT, &act, NULL);
 170     got_interrupt = 0;
 171 }
 172 
 173 /* --------------------------------------------------------------------------------------------- */
 174 
 175 extern void
 176 tty_disable_interrupt_key (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 177 {
 178     struct sigaction act;
 179 
 180     memset (&act, 0, sizeof (act));
 181     act.sa_handler = SIG_IGN;
 182     sigemptyset (&act.sa_mask);
 183     my_sigaction (SIGINT, &act, NULL);
 184 }
 185 
 186 /* --------------------------------------------------------------------------------------------- */
 187 
 188 extern gboolean
 189 tty_got_interrupt (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 190 {
 191     gboolean rv;
 192 
 193     rv = (got_interrupt != 0);
 194     got_interrupt = 0;
 195     return rv;
 196 }
 197 
 198 /* --------------------------------------------------------------------------------------------- */
 199 
 200 gboolean
 201 tty_got_winch (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 202 {
 203     fd_set fdset;
 204     // instant timeout
 205     struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 };
 206     int ok;
 207 
 208     FD_ZERO (&fdset);
 209     FD_SET (sigwinch_pipe[0], &fdset);
 210 
 211     while ((ok = select (sigwinch_pipe[0] + 1, &fdset, NULL, NULL, &timeout)) < 0)
 212         if (errno != EINTR)
 213         {
 214             perror (_ ("Cannot check SIGWINCH pipe"));
 215             exit (EXIT_FAILURE);
 216         }
 217 
 218     return (ok != 0 && FD_ISSET (sigwinch_pipe[0], &fdset));
 219 }
 220 
 221 /* --------------------------------------------------------------------------------------------- */
 222 
 223 gboolean
 224 tty_flush_winch (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 225 {
 226     ssize_t n;
 227     gboolean ret = FALSE;
 228 
 229     // merge all SIGWINCH events raised to this moment
 230     do
 231     {
 232         char x[16];
 233 
 234         // read multiple events at a time
 235         n = read (sigwinch_pipe[0], &x, sizeof (x));
 236 
 237         // at least one SIGWINCH came
 238         if (n > 0)
 239             ret = TRUE;
 240     }
 241     while (n > 0 || (n == -1 && errno == EINTR));
 242 
 243     return ret;
 244 }
 245 
 246 /* --------------------------------------------------------------------------------------------- */
 247 
 248 void
 249 tty_print_one_hline (gboolean single)
     /* [previous][next][first][last][top][bottom][index][help]  */
 250 {
 251     tty_print_alt_char (ACS_HLINE, single);
 252 }
 253 
 254 /* --------------------------------------------------------------------------------------------- */
 255 
 256 void
 257 tty_print_one_vline (gboolean single)
     /* [previous][next][first][last][top][bottom][index][help]  */
 258 {
 259     tty_print_alt_char (ACS_VLINE, single);
 260 }
 261 
 262 /* --------------------------------------------------------------------------------------------- */
 263 
 264 void
 265 tty_draw_box (int y, int x, int ys, int xs, gboolean single)
     /* [previous][next][first][last][top][bottom][index][help]  */
 266 {
 267     int y2, x2;
 268 
 269     if (ys <= 0 || xs <= 0)
 270         return;
 271 
 272     ys--;
 273     xs--;
 274 
 275     y2 = y + ys;
 276     x2 = x + xs;
 277 
 278     tty_draw_vline (y, x, mc_tty_frm[single ? MC_TTY_FRM_VERT : MC_TTY_FRM_DVERT], ys);
 279     tty_draw_vline (y, x2, mc_tty_frm[single ? MC_TTY_FRM_VERT : MC_TTY_FRM_DVERT], ys);
 280     tty_draw_hline (y, x, mc_tty_frm[single ? MC_TTY_FRM_HORIZ : MC_TTY_FRM_DHORIZ], xs);
 281     tty_draw_hline (y2, x, mc_tty_frm[single ? MC_TTY_FRM_HORIZ : MC_TTY_FRM_DHORIZ], xs);
 282     tty_gotoyx (y, x);
 283     tty_print_alt_char (ACS_ULCORNER, single);
 284     tty_gotoyx (y2, x);
 285     tty_print_alt_char (ACS_LLCORNER, single);
 286     tty_gotoyx (y, x2);
 287     tty_print_alt_char (ACS_URCORNER, single);
 288     tty_gotoyx (y2, x2);
 289     tty_print_alt_char (ACS_LRCORNER, single);
 290 }
 291 
 292 /* --------------------------------------------------------------------------------------------- */
 293 
 294 void
 295 tty_draw_box_shadow (int y, int x, int rows, int cols, int shadow_color)
     /* [previous][next][first][last][top][bottom][index][help]  */
 296 {
 297     // draw right shadow
 298     tty_colorize_area (y + 1, x + cols, rows - 1, 2, shadow_color);
 299     // draw bottom shadow
 300     tty_colorize_area (y + rows, x + 2, 1, cols, shadow_color);
 301 }
 302 
 303 /* --------------------------------------------------------------------------------------------- */
 304 
 305 char *
 306 mc_tty_normalize_from_utf8 (const char *str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 307 {
 308     GIConv conv;
 309     GString *buffer;
 310     const char *_system_codepage = str_detect_termencoding ();
 311 
 312     if (str_isutf8 (_system_codepage))
 313         return g_strdup (str);
 314 
 315     conv = g_iconv_open (_system_codepage, "UTF-8");
 316     if (conv == INVALID_CONV)
 317         return g_strdup (str);
 318 
 319     buffer = g_string_new ("");
 320 
 321     if (str_convert (conv, str, buffer) == ESTR_FAILURE)
 322     {
 323         g_string_free (buffer, TRUE);
 324         str_close_conv (conv);
 325         return g_strdup (str);
 326     }
 327     str_close_conv (conv);
 328 
 329     return g_string_free (buffer, FALSE);
 330 }
 331 
 332 /* --------------------------------------------------------------------------------------------- */
 333 
 334 /** Resize given terminal using TIOCSWINSZ, return ioctl() result */
 335 int
 336 tty_resize (int fd)
     /* [previous][next][first][last][top][bottom][index][help]  */
 337 {
 338 #if defined TIOCSWINSZ
 339     struct winsize tty_size;
 340 
 341     tty_size.ws_row = LINES;
 342     tty_size.ws_col = COLS;
 343     tty_size.ws_xpixel = tty_size.ws_ypixel = 0;
 344 
 345     return ioctl (fd, TIOCSWINSZ, &tty_size);
 346 #else
 347     return 0;
 348 #endif
 349 }
 350 
 351 /* --------------------------------------------------------------------------------------------- */
 352 
 353 /** Clear screen */
 354 void
 355 tty_clear_screen (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 356 {
 357     tty_set_normal_attrs ();
 358     tty_fill_region (0, 0, LINES, COLS, ' ');
 359     tty_refresh ();
 360 }
 361 
 362 /* --------------------------------------------------------------------------------------------- */
 363 
 364 void
 365 tty_init_xterm_support (gboolean is_xterm)
     /* [previous][next][first][last][top][bottom][index][help]  */
 366 {
 367     const char *termvalue;
 368 
 369     termvalue = getenv ("TERM");
 370 
 371     // Check mouse and ca capabilities
 372     /* terminfo/termcap structures have been already initialized,
 373        in slang_init() or/and init_curses()  */
 374     // Check terminfo at first, then check termcap
 375     xmouse_seq = tty_tgetstr ("kmous");
 376     if (xmouse_seq == NULL)
 377         xmouse_seq = tty_tgetstr ("Km");
 378     smcup = tty_tgetstr ("smcup");
 379     if (smcup == NULL)
 380         smcup = tty_tgetstr ("ti");
 381     rmcup = tty_tgetstr ("rmcup");
 382     if (rmcup == NULL)
 383         rmcup = tty_tgetstr ("te");
 384 
 385     if (strcmp (termvalue, "cygwin") == 0)
 386     {
 387         is_xterm = TRUE;
 388         use_mouse_p = MOUSE_DISABLED;
 389     }
 390 
 391     if (is_xterm)
 392     {
 393         // Default to the standard xterm sequence
 394         if (xmouse_seq == NULL)
 395             xmouse_seq = ESC_STR "[M";
 396 
 397         // Enable mouse unless explicitly disabled by --nomouse
 398         if (use_mouse_p != MOUSE_DISABLED)
 399         {
 400             if (mc_global.tty.old_mouse)
 401                 use_mouse_p = MOUSE_XTERM_NORMAL_TRACKING;
 402             else
 403             {
 404                 // FIXME: this dirty hack to set supported type of tracking the mouse
 405                 const char *color_term = getenv ("COLORTERM");
 406                 if (strncmp (termvalue, "rxvt", 4) == 0
 407                     || (color_term != NULL && strncmp (color_term, "rxvt", 4) == 0)
 408                     || strcmp (termvalue, "Eterm") == 0)
 409                     use_mouse_p = MOUSE_XTERM_NORMAL_TRACKING;
 410                 else
 411                     use_mouse_p = MOUSE_XTERM_BUTTON_EVENT_TRACKING;
 412             }
 413         }
 414     }
 415 
 416     /* There's only one termcap entry "kmous", typically containing "\E[M" or "\E[<".
 417      * We need the former in xmouse_seq, the latter in xmouse_extended_seq.
 418      * See tickets 2956, 3954, and 4063 for details. */
 419     if (xmouse_seq != NULL)
 420     {
 421         if (strcmp (xmouse_seq, ESC_STR "[<") == 0)
 422             xmouse_seq = ESC_STR "[M";
 423 
 424         xmouse_extended_seq = ESC_STR "[<";
 425     }
 426 }
 427 
 428 /* --------------------------------------------------------------------------------------------- */

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