Support for the new Xwidget feature.

* configure.ac:
(HAVE_XWIDGETS, WIDGET_OBJ, EMACS_CONFIG_FEATURES):
* xterm.c (x_draw_glyph_string, x_draw_bar_cursor):
* xdisp.c:
(handle_display_spec, handle_single_display_spec, push_it)
(pop_it, set_iterator_to_next, dump_glyph)
(calc_pixel_width_or_height, fill_xwidget_glyph_string)
(BUILD_XWIDGET_GLYPH_STRING, BUILD_GLYPH_STRINGS)
(produce_xwidget_glyph, x_produce_glyphs)
(get_window_cursor_type):
* window.c (Fdelete_window_internal):
* termhooks.h (e):
* print.c (print_object):
* lisp.h (ptrdiff_t):
* keyboard.c (kbd_buffer_get_event, make_lispy_event)
(syms_of_keyboard):
* emacs.c (main):
* dispnew.c (update_window, scrolling_window):
* dispextern.h (g, i):
* Makefile.in (XWIDGETS_OBJ, WEBKIT_CFLAGS, WEBKIT_LIBS)
(GIR_LIBS, ALL_CFLAGS, base_obj, LIBES):
* keyboard.c (kbd_buffer_get_event):
* emacsgtkfixed.c (emacs_fixed_gtk_widget_size_allocate)
(emacs_fixed_class_init): Add case for an xwidget view.

* xwidget.c, xwidget.h, xwidget.el: New files for xwidgets

Co-authored-by:  Grégoire Jadi  <daimrod@gmail.com>

Various improvements to the Xwidget feature.
* xwidgets.c:
* emacsgtkfixed.c:
* xwidget.el:
This commit is contained in:
Joakim Verona 2016-01-19 20:27:12 +01:00
parent 663d379bbc
commit 7c1f66a94b
19 changed files with 2622 additions and 23 deletions

View file

