Merge branch '4524_cleanup'
[midnight-commander.git] / lib / tty / tty-slang.c
blob752825b6e1ce4eb22e2d2df7f0e0e6918aee1f42
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[] =
99 /* *INDENT-OFF* */
100 { KEY_F (0), "k0" },
101 { KEY_F (1), "k1" },
102 { KEY_F (2), "k2" },
103 { KEY_F (3), "k3" },
104 { KEY_F (4), "k4" },
105 { KEY_F (5), "k5" },
106 { KEY_F (6), "k6" },
107 { KEY_F (7), "k7" },
108 { KEY_F (8), "k8" },
109 { KEY_F (9), "k9" },
110 { KEY_F (10), "k;" },
111 { KEY_F (11), "F1" },
112 { KEY_F (12), "F2" },
113 { KEY_F (13), "F3" },
114 { KEY_F (14), "F4" },
115 { KEY_F (15), "F5" },
116 { KEY_F (16), "F6" },
117 { KEY_F (17), "F7" },
118 { KEY_F (18), "F8" },
119 { KEY_F (19), "F9" },
120 { KEY_F (20), "FA" },
121 { KEY_IC, "kI" },
122 { KEY_NPAGE, "kN" },
123 { KEY_PPAGE, "kP" },
124 { KEY_LEFT, "kl" },
125 { KEY_RIGHT, "kr" },
126 { KEY_UP, "ku" },
127 { KEY_DOWN, "kd" },
128 { KEY_DC, "kD" },
129 { KEY_BACKSPACE, "kb" },
130 { KEY_HOME, "kh" },
131 { KEY_END, "@7" },
132 { 0, NULL }
133 /* *INDENT-ON* */
136 /* --------------------------------------------------------------------------------------------- */
137 /*** file scope functions ************************************************************************/
138 /* --------------------------------------------------------------------------------------------- */
140 static void
141 tty_setup_sigwinch (void (*handler) (int))
143 (void) SLsignal (SIGWINCH, handler);
144 tty_create_winch_pipe ();
147 /* --------------------------------------------------------------------------------------------- */
149 static void
150 sigwinch_handler (int dummy)
152 ssize_t n = 0;
154 (void) dummy;
156 n = write (sigwinch_pipe[1], "", 1);
157 (void) n;
159 (void) SLsignal (SIGWINCH, sigwinch_handler);
162 /* --------------------------------------------------------------------------------------------- */
164 /* HP Terminals have capabilities (pfkey, pfloc, pfx) to program function keys.
165 elm 2.4pl15 invoked with the -K option utilizes these softkeys and the
166 consequence is that function keys don't work in MC sometimes...
167 Unfortunately I don't now the one and only escape sequence to turn off.
168 softkeys (elm uses three different capabilities to turn on softkeys and two.
169 capabilities to turn them off)..
170 Among other things elm uses the pair we already use in slang_keypad. That's.
171 the reason why I call slang_reset_softkeys from slang_keypad. In lack of
172 something better the softkeys are programmed to their defaults from the
173 termcap/terminfo database.
174 The escape sequence to program the softkeys is taken from elm and it is.
175 hardcoded because neither slang nor ncurses 4.1 know how to 'printf' this.
176 sequence. -- Norbert
179 static void
180 slang_reset_softkeys (void)
182 int key;
183 static const char display[] = " ";
184 char tmp[BUF_SMALL];
186 for (key = 1; key < 9; key++)
188 char *send;
190 g_snprintf (tmp, sizeof (tmp), "k%d", key);
191 send = SLtt_tgetstr (tmp);
192 if (send != NULL)
194 g_snprintf (tmp, sizeof (tmp), ESC_STR "&f%dk%dd%dL%s%s", key,
195 (int) (sizeof (display) - 1), (int) strlen (send), display, send);
196 SLtt_write_string (tmp);
201 /* --------------------------------------------------------------------------------------------- */
203 static void
204 do_define_key (int code, const char *strcap)
206 char *seq;
208 seq = SLtt_tgetstr ((SLFUTURE_CONST char *) strcap);
209 if (seq != NULL)
210 define_sequence (code, seq, MCKEY_NOACTION);
213 /* --------------------------------------------------------------------------------------------- */
215 static void
216 load_terminfo_keys (void)
218 int i;
220 for (i = 0; key_table[i].key_code; i++)
221 do_define_key (key_table[i].key_code, key_table[i].key_name);
224 /* --------------------------------------------------------------------------------------------- */
225 /*** public functions ****************************************************************************/
226 /* --------------------------------------------------------------------------------------------- */
229 mc_tty_normalize_lines_char (const char *str)
231 char *str2;
232 int res;
234 struct mc_tty_lines_struct
236 const char *line;
237 int line_code;
238 } const lines_codes[] = {
239 {"\342\224\214", SLSMG_ULCORN_CHAR},
240 {"\342\224\220", SLSMG_URCORN_CHAR},
241 {"\342\224\224", SLSMG_LLCORN_CHAR},
242 {"\342\224\230", SLSMG_LRCORN_CHAR},
243 {"\342\224\234", SLSMG_LTEE_CHAR},
244 {"\342\224\244", SLSMG_RTEE_CHAR},
245 {"\342\224\254", SLSMG_UTEE_CHAR},
246 {"\342\224\264", SLSMG_DTEE_CHAR},
247 {"\342\224\200", SLSMG_HLINE_CHAR},
248 {"\342\224\202", SLSMG_VLINE_CHAR},
249 {"\342\224\274", SLSMG_PLUS_CHAR},
251 {NULL, 0}
254 if (!str)
255 return (int) ' ';
257 for (res = 0; lines_codes[res].line; res++)
259 if (strcmp (str, lines_codes[res].line) == 0)
260 return lines_codes[res].line_code;
263 str2 = mc_tty_normalize_from_utf8 (str);
264 res = g_utf8_get_char_validated (str2, -1);
266 if (res < 0)
267 res = (unsigned char) str2[0];
268 g_free (str2);
270 return res;
273 /* --------------------------------------------------------------------------------------------- */
275 void
276 tty_init (gboolean mouse_enable, gboolean is_xterm)
278 SLtt_Ignore_Beep = 1;
280 SLutf8_enable (-1); /* has to be called first before any of the other functions. */
281 SLtt_get_terminfo ();
283 * If the terminal in not in terminfo but begins with a well-known
284 * string such as "linux" or "xterm" S-Lang will go on, but the
285 * terminal size and several other variables won't be initialized
286 * (as of S-Lang 1.4.4). Detect it and abort. Also detect extremely
287 * small screen dimensions.
289 if ((COLS < 10) || (LINES < 5)
290 #if SLANG_VERSION < 20303
291 /* Beginning from pre2.3.3-8 (55f58798c267d76a1b93d0d916027b71a10ac1ee),
292 these limitations were eliminated. */
293 || (COLS > SLTT_MAX_SCREEN_COLS) || (LINES > SLTT_MAX_SCREEN_ROWS)
294 #endif
297 fprintf (stderr,
298 _("Screen size %dx%d is not supported.\n"
299 "Check the TERM environment variable.\n"), COLS, LINES);
300 exit (EXIT_FAILURE);
303 tcgetattr (fileno (stdin), &boot_mode);
304 /* 255 = ignore abort char; XCTRL('g') for abort char = ^g */
305 SLang_init_tty (XCTRL ('g'), 1, 0);
307 if (mc_global.tty.ugly_line_drawing)
308 SLtt_Has_Alt_Charset = 0;
310 tcgetattr (SLang_TT_Read_FD, &new_mode);
312 tty_reset_prog_mode ();
313 load_terminfo_keys ();
315 SLtt_Blink_Mode = (tty_use_256colors (NULL) || tty_use_truecolors (NULL)) ? 1 : 0;
317 tty_start_interrupt_key ();
319 /* It's the small part from the previous init_key() */
320 init_key_input_fd ();
322 /* For 8-bit locales, NCurses handles 154 (0x9A) symbol properly, while S-Lang
323 * requires SLsmg_Display_Eight_Bit >= 154 (OR manual filtering if xterm display
324 * detected - but checking TERM would fail under screen, OR running xterm
325 * with allowC1Printable).
327 tty_display_8bit (FALSE);
329 SLsmg_init_smg ();
330 slsmg_active = TRUE;
331 if (!mouse_enable)
332 use_mouse_p = MOUSE_DISABLED;
333 tty_init_xterm_support (is_xterm); /* do it before tty_enter_ca_mode() call */
334 tty_enter_ca_mode ();
335 tty_keypad (TRUE);
336 tty_nodelay (FALSE);
338 tty_setup_sigwinch (sigwinch_handler);
341 /* --------------------------------------------------------------------------------------------- */
343 void
344 tty_shutdown (void)
346 char *op_cap;
348 tty_destroy_winch_pipe ();
349 tty_reset_shell_mode ();
350 tty_noraw_mode ();
351 tty_keypad (FALSE);
352 tty_reset_screen ();
353 tty_exit_ca_mode ();
354 SLang_reset_tty ();
355 slsmg_active = FALSE;
357 /* Load the op capability to reset the colors to those that were
358 * active when the program was started up
360 op_cap = SLtt_tgetstr ((SLFUTURE_CONST char *) "op");
361 if (op_cap != NULL)
363 fputs (op_cap, stdout);
364 fflush (stdout);
368 /* --------------------------------------------------------------------------------------------- */
370 void
371 tty_enter_ca_mode (void)
373 /* S-Lang handles alternate screen switching and cursor position saving */
376 /* --------------------------------------------------------------------------------------------- */
378 void
379 tty_exit_ca_mode (void)
381 /* S-Lang handles alternate screen switching and cursor position restoring */
384 /* --------------------------------------------------------------------------------------------- */
386 void
387 tty_change_screen_size (void)
389 SLtt_get_screen_size ();
390 if (slsmg_active)
391 SLsmg_reinit_smg ();
393 #ifdef ENABLE_SUBSHELL
394 if (mc_global.tty.use_subshell)
395 tty_resize (mc_global.tty.subshell_pty);
396 #endif
399 /* --------------------------------------------------------------------------------------------- */
400 /* Done each time we come back from done mode */
402 void
403 tty_reset_prog_mode (void)
405 tcsetattr (SLang_TT_Read_FD, TCSANOW, &new_mode);
406 SLsmg_init_smg ();
407 slsmg_active = TRUE;
408 SLsmg_touch_lines (0, LINES);
411 /* --------------------------------------------------------------------------------------------- */
412 /* Called each time we want to shutdown slang screen manager */
414 void
415 tty_reset_shell_mode (void)
417 tcsetattr (SLang_TT_Read_FD, TCSANOW, &boot_mode);
420 /* --------------------------------------------------------------------------------------------- */
422 void
423 tty_raw_mode (void)
425 tcsetattr (SLang_TT_Read_FD, TCSANOW, &new_mode);
428 /* --------------------------------------------------------------------------------------------- */
430 void
431 tty_noraw_mode (void)
435 /* --------------------------------------------------------------------------------------------- */
437 void
438 tty_noecho (void)
442 /* --------------------------------------------------------------------------------------------- */
445 tty_flush_input (void)
447 return 0; /* OK */
450 /* --------------------------------------------------------------------------------------------- */
452 void
453 tty_keypad (gboolean set)
455 char *keypad_string;
457 keypad_string = SLtt_tgetstr ((SLFUTURE_CONST char *) (set ? "ks" : "ke"));
458 if (keypad_string != NULL)
459 SLtt_write_string (keypad_string);
460 if (set && reset_hp_softkeys)
461 slang_reset_softkeys ();
464 /* --------------------------------------------------------------------------------------------- */
466 void
467 tty_nodelay (gboolean set)
469 no_slang_delay = set;
472 /* --------------------------------------------------------------------------------------------- */
475 tty_baudrate (void)
477 return SLang_TT_Baud_Rate;
480 /* --------------------------------------------------------------------------------------------- */
483 tty_lowlevel_getch (void)
485 int c;
487 if (no_slang_delay && (SLang_input_pending (0) == 0))
488 return -1;
490 c = SLang_getkey ();
491 if (c == SLANG_GETKEY_ERROR)
493 fprintf (stderr,
494 "SLang_getkey returned SLANG_GETKEY_ERROR\n"
495 "Assuming EOF on stdin and exiting\n");
496 exit (EXIT_FAILURE);
499 return c;
502 /* --------------------------------------------------------------------------------------------- */
505 tty_reset_screen (void)
507 SLsmg_reset_smg ();
508 slsmg_active = FALSE;
509 return 0; /* OK */
512 /* --------------------------------------------------------------------------------------------- */
514 void
515 tty_touch_screen (void)
517 SLsmg_touch_lines (0, LINES);
520 /* --------------------------------------------------------------------------------------------- */
522 void
523 tty_gotoyx (int y, int x)
525 SLsmg_gotorc (y, x);
528 /* --------------------------------------------------------------------------------------------- */
530 void
531 tty_getyx (int *py, int *px)
533 *py = SLsmg_get_row ();
534 *px = SLsmg_get_column ();
537 /* --------------------------------------------------------------------------------------------- */
539 void
540 tty_draw_hline (int y, int x, int ch, int len)
542 int x1;
544 if (y < 0 || y >= LINES || x >= COLS)
545 return;
547 x1 = x;
549 if (x < 0)
551 len += x;
552 if (len <= 0)
553 return;
554 x = 0;
557 if (ch == ACS_HLINE)
558 ch = mc_tty_frm[MC_TTY_FRM_HORIZ];
559 if (ch == 0)
560 ch = ACS_HLINE;
562 SLsmg_gotorc (y, x);
564 if (ch == ACS_HLINE)
565 SLsmg_draw_hline (len);
566 else
567 while (len-- != 0)
568 tty_print_char (ch);
570 SLsmg_gotorc (y, x1);
573 /* --------------------------------------------------------------------------------------------- */
575 void
576 tty_draw_vline (int y, int x, int ch, int len)
578 int y1;
580 if (x < 0 || x >= COLS || y >= LINES)
581 return;
583 y1 = y;
585 if (y < 0)
587 len += y;
588 if (len <= 0)
589 return;
590 y = 0;
593 if (ch == ACS_VLINE)
594 ch = mc_tty_frm[MC_TTY_FRM_VERT];
595 if (ch == 0)
596 ch = ACS_VLINE;
598 SLsmg_gotorc (y, x);
600 if (ch == ACS_VLINE)
601 SLsmg_draw_vline (len);
602 else
604 int pos = 0;
606 while (len-- != 0)
608 SLsmg_gotorc (y + pos, x);
609 tty_print_char (ch);
610 pos++;
614 SLsmg_gotorc (y1, x);
617 /* --------------------------------------------------------------------------------------------- */
619 void
620 tty_fill_region (int y, int x, int rows, int cols, unsigned char ch)
622 SLsmg_fill_region (y, x, rows, cols, ch);
625 /* --------------------------------------------------------------------------------------------- */
627 void
628 tty_colorize_area (int y, int x, int rows, int cols, int color)
630 if (use_colors)
631 SLsmg_set_color_in_region (color, y, x, rows, cols);
634 /* --------------------------------------------------------------------------------------------- */
636 void
637 tty_set_alt_charset (gboolean alt_charset)
639 SLsmg_set_char_set ((int) alt_charset);
642 /* --------------------------------------------------------------------------------------------- */
644 void
645 tty_display_8bit (gboolean what)
647 SLsmg_Display_Eight_Bit = what ? 128 : 160;
650 /* --------------------------------------------------------------------------------------------- */
652 void
653 tty_print_char (int c)
655 SLsmg_write_char ((SLwchar_Type) ((unsigned int) c));
658 /* --------------------------------------------------------------------------------------------- */
660 void
661 tty_print_alt_char (int c, gboolean single)
663 #define DRAW(x, y) (x == y) \
664 ? SLsmg_draw_object (SLsmg_get_row(), SLsmg_get_column(), x) \
665 : SLsmg_write_char ((unsigned int) y)
666 switch (c)
668 case ACS_VLINE:
669 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_VERT : MC_TTY_FRM_DVERT]);
670 break;
671 case ACS_HLINE:
672 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_HORIZ : MC_TTY_FRM_DHORIZ]);
673 break;
674 case ACS_LTEE:
675 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTMIDDLE : MC_TTY_FRM_DLEFTMIDDLE]);
676 break;
677 case ACS_RTEE:
678 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTMIDDLE : MC_TTY_FRM_DRIGHTMIDDLE]);
679 break;
680 case ACS_TTEE:
681 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_TOPMIDDLE : MC_TTY_FRM_DTOPMIDDLE]);
682 break;
683 case ACS_BTEE:
684 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_BOTTOMMIDDLE : MC_TTY_FRM_DBOTTOMMIDDLE]);
685 break;
686 case ACS_ULCORNER:
687 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTTOP : MC_TTY_FRM_DLEFTTOP]);
688 break;
689 case ACS_LLCORNER:
690 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTBOTTOM : MC_TTY_FRM_DLEFTBOTTOM]);
691 break;
692 case ACS_URCORNER:
693 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTTOP : MC_TTY_FRM_DRIGHTTOP]);
694 break;
695 case ACS_LRCORNER:
696 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTBOTTOM : MC_TTY_FRM_DRIGHTBOTTOM]);
697 break;
698 case ACS_PLUS:
699 DRAW (c, mc_tty_frm[MC_TTY_FRM_CROSS]);
700 break;
701 default:
702 SLsmg_write_char ((unsigned int) c);
704 #undef DRAW
707 /* --------------------------------------------------------------------------------------------- */
709 void
710 tty_print_anychar (int c)
712 if (c > 255)
714 char str[UTF8_CHAR_LEN + 1];
715 int res;
717 res = g_unichar_to_utf8 (c, str);
718 if (res == 0)
720 str[0] = '.';
721 str[1] = '\0';
723 else
725 str[res] = '\0';
727 SLsmg_write_string ((char *) str_term_form (str));
729 else
731 if (!is_printable (c))
732 c = '.';
733 SLsmg_write_char ((SLwchar_Type) ((unsigned int) c));
737 /* --------------------------------------------------------------------------------------------- */
739 void
740 tty_print_string (const char *s)
742 SLsmg_write_string ((char *) str_term_form (s));
745 /* --------------------------------------------------------------------------------------------- */
747 void
748 tty_printf (const char *fmt, ...)
750 va_list args;
752 va_start (args, fmt);
753 SLsmg_vprintf ((char *) fmt, args);
754 va_end (args);
757 /* --------------------------------------------------------------------------------------------- */
759 char *
760 tty_tgetstr (const char *cap)
762 return SLtt_tgetstr ((SLFUTURE_CONST char *) cap);
765 /* --------------------------------------------------------------------------------------------- */
767 void
768 tty_refresh (void)
770 SLsmg_refresh ();
773 /* --------------------------------------------------------------------------------------------- */
775 void
776 tty_beep (void)
778 SLtt_beep ();
781 /* --------------------------------------------------------------------------------------------- */