X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/764cb3f9d25bd879a9af4cabf2d08f7db26052da..e8589b1137d5384ff4d1789b53525d25ef510443:/src/keyboard.c diff --git a/src/keyboard.c b/src/keyboard.c index 278f0dbb34..7dd56e9072 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -20,7 +20,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Allow config.h to undefine symbols found here. */ #include -#include "config.h" +#include #include #undef NULL #include "termchar.h" @@ -84,7 +84,7 @@ struct backtrace /* Non-nil disable property on a command means do not execute it; call disabled-command-hook's value instead. */ -Lisp_Object Qdisabled, Vdisabled_command_hook; +Lisp_Object Qdisabled, Qdisabled_command_hook; #define NUM_RECENT_KEYS (100) int recent_keys_index; /* Index for storing next element into recent_keys */ @@ -122,11 +122,17 @@ static int echoing; int immediate_quit; /* Character to recognize as the help char. */ -Lisp_Object help_char; +Lisp_Object Vhelp_char; /* Form to execute when help char is typed. */ Lisp_Object Vhelp_form; +/* Command to run when the help character follows a prefix key. */ +Lisp_Object Vprefix_help_command; + +/* List of items that should move to the end of the menu bar. */ +Lisp_Object Vmenu_bar_final_items; + /* Character that causes a quit. Normally C-g. If we are running on an ordinary terminal, this must be an ordinary @@ -239,6 +245,7 @@ unsigned long last_event_timestamp; Lisp_Object Qself_insert_command; Lisp_Object Qforward_char; Lisp_Object Qbackward_char; +Lisp_Object Qundefined; /* read_key_sequence stores here the command definition of the key sequence that it reads. */ @@ -253,6 +260,9 @@ Lisp_Object Vkeyboard_translate_table; /* Keymap mapping ASCII function key sequences onto their preferred forms. */ extern Lisp_Object Vfunction_key_map; +/* Keymap mapping ASCII function key sequences onto their preferred forms. */ +Lisp_Object Vkey_translation_map; + /* Non-nil means deactivate the mark at end of this command. */ Lisp_Object Vdeactivate_mark; @@ -392,6 +402,7 @@ extern Lisp_Object Qmenu_enable; Lisp_Object recursive_edit_unwind (), command_loop (); Lisp_Object Fthis_command_keys (); +Lisp_Object Qextended_command_history; /* Address (if not 0) of EMACS_TIME to zero out if a SIGIO interrupt happens. */ @@ -420,13 +431,11 @@ int flow_control; #endif #endif -/* If we support X Windows, and won't get an interrupt when input - arrives from the server, poll periodically so we can detect C-g. */ +/* If we support X Windows, turn on the code to poll periodically + to detect C-g. It isn't actually used when doing interrupt input. */ #ifdef HAVE_X_WINDOWS -#ifndef SIGIO #define POLL_FOR_INPUT #endif -#endif /* Global variable declarations. */ @@ -435,7 +444,12 @@ void (*keyboard_init_hook) (); static int read_avail_input (); static void get_input_pending (); +static int readable_events (); static Lisp_Object read_char_menu_prompt (); +static Lisp_Object make_lispy_event (); +static Lisp_Object make_lispy_movement (); +static Lisp_Object modify_event_symbol (); +static Lisp_Object make_lispy_switch_frame (); /* > 0 if we are to echo keystrokes. */ static int echo_keystrokes; @@ -505,7 +519,7 @@ echo_char (c) ptr += name->size; } - if (echoptr == echobuf && EQ (c, help_char)) + if (echoptr == echobuf && EQ (c, Vhelp_char)) { strcpy (ptr, " (Type ? for further options)"); ptr += strlen (ptr); @@ -881,6 +895,11 @@ command_loop_1 () no_redisplay = 0; this_command_key_count = 0; + /* Make sure this hook runs after commands that get errors and + throw to top level. */ + if (!NILP (Vpost_command_hook)) + call1 (Vrun_hooks, Qpost_command_hook); + while (1) { /* Install chars successfully executed in kbd macro. */ @@ -941,7 +960,8 @@ command_loop_1 () #endif /* If it has changed current-menubar from previous value, really recompute the menubar from the value. */ - if (! NILP (Vlucid_menu_bar_dirty_flag)) + if (! NILP (Vlucid_menu_bar_dirty_flag) + && !NILP (Ffboundp (Qrecompute_lucid_menubar))) call0 (Qrecompute_lucid_menubar); #if 0 /* This is done in xdisp.c now. */ @@ -974,7 +994,7 @@ command_loop_1 () #endif /* 0 */ /* Read next key sequence; i gets its length. */ - i = read_key_sequence (keybuf, (sizeof keybuf / sizeof (keybuf[0])), 0); + i = read_key_sequence (keybuf, (sizeof keybuf / sizeof (keybuf[0])), Qnil); ++num_input_keys; @@ -983,6 +1003,14 @@ command_loop_1 () if (i == 0) /* End of file -- happens only in */ return Qnil; /* a kbd macro, at the end. */ + /* -1 means read_key_sequence got a menu that was rejected. + Just loop around and read another command. */ + if (i == -1) + { + cancel_echoing (); + this_command_key_count = 0; + continue; + } last_command_char = keybuf[i - 1]; @@ -1037,7 +1065,7 @@ command_loop_1 () lose = FETCH_CHAR (point); SET_PT (point + 1); if ((dp - ? (XTYPE (DISP_CHAR_VECTOR (dp, lose)) != Lisp_Vector + ? (VECTORP (DISP_CHAR_VECTOR (dp, lose)) && XVECTOR (DISP_CHAR_VECTOR (dp, lose))->size == 1) : (lose >= 0x20 && lose < 0x7f)) && (XFASTINT (XWINDOW (selected_window)->last_modified) @@ -1202,7 +1230,7 @@ input_poll_signal () start_polling () { #ifdef POLL_FOR_INPUT - if (read_socket_hook) + if (read_socket_hook && !interrupt_input) { poll_suppress_count--; if (poll_suppress_count == 0) @@ -1220,7 +1248,7 @@ start_polling () stop_polling () { #ifdef POLL_FOR_INPUT - if (read_socket_hook) + if (read_socket_hook && !interrupt_input) { if (poll_suppress_count == 0) { @@ -1231,6 +1259,27 @@ stop_polling () } #endif } + +/* Set the value of poll_suppress_count to COUNT + and start or stop polling accordingly. */ + +void +set_poll_suppress_count (count) + int count; +{ +#ifdef POLL_FOR_INPUT + if (count == 0 && poll_suppress_count != 0) + { + poll_suppress_count = 1; + start_polling (); + } + else if (count != 0 && poll_suppress_count == 0) + { + stop_polling (); + } + poll_suppress_count = count; +#endif +} /* Applying the control modifier to CHARACTER. */ int @@ -1289,7 +1338,9 @@ static Lisp_Object kbd_buffer_get_event (); If USED_MOUSE_MENU is non-zero, then we set *USED_MOUSE_MENU to 1 if we used a mouse menu to read the input, or zero otherwise. If - USED_MOUSE_MENU is zero, *USED_MOUSE_MENU is left alone. */ + USED_MOUSE_MENU is zero, *USED_MOUSE_MENU is left alone. + + Value is t if we showed a menu and the user rejected it. */ Lisp_Object read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) @@ -1371,6 +1422,9 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) goto reread_first; } + if (commandflag >= 0 && !input_pending && !detect_input_pending ()) + prepare_menu_bars (); + /* Save outer setjmp data, in case called recursively. */ save_getcjmp (save_jump); @@ -1404,6 +1458,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) /* If in middle of key sequence and minibuffer not active, start echoing if enough time elapses. */ if (minibuf_level == 0 && !immediate_echo && this_command_key_count > 0 + && ! noninteractive && echo_keystrokes > 0 && (echo_area_glyphs == 0 || *echo_area_glyphs == 0)) { @@ -1480,7 +1535,12 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) consing going on to make it worthwhile. */ if (!detect_input_pending () && consing_since_gc > gc_cons_threshold / 2) - Fgarbage_collect (); + { + Fgarbage_collect (); + /* prepare_menu_bars isn't safe here, but it should + also be unnecessary. */ + redisplay (); + } } } } @@ -1587,7 +1647,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) num_input_chars++; /* Process the help character specially if enabled */ - if (EQ (c, help_char) && !NILP (Vhelp_form)) + if (EQ (c, Vhelp_char) && !NILP (Vhelp_form)) { Lisp_Object tem0; count = specpdl_ptr - specpdl; @@ -1603,6 +1663,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) c = read_char (0, 0, 0, Qnil, 0); /* Remove the help from the frame */ unbind_to (count, Qnil); + prepare_menu_bars (); redisplay (); if (EQ (c, make_number (040))) { @@ -1640,26 +1701,6 @@ restore_getcjmp (temp) } -/* Low level keyboard/mouse input. - kbd_buffer_store_event places events in kbd_buffer, and - kbd_buffer_get_event retrieves them. - mouse_moved indicates when the mouse has moved again, and - *mouse_position_hook provides the mouse position. */ - -/* Set this for debugging, to have a way to get out */ -int stop_character; - -extern int frame_garbaged; - -/* Return true iff there are any events in the queue that read-char - would return. If this returns false, a read-char would block. */ -static int -readable_events () -{ - return ! EVENT_QUEUES_EMPTY; -} - - /* Restore mouse tracking enablement. See Ftrack_mouse for the only use of this function. */ static Lisp_Object @@ -1677,6 +1718,7 @@ tracking_off (old_value) redisplay. */ if (!readable_events ()) { + prepare_menu_bars (); redisplay_preserve_echo_area (); get_input_pending (&input_pending); } @@ -1702,6 +1744,23 @@ Normally, mouse motion is ignored.") val = Fprogn (args); return unbind_to (count, val); } + +/* Low level keyboard/mouse input. + kbd_buffer_store_event places events in kbd_buffer, and + kbd_buffer_get_event retrieves them. + mouse_moved indicates when the mouse has moved again, and + *mouse_position_hook provides the mouse position. */ + +/* Return true iff there are any events in the queue that read-char + would return. If this returns false, a read-char would block. */ +static int +readable_events () +{ + return ! EVENT_QUEUES_EMPTY; +} + +/* Set this for debugging, to have a way to get out */ +int stop_character; /* Store an event obtained at interrupt level into kbd_buffer, fifo */ @@ -1714,11 +1773,15 @@ kbd_buffer_store_event (event) if (event->kind == ascii_keystroke) { - register int c = XFASTINT (event->code) & 0377; + register int c = event->code & 0377; if (event->modifiers & ctrl_modifier) c = make_ctrl_char (c); + c |= (event->modifiers + & (meta_modifier | alt_modifier + | hyper_modifier | super_modifier)); + if (c == quit_char) { extern SIGTYPE interrupt_signal (); @@ -1729,8 +1792,8 @@ kbd_buffer_store_event (event) get returned to Emacs as an event, the next event read will set Vlast_event_frame again, so this is safe to do. */ { - Lisp_Object focus = - FRAME_FOCUS_FRAME (XFRAME (event->frame_or_window)); + Lisp_Object focus + = FRAME_FOCUS_FRAME (XFRAME (event->frame_or_window)); if (NILP (focus)) internal_last_event_frame = event->frame_or_window; @@ -1750,8 +1813,6 @@ kbd_buffer_store_event (event) sys_suspend (); return; } - - XSET (event->code, Lisp_Int, c); } if (kbd_store_ptr - kbd_buffer == KBD_BUFFER_SIZE) @@ -1764,13 +1825,23 @@ kbd_buffer_store_event (event) if (kbd_fetch_ptr - 1 != kbd_store_ptr) { kbd_store_ptr->kind = event->kind; - kbd_store_ptr->code = event->code; - kbd_store_ptr->part = event->part; - kbd_store_ptr->frame_or_window = event->frame_or_window; - kbd_store_ptr->modifiers = event->modifiers; - kbd_store_ptr->x = event->x; - kbd_store_ptr->y = event->y; - kbd_store_ptr->timestamp = event->timestamp; + if (event->kind == selection_request_event) + { + /* We must not use the ordinary copying code for this case, + since `part' is an enum and copying it might not copy enough + in this case. */ + bcopy (event, kbd_store_ptr, sizeof (*event)); + } + else + { + kbd_store_ptr->code = event->code; + kbd_store_ptr->part = event->part; + kbd_store_ptr->frame_or_window = event->frame_or_window; + kbd_store_ptr->modifiers = event->modifiers; + kbd_store_ptr->x = event->x; + kbd_store_ptr->y = event->y; + kbd_store_ptr->timestamp = event->timestamp; + } (XVECTOR (kbd_buffer_frame_or_window)->contents[kbd_store_ptr - kbd_buffer] = event->frame_or_window); @@ -1778,11 +1849,12 @@ kbd_buffer_store_event (event) kbd_store_ptr++; } } - -static Lisp_Object make_lispy_event (); -static Lisp_Object make_lispy_movement (); -static Lisp_Object modify_event_symbol (); -static Lisp_Object make_lispy_switch_frame (); + +/* Read one event from the event buffer, waiting if necessary. + The value is a Lisp object representing the event. + The value is nil for an event that should be ignored, + or that was handled here. + We always read and discard one event. */ static Lisp_Object kbd_buffer_get_event () @@ -1797,7 +1869,6 @@ kbd_buffer_get_event () return obj; } - retry: /* Wait until there is input available. */ for (;;) { @@ -1850,7 +1921,8 @@ kbd_buffer_get_event () obj = Qnil; /* These two kinds of events get special handling - and don't actually appear to the command loop. */ + and don't actually appear to the command loop. + We return nil for them. */ if (event->kind == selection_request_event) { #ifdef HAVE_X11 @@ -1874,6 +1946,36 @@ kbd_buffer_get_event () abort (); #endif } +#ifdef HAVE_X11 + else if (event->kind == delete_window_event) + { + Lisp_Object tail, frame; + struct frame *f; + + /* If the user destroys the only frame, Emacs should exit. + Count visible frames and iconified frames. */ + for (tail = Vframe_list; CONSP (tail); tail = XCONS (tail)->cdr) + { + frame = XCONS (tail)->car; + if (XTYPE (frame) != Lisp_Frame || EQ (frame, event->frame_or_window)) + continue; + f = XFRAME (frame); + if (FRAME_VISIBLE_P (f) || FRAME_ICONIFIED_P (f)) + break; + } + + if (! CONSP (tail)) + kill (getpid (), SIGHUP); + + Fdelete_frame (event->frame_or_window, Qt); + kbd_fetch_ptr = event + 1; + } +#endif + /* Just discard these, by returning nil. + (They shouldn't be found in the buffer, + but on some machines it appears they do show up.) */ + else if (event->kind == no_event) + kbd_fetch_ptr = event + 1; /* If this event is on a different frame, return a switch-frame this time, and leave the event in the queue for next time. */ @@ -1912,9 +2014,10 @@ kbd_buffer_get_event () } } } + /* Try generating a mouse motion event. */ else if (do_mouse_tracking && mouse_moved) { - FRAME_PTR f; + FRAME_PTR f = 0; Lisp_Object bar_window; enum scroll_bar_part part; Lisp_Object x, y; @@ -1952,13 +2055,6 @@ kbd_buffer_get_event () something for us to read! */ abort (); -#if 0 - /* If something gave back nil as the Lispy event, - it means the event was discarded, so try again. */ - if (NILP (obj)) - goto retry; -#endif - input_pending = readable_events (); #ifdef MULTI_FRAME @@ -1967,8 +2063,9 @@ kbd_buffer_get_event () return (obj); } - -/* Process any events that are not user-visible. */ + +/* Process any events that are not user-visible, + then return, without reading any user-visible events. */ void swallow_events () @@ -2014,11 +2111,104 @@ swallow_events () get_input_pending (&input_pending); } - + /* Caches for modify_event_symbol. */ +static Lisp_Object accent_key_syms; static Lisp_Object func_key_syms; static Lisp_Object mouse_syms; +/* This is a list of keysym codes for special "accent" characters. + It parallels lispy_accent_keys. */ + +static int lispy_accent_codes[] = +{ +#ifdef XK_dead_circumflex + XK_dead_circumflex, +#else + 0, +#endif +#ifdef XK_dead_grave + XK_dead_grave, +#else + 0, +#endif +#ifdef XK_dead_tilde + XK_dead_tilde, +#else + 0, +#endif +#ifdef XK_dead_diaeresis + XK_dead_diaeresis, +#else + 0, +#endif +#ifdef XK_dead_macron + XK_dead_macron, +#else + 0, +#endif +#ifdef XK_dead_degree + XK_dead_degree, +#else + 0, +#endif +#ifdef XK_dead_acute + XK_dead_acute, +#else + 0, +#endif +#ifdef XK_dead_cedilla + XK_dead_cedilla, +#else + 0, +#endif +#ifdef XK_dead_breve + XK_dead_breve, +#else + 0, +#endif +#ifdef XK_dead_ogonek + XK_dead_ogonek, +#else + 0, +#endif +#ifdef XK_dead_caron + XK_dead_caron, +#else + 0, +#endif +#ifdef XK_dead_doubleacute + XK_dead_doubleacute, +#else + 0, +#endif +#ifdef XK_dead_abovedot + XK_dead_abovedot, +#else + 0, +#endif +}; + +/* This is a list of Lisp names for special "accent" characters. + It parallels lispy_accent_codes. */ + +static char *lispy_accent_keys[] = +{ + "dead-circumflex", + "dead-grave", + "dead-tilde", + "dead-diaeresis", + "dead-macron", + "dead-degree", + "dead-acute", + "dead-cedilla", + "dead-breve", + "dead-ogonek", + "dead-caron", + "dead-doubleacute", + "dead-abovedot", +}; + /* You'll notice that this table is arranged to be conveniently indexed by X Windows keysym values. */ static char *lispy_function_keys[] = @@ -2146,14 +2336,15 @@ static Lisp_Object button_down_location; /* Information about the most recent up-going button event: Which button, what location, and what time. */ -static int button_up_button; -static int button_up_x; -static int button_up_y; -static unsigned long button_up_time; - -/* The minimum time between clicks to make a double-click. */ +static int last_mouse_button; +static int last_mouse_x; +static int last_mouse_y; +static unsigned long button_down_time; -int double_click_time; +/* The maximum time between clicks to make a double-click, + or Qnil to disable double-click detection, + or Qt for no time limit. */ +Lisp_Object Vdouble_click_time; /* The number of clicks in this multiple-click. */ @@ -2171,6 +2362,8 @@ static Lisp_Object make_lispy_event (event) struct input_event *event; { + int i; + #ifdef SWITCH_ENUM_BUG switch ((int) event->kind) #else @@ -2180,7 +2373,7 @@ make_lispy_event (event) /* A simple keystroke. */ case ascii_keystroke: { - int c = XFASTINT (event->code); + int c = event->code & 0377; /* Turn ASCII characters into control characters when proper. */ if (event->modifiers & ctrl_modifier) @@ -2192,15 +2385,26 @@ make_lispy_event (event) c |= (event->modifiers & (meta_modifier | alt_modifier | hyper_modifier | super_modifier)); - button_up_time = 0; + button_down_time = 0; return c; } /* A function key. The symbol may need to have modifier prefixes tacked onto it. */ case non_ascii_keystroke: - button_up_time = 0; - return modify_event_symbol (XFASTINT (event->code), event->modifiers, + button_down_time = 0; + + for (i = 0; i < sizeof (lispy_accent_codes) / sizeof (int); i++) + if (event->code == lispy_accent_codes[i]) + return modify_event_symbol (i, + event->modifiers, + Qfunction_key, + lispy_accent_keys, &accent_key_syms, + (sizeof (lispy_accent_keys) + / sizeof (lispy_accent_keys[0]))); + + return modify_event_symbol (event->code - 0xff00, + event->modifiers, Qfunction_key, lispy_function_keys, &func_key_syms, (sizeof (lispy_function_keys) @@ -2212,7 +2416,8 @@ make_lispy_event (event) case mouse_click: case scroll_bar_click: { - int button = XFASTINT (event->code); + int button = event->code; + int is_double; Lisp_Object position; Lisp_Object *start_pos_ptr; Lisp_Object start_pos; @@ -2304,10 +2509,34 @@ make_lispy_event (event) start_pos = *start_pos_ptr; *start_pos_ptr = Qnil; + is_double = (button == last_mouse_button + && XINT (event->x) == last_mouse_x + && XINT (event->y) == last_mouse_y + && button_down_time != 0 + && (EQ (Vdouble_click_time, Qt) + || (INTEGERP (Vdouble_click_time) + && ((int)(event->timestamp - button_down_time) + < XINT (Vdouble_click_time))))); + last_mouse_button = button; + last_mouse_x = XINT (event->x); + last_mouse_y = XINT (event->y); + /* If this is a button press, squirrel away the location, so we can decide later whether it was a click or a drag. */ if (event->modifiers & down_modifier) - *start_pos_ptr = Fcopy_alist (position); + { + if (is_double) + { + double_click_count++; + event->modifiers |= ((double_click_count > 2) + ? triple_modifier + : double_modifier); + } + else + double_click_count = 1; + button_down_time = event->timestamp; + *start_pos_ptr = Fcopy_alist (position); + } /* Now we're releasing a button - check the co-ordinates to see if this was a click or a drag. */ @@ -2337,31 +2566,16 @@ make_lispy_event (event) if (EQ (event->x, XCONS (down)->car) && EQ (event->y, XCONS (down)->cdr)) { - if (button == button_up_button - && XINT (event->x) == button_up_x - && XINT (event->y) == button_up_y - && button_up_time != 0 - && ((int)(event->timestamp - button_up_time) - < double_click_time)) - { - double_click_count++; - event->modifiers |= ((double_click_count > 2) - ? triple_modifier - : double_modifier); - } + if (is_double && double_click_count > 1) + event->modifiers |= ((double_click_count > 2) + ? triple_modifier + : double_modifier); else - { - double_click_count = 1; - event->modifiers |= click_modifier; - } - button_up_button = button; - button_up_x = XINT (event->x); - button_up_y = XINT (event->y); - button_up_time = event->timestamp; + event->modifiers |= click_modifier; } else { - button_up_time = 0; + button_down_time = 0; event->modifiers |= drag_modifier; } } @@ -2451,6 +2665,11 @@ make_lispy_movement (frame, bar_window, part, x, y, time) buffer_posn_from_coords (XWINDOW (window), XINT (x), XINT (y))); } + else if (frame != 0) + { + XSET (window, Lisp_Frame, frame); + posn = Qnil; + } else { window = Qnil; @@ -2621,10 +2840,10 @@ apply_modifiers_uncached (modifiers, base, base_len) if (modifiers & meta_modifier) { *p++ = 'M'; *p++ = '-'; } if (modifiers & shift_modifier) { *p++ = 'S'; *p++ = '-'; } if (modifiers & super_modifier) { *p++ = 's'; *p++ = '-'; } - if (modifiers & down_modifier) { strcpy (p, "down-"); p += 5; } - if (modifiers & drag_modifier) { strcpy (p, "drag-"); p += 5; } if (modifiers & double_modifier) { strcpy (p, "double-"); p += 7; } if (modifiers & triple_modifier) { strcpy (p, "triple-"); p += 7; } + if (modifiers & down_modifier) { strcpy (p, "down-"); p += 5; } + if (modifiers & drag_modifier) { strcpy (p, "drag-"); p += 5; } /* The click modifier is denoted by the absence of other modifiers. */ *p = '\0'; @@ -2951,7 +3170,10 @@ read_avail_input (expected) nread = (*read_socket_hook) (0, buf, KBD_BUFFER_SIZE, expected, expected); else { - unsigned char cbuf[KBD_BUFFER_SIZE]; + /* Using KBD_BUFFER_SIZE - 1 here avoids reading more than + the kbd_buffer can really hold. That may prevent loss + of characters on some systems when input is stuffed at us. */ + unsigned char cbuf[KBD_BUFFER_SIZE - 1]; #ifdef FIONREAD /* Find out how much input is available. */ @@ -2968,7 +3190,7 @@ read_avail_input (expected) if (nread > sizeof cbuf) nread = sizeof cbuf; #else /* no FIONREAD */ -#ifdef USG +#if defined(USG) || defined(DGUX) /* Read some input if available, but don't wait. */ nread = sizeof cbuf; fcntl (fileno (stdin), F_SETFL, O_NDELAY); @@ -2989,6 +3211,8 @@ read_avail_input (expected) if (nread == 0) kill (SIGHUP, 0); #endif + /* This code is wrong, but at least it gets the right results. + Fix it for 19.23. */ /* Retry the read if it is interrupted. */ if (nread >= 0 || ! (errno == EAGAIN || errno == EFAULT @@ -3000,9 +3224,9 @@ read_avail_input (expected) } #ifndef FIONREAD -#ifdef USG +#if defined (USG) || defined (DGUX) fcntl (fileno (stdin), F_SETFL, 0); -#endif /* USG */ +#endif /* USG or DGUX */ #endif /* no FIONREAD */ for (i = 0; i < nread; i++) { @@ -3138,7 +3362,7 @@ menu_bar_items () in the current keymaps, or nil where it is not a prefix. */ Lisp_Object *maps; - Lisp_Object def, tem; + Lisp_Object def, tem, tail; Lisp_Object result; @@ -3177,7 +3401,7 @@ menu_bar_items () result = Qnil; - for (mapno = 0; mapno < nmaps; mapno++) + for (mapno = nmaps - 1; mapno >= 0; mapno--) { if (! NILP (maps[mapno])) def = get_keyelt (access_keymap (maps[mapno], Qmenu_bar, 1, 0)); @@ -3189,6 +3413,15 @@ menu_bar_items () result = menu_bar_one_keymap (def, result); } + for (tail = Vmenu_bar_final_items; CONSP (tail); tail = XCONS (tail)->cdr) + { + Lisp_Object elt; + + elt = Fassq (XCONS (tail)->car, result); + if (!NILP (elt)) + result = Fcons (elt, Fdelq (elt, result)); + } + result = Fnreverse (result); Vinhibit_quit = oquit; return result; @@ -3218,6 +3451,9 @@ menu_bar_one_keymap (keymap, result) result = menu_bar_item (key, item_string, Fcdr (binding), result); } + else if (EQ (binding, Qundefined)) + result = menu_bar_item (key, item_string, + binding, result); } else if (XTYPE (item) == Lisp_Vector) { @@ -3236,6 +3472,9 @@ menu_bar_one_keymap (keymap, result) result = menu_bar_item (key, item_string, Fcdr (binding), result); } + else if (EQ (binding, Qundefined)) + result = menu_bar_item (key, item_string, + binding, result); } } } @@ -3247,9 +3486,17 @@ static Lisp_Object menu_bar_item (key, item_string, def, result) Lisp_Object key, item_string, def, result; { - Lisp_Object tem, elt; + Lisp_Object tem; Lisp_Object enabled; + if (EQ (def, Qundefined)) + { + /* If a map has an explicit nil as definition, + discard any previously made menu bar item. */ + tem = Fassq (key, result); + return Fdelq (tem, result); + } + /* See if this entry is enabled. */ enabled = Qt; @@ -3264,20 +3511,16 @@ menu_bar_item (key, item_string, def, result) /* Add an entry for this key and string if there is none yet. */ - elt = Fassq (key, result); - if (!NILP (enabled) && NILP (elt)) + tem = Fassq (key, result); + if (!NILP (enabled) && NILP (tem)) result = Fcons (Fcons (key, Fcons (item_string, Qnil)), result); return result; } -static int echo_flag; -static int echo_now; - -/* Read a character like read_char but optionally prompt based on maps - in the array MAPS. NMAPS is the length of MAPS. Return nil if we - decided not to read a character, because there are no menu items in - MAPS. +/* Read a character using menus based on maps in the array MAPS. + NMAPS is the length of MAPS. Return nil if there are no menus in the maps. + Return t if we displayed a menu but the user rejected it. PREV_EVENT is the previous input event, or nil if we are reading the first event of a key sequence. @@ -3352,7 +3595,7 @@ read_char_menu_prompt (nmaps, maps, prev_event, used_mouse_menu) value = XCONS (value)->car; } if (NILP (value)) - XSET (value, Lisp_Int, quit_char); + value = Qt; if (used_mouse_menu) *used_mouse_menu = 1; return value; @@ -3493,6 +3736,8 @@ read_char_menu_prompt (nmaps, maps, prev_event, used_mouse_menu) When KEY is not defined in any of the keymaps, if it is an upper case letter and there are bindings for the corresponding lower-case letter, return the bindings for the lower-case letter. + We store 1 in *CASE_CONVERTED in this case. + Otherwise, we don't change *CASE_CONVERTED. If KEY has no bindings in any of the CURRENT maps, NEXT is left unmodified. @@ -3500,10 +3745,11 @@ read_char_menu_prompt (nmaps, maps, prev_event, used_mouse_menu) NEXT may == CURRENT. */ static int -follow_key (key, nmaps, current, defs, next) +follow_key (key, nmaps, current, defs, next, case_converted) Lisp_Object key; Lisp_Object *current, *defs, *next; int nmaps; + int *case_converted; { int i, first_binding; @@ -3555,7 +3801,8 @@ follow_key (key, nmaps, current, defs, next) if (XINT (key) & shift_modifier) XSETINT (key, XINT (key) & ~shift_modifier); else - XSETINT (key, DOWNCASE (XINT (key))); + XSETINT (key, (DOWNCASE (XINT (key) & 0x3ffff) + | (XINT (key) & ~0x3ffff))); first_binding = nmaps; for (i = nmaps - 1; i >= 0; i--) @@ -3569,6 +3816,8 @@ follow_key (key, nmaps, current, defs, next) else defs[i] = Qnil; } + if (first_binding != nmaps) + *case_converted = 1; } /* Given the set of bindings we've found, produce the next set of maps. */ @@ -3583,6 +3832,7 @@ follow_key (key, nmaps, current, defs, next) storing it in KEYBUF, a buffer of size BUFSIZE. Prompt with PROMPT. Return the length of the key sequence stored. + Return -1 if the user rejected a command menu. Echo starting immediately unless `prompt' is 0. @@ -3616,7 +3866,7 @@ static int read_key_sequence (keybuf, bufsize, prompt) Lisp_Object *keybuf; int bufsize; - char *prompt; + Lisp_Object prompt; { int count = specpdl_ptr - specpdl; @@ -3672,11 +3922,28 @@ read_key_sequence (keybuf, bufsize, prompt) int fkey_start = 0, fkey_end = 0; Lisp_Object fkey_map; + /* Likewise, for key_translation_map. */ + int keytran_start = 0, keytran_end = 0; + Lisp_Object keytran_map; + /* If we receive a ``switch-frame'' event in the middle of a key sequence, we put it off for later. While we're reading, we keep the event here. */ Lisp_Object delayed_switch_frame; + /* See the comment below... */ +#if defined (GOBBLE_FIRST_EVENT) Lisp_Object first_event; +#endif + + struct buffer *starting_buffer; + + /* Nonzero if we found the binding for one of the chars + in this key sequence by downcasing it. */ + int case_converted = 0; + + /* Nonzero if we seem to have got the beginning of a binding + in function_key_map. */ + int function_key_possible = 0; int junk; @@ -3684,15 +3951,20 @@ read_key_sequence (keybuf, bufsize, prompt) delayed_switch_frame = Qnil; fkey_map = Vfunction_key_map; + keytran_map = Vkey_translation_map; - /* If there is no function key map, turn off function key scanning. */ + /* If there is no function-key-map, turn off function key scanning. */ if (NILP (Fkeymapp (Vfunction_key_map))) fkey_start = fkey_end = bufsize + 1; + /* If there is no key-translation-map, turn off scanning. */ + if (NILP (Fkeymapp (Vkey_translation_map))) + keytran_start = keytran_end = bufsize + 1; + if (INTERACTIVE) { - if (prompt) - echo_prompt (prompt); + if (!NILP (prompt)) + echo_prompt (XSTRING (prompt)->data); else if (cursor_in_echo_area) /* This doesn't put in a dash if the echo buffer is empty, so you don't always see a dash hanging out in the minibuffer. */ @@ -3705,20 +3977,26 @@ read_key_sequence (keybuf, bufsize, prompt) echo_start = echo_length (); keys_start = this_command_key_count; -#if 0 /* This doesn't quite work, because some of the things - that read_char does cannot safely be bypassed. - It seems too risky to try to make this work right. */ +#if defined (GOBBLE_FIRST_EVENT) + /* This doesn't quite work, because some of the things that read_char + does cannot safely be bypassed. It seems too risky to try to make + this work right. */ + /* Read the first char of the sequence specially, before setting up any keymaps, in case a filter runs and switches buffers on us. */ - first_event = read_char (!prompt, 0, submaps, last_nonmenu_event, + first_event = read_char (NILP (prompt), 0, submaps, last_nonmenu_event, &junk); -#endif +#endif /* GOBBLE_FIRST_EVENT */ /* We jump here when the key sequence has been thoroughly changed, and we need to rescan it starting from the beginning. When we jump here, keybuf[0..mock_input] holds the sequence we should reread. */ replay_sequence: + starting_buffer = current_buffer; + case_converted = 0; + function_key_possible = 0; + /* Build our list of keymaps. If we recognize a function key and replace its escape sequence in keybuf with its symbol, or if the sequence starts with a mouse @@ -3748,9 +4026,7 @@ read_key_sequence (keybuf, bufsize, prompt) if (! NILP (submaps[first_binding])) break; - /* We jump here when a function key substitution has forced us to - reprocess the current key sequence. keybuf[0..mock_input] is the - sequence we want to reread. */ + /* Start from the beginning in keybuf. */ t = 0; /* These are no-ops the first time through, but if we restart, they @@ -3766,7 +4042,17 @@ read_key_sequence (keybuf, bufsize, prompt) || (first_binding >= nmaps && fkey_start < t /* mock input is never part of a function key's sequence. */ - && mock_input <= fkey_start)) + && mock_input <= fkey_start) + || (first_binding >= nmaps + && keytran_start < t + /* mock input is never part of a function key's sequence. */ + && mock_input <= keytran_start) + /* Don't return in the middle of a possible function key sequence, + if the only bindings we found were via case conversion. + Thus, if ESC O a has a function-key-map translation + and ESC o has a binding, don't return after ESC O, + so that we can translate ESC O plus the next character. */ + || (function_key_possible && case_converted)) { Lisp_Object key; int used_mouse_menu = 0; @@ -3802,6 +4088,9 @@ read_key_sequence (keybuf, bufsize, prompt) this_command_key_count = keys_local_start; first_binding = local_first_binding; + /* By default, assume each event is "real". */ + last_real_key_start = t; + /* Does mock_input indicate that we are re-reading a key sequence? */ if (t < mock_input) { @@ -3815,11 +4104,14 @@ read_key_sequence (keybuf, bufsize, prompt) { struct buffer *buf = current_buffer; - last_real_key_start = t; - - key = read_char (!prompt, nmaps, submaps, last_nonmenu_event, + key = read_char (NILP (prompt), nmaps, submaps, last_nonmenu_event, &used_mouse_menu); + /* read_char returns t when it shows a menu and the user rejects it. + Just return -1. */ + if (EQ (key, Qt)) + return -1; + /* read_char returns -1 at the end of a macro. Emacs 18 handles this by returning immediately with a zero, so that's what we'll do. */ @@ -3829,126 +4121,143 @@ read_key_sequence (keybuf, bufsize, prompt) goto done; } + /* If we have a quit that was typed in another frame, and + quit_throw_to_read_char switched buffers, + replay to get the right keymap. */ + if (EQ (key, quit_char) && current_buffer != starting_buffer) + { + keybuf[t++] = key; + mock_input = t; + Vquit_flag = Qnil; + goto replay_sequence; + } + Vquit_flag = Qnil; + } - /* Clicks in non-text areas get prefixed by the symbol - in their CHAR-ADDRESS field. For example, a click on - the mode line is prefixed by the symbol `mode-line'. - - Furthermore, key sequences beginning with mouse clicks - are read using the keymaps of the buffer clicked on, not - the current buffer. So we may have to switch the buffer - here. + /* Clicks in non-text areas get prefixed by the symbol + in their CHAR-ADDRESS field. For example, a click on + the mode line is prefixed by the symbol `mode-line'. + + Furthermore, key sequences beginning with mouse clicks + are read using the keymaps of the buffer clicked on, not + the current buffer. So we may have to switch the buffer + here. + + When we turn one event into two events, we must make sure + that neither of the two looks like the original--so that, + if we replay the events, they won't be expanded again. + If not for this, such reexpansion could happen either here + or when user programs play with this-command-keys. */ + if (EVENT_HAS_PARAMETERS (key)) + { + Lisp_Object kind = EVENT_HEAD_KIND (EVENT_HEAD (key)); - If the event was obtained from the unread_command_events - queue, then don't expand it; we did that the first time - we read it. */ - if (EVENT_HAS_PARAMETERS (key)) + if (EQ (kind, Qmouse_click)) { - Lisp_Object kind = EVENT_HEAD_KIND (EVENT_HEAD (key)); + Lisp_Object window = POSN_WINDOW (EVENT_START (key)); + Lisp_Object posn = POSN_BUFFER_POSN (EVENT_START (key)); - if (EQ (kind, Qmouse_click)) + if (XTYPE (posn) == Lisp_Cons) { - Lisp_Object window = POSN_WINDOW (EVENT_START (key)); - Lisp_Object posn = POSN_BUFFER_POSN (EVENT_START (key)); - - /* Key sequences beginning with mouse clicks are - read using the keymaps in the buffer clicked on, - not the current buffer. If we're at the - beginning of a key sequence, switch buffers. */ - if (t == 0 - && XTYPE (window) == Lisp_Window - && XTYPE (XWINDOW (window)->buffer) == Lisp_Buffer - && XBUFFER (XWINDOW (window)->buffer) != current_buffer) - { - keybuf[t] = key; - mock_input = t + 1; - - /* Arrange to go back to the original buffer once we're - done reading the key sequence. Note that we can't - use save_excursion_{save,restore} here, because they - save point as well as the current buffer; we don't - want to save point, because redisplay may change it, - to accommodate a Fset_window_start or something. We - don't want to do this at the top of the function, - because we may get input from a subprocess which - wants to change the selected window and stuff (say, - emacsclient). */ - record_unwind_protect (Fset_buffer, Fcurrent_buffer ()); - - set_buffer_internal (XBUFFER (XWINDOW (window)->buffer)); - goto replay_sequence; - } - else if (XTYPE (posn) == Lisp_Symbol) - { - if (t + 1 >= bufsize) - error ("key sequence too long"); - keybuf[t] = posn; - keybuf[t+1] = key; - mock_input = t + 2; - - /* Zap the position in key, so we know that we've - expanded it, and don't try to do so again. */ - POSN_BUFFER_POSN (EVENT_START (key)) - = Fcons (posn, Qnil); - - /* If we switched buffers while reading the first event, - replay in case we switched keymaps too. */ - if (buf != current_buffer && t == 0) - goto replay_sequence; - goto replay_key; - } - else if (XTYPE (posn) == Lisp_Cons) - { - /* We're looking at the second event of a - sequence which we expanded before. Set - last_real_key_start appropriately. */ - if (last_real_key_start == t && t > 0) - last_real_key_start = t - 1; - } + /* We're looking at the second event of a + sequence which we expanded before. Set + last_real_key_start appropriately. */ + if (t > 0) + last_real_key_start = t - 1; } - else if (EQ (kind, Qswitch_frame)) + + /* Key sequences beginning with mouse clicks are + read using the keymaps in the buffer clicked on, + not the current buffer. If we're at the + beginning of a key sequence, switch buffers. */ + if (last_real_key_start == 0 + && XTYPE (window) == Lisp_Window + && XTYPE (XWINDOW (window)->buffer) == Lisp_Buffer + && XBUFFER (XWINDOW (window)->buffer) != current_buffer) { - /* If we're at the beginning of a key sequence, go - ahead and return this event. If we're in the - midst of a key sequence, delay it until the end. */ - if (t > 0) - { - delayed_switch_frame = key; - goto replay_key; - } + keybuf[t] = key; + mock_input = t + 1; + + /* Arrange to go back to the original buffer once we're + done reading the key sequence. Note that we can't + use save_excursion_{save,restore} here, because they + save point as well as the current buffer; we don't + want to save point, because redisplay may change it, + to accommodate a Fset_window_start or something. We + don't want to do this at the top of the function, + because we may get input from a subprocess which + wants to change the selected window and stuff (say, + emacsclient). */ + record_unwind_protect (Fset_buffer, Fcurrent_buffer ()); + + set_buffer_internal (XBUFFER (XWINDOW (window)->buffer)); + goto replay_sequence; } - else + else if (XTYPE (posn) == Lisp_Symbol) { - Lisp_Object posn = POSN_BUFFER_POSN (EVENT_START (key)); + /* Expand mode-line and scroll-bar events into two events: + use posn as a fake prefix key. */ - /* Handle menu-bar events: - insert the dummy prefix char `menu-bar'. */ - if (EQ (posn, Qmenu_bar)) - { - if (t + 1 >= bufsize) - error ("key sequence too long"); - /* Run the Lucid hook. */ - call1 (Vrun_hooks, Qactivate_menubar_hook); - /* If it has changed current-menubar from previous value, - really recompute the menubar from the value. */ - if (! NILP (Vlucid_menu_bar_dirty_flag)) - call0 (Qrecompute_lucid_menubar); - keybuf[t] = posn; - keybuf[t+1] = key; - mock_input = t + 2; - goto replay_sequence; - } + if (t + 1 >= bufsize) + error ("key sequence too long"); + keybuf[t] = posn; + keybuf[t+1] = key; + mock_input = t + 2; + + /* Zap the position in key, so we know that we've + expanded it, and don't try to do so again. */ + POSN_BUFFER_POSN (EVENT_START (key)) + = Fcons (posn, Qnil); + goto replay_key; } } - - /* If we switched buffers while reading the first event, - replay in case we switched keymaps too. */ - if (buf != current_buffer && t == 0) + else if (EQ (kind, Qswitch_frame)) { - keybuf[t++] = key; - mock_input = t; - goto replay_sequence; + /* If we're at the beginning of a key sequence, go + ahead and return this event. If we're in the + midst of a key sequence, delay it until the end. */ + if (t > 0) + { + delayed_switch_frame = key; + goto replay_key; + } + } + else + { + Lisp_Object posn = POSN_BUFFER_POSN (EVENT_START (key)); + + /* Handle menu-bar events: + insert the dummy prefix event `menu-bar'. */ + if (EQ (posn, Qmenu_bar)) + { + if (t + 1 >= bufsize) + error ("key sequence too long"); + /* Run the Lucid hook. */ + call1 (Vrun_hooks, Qactivate_menubar_hook); + /* If it has changed current-menubar from previous value, + really recompute the menubar from the value. */ + if (! NILP (Vlucid_menu_bar_dirty_flag)) + call0 (Qrecompute_lucid_menubar); + keybuf[t] = posn; + keybuf[t+1] = key; + + /* Zap the position in key, so we know that we've + expanded it, and don't try to do so again. */ + POSN_BUFFER_POSN (EVENT_START (key)) + = Fcons (posn, Qnil); + + mock_input = t + 2; + goto replay_sequence; + } + else if (XTYPE (posn) == Lisp_Cons) + { + /* We're looking at the second event of a + sequence which we expanded before. Set + last_real_key_start appropriately. */ + if (last_real_key_start == t && t > 0) + last_real_key_start = t - 1; + } } } @@ -3958,7 +4267,8 @@ read_key_sequence (keybuf, bufsize, prompt) nmaps - first_binding, submaps + first_binding, defs + first_binding, - submaps + first_binding) + submaps + first_binding, + &case_converted) + first_binding); /* If KEY wasn't bound, we'll try some fallbacks. */ @@ -3966,85 +4276,100 @@ read_key_sequence (keybuf, bufsize, prompt) { Lisp_Object head = EVENT_HEAD (key); + if (EQ (head, Vhelp_char)) + { + read_key_sequence_cmd = Vprefix_help_command; + keybuf[t++] = key; + last_nonmenu_event = key; + goto done; + } + if (XTYPE (head) == Lisp_Symbol) { Lisp_Object breakdown = parse_modifiers (head); int modifiers = XINT (XCONS (XCONS (breakdown)->cdr)->car); - /* We drop unbound `down-' events altogether. */ - if (modifiers & down_modifier) + /* Attempt to reduce an unbound mouse event to a simpler + event that is bound: + Drags reduce to clicks. + Double-clicks reduce to clicks. + Triple-clicks reduce to double-clicks, then to clicks. + Down-clicks are eliminated. + Double-downs reduce to downs, then are eliminated. + Triple-downs reduce to double-downs, then to downs, + then are eliminated. */ + if (modifiers & (down_modifier | drag_modifier + | double_modifier | triple_modifier)) { - /* Dispose of this event by simply jumping back to - replay_key, to get another event. - - Note that if this event came from mock input, - then just jumping back to replay_key will just - hand it to us again. So we have to wipe out any - mock input. - - We could delete keybuf[t] and shift everything - after that to the left by one spot, but we'd also - have to fix up any variable that points into - keybuf, and shifting isn't really necessary - anyway. - - Adding prefixes for non-textual mouse clicks - creates two characters of mock input, and both - must be thrown away. If we're only looking at - the prefix now, we can just jump back to - replay_key. On the other hand, if we've already - processed the prefix, and now the actual click - itself is giving us trouble, then we've lost the - state of the keymaps we want to backtrack to, and - we need to replay the whole sequence to rebuild - it. - - Beyond that, only function key expansion could - create more than two keys, but that should never - generate mouse events, so it's okay to zero - mock_input in that case too. - - Isn't this just the most wonderful code ever? */ - if (t == last_real_key_start) - { - mock_input = 0; - goto replay_key; - } - else - { - mock_input = last_real_key_start; - goto replay_sequence; - } - } - - /* We turn unbound `drag-' events into `click-' - events, if the click would be bound. */ - else if (modifiers & (drag_modifier | double_modifier - | triple_modifier)) - { - while (modifiers & (drag_modifier | double_modifier - | triple_modifier)) + while (modifiers & (down_modifier | drag_modifier + | double_modifier | triple_modifier)) { Lisp_Object new_head, new_click; if (modifiers & triple_modifier) modifiers ^= (double_modifier | triple_modifier); - else + else if (modifiers & (drag_modifier | double_modifier)) modifiers &= ~(drag_modifier | double_modifier); - new_head = - apply_modifiers (modifiers, XCONS (breakdown)->car); - new_click = - Fcons (new_head, Fcons (EVENT_START (key), Qnil)); + else + { + /* Dispose of this `down' event by simply jumping + back to replay_key, to get another event. + + Note that if this event came from mock input, + then just jumping back to replay_key will just + hand it to us again. So we have to wipe out any + mock input. + + We could delete keybuf[t] and shift everything + after that to the left by one spot, but we'd also + have to fix up any variable that points into + keybuf, and shifting isn't really necessary + anyway. + + Adding prefixes for non-textual mouse clicks + creates two characters of mock input, and both + must be thrown away. If we're only looking at + the prefix now, we can just jump back to + replay_key. On the other hand, if we've already + processed the prefix, and now the actual click + itself is giving us trouble, then we've lost the + state of the keymaps we want to backtrack to, and + we need to replay the whole sequence to rebuild + it. + + Beyond that, only function key expansion could + create more than two keys, but that should never + generate mouse events, so it's okay to zero + mock_input in that case too. + + Isn't this just the most wonderful code ever? */ + if (t == last_real_key_start) + { + mock_input = 0; + goto replay_key; + } + else + { + mock_input = last_real_key_start; + goto replay_sequence; + } + } + + new_head + = apply_modifiers (modifiers, XCONS (breakdown)->car); + new_click + = Fcons (new_head, Fcons (EVENT_START (key), Qnil)); /* Look for a binding for this new key. follow_key promises that it didn't munge submaps the last time we called it, since key was unbound. */ - first_binding = - (follow_key (new_click, - nmaps - local_first_binding, - submaps + local_first_binding, - defs + local_first_binding, - submaps + local_first_binding) - + local_first_binding); + first_binding + = (follow_key (new_click, + nmaps - local_first_binding, + submaps + local_first_binding, + defs + local_first_binding, + submaps + local_first_binding, + &case_converted) + + local_first_binding); /* If that click is bound, go for it. */ if (first_binding < nmaps) @@ -4070,12 +4395,14 @@ read_key_sequence (keybuf, bufsize, prompt) off the end of it. We only want to scan real keyboard input for function key sequences, so if mock_input says that we're re-reading old events, don't examine it. */ - if (first_binding >= nmaps + if ((first_binding >= nmaps || case_converted) && t >= mock_input) { Lisp_Object fkey_next; - /* Scan from fkey_end until we find a bound suffix. */ + /* Continue scan from fkey_end until we find a bound suffix. + If we fail, increment fkey_start + and start fkey_end from there. */ while (fkey_end < t) { Lisp_Object key; @@ -4098,20 +4425,54 @@ read_key_sequence (keybuf, bufsize, prompt) fkey_next = get_keyelt (access_keymap (fkey_next, key, 1, 0)); + /* If the function key map gives a function, not an + array, then call the function with no args and use + its value instead. */ + if (SYMBOLP (fkey_next) && ! NILP (Ffboundp (fkey_next)) + && fkey_end == t) + { + struct gcpro gcpro1, gcpro2, gcpro3; + Lisp_Object tem; + tem = fkey_next; + + GCPRO3 (fkey_map, keytran_map, delayed_switch_frame); + fkey_next = call1 (fkey_next, prompt); + UNGCPRO; + /* If the function returned something invalid, + barf--don't ignore it. + (To ignore it safely, we would need to gcpro a bunch of + other variables.) */ + if (! (VECTORP (fkey_next) || STRINGP (fkey_next))) + error ("Function in function-key-map returns invalid key sequence"); + } + + function_key_possible = ! NILP (fkey_next); + /* If keybuf[fkey_start..fkey_end] is bound in the function key map and it's a suffix of the current sequence (i.e. fkey_end == t), replace it with the binding and restart with fkey_start at the end. */ - if (XTYPE (fkey_next) == Lisp_Vector + if ((VECTORP (fkey_next) || STRINGP (fkey_next)) && fkey_end == t) { - t = fkey_start + XVECTOR (fkey_next)->size; + int len = XFASTINT (Flength (fkey_next)); + + t = fkey_start + len; if (t >= bufsize) error ("key sequence too long"); - bcopy (XVECTOR (fkey_next)->contents, - keybuf + fkey_start, - (t - fkey_start) * sizeof (keybuf[0])); + if (VECTORP (fkey_next)) + bcopy (XVECTOR (fkey_next)->contents, + keybuf + fkey_start, + (t - fkey_start) * sizeof (keybuf[0])); + else if (STRINGP (fkey_next)) + { + int i; + + for (i = 0; i < len; i++) + XFASTINT (keybuf[fkey_start + i]) + = XSTRING (fkey_next)->data[i]; + } mock_input = t; fkey_start = fkey_end = t; @@ -4128,9 +4489,103 @@ read_key_sequence (keybuf, bufsize, prompt) { fkey_end = ++fkey_start; fkey_map = Vfunction_key_map; + function_key_possible = 0; } } } + + /* Look for this sequence in key-translation-map. */ + { + Lisp_Object keytran_next; + + /* Scan from keytran_end until we find a bound suffix. */ + while (keytran_end < t) + { + Lisp_Object key; + + key = keybuf[keytran_end++]; + /* Look up meta-characters by prefixing them + with meta_prefix_char. I hate this. */ + if (XTYPE (key) == Lisp_Int && XINT (key) & meta_modifier) + { + keytran_next + = get_keymap_1 + (get_keyelt + (access_keymap (keytran_map, meta_prefix_char, 1, 0)), + 0, 1); + XFASTINT (key) = XFASTINT (key) & ~meta_modifier; + } + else + keytran_next = keytran_map; + + keytran_next + = get_keyelt (access_keymap (keytran_next, key, 1, 0)); + + /* If the key translation map gives a function, not an + array, then call the function with no args and use + its value instead. */ + if (SYMBOLP (keytran_next) && ! NILP (Ffboundp (keytran_next)) + && keytran_end == t) + { + struct gcpro gcpro1, gcpro2, gcpro3; + Lisp_Object tem; + tem = keytran_next; + + GCPRO3 (keytran_map, keytran_map, delayed_switch_frame); + keytran_next = call1 (keytran_next, prompt); + UNGCPRO; + /* If the function returned something invalid, + barf--don't ignore it. + (To ignore it safely, we would need to gcpro a bunch of + other variables.) */ + if (! (VECTORP (keytran_next) || STRINGP (keytran_next))) + error ("Function in function-key-map returns invalid key sequence"); + } + + /* If keybuf[keytran_start..keytran_end] is bound in the + key translation map and it's a suffix of the current + sequence (i.e. keytran_end == t), replace it with + the binding and restart with keytran_start at the end. */ + if ((VECTORP (keytran_next) || STRINGP (keytran_next)) + && keytran_end == t) + { + int len = XFASTINT (Flength (keytran_next)); + + t = keytran_start + len; + if (t >= bufsize) + error ("key sequence too long"); + + if (VECTORP (keytran_next)) + bcopy (XVECTOR (keytran_next)->contents, + keybuf + keytran_start, + (t - keytran_start) * sizeof (keybuf[0])); + else if (STRINGP (keytran_next)) + { + int i; + + for (i = 0; i < len; i++) + XFASTINT (keybuf[keytran_start + i]) + = XSTRING (keytran_next)->data[i]; + } + + mock_input = t; + keytran_start = keytran_end = t; + keytran_map = Vkey_translation_map; + + goto replay_sequence; + } + + keytran_map = get_keymap_1 (keytran_next, 0, 1); + + /* If we no longer have a bound suffix, try a new positions for + keytran_start. */ + if (NILP (keytran_map)) + { + keytran_end = ++keytran_start; + keytran_map = Vkey_translation_map; + } + } + } } read_key_sequence_cmd = (first_binding < nmaps @@ -4159,6 +4614,8 @@ read_key_sequence (keybuf, bufsize, prompt) return t; } +#if 0 /* This doc string is too long for some compilers. + This commented-out definition serves for DOC. */ DEFUN ("read-key-sequence", Fread_key_sequence, Sread_key_sequence, 1, 2, 0, "Read a sequence of keystrokes and return as a string or vector.\n\ The sequence is sufficient to specify a non-prefix command in the\n\ @@ -4174,7 +4631,7 @@ and `quit-flag' is not set.\n\ If the key sequence starts with a mouse click, then the sequence is read\n\ using the keymaps of the buffer of the window clicked in, not the buffer\n\ of the selected window as normal.\n\ -\n\ +""\n\ `read-key-sequence' drops unbound button-down events, since you normally\n\ only care about the click or drag events which follow them. If a drag\n\ or multi-click event is unbound, but the corresponding click event would\n\ @@ -4192,6 +4649,11 @@ frame-switch event is put off until after the current key sequence.\n\ `read-key-sequence' checks `function-key-map' for function key\n\ sequences, where they wouldn't conflict with ordinary bindings. See\n\ `function-key-map' for more details.") + (prompt, continue_echo) +#endif + +DEFUN ("read-key-sequence", Fread_key_sequence, Sread_key_sequence, 1, 2, 0, + 0) (prompt, continue_echo) Lisp_Object prompt, continue_echo; { @@ -4210,9 +4672,13 @@ sequences, where they wouldn't conflict with ordinary bindings. See\n\ if (NILP (continue_echo)) this_command_key_count = 0; - i = read_key_sequence (keybuf, (sizeof keybuf/sizeof (keybuf[0])), - NILP (prompt) ? 0 : XSTRING (prompt)->data); + i = read_key_sequence (keybuf, (sizeof keybuf/sizeof (keybuf[0])), prompt); + if (i == -1) + { + Vquit_flag = Qt; + QUIT; + } UNGCPRO; return make_event_array (i, keybuf); } @@ -4240,7 +4706,7 @@ Otherwise, that is done only if an arg is read using the minibuffer.") { tem = Fget (cmd, Qdisabled); if (!NILP (tem)) - return call1 (Vrun_hooks, Vdisabled_command_hook); + return call1 (Vrun_hooks, Qdisabled_command_hook); } while (1) @@ -4316,11 +4782,11 @@ DEFUN ("execute-extended-command", Fexecute_extended_command, Sexecute_extended_ /* Prompt with buf, and then read a string, completing from and restricting to the set of all defined commands. Don't provide - any initial input. The last Qnil says not to perform a - peculiar hack on the initial input. */ + any initial input. Save the command read on the extended-command + history list. */ function = Fcompleting_read (build_string (buf), Vobarray, Qcommandp, - Qt, Qnil, Qnil); + Qt, Qnil, Qextended_command_history); /* Set this_command_keys to the concatenation of saved_keys and function, followed by a RET. */ @@ -4514,7 +4980,7 @@ On such systems, Emacs starts a subshell instead of suspending.") with a window system; but suspend should be disabled in that case. */ get_frame_size (&width, &height); if (width != old_width || height != old_height) - change_frame_size (0, height, width, 0, 0); + change_frame_size (selected_frame, height, width, 0, 0); /* Run suspend-resume-hook. */ if (!NILP (Vrun_hooks)) @@ -4551,7 +5017,7 @@ stuff_buffered_input (stuffstring) if (kbd_fetch_ptr == kbd_buffer + KBD_BUFFER_SIZE) kbd_fetch_ptr = kbd_buffer; if (kbd_fetch_ptr->kind == ascii_keystroke) - stuff_char (XINT (kbd_fetch_ptr->code)); + stuff_char (kbd_fetch_ptr->code); kbd_fetch_ptr->kind = no_event; (XVECTOR (kbd_buffer_frame_or_window)->contents[kbd_fetch_ptr - kbd_buffer] @@ -4575,13 +5041,6 @@ set_waiting_for_input (time_to_clear) make it run again now, to avoid timing error. */ if (!NILP (Vquit_flag)) quit_throw_to_read_char (); - - /* If alarm has gone off already, echo now. */ - if (echo_flag) - { - echo (); - echo_flag = 0; - } } clear_waiting_for_input () @@ -4651,7 +5110,10 @@ interrupt_signal () printf ("Auto-save? (y or n) "); fflush (stdout); if (((c = getchar ()) & ~040) == 'Y') - Fdo_auto_save (Qnil, Qnil); + { + Fdo_auto_save (Qt, Qnil); + printf ("Auto-save done\n"); + } while (c != '\n') c = getchar (); #ifdef VMS printf ("Abort (and enter debugger)? (y or n) "); @@ -4706,6 +5168,11 @@ quit_throw_to_read_char () if (poll_suppress_count == 0) abort (); #endif +#ifdef MULTI_FRAME + if (XTYPE (internal_last_event_frame) == Lisp_Frame + && XFRAME (internal_last_event_frame) != selected_frame) + Fhandle_switch_frame (make_lispy_switch_frame (internal_last_event_frame)); +#endif _longjmp (getcjmp, 1); } @@ -4727,7 +5194,11 @@ See also `current-input-mode'.") if (!NILP (quit) && (XTYPE (quit) != Lisp_Int || XINT (quit) < 0 || XINT (quit) > 0400)) - error ("set-input-mode: QUIT must be an ASCII character."); + error ("set-input-mode: QUIT must be an ASCII character"); + +#ifdef POLL_FOR_INPUT + stop_polling (); +#endif reset_sys_modes (); #ifdef SIGIO @@ -4757,6 +5228,11 @@ See also `current-input-mode'.") quit_char = XINT (quit) & (meta_key ? 0377 : 0177); init_sys_modes (); + +#ifdef POLL_FOR_INPUT + poll_suppress_count = 1; + start_polling (); +#endif return Qnil; } @@ -4874,6 +5350,9 @@ struct event_head head_table[] = { syms_of_keyboard () { + Qdisabled_command_hook = intern ("disabled-command-hook"); + staticpro (&Qdisabled_command_hook); + Qself_insert_command = intern ("self-insert-command"); staticpro (&Qself_insert_command); @@ -4886,6 +5365,9 @@ syms_of_keyboard () Qdisabled = intern ("disabled"); staticpro (&Qdisabled); + Qundefined = intern ("undefined"); + staticpro (&Qundefined); + Qpre_command_hook = intern ("pre-command-hook"); staticpro (&Qpre_command_hook); @@ -4964,6 +5446,10 @@ syms_of_keyboard () this_command_keys = Fmake_vector (make_number (40), Qnil); staticpro (&this_command_keys); + Qextended_command_history = intern ("extended-command-history"); + Fset (Qextended_command_history, Qnil); + staticpro (&Qextended_command_history); + kbd_buffer_frame_or_window = Fmake_vector (make_number (KBD_BUFFER_SIZE), Qnil); staticpro (&kbd_buffer_frame_or_window); @@ -4995,10 +5481,6 @@ syms_of_keyboard () defsubr (&Scurrent_input_mode); defsubr (&Sexecute_extended_command); - DEFVAR_LISP ("disabled-command-hook", &Vdisabled_command_hook, - "Value is called instead of any command that is disabled\n\ -\(has a non-nil `disabled' property)."); - DEFVAR_LISP ("last-command-char", &last_command_char, "Last input event that was part of a command."); @@ -5063,12 +5545,12 @@ Polling is needed only when using X windows and SIGIO does not work.\n\ Polling is automatically disabled in all other cases."); polling_period = 2; - DEFVAR_INT ("double-click-time", &double_click_time, + DEFVAR_LISP ("double-click-time", &Vdouble_click_time, "*Maximum time between mouse clicks to make a double-click.\n\ -Measured in milliseconds. Zero means disable double-click recognition;\n\ -a large number means double-clicks have no time limit and are detected\n\ +Measured in milliseconds. nil means disable double-click recognition;\n\ +t means double-clicks have no time limit and are detected\n\ by position only."); - double_click_time = 500; + Vdouble_click_time = make_number (500); DEFVAR_INT ("num-input-keys", &num_input_keys, "*Number of complete keys read from the keyboard so far."); @@ -5081,18 +5563,24 @@ If the last event came from a keyboard macro, this is set to `macro'."); Vlast_event_frame = Qnil; #endif - DEFVAR_LISP ("help-char", &help_char, + DEFVAR_LISP ("help-char", &Vhelp_char, "Character to recognize as meaning Help.\n\ When it is read, do `(eval help-form)', and display result if it's a string.\n\ If the value of `help-form' is nil, this char can be read normally."); - XSET (help_char, Lisp_Int, Ctl ('H')); + XSET (Vhelp_char, Lisp_Int, Ctl ('H')); DEFVAR_LISP ("help-form", &Vhelp_form, - "Form to execute when character help-char is read.\n\ + "Form to execute when character `help-char' is read.\n\ If the form returns a string, that string is displayed.\n\ If `help-form' is nil, the help char is not recognized."); Vhelp_form = Qnil; + DEFVAR_LISP ("prefix-help-command", &Vprefix_help_command, + "Command to run when `help-char' character follows a prefix key.\n\ +This command is used only when there is no actual binding\n\ +for that character after that prefix key."); + Vprefix_help_command = Qnil; + DEFVAR_LISP ("top-level", &Vtop_level, "Form to evaluate when Emacs starts up.\n\ Useful to set before you dump a modified Emacs."); @@ -5104,6 +5592,12 @@ Each character is looked up in this string and the contents used instead.\n\ If string is of length N, character codes N and up are untranslated."); Vkeyboard_translate_table = Qnil; + DEFVAR_LISP ("key-translation-map", &Vkey_translation_map, + "Keymap of key translations that can override keymaps.\n\ +This keymap works like `function-key-map', but comes after that,\n\ +and applies even for keys that have ordinary bindings."); + Vkey_translation_map = Qnil; + DEFVAR_BOOL ("menu-prompting", &menu_prompting, "Non-nil means prompt with menus when appropriate.\n\ This is done when reading from a keymap that has a prompt string,\n\ @@ -5128,7 +5622,7 @@ all input characters will have the control modifier applied to them.\n\ \n\ Note that the character ?\C-@, equivalent to the integer zero, does\n\ not count as a control character; rather, it counts as a character\n\ -with no modifiers; thus, setting extra_keyboard_modifiers to zero\n\ +with no modifiers; thus, setting `extra-keyboard-modifiers' to zero\n\ cancels any modification."); extra_keyboard_modifiers = 0; @@ -5144,12 +5638,20 @@ Buffer modification stores t in this variable."); Vpre_command_hook = Qnil; DEFVAR_LISP ("post-command-hook", &Vpost_command_hook, - "Normal hook run before each command is executed."); + "Normal hook run after each command is executed."); Vpost_command_hook = Qnil; DEFVAR_LISP ("lucid-menu-bar-dirty-flag", &Vlucid_menu_bar_dirty_flag, "t means menu bar, specified Lucid style, needs to be recomputed."); Vlucid_menu_bar_dirty_flag = Qnil; + + DEFVAR_LISP ("menu-bar-final-items", &Vmenu_bar_final_items, + "List of menu bar items to move to the end of the menu bar.\n\ +The elements of the list are event types that may have menu bar bindings."); + Vmenu_bar_final_items = Qnil; + + DEFVAR_BOOL ("track-mouse", &do_mouse_tracking, + "*Non-nil means generate motion events for mouse motion."); } keys_of_keyboard ()