Manual pages: mcmcdiffmceditmcview

root/lib/tty/tty-ncurses.c

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

DEFINITIONS

This source file includes following definitions.
  1. tty_setup_sigwinch
  2. sigwinch_handler
  3. tty_clip
  4. get_maybe_acs
  5. addch_maybe_acs
  6. hline_maybe_acs
  7. vline_maybe_acs
  8. tty_init
  9. tty_shutdown
  10. tty_enter_ca_mode
  11. tty_exit_ca_mode
  12. tty_change_screen_size
  13. tty_reset_prog_mode
  14. tty_reset_shell_mode
  15. tty_raw_mode
  16. tty_noraw_mode
  17. tty_noecho
  18. tty_flush_input
  19. tty_keypad
  20. tty_nodelay
  21. tty_baudrate
  22. tty_lowlevel_getch
  23. tty_reset_screen
  24. tty_touch_screen
  25. tty_gotoyx
  26. tty_getyx
  27. tty_draw_hline
  28. tty_draw_vline
  29. tty_fill_region
  30. tty_colorize_area
  31. tty_display_8bit
  32. tty_print_char
  33. tty_print_anychar
  34. tty_print_string
  35. tty_printf
  36. tty_tigetflag
  37. tty_tigetnum
  38. tty_tigetstr
  39. tty_refresh
  40. tty_beep

   1 /*
   2    Interface to the terminal controlling library.
   3    Ncurses wrapper.
   4 
   5    Copyright (C) 2005-2025
   6    Free Software Foundation, Inc.
   7 
   8    Written by:
   9    Andrew Borodin <aborodin@vmail.ru>, 2009.
  10    Ilia Maslakov <il.smind@gmail.com>, 2009.
  11 
  12    This file is part of the Midnight Commander.
  13 
  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.
  18 
  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.
  23 
  24    You should have received a copy of the GNU General Public License
  25    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  26  */
  27 
  28 /** \file
  29  *  \brief Source: NCurses-based tty layer of Midnight-commander
  30  */
  31 
  32 #include <config.h>
  33 
  34 #include <limits.h>  // MB_LEN_MAX
  35 #include <stdlib.h>
  36 #include <stdarg.h>
  37 #include <signal.h>
  38 #ifdef HAVE_SYS_IOCTL_H
  39 #include <sys/ioctl.h>
  40 #endif
  41 #include <termios.h>
  42 
  43 #include "lib/global.h"
  44 #include "lib/strutil.h"  // str_term_form
  45 #include "lib/util.h"
  46 
  47 #include "tty-internal.h"  // mc_tty_normalize_from_utf8()
  48 #include "tty.h"
  49 #include "color.h"  // tty_setcolor
  50 #include "color-internal.h"
  51 #include "key.h"
  52 #include "mouse.h"
  53 #include "win.h"
  54 
  55 /* include at last !!! */
  56 #ifdef HAVE_NCURSESW_TERM_H
  57 #include <ncursesw/term.h>
  58 #elif defined HAVE_NCURSES_TERM_H
  59 #include <ncurses/term.h>
  60 #else
  61 #include <term.h>
  62 #endif
  63 
  64 /*** global variables ****************************************************************************/
  65 
  66 gboolean ncurses_koi8r_double_line_bug;
  67 
  68 /*** file scope macro definitions ****************************************************************/
  69 
  70 #if !defined(CTRL)
  71 #define CTRL(x) ((x) & 0x1f)
  72 #endif
  73 
  74 #define yx_in_screen(y, x) (y >= 0 && y < LINES && x >= 0 && x < COLS)
  75 
  76 /*** global variables ****************************************************************************/
  77 
  78 /*** file scope type declarations ****************************************************************/
  79 
  80 /*** forward declarations (file scope functions) *************************************************/
  81 
  82 /*** file scope variables ************************************************************************/
  83 
  84 /* ncurses supports cursor positions only within window */
  85 /* We use our own cursor coordinates to support partially visible widgets */
  86 static int mc_curs_row, mc_curs_col;
  87 
  88 /* --------------------------------------------------------------------------------------------- */
  89 /*** file scope functions ************************************************************************/
  90 /* --------------------------------------------------------------------------------------------- */
  91 
  92 static void
  93 tty_setup_sigwinch (void (*handler) (int))
     /* [previous][next][first][last][top][bottom][index][help]  */
  94 {
  95 #if (NCURSES_VERSION_MAJOR >= 4) && defined(SIGWINCH)
  96     struct sigaction act, oact;
  97 
  98     memset (&act, 0, sizeof (act));
  99     act.sa_handler = handler;
 100     sigemptyset (&act.sa_mask);
 101 #ifdef SA_RESTART
 102     act.sa_flags = SA_RESTART;
 103 #endif
 104     my_sigaction (SIGWINCH, &act, &oact);
 105 #endif
 106 
 107     tty_create_winch_pipe ();
 108 }
 109 
 110 /* --------------------------------------------------------------------------------------------- */
 111 
 112 static void
 113 sigwinch_handler (int dummy)
     /* [previous][next][first][last][top][bottom][index][help]  */
 114 {
 115     ssize_t n = 0;
 116 
 117     (void) dummy;
 118 
 119     n = write (sigwinch_pipe[1], "", 1);
 120     (void) n;
 121 }
 122 
 123 /* --------------------------------------------------------------------------------------------- */
 124 
 125 /**
 126  * Get visible part of area.
 127  *
 128  * @return TRUE if any part of area is in screen bounds, FALSE otherwise.
 129  */
 130 static gboolean
 131 tty_clip (int *y, int *x, int *rows, int *cols)
     /* [previous][next][first][last][top][bottom][index][help]  */
 132 {
 133     if (*y < 0)
 134     {
 135         *rows += *y;
 136 
 137         if (*rows <= 0)
 138             return FALSE;
 139 
 140         *y = 0;
 141     }
 142 
 143     if (*x < 0)
 144     {
 145         *cols += *x;
 146 
 147         if (*cols <= 0)
 148             return FALSE;
 149 
 150         *x = 0;
 151     }
 152 
 153     if (*y + *rows > LINES)
 154         *rows = LINES - *y;
 155 
 156     if (*rows <= 0)
 157         return FALSE;
 158 
 159     if (*x + *cols > COLS)
 160         *cols = COLS - *x;
 161 
 162     if (*cols <= 0)
 163         return FALSE;
 164 
 165     return TRUE;
 166 }
 167 
 168 /* --------------------------------------------------------------------------------------------- */
 169 
 170 static chtype
 171 get_maybe_acs (mc_tty_char_t ch)
     /* [previous][next][first][last][top][bottom][index][help]  */
 172 {
 173     switch (ch)
 174     {
 175     case MC_ACS_HLINE:
 176         return ACS_HLINE;
 177     case MC_ACS_VLINE:
 178         return ACS_VLINE;
 179     case MC_ACS_ULCORNER:
 180         return ACS_ULCORNER;
 181     case MC_ACS_URCORNER:
 182         return ACS_URCORNER;
 183     case MC_ACS_LLCORNER:
 184         return ACS_LLCORNER;
 185     case MC_ACS_LRCORNER:
 186         return ACS_LRCORNER;
 187     case MC_ACS_LTEE:
 188         return ACS_LTEE;
 189     case MC_ACS_RTEE:
 190         return ACS_RTEE;
 191     case MC_ACS_TTEE:
 192         return ACS_TTEE;
 193     case MC_ACS_BTEE:
 194         return ACS_BTEE;
 195     case MC_ACS_PLUS:
 196         return ACS_PLUS;
 197 
 198     default:
 199         return ch;
 200     }
 201 }
 202 
 203 /* --------------------------------------------------------------------------------------------- */
 204 
 205 static inline void
 206 addch_maybe_acs (mc_tty_char_t ch)
     /* [previous][next][first][last][top][bottom][index][help]  */
 207 {
 208 #ifdef HAVE_NCURSES_WIDECHAR
 209     if (mc_global.utf8_display)
 210     {
 211         wchar_t wch[2] = { ch, 0 };
 212         cchar_t ctext;
 213 
 214         setcchar (&ctext, wch, 0, 0, NULL);
 215         add_wch (&ctext);
 216     }
 217     else
 218 #endif
 219         addch (get_maybe_acs (ch));
 220 }
 221 
 222 /* --------------------------------------------------------------------------------------------- */
 223 
 224 static inline void
 225 hline_maybe_acs (mc_tty_char_t ch, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 226 {
 227 #ifdef HAVE_NCURSES_WIDECHAR
 228     if (mc_global.utf8_display)
 229     {
 230         wchar_t wch[2] = { ch, 0 };
 231         cchar_t ctext;
 232 
 233         setcchar (&ctext, wch, 0, 0, NULL);
 234         hline_set (&ctext, len);
 235     }
 236     else
 237 #endif
 238         hline (get_maybe_acs (ch), len);
 239 }
 240 
 241 /* --------------------------------------------------------------------------------------------- */
 242 
 243 static inline void
 244 vline_maybe_acs (mc_tty_char_t ch, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 245 {
 246 #ifdef HAVE_NCURSES_WIDECHAR
 247     if (mc_global.utf8_display)
 248     {
 249         wchar_t wch[2] = { ch, 0 };
 250         cchar_t ctext;
 251 
 252         setcchar (&ctext, wch, 0, 0, NULL);
 253         vline_set (&ctext, len);
 254     }
 255     else
 256 #endif
 257         vline (get_maybe_acs (ch), len);
 258 }
 259 
 260 /* --------------------------------------------------------------------------------------------- */
 261 /*** public functions ****************************************************************************/
 262 /* --------------------------------------------------------------------------------------------- */
 263 
 264 void
 265 tty_init (gboolean mouse_enable, gboolean is_xterm)
     /* [previous][next][first][last][top][bottom][index][help]  */
 266 {
 267     struct termios mode;
 268 
 269     // ncurses versions before 6.5-20251115 are buggy in KOI8-R locale, see mc #4799.
 270     // Note: This check will fail at ncurses version 10. We can surely remove this workaround then.
 271     const char *ncurses_ver = curses_version ();
 272     ncurses_koi8r_double_line_bug =
 273         (strncmp (ncurses_ver, "ncurses ", 8) == 0 && strcmp (ncurses_ver + 8, "6.5.20251115") < 0);
 274 
 275     initscr ();
 276 
 277 #ifdef HAVE_ESCDELAY
 278     /*
 279      * If ncurses exports the ESCDELAY variable, it should be set to
 280      * a low value, or you'll experience a delay in processing escape
 281      * sequences that are recognized by mc (e.g. Esc-Esc).  On the other
 282      * hand, making ESCDELAY too small can result in some sequences
 283      * (e.g. cursor arrows) being reported as separate keys under heavy
 284      * processor load, and this can be a problem if mc hasn't learned
 285      * them in the "Learn Keys" dialog.  The value is in milliseconds.
 286      */
 287     ESCDELAY = 200;
 288 #endif
 289 
 290     tcgetattr (STDIN_FILENO, &mode);
 291     // use Ctrl-g to generate SIGINT
 292     mode.c_cc[VINTR] = CTRL ('g');  // ^g
 293     // disable SIGQUIT to allow use Ctrl-\ key
 294     mode.c_cc[VQUIT] = NULL_VALUE;
 295     tcsetattr (STDIN_FILENO, TCSANOW, &mode);
 296 
 297     // curses remembers the "in-program" modes after this call
 298     def_prog_mode ();
 299 
 300     tty_start_interrupt_key ();
 301 
 302     if (!mouse_enable)
 303         use_mouse_p = MOUSE_DISABLED;
 304     tty_init_xterm_support (is_xterm);  // do it before tty_enter_ca_mode() call
 305     tty_enter_ca_mode ();
 306     tty_raw_mode ();
 307     noecho ();
 308     keypad (stdscr, TRUE);
 309     nodelay (stdscr, FALSE);
 310 
 311     tty_setup_sigwinch (sigwinch_handler);
 312 }
 313 
 314 /* --------------------------------------------------------------------------------------------- */
 315 
 316 void
 317 tty_shutdown (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 318 {
 319     tty_destroy_winch_pipe ();
 320     tty_reset_shell_mode ();
 321     tty_noraw_mode ();
 322     tty_keypad (FALSE);
 323     tty_reset_screen ();
 324     tty_exit_ca_mode ();
 325 }
 326 
 327 /* --------------------------------------------------------------------------------------------- */
 328 
 329 void
 330 tty_enter_ca_mode (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 331 {
 332     if (mc_global.tty.xterm_flag && smcup != NULL)
 333     {
 334         fprintf (stdout, ESC_STR "7" ESC_STR "[?47h");
 335         fflush (stdout);
 336     }
 337 }
 338 
 339 /* --------------------------------------------------------------------------------------------- */
 340 
 341 void
 342 tty_exit_ca_mode (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 343 {
 344     if (mc_global.tty.xterm_flag && rmcup != NULL)
 345     {
 346         fprintf (stdout, ESC_STR "[?47l" ESC_STR "8" ESC_STR "[m");
 347         fflush (stdout);
 348     }
 349 }
 350 
 351 /* --------------------------------------------------------------------------------------------- */
 352 
 353 void
 354 tty_change_screen_size (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 355 {
 356 #if defined(TIOCGWINSZ) && NCURSES_VERSION_MAJOR >= 4
 357     struct winsize winsz;
 358 
 359     winsz.ws_col = winsz.ws_row = 0;
 360 
 361 #ifndef NCURSES_VERSION
 362     tty_noraw_mode ();
 363     tty_reset_screen ();
 364 #endif
 365 
 366     // Ioctl on the STDIN_FILENO
 367     ioctl (fileno (stdout), TIOCGWINSZ, &winsz);
 368     if (winsz.ws_col != 0 && winsz.ws_row != 0)
 369     {
 370 #if defined(NCURSES_VERSION) && defined(HAVE_RESIZETERM)
 371         resizeterm (winsz.ws_row, winsz.ws_col);
 372         clearok (stdscr, TRUE);  // sigwinch's should use a semaphore!
 373 #else
 374         COLS = winsz.ws_col;
 375         LINES = winsz.ws_row;
 376 #endif
 377     }
 378 #endif
 379 
 380 #ifdef ENABLE_SUBSHELL
 381     if (mc_global.tty.use_subshell)
 382         tty_resize (mc_global.tty.subshell_pty);
 383 #endif
 384 }
 385 
 386 /* --------------------------------------------------------------------------------------------- */
 387 
 388 void
 389 tty_reset_prog_mode (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 390 {
 391     reset_prog_mode ();
 392 }
 393 
 394 /* --------------------------------------------------------------------------------------------- */
 395 
 396 void
 397 tty_reset_shell_mode (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 398 {
 399     reset_shell_mode ();
 400 }
 401 
 402 /* --------------------------------------------------------------------------------------------- */
 403 
 404 void
 405 tty_raw_mode (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 406 {
 407     raw ();  // FIXME: unneeded?
 408     cbreak ();
 409 }
 410 
 411 /* --------------------------------------------------------------------------------------------- */
 412 
 413 void
 414 tty_noraw_mode (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 415 {
 416     nocbreak ();  // FIXME: unneeded?
 417     noraw ();
 418 }
 419 
 420 /* --------------------------------------------------------------------------------------------- */
 421 
 422 void
 423 tty_noecho (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 424 {
 425     noecho ();
 426 }
 427 
 428 /* --------------------------------------------------------------------------------------------- */
 429 
 430 int
 431 tty_flush_input (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 432 {
 433     return flushinp ();
 434 }
 435 
 436 /* --------------------------------------------------------------------------------------------- */
 437 
 438 void
 439 tty_keypad (gboolean set)
     /* [previous][next][first][last][top][bottom][index][help]  */
 440 {
 441     keypad (stdscr, (bool) set);
 442 }
 443 
 444 /* --------------------------------------------------------------------------------------------- */
 445 
 446 void
 447 tty_nodelay (gboolean set)
     /* [previous][next][first][last][top][bottom][index][help]  */
 448 {
 449     nodelay (stdscr, (bool) set);
 450 }
 451 
 452 /* --------------------------------------------------------------------------------------------- */
 453 
 454 int
 455 tty_baudrate (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 456 {
 457     return baudrate ();
 458 }
 459 
 460 /* --------------------------------------------------------------------------------------------- */
 461 
 462 int
 463 tty_lowlevel_getch (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 464 {
 465     return getch ();
 466 }
 467 
 468 /* --------------------------------------------------------------------------------------------- */
 469 
 470 int
 471 tty_reset_screen (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 472 {
 473     return endwin ();
 474 }
 475 
 476 /* --------------------------------------------------------------------------------------------- */
 477 
 478 void
 479 tty_touch_screen (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 480 {
 481     touchwin (stdscr);
 482 }
 483 
 484 /* --------------------------------------------------------------------------------------------- */
 485 
 486 void
 487 tty_gotoyx (int y, int x)
     /* [previous][next][first][last][top][bottom][index][help]  */
 488 {
 489     mc_curs_row = y;
 490     mc_curs_col = x;
 491 
 492     if (y < 0)
 493         y = 0;
 494     if (y >= LINES)
 495         y = LINES - 1;
 496 
 497     if (x < 0)
 498         x = 0;
 499     if (x >= COLS)
 500         x = COLS - 1;
 501 
 502     move (y, x);
 503 }
 504 
 505 /* --------------------------------------------------------------------------------------------- */
 506 
 507 void
 508 tty_getyx (int *py, int *px)
     /* [previous][next][first][last][top][bottom][index][help]  */
 509 {
 510     *py = mc_curs_row;
 511     *px = mc_curs_col;
 512 }
 513 
 514 /* --------------------------------------------------------------------------------------------- */
 515 
 516 void
 517 tty_draw_hline (int y, int x, mc_tty_char_t ch, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 518 {
 519     int x1;
 520 
 521     if (y < 0 || y >= LINES || x >= COLS)
 522         return;
 523 
 524     x1 = x;
 525 
 526     if (x < 0)
 527     {
 528         len += x;
 529         if (len <= 0)
 530             return;
 531         x = 0;
 532     }
 533 
 534     move (y, x);
 535     hline_maybe_acs (ch, len);
 536     move (y, x1);
 537 
 538     mc_curs_row = y;
 539     mc_curs_col = x1;
 540 }
 541 
 542 /* --------------------------------------------------------------------------------------------- */
 543 
 544 void
 545 tty_draw_vline (int y, int x, mc_tty_char_t ch, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 546 {
 547     int y1;
 548 
 549     if (x < 0 || x >= COLS || y >= LINES)
 550         return;
 551 
 552     y1 = y;
 553 
 554     if (y < 0)
 555     {
 556         len += y;
 557         if (len <= 0)
 558             return;
 559         y = 0;
 560     }
 561 
 562     move (y, x);
 563     vline_maybe_acs (ch, len);
 564     move (y1, x);
 565 
 566     mc_curs_row = y1;
 567     mc_curs_col = x;
 568 }
 569 
 570 /* --------------------------------------------------------------------------------------------- */
 571 
 572 void
 573 tty_fill_region (int y, int x, int rows, int cols, unsigned char ch)
     /* [previous][next][first][last][top][bottom][index][help]  */
 574 {
 575     int i;
 576 
 577     if (!tty_clip (&y, &x, &rows, &cols))
 578         return;
 579 
 580     for (i = 0; i < rows; i++)
 581     {
 582         move (y + i, x);
 583         hline (ch, cols);
 584     }
 585 
 586     move (y, x);
 587 
 588     mc_curs_row = y;
 589     mc_curs_col = x;
 590 }
 591 
 592 /* --------------------------------------------------------------------------------------------- */
 593 
 594 void
 595 tty_colorize_area (int y, int x, int rows, int cols, int color)
     /* [previous][next][first][last][top][bottom][index][help]  */
 596 {
 597 #ifdef ENABLE_SHADOWS
 598     cchar_t *ctext;
 599     wchar_t wch[10];  // TODO not sure if the length is correct
 600     attr_t attrs;
 601     short color_pair;
 602 
 603     if (!use_colors || !tty_clip (&y, &x, &rows, &cols))
 604         return;
 605 
 606     color = tty_maybe_map_color (color);
 607     tty_setcolor (color);
 608     ctext = g_malloc (sizeof (cchar_t) * (cols + 1));
 609 
 610     for (int row = 0; row < rows; row++)
 611     {
 612         mvin_wchnstr (y + row, x, ctext, cols);
 613 
 614         for (int col = 0; col < cols; col++)
 615         {
 616             getcchar (&ctext[col], wch, &attrs, &color_pair, NULL);
 617             setcchar (&ctext[col], wch, attrs, color, NULL);
 618         }
 619 
 620         mvadd_wchnstr (y + row, x, ctext, cols);
 621     }
 622 
 623     g_free (ctext);
 624 #else
 625     (void) y;
 626     (void) x;
 627     (void) rows;
 628     (void) cols;
 629     (void) color;
 630 #endif
 631 }
 632 
 633 /* --------------------------------------------------------------------------------------------- */
 634 
 635 void
 636 tty_display_8bit (gboolean what)
     /* [previous][next][first][last][top][bottom][index][help]  */
 637 {
 638     meta (stdscr, (int) what);
 639 }
 640 
 641 /* --------------------------------------------------------------------------------------------- */
 642 
 643 void
 644 tty_print_char (mc_tty_char_t c)
     /* [previous][next][first][last][top][bottom][index][help]  */
 645 {
 646     if (yx_in_screen (mc_curs_row, mc_curs_col))
 647         addch_maybe_acs (c);
 648     mc_curs_col++;
 649 }
 650 
 651 /* --------------------------------------------------------------------------------------------- */
 652 
 653 void
 654 tty_print_anychar (mc_tty_char_t c)
     /* [previous][next][first][last][top][bottom][index][help]  */
 655 {
 656     if (mc_global.utf8_display || c > 255)
 657     {
 658         int res;
 659         unsigned char str[MB_LEN_MAX + 1];
 660 
 661         res = g_unichar_to_utf8 (c, (char *) str);
 662         if (res == 0)
 663         {
 664             if (yx_in_screen (mc_curs_row, mc_curs_col))
 665                 addch_maybe_acs ('.');
 666             mc_curs_col++;
 667         }
 668         else
 669         {
 670             const char *s;
 671 
 672             str[res] = '\0';
 673             s = str_term_form ((char *) str);
 674 
 675             if (yx_in_screen (mc_curs_row, mc_curs_col))
 676                 addstr (s);
 677 
 678             if (g_unichar_iswide (c))
 679                 mc_curs_col += 2;
 680             else if (!g_unichar_iszerowidth (c))
 681                 mc_curs_col++;
 682         }
 683     }
 684     else
 685     {
 686         if (yx_in_screen (mc_curs_row, mc_curs_col))
 687             addch_maybe_acs (c);
 688         mc_curs_col++;
 689     }
 690 }
 691 
 692 /* --------------------------------------------------------------------------------------------- */
 693 
 694 void
 695 tty_print_string (const char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 696 {
 697     int len;
 698     int start = 0;
 699 
 700     s = str_term_form (s);
 701     len = str_term_width1 (s);
 702 
 703     // line is upper or below the screen or entire line is before or after screen
 704     if (mc_curs_row < 0 || mc_curs_row >= LINES || mc_curs_col + len <= 0 || mc_curs_col >= COLS)
 705     {
 706         mc_curs_col += len;
 707         return;
 708     }
 709 
 710     // skip invisible left part
 711     if (mc_curs_col < 0)
 712     {
 713         start = -mc_curs_col;
 714         len += mc_curs_col;
 715         mc_curs_col = 0;
 716     }
 717 
 718     mc_curs_col += len;
 719     if (mc_curs_col >= COLS)
 720         len = COLS - (mc_curs_col - len);
 721 
 722     addstr (str_term_substring (s, start, len));
 723 }
 724 
 725 /* --------------------------------------------------------------------------------------------- */
 726 
 727 void
 728 tty_printf (const char *fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help]  */
 729 {
 730     va_list args;
 731     char buf[BUF_1K];  // FIXME: is it enough?
 732 
 733     va_start (args, fmt);
 734     g_vsnprintf (buf, sizeof (buf), fmt, args);
 735     va_end (args);
 736     tty_print_string (buf);
 737 }
 738 
 739 /* --------------------------------------------------------------------------------------------- */
 740 
 741 int
 742 tty_tigetflag (const char *terminfo_cap, MC_UNUSED const char *termcap_cap)
     /* [previous][next][first][last][top][bottom][index][help]  */
 743 {
 744     return tigetflag ((NCURSES_CONST char *) terminfo_cap);
 745 }
 746 
 747 /* --------------------------------------------------------------------------------------------- */
 748 
 749 int
 750 tty_tigetnum (const char *terminfo_cap, MC_UNUSED const char *termcap_cap)
     /* [previous][next][first][last][top][bottom][index][help]  */
 751 {
 752     return tigetnum ((NCURSES_CONST char *) terminfo_cap);
 753 }
 754 
 755 /* --------------------------------------------------------------------------------------------- */
 756 
 757 char *
 758 tty_tigetstr (const char *terminfo_cap, MC_UNUSED const char *termcap_cap)
     /* [previous][next][first][last][top][bottom][index][help]  */
 759 {
 760     return tigetstr ((NCURSES_CONST char *) terminfo_cap);
 761 }
 762 
 763 /* --------------------------------------------------------------------------------------------- */
 764 
 765 void
 766 tty_refresh (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 767 {
 768     refresh ();
 769     doupdate ();
 770 }
 771 
 772 /* --------------------------------------------------------------------------------------------- */
 773 
 774 void
 775 tty_beep (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 776 {
 777     beep ();
 778 }
 779 
 780 /* --------------------------------------------------------------------------------------------- */

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