From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Daniel Colascione Newsgroups: gmane.emacs.bugs Subject: bug#57012: Activating versus raising frames Date: Sat, 06 Aug 2022 19:57:41 -0400 Message-ID: <6c3817726a4fc63e83a3d004dffdf072cae278c5.camel@dancol.org> References: <18270a59cb0.2829.cc5b3318d7e9908e2c46732289705cb0@dancol.org> <878ro25eo7.fsf@yahoo.com> Mime-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="21233"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Evolution 3.44.1-0ubuntu1 Cc: 57012@debbugs.gnu.org To: Po Lu Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Sun Aug 07 01:58:20 2022 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1oKTgQ-0005KI-Kz for geb-bug-gnu-emacs@m.gmane-mx.org; Sun, 07 Aug 2022 01:58:19 +0200 Original-Received: from localhost ([::1]:43148 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1oKTgP-0002Lz-89 for geb-bug-gnu-emacs@m.gmane-mx.org; Sat, 06 Aug 2022 19:58:17 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:54758) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1oKTgA-0002Lk-Rg for bug-gnu-emacs@gnu.org; Sat, 06 Aug 2022 19:58:03 -0400 Original-Received: from debbugs.gnu.org ([209.51.188.43]:45912) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1oKTgA-0000o6-Ip for bug-gnu-emacs@gnu.org; Sat, 06 Aug 2022 19:58:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1oKTgA-0003oM-EF for bug-gnu-emacs@gnu.org; Sat, 06 Aug 2022 19:58:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Daniel Colascione Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sat, 06 Aug 2022 23:58:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 57012 X-GNU-PR-Package: emacs Original-Received: via spool by 57012-submit@debbugs.gnu.org id=B57012.165983027314636 (code B ref 57012); Sat, 06 Aug 2022 23:58:02 +0000 Original-Received: (at 57012) by debbugs.gnu.org; 6 Aug 2022 23:57:53 +0000 Original-Received: from localhost ([127.0.0.1]:35661 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oKTg0-0003nz-HB for submit@debbugs.gnu.org; Sat, 06 Aug 2022 19:57:53 -0400 Original-Received: from dancol.org ([96.126.100.184]:51958) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oKTfu-0003nm-VN for 57012@debbugs.gnu.org; Sat, 06 Aug 2022 19:57:51 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=dancol.org; s=x; h=MIME-Version:Content-Transfer-Encoding:Content-Type:References: In-Reply-To:Date:Cc:To:From:Subject:Message-ID:Sender:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=e2AGEuzQH/7NSVKJjN9iv+bmMpoXNJ2eBq9PfZMyT9g=; b=HCUVF/mv7vF8lEdsQHvlPuJuQS fxLelWmyq2qzrpHfm/24PavNgJssxuLrgxT5IbG75XnFVGo4kJedH7w+zSmD+gWHITpOo3oIp27ln 5Cs6xs1iWkWjlFmII+uinto6x88k4cnn81hjmzoDUNybESef2KqqlF86zHQQesO5jIVwMWOFnF/Kb JGUpR4ZRvWUkStLFvnuuwofWrhEr5oLcIMwfDN9GG5StlOCt1ufsA2lC87wxIHxhjdZzYfmUE0y4z VhwEq8aTLpv20lGSxKgpuGH6WyLwLcD1ACgJ83zVBDYviEuEUOH0EhdW4VhyzM84oSHDIzpWzD9Av cJlke27g==; Original-Received: from 2603-9001-4203-1ab2-640d-a545-3db0-5adc.inf6.spectrum.com ([2603:9001:4203:1ab2:640d:a545:3db0:5adc]:39122) by dancol.org with esmtpsa (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1oKTfq-0006Wz-5V; Sat, 06 Aug 2022 16:57:44 -0700 In-Reply-To: <878ro25eo7.fsf@yahoo.com> X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.io gmane.emacs.bugs:239007 Archived-At: On Sat, 2022-08-06 at 09:44 +0800, Po Lu wrote: > Daniel Colascione writes: >=20 > > I noticed that raising an Emacs frame (latest master) with > > emacsclient > > does nothing under current Cinnamon. Emacs gains focus, but the > > focused window isn't raised. > >=20 > > I can reproduce the problem with xdotool's windowraise command, > > which > > similarly did nothing. xdotool windowactivate works though, so > > I'm > > guessing the EWMH activate code in xterm.c would similarly do the > > trick. Emacs used to use EWMH activate on frame raise but stopped > > in > > 2007 to work around deadlocks in a version of metacity in use at > > the > > time. Can we once again activate Emacs frames on raise? >=20 > Wouldn't it make more sense for emacsclient to focus the frame, > which > does activate it by default? >=20 Hoo boy. I spent a bit of time digging into the event code. The root cause of the inability of emacsclient to raise the frame is that we've been getting X11 event timestamps wrong for some time. In particular, 1) in GTK builds, we're not updating the X timestamps for keyboard and mouse input events, and 2) we're not updating the X timestamp when we get an emacsclient request. Because of #2, when we=C2=A0call x-focus-window in select-frame-set-input-focus, the timestamp we send along with the _NET_ACTIVE_WINDOW request is stale, causing some window managers (e.g. cinnamon and kwin) to just ignore the _NET_ACTIVE_WINDOW. But because we use _NET_ACTIVE_WINDOW *and* XSetInputFocus and the latter works, the overall effect is that the call to select-frame-set-input-focus in server.el focuses the Emacs window, but doesn't raise it. The following patch should fix both problems: diff --git a/lisp/server.el b/lisp/server.el index a06f2f952f..cd3a8f80f0 100644 --- a/lisp/server.el +++ b/lisp/server.el @@ -1721,7 +1721,9 @@ server-switch-buffer ;; a minibuffer/dedicated-window (if there's no other). (error (pop-to-buffer next-buffer))))))) (when server-raise-frame - (select-frame-set-input-focus (window-frame))))) + (let ((frame (window-frame))) + (frame-note-oob-interaction frame) + (select-frame-set-input-focus frame))))) =20 (defvar server-stop-automatically nil "Internal status variable for `server-stop-automatically'.") diff --git a/src/frame.c b/src/frame.c index 25d71e0769..084df8ef21 100644 --- a/src/frame.c +++ b/src/frame.c @@ -5942,6 +5942,25 @@ DEFUN ("frame--set-was-invisible", Fframe__set_was_invisible, =20 return f->was_invisible ? Qt : Qnil; } + +DEFUN ("frame-note-oob-interaction", + Fframe_note_oob_interaction, + Sframe_note_oob_interaction, 0, 1, 0, + doc: /* Note that the user has interacted with a frame. +This function is useful when the user interacts with Emacs out-of- band +(e.g., via the server) and we want to pretend for purposes of Emacs +interacting with the window system that the last interaction time was +the time of that out-of-band interaction, not the time of the last +window system input event delivered to that frame. */) + (Lisp_Object frame) +{ + struct frame *f =3D decode_any_frame (frame); + if (FRAME_LIVE_P (f) && + FRAME_TERMINAL (f)->note_oob_interaction_hook) + FRAME_TERMINAL (f)->note_oob_interaction_hook (f); + return Qnil; +} + =0C /********************************************************************* ** Multimonitor data @@ -6626,6 +6645,7 @@ focus (where a frame immediately loses focus when it's left by the mouse defsubr (&Sframe_window_state_change); defsubr (&Sset_frame_window_state_change); defsubr (&Sframe_scale_factor); + defsubr (&Sframe_note_oob_interaction); =20 #ifdef HAVE_WINDOW_SYSTEM defsubr (&Sx_get_resource); diff --git a/src/gtkutil.c b/src/gtkutil.c index a6bba096a4..b2af5ff5c2 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -6658,6 +6658,17 @@ xg_filter_key (struct frame *frame, XEvent *xkey) } #endif =20 +#ifndef HAVE_PGTK +void +xg_set_user_timestamp (struct frame *frame, guint32 time) +{ + GtkWidget *widget =3D FRAME_GTK_OUTER_WIDGET (frame); + GdkWindow *window =3D gtk_widget_get_window (widget); + eassert (window); + gdk_x11_window_set_user_time (window, time); +} +#endif + #if GTK_CHECK_VERSION (3, 10, 0) static void xg_widget_style_updated (GtkWidget *widget, gpointer user_data) diff --git a/src/gtkutil.h b/src/gtkutil.h index 190d662831..bca7ea8176 100644 --- a/src/gtkutil.h +++ b/src/gtkutil.h @@ -224,6 +224,10 @@ #define XG_ITEM_DATA "emacs_menuitem" extern bool xg_filter_key (struct frame *frame, XEvent *xkey); #endif =20 +#ifndef HAVE_PGTK +extern void xg_set_user_timestamp (struct frame *frame, guint32 time); +#endif + /* Mark all callback data that are Lisp_Objects during GC. */ extern void xg_mark_data (void); =20 diff --git a/src/termhooks.h b/src/termhooks.h index c5f1e286e9..2c204afeca 100644 --- a/src/termhooks.h +++ b/src/termhooks.h @@ -860,6 +860,13 @@ #define EVENT_INIT(event) (memset (&(event), 0, sizeof (struct input_event)), \ will be considered as grabbed. */ bool (*any_grab_hook) (Display_Info *); #endif + + /* Called to note that the user has interacted with a window system + frame outside the window system and that we should update the + window system's notion of the user's last interaction time with + that frame. */ + void (*note_oob_interaction_hook) (struct frame *); + } GCALIGNED_STRUCT; =20 INLINE bool diff --git a/src/xterm.c b/src/xterm.c index 4bbcfb0e59..4d42f30e20 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -6581,12 +6581,6 @@ x_set_frame_alpha (struct frame *f) x_stop_ignoring_errors (dpyinfo); } =20 - /********************************************************************* ** - Starting and ending an update - ********************************************************************* **/ - -#if defined HAVE_XSYNC && !defined USE_GTK - /* Wait for an event matching PREDICATE to show up in the event queue, or TIMEOUT to elapse. =20 @@ -6640,6 +6634,12 @@ x_if_event (Display *dpy, XEvent *event_return, } } =20 +/******************************************************************* **** + Starting and ending an update + ********************************************************************* **/ + +#if defined HAVE_XSYNC && !defined USE_GTK + /* Return the monotonic time corresponding to the high-resolution server timestamp TIMESTAMP. Return 0 if the necessary information is not available. */ @@ -7521,26 +7521,25 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row *row, user time. We don't sanitize timestamps from events sent by the X server itself because some Lisp might have set the user time to a ridiculously large value, and this way a more reasonable timestamp - can be obtained upon the next event. */ + can be obtained upon the next event. If EXPLICIT_FRAME is NULL, + update the focused frame's timestamp; otherwise, update + EXPLICIT_FRAME's. */ =20 static void -x_display_set_last_user_time (struct x_display_info *dpyinfo, Time time, - bool send_event) +x_display_set_last_user_time_1 (struct x_display_info *dpyinfo, Time time, + bool send_event, + struct frame *explicit_frame) { -#ifndef USE_GTK - struct frame *focus_frame; + struct frame *frame; Time old_time; -#if defined HAVE_XSYNC +#if defined HAVE_XSYNC && !defined USE_GTK uint64_t monotonic_time; #endif =20 - focus_frame =3D dpyinfo->x_focus_frame; + frame =3D explicit_frame ? explicit_frame : dpyinfo->x_focus_frame; old_time =3D dpyinfo->last_user_time; -#endif =20 -#ifdef ENABLE_CHECKING eassert (time <=3D X_ULONG_MAX); -#endif =20 if (!send_event || time > dpyinfo->last_user_time) dpyinfo->last_user_time =3D time; @@ -7567,23 +7566,35 @@ x_display_set_last_user_time (struct x_display_info *dpyinfo, Time time, } #endif =20 -#ifndef USE_GTK - /* Don't waste bandwidth if the time hasn't actually changed. */ - if (focus_frame && old_time !=3D dpyinfo->last_user_time) + /* Don't waste bandwidth if the time hasn't actually changed. + Update anyway if we're updating the timestamp for a non-focused + frame, since the event loop might not have gotten around to + updating that frame's timestamp. */ + if (frame && (explicit_frame || old_time !=3D dpyinfo- >last_user_time)) { time =3D dpyinfo->last_user_time; =20 - while (FRAME_PARENT_FRAME (focus_frame)) - focus_frame =3D FRAME_PARENT_FRAME (focus_frame); + while (FRAME_PARENT_FRAME (frame)) + frame =3D FRAME_PARENT_FRAME (frame); =20 - if (FRAME_X_OUTPUT (focus_frame)->user_time_window !=3D None) +#if defined USE_GTK + xg_set_user_timestamp (frame, time); +#else + if (FRAME_X_OUTPUT (frame)->user_time_window !=3D None) XChangeProperty (dpyinfo->display, - FRAME_X_OUTPUT (focus_frame)- >user_time_window, + FRAME_X_OUTPUT (frame)->user_time_window, dpyinfo->Xatom_net_wm_user_time, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &time, 1); - } #endif + } +} + +static void +x_display_set_last_user_time (struct x_display_info *dpyinfo, Time time, + bool send_event) +{ + x_display_set_last_user_time_1 (dpyinfo, time, send_event, NULL); } =20 /* Not needed on GTK because GTK handles reporting the user time @@ -25859,9 +25870,11 @@ xembed_request_focus (struct frame *f) XEMBED_REQUEST_FOCUS, 0, 0, 0); } =20 -/* Activate frame with Extended Window Manager Hints */ +/* Activate frame with Extended Window Manager Hints =20 -static void +Return whether we were successful in doing so. */ + +static bool x_ewmh_activate_frame (struct frame *f) { XEvent msg; @@ -25869,8 +25882,7 @@ x_ewmh_activate_frame (struct frame *f) =20 dpyinfo =3D FRAME_DISPLAY_INFO (f); =20 - if (FRAME_VISIBLE_P (f) - && x_wm_supports (f, dpyinfo->Xatom_net_active_window)) + if (x_wm_supports (f, dpyinfo->Xatom_net_active_window)) { /* See the documentation at =09 https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html @@ -25890,7 +25902,9 @@ x_ewmh_activate_frame (struct frame *f) XSendEvent (dpyinfo->display, dpyinfo->root_window, False, (SubstructureRedirectMask | SubstructureNotifyMask), &msg); + return true; } + return false; } =20 static Lisp_Object @@ -25928,16 +25942,14 @@ x_focus_frame (struct frame *f, bool noactivate) events. See XEmbed Protocol Specification at https://freedesktop.org/wiki/Specifications/xembed-spec/ */ xembed_request_focus (f); - else + else if (noactivate || + (!FRAME_PARENT_FRAME (f) && !x_ewmh_activate_frame (f))) { /* Ignore any BadMatch error this request might result in. */ x_ignore_errors_for_next_request (dpyinfo); XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), RevertToParent, CurrentTime); x_stop_ignoring_errors (dpyinfo); - - if (!noactivate) - x_ewmh_activate_frame (f); } } =20 @@ -28552,6 +28564,54 @@ x_have_any_grab (struct x_display_info *dpyinfo) } #endif =20 +static Bool +server_timestamp_predicate (Display *display, + XEvent *xevent, + XPointer arg) +{ + XID *args =3D (XID *) arg; + + if (xevent->type =3D=3D PropertyNotify + && xevent->xproperty.window =3D=3D args[0] + && xevent->xproperty.atom =3D=3D args[1]) + return True; + + return False; +} + +static bool +x_get_server_time (struct frame* f, Time* time) +{ + struct x_display_info *dpyinfo =3D FRAME_DISPLAY_INFO (f); + Atom property_atom =3D dpyinfo->Xatom_EMACS_SERVER_TIME_PROP; + XEvent event; + + XChangeProperty (dpyinfo->display, FRAME_OUTER_WINDOW (f), + property_atom, XA_ATOM, 32, + PropModeReplace, (unsigned char *) &property_atom, 1); + + if (x_if_event (dpyinfo->display, &event, server_timestamp_predicate, + (XPointer) &(XID[]) {FRAME_OUTER_WINDOW (f), property_atom}, + dtotimespec (XFLOAT_DATA (Vx_wait_for_event_timeout)))) + return false; + *time =3D event.xproperty.time; + return true; +} + +static void +x_note_oob_interaction (struct frame *f) +{ + while (FRAME_PARENT_FRAME (f)) + f =3D FRAME_PARENT_FRAME (f); + if (FRAME_LIVE_P (f)) { + Time server_time; + if (!x_get_server_time (f, &server_time)) + error ("Timed out waiting for server timestamp"); + x_display_set_last_user_time_1 ( + FRAME_DISPLAY_INFO (f), server_time, false, f); + } +} + /* Create a struct terminal, initialize it with the X11 specific functions and make DISPLAY->TERMINAL point to it. */ =20 @@ -28622,6 +28682,7 @@ x_create_terminal (struct x_display_info *dpyinfo) #ifdef HAVE_XINPUT2 terminal->any_grab_hook =3D x_have_any_grab; #endif + terminal->note_oob_interaction_hook =3D x_note_oob_interaction; /* Other hooks are NULL by default. */ =20 return terminal;