Optimize coordinate translation during event handling
These changes noticeably improve turning the mouse wheel on top of scroll bars etc over slow network connections. * src/xterm.c (x_dnd_note_self_position, x_dnd_note_self_wheel) (x_dnd_note_self_drop): Use x_translate_coordinates. (x_compute_root_window_offset): New function for calculating and caching root window offsets of edit window. (x_translate_coordinates): New function. Use cached values whenever possible. (xi_compute_root_window_offset) (xi_compute_root_window_offset_pinch): New wrappers for XI2 events. (x_construct_mouse_click, handle_one_xevent): Use x_translate_coordinates wherever appropriate. * src/xterm.h (struct x_output): New fields for keeping track of the root window offset of the edit window.
This commit is contained in:
parent
cd88f6de4b
commit
51ec68b318
2 changed files with 191 additions and 45 deletions
227
src/xterm.c
227
src/xterm.c
|
@ -1143,6 +1143,7 @@ static Window x_get_window_below (Display *, Window, int, int, int *, int *);
|
|||
#ifndef USE_TOOLKIT_SCROLL_BARS
|
||||
static void x_scroll_bar_redraw (struct scroll_bar *);
|
||||
#endif
|
||||
static void x_translate_coordinates (struct frame *, int, int, int *, int *);
|
||||
|
||||
/* Global state maintained during a drag-and-drop operation. */
|
||||
|
||||
|
@ -4852,16 +4853,13 @@ x_dnd_note_self_position (struct x_display_info *dpyinfo, Window target,
|
|||
{
|
||||
struct frame *f;
|
||||
int dest_x, dest_y;
|
||||
Window child_return;
|
||||
|
||||
f = x_top_window_to_frame (dpyinfo, target);
|
||||
|
||||
if (f && XTranslateCoordinates (dpyinfo->display,
|
||||
dpyinfo->root_window,
|
||||
FRAME_X_WINDOW (f),
|
||||
root_x, root_y, &dest_x,
|
||||
&dest_y, &child_return))
|
||||
if (f)
|
||||
{
|
||||
x_translate_coordinates (f, root_x, root_y, &dest_x, &dest_y);
|
||||
|
||||
x_dnd_movement_frame = f;
|
||||
x_dnd_movement_x = dest_x;
|
||||
x_dnd_movement_y = dest_y;
|
||||
|
@ -4877,19 +4875,16 @@ x_dnd_note_self_wheel (struct x_display_info *dpyinfo, Window target,
|
|||
{
|
||||
struct frame *f;
|
||||
int dest_x, dest_y;
|
||||
Window child_return;
|
||||
|
||||
if (button < 4 || button > 7)
|
||||
return;
|
||||
|
||||
f = x_top_window_to_frame (dpyinfo, target);
|
||||
|
||||
if (f && XTranslateCoordinates (dpyinfo->display,
|
||||
dpyinfo->root_window,
|
||||
FRAME_X_WINDOW (f),
|
||||
root_x, root_y, &dest_x,
|
||||
&dest_y, &child_return))
|
||||
if (f)
|
||||
{
|
||||
x_translate_coordinates (f, root_x, root_y, &dest_x, &dest_y);
|
||||
|
||||
x_dnd_wheel_frame = f;
|
||||
x_dnd_wheel_x = dest_x;
|
||||
x_dnd_wheel_y = dest_y;
|
||||
|
@ -4912,7 +4907,6 @@ x_dnd_note_self_drop (struct x_display_info *dpyinfo, Window target,
|
|||
char **atom_names;
|
||||
char *name;
|
||||
int win_x, win_y, i;
|
||||
Window dummy;
|
||||
|
||||
if (!x_dnd_allow_current_frame
|
||||
&& (FRAME_OUTER_WINDOW (x_dnd_frame)
|
||||
|
@ -4927,10 +4921,7 @@ x_dnd_note_self_drop (struct x_display_info *dpyinfo, Window target,
|
|||
if (NILP (Vx_dnd_native_test_function))
|
||||
return;
|
||||
|
||||
if (!XTranslateCoordinates (dpyinfo->display, dpyinfo->root_window,
|
||||
FRAME_X_WINDOW (f), root_x, root_y,
|
||||
&win_x, &win_y, &dummy))
|
||||
return;
|
||||
x_translate_coordinates (f, root_x, root_y, &win_x, &win_y);
|
||||
|
||||
/* Emacs can't respond to DND events inside the nested event loop,
|
||||
so when dragging items to itself, call the test function
|
||||
|
@ -13523,6 +13514,91 @@ get_keysym_name (int keysym)
|
|||
return value;
|
||||
}
|
||||
|
||||
/* Given the root and event coordinates of an X event destined for F's
|
||||
edit window, compute the offset between that window and F's root
|
||||
window. This information is then used as an optimization to avoid
|
||||
synchronizing when converting coordinates from some other event to
|
||||
F's edit window. */
|
||||
|
||||
static void
|
||||
x_compute_root_window_offset (struct frame *f, int root_x, int root_y,
|
||||
int event_x, int event_y)
|
||||
{
|
||||
FRAME_X_OUTPUT (f)->window_offset_certain_p = true;
|
||||
FRAME_X_OUTPUT (f)->root_x = root_x - event_x;
|
||||
FRAME_X_OUTPUT (f)->root_y = root_y - event_y;
|
||||
}
|
||||
|
||||
/* Translate the given coordinates from the root window to the edit
|
||||
window of FRAME, taking into account any cached root window
|
||||
offsets. This allows Emacs to avoid excessive calls to _XReply in
|
||||
many cases while handling events, which would otherwise result in
|
||||
slowdowns over slow network connections. */
|
||||
|
||||
static void
|
||||
x_translate_coordinates (struct frame *f, int root_x, int root_y,
|
||||
int *x_out, int *y_out)
|
||||
{
|
||||
struct x_output *output;
|
||||
Window dummy;
|
||||
|
||||
output = FRAME_X_OUTPUT (f);
|
||||
|
||||
if (output->window_offset_certain_p)
|
||||
{
|
||||
/* Use the cached root window offset. */
|
||||
*x_out = root_x - output->root_x;
|
||||
*y_out = root_y - output->root_y;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Otherwise, do the transformation manually. Then, cache the root
|
||||
window position. */
|
||||
if (!XTranslateCoordinates (FRAME_X_DISPLAY (f),
|
||||
FRAME_DISPLAY_INFO (f)->root_window,
|
||||
FRAME_X_WINDOW (f), root_x, root_y,
|
||||
x_out, y_out, &dummy))
|
||||
/* Use some dummy values. This is not supposed to be called with
|
||||
coordinates out of the screen. */
|
||||
*x_out = 0, *y_out = 0;
|
||||
else
|
||||
{
|
||||
/* Cache the root window offset of the edit window. */
|
||||
output->window_offset_certain_p = true;
|
||||
output->root_x = root_x - *x_out;
|
||||
output->root_y = root_y - *y_out;
|
||||
}
|
||||
}
|
||||
|
||||
/* The same, but for an XIDeviceEvent. */
|
||||
|
||||
#ifdef HAVE_XINPUT2
|
||||
|
||||
static void
|
||||
xi_compute_root_window_offset (struct frame *f, XIDeviceEvent *xev)
|
||||
{
|
||||
/* Truncate coordinates instead of rounding them, because that is
|
||||
how the X server handles window hierarchy. */
|
||||
x_compute_root_window_offset (f, xev->root_x, xev->root_y,
|
||||
xev->event_x, xev->event_y);
|
||||
}
|
||||
|
||||
#ifdef HAVE_XINPUT2_4
|
||||
|
||||
static void
|
||||
xi_compute_root_window_offset_pinch (struct frame *f, XIGesturePinchEvent *pev)
|
||||
{
|
||||
/* Truncate coordinates instead of rounding them, because that is
|
||||
how the X server handles window hierarchy. */
|
||||
x_compute_root_window_offset (f, pev->root_x, pev->root_y,
|
||||
pev->event_x, pev->event_y);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
static Bool
|
||||
x_query_pointer_1 (struct x_display_info *dpyinfo,
|
||||
int client_pointer_device, Window w,
|
||||
|
@ -13651,10 +13727,10 @@ x_query_pointer (Display *dpy, Window w, Window *root_return,
|
|||
X server, and instead be artificially constructed from input
|
||||
extension events. In these special events, the only fields that
|
||||
are initialized are `time', `button', `state', `type', `window' and
|
||||
`x' and `y'. This function should not access any other fields in
|
||||
EVENT without also initializing the corresponding fields in `bv'
|
||||
under the XI_ButtonPress and XI_ButtonRelease labels inside
|
||||
`handle_one_xevent'. */
|
||||
`x', `y', `x_root' and `y_root'. This function should not access
|
||||
any other fields in EVENT without also initializing the
|
||||
corresponding fields in `bv' under the XI_ButtonPress and
|
||||
XI_ButtonRelease labels inside `handle_one_xevent'. */
|
||||
|
||||
static Lisp_Object
|
||||
x_construct_mouse_click (struct input_event *result,
|
||||
|
@ -13663,7 +13739,6 @@ x_construct_mouse_click (struct input_event *result,
|
|||
{
|
||||
int x = event->x;
|
||||
int y = event->y;
|
||||
Window dummy;
|
||||
|
||||
/* Make the event type NO_EVENT; we'll change that when we decide
|
||||
otherwise. */
|
||||
|
@ -13680,9 +13755,8 @@ x_construct_mouse_click (struct input_event *result,
|
|||
happen with GTK+ scroll bars, for example), translate the
|
||||
coordinates so they appear at the correct position. */
|
||||
if (event->window != FRAME_X_WINDOW (f))
|
||||
XTranslateCoordinates (FRAME_X_DISPLAY (f),
|
||||
event->window, FRAME_X_WINDOW (f),
|
||||
x, y, &x, &y, &dummy);
|
||||
x_translate_coordinates (f, event->x_root, event->y_root,
|
||||
&x, &y);
|
||||
|
||||
XSETINT (result->x, x);
|
||||
XSETINT (result->y, y);
|
||||
|
@ -18968,6 +19042,13 @@ handle_one_xevent (struct x_display_info *dpyinfo,
|
|||
`event' itself. */
|
||||
XKeyEvent xkey = event->xkey;
|
||||
|
||||
if (event->xkey.window == FRAME_X_WINDOW (f))
|
||||
/* See the comment above x_compute_root_window_offset for
|
||||
why this optimization is performed. */
|
||||
x_compute_root_window_offset (f, event->xkey.x_root,
|
||||
event->xkey.y_root,
|
||||
event->xkey.x, event->xkey.y);
|
||||
|
||||
#ifdef HAVE_XINPUT2
|
||||
Time pending_keystroke_time;
|
||||
struct xi_device_t *source;
|
||||
|
@ -19601,6 +19682,14 @@ handle_one_xevent (struct x_display_info *dpyinfo,
|
|||
|
||||
f = mouse_or_wdesc_frame (dpyinfo, event->xmotion.window);
|
||||
|
||||
if (f && event->xmotion.window == FRAME_X_WINDOW (f))
|
||||
/* See the comment above x_compute_root_window_offset for
|
||||
why this optimization is performed. */
|
||||
x_compute_root_window_offset (f, event->xmotion.x_root,
|
||||
event->xmotion.y_root,
|
||||
event->xmotion.x,
|
||||
event->xmotion.y);
|
||||
|
||||
if (x_dnd_in_progress
|
||||
/* Handle these events normally if the recursion
|
||||
level is higher than when the drag-and-drop
|
||||
|
@ -19865,10 +19954,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
|
|||
|
||||
if (xmotion.window != FRAME_X_WINDOW (f))
|
||||
{
|
||||
XTranslateCoordinates (FRAME_X_DISPLAY (f),
|
||||
xmotion.window, FRAME_X_WINDOW (f),
|
||||
xmotion.x, xmotion.y, &xmotion.x,
|
||||
&xmotion.y, &xmotion.subwindow);
|
||||
x_translate_coordinates (f, xmotion.x_root, xmotion.y_root,
|
||||
&xmotion.x, &xmotion.y);
|
||||
xmotion.window = FRAME_X_WINDOW (f);
|
||||
}
|
||||
|
||||
|
@ -20091,6 +20178,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
|
|||
#endif
|
||||
|
||||
f = x_top_window_to_frame (dpyinfo, configureEvent.xconfigure.window);
|
||||
|
||||
/* This means we can no longer be certain of the root window
|
||||
coordinates of any's edit window. */
|
||||
if (any)
|
||||
FRAME_X_OUTPUT (any)->window_offset_certain_p = false;
|
||||
|
||||
/* Unfortunately, we need to call x_drop_xrender_surfaces for
|
||||
_all_ ConfigureNotify events, otherwise we miss some and
|
||||
flicker. Don't try to optimize these calls by looking only
|
||||
|
@ -20320,6 +20413,14 @@ handle_one_xevent (struct x_display_info *dpyinfo,
|
|||
{
|
||||
f = mouse_or_wdesc_frame (dpyinfo, event->xbutton.window);
|
||||
|
||||
if (f && event->xbutton.window == FRAME_X_WINDOW (f))
|
||||
/* See the comment above x_compute_root_window_offset
|
||||
for why this optimization is performed. */
|
||||
x_compute_root_window_offset (f, event->xbutton.x_root,
|
||||
event->xbutton.y_root,
|
||||
event->xbutton.x,
|
||||
event->xbutton.y);
|
||||
|
||||
if (event->type == ButtonPress)
|
||||
{
|
||||
x_display_set_last_user_time (dpyinfo, event->xbutton.time,
|
||||
|
@ -20493,6 +20594,15 @@ handle_one_xevent (struct x_display_info *dpyinfo,
|
|||
dpyinfo->last_mouse_glyph_frame = NULL;
|
||||
|
||||
f = mouse_or_wdesc_frame (dpyinfo, event->xbutton.window);
|
||||
|
||||
if (f && event->xbutton.window == FRAME_X_WINDOW (f))
|
||||
/* See the comment above x_compute_root_window_offset
|
||||
for why this optimization is performed. */
|
||||
x_compute_root_window_offset (f, event->xbutton.x_root,
|
||||
event->xbutton.y_root,
|
||||
event->xbutton.x,
|
||||
event->xbutton.y);
|
||||
|
||||
if (f && event->xbutton.type == ButtonPress
|
||||
&& !popup_activated ()
|
||||
&& !x_window_to_scroll_bar (event->xbutton.display,
|
||||
|
@ -21178,8 +21288,6 @@ handle_one_xevent (struct x_display_info *dpyinfo,
|
|||
goto XI_OTHER;
|
||||
#endif
|
||||
|
||||
Window dummy;
|
||||
|
||||
#ifdef HAVE_XINPUT2_1
|
||||
#ifdef HAVE_XWIDGETS
|
||||
struct xwidget_view *xv = xwidget_view_from_window (xev->event);
|
||||
|
@ -21257,11 +21365,10 @@ handle_one_xevent (struct x_display_info *dpyinfo,
|
|||
real_y = lrint (xev->event_y + bar->top);
|
||||
}
|
||||
else
|
||||
XTranslateCoordinates (dpyinfo->display,
|
||||
xev->event, FRAME_X_WINDOW (f),
|
||||
lrint (xev->event_x),
|
||||
lrint (xev->event_y),
|
||||
&real_x, &real_y, &dummy);
|
||||
x_translate_coordinates (f,
|
||||
lrint (xev->root_x),
|
||||
lrint (xev->root_y),
|
||||
&real_x, &real_y);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -21463,6 +21570,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
|
|||
|
||||
f = mouse_or_wdesc_frame (dpyinfo, xev->event);
|
||||
|
||||
if (f && xev->event == FRAME_X_WINDOW (f))
|
||||
/* See the comment above x_compute_root_window_offset
|
||||
for why this optimization is performed. */
|
||||
xi_compute_root_window_offset (f, xev);
|
||||
|
||||
if (x_dnd_in_progress
|
||||
/* Handle these events normally if the recursion
|
||||
level is higher than when the drag-and-drop
|
||||
|
@ -21711,9 +21823,9 @@ handle_one_xevent (struct x_display_info *dpyinfo,
|
|||
{
|
||||
if (xev->event != FRAME_X_WINDOW (f))
|
||||
{
|
||||
XTranslateCoordinates (FRAME_X_DISPLAY (f),
|
||||
xev->event, FRAME_X_WINDOW (f),
|
||||
ev.x, ev.y, &ev.x, &ev.y, &dummy);
|
||||
x_translate_coordinates (f, lrint (xev->root_x),
|
||||
lrint (xev->root_y),
|
||||
&ev.x, &ev.y);
|
||||
ev.window = FRAME_X_WINDOW (f);
|
||||
}
|
||||
|
||||
|
@ -21819,6 +21931,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
|
|||
f = mouse_or_wdesc_frame (dpyinfo, xev->event);
|
||||
device = xi_device_from_id (dpyinfo, xev->deviceid);
|
||||
|
||||
if (f && xev->event == FRAME_X_WINDOW (f))
|
||||
/* See the comment above
|
||||
x_compute_root_window_offset for why this
|
||||
optimization is performed. */
|
||||
xi_compute_root_window_offset (f, xev);
|
||||
|
||||
/* Don't track grab status for emulated pointer
|
||||
events, because they are ignored by the regular
|
||||
mouse click processing code. */
|
||||
|
@ -22150,6 +22268,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
|
|||
bv.type = xev->evtype == XI_ButtonPress ? ButtonPress : ButtonRelease;
|
||||
bv.x = lrint (xev->event_x);
|
||||
bv.y = lrint (xev->event_y);
|
||||
bv.x_root = lrint (xev->root_x);
|
||||
bv.y_root = lrint (xev->root_y);
|
||||
bv.window = xev->event;
|
||||
bv.state = xi_convert_event_state (xev);
|
||||
bv.time = xev->time;
|
||||
|
@ -22158,6 +22278,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
|
|||
|
||||
f = mouse_or_wdesc_frame (dpyinfo, xev->event);
|
||||
|
||||
if (f && xev->event == FRAME_X_WINDOW (f))
|
||||
/* See the comment above x_compute_root_window_offset
|
||||
for why this optimization is performed. */
|
||||
xi_compute_root_window_offset (f, xev);
|
||||
|
||||
if (f && xev->evtype == XI_ButtonPress
|
||||
&& !popup_activated ()
|
||||
&& !x_window_to_scroll_bar (dpyinfo->display, xev->event, 2)
|
||||
|
@ -22209,9 +22334,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
|
|||
#ifdef USE_GTK
|
||||
if (!f)
|
||||
{
|
||||
int real_x = lrint (xev->event_x);
|
||||
int real_y = lrint (xev->event_y);
|
||||
Window child;
|
||||
int real_x = lrint (xev->root_x);
|
||||
int real_y = lrint (xev->root_y);
|
||||
|
||||
f = x_any_window_to_frame (dpyinfo, xev->event);
|
||||
|
||||
|
@ -22220,9 +22344,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
|
|||
if (xev->evtype == XI_ButtonRelease)
|
||||
{
|
||||
if (FRAME_X_WINDOW (f) != xev->event)
|
||||
XTranslateCoordinates (dpyinfo->display, xev->event,
|
||||
FRAME_X_WINDOW (f), real_x,
|
||||
real_y, &real_x, &real_y, &child);
|
||||
x_translate_coordinates (f, real_x, real_y,
|
||||
&real_x, &real_y);
|
||||
|
||||
if (xev->detail <= 5)
|
||||
inev.ie.kind = WHEEL_EVENT;
|
||||
|
@ -22487,6 +22610,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
|
|||
|
||||
f = x_any_window_to_frame (dpyinfo, xev->event);
|
||||
|
||||
if (f && xev->event == FRAME_X_WINDOW (f))
|
||||
/* See the comment above x_compute_root_window_offset
|
||||
for why this optimization is performed. */
|
||||
xi_compute_root_window_offset (f, xev);
|
||||
|
||||
/* GTK handles TAB events in an undesirable manner, so
|
||||
keyboard events are always dropped. But as a side
|
||||
effect, the user time will no longer be set by GDK,
|
||||
|
@ -23126,6 +23254,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
|
|||
|
||||
f = x_window_to_frame (dpyinfo, xev->event);
|
||||
|
||||
if (f)
|
||||
/* See the comment above x_compute_root_window_offset
|
||||
for why this optimization is performed. */
|
||||
xi_compute_root_window_offset (f, xev);
|
||||
|
||||
#ifdef HAVE_GTK3
|
||||
menu_bar_p = (f && FRAME_X_OUTPUT (f)->menubar_widget
|
||||
&& xg_event_is_for_menubar (f, event));
|
||||
|
@ -23314,8 +23447,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
|
|||
#endif
|
||||
|
||||
any = x_window_to_frame (dpyinfo, pev->event);
|
||||
|
||||
if (any)
|
||||
{
|
||||
if (pev->event == FRAME_X_WINDOW (any))
|
||||
xi_compute_root_window_offset_pinch (any, pev);
|
||||
|
||||
inev.ie.kind = PINCH_EVENT;
|
||||
inev.ie.modifiers = x_x_to_emacs_modifiers (FRAME_DISPLAY_INFO (any),
|
||||
pev->mods.effective);
|
||||
|
|
|
@ -1196,6 +1196,15 @@ struct x_output
|
|||
XIEventMask *xi_masks;
|
||||
int num_xi_masks;
|
||||
#endif
|
||||
|
||||
/* Whether or not we are certain we know the offset from the root
|
||||
window to this frame. */
|
||||
bool window_offset_certain_p;
|
||||
|
||||
/* The offset of the edit window from the root window. This is
|
||||
strictly an optimization to avoid extraneous synchronizing in
|
||||
some cases. */
|
||||
int root_x, root_y;
|
||||
};
|
||||
|
||||
enum
|
||||
|
|
Loading…
Add table
Reference in a new issue