From 7f9335f91d3e0ecdff9c3adb93aad08454002035 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Wed, 9 Feb 2022 16:07:59 +0800 Subject: [PATCH] Implement _NET_WM_USER_TIME protocol * src/xterm.c (x_display_set_last_user_time): Set user time on the active window. (handle_one_xevent): Only set user time when the user actually interacted with Emacs. (x_make_frame_visible): Set user time if non-zero. (x_term_init): Intern `_NET_WM_USER_TIME'. * src/xterm.h (struct x_display_info): New field `Xatom_net_wm_user_time'. (x_display_set_last_user_time): Implement in xterm.c instead. --- src/xterm.c | 101 +++++++++++++++++++++++++++++++++------------------- src/xterm.h | 14 +++----- 2 files changed, 69 insertions(+), 46 deletions(-) diff --git a/src/xterm.c b/src/xterm.c index aed97fb37e9..38b50bcd3b7 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -2217,6 +2217,29 @@ static void x_scroll_bar_clear (struct frame *); static void x_check_font (struct frame *, struct font *); #endif +void +x_display_set_last_user_time (struct x_display_info *dpyinfo, Time time) +{ + struct frame *focus_frame = dpyinfo->x_focus_frame; + +#ifdef ENABLE_CHECKING + eassert (t <= X_ULONG_MAX); +#endif + dpyinfo->last_user_time = time; + + if (focus_frame) + { + while (FRAME_PARENT_FRAME (focus_frame)) + focus_frame = FRAME_PARENT_FRAME (focus_frame); + + XChangeProperty (dpyinfo->display, + FRAME_OUTER_WINDOW (dpyinfo->x_focus_frame), + dpyinfo->Xatom_net_wm_user_time, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) &time, 1); + } +} + /* Set S->gc to a suitable GC for drawing glyph string S in cursor face. */ @@ -9307,7 +9330,6 @@ handle_one_xevent (struct x_display_info *dpyinfo, break; case SelectionNotify: - x_display_set_last_user_time (dpyinfo, event->xselection.time); #ifdef USE_X_TOOLKIT if (! x_window_to_frame (dpyinfo, event->xselection.requestor)) goto OTHER; @@ -9316,7 +9338,6 @@ handle_one_xevent (struct x_display_info *dpyinfo, break; case SelectionClear: /* Someone has grabbed ownership. */ - x_display_set_last_user_time (dpyinfo, event->xselectionclear.time); #ifdef USE_X_TOOLKIT if (! x_window_to_frame (dpyinfo, event->xselectionclear.window)) goto OTHER; @@ -9332,7 +9353,6 @@ handle_one_xevent (struct x_display_info *dpyinfo, break; case SelectionRequest: /* Someone wants our selection. */ - x_display_set_last_user_time (dpyinfo, event->xselectionrequest.time); #ifdef USE_X_TOOLKIT if (!x_window_to_frame (dpyinfo, event->xselectionrequest.owner)) goto OTHER; @@ -9351,7 +9371,6 @@ handle_one_xevent (struct x_display_info *dpyinfo, break; case PropertyNotify: - x_display_set_last_user_time (dpyinfo, event->xproperty.time); f = x_top_window_to_frame (dpyinfo, event->xproperty.window); if (f && event->xproperty.atom == dpyinfo->Xatom_net_wm_state) { @@ -9682,7 +9701,6 @@ handle_one_xevent (struct x_display_info *dpyinfo, goto OTHER; case KeyPress: - x_display_set_last_user_time (dpyinfo, event->xkey.time); ignore_next_mouse_click_timeout = 0; @@ -10018,7 +10036,6 @@ handle_one_xevent (struct x_display_info *dpyinfo, #endif case KeyRelease: - x_display_set_last_user_time (dpyinfo, event->xkey.time); #ifdef HAVE_X_I18N /* Don't dispatch this event since XtDispatchEvent calls XFilterEvent, and two calls in a row may freeze the @@ -10172,7 +10189,6 @@ handle_one_xevent (struct x_display_info *dpyinfo, case MotionNotify: { - x_display_set_last_user_time (dpyinfo, event->xmotion.time); previous_help_echo_string = help_echo_string; help_echo_string = Qnil; @@ -10482,7 +10498,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, memset (&compose_status, 0, sizeof (compose_status)); dpyinfo->last_mouse_glyph_frame = NULL; - x_display_set_last_user_time (dpyinfo, event->xbutton.time); + + if (event->xbutton.type == ButtonPress) + x_display_set_last_user_time (dpyinfo, event->xbutton.time); f = mouse_or_wdesc_frame (dpyinfo, event->xmotion.window); if (f && event->xbutton.type == ButtonPress @@ -10901,8 +10919,6 @@ handle_one_xevent (struct x_display_info *dpyinfo, goto XI_OTHER; #endif - x_display_set_last_user_time (dpyinfo, xi_event->time); - #ifdef HAVE_XWIDGETS struct xwidget_view *xv = xwidget_view_from_window (xev->event); double xv_total_x = 0.0; @@ -11224,7 +11240,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, bv.time = xev->time; dpyinfo->last_mouse_glyph_frame = NULL; - x_display_set_last_user_time (dpyinfo, xev->time); + + if (xev->evtype == XI_ButtonPress) + x_display_set_last_user_time (dpyinfo, xev->time); f = mouse_or_wdesc_frame (dpyinfo, xev->event); @@ -11735,34 +11753,32 @@ handle_one_xevent (struct x_display_info *dpyinfo, } case XI_KeyRelease: - x_display_set_last_user_time (dpyinfo, xev->time); - #if defined HAVE_X_I18N || defined USE_GTK - XKeyPressedEvent xkey; - - memset (&xkey, 0, sizeof xkey); - - xkey.type = KeyRelease; - xkey.serial = xev->serial; - xkey.send_event = xev->send_event; - xkey.display = dpyinfo->display; - xkey.window = xev->event; - xkey.root = xev->root; - xkey.subwindow = xev->child; - xkey.time = xev->time; - xkey.state = ((xev->mods.effective & ~(1 << 13 | 1 << 14)) - | (xev->group.effective << 13)); - xkey.keycode = xev->detail; - xkey.same_screen = True; + XKeyPressedEvent xkey; + + memset (&xkey, 0, sizeof xkey); + + xkey.type = KeyRelease; + xkey.serial = xev->serial; + xkey.send_event = xev->send_event; + xkey.display = dpyinfo->display; + xkey.window = xev->event; + xkey.root = xev->root; + xkey.subwindow = xev->child; + xkey.time = xev->time; + xkey.state = ((xev->mods.effective & ~(1 << 13 | 1 << 14)) + | (xev->group.effective << 13)); + xkey.keycode = xev->detail; + xkey.same_screen = True; #ifdef HAVE_X_I18N - if (x_filter_event (dpyinfo, (XEvent *) &xkey)) - *finish = X_EVENT_DROP; + if (x_filter_event (dpyinfo, (XEvent *) &xkey)) + *finish = X_EVENT_DROP; #else - f = x_any_window_to_frame (xkey->event); + f = x_any_window_to_frame (xkey->event); - if (f && xg_filter_key (f, event)) - *finish = X_EVENT_DROP; + if (f && xg_filter_key (f, event)) + *finish = X_EVENT_DROP; #endif #endif @@ -11989,8 +12005,6 @@ handle_one_xevent (struct x_display_info *dpyinfo, case XI_GesturePinchEnd: { - x_display_set_last_user_time (dpyinfo, xi_event->time); - #if defined HAVE_XWIDGETS && HAVE_USABLE_XI_GESTURE_PINCH_EVENT XIGesturePinchEvent *pev = (XIGesturePinchEvent *) xi_event; struct xwidget_view *xvw = xwidget_view_from_window (pev->event); @@ -14466,6 +14480,8 @@ xembed_send_message (struct frame *f, Time t, enum xembed_message msg, void x_make_frame_visible (struct frame *f) { + struct x_display_info *dpyinfo; + if (FRAME_PARENT_FRAME (f)) { if (!FRAME_VISIBLE_P (f)) @@ -14489,6 +14505,7 @@ x_make_frame_visible (struct frame *f) block_input (); gui_set_bitmap_icon (f); + dpyinfo = FRAME_DISPLAY_INFO (f); if (! FRAME_VISIBLE_P (f)) { @@ -14501,6 +14518,17 @@ x_make_frame_visible (struct frame *f) && ! f->output_data.x->asked_for_visible) x_set_offset (f, f->left_pos, f->top_pos, 0); + if (dpyinfo->last_user_time) + XChangeProperty (dpyinfo->display, + FRAME_OUTER_WINDOW (f), + dpyinfo->Xatom_net_wm_user_time, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) &dpyinfo->last_user_time, 1); + else + XDeleteProperty (dpyinfo->display, + FRAME_OUTER_WINDOW (f), + dpyinfo->Xatom_net_wm_user_time); + f->output_data.x->asked_for_visible = true; if (! EQ (Vx_no_window_manager, Qt)) @@ -16104,6 +16132,7 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name) ATOM_REFS_INIT ("_NET_WM_SYNC_REQUEST", Xatom_net_wm_sync_request) ATOM_REFS_INIT ("_NET_WM_SYNC_REQUEST_COUNTER", Xatom_net_wm_sync_request_counter) ATOM_REFS_INIT ("_NET_WM_FRAME_DRAWN", Xatom_net_wm_frame_drawn) + ATOM_REFS_INIT ("_NET_WM_USER_TIME", Xatom_net_wm_user_time) /* Session management */ ATOM_REFS_INIT ("SM_CLIENT_ID", Xatom_SM_CLIENT_ID) ATOM_REFS_INIT ("_XSETTINGS_SETTINGS", Xatom_xsettings_prop) diff --git a/src/xterm.h b/src/xterm.h index 435569943c4..9aa7021c4dc 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -505,7 +505,8 @@ struct x_display_info Xatom_net_wm_state_hidden, Xatom_net_wm_state_skip_taskbar, Xatom_net_frame_extents, Xatom_net_current_desktop, Xatom_net_workarea, Xatom_net_wm_opaque_region, Xatom_net_wm_ping, Xatom_net_wm_sync_request, - Xatom_net_wm_sync_request_counter, Xatom_net_wm_frame_drawn; + Xatom_net_wm_sync_request_counter, Xatom_net_wm_frame_drawn, + Xatom_net_wm_user_time; /* XSettings atoms and windows. */ Atom Xatom_xsettings_sel, Xatom_xsettings_prop, Xatom_xsettings_mgr; @@ -1283,6 +1284,8 @@ extern void x_xr_apply_ext_clip (struct frame *f, GC gc); extern void x_xr_reset_ext_clip (struct frame *f); #endif +extern void x_display_set_last_user_time (struct x_display_info *, Time); + INLINE int x_display_pixel_height (struct x_display_info *dpyinfo) { @@ -1295,15 +1298,6 @@ x_display_pixel_width (struct x_display_info *dpyinfo) return WidthOfScreen (dpyinfo->screen); } -INLINE void -x_display_set_last_user_time (struct x_display_info *dpyinfo, Time t) -{ -#ifdef ENABLE_CHECKING - eassert (t <= X_ULONG_MAX); -#endif - dpyinfo->last_user_time = t; -} - INLINE unsigned long x_make_truecolor_pixel (struct x_display_info *dpyinfo, int r, int g, int b) { -- 2.39.5