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

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