X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/f573ac33a3299ae83d88f5c3a04cc1bc6f5c205a..e147461d406b00eb9b0281d35cf512e7a69f6601:/src/term.c diff --git a/src/term.c b/src/term.c index 775df1e9bc..8661cba116 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-2014 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -51,6 +51,10 @@ along with GNU Emacs. If not, see . */ static int been_here = -1; #endif +#ifdef USE_X_TOOLKIT +#include "../lwlib/lwlib.h" +#endif + #include "cm.h" #ifdef HAVE_X_WINDOWS #include "xterm.h" @@ -216,6 +220,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. */ @@ -495,8 +500,12 @@ static ptrdiff_t encode_terminal_dst_size; Set CODING->produced to the byte-length of the resulting byte sequence, and return a pointer to that byte sequence. */ +#ifndef DOS_NT +static +#endif 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; @@ -1413,18 +1422,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 } @@ -1883,55 +1880,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); @@ -1956,7 +1916,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); @@ -1964,7 +1924,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); @@ -2018,12 +1978,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))) \ @@ -2094,17 +2052,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; @@ -2437,7 +2387,7 @@ frame's terminal). */) 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, 0, 0, 0, 0); SET_FRAME_VISIBLE (XFRAME (t->display_info.tty->top_frame), 1); } @@ -2769,17 +2719,17 @@ DEFUN ("gpm-mouse-stop", Fgpm_mouse_stop, Sgpm_mouse_stop, Menus ***********************************************************************/ -#if defined (HAVE_MENUS) && !defined (MSDOS) +#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 directly 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 + 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. @@ -2789,6 +2739,8 @@ DEFUN ("gpm-mouse-stop", Fgpm_mouse_stop, Sgpm_mouse_stop, #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; @@ -2807,8 +2759,8 @@ typedef struct tty_menu_struct int count; char **text; struct tty_menu_struct **submenu; - int *panenumber; /* Also used as enable. */ - int allocated; + int *panenumber; /* Also used as enabled flag. */ + ptrdiff_t allocated; int panecount; int width; const char **help_text; @@ -2819,38 +2771,27 @@ typedef struct tty_menu_struct static tty_menu * tty_menu_create (void) { - tty_menu *menu; - - menu = (tty_menu *) xmalloc (sizeof (tty_menu)); - menu->allocated = menu->count = menu->panecount = menu->width = 0; - return menu; + return xzalloc (sizeof *tty_menu_create ()); } /* Allocate some (more) memory for MENU ensuring that there is room for one - for item. */ + more item. */ static void tty_menu_make_room (tty_menu *menu) { - if (menu->allocated == 0) + if (menu->allocated == menu->count) { - int count = menu->allocated = 10; - menu->text = (char **) xmalloc (count * sizeof (char *)); - menu->submenu = (tty_menu **) xmalloc (count * sizeof (tty_menu *)); - menu->panenumber = (int *) xmalloc (count * sizeof (int)); - menu->help_text = (const char **) xmalloc (count * sizeof (char *)); - } - else if (menu->allocated == menu->count) - { - int count = menu->allocated = menu->allocated + 10; - menu->text - = (char **) xrealloc (menu->text, count * sizeof (char *)); - menu->submenu - = (tty_menu **) xrealloc (menu->submenu, count * sizeof (tty_menu *)); - menu->panenumber - = (int *) xrealloc (menu->panenumber, count * sizeof (int)); - menu->help_text - = (const char **) xrealloc (menu->help_text, count * sizeof (char *)); + 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; } } @@ -2867,7 +2808,8 @@ tty_menu_search_pane (tty_menu *menu, int pane) { if (pane == menu->panenumber[i]) return menu->submenu[i]; - if ((try = tty_menu_search_pane (menu->submenu[i], pane))) + try = tty_menu_search_pane (menu->submenu[i], pane); + if (try) return try; } return (tty_menu *) 0; @@ -2899,7 +2841,7 @@ static void mouse_get_xy (int *x, int *y) { struct frame *sf = SELECTED_FRAME (); - Lisp_Object lmx, lmy, lisp_dummy; + Lisp_Object lmx = Qnil, lmy = Qnil, lisp_dummy; enum scroll_bar_part part_dummy; Time time_dummy; @@ -2915,33 +2857,35 @@ mouse_get_xy (int *x, int *y) } } -/* Display MENU at (X,Y) using FACES. */ +/* 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 disp_help) + 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_LINES (sf) - 1 - y); menu_help_message = NULL; width = menu->width; col = cursorX (tty); row = cursorY (tty); -#if 0 - IT_update_begin (sf); /* FIXME: do we need an update_begin_hook? */ -#endif - for (i = 0; i < menu->count; i++) + for (i = 0; i < max_items; i++) { int max_width = width + 2; /* +2 for padding blanks on each side */ + int j = i + first_item; - cursor_to (sf, y + i, x); - if (menu->submenu[i]) + if (menu->submenu[j]) max_width += 2; /* for displaying " >" after the item */ enabled - = (!menu->submenu[i] && menu->panenumber[i]) || (menu->submenu[i]); + = (!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 @@ -2949,31 +2893,32 @@ tty_menu_display (tty_menu *menu, int x, int y, int pn, int *faces, code does. */ if (disp_help && enabled + mousehere * 2 >= 2) { - menu_help_message = menu->help_text[i]; + menu_help_message = menu->help_text[j]; menu_help_paneno = pn - 1; - menu_help_itemno = i; + 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[i], max_width, face, x, y + i, - menu->submenu[i] != NULL); + display_tty_menu_item (menu->text[j], max_width, face, x, y + i, + menu->submenu[j] != NULL); } - update_frame_with_menu (sf); - cursor_to (sf, row, col); + update_frame_with_menu (sf, row, col); } /* --------------------------- X Menu emulation ---------------------- */ -/* Report availability of menus. */ - -int -have_menus_p (void) { return 1; } - /* 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; - const char *p; + const unsigned char *p; tty_menu_make_room (menu); menu->submenu[menu->count] = tty_menu_create (); @@ -2983,7 +2928,7 @@ tty_menu_add_pane (tty_menu *menu, const char *txt) menu->count++; /* Update the menu width, if necessary. */ - for (len = 0, p = txt; *p; ) + for (len = 0, p = (unsigned char *) txt; *p; ) { int ch_len; int ch = STRING_CHAR_AND_LENGTH (p, ch_len); @@ -3000,16 +2945,19 @@ tty_menu_add_pane (tty_menu *menu, const char *txt) /* Create a new item in a menu pane. */ -int +static bool tty_menu_add_selection (tty_menu *menu, int pane, - char *txt, int enable, char const *help_text) + char *txt, bool enable, char const *help_text) { int len; - char *p; + unsigned char *p; if (pane) - if (!(menu = tty_menu_search_pane (menu, pane))) - return TTYM_FAILURE; + { + 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; @@ -3018,7 +2966,7 @@ tty_menu_add_selection (tty_menu *menu, int pane, menu->count++; /* Update the menu width, if necessary. */ - for (len = 0, p = txt; *p; ) + for (len = 0, p = (unsigned char *) txt; *p; ) { int ch_len; int ch = STRING_CHAR_AND_LENGTH (p, ch_len); @@ -3030,12 +2978,12 @@ tty_menu_add_selection (tty_menu *menu, int pane, if (len > menu->width) menu->width = len; - return TTYM_SUCCESS; + return 1; } /* Decide where the menu would be placed if requested at (X,Y). */ -void +static void tty_menu_locate (tty_menu *menu, int x, int y, int *ulx, int *uly, int *width, int *height) { @@ -3057,7 +3005,7 @@ struct tty_menu_state enable all its rows. Value is a glyph matrix holding the contents of F's current frame matrix with all its glyph rows enabled. */ -struct glyph_matrix * +static struct glyph_matrix * save_and_enable_current_matrix (struct frame *f) { int i; @@ -3077,24 +3025,8 @@ save_and_enable_current_matrix (struct frame *f) /* 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 = 1; + to->enabled_p = true; to->hash = from->hash; - if (from->used[LEFT_MARGIN_AREA]) - { - nbytes = from->used[LEFT_MARGIN_AREA] * sizeof (struct glyph); - to->glyphs[LEFT_MARGIN_AREA] = (struct glyph *) xmalloc (nbytes); - memcpy (to->glyphs[LEFT_MARGIN_AREA], - from->glyphs[LEFT_MARGIN_AREA], nbytes); - to->used[LEFT_MARGIN_AREA] = from->used[LEFT_MARGIN_AREA]; - } - if (from->used[RIGHT_MARGIN_AREA]) - { - nbytes = from->used[RIGHT_MARGIN_AREA] * sizeof (struct glyph); - to->glyphs[RIGHT_MARGIN_AREA] = (struct glyph *) xmalloc (nbytes); - memcpy (to->glyphs[RIGHT_MARGIN_AREA], - from->glyphs[RIGHT_MARGIN_AREA], nbytes); - to->used[RIGHT_MARGIN_AREA] = from->used[RIGHT_MARGIN_AREA]; - } } return saved; @@ -3119,26 +3051,6 @@ restore_desired_matrix (struct frame *f, struct glyph_matrix *saved) to->used[TEXT_AREA] = from->used[TEXT_AREA]; to->enabled_p = from->enabled_p; to->hash = from->hash; - nbytes = from->used[LEFT_MARGIN_AREA] * sizeof (struct glyph); - if (nbytes) - { - eassert (to->glyphs[LEFT_MARGIN_AREA] != from->glyphs[LEFT_MARGIN_AREA]); - memcpy (to->glyphs[LEFT_MARGIN_AREA], - from->glyphs[LEFT_MARGIN_AREA], nbytes); - to->used[LEFT_MARGIN_AREA] = from->used[LEFT_MARGIN_AREA]; - } - else - to->used[LEFT_MARGIN_AREA] = 0; - nbytes = from->used[RIGHT_MARGIN_AREA] * sizeof (struct glyph); - if (nbytes) - { - eassert (to->glyphs[RIGHT_MARGIN_AREA] != from->glyphs[RIGHT_MARGIN_AREA]); - memcpy (to->glyphs[RIGHT_MARGIN_AREA], - from->glyphs[RIGHT_MARGIN_AREA], nbytes); - to->used[RIGHT_MARGIN_AREA] = from->used[RIGHT_MARGIN_AREA]; - } - else - to->used[RIGHT_MARGIN_AREA] = 0; } } @@ -3148,17 +3060,13 @@ free_saved_screen (struct glyph_matrix *saved) int i; if (!saved) - return; /* already freed */ + return; /* Already freed! */ for (i = 0; i < saved->nrows; ++i) { struct glyph_row *from = saved->rows + i; xfree (from->glyphs[TEXT_AREA]); - if (from->used[LEFT_MARGIN_AREA]) - xfree (from->glyphs[LEFT_MARGIN_AREA]); - if (from->used[RIGHT_MARGIN_AREA]) - xfree (from->glyphs[RIGHT_MARGIN_AREA]); } xfree (saved->rows); @@ -3170,16 +3078,23 @@ static void screen_update (struct frame *f, struct glyph_matrix *mtx) { restore_desired_matrix (f, mtx); - update_frame_with_menu (f); + update_frame_with_menu (f, -1, -1); } -/* 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. +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; - Value is -1 if C-g was pressed, 1 if an item was selected, zero - otherwise. */ -static int +/* 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) { @@ -3187,16 +3102,12 @@ read_menu_input (struct frame *sf, int *x, int *y, int min_y, int max_y, { *first_time = false; sf->mouse_moved = 1; - return 0; } - - while (1) + else { -#if 1 - extern Lisp_Object read_menu_command (void); Lisp_Object cmd; - int usable_input = 1; - int st = 0; + 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; @@ -3211,98 +3122,68 @@ read_menu_input (struct frame *sf, int *x, int *y, int min_y, int max_y, tty->showing_menu = 0; do_mouse_tracking = saved_mouse_tracking; - if (EQ (cmd, Qt) || EQ (cmd, Qtty_menu_exit)) - return -1; + 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)) { - int mx, my; - - mouse_get_xy (&mx, &my); - *x = mx; - *y = my; + usable_input = 0; + st = MI_NEXT_ITEM; } - else if (EQ (cmd, Qtty_menu_next_menu)) - *x += 1; else if (EQ (cmd, Qtty_menu_prev_menu)) - *x -= 1; + { + 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 = 1; + st = MI_ITEM_SELECTED; else if (!EQ (cmd, Qtty_menu_ignore)) usable_input = 0; if (usable_input) sf->mouse_moved = 1; -#else - int volatile dx = 0; - int volatile dy = 0; - int volatile st = 0; - - *x += dx; - *y += dy; - if (dx != 0 || dy != 0) - sf->mouse_moved = 1; - Sleep (300); -#endif return st; } - return 0; -} - -/* FIXME */ -static bool mouse_visible; -static void -mouse_off (void) -{ - mouse_visible = false; -} - -static void -mouse_on (void) -{ - mouse_visible = true; -} - -static bool -mouse_pressed (int b, int *x, int *y) -{ - return false; -} - -static bool -mouse_button_depressed (int b, int *x, int *y) -{ - return false; -} - -static bool -mouse_released (int b, int *x, int *y) -{ - return true; + return MI_CONTINUE; } /* Display menu, wait for user's response, and return that response. */ -int +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)) + void (*help_callback)(char const *, int, int), + bool kbd_navigation) { struct tty_menu_state *state; - int statecount, x, y, i, b, leave, result, onepane; - int title_faces[4]; /* face to display the menu title */ + 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 saved_echo_area_message, selectface; + Lisp_Object selectface; + int first_item = 0; + int col, row; /* Don't allow non-positive x0 and y0, lest the menu will wrap around the display. */ @@ -3311,11 +3192,6 @@ tty_menu_activate (tty_menu *menu, int *pane, int *selidx, if (y0 <= 0) y0 = 1; -#if 0 - /* We will process all the mouse events directly. */ - mouse_preempted++; -#endif - state = alloca (menu->panecount * sizeof (struct tty_menu_state)); memset (state, 0, sizeof (*state)); faces[0] @@ -3349,31 +3225,28 @@ tty_menu_activate (tty_menu *menu, int *pane, int *selidx, buffers_num_deleted = 1; } -#if 0 - /* We need to save the current echo area message, so that we could - restore it below, before we exit. See the commentary below, - before the call to message_with_string. */ - saved_echo_area_message = Fcurrent_message (); -#endif /* Force update of the current frame, so that the desired and the current matrices are identical. */ - update_frame_with_menu (sf); + update_frame_with_menu (sf, -1, -1); state[0].menu = menu; - mouse_off (); /* FIXME */ state[0].screen_behind = save_and_enable_current_matrix (sf); - /* Turn off the cursor. Otherwise it shows through the menu - panes, which is ugly. */ - tty_hide_cursor (tty); - /* 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); + 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] = ' '; - if ((onepane = menu->count == 1 && menu->submenu[0])) + onepane = menu->count == 1 && menu->submenu[0]; + if (onepane) { menu->width = menu->submenu[0]->width; state[0].menu = menu->submenu[0]; @@ -3393,31 +3266,67 @@ tty_menu_activate (tty_menu *menu, int *pane, int *selidx, leave = 0; while (!leave) { - int mouse_button_count = 3; /* FIXME */ - int input_status; - int min_y = state[0].y, max_y = min_y + state[0].menu->count - 1; + mi_result input_status; + int min_y = state[0].y; + int max_y = min (min_y + state[0].menu->count, FRAME_LINES (sf) - 1) - 1; - if (!mouse_visible) mouse_on (); input_status = read_menu_input (sf, &x, &y, min_y, max_y, &first_time); if (input_status) { - if (input_status == -1) + 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; } - leave = 1; } - if (sf->mouse_moved && input_status != -1) + 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; + int dy = y - state[i].y + first_item; if (0 <= dy && dy < state[i].menu->count) { if (!state[i].menu->submenu[dy]) @@ -3433,11 +3342,10 @@ tty_menu_activate (tty_menu *menu, int *pane, int *selidx, 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) + || state[i].menu->submenu[dy] != state[i + 1].menu) while (i != statecount - 1) { statecount--; - mouse_off (); /* FIXME */ screen_update (sf, state[statecount].screen_behind); state[statecount].screen_behind = NULL; } @@ -3447,10 +3355,9 @@ tty_menu_activate (tty_menu *menu, int *pane, int *selidx, state[i].x, state[i].y, state[i].pane, - faces, x, y, 1); + faces, x, y, first_item, 1); state[statecount].menu = state[i].menu->submenu[dy]; state[statecount].pane = state[i].menu->panenumber[dy]; - mouse_off (); /* FIXME */ state[statecount].screen_behind = save_and_enable_current_matrix (sf); state[statecount].x @@ -3464,7 +3371,12 @@ tty_menu_activate (tty_menu *menu, int *pane, int *selidx, state[statecount - 1].x, state[statecount - 1].y, state[statecount - 1].pane, - faces, x, y, 1); + 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 @@ -3474,60 +3386,25 @@ tty_menu_activate (tty_menu *menu, int *pane, int *selidx, { help_callback (menu_help_message, menu_help_paneno, menu_help_itemno); - tty_hide_cursor (tty); + /* 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; } -#if 0 - /* We are busy-waiting for the mouse to move, so let's be nice - to other Windows applications by releasing our time slice. */ - Sleep (20); /* FIXME */ - - for (b = 0; b < mouse_button_count && !leave; b++) - { - /* Only leave if user both pressed and released the mouse, and in - that order. This avoids popping down the menu pane unless - the user is really done with it. */ - if (mouse_pressed (b, &x, &y)) - { - while (mouse_button_depressed (b, &x, &y)) - Sleep (20); /* FIXME */ - leave = 1; - } - (void) mouse_released (b, &x, &y); - } -#endif + /* 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); } - mouse_off (); /* FIXME */ sf->mouse_moved = 0; - /* FIXME: Since we set the fram's garbaged flag, do we need this - call to screen_update? */ screen_update (sf, state[0].screen_behind); -#if 0 - /* We have a situation here. ScreenUpdate has just restored the - screen contents as it was before we started drawing this menu. - That includes any echo area message that could have been - displayed back then. (In reality, that echo area message will - almost always be the ``keystroke echo'' that echoes the sequence - of menu items chosen by the user.) However, if the menu had some - help messages, then displaying those messages caused Emacs to - forget about the original echo area message. So when - ScreenUpdate restored it, it created a discrepancy between the - actual screen contents and what Emacs internal data structures - know about it. - - To avoid this conflict, we force Emacs to restore the original - echo area message as we found it when we entered this function. - The irony of this is that we then erase the restored message - right away, so the only purpose of restoring it is so that - erasing it works correctly... */ - if (! NILP (saved_echo_area_message)) - message_with_string ("%s", saved_echo_area_message, 0); - message (0); -#endif while (statecount--) free_saved_screen (state[statecount].screen_behind); - tty_show_cursor (tty); /* turn cursor back on */ + 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 @@ -3535,22 +3412,14 @@ tty_menu_activate (tty_menu *menu, int *pane, int *selidx, (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 0 - mouse_clear_clicks (); -#endif if (!kbd_buffer_events_waiting ()) clear_input_pending (); -#if 0 - /* Allow mouse events generation by dos_rawgetc. */ - mouse_preempted--; -#endif - SET_FRAME_GARBAGED (sf); return result; } /* Dispose of a menu. */ -void +static void tty_menu_destroy (tty_menu *menu) { int i; @@ -3568,18 +3437,10 @@ tty_menu_destroy (tty_menu *menu) menu_help_message = prev_menu_help_message = NULL; } -static struct frame *tty_menu_help_frame; - /* 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). - - This cannot be done with generating a HELP_EVENT because - XMenuActivate contains a loop that doesn't let Emacs process - keyboard events. - - FIXME: Do we need this in TTY menus? */ + the menu (currently not used). */ static void tty_menu_help_callback (char const *help_string, int pane, int item) @@ -3588,7 +3449,7 @@ tty_menu_help_callback (char const *help_string, int pane, int item) Lisp_Object pane_name; Lisp_Object menu_object; - first_item = XVECTOR (menu_items)->u.contents; + 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)) @@ -3613,24 +3474,103 @@ tty_pop_down_menu (Lisp_Object arg) 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; + } + } +} + Lisp_Object -tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps, - Lisp_Object title, const char **error_name) +tty_menu_show (struct frame *f, int x, int y, bool for_click, bool keymaps, + Lisp_Object title, bool kbd_navigation, 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; - int dummy_int; - unsigned int dummy_uint; - ptrdiff_t specpdl_count = SPECPDL_INDEX (); + ptrdiff_t specpdl_count; - if (! FRAME_TERMCAP_P (f)) - emacs_abort (); + eassert (FRAME_TERMCAP_P (f)); *error_name = 0; if (menu_items_n_panes == 0) @@ -3652,11 +3592,11 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps, /* Don't GC while we prepare and show the menu, because we give the menu functions pointers to the contents of strings. */ - inhibit_garbage_collection (); + specpdl_count = inhibit_garbage_collection (); /* Adjust coordinates to be root-window-relative. */ - x += f->left_pos; - y += f->top_pos; + item_x = x += f->left_pos; + item_y = y += f->top_pos; /* Create all the necessary panes and their items. */ maxwidth = maxlines = lines = i = 0; @@ -3683,7 +3623,8 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps, { tty_menu_destroy (menu); *error_name = "Can't create pane"; - return Qnil; + entry = Qnil; + goto tty_menu_end; } i += MENU_ITEMS_PANE_LENGTH; @@ -3726,7 +3667,7 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps, if (!NILP (descrip)) { - /* if alloca is fast, use that to make the space, + /* If alloca is fast, use that to make the space, to reduce gc needs. */ item_data = (char *) alloca (maxwidth + SBYTES (descrip) + 1); memcpy (item_data, SSDATA (item_name), SBYTES (item_name)); @@ -3739,13 +3680,13 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps, item_data = SSDATA (item_name); if (lpane == TTYM_FAILURE - || (tty_menu_add_selection (menu, lpane, item_data, - !NILP (enable), help_string) - == 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"; - return Qnil; + entry = Qnil; + goto tty_menu_end; } i += MENU_ITEMS_ITEM_LENGTH; lines++; @@ -3762,18 +3703,18 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps, x = max (x, 1); y = max (y, 1); tty_menu_locate (menu, x, y, &ulx, &uly, &width, &height); - if (ulx+width > dispwidth) + if (ulx + width > dispwidth) { x -= (ulx + width) - dispwidth; ulx = dispwidth - width; } - if (uly+height > dispheight) + if (uly + height > dispheight) { y -= (uly + height) - dispheight; uly = dispheight - height; } - if (FRAME_HAS_MINIBUF_P (f) && uly+height > dispheight - 2) + 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. */ @@ -3795,14 +3736,15 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps, #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. */ + 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); + x += width / 2; + y += 1.5 * height / (maxlines + 2); } #endif @@ -3810,11 +3752,10 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps, record_unwind_protect (tty_pop_down_menu, make_save_ptr (menu)); - tty_menu_help_frame = f; /* FIXME: This seems unused. */ 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); + tty_menu_help_callback, kbd_navigation); entry = pane_prefix = Qnil; switch (status) @@ -3855,11 +3796,22 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps, } 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 (! for_click) @@ -3867,12 +3819,13 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps, break; } - unbind_to (specpdl_count, Qnil); + tty_menu_end: + unbind_to (specpdl_count, Qnil); return entry; } -#endif /* HAVE_MENUS && !MSDOS */ +#endif /* !MSDOS */ #ifndef MSDOS @@ -3881,7 +3834,7 @@ tty_menu_show (struct frame *f, int x, int y, int for_click, int keymaps, ***********************************************************************/ /* 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) @@ -3895,30 +3848,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 */ @@ -4261,9 +4209,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"); @@ -4283,7 +4231,7 @@ 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; + tty->char_ins_del_ok = 1; baud_rate = 19200; } #else /* MSDOS */ @@ -4296,7 +4244,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 */ @@ -4315,12 +4263,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 */ @@ -4330,11 +4278,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 @@ -4455,23 +4399,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 */ @@ -4635,6 +4577,7 @@ bigger, or it may make it blink, or it may do nothing at all. */); encode_terminal_src = NULL; encode_terminal_dst = NULL; +#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"); @@ -4644,4 +4587,5 @@ bigger, or it may make it blink, or it may do nothing at all. */); 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 }