From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Po Lu via "Bug reports for GNU Emacs, the Swiss army knife of text editors" Newsgroups: gmane.emacs.bugs Subject: bug#51473: [PATCH] Enable xwidget scrolling optimizations, and other xwidgets improvements Date: Fri, 29 Oct 2021 12:30:45 +0800 Message-ID: <87zgqslafe.fsf@yahoo.com> References: <87zgqslafe.fsf.ref@yahoo.com> Reply-To: Po Lu Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="34016"; mail-complaints-to="usenet@ciao.gmane.io" To: 51473@debbugs.gnu.org Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Fri Oct 29 06:32:40 2021 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 1mgJZH-0008kZ-KG for geb-bug-gnu-emacs@m.gmane-mx.org; Fri, 29 Oct 2021 06:32:39 +0200 Original-Received: from localhost ([::1]:41078 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1mgJZF-000360-WA for geb-bug-gnu-emacs@m.gmane-mx.org; Fri, 29 Oct 2021 00:32:38 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:50650) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mgJYg-00035s-H5 for bug-gnu-emacs@gnu.org; Fri, 29 Oct 2021 00:32:02 -0400 Original-Received: from debbugs.gnu.org ([209.51.188.43]:42201) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1mgJYg-0007jw-7U for bug-gnu-emacs@gnu.org; Fri, 29 Oct 2021 00:32:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1mgJYg-0007ML-1L for bug-gnu-emacs@gnu.org; Fri, 29 Oct 2021 00:32:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Po Lu Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Fri, 29 Oct 2021 04:32:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 51473 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch X-Debbugs-Original-To: bug-gnu-emacs@gnu.org Original-Received: via spool by submit@debbugs.gnu.org id=B.163548187028227 (code B ref -1); Fri, 29 Oct 2021 04:32:01 +0000 Original-Received: (at submit) by debbugs.gnu.org; 29 Oct 2021 04:31:10 +0000 Original-Received: from localhost ([127.0.0.1]:53747 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1mgJXn-0007L9-2i for submit@debbugs.gnu.org; Fri, 29 Oct 2021 00:31:10 -0400 Original-Received: from lists.gnu.org ([209.51.188.17]:44470) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1mgJXi-0007Ks-99 for submit@debbugs.gnu.org; Fri, 29 Oct 2021 00:31:06 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:50540) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mgJXh-00033a-NS for bug-gnu-emacs@gnu.org; Fri, 29 Oct 2021 00:31:02 -0400 Original-Received: from sonic304-20.consmr.mail.ne1.yahoo.com ([66.163.191.146]:41391) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1mgJXe-0007Up-DQ for bug-gnu-emacs@gnu.org; Fri, 29 Oct 2021 00:31:01 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1635481856; bh=M9jlSGiQ5ttmM+YxY7RZ2yN8grjviVnlQzAmygiOIq0=; h=From:To:Subject:Date:References:From:Subject:Reply-To; b=TttAfSZ77R59Kq2bwXvaro3AngbFV6QnToHDrvDAAsQDY1I2ZjHcMiVAdnsQhLJh2MlOyDdX5jkU/4AZwmhXH1WiDJ2RT6i3O2ktWK5w4xO7ixqxrEQ6e5G0LeHgYVqnebqgQC7e8Ju3XZD2JnaOo6xepFFDKFP5Bi0pJ76rramUyAByM0w3bkV2MhTCqlhLcAkpc+aI/OIMmccS3RTxIQY02OBqDbEXQxPRayKh5PH9RolEACZ3gKZsluWPnmjdU31GKPw+Ch7NTs8QyALyopUwMes69kegn6wAtVx6V+69ssvUdX7k/wPeLo845ekZPZ0KiENndJnlXuWUou7FAw== X-SONIC-DKIM-SIGN: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1635481856; bh=0GRlMDuenKVN6CM2pK/rbADigqzF6NqeEQ/AYYgLoft=; h=X-Sonic-MF:From:To:Subject:Date:From:Subject; b=o53XY3VmopfxYdD4nFlI6ZyuCvUtQSAbg63ohyvt8Ivqm6HymcsX6321Lpu0a+BdzIEMZg8wXKxRMdXFvx4osTPyJ6FwXdLFjh8yBxMiIbUAVy5RS63Fsmy+fuYW0wJlMEAXZc9NoTqLlBdLezKgRYNc/25DzHIg4kgzYYuJLF5v4bqD8usaYGj7TMLyuccIv3HaFHbRvoKadk9InQ8DfyhXLrShYHMcBls9BlQuzn46pxBc8htCe63Axjby55YNOce5B6SzcTW/eDml65Fw85FseBIxfrErzIwSaYennHdpWoY4cXNO5Ls15WL5JIBV2QLafSI2wN6beX910I4orw== X-YMail-OSG: o0lGEyAVM1lT6MrSvIvNlIYuheM4ifCTJUcpfCZnrseKAJfiX1v0WF6PNIx0LR8 ZevTjn4VqFPJCX6YbOsprJcc60yTFbXB1aDi0Zc3bTH23YJmB.XmTZEJDIXo.LLw4Fxfz6T3EVX6 uW5U3QJ51B5_OnvyH9geGEL_DeMfcdCDDE6N.AEXtR7.VdbJc04Kha.J1EDdYwfboOikqUwQVh12 rAzfQpucMlGBqlzO5fzVSHfOyBWJMC5P5HQiKJHxWVxchM4rZSW6zbbtV_utqmFjFBqVMQ8t05cz pjo_miOiuCIQ8kHsrYUedtBnVCzRZv.71Cy_JB7xaMWchkMOIO2qzaos1mhyKotpB7gq2DpMbIgn YrEfDqZxFr_AbrA85LIuzC9XupSP9_WDsPpwyBa3WHbjMRg.GMVfwEid8CrjXxaQo7ZR0BU0nyW6 Plhah7PJprUPshSDEu7NKXuiFWJLJ8RQBq.akiLgkW.eiVkP1o9mEIvF9TVNobBPHa9QU1A6TOrN zkuuwLFSLebUpvPBQxoeN3lsskOutpA892rCQJoduWvRlJkM1eEtwFqhNupet.WByC_ZqENdyx1m u34eCDmyryt6MEdAcaZnTHtwv9ipHIrn8A_.Q49G5Img92PAnoCVpidTtTGJ1_aR6EHxfJtFkNUE 34fLJxmbx8swqX.hK25T5YAkC4Zmqfi3VPebeKOIJL6mEBdmoSVgbGANPxfTksb1sJEJFRPfndoH CbYXCkHkEZo9PfB7F8g8qvl1ouMxStsX8PuiUYbJZyGVVUCySW21j6RdJ3TzaD8lDSEWb8JWP0z2 vTMfCRg1pZR3_PRnAboh1KS1u7mxSE.BT5voXuNHmG X-Sonic-MF: Original-Received: from sonic.gate.mail.ne1.yahoo.com by sonic304.consmr.mail.ne1.yahoo.com with HTTP; Fri, 29 Oct 2021 04:30:56 +0000 Original-Received: by kubenode514.mail-prod1.omega.sg3.yahoo.com (VZM Hermes SMTP Server) with ESMTPA ID febf8a47ece21e54376e4adf1664236f; Fri, 29 Oct 2021 04:30:48 +0000 (UTC) X-Mailer: WebService/1.1.19198 mail.backend.jedi.jws.acl:role.jedi.acl.token.atz.jws.hermes.yahoo Received-SPF: pass client-ip=66.163.191.146; envelope-from=luangruo@yahoo.com; helo=sonic304-20.consmr.mail.ne1.yahoo.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action 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:218529 Archived-At: --=-=-= Content-Type: text/plain The attached changes fix flickering xwidgets by moving xwidget display into a separate X window, and also enable the scrolling optimization for xwidgets. Someone with access to a Mac will have to do the changes necessary for xwidget scrolling to work correctly on macOS, as they don't work on GNUstep in the first place. However, event passthrough doesn't work yet, and will require some thought. But that would be a fantastic opportunity to clean up the current xwidget event handling mess (and perhaps even define a mechanism for Lisp code to send events into xwidgets, which would make integrating them into the existing Emacs event model much easier.) Any thoughts? Thanks. --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-Use-an-X-window-to-display-xwidgets-on-X11.patch >From 09f3697b2904dac7616697ba252d93e4748f7418 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Thu, 28 Oct 2021 21:46:01 +0800 Subject: [PATCH 1/2] Use an X window to display xwidgets on X11 Since Emacs draws using X11 primitives, Emacs drawing operations can erase the GtkDrawingArea used to draw the offscreen widget, which leads to unpleasant flickering effects, all of which can be prevented by creating a native X window and drawing to that instead. * src/xterm.c (x_window_to_frame): Return xwidget window if appropriate. (handle_one_xevent): Handle xwidget expose events. * src/xwidget.c (x_window_to_xvw_map): New variable. (xwidget_view_from_window): New function. (xwidget_show_view, xwidget_hide_view) (offscreen_damage_event): Move to drawing xwidgets with X windows. (xv_do_draw) (xwidget_expose): New function. (xwidget_osr_draw_cb, xwidget_osr_event_forward) (xwidget_osr_event_set_embedder): Removed. (xwidget_init_view): Replace use of GtkDrawingArea with that of an X window. (x_draw_xwidget_glyph_string): Prevent xwidget views from constantly generating expose events, and use an X window instead of a GtkDrawingArea. (syms_of_xwidget): Initialize x_window_to_xwv_map. src/xwidget.h (struct xwidget_view): Replace GTK widgets with X-related fields. (xwidget_view_from_window, xwidget_expose): New functions. --- src/xterm.c | 24 ++++- src/xwidget.c | 277 ++++++++++++++++++++++++++------------------------ src/xwidget.h | 12 ++- 3 files changed, 175 insertions(+), 138 deletions(-) diff --git a/src/xterm.c b/src/xterm.c index aa1a1a5eed..54bfb65bd0 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -4563,8 +4563,9 @@ x_focus_changed (int type, int state, struct x_display_info *dpyinfo, struct fra } } -/* Return the Emacs frame-object corresponding to an X window. - It could be the frame's main window or an icon window. */ +/* Return the Emacs frame-object corresponding to an X window. It + could be the frame's main window, an icon window, or an xwidget + window. */ static struct frame * x_window_to_frame (struct x_display_info *dpyinfo, int wdesc) @@ -4575,6 +4576,13 @@ x_window_to_frame (struct x_display_info *dpyinfo, int wdesc) if (wdesc == None) return NULL; +#ifdef HAVE_XWIDGETS + struct xwidget_view *xvw = xwidget_view_from_window (wdesc); + + if (xvw && xvw->frame) + return xvw->frame; +#endif + FOR_EACH_FRAME (tail, frame) { f = XFRAME (frame); @@ -8211,6 +8219,18 @@ handle_one_xevent (struct x_display_info *dpyinfo, case Expose: f = x_window_to_frame (dpyinfo, event->xexpose.window); +#ifdef HAVE_XWIDGETS + { + struct xwidget_view *xv = + xwidget_view_from_window (event->xexpose.window); + + if (xv) + { + xwidget_expose (xv); + goto OTHER; + } + } +#endif if (f) { if (!FRAME_VISIBLE_P (f)) diff --git a/src/xwidget.c b/src/xwidget.c index e4b42e6e0c..62b30a07ab 100644 --- a/src/xwidget.c +++ b/src/xwidget.c @@ -35,10 +35,16 @@ Copyright (C) 2011-2021 Free Software Foundation, Inc. #ifdef USE_GTK #include #include +#include +#include #elif defined NS_IMPL_COCOA #include "nsxwidget.h" #endif +#ifdef USE_GTK +static Lisp_Object x_window_to_xwv_map; +#endif + static struct xwidget * allocate_xwidget (void) { @@ -222,15 +228,28 @@ xwidget_hidden (struct xwidget_view *xv) } #ifdef USE_GTK + +struct xwidget_view * +xwidget_view_from_window (Window wdesc) +{ + Lisp_Object key = make_fixnum (wdesc); + Lisp_Object xwv = Fgethash (key, x_window_to_xwv_map, Qnil); + + if (NILP (xwv)) + return NULL; + + return XXWIDGET_VIEW (xwv); +} + static void xwidget_show_view (struct xwidget_view *xv) { xv->hidden = false; - gtk_widget_show (xv->widgetwindow); - gtk_fixed_move (GTK_FIXED (xv->emacswindow), - xv->widgetwindow, - xv->x + xv->clip_left, - xv->y + xv->clip_top); + XMoveWindow (xv->dpy, xv->wdesc, + xv->x + xv->clip_left, + xv->y + xv->clip_top); + XMapWindow (xv->dpy, xv->wdesc); + XFlush (xv->dpy); } /* Hide an xwidget view. */ @@ -238,28 +257,48 @@ xwidget_show_view (struct xwidget_view *xv) xwidget_hide_view (struct xwidget_view *xv) { xv->hidden = true; - gtk_fixed_move (GTK_FIXED (xv->emacswindow), xv->widgetwindow, - 10000, 10000); + XUnmapWindow (xv->dpy, xv->wdesc); + XFlush (xv->dpy); +} + +static void +xv_do_draw (struct xwidget_view *xw, struct xwidget *w) +{ + block_input (); + + cairo_save (xw->cr_context); + cairo_translate (xw->cr_context, -xw->clip_left, + -xw->clip_top); + gtk_widget_draw (w->widget_osr, xw->cr_context); + cairo_restore (xw->cr_context); + + unblock_input (); } /* When the off-screen webkit master view changes this signal is called. It copies the bitmap from the off-screen instance. */ static gboolean offscreen_damage_event (GtkWidget *widget, GdkEvent *event, - gpointer xv_widget) + gpointer xwidget_view) { - /* Queue a redraw of onscreen widget. - There is a guard against receiving an invalid widget, - which should only happen if we failed to remove the - specific signal handler for the damage event. */ - if (GTK_IS_WIDGET (xv_widget)) - gtk_widget_queue_draw (GTK_WIDGET (xv_widget)); - else - message ("Warning, offscreen_damage_event received invalid xv pointer:%p\n", - xv_widget); + struct xwidget_view *xw = xwidget_view; + struct xwidget *w = XXWIDGET (xw->model); + + if (xw->wdesc == None) + return FALSE; + + xv_do_draw (xw, w); return FALSE; } + +void +xwidget_expose (struct xwidget_view *xv) +{ + struct xwidget *xw = XXWIDGET (xv->model); + + xv_do_draw (xv, xw); +} #endif /* USE_GTK */ void @@ -498,51 +537,6 @@ webkit_decide_policy_cb (WebKitWebView *webView, return FALSE; } } - - -/* For gtk3 offscreen rendered widgets. */ -static gboolean -xwidget_osr_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data) -{ - struct xwidget *xw = g_object_get_data (G_OBJECT (widget), XG_XWIDGET); - struct xwidget_view *xv = g_object_get_data (G_OBJECT (widget), - XG_XWIDGET_VIEW); - - cairo_rectangle (cr, 0, 0, xv->clip_right, xv->clip_bottom); - cairo_clip (cr); - - gtk_widget_draw (xw->widget_osr, cr); - return FALSE; -} - -static gboolean -xwidget_osr_event_forward (GtkWidget *widget, GdkEvent *event, - gpointer user_data) -{ - /* Copy events that arrive at the outer widget to the offscreen widget. */ - struct xwidget *xw = g_object_get_data (G_OBJECT (widget), XG_XWIDGET); - GdkEvent *eventcopy = gdk_event_copy (event); - eventcopy->any.window = gtk_widget_get_window (xw->widget_osr); - - /* TODO: This might leak events. They should be deallocated later, - perhaps in xwgir_event_cb. */ - gtk_main_do_event (eventcopy); - - /* Don't propagate this event further. */ - return TRUE; -} - -static gboolean -xwidget_osr_event_set_embedder (GtkWidget *widget, GdkEvent *event, - gpointer data) -{ - struct xwidget_view *xv = data; - struct xwidget *xww = XXWIDGET (xv->model); - gdk_offscreen_window_set_embedder (gtk_widget_get_window - (xww->widgetwindow_osr), - gtk_widget_get_window (xv->widget)); - return FALSE; -} #endif /* USE_GTK */ @@ -568,63 +562,21 @@ xwidget_init_view (struct xwidget *xww, XSETXWIDGET (xv->model, xww); #ifdef USE_GTK - if (EQ (xww->type, Qwebkit)) - { - xv->widget = gtk_drawing_area_new (); - /* Expose event handling. */ - gtk_widget_set_app_paintable (xv->widget, TRUE); - gtk_widget_add_events (xv->widget, GDK_ALL_EVENTS_MASK); - - /* Draw the view on damage-event. */ - g_signal_connect (G_OBJECT (xww->widgetwindow_osr), "damage-event", - G_CALLBACK (offscreen_damage_event), xv->widget); - - if (EQ (xww->type, Qwebkit)) - { - g_signal_connect (G_OBJECT (xv->widget), "button-press-event", - G_CALLBACK (xwidget_osr_event_forward), NULL); - g_signal_connect (G_OBJECT (xv->widget), "button-release-event", - G_CALLBACK (xwidget_osr_event_forward), NULL); - g_signal_connect (G_OBJECT (xv->widget), "motion-notify-event", - G_CALLBACK (xwidget_osr_event_forward), NULL); - } - else - { - /* xwgir debug, orthogonal to forwarding. */ - g_signal_connect (G_OBJECT (xv->widget), "enter-notify-event", - G_CALLBACK (xwidget_osr_event_set_embedder), xv); - } - g_signal_connect (G_OBJECT (xv->widget), "draw", - G_CALLBACK (xwidget_osr_draw_cb), NULL); - } + xv->dpy = FRAME_X_DISPLAY (s->f); - /* Widget realization. - - Make container widget first, and put the actual widget inside the - container later. Drawing should crop container window if necessary - to handle case where xwidget is partially obscured by other Emacs - windows. Other containers than gtk_fixed where explored, but - gtk_fixed had the most predictable behavior so far. */ - - xv->emacswindow = FRAME_GTK_WIDGET (s->f); - xv->widgetwindow = gtk_fixed_new (); - gtk_widget_set_has_window (xv->widgetwindow, TRUE); - gtk_container_add (GTK_CONTAINER (xv->widgetwindow), xv->widget); - - /* Store some xwidget data in the gtk widgets. */ - g_object_set_data (G_OBJECT (xv->widget), XG_FRAME_DATA, s->f); - g_object_set_data (G_OBJECT (xv->widget), XG_XWIDGET, xww); - g_object_set_data (G_OBJECT (xv->widget), XG_XWIDGET_VIEW, xv); - g_object_set_data (G_OBJECT (xv->widgetwindow), XG_XWIDGET, xww); - g_object_set_data (G_OBJECT (xv->widgetwindow), XG_XWIDGET_VIEW, xv); - - gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xww->width, - xww->height); - gtk_widget_set_size_request (xv->widgetwindow, xww->width, xww->height); - gtk_fixed_put (GTK_FIXED (FRAME_GTK_WIDGET (s->f)), xv->widgetwindow, x, y); xv->x = x; xv->y = y; - gtk_widget_show_all (xv->widgetwindow); + + xv->clip_left = 0; + xv->clip_right = xww->width; + xv->clip_top = 0; + xv->clip_bottom = xww->height; + + xv->wdesc = None; + xv->frame = s->f; + + g_signal_connect (G_OBJECT (xww->widgetwindow_osr), "damage-event", + G_CALLBACK (offscreen_damage_event), xv); #elif defined NS_IMPL_COCOA nsxwidget_init_view (xv, xww, s, x, y); nsxwidget_resize_view(xv, xww->width, xww->height); @@ -681,6 +633,8 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) window_box (s->w, TEXT_AREA, &text_area_x, &text_area_y, &text_area_width, &text_area_height); + /* On X11, this keeps generating expose events. */ +#ifndef USE_GTK /* Resize xwidget webkit if its container window size is changed in some ways, for example, a buffer became hidden in small split window, then it can appear front in merged whole window. */ @@ -693,6 +647,7 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) make_int (text_area_width), make_int (text_area_height)); } +#endif clip_left = max (0, text_area_x - x); clip_right = max (clip_left, @@ -711,15 +666,45 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) later. */ bool moved = (xv->x + xv->clip_left != x + clip_left || xv->y + xv->clip_top != y + clip_top); + +#ifdef USE_GTK + bool wdesc_was_none = xv->wdesc == None; +#endif xv->x = x; xv->y = y; +#ifdef USE_GTK + block_input (); + if (xv->wdesc == None) + { + Lisp_Object xvw; + XSETXWIDGET_VIEW (xvw, xv); + XSetWindowAttributes a; + a.event_mask = ExposureMask; + + xv->wdesc = XCreateWindow (xv->dpy, FRAME_X_WINDOW (s->f), + x + clip_left, y + clip_top, + clip_right - clip_left, + clip_bottom - clip_top, 0, + CopyFromParent, CopyFromParent, + CopyFromParent, CWEventMask, &a); + xv->cr_surface = cairo_xlib_surface_create (xv->dpy, + xv->wdesc, + FRAME_DISPLAY_INFO (s->f)->visual, + clip_right - clip_left, + clip_bottom - clip_top); + xv->cr_context = cairo_create (xv->cr_surface); + Fputhash (make_fixnum (xv->wdesc), xvw, x_window_to_xwv_map); + + moved = false; + } +#endif + /* Has it moved? */ if (moved) { #ifdef USE_GTK - gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)), - xv->widgetwindow, x + clip_left, y + clip_top); + XMoveWindow (xv->dpy, xv->wdesc, x + clip_left, y + clip_top); #elif defined NS_IMPL_COCOA nsxwidget_move_view (xv, x + clip_left, y + clip_top); #endif @@ -735,10 +720,14 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) || xv->clip_top != clip_top || xv->clip_left != clip_left) { #ifdef USE_GTK - gtk_widget_set_size_request (xv->widgetwindow, clip_right - clip_left, - clip_bottom - clip_top); - gtk_fixed_move (GTK_FIXED (xv->widgetwindow), xv->widget, -clip_left, - -clip_top); + if (!wdesc_was_none) + { + XResizeWindow (xv->dpy, xv->wdesc, clip_right - clip_left, + clip_bottom - clip_top); + XFlush (xv->dpy); + cairo_xlib_surface_set_size (xv->cr_surface, clip_right - clip_left, + clip_bottom - clip_top); + } #elif defined NS_IMPL_COCOA nsxwidget_resize_view (xv, clip_right - clip_left, clip_bottom - clip_top); @@ -758,12 +747,15 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) if (!xwidget_hidden (xv)) { #ifdef USE_GTK - gtk_widget_queue_draw (xv->widgetwindow); - gtk_widget_queue_draw (xv->widget); + xv_do_draw (xv, xww); #elif defined NS_IMPL_COCOA nsxwidget_set_needsdisplay (xv); #endif } + +#ifdef USE_GTK + unblock_input (); +#endif } static bool @@ -976,8 +968,13 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0, if (XXWIDGET (xv->model) == xw) { #ifdef USE_GTK - gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xw->width, - xw->height); + if (xv->wdesc != None) + { + XResizeWindow (xv->dpy, xv->wdesc, xw->width, xw->height); + XFlush (xv->dpy); + cairo_xlib_surface_set_size (xv->cr_surface, + xw->width, xw->height); + } #elif defined NS_IMPL_COCOA nsxwidget_resize_view(xv, xw->width, xw->height); #endif @@ -1084,13 +1081,21 @@ DEFUN ("delete-xwidget-view", CHECK_XWIDGET_VIEW (xwidget_view); struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view); #ifdef USE_GTK - gtk_widget_destroy (xv->widgetwindow); - /* xv->model still has signals pointing to the view. There can be - several views. Find the matching signals and delete them all. */ - g_signal_handlers_disconnect_matched (XXWIDGET (xv->model)->widgetwindow_osr, - G_SIGNAL_MATCH_DATA, - 0, 0, 0, 0, - xv->widget); + if (xv->wdesc != None) + { + block_input (); + XDestroyWindow (xv->dpy, xv->wdesc); + /* xv->model still has signals pointing to the view. There can be + several views. Find the matching signals and delete them all. */ + g_signal_handlers_disconnect_matched (XXWIDGET (xv->model)->widgetwindow_osr, + G_SIGNAL_MATCH_DATA, + 0, 0, 0, 0, xv); + + cairo_destroy (xv->cr_context); + cairo_surface_destroy (xv->cr_surface); + Fremhash (make_fixnum (xv->wdesc), x_window_to_xwv_map); + unblock_input (); + } #elif defined NS_IMPL_COCOA nsxwidget_delete_view (xv); #endif @@ -1236,6 +1241,12 @@ syms_of_xwidget (void) Vxwidget_view_list = Qnil; Fprovide (intern ("xwidget-internal"), Qnil); + +#ifdef USE_GTK + x_window_to_xwv_map = CALLN (Fmake_hash_table, QCtest, Qeq); + + staticpro (&x_window_to_xwv_map); +#endif } diff --git a/src/xwidget.h b/src/xwidget.h index 591f23489d..fc68b52cdb 100644 --- a/src/xwidget.h +++ b/src/xwidget.h @@ -32,6 +32,7 @@ #define XWIDGET_H_INCLUDED #if defined (USE_GTK) #include +#include #elif defined (NS_IMPL_COCOA) && defined (__OBJC__) #import #import "nsxwidget.h" @@ -98,9 +99,12 @@ #define XWIDGET_H_INCLUDED bool hidden; #if defined (USE_GTK) - GtkWidget *widget; - GtkWidget *widgetwindow; - GtkWidget *emacswindow; + Display *dpy; + Window wdesc; + struct frame *frame; + + cairo_surface_t *cr_surface; + cairo_t *cr_context; #elif defined (NS_IMPL_COCOA) # ifdef __OBJC__ XvWindow *xvWindow; @@ -162,6 +166,8 @@ #define XG_XWIDGET_VIEW "emacs_xwidget_view" void store_xwidget_js_callback_event (struct xwidget *xw, Lisp_Object proc, Lisp_Object argument); +struct xwidget_view *xwidget_view_from_window (Window wdesc); +void xwidget_expose (struct xwidget_view *xv); #else INLINE_HEADER_BEGIN INLINE void syms_of_xwidget (void) {} -- 2.31.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0002-Enable-scrolling-optimization-for-xwidgets.patch >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 --=-=-=--