From: Po Lu via "Bug reports for GNU Emacs, the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org>
To: Lars Ingebrigtsen <larsi@gnus.org>
Cc: Eli Zaretskii <eliz@gnu.org>, 51473@debbugs.gnu.org
Subject: bug#51473: [PATCH] Enable xwidget scrolling optimizations, and other xwidgets improvements
Date: Sun, 07 Nov 2021 09:26:41 +0800 [thread overview]
Message-ID: <87v914ycvi.fsf@yahoo.com> (raw)
In-Reply-To: <87r1bsrc7a.fsf@gnus.org> (Lars Ingebrigtsen's message of "Sun, 07 Nov 2021 02:23:05 +0100")
[-- Attachment #1: Type: text/plain, Size: 699 bytes --]
Lars Ingebrigtsen <larsi@gnus.org> writes:
> It gets further:
>
> xwidget.c:1951:12: error: no member named 'find_text' in 'struct xwidget'
> if (!xw->find_text)
> ~~ ^
> xwidget.c:1983:12: error: no member named 'find_text' in 'struct xwidget'
> if (!xw->find_text)
> ~~ ^
> xwidget.c:2021:12: error: no member named 'find_text' in 'struct xwidget'
> if (!xw->find_text)
> ~~ ^
>
> Oh, and I didn't notice this error the first time around, apparently:
>
> print.c:1529:23: error: no member named 'widget_osr' in 'struct xwidget'
> XXWIDGET (obj)->widget_osr);
> ~~~~~~~~~~~~~~ ^
Thanks, does this work instead?
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: test.diff --]
[-- Type: text/x-patch, Size: 94402 bytes --]
diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi
index 7f91e1c188..1207ab5e9a 100644
--- a/doc/emacs/misc.texi
+++ b/doc/emacs/misc.texi
@@ -2953,6 +2953,34 @@ Embedded WebKit Widgets
reloading it. Type @w{@kbd{C-h b}} in that buffer to see the key
bindings.
+@findex xwidget-webkit-edit-mode
+@cindex xwidget-webkit-edit-mode
+ By default, typing a self-inserting character inside an xwidget
+webkit buffer will do nothing, or trigger some special action. To
+make those characters and other common editing keys insert themselves
+when pressed, you can enable @code{xwidget-webkit-edit-mode}, which
+redefines them to be passed through to the WebKit xwidget.
+
+You can also enable @code{xwidget-webkit-edit-mode} by typing @kbd{e}
+inside the xwidget webkit buffer.
+
+@findex xwidget-webkit-isearch-mode
+@cindex xwidget-webkit-isearch-mode
+@cindex searching in webkit buffers
+ @code{xwidget-webkit-isearch-mode} is a minor mode that behaves
+similarly to incremental search (@pxref{Incremental Search}), but
+operates on the contents of a WebKit widget instead of the current
+buffer. It is bound to @kbd{C-s} and @kbd{C-r} inside xwidget-webkit
+buffers. When it is enabled through @kbd{C-r}, the initial search
+will be performed in reverse direction.
+
+Typing any self-inserting character will cause the character to be
+inserted into the current search query. Typing @kbd{C-s} will cause
+the WebKit widget to display the next search result, while typing
+@kbd{C-r} will cause it to display the last.
+
+To leave incremental search, you can type @kbd{C-g}.
+
@node Browse-URL
@subsection Following URLs
@cindex World Wide Web
diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi
index b38a83b4fe..832b570b6a 100644
--- a/doc/lispref/commands.texi
+++ b/doc/lispref/commands.texi
@@ -1176,6 +1176,7 @@ Input Events
* Repeat Events:: Double and triple click (or drag, or down).
* Motion Events:: Just moving the mouse, not pushing a button.
* Focus Events:: Moving the mouse between frames.
+* Xwidget Events:: Events generated by xwidgets.
* Misc Events:: Other events the system can generate.
* Event Examples:: Examples of the lists for mouse events.
* Classifying Events:: Finding the modifier keys in an event symbol.
@@ -1871,6 +1872,76 @@ Focus Events
so that the focus event comes either before or after the multi-event key
sequence, and not within it.
+@node Xwidget Events
+@subsection Xwidget events
+
+Xwidgets (@pxref{Xwidgets}) can send events to update Lisp programs on
+their status. These events are dubbed @code{xwidget-events}, and
+contain various data describing the nature of the change.
+
+@table @code
+@cindex @code{xwidget-event} event
+@item (xwidget-event @var{kind} @var{xwidget} @var{arg})
+This event is sent whenever some kind of update occurs in
+@var{xwidget}. There are several types of updates, which are
+identified by @var{kind}.
+
+@cindex @code{load-changed} xwidget events
+An xwidget event with @var{kind} set to @code{load-changed} indicates
+that the @var{xwidget} has reached a particular point of the
+page-loading process. When these events are sent, @var{arg} will
+contain a string that futher describes the status of the widget.
+
+@cindex @samp{"load-finished"} in xwidgets
+When @var{arg} is @samp{"load-finished"}, it means the xwidget has
+finished processing whatever page-loading operation that it was
+previously performing.
+
+@cindex @samp{"load-started"} in xwidgets
+Otherwise, if it is @samp{"load-started"}, then the widget has begun a
+page-loading operation.
+
+@cindex @samp{"load-redirected"} in xwidgets
+If @var{arg} is @samp{"load-redirected"}, it means the widget has
+encountered and followed a redirect during the page-loading operation.
+
+@cindex @samp{"load-committed"} in xwidgets
+If @var{arg} is @samp{"load-committed"}, then the widget has committed
+to a given URL during the page-loading operation. This means that the
+URL is the final URL that will be rendered by @var{xwidget} during the
+current page-loading operation.
+
+@cindex @code{download-callback} xwidget events
+An event with @var{kind} set to @code{download-callback} indicates
+that a download of some kind has been completed.
+
+In these events, there can be arguments after @var{arg}, which itself
+indicates the URL that the download file was retrieved from: the first
+argument after @var{arg} indicates the MIME type of the download, as a
+string, while the second such argument contains the full file path to
+the downloaded file.
+
+@cindex @code{download-started} xwidget events
+An event with @var{kind} set to @code{download-started} indicates that
+a download has been started. In these events, @var{arg} contains the
+URL of the file that is currently being downloaded.
+
+@cindex @code{javascript-callback} xwidget events
+An event with @var{kind} set to @code{javascript-callback} contains
+JavaScript callback data. These events are used internally by
+@code{xwidget-webkit-execute-script}.
+
+@cindex @code{xwidget-display-event} event
+@item (xwidget-display-event @var{xwidget})
+This event is sent whenever an xwidget requests that another xwidget
+be displayed. @var{xwidget} is the xwidget that should be displayed.
+
+@var{xwidget}'s buffer will be set to a temporary buffer. When
+displaying the widget, care should be taken to replace the buffer with
+the buffer in which the xwidget will be displayed, using
+@code{set-xwidget-buffer} (@pxref{Xwidgets}).
+@end table
+
@node Misc Events
@subsection Miscellaneous System Events
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 22528a1b0f..60bca15eb2 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -6784,7 +6784,10 @@ Xwidgets
in a @code{display} text or overlay property (@pxref{Display
Property}).
-@defun make-xwidget type title width height arguments &optional buffer
+ Embedded widgets can send events notifying Lisp code about changes
+occurring within them. (@pxref{Xwidget Events}).
+
+@defun make-xwidget type title width height arguments &optional buffer related
This creates and returns an xwidget object. If
@var{buffer} is omitted or @code{nil}, it defaults to the current
buffer. If @var{buffer} names a buffer that doesn't exist, it will be
@@ -6797,7 +6800,10 @@ Xwidgets
@end table
The @var{width} and @var{height} arguments specify the widget size in
-pixels, and @var{title}, a string, specifies its title.
+pixels, and @var{title}, a string, specifies its title. @var{related}
+is used internally by the WebKit widget, and specifies another WebKit
+widget that the newly created widget should share settings and
+subprocesses with.
@end defun
@defun xwidgetp object
@@ -6818,6 +6824,10 @@ Xwidgets
This function returns the buffer of @var{xwidget}.
@end defun
+@defun set-xwidget-buffer xwidget buffer
+This function sets the buffer of @var{xwidget} to @var{buffer}.
+@end defun
+
@defun get-buffer-xwidgets buffer
This function returns a list of xwidget objects associated with the
@var{buffer}, which can be specified as a buffer object or a name of
@@ -6878,6 +6888,61 @@ Xwidgets
query-on-exit flag, either @code{t} or @code{nil}.
@end defun
+@defun xwidget-perform-lispy-event xwidget event frame
+Send an input event @var{event} to @var{xwidget}. The precise action
+performed is platform-specific. See @ref{Input Events}.
+
+You can optionally pass the frame the event was generated from via
+@var{frame}. On X11, modifier keys in key events will not be
+considered if @var{frame} is @code{nil}, and the selected frame is not
+an X-Windows frame.
+
+On GTK, only keyboard and function key events are implemented. Mouse,
+motion, and click events are dispatched to the xwidget without going
+through Lisp code, and as such shouldn't require this function to be
+sent.
+@end defun
+
+@defun xwidget-webkit-search query xwidget &optional case-insensitive backwards wrap-around
+Start an incremental search on the WebKit widget @var{xwidget} with
+the string @var{query} as a query. @var{case-insensitive} denotes
+whether or not the search is case-insensitive, @var{backwards}
+determines if the search is performed backwards towards the start of
+the document, and @var{wrap-around} determines whether or not the
+search terminates at the end of the document.
+
+If the function is called while a search query is already present,
+then the query specified here will replace the existing query.
+
+To stop a search query, use @code{xwidget-webkit-finish-search}.
+@end defun
+
+@defun xwidget-webkit-next-result xwidget
+Display the next search result in @var{xwidget}. This function will
+error unless a search query has already been started in @var{xwidget}
+through @code{xwidget-webkit-search}.
+
+If @code{wrap-around} was non-nil when @code{xwidget-webkit-search}
+was called, then the search will restart from the beginning of the
+document if the end is reached.
+@end defun
+
+@defun xwidget-webkit-previous-result xwidget
+Display the previous search result in @var{xwidget}. This function
+will error unless a search query has already been started in
+@var{xwidget} through @code{xwidget-webkit-search}.
+
+If @code{wrap-around} was non-nil when @code{xwidget-webkit-search}
+was called, then the search will restart from the end of the
+document if the beginning is reached.
+@end defun
+
+@defun xwidget-webkit-finish-search xwidget
+Finish a search operation started with @code{xwidget-webkit-search} in
+@var{xwidget}. If there is no query currently ongoing, then this
+function will error.
+@end defun
+
@node Buttons
@section Buttons
@cindex buttons in buffers
diff --git a/etc/NEWS b/etc/NEWS
index a50229916f..b14f9a2549 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -495,6 +495,31 @@ the buffer will take you to that directory.
This is a convenience function to extract the field data from
'exif-parse-file' and 'exif-parse-buffer'.
+** Xwidgets
+
++++
+*** New minor mode `xwidget-webkit-edit-mode'.
+When this mode is enabled, self-inserting characters and other common
+web browser shotcut keys are redefined to send themselves to the
+WebKit widget.
+
++++
+*** New minor mode `xwidget-webkit-isearch-mode'.
+This mode acts similarly to incremental search, and allows to search
+the contents of a WebKit widget. In xwidget-webkit mode, it is bound
+to `C-s' and `C-r'.
+
+---
+*** On X11, the WebKit inspector is now available inside xwidgets.
+To access the inspector, right click on the widget and select "Inspect
+Element".
+
+---
+*** "Open in New Window" in a WebKit widget's context menu now works.
+The newly created buffer will be displayed via display-buffer, which
+can be customized through the usual mechanism of display-buffer-alist
+and friends.
+
\f
* New Modes and Packages in Emacs 29.1
@@ -719,6 +744,39 @@ an exact match, then the lowercased '[menu-bar foo\ bar]' and finally
'[menu-bar foo-bar]'. This further improves backwards-compatibility
when converting menus to use 'easy-menu-define'.
++++
+** The function `make-xwidget' now accepts an optional RELATED argument.
+This argument is used as another widget for the newly created WebKit
+widget to share settings and subprocesses with. It must be another
+WebKit widget.
+
++++
+** New function `xwidget-perform-lispy-event'.
+This function allows you to send events to xwidgets. Usually, some
+equivalent of the event will be sent, but there is no guarantee of
+what the widget will actually receive.
+
+On GTK+, only key and function key events are implemented.
+
++++
+** New functions for performing searches on WebKit xwidgets.
+Some new functions, such as `xwidget-webkit-search', have been added
+for performing searches on WebKit xwidgets.
+
++++
+** `load-changed' xwidget events are now more detailed.
+In particular, they can now have different arguments based on the
+state of the WebKit widget. `load-finished' is sent when a load has
+completed, `load-started' when a load first starts, `load-redirected'
+after a redirect, and `load-committed' when the WebKit widget first
+commits to the load.
+
++++
+** New event type `xwidget-display-event'.
+These events are sent whenever an xwidget requests that Emacs display
+another. The only argument to this event is the xwidget that should
+be displayed.
+
\f
* Changes in Emacs 29.1 on Non-Free Operating Systems
diff --git a/etc/images/README b/etc/images/README
index 9bbe796cc9..561cfff765 100644
--- a/etc/images/README
+++ b/etc/images/README
@@ -68,6 +68,7 @@ Emacs images and their source in the GNOME icons stock/ directory:
bookmark_add.xpm actions/bookmark_add
cancel.xpm slightly modified generic/stock_stop
connect.xpm net/stock_connect
+ connect-to-url.xpm net/stock_connect-to-url
contact.xpm net/stock_contact
data-save.xpm data/stock_data-save
delete.xpm generic/stock_delete
diff --git a/etc/images/connect-to-url.pbm b/etc/images/connect-to-url.pbm
new file mode 100644
index 0000000000..f142349f4a
Binary files /dev/null and b/etc/images/connect-to-url.pbm differ
diff --git a/etc/images/connect-to-url.xpm b/etc/images/connect-to-url.xpm
new file mode 100644
index 0000000000..38fefeaf61
--- /dev/null
+++ b/etc/images/connect-to-url.xpm
@@ -0,0 +1,281 @@
+/* XPM */
+static char *connect_to_url[] = {
+/* columns rows colors chars-per-pixel */
+"24 24 251 2 ",
+" c black",
+". c #010101",
+"X c #000103",
+"o c #010204",
+"O c #010305",
+"+ c #020407",
+"@ c #020609",
+"# c #03070C",
+"$ c #04080D",
+"% c #0F0F0D",
+"& c #030A10",
+"* c #050B10",
+"= c #060C11",
+"- c #070D13",
+"; c #070D14",
+": c #060C15",
+"> c #070E14",
+", c #0B1824",
+"< c #0A1B2B",
+"1 c #0A1C2E",
+"2 c #141A20",
+"3 c #161E25",
+"4 c #181E23",
+"5 c #0D2032",
+"6 c #142534",
+"7 c #1F2830",
+"8 c #1D2933",
+"9 c #102438",
+"0 c #272622",
+"q c #21292F",
+"w c #272F36",
+"e c #282F33",
+"r c #222F3A",
+"t c #2E3337",
+"y c #2D373E",
+"u c #32383C",
+"i c #33383C",
+"p c #343A3E",
+"a c #43423C",
+"s c #112941",
+"d c #102A44",
+"f c #132D47",
+"g c #192F46",
+"h c #17314B",
+"j c #15314F",
+"k c #163351",
+"l c #163554",
+"z c #173554",
+"x c #1F3A53",
+"c c #1D3955",
+"v c #1A3958",
+"b c #1C3B5B",
+"n c #1F3C58",
+"m c #1D3C5C",
+"M c #1E3E5D",
+"N c #1F3F5F",
+"B c #303B44",
+"V c #313C44",
+"C c #313D47",
+"Z c #213C56",
+"A c #233E57",
+"S c #1F405F",
+"D c #374148",
+"F c #2D4050",
+"G c #25405B",
+"H c #25425E",
+"J c #214262",
+"K c #244565",
+"L c #264665",
+"P c #254666",
+"I c #2A4967",
+"U c #284969",
+"Y c #2A4C6C",
+"T c #2C4F6F",
+"R c #33526E",
+"E c #385269",
+"W c #2D5070",
+"Q c #2E5172",
+"! c #335473",
+"~ c #3F5B75",
+"^ c #3D5F7D",
+"/ c #41494F",
+"( c #646056",
+") c #6C685E",
+"_ c #505F6C",
+"` c #48657C",
+"' c #556A7A",
+"] c #5B6C78",
+"[ c #5F6F7B",
+"{ c #5D6F7D",
+"} c #706C62",
+"| c #726D63",
+" . c #78756B",
+".. c #7D786E",
+"X. c #60727F",
+"o. c #807D74",
+"O. c #8A857B",
+"+. c #8B877E",
+"@. c #4E6A83",
+"#. c #4A6A86",
+"$. c #4A7090",
+"%. c #587790",
+"&. c #5F7E95",
+"*. c #587B98",
+"=. c #6F7980",
+"-. c #697F8F",
+";. c #66839B",
+":. c #6A879F",
+">. c #708391",
+",. c #728A9A",
+"<. c #748898",
+"1. c #758A99",
+"2. c #7B8F9F",
+"3. c #708DA4",
+"4. c #7990A1",
+"5. c #7292AB",
+"6. c #7691A8",
+"7. c #7693AB",
+"8. c #7B98AE",
+"9. c #7E98AD",
+"0. c #7E9DB3",
+"q. c #7F9EB4",
+"w. c #8C8981",
+"e. c #989389",
+"r. c #A6A29B",
+"t. c #8093A1",
+"y. c #8598A3",
+"u. c #8498A7",
+"i. c #809AAD",
+"p. c #8F9FAA",
+"a. c #899FAE",
+"s. c #819FB5",
+"d. c #86A2B8",
+"f. c #87A5BB",
+"g. c #88A3B8",
+"h. c #89A5BA",
+"j. c #8FABBF",
+"k. c #97A7B1",
+"l. c #90AABE",
+"z. c #91ABBF",
+"x. c #98ACB9",
+"c. c #AAA7A0",
+"v. c #B1ADA4",
+"b. c #B3B1AA",
+"n. c #B7B3AA",
+"m. c #A3B1BC",
+"M. c #A5B1BC",
+"N. c #A9B6BF",
+"B. c #BEBBB5",
+"V. c #C4C2BD",
+"C. c #94AEC1",
+"Z. c #96AEC1",
+"A. c #94AFC2",
+"S. c #95AFC2",
+"D. c #96B0C3",
+"F. c #98B0C3",
+"G. c #9FB5C3",
+"H. c #99B3C6",
+"J. c #98B3C7",
+"K. c #9AB3C6",
+"L. c #9BB4C7",
+"P. c #9FB8CA",
+"I. c #9FB8CB",
+"U. c #A2B8C9",
+"Y. c #A3B9C9",
+"T. c #A0B9CB",
+"R. c #A3BACB",
+"E. c #A0B9CC",
+"W. c #A2BACC",
+"Q. c #A4BDCE",
+"!. c #A6BECF",
+"~. c #B8BEC2",
+"^. c #B8C3CA",
+"/. c #BCC5CB",
+"(. c #BDC8CE",
+"). c #A8C0D1",
+"_. c #AAC0D0",
+"`. c #ABC1D1",
+"'. c #ACC2D3",
+"]. c #AAC5D7",
+"[. c #B4C8D6",
+"{. c #BDCBD5",
+"}. c #B4C9D8",
+"|. c #B6CAD8",
+" X c #B8CBD9",
+".X c #BBCDDB",
+"XX c #B7D0E0",
+"oX c #BDD3E2",
+"OX c #BCD5E5",
+"+X c #CECAC3",
+"@X c #C5D2C8",
+"#X c #C0D2DE",
+"$X c #C4D3DF",
+"%X c #CCD7DE",
+"&X c #D2D8DC",
+"*X c #E1DFDB",
+"=X c #E2E1DD",
+"-X c #C2D3E0",
+";X c #C2D4E1",
+":X c #C5D5E1",
+">X c #C6D6E1",
+",X c #C4D6E2",
+"<X c #C5D6E3",
+"1X c #C6D7E3",
+"2X c #C3D7E4",
+"3X c #C1D7E6",
+"4X c #C7D8E3",
+"5X c #C5D8E5",
+"6X c #C7D9E5",
+"7X c #CBD9E4",
+"8X c #CBDAE5",
+"9X c #CDDAE4",
+"0X c #CCDBE5",
+"qX c #CFDBE5",
+"wX c #CBDCE7",
+"eX c #C0D9E8",
+"rX c #C2DBEA",
+"tX c #C4DAE8",
+"yX c #D0DEE7",
+"uX c #D1DFE8",
+"iX c #D0DFE9",
+"pX c #D0E0EA",
+"aX c #D1E1EB",
+"sX c #D3E1EA",
+"dX c #D4E1E9",
+"fX c #D4E1EA",
+"gX c #D5E2EA",
+"hX c #D4E2EB",
+"jX c #D6E2EB",
+"kX c #D3E2EC",
+"lX c #D8E3EA",
+"zX c #DFE6EB",
+"xX c #D9E4EC",
+"cX c #D9E5ED",
+"vX c #DAE5ED",
+"bX c #DAE6ED",
+"nX c #DCE7EE",
+"mX c #DBE8EF",
+"MX c #DDE8EF",
+"NX c #DFE8EF",
+"BX c #EAE8E3",
+"VX c #EBEAE6",
+"CX c #ECEBE8",
+"ZX c #E9EEEA",
+"AX c #F0EFEC",
+"SX c #F2F0ED",
+"DX c #E1ECF3",
+"FX c #E4EDF3",
+"GX c #E8EFF4",
+"HX c #F0F3F1",
+"JX c None",
+/* pixels */
+"JXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJX",
+"JXJXJXJXJXJXJXJXu D p t i V w JXJXJXJXJXJXJXJXJX",
+"JXJXJXJXJXJXC X./.&XDXGX%X{.m._ r JXJXJXJXJXJXJX",
+"JXJXJXJXJXi /.DXnXnXFXuX7X$X$XjXM.w JXJXJXJXJXJX",
+"JXJXJXJX/ ^.qXbX1XkX5X5X-X;XsXqXjXN.B JXJXJXJXJX",
+"JXJXJXe (.bXMXDXaXtXtX3XoXbXjXsXyX7Xx.q JXJXJXJX",
+"JXJX7 k.jXbXbX5X3XeXrXOXXX1XsXyXwX$X|.4.3 JXJXJX",
+"JXJXX.:XuXjX'.]._.y. G.sXW.|..X$X[.H.' JXJXJX",
+"JXJXu.$XqXT.H.>. e.o. sXwX}.R.R.`.H.1.- JXJX",
+"JX4 a.9.C.h.] a n.V.BXo. p.!.T.l.4.- JXJX",
+"JX2 F.d.5.7. =XAXc.BXo. @X@XZX !.C.F.@.> JXJX",
+" o.=XAXc.BXo. t.U.z.3.Y $ JXJX",
+"BXBXBXBXVXBXBXAXVXO.CXo. P.C.!.I.J.C.;.L * JXJX",
+"o.o.o.o.o. . .B.b...*X . $.*.T.J.A.h.Y c @ JXJX",
+" .w.r.| +X . 1.C.3.L h JXJX",
+"JXJX6 Q ^ 1.% w.r.| +X . @X@XHX h.:.M , JXJX",
+"JXJXO x T #.] 0 +.} v.) -.s.H 9 O JXJXJX",
+"JXJXJX+ n ! i.X.% % e.( Q Y %.0.&.f O JXJXJX",
+"JXJXJXJX& A s.8.E A % % A K J R ` g @ JXJXJXJX",
+"JXJXJXJXJX@ C ~ m M J N M b v l < O JXJXJXJXJX",
+"JXJXJXJXJXJX : 5 d k z k d 1 & JXJXJXJXJXJX",
+"JXJXJXJXJXJXJXJX JXJXJXJXJXJXJXJX",
+"JXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJX",
+"JXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJXJX"
+};
diff --git a/lisp/xwidget.el b/lisp/xwidget.el
index 8c593abea8..4046140895 100644
--- a/lisp/xwidget.el
+++ b/lisp/xwidget.el
@@ -35,8 +35,9 @@
(require 'bookmark)
(declare-function make-xwidget "xwidget.c"
- (type title width height arguments &optional buffer))
+ (type title width height arguments &optional buffer related))
(declare-function xwidget-buffer "xwidget.c" (xwidget))
+(declare-function set-xwidget-buffer "xwidget.c" (xwidget buffer))
(declare-function xwidget-size-request "xwidget.c" (xwidget))
(declare-function xwidget-resize "xwidget.c" (xwidget new-width new-height))
(declare-function xwidget-webkit-execute-script "xwidget.c"
@@ -58,14 +59,14 @@ xwidget
"Displaying native widgets in Emacs buffers."
:group 'widgets)
-(defun xwidget-insert (pos type title width height &optional args)
+(defun xwidget-insert (pos type title width height &optional args related)
"Insert an xwidget at position POS.
-Supply the xwidget's TYPE, TITLE, WIDTH, and HEIGHT.
+Supply the xwidget's TYPE, TITLE, WIDTH, HEIGHT, and RELATED.
See `make-xwidget' for the possible TYPE values.
The usage of optional argument ARGS depends on the xwidget.
This returns the result of `make-xwidget'."
(goto-char pos)
- (let ((id (make-xwidget type title width height args)))
+ (let ((id (make-xwidget type title width height args nil related)))
(put-text-property (point) (+ 1 (point))
'display (list 'xwidget ':xwidget id))
id))
@@ -88,6 +89,9 @@ xwidget-at
(require 'seq)
(require 'url-handlers)
+(defvar-local xwidget-webkit--title ""
+ "The title of the WebKit widget, used for the header line.")
+
;;;###autoload
(defun xwidget-webkit-browse-url (url &optional new-session)
"Ask xwidget-webkit to browse URL.
@@ -124,6 +128,14 @@ xwidget-webkit-clone-and-split-right
(with-selected-window (split-window-right)
(xwidget-webkit-new-session url))))
+(declare-function xwidget-perform-lispy-event "xwidget.c")
+
+(defun xwidget-webkit-pass-command-event ()
+ "Pass `last-command-event' to the current buffer's WebKit widget."
+ (interactive)
+ (xwidget-perform-lispy-event (xwidget-webkit-current-session)
+ last-command-event))
+
;;todo.
;; - check that the webkit support is compiled in
(defvar xwidget-webkit-mode-map
@@ -138,6 +150,9 @@ xwidget-webkit-mode-map
(define-key map "w" 'xwidget-webkit-current-url)
(define-key map "+" 'xwidget-webkit-zoom-in)
(define-key map "-" 'xwidget-webkit-zoom-out)
+ (define-key map "e" 'xwidget-webkit-edit-mode)
+ (define-key map "\C-r" 'xwidget-webkit-isearch-mode)
+ (define-key map "\C-s" 'xwidget-webkit-isearch-mode)
;;similar to image mode bindings
(define-key map (kbd "SPC") 'xwidget-webkit-scroll-up)
@@ -164,6 +179,63 @@ xwidget-webkit-mode-map
map)
"Keymap for `xwidget-webkit-mode'.")
+(easy-menu-define nil xwidget-webkit-mode-map "Xwidget WebKit menu."
+ (list "Xwidget WebKit"
+ ["Browse URL" xwidget-webkit-browse-url
+ :active t
+ :help "Prompt for a URL, then instruct WebKit to browse it"]
+ ["Back" xwidget-webkit-back t]
+ ["Forward" xwidget-webkit-forward t]
+ ["Reload" xwidget-webkit-reload t]
+ ["Insert String" xwidget-webkit-insert-string
+ :active t
+ :help "Insert a string into the currently active field"]
+ ["Zoom In" xwidget-webkit-zoom-in t]
+ ["Zoom Out" xwidget-webkit-zoom-out t]
+ ["Edit Mode" xwidget-webkit-edit-mode
+ :active t
+ :style toggle
+ :selected xwidget-webkit-edit-mode
+ :help "Send self inserting characters to the WebKit widget"]
+ ["Save Selection" xwidget-webkit-copy-selection-as-kill
+ :active t
+ :help "Save the browser's selection in the kill ring"]
+ ["Incremental Search" xwidget-webkit-isearch-mode
+ :active (not xwidget-webkit-isearch-mode)
+ :help "Perform incremental search inside the WebKit widget"]))
+
+(defvar xwidget-webkit-tool-bar-map
+ (let ((map (make-sparse-keymap)))
+ (prog1 map
+ (tool-bar-local-item-from-menu 'xwidget-webkit-back
+ "left-arrow"
+ map
+ xwidget-webkit-mode-map)
+ (tool-bar-local-item-from-menu 'xwidget-webkit-forward
+ "right-arrow"
+ map
+ xwidget-webkit-mode-map)
+ (tool-bar-local-item-from-menu 'xwidget-webkit-reload
+ "refresh"
+ map
+ xwidget-webkit-mode-map)
+ (tool-bar-local-item-from-menu 'xwidget-webkit-zoom-in
+ "zoom-in"
+ map
+ xwidget-webkit-mode-map)
+ (tool-bar-local-item-from-menu 'xwidget-webkit-zoom-out
+ "zoom-out"
+ map
+ xwidget-webkit-mode-map)
+ (tool-bar-local-item-from-menu 'xwidget-webkit-browse-url
+ "connect-to-url"
+ map
+ xwidget-webkit-mode-map)
+ (tool-bar-local-item-from-menu 'xwidget-webkit-isearch-mode
+ "search"
+ map
+ xwidget-webkit-mode-map))))
+
(defun xwidget-webkit-zoom-in ()
"Increase webkit view zoom factor."
(interactive nil xwidget-webkit-mode)
@@ -276,6 +348,8 @@ xwidget-webkit-callback
(with-current-buffer (xwidget-buffer xwidget)
(cond ((eq xwidget-event-type 'load-changed)
(let ((title (xwidget-webkit-title xwidget)))
+ (setq xwidget-webkit--title title)
+ (force-mode-line-update)
(xwidget-log "webkit finished loading: %s" title)
;; Do not adjust webkit size to window here, the selected window
;; can be the mini-buffer window unwantedly.
@@ -309,8 +383,10 @@ bookmark-make-record-function
(define-derived-mode xwidget-webkit-mode special-mode "xwidget-webkit"
"Xwidget webkit view mode."
(setq buffer-read-only t)
+ (setq-local tool-bar-map xwidget-webkit-tool-bar-map)
(setq-local bookmark-make-record-function
#'xwidget-webkit-bookmark-make-record)
+ (setq-local header-line-format 'xwidget-webkit--title)
;; Keep track of [vh]scroll when switching buffers
(image-mode-setup-winprops))
@@ -609,6 +685,7 @@ xwidget-webkit-new-session
(let*
((bufname (generate-new-buffer-name "*xwidget-webkit*"))
(callback (or callback #'xwidget-webkit-callback))
+ (current-session (xwidget-webkit-current-session))
xw)
(setq xwidget-webkit-last-session-buffer (switch-to-buffer
(get-buffer-create bufname)))
@@ -621,11 +698,35 @@ xwidget-webkit-new-session
(setq xw (xwidget-insert
start 'webkit bufname
(xwidget-window-inside-pixel-width (selected-window))
- (xwidget-window-inside-pixel-height (selected-window)))))
+ (xwidget-window-inside-pixel-height (selected-window))
+ nil current-session)))
(xwidget-put xw 'callback callback)
(xwidget-webkit-mode)
(xwidget-webkit-goto-uri (xwidget-webkit-last-session) url)))
+(defun xwidget-webkit-import-widget (xwidget)
+ "Create a new webkit session buffer from XWIDGET, an existing xwidget.
+Return the buffer."
+ (let* ((bufname (generate-new-buffer-name "*xwidget-webkit*"))
+ (callback #'xwidget-webkit-callback)
+ (buffer (get-buffer-create bufname)))
+ (with-current-buffer buffer
+ (save-excursion
+ (erase-buffer)
+ (insert ".")
+ (put-text-property (point-min) (point-max)
+ 'display (list 'xwidget :xwidget xwidget)))
+ (xwidget-put xwidget 'callback callback)
+ (set-xwidget-buffer xwidget buffer)
+ (xwidget-webkit-mode))
+ buffer))
+
+(defun xwidget-webkit-display-event (event)
+ "Import the xwidget inside EVENT and display it."
+ (interactive "e")
+ (display-buffer (xwidget-webkit-import-widget (nth 1 event))))
+
+(global-set-key [xwidget-display-event] 'xwidget-webkit-display-event)
(defun xwidget-webkit-goto-url (url)
"Goto URL with xwidget webkit."
@@ -684,6 +785,165 @@ xwidget-put
(set-xwidget-plist xwidget
(plist-put (xwidget-plist xwidget) propname value)))
+(defvar xwidget-webkit-edit-mode-map (make-keymap))
+
+(define-key xwidget-webkit-edit-mode-map [backspace] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [tab] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [left] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [right] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [up] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [down] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [return] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [C-left] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [C-right] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [C-up] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [C-down] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [C-return] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [S-left] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [S-right] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [S-up] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [S-down] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [S-return] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [M-left] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [M-right] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [M-up] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [M-down] 'xwidget-webkit-pass-command-event)
+(define-key xwidget-webkit-edit-mode-map [M-return] 'xwidget-webkit-pass-command-event)
+
+(define-minor-mode xwidget-webkit-edit-mode
+ "Minor mode for editing the content of WebKit buffers.
+
+This defines most self-inserting characters and some common
+keyboard shortcuts to `xwidget-webkit-pass-command-event', which
+will pass the key events corresponding to these characters to the
+WebKit widget."
+ :keymap xwidget-webkit-edit-mode-map)
+
+(substitute-key-definition 'self-insert-command
+ 'xwidget-webkit-pass-command-event
+ xwidget-webkit-edit-mode-map
+ global-map)
+
+(declare-function xwidget-webkit-search "xwidget.c")
+(declare-function xwidget-webkit-next-result "xwidget.c")
+(declare-function xwidget-webkit-previous-result "xwidget.c")
+(declare-function xwidget-webkit-finish-search "xwidget.c")
+
+(defvar-local xwidget-webkit-isearch--string ""
+ "The current search query.")
+(defvar-local xwidget-webkit-isearch--is-reverse nil
+ "Whether or not the current isearch should be reverse.")
+
+(defun xwidget-webkit-isearch--update (&optional only-message)
+ "Update the current buffer's WebKit widget's search query.
+If ONLY-MESSAGE is non-nil, the query will not be sent to the
+WebKit widget. The query will be set to the contents of
+`xwidget-webkit-isearch--string'."
+ (unless only-message
+ (xwidget-webkit-search xwidget-webkit-isearch--string
+ (xwidget-webkit-current-session)
+ t xwidget-webkit-isearch--is-reverse t))
+ (message (concat (propertize "Search contents: " 'face 'minibuffer-prompt)
+ xwidget-webkit-isearch--string)))
+
+(defun xwidget-webkit-isearch-erasing-char (count)
+ "Erase the last COUNT characters of the current query."
+ (interactive (list (prefix-numeric-value current-prefix-arg)))
+ (when (> (length xwidget-webkit-isearch--string) 0)
+ (setq xwidget-webkit-isearch--string
+ (substring xwidget-webkit-isearch--string 0
+ (- (length xwidget-webkit-isearch--string) count))))
+ (xwidget-webkit-isearch--update))
+
+(defun xwidget-webkit-isearch-printing-char (char &optional count)
+ "Add ordinary character CHAR to the search string and search.
+With argument, add COUNT copies of CHAR."
+ (interactive (list last-command-event
+ (prefix-numeric-value current-prefix-arg)))
+ (setq xwidget-webkit-isearch--string (concat xwidget-webkit-isearch--string
+ (make-string (or count 1) char)))
+ (xwidget-webkit-isearch--update))
+
+(defun xwidget-webkit-isearch-forward (count)
+ "Move to the next search result COUNT times."
+ (interactive (list (prefix-numeric-value current-prefix-arg)))
+ (let ((was-reverse xwidget-webkit-isearch--is-reverse))
+ (setq xwidget-webkit-isearch--is-reverse nil)
+ (when was-reverse
+ (xwidget-webkit-isearch--update)))
+ (let ((i 0))
+ (while (< i count)
+ (xwidget-webkit-next-result (xwidget-webkit-current-session))
+ (cl-incf i)))
+ (xwidget-webkit-isearch--update t))
+
+(defun xwidget-webkit-isearch-backward (count)
+ "Move to the previous search result COUNT times."
+ (interactive (list (prefix-numeric-value current-prefix-arg)))
+ (let ((was-reverse xwidget-webkit-isearch--is-reverse))
+ (setq xwidget-webkit-isearch--is-reverse t)
+ (unless was-reverse
+ (xwidget-webkit-isearch--update)))
+ (let ((i 0))
+ (while (< i count)
+ (xwidget-webkit-next-result (xwidget-webkit-current-session))
+ (cl-incf i)))
+ (xwidget-webkit-isearch--update t))
+
+(defun xwidget-webkit-isearch-exit ()
+ "Exit incremental search of a WebKit buffer."
+ (interactive)
+ (xwidget-webkit-isearch-mode 0))
+
+(defvar xwidget-webkit-isearch-mode-map (make-keymap)
+ "The keymap used inside xwidget-webkit-isearch-mode.")
+
+(set-char-table-range (nth 1 xwidget-webkit-isearch-mode-map)
+ (cons 0 (max-char))
+ 'xwidget-webkit-isearch-exit)
+
+(substitute-key-definition 'self-insert-command
+ 'xwidget-webkit-isearch-printing-char
+ xwidget-webkit-isearch-mode-map
+ global-map)
+
+(define-key xwidget-webkit-isearch-mode-map (kbd "DEL")
+ 'xwidget-webkit-isearch-erasing-char)
+(define-key xwidget-webkit-isearch-mode-map [return] 'xwidget-webkit-isearch-exit)
+(define-key xwidget-webkit-isearch-mode-map "\r" 'xwidget-webkit-isearch-exit)
+(define-key xwidget-webkit-isearch-mode-map "\C-g" 'xwidget-webkit-isearch-exit)
+(define-key xwidget-webkit-isearch-mode-map "\C-r" 'xwidget-webkit-isearch-backward)
+(define-key xwidget-webkit-isearch-mode-map "\C-s" 'xwidget-webkit-isearch-forward)
+(define-key xwidget-webkit-isearch-mode-map "\t" 'xwidget-webkit-isearch-printing-char)
+
+(let ((meta-map (make-keymap)))
+ (set-char-table-range (nth 1 meta-map)
+ (cons 0 (max-char))
+ 'xwidget-webkit-isearch-exit)
+ (define-key xwidget-webkit-isearch-mode-map (char-to-string meta-prefix-char) meta-map))
+
+(define-minor-mode xwidget-webkit-isearch-mode
+ "Minor mode for performing incremental search inside WebKit buffers.
+
+An attempt was made for this to resemble regular incremental
+search, but it suffers from several limitations, such as not
+supporting recursive edits.
+
+If this mode is enabled with `C-r', then the search will default
+to being performed in reverse direction.
+
+To navigate around the search results, type
+\\[xwidget-webkit-isearch-forward] to move forward, and
+\\[xwidget-webkit-isearch-backward] to move backward.
+
+Press \\[xwidget-webkit-isearch-exit] to exit incremental search."
+ :keymap xwidget-webkit-isearch-mode-map
+ (if xwidget-webkit-isearch-mode
+ (progn
+ (setq xwidget-webkit-isearch--string "")
+ (setq xwidget-webkit-isearch--is-reverse (eq last-command-event ?\C-r))
+ (xwidget-webkit-isearch--update))
+ (xwidget-webkit-finish-search (xwidget-webkit-current-session))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
diff --git a/src/dispextern.h b/src/dispextern.h
index 5b28fe7666..f17f095e0d 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 4a73244c89..632eec2f03 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -4449,16 +4449,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/keyboard.c b/src/keyboard.c
index aa6a4b9e97..c4a5671b10 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -3993,6 +3993,7 @@ kbd_buffer_get_event (KBOARD **kbp,
#endif
#ifdef HAVE_XWIDGETS
case XWIDGET_EVENT:
+ case XWIDGET_DISPLAY_EVENT:
#endif
case SAVE_SESSION_EVENT:
case NO_EVENT:
@@ -4897,7 +4898,7 @@ #define FUNCTION_KEY_OFFSET 0xff00
/* You'll notice that this table is arranged to be conveniently
indexed by X Windows keysym values. */
-static const char *const lispy_function_keys[] =
+const char *const lispy_function_keys[] =
{
/* X Keysym value */
@@ -6139,6 +6140,11 @@ make_lispy_event (struct input_event *event)
{
return Fcons (Qxwidget_event, event->arg);
}
+
+ case XWIDGET_DISPLAY_EVENT:
+ {
+ return list2 (Qxwidget_display_event, event->arg);
+ }
#endif
#ifdef USE_FILE_NOTIFY
@@ -11732,6 +11738,7 @@ syms_of_keyboard (void)
#ifdef HAVE_XWIDGETS
DEFSYM (Qxwidget_event, "xwidget-event");
+ DEFSYM (Qxwidget_display_event, "xwidget-display-event");
#endif
#ifdef USE_FILE_NOTIFY
diff --git a/src/keyboard.h b/src/keyboard.h
index 8bdffaa2bf..21c51ec386 100644
--- a/src/keyboard.h
+++ b/src/keyboard.h
@@ -491,7 +491,7 @@ kbd_buffer_store_event_hold (struct input_event *event,
extern struct timespec timer_check (void);
extern void mark_kboards (void);
-#ifdef HAVE_NTGUI
+#if defined HAVE_NTGUI || defined HAVE_X_WINDOWS
extern const char *const lispy_function_keys[];
#endif
diff --git a/src/print.c b/src/print.c
index c13294c8e6..adadb289de 100644
--- a/src/print.c
+++ b/src/print.c
@@ -1521,8 +1521,26 @@ print_vectorlike (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag,
printchar ('>', printcharfun);
break;
- case PVEC_XWIDGET: case PVEC_XWIDGET_VIEW:
- print_c_string ("#<xwidget ", printcharfun);
+ case PVEC_XWIDGET:
+#ifdef HAVE_XWIDGETS
+ {
+#ifdef USE_GTK
+ int len = sprintf (buf, "#<xwidget %u %p>",
+ XXWIDGET (obj)->xwidget_id,
+ XXWIDGET (obj)->widget_osr);
+#else
+ int len = sprintf (buf, "#<xwidget %u %p>",
+ XXWIDGET (obj)->xwidget_id,
+ XXWIDGET (obj)->xwWidget);
+#endif
+ strout (buf, len, len, printcharfun);
+ break;
+ }
+#else
+ emacs_abort ();
+#endif
+ case PVEC_XWIDGET_VIEW:
+ print_c_string ("#<xwidget view", printcharfun);
printchar ('>', printcharfun);
break;
diff --git a/src/termhooks.h b/src/termhooks.h
index 1d3cdc8fe8..e7539bbce2 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -255,6 +255,8 @@ #define EMACS_TERMHOOKS_H
#ifdef HAVE_XWIDGETS
/* events generated by xwidgets*/
, XWIDGET_EVENT
+ /* Event generated when WebKit asks us to display another widget. */
+ , XWIDGET_DISPLAY_EVENT
#endif
#ifdef USE_FILE_NOTIFY
diff --git a/src/xdisp.c b/src/xdisp.c
index 86c4e704d5..d7ad548917 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -28429,7 +28429,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.
@@ -29832,7 +29832,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 aa1a1a5eed..9b434bffcc 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -4390,6 +4390,86 @@ 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;
+
+ Emacs_Rectangle r1, r2, result;
+ r1.x = w->pixel_left;
+ r1.y = from_y;
+ r1.width = w->pixel_width;
+ r1.height = height;
+ 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))
{
@@ -4563,8 +4643,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 +4656,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);
@@ -4997,7 +5085,7 @@ x_x_to_emacs_modifiers (struct x_display_info *dpyinfo, int state)
| ((state & dpyinfo->hyper_mod_mask) ? mod_hyper : 0));
}
-static int
+int
x_emacs_to_x_modifiers (struct x_display_info *dpyinfo, intmax_t state)
{
EMACS_INT mod_ctrl = ctrl_modifier;
@@ -8211,6 +8299,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))
@@ -8791,6 +8891,31 @@ handle_one_xevent (struct x_display_info *dpyinfo,
x_display_set_last_user_time (dpyinfo, event->xcrossing.time);
x_detect_focus_change (dpyinfo, any, event, &inev.ie);
+#ifdef HAVE_XWIDGETS
+ {
+ struct xwidget_view *xvw = xwidget_view_from_window (event->xcrossing.window);
+ Mouse_HLInfo *hlinfo;
+
+ if (xvw)
+ {
+ xwidget_motion_or_crossing (xvw, event);
+ hlinfo = MOUSE_HL_INFO (xvw->frame);
+
+ if (xvw->frame == hlinfo->mouse_face_mouse_frame)
+ {
+ clear_mouse_face (hlinfo);
+ hlinfo->mouse_face_mouse_frame = 0;
+ }
+
+ if (any_help_event_p)
+ {
+ do_help = -1;
+ }
+ goto OTHER;
+ }
+ }
+#endif
+
f = any;
if (f && x_mouse_click_focus_ignore_position)
@@ -8834,6 +8959,17 @@ handle_one_xevent (struct x_display_info *dpyinfo,
goto OTHER;
case LeaveNotify:
+#ifdef HAVE_XWIDGETS
+ {
+ struct xwidget_view *xvw = xwidget_view_from_window (event->xcrossing.window);
+
+ if (xvw)
+ {
+ xwidget_motion_or_crossing (xvw, event);
+ goto OTHER;
+ }
+ }
+#endif
x_display_set_last_user_time (dpyinfo, event->xcrossing.time);
x_detect_focus_change (dpyinfo, any, event, &inev.ie);
@@ -8883,6 +9019,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
#ifdef USE_GTK
if (f && xg_event_is_for_scrollbar (f, event))
f = 0;
+#endif
+#ifdef HAVE_XWIDGETS
+ struct xwidget_view *xvw = xwidget_view_from_window (event->xmotion.window);
+
+ if (xvw)
+ xwidget_motion_or_crossing (xvw, event);
#endif
if (f)
{
@@ -9138,6 +9280,24 @@ handle_one_xevent (struct x_display_info *dpyinfo,
case ButtonRelease:
case ButtonPress:
{
+#ifdef HAVE_XWIDGETS
+ struct xwidget_view *xvw = xwidget_view_from_window (event->xmotion.window);
+
+ if (xvw)
+ {
+ xwidget_button (xvw, event->type == ButtonPress,
+ event->xbutton.x, event->xbutton.y,
+ event->xbutton.button, event->xbutton.state,
+ event->xbutton.time);
+
+ if (!EQ (selected_window, xvw->w))
+ {
+ inev.ie.kind = SELECT_WINDOW_EVENT;
+ inev.ie.frame_or_window = xvw->w;
+ }
+ goto OTHER;
+ }
+#endif
/* If we decide we want to generate an event to be seen
by the rest of Emacs, we put it here. */
Lisp_Object tab_bar_arg = Qnil;
@@ -12108,6 +12268,10 @@ x_free_frame_resources (struct frame *f)
xfree (f->shell_position);
#else /* !USE_X_TOOLKIT */
+#ifdef HAVE_XWIDGETS
+ kill_frame_xwidget_views (f);
+#endif
+
#ifdef USE_GTK
xg_free_frame_widgets (f);
#endif /* USE_GTK */
diff --git a/src/xterm.h b/src/xterm.h
index de6ea50385..9d9534dd62 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -1108,6 +1108,7 @@ #define SELECTION_EVENT_TIME(eventp) \
extern int x_dispatch_event (XEvent *, Display *);
#endif
extern int x_x_to_emacs_modifiers (struct x_display_info *, int);
+extern int x_emacs_to_x_modifiers (struct x_display_info *, intmax_t);
#ifdef USE_CAIRO
extern void x_cr_destroy_frame_context (struct frame *);
extern void x_cr_update_surface_desired_size (struct frame *, int, int);
diff --git a/src/xwidget.c b/src/xwidget.c
index e4b42e6e0c..78a3860490 100644
--- a/src/xwidget.c
+++ b/src/xwidget.c
@@ -19,6 +19,7 @@ Copyright (C) 2011-2021 Free Software Foundation, Inc.
#include <config.h>
+#include "buffer.h"
#include "xwidget.h"
#include "lisp.h"
@@ -35,10 +36,22 @@ Copyright (C) 2011-2021 Free Software Foundation, Inc.
#ifdef USE_GTK
#include <webkit2/webkit2.h>
#include <JavaScriptCore/JavaScript.h>
+#include <cairo.h>
+#include <X11/Xlib.h>
#elif defined NS_IMPL_COCOA
#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;
+static gboolean offscreen_damage_event (GtkWidget *, GdkEvent *, gpointer);
+static void synthesize_focus_in_event (GtkWidget *);
+static GdkDevice *find_suitable_keyboard (struct frame *);
+#endif
+
static struct xwidget *
allocate_xwidget (void)
{
@@ -64,18 +77,32 @@ #define XSETXWIDGET_VIEW(a, b) XSETPSEUDOVECTOR (a, b, PVEC_XWIDGET_VIEW)
GAsyncResult *,
gpointer);
static gboolean webkit_download_cb (WebKitWebContext *, WebKitDownload *, gpointer);
-
+static GtkWidget *webkit_create_cb (WebKitWebView *, WebKitNavigationAction *, gpointer);
static gboolean
webkit_decide_policy_cb (WebKitWebView *,
WebKitPolicyDecision *,
WebKitPolicyDecisionType,
gpointer);
+static GtkWidget *find_widget_at_pos (GtkWidget *, int, int, int *, int *);
+
+struct widget_search_data
+{
+ int x;
+ int y;
+ bool foundp;
+ bool first;
+ GtkWidget *data;
+};
+
+static void find_widget (GtkWidget *t, struct widget_search_data *);
+static void mouse_target_changed (WebKitWebView *, WebKitHitTestResult *, guint,
+ gpointer);
#endif
DEFUN ("make-xwidget",
Fmake_xwidget, Smake_xwidget,
- 5, 6, 0,
+ 5, 7, 0,
doc: /* Make an xwidget of TYPE.
If BUFFER is nil, use the current buffer.
If BUFFER is a string and no such buffer exists, create it.
@@ -83,10 +110,13 @@ DEFUN ("make-xwidget",
- webkit
-Returns the newly constructed xwidget, or nil if construction fails. */)
+RELATED is nil, or an xwidget. When constructing a WebKit widget, it
+will share the same settings and internal subprocess as RELATED.
+Returns the newly constructed xwidget, or nil if construction
+fails. */)
(Lisp_Object type,
Lisp_Object title, Lisp_Object width, Lisp_Object height,
- Lisp_Object arguments, Lisp_Object buffer)
+ Lisp_Object arguments, Lisp_Object buffer, Lisp_Object related)
{
#ifdef USE_GTK
if (!xg_gtk_initialized)
@@ -108,13 +138,19 @@ DEFUN ("make-xwidget",
XSETXWIDGET (val, xw);
Vxwidget_list = Fcons (val, Vxwidget_list);
xw->plist = Qnil;
+ xw->xwidget_id = ++xwidget_counter;
+ xw->find_text = NULL;
+
+ Fputhash (make_fixnum (xw->xwidget_id), val, id_to_xwidget_map);
#ifdef USE_GTK
xw->widgetwindow_osr = NULL;
xw->widget_osr = NULL;
+ xw->hit_result = 0;
if (EQ (xw->type, Qwebkit))
{
block_input ();
+ WebKitSettings *settings;
WebKitWebContext *webkit_context = webkit_web_context_get_default ();
# if WEBKIT_CHECK_VERSION (2, 26, 0)
@@ -128,18 +164,34 @@ DEFUN ("make-xwidget",
if (EQ (xw->type, Qwebkit))
{
- xw->widget_osr = webkit_web_view_new ();
-
- /* webkitgtk uses GSubprocess which sets sigaction causing
- Emacs to not catch SIGCHLD with its usual handle setup in
- catch_child_signal(). This resets the SIGCHLD
- sigaction. */
- struct sigaction old_action;
- sigaction (SIGCHLD, NULL, &old_action);
- webkit_web_view_load_uri(WEBKIT_WEB_VIEW (xw->widget_osr),
- "about:blank");
- sigaction (SIGCHLD, &old_action, NULL);
- }
+ WebKitWebView *related_view;
+
+ if (NILP (related)
+ || !XWIDGETP (related)
+ || !EQ (XXWIDGET (related)->type, Qwebkit))
+ {
+ xw->widget_osr = webkit_web_view_new ();
+
+ /* webkitgtk uses GSubprocess which sets sigaction causing
+ Emacs to not catch SIGCHLD with its usual handle setup in
+ catch_child_signal(). This resets the SIGCHLD
+ sigaction. */
+ struct sigaction old_action;
+ sigaction (SIGCHLD, NULL, &old_action);
+ webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr),
+ "about:blank");
+ sigaction (SIGCHLD, &old_action, NULL);
+ }
+ else
+ {
+ related_view = WEBKIT_WEB_VIEW (XXWIDGET (related)->widget_osr);
+ xw->widget_osr = webkit_web_view_new_with_related_view (related_view);
+ }
+
+ /* Enable the developer extras */
+ settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (xw->widget_osr));
+ g_object_set (G_OBJECT (settings), "enable-developer-extras", TRUE, NULL);
+ }
gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width,
xw->height);
@@ -157,6 +209,7 @@ DEFUN ("make-xwidget",
gtk_widget_show (xw->widget_osr);
gtk_widget_show (xw->widgetwindow_osr);
+ synthesize_focus_in_event (xw->widgetwindow_osr);
/* Store some xwidget data in the gtk widgets for convenient
retrieval in the event handlers. */
@@ -179,8 +232,20 @@ DEFUN ("make-xwidget",
G_CALLBACK
(webkit_decide_policy_cb),
xw);
+
+ g_signal_connect (G_OBJECT (xw->widget_osr),
+ "mouse-target-changed",
+ G_CALLBACK (mouse_target_changed),
+ xw);
+ g_signal_connect (G_OBJECT (xw->widget_osr),
+ "create",
+ G_CALLBACK (webkit_create_cb),
+ xw);
}
+ g_signal_connect (G_OBJECT (xw->widgetwindow_osr), "damage-event",
+ G_CALLBACK (offscreen_damage_event), xw);
+
unblock_input ();
}
#elif defined NS_IMPL_COCOA
@@ -190,6 +255,158 @@ DEFUN ("make-xwidget",
return val;
}
+#ifdef USE_GTK
+static void
+set_widget_if_text_view (GtkWidget *widget, void *data)
+{
+ GtkWidget **pointer = data;
+
+ if (GTK_IS_TEXT_VIEW (widget))
+ {
+ *pointer = widget;
+ }
+}
+#endif
+
+DEFUN ("xwidget-perform-lispy-event",
+ Fxwidget_perform_lispy_event, Sxwidget_perform_lispy_event,
+ 2, 3, 0, doc: /* Send a lispy event to XWIDGET.
+EVENT should be the event that will be sent. FRAME should be the
+frame which generated the event, or nil. On X11, modifier keys will
+not be processed if FRAME is nil and the selected frame is not an
+X-Windows frame. */)
+ (Lisp_Object xwidget, Lisp_Object event, Lisp_Object frame)
+{
+ struct xwidget *xw;
+ struct frame *f = NULL;
+ int character = -1, keycode = -1;
+ int modifiers = 0;
+
+#ifdef USE_GTK
+ GdkEvent *xg_event;
+ GtkContainerClass *klass;
+ GtkWidget *widget;
+ GtkWidget *temp = NULL;
+#endif
+
+ CHECK_XWIDGET (xwidget);
+ xw = XXWIDGET (xwidget);
+
+ if (!NILP (frame))
+ f = decode_window_system_frame (frame);
+ else if (FRAME_X_P (SELECTED_FRAME ()))
+ f = SELECTED_FRAME ();
+
+#ifdef USE_GTK
+ widget = gtk_window_get_focus (GTK_WINDOW (xw->widgetwindow_osr));
+
+ if (!widget)
+ widget = xw->widget_osr;
+
+ if (RANGED_FIXNUMP (0, event, INT_MAX))
+ {
+ character = XFIXNUM (event);
+
+ if (character < 32)
+ modifiers |= ctrl_modifier;
+
+ modifiers |= character & meta_modifier;
+ modifiers |= character & hyper_modifier;
+ modifiers |= character & super_modifier;
+ modifiers |= character & shift_modifier;
+ modifiers |= character & ctrl_modifier;
+
+ character = character & ~(1 << 21);
+
+ if (character < 32)
+ character += '_';
+
+ if (f)
+ modifiers = x_emacs_to_x_modifiers (FRAME_DISPLAY_INFO (f), modifiers);
+ else
+ modifiers = 0;
+ }
+ else if (SYMBOLP (event))
+ {
+ Lisp_Object decoded = parse_modifiers (event);
+ Lisp_Object decoded_name = SYMBOL_NAME (XCAR (decoded));
+
+ int off = 0;
+ bool found = false;
+
+ while (off < 256)
+ {
+ if (lispy_function_keys[off]
+ && !strcmp (lispy_function_keys[off],
+ SSDATA (decoded_name)))
+ {
+ found = true;
+ break;
+ }
+ ++off;
+ }
+
+ if (f)
+ modifiers = x_emacs_to_x_modifiers (FRAME_DISPLAY_INFO (f),
+ XFIXNUM (XCAR (XCDR (decoded))));
+ else
+ modifiers = 0;
+
+ if (found)
+ keycode = off + 0xff00;
+ }
+
+ if (character == -1 && keycode == -1)
+ return Qnil;
+
+ block_input ();
+ xg_event = gdk_event_new (GDK_KEY_PRESS);
+ xg_event->any.window = gtk_widget_get_window (xw->widget_osr);
+ g_object_ref (xg_event->any.window);
+
+ if (character > -1)
+ keycode = gdk_unicode_to_keyval (character);
+
+ xg_event->key.keyval = keycode;
+ xg_event->key.state = modifiers;
+
+ if (keycode > -1)
+ {
+ /* WebKitGTK internals abuse follows. */
+ if (WEBKIT_IS_WEB_VIEW (widget))
+ {
+ /* WebKitGTK relies on an internal GtkTextView object to
+ "translate" keys such as backspace. We must find that
+ widget and activate its binding to this key if any. */
+ klass = GTK_CONTAINER_CLASS (G_OBJECT_GET_CLASS (widget));
+
+ klass->forall (GTK_CONTAINER (xw->widget_osr), TRUE,
+ set_widget_if_text_view, &temp);
+
+ if (GTK_IS_WIDGET (temp))
+ {
+ if (!gtk_widget_get_realized (temp))
+ gtk_widget_realize (temp);
+
+ gtk_bindings_activate (G_OBJECT (temp), keycode, modifiers);
+ }
+ }
+ }
+
+ if (f)
+ gdk_event_set_device (xg_event,
+ find_suitable_keyboard (SELECTED_FRAME ()));
+
+ gtk_main_do_event (xg_event);
+ xg_event->type = GDK_KEY_RELEASE;
+ gtk_main_do_event (xg_event);
+ gdk_event_free (xg_event);
+ unblock_input ();
+#endif
+
+ return Qnil;
+}
+
DEFUN ("get-buffer-xwidgets", Fget_buffer_xwidgets, Sget_buffer_xwidgets,
1, 1, 0,
doc: /* Return a list of xwidgets associated with BUFFER.
@@ -221,16 +438,397 @@ 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
+
+static GdkDevice *
+find_suitable_pointer (struct frame *f)
+{
+ GdkSeat *seat = gdk_display_get_default_seat
+ (gtk_widget_get_display (FRAME_GTK_WIDGET (f)));
+
+ if (!seat)
+ return NULL;
+
+ return gdk_seat_get_pointer (seat);
+}
+
+static GdkDevice *
+find_suitable_keyboard (struct frame *f)
+{
+ GdkSeat *seat = gdk_display_get_default_seat
+ (gtk_widget_get_display (FRAME_GTK_WIDGET (f)));
+
+ if (!seat)
+ return NULL;
+
+ return gdk_seat_get_keyboard (seat);
+}
+
+static void
+find_widget_cb (GtkWidget *widget, void *user)
+{
+ find_widget (widget, user);
+}
+
+static void
+find_widget (GtkWidget *widget,
+ struct widget_search_data *data)
+{
+ GtkAllocation new_allocation;
+ GdkWindow *window;
+ int x_offset = 0;
+ int y_offset = 0;
+
+ gtk_widget_get_allocation (widget, &new_allocation);
+
+ if (gtk_widget_get_has_window (widget))
+ {
+ new_allocation.x = 0;
+ new_allocation.y = 0;
+ }
+
+ if (gtk_widget_get_parent (widget) && !data->first)
+ {
+ window = gtk_widget_get_window (widget);
+ while (window != gtk_widget_get_window (gtk_widget_get_parent (widget)))
+ {
+ gint tx, ty, twidth, theight;
+
+ if (!window)
+ return;
+
+ twidth = gdk_window_get_width (window);
+ theight = gdk_window_get_height (window);
+
+ if (new_allocation.x < 0)
+ {
+ new_allocation.width += new_allocation.x;
+ new_allocation.x = 0;
+ }
+
+ if (new_allocation.y < 0)
+ {
+ new_allocation.height += new_allocation.y;
+ new_allocation.y = 0;
+ }
+
+ if (new_allocation.x + new_allocation.width > twidth)
+ new_allocation.width = twidth - new_allocation.x;
+ if (new_allocation.y + new_allocation.height > theight)
+ new_allocation.height = theight - new_allocation.y;
+
+ gdk_window_get_position (window, &tx, &ty);
+ new_allocation.x += tx;
+ x_offset += tx;
+ new_allocation.y += ty;
+ y_offset += ty;
+
+ window = gdk_window_get_parent (window);
+ }
+ }
+
+ if ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
+ (data->x < new_allocation.x + new_allocation.width) &&
+ (data->y < new_allocation.y + new_allocation.height))
+ {
+ /* First, check if the drag is in a valid drop site in
+ * one of our children
+ */
+ if (GTK_IS_CONTAINER (widget))
+ {
+ struct widget_search_data new_data = *data;
+
+ new_data.x -= x_offset;
+ new_data.y -= y_offset;
+ new_data.foundp = false;
+ new_data.first = false;
+
+ gtk_container_forall (GTK_CONTAINER (widget),
+ find_widget_cb, &new_data);
+
+ data->foundp = new_data.foundp;
+ if (data->foundp)
+ data->data = new_data.data;
+ }
+
+ /* If not, and this widget is registered as a drop site, check to
+ * emit "drag_motion" to check if we are actually in
+ * a drop site.
+ */
+ if (!data->foundp)
+ {
+ data->foundp = true;
+ data->data = widget;
+ }
+ }
+}
+
+static GtkWidget *
+find_widget_at_pos (GtkWidget *w, int x, int y,
+ int *new_x, int *new_y)
+{
+ struct widget_search_data data;
+
+ data.x = x;
+ data.y = y;
+ data.foundp = false;
+ data.first = true;
+
+ find_widget (w, &data);
+
+ if (data.foundp)
+ {
+ gtk_widget_translate_coordinates (w, data.data, x,
+ y, new_x, new_y);
+ return data.data;
+ }
+
+ *new_x = x;
+ *new_y = y;
+
+ return NULL;
+}
+
+static Emacs_Cursor
+cursor_for_hit (guint result, struct frame *frame)
+{
+ Emacs_Cursor cursor = FRAME_OUTPUT_DATA (frame)->nontext_cursor;
+
+ if ((result & WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE)
+ || (result & WEBKIT_HIT_TEST_RESULT_CONTEXT_SELECTION)
+ || (result & WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT))
+ cursor = FRAME_X_OUTPUT (frame)->text_cursor;
+
+ if (result & WEBKIT_HIT_TEST_RESULT_CONTEXT_SCROLLBAR)
+ cursor = FRAME_X_OUTPUT (frame)->vertical_drag_cursor;
+
+ if (result & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK)
+ cursor = FRAME_X_OUTPUT (frame)->hand_cursor;
+
+ return cursor;
+}
+
+static void
+define_cursors (struct xwidget *xw, WebKitHitTestResult *res)
+{
+ struct xwidget_view *xvw;
+
+ xw->hit_result = webkit_hit_test_result_get_context (res);
+
+ for (Lisp_Object tem = Vxwidget_view_list; CONSP (tem);
+ tem = XCDR (tem))
+ {
+ if (XWIDGET_VIEW_P (XCAR (tem)))
+ {
+ xvw = XXWIDGET_VIEW (XCAR (tem));
+
+ if (XXWIDGET (xvw->model) == xw)
+ {
+ xvw->cursor = cursor_for_hit (xw->hit_result, xvw->frame);
+ if (xvw->wdesc != None)
+ XDefineCursor (xvw->dpy, xvw->wdesc, xvw->cursor);
+ }
+ }
+ }
+}
+
+static void
+mouse_target_changed (WebKitWebView *webview,
+ WebKitHitTestResult *hitresult,
+ guint modifiers, gpointer xw)
+{
+ define_cursors (xw, hitresult);
+}
+
+
+static void
+xwidget_button_1 (struct xwidget_view *view,
+ bool down_p, int x, int y, int button,
+ int modifier_state, Time time)
+{
+ GdkEvent *xg_event = gdk_event_new (down_p ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE);
+ struct xwidget *model = XXWIDGET (view->model);
+ GtkWidget *target;
+
+ /* X and Y should be relative to the origin of view->wdesc. */
+ x += view->clip_left;
+ y += view->clip_top;
+
+ target = find_widget_at_pos (model->widgetwindow_osr, x, y, &x, &y);
+
+ if (!target)
+ target = model->widget_osr;
+
+ xg_event->any.window = gtk_widget_get_window (target);
+ g_object_ref (xg_event->any.window); /* The window will be unrefed
+ later by gdk_event_free. */
+
+ xg_event->button.x = x;
+ xg_event->button.x_root = x;
+ xg_event->button.y = y;
+ xg_event->button.y_root = y;
+ xg_event->button.button = button;
+ xg_event->button.state = modifier_state;
+ xg_event->button.time = time;
+ xg_event->button.device = find_suitable_pointer (view->frame);
+
+ gtk_main_do_event (xg_event);
+ gdk_event_free (xg_event);
+}
+
+void
+xwidget_button (struct xwidget_view *view,
+ bool down_p, int x, int y, int button,
+ int modifier_state, Time time)
+{
+ if (button < 4 || button > 8)
+ xwidget_button_1 (view, down_p, x, y, button, modifier_state, time);
+ else
+ {
+ GdkEvent *xg_event = gdk_event_new (GDK_SCROLL);
+ struct xwidget *model = XXWIDGET (view->model);
+ GtkWidget *target;
+
+ x += view->clip_left;
+ y += view->clip_top;
+
+ target = find_widget_at_pos (model->widgetwindow_osr, x, y, &x, &y);
+
+ if (!target)
+ target = model->widget_osr;
+
+ xg_event->any.window = gtk_widget_get_window (target);
+ g_object_ref (xg_event->any.window); /* The window will be unrefed
+ later by gdk_event_free. */
+ if (button == 4)
+ xg_event->scroll.direction = GDK_SCROLL_UP;
+ else if (button == 5)
+ xg_event->scroll.direction = GDK_SCROLL_DOWN;
+ else if (button == 6)
+ xg_event->scroll.direction = GDK_SCROLL_LEFT;
+ else
+ xg_event->scroll.direction = GDK_SCROLL_RIGHT;
+
+ xg_event->scroll.device = find_suitable_pointer (view->frame);
+
+ xg_event->scroll.x = x;
+ xg_event->scroll.x_root = x;
+ xg_event->scroll.y = y;
+ xg_event->scroll.y_root = y;
+ xg_event->scroll.state = modifier_state;
+ xg_event->scroll.time = time;
+
+ xg_event->scroll.delta_x = 0;
+ xg_event->scroll.delta_y = 0;
+
+ gtk_main_do_event (xg_event);
+ gdk_event_free (xg_event);
+ }
+}
+
+void
+xwidget_motion_or_crossing (struct xwidget_view *view, const XEvent *event)
+{
+ GdkEvent *xg_event = gdk_event_new (event->type == MotionNotify ? GDK_MOTION_NOTIFY :
+ (event->type == LeaveNotify ? GDK_LEAVE_NOTIFY :
+ GDK_ENTER_NOTIFY));
+ struct xwidget *model = XXWIDGET (view->model);
+ int x;
+ int y;
+ GtkWidget *target = find_widget_at_pos (model->widgetwindow_osr,
+ (event->type == MotionNotify
+ ? event->xmotion.x + view->clip_left
+ : event->xmotion.y + view->clip_top),
+ (event->type == MotionNotify
+ ? event->xmotion.y + view->clip_left
+ : event->xcrossing.y + view->clip_top),
+ &x, &y);
+
+ if (!target)
+ target = model->widgetwindow_osr;
+
+ xg_event->any.window = gtk_widget_get_window (target);
+ g_object_ref (xg_event->any.window); /* The window will be unrefed
+ later by gdk_event_free. */
+
+ if (event->type == MotionNotify)
+ {
+ xg_event->motion.x = x;
+ xg_event->motion.y = y;
+ xg_event->motion.x_root = event->xmotion.x_root;
+ xg_event->motion.y_root = event->xmotion.y_root;
+ xg_event->motion.time = event->xmotion.time;
+ xg_event->motion.state = event->xmotion.state;
+ xg_event->motion.device = find_suitable_pointer (view->frame);
+ }
+ else
+ {
+ xg_event->crossing.detail = min (5, event->xcrossing.detail);
+ xg_event->crossing.time = event->xcrossing.time;
+ xg_event->crossing.x = x;
+ xg_event->crossing.y = y;
+ xg_event->crossing.x_root = event->xcrossing.x_root;
+ xg_event->crossing.y_root = event->xcrossing.y_root;
+ gdk_event_set_device (xg_event, find_suitable_pointer (view->frame));
+ }
+
+ gtk_main_do_event (xg_event);
+ gdk_event_free (xg_event);
+}
+
+static void
+synthesize_focus_in_event (GtkWidget *offscreen_window)
+{
+ GdkWindow *wnd;
+ GdkEvent *focus_event;
+
+ if (!gtk_widget_get_realized (offscreen_window))
+ gtk_widget_realize (offscreen_window);
+
+ wnd = gtk_widget_get_window (offscreen_window);
+
+ focus_event = gdk_event_new (GDK_FOCUS_CHANGE);
+ focus_event->any.window = wnd;
+ focus_event->focus_change.in = TRUE;
+ g_object_ref (wnd);
+
+ gtk_main_do_event (focus_event);
+ gdk_event_free (focus_event);
+}
+
+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 +836,64 @@ 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)
+{
+ GtkOffscreenWindow *wnd;
+ cairo_surface_t *surface;
+ block_input ();
+ wnd = GTK_OFFSCREEN_WINDOW (w->widgetwindow_osr);
+ surface = gtk_offscreen_window_get_surface (wnd);
+
+ cairo_save (xw->cr_context);
+ if (surface)
+ {
+ cairo_set_source_surface (xw->cr_context, surface, xw->clip_left,
+ xw->clip_top);
+ cairo_set_operator (xw->cr_context, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (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)
-{
- /* 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);
+ gpointer xwidget)
+{
+ block_input ();
+
+ for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
+ tail = XCDR (tail))
+ {
+ if (XWIDGET_VIEW_P (XCAR (tail)))
+ {
+ struct xwidget_view *view = XXWIDGET_VIEW (XCAR (tail));
+
+ if (view->wdesc && XXWIDGET (view->model) == xwidget)
+ xv_do_draw (view, XXWIDGET (view->model));
+ }
+ }
+
+ unblock_input ();
return FALSE;
}
+
+void
+xwidget_expose (struct xwidget_view *xv)
+{
+ struct xwidget *xw = XXWIDGET (xv->model);
+
+ xv_do_draw (xv, xw);
+}
#endif /* USE_GTK */
void
@@ -313,22 +947,108 @@ store_xwidget_js_callback_event (struct xwidget *xw,
#ifdef USE_GTK
+static void
+store_xwidget_display_event (struct xwidget *xw)
+{
+ struct input_event evt;
+ Lisp_Object val;
+
+ XSETXWIDGET (val, xw);
+ EVENT_INIT (evt);
+ evt.kind = XWIDGET_DISPLAY_EVENT;
+ evt.frame_or_window = Qnil;
+ evt.arg = val;
+ kbd_buffer_store_event (&evt);
+}
+
+static void
+webkit_ready_to_show (WebKitWebView *new_view,
+ gpointer user_data)
+{
+ Lisp_Object tem;
+ struct xwidget *xw;
+
+ for (tem = Vxwidget_list; CONSP (tem); tem = XCDR (tem))
+ {
+ if (XWIDGETP (XCAR (tem)))
+ {
+ xw = XXWIDGET (XCAR (tem));
+
+ if (EQ (xw->type, Qwebkit)
+ && WEBKIT_WEB_VIEW (xw->widget_osr) == new_view)
+ store_xwidget_display_event (xw);
+ }
+ }
+}
+
+static GtkWidget *
+webkit_create_cb_1 (WebKitWebView *webview,
+ struct xwidget_view *xv)
+{
+ Lisp_Object related;
+ Lisp_Object xwidget;
+ GtkWidget *widget;
+
+ XSETXWIDGET (related, xv);
+ xwidget = Fmake_xwidget (Qwebkit, Qnil, make_fixnum (0),
+ make_fixnum (0), Qnil,
+ build_string (" *detached xwidget buffer*"),
+ related);
+
+ if (NILP (xwidget))
+ return NULL;
+
+ widget = XXWIDGET (xwidget)->widget_osr;
+
+ g_signal_connect (G_OBJECT (widget), "ready-to-show",
+ G_CALLBACK (webkit_ready_to_show), NULL);
+
+ return widget;
+}
+
+static GtkWidget *
+webkit_create_cb (WebKitWebView *webview,
+ WebKitNavigationAction *nav_action,
+ gpointer user_data)
+{
+ switch (webkit_navigation_action_get_navigation_type (nav_action))
+ {
+ case WEBKIT_NAVIGATION_TYPE_OTHER:
+ return webkit_create_cb_1 (webview, user_data);
+
+ case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD:
+ case WEBKIT_NAVIGATION_TYPE_RELOAD:
+ case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED:
+ case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED:
+ case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED:
+ default:
+ return NULL;
+ }
+}
+
void
webkit_view_load_changed_cb (WebKitWebView *webkitwebview,
WebKitLoadEvent load_event,
gpointer data)
{
- switch (load_event) {
- case WEBKIT_LOAD_FINISHED:
+ struct xwidget *xw = g_object_get_data (G_OBJECT (webkitwebview),
+ XG_XWIDGET);
+
+ switch (load_event)
{
- struct xwidget *xw = g_object_get_data (G_OBJECT (webkitwebview),
- XG_XWIDGET);
- store_xwidget_event_string (xw, "load-changed", "");
+ case WEBKIT_LOAD_FINISHED:
+ store_xwidget_event_string (xw, "load-changed", "load-finished");
+ break;
+ case WEBKIT_LOAD_STARTED:
+ store_xwidget_event_string (xw, "load-changed", "load-started");
+ break;
+ case WEBKIT_LOAD_REDIRECTED:
+ store_xwidget_event_string (xw, "load-changed", "load-redirected");
+ break;
+ case WEBKIT_LOAD_COMMITTED:
+ store_xwidget_event_string (xw, "load-changed", "load-committed");
break;
}
- default:
- break;
- }
}
/* Recursively convert a JavaScript value to a Lisp value. */
@@ -498,51 +1218,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 +1243,19 @@ 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);
+ xv->dpy = FRAME_X_DISPLAY (s->f);
- 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);
- }
-
- /* 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;
+ xv->cursor = cursor_for_hit (xww->hit_result, s->f);
#elif defined NS_IMPL_COCOA
nsxwidget_init_view (xv, xww, s, x, y);
nsxwidget_resize_view(xv, xww->width, xww->height);
@@ -681,6 +1312,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 +1326,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 +1345,51 @@ 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 | ButtonPressMask | ButtonReleaseMask
+ | PointerMotionMask | EnterWindowMask | LeaveWindowMask);
+
+ 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);
+ XDefineCursor (xv->dpy, xv->wdesc, xv->cursor);
+ 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);
+ XMoveResizeWindow (xv->dpy, xv->wdesc, x + clip_left, y + clip_top,
+ 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_move_view (xv, x + clip_left, y + clip_top);
#endif
@@ -735,10 +1405,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 && !moved)
+ {
+ 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 +1432,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);
+ gtk_widget_queue_draw (xww->widget_osr);
#elif defined NS_IMPL_COCOA
nsxwidget_set_needsdisplay (xv);
#endif
}
+
+#ifdef USE_GTK
+ unblock_input ();
+#endif
}
static bool
@@ -975,16 +1652,13 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0,
struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail));
if (XXWIDGET (xv->model) == xw)
{
-#ifdef USE_GTK
- gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xw->width,
- xw->height);
-#elif defined NS_IMPL_COCOA
- nsxwidget_resize_view(xv, xw->width, xw->height);
-#endif
+ wset_redisplay (XWINDOW (xv->w));
}
}
}
+ redisplay ();
+
return Qnil;
}
@@ -1084,13 +1758,15 @@ 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 ();
+ cairo_destroy (xv->cr_context);
+ cairo_surface_destroy (xv->cr_surface);
+ XDestroyWindow (xv->dpy, xv->wdesc);
+ Fremhash (make_fixnum (xv->wdesc), x_window_to_xwv_map);
+ unblock_input ();
+ }
#elif defined NS_IMPL_COCOA
nsxwidget_delete_view (xv);
#endif
@@ -1145,6 +1821,19 @@ DEFUN ("xwidget-buffer",
return XXWIDGET (xwidget)->buffer;
}
+DEFUN ("set-xwidget-buffer",
+ Fset_xwidget_buffer, Sset_xwidget_buffer,
+ 2, 2, 0,
+ doc: /* Set XWIDGET's buffer to BUFFER. */)
+ (Lisp_Object xwidget, Lisp_Object buffer)
+{
+ CHECK_XWIDGET (xwidget);
+ CHECK_BUFFER (buffer);
+
+ XXWIDGET (xwidget)->buffer = buffer;
+ return Qnil;
+}
+
DEFUN ("set-xwidget-plist",
Fset_xwidget_plist, Sset_xwidget_plist,
2, 2, 0,
@@ -1183,6 +1872,166 @@ DEFUN ("xwidget-query-on-exit-flag",
return (XXWIDGET (xwidget)->kill_without_query ? Qnil : Qt);
}
+DEFUN ("xwidget-webkit-search", Fxwidget_webkit_search, Sxwidget_webkit_search,
+ 2, 5, 0,
+ doc: /* Begin an incremental search operation in an xwidget.
+QUERY should be a string containing the text to search for. XWIDGET
+should be a WebKit xwidget where the search will take place. When the
+search operation is complete, callers should also call
+`xwidget-webkit-finish-search' to complete the search operation.
+
+CASE-INSENSITIVE, when non-nil, will cause the search to ignore the
+case of characters inside QUERY. BACKWARDS, when non-nil, will cause
+the search to proceed towards the beginning of the widget's contents.
+WRAP-AROUND, when nil, will cause the search to stop upon hitting the
+end of the widget's contents.
+
+It is OK to call this function even when a search is already in
+progress. In that case, the previous search query will be replaced
+with QUERY. */)
+ (Lisp_Object query, Lisp_Object xwidget, Lisp_Object case_insensitive,
+ Lisp_Object backwards, Lisp_Object wrap_around)
+{
+#ifdef USE_GTK
+ WebKitWebView *webview;
+ WebKitFindController *controller;
+ WebKitFindOptions opt;
+ struct xwidget *xw;
+ gchar *g_query;
+#endif
+
+ CHECK_STRING (query);
+ CHECK_XWIDGET (xwidget);
+
+#ifdef USE_GTK
+ xw = XXWIDGET (xwidget);
+ webview = WEBKIT_WEB_VIEW (xw->widget_osr);
+ query = ENCODE_UTF_8 (query);
+ opt = WEBKIT_FIND_OPTIONS_NONE;
+ g_query = xstrdup (SSDATA (query));
+
+ if (!NILP (case_insensitive))
+ opt |= WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE;
+ if (!NILP (backwards))
+ opt |= WEBKIT_FIND_OPTIONS_BACKWARDS;
+ if (!NILP (wrap_around))
+ opt |= WEBKIT_FIND_OPTIONS_WRAP_AROUND;
+
+ if (xw->find_text)
+ xfree (xw->find_text);
+ xw->find_text = g_query;
+
+ block_input ();
+ controller = webkit_web_view_get_find_controller (webview);
+ webkit_find_controller_search (controller, g_query, opt, G_MAXUINT);
+ unblock_input ();
+#endif
+
+ return Qnil;
+}
+
+DEFUN ("xwidget-webkit-next-result", Fxwidget_webkit_next_result,
+ Sxwidget_webkit_next_result, 1, 1, 0,
+ doc: /* Show the next result matching the current search query.
+
+XWIDGET should be an xwidget that currently has a search query.
+Before calling this function, you should start a search operation
+using `xwidget-webkit-search'. */)
+ (Lisp_Object xwidget)
+{
+ struct xwidget *xw;
+#ifdef USE_GTK
+ WebKitWebView *webview;
+ WebKitFindController *controller;
+#endif
+
+ CHECK_XWIDGET (xwidget);
+ xw = XXWIDGET (xwidget);
+
+ if (!xw->find_text)
+ error ("Widget has no ongoing search operation");
+
+#ifdef USE_GTK
+ block_input ();
+ webview = WEBKIT_WEB_VIEW (xw->widget_osr);
+ controller = webkit_web_view_get_find_controller (webview);
+ webkit_find_controller_search_next (controller);
+ unblock_input ();
+#endif
+
+ return Qnil;
+}
+
+DEFUN ("xwidget-webkit-previous-result", Fxwidget_webkit_previous_result,
+ Sxwidget_webkit_previous_result, 1, 1, 0,
+ doc: /* Show the previous result matching the current search query.
+
+XWIDGET should be an xwidget that currently has a search query.
+Before calling this function, you should start a search operation
+using `xwidget-webkit-search'. */)
+ (Lisp_Object xwidget)
+{
+ struct xwidget *xw;
+#ifdef USE_GTK
+ WebKitWebView *webview;
+ WebKitFindController *controller;
+#endif
+
+ CHECK_XWIDGET (xwidget);
+ xw = XXWIDGET (xwidget);
+
+ if (!xw->find_text)
+ error ("Widget has no ongoing search operation");
+
+#ifdef USE_GTK
+ block_input ();
+ webview = WEBKIT_WEB_VIEW (xw->widget_osr);
+ controller = webkit_web_view_get_find_controller (webview);
+ webkit_find_controller_search_previous (controller);
+
+ if (xw->find_text)
+ {
+ xfree (xw->find_text);
+ xw->find_text = NULL;
+ }
+ unblock_input ();
+#endif
+
+ return Qnil;
+}
+
+DEFUN ("xwidget-webkit-finish-search", Fxwidget_webkit_finish_search,
+ Sxwidget_webkit_finish_search, 1, 1, 0,
+ doc: /* Finish XWIDGET's search operation.
+
+XWIDGET should be an xwidget that currently has a search query.
+Before calling this function, you should start a search operation
+using `xwidget-webkit-search'. */)
+ (Lisp_Object xwidget)
+{
+ struct xwidget *xw;
+#ifdef USE_GTK
+ WebKitWebView *webview;
+ WebKitFindController *controller;
+#endif
+
+ CHECK_XWIDGET (xwidget);
+ xw = XXWIDGET (xwidget);
+
+ if (!xw->find_text)
+ error ("Widget has no ongoing search operation");
+
+#ifdef USE_GTK
+ block_input ();
+ webview = WEBKIT_WEB_VIEW (xw->widget_osr);
+ controller = webkit_web_view_get_find_controller (webview);
+ webkit_find_controller_search_finish (controller);
+ unblock_input ();
+#endif
+
+ return Qnil;
+}
+
void
syms_of_xwidget (void)
{
@@ -1215,6 +2064,12 @@ syms_of_xwidget (void)
defsubr (&Sxwidget_plist);
defsubr (&Sxwidget_buffer);
defsubr (&Sset_xwidget_plist);
+ defsubr (&Sxwidget_perform_lispy_event);
+ defsubr (&Sxwidget_webkit_search);
+ defsubr (&Sxwidget_webkit_finish_search);
+ defsubr (&Sxwidget_webkit_next_result);
+ defsubr (&Sxwidget_webkit_previous_result);
+ defsubr (&Sset_xwidget_buffer);
DEFSYM (QCxwidget, ":xwidget");
DEFSYM (QCtitle, ":title");
@@ -1236,6 +2091,15 @@ syms_of_xwidget (void)
Vxwidget_view_list = Qnil;
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);
+
+ staticpro (&x_window_to_xwv_map);
+#endif
}
@@ -1374,7 +2238,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.
@@ -1424,6 +2288,26 @@ xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix)
}
}
+#ifdef USE_GTK
+void
+kill_frame_xwidget_views (struct frame *f)
+{
+ Lisp_Object rem = Qnil;
+
+ for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
+ tail = XCDR (tail))
+ {
+ if (XXWIDGET_VIEW (XCAR (tail))->frame == f)
+ rem = Fcons (XCAR (tail), rem);
+ }
+
+ for (; CONSP (rem); rem = XCDR (rem))
+ {
+ Fdelete_xwidget_view (XCAR (rem));
+ }
+}
+#endif
+
/* Kill all xwidget in BUFFER. */
void
kill_buffer_xwidgets (Lisp_Object buffer)
@@ -1437,12 +2321,15 @@ 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)
{
gtk_widget_destroy (xw->widget_osr);
gtk_widget_destroy (xw->widgetwindow_osr);
}
+ if (xw->find_text)
+ xfree (xw->find_text);
if (!NILP (xw->script_callbacks))
for (ptrdiff_t idx = 0; idx < ASIZE (xw->script_callbacks); idx++)
{
diff --git a/src/xwidget.h b/src/xwidget.h
index 591f23489d..35b9e8b493 100644
--- a/src/xwidget.h
+++ b/src/xwidget.h
@@ -32,6 +32,8 @@ #define XWIDGET_H_INCLUDED
#if defined (USE_GTK)
#include <gtk/gtk.h>
+#include <X11/Xlib.h>
+#include "xterm.h"
#elif defined (NS_IMPL_COCOA) && defined (__OBJC__)
#import <AppKit/NSView.h>
#import "nsxwidget.h"
@@ -59,11 +61,14 @@ #define XWIDGET_H_INCLUDED
int height;
int width;
+ uint32_t xwidget_id;
+ gchar *find_text;
#if defined (USE_GTK)
/* For offscreen widgets, unused if not osr. */
GtkWidget *widget_osr;
GtkWidget *widgetwindow_osr;
+ guint hit_result;
#elif defined (NS_IMPL_COCOA)
# ifdef __OBJC__
/* For offscreen widgets, unused if not osr. */
@@ -98,9 +103,13 @@ #define XWIDGET_H_INCLUDED
bool hidden;
#if defined (USE_GTK)
- GtkWidget *widget;
- GtkWidget *widgetwindow;
- GtkWidget *emacswindow;
+ Display *dpy;
+ Window wdesc;
+ Emacs_Cursor cursor;
+ struct frame *frame;
+
+ cairo_surface_t *cr_surface;
+ cairo_t *cr_context;
#elif defined (NS_IMPL_COCOA)
# ifdef __OBJC__
XvWindow *xvWindow;
@@ -162,6 +171,18 @@ #define XG_XWIDGET_VIEW "emacs_xwidget_view"
void store_xwidget_js_callback_event (struct xwidget *xw,
Lisp_Object proc,
Lisp_Object argument);
+
+extern struct xwidget *xwidget_from_id (uint32_t id);
+
+#ifdef HAVE_X_WINDOWS
+struct xwidget_view *xwidget_view_from_window (Window wdesc);
+void xwidget_expose (struct xwidget_view *xv);
+extern void kill_frame_xwidget_views (struct frame *f);
+extern void xwidget_button (struct xwidget_view *, bool, int,
+ int, int, int, Time);
+extern void xwidget_motion_or_crossing (struct xwidget_view *,
+ const XEvent *);
+#endif
#else
INLINE_HEADER_BEGIN
INLINE void syms_of_xwidget (void) {}
next prev parent reply other threads:[~2021-11-07 1:26 UTC|newest]
Thread overview: 39+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <87zgqslafe.fsf.ref@yahoo.com>
2021-10-29 4:30 ` bug#51473: [PATCH] Enable xwidget scrolling optimizations, and other xwidgets improvements Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-10-29 13:18 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-04 23:11 ` Lars Ingebrigtsen
2021-11-05 7:20 ` Eli Zaretskii
2021-11-05 7:29 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-05 7:33 ` Lars Ingebrigtsen
2021-11-05 7:47 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-05 13:35 ` Lars Ingebrigtsen
2021-11-05 7:34 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-05 13:10 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-05 13:32 ` Lars Ingebrigtsen
2021-11-05 13:28 ` Lars Ingebrigtsen
2021-11-05 13:40 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-05 13:48 ` Lars Ingebrigtsen
2021-11-06 0:00 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-06 0:22 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-06 0:25 ` Lars Ingebrigtsen
2021-11-06 2:06 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-06 5:39 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-06 8:58 ` Eli Zaretskii
2021-11-06 10:41 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-06 17:42 ` Lars Ingebrigtsen
2021-11-07 0:41 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-07 0:55 ` Lars Ingebrigtsen
2021-11-07 1:16 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-07 1:04 ` Lars Ingebrigtsen
2021-11-07 1:18 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-07 1:23 ` Lars Ingebrigtsen
2021-11-07 1:26 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors [this message]
2021-11-07 1:29 ` Lars Ingebrigtsen
2021-11-07 1:34 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-07 1:39 ` Lars Ingebrigtsen
2021-11-07 1:55 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-07 2:04 ` Lars Ingebrigtsen
2021-11-07 10:09 ` Eli Zaretskii
2021-11-07 12:03 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-07 12:33 ` Eli Zaretskii
2021-11-07 12:40 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-07 12:52 ` Eli Zaretskii
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87v914ycvi.fsf@yahoo.com \
--to=bug-gnu-emacs@gnu.org \
--cc=51473@debbugs.gnu.org \
--cc=eliz@gnu.org \
--cc=larsi@gnus.org \
--cc=luangruo@yahoo.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.