diff options
Diffstat (limited to 'src/term.c')
| -rw-r--r-- | src/term.c | 1573 |
1 files changed, 1277 insertions, 296 deletions
diff --git a/src/term.c b/src/term.c index 2966466aed2..9b1e7cad4b2 100644 --- a/src/term.c +++ b/src/term.c @@ -1,5 +1,5 @@ /* Terminal control module for terminals described by TERMCAP - Copyright (C) 1985-1987, 1993-1995, 1998, 2000-2013 Free Software + Copyright (C) 1985-1987, 1993-1995, 1998, 2000-2015 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -44,18 +44,18 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include "keymap.h" #include "blockinput.h" #include "syssignal.h" -#include "systty.h" -#include "intervals.h" #ifdef MSDOS #include "msdos.h" static int been_here = -1; #endif -#include "cm.h" -#ifdef HAVE_X_WINDOWS -#include "xterm.h" +#ifdef USE_X_TOOLKIT +#include "../lwlib/lwlib.h" #endif +#include "cm.h" +#include "menu.h" + /* The name of the default console device. */ #ifdef WINDOWSNT #define DEV_TTY "CONOUT$" @@ -71,7 +71,6 @@ static void tty_turn_off_highlight (struct tty_display_info *); static void tty_show_cursor (struct tty_display_info *); static void tty_hide_cursor (struct tty_display_info *); static void tty_background_highlight (struct tty_display_info *tty); -static struct terminal *get_tty_terminal (Lisp_Object, bool); static void clear_tty_hooks (struct terminal *terminal); static void set_tty_hooks (struct terminal *terminal); static void dissociate_if_controlling_tty (int fd); @@ -85,7 +84,7 @@ static _Noreturn void vfatal (const char *str, va_list ap) #define OUTPUT(tty, a) \ emacs_tputs ((tty), a, \ - FRAME_LINES (XFRAME (selected_frame)) - curY (tty), \ + FRAME_TOTAL_LINES (XFRAME (selected_frame)) - curY (tty), \ cmputc) #define OUTPUT1(tty, a) emacs_tputs ((tty), a, 1, cmputc) @@ -99,9 +98,9 @@ static _Noreturn void vfatal (const char *str, va_list ap) #define OUTPUT1_IF(tty, a) do { if (a) emacs_tputs ((tty), a, 1, cmputc); } while (0) -/* Display space properties */ +/* Display space properties. */ -/* Chain of all tty device parameters. */ +/* Chain of all tty device parameters. */ struct tty_display_info *tty_list; /* Meaning of bits in no_color_video. Each bit set means that the @@ -156,6 +155,28 @@ tty_ring_bell (struct frame *f) /* Set up termcap modes for Emacs. */ static void +tty_send_additional_strings (struct terminal *terminal, Lisp_Object sym) +{ + Lisp_Object lisp_terminal; + Lisp_Object extra_codes; + struct tty_display_info *tty = terminal->display_info.tty; + + XSETTERMINAL (lisp_terminal, terminal); + for (extra_codes = Fterminal_parameter (lisp_terminal, sym); + CONSP (extra_codes); + extra_codes = XCDR (extra_codes)) + { + Lisp_Object string = XCAR (extra_codes); + if (STRINGP (string)) + { + fwrite (SDATA (string), 1, SBYTES (string), tty->output); + if (tty->termscript) + fwrite (SDATA (string), 1, SBYTES (string), tty->termscript); + } + } +} + +static void tty_set_terminal_modes (struct terminal *terminal) { struct tty_display_info *tty = terminal->display_info.tty; @@ -170,13 +191,14 @@ tty_set_terminal_modes (struct terminal *terminal) off the screen, so it won't be overwritten and lost. */ int i; current_tty = tty; - for (i = 0; i < FRAME_LINES (XFRAME (selected_frame)); i++) + for (i = 0; i < FRAME_TOTAL_LINES (XFRAME (selected_frame)); i++) cmputc ('\n'); } OUTPUT_IF (tty, visible_cursor ? tty->TS_cursor_visible : tty->TS_cursor_normal); OUTPUT_IF (tty, tty->TS_keypad_mode); losecursor (tty); + tty_send_additional_strings (terminal, Qtty_mode_set_strings); fflush (tty->output); } } @@ -190,6 +212,7 @@ tty_reset_terminal_modes (struct terminal *terminal) if (tty->output) { + tty_send_additional_strings (terminal, Qtty_mode_reset_strings); tty_turn_off_highlight (tty); tty_turn_off_insert (tty); OUTPUT_IF (tty, tty->TS_end_keypad_mode); @@ -214,6 +237,7 @@ tty_update_end (struct frame *f) tty_show_cursor (tty); tty_turn_off_insert (tty); tty_background_highlight (tty); + fflush (tty->output); } /* The implementation of set_terminal_window for termcap frames. */ @@ -223,7 +247,7 @@ tty_set_terminal_window (struct frame *f, int size) { struct tty_display_info *tty = FRAME_TTY (f); - tty->specified_window = size ? size : FRAME_LINES (f); + tty->specified_window = size ? size : FRAME_TOTAL_LINES (f); if (FRAME_SCROLL_REGION_OK (f)) tty_set_scroll_region (f, 0, tty->specified_window); } @@ -238,9 +262,9 @@ tty_set_scroll_region (struct frame *f, int start, int stop) buf = tparam (tty->TS_set_scroll_region, 0, 0, start, stop - 1, 0, 0); else if (tty->TS_set_scroll_region_1) buf = tparam (tty->TS_set_scroll_region_1, 0, 0, - FRAME_LINES (f), start, - FRAME_LINES (f) - stop, - FRAME_LINES (f)); + FRAME_TOTAL_LINES (f), start, + FRAME_TOTAL_LINES (f) - stop, + FRAME_TOTAL_LINES (f)); else buf = tparam (tty->TS_set_window, 0, 0, start, 0, stop, FRAME_COLS (f)); @@ -302,7 +326,11 @@ tty_hide_cursor (struct tty_display_info *tty) if (tty->cursor_hidden == 0) { tty->cursor_hidden = 1; +#ifdef WINDOWSNT + w32con_hide_cursor (); +#else OUTPUT_IF (tty, tty->TS_cursor_invisible); +#endif } } @@ -315,9 +343,13 @@ tty_show_cursor (struct tty_display_info *tty) if (tty->cursor_hidden) { tty->cursor_hidden = 0; +#ifdef WINDOWSNT + w32con_show_cursor (); +#else OUTPUT_IF (tty, tty->TS_cursor_normal); if (visible_cursor) OUTPUT_IF (tty, tty->TS_cursor_visible); +#endif } } @@ -404,7 +436,7 @@ tty_clear_to_end (struct frame *f) } else { - for (i = curY (tty); i < FRAME_LINES (f); i++) + for (i = curY (tty); i < FRAME_TOTAL_LINES (f); i++) { cursor_to (f, i, 0); clear_end_of_line (f, FRAME_COLS (f)); @@ -486,7 +518,8 @@ static ptrdiff_t encode_terminal_dst_size; sequence, and return a pointer to that byte sequence. */ unsigned char * -encode_terminal_code (struct glyph *src, int src_len, struct coding_system *coding) +encode_terminal_code (struct glyph *src, int src_len, + struct coding_system *coding) { struct glyph *src_end = src + src_len; unsigned char *buf; @@ -705,7 +738,7 @@ tty_write_glyphs (struct frame *f, struct glyph *string, int len) since that would scroll the whole frame on some terminals. */ if (AutoWrap (tty) - && curY (tty) + 1 == FRAME_LINES (f) + && curY (tty) + 1 == FRAME_TOTAL_LINES (f) && (curX (tty) + len) == FRAME_COLS (f)) len --; if (len <= 0) @@ -777,7 +810,7 @@ tty_write_glyphs_with_face (register struct frame *f, register struct glyph *str since that would scroll the whole frame on some terminals. */ if (AutoWrap (tty) - && curY (tty) + 1 == FRAME_LINES (f) + && curY (tty) + 1 == FRAME_TOTAL_LINES (f) && (curX (tty) + len) == FRAME_COLS (f)) len --; if (len <= 0) @@ -966,7 +999,7 @@ tty_ins_del_lines (struct frame *f, int vpos, int n) && vpos + i >= tty->specified_window) return; if (!FRAME_MEMORY_BELOW_FRAME (f) - && vpos + i >= FRAME_LINES (f)) + && vpos + i >= FRAME_TOTAL_LINES (f)) return; if (multi) @@ -1003,7 +1036,7 @@ tty_ins_del_lines (struct frame *f, int vpos, int n) && FRAME_MEMORY_BELOW_FRAME (f) && n < 0) { - cursor_to (f, FRAME_LINES (f) + n, 0); + cursor_to (f, FRAME_TOTAL_LINES (f) + n, 0); clear_to_end (f); } } @@ -1320,7 +1353,7 @@ term_get_fkeys_1 (void) if (!KEYMAPP (KVAR (kboard, Vinput_decode_map))) kset_input_decode_map (kboard, Fmake_sparse_keymap (Qnil)); - for (i = 0; i < (sizeof (keys) / sizeof (keys[0])); i++) + for (i = 0; i < ARRAYELTS (keys); i++) { char *sequence = tgetstr (keys[i].cap, address); if (sequence) @@ -1403,18 +1436,6 @@ term_get_fkeys_1 (void) CONDITIONAL_REASSIGN ("kD", "kI", "insert"); /* if there's no key_end keycap, map key_ll to 'end' keysym */ CONDITIONAL_REASSIGN ("@7", "kH", "end"); - - /* IBM has their own non-standard dialect of terminfo. - If the standard name isn't found, try the IBM name. */ - CONDITIONAL_REASSIGN ("kB", "KO", "backtab"); - CONDITIONAL_REASSIGN ("@4", "kJ", "execute"); /* actually "action" */ - CONDITIONAL_REASSIGN ("@4", "kc", "execute"); /* actually "command" */ - CONDITIONAL_REASSIGN ("%7", "ki", "menu"); - CONDITIONAL_REASSIGN ("@7", "kw", "end"); - CONDITIONAL_REASSIGN ("F1", "k<", "f11"); - CONDITIONAL_REASSIGN ("F2", "k>", "f12"); - CONDITIONAL_REASSIGN ("%1", "kq", "help"); - CONDITIONAL_REASSIGN ("*6", "kU", "select"); #undef CONDITIONAL_REASSIGN } @@ -1483,8 +1504,7 @@ append_glyph (struct it *it) if (it->bidi_p) { glyph->resolved_level = it->bidi_it.resolved_level; - if ((it->bidi_it.type & 7) != it->bidi_it.type) - emacs_abort (); + eassert ((it->bidi_it.type & 7) == it->bidi_it.type); glyph->bidi_type = it->bidi_it.type; } else @@ -1680,8 +1700,7 @@ append_composite_glyph (struct it *it) if (it->bidi_p) { glyph->resolved_level = it->bidi_it.resolved_level; - if ((it->bidi_it.type & 7) != it->bidi_it.type) - emacs_abort (); + eassert ((it->bidi_it.type & 7) == it->bidi_it.type); glyph->bidi_type = it->bidi_it.type; } else @@ -1765,8 +1784,7 @@ append_glyphless_glyph (struct it *it, int face_id, const char *str) if (it->bidi_p) { glyph->resolved_level = it->bidi_it.resolved_level; - if ((it->bidi_it.type & 7) != it->bidi_it.type) - emacs_abort (); + eassert ((it->bidi_it.type & 7) == it->bidi_it.type); glyph->bidi_type = it->bidi_it.type; } else @@ -1800,27 +1818,10 @@ append_glyphless_glyph (struct it *it, int face_id, const char *str) static void produce_glyphless_glyph (struct it *it, Lisp_Object acronym) { - int face_id; - int len; + int len, face_id = merge_glyphless_glyph_face (it); char buf[sizeof "\\x" + max (6, (sizeof it->c * CHAR_BIT + 3) / 4)]; char const *str = " "; - /* Get a face ID for the glyph by utilizing a cache (the same way as - done for `escape-glyph' in get_next_display_element). */ - if (it->f == last_glyphless_glyph_frame - && it->face_id == last_glyphless_glyph_face_id) - { - face_id = last_glyphless_glyph_merged_face_id; - } - else - { - /* Merge the `glyphless-char' face into the current face. */ - face_id = merge_faces (it->f, Qglyphless_char, 0, it->face_id); - last_glyphless_glyph_frame = it->f; - last_glyphless_glyph_face_id = it->face_id; - last_glyphless_glyph_merged_face_id = face_id; - } - if (it->glyphless_method == GLYPHLESS_DISPLAY_THIN_SPACE) { /* As there's no way to produce a thin space, we produce a space @@ -1847,7 +1848,7 @@ produce_glyphless_glyph (struct it *it, Lisp_Object acronym) acronym = XCDR (acronym); buf[0] = '['; str = STRINGP (acronym) ? SSDATA (acronym) : ""; - for (len = 0; len < 6 && str[len] && ASCII_BYTE_P (str[len]); len++) + for (len = 0; len < 6 && str[len] && ASCII_CHAR_P (str[len]); len++) buf[1 + len] = str[len]; buf[1 + len] = ']'; len += 2; @@ -1855,9 +1856,11 @@ produce_glyphless_glyph (struct it *it, Lisp_Object acronym) else { eassert (it->glyphless_method == GLYPHLESS_DISPLAY_HEX_CODE); - len = (it->c < 0x10000 ? sprintf (buf, "\\u%04X", it->c) - : it->c <= MAX_UNICODE_CHAR ? sprintf (buf, "\\U%06X", it->c) - : sprintf (buf, "\\x%06X", it->c)); + len = sprintf (buf, + (it->c < 0x10000 ? "\\u%04X" + : it->c <= MAX_UNICODE_CHAR ? "\\U%06X" + : "\\x%06X"), + it->c + 0u); } str = buf; } @@ -1890,55 +1893,18 @@ static void turn_on_face (struct frame *f, int face_id) { struct face *face = FACE_FROM_ID (f, face_id); - long fg = face->foreground; - long bg = face->background; + unsigned long fg = face->foreground; + unsigned long bg = face->background; struct tty_display_info *tty = FRAME_TTY (f); - /* Do this first because TS_end_standout_mode may be the same + /* Use reverse video if the face specifies that. + Do this first because TS_end_standout_mode may be the same as TS_exit_attribute_mode, which turns all appearances off. */ - if (MAY_USE_WITH_COLORS_P (tty, NC_REVERSE)) - { - if (tty->TN_max_colors > 0) - { - if (fg >= 0 && bg >= 0) - { - /* If the terminal supports colors, we can set them - below without using reverse video. The face's fg - and bg colors are set as they should appear on - the screen, i.e. they take the inverse-video'ness - of the face already into account. */ - } - else if (inverse_video) - { - if (fg == FACE_TTY_DEFAULT_FG_COLOR - || bg == FACE_TTY_DEFAULT_BG_COLOR) - tty_toggle_highlight (tty); - } - else - { - if (fg == FACE_TTY_DEFAULT_BG_COLOR - || bg == FACE_TTY_DEFAULT_FG_COLOR) - tty_toggle_highlight (tty); - } - } - else - { - /* If we can't display colors, use reverse video - if the face specifies that. */ - if (inverse_video) - { - if (fg == FACE_TTY_DEFAULT_FG_COLOR - || bg == FACE_TTY_DEFAULT_BG_COLOR) - tty_toggle_highlight (tty); - } - else - { - if (fg == FACE_TTY_DEFAULT_BG_COLOR - || bg == FACE_TTY_DEFAULT_FG_COLOR) - tty_toggle_highlight (tty); - } - } - } + if (MAY_USE_WITH_COLORS_P (tty, NC_REVERSE) + && (inverse_video + ? fg == FACE_TTY_DEFAULT_FG_COLOR || bg == FACE_TTY_DEFAULT_BG_COLOR + : fg == FACE_TTY_DEFAULT_BG_COLOR || bg == FACE_TTY_DEFAULT_FG_COLOR)) + tty_toggle_highlight (tty); if (face->tty_bold_p && MAY_USE_WITH_COLORS_P (tty, NC_BOLD)) OUTPUT1_IF (tty, tty->TS_enter_bold_mode); @@ -1963,7 +1929,7 @@ turn_on_face (struct frame *f, int face_id) char *p; ts = tty->standout_mode ? tty->TS_set_background : tty->TS_set_foreground; - if (fg >= 0 && ts) + if (face_tty_specified_color (fg) && ts) { p = tparam (ts, NULL, 0, fg, 0, 0, 0); OUTPUT (tty, p); @@ -1971,7 +1937,7 @@ turn_on_face (struct frame *f, int face_id) } ts = tty->standout_mode ? tty->TS_set_foreground : tty->TS_set_background; - if (bg >= 0 && ts) + if (face_tty_specified_color (bg) && ts) { p = tparam (ts, NULL, 0, bg, 0, 0, 0); OUTPUT (tty, p); @@ -2025,12 +1991,10 @@ turn_off_face (struct frame *f, int face_id) /* Return true if the terminal on frame F supports all of the - capabilities in CAPS simultaneously, with foreground and background - colors FG and BG. */ + capabilities in CAPS simultaneously. */ bool -tty_capable_p (struct tty_display_info *tty, unsigned int caps, - unsigned long fg, unsigned long bg) +tty_capable_p (struct tty_display_info *tty, unsigned int caps) { #define TTY_CAPABLE_P_TRY(tty, cap, TS, NC_bit) \ if ((caps & (cap)) && (!(TS) || !MAY_USE_WITH_COLORS_P(tty, NC_bit))) \ @@ -2057,11 +2021,9 @@ selected frame's terminal). This function always returns nil if TERMINAL does not refer to a text terminal. */) (Lisp_Object terminal) { - struct terminal *t = get_tty_terminal (terminal, 0); - if (!t) - return Qnil; - else - return t->display_info.tty->TN_max_colors > 0 ? Qt : Qnil; + struct terminal *t = decode_tty_terminal (terminal); + + return (t && t->display_info.tty->TN_max_colors > 0) ? Qt : Qnil; } /* Return the number of supported colors. */ @@ -2074,11 +2036,9 @@ selected frame's terminal). This function always returns 0 if TERMINAL does not refer to a text terminal. */) (Lisp_Object terminal) { - struct terminal *t = get_tty_terminal (terminal, 0); - if (!t) - return make_number (0); - else - return make_number (t->display_info.tty->TN_max_colors); + struct terminal *t = decode_tty_terminal (terminal); + + return make_number (t ? t->display_info.tty->TN_max_colors : 0); } #ifndef DOS_NT @@ -2101,17 +2061,9 @@ tty_default_color_capabilities (struct tty_display_info *tty, bool save) if (save) { - xfree (default_orig_pair); - default_orig_pair = tty->TS_orig_pair ? xstrdup (tty->TS_orig_pair) : NULL; - - xfree (default_set_foreground); - default_set_foreground = tty->TS_set_foreground ? xstrdup (tty->TS_set_foreground) - : NULL; - - xfree (default_set_background); - default_set_background = tty->TS_set_background ? xstrdup (tty->TS_set_background) - : NULL; - + dupstring (&default_orig_pair, tty->TS_orig_pair); + dupstring (&default_set_foreground, tty->TS_set_foreground); + dupstring (&default_set_background, tty->TS_set_background); default_max_colors = tty->TN_max_colors; default_max_pairs = tty->TN_max_pairs; default_no_color_video = tty->TN_no_color_video; @@ -2201,52 +2153,6 @@ set_tty_color_mode (struct tty_display_info *tty, struct frame *f) #endif /* !DOS_NT */ - - -/* Return the tty display object specified by TERMINAL. */ - -static struct terminal * -get_tty_terminal (Lisp_Object terminal, bool throw) -{ - struct terminal *t = get_terminal (terminal, throw); - - if (t && t->type != output_termcap && t->type != output_msdos_raw) - { - if (throw) - error ("Device %d is not a termcap terminal device", t->id); - else - return NULL; - } - - return t; -} - -/* Return an active termcap device that uses the tty device with the - given name. - - This function ignores suspended devices. - - Returns NULL if the named terminal device is not opened. */ - -struct terminal * -get_named_tty (const char *name) -{ - struct terminal *t; - - eassert (name); - - for (t = terminal_list; t; t = t->next_terminal) - { - if ((t->type == output_termcap || t->type == output_msdos_raw) - && !strcmp (t->display_info.tty->name, name) - && TERMINAL_ACTIVE_P (t)) - return t; - } - - return 0; -} - - DEFUN ("tty-type", Ftty_type, Stty_type, 0, 1, 0, doc: /* Return the type of the tty device that TERMINAL uses. Returns nil if TERMINAL is not on a tty device. @@ -2255,15 +2161,10 @@ TERMINAL can be a terminal object, a frame, or nil (meaning the selected frame's terminal). */) (Lisp_Object terminal) { - struct terminal *t = get_terminal (terminal, 1); - - if (t->type != output_termcap && t->type != output_msdos_raw) - return Qnil; + struct terminal *t = decode_tty_terminal (terminal); - if (t->display_info.tty->type) - return build_string (t->display_info.tty->type); - else - return Qnil; + return (t && t->display_info.tty->type + ? build_string (t->display_info.tty->type) : Qnil); } DEFUN ("controlling-tty-p", Fcontrolling_tty_p, Scontrolling_tty_p, 0, 1, 0, @@ -2274,13 +2175,9 @@ selected frame's terminal). This function always returns nil if TERMINAL is not on a tty device. */) (Lisp_Object terminal) { - struct terminal *t = get_terminal (terminal, 1); + struct terminal *t = decode_tty_terminal (terminal); - if ((t->type != output_termcap && t->type != output_msdos_raw) - || strcmp (t->display_info.tty->name, DEV_TTY) != 0) - return Qnil; - else - return Qt; + return (t && !strcmp (t->display_info.tty->name, DEV_TTY) ? Qt : Qnil); } DEFUN ("tty-no-underline", Ftty_no_underline, Stty_no_underline, 0, 1, 0, @@ -2294,7 +2191,7 @@ selected frame's terminal). This function always returns nil if TERMINAL does not refer to a text terminal. */) (Lisp_Object terminal) { - struct terminal *t = get_terminal (terminal, 1); + struct terminal *t = decode_live_terminal (terminal); if (t->type == output_termcap) t->display_info.tty->TS_enter_underline_mode = 0; @@ -2309,7 +2206,7 @@ does not refer to a text terminal. Otherwise, it returns the top-most frame on the text terminal. */) (Lisp_Object terminal) { - struct terminal *t = get_terminal (terminal, 1); + struct terminal *t = decode_live_terminal (terminal); if (t->type == output_termcap) return t->display_info.tty->top_frame; @@ -2339,11 +2236,11 @@ suspended. A suspended tty may be resumed by calling `resume-tty' on it. */) (Lisp_Object tty) { - struct terminal *t = get_tty_terminal (tty, 1); + struct terminal *t = decode_tty_terminal (tty); FILE *f; if (!t) - error ("Unknown tty device"); + error ("Attempt to suspend a non-text terminal device"); f = t->display_info.tty->input; @@ -2352,10 +2249,9 @@ A suspended tty may be resumed by calling `resume-tty' on it. */) /* First run `suspend-tty-functions' and then clean up the tty state because `suspend-tty-functions' might need to change the tty state. */ - Lisp_Object args[2]; - args[0] = intern ("suspend-tty-functions"); - XSETTERMINAL (args[1], t); - Frun_hook_with_args (2, args); + Lisp_Object term; + XSETTERMINAL (term, t); + CALLN (Frun_hook_with_args, intern ("suspend-tty-functions"), term); reset_sys_modes (t->display_info.tty); delete_keyboard_wait_descriptor (fileno (f)); @@ -2399,15 +2295,15 @@ TTY may be a terminal object, a frame, or nil (meaning the selected frame's terminal). */) (Lisp_Object tty) { - struct terminal *t = get_tty_terminal (tty, 1); + struct terminal *t = decode_tty_terminal (tty); int fd; if (!t) - error ("Unknown tty device"); + error ("Attempt to resume a non-text terminal device"); if (!t->display_info.tty->input) { - if (get_named_tty (t->display_info.tty->name)) + if (get_named_terminal (t->display_info.tty->name)) error ("Cannot resume display while another display is active on the same device"); #ifdef MSDOS @@ -2438,26 +2334,24 @@ frame's terminal). */) struct frame *f = XFRAME (t->display_info.tty->top_frame); int width, height; int old_height = FRAME_COLS (f); - int old_width = FRAME_LINES (f); + int old_width = FRAME_TOTAL_LINES (f); /* Check if terminal/window size has changed while the frame was suspended. */ get_tty_size (fileno (t->display_info.tty->input), &width, &height); if (width != old_width || height != old_height) - change_frame_size (f, height, width, 0, 0, 0); + change_frame_size (f, width, height - FRAME_MENU_BAR_LINES (f), + 0, 0, 0, 0); SET_FRAME_VISIBLE (XFRAME (t->display_info.tty->top_frame), 1); } set_tty_hooks (t); init_sys_modes (t->display_info.tty); - { - /* Run `resume-tty-functions'. */ - Lisp_Object args[2]; - args[0] = intern ("resume-tty-functions"); - XSETTERMINAL (args[1], t); - Frun_hook_with_args (2, args); - } + /* Run `resume-tty-functions'. */ + Lisp_Object term; + XSETTERMINAL (term, t); + CALLN (Frun_hook_with_args, intern ("resume-tty-functions"), term); } set_tty_hooks (t); @@ -2572,7 +2466,7 @@ term_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window, (*fp)->mouse_moved = 0; *bar_window = Qnil; - *part = 0; + *part = scroll_bar_above_handle; XSETINT (*x, last_mouse_x); XSETINT (*y, last_mouse_y); @@ -2772,13 +2666,1112 @@ DEFUN ("gpm-mouse-stop", Fgpm_mouse_stop, Sgpm_mouse_stop, #endif /* HAVE_GPM */ +/*********************************************************************** + Menus + ***********************************************************************/ + +#if !defined (MSDOS) + +/* TTY menu implementation and main ideas are borrowed from msdos.c. + + However, unlike on MSDOS, where the menu text is drawn directly to + the display video memory, on a TTY we use display_string (see + display_tty_menu_item in xdisp.c) to put the glyphs produced from + the menu items into the frame's 'desired_matrix' glyph matrix, and + then call update_frame_with_menu to deliver the results to the + glass. The previous contents of the screen, in the form of the + current_matrix, is stashed away, and used to restore screen + contents when the menu selection changes or when the final + selection is made and the menu should be popped down. + + The idea of this implementation was suggested by Gerd Moellmann. */ + +#define TTYM_FAILURE -1 +#define TTYM_SUCCESS 1 +#define TTYM_NO_SELECT 2 +#define TTYM_IA_SELECT 3 +#define TTYM_NEXT 4 +#define TTYM_PREV 5 + +/* These hold text of the current and the previous menu help messages. */ +static const char *menu_help_message, *prev_menu_help_message; +/* Pane number and item number of the menu item which generated the + last menu help message. */ +static int menu_help_paneno, menu_help_itemno; + +typedef struct tty_menu_struct +{ + int count; + char **text; + struct tty_menu_struct **submenu; + int *panenumber; /* Also used as enabled flag. */ + ptrdiff_t allocated; + int panecount; + int width; + const char **help_text; +} tty_menu; + +/* Create a brand new menu structure. */ + +static tty_menu * +tty_menu_create (void) +{ + return xzalloc (sizeof *tty_menu_create ()); +} + +/* Allocate some (more) memory for MENU ensuring that there is room for one + more item. */ + +static void +tty_menu_make_room (tty_menu *menu) +{ + if (menu->allocated == menu->count) + { + ptrdiff_t allocated = menu->allocated; + menu->text = xpalloc (menu->text, &allocated, 1, -1, sizeof *menu->text); + menu->text = xrealloc (menu->text, allocated * sizeof *menu->text); + menu->submenu = xrealloc (menu->submenu, + allocated * sizeof *menu->submenu); + menu->panenumber = xrealloc (menu->panenumber, + allocated * sizeof *menu->panenumber); + menu->help_text = xrealloc (menu->help_text, + allocated * sizeof *menu->help_text); + menu->allocated = allocated; + } +} + +/* Search the given menu structure for a given pane number. */ + +static tty_menu * +tty_menu_search_pane (tty_menu *menu, int pane) +{ + int i; + tty_menu *try; + + for (i = 0; i < menu->count; i++) + if (menu->submenu[i]) + { + if (pane == menu->panenumber[i]) + return menu->submenu[i]; + try = tty_menu_search_pane (menu->submenu[i], pane); + if (try) + return try; + } + return (tty_menu *) 0; +} + +/* Determine how much screen space a given menu needs. */ + +static void +tty_menu_calc_size (tty_menu *menu, int *width, int *height) +{ + int i, h2, w2, maxsubwidth, maxheight; + + maxsubwidth = menu->width; + maxheight = menu->count; + for (i = 0; i < menu->count; i++) + { + if (menu->submenu[i]) + { + tty_menu_calc_size (menu->submenu[i], &w2, &h2); + if (w2 > maxsubwidth) maxsubwidth = w2; + if (i + h2 > maxheight) maxheight = i + h2; + } + } + *width = maxsubwidth; + *height = maxheight; +} + +static void +mouse_get_xy (int *x, int *y) +{ + struct frame *sf = SELECTED_FRAME (); + Lisp_Object lmx = Qnil, lmy = Qnil, lisp_dummy; + enum scroll_bar_part part_dummy; + Time time_dummy; + + if (FRAME_TERMINAL (sf)->mouse_position_hook) + (*FRAME_TERMINAL (sf)->mouse_position_hook) (&sf, -1, + &lisp_dummy, &part_dummy, + &lmx, &lmy, + &time_dummy); + if (!NILP (lmx)) + { + *x = XINT (lmx); + *y = XINT (lmy); + } +} + +/* Display MENU at (X,Y) using FACES, starting with FIRST_ITEM + (zero-based). */ + +static void +tty_menu_display (tty_menu *menu, int x, int y, int pn, int *faces, + int mx, int my, int first_item, bool disp_help) +{ + int i, face, width, enabled, mousehere, row, col; + struct frame *sf = SELECTED_FRAME (); + struct tty_display_info *tty = FRAME_TTY (sf); + /* Don't try to display more menu items than the console can display + using the available screen lines. Exclude the echo area line, as + it will be overwritten by the help-echo anyway. */ + int max_items = min (menu->count - first_item, FRAME_TOTAL_LINES (sf) - 1 - y); + + menu_help_message = NULL; + + width = menu->width; + col = cursorX (tty); + row = cursorY (tty); + for (i = 0; i < max_items; i++) + { + int max_width = width + 2; /* +2 for padding blanks on each side */ + int j = i + first_item; + + if (menu->submenu[j]) + max_width += 2; /* for displaying " >" after the item */ + enabled + = (!menu->submenu[j] && menu->panenumber[j]) || (menu->submenu[j]); + mousehere = (y + i == my && x <= mx && mx < x + max_width); + face = faces[enabled + mousehere * 2]; + /* Display the menu help string for the i-th menu item even if + the menu item is currently disabled. That's what the GUI + code does. */ + if (disp_help && enabled + mousehere * 2 >= 2) + { + menu_help_message = menu->help_text[j]; + menu_help_paneno = pn - 1; + menu_help_itemno = j; + } + /* Take note of the coordinates of the active menu item, to + display the cursor there. */ + if (mousehere) + { + row = y + i; + col = x; + } + display_tty_menu_item (menu->text[j], max_width, face, x, y + i, + menu->submenu[j] != NULL); + } + update_frame_with_menu (sf, row, col); +} + +/* --------------------------- X Menu emulation ---------------------- */ + +/* Create a new pane and place it on the outer-most level. */ + +static int +tty_menu_add_pane (tty_menu *menu, const char *txt) +{ + int len; + + tty_menu_make_room (menu); + menu->submenu[menu->count] = tty_menu_create (); + menu->text[menu->count] = (char *)txt; + menu->panenumber[menu->count] = ++menu->panecount; + menu->help_text[menu->count] = NULL; + menu->count++; + + /* Update the menu width, if necessary. */ + len = menu_item_width ((const unsigned char *) txt); + if (len > menu->width) + menu->width = len; + + return menu->panecount; +} + +/* Create a new item in a menu pane. */ + +static bool +tty_menu_add_selection (tty_menu *menu, int pane, + char *txt, bool enable, char const *help_text) +{ + int len; + + if (pane) + { + menu = tty_menu_search_pane (menu, pane); + if (! menu) + return 0; + } + tty_menu_make_room (menu); + menu->submenu[menu->count] = (tty_menu *) 0; + menu->text[menu->count] = txt; + menu->panenumber[menu->count] = enable; + menu->help_text[menu->count] = help_text; + menu->count++; + + /* Update the menu width, if necessary. */ + len = menu_item_width ((const unsigned char *) txt); + if (len > menu->width) + menu->width = len; + + return 1; +} + +/* Decide where the menu would be placed if requested at (X,Y). */ + +static void +tty_menu_locate (tty_menu *menu, int x, int y, + int *ulx, int *uly, int *width, int *height) +{ + tty_menu_calc_size (menu, width, height); + *ulx = x + 1; + *uly = y; + *width += 2; +} + +struct tty_menu_state +{ + struct glyph_matrix *screen_behind; + tty_menu *menu; + int pane; + int x, y; +}; + +/* Save away the contents of frame F's current frame matrix, and + enable all its rows. Value is a glyph matrix holding the contents + of F's current frame matrix with all its glyph rows enabled. */ + +static struct glyph_matrix * +save_and_enable_current_matrix (struct frame *f) +{ + int i; + struct glyph_matrix *saved = xzalloc (sizeof *saved); + saved->nrows = f->current_matrix->nrows; + saved->rows = xzalloc (saved->nrows * sizeof *saved->rows); + + for (i = 0; i < saved->nrows; ++i) + { + struct glyph_row *from = f->current_matrix->rows + i; + struct glyph_row *to = saved->rows + i; + ptrdiff_t nbytes = from->used[TEXT_AREA] * sizeof (struct glyph); + + to->glyphs[TEXT_AREA] = xmalloc (nbytes); + memcpy (to->glyphs[TEXT_AREA], from->glyphs[TEXT_AREA], nbytes); + to->used[TEXT_AREA] = from->used[TEXT_AREA]; + /* Make sure every row is enabled, or else update_frame will not + redraw them. (Rows that are identical to what is already on + screen will not be redrawn anyway.) */ + to->enabled_p = true; + to->hash = from->hash; + } + + return saved; +} + +/* Restore the contents of frame F's desired frame matrix from SAVED, + and free memory associated with SAVED. */ + +static void +restore_desired_matrix (struct frame *f, struct glyph_matrix *saved) +{ + int i; + + for (i = 0; i < saved->nrows; ++i) + { + struct glyph_row *from = saved->rows + i; + struct glyph_row *to = f->desired_matrix->rows + i; + ptrdiff_t nbytes = from->used[TEXT_AREA] * sizeof (struct glyph); + + eassert (to->glyphs[TEXT_AREA] != from->glyphs[TEXT_AREA]); + memcpy (to->glyphs[TEXT_AREA], from->glyphs[TEXT_AREA], nbytes); + to->used[TEXT_AREA] = from->used[TEXT_AREA]; + to->enabled_p = from->enabled_p; + to->hash = from->hash; + } +} + +static void +free_saved_screen (struct glyph_matrix *saved) +{ + int i; + + if (!saved) + return; /* Already freed! */ + + for (i = 0; i < saved->nrows; ++i) + { + struct glyph_row *from = saved->rows + i; + + xfree (from->glyphs[TEXT_AREA]); + } + + xfree (saved->rows); + xfree (saved); +} + +/* Update the display of frame F from its saved contents. */ +static void +screen_update (struct frame *f, struct glyph_matrix *mtx) +{ + restore_desired_matrix (f, mtx); + update_frame_with_menu (f, -1, -1); +} + +typedef enum { + MI_QUIT_MENU = -1, + MI_CONTINUE = 0, + MI_ITEM_SELECTED = 1, + MI_NEXT_ITEM = 2, + MI_PREV_ITEM = 3, + MI_SCROLL_FORWARD = 4, + MI_SCROLL_BACK = 5 +} mi_result; + +/* Read user input and return X and Y coordinates where that input + puts us. We only consider mouse movement and click events, and + keyboard movement commands; the rest are ignored. */ +static mi_result +read_menu_input (struct frame *sf, int *x, int *y, int min_y, int max_y, + bool *first_time) +{ + if (*first_time) + { + *first_time = false; + sf->mouse_moved = 1; + } + else + { + Lisp_Object cmd; + bool usable_input = 1; + mi_result st = MI_CONTINUE; + struct tty_display_info *tty = FRAME_TTY (sf); + Lisp_Object saved_mouse_tracking = do_mouse_tracking; + + /* Signal the keyboard reading routines we are displaying a menu + on this terminal. */ + tty->showing_menu = 1; + /* We want mouse movements be reported by read_menu_command. */ + do_mouse_tracking = Qt; + do { + cmd = read_menu_command (); + } while (NILP (cmd)); + tty->showing_menu = 0; + do_mouse_tracking = saved_mouse_tracking; + + if (EQ (cmd, Qt) || EQ (cmd, Qtty_menu_exit) + /* If some input switched frames under our feet, exit the + menu, since the menu faces are no longer valid, and the + menu is no longer relevant anyway. */ + || sf != SELECTED_FRAME ()) + return MI_QUIT_MENU; + if (EQ (cmd, Qtty_menu_mouse_movement)) + mouse_get_xy (x, y); + else if (EQ (cmd, Qtty_menu_next_menu)) + { + usable_input = 0; + st = MI_NEXT_ITEM; + } + else if (EQ (cmd, Qtty_menu_prev_menu)) + { + usable_input = 0; + st = MI_PREV_ITEM; + } + else if (EQ (cmd, Qtty_menu_next_item)) + { + if (*y < max_y) + *y += 1; + else + st = MI_SCROLL_FORWARD; + } + else if (EQ (cmd, Qtty_menu_prev_item)) + { + if (*y > min_y) + *y -= 1; + else + st = MI_SCROLL_BACK; + } + else if (EQ (cmd, Qtty_menu_select)) + st = MI_ITEM_SELECTED; + else if (!EQ (cmd, Qtty_menu_ignore)) + usable_input = 0; + if (usable_input) + sf->mouse_moved = 1; + return st; + } + return MI_CONTINUE; +} + +/* Display menu, wait for user's response, and return that response. */ +static int +tty_menu_activate (tty_menu *menu, int *pane, int *selidx, + int x0, int y0, char **txt, + void (*help_callback)(char const *, int, int), + bool kbd_navigation) +{ + struct tty_menu_state *state; + int statecount, x, y, i; + bool leave, onepane; + int result IF_LINT (= 0); + int title_faces[4]; /* Face to display the menu title. */ + int faces[4], buffers_num_deleted = 0; + struct frame *sf = SELECTED_FRAME (); + struct tty_display_info *tty = FRAME_TTY (sf); + bool first_time; + Lisp_Object selectface; + int first_item = 0; + int col, row; + Lisp_Object prev_inhibit_redisplay = Vinhibit_redisplay; + USE_SAFE_ALLOCA; + + /* Don't allow non-positive x0 and y0, lest the menu will wrap + around the display. */ + if (x0 <= 0) + x0 = 1; + if (y0 <= 0) + y0 = 1; + + SAFE_NALLOCA (state, 1, menu->panecount); + memset (state, 0, sizeof (*state)); + faces[0] + = lookup_derived_face (sf, intern ("tty-menu-disabled-face"), + DEFAULT_FACE_ID, 1); + faces[1] + = lookup_derived_face (sf, intern ("tty-menu-enabled-face"), + DEFAULT_FACE_ID, 1); + selectface = intern ("tty-menu-selected-face"); + faces[2] = lookup_derived_face (sf, selectface, + faces[0], 1); + faces[3] = lookup_derived_face (sf, selectface, + faces[1], 1); + + /* Make sure the menu title is always displayed with + `tty-menu-selected-face', no matter where the mouse pointer is. */ + for (i = 0; i < 4; i++) + title_faces[i] = faces[3]; + + statecount = 1; + + /* Don't let the title for the "Buffers" popup menu include a + digit (which is ugly). + + This is a terrible kludge, but I think the "Buffers" case is + the only one where the title includes a number, so it doesn't + seem to be necessary to make this more general. */ + if (strncmp (menu->text[0], "Buffers 1", 9) == 0) + { + menu->text[0][7] = '\0'; + buffers_num_deleted = 1; + } + + /* Inhibit redisplay for as long as the menu is active, to avoid + messing the screen if some timer calls sit-for or a similar + function. */ + Vinhibit_redisplay = Qt; + + /* Force update of the current frame, so that the desired and the + current matrices are identical. */ + update_frame_with_menu (sf, -1, -1); + state[0].menu = menu; + state[0].screen_behind = save_and_enable_current_matrix (sf); + + /* Display the menu title. We subtract 1 from x0 and y0 because we + want to interpret them as zero-based column and row coordinates, + and also because we want the first item of the menu, not its + title, to appear at x0,y0. */ + tty_menu_display (menu, x0 - 1, y0 - 1, 1, title_faces, x0 - 1, y0 - 1, 0, 0); + + /* Turn off the cursor. Otherwise it shows through the menu + panes, which is ugly. */ + col = cursorX (tty); + row = cursorY (tty); + tty_hide_cursor (tty); + + if (buffers_num_deleted) + menu->text[0][7] = ' '; + onepane = menu->count == 1 && menu->submenu[0]; + if (onepane) + { + menu->width = menu->submenu[0]->width; + state[0].menu = menu->submenu[0]; + } + else + { + state[0].menu = menu; + } + state[0].x = x0 - 1; + state[0].y = y0; + state[0].pane = onepane; + + x = state[0].x; + y = state[0].y; + first_time = true; + + leave = 0; + while (!leave) + { + mi_result input_status; + int min_y = state[0].y; + int max_y = min (min_y + state[0].menu->count, FRAME_TOTAL_LINES (sf) - 1) - 1; + + input_status = read_menu_input (sf, &x, &y, min_y, max_y, &first_time); + if (input_status) + { + leave = 1; + switch (input_status) + { + case MI_QUIT_MENU: + /* Remove the last help-echo, so that it doesn't + re-appear after "Quit". */ + show_help_echo (Qnil, Qnil, Qnil, Qnil); + result = TTYM_NO_SELECT; + break; + case MI_NEXT_ITEM: + if (kbd_navigation) + result = TTYM_NEXT; + else + leave = 0; + break; + case MI_PREV_ITEM: + if (kbd_navigation) + result = TTYM_PREV; + else + leave = 0; + break; + case MI_SCROLL_FORWARD: + if (y - min_y == state[0].menu->count - 1 - first_item) + { + y = min_y; + first_item = 0; + } + else + first_item++; + leave = 0; + break; + case MI_SCROLL_BACK: + if (first_item == 0) + { + y = max_y; + first_item = state[0].menu->count - 1 - (y - min_y); + } + else + first_item--; + leave = 0; + break; + default: + /* MI_ITEM_SELECTED is handled below, so nothing to do. */ + break; + } + } + if (sf->mouse_moved && input_status != MI_QUIT_MENU) + { + sf->mouse_moved = 0; + result = TTYM_IA_SELECT; + for (i = 0; i < statecount; i++) + if (state[i].x <= x && x < state[i].x + state[i].menu->width + 2) + { + int dy = y - state[i].y + first_item; + if (0 <= dy && dy < state[i].menu->count) + { + if (!state[i].menu->submenu[dy]) + { + if (state[i].menu->panenumber[dy]) + result = TTYM_SUCCESS; + else + result = TTYM_IA_SELECT; + } + *pane = state[i].pane - 1; + *selidx = dy; + /* We hit some part of a menu, so drop extra menus that + have been opened. That does not include an open and + active submenu. */ + if (i != statecount - 2 + || state[i].menu->submenu[dy] != state[i + 1].menu) + while (i != statecount - 1) + { + statecount--; + screen_update (sf, state[statecount].screen_behind); + state[statecount].screen_behind = NULL; + } + if (i == statecount - 1 && state[i].menu->submenu[dy]) + { + tty_menu_display (state[i].menu, + state[i].x, + state[i].y, + state[i].pane, + faces, x, y, first_item, 1); + state[statecount].menu = state[i].menu->submenu[dy]; + state[statecount].pane = state[i].menu->panenumber[dy]; + state[statecount].screen_behind + = save_and_enable_current_matrix (sf); + state[statecount].x + = state[i].x + state[i].menu->width + 2; + state[statecount].y = y; + statecount++; + } + } + } + tty_menu_display (state[statecount - 1].menu, + state[statecount - 1].x, + state[statecount - 1].y, + state[statecount - 1].pane, + faces, x, y, first_item, 1); + /* The call to display help-echo below will move the cursor, + so remember its current position as computed by + tty_menu_display. */ + col = cursorX (tty); + row = cursorY (tty); + } + + /* Display the help-echo message for the currently-selected menu + item. */ + if ((menu_help_message || prev_menu_help_message) + && menu_help_message != prev_menu_help_message) + { + help_callback (menu_help_message, + menu_help_paneno, menu_help_itemno); + /* Move the cursor to the beginning of the current menu + item, so that screen readers and other accessibility aids + know where the active region is. */ + cursor_to (sf, row, col); + prev_menu_help_message = menu_help_message; + } + /* Both tty_menu_display and help_callback invoke update_end, + which calls tty_show_cursor. Re-hide it, so it doesn't show + through the menus. */ + tty_hide_cursor (tty); + fflush (tty->output); + } + + sf->mouse_moved = 0; + screen_update (sf, state[0].screen_behind); + while (statecount--) + free_saved_screen (state[statecount].screen_behind); + tty_show_cursor (tty); /* Turn cursor back on. */ + fflush (tty->output); + +/* Clean up any mouse events that are waiting inside Emacs event queue. + These events are likely to be generated before the menu was even + displayed, probably because the user pressed and released the button + (which invoked the menu) too quickly. If we don't remove these events, + Emacs will process them after we return and surprise the user. */ + discard_mouse_events (); + if (!kbd_buffer_events_waiting ()) + clear_input_pending (); + SAFE_FREE (); + Vinhibit_redisplay = prev_inhibit_redisplay; + return result; +} + +/* Dispose of a menu. */ + +static void +tty_menu_destroy (tty_menu *menu) +{ + int i; + if (menu->allocated) + { + for (i = 0; i < menu->count; i++) + if (menu->submenu[i]) + tty_menu_destroy (menu->submenu[i]); + xfree (menu->text); + xfree (menu->submenu); + xfree (menu->panenumber); + xfree (menu->help_text); + } + xfree (menu); + menu_help_message = prev_menu_help_message = NULL; +} + +/* Show help HELP_STRING, or clear help if HELP_STRING is null. + + PANE is the pane number, and ITEM is the menu item number in + the menu (currently not used). */ + +static void +tty_menu_help_callback (char const *help_string, int pane, int item) +{ + Lisp_Object *first_item; + Lisp_Object pane_name; + Lisp_Object menu_object; + + first_item = XVECTOR (menu_items)->contents; + if (EQ (first_item[0], Qt)) + pane_name = first_item[MENU_ITEMS_PANE_NAME]; + else if (EQ (first_item[0], Qquote)) + /* This shouldn't happen, see xmenu_show. */ + pane_name = empty_unibyte_string; + else + pane_name = first_item[MENU_ITEMS_ITEM_NAME]; + + /* (menu-item MENU-NAME PANE-NUMBER) */ + menu_object = list3 (Qmenu_item, pane_name, make_number (pane)); + show_help_echo (help_string ? build_string (help_string) : Qnil, + Qnil, menu_object, make_number (item)); +} + +static void +tty_pop_down_menu (Lisp_Object arg) +{ + tty_menu *menu = XSAVE_POINTER (arg, 0); + + block_input (); + tty_menu_destroy (menu); + unblock_input (); +} + +/* Return the zero-based index of the last menu-bar item on frame F. */ +static int +tty_menu_last_menubar_item (struct frame *f) +{ + int i = 0; + + eassert (FRAME_TERMCAP_P (f) && FRAME_LIVE_P (f)); + if (FRAME_TERMCAP_P (f) && FRAME_LIVE_P (f)) + { + Lisp_Object items = FRAME_MENU_BAR_ITEMS (f); + + while (i < ASIZE (items)) + { + Lisp_Object str; + + str = AREF (items, i + 1); + if (NILP (str)) + break; + i += 4; + } + i -= 4; /* Went one too far! */ + } + return i; +} + +/* Find in frame F's menu bar the menu item that is next or previous + to the item at X/Y, and return that item's position in X/Y. WHICH + says which one--next or previous--item to look for. X and Y are + measured in character cells. This should only be called on TTY + frames. */ +static void +tty_menu_new_item_coords (struct frame *f, int which, int *x, int *y) +{ + eassert (FRAME_TERMCAP_P (f) && FRAME_LIVE_P (f)); + if (FRAME_TERMCAP_P (f) && FRAME_LIVE_P (f)) + { + Lisp_Object items = FRAME_MENU_BAR_ITEMS (f); + int last_i = tty_menu_last_menubar_item (f); + int i, prev_x; + + /* This loop assumes a single menu-bar line, and will fail to + find an item if it is not in the first line. Note that + make_lispy_event in keyboard.c makes the same assumption. */ + for (i = 0, prev_x = -1; i < ASIZE (items); i += 4) + { + Lisp_Object pos, str; + int ix; + + str = AREF (items, i + 1); + pos = AREF (items, i + 3); + if (NILP (str)) + return; + ix = XINT (pos); + if (ix <= *x + /* We use <= so the blank between 2 items on a TTY is + considered part of the previous item. */ + && *x <= ix + menu_item_width (SDATA (str))) + { + /* Found current item. Now compute the X coordinate of + the previous or next item. */ + if (which == TTYM_NEXT) + { + if (i < last_i) + *x = XINT (AREF (items, i + 4 + 3)); + else + *x = 0; /* Wrap around to the first item. */ + } + else if (prev_x < 0) + { + /* Wrap around to the last item. */ + *x = XINT (AREF (items, last_i + 3)); + } + else + *x = prev_x; + return; + } + prev_x = ix; + } + } +} + +/* WINDOWSNT uses this as menu_show_hook, see w32console.c. */ +Lisp_Object +tty_menu_show (struct frame *f, int x, int y, int menuflags, + Lisp_Object title, const char **error_name) +{ + tty_menu *menu; + int pane, selidx, lpane, status; + Lisp_Object entry, pane_prefix; + char *datap; + int ulx, uly, width, height; + int item_x, item_y; + int dispwidth, dispheight; + int i, j, lines, maxlines; + int maxwidth; + ptrdiff_t specpdl_count; + + eassert (FRAME_TERMCAP_P (f)); + + *error_name = 0; + if (menu_items_n_panes == 0) + return Qnil; + + if (menu_items_used <= MENU_ITEMS_PANE_LENGTH) + { + *error_name = "Empty menu"; + return Qnil; + } + + /* Make the menu on that window. */ + menu = tty_menu_create (); + + /* Don't GC while we prepare and show the menu, because we give the + menu functions pointers to the contents of strings. */ + specpdl_count = inhibit_garbage_collection (); + + /* Avoid crashes if, e.g., another client will connect while we + are in a menu. */ + temporarily_switch_to_single_kboard (f); + + /* Adjust coordinates to be root-window-relative. */ + item_x = x += f->left_pos; + item_y = y += f->top_pos; + + /* Create all the necessary panes and their items. */ + USE_SAFE_ALLOCA; + maxwidth = maxlines = lines = i = 0; + lpane = TTYM_FAILURE; + while (i < menu_items_used) + { + if (EQ (AREF (menu_items, i), Qt)) + { + /* Create a new pane. */ + Lisp_Object pane_name, prefix; + const char *pane_string; + + maxlines = max (maxlines, lines); + lines = 0; + pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME); + prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX); + pane_string = (NILP (pane_name) + ? "" : SSDATA (pane_name)); + if ((menuflags & MENU_KEYMAPS) && !NILP (prefix)) + pane_string++; + + lpane = tty_menu_add_pane (menu, pane_string); + if (lpane == TTYM_FAILURE) + { + tty_menu_destroy (menu); + *error_name = "Can't create pane"; + entry = Qnil; + goto tty_menu_end; + } + i += MENU_ITEMS_PANE_LENGTH; + + /* Find the width of the widest item in this pane. */ + j = i; + while (j < menu_items_used) + { + Lisp_Object item; + item = AREF (menu_items, j); + if (EQ (item, Qt)) + break; + if (NILP (item)) + { + j++; + continue; + } + width = SBYTES (item); + if (width > maxwidth) + maxwidth = width; + + j += MENU_ITEMS_ITEM_LENGTH; + } + } + /* Ignore a nil in the item list. + It's meaningful only for dialog boxes. */ + else if (EQ (AREF (menu_items, i), Qquote)) + i += 1; + else + { + /* Create a new item within current pane. */ + Lisp_Object item_name, enable, descrip, help; + char *item_data; + char const *help_string; + + item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME); + enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE); + descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY); + help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP); + help_string = STRINGP (help) ? SSDATA (help) : NULL; + + if (!NILP (descrip)) + { + item_data = SAFE_ALLOCA (maxwidth + SBYTES (descrip) + 1); + memcpy (item_data, SSDATA (item_name), SBYTES (item_name)); + for (j = SCHARS (item_name); j < maxwidth; j++) + item_data[j] = ' '; + memcpy (item_data + j, SSDATA (descrip), SBYTES (descrip)); + item_data[j + SBYTES (descrip)] = 0; + } + else + item_data = SSDATA (item_name); + + if (lpane == TTYM_FAILURE + || (! tty_menu_add_selection (menu, lpane, item_data, + !NILP (enable), help_string))) + { + tty_menu_destroy (menu); + *error_name = "Can't add selection to menu"; + entry = Qnil; + goto tty_menu_end; + } + i += MENU_ITEMS_ITEM_LENGTH; + lines++; + } + } + + maxlines = max (maxlines, lines); + + /* All set and ready to fly. */ + dispwidth = f->text_cols; + dispheight = f->text_lines; + x = min (x, dispwidth); + y = min (y, dispheight); + x = max (x, 1); + y = max (y, 1); + tty_menu_locate (menu, x, y, &ulx, &uly, &width, &height); + if (ulx + width > dispwidth) + { + x -= (ulx + width) - dispwidth; + ulx = dispwidth - width; + } + if (uly + height > dispheight) + { + y -= (uly + height) - dispheight; + uly = dispheight - height; + } + + if (FRAME_HAS_MINIBUF_P (f) && uly + height > dispheight - 2) + { + /* Move the menu away of the echo area, to avoid overwriting the + menu with help echo messages or vice versa. */ + if (BUFFERP (echo_area_buffer[0]) && WINDOWP (echo_area_window)) + { + y -= WINDOW_TOTAL_LINES (XWINDOW (echo_area_window)) + 1; + uly -= WINDOW_TOTAL_LINES (XWINDOW (echo_area_window)) + 1; + } + else + { + y -= 2; + uly -= 2; + } + } + + if (ulx < 0) x -= ulx; + if (uly < 0) y -= uly; + +#if 0 + /* This code doesn't make sense on a TTY, since it can easily annul + the adjustments above that carefully avoid truncation of the menu + items. I think it was written to fix some problem that only + happens on X11. */ + if (! for_click) + { + /* If position was not given by a mouse click, adjust so upper left + corner of the menu as a whole ends up at given coordinates. This + is what x-popup-menu says in its documentation. */ + x += width / 2; + y += 1.5 * height / (maxlines + 2); + } +#endif + + pane = selidx = 0; + + record_unwind_protect (tty_pop_down_menu, make_save_ptr (menu)); + + specbind (Qoverriding_terminal_local_map, + Fsymbol_value (Qtty_menu_navigation_map)); + status = tty_menu_activate (menu, &pane, &selidx, x, y, &datap, + tty_menu_help_callback, + menuflags & MENU_KBD_NAVIGATION); + entry = pane_prefix = Qnil; + + switch (status) + { + case TTYM_SUCCESS: + /* Find the item number SELIDX in pane number PANE. */ + i = 0; + while (i < menu_items_used) + { + if (EQ (AREF (menu_items, i), Qt)) + { + if (pane == 0) + pane_prefix + = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX); + pane--; + i += MENU_ITEMS_PANE_LENGTH; + } + else + { + if (pane == -1) + { + if (selidx == 0) + { + entry + = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE); + if (menuflags & MENU_KEYMAPS) + { + entry = Fcons (entry, Qnil); + if (!NILP (pane_prefix)) + entry = Fcons (pane_prefix, entry); + } + break; + } + selidx--; + } + i += MENU_ITEMS_ITEM_LENGTH; + } + } + break; + + case TTYM_NEXT: + case TTYM_PREV: + tty_menu_new_item_coords (f, status, &item_x, &item_y); + entry = Fcons (make_number (item_x), make_number (item_y)); + break; + + case TTYM_FAILURE: + *error_name = "Can't activate menu"; + case TTYM_IA_SELECT: + break; + case TTYM_NO_SELECT: + /* If the selected frame was changed while we displayed a menu, + throw to top level in order to undo any temporary settings + made by TTY menu code. */ + if (f != SELECTED_FRAME ()) + Ftop_level (); + /* Make "Cancel" equivalent to C-g unless FOR_CLICK (which means + the menu was invoked with a mouse event as POSITION). */ + if (!(menuflags & MENU_FOR_CLICK)) + Fsignal (Qquit, Qnil); + break; + } + + tty_menu_end: + + SAFE_FREE (); + unbind_to (specpdl_count, Qnil); + return entry; +} + +#endif /* !MSDOS */ + + #ifndef MSDOS /*********************************************************************** Initialization ***********************************************************************/ /* Initialize the tty-dependent part of frame F. The frame must - already have its device initialized. */ + already have its device initialized. */ void create_tty_output (struct frame *f) @@ -2792,30 +3785,25 @@ create_tty_output (struct frame *f) f->output_data.tty = t; } -/* Delete frame F's face cache, and its tty-dependent part. */ +/* Delete frame F's face cache, and its tty-dependent part. */ static void tty_free_frame_resources (struct frame *f) { eassert (FRAME_TERMCAP_P (f)); - - if (FRAME_FACE_CACHE (f)) - free_frame_faces (f); - + free_frame_faces (f); xfree (f->output_data.tty); } #else /* MSDOS */ -/* Delete frame F's face cache. */ +/* Delete frame F's face cache. */ static void tty_free_frame_resources (struct frame *f) { eassert (FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)); - - if (FRAME_FACE_CACHE (f)) - free_frame_faces (f); + free_frame_faces (f); } #endif /* MSDOS */ @@ -2844,7 +3832,9 @@ clear_tty_hooks (struct terminal *terminal) terminal->frame_rehighlight_hook = 0; terminal->frame_raise_lower_hook = 0; terminal->fullscreen_hook = 0; + terminal->menu_show_hook = 0; terminal->set_vertical_scroll_bar_hook = 0; + terminal->set_horizontal_scroll_bar_hook = 0; terminal->condemn_scroll_bars_hook = 0; terminal->redeem_scroll_bar_hook = 0; terminal->judge_scroll_bars_hook = 0; @@ -2862,43 +3852,29 @@ clear_tty_hooks (struct terminal *terminal) static void set_tty_hooks (struct terminal *terminal) { - terminal->rif = 0; /* ttys don't support window-based redisplay. */ - terminal->cursor_to_hook = &tty_cursor_to; terminal->raw_cursor_to_hook = &tty_raw_cursor_to; - terminal->clear_to_end_hook = &tty_clear_to_end; terminal->clear_frame_hook = &tty_clear_frame; terminal->clear_end_of_line_hook = &tty_clear_end_of_line; - terminal->ins_del_lines_hook = &tty_ins_del_lines; - terminal->insert_glyphs_hook = &tty_insert_glyphs; terminal->write_glyphs_hook = &tty_write_glyphs; terminal->delete_glyphs_hook = &tty_delete_glyphs; - terminal->ring_bell_hook = &tty_ring_bell; - terminal->reset_terminal_modes_hook = &tty_reset_terminal_modes; terminal->set_terminal_modes_hook = &tty_set_terminal_modes; - terminal->update_begin_hook = 0; /* Not needed. */ terminal->update_end_hook = &tty_update_end; +#ifdef MSDOS + terminal->menu_show_hook = &x_menu_show; +#else + terminal->menu_show_hook = &tty_menu_show; +#endif terminal->set_terminal_window_hook = &tty_set_terminal_window; - - terminal->mouse_position_hook = 0; /* Not needed. */ - terminal->frame_rehighlight_hook = 0; /* Not needed. */ - terminal->frame_raise_lower_hook = 0; /* Not needed. */ - - terminal->set_vertical_scroll_bar_hook = 0; /* Not needed. */ - terminal->condemn_scroll_bars_hook = 0; /* Not needed. */ - terminal->redeem_scroll_bar_hook = 0; /* Not needed. */ - terminal->judge_scroll_bars_hook = 0; /* Not needed. */ - terminal->read_socket_hook = &tty_read_avail_input; /* keyboard.c */ - terminal->frame_up_to_date_hook = 0; /* Not needed. */ - terminal->delete_frame_hook = &tty_free_frame_resources; terminal->delete_terminal_hook = &delete_tty; + /* Other hooks are NULL by default. */ } /* If FD is the controlling terminal, drop it. */ @@ -2913,9 +3889,10 @@ dissociate_if_controlling_tty (int fd) /* setsid failed, presumably because Emacs is already a process group leader. Fall back on the obsolescent way to dissociate a controlling tty. */ - block_tty_out_signal (); + sigset_t oldset; + block_tty_out_signal (&oldset); ioctl (fd, TIOCNOTTY, 0); - unblock_tty_out_signal (); + unblock_tty_out_signal (&oldset); #endif } } @@ -2934,12 +3911,12 @@ dissociate_if_controlling_tty (int fd) struct terminal * init_tty (const char *name, const char *terminal_type, bool must_succeed) { - char *area = NULL; + char *area; char **address = &area; - int buffer_size = 4096; int status; struct tty_display_info *tty = NULL; struct terminal *terminal = NULL; + sigset_t oldset; bool ctty = false; /* True if asked to open controlling tty. */ if (!terminal_type) @@ -2957,11 +3934,11 @@ init_tty (const char *name, const char *terminal_type, bool must_succeed) /* XXX Perhaps this should be made explicit by having init_tty always create a new terminal and separating terminal and frame creation on Lisp level. */ - terminal = get_named_tty (name); + terminal = get_named_terminal (name); if (terminal) return terminal; - terminal = create_terminal (); + terminal = create_terminal (output_termcap, NULL); #ifdef MSDOS if (been_here > 0) maybe_fatal (0, 0, "Attempt to create another terminal %s", "", @@ -2975,7 +3952,6 @@ init_tty (const char *name, const char *terminal_type, bool must_succeed) tty->next = tty_list; tty_list = tty; - terminal->type = output_termcap; terminal->display_info.tty = tty; tty->terminal = terminal; @@ -3001,12 +3977,15 @@ init_tty (const char *name, const char *terminal_type, bool must_succeed) open a frame on the same terminal. */ int flags = O_RDWR | O_NOCTTY | (ctty ? 0 : O_IGNORE_CTTY); int fd = emacs_open (name, flags, 0); - tty->input = tty->output = fd < 0 || ! isatty (fd) ? 0 : fdopen (fd, "w+"); + tty->input = tty->output + = ((fd < 0 || ! isatty (fd)) + ? NULL + : fdopen (fd, "w+")); if (! tty->input) { char const *diagnostic - = tty->input ? "Not a tty device: %s" : "Could not open file: %s"; + = (fd < 0) ? "Could not open file: %s" : "Not a tty device: %s"; emacs_close (fd); maybe_fatal (must_succeed, terminal, diagnostic, diagnostic, name); } @@ -3024,13 +4003,13 @@ init_tty (const char *name, const char *terminal_type, bool must_succeed) Wcm_clear (tty); - tty->termcap_term_buffer = xmalloc (buffer_size); - /* On some systems, tgetent tries to access the controlling terminal. */ - block_tty_out_signal (); + block_tty_out_signal (&oldset); status = tgetent (tty->termcap_term_buffer, terminal_type); - unblock_tty_out_signal (); + if (tty->termcap_term_buffer[TERMCAP_BUFFER_SIZE - 1]) + emacs_abort (); + unblock_tty_out_signal (&oldset); if (status < 0) { @@ -3050,22 +4029,17 @@ init_tty (const char *name, const char *terminal_type, bool must_succeed) "Terminal type %s is not defined", "Terminal type %s is not defined.\n\ If that is not the actual type of terminal you have,\n\ -use the Bourne shell command `TERM=... export TERM' (C-shell:\n\ -`setenv TERM ...') to specify the correct type. It may be necessary\n" +use the Bourne shell command 'TERM=...; export TERM' (C-shell:\n\ +'setenv TERM ...') to specify the correct type. It may be necessary\n" #ifdef TERMINFO -"to do `unset TERMINFO' (C-shell: `unsetenv TERMINFO') as well.", +"to do 'unset TERMINFO' (C-shell: 'unsetenv TERMINFO') as well.", #else -"to do `unset TERMCAP' (C-shell: `unsetenv TERMCAP') as well.", +"to do 'unset TERMCAP' (C-shell: 'unsetenv TERMCAP') as well.", #endif terminal_type); } -#ifndef TERMINFO - if (strlen (tty->termcap_term_buffer) >= buffer_size) - emacs_abort (); - buffer_size = strlen (tty->termcap_term_buffer); -#endif - tty->termcap_strings_buffer = area = xmalloc (buffer_size); + area = tty->termcap_strings_buffer; tty->TS_ins_line = tgetstr ("al", address); tty->TS_ins_multi_lines = tgetstr ("AL", address); tty->TS_bell = tgetstr ("bl", address); @@ -3164,9 +4138,9 @@ use the Bourne shell command `TERM=... export TERM' (C-shell:\n\ /* Since we make MagicWrap terminals look like AutoWrap, we need to have the former flag imply the latter. */ AutoWrap (tty) = MagicWrap (tty) || tgetflag ("am"); - terminal->memory_below_frame = tgetflag ("db"); + tty->memory_below_frame = tgetflag ("db"); tty->TF_hazeltine = tgetflag ("hz"); - terminal->must_write_spaces = tgetflag ("in"); + tty->must_write_spaces = tgetflag ("in"); tty->meta_key = tgetflag ("km") || tgetflag ("MT"); tty->TF_insmode_motion = tgetflag ("mi"); tty->TF_standout_motion = tgetflag ("ms"); @@ -3186,7 +4160,8 @@ use the Bourne shell command `TERM=... export TERM' (C-shell:\n\ tty->specified_window = height; FRAME_VERTICAL_SCROLL_BAR_TYPE (f) = vertical_scroll_bar_none; - terminal->char_ins_del_ok = 1; + FRAME_HAS_HORIZONTAL_SCROLL_BARS (f) = 0; + tty->char_ins_del_ok = 1; baud_rate = 19200; } #else /* MSDOS */ @@ -3199,7 +4174,7 @@ use the Bourne shell command `TERM=... export TERM' (C-shell:\n\ get_tty_size (fileno (tty->input), &width, &height); FrameCols (tty) = width; FrameRows (tty) = height; - terminal->char_ins_del_ok = 0; + tty->char_ins_del_ok = 0; init_baud_rate (fileno (tty->input)); } #endif /* MSDOS */ @@ -3218,12 +4193,12 @@ use the Bourne shell command `TERM=... export TERM' (C-shell:\n\ tty->delete_in_insert_mode = 1; UseTabs (tty) = 0; - terminal->scroll_region_ok = 0; + tty->scroll_region_ok = 0; /* Seems to insert lines when it's not supposed to, messing up the display. In doing a trace, it didn't seem to be called much, so I don't think we're losing anything by turning it off. */ - terminal->line_ins_del_ok = 0; + tty->line_ins_del_ok = 0; tty->TN_max_colors = 16; /* Must be non-zero for tty-display-color-p. */ #endif /* DOS_NT */ @@ -3233,11 +4208,7 @@ use the Bourne shell command `TERM=... export TERM' (C-shell:\n\ tty->mouse_highlight.mouse_face_window = Qnil; #endif - terminal->kboard = xmalloc (sizeof *terminal->kboard); - init_kboard (terminal->kboard); - kset_window_system (terminal->kboard, Qnil); - terminal->kboard->next_kboard = all_kboards; - all_kboards = terminal->kboard; + terminal->kboard = allocate_kboard (Qnil); terminal->kboard->reference_count++; /* Don't let the initial kboard remain current longer than necessary. That would cause problems if a file loaded on startup tries to @@ -3337,12 +4308,12 @@ use the Bourne shell command `TERM=... export TERM' (C-shell:\n\ "Terminal type \"%s\" is not powerful enough to run Emacs.\n\ It lacks the ability to position the cursor.\n\ If that is not the actual type of terminal you have,\n\ -use the Bourne shell command `TERM=... export TERM' (C-shell:\n\ -`setenv TERM ...') to specify the correct type. It may be necessary\n" +use the Bourne shell command 'TERM=...; export TERM' (C-shell:\n\ +'setenv TERM ...') to specify the correct type. It may be necessary\n" # ifdef TERMINFO -"to do `unset TERMINFO' (C-shell: `unsetenv TERMINFO') as well.", +"to do 'unset TERMINFO' (C-shell: 'unsetenv TERMINFO') as well.", # else /* TERMCAP */ -"to do `unset TERMCAP' (C-shell: `unsetenv TERMCAP') as well.", +"to do 'unset TERMCAP' (C-shell: 'unsetenv TERMCAP') as well.", # endif /* TERMINFO */ terminal_type); } @@ -3358,23 +4329,21 @@ use the Bourne shell command `TERM=... export TERM' (C-shell:\n\ UseTabs (tty) = tabs_safe_p (fileno (tty->input)) && TabWidth (tty) == 8; - terminal->scroll_region_ok + tty->scroll_region_ok = (tty->Wcm->cm_abs && (tty->TS_set_window || tty->TS_set_scroll_region || tty->TS_set_scroll_region_1)); - terminal->line_ins_del_ok + tty->line_ins_del_ok = (((tty->TS_ins_line || tty->TS_ins_multi_lines) && (tty->TS_del_line || tty->TS_del_multi_lines)) - || (terminal->scroll_region_ok + || (tty->scroll_region_ok && tty->TS_fwd_scroll && tty->TS_rev_scroll)); - terminal->char_ins_del_ok + tty->char_ins_del_ok = ((tty->TS_ins_char || tty->TS_insert_mode || tty->TS_pad_inserted_char || tty->TS_ins_multi_chars) && (tty->TS_del_char || tty->TS_del_multi_chars)); - terminal->fast_clear_end_of_line = tty->TS_clr_line != 0; - init_baud_rate (fileno (tty->input)); #endif /* not DOS_NT */ @@ -3427,7 +4396,7 @@ fatal (const char *str, ...) -/* Delete the given tty terminal, closing all frames on it. */ +/* Delete the given tty terminal, closing all frames on it. */ static void delete_tty (struct terminal *terminal) @@ -3452,7 +4421,7 @@ delete_tty (struct terminal *terminal) ; if (! p) - /* This should not happen. */ + /* This should not happen. */ emacs_abort (); p->next = tty->next; @@ -3460,7 +4429,7 @@ delete_tty (struct terminal *terminal) } /* reset_sys_modes needs a valid device, so this call needs to be - before delete_terminal. */ + before delete_terminal. */ reset_sys_modes (tty); delete_terminal (terminal); @@ -3481,9 +4450,6 @@ delete_tty (struct terminal *terminal) xfree (tty->old_tty); xfree (tty->Wcm); - xfree (tty->termcap_strings_buffer); - xfree (tty->termcap_term_buffer); - xfree (tty); } @@ -3540,4 +4506,19 @@ bigger, or it may make it blink, or it may do nothing at all. */); encode_terminal_src = NULL; encode_terminal_dst = NULL; + + DEFSYM (Qtty_mode_set_strings, "tty-mode-set-strings"); + DEFSYM (Qtty_mode_reset_strings, "tty-mode-reset-strings"); + +#ifndef MSDOS + DEFSYM (Qtty_menu_next_item, "tty-menu-next-item"); + DEFSYM (Qtty_menu_prev_item, "tty-menu-prev-item"); + DEFSYM (Qtty_menu_next_menu, "tty-menu-next-menu"); + DEFSYM (Qtty_menu_prev_menu, "tty-menu-prev-menu"); + DEFSYM (Qtty_menu_select, "tty-menu-select"); + DEFSYM (Qtty_menu_ignore, "tty-menu-ignore"); + DEFSYM (Qtty_menu_exit, "tty-menu-exit"); + DEFSYM (Qtty_menu_mouse_movement, "tty-menu-mouse-movement"); + DEFSYM (Qtty_menu_navigation_map, "tty-menu-navigation-map"); +#endif } |
