123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755 |
- /*
- Interface to the terminal controlling library.
- Slang wrapper.
- Copyright (C) 2005-2024
- Free Software Foundation, Inc.
- Written by:
- Andrew Borodin <aborodin@vmail.ru>, 2009
- Egmont Koblinger <egmont@gmail.com>, 2010
- This file is part of the Midnight Commander.
- The Midnight Commander is free software: you can redistribute it
- and/or modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation, either version 3 of the License,
- or (at your option) any later version.
- The Midnight Commander is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /** \file
- * \brief Source: S-Lang-based tty layer of Midnight Commander
- */
- #include <config.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/types.h> /* size_t */
- #include <unistd.h>
- #ifdef HAVE_SYS_IOCTL_H
- #include <sys/ioctl.h>
- #endif
- #include <termios.h>
- #include "lib/global.h"
- #include "lib/strutil.h" /* str_term_form */
- #include "lib/util.h" /* is_printable() */
- #include "tty-internal.h" /* mc_tty_normalize_from_utf8() */
- #include "tty.h"
- #include "color.h"
- #include "color-slang.h"
- #include "color-internal.h"
- #include "mouse.h" /* Gpm_Event is required in key.h */
- #include "key.h" /* define_sequence */
- #include "win.h"
- /*** global variables ****************************************************************************/
- /* If true program softkeys (HP terminals only) on startup and after every
- command ran in the subshell to the description found in the termcap/terminfo
- database */
- int reset_hp_softkeys = 0;
- /*** file scope macro definitions ****************************************************************/
- #ifndef SLTT_MAX_SCREEN_COLS
- #define SLTT_MAX_SCREEN_COLS 512
- #endif
- #ifndef SLTT_MAX_SCREEN_ROWS
- #define SLTT_MAX_SCREEN_ROWS 512
- #endif
- /*** file scope type declarations ****************************************************************/
- /*** forward declarations (file scope functions) *************************************************/
- /*** file scope variables ************************************************************************/
- /* Various saved termios settings that we control here */
- static struct termios boot_mode;
- static struct termios new_mode;
- /* Controls whether we should wait for input in tty_lowlevel_getch */
- static gboolean no_slang_delay;
- static gboolean slsmg_active = FALSE;
- /* This table describes which capabilities we want and which values we
- * assign to them.
- */
- static const struct
- {
- int key_code;
- const char *key_name;
- } key_table[] = {
- /* *INDENT-OFF* */
- { KEY_F (0), "k0" }, { KEY_F (1), "k1" }, { KEY_F (2), "k2" }, { KEY_F (3), "k3" },
- { KEY_F (4), "k4" }, { KEY_F (5), "k5" }, { KEY_F (6), "k6" }, { KEY_F (7), "k7" },
- { KEY_F (8), "k8" }, { KEY_F (9), "k9" }, { KEY_F (10), "k;" }, { KEY_F (11), "F1" },
- { KEY_F (12), "F2" }, { KEY_F (13), "F3" }, { KEY_F (14), "F4" }, { KEY_F (15), "F5" },
- { KEY_F (16), "F6" }, { KEY_F (17), "F7" }, { KEY_F (18), "F8" }, { KEY_F (19), "F9" },
- { KEY_F (20), "FA" }, { KEY_IC, "kI" }, { KEY_NPAGE, "kN" }, { KEY_PPAGE, "kP" },
- { KEY_LEFT, "kl" }, { KEY_RIGHT, "kr" }, { KEY_UP, "ku" }, { KEY_DOWN, "kd" },
- { KEY_DC, "kD" }, { KEY_BACKSPACE, "kb" }, { KEY_HOME, "kh" }, { KEY_END, "@7" },
- { 0, NULL }
- /* *INDENT-ON* */
- };
- /* --------------------------------------------------------------------------------------------- */
- /*** file scope functions ************************************************************************/
- /* --------------------------------------------------------------------------------------------- */
- static void
- tty_setup_sigwinch (void (*handler) (int))
- {
- (void) SLsignal (SIGWINCH, handler);
- tty_create_winch_pipe ();
- }
- /* --------------------------------------------------------------------------------------------- */
- static void
- sigwinch_handler (int dummy)
- {
- ssize_t n = 0;
- (void) dummy;
- n = write (sigwinch_pipe[1], "", 1);
- (void) n;
- (void) SLsignal (SIGWINCH, sigwinch_handler);
- }
- /* --------------------------------------------------------------------------------------------- */
- /* HP Terminals have capabilities (pfkey, pfloc, pfx) to program function keys.
- elm 2.4pl15 invoked with the -K option utilizes these softkeys and the
- consequence is that function keys don't work in MC sometimes...
- Unfortunately I don't now the one and only escape sequence to turn off.
- softkeys (elm uses three different capabilities to turn on softkeys and two.
- capabilities to turn them off)..
- Among other things elm uses the pair we already use in slang_keypad. That's.
- the reason why I call slang_reset_softkeys from slang_keypad. In lack of
- something better the softkeys are programmed to their defaults from the
- termcap/terminfo database.
- The escape sequence to program the softkeys is taken from elm and it is.
- hardcoded because neither slang nor ncurses 4.1 know how to 'printf' this.
- sequence. -- Norbert
- */
- static void
- slang_reset_softkeys (void)
- {
- int key;
- static const char display[] = " ";
- char tmp[BUF_SMALL];
- for (key = 1; key < 9; key++)
- {
- char *send;
- g_snprintf (tmp, sizeof (tmp), "k%d", key);
- send = SLtt_tgetstr (tmp);
- if (send != NULL)
- {
- g_snprintf (tmp, sizeof (tmp), ESC_STR "&f%dk%dd%dL%s%s", key,
- (int) (sizeof (display) - 1), (int) strlen (send), display, send);
- SLtt_write_string (tmp);
- }
- }
- }
- /* --------------------------------------------------------------------------------------------- */
- static void
- do_define_key (int code, const char *strcap)
- {
- char *seq;
- seq = SLtt_tgetstr ((SLFUTURE_CONST char *) strcap);
- if (seq != NULL)
- define_sequence (code, seq, MCKEY_NOACTION);
- }
- /* --------------------------------------------------------------------------------------------- */
- static void
- load_terminfo_keys (void)
- {
- int i;
- for (i = 0; key_table[i].key_code; i++)
- do_define_key (key_table[i].key_code, key_table[i].key_name);
- }
- /* --------------------------------------------------------------------------------------------- */
- /*** public functions ****************************************************************************/
- /* --------------------------------------------------------------------------------------------- */
- int
- mc_tty_normalize_lines_char (const char *str)
- {
- char *str2;
- int res;
- struct mc_tty_lines_struct
- {
- const char *line;
- int line_code;
- } const lines_codes[] = { { "\342\224\214", SLSMG_ULCORN_CHAR },
- { "\342\224\220", SLSMG_URCORN_CHAR },
- { "\342\224\224", SLSMG_LLCORN_CHAR },
- { "\342\224\230", SLSMG_LRCORN_CHAR },
- { "\342\224\234", SLSMG_LTEE_CHAR },
- { "\342\224\244", SLSMG_RTEE_CHAR },
- { "\342\224\254", SLSMG_UTEE_CHAR },
- { "\342\224\264", SLSMG_DTEE_CHAR },
- { "\342\224\200", SLSMG_HLINE_CHAR },
- { "\342\224\202", SLSMG_VLINE_CHAR },
- { "\342\224\274", SLSMG_PLUS_CHAR },
- { NULL, 0 } };
- if (!str)
- return (int) ' ';
- for (res = 0; lines_codes[res].line; res++)
- {
- if (strcmp (str, lines_codes[res].line) == 0)
- return lines_codes[res].line_code;
- }
- str2 = mc_tty_normalize_from_utf8 (str);
- res = g_utf8_get_char_validated (str2, -1);
- if (res < 0)
- res = (unsigned char) str2[0];
- g_free (str2);
- return res;
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_init (gboolean mouse_enable, gboolean is_xterm)
- {
- SLtt_Ignore_Beep = 1;
- SLutf8_enable (-1); /* has to be called first before any of the other functions. */
- SLtt_get_terminfo ();
- /*
- * If the terminal in not in terminfo but begins with a well-known
- * string such as "linux" or "xterm" S-Lang will go on, but the
- * terminal size and several other variables won't be initialized
- * (as of S-Lang 1.4.4). Detect it and abort. Also detect extremely
- * small screen dimensions.
- */
- if ((COLS < 10)
- || (LINES < 5)
- #if SLANG_VERSION < 20303
- /* Beginning from pre2.3.3-8 (55f58798c267d76a1b93d0d916027b71a10ac1ee),
- these limitations were eliminated. */
- || (COLS > SLTT_MAX_SCREEN_COLS) || (LINES > SLTT_MAX_SCREEN_ROWS)
- #endif
- )
- {
- fprintf (stderr,
- _ ("Screen size %dx%d is not supported.\n"
- "Check the TERM environment variable.\n"),
- COLS, LINES);
- exit (EXIT_FAILURE);
- }
- tcgetattr (fileno (stdin), &boot_mode);
- /* 255 = ignore abort char; XCTRL('g') for abort char = ^g */
- SLang_init_tty (XCTRL ('g'), 1, 0);
- if (mc_global.tty.ugly_line_drawing)
- SLtt_Has_Alt_Charset = 0;
- tcgetattr (SLang_TT_Read_FD, &new_mode);
- tty_reset_prog_mode ();
- load_terminfo_keys ();
- SLtt_Blink_Mode = (tty_use_256colors (NULL) || tty_use_truecolors (NULL)) ? 1 : 0;
- tty_start_interrupt_key ();
- /* It's the small part from the previous init_key() */
- init_key_input_fd ();
- /* For 8-bit locales, NCurses handles 154 (0x9A) symbol properly, while S-Lang
- * requires SLsmg_Display_Eight_Bit >= 154 (OR manual filtering if xterm display
- * detected - but checking TERM would fail under screen, OR running xterm
- * with allowC1Printable).
- */
- tty_display_8bit (FALSE);
- SLsmg_init_smg ();
- slsmg_active = TRUE;
- if (!mouse_enable)
- use_mouse_p = MOUSE_DISABLED;
- tty_init_xterm_support (is_xterm); /* do it before tty_enter_ca_mode() call */
- tty_enter_ca_mode ();
- tty_keypad (TRUE);
- tty_nodelay (FALSE);
- tty_setup_sigwinch (sigwinch_handler);
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_shutdown (void)
- {
- char *op_cap;
- tty_destroy_winch_pipe ();
- tty_reset_shell_mode ();
- tty_noraw_mode ();
- tty_keypad (FALSE);
- tty_reset_screen ();
- tty_exit_ca_mode ();
- SLang_reset_tty ();
- slsmg_active = FALSE;
- /* Load the op capability to reset the colors to those that were
- * active when the program was started up
- */
- op_cap = SLtt_tgetstr ((SLFUTURE_CONST char *) "op");
- if (op_cap != NULL)
- {
- fputs (op_cap, stdout);
- fflush (stdout);
- }
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_enter_ca_mode (void)
- {
- /* S-Lang handles alternate screen switching and cursor position saving */
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_exit_ca_mode (void)
- {
- /* S-Lang handles alternate screen switching and cursor position restoring */
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_change_screen_size (void)
- {
- SLtt_get_screen_size ();
- if (slsmg_active)
- SLsmg_reinit_smg ();
- #ifdef ENABLE_SUBSHELL
- if (mc_global.tty.use_subshell)
- tty_resize (mc_global.tty.subshell_pty);
- #endif
- }
- /* --------------------------------------------------------------------------------------------- */
- /* Done each time we come back from done mode */
- void
- tty_reset_prog_mode (void)
- {
- tcsetattr (SLang_TT_Read_FD, TCSANOW, &new_mode);
- SLsmg_init_smg ();
- slsmg_active = TRUE;
- SLsmg_touch_lines (0, LINES);
- }
- /* --------------------------------------------------------------------------------------------- */
- /* Called each time we want to shutdown slang screen manager */
- void
- tty_reset_shell_mode (void)
- {
- tcsetattr (SLang_TT_Read_FD, TCSANOW, &boot_mode);
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_raw_mode (void)
- {
- tcsetattr (SLang_TT_Read_FD, TCSANOW, &new_mode);
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_noraw_mode (void)
- {
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_noecho (void)
- {
- }
- /* --------------------------------------------------------------------------------------------- */
- int
- tty_flush_input (void)
- {
- return 0; /* OK */
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_keypad (gboolean set)
- {
- char *keypad_string;
- keypad_string = SLtt_tgetstr ((SLFUTURE_CONST char *) (set ? "ks" : "ke"));
- if (keypad_string != NULL)
- SLtt_write_string (keypad_string);
- if (set && reset_hp_softkeys)
- slang_reset_softkeys ();
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_nodelay (gboolean set)
- {
- no_slang_delay = set;
- }
- /* --------------------------------------------------------------------------------------------- */
- int
- tty_baudrate (void)
- {
- return SLang_TT_Baud_Rate;
- }
- /* --------------------------------------------------------------------------------------------- */
- int
- tty_lowlevel_getch (void)
- {
- int c;
- if (no_slang_delay && (SLang_input_pending (0) == 0))
- return -1;
- c = SLang_getkey ();
- if (c == SLANG_GETKEY_ERROR)
- {
- fprintf (stderr,
- "SLang_getkey returned SLANG_GETKEY_ERROR\n"
- "Assuming EOF on stdin and exiting\n");
- exit (EXIT_FAILURE);
- }
- return c;
- }
- /* --------------------------------------------------------------------------------------------- */
- int
- tty_reset_screen (void)
- {
- SLsmg_reset_smg ();
- slsmg_active = FALSE;
- return 0; /* OK */
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_touch_screen (void)
- {
- SLsmg_touch_lines (0, LINES);
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_gotoyx (int y, int x)
- {
- SLsmg_gotorc (y, x);
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_getyx (int *py, int *px)
- {
- *py = SLsmg_get_row ();
- *px = SLsmg_get_column ();
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_draw_hline (int y, int x, int ch, int len)
- {
- int x1;
- if (y < 0 || y >= LINES || x >= COLS)
- return;
- x1 = x;
- if (x < 0)
- {
- len += x;
- if (len <= 0)
- return;
- x = 0;
- }
- if (ch == ACS_HLINE)
- ch = mc_tty_frm[MC_TTY_FRM_HORIZ];
- if (ch == 0)
- ch = ACS_HLINE;
- SLsmg_gotorc (y, x);
- if (ch == ACS_HLINE)
- SLsmg_draw_hline (len);
- else
- while (len-- != 0)
- tty_print_char (ch);
- SLsmg_gotorc (y, x1);
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_draw_vline (int y, int x, int ch, int len)
- {
- int y1;
- if (x < 0 || x >= COLS || y >= LINES)
- return;
- y1 = y;
- if (y < 0)
- {
- len += y;
- if (len <= 0)
- return;
- y = 0;
- }
- if (ch == ACS_VLINE)
- ch = mc_tty_frm[MC_TTY_FRM_VERT];
- if (ch == 0)
- ch = ACS_VLINE;
- SLsmg_gotorc (y, x);
- if (ch == ACS_VLINE)
- SLsmg_draw_vline (len);
- else
- {
- int pos = 0;
- while (len-- != 0)
- {
- SLsmg_gotorc (y + pos, x);
- tty_print_char (ch);
- pos++;
- }
- }
- SLsmg_gotorc (y1, x);
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_fill_region (int y, int x, int rows, int cols, unsigned char ch)
- {
- SLsmg_fill_region (y, x, rows, cols, ch);
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_colorize_area (int y, int x, int rows, int cols, int color)
- {
- if (use_colors)
- SLsmg_set_color_in_region (color, y, x, rows, cols);
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_set_alt_charset (gboolean alt_charset)
- {
- SLsmg_set_char_set ((int) alt_charset);
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_display_8bit (gboolean what)
- {
- SLsmg_Display_Eight_Bit = what ? 128 : 160;
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_print_char (int c)
- {
- SLsmg_write_char ((SLwchar_Type) ((unsigned int) c));
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_print_alt_char (int c, gboolean single)
- {
- #define DRAW(x, y) \
- (x == y) ? SLsmg_draw_object (SLsmg_get_row (), SLsmg_get_column (), x) \
- : SLsmg_write_char ((unsigned int) y)
- switch (c)
- {
- case ACS_VLINE:
- DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_VERT : MC_TTY_FRM_DVERT]);
- break;
- case ACS_HLINE:
- DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_HORIZ : MC_TTY_FRM_DHORIZ]);
- break;
- case ACS_LTEE:
- DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTMIDDLE : MC_TTY_FRM_DLEFTMIDDLE]);
- break;
- case ACS_RTEE:
- DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTMIDDLE : MC_TTY_FRM_DRIGHTMIDDLE]);
- break;
- case ACS_TTEE:
- DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_TOPMIDDLE : MC_TTY_FRM_DTOPMIDDLE]);
- break;
- case ACS_BTEE:
- DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_BOTTOMMIDDLE : MC_TTY_FRM_DBOTTOMMIDDLE]);
- break;
- case ACS_ULCORNER:
- DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTTOP : MC_TTY_FRM_DLEFTTOP]);
- break;
- case ACS_LLCORNER:
- DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTBOTTOM : MC_TTY_FRM_DLEFTBOTTOM]);
- break;
- case ACS_URCORNER:
- DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTTOP : MC_TTY_FRM_DRIGHTTOP]);
- break;
- case ACS_LRCORNER:
- DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTBOTTOM : MC_TTY_FRM_DRIGHTBOTTOM]);
- break;
- case ACS_PLUS:
- DRAW (c, mc_tty_frm[MC_TTY_FRM_CROSS]);
- break;
- default:
- SLsmg_write_char ((unsigned int) c);
- }
- #undef DRAW
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_print_anychar (int c)
- {
- if (c > 255)
- {
- char str[UTF8_CHAR_LEN + 1];
- int res;
- res = g_unichar_to_utf8 (c, str);
- if (res == 0)
- {
- str[0] = '.';
- str[1] = '\0';
- }
- else
- {
- str[res] = '\0';
- }
- SLsmg_write_string ((char *) str_term_form (str));
- }
- else
- {
- if (!is_printable (c))
- c = '.';
- SLsmg_write_char ((SLwchar_Type) ((unsigned int) c));
- }
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_print_string (const char *s)
- {
- SLsmg_write_string ((char *) str_term_form (s));
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_printf (const char *fmt, ...)
- {
- va_list args;
- va_start (args, fmt);
- SLsmg_vprintf ((char *) fmt, args);
- va_end (args);
- }
- /* --------------------------------------------------------------------------------------------- */
- char *
- tty_tgetstr (const char *cap)
- {
- return SLtt_tgetstr ((SLFUTURE_CONST char *) cap);
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_refresh (void)
- {
- SLsmg_refresh ();
- }
- /* --------------------------------------------------------------------------------------------- */
- void
- tty_beep (void)
- {
- SLtt_beep ();
- }
- /* --------------------------------------------------------------------------------------------- */
|