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

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