X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/0afc861ba9d0f4780fe4031eeecdf660fbe86cfc..0377370fbbff24ff79669d5d776c4590053b42fc:/src/keyboard.c diff --git a/src/keyboard.c b/src/keyboard.c index 9b6c076558..a50322f425 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -166,6 +166,11 @@ 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; @@ -179,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; @@ -459,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 @@ -482,6 +496,7 @@ 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; @@ -600,6 +615,7 @@ 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; @@ -731,9 +747,9 @@ echo_now () } echoing = 1; + 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)) @@ -749,6 +765,7 @@ cancel_echoing () 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. */ @@ -1014,17 +1031,26 @@ cmd_error_internal (data, context) 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 (); @@ -1039,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)); @@ -1222,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 @@ -1307,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; @@ -1421,7 +1449,6 @@ command_loop_1 () { unsigned int c = XINT (last_command_char); int value; - if (NILP (Vexecuting_macro) && !EQ (minibuf_window, selected_window)) { @@ -1432,6 +1459,7 @@ command_loop_1 () } nonundocount++; } + lose = ((XFASTINT (XWINDOW (selected_window)->last_modified) < MODIFF) || (XFASTINT (XWINDOW (selected_window)->last_overlay_modified) @@ -1444,58 +1472,33 @@ 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_BYTE) == '\n')) - { - struct Lisp_Char_Table *dp - = window_display_table (XWINDOW (selected_window)); - int lose = c; - - /* Add the offset to the character, for Finsert_char. - We pass internal_self_insert the unmodified character - because it itself does this offsetting. */ - if (! NILP (current_buffer->enable_multibyte_characters)) - lose = unibyte_char_to_multibyte (lose); - - 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 (); @@ -1810,7 +1813,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) jmp_buf save_jump; int key_already_recorded = 0; Lisp_Object tem, save; - Lisp_Object echo_area_message; + Lisp_Object previous_echo_area_message; Lisp_Object also_record; int reread; struct gcpro gcpro1, gcpro2; @@ -1820,9 +1823,9 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) before_command_key_count = this_command_key_count; before_command_echo_length = echo_length (); c = Qnil; - echo_area_message = Qnil; + previous_echo_area_message = Qnil; - GCPRO2 (c, echo_area_message); + GCPRO2 (c, previous_echo_area_message); retry: @@ -1958,9 +1961,13 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) } /* 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. */ @@ -2034,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. */ @@ -2081,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) @@ -2333,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); @@ -2357,12 +2373,15 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) && ! NILP (Vinput_method_function) && (unsigned) XINT (c) >= ' ' && (unsigned) XINT (c) < 127) - Vinput_method_previous_message = echo_area_message = Fcurrent_message (); + { + previous_echo_area_message = Fcurrent_message (); + Vinput_method_previous_message = previous_echo_area_message; + } /* Now wipe the echo area. */ - if (echo_area_glyphs) + if (!NILP (echo_area_buffer[0])) safe_run_hooks (Qecho_area_clear_hook); - echo_area_glyphs = 0; + clear_message (1, 0); reread_for_input_method: from_macro: @@ -2382,7 +2401,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) /* Save the echo status. */ int saved_immediate_echo = current_kboard->immediate_echo; - char *saved_ok_to_echo = ok_to_echo_at_next_pause; + 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) @@ -2407,9 +2426,9 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) this_command_key_count = 0; /* Now wipe the echo area. */ - if (echo_area_glyphs) + if (!NILP (echo_area_buffer[0])) safe_run_hooks (Qecho_area_clear_hook); - echo_area_glyphs = 0; + clear_message (1, 0); echo_truncate (0); /* If we are not reading a key sequence, @@ -2442,8 +2461,8 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) if (! CONSP (tem)) { /* Bring back the previous message, if any. */ - if (! NILP (echo_area_message)) - message_with_string ("%s", echo_area_message, 0); + if (! NILP (previous_echo_area_message)) + message_with_string ("%s", previous_echo_area_message, 0); goto retry; } /* It returned one event or more. */ @@ -2454,6 +2473,25 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) reread_first: + /* Display help if not echoing. */ + if (CONSP (c) + && EQ (XCAR (c), Qhelp_echo)) + { + 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; @@ -2469,7 +2507,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu) 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; + ok_to_echo_at_next_pause = current_kboard; } /* Record this character as part of the current key. */ @@ -2522,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); @@ -2855,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) { @@ -3100,7 +3138,13 @@ kbd_buffer_get_event (kbp, used_mouse_menu) 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 @@ -3135,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 @@ -3471,6 +3517,9 @@ timer_check (do_it_now) 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. */ @@ -3478,9 +3527,17 @@ timer_check (do_it_now) specbind (Qinhibit_quit, Qt); +#ifdef HAVE_WINDOW_SYSTEM + inhibit_busy_cursor = 2; +#endif + call1 (Qtimer_event_handler, chosen_timer); timers_run++; +#ifdef HAVE_WINDOW_SYSTEM + inhibit_busy_cursor = old_inhibit_busy_cursor; +#endif + unbind_to (count, Qnil); /* Resume allowing input from any kboard, if that was true before. */ @@ -3946,12 +4003,21 @@ static char *lispy_drag_n_drop_names[] = /* 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 }; @@ -4087,7 +4153,9 @@ make_lispy_event (event) /* 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; @@ -4105,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 @@ -4112,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 @@ -4138,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) @@ -4155,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, @@ -4166,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)) { @@ -4175,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 @@ -4197,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; @@ -4216,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]; @@ -4327,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: { @@ -4389,7 +4538,7 @@ make_lispy_event (event) 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); + window = window_from_coordinates (f, column, row, &part, 0); if (!WINDOWP (window)) { @@ -4409,6 +4558,8 @@ make_lispy_event (event) 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), @@ -4462,7 +4613,7 @@ make_lispy_event (event) 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); + window = window_from_coordinates (f, column, row, &part, 0); if (!WINDOWP (window)) { @@ -4471,21 +4622,27 @@ make_lispy_event (event) } 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); + /* 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 (XWINDOW (window), - column, row)); + XSETINT (posn, buffer_posn_from_coords (w, &wx, &wy)); } { @@ -4518,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 (); @@ -4556,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) { @@ -5834,7 +6006,7 @@ menu_item_eval_property_1 (arg) /* Evaluate an expression and return the result (or nil if something went wrong). Used to evaluate dynamic parts of menu items. */ -static Lisp_Object +Lisp_Object menu_item_eval_property (sexpr) Lisp_Object sexpr; { @@ -6194,6 +6366,392 @@ parse_menu_item (item, notreal, inmenubar) 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; + + /* 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))) + { + 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)) + { + /* `: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 (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)) + { + /* `:button (TYPE . SELECTED)'. */ + Lisp_Object type, selected; + + type = XCAR (value); + selected = XCDR (value); + if (EQ (type, QCtoggle) || EQ (type, QCradio)) + { + 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; + } + + /* 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)))); + + /* 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 +} + + +/* Initialize Vtoolbar_items. REUSE, if non-nil, is a vector that can + be reused. */ + +static void +init_toolbar_items (reuse) + Lisp_Object reuse; +{ + if (VECTORP (reuse)) + toolbar_items_vector = reuse; + else + toolbar_items_vector = Fmake_vector (make_number (64), Qnil); + ntoolbar_items = 0; +} + + +/* Append parsed toolbar item properties from toolbar_item_properties */ + +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; + } + + /* 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; +} + + + + /* 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. @@ -6257,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 @@ -7102,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 @@ -7142,15 +7702,18 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last, 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); @@ -7179,11 +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); + + /* 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; } } @@ -7196,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"); @@ -7478,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; @@ -7595,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; } @@ -8071,7 +8658,7 @@ DEFUN ("execute-extended-command", Fexecute_extended_command, Sexecute_extended_ { /* But first wait, and skip the message if there is input. */ int delay_time; - if (echo_area_glyphs != 0) + 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) @@ -8086,9 +8673,7 @@ DEFUN ("execute-extended-command", Fexecute_extended_command, Sexecute_extended_ { Lisp_Object binding; char *newmessage; - char *oldmessage = echo_area_glyphs; - int oldmessage_len = echo_area_glyphs_length; - int oldmultibyte = message_enable_multibyte; + int message_p = push_message (); binding = Fkey_description (bindings); @@ -8104,8 +8689,11 @@ DEFUN ("execute-extended-command", Fexecute_extended_command, Sexecute_extended_ STRING_MULTIBYTE (binding)); if (!NILP (Fsit_for ((NUMBERP (Vsuggest_key_bindings) ? Vsuggest_key_bindings : make_number (2)), - Qnil, Qnil))) - message2_nolog (oldmessage, oldmessage_len, oldmultibyte); + Qnil, Qnil)) + && message_p) + restore_message (); + + pop_message (); } } @@ -8176,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; } @@ -8355,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; @@ -8412,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)) @@ -8644,7 +9238,6 @@ interrupt_signal (signalnum) /* If we don't have an argument, */ void quit_throw_to_read_char () { - quit_error_check (); sigfree (); /* Prevent another signal from doing this before we finish. */ clear_waiting_for_input (); @@ -8922,9 +9515,21 @@ struct event_head head_table[] = { 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; @@ -8977,6 +9582,11 @@ syms_of_keyboard () 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"); @@ -8985,6 +9595,8 @@ syms_of_keyboard () staticpro (&QCenable); QCvisible = intern (":visible"); staticpro (&QCvisible); + QChelp = intern (":help"); + staticpro (&QChelp); QCfilter = intern (":filter"); staticpro (&QCfilter); QCbutton = intern (":button"); @@ -9017,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); @@ -9474,6 +10092,11 @@ for guidance on what to do."); 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