@ -373,6 +373,9 @@ otherwise for the first of 'inotify' or 'gfile' that is usable.])
],
[with_file_notification=$with_features])
OPTION_DEFAULT_OFF([xwidgets],
[enable use of some gtk widgets in Emacs buffers])
## For the times when you want to build Emacs but don't have
## a suitable makeinfo, and can live without the manuals.
dnl http://lists.gnu.org/archive/html/emacs-devel/2008-04/msg01844.html
@ -2562,6 +2565,42 @@ if test "${HAVE_GTK}" = "yes"; then
term_header=gtkutil.h
fi
HAVE_XWIDGETS=no
HAVE_WEBKIT=no
HAVE_GIR=no
XWIDGETS_OBJ=
if test "$with_xwidgets" != "no" && test "$USE_GTK_TOOLKIT" = "GTK3" &&
test "$window_system" != "none"
then
HAVE_XWIDGETS=yes
AC_DEFINE([HAVE_XWIDGETS], 1, [Define to 1 if you have xwidgets support.])
dnl xwidgets
dnl - enable only if GTK3 is enabled, and we have a window system
dnl - check for webkit and gobject introspection
dnl webkit version for gtk3.
WEBKIT_REQUIRED=1.4.0
WEBKIT_MODULES="webkitgtk-3.0 >= $WEBKIT_REQUIRED"
if test "${with_gtk3}" = "yes"; then
PKG_CHECK_MODULES(WEBKIT, $WEBKIT_MODULES, HAVE_WEBKIT=yes, HAVE_WEBKIT=no)
if test $HAVE_WEBKIT = yes; then
AC_DEFINE([HAVE_WEBKIT_OSR], 1,
[Define to 1 if you have webkit_osr support.])
fi
fi
GIR_REQUIRED=1.32.1
GIR_MODULES="gobject-introspection-1.0 >= $GIR_REQUIRED"
PKG_CHECK_MODULES(GIR, $GIR_MODULES, HAVE_GIR=yes, HAVE_GIR=no)
if test $HAVE_GIR = yes; then
AC_DEFINE([HAVE_GIR], 1, [Define to 1 if you have GIR support.])
fi
XWIDGETS_OBJ=xwidget.o
fi
AC_SUBST(XWIDGETS_OBJ)
CFLAGS=$OLD_CFLAGS
LIBS=$OLD_LIBS
@ -4925,6 +4964,9 @@ case "$USE_X_TOOLKIT" in
LUCID) TOOLKIT_LIBW="$LUCID_LIBW" ;;
none) test "x$HAVE_GTK" = "xyes" && TOOLKIT_LIBW="$GTK_LIBS" ;;
esac
if test "$HAVE_XWIDGETS" = "yes"; then
TOOLKIT_LIBW="$TOOLKIT_LIBW -lXcomposite"
fi
AC_SUBST(TOOLKIT_LIBW)
if test "${opsys}" != "mingw32"; then
@ -5264,6 +5306,9 @@ AS_ECHO([" Does Emacs use -lXaw3d? ${HAVE_XAW3D
Does Emacs directly use zlib? ${HAVE_ZLIB}
Does Emacs have dynamic modules support? ${HAVE_MODULES}
Does Emacs use toolkit scroll bars? ${USE_TOOLKIT_SCROLL_BARS}
Does Emacs support Xwidgets? ${HAVE_XWIDGETS}
Does xwidgets support webkit(requires gtk3)? ${HAVE_WEBKIT}
Does xwidgets support gobject introspection? ${HAVE_GIR}
"])
if test -n "${EMACSDATA}"; then

View file

@ -120,6 +120,21 @@ and can contain escape sequences for command keys, quotes, and the like.
* Changes in Emacs 25.1
+++
** Xwidgets : A new feature for embedding native widgets
inside Emacs buffers. If you have gtk3 and webkit-devel installed, you
can access the embedded webkit browser with m-x
xwidget-webkit-browse-url. This will open a new buffer with the
embedded browser. The buffer will have a new mode, xwidget-webkit
mode which is similar to image mode, which supports the webkit widget.
*** New functions for xwidget-webkit mode `xwidget-webkit-insert-string',
`xwidget-webkit-adjust-size-dispatch', `xwidget-webkit-back',
`xwidget-webkit-browse-url', `xwidget-webkit-reload',
`xwidget-webkit-current-url', `xwidget-webkit-scroll-backward',
`xwidget-webkit-scroll-forward', `xwidget-webkit-scroll-down',
`xwidget-webkit-scroll-up',
+++
** Emacs can now load shared/dynamic libraries (modules).
A dynamic Emacs module is a shared library that provides additional

608
lisp/xwidget.el Normal file
View file

@ -0,0 +1,608 @@
;;; xwidget.el --- api functions for xwidgets -*- lexical-binding: t -*-
;;
;; Copyright (C) 2011-2015 Free Software Foundation, Inc.
;;
;; Author: Joakim Verona (joakim@verona.se)
;;
;; This file is part of GNU Emacs.
;;
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;
;; --------------------------------------------------------------------
;;; Commentary:
;;
;; See xwidget.c for more api functions
;;TODO this breaks compilation when we dont have xwidgets
;;(require 'xwidget-internal)
;;; Code:
(require 'cl-lib)
(require 'reporter)
(require 'bookmark)
(defcustom xwidget-webkit-scroll-behaviour 'native
"Scroll behaviour of the webkit instance.
'native or 'image."
:group 'xwidgets)
(defun xwidget-insert (pos type title width height &optional args)
"Insert an xwidget at POS.
given ID, TYPE, TITLE WIDTH and
HEIGHT in the current buffer.
Return ID
see `make-xwidget' for types suitable for TYPE.
Optional argument ARGS usage depends on the xwidget."
(goto-char pos)
(let ((id (make-xwidget (point) (point)
type title width height args)))
(put-text-property (point) (+ 1 (point))
'display (list 'xwidget ':xwidget id))
id))
(defun xwidget-at (pos)
"Return xwidget at POS."
;;TODO this function is a bit tedious because the C layer isnt well
;;protected yet and xwidgetp aparently doesnt work yet
(let* ((disp (get-text-property pos 'display))
(xw (car (cdr (cdr disp)))))
;;(if ( xwidgetp xw) xw nil)
(if (equal 'xwidget (car disp)) xw)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; webkit support
(require 'browse-url)
(require 'image-mode);;for some image-mode alike functionality
;;;###autoload
(defun xwidget-webkit-browse-url (url &optional new-session)
"Ask xwidget-webkit to browse URL.
NEW-SESSION specifies whether to create a new xwidget-webkit session. URL
defaults to the string looking like a url around the cursor position."
(interactive (progn
(require 'browse-url)
(browse-url-interactive-arg "xwidget-webkit URL: "
;;( xwidget-webkit-current-url)
)))
(when (stringp url)
(if new-session
(xwidget-webkit-new-session url)
(xwidget-webkit-goto-url url))))
;;shims for adapting image mode code to the webkit browser window
(defun xwidget-image-display-size (spec &optional pixels frame)
"Image code adaptor. SPEC PIXELS FRAME like the corresponding
`image-mode' fn."
(let ((xwi (xwidget-info (xwidget-at 1))))
(cons (aref xwi 2)
(aref xwi 3))))
(defadvice image-display-size (around image-display-size-for-xwidget
(spec &optional pixels frame)
activate)
"Advice for re-using image mode for xwidget."
(if (eq (car spec) 'xwidget)
(setq ad-return-value (xwidget-image-display-size spec pixels frame))
ad-do-it))
;;todo.
;; - check that the webkit support is compiled in
(defvar xwidget-webkit-mode-map
(let ((map (make-sparse-keymap)))
(define-key map "g" 'xwidget-webkit-browse-url)
(define-key map "a" 'xwidget-webkit-adjust-size-dispatch)
(define-key map "b" 'xwidget-webkit-back )
(define-key map "r" 'xwidget-webkit-reload )
(define-key map "t" (lambda () (interactive) (message "o")) )
(define-key map "\C-m" 'xwidget-webkit-insert-string)
(define-key map "w" 'xwidget-webkit-current-url)
;;similar to image mode bindings
(define-key map (kbd "SPC") 'xwidget-webkit-scroll-up)
(define-key map (kbd "DEL") 'xwidget-webkit-scroll-down)
(define-key map [remap scroll-up] 'xwidget-webkit-scroll-up)
(define-key map [remap scroll-up-command] 'xwidget-webkit-scroll-up)
(define-key map [remap scroll-down] 'xwidget-webkit-scroll-down)
(define-key map [remap scroll-down-command] 'xwidget-webkit-scroll-down)
(define-key map [remap forward-char] 'xwidget-webkit-scroll-forward)
(define-key map [remap backward-char] 'xwidget-webkit-scroll-backward)
(define-key map [remap right-char] 'xwidget-webkit-scroll-forward)
(define-key map [remap left-char] 'xwidget-webkit-scroll-backward)
;; (define-key map [remap previous-line] 'image-previous-line)
;; (define-key map [remap next-line] 'image-next-line)
;; (define-key map [remap move-beginning-of-line] 'image-bol)
;; (define-key map [remap move-end-of-line] 'image-eol)
;; (define-key map [remap beginning-of-buffer] 'image-bob)
;; (define-key map [remap end-of-buffer] 'image-eob)
map)
"Keymap for `xwidget-webkit-mode'.")
(defun xwidget-webkit-scroll-up ()
"Scroll webkit up,either native or like image mode."
(interactive)
(if (eq xwidget-webkit-scroll-behaviour 'native)
(xwidget-set-adjustment (xwidget-webkit-last-session) 'vertical t 50)
(image-scroll-up)))
(defun xwidget-webkit-scroll-down ()
"Scroll webkit down,either native or like image mode."
(interactive)
(if (eq xwidget-webkit-scroll-behaviour 'native)
(xwidget-set-adjustment (xwidget-webkit-last-session) 'vertical t -50)
(image-scroll-down)))
(defun xwidget-webkit-scroll-forward ()
"Scroll webkit forward,either native or like image mode."
(interactive)
(if (eq xwidget-webkit-scroll-behaviour 'native)
(xwidget-set-adjustment (xwidget-webkit-last-session) 'horizontal t 50)
(xwidget-webkit-scroll-forward)))
(defun xwidget-webkit-scroll-backward ()
"Scroll webkit backward,either native or like image mode."
(interactive)
(if (eq xwidget-webkit-scroll-behaviour 'native)
(xwidget-set-adjustment (xwidget-webkit-last-session) 'horizontal t -50)
(xwidget-webkit-scroll-backward)))
;;the xwidget event needs to go into a higher level handler
;;since the xwidget can generate an event even if its offscreen
;;TODO this needs to use callbacks and consider different xw ev types
(define-key (current-global-map) [xwidget-event] 'xwidget-event-handler)
(defun xwidget-log ( &rest msg)
"Log MSG to a buffer."
(let ( (buf (get-buffer-create "*xwidget-log*")))
(save-excursion
(buffer-disable-undo buf)
(set-buffer buf)
(insert (apply 'format msg))
(insert "\n"))))
(defun xwidget-event-handler ()
"Receive xwidget event."
(interactive)
(xwidget-log "stuff happened to xwidget %S" last-input-event)
(let*
((xwidget-event-type (nth 1 last-input-event))
(xwidget (nth 2 last-input-event))
;;(xwidget-callback (xwidget-get xwidget 'callback))
;;TODO stopped working for some reason
)
;;(funcall xwidget-callback xwidget xwidget-event-type)
(message "xw callback %s" xwidget)
(funcall 'xwidget-webkit-callback xwidget xwidget-event-type)))
(defun xwidget-webkit-callback (xwidget xwidget-event-type)
"Callback for xwidgets.
XWIDGET instance, XWIDGET-EVENT-TYPE depends on the originating xwidget."
(save-excursion
(cond ((buffer-live-p (xwidget-buffer xwidget))
(set-buffer (xwidget-buffer xwidget))
(let* ((strarg (nth 3 last-input-event)))
(cond ((eq xwidget-event-type 'document-load-finished)
(xwidget-log "webkit finished loading: '%s'"
(xwidget-webkit-get-title xwidget))
;;TODO - check the native/internal scroll
;;(xwidget-adjust-size-to-content xwidget)
(xwidget-webkit-adjust-size-dispatch) ;;TODO xwidget arg
(rename-buffer (format "*xwidget webkit: %s *"
(xwidget-webkit-get-title xwidget)))
(pop-to-buffer (current-buffer)))
((eq xwidget-event-type
'navigation-policy-decision-requested)
(if (string-match ".*#\\(.*\\)" strarg)
(xwidget-webkit-show-id-or-named-element
xwidget
(match-string 1 strarg))))
(t (xwidget-log "unhandled event:%s" xwidget-event-type)))))
(t (xwidget-log
"error: callback called for xwidget with dead buffer")))))
(defvar 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 bookmark-make-record-function
#'xwidget-webkit-bookmark-make-record)
;; Keep track of [vh]scroll when switching buffers
(image-mode-setup-winprops))
(defun xwidget-webkit-bookmark-make-record ()
"Integrate Emacs bookmarks with the webkit xwidget."
(nconc (bookmark-make-record-default t t)
`((page . ,(xwidget-webkit-current-url))
(handler . (lambda (bmk) (browse-url
(bookmark-prop-get bmk 'page)))))))
(defvar xwidget-webkit-last-session-buffer nil)
(defun xwidget-webkit-last-session ()
"Last active webkit, or nil."
(if (buffer-live-p xwidget-webkit-last-session-buffer)
(with-current-buffer xwidget-webkit-last-session-buffer
(xwidget-at 1))
nil))
(defun xwidget-webkit-current-session ()
"Either the webkit in the current buffer, or the last one used,
which might be nil."
(if (xwidget-at 1)
(xwidget-at 1)
(xwidget-webkit-last-session)))
(defun xwidget-adjust-size-to-content (xw)
"Resize XW to content."
;;xwidgets doesnt support widgets that have their own opinions about
;;size well yet this reads the desired size and resizes the emacs
;;allocated area accordingly
(let ((size (xwidget-size-request xw)))
(xwidget-resize xw (car size) (cadr size))))
(defvar xwidget-webkit-activeelement-js"
function findactiveelement(doc){
//alert(doc.activeElement.value);
if(doc.activeElement.value != undefined){
return doc.activeElement;
}else{
// recurse over the child documents:
var frames = doc.getElementsByTagName('frame');
for (var i = 0; i < frames.length; i++)
{
var d = frames[i].contentDocument;
var rv = findactiveelement(d);
if(rv != undefined){
return rv;
}
}
}
return undefined;
};
"
"javascript that finds the active element."
;;yes its ugly. because:
;; - there is aparently no way to find the active frame other than recursion
;; - the js "for each" construct missbehaved on the "frames" collection
;; - a window with no frameset still has frames.length == 1, but
;; frames[0].document.activeElement != document.activeElement
;;TODO the activeelement type needs to be examined, for iframe, etc.
)
(defun xwidget-webkit-insert-string (xw str)
"Insert string in the active field in the webkit.
Argument XW webkit.
Argument STR string."
;;read out the string in the field first and provide for edit
(interactive
(let* ((xww (xwidget-webkit-current-session))
(field-value
(progn
(xwidget-webkit-execute-script xww xwidget-webkit-activeelement-js)
(xwidget-webkit-execute-script-rv
xww
"findactiveelement(document).value;" )))
(field-type (xwidget-webkit-execute-script-rv
xww
"findactiveelement(document).type;" )))
(list xww
(cond ((equal "text" field-type)
(read-string "text:" field-value))
((equal "password" field-type)
(read-passwd "password:" nil field-value))
((equal "textarea" field-type)
(xwidget-webkit-begin-edit-textarea xww field-value))))))
(xwidget-webkit-execute-script
xw
(format "findactiveelement(document).value='%s'" str)))
(defvar xwidget-xwbl)
(defun xwidget-webkit-begin-edit-textarea (xw text)
"Start editing of a webkit text area.
XW is the xwidget identifier, TEXT is retrieved from the webkit."
(switch-to-buffer
(generate-new-buffer "textarea"))
(set (make-local-variable 'xwidget-xwbl) xw)
(insert text))
(defun xwidget-webkit-end-edit-textarea ()
"End editing of a webkit text area."
(interactive)
(goto-char (point-min))
(while (search-forward "\n" nil t)
(replace-match "\\n" nil t))
(xwidget-webkit-execute-script
xwidget-xwbl
(format "findactiveelement(document).value='%s'"
(buffer-substring (point-min) (point-max))))
;;TODO convert linefeed to \n
)
(defun xwidget-webkit-show-named-element (xw element-name)
"Make named-element show. for instance an anchor.
Argument XW is the xwidget.
Argument ELEMENT-NAME is the element name to display in the webkit xwidget."
(interactive (list (xwidget-webkit-current-session)
(read-string "element name:")))
;;TODO since an xwidget is an Emacs object, it is not trivial to do
;; some things that are taken for granted in a normal browser.
;; scrolling an anchor/named-element into view is one such thing.
;; This function implements a proof-of-concept for this. Problems
;; remaining: - The selected window is scrolled but this is not
;; always correct - This needs to be interfaced into browse-url
;; somehow. the tricky part is that we need to do this in two steps:
;; A: load the base url, wait for load signal to arrive B: navigate
;; to the anchor when the base url is finished rendering
;; This part figures out the Y coordinate of the element
(let ((y (string-to-number
(xwidget-webkit-execute-script-rv
xw
(format
"document.getElementsByName('%s')[0].getBoundingClientRect().top"
element-name)
0))))
;; Now we need to tell emacs to scroll the element into view.
(xwidget-log "scroll: %d" y)
(set-window-vscroll (selected-window) y t)))
(defun xwidget-webkit-show-id-element (xw element-id)
"Make id-element show. for instance an anchor.
Argument XW is the webkit xwidget.
Argument ELEMENT-ID is the id of the element to show."
(interactive (list (xwidget-webkit-current-session)
(read-string "element id:")))
(let ((y (string-to-number
(xwidget-webkit-execute-script-rv
xw
(format "document.getElementById('%s').getBoundingClientRect().top"
element-id)
0))))
;; Now we need to tell emacs to scroll the element into view.
(xwidget-log "scroll: %d" y)
(set-window-vscroll (selected-window) y t)))
(defun xwidget-webkit-show-id-or-named-element (xw element-id)
"Make id-element show. for instance an anchor.
Argument XW is the webkit xwidget.
Argument ELEMENT-ID is either a name or an element id."
(interactive (list (xwidget-webkit-current-session)
(read-string "element id:")))
(let* ((y1 (string-to-number
(xwidget-webkit-execute-script-rv
xw
(format "document.getElementsByName('%s')[0].getBoundingClientRect().top" element-id)
"0")))
(y2 (string-to-number
(xwidget-webkit-execute-script-rv
xw
(format "document.getElementById('%s').getBoundingClientRect().top" element-id)
"0")))
(y3 (max y1 y2)))
;; Now we need to tell emacs to scroll the element into view.
(xwidget-log "scroll: %d" y3)
(set-window-vscroll (selected-window) y3 t)))
(defun xwidget-webkit-adjust-size-to-content ()
"Adjust webkit to content size."
(interactive)
(xwidget-adjust-size-to-content (xwidget-webkit-current-session)))
(defun xwidget-webkit-adjust-size-dispatch ()
"Adjust size according to mode."
(interactive)
(if (eq xwidget-webkit-scroll-behaviour 'native)
(xwidget-webkit-adjust-size-to-window)
(xwidget-webkit-adjust-size-to-content))
;; The recenter is intended to correct a visual glitch.
;; It errors out if the buffer isn't visible, but then we dont get the glitch,
;; so silence errors
(ignore-errors
(recenter-top-bottom))
)
(defun xwidget-webkit-adjust-size-to-window ()
"Adjust webkit to window."
(interactive)
(xwidget-resize ( xwidget-webkit-current-session) (window-pixel-width)
(window-pixel-height)))
(defun xwidget-webkit-adjust-size (w h)
"Manualy set webkit size.
Argument W width.
Argument H height."
;; TODO shouldn't be tied to the webkit xwidget
(interactive "nWidth:\nnHeight:\n")
(xwidget-resize ( xwidget-webkit-current-session) w h))
(defun xwidget-webkit-fit-width ()
"Adjust width of webkit to window width."
(interactive)
(xwidget-webkit-adjust-size (- (nth 2 (window-inside-pixel-edges))
(car (window-inside-pixel-edges)))
1000))
(defun xwidget-webkit-new-session (url)
"Create a new webkit session buffer with URL."
(let*
((bufname (generate-new-buffer-name "*xwidget-webkit*"))
xw)
(setq xwidget-webkit-last-session-buffer (switch-to-buffer
(get-buffer-create bufname)))
(insert " 'a' adjusts the xwidget size.")
(setq xw (xwidget-insert 1 'webkit-osr bufname 1000 1000))
(xwidget-put xw 'callback 'xwidget-webkit-callback)
(xwidget-webkit-mode)
(xwidget-webkit-goto-uri (xwidget-webkit-last-session) url )))
(defun xwidget-webkit-goto-url (url)
"Goto URL."
(if (xwidget-webkit-current-session)
(progn
(xwidget-webkit-goto-uri (xwidget-webkit-current-session) url))
(xwidget-webkit-new-session url)))
(defun xwidget-webkit-back ()
"Back in history."
(interactive)
(xwidget-webkit-execute-script (xwidget-webkit-current-session)
"history.go(-1);"))
(defun xwidget-webkit-reload ()
"Reload current url."
(interactive)
(xwidget-webkit-execute-script (xwidget-webkit-current-session)
"history.go(0);"))
(defun xwidget-webkit-current-url ()
"Get the webkit url. place it on kill ring."
(interactive)
(let* ((rv (xwidget-webkit-execute-script-rv (xwidget-webkit-current-session)
"document.URL"))
(url (kill-new (or rv ""))))
(message "url: %s" url )
url))
(defun xwidget-webkit-execute-script-rv (xw script &optional default)
"Same as 'xwidget-webkit-execute-script' but but with return value.
XW is the webkit instance. SCRIPT is the script to execut.
DEFAULT is the defaultreturn value."
;; Notice the ugly "title" hack. It is needed because the Webkit
;; API at the time of writing didn't support returning values. This
;; is a wrapper for the title hack so its easy to remove should
;; Webkit someday support JS return values or we find some other way
;; to access the DOM.
;; Reset webkit title. Not very nice.
(let* ((emptytag "titlecantbewhitespaceohthehorror")
title)
(xwidget-webkit-execute-script xw (format "document.title=\"%s\";"
(or default emptytag)))
(xwidget-webkit-execute-script xw (format "document.title=%s;" script))
(setq title (xwidget-webkit-get-title xw))
(if (equal emptytag title)
(setq title ""))
(unless title
(setq title default))
title))
;; Use declare here?
;; (declare-function xwidget-resize-internal "xwidget.c" )
;; check-declare-function?
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun xwidget-webkit-get-selection ()
"Get the webkit selection."
(xwidget-webkit-execute-script-rv (xwidget-webkit-current-session)
"window.getSelection().toString();"))
(defun xwidget-webkit-copy-selection-as-kill ()
"Get the webkit selection and put it on the kill ring."
(interactive)
(kill-new (xwidget-webkit-get-selection)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Xwidget plist management(similar to the process plist functions)
(defun xwidget-get (xwidget propname)
"Return the value of XWIDGET' PROPNAME property.
This is the last value stored with `(xwidget-put XWIDGET PROPNAME VALUE)'."
(plist-get (xwidget-plist xwidget) propname))
(defun xwidget-put (xwidget propname value)
"Change XWIDGET' PROPNAME property to VALUE.
It can be retrieved with `(xwidget-get XWIDGET PROPNAME)'."
(set-xwidget-plist xwidget
(plist-put (xwidget-plist xwidget) propname value)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun xwidget-delete-zombies ()
"Helper for `xwidget-cleanup'."
(dolist (xwidget-view xwidget-view-list)
(when (or (not (window-live-p (xwidget-view-window xwidget-view)))
(not (memq (xwidget-view-model xwidget-view)
xwidget-list)))
(delete-xwidget-view xwidget-view))))
(defun xwidget-cleanup ()
"Delete zombie xwidgets."
;; During development it was sometimes easy to wind up with zombie
;; xwidget instances.
;; This function tries to implement a workaround should it occur again.
(interactive)
;; Kill xviews who should have been deleted but stull linger.
(xwidget-delete-zombies)
;; Redraw display otherwise ghost of zombies will remain to haunt the screen
(redraw-display))
;; This would have felt better in C, but this seems to work well in
;; practice though.
(add-hook 'window-configuration-change-hook 'xwidget-delete-zombies)
(defun xwidget-kill-buffer-query-function ()
"Ask beforek illing a buffer that has xwidgets."
(let ((xwidgets (get-buffer-xwidgets (current-buffer))))
(or (not xwidgets)
(not (memq t (mapcar 'xwidget-query-on-exit-flag xwidgets)))
(yes-or-no-p
(format "Buffer %S has xwidgets; kill it? "
(buffer-name (current-buffer)))))))
(add-hook 'kill-buffer-query-functions 'xwidget-kill-buffer-query-function)
(defun report-xwidget-bug ()
"Report a bug in GNU Emacs about the XWidget branch.
Prompts for bug subject. Leaves you in a mail buffer."
(interactive)
(let ((reporter-prompt-for-summary-p t))
(reporter-submit-bug-report "submit@debbugs.gnu.org" nil nil nil nil
(format "Package: emacs-xwidgets
Please describe exactly what actions triggered the bug, and the
precise symptoms of the bug. If you can, give a recipe starting
from `emacs -Q'.
If Emacs crashed, and you have the Emacs process in the gdb
deubbger, please include the output from the following gdb
commands:
`bt full' and `xbacktrace'.
For information about debugging Emacs, please read the file
%s" (expand-file-name "DEBUG" data-directory)))))
(provide 'xwidget)
;;; xwidget.el ends here

View file

@ -152,6 +152,9 @@ DBUS_LIBS = @DBUS_LIBS@
## dbusbind.o if HAVE_DBUS, else empty.
DBUS_OBJ = @DBUS_OBJ@
## xwidgets.o if HAVE_XWIDGETS, else empty.
XWIDGETS_OBJ = @XWIDGETS_OBJ@
LIB_EXECINFO=@LIB_EXECINFO@
SETTINGS_CFLAGS = @SETTINGS_CFLAGS@
@ -219,6 +222,11 @@ CFLAGS_SOUND= @CFLAGS_SOUND@
RSVG_LIBS= @RSVG_LIBS@
RSVG_CFLAGS= @RSVG_CFLAGS@
WEBKIT_LIBS= @WEBKIT_LIBS@
WEBKIT_CFLAGS= @WEBKIT_CFLAGS@
GIR_LIBS= @GIR_LIBS@
GIR_CFLAGS= @GIR_CFLAGS@
CAIRO_LIBS= @CAIRO_LIBS@
CAIRO_CFLAGS= @CAIRO_CFLAGS@
@ -358,6 +366,7 @@ ALL_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \
$(GNUSTEP_CFLAGS) $(CFLAGS_SOUND) $(RSVG_CFLAGS) $(IMAGEMAGICK_CFLAGS) \
$(PNG_CFLAGS) $(LIBXML2_CFLAGS) $(DBUS_CFLAGS) \
$(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) \
$(WEBKIT_CFLAGS) $(GIR_CFLAGS) \
$(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \
$(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \
$(LIBGNUTLS_CFLAGS) $(GFILENOTIFY_CFLAGS) $(CAIRO_CFLAGS) \
@ -387,6 +396,7 @@ base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o \
process.o gnutls.o callproc.o \
region-cache.o sound.o atimer.o \
doprnt.o intervals.o textprop.o composite.o xml.o $(NOTIFY_OBJ) \
$(XWIDGETS_OBJ) \
profiler.o decompress.o \
$(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ) \
$(W32_OBJ) $(WINDOW_SYSTEM_OBJ) $(XGSELOBJ)
@ -467,6 +477,7 @@ lisp = $(addprefix ${lispsource}/,${shortlisp})
LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(LIBX_BASE) $(LIBIMAGE) \
$(LIBX_OTHER) $(LIBSOUND) \
$(RSVG_LIBS) $(IMAGEMAGICK_LIBS) $(LIB_ACL) $(LIB_CLOCK_GETTIME) \
$(WEBKIT_LIBS) $(GIR_LIBS) \
$(LIB_EACCESS) $(LIB_FDATASYNC) $(LIB_TIMER_TIME) $(DBUS_LIBS) \
$(LIB_EXECINFO) $(XRANDR_LIBS) $(XINERAMA_LIBS) $(XFIXES_LIBS) \
$(LIBXML2_LIBS) $(LIBGPM) $(LIBRESOLV) $(LIBS_SYSTEM) $(CAIRO_LIBS) \

View file

@ -43,6 +43,9 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include "keymap.h"
#include "frame.h"
#ifdef HAVE_XWIDGETS
# include "xwidget.h"
#endif
#ifdef WINDOWSNT
#include "w32heap.h" /* for mmap_* */
#endif
@ -1747,6 +1750,9 @@ cleaning up all windows currently displaying the buffer to be killed. */)
kill_buffer_processes (buffer);
#ifdef HAVE_XWIDGETS
kill_buffer_xwidgets (buffer);
#endif
/* Killing buffer processes may run sentinels which may have killed
our buffer. */
if (!BUFFER_LIVE_P (b))

View file

@ -348,6 +348,10 @@ enum glyph_type
/* Glyph is a space of fractional width and/or height. */
STRETCH_GLYPH
#ifdef HAVE_XWIDGETS
/* Glyph is an external widget drawn by the GUI toolkit. */
,XWIDGET_GLYPH
#endif
};
@ -499,6 +503,9 @@ struct glyph
/* Image ID for image glyphs (type == IMAGE_GLYPH). */
int img_id;
#ifdef HAVE_XWIDGETS
struct xwidget *xwidget;
#endif
/* Sub-structure for type == STRETCH_GLYPH. */
struct
{
@ -1350,6 +1357,9 @@ struct glyph_string
/* Image, if any. */
struct image *img;
#ifdef HAVE_XWIDGETS
struct xwidget *xwidget;
#endif
/* Slice */
struct glyph_slice slice;
@ -2102,6 +2112,10 @@ enum display_element_type
/* Continuation glyphs. See the comment for IT_TRUNCATION. */
IT_CONTINUATION
#ifdef HAVE_XWIDGETS
,IT_XWIDGET
#endif
};
@ -2165,6 +2179,9 @@ enum it_method {
GET_FROM_C_STRING,
GET_FROM_IMAGE,
GET_FROM_STRETCH,
#ifdef HAVE_XWIDGETS
GET_FROM_XWIDGET,
#endif
NUM_IT_METHODS
};
@ -2382,6 +2399,12 @@ struct it
struct {
Lisp_Object object;
} stretch;
#ifdef HAVE_XWIDGETS
/* method == GET_FROM_XWIDGET */
struct {
Lisp_Object object;
} xwidget;
#endif
} u;
/* Current text and display positions. */
@ -2506,6 +2529,11 @@ struct it
/* If what == IT_IMAGE, the id of the image to display. */
ptrdiff_t image_id;
#ifdef HAVE_XWIDGETS
/* If what == IT_XWIDGET. */
struct xwidget *xwidget;
#endif
/* Values from `slice' property. */
struct it_slice slice;

View file

@ -44,6 +44,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include TERM_HEADER
#endif /* HAVE_WINDOW_SYSTEM */
#ifdef HAVE_XWIDGETS
# include "xwidget.h"
#endif
#include <errno.h>
#include <fpending.h>
@ -3545,6 +3549,9 @@ update_window (struct window *w, bool force_p)
add_window_display_history (w, w->current_matrix->method, paused_p);
#endif
#ifdef HAVE_XWIDGETS
xwidget_end_redisplay (w, w->current_matrix);
#endif
clear_glyph_matrix (desired_matrix);
return paused_p;
@ -4118,6 +4125,11 @@ scrolling_window (struct window *w, bool header_line_p)
break;
}
#ifdef HAVE_XWIDGETS
/* Currently this seems needed to detect xwidget movement reliably. */
return 0;
#endif
/* Give up if some rows in the desired matrix are not enabled. */
if (! MATRIX_ROW_ENABLED_P (desired_matrix, i))
return -1;

View file

@ -66,6 +66,9 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include "buffer.h"
#include "window.h"
#ifdef HAVE_XWIDGETS
# include "xwidget.h"
#endif
#include "atimer.h"
#include "blockinput.h"
#include "syssignal.h"
@ -1485,6 +1488,9 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
syms_of_xfns ();
syms_of_xmenu ();
syms_of_fontset ();
#ifdef HAVE_XWIDGETS
syms_of_xwidget ();
#endif
syms_of_xsettings ();
#ifdef HAVE_X_SM
syms_of_xsmfns ();

View file

@ -23,6 +23,9 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include "lisp.h"
#include "frame.h"
#include "xterm.h"
#ifdef HAVE_XWIDGETS
# include "xwidget.h"
#endif
#include "emacsgtkfixed.h"
/* Silence a bogus diagnostic; see GNOME bug 683906. */
@ -31,27 +34,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
# pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#endif
#define EMACS_TYPE_FIXED emacs_fixed_get_type ()
#define EMACS_FIXED(obj) \
G_TYPE_CHECK_INSTANCE_CAST (obj, EMACS_TYPE_FIXED, EmacsFixed)
typedef struct _EmacsFixed EmacsFixed;
typedef struct _EmacsFixedPrivate EmacsFixedPrivate;
typedef struct _EmacsFixedClass EmacsFixedClass;
struct _EmacsFixed
{
GtkFixed container;
/*< private >*/
EmacsFixedPrivate *priv;
};
struct _EmacsFixedClass
{
GtkFixedClass parent_class;
};
struct _EmacsFixedPrivate
{
struct frame *f;
@ -64,9 +50,87 @@ static void emacs_fixed_get_preferred_width (GtkWidget *widget,
static void emacs_fixed_get_preferred_height (GtkWidget *widget,
gint *minimum,
gint *natural);
static GType emacs_fixed_get_type (void);
G_DEFINE_TYPE (EmacsFixed, emacs_fixed, GTK_TYPE_FIXED)
#ifdef HAVE_XWIDGETS
struct GtkFixedPrivateL
{
GList *children;
};
static void emacs_fixed_gtk_widget_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
// For xwidgets.
// This basically re-implements the base class method and adds an
// additional case for an xwidget view.
// It would be nicer if the bse class method could be called first,
// and the the xview modification only would remain here. It wasn't
// possible to solve it that way yet.
EmacsFixedClass *klass;
GtkWidgetClass *parent_class;
struct GtkFixedPrivateL* priv;
klass = EMACS_FIXED_GET_CLASS (widget);
parent_class = g_type_class_peek_parent (klass);
parent_class->size_allocate (widget, allocation);
priv = G_TYPE_INSTANCE_GET_PRIVATE (widget,
GTK_TYPE_FIXED,
struct GtkFixedPrivateL);
gtk_widget_set_allocation (widget, allocation);
if (gtk_widget_get_has_window (widget))
{
if (gtk_widget_get_realized (widget))
gdk_window_move_resize (gtk_widget_get_window (widget),
allocation->x,
allocation->y,
allocation->width,
allocation->height);
}
for (GList *children = priv->children; children; children = children->next)
{
GtkFixedChild *child = children->data;
if (!gtk_widget_get_visible (child->widget))
continue;
GtkRequisition child_requisition;
gtk_widget_get_preferred_size (child->widget, &child_requisition, NULL);
GtkAllocation child_allocation;
child_allocation.x = child->x;
child_allocation.y = child->y;
if (!gtk_widget_get_has_window (widget))
{
child_allocation.x += allocation->x;
child_allocation.y += allocation->y;
}
child_allocation.width = child_requisition.width;
child_allocation.height = child_requisition.height;
struct xwidget_view *xv
= g_object_get_data (G_OBJECT (child->widget), XG_XWIDGET_VIEW);
if (xv)
{
child_allocation.width = xv->clip_right;
child_allocation.height = xv->clip_bottom - xv->clip_top;
}
gtk_widget_size_allocate (child->widget, &child_allocation);
}
}
#endif /* HAVE_XWIDGETS */
static void
emacs_fixed_class_init (EmacsFixedClass *klass)
{
@ -74,11 +138,16 @@ emacs_fixed_class_init (EmacsFixedClass *klass)
widget_class = (GtkWidgetClass*) klass;
widget_class->get_preferred_width = emacs_fixed_get_preferred_width;
widget_class->get_preferred_height = emacs_fixed_get_preferred_height;
#ifdef HAVE_XWIDGETS
widget_class->size_allocate = emacs_fixed_gtk_widget_size_allocate;
#endif
g_type_class_add_private (klass, sizeof (EmacsFixedPrivate));
}
static void
emacs_fixed_init (EmacsFixed *fixed)
{

View file

@ -27,7 +27,35 @@ struct frame;
G_BEGIN_DECLS
struct frame;
#define EMACS_TYPE_FIXED (emacs_fixed_get_type ())
#define EMACS_FIXED(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EMACS_TYPE_FIXED, EmacsFixed))
#define EMACS_FIXED_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EMACS_TYPE_FIXED, EmacsFixedClass))
#define EMACS_IS_FIXED(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EMACS_TYPE_FIXED))
#define EMACS_IS_FIXED_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EMACS_TYPE_FIXED))
#define EMACS_FIXED_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EMACS_TYPE_FIXED, EmacsFixedClass))
//typedef struct _EmacsFixed EmacsFixed;
typedef struct _EmacsFixedPrivate EmacsFixedPrivate;
typedef struct _EmacsFixedClass EmacsFixedClass;
struct _EmacsFixed
{
GtkFixed container;
/*< private >*/
EmacsFixedPrivate *priv;
};
struct _EmacsFixedClass
{
GtkFixedClass parent_class;
};
extern GtkWidget *emacs_fixed_new (struct frame *f);
extern GType emacs_fixed_get_type (void);
G_END_DECLS

View file

@ -4013,6 +4013,13 @@ kbd_buffer_get_event (KBOARD **kbp,
obj = make_lispy_event (&event->ie);
kbd_fetch_ptr = event + 1;
}
#endif
#ifdef HAVE_XWIDGETS
else if (event->kind == XWIDGET_EVENT)
{
obj = make_lispy_event (&event->ie);
kbd_fetch_ptr = event + 1;
}
#endif
else if (event->kind == CONFIG_CHANGED_EVENT)
{
@ -5950,6 +5957,14 @@ make_lispy_event (struct input_event *event)
}
#endif /* HAVE_DBUS */
#ifdef HAVE_XWIDGETS
case XWIDGET_EVENT:
{
return Fcons (Qxwidget_event,event->arg);
}
#endif
#if defined HAVE_GFILENOTIFY || defined HAVE_INOTIFY
case FILE_NOTIFY_EVENT:
{
@ -10956,6 +10971,10 @@ syms_of_keyboard (void)
DEFSYM (Qdbus_event, "dbus-event");
#endif
#ifdef HAVE_XWIDGETS
DEFSYM (Qxwidget_event,"xwidget-event");
#endif
#ifdef USE_FILE_NOTIFY
DEFSYM (Qfile_notify, "file-notify");
#endif /* USE_FILE_NOTIFY */

View file

@ -799,6 +799,12 @@ enum pvec_type
PVEC_WINDOW_CONFIGURATION,
PVEC_SUBR,
PVEC_OTHER,
#ifdef HAVE_XWIDGETS
PVEC_XWIDGET,
PVEC_XWIDGET_VIEW,
#endif
/* These should be last, check internal_equal to see why. */
PVEC_COMPILED,
PVEC_CHAR_TABLE,

View file

@ -33,6 +33,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include "intervals.h"
#include "blockinput.h"
#ifdef HAVE_XWIDGETS
# include "xwidget.h"
#endif
#include <c-ctype.h>
#include <float.h>
#include <ftoastr.h>
@ -1736,6 +1740,18 @@ print_object (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag)
print_c_string (XSUBR (obj)->symbol_name, printcharfun);
printchar ('>', printcharfun);
}
#ifdef HAVE_XWIDGETS
else if (XWIDGETP (obj))
{
print_c_string ("#<xwidget ", printcharfun);
printchar ('>', printcharfun);
}
else if (XWIDGET_VIEW_P (obj))
{
print_c_string ("#<xwidget ", printcharfun);
printchar ('>', printcharfun);
}
#endif
else if (WINDOWP (obj))
{
int len = sprintf (buf, "#<window %"pI"d",

View file

@ -239,6 +239,11 @@ enum event_kind
, NS_NONKEY_EVENT
#endif
#ifdef HAVE_XWIDGETS
/* events generated by xwidgets*/
, XWIDGET_EVENT
#endif
#ifdef USE_FILE_NOTIFY
/* File or directory was changed. */
, FILE_NOTIFY_EVENT

View file

@ -41,6 +41,9 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#ifdef MSDOS
#include "msdos.h"
#endif
#ifdef HAVE_XWIDGETS
# include "xwidget.h"
#endif
static ptrdiff_t count_windows (struct window *);
static ptrdiff_t get_leaf_windows (struct window *, struct window **,
@ -4370,6 +4373,9 @@ Signal an error when WINDOW is the only window on its frame. */)
/* Block input. */
block_input ();
#ifdef HAVE_XWIDGETS
xwidget_view_delete_all_in_window (w);
#endif
window_resize_apply (p, horflag);
/* If this window is referred to by the dpyinfo's mouse
highlight, invalidate that slot to be safe (Bug#9904). */

View file

@ -318,6 +318,9 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include TERM_HEADER
#endif /* HAVE_WINDOW_SYSTEM */
#ifdef HAVE_XWIDGETS
# include "xwidget.h"
#endif
#ifndef FRAME_X_OUTPUT
#define FRAME_X_OUTPUT(f) ((f)->output_data.x)
#endif
@ -854,6 +857,9 @@ static bool next_element_from_buffer (struct it *);
static bool next_element_from_composition (struct it *);
static bool next_element_from_image (struct it *);
static bool next_element_from_stretch (struct it *);
#ifdef HAVE_XWIDGETS
static bool next_element_from_xwidget (struct it *);
#endif
static void load_overlay_strings (struct it *, ptrdiff_t);
static bool get_next_display_element (struct it *);
static enum move_it_result
@ -4690,6 +4696,9 @@ handle_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
if (CONSP (spec)
/* Simple specifications. */
&& !EQ (XCAR (spec), Qimage)
#ifdef HAVE_XWIDGETS
&& !EQ (XCAR (spec), Qxwidget)
#endif
&& !EQ (XCAR (spec), Qspace)
&& !EQ (XCAR (spec), Qwhen)
&& !EQ (XCAR (spec), Qslice)
@ -5137,7 +5146,12 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
|| ((it ? FRAME_WINDOW_P (it->f) : frame_window_p)
&& valid_image_p (value))
#endif /* not HAVE_WINDOW_SYSTEM */
|| (CONSP (value) && EQ (XCAR (value), Qspace)));
|| (CONSP (value) && EQ (XCAR (value), Qspace))
#ifdef HAVE_XWIDGETS
|| ((it ? FRAME_WINDOW_P (it->f) : frame_window_p)
&& valid_xwidget_spec_p (value))
#endif
);
if (valid_p && display_replaced == 0)
{
@ -5212,6 +5226,17 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
*position = it->position = start_pos;
retval = 1 + (it->area == TEXT_AREA);
}
#ifdef HAVE_XWIDGETS
else if (valid_xwidget_spec_p(value))
{
it->what = IT_XWIDGET;
it->method = GET_FROM_XWIDGET;
it->position = start_pos;
it->object = NILP (object) ? it->w->contents : object;
*position = start_pos;
it->xwidget = lookup_xwidget(value);
}
#endif
#ifdef HAVE_WINDOW_SYSTEM
else
{
@ -5964,6 +5989,11 @@ push_it (struct it *it, struct text_pos *position)
case GET_FROM_STRETCH:
p->u.stretch.object = it->object;
break;
#ifdef HAVE_XWIDGETS
case GET_FROM_XWIDGET:
p->u.xwidget.object = it->object;
break;
#endif
case GET_FROM_BUFFER:
case GET_FROM_DISPLAY_VECTOR:
case GET_FROM_STRING:
@ -6065,6 +6095,11 @@ pop_it (struct it *it)
it->object = p->u.image.object;
it->slice = p->u.image.slice;
break;
#ifdef HAVE_XWIDGETS
case GET_FROM_XWIDGET:
it->object = p->u.xwidget.object;
break;
#endif
case GET_FROM_STRETCH:
it->object = p->u.stretch.object;
break;
@ -6739,7 +6774,10 @@ static next_element_function const get_next_element[NUM_IT_METHODS] =
next_element_from_string,
next_element_from_c_string,
next_element_from_image,
next_element_from_stretch
next_element_from_stretch,
#ifdef HAVE_XWIDGETS
next_element_from_xwidget,
#endif
};
#define GET_NEXT_DISPLAY_ELEMENT(it) (*get_next_element[(it)->method]) (it)
@ -7600,6 +7638,10 @@ set_iterator_to_next (struct it *it, bool reseat_p)
case GET_FROM_IMAGE:
case GET_FROM_STRETCH:
#ifdef HAVE_XWIDGETS
case GET_FROM_XWIDGET:
#endif
/* The position etc with which we have to proceed are on
the stack. The position may be at the end of a string,
if the `display' property takes up the whole string. */
@ -8061,6 +8103,15 @@ next_element_from_image (struct it *it)
return true;
}
#ifdef HAVE_XWIDGETS
static bool
next_element_from_xwidget (struct it *it)
{
it->what = IT_XWIDGET;
return true;
}
#endif
/* Fill iterator IT with next display element from a stretch glyph
property. IT->object is the value of the text property. Value is
@ -18793,6 +18844,28 @@ dump_glyph (struct glyph_row *row, struct glyph *glyph, int area)
glyph->left_box_line_p,
glyph->right_box_line_p);
}
#ifdef HAVE_XWIDGETS
else if (glyph->type == XWIDGET_GLYPH)
{
fprintf (stderr,
" %5d %4c %6d %c %3d 0x%05x %c %4d %1.1d%1.1d\n",
glyph - row->glyphs[TEXT_AREA],
'X',
glyph->charpos,
(BUFFERP (glyph->object)
? 'B'
: (STRINGP (glyph->object)
? 'S'
: '-')),
glyph->pixel_width,
glyph->u.xwidget,
'.',
glyph->face_id,
glyph->left_box_line_p,
glyph->right_box_line_p);
}
#endif
}
@ -24291,6 +24364,13 @@ calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
return OK_PIXELS (width_p ? img->width : img->height);
}
# ifdef HAVE_XWIDGETS
if (FRAME_WINDOW_P (it->f) && valid_xwidget_spec_p (prop))
{
// TODO: Don't return dummy size.
return OK_PIXELS (100);
}
# endif
#endif
if (EQ (car, Qplus) || EQ (car, Qminus))
{
@ -24796,6 +24876,18 @@ fill_image_glyph_string (struct glyph_string *s)
}
#ifdef HAVE_XWIDGETS
static void
fill_xwidget_glyph_string (struct glyph_string *s)
{
eassert (s->first_glyph->type == XWIDGET_GLYPH);
s->face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
s->font = s->face->font;
s->width = s->first_glyph->pixel_width;
s->ybase += s->first_glyph->voffset;
s->xwidget = s->first_glyph->u.xwidget;
}
#endif
/* Fill glyph string S from a sequence of stretch glyphs.
START is the index of the first glyph to consider,
@ -25181,6 +25273,20 @@ compute_overhangs_and_x (struct glyph_string *s, int x, bool backward_p)
} \
while (false)
#ifdef HAVE_XWIDGETS
#define BUILD_XWIDGET_GLYPH_STRING(START, END, HEAD, TAIL, HL, X, LAST_X) \
do \
{ \
s = alloca (sizeof *s); \
INIT_GLYPH_STRING (s, NULL, w, row, area, START, HL); \
fill_xwidget_glyph_string (s); \
append_glyph_string (&(HEAD), &(TAIL), s); \
++(START); \
s->x = (X); \
} \
while (false)
#endif
/* Add a glyph string for a sequence of character glyphs to the list
of strings between HEAD and TAIL. START is the index of the first
@ -25302,7 +25408,7 @@ compute_overhangs_and_x (struct glyph_string *s, int x, bool backward_p)
to allocate glyph strings (because draw_glyphs can be called
asynchronously). */
#define BUILD_GLYPH_STRINGS(START, END, HEAD, TAIL, HL, X, LAST_X) \
#define BUILD_GLYPH_STRINGS_1(START, END, HEAD, TAIL, HL, X, LAST_X) \
do \
{ \
HEAD = TAIL = NULL; \
@ -25333,8 +25439,17 @@ compute_overhangs_and_x (struct glyph_string *s, int x, bool backward_p)
case IMAGE_GLYPH: \
BUILD_IMAGE_GLYPH_STRING (START, END, HEAD, TAIL, \
HL, X, LAST_X); \
break; \
\
break;
#ifdef HAVE_XWIDGETS
# define BUILD_GLYPH_STRINGS_XW(START, END, HEAD, TAIL, HL, X, LAST_X) \
case XWIDGET_GLYPH: \
BUILD_XWIDGET_GLYPH_STRING (START, END, HEAD, TAIL, \
HL, X, LAST_X); \
break;
#endif
#define BUILD_GLYPH_STRINGS_2(START, END, HEAD, TAIL, HL, X, LAST_X) \
case GLYPHLESS_GLYPH: \
BUILD_GLYPHLESS_GLYPH_STRING (START, END, HEAD, TAIL, \
HL, X, LAST_X); \
@ -25353,6 +25468,18 @@ compute_overhangs_and_x (struct glyph_string *s, int x, bool backward_p)
} while (false)
#ifdef HAVE_XWIDGETS
# define BUILD_GLYPH_STRINGS(START, END, HEAD, TAIL, HL, X, LAST_X) \
BUILD_GLYPH_STRINGS_1(START, END, HEAD, TAIL, HL, X, LAST_X) \
BUILD_GLYPH_STRINGS_XW(START, END, HEAD, TAIL, HL, X, LAST_X) \
BUILD_GLYPH_STRINGS_2(START, END, HEAD, TAIL, HL, X, LAST_X)
#else
# define BUILD_GLYPH_STRINGS(START, END, HEAD, TAIL, HL, X, LAST_X) \
BUILD_GLYPH_STRINGS_1(START, END, HEAD, TAIL, HL, X, LAST_X) \
BUILD_GLYPH_STRINGS_2(START, END, HEAD, TAIL, HL, X, LAST_X)
#endif
/* Draw glyphs between START and END in AREA of ROW on window W,
starting at x-position X. X is relative to AREA in W. HL is a
face-override with the following meaning:
@ -25991,6 +26118,109 @@ produce_image_glyph (struct it *it)
}
}
#ifdef HAVE_XWIDGETS
static void
produce_xwidget_glyph (struct it *it)
{
struct xwidget *xw;
int glyph_ascent, crop;
eassert (it->what == IT_XWIDGET);
struct face *face = FACE_FROM_ID (it->f, it->face_id);
eassert (face);
/* Make sure X resources of the face is loaded. */
prepare_face_for_display (it->f, face);
xw = it->xwidget;
it->ascent = it->phys_ascent = glyph_ascent = xw->height/2;
it->descent = xw->height/2;
it->phys_descent = it->descent;
it->pixel_width = xw->width;
/* It's quite possible for images to have an ascent greater than
their height, so don't get confused in that case. */
if (it->descent < 0)
it->descent = 0;
it->nglyphs = 1;
if (face->box != FACE_NO_BOX)
{
if (face->box_line_width > 0)
{
it->ascent += face->box_line_width;
it->descent += face->box_line_width;
}
if (it->start_of_box_run_p)
it->pixel_width += eabs (face->box_line_width);
it->pixel_width += eabs (face->box_line_width);
}
take_vertical_position_into_account (it);
/* Automatically crop wide image glyphs at right edge so we can
draw the cursor on same display row. */
crop = it->pixel_width - (it->last_visible_x - it->current_x);
if (crop > 0 && (it->hpos == 0 || it->pixel_width > it->last_visible_x / 4))
it->pixel_width -= crop;
if (it->glyph_row)
{
enum glyph_row_area area = it->area;
struct glyph *glyph
= it->glyph_row->glyphs[area] + it->glyph_row->used[area];
if (it->glyph_row->reversed_p)
{
struct glyph *g;
/* Make room for the new glyph. */
for (g = glyph - 1; g >= it->glyph_row->glyphs[it->area]; g--)
g[1] = *g;
glyph = it->glyph_row->glyphs[it->area];
}
if (glyph < it->glyph_row->glyphs[area + 1])
{
glyph->charpos = CHARPOS (it->position);
glyph->object = it->object;
glyph->pixel_width = it->pixel_width;
glyph->ascent = glyph_ascent;
glyph->descent = it->descent;
glyph->voffset = it->voffset;
glyph->type = XWIDGET_GLYPH;
glyph->avoid_cursor_p = it->avoid_cursor_p;
glyph->multibyte_p = it->multibyte_p;
if (it->glyph_row->reversed_p && area == TEXT_AREA)
{
/* In R2L rows, the left and the right box edges need to be
drawn in reverse direction. */
glyph->right_box_line_p = it->start_of_box_run_p;
glyph->left_box_line_p = it->end_of_box_run_p;
}
else
{
glyph->left_box_line_p = it->start_of_box_run_p;
glyph->right_box_line_p = it->end_of_box_run_p;
}
glyph->overlaps_vertically_p = 0;
glyph->padding_p = 0;
glyph->glyph_not_available_p = 0;
glyph->face_id = it->face_id;
glyph->u.xwidget = it->xwidget;
glyph->font_type = FONT_TYPE_UNKNOWN;
if (it->bidi_p)
{
glyph->resolved_level = it->bidi_it.resolved_level;
eassert ((it->bidi_it.type & 7) == it->bidi_it.type);
glyph->bidi_type = it->bidi_it.type;
}
++it->glyph_row->used[area];
}
else
IT_EXPAND_MATRIX_WIDTH (it, area);
}
}
#endif
/* Append a stretch glyph to IT->glyph_row. OBJECT is the source
of the glyph, WIDTH and HEIGHT are the width and height of the
@ -27401,6 +27631,10 @@ x_produce_glyphs (struct it *it)
produce_image_glyph (it);
else if (it->what == IT_STRETCH)
produce_stretch_glyph (it);
#ifdef HAVE_XWIDGETS
else if (it->what == IT_XWIDGET)
produce_xwidget_glyph (it);
#endif
done:
/* Accumulate dimensions. Note: can't assume that it->descent > 0
@ -27770,6 +28004,10 @@ get_window_cursor_type (struct window *w, struct glyph *glyph, int *width,
/* Use normal cursor if not blinked off. */
if (!w->cursor_off_p)
{
#ifdef HAVE_XWIDGETS
if (glyph != NULL && glyph->type == XWIDGET_GLYPH)
return NO_CURSOR;
#endif
if (glyph != NULL && glyph->type == IMAGE_GLYPH)
{
if (cursor_type == FILLED_BOX_CURSOR)

View file

@ -62,6 +62,9 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include "composite.h"
#include "frame.h"
#include "dispextern.h"
#ifdef HAVE_XWIDGETS
# include "xwidget.h"
#endif
#include "fontset.h"
#include "termhooks.h"
#include "termopts.h"
@ -3511,6 +3514,12 @@ x_draw_glyph_string (struct glyph_string *s)
x_draw_image_glyph_string (s);
break;
#ifdef HAVE_XWIDGETS
case XWIDGET_GLYPH:
x_draw_xwidget_glyph_string (s);
break;
#endif
case STRETCH_GLYPH:
x_draw_stretch_glyph_string (s);
break;
@ -8920,6 +8929,11 @@ x_draw_bar_cursor (struct window *w, struct glyph_row *row, int width, enum text
if (cursor_glyph == NULL)
return;
#ifdef HAVE_XWIDGETS
if (cursor_glyph->type == XWIDGET_GLYPH)
return; // Experimental avoidance of cursor on xwidget.
#endif
/* If on an image, draw like a normal cursor. That's usually better
visible than drawing a bar, esp. if the image is large so that
the bar might not be in the window. */

1332
src/xwidget.c Normal file

File diff suppressed because it is too large Load diff

135
src/xwidget.h Normal file
View file

@ -0,0 +1,135 @@
/* Support for embedding graphical components in a buffer.
Copyright (C) 2011-2015 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#ifndef XWIDGET_H_INCLUDED
#define XWIDGET_H_INCLUDED
void x_draw_xwidget_glyph_string (struct glyph_string *s);
void syms_of_xwidget (void);
//extern Lisp_Object Qxwidget;
bool valid_xwidget_spec_p (Lisp_Object object);
#include <gtk/gtk.h>
/*
each xwidget instance/model is described by this struct.
lisp pseudovector.
*/
struct xwidget
{
struct vectorlike_header header;
Lisp_Object plist; //auxilliary data
Lisp_Object type; //the widget type
Lisp_Object buffer; //buffer where xwidget lives
Lisp_Object title; //a title that is used for button labels for instance
//here ends the lisp part.
//"height" is the marker field
int height;
int width;
//for offscreen widgets, unused if not osr
GtkWidget *widget_osr;
GtkWidget *widgetwindow_osr;
//this is used if the widget (webkit) is to be wrapped in a scrolled window,
GtkWidget *widgetscrolledwindow_osr;
/* Non-nil means kill silently if Emacs is exited. */
unsigned int kill_without_query:1;
};
//struct for each xwidget view
struct xwidget_view
{
struct vectorlike_header header;
Lisp_Object model;
Lisp_Object w;
//here ends the lisp part.
//"redisplayed" is the marker field
int redisplayed; //if touched by redisplay
int hidden; //if the "live" instance isnt drawn
GtkWidget *widget;
GtkWidget *widgetwindow;
GtkWidget *emacswindow;
int x;
int y;
int clip_right;
int clip_bottom;
int clip_top;
int clip_left;
long handler_id;
};
/* Test for xwidget pseudovector*/
#define XWIDGETP(x) PSEUDOVECTORP (x, PVEC_XWIDGET)
#define XXWIDGET(a) (eassert (XWIDGETP(a)), \
(struct xwidget *) XUNTAG(a, Lisp_Vectorlike))
#define CHECK_XWIDGET(x) \
CHECK_TYPE (XWIDGETP (x), Qxwidgetp, x)
/* Test for xwidget_view pseudovector */
#define XWIDGET_VIEW_P(x) PSEUDOVECTORP (x, PVEC_XWIDGET_VIEW)
#define XXWIDGET_VIEW(a) (eassert (XWIDGET_VIEW_P(a)), \
(struct xwidget_view *) XUNTAG(a, Lisp_Vectorlike))
#define CHECK_XWIDGET_VIEW(x) \
CHECK_TYPE (XWIDGET_VIEW_P (x), Qxwidget_view_p, x)
struct xwidget_type
{
/* A symbol uniquely identifying the xwidget type, */
Lisp_Object *type;
/* Check that SPEC is a valid image specification for the given
image type. Value is non-zero if SPEC is valid. */
int (*valid_p) (Lisp_Object spec);
/* Next in list of all supported image types. */
struct xwidget_type *next;
};
struct xwidget *xwidget_from_id (int id);
void xwidget_start_redisplay (void);
void xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix);
void xwidget_touch (struct xwidget_view *xw);
struct xwidget *lookup_xwidget (Lisp_Object spec);
#define XG_XWIDGET "emacs_xwidget"
#define XG_XWIDGET_VIEW "emacs_xwidget_view"
void xwidget_view_delete_all_in_window (struct window *w);
void kill_buffer_xwidgets (Lisp_Object buffer);
#endif /* XWIDGET_H_INCLUDED */