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: Patch for Emacs X focus issue Date: Sun, 23 Jun 2002 21:16:32 +0200 (CEST) Sender: emacs-devel-admin@gnu.org Message-ID: <200206231914.g5NJEtuM019863@stubby.bodenonline.com> 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 1024860119 1663 127.0.0.1 (23 Jun 2002 19:21:59 GMT) X-Complaints-To: usenet@main.gmane.org NNTP-Posting-Date: Sun, 23 Jun 2002 19:21:59 +0000 (UTC) Return-path: Original-Received: from quimby.gnus.org ([80.91.224.244]) by main.gmane.org with esmtp (Exim 3.33 #1 (Debian)) id 17MCvv-0000Qi-00 for ; Sun, 23 Jun 2002 21:21:59 +0200 Original-Received: from fencepost.gnu.org ([199.232.76.164]) by quimby.gnus.org with esmtp (Exim 3.12 #1 (Debian)) id 17MCwi-0003sS-00 for ; Sun, 23 Jun 2002 21:22:48 +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 17MCvY-0002kt-00; Sun, 23 Jun 2002 15:21:36 -0400 Original-Received: from stubby.bodenonline.com ([213.115.192.62]) by fencepost.gnu.org with esmtp (Exim 3.34 #1 (Debian)) id 17MCtt-0002ff-00 for ; Sun, 23 Jun 2002 15:19:54 -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 g5NJEtuM019863 for ; Sun, 23 Jun 2002 21:14:59 +0200 Original-To: emacs-devel@gnu.org Errors-To: emacs-devel-admin@gnu.org X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.0.9 Precedence: bulk List-Help: List-Post: List-Subscribe: , List-Id: Emacs development discussions. List-Unsubscribe: , List-Archive: Xref: main.gmane.org gmane.emacs.devel:5127 X-Report-Spam: http://spam.gmane.org/gmane.emacs.devel:5127 Hello. I've made a patch for the issues with focus under X. For example, Emacs does not detect focus when no window manager is running, and it can get out of sync even with a window manager running. Richard Stallman mailed me a description of how this could be done, but I could not make it work in all situations. The situation without any window manager was not handeled correctly. So, since xterm always seemed to get things right (I used that as a reference when testing), I looked in that code instead and adopted that strategy for Emacs. Both ways share the same general strategy but differs in details. I haven't managed to get Emacs out of sync with respect with focus with the xterm strategy. However, there are many ways to configure Emacs and window managers, it is hard to cover all cases. The patch is attached below. Please comment, and if it is OK, I will commit it into CVS. Thanks, Jan D. Index: src/keyboard.c *** src/keyboard.c.~1.683.~ 2002-06-22 16:17:17.000000000 +0200 --- src/keyboard.c 2002-06-23 19:21:07.000000000 +0200 *************** *** 3259,3285 **** 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 if (!NILP (do_mouse_tracking) && some_mouse_moved ()) return 1; --- 3259,3266 ---- Index: src/xterm.c *** src/xterm.c.~1.736.~ 2002-06-15 21:57:57.000000000 +0200 --- src/xterm.c 2002-06-23 20:37:58.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_HAVEIT)) + nr_events = x_focus_changed (event->type == EnterNotify ? + FocusIn : FocusOut, + FOCUS_INWINDOW, + dpyinfo, + frame, + bufp, + numchars); + break; + + case FocusIn: + case FocusOut: + nr_events = x_focus_changed (event->type, + event->xfocus.detail == NotifyPointer ? + FOCUS_INWINDOW : FOCUS_HAVEIT, + 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,10888 ---- goto OTHER; #endif case EnterNotify: { + int n; + + if ((n = x_detect_focus_change (dpyinfo, + &event, + bufp, + numchars)) > 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) { --- 10909,10941 ---- } case FocusIn: { ! int n; ! if ((n = x_detect_focus_change (dpyinfo, ! &event, ! bufp, ! numchars)) > 0) { ! bufp += n, count += n, numchars -= n; } } goto OTHER; case LeaveNotify: + { + int n; + + if ((n = x_detect_focus_change (dpyinfo, + &event, + bufp, + numchars)) > 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; --- 10963,10983 ---- bufp += n, count += n, numchars -= n; } } goto OTHER; case FocusOut: ! { ! int n; ! if ((n = x_detect_focus_change (dpyinfo, ! &event, ! bufp, ! numchars)) > 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-23 19:09:21.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 HAVEIT if we received a FocusIn for this + frame, or INWINDOW if we received an EnterNotify. + FocusOut and LeaveNotify clears HAVEIT/INWINDOW. */ + int focus_state; }; enum *************** *** 631,636 **** --- 636,650 ---- FULLSCREEN_MOVE_WAIT = 8, }; + enum + { + /* Values for focus_state, used as bit mask. */ + FOCUS_NONE = 0, + FOCUS_INWINDOW = 1, + FOCUS_HAVEIT = 2 + }; + + /* Return the X window used for displaying data in frame F. */ #define FRAME_X_WINDOW(f) ((f)->output_data.x->window_desc)