X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/37cd9f309e978b17de5dd314d880446c675181f9..0377370fbbff24ff79669d5d776c4590053b42fc:/src/keyboard.c diff --git a/src/keyboard.c b/src/keyboard.c index 0daf9a01cc..a50322f425 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -1,5 +1,5 @@ /* Keyboard and mouse input; editor command loop. - Copyright (C) 1985,86,87,88,89,93,94,95,96 Free Software Foundation, Inc. + Copyright (C) 1985,86,87,88,89,93,94,95,96,97 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -36,8 +36,10 @@ Boston, MA 02111-1307, USA. */ #include "disptab.h" #include "dispextern.h" #include "keyboard.h" +#include "syntax.h" #include "intervals.h" #include "blockinput.h" +#include "puresize.h" #include #include @@ -126,6 +128,22 @@ Lisp_Object recent_keys; /* A vector, holding the last 100 keystrokes */ Lisp_Object this_command_keys; int this_command_key_count; +/* This vector is used as a buffer to record the events that were actually read + by read_key_sequence. */ +Lisp_Object raw_keybuf; +int raw_keybuf_count; + +#define GROW_RAW_KEYBUF \ +if (raw_keybuf_count == XVECTOR (raw_keybuf)->size) \ + { \ + int newsize = 2 * XVECTOR (raw_keybuf)->size; \ + Lisp_Object new; \ + new = Fmake_vector (make_number (newsize), Qnil); \ + bcopy (XVECTOR (raw_keybuf)->contents, XVECTOR (new)->contents, \ + raw_keybuf_count * sizeof (Lisp_Object)); \ + raw_keybuf = new; \ + } + /* Number of elements of this_command_keys that precede this key sequence. */ int this_single_command_key_start; @@ -144,8 +162,15 @@ static int before_command_restore_flag; extern int minbuf_level; +extern int message_enable_multibyte; + extern struct backtrace *backtrace_list; +/* If non-nil, the function that implements the display of help. + It's called with one argument, the help string to display. */ + +Lisp_Object Vshow_help_function; + /* Nonzero means do menu prompting. */ static int menu_prompting; @@ -159,11 +184,20 @@ static jmp_buf getcjmp; int waiting_for_input; /* True while displaying for echoing. Delays C-g throwing. */ + static int echoing; -/* True means we can start echoing at the next input pause - even though there is something in the echo area. */ -static char *ok_to_echo_at_next_pause; +/* Non-null means we can start echoing at the next input pause even + though there is something in the echo area. */ + +static struct kboard *ok_to_echo_at_next_pause; + +/* The kboard currently echoing, or null for none. Set in echo_now to + the kboard echoing. Reset to 0 in cancel_echoing. If non-null, + and a current echo area message exists, we know that it comes from + echoing. */ + +static struct kboard *echo_kboard; /* Nonzero means disregard local maps for the menu bar. */ static int inhibit_local_menu_bar_menus; @@ -171,6 +205,9 @@ static int inhibit_local_menu_bar_menus; /* Nonzero means C-g should cause immediate error-signal. */ int immediate_quit; +/* The user's ERASE setting. */ +Lisp_Object Vtty_erase_char; + /* Character to recognize as the help char. */ Lisp_Object Vhelp_char; @@ -242,6 +279,14 @@ Lisp_Object last_input_char; /* If not Qnil, a list of objects to be read as subsequent command input. */ Lisp_Object Vunread_command_events; +/* If not Qnil, a list of objects to be read as subsequent command input + including input method processing. */ +Lisp_Object Vunread_input_method_events; + +/* If not Qnil, a list of objects to be read as subsequent command input + but NOT including input method processing. */ +Lisp_Object Vunread_post_input_method_events; + /* If not -1, an event to be read as subsequent command input. */ int unread_command_char; @@ -269,24 +314,27 @@ static int last_non_minibuf_size; static Lisp_Object Vauto_save_timeout; /* Total number of times read_char has returned. */ -int num_input_chars; +int num_input_events; /* Total number of times read_char has returned, outside of macros. */ -int num_nonmacro_input_chars; +int num_nonmacro_input_events; /* Auto-save automatically when this many characters have been typed since the last time. */ static int auto_save_interval; -/* Value of num_nonmacro_input_chars as of last auto save. */ +/* Value of num_nonmacro_input_events as of last auto save. */ int last_auto_save; /* The command being executed by the command loop. Commands may set this, and the value set will be copied into current_kboard->Vlast_command instead of the actual command. */ -Lisp_Object this_command; +Lisp_Object Vthis_command; + +/* This is like Vthis_command, except that commands never set it. */ +Lisp_Object real_this_command; /* The value of point when the last command was executed. */ int last_point_position; @@ -314,6 +362,7 @@ Lisp_Object Qself_insert_command; Lisp_Object Qforward_char; Lisp_Object Qbackward_char; Lisp_Object Qundefined; +Lisp_Object Qtimer_event_handler; /* read_key_sequence stores here the command definition of the key sequence that it reads. */ @@ -332,6 +381,14 @@ extern Lisp_Object Vfunction_key_map; This one takes precedence over ordinary definitions. */ extern Lisp_Object Vkey_translation_map; +/* If non-nil, this implements the current input method. */ +Lisp_Object Vinput_method_function; +Lisp_Object Qinput_method_function; + +/* When we call Vinput_method_function, + this holds the echo area message that was just erased. */ +Lisp_Object Vinput_method_previous_message; + /* Non-nil means deactivate the mark at end of this command. */ Lisp_Object Vdeactivate_mark; @@ -360,6 +417,9 @@ Lisp_Object Vdeferred_action_list; Lisp_Object Vdeferred_action_function; Lisp_Object Qdeferred_action_function; +Lisp_Object Qinput_method_exit_on_first_char; +Lisp_Object Qinput_method_use_echo_area; + /* File in which we write all commands we read. */ FILE *dribble; @@ -413,7 +473,7 @@ static struct input_event *kbd_fetch_ptr; /* Pointer to next place to store character in kbd_buffer. This may be kbd_buffer + KBD_BUFFER_SIZE, meaning that the next character should go in kbd_buffer[0]. */ -static volatile struct input_event *kbd_store_ptr; +static struct input_event * volatile kbd_store_ptr; /* The above pair of variables forms a "queue empty" flag. When we enqueue a non-hook event, we increment kbd_store_ptr. When we @@ -436,18 +496,29 @@ Lisp_Object Qswitch_frame; Lisp_Object Qdelete_frame; Lisp_Object Qiconify_frame; Lisp_Object Qmake_frame_visible; +Lisp_Object Qhelp_echo; /* Symbols to denote kinds of events. */ Lisp_Object Qfunction_key; Lisp_Object Qmouse_click; -Lisp_Object Qtimer_event; +#ifdef WINDOWSNT +Lisp_Object Qmouse_wheel; +Lisp_Object Qlanguage_change; +#endif +Lisp_Object Qdrag_n_drop; /* Lisp_Object Qmouse_movement; - also an event header */ /* Properties of event headers. */ Lisp_Object Qevent_kind; Lisp_Object Qevent_symbol_elements; +/* menu item parts */ +Lisp_Object Qmenu_alias; Lisp_Object Qmenu_enable; +Lisp_Object QCenable, QCvisible, QChelp, QCfilter, QCkeys, QCkey_sequence; +Lisp_Object QCbutton, QCtoggle, QCradio; +extern Lisp_Object Vdefine_key_rebound_commands; +extern Lisp_Object Qmenu_item; /* An event header symbol HEAD may have a property named Qevent_symbol_element_mask, which is of the form (BASE MODIFIERS); @@ -468,13 +539,13 @@ Lisp_Object Qvertical_line; Lisp_Object Qvertical_scroll_bar; Lisp_Object Qmenu_bar; -extern Lisp_Object Qmenu_enable; - Lisp_Object recursive_edit_unwind (), command_loop (); Lisp_Object Fthis_command_keys (); Lisp_Object Qextended_command_history; EMACS_TIME timer_check (); +extern Lisp_Object Vhistory_length; + extern char *x_get_keysym_name (); static void record_menu_key (); @@ -542,6 +613,9 @@ static Lisp_Object make_lispy_movement (); static Lisp_Object modify_event_symbol (); static Lisp_Object make_lispy_switch_frame (); static int parse_solitary_modifier (); +static void save_getcjmp (); +static void restore_getcjmp (); +static Lisp_Object apply_modifiers P_ ((int, Lisp_Object)); /* > 0 if we are to echo keystrokes. */ static int echo_keystrokes; @@ -557,6 +631,7 @@ static int cannot_suspend; so that it serves as a prompt for the next character. Also start echoing. */ +void echo_prompt (str) char *str; { @@ -577,6 +652,7 @@ echo_prompt (str) C can be a character, which is printed prettily ("M-C-x" and all that jazz), or a symbol, whose name is printed. */ +void echo_char (c) Lisp_Object c; { @@ -602,10 +678,11 @@ echo_char (c) else if (SYMBOLP (c)) { struct Lisp_String *name = XSYMBOL (c)->name; - if ((ptr - current_kboard->echobuf) + name->size + 4 > ECHOBUFSIZE) + if ((ptr - current_kboard->echobuf) + STRING_BYTES (name) + 4 + > ECHOBUFSIZE) return; - bcopy (name->data, ptr, name->size); - ptr += name->size; + bcopy (name->data, ptr, STRING_BYTES (name)); + ptr += STRING_BYTES (name); } if (current_kboard->echoptr == current_kboard->echobuf @@ -625,6 +702,7 @@ echo_char (c) /* Temporarily add a dash to the end of the echo string if it's not empty, so that it serves as a mini-prompt for the very next character. */ +void echo_dash () { if (!current_kboard->immediate_echo @@ -649,6 +727,7 @@ echo_dash () /* Display the current echo string, and begin echoing if not already doing so. */ +void echo_now () { if (!current_kboard->immediate_echo) @@ -668,7 +747,9 @@ echo_now () } echoing = 1; - message1_nolog (current_kboard->echobuf); + echo_kboard = current_kboard; + message2_nolog (current_kboard->echobuf, strlen (current_kboard->echobuf), + ! NILP (current_buffer->enable_multibyte_characters)); echoing = 0; if (waiting_for_input && !NILP (Vquit_flag)) @@ -677,12 +758,14 @@ echo_now () /* Turn off echoing, for the start of a new command. */ +void cancel_echoing () { current_kboard->immediate_echo = 0; current_kboard->echoptr = current_kboard->echobuf; current_kboard->echo_after_prompt = -1; ok_to_echo_at_next_pause = 0; + echo_kboard = 0; } /* Return the length of the current echo string. */ @@ -765,13 +848,15 @@ recursive_edit_1 () /* When an auto-save happens, record the "time", and don't do again soon. */ +void record_auto_save () { - last_auto_save = num_nonmacro_input_chars; + last_auto_save = num_nonmacro_input_events; } /* Make an auto save happen as soon as possible at command level. */ +void force_auto_save_soon () { last_auto_save = - auto_save_interval - 1; @@ -910,6 +995,7 @@ cmd_error (data) Vexecuting_macro = Qnil; executing_macro = Qnil; current_kboard->Vprefix_arg = Qnil; + current_kboard->Vlast_prefix_arg = Qnil; cancel_echoing (); /* Avoid unquittable loop if data contains a circular list. */ @@ -931,22 +1017,40 @@ cmd_error (data) return make_number (0); } +/* Take actions on handling an error. DATA is the data that describes + the error. + + CONTEXT is a C-string containing ASCII characters only which + describes the context in which the error happened. If we need to + generalize CONTEXT to allow multibyte characters, make it a Lisp + string. */ + +void cmd_error_internal (data, context) Lisp_Object data; char *context; { Lisp_Object stream; + int kill_emacs_p = 0; Vquit_flag = Qnil; Vinhibit_quit = Qt; - echo_area_glyphs = 0; + clear_message (1, 0); /* If the window system or terminal frame hasn't been initialized yet, or we're not interactive, it's best to dump this message out to stderr and exit. */ - if (! FRAME_MESSAGE_BUF (selected_frame) + if (!selected_frame->glyphs_initialized_p + /* This is the case of the frame dumped with Emacs, when we're + running under a window system. */ + || (!NILP (Vwindow_system) + && !inhibit_window_system + && FRAME_TERMCAP_P (selected_frame)) || noninteractive) - stream = Qexternal_debugging_output; + { + stream = Qexternal_debugging_output; + kill_emacs_p = 1; + } else { Fdiscard_input (); @@ -961,8 +1065,7 @@ cmd_error_internal (data, context) /* If the window system or terminal frame hasn't been initialized yet, or we're in -batch mode, this error should cause Emacs to exit. */ - if (! FRAME_MESSAGE_BUF (selected_frame) - || noninteractive) + if (kill_emacs_p) { Fterpri (stream); Fkill_emacs (make_number (-1)); @@ -982,13 +1085,17 @@ command_loop () { if (command_loop_level > 0 || minibuf_level > 0) { - return internal_catch (Qexit, command_loop_2, Qnil); + Lisp_Object val; + val = internal_catch (Qexit, command_loop_2, Qnil); + executing_macro = Qnil; + return val; } else while (1) { internal_catch (Qtop_level, top_level_1, Qnil); internal_catch (Qtop_level, command_loop_2, Qnil); + executing_macro = Qnil; /* End of file in -batch run causes exit here. */ if (noninteractive) @@ -1084,6 +1191,7 @@ command_loop_1 () #endif current_kboard->Vprefix_arg = Qnil; + current_kboard->Vlast_prefix_arg = Qnil; Vdeactivate_mark = Qnil; waiting_for_input = 0; cancel_echoing (); @@ -1106,16 +1214,22 @@ command_loop_1 () if (!NILP (Vpost_command_idle_hook) && !NILP (Vrun_hooks)) { if (NILP (Vunread_command_events) + && NILP (Vunread_input_method_events) + && NILP (Vunread_post_input_method_events) && NILP (Vexecuting_macro) - && !NILP (sit_for (0, post_command_idle_delay, 0, 1))) + && !NILP (sit_for (0, post_command_idle_delay, 0, 1, 1))) safe_run_hooks (Qpost_command_idle_hook); } /* Do this after running Vpost_command_hook, for consistency. */ - current_kboard->Vlast_command = this_command; + current_kboard->Vlast_command = Vthis_command; + current_kboard->Vreal_last_command = real_this_command; while (1) { + if (! FRAME_LIVE_P (selected_frame)) + Fkill_emacs (Qnil); + /* Make sure the current window's buffer is selected. */ if (XBUFFER (XWINDOW (selected_window)->buffer) != current_buffer) set_buffer_internal (XBUFFER (XWINDOW (selected_window)->buffer)); @@ -1133,7 +1247,8 @@ command_loop_1 () /* If minibuffer on and echo area in use, wait 2 sec and redraw minibuffer. */ - if (minibuf_level && echo_area_glyphs + if (minibuf_level + && !NILP (echo_area_buffer[0]) && EQ (minibuf_window, echo_area_window)) { /* Bind inhibit-quit to t so that C-g gets read in @@ -1143,7 +1258,7 @@ command_loop_1 () Fsit_for (make_number (2), Qnil, Qnil); /* Clear the echo area. */ - message2 (0); + message2 (0, 0, 0); safe_run_hooks (Qecho_area_clear_hook); unbind_to (count, Qnil); @@ -1179,13 +1294,16 @@ command_loop_1 () before_command_key_count = this_command_key_count; before_command_echo_length = echo_length (); - this_command = Qnil; + Vthis_command = Qnil; + real_this_command = Qnil; /* Read next key sequence; i gets its length. */ i = read_key_sequence (keybuf, sizeof keybuf / sizeof keybuf[0], - Qnil, 0, 1); + Qnil, 0, 1, 1); /* A filter may have run while we were reading the input. */ + if (! FRAME_LIVE_P (selected_frame)) + Fkill_emacs (Qnil); if (XBUFFER (XWINDOW (selected_window)->buffer) != current_buffer) set_buffer_internal (XBUFFER (XWINDOW (selected_window)->buffer)); @@ -1215,8 +1333,10 @@ command_loop_1 () update the whole window properly. */ if (!NILP (XWINDOW (selected_window)->force_start)) { + struct buffer *b; XWINDOW (selected_window)->force_start = Qnil; - beg_unchanged = end_unchanged = 0; + b = XBUFFER (XWINDOW (selected_window)->buffer); + BUF_BEG_UNCHANGED (b) = BUF_END_UNCHANGED (b) = 0; } cmd = read_key_sequence_cmd; @@ -1244,13 +1364,14 @@ command_loop_1 () /* Execute the command. */ - this_command = cmd; + Vthis_command = cmd; + real_this_command = cmd; /* Note that the value cell will never directly contain nil if the symbol is a local variable. */ if (!NILP (Vpre_command_hook) && !NILP (Vrun_hooks)) safe_run_hooks (Qpre_command_hook); - - if (NILP (this_command)) + + if (NILP (Vthis_command)) { /* nil means key is undefined. */ bitch_at_user (); @@ -1262,14 +1383,17 @@ command_loop_1 () { if (NILP (current_kboard->Vprefix_arg) && ! no_direct) { + /* In case we jump to directly_done. */ + Vcurrent_prefix_arg = current_kboard->Vprefix_arg; + /* Recognize some common commands in common situations and do them directly. */ - if (EQ (this_command, Qforward_char) && PT < ZV) + if (EQ (Vthis_command, Qforward_char) && PT < ZV) { struct Lisp_Char_Table *dp = window_display_table (XWINDOW (selected_window)); - lose = FETCH_BYTE (PT); - SET_PT (forward_point (1)); + lose = FETCH_CHAR (PT_BYTE); + SET_PT (PT + 1); if ((dp ? (VECTORP (DISP_CHAR_VECTOR (dp, lose)) ? XVECTOR (DISP_CHAR_VECTOR (dp, lose))->size == 1 @@ -1278,7 +1402,7 @@ command_loop_1 () : (lose >= 0x20 && lose < 0x7f)) /* To extract the case of continuation on wide-column characters. */ - && (WIDTH_BY_CHAR_HEAD (FETCH_BYTE (PT)) == 1) + && (WIDTH_BY_CHAR_HEAD (FETCH_BYTE (PT_BYTE)) == 1) && (XFASTINT (XWINDOW (selected_window)->last_modified) >= MODIFF) && (XFASTINT (XWINDOW (selected_window)->last_overlay_modified) @@ -1293,12 +1417,12 @@ command_loop_1 () no_redisplay = direct_output_forward_char (1); goto directly_done; } - else if (EQ (this_command, Qbackward_char) && PT > BEGV) + else if (EQ (Vthis_command, Qbackward_char) && PT > BEGV) { struct Lisp_Char_Table *dp = window_display_table (XWINDOW (selected_window)); - SET_PT (forward_point (-1)); - lose = FETCH_BYTE (PT); + SET_PT (PT - 1); + lose = FETCH_CHAR (PT_BYTE); if ((dp ? (VECTORP (DISP_CHAR_VECTOR (dp, lose)) ? XVECTOR (DISP_CHAR_VECTOR (dp, lose))->size == 1 @@ -1319,13 +1443,12 @@ command_loop_1 () no_redisplay = direct_output_forward_char (-1); goto directly_done; } - else if (EQ (this_command, Qself_insert_command) + else if (EQ (Vthis_command, Qself_insert_command) /* Try this optimization only on ascii keystrokes. */ && INTEGERP (last_command_char)) { - unsigned char c = XINT (last_command_char); + unsigned int c = XINT (last_command_char); int value; - if (NILP (Vexecuting_macro) && !EQ (minibuf_window, selected_window)) { @@ -1336,6 +1459,7 @@ command_loop_1 () } nonundocount++; } + lose = ((XFASTINT (XWINDOW (selected_window)->last_modified) < MODIFF) || (XFASTINT (XWINDOW (selected_window)->last_overlay_modified) @@ -1348,59 +1472,58 @@ command_loop_1 () || detect_input_pending () || !NILP (XWINDOW (selected_window)->column_number_displayed) || !NILP (Vexecuting_macro)); + value = internal_self_insert (c, 0); - if (value) - lose = 1; + if (value == 2) nonundocount = 0; - if (!lose - && (PT == ZV || FETCH_BYTE (PT) == '\n')) - { - struct Lisp_Char_Table *dp - = window_display_table (XWINDOW (selected_window)); - int lose = c; - - if (dp) - { - Lisp_Object obj; - - obj = DISP_CHAR_VECTOR (dp, lose); - if (NILP (obj)) - { - /* Do it only for char codes - that by default display as themselves. */ - if (lose >= 0x20 && lose <= 0x7e) - no_redisplay = direct_output_for_insert (lose); - } - else if (VECTORP (obj) - && XVECTOR (obj)->size == 1 - && (obj = XVECTOR (obj)->contents[0], - INTEGERP (obj)) - /* Insist face not specified in glyph. */ - && (XINT (obj) & ((-1) << 8)) == 0) - no_redisplay - = direct_output_for_insert (XINT (obj)); - } - else - { - if (lose >= 0x20 && lose <= 0x7e) - no_redisplay = direct_output_for_insert (lose); - } - } + /* VALUE == 1 when AFTER-CHANGE functions are + installed which is the case most of the time + because FONT-LOCK installs one. */ + if (!lose && !value) + no_redisplay = direct_output_for_insert (c); goto directly_done; } } /* Here for a command that isn't executed directly */ +#ifdef HAVE_X_WINDOWS + if (display_busy_cursor_p) + { + if (inhibit_busy_cursor != 2) + inhibit_busy_cursor = 0; + if (!inhibit_busy_cursor) + Fx_show_busy_cursor (); + } +#endif + nonundocount = 0; if (NILP (current_kboard->Vprefix_arg)) Fundo_boundary (); - Fcommand_execute (this_command, Qnil, Qnil, Qnil); - + Fcommand_execute (Vthis_command, Qnil, Qnil, Qnil); } directly_done: ; + current_kboard->Vlast_prefix_arg = Vcurrent_prefix_arg; + + /* Note that the value cell will never directly contain nil + if the symbol is a local variable. */ + if (!NILP (Vpost_command_hook) && !NILP (Vrun_hooks)) + safe_run_hooks (Qpost_command_hook); + + if (!NILP (Vdeferred_action_list)) + safe_run_hooks (Qdeferred_action_function); + + if (!NILP (Vpost_command_idle_hook) && !NILP (Vrun_hooks)) + { + if (NILP (Vunread_command_events) + && NILP (Vunread_input_method_events) + && NILP (Vunread_post_input_method_events) + && NILP (Vexecuting_macro) + && !NILP (sit_for (0, post_command_idle_delay, 0, 1, 1))) + safe_run_hooks (Qpost_command_idle_hook); + } /* If there is a prefix argument, 1) We don't want Vlast_command to be ``universal-argument'' @@ -1417,28 +1540,13 @@ command_loop_1 () then the above doesn't apply. */ if (NILP (current_kboard->Vprefix_arg) || CONSP (last_command_char)) { - current_kboard->Vlast_command = this_command; + current_kboard->Vlast_command = Vthis_command; + current_kboard->Vreal_last_command = real_this_command; cancel_echoing (); this_command_key_count = 0; this_single_command_key_start = 0; } - /* Note that the value cell will never directly contain nil - if the symbol is a local variable. */ - if (!NILP (Vpost_command_hook) && !NILP (Vrun_hooks)) - safe_run_hooks (Qpost_command_hook); - - if (!NILP (Vdeferred_action_list)) - safe_run_hooks (Qdeferred_action_function); - - if (!NILP (Vpost_command_idle_hook) && !NILP (Vrun_hooks)) - { - if (NILP (Vunread_command_events) - && NILP (Vexecuting_macro) - && !NILP (sit_for (0, post_command_idle_delay, 0, 1))) - safe_run_hooks (Qpost_command_idle_hook); - } - if (!NILP (current_buffer->mark_active) && !NILP (Vrun_hooks)) { if (!NILP (Vdeactivate_mark) && !NILP (Vtransient_mark_mode)) @@ -1533,6 +1641,7 @@ input_poll_signal (signalnum) /* If we don't have an argument, */ /* Begin signals to poll for input, if they are appropriate. This function is called unconditionally from various places. */ +void start_polling () { #ifdef POLL_FOR_INPUT @@ -1563,6 +1672,7 @@ input_polling_used () /* Turn off polling. */ +void stop_polling () { #ifdef POLL_FOR_INPUT @@ -1602,6 +1712,7 @@ set_poll_suppress_count (count) /* Bind polling_period to a value at least N. But don't decrease it. */ +void bind_polling_period (n) int n; { @@ -1677,7 +1788,10 @@ static jmp_buf wrong_kboard_jmpbuf; MAPS is an array of keymaps; NMAPS is the length of MAPS. PREV_EVENT is the previous input event, or nil if we are reading - the first event of a key sequence. + the first event of a key sequence (or not reading a key sequence). + If PREV_EVENT is t, that is a "magic" value that says + not to run input methods, but in other respects to act as if + not reading a key sequence. If USED_MOUSE_MENU is non-null, then we set *USED_MOUSE_MENU to 1 if we used a mouse menu to read the input, or zero otherwise. If @@ -1693,24 +1807,34 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) Lisp_Object prev_event; int *used_mouse_menu; { - register Lisp_Object c; + Lisp_Object c; int count; jmp_buf local_getcjmp; jmp_buf save_jump; int key_already_recorded = 0; Lisp_Object tem, save; + Lisp_Object previous_echo_area_message; Lisp_Object also_record; + int reread; + struct gcpro gcpro1, gcpro2; + also_record = Qnil; before_command_key_count = this_command_key_count; before_command_echo_length = echo_length (); + c = Qnil; + previous_echo_area_message = Qnil; + + GCPRO2 (c, previous_echo_area_message); retry: - if (CONSP (Vunread_command_events)) + reread = 0; + if (CONSP (Vunread_post_input_method_events)) { - c = XCONS (Vunread_command_events)->car; - Vunread_command_events = XCONS (Vunread_command_events)->cdr; + c = XCONS (Vunread_post_input_method_events)->car; + Vunread_post_input_method_events + = XCONS (Vunread_post_input_method_events)->cdr; /* Undo what read_char_x_menu_prompt did when it unread additional keys returned by Fx_popup_menu. */ @@ -1719,10 +1843,8 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) && NILP (XCONS (c)->cdr)) c = XCONS (c)->car; - if (this_command_key_count == 0) - goto reread_first; - else - goto reread; + reread = 1; + goto reread_first; } if (unread_command_char != -1) @@ -1730,10 +1852,39 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) XSETINT (c, unread_command_char); unread_command_char = -1; - if (this_command_key_count == 0) - goto reread_first; - else - goto reread; + reread = 1; + goto reread_first; + } + + if (CONSP (Vunread_command_events)) + { + c = XCONS (Vunread_command_events)->car; + Vunread_command_events = XCONS (Vunread_command_events)->cdr; + + /* Undo what read_char_x_menu_prompt did when it unread + additional keys returned by Fx_popup_menu. */ + if (CONSP (c) + && (SYMBOLP (XCONS (c)->car) || INTEGERP (XCONS (c)->car)) + && NILP (XCONS (c)->cdr)) + c = XCONS (c)->car; + + reread = 1; + goto reread_for_input_method; + } + + if (CONSP (Vunread_input_method_events)) + { + c = XCONS (Vunread_input_method_events)->car; + Vunread_input_method_events = XCONS (Vunread_input_method_events)->cdr; + + /* Undo what read_char_x_menu_prompt did when it unread + additional keys returned by Fx_popup_menu. */ + if (CONSP (c) + && (SYMBOLP (XCONS (c)->car) || INTEGERP (XCONS (c)->car)) + && NILP (XCONS (c)->cdr)) + c = XCONS (c)->car; + reread = 1; + goto reread_for_input_method; } /* If there is no function key translated before @@ -1761,7 +1912,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) || executing_macro_index >= XFASTINT (Flength (Vexecuting_macro))) { XSETINT (c, -1); - return c; + RETURN_UNGCPRO (c); } c = Faref (Vexecuting_macro, make_number (executing_macro_index)); @@ -1780,18 +1931,43 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) unread_switch_frame = Qnil; /* This event should make it into this_command_keys, and get echoed - again, so we go to reread_first, rather than reread. */ + again, so we do not set `reread'. */ goto reread_first; } - if (commandflag >= 0 && !input_pending - && !detect_input_pending_run_timers (0)) - redisplay (); + /* if redisplay was requested */ + if (commandflag >= 0) + { + /* If there is pending input, process any events which are not + user-visible, such as X selection_request events. */ + if (input_pending + || detect_input_pending_run_timers (0)) + swallow_events (0); /* may clear input_pending */ + + /* Redisplay if no pending input. */ + while (!input_pending) + { + redisplay (); + + if (!input_pending) + /* Normal case: no input arrived during redisplay. */ + break; + + /* Input arrived and pre-empted redisplay. + Process any events which are not user-visible. */ + swallow_events (0); + /* If that cleared input_pending, try again to redisplay. */ + } + } /* Message turns off echoing unless more keystrokes turn it on again. */ - if (echo_area_glyphs && *echo_area_glyphs - && echo_area_glyphs != current_kboard->echobuf - && ok_to_echo_at_next_pause != echo_area_glyphs) + if (/* There is a current message. */ + !NILP (echo_area_buffer[0]) + /* And we're not echoing from this kboard. */ + && echo_kboard != current_kboard + /* And it's either not ok to echo (ok_to_echo == NULL), or the + last char echoed was from a different kboard. */ + && ok_to_echo_at_next_pause != echo_kboard) cancel_echoing (); else /* If already echoing, continue. */ @@ -1850,6 +2026,9 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) *tailp = Fcons (c, Qnil); kb->kbd_queue_has_data = 1; current_kboard = kb; + /* This is going to exit from read_char + so we had better get rid of this frame's stuff. */ + UNGCPRO; longjmp (wrong_kboard_jmpbuf, 1); } } @@ -1862,15 +2041,23 @@ 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 && !current_kboard->immediate_echo + if (minibuf_level == 0 + && !current_kboard->immediate_echo && this_command_key_count > 0 && ! noninteractive && echo_keystrokes > 0 - && (echo_area_glyphs == 0 || *echo_area_glyphs == 0 - || ok_to_echo_at_next_pause == echo_area_glyphs)) + && (/* No message. */ + NILP (echo_area_buffer[0]) + /* Or empty message. */ + || (BUF_BEG (XBUFFER (echo_area_buffer[0])) + == BUF_Z (XBUFFER (echo_area_buffer[0]))) + /* Or already echoing from same kboard. */ + || (echo_kboard && ok_to_echo_at_next_pause == echo_kboard) + /* Or not echoing before and echoing allowed. */ + || (!echo_kboard && ok_to_echo_at_next_pause))) { Lisp_Object tem0; - + /* After a mouse event, start echoing right away. This is because we are probably about to display a menu, and we don't want to delay before doing so. */ @@ -1880,9 +2067,10 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) { save_getcjmp (save_jump); restore_getcjmp (local_getcjmp); - tem0 = sit_for (echo_keystrokes, 0, 1, 1); + tem0 = sit_for (echo_keystrokes, 0, 1, 1, 0); restore_getcjmp (save_jump); - if (EQ (tem0, Qt)) + if (EQ (tem0, Qt) + && ! CONSP (Vunread_command_events)) echo_now (); } } @@ -1891,7 +2079,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) if (commandflag != 0 && auto_save_interval > 0 - && num_nonmacro_input_chars - last_auto_save > max (auto_save_interval, 20) + && num_nonmacro_input_events - last_auto_save > max (auto_save_interval, 20) && !detect_input_pending_run_timers (0)) { Fdo_auto_save (Qnil, Qnil); @@ -1908,6 +2096,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) && !NILP (prev_event) && EVENT_HAS_PARAMETERS (prev_event) && !EQ (XCONS (prev_event)->car, Qmenu_bar) + && !EQ (XCONS (prev_event)->car, Qtoolbar) /* Don't bring up a menu if we already have another event. */ && NILP (Vunread_command_events) && unread_command_char < 0) @@ -1917,7 +2106,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) /* Now that we have read an event, Emacs is not idle. */ timer_stop_idle (); - return c; + RETURN_UNGCPRO (c); } /* Maybe autosave and/or garbage collect due to idleness. */ @@ -1940,7 +2129,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) /* Auto save if enough time goes by without input. */ if (commandflag != 0 - && num_nonmacro_input_chars > last_auto_save + && num_nonmacro_input_events > last_auto_save && INTEGERP (Vauto_save_timeout) && XINT (Vauto_save_timeout) > 0) { @@ -1949,10 +2138,11 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) save_getcjmp (save_jump); restore_getcjmp (local_getcjmp); tem0 = sit_for (delay_level * XFASTINT (Vauto_save_timeout) / 4, - 0, 1, 1); + 0, 1, 1, 0); restore_getcjmp (save_jump); - if (EQ (tem0, Qt)) + if (EQ (tem0, Qt) + && ! CONSP (Vunread_command_events)) { Fdo_auto_save (Qnil, Qnil); @@ -1968,6 +2158,14 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) } } + /* If this has become non-nil here, it has been set by a timer + or sentinel or filter. */ + if (CONSP (Vunread_command_events)) + { + c = XCONS (Vunread_command_events)->car; + Vunread_command_events = XCONS (Vunread_command_events)->cdr; + } + /* Read something from current KBOARD's side queue, if possible. */ if (NILP (c)) @@ -2005,6 +2203,9 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) if (kb->kbd_queue_has_data) { current_kboard = kb; + /* This is going to exit from read_char + so we had better get rid of this frame's stuff. */ + UNGCPRO; longjmp (wrong_kboard_jmpbuf, 1); } } @@ -2042,6 +2243,9 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) if (single_kboard) goto wrong_kboard; current_kboard = kb; + /* This is going to exit from read_char + so we had better get rid of this frame's stuff. */ + UNGCPRO; longjmp (wrong_kboard_jmpbuf, 1); } #endif @@ -2067,10 +2271,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) non_reread: - /* Now that we have read an event, Emacs is not idle-- - unless the event was a timer event (not used now). */ - if (! (CONSP (c) && EQ (XCONS (c)->car, Qtimer_event))) - timer_stop_idle (); + timer_stop_idle (); start_polling (); @@ -2086,12 +2287,10 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) non_reread_1: /* Buffer switch events are only for internal wakeups - so don't show them to the user. */ - if (BUFFERP (c)) - return c; - - if (key_already_recorded) - return c; + so don't show them to the user. + Also, don't record a key if we already did. */ + if (BUFFERP (c) || key_already_recorded) + RETURN_UNGCPRO (c); /* Process special events within read_char and loop around to read another event. */ @@ -2115,25 +2314,19 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) goto retry; } - /* Wipe the echo area. */ - if (echo_area_glyphs) - safe_run_hooks (Qecho_area_clear_hook); - echo_area_glyphs = 0; - /* Handle things that only apply to characters. */ if (INTEGERP (c)) { /* If kbd_buffer_get_event gave us an EOF, return that. */ if (XINT (c) == -1) - return c; - - if (STRINGP (Vkeyboard_translate_table) - && XSTRING (Vkeyboard_translate_table)->size > (unsigned) XFASTINT (c)) - XSETINT (c, XSTRING (Vkeyboard_translate_table)->data[XFASTINT (c)]); - else if ((VECTORP (Vkeyboard_translate_table) - && XVECTOR (Vkeyboard_translate_table)->size > (unsigned) XFASTINT (c)) - || (CHAR_TABLE_P (Vkeyboard_translate_table) - && CHAR_TABLE_ORDINARY_SLOTS > (unsigned) XFASTINT (c))) + RETURN_UNGCPRO (c); + + if ((STRINGP (Vkeyboard_translate_table) + && XSTRING (Vkeyboard_translate_table)->size > (unsigned) XFASTINT (c)) + || (VECTORP (Vkeyboard_translate_table) + && XVECTOR (Vkeyboard_translate_table)->size > (unsigned) XFASTINT (c)) + || (CHAR_TABLE_P (Vkeyboard_translate_table) + && CHAR_TABLE_ORDINARY_SLOTS > (unsigned) XFASTINT (c))) { Lisp_Object d; d = Faref (Vkeyboard_translate_table, c); @@ -2156,7 +2349,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) posn = POSN_BUFFER_POSN (EVENT_START (c)); /* Handle menu-bar events: insert the dummy prefix event `menu-bar'. */ - if (EQ (posn, Qmenu_bar)) + if (EQ (posn, Qmenu_bar) || EQ (posn, Qtoolbar)) { /* Change menu-bar to (menu-bar) as the event "position". */ POSN_BUFFER_POSN (EVENT_START (c)) = Fcons (posn, Qnil); @@ -2167,37 +2360,164 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) } } + /* Store these characters into recent_keys, the dribble file if any, + and the keyboard macro being defined, if any. */ record_char (c); if (! NILP (also_record)) record_char (also_record); + /* Wipe the echo area. + But first, if we are about to use an input method, + save the echo area contents for it to refer to. */ + if (INTEGERP (c) + && ! NILP (Vinput_method_function) + && (unsigned) XINT (c) >= ' ' + && (unsigned) XINT (c) < 127) + { + previous_echo_area_message = Fcurrent_message (); + Vinput_method_previous_message = previous_echo_area_message; + } + + /* Now wipe the echo area. */ + if (!NILP (echo_area_buffer[0])) + safe_run_hooks (Qecho_area_clear_hook); + clear_message (1, 0); + + reread_for_input_method: from_macro: + /* Pass this to the input method, if appropriate. */ + if (INTEGERP (c) + && ! NILP (Vinput_method_function) + /* Don't run the input method within a key sequence, + after the first event of the key sequence. */ + && NILP (prev_event) + && (unsigned) XINT (c) >= ' ' + && (unsigned) XINT (c) < 127) + { + Lisp_Object keys; + int key_count; + struct gcpro gcpro1; + int count = specpdl_ptr - specpdl; + + /* Save the echo status. */ + int saved_immediate_echo = current_kboard->immediate_echo; + struct kboard *saved_ok_to_echo = ok_to_echo_at_next_pause; + int saved_echo_after_prompt = current_kboard->echo_after_prompt; + + if (before_command_restore_flag) + { + this_command_key_count = before_command_key_count_1; + if (this_command_key_count < this_single_command_key_start) + this_single_command_key_start = this_command_key_count; + echo_truncate (before_command_echo_length_1); + before_command_restore_flag = 0; + } + + /* Save the this_command_keys status. */ + key_count = this_command_key_count; + + if (key_count > 0) + keys = Fcopy_sequence (this_command_keys); + else + keys = Qnil; + GCPRO1 (keys); + + /* Clear out this_command_keys. */ + this_command_key_count = 0; + + /* Now wipe the echo area. */ + if (!NILP (echo_area_buffer[0])) + safe_run_hooks (Qecho_area_clear_hook); + clear_message (1, 0); + echo_truncate (0); + + /* If we are not reading a key sequence, + never use the echo area. */ + if (maps == 0) + { + specbind (Qinput_method_use_echo_area, Qt); + } + + /* Call the input method. */ + tem = call1 (Vinput_method_function, c); + + tem = unbind_to (count, tem); + + /* Restore the saved echoing state + and this_command_keys state. */ + this_command_key_count = key_count; + if (key_count > 0) + this_command_keys = keys; + + cancel_echoing (); + ok_to_echo_at_next_pause = saved_ok_to_echo; + current_kboard->echo_after_prompt = saved_echo_after_prompt; + if (saved_immediate_echo) + echo_now (); + + UNGCPRO; + + /* The input method can return no events. */ + if (! CONSP (tem)) + { + /* Bring back the previous message, if any. */ + if (! NILP (previous_echo_area_message)) + message_with_string ("%s", previous_echo_area_message, 0); + goto retry; + } + /* It returned one event or more. */ + c = XCONS (tem)->car; + Vunread_post_input_method_events + = nconc2 (XCONS (tem)->cdr, Vunread_post_input_method_events); + } + reread_first: - before_command_key_count = this_command_key_count; - before_command_echo_length = echo_length (); - /* Don't echo mouse motion events. */ - if (echo_keystrokes - && ! (EVENT_HAS_PARAMETERS (c) - && EQ (EVENT_HEAD_KIND (EVENT_HEAD (c)), Qmouse_movement))) + /* Display help if not echoing. */ + if (CONSP (c) + && EQ (XCAR (c), Qhelp_echo)) { - echo_char (c); - if (! NILP (also_record)) - echo_char (also_record); - /* Once we reread a character, echoing can happen - the next time we pause to read a new one. */ - ok_to_echo_at_next_pause = echo_area_glyphs; + Lisp_Object msg = XCDR (XCDR (c)); + + if (!NILP (Vshow_help_function)) + call1 (Vshow_help_function, msg); + else if (!echoing && !MINI_WINDOW_P (XWINDOW (selected_window))) + { + if (STRINGP (msg)) + message3_nolog (msg, XSTRING (msg)->size, STRING_MULTIBYTE (msg)); + else + message (0); + } + + goto retry; } + + if (this_command_key_count == 0 || ! reread) + { + before_command_key_count = this_command_key_count; + before_command_echo_length = echo_length (); - /* Record this character as part of the current key. */ - add_command_key (c); - if (! NILP (also_record)) - add_command_key (also_record); + /* Don't echo mouse motion events. */ + if (echo_keystrokes + && ! (EVENT_HAS_PARAMETERS (c) + && EQ (EVENT_HEAD_KIND (EVENT_HEAD (c)), Qmouse_movement))) + { + echo_char (c); + if (! NILP (also_record)) + echo_char (also_record); + /* Once we reread a character, echoing can happen + the next time we pause to read a new one. */ + ok_to_echo_at_next_pause = current_kboard; + } + + /* Record this character as part of the current key. */ + add_command_key (c); + if (! NILP (also_record)) + add_command_key (also_record); + } - /* Re-reading in the middle of a command */ - reread: last_input_char = c; - num_input_chars++; + num_input_events++; /* Process the help character specially if enabled */ if (!NILP (Vhelp_form) && help_char_p (c)) @@ -2229,7 +2549,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) } } - return c; + RETURN_UNGCPRO (c); } /* Record a key that came from a mouse menu. @@ -2240,7 +2560,7 @@ record_menu_key (c) Lisp_Object c; { /* Wipe the echo area. */ - echo_area_glyphs = 0; + clear_message (1, 0); record_char (c); @@ -2262,7 +2582,7 @@ record_menu_key (c) /* Re-reading in the middle of a command */ last_input_char = c; - num_input_chars++; + num_input_events++; } /* Return 1 if should recognize C as "the help character". */ @@ -2315,7 +2635,7 @@ record_char (c) { putc ('<', dribble); fwrite (XSYMBOL (dribblee)->name->data, sizeof (char), - XSYMBOL (dribblee)->name->size, + STRING_BYTES (XSYMBOL (dribblee)->name), dribble); putc ('>', dribble); } @@ -2326,7 +2646,7 @@ record_char (c) store_kbd_macro_char (c); - num_nonmacro_input_chars++; + num_nonmacro_input_events++; } Lisp_Object @@ -2346,12 +2666,14 @@ print_help (object) in case get_char is called recursively. See read_process_output. */ +static void save_getcjmp (temp) jmp_buf temp; { bcopy (getcjmp, temp, sizeof getcjmp); } +static void restore_getcjmp (temp) jmp_buf temp; { @@ -2571,7 +2893,7 @@ kbd_buffer_store_event (event) Discard the event if it would fill the last slot. */ if (kbd_fetch_ptr - 1 != kbd_store_ptr) { - volatile struct input_event *sp = kbd_store_ptr; + struct input_event *sp = kbd_store_ptr; sp->kind = event->kind; if (event->kind == selection_request_event) { @@ -2598,6 +2920,28 @@ kbd_buffer_store_event (event) } } +/* Discard any mouse events in the event buffer by setting them to + no_event. */ +void +discard_mouse_events () +{ + struct input_event *sp; + for (sp = kbd_fetch_ptr; sp != kbd_store_ptr; sp++) + { + if (sp == kbd_buffer + KBD_BUFFER_SIZE) + sp = kbd_buffer; + + if (sp->kind == mouse_click +#ifdef WINDOWSNT + || sp->kind == w32_scroll_bar_click +#endif + || sp->kind == scroll_bar_click) + { + sp->kind = no_event; + } + } +} + /* 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, @@ -2665,6 +3009,15 @@ kbd_buffer_get_event (kbp, used_mouse_menu) #endif /* not VMS */ } + if (CONSP (Vunread_command_events)) + { + Lisp_Object first; + first = XCONS (Vunread_command_events)->car; + Vunread_command_events = XCONS (Vunread_command_events)->cdr; + *kbp = current_kboard; + return first; + } + /* At this point, we know that there is a readable event available somewhere. If the event queue is empty, then there must be a mouse movement enabled and available. */ @@ -2755,7 +3108,7 @@ kbd_buffer_get_event (kbp, used_mouse_menu) XSETBUFFER (obj, current_buffer); kbd_fetch_ptr = event + 1; } -#ifdef USE_X_TOOLKIT +#if defined (USE_X_TOOLKIT) || defined (HAVE_NTGUI) else if (event->kind == menu_bar_activate_event) { kbd_fetch_ptr = event + 1; @@ -2763,6 +3116,17 @@ kbd_buffer_get_event (kbp, used_mouse_menu) if (FRAME_LIVE_P (XFRAME (event->frame_or_window))) x_activate_menubar (XFRAME (event->frame_or_window)); } +#endif +#ifdef WINDOWSNT + else if (event->kind == language_change_event) + { + /* Make an event (language-change (FRAME CHARSET LCID)). */ + obj = Fcons (event->modifiers, Qnil); + obj = Fcons (event->code, Qnil); + obj = Fcons (event->frame_or_window, obj); + obj = Fcons (Qlanguage_change, Fcons (obj, Qnil)); + kbd_fetch_ptr = event + 1; + } #endif /* Just discard these, by returning nil. With MULTI_KBOARD, these events are used as placeholders @@ -2770,9 +3134,17 @@ kbd_buffer_get_event (kbp, used_mouse_menu) (They shouldn't otherwise be found in the buffer, but on some machines it appears they do show up even without MULTI_KBOARD.) */ + /* On Windows NT/9X, no_event is used to delete extraneous + mouse events during a popup-menu call. */ else if (event->kind == no_event) kbd_fetch_ptr = event + 1; - + else if (event->kind == HELP_EVENT) + { + /* The car of event->frame_or_window is a frame, + the cdr is the help to display. */ + obj = Fcons (Qhelp_echo, event->frame_or_window); + 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. */ else @@ -2807,8 +3179,10 @@ kbd_buffer_get_event (kbp, used_mouse_menu) we're returning is (menu-bar), though; that indicates the beginning of the menu sequence, and we might as well leave that as the `event with parameters' for this selection. */ - if (event->kind == menu_bar_event + if ((event->kind == menu_bar_event + || event->kind == TOOLBAR_EVENT) && !(CONSP (obj) && EQ (XCONS (obj)->car, Qmenu_bar)) + && !(CONSP (obj) && EQ (XCONS (obj)->car, Qtoolbar)) && used_mouse_menu) *used_mouse_menu = 1; #endif @@ -2932,29 +3306,6 @@ swallow_events (do_display) abort (); #endif } - /* Note that timer_event is currently never used. */ - else if (event->kind == timer_event) - { - Lisp_Object tem, lisp_event; - int was_locked = single_kboard; - - tem = get_keymap_1 (Vspecial_event_map, 0, 0); - tem = get_keyelt (access_keymap (tem, Qtimer_event, 0, 0), - 1); - lisp_event = Fcons (Qtimer_event, - Fcons (Fcdr (event->frame_or_window), Qnil)); - kbd_fetch_ptr = event + 1; - if (kbd_fetch_ptr == kbd_store_ptr) - input_pending = 0; - Fcommand_execute (tem, Qnil, Fvector (1, &lisp_event), Qt); - timers_run++; - if (do_display) - redisplay_preserve_echo_area (); - - /* Resume allowing input from any kboard, if that was true before. */ - if (!was_locked) - any_kboard_state (); - } else break; } @@ -2971,6 +3322,7 @@ static EMACS_TIME timer_idleness_start_time; /* Record the start of when Emacs is idle, for the sake of running idle-time timers. */ +void timer_start_idle () { Lisp_Object timers; @@ -2996,6 +3348,7 @@ timer_start_idle () /* Record that Emacs is no longer idle, so stop running idle-time timers. */ +void timer_stop_idle () { EMACS_SET_SECS_USECS (timer_idleness_start_time, -1, -1); @@ -3025,8 +3378,6 @@ timer_check (do_it_now) EMACS_TIME nexttime; EMACS_TIME now, idleness_now; Lisp_Object timers, idle_timers, chosen_timer; - /* Nonzero if we generate some events. */ - int events_generated = 0; struct gcpro gcpro1, gcpro2, gcpro3; EMACS_SET_SECS (nexttime, -1); @@ -3163,64 +3514,38 @@ timer_check (do_it_now) { if (NILP (vector[0])) { + Lisp_Object tem; + int was_locked = single_kboard; + int count = specpdl_ptr - specpdl; +#ifdef HAVE_WINDOW_SYSTEM + int old_inhibit_busy_cursor = inhibit_busy_cursor; +#endif + /* Mark the timer as triggered to prevent problems if the lisp code fails to reschedule it right. */ vector[0] = Qt; - /* Run the timer or queue a timer event. */ - if (1) - { - Lisp_Object tem, event; - int was_locked = single_kboard; - int count = specpdl_ptr - specpdl; + specbind (Qinhibit_quit, Qt); - specbind (Qinhibit_quit, Qt); +#ifdef HAVE_WINDOW_SYSTEM + inhibit_busy_cursor = 2; +#endif + + call1 (Qtimer_event_handler, chosen_timer); + timers_run++; - tem = get_keymap_1 (Vspecial_event_map, 0, 0); - tem = get_keyelt (access_keymap (tem, Qtimer_event, 0, 0), - 1); - event = Fcons (Qtimer_event, Fcons (chosen_timer, Qnil)); - Fcommand_execute (tem, Qnil, Fvector (1, &event), Qt); - timers_run++; +#ifdef HAVE_WINDOW_SYSTEM + inhibit_busy_cursor = old_inhibit_busy_cursor; +#endif - unbind_to (count, Qnil); + unbind_to (count, Qnil); - /* Resume allowing input from any kboard, if that was true before. */ - if (!was_locked) - any_kboard_state (); + /* Resume allowing input from any kboard, if that was true before. */ + if (!was_locked) + any_kboard_state (); - /* Since we have handled the event, - we don't need to tell the caller to wake up and do it. */ - } -#if 0 - else - { - /* Generate a timer event so the caller will handle it. */ - struct input_event event; - - event.kind = timer_event; - event.modifiers = 0; - event.x = event.y = Qnil; - event.timestamp = triggertime; - /* Store the timer in the frame slot. */ - event.frame_or_window - = Fcons (Fselected_frame (), chosen_timer); - kbd_buffer_store_event (&event); - - last_timer_event = event; - - /* Tell caller to handle this event right away. */ - events_generated = 1; - EMACS_SET_SECS (nexttime, 0); - EMACS_SET_USECS (nexttime, 0); - - /* Don't queue more than one event at once. - When Emacs is ready for another, it will - queue the next one. */ - UNGCPRO; - return nexttime; - } -#endif /* 0 */ + /* Since we have handled the event, + we don't need to tell the caller to wake up and do it. */ } } else @@ -3228,10 +3553,6 @@ timer_check (do_it_now) return the amount of time to wait before it is ripe. */ { UNGCPRO; - /* But if we generated an event, - tell the caller to handle it now. */ - if (events_generated) - return nexttime; return difference; } } @@ -3246,6 +3567,10 @@ timer_check (do_it_now) static Lisp_Object accent_key_syms; static Lisp_Object func_key_syms; static Lisp_Object mouse_syms; +#ifdef WINDOWSNT +static Lisp_Object mouse_wheel_syms; +#endif +static Lisp_Object drag_n_drop_syms; /* This is a list of keysym codes for special "accent" characters. It parallels lispy_accent_keys. */ @@ -3363,15 +3688,15 @@ char *lispy_function_keys[] = 0, 0, /* 0x0E .. 0x0F */ - "shift", /* VK_SHIFT 0x10 */ - "control", /* VK_CONTROL 0x11 */ - "menu", /* VK_MENU 0x12 */ + 0, /* VK_SHIFT 0x10 */ + 0, /* VK_CONTROL 0x11 */ + 0, /* VK_MENU 0x12 */ "pause", /* VK_PAUSE 0x13 */ - "capital", /* VK_CAPITAL 0x14 */ + "capslock", /* VK_CAPITAL 0x14 */ 0, 0, 0, 0, 0, 0, /* 0x15 .. 0x1A */ - 0, /* VK_ESCAPE 0x1B */ + "escape", /* VK_ESCAPE 0x1B */ 0, 0, 0, 0, /* 0x1C .. 0x1F */ @@ -3474,7 +3799,7 @@ char *lispy_function_keys[] = /* * VK_L* & VK_R* - left and right Alt, Ctrl and Shift virtual keys. - * Used only as parameters to GetAsyncKeyState() and GetKeyState(). + * Used only as parameters to GetAsyncKeyState and GetKeyState. * No other API or message will distinguish left and right keys this way. */ /* 0xA0 .. 0xEF */ @@ -3498,11 +3823,10 @@ char *lispy_function_keys[] = "noname", /* VK_NONAME 0xFC */ "pa1", /* VK_PA1 0xFD */ "oem_clear", /* VK_OEM_CLEAR 0xFE */ + 0 /* 0xFF */ }; -#else - -#define FUNCTION_KEY_OFFSET 0xff00 +#else /* not HAVE_NTGUI */ #ifdef XK_kana_A static char *lispy_kana_keys[] = @@ -3539,41 +3863,31 @@ static char *lispy_kana_keys[] = }; #endif /* XK_kana_A */ -/* You'll notice that this table is arranged to be conveniently +#define FUNCTION_KEY_OFFSET 0xff00 + +/* You'll notice that this table is arranged to be conveniently indexed by X Windows keysym values. */ static char *lispy_function_keys[] = { /* X Keysym value */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0xff00 */ - "backspace", - "tab", - "linefeed", - "clear", - 0, - "return", - 0, 0, - 0, 0, 0, /* 0xff10 */ - "pause", - 0, 0, 0, 0, 0, 0, 0, - "escape", + 0, 0, 0, 0, 0, 0, 0, 0, /* 0xff00...0f */ + "backspace", "tab", "linefeed", "clear", + 0, "return", 0, 0, + 0, 0, 0, "pause", /* 0xff10...1f */ + 0, 0, 0, 0, 0, 0, 0, "escape", 0, 0, 0, 0, - 0, "kanji", "muhenkan", - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xff20...2f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xff30...3f */ + 0, "kanji", "muhenkan", "henkan", /* 0xff20...2f */ + "romaji", "hiragana", "katakana", "hiragana-katakana", + "zenkaku", "hankaku", "zenkaku-hankaku", "touroku", + "massyo", "kana-lock", "kana-shift", "eisu-shift", + "eisu-toggle", /* 0xff30...3f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xff40...4f */ - "home", /* 0xff50 */ /* IsCursorKey */ - "left", - "up", - "right", - "down", - "prior", - "next", - "end", - "begin", - 0, /* 0xff59 */ - 0, 0, 0, 0, 0, 0, + "home", "left", "up", "right", /* 0xff50 */ /* IsCursorKey */ + "down", "prior", "next", "end", + "begin", 0, 0, 0, 0, 0, 0, 0, "select", /* 0xff60 */ /* IsMiscFunctionKey */ "print", "execute", @@ -3587,9 +3901,9 @@ static char *lispy_function_keys[] = "help", "break", /* 0xff6b */ - 0, 0, 0, 0, 0, 0, 0, 0, "backtab", 0, - 0, /* 0xff76 */ - 0, 0, 0, 0, 0, 0, 0, 0, "kp-numlock", /* 0xff7f */ + 0, 0, 0, 0, + 0, 0, 0, 0, "backtab", 0, 0, 0, /* 0xff70... */ + 0, 0, 0, 0, 0, 0, 0, "kp-numlock", /* 0xff78... */ "kp-space", /* 0xff80 */ /* IsKeypadKey */ 0, 0, 0, 0, 0, 0, 0, 0, "kp-tab", /* 0xff89 */ @@ -3634,23 +3948,76 @@ static char *lispy_function_keys[] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xfff0 */ 0, 0, 0, 0, 0, 0, 0, "delete" - }; + }; -#endif /* HAVE_NTGUI */ +/* ISO 9995 Function and Modifier Keys; the first byte is 0xFE. */ +#define ISO_FUNCTION_KEY_OFFSET 0xfe00 + +static char *iso_lispy_function_keys[] = + { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0xfe00 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0xfe08 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0xfe10 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0xfe18 */ + "iso-lefttab", /* 0xfe20 */ + "iso-move-line-up", "iso-move-line-down", + "iso-partial-line-up", "iso-partial-line-down", + "iso-partial-space-left", "iso-partial-space-right", + "iso-set-margin-left", "iso-set-margin-right", /* 0xffe27, 28 */ + "iso-release-margin-left", "iso-release-margin-right", + "iso-release-both-margins", + "iso-fast-cursor-left", "iso-fast-cursor-right", + "iso-fast-cursor-up", "iso-fast-cursor-down", + "iso-continuous-underline", "iso-discontinuous-underline", /* 0xfe30, 31 */ + "iso-emphasize", "iso-center-object", "iso-enter", /* ... 0xfe34 */ + }; + +#endif /* not HAVE_NTGUI */ static char *lispy_mouse_names[] = { "mouse-1", "mouse-2", "mouse-3", "mouse-4", "mouse-5" }; +#ifdef WINDOWSNT +/* mouse-wheel events are generated by the wheel on devices such as + the MS Intellimouse. The wheel sits in between the left and right + mouse buttons, and is typically used to scroll or zoom the window + underneath the pointer. mouse-wheel events specify the object on + which they operate, and a delta corresponding to the amount and + direction that the wheel is rotated. Clicking the mouse-wheel + generates a mouse-2 event. */ +static char *lispy_mouse_wheel_names[] = +{ + "mouse-wheel" +}; + +#endif /* WINDOWSNT */ + +/* drag-n-drop events are generated when a set of selected files are + dragged from another application and dropped onto an Emacs window. */ +static char *lispy_drag_n_drop_names[] = +{ + "drag-n-drop" +}; + /* Scroll bar parts. */ Lisp_Object Qabove_handle, Qhandle, Qbelow_handle; -Lisp_Object Qup, Qdown; +Lisp_Object Qup, Qdown, Qbottom, Qend_scroll; +Lisp_Object Qtop; /* An array of scroll bar parts, indexed by an enum scroll_bar_part value. */ Lisp_Object *scroll_bar_parts[] = { &Qabove_handle, &Qhandle, &Qbelow_handle, - &Qup, &Qdown, + &Qup, &Qdown, &Qtop, &Qbottom, &Qend_scroll +}; + +/* User signal events. */ +Lisp_Object Qusr1_signal, Qusr2_signal; + +Lisp_Object *lispy_user_signals[] = +{ + &Qusr1_signal, &Qusr2_signal }; @@ -3716,6 +4083,10 @@ make_lispy_event (event) c |= (event->modifiers & (meta_modifier | alt_modifier | hyper_modifier | super_modifier)); + /* Distinguish Shift-SPC from SPC. */ + if ((event->code & 0377) == 040 + && event->modifiers & shift_modifier) + c |= shift_modifier; button_down_time = 0; XSETFASTINT (lispy_c, c); return lispy_c; @@ -3760,23 +4131,31 @@ make_lispy_event (event) / sizeof (lispy_kana_keys[0]))); #endif /* XK_kana_A */ - return modify_event_symbol (event->code - FUNCTION_KEY_OFFSET, - event->modifiers, - Qfunction_key, Qnil, - lispy_function_keys, &func_key_syms, - (sizeof (lispy_function_keys) - / sizeof (lispy_function_keys[0]))); - break; - - /* Note that timer_event is currently never used. */ - case timer_event: - return Fcons (Qtimer_event, Fcons (Fcdr (event->frame_or_window), Qnil)); +#ifdef ISO_FUNCTION_KEY_OFFSET + if (event->code < FUNCTION_KEY_OFFSET + && event->code >= ISO_FUNCTION_KEY_OFFSET) + return modify_event_symbol (event->code - ISO_FUNCTION_KEY_OFFSET, + event->modifiers, + Qfunction_key, Qnil, + iso_lispy_function_keys, &func_key_syms, + (sizeof (iso_lispy_function_keys) + / sizeof (iso_lispy_function_keys[0]))); + else +#endif + return modify_event_symbol (event->code - FUNCTION_KEY_OFFSET, + event->modifiers, + Qfunction_key, Qnil, + lispy_function_keys, &func_key_syms, + (sizeof (lispy_function_keys) + / sizeof (lispy_function_keys[0]))); #ifdef HAVE_MOUSE /* A mouse click. Figure out where it is, decide whether it's a press, click or drag, and build the appropriate structure. */ case mouse_click: +#ifndef USE_TOOLKIT_SCROLL_BARS case scroll_bar_click: +#endif { int button = event->code; int is_double; @@ -3794,6 +4173,7 @@ make_lispy_event (event) FRAME_PTR f = XFRAME (event->frame_or_window); Lisp_Object window; Lisp_Object posn; + Lisp_Object string_info = Qnil; int row, column; /* Ignore mouse events that were made on frame that @@ -3801,8 +4181,13 @@ make_lispy_event (event) if (! FRAME_LIVE_P (f)) return Qnil; - pixel_to_glyph_coords (f, XINT (event->x), XINT (event->y), - &column, &row, NULL, 1); + /* EVENT->x and EVENT->y are frame-relative pixel + coordinates at this place. Under old redisplay, COLUMN + and ROW are set to frame relative glyph coordinates + which are then used to determine whether this click is + in a menu (non-toolkit version). */ + pixel_to_glyph_coords (f, XINT (event->x), XINT (event->y), + &column, &row, NULL, 1); #ifndef USE_X_TOOLKIT /* In the non-toolkit version, clicks on the menu bar @@ -3827,6 +4212,7 @@ make_lispy_event (event) return Qnil; #endif + /* Find the menu bar item under `column'. */ item = Qnil; items = FRAME_MENU_BAR_ITEMS (f); for (i = 0; i < XVECTOR (items)->size; i += 4) @@ -3844,6 +4230,8 @@ make_lispy_event (event) } } + /* ELisp manual 2.4b says (x y) are window relative but + code says they are frame-relative. */ position = Fcons (event->frame_or_window, Fcons (Qmenu_bar, @@ -3855,7 +4243,10 @@ make_lispy_event (event) } #endif /* not USE_X_TOOLKIT */ - window = window_from_coordinates (f, column, row, &part); + /* Set `window' to the window under frame pixel coordinates + event->x/event->y. */ + window = window_from_coordinates (f, XINT (event->x), + XINT (event->y), &part, 0); if (!WINDOWP (window)) { @@ -3864,21 +4255,36 @@ make_lispy_event (event) } else { - int pixcolumn, pixrow; - column -= WINDOW_LEFT_MARGIN (XWINDOW (window)); - row -= XINT (XWINDOW (window)->top); - glyph_to_pixel_coords (f, column, row, &pixcolumn, &pixrow); - XSETINT (event->x, pixcolumn); - XSETINT (event->y, pixrow); - - if (part == 1) - posn = Qmode_line; + /* It's a click in window window at frame coordinates + event->x/ event->y. */ + struct window *w = XWINDOW (window); + + /* Get window relative coordinates. Original code + `rounded' this to glyph boundaries. */ + int wx = FRAME_TO_WINDOW_PIXEL_X (w, XINT (event->x)); + int wy = FRAME_TO_WINDOW_PIXEL_Y (w, XINT (event->y)); + + /* Set event coordinates to window-relative coordinates + for constructing the Lisp event below. */ + XSETINT (event->x, wx); + XSETINT (event->y, wy); + + if (part == 1 || part == 3) + { + /* Mode line or top line. Look for a string under + the mouse that may have a `local-map' property. */ + Lisp_Object string; + int charpos; + + posn = part == 1 ? Qmode_line : Qtop_line; + string = mode_line_string (w, wx, wy, part == 1, &charpos); + if (STRINGP (string)) + string_info = Fcons (string, make_number (charpos)); + } else if (part == 2) posn = Qvertical_line; else - XSETINT (posn, - buffer_posn_from_coords (XWINDOW (window), - column, row)); + XSETINT (posn, buffer_posn_from_coords (w, &wx, &wy)); } position @@ -3886,10 +4292,14 @@ make_lispy_event (event) Fcons (posn, Fcons (Fcons (event->x, event->y), Fcons (make_number (event->timestamp), - Qnil)))); + (NILP (string_info) + ? Qnil + : Fcons (string_info, Qnil)))))); } +#ifndef USE_TOOLKIT_SCROLL_BARS else { + /* It's a scrollbar click. */ Lisp_Object window; Lisp_Object portion_whole; Lisp_Object part; @@ -3905,6 +4315,7 @@ make_lispy_event (event) Fcons (make_number (event->timestamp), Fcons (part, Qnil))))); } +#endif /* not USE_TOOLKIT_SCROLL_BARS */ start_pos_ptr = &XVECTOR (button_down_location)->contents[button]; @@ -4016,6 +4427,55 @@ make_lispy_event (event) } } +#if USE_TOOLKIT_SCROLL_BARS + + /* We don't have down and up events if using toolkit scroll bars, + so make this always a click event. Store in the `part' of + the Lisp event a symbol which maps to the following actions: + + `above_handle' page up + `below_handle' page down + `up' line up + `down' line down + `top' top of buffer + `bottom' bottom of buffer + `handle' thumb has been dragged. + `end-scroll' end of interaction with scroll bar + + The incoming input_event contains in its `part' member an + index of type `enum scroll_bar_part' which we can use as an + index in scroll_bar_parts to get the appropriate symbol. */ + + case scroll_bar_click: + { + Lisp_Object position, head, window, portion_whole, part; + + window = event->frame_or_window; + portion_whole = Fcons (event->x, event->y); + part = *scroll_bar_parts[(int) event->part]; + + position + = Fcons (window, + Fcons (Qvertical_scroll_bar, + Fcons (portion_whole, + Fcons (make_number (event->timestamp), + Fcons (part, Qnil))))); + + /* Always treat scroll bar events as clicks. */ + event->modifiers |= click_modifier; + + /* Get the symbol we should use for the mouse click. */ + head = modify_event_symbol (event->code, + event->modifiers, + Qmouse_click, Qnil, + lispy_mouse_names, &mouse_syms, + (sizeof (lispy_mouse_names) + / sizeof (lispy_mouse_names[0]))); + return Fcons (head, Fcons (position, Qnil)); + } + +#endif /* USE_TOOLKIT_SCROLL_BARS */ + #ifdef WINDOWSNT case w32_scroll_bar_click: { @@ -4037,12 +4497,12 @@ make_lispy_event (event) portion_whole = Fcons (event->x, event->y); part = *scroll_bar_parts[(int) event->part]; - position = - Fcons (window, - Fcons (Qvertical_scroll_bar, - Fcons (portion_whole, - Fcons (make_number (event->timestamp), - Fcons (part, Qnil))))); + position + = Fcons (window, + Fcons (Qvertical_scroll_bar, + Fcons (portion_whole, + Fcons (make_number (event->timestamp), + Fcons (part, Qnil))))); } /* Always treat W32 scroll bar events as clicks. */ @@ -4063,8 +4523,148 @@ make_lispy_event (event) Qnil)); } } -#endif + case mouse_wheel: + { + int part; + FRAME_PTR f = XFRAME (event->frame_or_window); + Lisp_Object window; + Lisp_Object posn; + Lisp_Object head, position; + int row, column; + + /* Ignore mouse events that were made on frame that + have been deleted. */ + if (! FRAME_LIVE_P (f)) + return Qnil; + pixel_to_glyph_coords (f, XINT (event->x), XINT (event->y), + &column, &row, NULL, 1); + window = window_from_coordinates (f, column, row, &part, 0); + + if (!WINDOWP (window)) + { + window = event->frame_or_window; + posn = Qnil; + } + else + { + int pixcolumn, pixrow; + column -= XINT (XWINDOW (window)->left); + row -= XINT (XWINDOW (window)->top); + glyph_to_pixel_coords (f, column, row, &pixcolumn, &pixrow); + XSETINT (event->x, pixcolumn); + XSETINT (event->y, pixrow); + + if (part == 1) + posn = Qmode_line; + else if (part == 2) + posn = Qvertical_line; + else if (part == 3) + posn = Qtop_line; + else + XSETINT (posn, + buffer_posn_from_coords (XWINDOW (window), + column, row)); + } + + { + Lisp_Object head, position; + + position + = Fcons (window, + Fcons (posn, + Fcons (Fcons (event->x, event->y), + Fcons (make_number (event->timestamp), + Qnil)))); + + head = modify_event_symbol (0, event->modifiers, + Qmouse_wheel, Qnil, + lispy_mouse_wheel_names, + &mouse_wheel_syms, 1); + return Fcons (head, + Fcons (position, + Fcons (make_number (event->code), + Qnil))); + } + } +#endif /* WINDOWSNT */ + + case drag_n_drop: + { + int part; + FRAME_PTR f; + Lisp_Object window; + Lisp_Object posn; + Lisp_Object head, position; + Lisp_Object files; + int row, column; + + /* The frame_or_window field should be a cons of the frame in + which the event occurred and a list of the filenames + dropped. */ + if (! CONSP (event->frame_or_window)) + abort (); + + f = XFRAME (XCONS (event->frame_or_window)->car); + files = XCONS (event->frame_or_window)->cdr; + /* Ignore mouse events that were made on frames that + have been deleted. */ + if (! FRAME_LIVE_P (f)) + return Qnil; + pixel_to_glyph_coords (f, XINT (event->x), XINT (event->y), + &column, &row, NULL, 1); + window = window_from_coordinates (f, column, row, &part, 0); + + if (!WINDOWP (window)) + { + window = XCONS (event->frame_or_window)->car; + posn = Qnil; + } + else + { + /* It's an event in window `window' at frame coordinates + event->x/ event->y. */ + struct window *w = XWINDOW (window); + + /* Get window relative coordinates. */ + int wx = FRAME_TO_WINDOW_PIXEL_X (w, XINT (event->x)); + int wy = FRAME_TO_WINDOW_PIXEL_Y (w, XINT (event->y)); + + /* Set event coordinates to window-relative coordinates + for constructing the Lisp event below. */ + XSETINT (event->x, wx); + XSETINT (event->y, wy); + + if (part == 1) + posn = Qmode_line; + else if (part == 2) + posn = Qvertical_line; + else if (part == 3) + posn = Qtop_line; + else + XSETINT (posn, buffer_posn_from_coords (w, &wx, &wy)); + } + + { + Lisp_Object head, position; + + position + = Fcons (window, + Fcons (posn, + Fcons (Fcons (event->x, event->y), + Fcons (make_number (event->timestamp), + Qnil)))); + + head = modify_event_symbol (0, event->modifiers, + Qdrag_n_drop, Qnil, + lispy_drag_n_drop_names, + &drag_n_drop_syms, 1); + return Fcons (head, + Fcons (position, + Fcons (files, + Qnil))); + } + } #endif /* HAVE_MOUSE */ #if defined (USE_X_TOOLKIT) || defined (HAVE_NTGUI) @@ -4075,6 +4675,21 @@ make_lispy_event (event) return XCONS (event->frame_or_window)->cdr; #endif + case TOOLBAR_EVENT: + { + Lisp_Object key; + if (!CONSP (event->frame_or_window)) + abort (); + key = XCDR (event->frame_or_window); + if (SYMBOLP (key)) + key = apply_modifiers (event->modifiers, key); + return key; + } + + case user_signal: + /* A user signal. */ + return *lispy_user_signals[event->code]; + /* The 'kind' field of the event is something we don't recognize. */ default: abort (); @@ -4113,34 +4728,34 @@ make_lispy_movement (frame, bar_window, part, x, y, time) int area; Lisp_Object window; Lisp_Object posn; - int column, row; if (frame) - { - /* It's in a frame; which window on that frame? */ - pixel_to_glyph_coords (frame, XINT (x), XINT (y), &column, &row, - NULL, 1); - window = window_from_coordinates (frame, column, row, &area); - } + /* It's in a frame; which window on that frame? */ + window = window_from_coordinates (frame, XINT (x), XINT (y), &area, 0); else window = Qnil; if (WINDOWP (window)) { - int pixcolumn, pixrow; - column -= WINDOW_LEFT_MARGIN (XWINDOW (window)); - row -= XINT (XWINDOW (window)->top); - glyph_to_pixel_coords (frame, column, row, &pixcolumn, &pixrow); - XSETINT (x, pixcolumn); - XSETINT (y, pixrow); - + struct window *w = XWINDOW (window); + int hpos, vpos; + int wx, wy; + int pos; + + /* Get window relative coordinates. */ + wx = FRAME_TO_WINDOW_PIXEL_X (w, XINT (x)); + wy = FRAME_TO_WINDOW_PIXEL_Y (w, XINT (y)); + XSETINT (x, wx); + XSETINT (y, wy); + if (area == 1) posn = Qmode_line; else if (area == 2) posn = Qvertical_line; + else if (area == 3) + posn = Qtop_line; else - XSETINT (posn, - buffer_posn_from_coords (XWINDOW (window), column, row)); + XSETINT (posn, buffer_posn_from_coords (w, &wx, &wy)); } else if (frame != 0) { @@ -4199,7 +4814,7 @@ parse_modifiers_uncached (symbol, modifier_end) modifiers = 0; name = XSYMBOL (symbol)->name; - for (i = 0; i+2 <= name->size; ) + for (i = 0; i+2 <= STRING_BYTES (name); ) { int this_mod_end = 0; int this_mod = 0; @@ -4246,7 +4861,8 @@ parse_modifiers_uncached (symbol, modifier_end) /* Check there is a dash after the modifier, so that it really is a modifier. */ - if (this_mod_end >= name->size || name->data[this_mod_end] != '-') + if (this_mod_end >= STRING_BYTES (name) + || name->data[this_mod_end] != '-') break; /* This modifier is real; look for another. */ @@ -4257,7 +4873,7 @@ parse_modifiers_uncached (symbol, modifier_end) /* Should we include the `click' modifier? */ if (! (modifiers & (down_modifier | drag_modifier | double_modifier | triple_modifier)) - && i + 7 == name->size + && i + 7 == STRING_BYTES (name) && strncmp (name->data + i, "mouse-", 6) == 0 && ('0' <= name->data[i + 6] && name->data[i + 6] <= '9')) modifiers |= click_modifier; @@ -4272,16 +4888,16 @@ parse_modifiers_uncached (symbol, modifier_end) prepended to the string BASE[0..BASE_LEN-1]. This doesn't use any caches. */ static Lisp_Object -apply_modifiers_uncached (modifiers, base, base_len) +apply_modifiers_uncached (modifiers, base, base_len, base_len_byte) int modifiers; char *base; - int base_len; + int base_len, base_len_byte; { /* Since BASE could contain nulls, we can't use intern here; we have to use Fintern, which expects a genuine Lisp_String, and keeps a reference to it. */ - char *new_mods = - (char *) alloca (sizeof ("A-C-H-M-S-s-down-drag-double-triple-")); + char *new_mods + = (char *) alloca (sizeof ("A-C-H-M-S-s-down-drag-double-triple-")); int mod_len; { @@ -4312,9 +4928,10 @@ apply_modifiers_uncached (modifiers, base, base_len) { Lisp_Object new_name; - new_name = make_uninit_string (mod_len + base_len); + new_name = make_uninit_multibyte_string (mod_len + base_len, + mod_len + base_len_byte); bcopy (new_mods, XSTRING (new_name)->data, mod_len); - bcopy (base, XSTRING (new_name)->data + mod_len, base_len); + bcopy (base, XSTRING (new_name)->data + mod_len, base_len_byte); return Fintern (new_name, Qnil); } @@ -4356,7 +4973,7 @@ lispy_modifier_list (modifiers) SYMBOL's Qevent_symbol_element_mask property, and maintains the Qevent_symbol_elements property. */ -static Lisp_Object +Lisp_Object parse_modifiers (symbol) Lisp_Object symbol; { @@ -4373,7 +4990,7 @@ parse_modifiers (symbol) Lisp_Object mask; unmodified = Fintern (make_string (XSYMBOL (symbol)->name->data + end, - XSYMBOL (symbol)->name->size - end), + STRING_BYTES (XSYMBOL (symbol)->name) - end), Qnil); if (modifiers & ~(((EMACS_INT)1 << VALBITS) - 1)) @@ -4427,7 +5044,8 @@ apply_modifiers (modifiers, base) /* We have to create the symbol ourselves. */ new_symbol = apply_modifiers_uncached (modifiers, XSYMBOL (base)->name->data, - XSYMBOL (base)->name->size); + XSYMBOL (base)->name->size, + STRING_BYTES (XSYMBOL (base)->name)); /* Add the new symbol to the base's cache. */ entry = Fcons (index, new_symbol); @@ -4675,11 +5293,11 @@ parse_solitary_modifier (symbol) switch (name->data[0]) { #define SINGLE_LETTER_MOD(BIT) \ - if (name->size == 1) \ + if (STRING_BYTES (name) == 1) \ return BIT; #define MULTI_LETTER_MOD(BIT, NAME, LEN) \ - if (LEN == name->size \ + if (LEN == STRING_BYTES (name) \ && ! strncmp (name->data, NAME, LEN)) \ return BIT; @@ -4793,7 +5411,7 @@ get_input_pending (addr, do_timers_now) /* Interface to read_avail_input, blocking SIGIO or SIGALRM if necessary. */ -int +void gobble_input (expected) int expected; { @@ -4825,6 +5443,7 @@ gobble_input (expected) /* Put a buffer_switch_event in the buffer so that read_key_sequence will notice the new current buffer. */ +void record_asynch_buffer_change () { struct input_event event; @@ -4942,6 +5561,13 @@ read_avail_input (expected) #else nread = read (input_fd, cbuf, n_to_read); #endif + /* POSIX infers that processes which are not in the session leader's + process group won't get SIGHUP's at logout time. BSDI adheres to + this part standard and returns -1 from read (0) with errno==EIO + when the control tty is taken away. + Jeffrey Honig says this is generally safe. */ + if (nread == -1 && errno == EIO) + kill (0, SIGHUP); #if defined (AIX) && (! defined (aix386) && defined (_BSD)) /* The kernel sometimes fails to deliver SIGHUP for ptys. This looks incorrect, but it isn't, because _BSD causes @@ -5183,7 +5809,7 @@ menu_bar_items (old) for (mapno = nmaps - 1; mapno >= 0; mapno--) { if (! NILP (maps[mapno])) - def = get_keyelt (access_keymap (maps[mapno], Qmenu_bar, 1, 0)); + def = get_keyelt (access_keymap (maps[mapno], Qmenu_bar, 1, 0), 0); else def = Qnil; @@ -5247,149 +5873,885 @@ menu_bar_items (old) /* Scan one map KEYMAP, accumulating any menu items it defines in menu_bar_items_vector. */ +static Lisp_Object menu_bar_one_keymap_changed_items; + static void menu_bar_one_keymap (keymap) Lisp_Object keymap; { - Lisp_Object tail, item, key, binding, item_string, table; + Lisp_Object tail, item, table; + + menu_bar_one_keymap_changed_items = Qnil; + + /* Loop over all keymap entries that have menu strings. */ + for (tail = keymap; CONSP (tail); tail = XCONS (tail)->cdr) + { + item = XCONS (tail)->car; + if (CONSP (item)) + menu_bar_item (XCONS (item)->car, XCONS (item)->cdr); + else if (VECTORP (item)) + { + /* Loop over the char values represented in the vector. */ + int len = XVECTOR (item)->size; + int c; + for (c = 0; c < len; c++) + { + Lisp_Object character; + XSETFASTINT (character, c); + menu_bar_item (character, XVECTOR (item)->contents[c]); + } + } + } +} + +/* Add one item to menu_bar_items_vector, for KEY, ITEM_STRING and DEF. + If there's already an item for KEY, add this DEF to it. */ + +Lisp_Object item_properties; + +static void +menu_bar_item (key, item) + Lisp_Object key, item; +{ + struct gcpro gcpro1; + int i; + Lisp_Object tem; + + if (EQ (item, Qundefined)) + { + /* If a map has an explicit `undefined' as definition, + discard any previously made menu bar item. */ + + for (i = 0; i < menu_bar_items_index; i += 4) + if (EQ (key, XVECTOR (menu_bar_items_vector)->contents[i])) + { + if (menu_bar_items_index > i + 4) + bcopy (&XVECTOR (menu_bar_items_vector)->contents[i + 4], + &XVECTOR (menu_bar_items_vector)->contents[i], + (menu_bar_items_index - i - 4) * sizeof (Lisp_Object)); + menu_bar_items_index -= 4; + return; + } + + /* If there's no definition for this key yet, + just ignore `undefined'. */ + return; + } + + GCPRO1 (key); /* Is this necessary? */ + i = parse_menu_item (item, 0, 1); + UNGCPRO; + if (!i) + return; + + /* If this keymap has already contributed to this KEY, + don't contribute to it a second time. */ + tem = Fmemq (key, menu_bar_one_keymap_changed_items); + if (!NILP (tem)) + return; + + menu_bar_one_keymap_changed_items + = Fcons (key, menu_bar_one_keymap_changed_items); + + item = XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF]; + + /* Find any existing item for this KEY. */ + for (i = 0; i < menu_bar_items_index; i += 4) + if (EQ (key, XVECTOR (menu_bar_items_vector)->contents[i])) + break; + + /* If we did not find this KEY, add it at the end. */ + if (i == menu_bar_items_index) + { + /* If vector is too small, get a bigger one. */ + if (i + 4 > XVECTOR (menu_bar_items_vector)->size) + { + Lisp_Object tem; + int newsize = 2 * i; + tem = Fmake_vector (make_number (2 * i), Qnil); + bcopy (XVECTOR (menu_bar_items_vector)->contents, + XVECTOR (tem)->contents, i * sizeof (Lisp_Object)); + menu_bar_items_vector = tem; + } + + /* Add this item. */ + XVECTOR (menu_bar_items_vector)->contents[i++] = key; + XVECTOR (menu_bar_items_vector)->contents[i++] + = XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME]; + XVECTOR (menu_bar_items_vector)->contents[i++] = Fcons (item, Qnil); + XVECTOR (menu_bar_items_vector)->contents[i++] = make_number (0); + menu_bar_items_index = i; + } + /* We did find an item for this KEY. Add ITEM to its list of maps. */ + else + { + Lisp_Object old; + old = XVECTOR (menu_bar_items_vector)->contents[i + 2]; + XVECTOR (menu_bar_items_vector)->contents[i + 2] = Fcons (item, old); + } +} + + /* This is used as the handler when calling menu_item_eval_property. */ +static Lisp_Object +menu_item_eval_property_1 (arg) + Lisp_Object arg; +{ + /* If we got a quit from within the menu computation, + quit all the way out of it. This takes care of C-] in the debugger. */ + if (CONSP (arg) && EQ (XCONS (arg)->car, Qquit)) + Fsignal (Qquit, Qnil); + + return Qnil; +} + +/* Evaluate an expression and return the result (or nil if something + went wrong). Used to evaluate dynamic parts of menu items. */ +Lisp_Object +menu_item_eval_property (sexpr) + Lisp_Object sexpr; +{ + int count = specpdl_ptr - specpdl; + Lisp_Object val; + specbind (Qinhibit_redisplay, Qt); + val = internal_condition_case_1 (Feval, sexpr, Qerror, + menu_item_eval_property_1); + return unbind_to (count, val); +} + +/* This function parses a menu item and leaves the result in the + vector item_properties. + ITEM is a key binding, a possible menu item. + If NOTREAL is nonzero, only check for equivalent key bindings, don't + evaluate dynamic expressions in the menu item. + INMENUBAR is > 0 when this is considered for an entry in a menu bar + top level. + INMENUBAR is < 0 when this is considered for an entry in a keyboard menu. + parse_menu_item returns true if the item is a menu item and false + otherwise. */ + +int +parse_menu_item (item, notreal, inmenubar) + Lisp_Object item; + int notreal, inmenubar; +{ + Lisp_Object def, tem, item_string, start; + Lisp_Object cachelist; + Lisp_Object filter; + Lisp_Object keyhint; + int i; + int newcache = 0; + + cachelist = Qnil; + filter = Qnil; + keyhint = Qnil; + + if (!CONSP (item)) + return 0; + + /* Create item_properties vector if necessary. */ + if (NILP (item_properties)) + item_properties + = Fmake_vector (make_number (ITEM_PROPERTY_ENABLE + 1), Qnil); + + /* Initialize optional entries. */ + for (i = ITEM_PROPERTY_DEF; i < ITEM_PROPERTY_ENABLE; i++) + XVECTOR (item_properties)->contents[i] = Qnil; + XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE] = Qt; + + /* Save the item here to protect it from GC. */ + XVECTOR (item_properties)->contents[ITEM_PROPERTY_ITEM] = item; + + item_string = XCONS (item)->car; + + start = item; + item = XCONS (item)->cdr; + if (STRINGP (item_string)) + { + /* Old format menu item. */ + XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME] = item_string; + + /* Maybe help string. */ + if (CONSP (item) && STRINGP (XCONS (item)->car)) + { + XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP] + = XCONS (item)->car; + start = item; + item = XCONS (item)->cdr; + } + + /* Maybee key binding cache. */ + if (CONSP (item) && CONSP (XCONS (item)->car) + && (NILP (XCONS (XCONS (item)->car)->car) + || VECTORP (XCONS (XCONS (item)->car)->car))) + { + cachelist = XCONS (item)->car; + item = XCONS (item)->cdr; + } + + /* This is the real definition--the function to run. */ + XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF] = item; + + /* Get enable property, if any. */ + if (SYMBOLP (item)) + { + tem = Fget (item, Qmenu_enable); + if (!NILP (tem)) + XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE] = tem; + } + } + else if (EQ (item_string, Qmenu_item) && CONSP (item)) + { + /* New format menu item. */ + XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME] + = XCONS (item)->car; + start = XCONS (item)->cdr; + if (CONSP (start)) + { + /* We have a real binding. */ + XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF] + = XCONS (start)->car; + + item = XCONS (start)->cdr; + /* Is there a cache list with key equivalences. */ + if (CONSP (item) && CONSP (XCONS (item)->car)) + { + cachelist = XCONS (item)->car; + item = XCONS (item)->cdr; + } + + /* Parse properties. */ + while (CONSP (item) && CONSP (XCONS (item)->cdr)) + { + tem = XCONS (item)->car; + item = XCONS (item)->cdr; + + if (EQ (tem, QCenable)) + XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE] + = XCONS (item)->car; + else if (EQ (tem, QCvisible) && !notreal) + { + /* If got a visible property and that evaluates to nil + then ignore this item. */ + tem = menu_item_eval_property (XCONS (item)->car); + if (NILP (tem)) + return 0; + } + else if (EQ (tem, QChelp)) + XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP] + = XCONS (item)->car; + else if (EQ (tem, QCfilter)) + filter = item; + else if (EQ (tem, QCkey_sequence)) + { + tem = XCONS (item)->car; + if (NILP (cachelist) + && (SYMBOLP (tem) || STRINGP (tem) || VECTORP (tem))) + /* Be GC protected. Set keyhint to item instead of tem. */ + keyhint = item; + } + else if (EQ (tem, QCkeys)) + { + tem = XCONS (item)->car; + if (CONSP (tem) || STRINGP (tem) && NILP (cachelist)) + XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ] + = tem; + } + else if (EQ (tem, QCbutton) && CONSP (XCONS (item)->car)) + { + Lisp_Object type; + tem = XCONS (item)->car; + type = XCONS (tem)->car; + if (EQ (type, QCtoggle) || EQ (type, QCradio)) + { + XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED] + = XCONS (tem)->cdr; + XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE] + = type; + } + } + item = XCONS (item)->cdr; + } + } + else if (inmenubar || !NILP (start)) + return 0; + } + else + return 0; /* not a menu item */ + + /* If item string is not a string, evaluate it to get string. + If we don't get a string, skip this item. */ + item_string = XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME]; + if (!(STRINGP (item_string) || notreal)) + { + item_string = menu_item_eval_property (item_string); + if (!STRINGP (item_string)) + return 0; + XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME] = item_string; + } + + /* If got a filter apply it on definition. */ + def = XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF]; + if (!NILP (filter)) + { + def = menu_item_eval_property (list2 (XCONS (filter)->car, + list2 (Qquote, def))); + + XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF] = def; + } + + /* If we got no definition, this item is just unselectable text which + is OK in a submenu but not in the menubar. */ + if (NILP (def)) + return (inmenubar ? 0 : 1); + + /* Enable or disable selection of item. */ + tem = XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE]; + if (!EQ (tem, Qt)) + { + if (notreal) + tem = Qt; + else + tem = menu_item_eval_property (tem); + if (inmenubar && NILP (tem)) + return 0; /* Ignore disabled items in menu bar. */ + XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE] = tem; + } + + /* See if this is a separate pane or a submenu. */ + def = XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF]; + tem = get_keymap_1 (def, 0, 1); + /* For a subkeymap, just record its details and exit. */ + if (!NILP (tem)) + { + XVECTOR (item_properties)->contents[ITEM_PROPERTY_MAP] = tem; + XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF] = tem; + return 1; + } + /* At the top level in the menu bar, do likewise for commands also. + The menu bar does not display equivalent key bindings anyway. + ITEM_PROPERTY_DEF is already set up properly. */ + if (inmenubar > 0) + return 1; + + /* This is a command. See if there is an equivalent key binding. */ + if (NILP (cachelist)) + { + /* We have to create a cachelist. */ + CHECK_IMPURE (start); + XCONS (start)->cdr = Fcons (Fcons (Qnil, Qnil), XCONS (start)->cdr); + cachelist = XCONS (XCONS (start)->cdr)->car; + newcache = 1; + tem = XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ]; + if (!NILP (keyhint)) + { + XCONS (cachelist)->car = XCONS (keyhint)->car; + newcache = 0; + } + else if (STRINGP (tem)) + { + XCONS (cachelist)->cdr = Fsubstitute_command_keys (tem); + XCONS (cachelist)->car = Qt; + } + } + tem = XCONS (cachelist)->car; + if (!EQ (tem, Qt)) + { + int chkcache = 0; + Lisp_Object prefix; + + if (!NILP (tem)) + tem = Fkey_binding (tem, Qnil); + + prefix = XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ]; + if (CONSP (prefix)) + { + def = XCONS (prefix)->car; + prefix = XCONS (prefix)->cdr; + } + else + def = XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF]; + + if (NILP (XCONS (cachelist)->car)) /* Have no saved key. */ + { + if (newcache /* Always check first time. */ + /* Should we check everything when precomputing key + bindings? */ + /* || notreal */ + /* If something had no key binding before, don't recheck it + because that is too slow--except if we have a list of + rebound commands in Vdefine_key_rebound_commands, do + recheck any command that appears in that list. */ + || (CONSP (Vdefine_key_rebound_commands) + && !NILP (Fmemq (def, Vdefine_key_rebound_commands)))) + chkcache = 1; + } + /* We had a saved key. Is it still bound to the command? */ + else if (NILP (tem) + || !EQ (tem, def) + /* If the command is an alias for another + (such as lmenu.el set it up), check if the + original command matches the cached command. */ + && !(SYMBOLP (def) && EQ (tem, XSYMBOL (def)->function))) + chkcache = 1; /* Need to recompute key binding. */ + + if (chkcache) + { + /* Recompute equivalent key binding. If the command is an alias + for another (such as lmenu.el set it up), see if the original + command name has equivalent keys. Otherwise look up the + specified command itself. We don't try both, because that + makes lmenu menus slow. */ + if (SYMBOLP (def) && SYMBOLP (XSYMBOL (def)->function) + && ! NILP (Fget (def, Qmenu_alias))) + def = XSYMBOL (def)->function; + tem = Fwhere_is_internal (def, Qnil, Qt, Qnil); + XCONS (cachelist)->car = tem; + if (NILP (tem)) + { + XCONS (cachelist)->cdr = Qnil; + chkcache = 0; + } + } + else if (!NILP (keyhint) && !NILP (XCONS (cachelist)->car)) + { + tem = XCONS (cachelist)->car; + chkcache = 1; + } + + newcache = chkcache; + if (chkcache) + { + tem = Fkey_description (tem); + if (CONSP (prefix)) + { + if (STRINGP (XCONS (prefix)->car)) + tem = concat2 (XCONS (prefix)->car, tem); + if (STRINGP (XCONS (prefix)->cdr)) + tem = concat2 (tem, XCONS (prefix)->cdr); + } + XCONS (cachelist)->cdr = tem; + } + } + + tem = XCONS (cachelist)->cdr; + if (newcache && !NILP (tem)) + { + tem = concat3 (build_string (" ("), tem, build_string (")")); + XCONS (cachelist)->cdr = tem; + } + + /* If we only want to precompute equivalent key bindings, stop here. */ + if (notreal) + return 1; + + /* If we have an equivalent key binding, use that. */ + XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ] = tem; + + /* Include this when menu help is implemented. + tem = XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP]; + if (!(NILP (tem) || STRINGP (tem))) + { + tem = menu_item_eval_property (tem); + if (!STRINGP (tem)) + tem = Qnil; + XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP] = tem; + } + */ + + /* Handle radio buttons or toggle boxes. */ + tem = XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED]; + if (!NILP (tem)) + XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED] + = menu_item_eval_property (tem); + + return 1; +} + + + +/*********************************************************************** + Tool-bars + ***********************************************************************/ + +/* A vector holding toolbar items while they are parsed in function + toolbar_items runs Each item occupies TOOLBAR_ITEM_NSCLOTS + elements in the vector. */ + +static Lisp_Object toolbar_items_vector; + +/* A vector holding the result of parse_toolbar_item. Layout is like + the one for a single item in toolbar_items_vector. */ + +static Lisp_Object toolbar_item_properties; + +/* Next free index in toolbar_items_vector. */ + +static int ntoolbar_items; + +/* The symbols `toolbar', `toolbar-item', and `:image'. */ + +extern Lisp_Object Qtoolbar; +Lisp_Object QCimage; + +/* Function prototypes. */ + +static void init_toolbar_items P_ ((Lisp_Object)); +static void process_toolbar_item P_ ((Lisp_Object, Lisp_Object)); +static int parse_toolbar_item P_ ((Lisp_Object, Lisp_Object)); +static void append_toolbar_item P_ ((void)); + + +/* Return a vector of toolbar items for keymaps currently in effect. + Reuse vector REUSE if non-nil. Return in *NITEMS the number of + toolbar items found. */ + +Lisp_Object +toolbar_items (reuse, nitems) + Lisp_Object reuse; + int *nitems; +{ + Lisp_Object *maps; + int nmaps, i; + Lisp_Object oquit; + Lisp_Object *tmaps; + extern Lisp_Object Voverriding_local_map_menu_flag; + extern Lisp_Object Voverriding_local_map; + + *nitems = 0; + + /* In order to build the menus, we need to call the keymap + accessors. They all call QUIT. But this function is called + during redisplay, during which a quit is fatal. So inhibit + quitting while building the menus. We do this instead of + specbind because (1) errors will clear it anyway and (2) this + avoids risk of specpdl overflow. */ + oquit = Vinhibit_quit; + Vinhibit_quit = Qt; + + /* Initialize toolbar_items_vector and protect it from GC. */ + init_toolbar_items (reuse); + + /* Build list of keymaps in maps. Set nmaps to the number of maps + to process. */ + + /* Should overriding-terminal-local-map and overriding-local-map apply? */ + if (!NILP (Voverriding_local_map_menu_flag)) + { + /* Yes, use them (if non-nil) as well as the global map. */ + maps = (Lisp_Object *) alloca (3 * sizeof (maps[0])); + nmaps = 0; + if (!NILP (current_kboard->Voverriding_terminal_local_map)) + maps[nmaps++] = current_kboard->Voverriding_terminal_local_map; + if (!NILP (Voverriding_local_map)) + maps[nmaps++] = Voverriding_local_map; + } + else + { + /* No, so use major and minor mode keymaps. */ + nmaps = current_minor_maps (NULL, &tmaps); + maps = (Lisp_Object *) alloca ((nmaps + 2) * sizeof (maps[0])); + bcopy (tmaps, maps, nmaps * sizeof (maps[0])); +#ifdef USE_TEXT_PROPERTIES + maps[nmaps++] = get_local_map (PT, current_buffer); +#else + maps[nmaps++] = current_buffer->keymap; +#endif + } + + /* Add global keymap at the end. */ + maps[nmaps++] = current_global_map; + + /* Process maps in reverse order and look up in each map the prefix + key `toolbar'. */ + for (i = nmaps - 1; i >= 0; --i) + if (!NILP (maps[i])) + { + Lisp_Object keymap; + + keymap = get_keyelt (access_keymap (maps[i], Qtoolbar, 1, 1), 0); + if (!NILP (Fkeymapp (keymap))) + { + Lisp_Object tail; + + /* KEYMAP is a list `(keymap (KEY . BINDING) ...)'. */ + for (tail = keymap; CONSP (tail); tail = XCONS (tail)->cdr) + { + Lisp_Object keydef = XCAR (tail); + if (CONSP (keydef)) + process_toolbar_item (XCAR (keydef), XCDR (keydef)); + } + } + } + + Vinhibit_quit = oquit; + *nitems = ntoolbar_items / TOOLBAR_ITEM_NSLOTS; + return toolbar_items_vector; +} + + +/* Process the definition of KEY which is DEF. */ + +static void +process_toolbar_item (key, def) + Lisp_Object key, def; +{ + int i; + extern Lisp_Object Qundefined; + struct gcpro gcpro1, gcpro2; + + /* Protect KEY and DEF from GC because parse_toolbar_item may call + eval. */ + GCPRO2 (key, def); + + if (EQ (def, Qundefined)) + { + /* If a map has an explicit `undefined' as definition, + discard any previously made item. */ + for (i = 0; i < ntoolbar_items; i += TOOLBAR_ITEM_NSLOTS) + { + Lisp_Object *v = XVECTOR (toolbar_items_vector)->contents + i; + + if (EQ (key, v[TOOLBAR_ITEM_KEY])) + { + if (ntoolbar_items > i + TOOLBAR_ITEM_NSLOTS) + bcopy (v + TOOLBAR_ITEM_NSLOTS, v, + ((ntoolbar_items - i - TOOLBAR_ITEM_NSLOTS) + * sizeof (Lisp_Object))); + ntoolbar_items -= TOOLBAR_ITEM_NSLOTS; + break; + } + } + } + else if (parse_toolbar_item (key, def)) + /* Append a new toolbar item to toolbar_items_vector. Accept + more than one definition for the same key. */ + append_toolbar_item (); + + UNGCPRO; +} + + +/* Parse a toolbar item specification ITEM for key KEY and return the + result in toolbar_item_properties. Value is zero if ITEM is + invalid. + + ITEM is a list `(menu-item CAPTION BINDING PROPS...)'. + + CAPTION is the caption of the item, If it's not a string, it is + evaluated to get a string. + + BINDING is the toolbar item's binding. Toolbar items with keymaps + as binding are currently ignored. + + The following properties are recognized: + + - `:enable FORM'. + + FORM is evaluated and specifies whether the toolbar item is enabled + or disabled. + + - `:visible FORM' + + FORM is evaluated and specifies whether the toolbar item is visible. + + - `:filter FUNCTION' + + FUNCTION is invoked with one parameter `(quote BINDING)'. Its + result is stored as the new binding. + + - `:button (TYPE SELECTED)' + + TYPE must be one of `:radio' or `:toggle'. SELECTED is evaluated + and specifies whether the button is selected (pressed) or not. + + - `:image IMAGES' + + IMAGES is either a single image specification or a vector of four + image specifications. See enum toolbar_item_images. + + - `:help HELP-STRING'. + + Gives a help string to display for the toolbar item. */ + +static int +parse_toolbar_item (key, item) + Lisp_Object key, item; +{ + /* Access slot with index IDX of vector toolbar_item_properties. */ +#define PROP(IDX) XVECTOR (toolbar_item_properties)->contents[IDX] + + Lisp_Object filter = Qnil; + Lisp_Object caption; + extern Lisp_Object QCenable, QCvisible, QChelp, QCfilter; + extern Lisp_Object QCbutton, QCtoggle, QCradio; + int i; + struct gcpro gcpro1; + + /* Defininition looks like `(toolbar-item CAPTION BINDING + PROPS...)'. Rule out items that aren't lists, don't start with + `toolbar-item' or whose rest following `toolbar-item' is not a + list. */ + if (!CONSP (item) + || !EQ (XCAR (item), Qmenu_item) + || (item = XCDR (item), + !CONSP (item))) + return 0; + + /* Create toolbar_item_properties vector if necessary. Reset it to + defaults. */ + if (VECTORP (toolbar_item_properties)) + { + for (i = 0; i < TOOLBAR_ITEM_NSLOTS; ++i) + PROP (i) = Qnil; + } + else + toolbar_item_properties + = Fmake_vector (make_number (TOOLBAR_ITEM_NSLOTS), Qnil); + + /* Set defaults. */ + PROP (TOOLBAR_ITEM_KEY) = key; + PROP (TOOLBAR_ITEM_ENABLED_P) = Qt; + + /* Get the caption of the item. If the caption is not a string, + evaluate it to get a string. If we don't get a string, skip this + item. */ + caption = XCAR (item); + if (!STRINGP (caption)) + { + caption = menu_item_eval_property (caption); + if (!STRINGP (caption)) + return 0; + } + PROP (TOOLBAR_ITEM_CAPTION) = caption; + + /* Give up if rest following the caption is not a list. */ + item = XCDR (item); + if (!CONSP (item)) + return 0; - /* Loop over all keymap entries that have menu strings. */ - for (tail = keymap; CONSP (tail); tail = XCONS (tail)->cdr) + /* Store the binding. */ + PROP (TOOLBAR_ITEM_BINDING) = XCAR (item); + item = XCDR (item); + + /* Process the rest of the properties. */ + for (; CONSP (item) && CONSP (XCDR (item)); item = XCDR (XCDR (item))) { - item = XCONS (tail)->car; - if (CONSP (item)) + Lisp_Object key, value; + + key = XCAR (item); + value = XCAR (XCDR (item)); + + if (EQ (key, QCenable)) + /* `:enable FORM'. */ + PROP (TOOLBAR_ITEM_ENABLED_P) = value; + else if (EQ (key, QCvisible)) { - key = XCONS (item)->car; - binding = XCONS (item)->cdr; - if (CONSP (binding)) - { - item_string = XCONS (binding)->car; - if (STRINGP (item_string)) - menu_bar_item (key, item_string, Fcdr (binding)); - } - else if (EQ (binding, Qundefined)) - menu_bar_item (key, Qnil, binding); + /* `:visible FORM'. If got a visible property and that + evaluates to nil then ignore this item. */ + if (NILP (menu_item_eval_property (value))) + return 0; } - else if (VECTORP (item)) + else if (EQ (key, QChelp)) + /* `:help HELP-STRING'. */ + PROP (TOOLBAR_ITEM_HELP) = value; + else if (EQ (key, QCfilter)) + /* ':filter FORM'. */ + filter = value; + else if (EQ (key, QCbutton) && CONSP (value)) { - /* Loop over the char values represented in the vector. */ - int len = XVECTOR (item)->size; - int c; - for (c = 0; c < len; c++) + /* `:button (TYPE . SELECTED)'. */ + Lisp_Object type, selected; + + type = XCAR (value); + selected = XCDR (value); + if (EQ (type, QCtoggle) || EQ (type, QCradio)) { - Lisp_Object character; - XSETFASTINT (character, c); - binding = XVECTOR (item)->contents[c]; - if (CONSP (binding)) - { - item_string = XCONS (binding)->car; - if (STRINGP (item_string)) - menu_bar_item (key, item_string, Fcdr (binding)); - } - else if (EQ (binding, Qundefined)) - menu_bar_item (key, Qnil, binding); + PROP (TOOLBAR_ITEM_SELECTED_P) = selected; + PROP (TOOLBAR_ITEM_TYPE) = type; } } + else if (EQ (key, QCimage) + && (CONSP (value) + || (VECTORP (value) && XVECTOR (value)->size == 4))) + /* Value is either a single image specification or a vector + of 4 such specifications for the different buttion states. */ + PROP (TOOLBAR_ITEM_IMAGES) = value; } -} -/* This is used as the handler when calling internal_condition_case_1. */ + /* If got a filter apply it on binding. */ + if (!NILP (filter)) + PROP (TOOLBAR_ITEM_BINDING) + = menu_item_eval_property (list2 (filter, + list2 (Qquote, + PROP (TOOLBAR_ITEM_BINDING)))); -static Lisp_Object -menu_bar_item_1 (arg) - Lisp_Object arg; -{ - return Qnil; + /* See if the binding is a keymap. Give up if it is. */ + if (!NILP (get_keymap_1 (PROP (TOOLBAR_ITEM_BINDING), 0, 1))) + return 0; + + /* Enable or disable selection of item. */ + if (!EQ (PROP (TOOLBAR_ITEM_ENABLED_P), Qt)) + PROP (TOOLBAR_ITEM_ENABLED_P) + = menu_item_eval_property (PROP (TOOLBAR_ITEM_ENABLED_P)); + + /* Handle radio buttons or toggle boxes. */ + if (!NILP (PROP (TOOLBAR_ITEM_SELECTED_P))) + PROP (TOOLBAR_ITEM_SELECTED_P) + = menu_item_eval_property (PROP (TOOLBAR_ITEM_SELECTED_P)); + + return 1; + +#undef PROP } -/* Add one item to menu_bar_items_vector, for KEY, ITEM_STRING and DEF. - If there's already an item for KEY, add this DEF to it. */ + +/* Initialize Vtoolbar_items. REUSE, if non-nil, is a vector that can + be reused. */ static void -menu_bar_item (key, item_string, def) - Lisp_Object key, item_string, def; +init_toolbar_items (reuse) + Lisp_Object reuse; { - Lisp_Object tem; - Lisp_Object enabled; - int i; - - /* Skip menu-bar equiv keys data. */ - if (CONSP (def) && CONSP (XCONS (def)->car)) - def = XCONS (def)->cdr; + if (VECTORP (reuse)) + toolbar_items_vector = reuse; + else + toolbar_items_vector = Fmake_vector (make_number (64), Qnil); + ntoolbar_items = 0; +} - if (EQ (def, Qundefined)) - { - /* If a map has an explicit `undefined' as definition, - discard any previously made menu bar item. */ - for (i = 0; i < menu_bar_items_index; i += 4) - if (EQ (key, XVECTOR (menu_bar_items_vector)->contents[i])) - { - if (menu_bar_items_index > i + 4) - bcopy (&XVECTOR (menu_bar_items_vector)->contents[i + 4], - &XVECTOR (menu_bar_items_vector)->contents[i], - (menu_bar_items_index - i - 4) * sizeof (Lisp_Object)); - menu_bar_items_index -= 4; - return; - } +/* Append parsed toolbar item properties from toolbar_item_properties */ - /* If there's no definition for this key yet, - just ignore `undefined'. */ - return; +static void +append_toolbar_item () +{ + Lisp_Object *to, *from; + + /* Enlarge toolbar_items_vector if necessary. */ + if (ntoolbar_items + TOOLBAR_ITEM_NSLOTS + >= XVECTOR (toolbar_items_vector)->size) + { + Lisp_Object new_vector; + int old_size = XVECTOR (toolbar_items_vector)->size; + + new_vector = Fmake_vector (make_number (2 * old_size), Qnil); + bcopy (XVECTOR (toolbar_items_vector)->contents, + XVECTOR (new_vector)->contents, + old_size * sizeof (Lisp_Object)); + toolbar_items_vector = new_vector; } - /* See if this entry is enabled. */ - enabled = Qt; + /* Append entries from toolbar_item_properties to the end of + toolbar_items_vector. */ + to = XVECTOR (toolbar_items_vector)->contents + ntoolbar_items; + from = XVECTOR (toolbar_item_properties)->contents; + bcopy (from, to, TOOLBAR_ITEM_NSLOTS * sizeof *to); + ntoolbar_items += TOOLBAR_ITEM_NSLOTS; +} - if (SYMBOLP (def)) - { - /* No property, or nil, means enable. - Otherwise, enable if value is not nil. */ - tem = Fget (def, Qmenu_enable); - if (!NILP (tem)) - /* (condition-case nil (eval tem) - (error nil)) */ - enabled = internal_condition_case_1 (Feval, tem, Qerror, - menu_bar_item_1); - } - /* Ignore this item if it's not enabled. */ - if (NILP (enabled)) - return; - /* Find any existing item for this KEY. */ - for (i = 0; i < menu_bar_items_index; i += 4) - if (EQ (key, XVECTOR (menu_bar_items_vector)->contents[i])) - break; - /* If we did not find this KEY, add it at the end. */ - if (i == menu_bar_items_index) - { - /* If vector is too small, get a bigger one. */ - if (i + 4 > XVECTOR (menu_bar_items_vector)->size) - { - Lisp_Object tem; - int newsize = 2 * i; - tem = Fmake_vector (make_number (2 * i), Qnil); - bcopy (XVECTOR (menu_bar_items_vector)->contents, - XVECTOR (tem)->contents, i * sizeof (Lisp_Object)); - menu_bar_items_vector = tem; - } - /* Add this item. */ - XVECTOR (menu_bar_items_vector)->contents[i++] = key; - XVECTOR (menu_bar_items_vector)->contents[i++] = item_string; - XVECTOR (menu_bar_items_vector)->contents[i++] = Fcons (def, Qnil); - XVECTOR (menu_bar_items_vector)->contents[i++] = make_number (0); - menu_bar_items_index = i; - } - /* We did find an item for this KEY. Add DEF to its list of maps. */ - else - { - Lisp_Object old; - old = XVECTOR (menu_bar_items_vector)->contents[i + 2]; - XVECTOR (menu_bar_items_vector)->contents[i + 2] = Fcons (def, old); - } -} /* 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. @@ -5453,7 +6815,8 @@ read_char_x_menu_prompt (nmaps, maps, prev_event, used_mouse_menu) /* If we got to this point via a mouse click, use a real menu for mouse selection. */ if (EVENT_HAS_PARAMETERS (prev_event) - && !EQ (XCONS (prev_event)->car, Qmenu_bar)) + && !EQ (XCONS (prev_event)->car, Qmenu_bar) + && !EQ (XCONS (prev_event)->car, Qtoolbar)) { /* Display the menu and get the selection. */ Lisp_Object *realmaps @@ -5560,7 +6923,7 @@ read_char_minibuf_menu_prompt (commandflag, nmaps, maps) /* Prompt string always starts with map's prompt, and a space. */ strcpy (menu, XSTRING (name)->data); - nlength = XSTRING (name)->size; + nlength = STRING_BYTES (XSTRING (name)); menu[nlength++] = ':'; menu[nlength++] = ' '; menu[nlength] = 0; @@ -5581,7 +6944,7 @@ read_char_minibuf_menu_prompt (commandflag, nmaps, maps) /* Loop over elements of map. */ while (i < width) { - Lisp_Object s, elt; + Lisp_Object elt; /* If reached end of map, start at beginning of next map. */ if (NILP (rest)) @@ -5614,26 +6977,27 @@ read_char_minibuf_menu_prompt (commandflag, nmaps, maps) else { /* An ordinary element. */ - Lisp_Object event; + Lisp_Object event, tem; if (idx < 0) { - s = Fcar_safe (Fcdr_safe (elt)); /* alist */ - event = Fcar_safe (elt); + event = Fcar_safe (elt); /* alist */ + elt = Fcdr_safe (elt); } else { - s = Fcar_safe (elt); /* vector */ - XSETINT (event, idx); + XSETINT (event, idx); /* vector */ } /* Ignore the element if it has no prompt string. */ - if (STRINGP (s) && INTEGERP (event)) + if (INTEGERP (event) && parse_menu_item (elt, 0, -1)) { /* 1 if the char to type matches the string. */ int char_matches; Lisp_Object upcased_event, downcased_event; Lisp_Object desc; + Lisp_Object s + = XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME]; upcased_event = Fupcase (event); downcased_event = Fdowncase (event); @@ -5642,6 +7006,27 @@ read_char_minibuf_menu_prompt (commandflag, nmaps, maps) if (! char_matches) desc = Fsingle_key_description (event); + tem + = XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ]; + if (!NILP (tem)) + /* Insert equivalent keybinding. */ + s = concat2 (s, tem); + + tem + = XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE]; + if (EQ (tem, QCradio) || EQ (tem, QCtoggle)) + { + /* Insert button prefix. */ + Lisp_Object selected + = XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED]; + if (EQ (tem, QCradio)) + tem = build_string (NILP (selected) ? "(*) " : "( ) "); + else + tem = build_string (NILP (selected) ? "[X] " : "[ ] "); + s = concat2 (tem, s); + } + + /* If we have room for the prompt string, add it to this line. If this is the first on the line, always add it. */ if ((XSTRING (s)->size + i + 2 @@ -5703,7 +7088,8 @@ read_char_minibuf_menu_prompt (commandflag, nmaps, maps) } /* Prompt with that and read response. */ - message1 (menu); + message2_nolog (menu, strlen (menu), + ! NILP (current_buffer->enable_multibyte_characters)); /* Make believe its not a keyboard macro in case the help char is pressed. Help characters are not recorded because menu prompting @@ -5770,7 +7156,7 @@ follow_key (key, nmaps, current, defs, next) { Lisp_Object def; def = get_keyelt (access_keymap (current[i], - meta_prefix_char, 1, 0)); + meta_prefix_char, 1, 0), 0); /* Note that since we pass the resulting bindings through get_keymap_1, non-prefix bindings for meta-prefix-char @@ -5795,7 +7181,7 @@ follow_key (key, nmaps, current, defs, next) else map = current[i]; - defs[i] = get_keyelt (access_keymap (map, key, 1, 0)); + defs[i] = get_keyelt (access_keymap (map, key, 1, 0), 0); if (! NILP (defs[i])) first_binding = i; } @@ -5843,16 +7229,20 @@ follow_key (key, nmaps, current, defs, next) If the user switches frames in the midst of a key sequence, we put off the switch-frame event until later; the next call to - read_char will return it. */ + read_char will return it. + + If FIX_CURRENT_BUFFER is nonzero, we restore current_buffer + from the selected window's buffer. */ static int read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last, - can_return_switch_frame) + can_return_switch_frame, fix_current_buffer) Lisp_Object *keybuf; int bufsize; Lisp_Object prompt; int dont_downcase_last; int can_return_switch_frame; + int fix_current_buffer; { int count = specpdl_ptr - specpdl; @@ -5944,15 +7334,17 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last, /* Save the status of key translation before each step, so that we can restore this after downcasing. */ Lisp_Object prev_fkey_map; - Lisp_Object prev_fkey_start; - Lisp_Object prev_fkey_end; + int prev_fkey_start; + int prev_fkey_end; Lisp_Object prev_keytran_map; - Lisp_Object prev_keytran_start; - Lisp_Object prev_keytran_end; + int prev_keytran_start; + int prev_keytran_end; int junk; + raw_keybuf_count = 0; + last_nonmenu_event = Qnil; delayed_switch_frame = Qnil; @@ -6173,7 +7565,10 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last, /* read_char returns t when it shows a menu and the user rejects it. Just return -1. */ if (EQ (key, Qt)) - return -1; + { + unbind_to (count, Qnil); + return -1; + } /* read_char returns -1 at the end of a macro. Emacs 18 handles this by returning immediately with a @@ -6192,6 +7587,18 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last, if (BUFFERP (key)) { mock_input = t; + /* Reset the current buffer from the selected window + in case something changed the former and not the latter. + This is to be more consistent with the behavior + of the command_loop_1. */ + if (fix_current_buffer) + { + if (! FRAME_LIVE_P (selected_frame)) + Fkill_emacs (Qnil); + if (XBUFFER (XWINDOW (selected_window)->buffer) != current_buffer) + Fset_buffer (XWINDOW (selected_window)->buffer); + } + orig_local_map = get_local_map (PT, current_buffer); goto replay_sequence; } @@ -6201,6 +7608,8 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last, replay to get the right keymap. */ if (XINT (key) == quit_char && current_buffer != starting_buffer) { + GROW_RAW_KEYBUF; + XVECTOR (raw_keybuf)->contents[raw_keybuf_count++] = key; keybuf[t++] = key; mock_input = t; Vquit_flag = Qnil; @@ -6209,6 +7618,22 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last, } Vquit_flag = Qnil; + + if (EVENT_HAS_PARAMETERS (key) + && EQ (EVENT_HEAD_KIND (EVENT_HEAD (key)), Qswitch_frame)) + { + /* If we're at the beginning of a key sequence, and the caller + says it's okay, go ahead and return this event. If we're + in the midst of a key sequence, delay it until the end. */ + if (t > 0 || !can_return_switch_frame) + { + delayed_switch_frame = key; + goto replay_key; + } + } + + GROW_RAW_KEYBUF; + XVECTOR (raw_keybuf)->contents[raw_keybuf_count++] = key; } /* Clicks in non-text areas get prefixed by the symbol @@ -6236,6 +7661,7 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last, window = POSN_WINDOW (EVENT_START (key)); posn = POSN_BUFFER_POSN (EVENT_START (key)); + if (CONSP (posn)) { /* We're looking at the second event of a @@ -6254,6 +7680,7 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last, && BUFFERP (XWINDOW (window)->buffer) && XBUFFER (XWINDOW (window)->buffer) != current_buffer) { + XVECTOR (raw_keybuf)->contents[raw_keybuf_count++] = key; keybuf[t] = key; mock_input = t + 1; @@ -6269,19 +7696,24 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last, emacsclient). */ record_unwind_protect (Fset_buffer, Fcurrent_buffer ()); + if (! FRAME_LIVE_P (selected_frame)) + Fkill_emacs (Qnil); set_buffer_internal (XBUFFER (XWINDOW (window)->buffer)); orig_local_map = get_local_map (PT, current_buffer); goto replay_sequence; } + /* For a mouse click, get the local text-property keymap of the place clicked on, rather than point. */ - if (last_real_key_start == 0 && CONSP (XCONS (key)->cdr) + if (last_real_key_start == 0 + && CONSP (XCONS (key)->cdr) && ! localized_local_map) { Lisp_Object map_here, start, pos; localized_local_map = 1; start = EVENT_START (key); + if (CONSP (start) && CONSP (XCONS (start)->cdr)) { pos = POSN_BUFFER_POSN (start); @@ -6310,22 +7742,33 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last, 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; - } - } - else if (EQ (kind, Qswitch_frame)) - { - /* If we're at the beginning of a key sequence, and the caller - says it's okay, go ahead and return this event. If we're - in the midst of a key sequence, delay it until the end. */ - if (t > 0 || !can_return_switch_frame) - { - delayed_switch_frame = key; + + /* If on a mode line string with a local keymap, + reconsider the key sequence with that keymap. */ + if (CONSP (POSN_STRING (EVENT_START (key)))) + { + Lisp_Object string, pos, map; + + string = POSN_STRING (EVENT_START (key)); + pos = XCDR (string); + string = XCAR (string); + + if (pos >= 0 + && pos < XSTRING (string)->size + && (map = Fget_text_property (pos, Qlocal_map, + string), + !NILP (map))) + { + orig_local_map = map; + goto replay_sequence; + } + } + goto replay_key; } } @@ -6338,7 +7781,7 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last, posn = POSN_BUFFER_POSN (EVENT_START (key)); /* Handle menu-bar events: insert the dummy prefix event `menu-bar'. */ - if (EQ (posn, Qmenu_bar)) + if (EQ (posn, Qmenu_bar) || EQ (posn, Qtoolbar)) { if (t + 1 >= bufsize) error ("Key sequence too long"); @@ -6535,7 +7978,7 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last, fkey_next = get_keymap_1 (get_keyelt - (access_keymap (fkey_map, meta_prefix_char, 1, 0)), + (access_keymap (fkey_map, meta_prefix_char, 1, 0), 0), 0, 1); XSETFASTINT (key, XFASTINT (key) & ~meta_modifier); } @@ -6543,7 +7986,21 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last, fkey_next = fkey_map; fkey_next - = get_keyelt (access_keymap (fkey_next, key, 1, 0)); + = get_keyelt (access_keymap (fkey_next, key, 1, 0), 1); + + /* Handle symbol with autoload definition. */ + if (SYMBOLP (fkey_next) && ! NILP (Ffboundp (fkey_next)) + && CONSP (XSYMBOL (fkey_next)->function) + && EQ (XCONS (XSYMBOL (fkey_next)->function)->car, Qautoload)) + do_autoload (XSYMBOL (fkey_next)->function, + fkey_next); + + /* Handle a symbol whose function definition is a keymap + or an array. */ + if (SYMBOLP (fkey_next) && ! NILP (Ffboundp (fkey_next)) + && (!NILP (Farrayp (XSYMBOL (fkey_next)->function)) + || !NILP (Fkeymapp (XSYMBOL (fkey_next)->function)))) + fkey_next = XSYMBOL (fkey_next)->function; #if 0 /* I didn't turn this on, because it might cause trouble for the mapping of return into C-m and tab into C-i. */ @@ -6606,8 +8063,10 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last, fkey_start = fkey_end = t; fkey_map = Vfunction_key_map; - /* Do pass the results through key-translation-map. */ - keytran_start = keytran_end = 0; + /* Do pass the results through key-translation-map. + But don't retranslate what key-translation-map + has already translated. */ + keytran_end = keytran_start; keytran_map = Vkey_translation_map; goto replay_sequence; @@ -6643,7 +8102,7 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last, keytran_next = get_keymap_1 (get_keyelt - (access_keymap (keytran_map, meta_prefix_char, 1, 0)), + (access_keymap (keytran_map, meta_prefix_char, 1, 0), 0), 0, 1); XSETFASTINT (key, XFASTINT (key) & ~meta_modifier); } @@ -6651,10 +8110,24 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last, keytran_next = keytran_map; keytran_next - = get_keyelt (access_keymap (keytran_next, key, 1, 0)); + = get_keyelt (access_keymap (keytran_next, key, 1, 0), 1); + /* Handle symbol with autoload definition. */ + if (SYMBOLP (keytran_next) && ! NILP (Ffboundp (keytran_next)) + && CONSP (XSYMBOL (keytran_next)->function) + && EQ (XCONS (XSYMBOL (keytran_next)->function)->car, Qautoload)) + do_autoload (XSYMBOL (keytran_next)->function, + keytran_next); + + /* Handle a symbol whose function definition is a keymap + or an array. */ + if (SYMBOLP (keytran_next) && ! NILP (Ffboundp (keytran_next)) + && (!NILP (Farrayp (XSYMBOL (keytran_next)->function)) + || !NILP (Fkeymapp (XSYMBOL (keytran_next)->function)))) + keytran_next = XSYMBOL (keytran_next)->function; + /* If the key translation map gives a function, not an - array, then call the function with no args and use + array, then call the function with one arg and use its value instead. */ if (SYMBOLP (keytran_next) && ! NILP (Ffboundp (keytran_next)) && keytran_end == t) @@ -6709,7 +8182,7 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last, /* Don't pass the results of key-translation-map through function-key-map. */ fkey_start = fkey_end = t; - fkey_map = Vkey_translation_map; + fkey_map = Vfunction_key_map; goto replay_sequence; } @@ -6735,7 +8208,7 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last, && ! key_translation_possible && INTEGERP (key) && ((((XINT (key) & 0x3ffff) - < XSTRING (current_buffer->downcase_table)->size) + < XCHAR_TABLE (current_buffer->downcase_table)->size) && UPPERCASEP (XINT (key) & 0x3ffff)) || (XINT (key) & shift_modifier))) { @@ -6836,6 +8309,8 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last, add_command_key (keybuf[t]); } + + return t; } @@ -6882,24 +8357,81 @@ is nil, then the event will be put off until after the current key sequence.\n\ \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, dont_downcase_last, can_return_switch_frame) +`function-key-map' for more details.\n\ +\n\ +The optional fifth argument COMMAND-LOOP, if non-nil, means\n\ +that this key sequence is being read by something that will\n\ +read commands one after another. It should be nil if the caller\n\ +will read just one key sequence.") + (prompt, continue_echo, dont_downcase_last, can_return_switch_frame, command-loop) #endif -DEFUN ("read-key-sequence", Fread_key_sequence, Sread_key_sequence, 1, 4, 0, +DEFUN ("read-key-sequence", Fread_key_sequence, Sread_key_sequence, 1, 5, 0, 0) - (prompt, continue_echo, dont_downcase_last, can_return_switch_frame) + (prompt, continue_echo, dont_downcase_last, can_return_switch_frame, + command_loop) + Lisp_Object prompt, continue_echo, dont_downcase_last; + Lisp_Object can_return_switch_frame, command_loop; +{ + Lisp_Object keybuf[30]; + register int i; + struct gcpro gcpro1, gcpro2; + int count = specpdl_ptr - specpdl; + + if (!NILP (prompt)) + CHECK_STRING (prompt, 0); + QUIT; + + specbind (Qinput_method_exit_on_first_char, + (NILP (command_loop) ? Qt : Qnil)); + specbind (Qinput_method_use_echo_area, + (NILP (command_loop) ? Qt : Qnil)); + + bzero (keybuf, sizeof keybuf); + GCPRO1 (keybuf[0]); + gcpro1.nvars = (sizeof keybuf/sizeof (keybuf[0])); + + if (NILP (continue_echo)) + { + this_command_key_count = 0; + this_single_command_key_start = 0; + } + + i = read_key_sequence (keybuf, (sizeof keybuf/sizeof (keybuf[0])), + prompt, ! NILP (dont_downcase_last), + ! NILP (can_return_switch_frame), 0); + + if (i == -1) + { + Vquit_flag = Qt; + QUIT; + } + UNGCPRO; + return unbind_to (count, make_event_array (i, keybuf)); +} + +DEFUN ("read-key-sequence-vector", Fread_key_sequence_vector, + Sread_key_sequence_vector, 1, 5, 0, + "Like `read-key-sequence' but always return a vector.") + (prompt, continue_echo, dont_downcase_last, can_return_switch_frame, + command_loop) Lisp_Object prompt, continue_echo, dont_downcase_last; - Lisp_Object can_return_switch_frame; + Lisp_Object can_return_switch_frame, command_loop; { Lisp_Object keybuf[30]; register int i; struct gcpro gcpro1, gcpro2; + int count = specpdl_ptr - specpdl; if (!NILP (prompt)) CHECK_STRING (prompt, 0); QUIT; + specbind (Qinput_method_exit_on_first_char, + (NILP (command_loop) ? Qt : Qnil)); + specbind (Qinput_method_use_echo_area, + (NILP (command_loop) ? Qt : Qnil)); + bzero (keybuf, sizeof keybuf); GCPRO1 (keybuf[0]); gcpro1.nvars = (sizeof keybuf/sizeof (keybuf[0])); @@ -6912,7 +8444,7 @@ DEFUN ("read-key-sequence", Fread_key_sequence, Sread_key_sequence, 1, 4, 0, i = read_key_sequence (keybuf, (sizeof keybuf/sizeof (keybuf[0])), prompt, ! NILP (dont_downcase_last), - ! NILP (can_return_switch_frame)); + ! NILP (can_return_switch_frame), 0); if (i == -1) { @@ -6920,7 +8452,7 @@ DEFUN ("read-key-sequence", Fread_key_sequence, Sread_key_sequence, 1, 4, 0, QUIT; } UNGCPRO; - return make_event_array (i, keybuf); + return unbind_to (count, Fvector (i, keybuf)); } DEFUN ("command-execute", Fcommand_execute, Scommand_execute, 1, 4, 0, @@ -6986,13 +8518,24 @@ a special event, so ignore the prefix argument and don't clear it.") other sorts of commands, call-interactively takes care of this. */ if (!NILP (record_flag)) - Vcommand_history - = Fcons (Fcons (Qexecute_kbd_macro, - Fcons (final, Fcons (prefixarg, Qnil))), - Vcommand_history); + { + Vcommand_history + = Fcons (Fcons (Qexecute_kbd_macro, + Fcons (final, Fcons (prefixarg, Qnil))), + Vcommand_history); + + /* Don't keep command history around forever. */ + if (NUMBERP (Vhistory_length) && XINT (Vhistory_length) > 0) + { + tem = Fnthcdr (Vhistory_length, Vcommand_history); + if (CONSP (tem)) + XCONS (tem)->cdr = Qnil; + } + } return Fexecute_kbd_macro (final, prefixarg); } + if (CONSP (final) || SUBRP (final) || COMPILEDP (final)) { backtrace.next = backtrace_list; @@ -7061,7 +8604,8 @@ DEFUN ("execute-extended-command", Fexecute_extended_command, Sexecute_extended_ history list. */ function = Fcompleting_read (build_string (buf), Vobarray, Qcommandp, - Qt, Qnil, Qextended_command_history); + Qt, Qnil, Qextended_command_history, Qnil, + Qnil); if (STRINGP (function) && XSTRING (function)->size == 0) error ("No command name given"); @@ -7072,7 +8616,6 @@ DEFUN ("execute-extended-command", Fexecute_extended_command, Sexecute_extended_ struct Lisp_String *str; Lisp_Object *keys; int i; - Lisp_Object tem; this_command_key_count = 0; this_single_command_key_start = 0; @@ -7083,20 +8626,17 @@ DEFUN ("execute-extended-command", Fexecute_extended_command, Sexecute_extended_ str = XSTRING (function); for (i = 0; i < str->size; i++) - { - XSETFASTINT (tem, str->data[i]); - add_command_key (tem); - } + add_command_key (Faref (function, make_number (i))); - XSETFASTINT (tem, '\015'); - add_command_key (tem); + add_command_key (make_number ('\015')); } UNGCPRO; function = Fintern (function, Qnil); current_kboard->Vprefix_arg = prefixarg; - this_command = function; + Vthis_command = function; + real_this_command = function; /* If enabled, show which key runs this command. */ if (!NILP (Vsuggest_key_bindings) @@ -7112,32 +8652,48 @@ DEFUN ("execute-extended-command", Fexecute_extended_command, Sexecute_extended_ value = Fcommand_execute (function, Qt, Qnil, Qnil); /* If the command has a key binding, print it now. */ - if (!NILP (bindings)) + if (!NILP (bindings) + && ! (VECTORP (bindings) && EQ (Faref (bindings, make_number (0)), + Qmouse_movement))) { /* But first wait, and skip the message if there is input. */ - if (!NILP (Fsit_for ((NUMBERP (Vsuggest_key_bindings) - ? Vsuggest_key_bindings : make_number (2)), - Qnil, Qnil))) + int delay_time; + if (!NILP (echo_area_buffer[0])) + /* This command displayed something in the echo area; + so wait a few seconds, then display our suggestion message. */ + delay_time = (NUMBERP (Vsuggest_key_bindings) + ? XINT (Vsuggest_key_bindings) : 2); + else + /* This command left the echo area empty, + so display our message immediately. */ + delay_time = 0; + + if (!NILP (Fsit_for (make_number (delay_time), Qnil, Qnil)) + && ! CONSP (Vunread_command_events)) { Lisp_Object binding; char *newmessage; - char *oldmessage = echo_area_glyphs; - int oldmessage_len = echo_area_glyphs_length; + int message_p = push_message (); binding = Fkey_description (bindings); newmessage = (char *) alloca (XSYMBOL (function)->name->size - + XSTRING (binding)->size + + STRING_BYTES (XSTRING (binding)) + 100); - sprintf (newmessage, "You can run the command `%s' by typing %s", + sprintf (newmessage, "You can run the command `%s' with %s", XSYMBOL (function)->name->data, XSTRING (binding)->data); - message1_nolog (newmessage); + message2_nolog (newmessage, + strlen (newmessage), + STRING_MULTIBYTE (binding)); if (!NILP (Fsit_for ((NUMBERP (Vsuggest_key_bindings) ? Vsuggest_key_bindings : make_number (2)), - Qnil, Qnil))) - message2_nolog (oldmessage, oldmessage_len); + Qnil, Qnil)) + && message_p) + restore_message (); + + pop_message (); } } @@ -7187,6 +8743,7 @@ current_active_maps (maps_p) /* Return nonzero if input events are pending. */ +int detect_input_pending () { if (!input_pending) @@ -7197,6 +8754,7 @@ detect_input_pending () /* Return nonzero if input events are pending, and run any pending timers. */ +int detect_input_pending_run_timers (do_display) int do_display; { @@ -7206,7 +8764,16 @@ detect_input_pending_run_timers (do_display) get_input_pending (&input_pending, 1); if (old_timers_run != timers_run && do_display) - redisplay_preserve_echo_area (); + { + redisplay_preserve_echo_area (); + /* The following fixes a bug when using lazy-lock with + lazy-lock-defer-on-the-fly set to t, i.e. when fontifying + from an idle timer function. The symptom of the bug is that + the cursor sometimes doesn't become visible until the next X + event is processed. --gerd. */ + if (rif) + rif->flush_display (NULL); + } return input_pending; } @@ -7214,6 +8781,8 @@ detect_input_pending_run_timers (do_display) /* This is called in some cases before a possible quit. It cases the next call to detect_input_pending to recompute input_pending. So calling this function unnecessarily can't do any harm. */ + +void clear_input_pending () { input_pending = 0; @@ -7225,6 +8794,7 @@ clear_input_pending () The problem is, kbd_buffer_get_event needs to be fixed to know what to do in that case. It isn't trivial. */ +int requeued_events_pending_p () { return (!NILP (Vunread_command_events) || unread_command_char != -1); @@ -7274,18 +8844,38 @@ The value is a string or a vector.") XVECTOR (this_command_keys)->contents); } +DEFUN ("this-command-keys-vector", Fthis_command_keys_vector, Sthis_command_keys_vector, 0, 0, 0, + "Return the key sequence that invoked this command, as a vector.") + () +{ + return Fvector (this_command_key_count, + XVECTOR (this_command_keys)->contents); +} + DEFUN ("this-single-command-keys", Fthis_single_command_keys, Sthis_single_command_keys, 0, 0, 0, "Return the key sequence that invoked this command.\n\ Unlike `this-command-keys', this function's value\n\ does not include prefix arguments.\n\ -The value is a string or a vector.") +The value is always a vector.") + () +{ + return Fvector (this_command_key_count + - this_single_command_key_start, + (XVECTOR (this_command_keys)->contents + + this_single_command_key_start)); +} + +DEFUN ("this-single-command-raw-keys", Fthis_single_command_raw_keys, + Sthis_single_command_raw_keys, 0, 0, 0, + "Return the raw events that were read for this command.\n\ +Unlike `this-single-command-keys', this function's value\n\ +shows the events before all translations (except for input methods).\n\ +The value is always a vector.") () { - return make_event_array (this_command_key_count - - this_single_command_key_start, - (XVECTOR (this_command_keys)->contents - + this_single_command_key_start)); + return Fvector (raw_keybuf_count, + (XVECTOR (raw_keybuf)->contents)); } DEFUN ("reset-this-command-lengths", Freset_this_command_lengths, @@ -7306,6 +8896,16 @@ appears in the echo area and in the value of `this-command-keys.'.") before_command_restore_flag = 1; before_command_key_count_1 = before_command_key_count; before_command_echo_length_1 = before_command_echo_length; + return Qnil; +} + +DEFUN ("clear-this-command-keys", Fclear_this_command_keys, + Sclear_this_command_keys, 0, 0, 0, + "Clear out the vector that `this-command-keys' returns.") + () +{ + this_command_key_count = 0; + return Qnil; } DEFUN ("recursion-depth", Frecursion_depth, Srecursion_depth, 0, 0, 0, @@ -7352,10 +8952,7 @@ Also cancel any kbd macro being defined.") discard_tty_input (); - /* Without the cast, GCC complains that this assignment loses the - volatile qualifier of kbd_store_ptr. Is there anything wrong - with that? */ - kbd_fetch_ptr = (struct input_event *) kbd_store_ptr; + kbd_fetch_ptr = kbd_store_ptr; Ffillarray (kbd_buffer_frame_or_window, Qnil); input_pending = 0; @@ -7382,7 +8979,6 @@ On such systems, Emacs starts a subshell instead of suspending.") int old_height, old_width; int width, height; struct gcpro gcpro1, gcpro2; - extern init_sys_modes (); if (!NILP (stuffstring)) CHECK_STRING (stuffstring, 0); @@ -7396,7 +8992,8 @@ On such systems, Emacs starts a subshell instead of suspending.") reset_sys_modes (); /* sys_suspend can get an error if it tries to fork a subshell and the system resources aren't available for that. */ - record_unwind_protect (init_sys_modes, 0); + record_unwind_protect ((Lisp_Object (*) P_ ((Lisp_Object))) init_sys_modes, + Qnil); stuff_buffered_input (stuffstring); if (cannot_suspend) sys_subshell (); @@ -7409,7 +9006,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 (selected_frame, height, width, 0, 0); + change_frame_size (selected_frame, height, width, 0, 0, 0); /* Run suspend-resume-hook. */ if (!NILP (Vrun_hooks)) @@ -7422,6 +9019,7 @@ On such systems, Emacs starts a subshell instead of suspending.") /* If STUFFSTRING is a string, stuff its contents as pending terminal input. Then in any case stuff anything Emacs has read ahead and not used. */ +void stuff_buffered_input (stuffstring) Lisp_Object stuffstring; { @@ -7435,7 +9033,7 @@ stuff_buffered_input (stuffstring) register int count; p = XSTRING (stuffstring)->data; - count = XSTRING (stuffstring)->size; + count = STRING_BYTES (XSTRING (stuffstring)); while (count-- > 0) stuff_char (*p++); stuff_char ('\n'); @@ -7459,6 +9057,7 @@ stuff_buffered_input (stuffstring) #endif /* BSD_SYSTEM and not BSD4_1 */ } +void set_waiting_for_input (time_to_clear) EMACS_TIME *time_to_clear; { @@ -7473,6 +9072,7 @@ set_waiting_for_input (time_to_clear) quit_throw_to_read_char (); } +void clear_waiting_for_input () { /* Tell interrupt_signal not to throw back to read_char, */ @@ -7610,9 +9210,17 @@ interrupt_signal (signalnum) /* If we don't have an argument, */ then quit right away. */ if (immediate_quit && NILP (Vinhibit_quit)) { + struct gl_state_s saved; + struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; + immediate_quit = 0; sigfree (); + saved = gl_state; + GCPRO4 (saved.object, saved.global_code, + saved.current_syntax_table, saved.old_prop); Fsignal (Qquit, Qnil); + gl_state = saved; + UNGCPRO; } else /* Else request quit when it's safe */ @@ -7627,9 +9235,9 @@ interrupt_signal (signalnum) /* If we don't have an argument, */ /* Handle a C-g by making read_char return C-g. */ +void quit_throw_to_read_char () { - quit_error_check (); sigfree (); /* Prevent another signal from doing this before we finish. */ clear_waiting_for_input (); @@ -7677,7 +9285,7 @@ See also `current-input-mode'.") stop_polling (); #endif -#ifndef MSDOS +#ifndef DOS_NT /* this causes startup screen to be restored and messes with the mouse */ reset_sys_modes (); #endif @@ -7716,7 +9324,7 @@ See also `current-input-mode'.") /* Don't let this value be out of range. */ quit_char = XINT (quit) & (meta_key ? 0377 : 0177); -#ifndef MSDOS +#ifndef DOS_NT init_sys_modes (); #endif @@ -7763,7 +9371,9 @@ init_kboard (kb) { kb->Voverriding_terminal_local_map = Qnil; kb->Vlast_command = Qnil; + kb->Vreal_last_command = Qnil; kb->Vprefix_arg = Qnil; + kb->Vlast_prefix_arg = Qnil; kb->kbd_queue = Qnil; kb->kbd_queue_has_data = 0; kb->immediate_echo = 0; @@ -7807,6 +9417,7 @@ delete_kboard (kb) } #endif +void init_keyboard () { /* This is correct before outermost invocation of the editor loop */ @@ -7901,8 +9512,30 @@ struct event_head head_table[] = { &Qmake_frame_visible, "make-frame-visible", &Qmake_frame_visible, }; +void syms_of_keyboard () { + /* Toolbars. */ + QCimage = intern (":image"); + staticpro (&QCimage); + + staticpro (&Qhelp_echo); + Qhelp_echo = intern ("help-echo"); + + staticpro (&item_properties); + item_properties = Qnil; + + staticpro (&toolbar_item_properties); + toolbar_item_properties = Qnil; + staticpro (&toolbar_items_vector); + toolbar_items_vector = Qnil; + + staticpro (&real_this_command); + real_this_command = Qnil; + + Qtimer_event_handler = intern ("timer-event-handler"); + staticpro (&Qtimer_event_handler); + Qdisabled_command_hook = intern ("disabled-command-hook"); staticpro (&Qdisabled_command_hook); @@ -7940,11 +9573,42 @@ syms_of_keyboard () staticpro (&Qfunction_key); Qmouse_click = intern ("mouse-click"); staticpro (&Qmouse_click); - Qtimer_event = intern ("timer-event"); - staticpro (&Qtimer_event); +#ifdef WINDOWSNT + Qmouse_wheel = intern ("mouse-wheel"); + staticpro (&Qmouse_wheel); + Qlanguage_change = intern ("language-change"); + staticpro (&Qlanguage_change); +#endif + Qdrag_n_drop = intern ("drag-n-drop"); + staticpro (&Qdrag_n_drop); + + Qusr1_signal = intern ("usr1-signal"); + staticpro (&Qusr1_signal); + Qusr2_signal = intern ("usr2-signal"); + staticpro (&Qusr2_signal); Qmenu_enable = intern ("menu-enable"); staticpro (&Qmenu_enable); + Qmenu_alias = intern ("menu-alias"); + staticpro (&Qmenu_alias); + QCenable = intern (":enable"); + staticpro (&QCenable); + QCvisible = intern (":visible"); + staticpro (&QCvisible); + QChelp = intern (":help"); + staticpro (&QChelp); + QCfilter = intern (":filter"); + staticpro (&QCfilter); + QCbutton = intern (":button"); + staticpro (&QCbutton); + QCkeys = intern (":keys"); + staticpro (&QCkeys); + QCkey_sequence = intern (":key-sequence"); + staticpro (&QCkey_sequence); + QCtoggle = intern (":toggle"); + staticpro (&QCtoggle); + QCradio = intern (":radio"); + staticpro (&QCradio); Qmode_line = intern ("mode-line"); staticpro (&Qmode_line); @@ -7965,6 +9629,12 @@ syms_of_keyboard () staticpro (&Qup); Qdown = intern ("down"); staticpro (&Qdown); + Qtop = intern ("top"); + staticpro (&Qtop); + Qbottom = intern ("bottom"); + staticpro (&Qbottom); + Qend_scroll = intern ("end-scroll"); + staticpro (&Qend_scroll); Qevent_kind = intern ("event-kind"); staticpro (&Qevent_kind); @@ -7983,6 +9653,17 @@ syms_of_keyboard () Qpolling_period = intern ("polling-period"); staticpro (&Qpolling_period); + Qinput_method_function = intern ("input-method-function"); + staticpro (&Qinput_method_function); + + Qinput_method_exit_on_first_char = intern ("input-method-exit-on-first-char"); + staticpro (&Qinput_method_exit_on_first_char); + Qinput_method_use_echo_area = intern ("input-method-use-echo-area"); + staticpro (&Qinput_method_use_echo_area); + + Fset (Qinput_method_exit_on_first_char, Qnil); + Fset (Qinput_method_use_echo_area, Qnil); + { struct event_head *p; @@ -8017,6 +9698,9 @@ syms_of_keyboard () this_command_keys = Fmake_vector (make_number (40), Qnil); staticpro (&this_command_keys); + raw_keybuf = Fmake_vector (make_number (30), Qnil); + staticpro (&raw_keybuf); + Qextended_command_history = intern ("extended-command-history"); Fset (Qextended_command_history, Qnil); staticpro (&Qextended_command_history); @@ -8034,6 +9718,14 @@ syms_of_keyboard () mouse_syms = Qnil; staticpro (&mouse_syms); +#ifdef WINDOWSNT + mouse_wheel_syms = Qnil; + staticpro (&mouse_wheel_syms); + + drag_n_drop_syms = Qnil; + staticpro (&drag_n_drop_syms); +#endif + unread_switch_frame = Qnil; staticpro (&unread_switch_frame); @@ -8043,8 +9735,12 @@ syms_of_keyboard () read_key_sequence_cmd = Qnil; staticpro (&read_key_sequence_cmd); + menu_bar_one_keymap_changed_items = Qnil; + staticpro (&menu_bar_one_keymap_changed_items); + defsubr (&Sevent_convert_list); defsubr (&Sread_key_sequence); + defsubr (&Sread_key_sequence_vector); defsubr (&Srecursive_edit); #ifdef HAVE_MOUSE defsubr (&Strack_mouse); @@ -8053,8 +9749,11 @@ syms_of_keyboard () defsubr (&Scommand_execute); defsubr (&Srecent_keys); defsubr (&Sthis_command_keys); + defsubr (&Sthis_command_keys_vector); defsubr (&Sthis_single_command_keys); + defsubr (&Sthis_single_command_raw_keys); defsubr (&Sreset_this_command_lengths); + defsubr (&Sclear_this_command_keys); defsubr (&Ssuspend_emacs); defsubr (&Sabort_recursive_edit); defsubr (&Sexit_recursive_edit); @@ -8085,20 +9784,35 @@ so that you can determine whether the command was run by mouse or not."); "Last input event."); DEFVAR_LISP ("unread-command-events", &Vunread_command_events, - "List of objects to be read as next command input events."); + "List of events to be read as the command input.\n\ +These events are processed first, before actual keyboard input."); + Vunread_command_events = Qnil; DEFVAR_INT ("unread-command-char", &unread_command_char, "If not -1, an object to be read as next command input event."); + DEFVAR_LISP ("unread-post-input-method-events", &Vunread_post_input_method_events, + "List of events to be processed as input by input methods.\n\ +These events are processed after `unread-command-events', but\n\ +before actual keyboard input."); + Vunread_post_input_method_events = Qnil; + + DEFVAR_LISP ("unread-input-method-events", &Vunread_input_method_events, + "List of events to be processed as input by input methods.\n\ +These events are processed after `unread-command-events', but\n\ +before actual keyboard input."); + Vunread_input_method_events = Qnil; + DEFVAR_LISP ("meta-prefix-char", &meta_prefix_char, - "Meta-prefix character code. Meta-foo as command input\n\ -turns into this character followed by foo."); + "Meta-prefix character code.\n\ +Meta-foo as command input turns into this character followed by foo."); XSETINT (meta_prefix_char, 033); DEFVAR_KBOARD ("last-command", Vlast_command, - "The last command executed. Normally a symbol with a function definition,\n\ -but can be whatever was found in the keymap, or whatever the variable\n\ -`this-command' was set to by that command.\n\ + "The last command executed.\n\ +Normally a symbol with a function definition, but can be whatever was found\n\ +in the keymap, or whatever the variable `this-command' was set to by that\n\ +command.\n\ \n\ The value `mode-exit' is special; it means that the previous command\n\ read an event that told it to exit, and it did so and unread that event.\n\ @@ -8108,11 +9822,14 @@ command exit.\n\ The value `kill-region' is special; it means that the previous command\n\ was a kill command."); - DEFVAR_LISP ("this-command", &this_command, + DEFVAR_KBOARD ("real-last-command", Vreal_last_command, + "Same as `last-command', but never altered by Lisp code."); + + DEFVAR_LISP ("this-command", &Vthis_command, "The command now being executed.\n\ The command can set this variable; whatever is put here\n\ will be in `last-command' during the following command."); - this_command = Qnil; + Vthis_command = Qnil; DEFVAR_INT ("auto-save-interval", &auto_save_interval, "*Number of keyboard input characters between auto-saves.\n\ @@ -8149,16 +9866,25 @@ by position only."); inhibit_local_menu_bar_menus = 0; DEFVAR_INT ("num-input-keys", &num_input_keys, - "Number of complete key sequences read from the keyboard so far.\n\ + "Number of complete key sequences read as input so far.\n\ This includes key sequences read from keyboard macros.\n\ The number is effectively the number of interactive command invocations."); num_input_keys = 0; + DEFVAR_INT ("num-nonmacro-input-events", &num_nonmacro_input_events, + "Number of input events read from the keyboard so far.\n\ +This does not include events generated by keyboard macros."); + num_nonmacro_input_events = 0; + DEFVAR_LISP ("last-event-frame", &Vlast_event_frame, "The frame in which the most recently read event occurred.\n\ If the last event came from a keyboard macro, this is set to `macro'."); Vlast_event_frame = Qnil; + /* This variable is set up in sysdep.c. */ + DEFVAR_LISP ("tty-erase-char", &Vtty_erase_char, + "The ERASE character as set by the user with stty."); + 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\ @@ -8197,8 +9923,8 @@ In a vector or a char-table, an element which is nil means \"no translation\".") Vkeyboard_translate_table = Qnil; DEFVAR_BOOL ("cannot-suspend", &cannot_suspend, - "Non-nil means to always spawn a subshell instead of suspending,\n\ -even if the operating system has support for stopping a process."); + "Non-nil means to always spawn a subshell instead of suspending.\n\ +\(Even if the operating system has support for stopping a process.\)"); cannot_suspend = 0; DEFVAR_BOOL ("menu-prompting", &menu_prompting, @@ -8242,12 +9968,16 @@ Buffer modification stores t in this variable."); DEFVAR_LISP ("pre-command-hook", &Vpre_command_hook, "Normal hook run before each command is executed.\n\ -Errors running the hook are caught and ignored."); +If an unhandled error happens in running this hook,\n\ +the hook value is set to nil, since otherwise the error\n\ +might happen repeatedly and make Emacs nonfunctional."); Vpre_command_hook = Qnil; DEFVAR_LISP ("post-command-hook", &Vpost_command_hook, "Normal hook run after each command is executed.\n\ -Errors running the hook are caught and ignored."); +If an unhandled error happens in running this hook,\n\ +the hook value is set to nil, since otherwise the error\n\ +might happen repeatedly and make Emacs nonfunctional."); Vpost_command_hook = Qnil; DEFVAR_LISP ("post-command-idle-hook", &Vpost_command_idle_hook, @@ -8279,9 +10009,11 @@ The elements of the list are event types that may have menu bar bindings."); DEFVAR_KBOARD ("overriding-terminal-local-map", Voverriding_terminal_local_map, - "Keymap that overrides all other local keymaps.\n\ + "Per-terminal keymap that overrides all other local keymaps.\n\ If this variable is non-nil, it is used as a keymap instead of the\n\ -buffer's local map, and the minor mode keymaps and text property keymaps."); +buffer's local map, and the minor mode keymaps and text property keymaps.\n\ +This variable is intended to let commands such as `universal-argumemnt'\n\ +set up a different keymap for reading the next command."); DEFVAR_LISP ("overriding-local-map", &Voverriding_local_map, "Keymap that overrides all other local keymaps.\n\ @@ -8320,7 +10052,7 @@ whenever `deferred-action-list' is non-nil."); Vdeferred_action_function = Qnil; DEFVAR_LISP ("suggest-key-bindings", &Vsuggest_key_bindings, - "Non-nil means show the equivalent key-binding when M-x command has one.\n\ + "*Non-nil means show the equivalent key-binding when M-x command has one.\n\ The value can be a length of time to show the message for.\n\ If the value is non-nil and not a number, we wait 2 seconds."); Vsuggest_key_bindings = Qt; @@ -8332,8 +10064,42 @@ If the value is non-nil and not a number, we wait 2 seconds."); DEFVAR_LISP ("timer-idle-list", &Vtimer_idle_list, "List of active idle-time timers in order of increasing time"); Vtimer_idle_list = Qnil; + + DEFVAR_LISP ("input-method-function", &Vinput_method_function, + "If non-nil, the function that implements the current input method.\n\ +It's called with one argument, a printing character that was just read.\n\ +\(That means a character with code 040...0176.)\n\ +Typically this function uses `read-event' to read additional events.\n\ +When it does so, it should first bind `input-method-function' to nil\n\ +so it will not be called recursively.\n\ +\n\ +The function should return a list of zero or more events\n\ +to be used as input. If it wants to put back some events\n\ +to be reconsidered, separately, by the input method,\n\ +it can add them to the beginning of `unread-command-events'.\n\ +\n\ +The input method function can find in `input-method-previous-method'\n\ +the previous echo area message.\n\ +\n\ +The input method function should refer to the variables\n\ +`input-method-use-echo-area' and `input-method-exit-on-first-char'\n\ +for guidance on what to do."); + Vinput_method_function = Qnil; + + DEFVAR_LISP ("input-method-previous-message", + &Vinput_method_previous_message, + "When `input-method-function' is called, hold the previous echo area message.\n\ +This variable exists because `read-event' clears the echo area\n\ +before running the input method. It is nil if there was no message."); + Vinput_method_previous_message = Qnil; + + DEFVAR_LISP ("show-help-function", &Vshow_help_function, + "If non-nil, the function that implements the display of help.\n\ +It's called with one argument, the help string to display."); + Vshow_help_function = Qnil; } +void keys_of_keyboard () { initial_define_key (global_map, Ctl ('Z'), "suspend-emacs");