Fixed behavior after pressing PageDown at end of file
[midnight-commander.git] / lib / tty / tty-slang.c
blob1ec971b6f32181857de84c215e49ec4c509bd40c
1 /*
2 Interface to the terminal controlling library.
3 Slang wrapper.
5 Copyright (C) 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
7 Written by:
8 Andrew Borodin <aborodin@vmail.ru>, 2009.
10 This file is part of the Midnight Commander.
12 The Midnight Commander is free software; you can redistribute it
13 and/or modify it under the terms of the GNU General Public License as
14 published by the Free Software Foundation; either version 2 of the
15 License, or (at your option) any later version.
17 The Midnight Commander is distributed in the hope that it will be
18 useful, but WITHOUT ANY WARRANTY; without even the implied warranty
19 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 MA 02110-1301, USA.
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 <termios.h>
38 #include <sys/types.h> /* size_t */
39 #include <unistd.h>
40 #include <signal.h>
42 #include "lib/global.h"
43 #include "lib/strutil.h" /* str_term_form */
45 #include "tty-internal.h" /* slow_tty */
46 #include "tty.h"
47 #include "color-slang.h"
48 #include "color-internal.h"
49 #include "mouse.h" /* Gpm_Event is required in key.h */
50 #include "key.h" /* define_sequence */
51 #include "win.h"
54 /*** global variables **************************************************/
56 extern int reset_hp_softkeys;
58 /*** file scope macro definitions **************************************/
60 #ifndef SA_RESTART
61 # define SA_RESTART 0
62 #endif
64 #ifndef SLTT_MAX_SCREEN_COLS
65 #define SLTT_MAX_SCREEN_COLS 512
66 #endif
68 #ifndef SLTT_MAX_SCREEN_ROWS
69 #define SLTT_MAX_SCREEN_ROWS 512
70 #endif
72 /*** file scope type declarations **************************************/
74 /*** file scope variables **********************************************/
76 /* Various saved termios settings that we control here */
77 static struct termios boot_mode;
78 static struct termios new_mode;
80 /* Controls whether we should wait for input in tty_lowlevel_getch */
81 static gboolean no_slang_delay;
83 /* This table describes which capabilities we want and which values we
84 * assign to them.
86 static const struct
88 int key_code;
89 const char *key_name;
90 } key_table[] =
92 /* *INDENT-OFF* */
93 { KEY_F (0), "k0" },
94 { KEY_F (1), "k1" },
95 { KEY_F (2), "k2" },
96 { KEY_F (3), "k3" },
97 { KEY_F (4), "k4" },
98 { KEY_F (5), "k5" },
99 { KEY_F (6), "k6" },
100 { KEY_F (7), "k7" },
101 { KEY_F (8), "k8" },
102 { KEY_F (9), "k9" },
103 { KEY_F (10), "k;" },
104 { KEY_F (11), "F1" },
105 { KEY_F (12), "F2" },
106 { KEY_F (13), "F3" },
107 { KEY_F (14), "F4" },
108 { KEY_F (15), "F5" },
109 { KEY_F (16), "F6" },
110 { KEY_F (17), "F7" },
111 { KEY_F (18), "F8" },
112 { KEY_F (19), "F9" },
113 { KEY_F (20), "FA" },
114 { KEY_IC, "kI" },
115 { KEY_NPAGE, "kN" },
116 { KEY_PPAGE, "kP" },
117 { KEY_LEFT, "kl" },
118 { KEY_RIGHT, "kr" },
119 { KEY_UP, "ku" },
120 { KEY_DOWN, "kd" },
121 { KEY_DC, "kD" },
122 { KEY_BACKSPACE, "kb" },
123 { KEY_HOME, "kh" },
124 { KEY_END, "@7" },
125 { 0, NULL }
126 /* *INDENT-ON* */
129 /*** file scope functions **********************************************/
131 /* HP Terminals have capabilities (pfkey, pfloc, pfx) to program function keys.
132 elm 2.4pl15 invoked with the -K option utilizes these softkeys and the
133 consequence is that function keys don't work in MC sometimes...
134 Unfortunately I don't now the one and only escape sequence to turn off.
135 softkeys (elm uses three different capabilities to turn on softkeys and two.
136 capabilities to turn them off)..
137 Among other things elm uses the pair we already use in slang_keypad. That's.
138 the reason why I call slang_reset_softkeys from slang_keypad. In lack of
139 something better the softkeys are programmed to their defaults from the
140 termcap/terminfo database.
141 The escape sequence to program the softkeys is taken from elm and it is.
142 hardcoded because neither slang nor ncurses 4.1 know how to 'printf' this.
143 sequence. -- Norbert
146 static void
147 slang_reset_softkeys (void)
149 int key;
150 char *send;
151 static const char display[] = " ";
152 char tmp[BUF_SMALL];
154 for (key = 1; key < 9; key++)
156 g_snprintf (tmp, sizeof (tmp), "k%d", key);
157 send = (char *) SLtt_tgetstr (tmp);
158 if (send != NULL)
160 g_snprintf (tmp, sizeof (tmp), "\033&f%dk%dd%dL%s%s", key,
161 (int) (sizeof (display) - 1), (int) strlen (send), display, send);
162 SLtt_write_string (tmp);
167 static void
168 do_define_key (int code, const char *strcap)
170 char *seq;
172 seq = (char *) SLtt_tgetstr ((char *) strcap);
173 if (seq != NULL)
174 define_sequence (code, seq, MCKEY_NOACTION);
177 static void
178 load_terminfo_keys (void)
180 int i;
182 for (i = 0; key_table[i].key_code; i++)
183 do_define_key (key_table[i].key_code, key_table[i].key_name);
186 /* --------------------------------------------------------------------------------------------- */
187 /*** public functions **************************************************/
188 /* --------------------------------------------------------------------------------------------- */
191 mc_tty_normalize_lines_char (const char *str)
193 char *str2;
194 int res;
196 struct mc_tty_lines_struct
198 const char *line;
199 int line_code;
200 } const lines_codes[] = {
201 {"\342\224\214", SLSMG_ULCORN_CHAR},
202 {"\342\224\220", SLSMG_URCORN_CHAR},
203 {"\342\224\224", SLSMG_LLCORN_CHAR},
204 {"\342\224\230", SLSMG_LRCORN_CHAR},
205 {"\342\224\234", SLSMG_LTEE_CHAR},
206 {"\342\224\244", SLSMG_RTEE_CHAR},
207 {"\342\224\254", SLSMG_UTEE_CHAR},
208 {"\342\224\264", SLSMG_DTEE_CHAR},
209 {"\342\224\200", SLSMG_HLINE_CHAR},
210 {"\342\224\202", SLSMG_VLINE_CHAR},
211 {"\342\224\274", SLSMG_PLUS_CHAR},
213 {NULL, 0}
216 if (!str)
217 return (int) ' ';
219 for (res = 0; lines_codes[res].line; res++)
221 if (strcmp (str, lines_codes[res].line) == 0)
222 return lines_codes[res].line_code;
225 str2 = mc_tty_normalize_from_utf8 (str);
226 res = g_utf8_get_char_validated (str2, -1);
228 if (res < 0)
229 res = (unsigned char) str2[0];
230 g_free (str2);
232 return res;
235 /* --------------------------------------------------------------------------------------------- */
236 void
237 tty_init (gboolean slow, gboolean ugly_lines)
239 slow_tty = slow;
240 ugly_line_drawing = ugly_lines;
242 SLtt_get_terminfo ();
243 SLutf8_enable (-1);
245 * If the terminal in not in terminfo but begins with a well-known
246 * string such as "linux" or "xterm" S-Lang will go on, but the
247 * terminal size and several other variables won't be initialized
248 * (as of S-Lang 1.4.4). Detect it and abort. Also detect extremely
249 * small, large and negative screen dimensions.
251 if ((COLS < 10) || (LINES < 5)
252 || (COLS > SLTT_MAX_SCREEN_COLS) || (LINES > SLTT_MAX_SCREEN_ROWS))
254 fprintf (stderr,
255 _("Screen size %dx%d is not supported.\n"
256 "Check the TERM environment variable.\n"), COLS, LINES);
257 exit (EXIT_FAILURE);
260 tcgetattr (fileno (stdin), &boot_mode);
261 /* 255 = ignore abort char; XCTRL('g') for abort char = ^g */
262 SLang_init_tty (XCTRL ('g'), 1, 0);
264 if (ugly_lines)
265 SLtt_Has_Alt_Charset = 0;
267 /* If SLang uses fileno(stderr) for terminal input MC will hang
268 if we call SLang_getkey between calls to open_error_pipe and
269 close_error_pipe, e.g. when we do a growing view of an gzipped
270 file. */
271 if (SLang_TT_Read_FD == fileno (stderr))
272 SLang_TT_Read_FD = fileno (stdin);
274 if (tcgetattr (SLang_TT_Read_FD, &new_mode) == 0)
276 #ifdef VDSUSP
277 new_mode.c_cc[VDSUSP] = NULL_VALUE; /* to ignore ^Y */
278 #endif
279 #ifdef VLNEXT
280 new_mode.c_cc[VLNEXT] = NULL_VALUE; /* to ignore ^V */
281 #endif
282 tcsetattr (SLang_TT_Read_FD, TCSADRAIN, &new_mode);
285 tty_reset_prog_mode ();
286 load_terminfo_keys ();
287 SLtt_Blink_Mode = 0;
289 tty_start_interrupt_key ();
291 /* It's the small part from the previous init_key() */
292 init_key_input_fd ();
294 SLsmg_init_smg ();
295 do_enter_ca_mode ();
296 tty_keypad (TRUE);
297 tty_nodelay (FALSE);
300 void
301 tty_shutdown (void)
303 char *op_cap;
305 SLsmg_reset_smg ();
306 tty_reset_shell_mode ();
307 do_exit_ca_mode ();
308 SLang_reset_tty ();
310 /* Load the op capability to reset the colors to those that were
311 * active when the program was started up
313 op_cap = SLtt_tgetstr ((char *) "op");
314 if (op_cap != NULL)
316 fputs (op_cap, stdout);
317 fflush (stdout);
321 /* Done each time we come back from done mode */
322 void
323 tty_reset_prog_mode (void)
325 tcsetattr (SLang_TT_Read_FD, TCSANOW, &new_mode);
326 SLsmg_init_smg ();
327 SLsmg_touch_lines (0, LINES);
330 /* Called each time we want to shutdown slang screen manager */
331 void
332 tty_reset_shell_mode (void)
334 tcsetattr (SLang_TT_Read_FD, TCSANOW, &boot_mode);
337 void
338 tty_raw_mode (void)
340 tcsetattr (SLang_TT_Read_FD, TCSANOW, &new_mode);
343 void
344 tty_noraw_mode (void)
348 void
349 tty_noecho (void)
354 tty_flush_input (void)
356 return 0; /* OK */
359 void
360 tty_keypad (gboolean set)
362 char *keypad_string;
364 keypad_string = (char *) SLtt_tgetstr ((char *) (set ? "ks" : "ke"));
365 if (keypad_string != NULL)
366 SLtt_write_string (keypad_string);
367 if (set && reset_hp_softkeys)
368 slang_reset_softkeys ();
371 void
372 tty_nodelay (gboolean set)
374 no_slang_delay = set;
378 tty_baudrate (void)
380 return SLang_TT_Baud_Rate;
384 tty_lowlevel_getch (void)
386 int c;
388 if (no_slang_delay && (SLang_input_pending (0) == 0))
389 return -1;
391 c = SLang_getkey ();
392 if (c == SLANG_GETKEY_ERROR)
394 fprintf (stderr,
395 "SLang_getkey returned SLANG_GETKEY_ERROR\n"
396 "Assuming EOF on stdin and exiting\n");
397 exit (EXIT_FAILURE);
400 return c;
404 tty_reset_screen (void)
406 SLsmg_reset_smg ();
407 return 0; /* OK */
410 void
411 tty_touch_screen (void)
413 SLsmg_touch_lines (0, LINES);
416 void
417 tty_gotoyx (int y, int x)
419 SLsmg_gotorc (y, x);
422 void
423 tty_getyx (int *py, int *px)
425 *py = SLsmg_get_row ();
426 *px = SLsmg_get_column ();
429 /* if x < 0 or y < 0, draw line staring from current position */
430 void
431 tty_draw_hline (int y, int x, int ch, int len)
433 if (ch == ACS_HLINE)
434 ch = mc_tty_frm[MC_TTY_FRM_HORIZ];
436 if ((y < 0) || (x < 0))
438 y = SLsmg_get_row ();
439 x = SLsmg_get_column ();
441 else
442 SLsmg_gotorc (y, x);
444 if (ch == 0)
445 ch = ACS_HLINE;
447 if (ch == ACS_HLINE)
448 SLsmg_draw_hline (len);
449 else
450 while (len-- != 0)
451 tty_print_char (ch);
453 SLsmg_gotorc (y, x);
456 /* if x < 0 or y < 0, draw line staring from current position */
457 void
458 tty_draw_vline (int y, int x, int ch, int len)
460 if (ch == ACS_VLINE)
461 ch = mc_tty_frm[MC_TTY_FRM_VERT];
463 if ((y < 0) || (x < 0))
465 y = SLsmg_get_row ();
466 x = SLsmg_get_column ();
468 else
469 SLsmg_gotorc (y, x);
471 if (ch == 0)
472 ch = ACS_VLINE;
474 if (ch == ACS_VLINE)
475 SLsmg_draw_vline (len);
476 else
478 int pos = 0;
480 while (len-- != 0)
482 SLsmg_gotorc (y + pos, x);
483 tty_print_char (ch);
484 pos++;
488 SLsmg_gotorc (y, x);
491 void
492 tty_fill_region (int y, int x, int rows, int cols, unsigned char ch)
494 SLsmg_fill_region (y, x, rows, cols, ch);
497 void
498 tty_set_alt_charset (gboolean alt_charset)
500 SLsmg_set_char_set ((int) alt_charset);
503 void
504 tty_display_8bit (gboolean what)
506 SLsmg_Display_Eight_Bit = what ? 128 : 160;
509 void
510 tty_print_char (int c)
512 SLsmg_write_char ((SLwchar_Type) ((unsigned int) c));
515 void
516 tty_print_alt_char (int c, gboolean single)
518 #define DRAW(x, y) (x == y) \
519 ? SLsmg_draw_object (SLsmg_get_row(), SLsmg_get_column(), x) \
520 : SLsmg_write_char ((unsigned int) y)
521 switch (c)
523 case ACS_VLINE:
524 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_VERT : MC_TTY_FRM_DVERT]);
525 break;
526 case ACS_HLINE:
527 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_HORIZ : MC_TTY_FRM_DHORIZ]);
528 break;
529 case ACS_LTEE:
530 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTMIDDLE : MC_TTY_FRM_DLEFTMIDDLE]);
531 break;
532 case ACS_RTEE:
533 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTMIDDLE : MC_TTY_FRM_DRIGHTMIDDLE]);
534 break;
535 case ACS_ULCORNER:
536 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTTOP : MC_TTY_FRM_DLEFTTOP]);
537 break;
538 case ACS_LLCORNER:
539 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_LEFTBOTTOM : MC_TTY_FRM_DLEFTBOTTOM]);
540 break;
541 case ACS_URCORNER:
542 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTTOP : MC_TTY_FRM_DRIGHTTOP]);
543 break;
544 case ACS_LRCORNER:
545 DRAW (c, mc_tty_frm[single ? MC_TTY_FRM_RIGHTBOTTOM : MC_TTY_FRM_DRIGHTBOTTOM]);
546 break;
547 case ACS_PLUS:
548 DRAW (c, mc_tty_frm[MC_TTY_FRM_CROSS]);
549 break;
550 default:
551 SLsmg_write_char ((unsigned int) c);
553 #undef DRAW
556 void
557 tty_print_anychar (int c)
559 char str[6 + 1];
561 if (c > 255)
563 int res = g_unichar_to_utf8 (c, str);
564 if (res == 0)
566 str[0] = '.';
567 str[1] = '\0';
569 else
571 str[res] = '\0';
573 SLsmg_write_string ((char *) str_term_form (str));
575 else
577 SLsmg_write_char ((SLwchar_Type) ((unsigned int) c));
581 void
582 tty_print_string (const char *s)
584 SLsmg_write_string ((char *) str_term_form (s));
587 void
588 tty_printf (const char *fmt, ...)
590 va_list args;
592 va_start (args, fmt);
593 SLsmg_vprintf ((char *) fmt, args);
594 va_end (args);
597 char *
598 tty_tgetstr (const char *cap)
600 return SLtt_tgetstr ((char *) cap);
603 void
604 tty_refresh (void)
606 SLsmg_refresh ();
609 void
610 tty_setup_sigwinch (void (*handler) (int))
612 #ifdef SIGWINCH
613 struct sigaction act, oact;
614 act.sa_handler = handler;
615 sigemptyset (&act.sa_mask);
616 act.sa_flags = 0;
617 #ifdef SA_RESTART
618 act.sa_flags |= SA_RESTART;
619 #endif /* SA_RESTART */
620 sigaction (SIGWINCH, &act, &oact);
621 #endif /* SIGWINCH */
624 void
625 tty_beep (void)
627 SLtt_beep ();