comparison src/event-Xt.c @ 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 e22b0213b713
children f3437b56874d
comparison
equal deleted inserted replaced
1267:c57f32e44416 1268:fffe735e63ee
1 /* The event_stream interface for X11 with Xt, and/or tty frames. 1 /* The event_stream interface for X11 with Xt, and/or tty frames.
2 Copyright (C) 1991-5, 1997 Free Software Foundation, Inc. 2 Copyright (C) 1991-5, 1997 Free Software Foundation, Inc.
3 Copyright (C) 1995 Sun Microsystems, Inc. 3 Copyright (C) 1995 Sun Microsystems, Inc.
4 Copyright (C) 1996, 2001, 2002 Ben Wing. 4 Copyright (C) 1996, 2001, 2002, 2003 Ben Wing.
5 5
6 This file is part of XEmacs. 6 This file is part of XEmacs.
7 7
8 XEmacs is free software; you can redistribute it and/or modify it 8 XEmacs is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the 9 under the terms of the GNU General Public License as published by the
96 static int process_events_occurred; 96 static int process_events_occurred;
97 static int tty_events_occurred; 97 static int tty_events_occurred;
98 static Widget widget_with_focus; 98 static Widget widget_with_focus;
99 99
100 /* Mask of bits indicating the descriptors that we wait for input on */ 100 /* Mask of bits indicating the descriptors that we wait for input on */
101 extern SELECT_TYPE input_wait_mask, process_only_mask, tty_only_mask; 101 extern SELECT_TYPE input_wait_mask, non_fake_input_wait_mask;
102 extern SELECT_TYPE process_only_mask, tty_only_mask;
102 103
103 static const String x_fallback_resources[] = 104 static const String x_fallback_resources[] =
104 { 105 {
105 /* This file is automatically generated from the app-defaults file 106 /* This file is automatically generated from the app-defaults file
106 in ../etc/Emacs.ad. These resources are consulted only if no 107 in ../etc/Emacs.ad. These resources are consulted only if no
118 119
119 static int last_quit_check_signal_tick_count; 120 static int last_quit_check_signal_tick_count;
120 121
121 Lisp_Object Qkey_mapping; 122 Lisp_Object Qkey_mapping;
122 Lisp_Object Qsans_modifiers; 123 Lisp_Object Qsans_modifiers;
124
125 #define THIS_IS_X
126 #include "event-xlike-inc.c"
123 127
124 128
125 /************************************************************************/ 129 /************************************************************************/
126 /* keymap handling */ 130 /* keymap handling */
127 /************************************************************************/ 131 /************************************************************************/
2769 !completed_timeouts && 2773 !completed_timeouts &&
2770 !fake_event_occurred && 2774 !fake_event_occurred &&
2771 !process_events_occurred && 2775 !process_events_occurred &&
2772 !tty_events_occurred) 2776 !tty_events_occurred)
2773 { 2777 {
2774 2778 if (in_modal_loop)
2775 /* Stupid logic in XtAppProcessEvent() dictates that, if process 2779 {
2776 events and X events are both available, the process event gets 2780 /* in_modal_loop gets set when we are in the process of
2777 taken first. This will cause an infinite loop if we're being 2781 dispatching an event (more specifically, when we are inside of
2778 called from Fdiscard_input(). 2782 a menu callback -- if we get here, it means we called a filter
2779 */ 2783 and the filter did something that tried to fetch an event,
2780 if (XtAppPending (Xt_app_con) & XtIMXEvent) 2784 e.g. sit-for). In such a case, we cannot safely dispatch any
2781 XtAppProcessEvent (Xt_app_con, XtIMXEvent); 2785 more events. This is because those dispatching those events
2786 could cause lwlib to be entered reentranty, specifically if
2787 they are menu events. lwlib is not designed for this and will
2788 crash. We used to see this crash constantly as a result of
2789 QUIT checking, but QUIT will not now function in a modal loop.
2790 However, we can't just not process any events at all, because
2791 that will make sit-for etc. hang. So we go ahead and process
2792 the non-X kinds of events. */
2793 XtInputMask pending_value = XtAppPending (Xt_app_con);
2794
2795 if (pending_value & (XtIMTimer | XtIMAlternateInput))
2796 XtAppProcessEvent (Xt_app_con, XtIMTimer | XtIMAlternateInput);
2797 }
2782 else 2798 else
2783 { 2799 {
2784 Lisp_Object devcons, concons; 2800 /* Stupid logic in XtAppProcessEvent() dictates that, if process
2785 2801 events and X events are both available, the process event gets
2786 /* We're about to block. Xt has a bug in it (big surprise, 2802 taken first. This will cause an infinite loop if we're being
2787 there) in that it blocks using select() and doesn't 2803 called from Fdiscard_input().
2788 flush the Xlib output buffers (XNextEvent() does this 2804 */
2789 automatically before blocking). So it's necessary 2805
2790 for us to do this ourselves. If we don't do it, then 2806 if (XtAppPending (Xt_app_con) & XtIMXEvent)
2791 display output may not be seen until the next time 2807 XtAppProcessEvent (Xt_app_con, XtIMXEvent);
2792 an X event is received. (This happens esp. with 2808 else
2793 subprocess output that gets sent to a visible buffer.)
2794
2795 #### The above comment may not have any validity. */
2796
2797 DEVICE_LOOP_NO_BREAK (devcons, concons)
2798 { 2809 {
2799 struct device *d; 2810 Lisp_Object devcons, concons;
2800 d = XDEVICE (XCAR (devcons)); 2811
2801 2812 /* We're about to block. Xt has a bug in it (big surprise,
2802 if (DEVICE_X_P (d) && DEVICE_X_DISPLAY (d)) 2813 there) in that it blocks using select() and doesn't
2803 /* emacs may be exiting */ 2814 flush the Xlib output buffers (XNextEvent() does this
2804 XFlush (DEVICE_X_DISPLAY (d)); 2815 automatically before blocking). So it's necessary
2816 for us to do this ourselves. If we don't do it, then
2817 display output may not be seen until the next time
2818 an X event is received. (This happens esp. with
2819 subprocess output that gets sent to a visible buffer.)
2820
2821 #### The above comment may not have any validity. */
2822
2823 DEVICE_LOOP_NO_BREAK (devcons, concons)
2824 {
2825 struct device *d;
2826 d = XDEVICE (XCAR (devcons));
2827
2828 if (DEVICE_X_P (d) && DEVICE_X_DISPLAY (d))
2829 /* emacs may be exiting */
2830 XFlush (DEVICE_X_DISPLAY (d));
2831 }
2832 XtAppProcessEvent (Xt_app_con, XtIMAll);
2805 } 2833 }
2806 XtAppProcessEvent (Xt_app_con, XtIMAll);
2807 } 2834 }
2808 } 2835 }
2809 2836
2810 if (!NILP (dispatch_event_queue)) 2837 if (!NILP (dispatch_event_queue))
2811 { 2838 {
2862 2889
2863 static void 2890 static void
2864 emacs_Xt_drain_queue (void) 2891 emacs_Xt_drain_queue (void)
2865 { 2892 {
2866 Lisp_Object devcons, concons; 2893 Lisp_Object devcons, concons;
2867 CONSOLE_LOOP (concons) 2894 if (!in_modal_loop)
2868 { 2895 {
2869 struct console *con = XCONSOLE (XCAR (concons)); 2896 CONSOLE_LOOP (concons)
2870 if (!con->input_enabled)
2871 continue;
2872
2873 CONSOLE_DEVICE_LOOP (devcons, con)
2874 { 2897 {
2875 struct device *d; 2898 struct console *con = XCONSOLE (XCAR (concons));
2876 Display *display; 2899 if (!con->input_enabled)
2877 d = XDEVICE (XCAR (devcons)); 2900 continue;
2878 if (DEVICE_X_P (d) && DEVICE_X_DISPLAY (d)) 2901
2902 CONSOLE_DEVICE_LOOP (devcons, con)
2879 { 2903 {
2880 display = DEVICE_X_DISPLAY (d); 2904 struct device *d;
2881 while (XEventsQueued (display, QueuedAfterReading)) 2905 Display *display;
2882 XtAppProcessEvent (Xt_app_con, XtIMXEvent); 2906 d = XDEVICE (XCAR (devcons));
2907 if (DEVICE_X_P (d) && DEVICE_X_DISPLAY (d))
2908 {
2909 display = DEVICE_X_DISPLAY (d);
2910 while (XEventsQueued (display, QueuedAfterReading))
2911 XtAppProcessEvent (Xt_app_con, XtIMXEvent);
2912 }
2883 } 2913 }
2884 } 2914 }
2885 } 2915 /*
2886 /* 2916 while (XtAppPending (Xt_app_con) & XtIMXEvent)
2887 while (XtAppPending (Xt_app_con) & XtIMXEvent) 2917 XtAppProcessEvent (Xt_app_con, XtIMXEvent);
2888 XtAppProcessEvent (Xt_app_con, XtIMXEvent); 2918 */
2889 */ 2919 }
2890 2920
2921 #ifdef HAVE_TTY
2891 drain_tty_devices (); 2922 drain_tty_devices ();
2892 }
2893
2894 static int
2895 emacs_Xt_event_pending_p (int user_p)
2896 {
2897 Lisp_Object event;
2898 int tick_count_val;
2899
2900 /* If `user_p' is false, then this function returns whether there are any
2901 X, timeout, or fd events pending (that is, whether emacs_Xt_next_event()
2902 would return immediately without blocking).
2903
2904 if `user_p' is true, then this function returns whether there are any
2905 *user generated* events available (that is, whether there are keyboard
2906 or mouse-click events ready to be read). This also implies that
2907 emacs_Xt_next_event() would not block.
2908
2909 In a non-SIGIO world, this also checks whether the user has typed ^G,
2910 since this is a convenient place to do so. We don't need to do this
2911 in a SIGIO world, since input causes an interrupt.
2912 */
2913
2914 #if 0
2915 /* I don't think there's any point to this and it will nullify
2916 the speed gains achieved by the sigio_happened checking below.
2917 Its only advantage is that it may possibly make C-g response
2918 a bit faster. The C-g will be noticed within 0.25 second, anyway,
2919 even without this. */
2920 #ifndef SIGIO
2921 /* First check for C-g if necessary */
2922 event_stream_quit_p ();
2923 #endif 2923 #endif
2924 #endif
2925
2926 /* This function used to simply check whether there were any X
2927 events (or if user_p was 1, it iterated over all the pending
2928 X events using XCheckIfEvent(), looking for keystrokes and
2929 button events). That worked in the old cheesoid event loop,
2930 which didn't go through XtAppDispatchEvent(), but it doesn't
2931 work any more -- X events may not result in anything. For
2932 example, a button press in a blank part of the menubar appears
2933 as an X event but will not result in any Emacs events (a
2934 button press that activates the menubar results in an Emacs
2935 event through the stop_next_event mechanism).
2936
2937 The only accurate way of determining whether these X events
2938 translate into Emacs events is to go ahead and dispatch them
2939 until there's something on the dispatch queue. */
2940
2941 /* See if there are any user events already on the queue. */
2942 EVENT_CHAIN_LOOP (event, dispatch_event_queue)
2943 if (!user_p || command_event_p (event))
2944 return 1;
2945
2946 /* See if there's any TTY input available.
2947 */
2948 if (poll_fds_for_input (tty_only_mask))
2949 return 1;
2950
2951 if (!user_p)
2952 {
2953 /* If not user_p and there are any timer or file-desc events
2954 pending, we know there will be an event so we're through. */
2955 XtInputMask pending_value;
2956
2957 /* Note that formerly we just checked the value of XtAppPending()
2958 to determine if there was file-desc input. This doesn't
2959 work any more with the signal_event_pipe; XtAppPending()
2960 will says "yes" in this case but there isn't really any
2961 input. Another way of fixing this problem is for the
2962 signal_event_pipe to generate actual input in the form
2963 of an identity eval event or something. (#### maybe this
2964 actually happens?) */
2965
2966 if (poll_fds_for_input (process_only_mask))
2967 return 1;
2968
2969 pending_value = XtAppPending (Xt_app_con);
2970
2971 if (pending_value & XtIMTimer)
2972 return 1;
2973 }
2974
2975 /* XtAppPending() can be super-slow, esp. over a network connection.
2976 Quantify results have indicated that in some cases the call to
2977 detect_input_pending() completely dominates the running time of
2978 redisplay(). Fortunately, in a SIGIO world we can more quickly
2979 determine whether there are any X events: if an event has
2980 happened since the last time we checked, then a SIGIO will have
2981 happened. On a machine with broken SIGIO, we'll still be in an
2982 OK state -- quit_check_signal_tick_count will get ticked at least
2983 every 1/4 second, so we'll be no more than that much behind
2984 reality. (In general it's OK if we erroneously report no input
2985 pending when input is actually pending() -- preemption is just a
2986 bit less efficient, that's all. It's bad bad bad if you err the
2987 other way -- you've promised that `next-event' won't block but it
2988 actually will, and some action might get delayed until the next
2989 time you hit a key.)
2990 */
2991
2992 /* quit_check_signal_tick_count is volatile so try to avoid race conditions
2993 by using a temporary variable */
2994 tick_count_val = quit_check_signal_tick_count;
2995 if (last_quit_check_signal_tick_count != tick_count_val
2996 #if !defined (SIGIO) || defined (CYGWIN)
2997 || (XtIMXEvent & XtAppPending (Xt_app_con))
2998 #endif
2999 )
3000 {
3001 last_quit_check_signal_tick_count = tick_count_val;
3002
3003 /* We need to drain the entire queue now -- if we only
3004 drain part of it, we may later on end up with events
3005 actually pending but detect_input_pending() returning
3006 false because there wasn't another SIGIO. */
3007 emacs_Xt_drain_queue ();
3008
3009 EVENT_CHAIN_LOOP (event, dispatch_event_queue)
3010 if (!user_p || command_event_p (event))
3011 return 1;
3012 }
3013
3014 return 0;
3015 } 2924 }
3016 2925
3017 int 2926 int
3018 check_if_pending_expose_event (struct device *dev) 2927 check_if_pending_expose_event (struct device *dev)
3019 { 2928 {