Merge branch '4561_tar_segfault'
[midnight-commander.git] / lib / tty / tty-slang.c
blobe012feb303615db734761df99cbabb9f48eeeff0
1 /*
2 Interface to the terminal controlling library.
3 Slang wrapper.
5 Copyright (C) 2005-2024
6 Free Software Foundation, Inc.
8 Written by:
9 Andrew Borodin <aborodin@vmail.ru>, 2009
10 Egmont Koblinger <egmont@gmail.com>, 2010
12 This file is part of the Midnight Commander.
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.
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.
24 You should have received a copy of the GNU General Public License
25 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 /** \file
29 * \brief Source: S-Lang-based tty layer of Midnight Commander
32 #include <config.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/types.h> /* size_t */
38 #include <unistd.h>
39 #ifdef HAVE_SYS_IOCTL_H
40 #include <sys/ioctl.h>
41 #endif
42 #include <termios.h>
44 #include "lib/global.h"
45 #include "lib/strutil.h" /* str_term_form */
46 #include "lib/util.h" /* is_printable() */
48 #include "tty-internal.h" /* mc_tty_normalize_from_utf8() */
49 #include "tty.h"
50 #include "color.h"
51 #include "color-slang.h"
52 #include "color-internal.h"
53 #include "mouse.h" /* Gpm_Event is required in key.h */
54 #include "key.h" /* define_sequence */
55 #include "win.h"
58 /*** global variables ****************************************************************************/
60 /* If true program softkeys (HP terminals only) on startup and after every
61 command ran in the subshell to the description found in the termcap/terminfo
62 database */
63 int reset_hp_softkeys = 0;
65 /*** file scope macro definitions ****************************************************************/
67 #ifndef SLTT_MAX_SCREEN_COLS
68 #define SLTT_MAX_SCREEN_COLS 512
69 #endif
71 #ifndef SLTT_MAX_SCREEN_ROWS
72 #define SLTT_MAX_SCREEN_ROWS 512
73 #endif
75 /*** file scope type declarations ****************************************************************/
77 /*** forward declarations (file scope functions) *************************************************/
79 /*** file scope variables ************************************************************************/
81 /* Various saved termios settings that we control here */
82 static struct termios boot_mode;
83 static struct termios new_mode;
85 /* Controls whether we should wait for input in tty_lowlevel_getch */
86 static gboolean no_slang_delay;
88 static gboolean slsmg_active = FALSE;
90 /* This table describes which capabilities we want and which values we
91 * assign to them.
93 static const struct
95 int key_code;
96 const char *key_name;
97 } key_table[] = {
98 /* *INDENT-OFF* */
99 { KEY_F (0), "k0" },
100 { KEY_F (1), "k1" },
101 { KEY_F (2), "k2" },
102 { KEY_F (3), "k3" },
103 { KEY_F (4), "k4" },
104 { KEY_F (5), "k5" },
105 { KEY_F (6), "k6" },
106 { KEY_F (7), "k7" },
107 { KEY_F (8), "k8" },
108 { KEY_F (9), "k9" },
109 { KEY_F (10), "k;" },
110 { KEY_F (11), "F1" },
111 { KEY_F (12), "F2" },
112 { KEY_F (13), "F3" },
113 { KEY_F (14), "F4" },
114 { KEY_F (15), "F5" },
115 { KEY_F (16), "F6" },
116 { KEY_F (17), "F7" },
117 { KEY_F (18), "F8" },
118 { KEY_F (19), "F9" },
119 { KEY_F (20), "FA" },
120 { KEY_IC, "kI" },
121 { KEY_NPAGE, "kN" },
122 { KEY_PPAGE, "kP" },
123 { KEY_LEFT, "kl" },
124 { KEY_RIGHT, "kr" },
125 { KEY_UP, "ku" },
126 { KEY_DOWN, "kd" },
127 { KEY_DC, "kD" },
128 { KEY_BACKSPACE, "kb" },
129 { KEY_HOME, "kh" },
130 { KEY_END, "@7" },
131 { 0, NULL }
132 /* *INDENT-ON* */
135 /* --------------------------------------------------------------------------------------------- */
136 /*** file scope functions ************************************************************************/
137 /* --------------------------------------------------------------------------------------------- */
139 static void
140 tty_setup_sigwinch (void (*handler) (int))
142 (void) SLsignal (SIGWINCH, handler);
143 tty_create_winch_pipe ();
146 /* --------------------------------------------------------------------------------------------- */
148 static void
149 sigwinch_handler (int dummy)
151 ssize_t n = 0;
153 (void) dummy;
155 n = write (sigwinch_pipe[1], "", 1);
156 (void) n;
158 (void) SLsignal (SIGWINCH, sigwinch_handler);
161 /* --------------------------------------------------------------------------------------------- */
163 /* HP Terminals have capabilities (pfkey, pfloc, pfx) to program function keys.
164 elm 2.4pl15 invoked with the -K option utilizes these softkeys and the
165 consequence is that function keys don't work in MC sometimes...
166 Unfortunately I don't now the one and only escape sequence to turn off.
167 softkeys (elm uses three different capabilities to turn on softkeys and two.
168 capabilities to turn them off)..
169 Among other things elm uses the pair we already use in slang_keypad. That's.
170 the reason why I call slang_reset_softkeys from slang_keypad. In lack of
171 something better the softkeys are programmed to their defaults from the
172 termcap/terminfo database.
173 The escape sequence to program the softkeys is taken from elm and it is.
174 hardcoded because neither slang nor ncurses 4.1 know how to 'printf' this.
175 sequence. -- Norbert
178 static void
179 slang_reset_softkeys (void)
181 int key;
182 static const char display[] = " ";
183 char tmp[BUF_SMALL];
185 for (key = 1; key < 9; key++)
187 char *send;
189 g_snprintf (tmp, sizeof (tmp), "k%d", key);
190 send = SLtt_tgetstr (tmp);
191 if (send != NULL)
193 g_snprintf (tmp, sizeof (tmp), ESC_STR "&f%dk%dd%dL%s%s", key,
194 (int) (sizeof (display) - 1), (int) strlen (send), display, send);
195 SLtt_write_string (tmp);
200 /* --------------------------------------------------------------------------------------------- */
202 static void
203 do_define_key (int code, const char *strcap)
205 char *seq;
207 seq = SLtt_tgetstr ((SLFUTURE_CONST char *) strcap);
208 if (seq != NULL)
209 define_sequence (code, seq, MCKEY_NOACTION);
212 /* --------------------------------------------------------------------------------------------- */
214 static void
215 load_terminfo_keys (void)
217 int i;
219 for (i = 0; key_table[i].key_code; i++)
220 do_define_key (key_table[i].key_code, key_table[i].key_name);
223 /* --------------------------------------------------------------------------------------------- */
224 /*** public functions ****************************************************************************/
225 /* --------------------------------------------------------------------------------------------- */
228 mc_tty_normalize_lines_char (const char *str)
230 char *str2;
231 int res;
233 struct mc_tty_lines_struct
235 const char *line;
236 int line_code;
237 } const lines_codes[] = {
238 {"\342\224\214", SLSMG_ULCORN_CHAR},
239 {"\342\224\220", SLSMG_URCORN_CHAR},
240 {"\342\224\224", SLSMG_LLCORN_CHAR},
241 {"\342\224\230", SLSMG_LRCORN_CHAR},
242 {"\342\224\234", SLSMG_LTEE_CHAR},
243 {"\342\224\244", SLSMG_RTEE_CHAR},
244 {"\342\224\254", SLSMG_UTEE_CHAR},
245 {"\342\224\264", SLSMG_DTEE_CHAR},
246 {"\342\224\200", SLSMG_HLINE_CHAR},
247 {"\342\224\202", SLSMG_VLINE_CHAR},
248 {"\342\224\274", SLSMG_PLUS_CHAR},
250 {NULL, 0}
253 if (!str)
254 return (int) ' ';
256 for (res = 0; lines_codes[res].line; res++)
258 if (strcmp (str, lines_codes[res].line) == 0)
259 return lines_codes[res].line_code;
262 str2 = mc_tty_normalize_from_utf8 (str);
263 res = g_utf8_get_char_validated (str2, -1);
265 if (res < 0)
266 res = (unsigned char) str2[0];
267 g_free (str2);
269 return res;
272 /* --------------------------------------------------------------------------------------------- */
274 void
275 tty_init (gboolean mouse_enable, gboolean is_xterm)
277 SLtt_Ignore_Beep = 1;
279 SLutf8_enable (-1); /* has to be called first before any of the other functions. */
280 SLtt_get_terminfo ();
282 * If the terminal in not in terminfo but begins with a well-known
283 * string such as "linux" or "xterm" S-Lang will go on, but the
284 * terminal size and several other variables won't be initialized
285 * (as of S-Lang 1.4.4). Detect it and abort. Also detect extremely
286 * small screen dimensions.
288 if ((COLS < 10) || (LINES < 5)
289 #if SLANG_VERSION < 20303
290 /* Beginning from pre2.3.3-8 (55f58798c267d76a1b93d0d916027b71a10ac1ee),
291 these limitations were eliminated. */
292 || (COLS > SLTT_MAX_SCREEN_COLS) || (LINES > SLTT_MAX_SCREEN_ROWS)
293 #endif
296 fprintf (stderr,
297 _("Screen size %dx%d is not supported.\n"
298 "Check the TERM environment variable.\n"), COLS, LINES);
299 exit (EXIT_FAILURE);
302 tcgetattr (fileno (stdin), &boot_mode);
303 /* 255 = ignore abort char; XCTRL('g') for abort char = ^g */
304 SLang_init_tty (XCTRL ('g'), 1, 0);
306 if (mc_global.tty.ugly_line_drawing)
307 SLtt_Has_Alt_Charset = 0;
309 tcgetattr (SLang_TT_Read_FD, &new_mode);
311 tty_reset_prog_mode ();
312 load_terminfo_keys ();
314 SLtt_Blink_Mode = (tty_use_256colors (NULL) || tty_use_truecolors (NULL)) ? 1 : 0;
316 tty_start_interrupt_key ();
318 /* It's the small part from the previous init_key() */
319 init_key_input_fd ();
321 /* For 8-bit locales, NCurses handles 154 (0x9A) symbol properly, while S-Lang
322 * requires SLsmg_Display_Eight_Bit >= 154 (OR manual filtering if xterm display
323 * detected - but checking TERM would fail under screen, OR running xterm
324 * with allowC1Printable).
326 tty_display_8bit (FALSE);
328 SLsmg_init_smg ();
329 slsmg_active = TRUE;
330 if (!mouse_enable)
331 use_mouse_p = MOUSE_DISABLED;
332 tty_init_xterm_support (is_xterm); /* do it before tty_enter_ca_mode() call */
333 tty_enter_ca_mode ();
334 tty_keypad (TRUE);
335 tty_nodelay (FALSE);
337 tty_setup_sigwinch (sigwinch_handler);
340 /* --------------------------------------------------------------------------------------------- */
342 void
343 tty_shutdown (void)
345 char *op_cap;
347 tty_destroy_winch_pipe ();
348 tty_reset_shell_mode ();
349 tty_noraw_mode ();
350 tty_keypad (FALSE);
351 tty_reset_screen ();
352 tty_exit_ca_mode ();
353 SLang_reset_tty ();
354 slsmg_active = FALSE;
356 /* Load the op capability to reset the colors to those that were
357 * active when the program was started up
359 op_cap = SLtt_tgetstr ((SLFUTURE_CONST char *) "op");
360 if (op_cap != NULL)
362 fputs (op_cap, stdout);
363 fflush (stdout);
367 /* --------------------------------------------------------------------------------------------- */
369 void
370 tty_enter_ca_mode (void)
372 /* S-Lang handles alternate screen switching and cursor position saving */
375 /* --------------------------------------------------------------------------------------------- */
377 void
378 tty_exit_ca_mode (void)
380 /* S-Lang handles alternate screen switching and cursor position restoring */
383 /* --------------------------------------------------------------------------------------------- */
385 void
386 tty_change_screen_size (void)
388 SLtt_get_screen_size ();
389 if (slsmg_active)
390 SLsmg_reinit_smg ();
392 #ifdef ENABLE_SUBSHELL
393 if (mc_global.tty.use_subshell)
394 tty_resize (mc_global.tty.subshell_pty);
395 #endif
398 /* --------------------------------------------------------------------------------------------- */
399 /* Done each time we come back from done mode */
401 void
402 tty_reset_prog_mode (void)
404 tcsetattr (SLang_TT_Read_FD, TCSANOW, &new_mode);
405 SLsmg_init_smg ();
406 slsmg_active = TRUE;
407 SLsmg_touch_lines (0, LINES);
410 /* --------------------------------------------------------------------------------------------- */
411 /* Called each time we want to shutdown slang screen manager */
413 void
414 tty_reset_shell_mode (void)
416 tcsetattr (SLang_TT_Read_FD, TCSANOW, &boot_mode);
419 /* --------------------------------------------------------------------------------------------- */
421 void
422 tty_raw_mode (void)
424 tcsetattr (SLang_TT_Read_FD, TCSANOW, &new_mode);
427 /* --------------------------------------------------------------------------------------------- */
429 void
430 tty_noraw_mode (void)
434 /* --------------------------------------------------------------------------------------------- */
436 void
437 tty_noecho (void)
441 /* --------------------------------------------------------------------------------------------- */
444 tty_flush_input (void)
446 return 0; /* OK */
449 /* --------------------------------------------------------------------------------------------- */
451 void
452 tty_keypad (gboolean set)
454 char *keypad_string;
456 keypad_string = SLtt_tgetstr ((SLFUTURE_CONST char *) (set ? "ks" : "ke"));
457 if (keypad_string != NULL)
458 SLtt_write_string (keypad_string);
459 if (set && reset_hp_softkeys)
460 slang_reset_softkeys ();
463 /* --------------------------------------------------------------------------------------------- */
465 void
466 tty_nodelay (gboolean set)
468 no_slang_delay = set;
471 /* --------------------------------------------------------------------------------------------- */
474 tty_baudrate (void)
476 return SLang_TT_Baud_Rate;
479 /* --------------------------------------------------------------------------------------------- */
482 tty_lowlevel_getch (void)
484 int c;
486 if (no_slang_delay && (SLang_input_pending (0) == 0))
487 return -1;
489 c = SLang_getkey ();
490 if (c == SLANG_GETKEY_ERROR)
492 fprintf (stderr,
493 "SLang_getkey returned SLANG_GETKEY_ERROR\n"
494 "Assuming EOF on stdin and exiting\n");
495 exit (EXIT_FAILURE);
498 return c;
501 /* --------------------------------------------------------------------------------------------- */
504 tty_reset_screen (void)
506 SLsmg_reset_smg ();
507 slsmg_active = FALSE;
508 return 0; /* OK */
511 /* --------------------------------------------------------------------------------------------- */
513 void
514 tty_touch_screen (void)
516 SLsmg_touch_lines (0, LINES);
519 /* --------------------------------------------------------------------------------------------- */
521 void
522 tty_gotoyx (int y, int x)
524 SLsmg_gotorc (y, x);
527 /* --------------------------------------------------------------------------------------------- */
529 void
530 tty_getyx (int *py, int *px)
532 *py = SLsmg_get_row ();
533 *px = SLsmg_get_column ();
536 /* --------------------------------------------------------------------------------------------- */
538 void
539 tty_draw_hline (int y, int x, int ch, int len)
541 int x1;
543 if (y < 0 || y >= LINES || x >= COLS)
544 return;
546 x1 = x;
548 if (x < 0)
550 len += x;
551 if (len <= 0)
552 return;
553 x = 0;
556 if (ch == ACS_HLINE)
557 ch = mc_tty_frm[MC_TTY_FRM_HORIZ];
558 if (ch == 0)
559 ch = ACS_HLINE;
561 SLsmg_gotorc (y, x);
563 if (ch == ACS_HLINE)
564 SLsmg_draw_hline (len);
565 else
566 while (len-- != 0)
567 tty_print_char (ch);
569 SLsmg_gotorc (y, x1);
572 /* --------------------------------------------------------------------------------------------- */
574 void
575 tty_draw_vline (int y, int x, int ch, int len)
577 int y1;
579 if (x < 0 || x >= COLS || y >= LINES)
580 return;
582 y1 = y;
584 if (y < 0)
586 len += y;
587 if (len <= 0)
588 return;
589 y = 0;
592 if (ch == ACS_VLINE)
593 ch = mc_tty_frm[MC_TTY_FRM_VERT];
594 if (ch == 0)
595 ch = ACS_VLINE;
597 SLsmg_gotorc (y, x);
599 if (ch == ACS_VLINE)
600 SLsmg_draw_vline (len);
601 else
603 int pos = 0;
605 while (len-- != 0)
607 SLsmg_gotorc (y + pos, x);
608 tty_print_char (ch);
609 pos++;
613 SLsmg_gotorc (y1, x);
616 /* --------------------------------------------------------------------------------------------- */
618 void
619 tty_fill_region (int y, int x, int rows, int cols, unsigned char ch)
621 SLsmg_fill_region (y, x, rows, cols, ch);
624 /* --------------------------------------------------------------------------------------------- */
626 void
627 tty_colorize_area (int y, int x, int rows, int cols, int color)
629 if (use_colors)
630 SLsmg_set_color_in_region (color, y, x, rows, cols);
633 /* --------------------------------------------------------------------------------------------- */
635 void
636 tty_set_alt_charset (gboolean alt_charset)
638 SLsmg_set_char_set ((int) alt_charset);
641 /* --------------------------------------------------------------------------------------------- */
643 void
644 tty_display_8bit (gboolean what)
646 SLsmg_Display_Eight_Bit = what ? 128 : 160;
649 /* --------------------------------------------------------------------------------------------- */
651 void
652 tty_print_char (int c)
654 SLsmg_write_char ((SLwchar_Type) ((unsigned int) c));
657 /* --------------------------------------------------------------------------------------------- */
659 void
660 tty_print_alt_char (int c, gboolean single)
662 #define DRAW(x, y) (x == y) \
663 ? SLsmg_draw_object (SLsmg_get_row(), SLsmg_get_column(), x) \
664 : SLsmg_write_char ((unsigned int) y)
665 switch (c)
667 case ACS_VLINE:
668 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_VERT : MC_TTY_FRM_DVERT]);
669 break;
670 case ACS_HLINE:
671 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_HORIZ : MC_TTY_FRM_DHORIZ]);
672 break;
673 case ACS_LTEE:
674 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTMIDDLE : MC_TTY_FRM_DLEFTMIDDLE]);
675 break;
676 case ACS_RTEE:
677 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTMIDDLE : MC_TTY_FRM_DRIGHTMIDDLE]);
678 break;
679 case ACS_TTEE:
680 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_TOPMIDDLE : MC_TTY_FRM_DTOPMIDDLE]);
681 break;
682 case ACS_BTEE:
683 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_BOTTOMMIDDLE : MC_TTY_FRM_DBOTTOMMIDDLE]);
684 break;
685 case ACS_ULCORNER:
686 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTTOP : MC_TTY_FRM_DLEFTTOP]);
687 break;
688 case ACS_LLCORNER:
689 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTBOTTOM : MC_TTY_FRM_DLEFTBOTTOM]);
690 break;
691 case ACS_URCORNER:
692 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTTOP : MC_TTY_FRM_DRIGHTTOP]);
693 break;
694 case ACS_LRCORNER:
695 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTBOTTOM : MC_TTY_FRM_DRIGHTBOTTOM]);
696 break;
697 case ACS_PLUS:
698 DRAW (c, mc_tty_frm[MC_TTY_FRM_CROSS]);
699 break;
700 default:
701 SLsmg_write_char ((unsigned int) c);
703 #undef DRAW
706 /* --------------------------------------------------------------------------------------------- */
708 void
709 tty_print_anychar (int c)
711 if (c > 255)
713 char str[UTF8_CHAR_LEN + 1];
714 int res;
716 res = g_unichar_to_utf8 (c, str);
717 if (res == 0)
719 str[0] = '.';
720 str[1] = '\0';
722 else
724 str[res] = '\0';
726 SLsmg_write_string ((char *) str_term_form (str));
728 else
730 if (!is_printable (c))
731 c = '.';
732 SLsmg_write_char ((SLwchar_Type) ((unsigned int) c));
736 /* --------------------------------------------------------------------------------------------- */
738 void
739 tty_print_string (const char *s)
741 SLsmg_write_string ((char *) str_term_form (s));
744 /* --------------------------------------------------------------------------------------------- */
746 void
747 tty_printf (const char *fmt, ...)
749 va_list args;
751 va_start (args, fmt);
752 SLsmg_vprintf ((char *) fmt, args);
753 va_end (args);
756 /* --------------------------------------------------------------------------------------------- */
758 char *
759 tty_tgetstr (const char *cap)
761 return SLtt_tgetstr ((SLFUTURE_CONST char *) cap);
764 /* --------------------------------------------------------------------------------------------- */
766 void
767 tty_refresh (void)
769 SLsmg_refresh ();
772 /* --------------------------------------------------------------------------------------------- */
774 void
775 tty_beep (void)
777 SLtt_beep ();
780 /* --------------------------------------------------------------------------------------------- */