Move content of src/filemanager/fileopctx.[ch] to src/filemanager/filegui.[ch].
[midnight-commander.git] / lib / tty / tty-ncurses.c
blob7766297799f5e54e96cd2f8921edcd5482e55801
1 /*
2 Interface to the terminal controlling library.
3 Ncurses wrapper.
5 Copyright (C) 2005-2024
6 Free Software Foundation, Inc.
8 Written by:
9 Andrew Borodin <aborodin@vmail.ru>, 2009.
10 Ilia Maslakov <il.smind@gmail.com>, 2009.
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: NCurses-based tty layer of Midnight-commander
32 #include <config.h>
34 #include <stdlib.h>
35 #include <stdarg.h>
36 #include <signal.h>
37 #ifdef HAVE_SYS_IOCTL_H
38 #include <sys/ioctl.h>
39 #endif
40 #include <termios.h>
42 #include "lib/global.h"
43 #include "lib/strutil.h" /* str_term_form */
44 #include "lib/util.h"
46 #ifndef WANT_TERM_H
47 #define WANT_TERM_H
48 #endif
50 #include "tty-internal.h" /* mc_tty_normalize_from_utf8() */
51 #include "tty.h"
52 #include "color.h" /* tty_setcolor */
53 #include "color-internal.h"
54 #include "key.h"
55 #include "mouse.h"
56 #include "win.h"
58 /* include at last !!! */
59 #ifdef WANT_TERM_H
60 #ifdef HAVE_NCURSES_TERM_H
61 #include <ncurses/term.h>
62 #else
63 #include <term.h>
64 #endif /* HAVE_NCURSES_TERM_H */
65 #endif /* WANT_TERM_H */
67 /*** global variables ****************************************************************************/
69 /*** file scope macro definitions ****************************************************************/
71 #if !defined(CTRL)
72 #define CTRL(x) ((x) & 0x1f)
73 #endif
75 #define yx_in_screen(y, x) \
76 (y >= 0 && y < LINES && x >= 0 && x < COLS)
78 /*** global variables ****************************************************************************/
80 /*** file scope type declarations ****************************************************************/
82 /*** forward declarations (file scope functions) *************************************************/
84 /*** file scope variables ************************************************************************/
86 /* ncurses supports cursor positions only within window */
87 /* We use our own cursor coordinates to support partially visible widgets */
88 static int mc_curs_row, mc_curs_col;
90 /* --------------------------------------------------------------------------------------------- */
91 /*** file scope functions ************************************************************************/
92 /* --------------------------------------------------------------------------------------------- */
94 static void
95 tty_setup_sigwinch (void (*handler) (int))
97 #if (NCURSES_VERSION_MAJOR >= 4) && defined (SIGWINCH)
98 struct sigaction act, oact;
100 memset (&act, 0, sizeof (act));
101 act.sa_handler = handler;
102 sigemptyset (&act.sa_mask);
103 #ifdef SA_RESTART
104 act.sa_flags = SA_RESTART;
105 #endif /* SA_RESTART */
106 my_sigaction (SIGWINCH, &act, &oact);
107 #endif /* SIGWINCH */
109 tty_create_winch_pipe ();
112 /* --------------------------------------------------------------------------------------------- */
114 static void
115 sigwinch_handler (int dummy)
117 ssize_t n = 0;
119 (void) dummy;
121 n = write (sigwinch_pipe[1], "", 1);
122 (void) n;
125 /* --------------------------------------------------------------------------------------------- */
128 * Get visible part of area.
130 * @returns TRUE if any part of area is in screen bounds, FALSE otherwise.
132 static gboolean
133 tty_clip (int *y, int *x, int *rows, int *cols)
135 if (*y < 0)
137 *rows += *y;
139 if (*rows <= 0)
140 return FALSE;
142 *y = 0;
145 if (*x < 0)
147 *cols += *x;
149 if (*cols <= 0)
150 return FALSE;
152 *x = 0;
155 if (*y + *rows > LINES)
156 *rows = LINES - *y;
158 if (*rows <= 0)
159 return FALSE;
161 if (*x + *cols > COLS)
162 *cols = COLS - *x;
164 if (*cols <= 0)
165 return FALSE;
167 return TRUE;
170 /* --------------------------------------------------------------------------------------------- */
171 /*** public functions ****************************************************************************/
172 /* --------------------------------------------------------------------------------------------- */
175 mc_tty_normalize_lines_char (const char *ch)
177 char *str2;
178 int res;
180 struct mc_tty_lines_struct
182 const char *line;
183 int line_code;
184 } const lines_codes[] = {
185 {"\342\224\230", ACS_LRCORNER}, /* ┌ */
186 {"\342\224\224", ACS_LLCORNER}, /* └ */
187 {"\342\224\220", ACS_URCORNER}, /* ┐ */
188 {"\342\224\214", ACS_ULCORNER}, /* ┘ */
189 {"\342\224\234", ACS_LTEE}, /* ├ */
190 {"\342\224\244", ACS_RTEE}, /* ┤ */
191 {"\342\224\254", ACS_TTEE}, /* ┬ */
192 {"\342\224\264", ACS_BTEE}, /* ┴ */
193 {"\342\224\200", ACS_HLINE}, /* ─ */
194 {"\342\224\202", ACS_VLINE}, /* │ */
195 {"\342\224\274", ACS_PLUS}, /* ┼ */
197 {"\342\225\235", ACS_LRCORNER | A_BOLD}, /* ╔ */
198 {"\342\225\232", ACS_LLCORNER | A_BOLD}, /* ╚ */
199 {"\342\225\227", ACS_URCORNER | A_BOLD}, /* ╗ */
200 {"\342\225\224", ACS_ULCORNER | A_BOLD}, /* ╝ */
201 {"\342\225\237", ACS_LTEE | A_BOLD}, /* ╟ */
202 {"\342\225\242", ACS_RTEE | A_BOLD}, /* ╢ */
203 {"\342\225\244", ACS_TTEE | A_BOLD}, /* ╤ */
204 {"\342\225\247", ACS_BTEE | A_BOLD}, /* ╧ */
205 {"\342\225\220", ACS_HLINE | A_BOLD}, /* ═ */
206 {"\342\225\221", ACS_VLINE | A_BOLD}, /* ║ */
208 {NULL, 0}
211 if (ch == NULL)
212 return (int) ' ';
214 for (res = 0; lines_codes[res].line; res++)
216 if (strcmp (ch, lines_codes[res].line) == 0)
217 return lines_codes[res].line_code;
220 str2 = mc_tty_normalize_from_utf8 (ch);
221 res = g_utf8_get_char_validated (str2, -1);
223 if (res < 0)
224 res = (unsigned char) str2[0];
225 g_free (str2);
227 return res;
230 /* --------------------------------------------------------------------------------------------- */
232 void
233 tty_init (gboolean mouse_enable, gboolean is_xterm)
235 struct termios mode;
237 initscr ();
239 #ifdef HAVE_ESCDELAY
241 * If ncurses exports the ESCDELAY variable, it should be set to
242 * a low value, or you'll experience a delay in processing escape
243 * sequences that are recognized by mc (e.g. Esc-Esc). On the other
244 * hand, making ESCDELAY too small can result in some sequences
245 * (e.g. cursor arrows) being reported as separate keys under heavy
246 * processor load, and this can be a problem if mc hasn't learned
247 * them in the "Learn Keys" dialog. The value is in milliseconds.
249 ESCDELAY = 200;
250 #endif /* HAVE_ESCDELAY */
252 tcgetattr (STDIN_FILENO, &mode);
253 /* use Ctrl-g to generate SIGINT */
254 mode.c_cc[VINTR] = CTRL ('g'); /* ^g */
255 /* disable SIGQUIT to allow use Ctrl-\ key */
256 mode.c_cc[VQUIT] = NULL_VALUE;
257 tcsetattr (STDIN_FILENO, TCSANOW, &mode);
259 /* curses remembers the "in-program" modes after this call */
260 def_prog_mode ();
262 tty_start_interrupt_key ();
264 if (!mouse_enable)
265 use_mouse_p = MOUSE_DISABLED;
266 tty_init_xterm_support (is_xterm); /* do it before tty_enter_ca_mode() call */
267 tty_enter_ca_mode ();
268 tty_raw_mode ();
269 noecho ();
270 keypad (stdscr, TRUE);
271 nodelay (stdscr, FALSE);
273 tty_setup_sigwinch (sigwinch_handler);
276 /* --------------------------------------------------------------------------------------------- */
278 void
279 tty_shutdown (void)
281 tty_destroy_winch_pipe ();
282 tty_reset_shell_mode ();
283 tty_noraw_mode ();
284 tty_keypad (FALSE);
285 tty_reset_screen ();
286 tty_exit_ca_mode ();
289 /* --------------------------------------------------------------------------------------------- */
291 void
292 tty_enter_ca_mode (void)
294 if (mc_global.tty.xterm_flag && smcup != NULL)
296 fprintf (stdout, /* ESC_STR ")0" */ ESC_STR "7" ESC_STR "[?47h");
297 fflush (stdout);
301 /* --------------------------------------------------------------------------------------------- */
303 void
304 tty_exit_ca_mode (void)
306 if (mc_global.tty.xterm_flag && rmcup != NULL)
308 fprintf (stdout, ESC_STR "[?47l" ESC_STR "8" ESC_STR "[m");
309 fflush (stdout);
313 /* --------------------------------------------------------------------------------------------- */
315 void
316 tty_change_screen_size (void)
318 #if defined(TIOCGWINSZ) && NCURSES_VERSION_MAJOR >= 4
319 struct winsize winsz;
321 winsz.ws_col = winsz.ws_row = 0;
323 #ifndef NCURSES_VERSION
324 tty_noraw_mode ();
325 tty_reset_screen ();
326 #endif
328 /* Ioctl on the STDIN_FILENO */
329 ioctl (fileno (stdout), TIOCGWINSZ, &winsz);
330 if (winsz.ws_col != 0 && winsz.ws_row != 0)
332 #if defined(NCURSES_VERSION) && defined(HAVE_RESIZETERM)
333 resizeterm (winsz.ws_row, winsz.ws_col);
334 clearok (stdscr, TRUE); /* sigwinch's should use a semaphore! */
335 #else
336 COLS = winsz.ws_col;
337 LINES = winsz.ws_row;
338 #endif
340 #endif /* defined(TIOCGWINSZ) || NCURSES_VERSION_MAJOR >= 4 */
342 #ifdef ENABLE_SUBSHELL
343 if (mc_global.tty.use_subshell)
344 tty_resize (mc_global.tty.subshell_pty);
345 #endif
348 /* --------------------------------------------------------------------------------------------- */
350 void
351 tty_reset_prog_mode (void)
353 reset_prog_mode ();
356 /* --------------------------------------------------------------------------------------------- */
358 void
359 tty_reset_shell_mode (void)
361 reset_shell_mode ();
364 /* --------------------------------------------------------------------------------------------- */
366 void
367 tty_raw_mode (void)
369 raw (); /* FIXME: unneeded? */
370 cbreak ();
373 /* --------------------------------------------------------------------------------------------- */
375 void
376 tty_noraw_mode (void)
378 nocbreak (); /* FIXME: unneeded? */
379 noraw ();
382 /* --------------------------------------------------------------------------------------------- */
384 void
385 tty_noecho (void)
387 noecho ();
390 /* --------------------------------------------------------------------------------------------- */
393 tty_flush_input (void)
395 return flushinp ();
398 /* --------------------------------------------------------------------------------------------- */
400 void
401 tty_keypad (gboolean set)
403 keypad (stdscr, (bool) set);
406 /* --------------------------------------------------------------------------------------------- */
408 void
409 tty_nodelay (gboolean set)
411 nodelay (stdscr, (bool) set);
414 /* --------------------------------------------------------------------------------------------- */
417 tty_baudrate (void)
419 return baudrate ();
422 /* --------------------------------------------------------------------------------------------- */
425 tty_lowlevel_getch (void)
427 return getch ();
430 /* --------------------------------------------------------------------------------------------- */
433 tty_reset_screen (void)
435 return endwin ();
438 /* --------------------------------------------------------------------------------------------- */
440 void
441 tty_touch_screen (void)
443 touchwin (stdscr);
446 /* --------------------------------------------------------------------------------------------- */
448 void
449 tty_gotoyx (int y, int x)
451 mc_curs_row = y;
452 mc_curs_col = x;
454 if (y < 0)
455 y = 0;
456 if (y >= LINES)
457 y = LINES - 1;
459 if (x < 0)
460 x = 0;
461 if (x >= COLS)
462 x = COLS - 1;
464 move (y, x);
467 /* --------------------------------------------------------------------------------------------- */
469 void
470 tty_getyx (int *py, int *px)
472 *py = mc_curs_row;
473 *px = mc_curs_col;
476 /* --------------------------------------------------------------------------------------------- */
478 void
479 tty_draw_hline (int y, int x, int ch, int len)
481 int x1;
483 if (y < 0 || y >= LINES || x >= COLS)
484 return;
486 x1 = x;
488 if (x < 0)
490 len += x;
491 if (len <= 0)
492 return;
493 x = 0;
496 if ((chtype) ch == ACS_HLINE)
497 ch = mc_tty_frm[MC_TTY_FRM_HORIZ];
499 move (y, x);
500 hline (ch, len);
501 move (y, x1);
503 mc_curs_row = y;
504 mc_curs_col = x1;
507 /* --------------------------------------------------------------------------------------------- */
509 void
510 tty_draw_vline (int y, int x, int ch, int len)
512 int y1;
514 if (x < 0 || x >= COLS || y >= LINES)
515 return;
517 y1 = y;
519 if (y < 0)
521 len += y;
522 if (len <= 0)
523 return;
524 y = 0;
527 if ((chtype) ch == ACS_VLINE)
528 ch = mc_tty_frm[MC_TTY_FRM_VERT];
530 move (y, x);
531 vline (ch, len);
532 move (y1, x);
534 mc_curs_row = y1;
535 mc_curs_col = x;
538 /* --------------------------------------------------------------------------------------------- */
540 void
541 tty_fill_region (int y, int x, int rows, int cols, unsigned char ch)
543 int i;
545 if (!tty_clip (&y, &x, &rows, &cols))
546 return;
548 for (i = 0; i < rows; i++)
550 move (y + i, x);
551 hline (ch, cols);
554 move (y, x);
556 mc_curs_row = y;
557 mc_curs_col = x;
560 /* --------------------------------------------------------------------------------------------- */
562 void
563 tty_colorize_area (int y, int x, int rows, int cols, int color)
565 #ifdef ENABLE_SHADOWS
566 cchar_t *ctext;
567 wchar_t wch[10]; /* TODO not sure if the length is correct */
568 attr_t attrs;
569 short color_pair;
571 if (!use_colors || !tty_clip (&y, &x, &rows, &cols))
572 return;
574 tty_setcolor (color);
575 ctext = g_malloc (sizeof (cchar_t) * (cols + 1));
577 for (int row = 0; row < rows; row++)
579 mvin_wchnstr (y + row, x, ctext, cols);
581 for (int col = 0; col < cols; col++)
583 getcchar (&ctext[col], wch, &attrs, &color_pair, NULL);
584 setcchar (&ctext[col], wch, attrs, color, NULL);
587 mvadd_wchnstr (y + row, x, ctext, cols);
590 g_free (ctext);
591 #else
592 (void) y;
593 (void) x;
594 (void) rows;
595 (void) cols;
596 (void) color;
597 #endif /* ENABLE_SHADOWS */
600 /* --------------------------------------------------------------------------------------------- */
602 void
603 tty_set_alt_charset (gboolean alt_charset)
605 (void) alt_charset;
608 /* --------------------------------------------------------------------------------------------- */
610 void
611 tty_display_8bit (gboolean what)
613 meta (stdscr, (int) what);
616 /* --------------------------------------------------------------------------------------------- */
618 void
619 tty_print_char (int c)
621 if (yx_in_screen (mc_curs_row, mc_curs_col))
622 addch (c);
623 mc_curs_col++;
626 /* --------------------------------------------------------------------------------------------- */
628 void
629 tty_print_anychar (int c)
631 if (mc_global.utf8_display || c > 255)
633 int res;
634 unsigned char str[UTF8_CHAR_LEN + 1];
636 res = g_unichar_to_utf8 (c, (char *) str);
637 if (res == 0)
639 if (yx_in_screen (mc_curs_row, mc_curs_col))
640 addch ('.');
641 mc_curs_col++;
643 else
645 const char *s;
647 str[res] = '\0';
648 s = str_term_form ((char *) str);
650 if (yx_in_screen (mc_curs_row, mc_curs_col))
651 addstr (s);
653 if (g_unichar_iswide (c))
654 mc_curs_col += 2;
655 else if (!g_unichar_iszerowidth (c))
656 mc_curs_col++;
659 else
661 if (yx_in_screen (mc_curs_row, mc_curs_col))
662 addch (c);
663 mc_curs_col++;
667 /* --------------------------------------------------------------------------------------------- */
669 void
670 tty_print_alt_char (int c, gboolean single)
672 if (yx_in_screen (mc_curs_row, mc_curs_col))
674 if ((chtype) c == ACS_VLINE)
675 c = mc_tty_frm[single ? MC_TTY_FRM_VERT : MC_TTY_FRM_DVERT];
676 else if ((chtype) c == ACS_HLINE)
677 c = mc_tty_frm[single ? MC_TTY_FRM_HORIZ : MC_TTY_FRM_DHORIZ];
678 else if ((chtype) c == ACS_LTEE)
679 c = mc_tty_frm[single ? MC_TTY_FRM_LEFTMIDDLE : MC_TTY_FRM_DLEFTMIDDLE];
680 else if ((chtype) c == ACS_RTEE)
681 c = mc_tty_frm[single ? MC_TTY_FRM_RIGHTMIDDLE : MC_TTY_FRM_DRIGHTMIDDLE];
682 else if ((chtype) c == ACS_ULCORNER)
683 c = mc_tty_frm[single ? MC_TTY_FRM_LEFTTOP : MC_TTY_FRM_DLEFTTOP];
684 else if ((chtype) c == ACS_LLCORNER)
685 c = mc_tty_frm[single ? MC_TTY_FRM_LEFTBOTTOM : MC_TTY_FRM_DLEFTBOTTOM];
686 else if ((chtype) c == ACS_URCORNER)
687 c = mc_tty_frm[single ? MC_TTY_FRM_RIGHTTOP : MC_TTY_FRM_DRIGHTTOP];
688 else if ((chtype) c == ACS_LRCORNER)
689 c = mc_tty_frm[single ? MC_TTY_FRM_RIGHTBOTTOM : MC_TTY_FRM_DRIGHTBOTTOM];
690 else if ((chtype) c == ACS_PLUS)
691 c = mc_tty_frm[MC_TTY_FRM_CROSS];
693 addch (c);
696 mc_curs_col++;
699 /* --------------------------------------------------------------------------------------------- */
701 void
702 tty_print_string (const char *s)
704 int len;
705 int start = 0;
707 s = str_term_form (s);
708 len = str_term_width1 (s);
710 /* line is upper or below the screen or entire line is before or after screen */
711 if (mc_curs_row < 0 || mc_curs_row >= LINES || mc_curs_col + len <= 0 || mc_curs_col >= COLS)
713 mc_curs_col += len;
714 return;
717 /* skip invisible left part */
718 if (mc_curs_col < 0)
720 start = -mc_curs_col;
721 len += mc_curs_col;
722 mc_curs_col = 0;
725 mc_curs_col += len;
726 if (mc_curs_col >= COLS)
727 len = COLS - (mc_curs_col - len);
729 addstr (str_term_substring (s, start, len));
732 /* --------------------------------------------------------------------------------------------- */
734 void
735 tty_printf (const char *fmt, ...)
737 va_list args;
738 char buf[BUF_1K]; /* FIXME: is it enough? */
740 va_start (args, fmt);
741 g_vsnprintf (buf, sizeof (buf), fmt, args);
742 va_end (args);
743 tty_print_string (buf);
746 /* --------------------------------------------------------------------------------------------- */
748 char *
749 tty_tgetstr (const char *cap)
751 char *unused = NULL;
753 return tgetstr ((NCURSES_CONST char *) cap, &unused);
756 /* --------------------------------------------------------------------------------------------- */
758 void
759 tty_refresh (void)
761 refresh ();
762 doupdate ();
765 /* --------------------------------------------------------------------------------------------- */
767 void
768 tty_beep (void)
770 beep ();
773 /* --------------------------------------------------------------------------------------------- */