Ticket #3660: unable to view archive content using F3.
[midnight-commander.git] / lib / tty / tty-slang.c
blob87e4a703edc1fc1ef8e0e1072ac4b1dfb872de9b
1 /*
2 Interface to the terminal controlling library.
3 Slang wrapper.
5 Copyright (C) 2005-2016
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 /*** file scope variables ************************************************************************/
79 /* Various saved termios settings that we control here */
80 static struct termios boot_mode;
81 static struct termios new_mode;
83 /* Controls whether we should wait for input in tty_lowlevel_getch */
84 static gboolean no_slang_delay;
86 static gboolean slsmg_active = FALSE;
88 /* This table describes which capabilities we want and which values we
89 * assign to them.
91 static const struct
93 int key_code;
94 const char *key_name;
95 } key_table[] =
97 /* *INDENT-OFF* */
98 { KEY_F (0), "k0" },
99 { KEY_F (1), "k1" },
100 { KEY_F (2), "k2" },
101 { KEY_F (3), "k3" },
102 { KEY_F (4), "k4" },
103 { KEY_F (5), "k5" },
104 { KEY_F (6), "k6" },
105 { KEY_F (7), "k7" },
106 { KEY_F (8), "k8" },
107 { KEY_F (9), "k9" },
108 { KEY_F (10), "k;" },
109 { KEY_F (11), "F1" },
110 { KEY_F (12), "F2" },
111 { KEY_F (13), "F3" },
112 { KEY_F (14), "F4" },
113 { KEY_F (15), "F5" },
114 { KEY_F (16), "F6" },
115 { KEY_F (17), "F7" },
116 { KEY_F (18), "F8" },
117 { KEY_F (19), "F9" },
118 { KEY_F (20), "FA" },
119 { KEY_IC, "kI" },
120 { KEY_NPAGE, "kN" },
121 { KEY_PPAGE, "kP" },
122 { KEY_LEFT, "kl" },
123 { KEY_RIGHT, "kr" },
124 { KEY_UP, "ku" },
125 { KEY_DOWN, "kd" },
126 { KEY_DC, "kD" },
127 { KEY_BACKSPACE, "kb" },
128 { KEY_HOME, "kh" },
129 { KEY_END, "@7" },
130 { 0, NULL }
131 /* *INDENT-ON* */
134 /* --------------------------------------------------------------------------------------------- */
135 /*** file scope functions ************************************************************************/
136 /* --------------------------------------------------------------------------------------------- */
138 static void
139 tty_setup_sigwinch (void (*handler) (int))
141 (void) SLsignal (SIGWINCH, handler);
144 /* --------------------------------------------------------------------------------------------- */
146 static void
147 sigwinch_handler (int dummy)
149 (void) dummy;
151 mc_global.tty.winch_flag = 1;
152 (void) SLsignal (SIGWINCH, sigwinch_handler);
155 /* --------------------------------------------------------------------------------------------- */
157 /* HP Terminals have capabilities (pfkey, pfloc, pfx) to program function keys.
158 elm 2.4pl15 invoked with the -K option utilizes these softkeys and the
159 consequence is that function keys don't work in MC sometimes...
160 Unfortunately I don't now the one and only escape sequence to turn off.
161 softkeys (elm uses three different capabilities to turn on softkeys and two.
162 capabilities to turn them off)..
163 Among other things elm uses the pair we already use in slang_keypad. That's.
164 the reason why I call slang_reset_softkeys from slang_keypad. In lack of
165 something better the softkeys are programmed to their defaults from the
166 termcap/terminfo database.
167 The escape sequence to program the softkeys is taken from elm and it is.
168 hardcoded because neither slang nor ncurses 4.1 know how to 'printf' this.
169 sequence. -- Norbert
172 static void
173 slang_reset_softkeys (void)
175 int key;
176 static const char display[] = " ";
177 char tmp[BUF_SMALL];
179 for (key = 1; key < 9; key++)
181 char *send;
183 g_snprintf (tmp, sizeof (tmp), "k%d", key);
184 send = SLtt_tgetstr (tmp);
185 if (send != NULL)
187 g_snprintf (tmp, sizeof (tmp), ESC_STR "&f%dk%dd%dL%s%s", key,
188 (int) (sizeof (display) - 1), (int) strlen (send), display, send);
189 SLtt_write_string (tmp);
194 /* --------------------------------------------------------------------------------------------- */
196 static void
197 do_define_key (int code, const char *strcap)
199 char *seq;
201 seq = SLtt_tgetstr ((SLFUTURE_CONST char *) strcap);
202 if (seq != NULL)
203 define_sequence (code, seq, MCKEY_NOACTION);
206 /* --------------------------------------------------------------------------------------------- */
208 static void
209 load_terminfo_keys (void)
211 int i;
213 for (i = 0; key_table[i].key_code; i++)
214 do_define_key (key_table[i].key_code, key_table[i].key_name);
217 /* --------------------------------------------------------------------------------------------- */
218 /*** public functions ****************************************************************************/
219 /* --------------------------------------------------------------------------------------------- */
222 mc_tty_normalize_lines_char (const char *str)
224 char *str2;
225 int res;
227 struct mc_tty_lines_struct
229 const char *line;
230 int line_code;
231 } const lines_codes[] = {
232 {"\342\224\214", SLSMG_ULCORN_CHAR},
233 {"\342\224\220", SLSMG_URCORN_CHAR},
234 {"\342\224\224", SLSMG_LLCORN_CHAR},
235 {"\342\224\230", SLSMG_LRCORN_CHAR},
236 {"\342\224\234", SLSMG_LTEE_CHAR},
237 {"\342\224\244", SLSMG_RTEE_CHAR},
238 {"\342\224\254", SLSMG_UTEE_CHAR},
239 {"\342\224\264", SLSMG_DTEE_CHAR},
240 {"\342\224\200", SLSMG_HLINE_CHAR},
241 {"\342\224\202", SLSMG_VLINE_CHAR},
242 {"\342\224\274", SLSMG_PLUS_CHAR},
244 {NULL, 0}
247 if (!str)
248 return (int) ' ';
250 for (res = 0; lines_codes[res].line; res++)
252 if (strcmp (str, lines_codes[res].line) == 0)
253 return lines_codes[res].line_code;
256 str2 = mc_tty_normalize_from_utf8 (str);
257 res = g_utf8_get_char_validated (str2, -1);
259 if (res < 0)
260 res = (unsigned char) str2[0];
261 g_free (str2);
263 return res;
266 /* --------------------------------------------------------------------------------------------- */
268 void
269 tty_init (gboolean mouse_enable, gboolean is_xterm)
271 SLtt_Ignore_Beep = 1;
273 SLutf8_enable (-1); /* has to be called first before any of the other functions. */
274 SLtt_get_terminfo ();
276 * If the terminal in not in terminfo but begins with a well-known
277 * string such as "linux" or "xterm" S-Lang will go on, but the
278 * terminal size and several other variables won't be initialized
279 * (as of S-Lang 1.4.4). Detect it and abort. Also detect extremely
280 * small, large and negative screen dimensions.
282 if ((COLS < 10) || (LINES < 5)
283 || (COLS > SLTT_MAX_SCREEN_COLS) || (LINES > SLTT_MAX_SCREEN_ROWS))
285 fprintf (stderr,
286 _("Screen size %dx%d is not supported.\n"
287 "Check the TERM environment variable.\n"), COLS, LINES);
288 exit (EXIT_FAILURE);
291 tcgetattr (fileno (stdin), &boot_mode);
292 /* 255 = ignore abort char; XCTRL('g') for abort char = ^g */
293 SLang_init_tty (XCTRL ('g'), 1, 0);
295 if (mc_global.tty.ugly_line_drawing)
296 SLtt_Has_Alt_Charset = 0;
298 /* If SLang uses fileno(stderr) for terminal input MC will hang
299 if we call SLang_getkey between calls to open_error_pipe and
300 close_error_pipe, e.g. when we do a growing view of an gzipped
301 file. */
302 if (SLang_TT_Read_FD == fileno (stderr))
303 SLang_TT_Read_FD = fileno (stdin);
305 if (tcgetattr (SLang_TT_Read_FD, &new_mode) == 0)
307 #ifdef VDSUSP
308 new_mode.c_cc[VDSUSP] = NULL_VALUE; /* to ignore ^Y */
309 #endif
310 #ifdef VLNEXT
311 new_mode.c_cc[VLNEXT] = NULL_VALUE; /* to ignore ^V */
312 #endif
313 tcsetattr (SLang_TT_Read_FD, TCSADRAIN, &new_mode);
316 tty_reset_prog_mode ();
317 load_terminfo_keys ();
319 SLtt_Blink_Mode = tty_use_256colors ()? 1 : 0;
321 tty_start_interrupt_key ();
323 /* It's the small part from the previous init_key() */
324 init_key_input_fd ();
326 /* For 8-bit locales, NCurses handles 154 (0x9A) symbol properly, while S-Lang
327 * requires SLsmg_Display_Eight_Bit >= 154 (OR manual filtering if xterm display
328 * detected - but checking TERM would fail under screen, OR running xterm
329 * with allowC1Printable).
331 tty_display_8bit (FALSE);
333 SLsmg_init_smg ();
334 slsmg_active = TRUE;
335 if (!mouse_enable)
336 use_mouse_p = MOUSE_DISABLED;
337 tty_init_xterm_support (is_xterm); /* do it before tty_enter_ca_mode() call */
338 tty_enter_ca_mode ();
339 tty_keypad (TRUE);
340 tty_nodelay (FALSE);
342 tty_setup_sigwinch (sigwinch_handler);
345 /* --------------------------------------------------------------------------------------------- */
347 void
348 tty_shutdown (void)
350 char *op_cap;
352 disable_mouse ();
353 disable_bracketed_paste ();
354 tty_reset_shell_mode ();
355 tty_noraw_mode ();
356 tty_keypad (FALSE);
357 tty_reset_screen ();
358 tty_exit_ca_mode ();
359 SLang_reset_tty ();
360 slsmg_active = FALSE;
362 /* Load the op capability to reset the colors to those that were
363 * active when the program was started up
365 op_cap = SLtt_tgetstr ((SLFUTURE_CONST char *) "op");
366 if (op_cap != NULL)
368 fputs (op_cap, stdout);
369 fflush (stdout);
373 /* --------------------------------------------------------------------------------------------- */
375 void
376 tty_enter_ca_mode (void)
378 /* S-Lang handles alternate screen switching and cursor position saving */
381 /* --------------------------------------------------------------------------------------------- */
383 void
384 tty_exit_ca_mode (void)
386 /* S-Lang handles alternate screen switching and cursor position restoring */
389 /* --------------------------------------------------------------------------------------------- */
391 void
392 tty_change_screen_size (void)
394 SLtt_get_screen_size ();
395 if (slsmg_active)
396 SLsmg_reinit_smg ();
398 #ifdef ENABLE_SUBSHELL
399 if (mc_global.tty.use_subshell)
400 tty_resize (mc_global.tty.subshell_pty);
401 #endif
404 /* --------------------------------------------------------------------------------------------- */
405 /* Done each time we come back from done mode */
407 void
408 tty_reset_prog_mode (void)
410 tcsetattr (SLang_TT_Read_FD, TCSANOW, &new_mode);
411 SLsmg_init_smg ();
412 slsmg_active = TRUE;
413 SLsmg_touch_lines (0, LINES);
416 /* --------------------------------------------------------------------------------------------- */
417 /* Called each time we want to shutdown slang screen manager */
419 void
420 tty_reset_shell_mode (void)
422 tcsetattr (SLang_TT_Read_FD, TCSANOW, &boot_mode);
425 /* --------------------------------------------------------------------------------------------- */
427 void
428 tty_raw_mode (void)
430 tcsetattr (SLang_TT_Read_FD, TCSANOW, &new_mode);
433 /* --------------------------------------------------------------------------------------------- */
435 void
436 tty_noraw_mode (void)
440 /* --------------------------------------------------------------------------------------------- */
442 void
443 tty_noecho (void)
447 /* --------------------------------------------------------------------------------------------- */
450 tty_flush_input (void)
452 return 0; /* OK */
455 /* --------------------------------------------------------------------------------------------- */
457 void
458 tty_keypad (gboolean set)
460 char *keypad_string;
462 keypad_string = SLtt_tgetstr ((SLFUTURE_CONST char *) (set ? "ks" : "ke"));
463 if (keypad_string != NULL)
464 SLtt_write_string (keypad_string);
465 if (set && reset_hp_softkeys)
466 slang_reset_softkeys ();
469 /* --------------------------------------------------------------------------------------------- */
471 void
472 tty_nodelay (gboolean set)
474 no_slang_delay = set;
477 /* --------------------------------------------------------------------------------------------- */
480 tty_baudrate (void)
482 return SLang_TT_Baud_Rate;
485 /* --------------------------------------------------------------------------------------------- */
488 tty_lowlevel_getch (void)
490 int c;
492 if (no_slang_delay && (SLang_input_pending (0) == 0))
493 return -1;
495 c = SLang_getkey ();
496 if (c == SLANG_GETKEY_ERROR)
498 fprintf (stderr,
499 "SLang_getkey returned SLANG_GETKEY_ERROR\n"
500 "Assuming EOF on stdin and exiting\n");
501 exit (EXIT_FAILURE);
504 return c;
507 /* --------------------------------------------------------------------------------------------- */
510 tty_reset_screen (void)
512 SLsmg_reset_smg ();
513 slsmg_active = FALSE;
514 return 0; /* OK */
517 /* --------------------------------------------------------------------------------------------- */
519 void
520 tty_touch_screen (void)
522 SLsmg_touch_lines (0, LINES);
525 /* --------------------------------------------------------------------------------------------- */
527 void
528 tty_gotoyx (int y, int x)
530 SLsmg_gotorc (y, x);
533 /* --------------------------------------------------------------------------------------------- */
535 void
536 tty_getyx (int *py, int *px)
538 *py = SLsmg_get_row ();
539 *px = SLsmg_get_column ();
542 /* --------------------------------------------------------------------------------------------- */
544 void
545 tty_draw_hline (int y, int x, int ch, int len)
547 int x1;
549 if (y < 0 || y >= LINES || x >= COLS)
550 return;
552 x1 = x;
554 if (x < 0)
556 len += x;
557 if (len <= 0)
558 return;
559 x = 0;
562 if (ch == ACS_HLINE)
563 ch = mc_tty_frm[MC_TTY_FRM_HORIZ];
564 if (ch == 0)
565 ch = ACS_HLINE;
567 SLsmg_gotorc (y, x);
569 if (ch == ACS_HLINE)
570 SLsmg_draw_hline (len);
571 else
572 while (len-- != 0)
573 tty_print_char (ch);
575 SLsmg_gotorc (y, x1);
578 /* --------------------------------------------------------------------------------------------- */
580 void
581 tty_draw_vline (int y, int x, int ch, int len)
583 int y1;
585 if (x < 0 || x >= COLS || y >= LINES)
586 return;
588 y1 = y;
590 if (y < 0)
592 len += y;
593 if (len <= 0)
594 return;
595 y = 0;
598 if (ch == ACS_VLINE)
599 ch = mc_tty_frm[MC_TTY_FRM_VERT];
600 if (ch == 0)
601 ch = ACS_VLINE;
603 SLsmg_gotorc (y, x);
605 if (ch == ACS_VLINE)
606 SLsmg_draw_vline (len);
607 else
609 int pos = 0;
611 while (len-- != 0)
613 SLsmg_gotorc (y + pos, x);
614 tty_print_char (ch);
615 pos++;
619 SLsmg_gotorc (y1, x);
622 /* --------------------------------------------------------------------------------------------- */
624 void
625 tty_fill_region (int y, int x, int rows, int cols, unsigned char ch)
627 SLsmg_fill_region (y, x, rows, cols, ch);
630 /* --------------------------------------------------------------------------------------------- */
632 void
633 tty_set_alt_charset (gboolean alt_charset)
635 SLsmg_set_char_set ((int) alt_charset);
638 /* --------------------------------------------------------------------------------------------- */
640 void
641 tty_display_8bit (gboolean what)
643 SLsmg_Display_Eight_Bit = what ? 128 : 160;
646 /* --------------------------------------------------------------------------------------------- */
648 void
649 tty_print_char (int c)
651 SLsmg_write_char ((SLwchar_Type) ((unsigned int) c));
654 /* --------------------------------------------------------------------------------------------- */
656 void
657 tty_print_alt_char (int c, gboolean single)
659 #define DRAW(x, y) (x == y) \
660 ? SLsmg_draw_object (SLsmg_get_row(), SLsmg_get_column(), x) \
661 : SLsmg_write_char ((unsigned int) y)
662 switch (c)
664 case ACS_VLINE:
665 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_VERT : MC_TTY_FRM_DVERT]);
666 break;
667 case ACS_HLINE:
668 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_HORIZ : MC_TTY_FRM_DHORIZ]);
669 break;
670 case ACS_LTEE:
671 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTMIDDLE : MC_TTY_FRM_DLEFTMIDDLE]);
672 break;
673 case ACS_RTEE:
674 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTMIDDLE : MC_TTY_FRM_DRIGHTMIDDLE]);
675 break;
676 case ACS_TTEE:
677 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_TOPMIDDLE : MC_TTY_FRM_DTOPMIDDLE]);
678 break;
679 case ACS_BTEE:
680 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_BOTTOMMIDDLE : MC_TTY_FRM_DBOTTOMMIDDLE]);
681 break;
682 case ACS_ULCORNER:
683 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTTOP : MC_TTY_FRM_DLEFTTOP]);
684 break;
685 case ACS_LLCORNER:
686 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTBOTTOM : MC_TTY_FRM_DLEFTBOTTOM]);
687 break;
688 case ACS_URCORNER:
689 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTTOP : MC_TTY_FRM_DRIGHTTOP]);
690 break;
691 case ACS_LRCORNER:
692 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTBOTTOM : MC_TTY_FRM_DRIGHTBOTTOM]);
693 break;
694 case ACS_PLUS:
695 DRAW (c, mc_tty_frm[MC_TTY_FRM_CROSS]);
696 break;
697 default:
698 SLsmg_write_char ((unsigned int) c);
700 #undef DRAW
703 /* --------------------------------------------------------------------------------------------- */
705 void
706 tty_print_anychar (int c)
708 if (c > 255)
710 char str[UTF8_CHAR_LEN + 1];
711 int res;
713 res = g_unichar_to_utf8 (c, str);
714 if (res == 0)
716 str[0] = '.';
717 str[1] = '\0';
719 else
721 str[res] = '\0';
723 SLsmg_write_string ((char *) str_term_form (str));
725 else
727 if (!is_printable (c))
728 c = '.';
729 SLsmg_write_char ((SLwchar_Type) ((unsigned int) c));
733 /* --------------------------------------------------------------------------------------------- */
735 void
736 tty_print_string (const char *s)
738 SLsmg_write_string ((char *) str_term_form (s));
741 /* --------------------------------------------------------------------------------------------- */
743 void
744 tty_printf (const char *fmt, ...)
746 va_list args;
748 va_start (args, fmt);
749 SLsmg_vprintf ((char *) fmt, args);
750 va_end (args);
753 /* --------------------------------------------------------------------------------------------- */
755 char *
756 tty_tgetstr (const char *cap)
758 return SLtt_tgetstr ((SLFUTURE_CONST char *) cap);
761 /* --------------------------------------------------------------------------------------------- */
763 void
764 tty_refresh (void)
766 SLsmg_refresh ();
769 /* --------------------------------------------------------------------------------------------- */
771 void
772 tty_beep (void)
774 SLtt_beep ();
777 /* --------------------------------------------------------------------------------------------- */