From mboxrd@z Thu Jan 1 00:00:00 1970 Path: main.gmane.org!not-for-mail From: "Jan D." Newsgroups: gmane.emacs.devel Subject: Re: Patch for Emacs X focus issue Date: Wed, 26 Jun 2002 15:03:08 +0200 (CEST) Sender: emacs-devel-admin@gnu.org Message-ID: <200206261258.g5QCwcuM001371@stubby.bodenonline.com> References: NNTP-Posting-Host: localhost.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit X-Trace: main.gmane.org 1025096789 19758 127.0.0.1 (26 Jun 2002 13:06:29 GMT) X-Complaints-To: usenet@main.gmane.org NNTP-Posting-Date: Wed, 26 Jun 2002 13:06:29 +0000 (UTC) Cc: emacs-devel@gnu.org Return-path: Original-Received: from quimby.gnus.org ([80.91.224.244]) by main.gmane.org with esmtp (Exim 3.33 #1 (Debian)) id 17NCVA-00058Z-00 for ; Wed, 26 Jun 2002 15:06:28 +0200 Original-Received: from fencepost.gnu.org ([199.232.76.164]) by quimby.gnus.org with esmtp (Exim 3.12 #1 (Debian)) id 17NCXI-0000Hd-00 for ; Wed, 26 Jun 2002 15:08:40 +0200 Original-Received: from localhost ([127.0.0.1] helo=fencepost.gnu.org) by fencepost.gnu.org with esmtp (Exim 3.34 #1 (Debian)) id 17NCV7-00035z-00; Wed, 26 Jun 2002 09:06:25 -0400 Original-Received: from stubby.bodenonline.com ([213.115.192.62]) by fencepost.gnu.org with esmtp (Exim 3.34 #1 (Debian)) id 17NCSV-0002vY-00; Wed, 26 Jun 2002 09:03:43 -0400 Original-Received: from pc35.bodenonline.com (IDENT:root@pc35.bodenonline.com [195.196.29.227] (may be forged)) by stubby.bodenonline.com (8.12.1/8.12.1) with ESMTP id g5QCwcuM001371; Wed, 26 Jun 2002 14:58:39 +0200 In-Reply-To: "from Richard Stallman at Jun 25, 2002 07:32:13 pm" Original-To: rms@gnu.org Errors-To: emacs-devel-admin@gnu.org X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.0.11 Precedence: bulk List-Help: List-Post: List-Subscribe: , List-Id: Emacs development discussions. List-Unsubscribe: , List-Archive: Xref: main.gmane.org gmane.emacs.devel:5213 X-Report-Spam: http://spam.gmane.org/gmane.emacs.devel:5213 > I think that the input-pending-p problem needs a real fix. > These events should be ignored for the sake of input-pending-p > even if they are not ignored for some other purposes. > > Perhaps there should be two different functions for asking > whether there is input, one for low-level Emacs purposes > and one for high-level purposes. Can you try fixing it that way? > Here's a modified patch, please comment. Jan D. Index: src/keyboard.c *** src/keyboard.c.~1.683.~ 2002-06-22 16:17:17.000000000 +0200 --- src/keyboard.c 2002-06-26 14:56:12.000000000 +0200 *************** *** 672,678 **** --- 672,680 ---- static int read_avail_input P_ ((int)); static void get_input_pending P_ ((int *, int)); + static void get_filtered_input_pending P_ ((int *, int, int)); static int readable_events P_ ((int)); + static int readable_filtered_events P_ ((int, int)); static Lisp_Object read_char_x_menu_prompt P_ ((int, Lisp_Object *, Lisp_Object, int *)); static Lisp_Object read_char_x_menu_prompt (); *************** *** 3253,3283 **** /* Return true iff there are any events in the queue that read-char would return. If this returns false, a read-char would block. */ static int ! readable_events (do_timers_now) int do_timers_now; { if (do_timers_now) timer_check (do_timers_now); /* If the buffer contains only FOCUS_IN_EVENT events, ! report it as empty. */ if (kbd_fetch_ptr != kbd_store_ptr) { struct input_event *event; event = ((kbd_fetch_ptr < kbd_buffer + KBD_BUFFER_SIZE) ? kbd_fetch_ptr : kbd_buffer); ! while (event->kind == FOCUS_IN_EVENT) { event++; if (event == kbd_buffer + KBD_BUFFER_SIZE) event = kbd_buffer; if (event == kbd_store_ptr) ! return 0; } ! return 1; } #ifdef HAVE_MOUSE --- 3255,3291 ---- /* Return true iff there are any events in the queue that read-char would return. If this returns false, a read-char would block. */ static int ! readable_filtered_events (do_timers_now, filter_events) int do_timers_now; + int filter_events; { if (do_timers_now) timer_check (do_timers_now); /* If the buffer contains only FOCUS_IN_EVENT events, ! and FILTER_EVENTS is nonzero, report it as empty. */ if (kbd_fetch_ptr != kbd_store_ptr) { + int have_live_event = 1; + + if (filter_events) + { struct input_event *event; event = ((kbd_fetch_ptr < kbd_buffer + KBD_BUFFER_SIZE) ? kbd_fetch_ptr : kbd_buffer); ! while (have_live_event && event->kind == FOCUS_IN_EVENT) { event++; if (event == kbd_buffer + KBD_BUFFER_SIZE) event = kbd_buffer; if (event == kbd_store_ptr) ! have_live_event = 0; } ! } ! if (have_live_event) return 1; } #ifdef HAVE_MOUSE *************** *** 3299,3304 **** --- 3307,3321 ---- return 0; } + /* Return true iff there are any events in the queue that read-char + would return. If this returns false, a read-char would block. */ + static int + readable_events (do_timers_now) + int do_timers_now; + { + return readable_filtered_events (do_timers_now, 0); + } + /* Set this for debugging, to have a way to get out */ int stop_character; *************** *** 6144,6158 **** but works even if FIONREAD does not exist. (In fact, this may actually read some input.) ! If DO_TIMERS_NOW is nonzero, actually run timer events that are ripe. */ static void ! get_input_pending (addr, do_timers_now) int *addr; int do_timers_now; { /* First of all, have we already counted some input? */ ! *addr = !NILP (Vquit_flag) || readable_events (do_timers_now); /* If input is being read as it arrives, and we have none, there is none. */ if (*addr > 0 || (interrupt_input && ! interrupts_deferred)) --- 6161,6178 ---- but works even if FIONREAD does not exist. (In fact, this may actually read some input.) ! If DO_TIMERS_NOW is nonzero, actually run timer events that are ripe. ! If FILTER_EVENTS is nonzero, ignore internal events (FOCUS_IN_EVENT). */ static void ! get_filtered_input_pending (addr, do_timers_now, filter_events) int *addr; int do_timers_now; + int filter_events; { /* First of all, have we already counted some input? */ ! *addr = (!NILP (Vquit_flag) ! || readable_filtered_events (do_timers_now, filter_events)); /* If input is being read as it arrives, and we have none, there is none. */ if (*addr > 0 || (interrupt_input && ! interrupts_deferred)) *************** *** 6160,6166 **** /* Try to read some input and see how much we get. */ gobble_input (0); ! *addr = !NILP (Vquit_flag) || readable_events (do_timers_now); } /* Interface to read_avail_input, blocking SIGIO or SIGALRM if necessary. */ --- 6180,6202 ---- /* Try to read some input and see how much we get. */ gobble_input (0); ! *addr = (!NILP (Vquit_flag) ! || readable_filtered_events (do_timers_now, filter_events)); ! } ! ! /* Store into *addr a value nonzero if terminal input chars are available. ! Serves the purpose of ioctl (0, FIONREAD, addr) ! but works even if FIONREAD does not exist. ! (In fact, this may actually read some input.) ! ! If DO_TIMERS_NOW is nonzero, actually run timer events that are ripe. */ ! ! static void ! get_input_pending (addr, do_timers_now) ! int *addr; ! int do_timers_now; ! { ! get_filtered_input_pending (addr, do_timers_now, 0); } /* Interface to read_avail_input, blocking SIGIO or SIGALRM if necessary. */ *************** *** 9563,9569 **** if (!NILP (Vunread_command_events) || unread_command_char != -1) return (Qt); ! get_input_pending (&input_pending, 1); return input_pending > 0 ? Qt : Qnil; } --- 9599,9605 ---- if (!NILP (Vunread_command_events) || unread_command_char != -1) return (Qt); ! get_filtered_input_pending (&input_pending, 1, 1); return input_pending > 0 ? Qt : Qnil; } Index: src/xterm.c *** src/xterm.c.~1.736.~ 2002-06-15 21:57:57.000000000 +0200 --- src/xterm.c 2002-06-24 23:32:07.000000000 +0200 *************** *** 466,471 **** --- 466,481 ---- static void frame_highlight P_ ((struct frame *)); static void frame_unhighlight P_ ((struct frame *)); static void x_new_focus_frame P_ ((struct x_display_info *, struct frame *)); + static int x_focus_changed P_ ((int, + int, + struct x_display_info *, + struct frame *, + struct input_event *, + int)); + static int x_detect_focus_change P_ ((struct x_display_info *, + XEvent *, + struct input_event *, + int)); static void XTframe_rehighlight P_ ((struct frame *)); static void x_frame_rehighlight P_ ((struct x_display_info *)); static void x_draw_hollow_cursor P_ ((struct window *, struct glyph_row *)); *************** *** 6291,6296 **** --- 6301,6421 ---- x_frame_rehighlight (dpyinfo); } + /* Handle FocusIn and FocusOut state changes for FRAME. + If FRAME has focus and there exists more than one frame, puts + an FOCUS_IN_EVENT into BUFP. + Returns number of events inserted into BUFP. */ + + static int + x_focus_changed (type, state, dpyinfo, frame, bufp, numchars) + int type; + int state; + struct x_display_info *dpyinfo; + struct frame *frame; + struct input_event *bufp; + int numchars; + { + int nr_events = 0; + + if (type == FocusIn) + { + if (dpyinfo->x_focus_event_frame != frame) + { + x_new_focus_frame (dpyinfo, frame); + dpyinfo->x_focus_event_frame = frame; + + /* Don't stop displaying the initial startup message + for a switch-frame event we don't need. */ + if (numchars > 0 + && GC_NILP (Vterminal_frame) + && GC_CONSP (Vframe_list) + && !GC_NILP (XCDR (Vframe_list))) + { + bufp->kind = FOCUS_IN_EVENT; + XSETFRAME (bufp->frame_or_window, frame); + bufp->arg = Qnil; + ++bufp; + numchars--; + ++nr_events; + } + } + + frame->output_data.x->focus_state |= state; + + #ifdef HAVE_X_I18N + if (FRAME_XIC (frame)) + XSetICFocus (FRAME_XIC (frame)); + #endif + } + else if (type == FocusOut) + { + frame->output_data.x->focus_state &= ~state; + + if (dpyinfo->x_focus_event_frame == frame) + { + dpyinfo->x_focus_event_frame = 0; + x_new_focus_frame (dpyinfo, 0); + } + + #ifdef HAVE_X_I18N + if (FRAME_XIC (frame)) + XUnsetICFocus (FRAME_XIC (frame)); + #endif + } + + return nr_events; + } + + /* The focus may have changed. Figure out if it is a real focus change, + by checking both FocusIn/Out and Enter/LeaveNotify events. + + Returns number of events inserted into BUFP. */ + + static int + x_detect_focus_change (dpyinfo, event, bufp, numchars) + struct x_display_info *dpyinfo; + XEvent *event; + struct input_event *bufp; + int numchars; + { + struct frame *frame; + int nr_events = 0; + + frame = x_top_window_to_frame (dpyinfo, event->xany.window); + if (! frame) return nr_events; + + switch (event->type) + { + case EnterNotify: + case LeaveNotify: + if (event->xcrossing.detail != NotifyInferior + && event->xcrossing.focus + && ! (frame->output_data.x->focus_state & FOCUS_EXPLICIT)) + nr_events = x_focus_changed ((event->type == EnterNotify + ? FocusIn : FocusOut), + FOCUS_IMPLICIT, + dpyinfo, + frame, + bufp, + numchars); + break; + + case FocusIn: + case FocusOut: + nr_events = x_focus_changed (event->type, + (event->xfocus.detail == NotifyPointer + ? FOCUS_IMPLICIT : FOCUS_EXPLICIT), + dpyinfo, + frame, + bufp, + numchars); + break; + } + + return nr_events; + } + + /* Handle an event saying the mouse has moved out of an Emacs frame. */ void *************** *** 10746,10760 **** goto OTHER; #endif - /* Here's a possible interpretation of the whole - FocusIn-EnterNotify FocusOut-LeaveNotify mess. If - you get a FocusIn event, you have to get a FocusOut - event before you relinquish the focus. If you - haven't received a FocusIn event, then a mere - LeaveNotify is enough to free you. */ - case EnterNotify: { f = x_any_window_to_frame (dpyinfo, event.xcrossing.window); #if 0 --- 10871,10886 ---- goto OTHER; #endif case EnterNotify: { + int n; + + n = x_detect_focus_change (dpyinfo, &event, bufp, numchars); + if (n > 0) + { + bufp += n, count += n, numchars -= n; + } + f = x_any_window_to_frame (dpyinfo, event.xcrossing.window); #if 0 *************** *** 10781,10814 **** } case FocusIn: - f = x_any_window_to_frame (dpyinfo, event.xfocus.window); - if (event.xfocus.detail != NotifyPointer) - dpyinfo->x_focus_event_frame = f; - if (f) { ! x_new_focus_frame (dpyinfo, f); ! /* Don't stop displaying the initial startup message ! for a switch-frame event we don't need. */ ! if (GC_NILP (Vterminal_frame) ! && GC_CONSP (Vframe_list) ! && !GC_NILP (XCDR (Vframe_list))) { ! bufp->kind = FOCUS_IN_EVENT; ! XSETFRAME (bufp->frame_or_window, f); ! bufp->arg = Qnil; ! ++bufp, ++count, --numchars; } } - #ifdef HAVE_X_I18N - if (f && FRAME_XIC (f)) - XSetICFocus (FRAME_XIC (f)); - #endif - goto OTHER; case LeaveNotify: f = x_top_window_to_frame (dpyinfo, event.xcrossing.window); if (f) { --- 10907,10935 ---- } case FocusIn: { ! int n; ! n = x_detect_focus_change (dpyinfo, &event, bufp, numchars); ! if (n > 0) { ! bufp += n, count += n, numchars -= n; } } goto OTHER; case LeaveNotify: + { + int n; + + n = x_detect_focus_change (dpyinfo, &event, bufp, numchars); + if (n > 0) + { + bufp += n, count += n, numchars -= n; + } + } + f = x_top_window_to_frame (dpyinfo, event.xcrossing.window); if (f) { *************** *** 10836,10867 **** bufp += n, count += n, numchars -= n; } - #if 0 - if (event.xcrossing.focus) - x_mouse_leave (dpyinfo); - else - { - if (f == dpyinfo->x_focus_event_frame) - dpyinfo->x_focus_event_frame = 0; - if (f == dpyinfo->x_focus_frame) - x_new_focus_frame (dpyinfo, 0); - } - #endif } goto OTHER; case FocusOut: ! f = x_any_window_to_frame (dpyinfo, event.xfocus.window); ! if (event.xfocus.detail != NotifyPointer ! && f == dpyinfo->x_focus_event_frame) ! dpyinfo->x_focus_event_frame = 0; ! if (f && f == dpyinfo->x_focus_frame) ! x_new_focus_frame (dpyinfo, 0); ! #ifdef HAVE_X_I18N ! if (f && FRAME_XIC (f)) ! XUnsetICFocus (FRAME_XIC (f)); ! #endif goto OTHER; --- 10957,10975 ---- bufp += n, count += n, numchars -= n; } } goto OTHER; case FocusOut: ! { ! int n; ! n = x_detect_focus_change (dpyinfo, &event, bufp, numchars); ! if (n > 0) ! { ! bufp += n, count += n, numchars -= n; ! } ! } goto OTHER; Index: src/xterm.h *** src/xterm.h.~1.131.~ 2002-06-15 21:57:57.000000000 +0200 --- src/xterm.h 2002-06-24 23:26:28.000000000 +0200 *************** *** 618,623 **** --- 618,628 ---- these may differ because this does not take into account possible menubar. y_pixels_diff is with menubar height included */ int y_pixels_outer_diff; + + /* Keep track of focus. May be EXPLICIT if we received a FocusIn for this + frame, or IMPLICIT if we received an EnterNotify. + FocusOut and LeaveNotify clears EXPLICIT/IMPLICIT. */ + int focus_state; }; enum *************** *** 631,636 **** --- 636,654 ---- FULLSCREEN_MOVE_WAIT = 8, }; + enum + { + /* Values for focus_state, used as bit mask. + EXPLICIT means if we received a FocusIn for the frame and know it has + the focus. IMPLICIT means we recevied an EnterNotify and the frame + may have the focus if no window manager is running. + FocusOut and LeaveNotify clears EXPLICIT/IMPLICIT. */ + FOCUS_NONE = 0, + FOCUS_IMPLICIT = 1, + FOCUS_EXPLICIT = 2 + }; + + /* Return the X window used for displaying data in frame F. */ #define FRAME_X_WINDOW(f) ((f)->output_data.x->window_desc)