Mercurial > hg > xemacs-beta
changeset 1268:fffe735e63ee
[xemacs-hg @ 2003-02-07 11:50:50 by ben]
fixes for menu crashes + better preemption behavior
This contains two related changes:
(1) Fix problems with reentrant calling of lwlib and associated
crashes when selecting menu items.
(2) Improve redisplay handling of preemption. Turn on lazy lock
and hold down page-down or page-up and you'll see what I mean.
They are related because they both touch on the code that retrieves
events and handles the internal queues.
console-msw.h, event-msw.c, event-stream.c, events.h, menubar-msw.c, menubar-x.c, menubar.h: mswindows_protect_modal_loop() has been generalized to
event_stream_protect_modal_loop(), and moved to event-stream.c.
mswindows_in_modal_loop ->in_modal_loop likewise. Changes in
event-msw.c and menubar-msw.c for the new names and calling format
(use structures instead of static variables in menubar-msw.c).
Delete former in_menu_callback and use in_modal_loop in its place.
Remove emacs_mswindows_quit_check_disallowed_p(), superseded by
in_modal_loop. Use event_stream_protect_modal_loop() in
pre_activate_callback() so that we get no lwlib reentrancy.
Rearrange some of the code in event-msw.c to be grouped better.
Make mswindows_drain_windows_queue() respect in_modal_loop and
do nothing if so.
cmdloop.c, event-stream.c: Don't conditionalize on LWLIB_MENUBARS_LUCID when giving error when
in_modal_loop, and give better error.
event-Xt.c, event-gtk.c: If in_modal_loop, only retrieve process and timeout events.
Don't retrieve any X events because processing them can lead
to reentrancy in lwlib -> death.
event-stream.c: Remove unused parameter to check_event_stream_ok() and change
all callers.
lisp.h, event-stream.c: Rearrange some functions for increased clarity -- in particular,
group all the input-pending/QUIT-related stuff together, and
put right next to next-event stuff, to which it's related.
Add the concept of "HOW_MANY" -- when asking whether user input
is pending, you can ask if at least HOW_MANY events are pending,
not just if any are. Add parameter to detect_input_pending()
for this. Change recursive_sit_for from a Lisp_Object (which
could only be Qt or Qnil) to an int, like it should be.
event-Xt.c, event-gtk.c, event-xlike-inc.c: New file.
Abstract out similar code in event_{Xt/gtk}_pending_p() and write
only once, using include-file tricks. Rewrite this function to
implement HOW_MANY and only process events when not in_modal_loop.
event-msw.c: Implement HOW_MANY and only process events when not in_modal_loop.
event-tty.c: Implement HOW_MANY.
redisplay.c: Add var `max-preempts' to control maximum number of preempts.
(#### perhaps not useful) Rewrite preemption check so that,
rather than preempting when any user events are available, only
preempt when a certain number (currently 4) of them are backed up.
This effectively allows redisplay to proceed to completion in the
presence of a fast auto-repeat (usually the auto-repeating is
generated dynamically as necessary), and you get much better
display behavior with lazy-lock active.
event-unixoid.c: Comment changes.
event-stream.c: Rewrite discard-input much more simply and safely using the
drain-queue functions. I think the old version might loop
forever if called when in_modal_loop.
SEMI-UNRELATED CHANGES:
-----------------------
event-stream.c: Turn QUIT-checking back on when running the pre-idle hook so it
can be quit out of.
indent.c: Document exact functioning of `vertical-motion' better, and its
differences from GNU Emacs.
author | ben |
---|---|
date | Fri, 07 Feb 2003 11:50:54 +0000 |
parents | c57f32e44416 |
children | 13aa577c1dd2 |
files | src/ChangeLog src/cmdloop.c src/console-msw.h src/event-Xt.c src/event-gtk.c src/event-msw.c src/event-stream.c src/event-tty.c src/event-unixoid.c src/event-xlike-inc.c src/events.h src/indent.c src/lisp.h src/menubar-msw.c src/menubar-x.c src/menubar.h src/redisplay.c |
diffstat | 17 files changed, 979 insertions(+), 927 deletions(-) [+] |
line wrap: on
line diff
--- a/src/ChangeLog Fri Feb 07 01:43:07 2003 +0000 +++ b/src/ChangeLog Fri Feb 07 11:50:54 2003 +0000 @@ -1,3 +1,158 @@ +2003-02-07 Ben Wing <ben@xemacs.org> + + This contains two related changes: + + (1) Fix problems with reentrant calling of lwlib and associated + crashes when selecting menu items. + (2) Improve redisplay handling of preemption. Turn on lazy lock + and hold down page-down or page-up and you'll see what I mean. + + They are related because they both touch on the code that retrieves + events and handles the internal queues. + + * console-msw.h: + * event-msw.c: + * event-msw.c (mswindows_unmodalize_signal_maybe): + * event-msw.c (mswindows_unsafe_pump_events): + * event-msw.c (mswindows_pump_outstanding_events): + * event-msw.c (mswindows_need_event_in_modal_loop): + * event-msw.c (mswindows_drain_windows_queue): + * event-msw.c (mswindows_need_event): + * event-msw.c (reinit_vars_of_event_mswindows): + * event-stream.c (event_stream_protect_modal_loop): + * events.h: + * events.h (struct event_stream): + * menubar-msw.c (struct handle_wm_initmenu): + * menubar-msw.c (unsafe_handle_wm_initmenupopup): + * menubar-msw.c (unsafe_handle_wm_initmenu): + * menubar-msw.c (mswindows_handle_wm_initmenupopup): + * menubar-msw.c (mswindows_handle_wm_initmenu): + * menubar-x.c: + * menubar-x.c (protected_menu_item_descriptor_to_widget_value): + * menubar-x.c (pre_activate_callback): + * menubar.h: + + mswindows_protect_modal_loop() has been generalized to + event_stream_protect_modal_loop(), and moved to event-stream.c. + mswindows_in_modal_loop ->in_modal_loop likewise. Changes in + event-msw.c and menubar-msw.c for the new names and calling format + (use structures instead of static variables in menubar-msw.c). + Delete former in_menu_callback and use in_modal_loop in its place. + Remove emacs_mswindows_quit_check_disallowed_p(), superseded by + in_modal_loop. Use event_stream_protect_modal_loop() in + pre_activate_callback() so that we get no lwlib reentrancy. + + Rearrange some of the code in event-msw.c to be grouped better. + Make mswindows_drain_windows_queue() respect in_modal_loop and + do nothing if so. + + * cmdloop.c (command_loop_3): + * event-stream.c (Fnext_event): + Don't conditionalize on LWLIB_MENUBARS_LUCID when giving error when + in_modal_loop, and give better error. + + * event-Xt.c (emacs_Xt_next_event): + * event-Xt.c (emacs_Xt_drain_queue): + * event-gtk.c (emacs_gtk_drain_queue): + If in_modal_loop, only retrieve process and timeout events. + Don't retrieve any X events because processing them can lead + to reentrancy in lwlib -> death. + + * event-stream.c: + * event-stream.c (check_event_stream_ok): + * event-stream.c (event_stream_handle_magic_event): + * event-stream.c (event_stream_format_magic_event): + * event-stream.c (event_stream_compare_magic_event): + * event-stream.c (event_stream_hash_magic_event): + * event-stream.c (event_stream_add_timeout): + * event-stream.c (event_stream_remove_timeout): + * event-stream.c (event_stream_select_console): + * event-stream.c (event_stream_unselect_console): + * event-stream.c (event_stream_select_process): + * event-stream.c (event_stream_unselect_process): + * event-stream.c (event_stream_create_io_streams): + * event-stream.c (event_stream_delete_io_streams): + * event-stream.c (event_stream_current_event_timestamp): + * event-stream.c (event_stream_next_event): + Remove unused parameter to check_event_stream_ok() and change + all callers. + + * lisp.h: + * event-stream.c (maybe_do_auto_save): + * event-stream.c (emacs_handle_focus_change_preliminary): + * event-stream.c (emacs_handle_focus_change_final): + * event-stream.c (detect_input_pending): + * event-stream.c (maybe_read_quit_event): + * event-stream.c (remove_quit_p_event): + * event-stream.c (event_stream_quit_p): + * event-stream.c (event_stream_next_event): + * event-stream.c (sit_for_unwind): + * event-stream.c (Faccept_process_output): + * event-stream.c (Fsleep_for): + * event-stream.c (Fsit_for): + * event-stream.c (maybe_kbd_translate): + * event-stream.c (command_builder_find_leaf_no_mule_processing): + * event-stream.c (reinit_vars_of_event_stream): + Rearrange some functions for increased clarity -- in particular, + group all the input-pending/QUIT-related stuff together, and + put right next to next-event stuff, to which it's related. + Add the concept of "HOW_MANY" -- when asking whether user input + is pending, you can ask if at least HOW_MANY events are pending, + not just if any are. Add parameter to detect_input_pending() + for this. Change recursive_sit_for from a Lisp_Object (which + could only be Qt or Qnil) to an int, like it should be. + + * event-Xt.c: + * event-Xt.c (THIS_IS_X): + * event-gtk.c: + * event-gtk.c (THIS_IS_GTK): + * event-xlike-inc.c: New file. + Abstract out similar code in event_{Xt/gtk}_pending_p() and write + only once, using include-file tricks. Rewrite this function to + implement HOW_MANY and only process events when not in_modal_loop. + + * event-msw.c (emacs_mswindows_event_pending_p): + Implement HOW_MANY and only process events when not in_modal_loop. + + * event-tty.c: + * event-tty.c (tty_timeout_to_emacs_event): + * event-tty.c (emacs_tty_event_pending_p): + Implement HOW_MANY. + + * redisplay.c: + * redisplay.c (QUEUED_EVENTS_REQUIRED_FOR_PREEMPTION): + * redisplay.c (init_redisplay): + * redisplay.c (vars_of_redisplay): + Add var `max-preempts' to control maximum number of preempts. + (#### perhaps not useful) Rewrite preemption check so that, + rather than preempting when any user events are available, only + preempt when a certain number (currently 4) of them are backed up. + This effectively allows redisplay to proceed to completion in the + presence of a fast auto-repeat (usually the auto-repeating is + generated dynamically as necessary), and you get much better + display behavior with lazy-lock active. + + * event-unixoid.c: + Comment changes. + + * event-stream.c (command_event_p_cb): + * event-stream.c (Fdiscard_input): + Rewrite discard-input much more simply and safely using the + drain-queue functions. I think the old version might loop + forever if called when in_modal_loop. + + SEMI-UNRELATED CHANGES: + ----------------------- + + * event-stream.c (run_pre_idle_hook): + Turn QUIT-checking back on when running the pre-idle hook so it + can be quit out of. + + * indent.c: + Document exact functioning of `vertical-motion' better, and its + differences from GNU Emacs. + + 2003-02-06 Ben Wing <ben@xemacs.org> * unicode.c:
--- a/src/cmdloop.c Fri Feb 07 01:43:07 2003 +0000 +++ b/src/cmdloop.c Fri Feb 07 11:50:54 2003 +0000 @@ -238,26 +238,13 @@ static DOESNT_RETURN command_loop_3 (void) { -#ifdef LWLIB_MENUBARS_LUCID - extern int in_menu_callback; /* defined in menubar-x.c */ -#endif /* LWLIB_MENUBARS_LUCID */ - -#ifdef LWLIB_MENUBARS_LUCID /* - * #### Fix the menu code so this isn't necessary. - * - * We cannot allow the lwmenu code to be reentered, because the - * code is not written to be reentrant and will crash. Therefore - * paths from the menu callbacks back into the menu code have to - * be blocked. Fnext_event is the normal path into the menu code, - * but waiting to signal an error there is too late in case where - * a new command loop has been started. The error will be caught - * and Fnext_event will be called again, looping forever. So we - * signal an error here to avoid the loop. + * If we are inside of a menu callback we cannot reenter the command loop + * because we will deadlock, as no input is allowed. */ - if (in_menu_callback) - invalid_operation ("Attempt to enter command_loop_3 inside menu callback", Qunbound); -#endif /* LWLIB_MENUBARS_LUCID */ + if (in_modal_loop) + invalid_operation ("Attempt to enter command loop inside menu callback", + Qunbound); /* This function can GC */ for (;;) {
--- a/src/console-msw.h Fri Feb 07 01:43:07 2003 +0000 +++ b/src/console-msw.h Fri Feb 07 11:50:54 2003 +0000 @@ -124,10 +124,6 @@ Lisp_Object object); Lisp_Object mswindows_cancel_dispatch_event (Lisp_Event *event); Lisp_Object mswindows_pump_outstanding_events (void); -Lisp_Object mswindows_protect_modal_loop (const char *error_string, - Lisp_Object (*bfun) - (Lisp_Object barg), - Lisp_Object barg, int flags); void mswindows_unmodalize_signal_maybe (void); COLORREF mswindows_string_to_color (const Ibyte *name);
--- a/src/event-Xt.c Fri Feb 07 01:43:07 2003 +0000 +++ b/src/event-Xt.c Fri Feb 07 11:50:54 2003 +0000 @@ -1,7 +1,7 @@ /* The event_stream interface for X11 with Xt, and/or tty frames. Copyright (C) 1991-5, 1997 Free Software Foundation, Inc. Copyright (C) 1995 Sun Microsystems, Inc. - Copyright (C) 1996, 2001, 2002 Ben Wing. + Copyright (C) 1996, 2001, 2002, 2003 Ben Wing. This file is part of XEmacs. @@ -98,7 +98,8 @@ static Widget widget_with_focus; /* Mask of bits indicating the descriptors that we wait for input on */ -extern SELECT_TYPE input_wait_mask, process_only_mask, tty_only_mask; +extern SELECT_TYPE input_wait_mask, non_fake_input_wait_mask; +extern SELECT_TYPE process_only_mask, tty_only_mask; static const String x_fallback_resources[] = { @@ -121,6 +122,9 @@ Lisp_Object Qkey_mapping; Lisp_Object Qsans_modifiers; +#define THIS_IS_X +#include "event-xlike-inc.c" + /************************************************************************/ /* keymap handling */ @@ -2771,39 +2775,62 @@ !process_events_occurred && !tty_events_occurred) { - - /* Stupid logic in XtAppProcessEvent() dictates that, if process - events and X events are both available, the process event gets - taken first. This will cause an infinite loop if we're being - called from Fdiscard_input(). - */ - if (XtAppPending (Xt_app_con) & XtIMXEvent) - XtAppProcessEvent (Xt_app_con, XtIMXEvent); + if (in_modal_loop) + { + /* in_modal_loop gets set when we are in the process of + dispatching an event (more specifically, when we are inside of + a menu callback -- if we get here, it means we called a filter + and the filter did something that tried to fetch an event, + e.g. sit-for). In such a case, we cannot safely dispatch any + more events. This is because those dispatching those events + could cause lwlib to be entered reentranty, specifically if + they are menu events. lwlib is not designed for this and will + crash. We used to see this crash constantly as a result of + QUIT checking, but QUIT will not now function in a modal loop. + However, we can't just not process any events at all, because + that will make sit-for etc. hang. So we go ahead and process + the non-X kinds of events. */ + XtInputMask pending_value = XtAppPending (Xt_app_con); + + if (pending_value & (XtIMTimer | XtIMAlternateInput)) + XtAppProcessEvent (Xt_app_con, XtIMTimer | XtIMAlternateInput); + } else { - Lisp_Object devcons, concons; - - /* We're about to block. Xt has a bug in it (big surprise, - there) in that it blocks using select() and doesn't - flush the Xlib output buffers (XNextEvent() does this - automatically before blocking). So it's necessary - for us to do this ourselves. If we don't do it, then - display output may not be seen until the next time - an X event is received. (This happens esp. with - subprocess output that gets sent to a visible buffer.) - - #### The above comment may not have any validity. */ - - DEVICE_LOOP_NO_BREAK (devcons, concons) + /* Stupid logic in XtAppProcessEvent() dictates that, if process + events and X events are both available, the process event gets + taken first. This will cause an infinite loop if we're being + called from Fdiscard_input(). + */ + + if (XtAppPending (Xt_app_con) & XtIMXEvent) + XtAppProcessEvent (Xt_app_con, XtIMXEvent); + else { - struct device *d; - d = XDEVICE (XCAR (devcons)); - - if (DEVICE_X_P (d) && DEVICE_X_DISPLAY (d)) - /* emacs may be exiting */ - XFlush (DEVICE_X_DISPLAY (d)); + Lisp_Object devcons, concons; + + /* We're about to block. Xt has a bug in it (big surprise, + there) in that it blocks using select() and doesn't + flush the Xlib output buffers (XNextEvent() does this + automatically before blocking). So it's necessary + for us to do this ourselves. If we don't do it, then + display output may not be seen until the next time + an X event is received. (This happens esp. with + subprocess output that gets sent to a visible buffer.) + + #### The above comment may not have any validity. */ + + DEVICE_LOOP_NO_BREAK (devcons, concons) + { + struct device *d; + d = XDEVICE (XCAR (devcons)); + + if (DEVICE_X_P (d) && DEVICE_X_DISPLAY (d)) + /* emacs may be exiting */ + XFlush (DEVICE_X_DISPLAY (d)); + } + XtAppProcessEvent (Xt_app_con, XtIMAll); } - XtAppProcessEvent (Xt_app_con, XtIMAll); } } @@ -2864,154 +2891,36 @@ emacs_Xt_drain_queue (void) { Lisp_Object devcons, concons; - CONSOLE_LOOP (concons) + if (!in_modal_loop) { - struct console *con = XCONSOLE (XCAR (concons)); - if (!con->input_enabled) - continue; - - CONSOLE_DEVICE_LOOP (devcons, con) + CONSOLE_LOOP (concons) { - struct device *d; - Display *display; - d = XDEVICE (XCAR (devcons)); - if (DEVICE_X_P (d) && DEVICE_X_DISPLAY (d)) + struct console *con = XCONSOLE (XCAR (concons)); + if (!con->input_enabled) + continue; + + CONSOLE_DEVICE_LOOP (devcons, con) { - display = DEVICE_X_DISPLAY (d); - while (XEventsQueued (display, QueuedAfterReading)) - XtAppProcessEvent (Xt_app_con, XtIMXEvent); + struct device *d; + Display *display; + d = XDEVICE (XCAR (devcons)); + if (DEVICE_X_P (d) && DEVICE_X_DISPLAY (d)) + { + display = DEVICE_X_DISPLAY (d); + while (XEventsQueued (display, QueuedAfterReading)) + XtAppProcessEvent (Xt_app_con, XtIMXEvent); + } } } + /* + while (XtAppPending (Xt_app_con) & XtIMXEvent) + XtAppProcessEvent (Xt_app_con, XtIMXEvent); + */ } - /* - while (XtAppPending (Xt_app_con) & XtIMXEvent) - XtAppProcessEvent (Xt_app_con, XtIMXEvent); - */ - + +#ifdef HAVE_TTY drain_tty_devices (); -} - -static int -emacs_Xt_event_pending_p (int user_p) -{ - Lisp_Object event; - int tick_count_val; - - /* If `user_p' is false, then this function returns whether there are any - X, timeout, or fd events pending (that is, whether emacs_Xt_next_event() - would return immediately without blocking). - - if `user_p' is true, then this function returns whether there are any - *user generated* events available (that is, whether there are keyboard - or mouse-click events ready to be read). This also implies that - emacs_Xt_next_event() would not block. - - In a non-SIGIO world, this also checks whether the user has typed ^G, - since this is a convenient place to do so. We don't need to do this - in a SIGIO world, since input causes an interrupt. - */ - -#if 0 - /* I don't think there's any point to this and it will nullify - the speed gains achieved by the sigio_happened checking below. - Its only advantage is that it may possibly make C-g response - a bit faster. The C-g will be noticed within 0.25 second, anyway, - even without this. */ -#ifndef SIGIO - /* First check for C-g if necessary */ - event_stream_quit_p (); -#endif #endif - - /* This function used to simply check whether there were any X - events (or if user_p was 1, it iterated over all the pending - X events using XCheckIfEvent(), looking for keystrokes and - button events). That worked in the old cheesoid event loop, - which didn't go through XtAppDispatchEvent(), but it doesn't - work any more -- X events may not result in anything. For - example, a button press in a blank part of the menubar appears - as an X event but will not result in any Emacs events (a - button press that activates the menubar results in an Emacs - event through the stop_next_event mechanism). - - The only accurate way of determining whether these X events - translate into Emacs events is to go ahead and dispatch them - until there's something on the dispatch queue. */ - - /* See if there are any user events already on the queue. */ - EVENT_CHAIN_LOOP (event, dispatch_event_queue) - if (!user_p || command_event_p (event)) - return 1; - - /* See if there's any TTY input available. - */ - if (poll_fds_for_input (tty_only_mask)) - return 1; - - if (!user_p) - { - /* If not user_p and there are any timer or file-desc events - pending, we know there will be an event so we're through. */ - XtInputMask pending_value; - - /* Note that formerly we just checked the value of XtAppPending() - to determine if there was file-desc input. This doesn't - work any more with the signal_event_pipe; XtAppPending() - will says "yes" in this case but there isn't really any - input. Another way of fixing this problem is for the - signal_event_pipe to generate actual input in the form - of an identity eval event or something. (#### maybe this - actually happens?) */ - - if (poll_fds_for_input (process_only_mask)) - return 1; - - pending_value = XtAppPending (Xt_app_con); - - if (pending_value & XtIMTimer) - return 1; - } - - /* XtAppPending() can be super-slow, esp. over a network connection. - Quantify results have indicated that in some cases the call to - detect_input_pending() completely dominates the running time of - redisplay(). Fortunately, in a SIGIO world we can more quickly - determine whether there are any X events: if an event has - happened since the last time we checked, then a SIGIO will have - happened. On a machine with broken SIGIO, we'll still be in an - OK state -- quit_check_signal_tick_count will get ticked at least - every 1/4 second, so we'll be no more than that much behind - reality. (In general it's OK if we erroneously report no input - pending when input is actually pending() -- preemption is just a - bit less efficient, that's all. It's bad bad bad if you err the - other way -- you've promised that `next-event' won't block but it - actually will, and some action might get delayed until the next - time you hit a key.) - */ - - /* quit_check_signal_tick_count is volatile so try to avoid race conditions - by using a temporary variable */ - tick_count_val = quit_check_signal_tick_count; - if (last_quit_check_signal_tick_count != tick_count_val -#if !defined (SIGIO) || defined (CYGWIN) - || (XtIMXEvent & XtAppPending (Xt_app_con)) -#endif - ) - { - last_quit_check_signal_tick_count = tick_count_val; - - /* We need to drain the entire queue now -- if we only - drain part of it, we may later on end up with events - actually pending but detect_input_pending() returning - false because there wasn't another SIGIO. */ - emacs_Xt_drain_queue (); - - EVENT_CHAIN_LOOP (event, dispatch_event_queue) - if (!user_p || command_event_p (event)) - return 1; - } - - return 0; } int
--- a/src/event-gtk.c Fri Feb 07 01:43:07 2003 +0000 +++ b/src/event-gtk.c Fri Feb 07 11:50:54 2003 +0000 @@ -1,7 +1,7 @@ /* The event_stream interface for X11 with gtk, and/or tty frames. Copyright (C) 1991-5, 1997 Free Software Foundation, Inc. Copyright (C) 1995 Sun Microsystems, Inc. - Copyright (C) 1996, 2001, 2002 Ben Wing. + Copyright (C) 1996, 2001, 2002, 2003 Ben Wing. Copyright (C) 2000 William Perry. This file is part of XEmacs. @@ -93,6 +93,8 @@ || ((keysym) == GDK_Mode_switch) \ || ((keysym) == GDK_Num_Lock)) +#define THIS_IS_GTK +#include "event-xlike-inc.c" /************************************************************************/ @@ -1556,120 +1558,9 @@ while (gdk_events_pending ()) gtk_main_iteration (); +#ifdef HAVE_TTY drain_tty_devices (); -} - -static int -emacs_gtk_event_pending_p (int user_p) -{ - Lisp_Object event; - int tick_count_val; - - /* If `user_p' is false, then this function returns whether there are any - X, timeout, or fd events pending (that is, whether emacs_gtk_next_event() - would return immediately without blocking). - - if `user_p' is true, then this function returns whether there are any - *user generated* events available (that is, whether there are keyboard - or mouse-click events ready to be read). This also implies that - emacs_Xt_next_event() would not block. - - In a non-SIGIO world, this also checks whether the user has typed ^G, - since this is a convenient place to do so. We don't need to do this - in a SIGIO world, since input causes an interrupt. - */ - - /* This function used to simply check whether there were any X - events (or if user_p was 1, it iterated over all the pending - X events using XCheckIfEvent(), looking for keystrokes and - button events). That worked in the old cheesoid event loop, - which didn't go through XtAppDispatchEvent(), but it doesn't - work any more -- X events may not result in anything. For - example, a button press in a blank part of the menubar appears - as an X event but will not result in any Emacs events (a - button press that activates the menubar results in an Emacs - event through the stop_next_event mechanism). - - The only accurate way of determining whether these X events - translate into Emacs events is to go ahead and dispatch them - until there's something on the dispatch queue. */ - - /* See if there are any user events already on the queue. */ - EVENT_CHAIN_LOOP (event, dispatch_event_queue) - if (!user_p || command_event_p (event)) - return 1; - - /* See if there's any TTY input available. - */ - if (poll_fds_for_input (tty_only_mask)) - return 1; - - if (!user_p) - { - /* If not user_p and there are any timer or file-desc events - pending, we know there will be an event so we're through. */ -/* XtInputMask pending_value; */ - - /* Note that formerly we just checked the value of XtAppPending() - to determine if there was file-desc input. This doesn't - work any more with the signal_event_pipe; XtAppPending() - will says "yes" in this case but there isn't really any - input. Another way of fixing this problem is for the - signal_event_pipe to generate actual input in the form - of an identity eval event or something. (#### maybe this - actually happens?) */ - - if (poll_fds_for_input (process_only_mask)) - return 1; - - /* #### Is there any way to do this in Gtk? I don't think there - is a 'peek' for events */ -#if 0 - pending_value = XtAppPending (Xt_app_con); - - if (pending_value & XtIMTimer) - return 1; #endif - } - - /* XtAppPending() can be super-slow, esp. over a network connection. - Quantify results have indicated that in some cases the - call to detect_input_pending() completely dominates the - running time of redisplay(). Fortunately, in a SIGIO world - we can more quickly determine whether there are any X events: - if an event has happened since the last time we checked, then - a SIGIO will have happened. On a machine with broken SIGIO, - we'll still be in an OK state -- the sigio_happened flag - will get set at least once a second, so we'll be no more than - one second behind reality. (In general it's OK if we - erroneously report no input pending when input is actually - pending() -- preemption is just a bit less efficient, that's - all. It's bad bad bad if you err the other way -- you've - promised that `next-event' won't block but it actually will, - and some action might get delayed until the next time you - hit a key.) - */ - - /* quit_check_signal_tick_count is volatile so try to avoid race conditions - by using a temporary variable */ - tick_count_val = quit_check_signal_tick_count; - if (last_quit_check_signal_tick_count != tick_count_val) - { - last_quit_check_signal_tick_count = tick_count_val; - - /* We need to drain the entire queue now -- if we only - drain part of it, we may later on end up with events - actually pending but detect_input_pending() returning - false because there wasn't another SIGIO. */ - - emacs_gtk_drain_queue (); - - EVENT_CHAIN_LOOP (event, dispatch_event_queue) - if (!user_p || command_event_p (event)) - return 1; - } - - return 0; } static void
--- a/src/event-msw.c Fri Feb 07 01:43:07 2003 +0000 +++ b/src/event-msw.c Fri Feb 07 11:50:54 2003 +0000 @@ -1,7 +1,7 @@ /* The mswindows event_stream interface. Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. Copyright (C) 1995 Sun Microsystems, Inc. - Copyright (C) 1996, 2000, 2001, 2002 Ben Wing. + Copyright (C) 1996, 2000, 2001, 2002, 2003 Ben Wing. Copyright (C) 1997 Jonathan Harris. This file is part of XEmacs. @@ -169,7 +169,6 @@ /* This is the event signaled by the event pump. See mswindows_pump_outstanding_events for comments */ static int mswindows_error_caught_in_modal_loop; -static int mswindows_in_modal_loop; /* Count of wound timers */ static int mswindows_pending_timers_count; @@ -1129,43 +1128,6 @@ return !ascii_strcasecmp (class_name_buf, XEMACS_CLASS); } -struct mswindows_protect_modal_loop -{ - Lisp_Object (*bfun) (Lisp_Object barg); - Lisp_Object barg; -}; - -static Lisp_Object -mswindows_protect_modal_loop_1 (void *gack) -{ - struct mswindows_protect_modal_loop *gata = - (struct mswindows_protect_modal_loop *) gack; - - return (gata->bfun) (gata->barg); -} - -Lisp_Object -mswindows_protect_modal_loop (const char *error_string, - Lisp_Object (*bfun) (Lisp_Object barg), - Lisp_Object barg, int flags) -{ - Lisp_Object tmp; - struct mswindows_protect_modal_loop bluh; - - bluh.bfun = bfun; - bluh.barg = barg; - - ++mswindows_in_modal_loop; - tmp = call_trapping_problems (Qevent, error_string, - flags, 0, - mswindows_protect_modal_loop_1, &bluh); - if (UNBOUNDP (tmp)) - mswindows_error_caught_in_modal_loop = 1; - --mswindows_in_modal_loop; - - return tmp; -} - void mswindows_unmodalize_signal_maybe (void) { @@ -1177,7 +1139,7 @@ * condition_case. See mswindows_pump_outstanding_events */ static Lisp_Object -mswindows_unsafe_pump_events (Lisp_Object u_n_u_s_e_d) +mswindows_unsafe_pump_events (void *arg) { /* This function can call lisp */ Lisp_Object event = Fmake_event (Qnil, Qnil); @@ -1185,7 +1147,7 @@ int do_redisplay = 0; GCPRO1 (event); - while (detect_input_pending ()) + while (detect_input_pending (1)) { Fnext_event (event, Qnil); Fdispatch_event (event); @@ -1212,7 +1174,7 @@ * neither are waitable handles checked. The function pumps * thus only dispatch events already queued, as well as those * resulted in dispatching thereof. This is done by setting - * module local variable mswindows_in_modal_loop to nonzero. + * in_modal_loop to nonzero. * * Return value is Qt if no errors was trapped, or Qunbound if * there was an error. @@ -1244,88 +1206,15 @@ GCPRO1 (result); if (!mswindows_error_caught_in_modal_loop) - result = mswindows_protect_modal_loop - ("Error during event handling", mswindows_unsafe_pump_events, Qnil, 0); + result = event_stream_protect_modal_loop + ("Error during event handling", mswindows_unsafe_pump_events, 0, 0); UNGCPRO; + if (UNBOUNDP (result)) + mswindows_error_caught_in_modal_loop = 1; return result; } /* - * KEYBOARD_ONLY_P is set to non-zero when we are called from - * QUITP, and are interesting in keyboard messages only. - */ -static void -mswindows_drain_windows_queue (void) -{ - MSG msg; - - /* should call mswindows_need_event_in_modal_loop() if in modal loop */ - assert (!mswindows_in_modal_loop); - - while (qxePeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) - { -#ifdef HAVE_DIALOGS - /* Don't translate messages destined for a dialog box, this - makes keyboard traversal work. I think?? */ - if (mswindows_is_dialog_msg (&msg)) - { - mswindows_unmodalize_signal_maybe (); - continue; - } -#endif /* HAVE_DIALOGS */ - - /* We have to translate messages that are not sent to an XEmacs - frame. This is so that key presses work ok in things like - edit fields. However, we *musn't* translate message for XEmacs - frames as this is handled in the wnd proc. - We also have to avoid generating paint magic events for windows - that aren't XEmacs frames */ - - if (!mswindows_window_is_xemacs (msg.hwnd)) - TranslateMessage (&msg); - else if (msg.message == WM_PAINT) - { - struct mswindows_frame *msframe; - - /* hdc will be NULL unless this is a subwindow - in which case we - shouldn't have received a paint message for it here. */ - assert (msg.wParam == 0); - - /* Queue a magic event for handling when safe */ - msframe = - FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (msg.hwnd))); - if (!msframe->paint_pending) - { - msframe->paint_pending = 1; - mswindows_enqueue_magic_event (msg.hwnd, WM_PAINT); - } - /* Don't dispatch. WM_PAINT is always the last message in the - queue so it's OK to just return. */ - return; - } - qxeDispatchMessage (&msg); - mswindows_unmodalize_signal_maybe (); - } -} - -static void -emacs_mswindows_drain_queue (void) -{ - mswindows_drain_windows_queue (); -#ifdef HAVE_TTY - drain_tty_devices (); -#endif -} - -static int -emacs_mswindows_quit_check_disallowed_p (void) -{ - /* Quit cannot happen in modal loop: all program - input is dedicated to Windows. */ - return mswindows_in_modal_loop; -} - -/* * This is a special flavor of the mswindows_need_event function, * used while in event pump. Actually, there is only kind of events * allowed while in event pump: a timer. An attempt to fetch any @@ -1371,6 +1260,73 @@ } } +/* BADLY_P non-zero means we were called from mswindows_need_event(1). It + only matters when we are in a modal loop, and causes us to fetch timer + events (the only kinds we can fetch in such a case). + */ +static void +mswindows_drain_windows_queue (int badly_p) +{ + MSG msg; + + if (in_modal_loop) + mswindows_need_event_in_modal_loop (badly_p); + else + while (qxePeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) + { +#ifdef HAVE_DIALOGS + /* Don't translate messages destined for a dialog box, this + makes keyboard traversal work. I think?? */ + if (mswindows_is_dialog_msg (&msg)) + { + mswindows_unmodalize_signal_maybe (); + continue; + } +#endif /* HAVE_DIALOGS */ + + /* We have to translate messages that are not sent to an XEmacs + frame. This is so that key presses work ok in things like + edit fields. However, we *musn't* translate message for XEmacs + frames as this is handled in the wnd proc. + We also have to avoid generating paint magic events for windows + that aren't XEmacs frames */ + + if (!mswindows_window_is_xemacs (msg.hwnd)) + TranslateMessage (&msg); + else if (msg.message == WM_PAINT) + { + struct mswindows_frame *msframe; + + /* hdc will be NULL unless this is a subwindow - in which case we + shouldn't have received a paint message for it here. */ + assert (msg.wParam == 0); + + /* Queue a magic event for handling when safe */ + msframe = + FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (msg.hwnd))); + if (!msframe->paint_pending) + { + msframe->paint_pending = 1; + mswindows_enqueue_magic_event (msg.hwnd, WM_PAINT); + } + /* Don't dispatch. WM_PAINT is always the last message in the + queue so it's OK to just return. */ + return; + } + qxeDispatchMessage (&msg); + mswindows_unmodalize_signal_maybe (); + } +} + +static void +emacs_mswindows_drain_queue (void) +{ + mswindows_drain_windows_queue (0); +#ifdef HAVE_TTY + drain_tty_devices (); +#endif +} + /* * This drains the event queue and fills up two internal queues until * an event of a type specified by USER_P is retrieved. @@ -1398,7 +1354,7 @@ EMACS_SET_SECS_USECS (sometime, 0, 0); EMACS_TIME_TO_SELECT_TIME (sometime, select_time_to_block); pointer_to_this = &select_time_to_block; - if (mswindows_in_modal_loop) + if (in_modal_loop) /* In modal loop with badly_p false, don't care about Windows events. */ FD_CLR (windows_fd, &temp_mask); @@ -1414,12 +1370,7 @@ else if (active > 0) { if (FD_ISSET (windows_fd, &temp_mask)) - { - if (mswindows_in_modal_loop) - mswindows_need_event_in_modal_loop (badly_p); - else - mswindows_drain_windows_queue (); - } + mswindows_drain_windows_queue (badly_p); else { #ifdef HAVE_TTY @@ -1486,7 +1437,7 @@ /* Now try getting a message or process event */ DWORD active; DWORD what_events; - if (mswindows_in_modal_loop) + if (in_modal_loop) /* In a modal loop, only look for timer events, and only if we really need one. */ { @@ -1607,13 +1558,7 @@ return; } else if (active == WAIT_OBJECT_0 + mswindows_waitable_count) - { - /* Got your message, thanks */ - if (mswindows_in_modal_loop) - mswindows_need_event_in_modal_loop (badly_p); - else - mswindows_drain_windows_queue (); - } + mswindows_drain_windows_queue (badly_p); else { int ix = active - WAIT_OBJECT_0; @@ -4384,11 +4329,32 @@ * emacs_mswindows_next_event() would not block. */ static int -emacs_mswindows_event_pending_p (int user_p) +emacs_mswindows_event_pending_p (int how_many) { - mswindows_need_event (0); - return (!NILP (dispatch_event_queue) - || (!user_p && !NILP (mswindows_s_dispatch_event_queue))); + if (!how_many) + { + mswindows_need_event (0); + return (!NILP (dispatch_event_queue) + || !NILP (mswindows_s_dispatch_event_queue)); + } + else + { + Lisp_Object event; + int count = 0; + + EVENT_CHAIN_LOOP (event, dispatch_event_queue) + count++; + + if (count >= how_many) + return 1; + + emacs_mswindows_drain_queue (); + + EVENT_CHAIN_LOOP (event, dispatch_event_queue) + count++; + + return count >= how_many; + } } /* @@ -5145,7 +5111,6 @@ void reinit_vars_of_event_mswindows (void) { - mswindows_in_modal_loop = 0; mswindows_pending_timers_count = 0; mswindows_event_stream = xnew_and_zero (struct event_stream); @@ -5159,7 +5124,6 @@ mswindows_event_stream->add_timeout_cb = emacs_mswindows_add_timeout; mswindows_event_stream->remove_timeout_cb = emacs_mswindows_remove_timeout; mswindows_event_stream->drain_queue_cb = emacs_mswindows_drain_queue; - mswindows_event_stream->quit_check_disallowed_p_cb = emacs_mswindows_quit_check_disallowed_p; mswindows_event_stream->select_console_cb = emacs_mswindows_select_console; mswindows_event_stream->unselect_console_cb = emacs_mswindows_unselect_console; mswindows_event_stream->select_process_cb = emacs_mswindows_select_process;
--- a/src/event-stream.c Fri Feb 07 01:43:07 2003 +0000 +++ b/src/event-stream.c Fri Feb 07 11:50:54 2003 +0000 @@ -2,7 +2,7 @@ Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. Copyright (C) 1995 Board of Trustees, University of Illinois. Copyright (C) 1995 Sun Microsystems, Inc. - Copyright (C) 1995, 1996, 2001, 2002 Ben Wing. + Copyright (C) 1995, 1996, 2001, 2002, 2003 Ben Wing. This file is part of XEmacs. @@ -140,11 +140,6 @@ do not execute it; call disabled-command-hook's value instead. */ Lisp_Object Qdisabled; -EXFUN (Fnext_command_event, 2); - -static void pre_command_hook (void); -static void post_command_hook (void); - /* Last keyboard or mouse input event read as a command. */ Lisp_Object Vlast_command_event; @@ -249,8 +244,10 @@ Lisp_Object Qself_insert_defer_undo; -/* this is in keymap.c */ -extern Lisp_Object Fmake_keymap (Lisp_Object name); +int in_modal_loop; + +/* the number of keyboard characters read. callint.c wants this. */ +Charcount num_input_chars; #ifdef DEBUG_XEMACS Fixnum debug_emacs_events; @@ -276,14 +273,17 @@ /* The callback routines for the window system or terminal driver */ struct event_stream *event_stream; -static void echo_key_event (struct command_builder *, Lisp_Object event); -static void maybe_kbd_translate (Lisp_Object event); - /* There are two event queues here -- the command event queue (#### which should be called "deferred event queue" and is in my glyph ws) and the - dispatch event queue (#### MS Windows actually has an extra dispatch - queue for non-user events and uses the generic one only for user events; - we should probably generalize this). + dispatch event queue. (MS Windows actually has an extra dispatch + queue for non-user events and uses the generic one only for user events. + This is because user and non-user events in Windows come through the + same place -- the window procedure -- but under X, it's possible to + selectively process events such that we take all the user events before + the non-user ones. #### In fact, given the way we now drain the queue, + we might need two separate queues, like under Windows. Need to think + carefully exactly how this works, and should certainly generalize the + two different queues. The dispatch queue (which used to occur duplicated inside of each event implementation) is used for events that have been read from the @@ -343,8 +343,16 @@ are not allowed to recursively call these routines. We record here if we are in that situation. */ -static Lisp_Object recursive_sit_for; - +static int recursive_sit_for; + +static void pre_command_hook (void); +static void post_command_hook (void); +static void maybe_kbd_translate (Lisp_Object event); +static void push_this_command_keys (Lisp_Object event); +static void push_recent_keys (Lisp_Object event); +static void dribble_out_event (Lisp_Object event); +static void execute_internal_event (Lisp_Object event); +static int is_scrollbar_event (Lisp_Object event); /**********************************************************************/ @@ -513,17 +521,8 @@ /* Low-level interfaces onto event methods */ /**********************************************************************/ -enum event_stream_operation -{ - EVENT_STREAM_PROCESS, - EVENT_STREAM_TIMEOUT, - EVENT_STREAM_CONSOLE, - EVENT_STREAM_READ, - EVENT_STREAM_NOTHING, -}; - static void -check_event_stream_ok (enum event_stream_operation op) +check_event_stream_ok (void) { if (!event_stream && noninteractive) /* See comment in init_event_stream() */ @@ -531,147 +530,52 @@ else assert (event_stream); } -static int -event_stream_event_pending_p (int user) -{ - /* #### Hmmm ... There may be some duplication in "drain queue" and - "event pending". Couldn't we just drain the queue and see what's in - it, and not maybe need a separate event method for this? Would this - work when USER is 0? Maybe this would be slow? */ - return event_stream && event_stream->event_pending_p (user); -} - -static void -event_stream_force_event_pending (struct frame *f) -{ - if (event_stream->force_event_pending_cb) - event_stream->force_event_pending_cb (f); -} - -static int -maybe_read_quit_event (Lisp_Event *event) -{ - /* A C-g that came from `sigint_happened' will always come from the - controlling terminal. If that doesn't exist, however, then the - user manually sent us a SIGINT, and we pretend the C-g came from - the selected console. */ - struct console *con; - - if (CONSOLEP (Vcontrolling_terminal) && - CONSOLE_LIVE_P (XCONSOLE (Vcontrolling_terminal))) - con = XCONSOLE (Vcontrolling_terminal); - else - con = XCONSOLE (Fselected_console ()); - - if (sigint_happened) - { - sigint_happened = 0; - Vquit_flag = Qnil; - Fcopy_event (CONSOLE_QUIT_EVENT (con), wrap_event (event)); - return 1; - } - return 0; -} - -static void -event_stream_next_event (Lisp_Event *event) -{ - Lisp_Object event_obj; - - check_event_stream_ok (EVENT_STREAM_READ); - - event_obj = wrap_event (event); - zero_event (event); - /* SIGINT occurs when C-g was pressed on a TTY. (SIGINT might have - been sent manually by the user, but we don't care; we treat it - the same.) - - The SIGINT signal handler sets Vquit_flag as well as sigint_happened - and write a byte on our "fake pipe", which unblocks us when we are - waiting for an event. */ - - /* If SIGINT was received after we disabled quit checking (because - we want to read C-g's as characters), but before we got a chance - to start reading, notice it now and treat it as a character to be - read. If above callers wanted this to be QUIT, they can - determine this by comparing the event against quit-char. */ - - if (maybe_read_quit_event (event)) - { - DEBUG_PRINT_EMACS_EVENT ("SIGINT", event_obj); - return; - } - - /* If a longjmp() happens in the callback, we're screwed. - Let's hope it doesn't. I think the code here is fairly - clean and doesn't do this. */ - emacs_is_blocking = 1; - event_stream->next_event_cb (event); - emacs_is_blocking = 0; - - /* Now check to see if C-g was pressed while we were blocking. - We treat it as an event, just like above. */ - if (maybe_read_quit_event (event)) - { - DEBUG_PRINT_EMACS_EVENT ("SIGINT", event_obj); - return; - } - -#ifdef DEBUG_XEMACS - /* timeout events have more info set later, so - print the event out in next_event_internal(). */ - if (event->event_type != timeout_event) - DEBUG_PRINT_EMACS_EVENT ("real", event_obj); -#endif - maybe_kbd_translate (event_obj); -} - void event_stream_handle_magic_event (Lisp_Event *event) { - check_event_stream_ok (EVENT_STREAM_READ); + check_event_stream_ok (); event_stream->handle_magic_event_cb (event); } void event_stream_format_magic_event (Lisp_Event *event, Lisp_Object pstream) { - check_event_stream_ok (EVENT_STREAM_NOTHING); + check_event_stream_ok (); event_stream->format_magic_event_cb (event, pstream); } int event_stream_compare_magic_event (Lisp_Event *e1, Lisp_Event *e2) { - check_event_stream_ok (EVENT_STREAM_NOTHING); + check_event_stream_ok (); return event_stream->compare_magic_event_cb (e1, e2); } Hashcode event_stream_hash_magic_event (Lisp_Event *e) { - check_event_stream_ok (EVENT_STREAM_NOTHING); + check_event_stream_ok (); return event_stream->hash_magic_event_cb (e); } static int event_stream_add_timeout (EMACS_TIME timeout) { - check_event_stream_ok (EVENT_STREAM_TIMEOUT); + check_event_stream_ok (); return event_stream->add_timeout_cb (timeout); } static void event_stream_remove_timeout (int id) { - check_event_stream_ok (EVENT_STREAM_TIMEOUT); + check_event_stream_ok (); event_stream->remove_timeout_cb (id); } void event_stream_select_console (struct console *con) { - check_event_stream_ok (EVENT_STREAM_CONSOLE); + check_event_stream_ok (); if (!con->input_enabled) { event_stream->select_console_cb (con); @@ -682,7 +586,7 @@ void event_stream_unselect_console (struct console *con) { - check_event_stream_ok (EVENT_STREAM_CONSOLE); + check_event_stream_ok (); if (con->input_enabled) { event_stream->unselect_console_cb (con); @@ -695,7 +599,7 @@ { int cur_in, cur_err; - check_event_stream_ok (EVENT_STREAM_PROCESS); + check_event_stream_ok (); cur_in = get_process_selected_p (proc, 0); if (cur_in) @@ -725,7 +629,7 @@ { int cur_in, cur_err; - check_event_stream_ok (EVENT_STREAM_PROCESS); + check_event_stream_ok (); cur_in = get_process_selected_p (proc, 0); if (!cur_in) @@ -759,7 +663,7 @@ USID *err_usid, int flags) { - check_event_stream_ok (EVENT_STREAM_PROCESS); + check_event_stream_ok (); event_stream->create_io_streams_cb (inhandle, outhandle, errhandle, instream, outstream, errstream, in_usid, err_usid, flags); @@ -772,72 +676,11 @@ USID *in_usid, USID *err_usid) { - check_event_stream_ok (EVENT_STREAM_PROCESS); + check_event_stream_ok (); event_stream->delete_io_streams_cb (instream, outstream, errstream, in_usid, err_usid); } -static void -event_stream_drain_queue (void) -{ - if (event_stream && event_stream->drain_queue_cb) - event_stream->drain_queue_cb (); -} - -struct remove_quit_p_data -{ - int critical; -}; - -static int -remove_quit_p_event (Lisp_Object ev, void *the_data) -{ - struct remove_quit_p_data *data = (struct remove_quit_p_data *) the_data; - struct console *con = event_console_or_selected (ev); - - if (XEVENT_TYPE (ev) == key_press_event) - { - if (event_matches_key_specifier_p (ev, CONSOLE_QUIT_EVENT (con))) - return 1; - if (event_matches_key_specifier_p (ev, - CONSOLE_CRITICAL_QUIT_EVENT (con))) - { - data->critical = 1; - return 1; - } - } - - return 0; -} - -static int -event_stream_quit_check_disallowed_p (void) -{ - if (event_stream && event_stream->quit_check_disallowed_p_cb) - return event_stream->quit_check_disallowed_p_cb (); - else - return 0; -} - -void -event_stream_quit_p (void) -{ - struct remove_quit_p_data data; - - if (event_stream_quit_check_disallowed_p ()) - return; - - /* Drain queue so we can check for pending C-g events. */ - event_stream_drain_queue (); - data.critical = 0; - - if (map_event_chain_remove (remove_quit_p_event, - &dispatch_event_queue, - &dispatch_event_queue_tail, - &data, MECR_DEALLOCATE_EVENT)) - Vquit_flag = data.critical ? Qcritical : Qt; -} - static int event_stream_current_event_timestamp (struct console *c) { @@ -963,80 +806,6 @@ /* random junk */ /**********************************************************************/ -static void -maybe_kbd_translate (Lisp_Object event) -{ - Ichar c; - int did_translate = 0; - - if (XEVENT_TYPE (event) != key_press_event) - return; - if (!HASH_TABLEP (Vkeyboard_translate_table)) - return; - if (EQ (Fhash_table_count (Vkeyboard_translate_table), Qzero)) - return; - - c = event_to_character (event, 0, 0, 0); - if (c != -1) - { - Lisp_Object traduit = Fgethash (make_char (c), Vkeyboard_translate_table, - Qnil); - if (!NILP (traduit) && SYMBOLP (traduit)) - { - XSET_EVENT_KEY_KEYSYM (event, traduit); - XSET_EVENT_KEY_MODIFIERS (event, 0); - did_translate = 1; - } - else if (CHARP (traduit)) - { - /* This used to call Fcharacter_to_event() directly into EVENT, - but that can eradicate timestamps and other such stuff. - This way is safer. */ - Lisp_Object ev2 = Fmake_event (Qnil, Qnil); - - character_to_event (XCHAR (traduit), XEVENT (ev2), - XCONSOLE (XEVENT_CHANNEL (event)), 0, 1); - XSET_EVENT_KEY_KEYSYM (event, XEVENT_KEY_KEYSYM (ev2)); - XSET_EVENT_KEY_MODIFIERS (event, XEVENT_KEY_MODIFIERS (ev2)); - Fdeallocate_event (ev2); - did_translate = 1; - } - } - - if (!did_translate) - { - Lisp_Object traduit = Fgethash (XEVENT_KEY_KEYSYM (event), - Vkeyboard_translate_table, Qnil); - if (!NILP (traduit) && SYMBOLP (traduit)) - { - XSET_EVENT_KEY_KEYSYM (event, traduit); - did_translate = 1; - } - else if (CHARP (traduit)) - { - /* This used to call Fcharacter_to_event() directly into EVENT, - but that can eradicate timestamps and other such stuff. - This way is safer. */ - Lisp_Object ev2 = Fmake_event (Qnil, Qnil); - - character_to_event (XCHAR (traduit), XEVENT (ev2), - XCONSOLE (XEVENT_CHANNEL (event)), 0, 1); - XSET_EVENT_KEY_KEYSYM (event, XEVENT_KEY_KEYSYM (ev2)); - XSET_EVENT_KEY_MODIFIERS (event, - XEVENT_KEY_MODIFIERS (event) | - XEVENT_KEY_MODIFIERS (ev2)); - - Fdeallocate_event (ev2); - did_translate = 1; - } - } - -#ifdef DEBUG_XEMACS - if (did_translate) - DEBUG_PRINT_EMACS_EVENT ("->keyboard-translate-table", event); -#endif -} - /* NB: The following auto-save stuff is in keyboard.c in FSFmacs, and keystrokes_since_auto_save is equivalent to the difference between num_nonmacro_input_chars and last_auto_save. */ @@ -1065,7 +834,7 @@ keystrokes_since_auto_save++; if (auto_save_interval > 0 && keystrokes_since_auto_save > max (auto_save_interval, 20) && - !detect_input_pending ()) + !detect_input_pending (1)) { Fdo_auto_save (Qnil, Qnil); record_auto_save (); @@ -1136,45 +905,6 @@ /**********************************************************************/ -/* input pending */ -/**********************************************************************/ - -int -detect_input_pending (void) -{ - /* Always call the event_pending_p hook even if there's an unread - character, because that might do some needed ^G detection (on - systems without SIGIO, for example). - */ - if (event_stream_event_pending_p (1)) - return 1; - if (!NILP (Vunread_command_events) || !NILP (Vunread_command_event)) - return 1; - if (!NILP (command_event_queue)) - { - Lisp_Object event; - - EVENT_CHAIN_LOOP (event, command_event_queue) - { - if (XEVENT_TYPE (event) != eval_event - && XEVENT_TYPE (event) != magic_eval_event) - return 1; - } - } - return 0; -} - -DEFUN ("input-pending-p", Finput_pending_p, 0, 0, 0, /* -Return t if command input is currently available with no waiting. -Actually, the value is nil only if we can be sure that no input is available. -*/ - ()) -{ - return detect_input_pending () ? Qt : Qnil; -} - - -/**********************************************************************/ /* timeouts */ /**********************************************************************/ @@ -2121,6 +1851,165 @@ /**********************************************************************/ +/* input pending/quit checking */ +/**********************************************************************/ + +/* If HOW_MANY is 0, return true if there are any user or non-user events + pending. If HOW_MANY is > 0, return true if there are that many *user* + events pending, irrespective of non-user events. */ + +static int +event_stream_event_pending_p (int how_many) +{ + /* #### Hmmm ... There may be some duplication in "drain queue" and + "event pending". Couldn't we just drain the queue and see what's in + it, and not maybe need a separate event method for this? Would this + work when HOW_MANY is 0? Maybe this would be slow? */ + return event_stream && event_stream->event_pending_p (how_many); +} + +static void +event_stream_force_event_pending (struct frame *f) +{ + if (event_stream->force_event_pending_cb) + event_stream->force_event_pending_cb (f); +} + +void +event_stream_drain_queue (void) +{ + if (event_stream && event_stream->drain_queue_cb) + event_stream->drain_queue_cb (); +} + +/* Return non-zero if at least HOW_MANY user events are pending. */ +int +detect_input_pending (int how_many) +{ + Lisp_Object event; + + if (!NILP (Vunread_command_event)) + how_many--; + + how_many -= XINT (Fsafe_length (Vunread_command_events)); + + if (how_many <= 0) + return 1; + + EVENT_CHAIN_LOOP (event, command_event_queue) + { + if (XEVENT_TYPE (event) != eval_event + && XEVENT_TYPE (event) != magic_eval_event) + { + how_many--; + if (how_many <= 0) + return 1; + } + } + + return event_stream_event_pending_p (how_many); +} + +DEFUN ("input-pending-p", Finput_pending_p, 0, 0, 0, /* +Return t if command input is currently available with no waiting. +Actually, the value is nil only if we can be sure that no input is available. +*/ + ()) +{ + return detect_input_pending (1) ? Qt : Qnil; +} + +static int +maybe_read_quit_event (Lisp_Event *event) +{ + /* A C-g that came from `sigint_happened' will always come from the + controlling terminal. If that doesn't exist, however, then the + user manually sent us a SIGINT, and we pretend the C-g came from + the selected console. */ + struct console *con; + + if (CONSOLEP (Vcontrolling_terminal) && + CONSOLE_LIVE_P (XCONSOLE (Vcontrolling_terminal))) + con = XCONSOLE (Vcontrolling_terminal); + else + con = XCONSOLE (Fselected_console ()); + + if (sigint_happened) + { + sigint_happened = 0; + Vquit_flag = Qnil; + Fcopy_event (CONSOLE_QUIT_EVENT (con), wrap_event (event)); + return 1; + } + return 0; +} + +struct remove_quit_p_data +{ + int critical; +}; + +static int +remove_quit_p_event (Lisp_Object ev, void *the_data) +{ + struct remove_quit_p_data *data = (struct remove_quit_p_data *) the_data; + struct console *con = event_console_or_selected (ev); + + if (XEVENT_TYPE (ev) == key_press_event) + { + if (event_matches_key_specifier_p (ev, CONSOLE_QUIT_EVENT (con))) + return 1; + if (event_matches_key_specifier_p (ev, + CONSOLE_CRITICAL_QUIT_EVENT (con))) + { + data->critical = 1; + return 1; + } + } + + return 0; +} + +void +event_stream_quit_p (void) +{ + struct remove_quit_p_data data; + + /* Quit checking cannot happen in modal loop. Because it attempts to + retrieve and dispatch events, it will cause lots of problems if we try + to do this when already in the process of doing this -- deadlocking + under Windows, crashes in lwlib etc. under X due to non-reentrant + code. This is automatically caught, however, in + event_stream_drain_queue() (checks for in_modal_loop in the + event-specific code). */ + + /* Drain queue so we can check for pending C-g events. */ + event_stream_drain_queue (); + data.critical = 0; + + if (map_event_chain_remove (remove_quit_p_event, + &dispatch_event_queue, + &dispatch_event_queue_tail, + &data, MECR_DEALLOCATE_EVENT)) + Vquit_flag = data.critical ? Qcritical : Qt; +} + +Lisp_Object +event_stream_protect_modal_loop (const char *error_string, + Lisp_Object (*bfun) (void *barg), + void *barg, int flags) +{ + Lisp_Object tmp; + + ++in_modal_loop; + tmp = call_trapping_problems (Qevent, error_string, flags, 0, bfun, barg); + --in_modal_loop; + + return tmp; +} + + +/**********************************************************************/ /* retrieving the next event */ /**********************************************************************/ @@ -2145,8 +2034,58 @@ return in_single_console; } -/* the number of keyboard characters read. callint.c wants this. */ -Charcount num_input_chars; +static void +event_stream_next_event (Lisp_Event *event) +{ + Lisp_Object event_obj; + + check_event_stream_ok (); + + event_obj = wrap_event (event); + zero_event (event); + /* SIGINT occurs when C-g was pressed on a TTY. (SIGINT might have + been sent manually by the user, but we don't care; we treat it + the same.) + + The SIGINT signal handler sets Vquit_flag as well as sigint_happened + and write a byte on our "fake pipe", which unblocks us when we are + waiting for an event. */ + + /* If SIGINT was received after we disabled quit checking (because + we want to read C-g's as characters), but before we got a chance + to start reading, notice it now and treat it as a character to be + read. If above callers wanted this to be QUIT, they can + determine this by comparing the event against quit-char. */ + + if (maybe_read_quit_event (event)) + { + DEBUG_PRINT_EMACS_EVENT ("SIGINT", event_obj); + return; + } + + /* If a longjmp() happens in the callback, we're screwed. + Let's hope it doesn't. I think the code here is fairly + clean and doesn't do this. */ + emacs_is_blocking = 1; + event_stream->next_event_cb (event); + emacs_is_blocking = 0; + + /* Now check to see if C-g was pressed while we were blocking. + We treat it as an event, just like above. */ + if (maybe_read_quit_event (event)) + { + DEBUG_PRINT_EMACS_EVENT ("SIGINT", event_obj); + return; + } + +#ifdef DEBUG_XEMACS + /* timeout events have more info set later, so + print the event out in next_event_internal(). */ + if (event->event_type != timeout_event) + DEBUG_PRINT_EMACS_EVENT ("real", event_obj); +#endif + maybe_kbd_translate (event_obj); +} /* Read an event from the window system (or tty). If ALLOW_QUEUED is non-zero, read from the command-event queue first. @@ -2225,18 +2164,15 @@ run_pre_idle_hook (void) { if (!NILP (Vpre_idle_hook) - && !detect_input_pending ()) + && !detect_input_pending (1)) safe_run_hook_trapping_problems ("Error in `pre-idle-hook' (setting hook to nil)", - Qpre_idle_hook, INHIBIT_EXISTING_PERMANENT_DISPLAY_OBJECT_DELETION); + Qpre_idle_hook, + /* Quit is inhibited as a result of being within next-event so + we need to fix that. */ + INHIBIT_EXISTING_PERMANENT_DISPLAY_OBJECT_DELETION | UNINHIBIT_QUIT); } -static void push_this_command_keys (Lisp_Object event); -static void push_recent_keys (Lisp_Object event); -static void dribble_out_event (Lisp_Object event); -static void execute_internal_event (Lisp_Object event); -static int is_scrollbar_event (Lisp_Object event); - DEFUN ("next-event", Fnext_event, 0, 2, 0, /* Return the next available event. Pass this object to `dispatch-event' to handle it. @@ -2295,23 +2231,25 @@ GCPRO1 (event); + /* This is not strictly necessary. Trying to retrieve an event inside of + a modal loop can cause major problems (see event_stream_quit_p()), but + the event-specific code knows about this and will make sure we don't + do anything dangerous. However, if we've gotten here, it's highly + likely that some code is trying to fetch user events (e.g. in custom + dialog-box code), and will almost certainly deadlock, so it's probably + best to error out. #### This could cause problems because there are + (potentially, at least) legitimate reasons for calling next-event + inside of a modal loop, in particular if the code is trying to search + for a timeout event, which will still get retrieved in such a case. + However, the code to error in such a case has already been present for + a long time without obvious problems so leaving it in isn't so + bad. --ben */ + if (in_modal_loop) + invalid_operation ("Attempt to call next-event inside modal loop", + Qunbound); + depth = begin_dont_check_for_quit (); -#ifdef LWLIB_MENUBARS_LUCID - /* - * #### Fix the menu code so this isn't necessary. - * - * We cannot allow the lwmenu code to be reentered, because the - * code is not written to be reentrant and will crash. Therefore - * paths from the menu callbacks back into the menu code have to - * be blocked. Fnext_event is the normal path into the menu code, - * so we signal an error here. - */ - if (in_menu_callback) - invalid_operation ("Attempt to call next-event inside menu callback", - Qunbound); -#endif /* LWLIB_MENUBARS_LUCID */ - if (NILP (event)) event = Fmake_event (Qnil, Qnil); else @@ -2642,6 +2580,12 @@ deallocate_event_chain (event); } +static int +command_event_p_cb (Lisp_Object ev, void *the_data) +{ + return command_event_p (ev); +} + DEFUN ("discard-input", Fdiscard_input, 0, 0, 0, /* Discard any pending "user" events. Also cancel any kbd macro being defined. @@ -2650,84 +2594,31 @@ */ ()) { - /* This throws away user-input on the queue, but doesn't process any - events. Calling dispatch_event() here leads to a race condition. - */ - Lisp_Object event = Fmake_event (Qnil, Qnil); - Lisp_Object head = Qnil, tail = Qnil; - struct gcpro gcpro1; - /* #### not correct here with Vselected_console? Should - discard-input take a console argument, or maybe map over - all consoles? */ - struct console *con = XCONSOLE (Vselected_console); - - /* next_event_internal() can cause arbitrary Lisp code to be evalled */ - GCPRO1 (event); - /* If a macro was being defined then we have to mark the modeline - has changed to ensure that it gets updated correctly. */ - if (!NILP (con->defining_kbd_macro)) - MARK_MODELINE_CHANGED; - con->defining_kbd_macro = Qnil; - reset_current_events (XCOMMAND_BUILDER (con->command_builder)); - - while (!NILP (command_event_queue) - || event_stream_event_pending_p (1)) + Lisp_Object concons; + + CONSOLE_LOOP (concons) { - /* We want to ignore C-g's along with all other keypresses. */ - int depth = begin_dont_check_for_quit (); - /* This will take stuff off the command_event_queue, or read it - from the event_stream, but it will not block. - */ - next_event_internal (event, 1); - /* The following comment used to be here: - - [[Treat C-g as a user event (ignore it). It is vitally - important that we reset Vquit_flag here. Otherwise, if we're - reading from a TTY console, maybe_read_quit_event() will - notice that C-g has been set and send us another C-g. That - will cause us to get right back here, and read another C-g, - ad infinitum ...]] - - but I don't think this is correct; maybe_read_quit_event() - checks and resets sigint_happened. It shouldn't matter if we - reset here or outside of the while loop. --ben */ - Vquit_flag = Qnil; /* see begin_dont_check_for_quit() */ - - unbind_to (depth); - - /* If the event is a user event, ignore it. */ - if (!command_event_p (event)) - { - /* Otherwise, chain the event onto our list of events not to ignore, - and keep reading until the queue is empty. This does not mean - that if a subprocess is generating an infinite amount of output, - we will never terminate (*provided* that the behavior of - next_event_cb() is correct -- see the comment in events.h), - because this loop ends as soon as there are no more user events - on the command_event_queue or event_stream. - */ - enqueue_event (Fcopy_event (event, Qnil), &head, &tail); - } + struct console *con = XCONSOLE (XCAR (concons)); + + /* If a macro was being defined then we have to mark the modeline + has changed to ensure that it gets updated correctly. */ + if (!NILP (con->defining_kbd_macro)) + MARK_MODELINE_CHANGED; + con->defining_kbd_macro = Qnil; + reset_current_events (XCOMMAND_BUILDER (con->command_builder)); } - if (!NILP (command_event_queue) || !NILP (command_event_queue_tail)) - abort (); - - /* Now tack our chain of events back on to the front of the queue. - Actually, since the queue is now drained, we can just replace it. - The effect of this will be that we have deleted all user events - from the input stream without changing the relative ordering of - any other events. (Some events may have been taken from the - event_stream and added to the command_event_queue, however.) - - At this time, the command_event_queue will contain only eval_events. - */ - - command_event_queue = head; - command_event_queue_tail = tail; - - Fdeallocate_event (event); - UNGCPRO; + /* This function used to be a lot more complicated. Now, we just + drain the pending queue and discard all user events from the + command and dispatch queues. */ + event_stream_drain_queue (); + + map_event_chain_remove (command_event_p_cb, + &dispatch_event_queue, &dispatch_event_queue_tail, + 0, MECR_DEALLOCATE_EVENT); + map_event_chain_remove (command_event_p_cb, + &command_event_queue, &command_event_queue_tail, + 0, MECR_DEALLOCATE_EVENT); return Qnil; } @@ -2739,8 +2630,8 @@ /* This is used in accept-process-output, sleep-for and sit-for. Before running any process_events in these routines, we set - recursive_sit_for to Qt, and use this unwind protect to reset it to - Qnil upon exit. When recursive_sit_for is Qt, calling sit-for will + recursive_sit_for to 1, and use this unwind protect to reset it to + Qnil upon exit. When recursive_sit_for is 1, calling sit-for will cause it to return immediately. All of these routines install timeouts, so we clear the installed @@ -2757,7 +2648,7 @@ if (!NILP(timeout_id)) Fdisable_timeout (timeout_id); - recursive_sit_for = Qnil; + recursive_sit_for = 0; return Qnil; } @@ -2822,7 +2713,7 @@ count = specpdl_depth (); record_unwind_protect (sit_for_unwind, timeout_enabled ? make_int (timeout_id) : Qnil); - recursive_sit_for = Qt; + recursive_sit_for = 1; while (!done && ((NILP (process) && timeout_enabled) || @@ -2919,7 +2810,7 @@ count = specpdl_depth (); record_unwind_protect (sit_for_unwind, make_int (id)); - recursive_sit_for = Qt; + recursive_sit_for = 1; while (1) { @@ -3000,7 +2891,7 @@ return Qnil; /* Recursive call from a filter function or timeout handler. */ - if (!NILP (recursive_sit_for)) + if (recursive_sit_for) { if (!event_stream_event_pending_p (1) && NILP (nodisplay)) redisplay (); @@ -3025,7 +2916,7 @@ count = specpdl_depth (); record_unwind_protect (sit_for_unwind, make_int (id)); - recursive_sit_for = Qt; + recursive_sit_for = 1; while (1) { @@ -3309,6 +3200,80 @@ return event_binding (event0, 1); } +static void +maybe_kbd_translate (Lisp_Object event) +{ + Ichar c; + int did_translate = 0; + + if (XEVENT_TYPE (event) != key_press_event) + return; + if (!HASH_TABLEP (Vkeyboard_translate_table)) + return; + if (EQ (Fhash_table_count (Vkeyboard_translate_table), Qzero)) + return; + + c = event_to_character (event, 0, 0, 0); + if (c != -1) + { + Lisp_Object traduit = Fgethash (make_char (c), Vkeyboard_translate_table, + Qnil); + if (!NILP (traduit) && SYMBOLP (traduit)) + { + XSET_EVENT_KEY_KEYSYM (event, traduit); + XSET_EVENT_KEY_MODIFIERS (event, 0); + did_translate = 1; + } + else if (CHARP (traduit)) + { + /* This used to call Fcharacter_to_event() directly into EVENT, + but that can eradicate timestamps and other such stuff. + This way is safer. */ + Lisp_Object ev2 = Fmake_event (Qnil, Qnil); + + character_to_event (XCHAR (traduit), XEVENT (ev2), + XCONSOLE (XEVENT_CHANNEL (event)), 0, 1); + XSET_EVENT_KEY_KEYSYM (event, XEVENT_KEY_KEYSYM (ev2)); + XSET_EVENT_KEY_MODIFIERS (event, XEVENT_KEY_MODIFIERS (ev2)); + Fdeallocate_event (ev2); + did_translate = 1; + } + } + + if (!did_translate) + { + Lisp_Object traduit = Fgethash (XEVENT_KEY_KEYSYM (event), + Vkeyboard_translate_table, Qnil); + if (!NILP (traduit) && SYMBOLP (traduit)) + { + XSET_EVENT_KEY_KEYSYM (event, traduit); + did_translate = 1; + } + else if (CHARP (traduit)) + { + /* This used to call Fcharacter_to_event() directly into EVENT, + but that can eradicate timestamps and other such stuff. + This way is safer. */ + Lisp_Object ev2 = Fmake_event (Qnil, Qnil); + + character_to_event (XCHAR (traduit), XEVENT (ev2), + XCONSOLE (XEVENT_CHANNEL (event)), 0, 1); + XSET_EVENT_KEY_KEYSYM (event, XEVENT_KEY_KEYSYM (ev2)); + XSET_EVENT_KEY_MODIFIERS (event, + XEVENT_KEY_MODIFIERS (event) | + XEVENT_KEY_MODIFIERS (ev2)); + + Fdeallocate_event (ev2); + did_translate = 1; + } + } + +#ifdef DEBUG_XEMACS + if (did_translate) + DEBUG_PRINT_EMACS_EVENT ("->keyboard-translate-table", event); +#endif +} + /* See if we can do function-key-map or key-translation-map translation on the current events in the command builder. If so, do this, and return the resulting binding, if any. @@ -3424,7 +3389,7 @@ /* #### this horribly-written crap can mess with global state, which this function should not do. i'm not fixing it now. someone needs to go and rewrite that shit correctly. --ben */ -#if defined(HAVE_X_WINDOWS) && defined(LWLIB_MENUBARS_LUCID) +#if defined (HAVE_X_WINDOWS) && defined (LWLIB_MENUBARS_LUCID) if (x_kludge_lw_menu_active ()) { return command_builder_operate_menu_accelerator (builder); @@ -3437,7 +3402,7 @@ if (NILP (result)) #endif result = command_builder_find_leaf_1 (builder); -#if defined(HAVE_X_WINDOWS) && defined(LWLIB_MENUBARS_LUCID) +#if defined (HAVE_X_WINDOWS) && defined (LWLIB_MENUBARS_LUCID) if (NILP (result) && EQ (Vmenu_accelerator_enabled, Qmenu_fallback)) result = command_builder_find_menu_accelerator (builder); @@ -4867,7 +4832,8 @@ the_low_level_timeout_blocktype = Blocktype_new (struct low_level_timeout_blocktype); something_happened = 0; - recursive_sit_for = Qnil; + recursive_sit_for = 0; + in_modal_loop = 0; } void
--- a/src/event-tty.c Fri Feb 07 01:43:07 2003 +0000 +++ b/src/event-tty.c Fri Feb 07 11:50:54 2003 +0000 @@ -1,7 +1,7 @@ /* The event_stream interface for tty's. Copyright (C) 1994, 1995 Board of Trustees, University of Illinois. Copyright (C) 1995 Sun Microsystems, Inc. - Copyright (C) 1995, 2002 Ben Wing. + Copyright (C) 1995, 2002, 2003 Ben Wing. This file is part of XEmacs. @@ -69,7 +69,8 @@ /* timeout events have nil as channel */ SET_EVENT_TYPE (emacs_event, timeout_event); SET_EVENT_TIMESTAMP_ZERO (emacs_event); /* #### */ - SET_EVENT_TIMEOUT_INTERVAL_ID (emacs_event, pop_low_level_timeout (&tty_timer_queue, 0)); + SET_EVENT_TIMEOUT_INTERVAL_ID (emacs_event, + pop_low_level_timeout (&tty_timer_queue, 0)); SET_EVENT_TIMEOUT_FUNCTION (emacs_event, Qnil); SET_EVENT_TIMEOUT_OBJECT (emacs_event, Qnil); } @@ -77,9 +78,9 @@ static int -emacs_tty_event_pending_p (int user_p) +emacs_tty_event_pending_p (int how_many) { - if (!user_p) + if (!how_many) { EMACS_TIME sometime; /* see if there's a pending timeout. */ @@ -87,10 +88,14 @@ if (tty_timer_queue && EMACS_TIME_EQUAL_OR_GREATER (sometime, tty_timer_queue->time)) return 1; + + return poll_fds_for_input (non_fake_input_wait_mask); } - return poll_fds_for_input (user_p ? tty_only_mask : - non_fake_input_wait_mask); + /* #### Not right! We need to *count* the number of pending events, which + means we need to have a dispatch queue and drain the pending events, + using drain_tty_devices(). */ + return poll_fds_for_input (tty_only_mask); } static void
--- a/src/event-unixoid.c Fri Feb 07 01:43:07 2003 +0000 +++ b/src/event-unixoid.c Fri Feb 07 11:50:54 2003 +0000 @@ -3,7 +3,7 @@ Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. Copyright (C) 1995 Board of Trustees, University of Illinois. Copyright (C) 1995 Sun Microsystems, Inc. - Copyright (C) 1995, 1996, 2001, 2002 Ben Wing. + Copyright (C) 1995, 1996, 2001, 2002, 2003 Ben Wing. This file is part of XEmacs. @@ -44,12 +44,14 @@ /* Mask of bits indicating the descriptors that we wait for input on. These work as follows: + In event-tty.c we call select() directly on this + to retrieve an event. In event-Xt.c we use + XtAppAddInput() and the call to select() is down in + the guts of Xt, but we still use the masks when checking for pending input, even in event-Xt.c. (We can't use XtAppPending() because of the presence of the signal event pipe.) + input_wait_mask == mask of all file descriptors we select() on, including TTY/stream console descriptors, process descriptors, and the signal event pipe. - Only used in event-tty.c; event-Xt.c uses - XtAppAddInput(), and the call to select() is down in - the guts of Xt. non_fake_input_wait_mask == same as input_wait_mask but minus the signal event pipe. Also only used in
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/event-xlike-inc.c Fri Feb 07 11:50:54 2003 +0000 @@ -0,0 +1,161 @@ +/* Shared event code between X and GTK -- include file. + Copyright (C) 1991-5, 1997 Free Software Foundation, Inc. + Copyright (C) 1995 Sun Microsystems, Inc. + Copyright (C) 1996, 2001, 2002, 2003 Ben Wing. + +This file is part of XEmacs. + +XEmacs is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +XEmacs is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with XEmacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Synched up with: Not in FSF. */ + +/* For some code it's reasonable to have only one copy and conditionalize + at run-time. For other code it isn't. #### Perhaps all code should be + included here, not in event-xlike.c. However, event-xlike.c is always + X-specific, whereas the following code isn't, in the GTK case. */ + +static int +#ifdef THIS_IS_GTK +emacs_gtk_event_pending_p (int how_many) +#else +emacs_Xt_event_pending_p (int how_many) +#endif +{ + Lisp_Object event; + int tick_count_val; + + /* If `how_many' is 0, then this function returns whether there are any + X, timeout, or fd events pending (that is, whether + emacs_Xt_next_event() would return immediately without blocking). + + If `how_many' is > 0, then this function returns whether there are + that many *user generated* events available (keyboard, mouse click, + etc.). This also implies that emacs_Xt_next_event() would not block. + */ + + /* This function used to simply check whether there were any X events (or + if user_p was 1, it iterated over all the pending X events using + XCheckIfEvent(), looking for keystrokes and button events). That + worked in the old cheesoid event loop, which didn't go through + XtAppDispatchEvent(), but it doesn't work any more -- X events may not + result in anything. For example, a button press in a blank part of + the menubar appears as an X event but will not result in any Emacs + events (a button press that activates the menubar results in an Emacs + event through the stop_next_event mechanism). + + The only accurate way of determining whether these X events translate + into Emacs events is to go ahead and dispatch them until there's + something on the dispatch queue. */ + + if (!how_many) + { + /* We're being asked for *ALL* events, not just user events. */ + + /* (1) Any pending events in the dispatch queue? */ + if (!NILP (dispatch_event_queue)) + return 1; + + /* (2) Any TTY or process input available? + + Note that formerly we just checked the value of XtAppPending() to + determine if there was file-desc input. This doesn't work any + more with the signal_event_pipe; XtAppPending() will says "yes" in + this case but there isn't really any input. So instead we keep + track of the file descriptors, and call select() ourselves. + Another way of fixing this problem is for the signal_event_pipe to + generate actual input in the form of an identity eval event or + something. (#### maybe this actually happens?) */ + + if (poll_fds_for_input (non_fake_input_wait_mask)) + return 1; + +#ifndef THIS_IS_GTK + /* (3) Any timeout input available? */ + if (XtAppPending (Xt_app_con) & XtIMTimer) + return 1; +#else + /* #### Is there any way to do this in Gtk? I don't think there + is a 'peek' for events */ +#endif + } + else + { + /* HOW_MANY > 0 */ + EVENT_CHAIN_LOOP (event, dispatch_event_queue) + { + if (command_event_p (event)) + { + how_many--; + if (how_many <= 0) + return 1; + } + } + } + + /* XtAppPending() can be super-slow, esp. over a network connection. + Quantify results have indicated that in some cases the call to + detect_input_pending() completely dominates the running time of + redisplay(). Fortunately, in a SIGIO world we can more quickly + determine whether there are any X events: if an event has happened + since the last time we checked, then a SIGIO will have happened. On a + machine with broken SIGIO, we'll still be in an OK state -- + quit_check_signal_tick_count will get ticked at least every 1/4 + second, so we'll be no more than that much behind reality. (In general + it's OK if we erroneously report no input pending when input is + actually pending() -- preemption is just a bit less efficient, that's + all. It's bad bad bad if you err the other way -- you've promised + that `next-event' won't block but it actually will, and some action + might get delayed until the next time you hit a key.) + */ + + if (!in_modal_loop) + { + /* quit_check_signal_tick_count is volatile so try to avoid race + conditions by using a temporary variable */ + tick_count_val = quit_check_signal_tick_count; + if (last_quit_check_signal_tick_count != tick_count_val +#if !defined (THIS_IS_GTK) && (!defined (SIGIO) || defined (CYGWIN)) + || (XtIMXEvent & XtAppPending (Xt_app_con)) +#endif + ) + { + last_quit_check_signal_tick_count = tick_count_val; + + /* We need to drain the entire queue now -- if we only drain part of + it, we may later on end up with events actually pending but + detect_input_pending() returning false because there wasn't + another SIGIO. */ + event_stream_drain_queue (); + + if (!how_many) + return !NILP (dispatch_event_queue); + + EVENT_CHAIN_LOOP (event, dispatch_event_queue) + { + if (command_event_p (event)) + { + how_many--; + if (how_many <= 0) + return 1; + } + } + + return 0; + } + } + + return 0; +}
--- a/src/events.h Fri Feb 07 01:43:07 2003 +0000 +++ b/src/events.h Fri Feb 07 11:50:54 2003 +0000 @@ -65,12 +65,13 @@ event_pending_cb A function which says whether there are events to be read. If called with an argument of 0, then this should say whether calling the next_event_cb will - block. If called with an argument of 1, then this - should say whether there are user-generated events - pending (that is, keypresses or mouse-clicks). This - is used for redisplay optimization, among other - things. On dumb ttys, these two results are the - same, but under a window system, they are not. + block. If called with a non-zero argument, then this + should say whether there are that many user-generated + events pending (that is, keypresses, mouse-clicks, + dialog-box selection events, etc.). (This is used for + redisplay optimization, among other things.) The + difference is that the former includes process events + and timer events, but the latter doesn't. If this function is not sure whether there are events to be read, it *must* return 0. Otherwise various @@ -205,7 +206,6 @@ void (*unselect_console_cb) (struct console *); void (*select_process_cb) (Lisp_Process *, int doin, int doerr); void (*unselect_process_cb) (Lisp_Process *, int doin, int doerr); - int (*quit_check_disallowed_p_cb)(void); void (*drain_queue_cb) (void); void (*force_event_pending_cb)(struct frame* f); void (*create_io_streams_cb) (void* /* inhandle*/, void* /*outhandle*/ , @@ -1031,6 +1031,7 @@ EXFUN (Fevent_point, 1); EXFUN (Fevent_window, 1); EXFUN (Fmake_event, 2); +EXFUN (Fnext_command_event, 2); extern Lisp_Object QKbackspace, QKdelete, QKescape, QKlinefeed, QKreturn; extern Lisp_Object QKspace, QKtab, Qmouse_event_p, Vcharacter_set_property; @@ -1127,6 +1128,10 @@ Lisp_Object errstream, USID* in_usid, USID* err_usid); +Lisp_Object event_stream_protect_modal_loop (const char *error_string, + Lisp_Object (*bfun) (void *barg), + void *barg, int flags); +void event_stream_drain_queue (void); void event_stream_quit_p (void); void run_pre_idle_hook (void); @@ -1173,7 +1178,7 @@ int in_single_console_state (void); extern int emacs_is_blocking; - +extern int in_modal_loop; extern volatile int sigint_happened; #ifdef HAVE_UNIXOID_EVENT_LOOP
--- a/src/indent.c Fri Feb 07 01:43:07 2003 +0000 +++ b/src/indent.c Fri Feb 07 11:50:54 2003 +0000 @@ -758,9 +758,15 @@ returned instead of the actual number of lines moved. A motion of zero lines returns the height of the current line. -Note that `vertical-motion' sets WINDOW's buffer's point, not -WINDOW's point. (This differs from FSF Emacs, which buggily always -sets current buffer's point, regardless of WINDOW.) +NOTE NOTE NOTE: GNU Emacs/XEmacs difference. + +What `vertical-motion' actually does is set WINDOW's buffer's point +if WINDOW is the selected window; else, it sets WINDOW's point. +This is unfortunately somewhat tricky to work with, and different +from GNU Emacs, which always uses the current buffer, not WINDOW's +buffer, always sets current buffer's point, and, from the +perspective of this function, temporarily makes WINDOW display +the current buffer if it wasn't already. */ (lines, window, pixels)) {
--- a/src/lisp.h Fri Feb 07 01:43:07 2003 +0000 +++ b/src/lisp.h Fri Feb 07 11:50:54 2003 +0000 @@ -4051,7 +4051,7 @@ EXFUN (Fsleep_for, 1); void wait_delaying_user_input (int (*) (void *), void *); -int detect_input_pending (void); +int detect_input_pending (int how_many); void reset_this_command_keys (Lisp_Object, int); Lisp_Object enqueue_misc_user_event (Lisp_Object, Lisp_Object, Lisp_Object); Lisp_Object enqueue_misc_user_event_pos (Lisp_Object, Lisp_Object, @@ -4216,6 +4216,7 @@ EXFUN (Frassq, 2); EXFUN (Fremassq, 2); EXFUN (Freplace_list, 2); +EXFUN (Fsafe_length, 1); EXFUN (Fsort, 2); EXFUN (Fstring_equal, 2); EXFUN (Fstring_lessp, 2);
--- a/src/menubar-msw.c Fri Feb 07 01:43:07 2003 +0000 +++ b/src/menubar-msw.c Fri Feb 07 11:50:54 2003 +0000 @@ -714,33 +714,43 @@ /* Message handling proxies */ /*------------------------------------------------------------------------*/ -static HMENU wm_initmenu_menu; -static struct frame *wm_initmenu_frame; +struct handle_wm_initmenu +{ + HMENU menu; + struct frame *frame; +}; static Lisp_Object -unsafe_handle_wm_initmenupopup (Lisp_Object u_n_u_s_e_d) +unsafe_handle_wm_initmenupopup (void *arg) { - return unsafe_handle_wm_initmenupopup_1 (wm_initmenu_menu, wm_initmenu_frame); + struct handle_wm_initmenu *z = (struct handle_wm_initmenu *) arg; + return unsafe_handle_wm_initmenupopup_1 (z->menu, z->frame); } static Lisp_Object -unsafe_handle_wm_initmenu (Lisp_Object u_n_u_s_e_d) +unsafe_handle_wm_initmenu (void *arg) { - return unsafe_handle_wm_initmenu_1 (wm_initmenu_frame); + struct handle_wm_initmenu *z = (struct handle_wm_initmenu *) arg; + return unsafe_handle_wm_initmenu_1 (z->frame); } Lisp_Object mswindows_handle_wm_initmenupopup (HMENU hmenu, struct frame *frm) { - /* We cannot pass hmenu as a lisp object. Use static var */ - wm_initmenu_menu = hmenu; - wm_initmenu_frame = frm; - /* Allow runaway filter code, e.g. custom, to be aborted. We are + struct handle_wm_initmenu z; + + z.menu = hmenu; + z.frame = frm; + + /* [[ Allow runaway filter code, e.g. custom, to be aborted. We are usually called from next_event_internal(), which has turned off - quit checking to read the C-g as an event. */ - return mswindows_protect_modal_loop ("Error during menu handling", - unsafe_handle_wm_initmenupopup, Qnil, - UNINHIBIT_QUIT); + quit checking to read the C-g as an event.]] + + #### This is bogus because by the very act of calling + event_stream_protect_modal_loop(), we disable event retrieval! */ + return event_stream_protect_modal_loop ("Error during menu handling", + unsafe_handle_wm_initmenupopup, &z, + UNINHIBIT_QUIT); } Lisp_Object @@ -749,10 +759,12 @@ /* Handle only frame menubar, ignore if from popup or system menu */ if (GetMenu (FRAME_MSWINDOWS_HANDLE (f)) == hmenu) { - wm_initmenu_frame = f; - return mswindows_protect_modal_loop ("Error during menu handling", - unsafe_handle_wm_initmenu, Qnil, - UNINHIBIT_QUIT); + struct handle_wm_initmenu z; + + z.frame = f; + return event_stream_protect_modal_loop ("Error during menu handling", + unsafe_handle_wm_initmenu, &z, + UNINHIBIT_QUIT); } return Qt; }
--- a/src/menubar-x.c Fri Feb 07 01:43:07 2003 +0000 +++ b/src/menubar-x.c Fri Feb 07 11:50:54 2003 +0000 @@ -396,18 +396,15 @@ midtwv.filter_p = filter_p; if (UNBOUNDP - (call_trapping_problems - (Qmenubar, "Error during menu callback", UNINHIBIT_QUIT, 0, - protected_menu_item_descriptor_to_widget_value_1, &midtwv))) + (event_stream_protect_modal_loop + ("Error during menu callback", + protected_menu_item_descriptor_to_widget_value_1, &midtwv, + UNINHIBIT_QUIT))) return 0; return midtwv.wv; } -#if defined (LWLIB_MENUBARS_LUCID) || (defined LWLIB_MENUBARS_MOTIF) -int in_menu_callback; -#endif - /* The order in which callbacks are run is funny to say the least. It's sometimes tricky to avoid running a callback twice, and to avoid returning prematurely. So, this function returns true @@ -435,7 +432,6 @@ struct device *d = get_device_from_display (XtDisplay (widget)); struct frame *f = x_any_window_to_frame (d, XtWindow (widget)); Lisp_Object frame; - int count; /* set in lwlib to the time stamp associated with the most recent menu operation */ @@ -460,17 +456,8 @@ assert (hack_wv->type == INCREMENTAL_TYPE); submenu_desc = VOID_TO_LISP (hack_wv->call_data); - /* - * #### Fix the menu code so this isn't necessary. - * - * Protect against reentering the menu code otherwise we will - * crash later when the code gets confused at the state - * changes. - */ - count = internal_bind_int (&in_menu_callback, 1); wv = (protected_menu_item_descriptor_to_widget_value (submenu_desc, SUBMENU_TYPE, 1, 0)); - unbind_to (count); if (!wv) {
--- a/src/menubar.h Fri Feb 07 01:43:07 2003 +0000 +++ b/src/menubar.h Fri Feb 07 11:50:54 2003 +0000 @@ -62,8 +62,6 @@ *builder); Lisp_Object command_builder_operate_menu_accelerator (struct command_builder *builder); - -extern int in_menu_callback; #endif extern int popup_menu_up_p;
--- a/src/redisplay.c Fri Feb 07 01:43:07 2003 +0000 +++ b/src/redisplay.c Fri Feb 07 11:50:54 2003 +0000 @@ -1,7 +1,7 @@ /* Display generation from window structure and buffer text. Copyright (C) 1994, 1995, 1996 Board of Trustees, University of Illinois. Copyright (C) 1995 Free Software Foundation, Inc. - Copyright (C) 1995, 1996, 2000, 2001, 2002 Ben Wing. + Copyright (C) 1995, 1996, 2000, 2001, 2002, 2003 Ben Wing. Copyright (C) 1995 Sun Microsystems, Inc. Copyright (C) 1996 Chuck Thompson. @@ -314,14 +314,17 @@ /* This used to be 10 but 30 seems to give much better performance. */ #define INIT_MAX_PREEMPTS 30 -static int max_preempts; +static Fixnum max_preempts; + +#define QUEUED_EVENTS_REQUIRED_FOR_PREEMPTION 4 #define REDISPLAY_PREEMPTION_CHECK \ ((void) \ (preempted = \ (!disable_preemption && \ ((preemption_count < max_preempts) || !NILP (Vexecuting_macro)) && \ - (!INTERACTIVE || detect_input_pending ())))) + (!INTERACTIVE || \ + detect_input_pending (QUEUED_EVENTS_REQUIRED_FOR_PREEMPTION))))) /* * Redisplay global variables. @@ -9606,7 +9609,6 @@ { disable_preemption = 0; preemption_count = 0; - max_preempts = INIT_MAX_PREEMPTS; #ifndef PDUMP if (!initialized) @@ -9734,6 +9736,11 @@ */ ); cache_adjustment = 2; + DEFVAR_INT ("maximum-preempts", &max_preempts /* +Maximum number of times redisplay can be preempted by user input. +*/ ); + max_preempts = INIT_MAX_PREEMPTS; + DEFVAR_INT_MAGIC ("pixel-vertical-clip-threshold", &vertical_clip /* Minimum pixel height for clipped bottom display line. A clipped line shorter than this won't be displayed.