From fda1685eb81d0a8b7fe19966572913b504606d9d Mon Sep 17 00:00:00 2001 From: Po Lu Date: Fri, 29 Oct 2021 11:33:06 +0800 Subject: [PATCH 2/2] Enable scrolling optimization for xwidgets * src/dispextern.h (struct glyph): Store xwidget ID instead of a reference. * src/dispnew.c (scrolling_window): Enable scrolling optimization on xwidget builds. * src/xdisp.c (fill_xwidget_glyph_string, produce_xwidget_glyph): Obtain xwidget from ID. * src/xterm.c (x_scroll_run): Scroll xwidget windows. * src/xwidget.c (id_to_xwidget_map, xwidget_counter): New xwidget variables. (Fmake_xwidget): Assign each xwidget a unique ID, and keep track of that ID. (xwidget_from_id): New function. (syms_of_xwidget): Initialize id_to_xwidget_map. (xwidget_end_redisplay): Lookup xwidgets via ID. * src/xwidget.h (struct xwidget): Add ID field. (xwidget_from_id): New function. --- src/dispextern.h | 4 +-- src/dispnew.c | 10 ------ src/xdisp.c | 4 +-- src/xterm.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++ src/xwidget.c | 24 +++++++++++++- src/xwidget.h | 3 ++ 6 files changed, 112 insertions(+), 15 deletions(-) diff --git a/src/dispextern.h b/src/dispextern.h index 08dac5d455..218675b021 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -536,8 +536,8 @@ #define FACE_ID_BITS 20 int img_id; #ifdef HAVE_XWIDGETS - /* Xwidget reference (type == XWIDGET_GLYPH). */ - struct xwidget *xwidget; + /* Xwidget ID. */ + uint32_t xwidget; #endif /* Sub-structure for type == STRETCH_GLYPH. */ diff --git a/src/dispnew.c b/src/dispnew.c index c3f6d0bfef..2e0b4801da 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -4447,16 +4447,6 @@ scrolling_window (struct window *w, int tab_line_p) break; } -#ifdef HAVE_XWIDGETS - /* Currently this seems needed to detect xwidget movement reliably. - This is most probably because an xwidget glyph is represented in - struct glyph's 'union u' by a pointer to a struct, which takes 8 - bytes in 64-bit builds, and thus the comparison of u.val values - done by GLYPH_EQUAL_P doesn't work reliably, since it assumes the - size of the union is 4 bytes. FIXME. */ - return 0; -#endif - /* Can't scroll the display of w32 GUI frames when position of point is indicated by the system caret, because scrolling the display will then "copy" the pixels used by the caret. */ diff --git a/src/xdisp.c b/src/xdisp.c index aa01db210b..198bfc06a4 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -28425,7 +28425,7 @@ fill_xwidget_glyph_string (struct glyph_string *s) } s->width = s->first_glyph->pixel_width; s->ybase += s->first_glyph->voffset; - s->xwidget = s->first_glyph->u.xwidget; + s->xwidget = xwidget_from_id (s->first_glyph->u.xwidget); } #endif /* Fill glyph string S from a sequence of stretch glyphs. @@ -29830,7 +29830,7 @@ produce_xwidget_glyph (struct it *it) glyph->padding_p = 0; glyph->glyph_not_available_p = 0; glyph->face_id = it->face_id; - glyph->u.xwidget = it->xwidget; + glyph->u.xwidget = it->xwidget->xwidget_id; glyph->font_type = FONT_TYPE_UNKNOWN; if (it->bidi_p) { diff --git a/src/xterm.c b/src/xterm.c index 54bfb65bd0..b12c15cb7a 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -4390,6 +4390,88 @@ x_scroll_run (struct window *w, struct run *run) /* Cursor off. Will be switched on again in gui_update_window_end. */ gui_clear_cursor (w); +#ifdef HAVE_XWIDGETS + /* "Copy" xwidget windows in the area that will be scrolled. */ + Display *dpy = FRAME_X_DISPLAY (f); + Window window = FRAME_X_WINDOW (f); + + Window root, parent, *children; + unsigned int nchildren; + + if (XQueryTree (dpy, window, &root, &parent, &children, &nchildren)) + { + /* Now find xwidget views situated between from_y and to_y, and + attached to w. */ + for (unsigned int i = 0; i < nchildren; ++i) + { + Window child = children[i]; + struct xwidget_view *view = xwidget_view_from_window (child); + + if (view) + { + int window_y = view->y + view->clip_top; + int window_height = view->clip_bottom - view->clip_top; + int min_y = min (from_y, to_y); + int max_y = max (from_y, to_y); + + Emacs_Rectangle r1, r2, result; + r1.x = w->pixel_left; + r1.y = min_y; + r1.width = w->pixel_width; + r1.height = max_y - min_y; + r2 = r1; + r2.y = window_y; + r2.height = window_height; + + /* The window is offscreen, just unmap it. */ + if (window_height == 0) + { + view->hidden = true; + XUnmapWindow (dpy, child); + continue; + } + + bool intersects_p = + gui_intersect_rectangles (&r1, &r2, &result); + + if (XWINDOW (view->w) == w && intersects_p) + { + int y = view->y + (to_y - from_y); + int text_area_x, text_area_y, text_area_width, text_area_height; + int clip_top, clip_bottom; + + window_box (w, TEXT_AREA, &text_area_x, &text_area_y, + &text_area_width, &text_area_height); + + clip_top = max (0, text_area_y - y); + clip_bottom = max (clip_top, + min (XXWIDGET (view->model)->height, + text_area_y + text_area_height - y)); + + view->y = y; + view->clip_top = clip_top; + view->clip_bottom = clip_bottom; + + /* This means the view has moved offscreen. Unmap + it and hide it here. */ + if ((view->clip_top - view->clip_bottom) <= 0) + { + view->hidden = true; + XUnmapWindow (dpy, child); + } + else + XMoveResizeWindow (dpy, child, view->x + view->clip_left, + view->y + view->clip_top, + view->clip_right - view->clip_left, + view->clip_top - view->clip_bottom); + XFlush (dpy); + } + } + } + XFree (children); + } +#endif + #ifdef USE_CAIRO if (FRAME_CR_CONTEXT (f)) { diff --git a/src/xwidget.c b/src/xwidget.c index 62b30a07ab..68188eba08 100644 --- a/src/xwidget.c +++ b/src/xwidget.c @@ -41,6 +41,9 @@ Copyright (C) 2011-2021 Free Software Foundation, Inc. #include "nsxwidget.h" #endif +static Lisp_Object id_to_xwidget_map; +static uint32_t xwidget_counter = 0; + #ifdef USE_GTK static Lisp_Object x_window_to_xwv_map; #endif @@ -114,6 +117,9 @@ DEFUN ("make-xwidget", XSETXWIDGET (val, xw); Vxwidget_list = Fcons (val, Vxwidget_list); xw->plist = Qnil; + xw->xwidget_id = ++xwidget_counter; + + Fputhash (make_fixnum (xw->xwidget_id), val, id_to_xwidget_map); #ifdef USE_GTK xw->widgetwindow_osr = NULL; @@ -227,6 +233,18 @@ xwidget_hidden (struct xwidget_view *xv) return xv->hidden; } +struct xwidget * +xwidget_from_id (uint32_t id) +{ + Lisp_Object key = make_fixnum (id); + Lisp_Object xwidget = Fgethash (key, id_to_xwidget_map, Qnil); + + if (NILP (xwidget)) + emacs_abort (); + + return XXWIDGET (xwidget); +} + #ifdef USE_GTK struct xwidget_view * @@ -1242,6 +1260,9 @@ syms_of_xwidget (void) Fprovide (intern ("xwidget-internal"), Qnil); + id_to_xwidget_map = CALLN (Fmake_hash_table, QCtest, Qeq); + staticpro (&id_to_xwidget_map); + #ifdef USE_GTK x_window_to_xwv_map = CALLN (Fmake_hash_table, QCtest, Qeq); @@ -1385,7 +1406,7 @@ xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix) /* The only call to xwidget_end_redisplay is in dispnew. xwidget_end_redisplay (w->current_matrix); */ struct xwidget_view *xv - = xwidget_view_lookup (glyph->u.xwidget, w); + = xwidget_view_lookup (xwidget_from_id (glyph->u.xwidget), w); #ifdef USE_GTK /* FIXME: Is it safe to assume xwidget_view_lookup always succeeds here? If so, this comment can be removed. @@ -1448,6 +1469,7 @@ kill_buffer_xwidgets (Lisp_Object buffer) { CHECK_XWIDGET (xwidget); struct xwidget *xw = XXWIDGET (xwidget); + Fremhash (make_fixnum (xw->xwidget_id), id_to_xwidget_map); #ifdef USE_GTK if (xw->widget_osr && xw->widgetwindow_osr) { diff --git a/src/xwidget.h b/src/xwidget.h index fc68b52cdb..28098c0b09 100644 --- a/src/xwidget.h +++ b/src/xwidget.h @@ -60,6 +60,7 @@ #define XWIDGET_H_INCLUDED int height; int width; + uint32_t xwidget_id; #if defined (USE_GTK) /* For offscreen widgets, unused if not osr. */ @@ -168,6 +169,8 @@ #define XG_XWIDGET_VIEW "emacs_xwidget_view" Lisp_Object argument); struct xwidget_view *xwidget_view_from_window (Window wdesc); void xwidget_expose (struct xwidget_view *xv); + +extern struct xwidget *xwidget_from_id (uint32_t id); #else INLINE_HEADER_BEGIN INLINE void syms_of_xwidget (void) {} -- 2.31.1