Add xwidget support for macOS
Co-authored-by: Jaesup Kwak <veshboo@gmail.com> * configure.ac: Allow '--with-xwidgets' for "${NS_IMPL_COCOA}". * etc/NEWS: Mention new feature. * etc/TODO: Remove done TODO to implement xwidget in NeXTstep port. * lisp/xwidget.el (xwidget-webkit-clone-and-split-below) (xwidget-webkit-clone-and-split-right): New procedures. (xwidget-webkit-callback): Remove call to 'xwidget-webkit-adjust-size-to-window' as adjusting xwidget size is handled in 'x_draw_xwidget_glyph_string'. (xwidget-webkit-enable-plugins): New variable. * nextstep/templates/Info.plist.in: Add 'NSAppTransportSecurity'. * src/Makefile.in: Add nsxwidget.o for compilation. * src/emacs.c (main): Move conditional call to 'syms_of_xwidget'. * src/nsterm.m (ns_draw_glyph_string): Add case for 'XWIDGET_GLYPH'. (note_mouse_movement mouseMoved): Make it easy to resize window by dragging mode-line or vertical separator adjacent to large glyph. * src/nsxwidget.h src/nsxwidget.m: Newly added files, xwidget webkit backend for macOS Cocoa. * src/xwidget.c (Fmake_xwidget, xwidget_init_view) (x_draw_xwidget_glyph_string, xwidget_is_web_view) (Fxwidget_webkit_goto_uri, Fxwidget_webkit_zoom, Fxwidget_resize) (Fxwidget_size_request, Fdelete_xwidget_view, xwidget_end_redisplay) (kill_buffer_xwidgets): Add macOS Cocoa specific functions and code with 'NS_IMPL_COCOA' and guard GTK specific functions and code with 'USE_GTK'. (x_draw_xwidget_glyph_string): Handle adjusting xwidget size. * src/xwidget.h (xwidget, xwidget_view): Add macOS Cocoa specific fields with 'NS_IMPL_COCOA' and guard GTK specific fields with USE_GTK.
This commit is contained in:
parent
38d70f79a6
commit
d089c4fbfc
11 changed files with 935 additions and 35 deletions
34
configure.ac
34
configure.ac
|
@ -489,7 +489,7 @@ otherwise for the first of 'inotify', 'kqueue' or 'gfile' that is usable.])
|
|||
[with_file_notification=$with_features])
|
||||
|
||||
OPTION_DEFAULT_OFF([xwidgets],
|
||||
[enable use of some gtk widgets in Emacs buffers (requires gtk3)])
|
||||
[enable use of xwidgets in Emacs buffers (requires gtk3 or macOS Cocoa)])
|
||||
|
||||
## For the times when you want to build Emacs but don't have
|
||||
## a suitable makeinfo, and can live without the manuals.
|
||||
|
@ -2754,20 +2754,34 @@ fi
|
|||
|
||||
|
||||
dnl Enable xwidgets if GTK3 and WebKitGTK+ are available.
|
||||
dnl Enable xwidgets if macOS Cocoa and WebKit framework are available.
|
||||
HAVE_XWIDGETS=no
|
||||
XWIDGETS_OBJ=
|
||||
if test "$with_xwidgets" != "no"; then
|
||||
test "$USE_GTK_TOOLKIT" = "GTK3" && test "$window_system" != "none" ||
|
||||
AC_MSG_ERROR([xwidgets requested but gtk3 not used.])
|
||||
if test "$USE_GTK_TOOLKIT" = "GTK3" && test "$window_system" != "none"; then
|
||||
WEBKIT_REQUIRED=2.12
|
||||
WEBKIT_MODULES="webkit2gtk-4.0 >= $WEBKIT_REQUIRED"
|
||||
EMACS_CHECK_MODULES([WEBKIT], [$WEBKIT_MODULES])
|
||||
HAVE_XWIDGETS=$HAVE_WEBKIT
|
||||
XWIDGETS_OBJ="xwidget.o"
|
||||
elif test "${NS_IMPL_COCOA}" = "yes"; then
|
||||
dnl FIXME: Check framework WebKit2
|
||||
dnl WEBKIT_REQUIRED=M.m.p
|
||||
WEBKIT_LIBS="-Wl,-framework -Wl,WebKit"
|
||||
WEBKIT_CFLAGS="-I/System/Library/Frameworks/WebKit.framework/Headers"
|
||||
HAVE_WEBKIT="yes"
|
||||
HAVE_XWIDGETS=$HAVE_WEBKIT
|
||||
XWIDGETS_OBJ="xwidget.o"
|
||||
NS_OBJC_OBJ="$NS_OBJC_OBJ nsxwidget.o"
|
||||
dnl Update NS_OBJC_OBJ with added nsxwidget.o
|
||||
AC_SUBST(NS_OBJC_OBJ)
|
||||
else
|
||||
AC_MSG_ERROR([xwidgets requested, it requires GTK3 as X window toolkit or macOS Cocoa as window system.])
|
||||
fi
|
||||
|
||||
WEBKIT_REQUIRED=2.12
|
||||
WEBKIT_MODULES="webkit2gtk-4.0 >= $WEBKIT_REQUIRED"
|
||||
EMACS_CHECK_MODULES([WEBKIT], [$WEBKIT_MODULES])
|
||||
HAVE_XWIDGETS=$HAVE_WEBKIT
|
||||
test $HAVE_XWIDGETS = yes ||
|
||||
AC_MSG_ERROR([xwidgets requested but WebKitGTK+ not found.])
|
||||
AC_MSG_ERROR([xwidgets requested but WebKitGTK+ or WebKit framework not found.])
|
||||
|
||||
XWIDGETS_OBJ=xwidget.o
|
||||
AC_DEFINE([HAVE_XWIDGETS], 1, [Define to 1 if you have xwidgets support.])
|
||||
fi
|
||||
AC_SUBST(XWIDGETS_OBJ)
|
||||
|
@ -5688,7 +5702,7 @@ 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 (requires gtk3)? ${HAVE_XWIDGETS}
|
||||
Does Emacs support Xwidgets? ${HAVE_XWIDGETS}
|
||||
Does Emacs have threading support in lisp? ${threads_enabled}
|
||||
Does Emacs support the portable dumper? ${with_pdumper}
|
||||
Does Emacs support legacy unexec dumping? ${with_unexec}
|
||||
|
|
13
etc/NEWS
13
etc/NEWS
|
@ -958,6 +958,18 @@ convert them to a list '(R G B)' of primary color values.
|
|||
|
||||
* Changes in Emacs 28.1 on Non-Free Operating Systems
|
||||
|
||||
---
|
||||
** On macOS, Xwidget is now supported.
|
||||
If Emacs was built with xwidget support, you can access the embedded
|
||||
webkit browser with 'M-x xwidget-webkit-browse-url'. Viewing two
|
||||
instances of xwidget webkit is not supported.
|
||||
|
||||
*** New functions for xwidget-webkit mode
|
||||
'xwidget-webkit-clone-and-split-below',
|
||||
'xwidget-webkit-clone-and-split-right'.
|
||||
|
||||
*** New variable 'xwidget-webkit-enable-plugins'.
|
||||
|
||||
+++
|
||||
** On macOS, Emacs can now load dynamic modules with a ".dylib" suffix.
|
||||
'module-file-suffix' now has the value ".dylib" on macOS, but the
|
||||
|
@ -998,6 +1010,7 @@ 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
|
||||
Local variables:
|
||||
coding: utf-8
|
||||
|
|
|
@ -99,6 +99,24 @@ Interactively, URL defaults to the string looking like a url around point."
|
|||
(xwidget-webkit-new-session url)
|
||||
(xwidget-webkit-goto-url url))))
|
||||
|
||||
(defun xwidget-webkit-clone-and-split-below ()
|
||||
"Clone current URL into a new widget place in new window below.
|
||||
Get the URL of current session, then browse to the URL
|
||||
in `split-window-below' with a new xwidget webkit session."
|
||||
(interactive)
|
||||
(let ((url (xwidget-webkit-current-url)))
|
||||
(with-selected-window (split-window-below)
|
||||
(xwidget-webkit-new-session url))))
|
||||
|
||||
(defun xwidget-webkit-clone-and-split-right ()
|
||||
"Clone current URL into a new widget place in new window right.
|
||||
Get the URL of current session, then browse to the URL
|
||||
in `split-window-right' with a new xwidget webkit session."
|
||||
(interactive)
|
||||
(let ((url (xwidget-webkit-current-url)))
|
||||
(with-selected-window (split-window-right)
|
||||
(xwidget-webkit-new-session url))))
|
||||
|
||||
;;todo.
|
||||
;; - check that the webkit support is compiled in
|
||||
(defvar xwidget-webkit-mode-map
|
||||
|
@ -222,9 +240,9 @@ XWIDGET instance, XWIDGET-EVENT-TYPE depends on the originating xwidget."
|
|||
xwidget "document.title"
|
||||
(lambda (title)
|
||||
(xwidget-log "webkit finished loading: '%s'" title)
|
||||
;;TODO - check the native/internal scroll
|
||||
;;(xwidget-adjust-size-to-content xwidget)
|
||||
(xwidget-webkit-adjust-size-to-window xwidget)
|
||||
;; Do not adjust webkit size to window here, the
|
||||
;; selected window can be the mini-buffer window
|
||||
;; unwantedly.
|
||||
(rename-buffer (format "*xwidget webkit: %s *" title))))
|
||||
(pop-to-buffer (current-buffer)))
|
||||
((eq xwidget-event-type 'decide-policy)
|
||||
|
@ -240,6 +258,11 @@ XWIDGET instance, XWIDGET-EVENT-TYPE depends on the originating xwidget."
|
|||
(t (xwidget-log "unhandled event:%s" xwidget-event-type))))))
|
||||
|
||||
(defvar bookmark-make-record-function)
|
||||
(when (memq window-system '(mac ns))
|
||||
(defvar xwidget-webkit-enable-plugins nil
|
||||
"Enable plugins for xwidget webkit.
|
||||
If non-nil, plugins are enabled. Otherwise, disabled."))
|
||||
|
||||
(define-derived-mode xwidget-webkit-mode
|
||||
special-mode "xwidget-webkit" "Xwidget webkit view mode."
|
||||
(setq buffer-read-only t)
|
||||
|
|
|
@ -675,8 +675,16 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
|||
</array>
|
||||
<key>NSAppleScriptEnabled</key>
|
||||
<string>YES</string>
|
||||
<key>NSAppleEventsUsageDescription</key>
|
||||
<string>Emacs requires permission to send AppleEvents to other applications.</string>
|
||||
<key>NSAppleEventsUsageDescription</key>
|
||||
<string>Emacs requires permission to send AppleEvents to other applications.</string>
|
||||
<!-- For xwidget webkit to browse remote url,
|
||||
but this set no restriction at all. Consult apple's documentation
|
||||
for detail information about `NSApplicationDefinedMask'. -->
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSDesktopFolderUsageDescription</key>
|
||||
<string>Emacs requires permission to access the Desktop folder.</string>
|
||||
<key>NSDocumentsFolderUsageDescription</key>
|
||||
|
|
|
@ -433,6 +433,7 @@ SOME_MACHINE_OBJECTS = dosfns.o msdos.o \
|
|||
xterm.o xfns.o xmenu.o xselect.o xrdb.o xsmfns.o fringe.o image.o \
|
||||
fontset.o dbusbind.o cygw32.o \
|
||||
nsterm.o nsfns.o nsmenu.o nsselect.o nsimage.o nsfont.o macfont.o \
|
||||
nsxwidget.o \
|
||||
w32.o w32console.o w32cygwinx.o w32fns.o w32heap.o w32inevt.o w32notify.o \
|
||||
w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \
|
||||
w16select.o widget.o xfont.o ftfont.o xftfont.o gtkutil.o \
|
||||
|
|
|
@ -1860,7 +1860,6 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
|
|||
syms_of_xfns ();
|
||||
syms_of_xmenu ();
|
||||
syms_of_fontset ();
|
||||
syms_of_xwidget ();
|
||||
syms_of_xsettings ();
|
||||
#ifdef HAVE_X_SM
|
||||
syms_of_xsmfns ();
|
||||
|
@ -1937,6 +1936,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
|
|||
#endif /* HAVE_W32NOTIFY */
|
||||
#endif /* WINDOWSNT */
|
||||
|
||||
syms_of_xwidget ();
|
||||
syms_of_threads ();
|
||||
syms_of_profiler ();
|
||||
syms_of_pdumper ();
|
||||
|
|
21
src/nsterm.m
21
src/nsterm.m
|
@ -49,6 +49,7 @@ Updated by Christian Limpach (chris@nice.ch)
|
|||
#include "nsterm.h"
|
||||
#include "systime.h"
|
||||
#include "character.h"
|
||||
#include "xwidget.h"
|
||||
#include "fontset.h"
|
||||
#include "composite.h"
|
||||
#include "ccl.h"
|
||||
|
@ -2600,7 +2601,8 @@ so some key presses (TAB) are swallowed by the system. */
|
|||
}
|
||||
|
||||
static int
|
||||
ns_note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y)
|
||||
ns_note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y,
|
||||
BOOL dragging)
|
||||
/* ------------------------------------------------------------------------
|
||||
Called by EmacsView on mouseMovement events. Passes on
|
||||
to emacs mainstream code if we moved off of a rect of interest
|
||||
|
@ -2609,17 +2611,24 @@ so some key presses (TAB) are swallowed by the system. */
|
|||
{
|
||||
struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
|
||||
NSRect *r;
|
||||
BOOL force_update = NO;
|
||||
|
||||
// NSTRACE ("note_mouse_movement");
|
||||
|
||||
dpyinfo->last_mouse_motion_frame = frame;
|
||||
r = &dpyinfo->last_mouse_glyph;
|
||||
|
||||
/* If the last rect is too large (ex, xwidget webkit), update at
|
||||
every move, or resizing by dragging modeline or vertical split is
|
||||
very hard to make its way. */
|
||||
if (dragging && (r->size.width > 32 || r->size.height > 32))
|
||||
force_update = YES;
|
||||
|
||||
/* Note, this doesn't get called for enter/leave, since we don't have a
|
||||
position. Those are taken care of in the corresponding NSView methods. */
|
||||
|
||||
/* Has movement gone beyond last rect we were tracking? */
|
||||
if (x < r->origin.x || x >= r->origin.x + r->size.width
|
||||
if (force_update || x < r->origin.x || x >= r->origin.x + r->size.width
|
||||
|| y < r->origin.y || y >= r->origin.y + r->size.height)
|
||||
{
|
||||
ns_update_begin (frame);
|
||||
|
@ -4368,6 +4377,10 @@ overwriting cursor (usually when cursor on a tab) */
|
|||
ns_unfocus (s->f);
|
||||
break;
|
||||
|
||||
case XWIDGET_GLYPH:
|
||||
x_draw_xwidget_glyph_string (s);
|
||||
break;
|
||||
|
||||
case STRETCH_GLYPH:
|
||||
ns_dumpglyphs_stretch (s);
|
||||
break;
|
||||
|
@ -7065,6 +7078,7 @@ - (void)mouseMoved: (NSEvent *)e
|
|||
struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
|
||||
Lisp_Object frame;
|
||||
NSPoint pt;
|
||||
BOOL dragging;
|
||||
|
||||
NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsView mouseMoved:]");
|
||||
|
||||
|
@ -7107,7 +7121,8 @@ - (void)mouseMoved: (NSEvent *)e
|
|||
last_mouse_window = window;
|
||||
}
|
||||
|
||||
if (!ns_note_mouse_movement (emacsframe, pt.x, pt.y))
|
||||
dragging = (e.type == NSEventTypeLeftMouseDragged);
|
||||
if (!ns_note_mouse_movement (emacsframe, pt.x, pt.y, dragging))
|
||||
help_echo_string = previous_help_echo_string;
|
||||
|
||||
XSETFRAME (frame, emacsframe);
|
||||
|
|
77
src/nsxwidget.h
Normal file
77
src/nsxwidget.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
/* Header for NS Cocoa part of xwidget and webkit widget.
|
||||
|
||||
Copyright (C) 2019 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 NSXWIDGET_H_INCLUDED
|
||||
#define NSXWIDGET_H_INCLUDED
|
||||
|
||||
/* This file can be included from non-objc files through 'xwidget.h'. */
|
||||
#ifdef __OBJC__
|
||||
#import <AppKit/NSView.h>
|
||||
#endif
|
||||
|
||||
#include "dispextern.h"
|
||||
#include "lisp.h"
|
||||
#include "xwidget.h"
|
||||
|
||||
/* Functions for xwidget webkit. */
|
||||
|
||||
bool nsxwidget_is_web_view (struct xwidget *xw);
|
||||
void nsxwidget_webkit_goto_uri (struct xwidget *xw, const char *uri);
|
||||
void nsxwidget_webkit_zoom (struct xwidget *xw, double zoom_change);
|
||||
void nsxwidget_webkit_execute_script (struct xwidget *xw, const char *script,
|
||||
Lisp_Object fun);
|
||||
|
||||
/* Functions for xwidget model. */
|
||||
|
||||
#ifdef __OBJC__
|
||||
@interface XwWindow : NSView
|
||||
@property struct xwidget *xw;
|
||||
@end
|
||||
#endif
|
||||
|
||||
void nsxwidget_init (struct xwidget *xw);
|
||||
void nsxwidget_kill (struct xwidget *xw);
|
||||
void nsxwidget_resize (struct xwidget *xw);
|
||||
Lisp_Object nsxwidget_get_size (struct xwidget *xw);
|
||||
|
||||
/* Functions for xwidget view. */
|
||||
|
||||
#ifdef __OBJC__
|
||||
@interface XvWindow : NSView
|
||||
@property struct xwidget *xw;
|
||||
@property struct xwidget_view *xv;
|
||||
@end
|
||||
#endif
|
||||
|
||||
void nsxwidget_init_view (struct xwidget_view *xv,
|
||||
struct xwidget *xww,
|
||||
struct glyph_string *s,
|
||||
int x, int y);
|
||||
void nsxwidget_delete_view (struct xwidget_view *xv);
|
||||
|
||||
void nsxwidget_show_view (struct xwidget_view *xv);
|
||||
void nsxwidget_hide_view (struct xwidget_view *xv);
|
||||
void nsxwidget_resize_view (struct xwidget_view *xv,
|
||||
int widget, int height);
|
||||
|
||||
void nsxwidget_move_view (struct xwidget_view *xv, int x, int y);
|
||||
void nsxwidget_move_widget_in_view (struct xwidget_view *xv, int x, int y);
|
||||
void nsxwidget_set_needsdisplay (struct xwidget_view *xv);
|
||||
|
||||
#endif /* NSXWIDGET_H_INCLUDED */
|
563
src/nsxwidget.m
Normal file
563
src/nsxwidget.m
Normal file
|
@ -0,0 +1,563 @@
|
|||
/* NS Cocoa part implementation of xwidget and webkit widget.
|
||||
|
||||
Copyright (C) 2019 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/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "lisp.h"
|
||||
#include "blockinput.h"
|
||||
#include "dispextern.h"
|
||||
#include "buffer.h"
|
||||
#include "frame.h"
|
||||
#include "nsterm.h"
|
||||
#include "xwidget.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
|
||||
/* Thoughts on NS Cocoa xwidget and webkit2:
|
||||
|
||||
Webkit2 process architecture seems to be very hostile for offscreen
|
||||
rendering techniques, which is used by GTK xwiget implementation;
|
||||
Specifically NSView level view sharing / copying is not working.
|
||||
|
||||
*** So only one view can be associcated with a model. ***
|
||||
|
||||
With this decision, implementation is plain and can expect best out
|
||||
of webkit2's rationale. But process and session structures will
|
||||
diverge from GTK xwiget. Though, cosmetically similar usages can
|
||||
be presented and will be preferred, if agreeable.
|
||||
|
||||
For other widget types, OSR seems possible, but will not care for a
|
||||
while. */
|
||||
|
||||
/* Xwidget webkit. */
|
||||
|
||||
@interface XwWebView : WKWebView
|
||||
<WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler>
|
||||
@property struct xwidget *xw;
|
||||
/* Map url to whether javascript is blocked by
|
||||
'Content-Security-Policy' sandbox without allow-scripts. */
|
||||
@property(retain) NSMutableDictionary *urlScriptBlocked;
|
||||
@end
|
||||
@implementation XwWebView : WKWebView
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
configuration:(WKWebViewConfiguration *)configuration
|
||||
xwidget:(struct xwidget *)xw
|
||||
{
|
||||
/* Script controller to add script message handler and user script. */
|
||||
WKUserContentController *scriptor = [[WKUserContentController alloc] init];
|
||||
configuration.userContentController = scriptor;
|
||||
|
||||
/* Enable inspect element context menu item for debugging. */
|
||||
[configuration.preferences setValue:@YES
|
||||
forKey:@"developerExtrasEnabled"];
|
||||
|
||||
Lisp_Object enablePlugins =
|
||||
Fintern (build_string ("xwidget-webkit-enable-plugins"), Qnil);
|
||||
if (!EQ (Fsymbol_value (enablePlugins), Qnil))
|
||||
configuration.preferences.plugInsEnabled = YES;
|
||||
|
||||
self = [super initWithFrame:frame configuration:configuration];
|
||||
if (self)
|
||||
{
|
||||
self.xw = xw;
|
||||
self.urlScriptBlocked = [[NSMutableDictionary alloc] init];
|
||||
self.navigationDelegate = self;
|
||||
self.UIDelegate = self;
|
||||
self.customUserAgent =
|
||||
@"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6)"
|
||||
@" AppleWebKit/603.3.8 (KHTML, like Gecko)"
|
||||
@" Version/11.0.1 Safari/603.3.8";
|
||||
[scriptor addScriptMessageHandler:self name:@"keyDown"];
|
||||
[scriptor addUserScript:[[WKUserScript alloc]
|
||||
initWithSource:xwScript
|
||||
injectionTime:
|
||||
WKUserScriptInjectionTimeAtDocumentStart
|
||||
forMainFrameOnly:NO]];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)webView:(WKWebView *)webView
|
||||
didFinishNavigation:(WKNavigation *)navigation
|
||||
{
|
||||
if (EQ (Fbuffer_live_p (self.xw->buffer), Qt))
|
||||
store_xwidget_event_string (self.xw, "load-changed", "");
|
||||
}
|
||||
|
||||
- (void)webView:(WKWebView *)webView
|
||||
decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
|
||||
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
|
||||
{
|
||||
switch (navigationAction.navigationType) {
|
||||
case WKNavigationTypeLinkActivated:
|
||||
decisionHandler (WKNavigationActionPolicyAllow);
|
||||
break;
|
||||
default:
|
||||
// decisionHandler (WKNavigationActionPolicyCancel);
|
||||
decisionHandler (WKNavigationActionPolicyAllow);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)webView:(WKWebView *)webView
|
||||
decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse
|
||||
decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
|
||||
{
|
||||
decisionHandler (WKNavigationResponsePolicyAllow);
|
||||
|
||||
self.urlScriptBlocked[navigationResponse.response.URL] =
|
||||
[NSNumber numberWithBool:NO];
|
||||
if ([navigationResponse.response isKindOfClass:[NSHTTPURLResponse class]])
|
||||
{
|
||||
NSDictionary *headers =
|
||||
((NSHTTPURLResponse *) navigationResponse.response).allHeaderFields;
|
||||
NSString *value = headers[@"Content-Security-Policy"];
|
||||
if (value)
|
||||
{
|
||||
/* TODO: Sloppy parsing of 'Content-Security-Policy' value. */
|
||||
NSRange sandbox = [value rangeOfString:@"sandbox"];
|
||||
if (sandbox.location != NSNotFound
|
||||
&& (sandbox.location == 0
|
||||
|| [value characterAtIndex:(sandbox.location - 1)] == ' '
|
||||
|| [value characterAtIndex:(sandbox.location - 1)] == ';'))
|
||||
{
|
||||
NSRange allowScripts = [value rangeOfString:@"allow-scripts"];
|
||||
if (allowScripts.location == NSNotFound
|
||||
|| allowScripts.location < sandbox.location)
|
||||
self.urlScriptBlocked[navigationResponse.response.URL] =
|
||||
[NSNumber numberWithBool:YES];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* No additional new webview or emacs window will be created
|
||||
for <a ... target="_blank">. */
|
||||
- (WKWebView *)webView:(WKWebView *)webView
|
||||
createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration
|
||||
forNavigationAction:(WKNavigationAction *)navigationAction
|
||||
windowFeatures:(WKWindowFeatures *)windowFeatures
|
||||
{
|
||||
if (!navigationAction.targetFrame.isMainFrame)
|
||||
[webView loadRequest:navigationAction.request];
|
||||
return nil;
|
||||
}
|
||||
|
||||
/* Open panel for file upload. */
|
||||
- (void)webView:(WKWebView *)webView
|
||||
runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters
|
||||
initiatedByFrame:(WKFrameInfo *)frame
|
||||
completionHandler:(void (^)(NSArray<NSURL *> *URLs))completionHandler
|
||||
{
|
||||
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
|
||||
openPanel.canChooseFiles = YES;
|
||||
openPanel.canChooseDirectories = NO;
|
||||
openPanel.allowsMultipleSelection = parameters.allowsMultipleSelection;
|
||||
if ([openPanel runModal] == NSModalResponseOK)
|
||||
completionHandler (openPanel.URLs);
|
||||
else
|
||||
completionHandler (nil);
|
||||
}
|
||||
|
||||
/* By forwarding mouse events to emacs view (frame)
|
||||
- Mouse click in webview selects the window contains the webview.
|
||||
- Correct mouse hand/arrow/I-beam is displayed (TODO: not perfect yet).
|
||||
*/
|
||||
|
||||
- (void)mouseDown:(NSEvent *)event
|
||||
{
|
||||
[self.xw->xv->emacswindow mouseDown:event];
|
||||
[super mouseDown:event];
|
||||
}
|
||||
|
||||
- (void)mouseUp:(NSEvent *)event
|
||||
{
|
||||
[self.xw->xv->emacswindow mouseUp:event];
|
||||
[super mouseUp:event];
|
||||
}
|
||||
|
||||
/* Basically we want keyboard events handled by emacs unless an input
|
||||
element has focus. Especially, while incremental search, we set
|
||||
emacs as first responder to avoid focus held in an input element
|
||||
with matching text. */
|
||||
|
||||
- (void)keyDown:(NSEvent *)event
|
||||
{
|
||||
Lisp_Object var = Fintern (build_string ("isearch-mode"), Qnil);
|
||||
Lisp_Object val = buffer_local_value (var, Fcurrent_buffer ());
|
||||
if (!EQ (val, Qunbound) && !EQ (val, Qnil))
|
||||
{
|
||||
[self.window makeFirstResponder:self.xw->xv->emacswindow];
|
||||
[self.xw->xv->emacswindow keyDown:event];
|
||||
return;
|
||||
}
|
||||
|
||||
/* Emacs handles keyboard events when javascript is blocked. */
|
||||
if ([self.urlScriptBlocked[self.URL] boolValue])
|
||||
{
|
||||
[self.xw->xv->emacswindow keyDown:event];
|
||||
return;
|
||||
}
|
||||
|
||||
[self evaluateJavaScript:@"xwHasFocus()"
|
||||
completionHandler:^(id result, NSError *error) {
|
||||
if (error)
|
||||
{
|
||||
NSLog (@"xwHasFocus: %@", error);
|
||||
[self.xw->xv->emacswindow keyDown:event];
|
||||
}
|
||||
else if (result)
|
||||
{
|
||||
NSNumber *hasFocus = result; /* __NSCFBoolean */
|
||||
if (!hasFocus.boolValue)
|
||||
[self.xw->xv->emacswindow keyDown:event];
|
||||
else
|
||||
[super keyDown:event];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)interpretKeyEvents:(NSArray<NSEvent *> *)eventArray
|
||||
{
|
||||
/* We should do nothing and do not forward (default implementation
|
||||
if we not override here) to let emacs collect key events and ask
|
||||
interpretKeyEvents to its superclass. */
|
||||
}
|
||||
|
||||
static NSString *xwScript;
|
||||
+ (void)initialize
|
||||
{
|
||||
/* Find out if an input element has focus.
|
||||
Message to script message handler when 'C-g' key down. */
|
||||
if (!xwScript)
|
||||
xwScript =
|
||||
@"function xwHasFocus() {"
|
||||
@" var ae = document.activeElement;"
|
||||
@" if (ae) {"
|
||||
@" var name = ae.nodeName;"
|
||||
@" return name == 'INPUT' || name == 'TEXTAREA';"
|
||||
@" } else {"
|
||||
@" return false;"
|
||||
@" }"
|
||||
@"}"
|
||||
@"function xwKeyDown(event) {"
|
||||
@" if (event.ctrlKey && event.key == 'g') {"
|
||||
@" window.webkit.messageHandlers.keyDown.postMessage('C-g');"
|
||||
@" }"
|
||||
@"}"
|
||||
@"document.addEventListener('keydown', xwKeyDown);"
|
||||
;
|
||||
}
|
||||
|
||||
/* Confirming to WKScriptMessageHandler, listens concerning keyDown in
|
||||
webkit. Currently 'C-g'. */
|
||||
- (void)userContentController:(WKUserContentController *)userContentController
|
||||
didReceiveScriptMessage:(WKScriptMessage *)message
|
||||
{
|
||||
if ([message.body isEqualToString:@"C-g"])
|
||||
{
|
||||
/* Just give up focus, no relay "C-g" to emacs, another "C-g"
|
||||
follows will be handled by emacs. */
|
||||
[self.window makeFirstResponder:self.xw->xv->emacswindow];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/* Xwidget webkit commands. */
|
||||
|
||||
static Lisp_Object build_string_with_nsstr (NSString *nsstr);
|
||||
|
||||
bool
|
||||
nsxwidget_is_web_view (struct xwidget *xw)
|
||||
{
|
||||
return xw->xwWidget != NULL &&
|
||||
[xw->xwWidget isKindOfClass:WKWebView.class];
|
||||
}
|
||||
/* @Note ATS - Need application transport security in 'Info.plist' or
|
||||
remote pages will not loaded. */
|
||||
void
|
||||
nsxwidget_webkit_goto_uri (struct xwidget *xw, const char *uri)
|
||||
{
|
||||
XwWebView *xwWebView = (XwWebView *) xw->xwWidget;
|
||||
NSString *urlString = [NSString stringWithUTF8String:uri];
|
||||
NSURL *url = [NSURL URLWithString:urlString];
|
||||
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
|
||||
[xwWebView loadRequest:urlRequest];
|
||||
}
|
||||
|
||||
void
|
||||
nsxwidget_webkit_zoom (struct xwidget *xw, double zoom_change)
|
||||
{
|
||||
XwWebView *xwWebView = (XwWebView *) xw->xwWidget;
|
||||
xwWebView.magnification += zoom_change;
|
||||
/* TODO: setMagnification:centeredAtPoint. */
|
||||
}
|
||||
|
||||
/* Build lisp string */
|
||||
static Lisp_Object
|
||||
build_string_with_nsstr (NSString *nsstr)
|
||||
{
|
||||
const char *utfstr = [nsstr UTF8String];
|
||||
NSUInteger bytes = [nsstr lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
|
||||
return make_string (utfstr, bytes);
|
||||
}
|
||||
|
||||
/* Recursively convert an objc native type JavaScript value to a Lisp
|
||||
value. Mostly copied from GTK xwidget 'webkit_js_to_lisp'. */
|
||||
static Lisp_Object
|
||||
js_to_lisp (id value)
|
||||
{
|
||||
if (value == nil || [value isKindOfClass:NSNull.class])
|
||||
return Qnil;
|
||||
else if ([value isKindOfClass:NSString.class])
|
||||
return build_string_with_nsstr ((NSString *) value);
|
||||
else if ([value isKindOfClass:NSNumber.class])
|
||||
{
|
||||
NSNumber *nsnum = (NSNumber *) value;
|
||||
char type = nsnum.objCType[0];
|
||||
if (type == 'c') /* __NSCFBoolean has type character 'c'. */
|
||||
return nsnum.boolValue? Qt : Qnil;
|
||||
else
|
||||
{
|
||||
if (type == 'i' || type == 'l')
|
||||
return make_int (nsnum.longValue);
|
||||
else if (type == 'f' || type == 'd')
|
||||
return make_float (nsnum.doubleValue);
|
||||
/* else fall through. */
|
||||
}
|
||||
}
|
||||
else if ([value isKindOfClass:NSArray.class])
|
||||
{
|
||||
NSArray *nsarr = (NSArray *) value;
|
||||
EMACS_INT n = nsarr.count;
|
||||
Lisp_Object obj;
|
||||
struct Lisp_Vector *p = allocate_vector (n);
|
||||
|
||||
for (ptrdiff_t i = 0; i < n; ++i)
|
||||
p->contents[i] = js_to_lisp ([nsarr objectAtIndex:i]);
|
||||
XSETVECTOR (obj, p);
|
||||
return obj;
|
||||
}
|
||||
else if ([value isKindOfClass:NSDictionary.class])
|
||||
{
|
||||
NSDictionary *nsdict = (NSDictionary *) value;
|
||||
NSArray *keys = nsdict.allKeys;
|
||||
ptrdiff_t n = keys.count;
|
||||
Lisp_Object obj;
|
||||
struct Lisp_Vector *p = allocate_vector (n);
|
||||
|
||||
for (ptrdiff_t i = 0; i < n; ++i)
|
||||
{
|
||||
NSString *prop_key = (NSString *) [keys objectAtIndex:i];
|
||||
id prop_value = [nsdict valueForKey:prop_key];
|
||||
p->contents[i] = Fcons (build_string_with_nsstr (prop_key),
|
||||
js_to_lisp (prop_value));
|
||||
}
|
||||
XSETVECTOR (obj, p);
|
||||
return obj;
|
||||
}
|
||||
NSLog (@"Unhandled type in javascript result");
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
void
|
||||
nsxwidget_webkit_execute_script (struct xwidget *xw, const char *script,
|
||||
Lisp_Object fun)
|
||||
{
|
||||
XwWebView *xwWebView = (XwWebView *) xw->xwWidget;
|
||||
if ([xwWebView.urlScriptBlocked[xwWebView.URL] boolValue])
|
||||
{
|
||||
message ("Javascript is blocked by 'CSP: sandbox'.");
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *javascriptString = [NSString stringWithUTF8String:script];
|
||||
[xwWebView evaluateJavaScript:javascriptString
|
||||
completionHandler:^(id result, NSError *error) {
|
||||
if (error)
|
||||
{
|
||||
NSLog (@"evaluateJavaScript error : %@", error.localizedDescription);
|
||||
NSLog (@"error script=%@", javascriptString);
|
||||
}
|
||||
else if (result && FUNCTIONP (fun))
|
||||
{
|
||||
// NSLog (@"result=%@, type=%@", result, [result class]);
|
||||
Lisp_Object lisp_value = js_to_lisp (result);
|
||||
store_xwidget_js_callback_event (xw, fun, lisp_value);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
/* Window containing an xwidget. */
|
||||
|
||||
@implementation XwWindow
|
||||
- (BOOL)isFlipped { return YES; }
|
||||
@end
|
||||
|
||||
/* Xwidget model, macOS Cocoa part. */
|
||||
|
||||
void
|
||||
nsxwidget_init(struct xwidget *xw)
|
||||
{
|
||||
block_input ();
|
||||
NSRect rect = NSMakeRect (0, 0, xw->width, xw->height);
|
||||
xw->xwWidget = [[XwWebView alloc]
|
||||
initWithFrame:rect
|
||||
configuration:[[WKWebViewConfiguration alloc] init]
|
||||
xwidget:xw];
|
||||
xw->xwWindow = [[XwWindow alloc]
|
||||
initWithFrame:rect];
|
||||
[xw->xwWindow addSubview:xw->xwWidget];
|
||||
xw->xv = NULL; /* for 1 to 1 relationship of webkit2. */
|
||||
unblock_input ();
|
||||
}
|
||||
|
||||
void
|
||||
nsxwidget_kill (struct xwidget *xw)
|
||||
{
|
||||
if (xw)
|
||||
{
|
||||
WKUserContentController *scriptor =
|
||||
((XwWebView *) xw->xwWidget).configuration.userContentController;
|
||||
[scriptor removeAllUserScripts];
|
||||
[scriptor removeScriptMessageHandlerForName:@"keyDown"];
|
||||
[scriptor release];
|
||||
if (xw->xv)
|
||||
xw->xv->model = Qnil; /* Make sure related view stale. */
|
||||
|
||||
/* This stops playing audio when a xwidget-webkit buffer is
|
||||
killed. I could not find other solution. */
|
||||
nsxwidget_webkit_goto_uri (xw, "about:blank");
|
||||
|
||||
[((XwWebView *) xw->xwWidget).urlScriptBlocked release];
|
||||
[xw->xwWidget removeFromSuperviewWithoutNeedingDisplay];
|
||||
[xw->xwWidget release];
|
||||
[xw->xwWindow removeFromSuperviewWithoutNeedingDisplay];
|
||||
[xw->xwWindow release];
|
||||
xw->xwWidget = nil;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsxwidget_resize (struct xwidget *xw)
|
||||
{
|
||||
if (xw->xwWidget)
|
||||
{
|
||||
[xw->xwWindow setFrameSize:NSMakeSize(xw->width, xw->height)];
|
||||
[xw->xwWidget setFrameSize:NSMakeSize(xw->width, xw->height)];
|
||||
}
|
||||
}
|
||||
|
||||
Lisp_Object
|
||||
nsxwidget_get_size (struct xwidget *xw)
|
||||
{
|
||||
return list2i (xw->xwWidget.frame.size.width,
|
||||
xw->xwWidget.frame.size.height);
|
||||
}
|
||||
|
||||
/* Xwidget view, macOS Cocoa part. */
|
||||
|
||||
@implementation XvWindow : NSView
|
||||
- (BOOL)isFlipped { return YES; }
|
||||
@end
|
||||
|
||||
void
|
||||
nsxwidget_init_view (struct xwidget_view *xv,
|
||||
struct xwidget *xw,
|
||||
struct glyph_string *s,
|
||||
int x, int y)
|
||||
{
|
||||
/* 'x_draw_xwidget_glyph_string' will calculate correct position and
|
||||
size of clip to draw in emacs buffer window. Thus, just begin at
|
||||
origin with no crop. */
|
||||
xv->x = x;
|
||||
xv->y = y;
|
||||
xv->clip_left = 0;
|
||||
xv->clip_right = xw->width;
|
||||
xv->clip_top = 0;
|
||||
xv->clip_bottom = xw->height;
|
||||
|
||||
xv->xvWindow = [[XvWindow alloc]
|
||||
initWithFrame:NSMakeRect (x, y, xw->width, xw->height)];
|
||||
xv->xvWindow.xw = xw;
|
||||
xv->xvWindow.xv = xv;
|
||||
|
||||
xw->xv = xv; /* For 1 to 1 relationship of webkit2. */
|
||||
[xv->xvWindow addSubview:xw->xwWindow];
|
||||
|
||||
xv->emacswindow = FRAME_NS_VIEW (s->f);
|
||||
[xv->emacswindow addSubview:xv->xvWindow];
|
||||
}
|
||||
|
||||
void
|
||||
nsxwidget_delete_view (struct xwidget_view *xv)
|
||||
{
|
||||
if (!EQ (xv->model, Qnil))
|
||||
{
|
||||
struct xwidget *xw = XXWIDGET (xv->model);
|
||||
[xw->xwWindow removeFromSuperviewWithoutNeedingDisplay];
|
||||
xw->xv = NULL; /* Now model has no view. */
|
||||
}
|
||||
[xv->xvWindow removeFromSuperviewWithoutNeedingDisplay];
|
||||
[xv->xvWindow release];
|
||||
}
|
||||
|
||||
void
|
||||
nsxwidget_show_view (struct xwidget_view *xv)
|
||||
{
|
||||
xv->hidden = NO;
|
||||
[xv->xvWindow setFrameOrigin:NSMakePoint(xv->x + xv->clip_left,
|
||||
xv->y + xv->clip_top)];
|
||||
}
|
||||
|
||||
void
|
||||
nsxwidget_hide_view (struct xwidget_view *xv)
|
||||
{
|
||||
xv->hidden = YES;
|
||||
[xv->xvWindow setFrameOrigin:NSMakePoint(10000, 10000)];
|
||||
}
|
||||
|
||||
void
|
||||
nsxwidget_resize_view (struct xwidget_view *xv, int width, int height)
|
||||
{
|
||||
[xv->xvWindow setFrameSize:NSMakeSize(width, height)];
|
||||
}
|
||||
|
||||
void
|
||||
nsxwidget_move_view (struct xwidget_view *xv, int x, int y)
|
||||
{
|
||||
[xv->xvWindow setFrameOrigin:NSMakePoint (x, y)];
|
||||
}
|
||||
|
||||
/* Move model window in container (view window). */
|
||||
void
|
||||
nsxwidget_move_widget_in_view (struct xwidget_view *xv, int x, int y)
|
||||
{
|
||||
struct xwidget *xww = xv->xvWindow.xw;
|
||||
[xww->xwWindow setFrameOrigin:NSMakePoint (x, y)];
|
||||
}
|
||||
|
||||
void
|
||||
nsxwidget_set_needsdisplay (struct xwidget_view *xv)
|
||||
{
|
||||
xv->xvWindow.needsDisplay = YES;
|
||||
}
|
175
src/xwidget.c
175
src/xwidget.c
|
@ -23,13 +23,21 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
|||
|
||||
#include "lisp.h"
|
||||
#include "blockinput.h"
|
||||
#include "dispextern.h"
|
||||
#include "frame.h"
|
||||
#include "keyboard.h"
|
||||
#include "gtkutil.h"
|
||||
#include "sysstdio.h"
|
||||
#include "termhooks.h"
|
||||
#include "window.h"
|
||||
|
||||
/* Include xwidget bottom end headers. */
|
||||
#ifdef USE_GTK
|
||||
#include <webkit2/webkit2.h>
|
||||
#include <JavaScriptCore/JavaScript.h>
|
||||
#elif defined NS_IMPL_COCOA
|
||||
#include "nsxwidget.h"
|
||||
#endif
|
||||
|
||||
static struct xwidget *
|
||||
allocate_xwidget (void)
|
||||
|
@ -48,6 +56,7 @@ allocate_xwidget_view (void)
|
|||
|
||||
static struct xwidget_view *xwidget_view_lookup (struct xwidget *,
|
||||
struct window *);
|
||||
#ifdef USE_GTK
|
||||
static void webkit_view_load_changed_cb (WebKitWebView *,
|
||||
WebKitLoadEvent,
|
||||
gpointer);
|
||||
|
@ -61,6 +70,7 @@ webkit_decide_policy_cb (WebKitWebView *,
|
|||
WebKitPolicyDecision *,
|
||||
WebKitPolicyDecisionType,
|
||||
gpointer);
|
||||
#endif
|
||||
|
||||
|
||||
DEFUN ("make-xwidget",
|
||||
|
@ -78,8 +88,10 @@ Returns the newly constructed xwidget, or nil if construction fails. */)
|
|||
Lisp_Object title, Lisp_Object width, Lisp_Object height,
|
||||
Lisp_Object arguments, Lisp_Object buffer)
|
||||
{
|
||||
#ifdef USE_GTK
|
||||
if (!xg_gtk_initialized)
|
||||
error ("make-xwidget: GTK has not been initialized");
|
||||
#endif
|
||||
CHECK_SYMBOL (type);
|
||||
CHECK_FIXNAT (width);
|
||||
CHECK_FIXNAT (height);
|
||||
|
@ -94,10 +106,11 @@ Returns the newly constructed xwidget, or nil if construction fails. */)
|
|||
xw->kill_without_query = false;
|
||||
XSETXWIDGET (val, xw);
|
||||
Vxwidget_list = Fcons (val, Vxwidget_list);
|
||||
xw->widgetwindow_osr = NULL;
|
||||
xw->widget_osr = NULL;
|
||||
xw->plist = Qnil;
|
||||
|
||||
#ifdef USE_GTK
|
||||
xw->widgetwindow_osr = NULL;
|
||||
xw->widget_osr = NULL;
|
||||
if (EQ (xw->type, Qwebkit))
|
||||
{
|
||||
block_input ();
|
||||
|
@ -152,6 +165,9 @@ Returns the newly constructed xwidget, or nil if construction fails. */)
|
|||
|
||||
unblock_input ();
|
||||
}
|
||||
#elif defined NS_IMPL_COCOA
|
||||
nsxwidget_init (xw);
|
||||
#endif
|
||||
|
||||
return val;
|
||||
}
|
||||
|
@ -187,6 +203,7 @@ xwidget_hidden (struct xwidget_view *xv)
|
|||
return xv->hidden;
|
||||
}
|
||||
|
||||
#ifdef USE_GTK
|
||||
static void
|
||||
xwidget_show_view (struct xwidget_view *xv)
|
||||
{
|
||||
|
@ -220,13 +237,14 @@ offscreen_damage_event (GtkWidget *widget, GdkEvent *event,
|
|||
if (GTK_IS_WIDGET (xv_widget))
|
||||
gtk_widget_queue_draw (GTK_WIDGET (xv_widget));
|
||||
else
|
||||
printf ("Warning, offscreen_damage_event received invalid xv pointer:%p\n",
|
||||
xv_widget);
|
||||
message ("Warning, offscreen_damage_event received invalid xv pointer:%p\n",
|
||||
xv_widget);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
#endif /* USE_GTK */
|
||||
|
||||
static void
|
||||
void
|
||||
store_xwidget_event_string (struct xwidget *xw, const char *eventname,
|
||||
const char *eventstr)
|
||||
{
|
||||
|
@ -240,7 +258,7 @@ store_xwidget_event_string (struct xwidget *xw, const char *eventname,
|
|||
kbd_buffer_store_event (&event);
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
store_xwidget_js_callback_event (struct xwidget *xw,
|
||||
Lisp_Object proc,
|
||||
Lisp_Object argument)
|
||||
|
@ -256,6 +274,7 @@ store_xwidget_js_callback_event (struct xwidget *xw,
|
|||
}
|
||||
|
||||
|
||||
#ifdef USE_GTK
|
||||
void
|
||||
webkit_view_load_changed_cb (WebKitWebView *webkitwebview,
|
||||
WebKitLoadEvent load_event,
|
||||
|
@ -486,6 +505,7 @@ xwidget_osr_event_set_embedder (GtkWidget *widget, GdkEvent *event,
|
|||
gtk_widget_get_window (xv->widget));
|
||||
return FALSE;
|
||||
}
|
||||
#endif /* USE_GTK */
|
||||
|
||||
|
||||
/* Initializes and does initial placement of an xwidget view on screen. */
|
||||
|
@ -495,8 +515,10 @@ xwidget_init_view (struct xwidget *xww,
|
|||
int x, int y)
|
||||
{
|
||||
|
||||
#ifdef USE_GTK
|
||||
if (!xg_gtk_initialized)
|
||||
error ("xwidget_init_view: GTK has not been initialized");
|
||||
#endif
|
||||
|
||||
struct xwidget_view *xv = allocate_xwidget_view ();
|
||||
Lisp_Object val;
|
||||
|
@ -507,6 +529,7 @@ xwidget_init_view (struct xwidget *xww,
|
|||
XSETWINDOW (xv->w, s->w);
|
||||
XSETXWIDGET (xv->model, xww);
|
||||
|
||||
#ifdef USE_GTK
|
||||
if (EQ (xww->type, Qwebkit))
|
||||
{
|
||||
xv->widget = gtk_drawing_area_new ();
|
||||
|
@ -564,6 +587,10 @@ xwidget_init_view (struct xwidget *xww,
|
|||
xv->x = x;
|
||||
xv->y = y;
|
||||
gtk_widget_show_all (xv->widgetwindow);
|
||||
#elif defined NS_IMPL_COCOA
|
||||
nsxwidget_init_view (xv, xww, s, x, y);
|
||||
nsxwidget_resize_view(xv, xww->width, xww->height);
|
||||
#endif
|
||||
|
||||
return xv;
|
||||
}
|
||||
|
@ -576,6 +603,7 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
|
|||
initialization. */
|
||||
struct xwidget *xww = s->xwidget;
|
||||
struct xwidget_view *xv = xwidget_view_lookup (xww, s->w);
|
||||
int text_area_x, text_area_y, text_area_width, text_area_height;
|
||||
int clip_right;
|
||||
int clip_bottom;
|
||||
int clip_top;
|
||||
|
@ -587,13 +615,47 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
|
|||
/* Do initialization here in the display loop because there is no
|
||||
other time to know things like window placement etc. Do not
|
||||
create a new view if we have found one that is usable. */
|
||||
#ifdef USE_GTK
|
||||
if (!xv)
|
||||
xv = xwidget_init_view (xww, s, x, y);
|
||||
|
||||
int text_area_x, text_area_y, text_area_width, text_area_height;
|
||||
#elif defined NS_IMPL_COCOA
|
||||
if (!xv)
|
||||
{
|
||||
/* Enforce 1 to 1, model and view for macOS Cocoa webkit2. */
|
||||
if (xww->xv)
|
||||
{
|
||||
if (xwidget_hidden (xww->xv))
|
||||
{
|
||||
Lisp_Object xvl;
|
||||
XSETXWIDGET_VIEW (xvl, xww->xv);
|
||||
Fdelete_xwidget_view (xvl);
|
||||
}
|
||||
else
|
||||
{
|
||||
message ("You can't share an xwidget (webkit2) among windows.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
xv = xwidget_init_view (xww, s, x, y);
|
||||
}
|
||||
#endif
|
||||
|
||||
window_box (s->w, TEXT_AREA, &text_area_x, &text_area_y,
|
||||
&text_area_width, &text_area_height);
|
||||
|
||||
/* 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. */
|
||||
if (EQ (xww->type, Qwebkit)
|
||||
&& (xww->width != text_area_width || xww->height != text_area_height))
|
||||
{
|
||||
Lisp_Object xwl;
|
||||
XSETXWIDGET (xwl, xww);
|
||||
Fxwidget_resize (xwl,
|
||||
make_int (text_area_width),
|
||||
make_int (text_area_height));
|
||||
}
|
||||
|
||||
clip_left = max (0, text_area_x - x);
|
||||
clip_right = max (clip_left,
|
||||
min (xww->width, text_area_x + text_area_width - x));
|
||||
|
@ -616,8 +678,14 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
|
|||
|
||||
/* Has it moved? */
|
||||
if (moved)
|
||||
gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)),
|
||||
xv->widgetwindow, x + clip_left, y + clip_top);
|
||||
{
|
||||
#ifdef USE_GTK
|
||||
gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)),
|
||||
xv->widgetwindow, x + clip_left, y + clip_top);
|
||||
#elif defined NS_IMPL_COCOA
|
||||
nsxwidget_move_view (xv, x + clip_left, y + clip_top);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Clip the widget window if some parts happen to be outside
|
||||
drawable area. An Emacs window is not a gtk window. A gtk window
|
||||
|
@ -628,10 +696,16 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
|
|||
|| xv->clip_bottom != clip_bottom
|
||||
|| 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);
|
||||
#elif defined NS_IMPL_COCOA
|
||||
nsxwidget_resize_view (xv, clip_right - clip_left,
|
||||
clip_bottom - clip_top);
|
||||
nsxwidget_move_widget_in_view (xv, -clip_left, -clip_top);
|
||||
#endif
|
||||
|
||||
xv->clip_right = clip_right;
|
||||
xv->clip_bottom = clip_bottom;
|
||||
|
@ -645,16 +719,30 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
|
|||
xwidgets background. It's just a visual glitch though. */
|
||||
if (!xwidget_hidden (xv))
|
||||
{
|
||||
#ifdef USE_GTK
|
||||
gtk_widget_queue_draw (xv->widgetwindow);
|
||||
gtk_widget_queue_draw (xv->widget);
|
||||
#elif defined NS_IMPL_COCOA
|
||||
nsxwidget_set_needsdisplay (xv);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Macro that checks WEBKIT_IS_WEB_VIEW (xw->widget_osr) first. */
|
||||
static bool
|
||||
xwidget_is_web_view (struct xwidget *xw)
|
||||
{
|
||||
#ifdef USE_GTK
|
||||
return xw->widget_osr != NULL && WEBKIT_IS_WEB_VIEW (xw->widget_osr);
|
||||
#elif defined NS_IMPL_COCOA
|
||||
return nsxwidget_is_web_view (xw);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Macro that checks xwidget hold webkit web view first. */
|
||||
#define WEBKIT_FN_INIT() \
|
||||
CHECK_XWIDGET (xwidget); \
|
||||
struct xwidget *xw = XXWIDGET (xwidget); \
|
||||
if (!xw->widget_osr || !WEBKIT_IS_WEB_VIEW (xw->widget_osr)) \
|
||||
if (!xwidget_is_web_view (xw)) \
|
||||
{ \
|
||||
fputs ("ERROR xw->widget_osr does not hold a webkit instance\n", \
|
||||
stdout); \
|
||||
|
@ -670,7 +758,11 @@ DEFUN ("xwidget-webkit-goto-uri",
|
|||
WEBKIT_FN_INIT ();
|
||||
CHECK_STRING (uri);
|
||||
uri = ENCODE_FILE (uri);
|
||||
#ifdef USE_GTK
|
||||
webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr), SSDATA (uri));
|
||||
#elif defined NS_IMPL_COCOA
|
||||
nsxwidget_webkit_goto_uri (xw, SSDATA (uri));
|
||||
#endif
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
|
@ -684,14 +776,19 @@ DEFUN ("xwidget-webkit-zoom",
|
|||
if (FLOATP (factor))
|
||||
{
|
||||
double zoom_change = XFLOAT_DATA (factor);
|
||||
#ifdef USE_GTK
|
||||
webkit_web_view_set_zoom_level
|
||||
(WEBKIT_WEB_VIEW (xw->widget_osr),
|
||||
webkit_web_view_get_zoom_level
|
||||
(WEBKIT_WEB_VIEW (xw->widget_osr)) + zoom_change);
|
||||
#elif defined NS_IMPL_COCOA
|
||||
nsxwidget_webkit_zoom (xw, zoom_change);
|
||||
#endif
|
||||
}
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
#ifdef USE_GTK
|
||||
/* Save script and fun in the script/callback save vector and return
|
||||
its index. */
|
||||
static ptrdiff_t
|
||||
|
@ -713,6 +810,7 @@ save_script_callback (struct xwidget *xw, Lisp_Object script, Lisp_Object fun)
|
|||
ASET (cbs, idx, Fcons (make_mint_ptr (xlispstrdup (script)), fun));
|
||||
return idx;
|
||||
}
|
||||
#endif
|
||||
|
||||
DEFUN ("xwidget-webkit-execute-script",
|
||||
Fxwidget_webkit_execute_script, Sxwidget_webkit_execute_script,
|
||||
|
@ -729,6 +827,7 @@ argument procedure FUN.*/)
|
|||
|
||||
script = ENCODE_SYSTEM (script);
|
||||
|
||||
#ifdef USE_GTK
|
||||
/* Protect script and fun during GC. */
|
||||
intptr_t idx = save_script_callback (xw, script, fun);
|
||||
|
||||
|
@ -742,6 +841,9 @@ argument procedure FUN.*/)
|
|||
NULL, /* cancelable */
|
||||
webkit_javascript_finished_cb,
|
||||
(gpointer) idx);
|
||||
#elif defined NS_IMPL_COCOA
|
||||
nsxwidget_webkit_execute_script (xw, SSDATA (script), fun);
|
||||
#endif
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
|
@ -758,6 +860,7 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0,
|
|||
xw->height = h;
|
||||
|
||||
/* If there is an offscreen widget resize it first. */
|
||||
#ifdef USE_GTK
|
||||
if (xw->widget_osr)
|
||||
{
|
||||
gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width,
|
||||
|
@ -766,6 +869,9 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0,
|
|||
gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width,
|
||||
xw->height);
|
||||
}
|
||||
#elif defined NS_IMPL_COCOA
|
||||
nsxwidget_resize (xw);
|
||||
#endif
|
||||
|
||||
for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail))
|
||||
{
|
||||
|
@ -773,8 +879,14 @@ 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -793,9 +905,13 @@ Emacs allocated area accordingly. */)
|
|||
(Lisp_Object xwidget)
|
||||
{
|
||||
CHECK_XWIDGET (xwidget);
|
||||
#ifdef USE_GTK
|
||||
GtkRequisition requisition;
|
||||
gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition);
|
||||
return list2i (requisition.width, requisition.height);
|
||||
#elif defined NS_IMPL_COCOA
|
||||
return nsxwidget_get_size (XXWIDGET (xwidget));
|
||||
#endif
|
||||
}
|
||||
|
||||
DEFUN ("xwidgetp",
|
||||
|
@ -872,14 +988,19 @@ 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);
|
||||
Vxwidget_view_list = Fdelq (xwidget_view, Vxwidget_view_list);
|
||||
/* 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);
|
||||
#elif defined NS_IMPL_COCOA
|
||||
nsxwidget_delete_view (xv);
|
||||
#endif
|
||||
|
||||
Vxwidget_view_list = Fdelq (xwidget_view, Vxwidget_view_list);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
|
@ -1156,11 +1277,19 @@ xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix)
|
|||
xwidget_end_redisplay (w->current_matrix); */
|
||||
struct xwidget_view *xv
|
||||
= xwidget_view_lookup (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.
|
||||
If not, the code probably needs fixing. */
|
||||
eassume (xv);
|
||||
xwidget_touch (xv);
|
||||
#elif defined NS_IMPL_COCOA
|
||||
/* In NS xwidget, xv can be NULL for the second or
|
||||
later views for a model, the result of 1 to 1
|
||||
model view relation enforcement. */
|
||||
if (xv)
|
||||
xwidget_touch (xv);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1177,9 +1306,21 @@ xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix)
|
|||
if (XWINDOW (xv->w) == w)
|
||||
{
|
||||
if (xwidget_touched (xv))
|
||||
xwidget_show_view (xv);
|
||||
{
|
||||
#ifdef USE_GTK
|
||||
xwidget_show_view (xv);
|
||||
#elif defined NS_IMPL_COCOA
|
||||
nsxwidget_show_view (xv);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
xwidget_hide_view (xv);
|
||||
{
|
||||
#ifdef USE_GTK
|
||||
xwidget_hide_view (xv);
|
||||
#elif defined NS_IMPL_COCOA
|
||||
nsxwidget_hide_view (xv);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1198,6 +1339,7 @@ kill_buffer_xwidgets (Lisp_Object buffer)
|
|||
{
|
||||
CHECK_XWIDGET (xwidget);
|
||||
struct xwidget *xw = XXWIDGET (xwidget);
|
||||
#ifdef USE_GTK
|
||||
if (xw->widget_osr && xw->widgetwindow_osr)
|
||||
{
|
||||
gtk_widget_destroy (xw->widget_osr);
|
||||
|
@ -1211,6 +1353,9 @@ kill_buffer_xwidgets (Lisp_Object buffer)
|
|||
xfree (xmint_pointer (XCAR (cb)));
|
||||
ASET (xw->script_callbacks, idx, Qnil);
|
||||
}
|
||||
#elif defined NS_IMPL_COCOA
|
||||
nsxwidget_kill (xw);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,13 @@ struct xwidget_view;
|
|||
struct window;
|
||||
|
||||
#ifdef HAVE_XWIDGETS
|
||||
# include <gtk/gtk.h>
|
||||
|
||||
#if defined (USE_GTK)
|
||||
#include <gtk/gtk.h>
|
||||
#elif defined (NS_IMPL_COCOA) && defined (__OBJC__)
|
||||
#import <AppKit/NSView.h>
|
||||
#import "nsxwidget.h"
|
||||
#endif
|
||||
|
||||
struct xwidget
|
||||
{
|
||||
|
@ -54,9 +60,25 @@ struct xwidget
|
|||
int height;
|
||||
int width;
|
||||
|
||||
#if defined (USE_GTK)
|
||||
/* For offscreen widgets, unused if not osr. */
|
||||
GtkWidget *widget_osr;
|
||||
GtkWidget *widgetwindow_osr;
|
||||
#elif defined (NS_IMPL_COCOA)
|
||||
# ifdef __OBJC__
|
||||
/* For offscreen widgets, unused if not osr. */
|
||||
NSView *xwWidget;
|
||||
XwWindow *xwWindow;
|
||||
|
||||
/* Used only for xwidget types (such as webkit2) enforcing 1 to 1
|
||||
relationship between model and view. */
|
||||
struct xwidget_view *xv;
|
||||
# else
|
||||
void *xwWidget;
|
||||
void *xwWindow;
|
||||
struct xwidget_view *xv;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Kill silently if Emacs is exited. */
|
||||
bool_bf kill_without_query : 1;
|
||||
|
@ -75,9 +97,20 @@ struct xwidget_view
|
|||
/* The "live" instance isn't drawn. */
|
||||
bool hidden;
|
||||
|
||||
#if defined (USE_GTK)
|
||||
GtkWidget *widget;
|
||||
GtkWidget *widgetwindow;
|
||||
GtkWidget *emacswindow;
|
||||
#elif defined (NS_IMPL_COCOA)
|
||||
# ifdef __OBJC__
|
||||
XvWindow *xvWindow;
|
||||
NSView *emacswindow;
|
||||
# else
|
||||
void *xvWindow;
|
||||
void *emacswindow;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
int x;
|
||||
int y;
|
||||
int clip_right;
|
||||
|
@ -116,6 +149,14 @@ void x_draw_xwidget_glyph_string (struct glyph_string *);
|
|||
struct xwidget *lookup_xwidget (Lisp_Object spec);
|
||||
void xwidget_end_redisplay (struct window *, struct glyph_matrix *);
|
||||
void kill_buffer_xwidgets (Lisp_Object);
|
||||
/* Defined in 'xwidget.c'. */
|
||||
void store_xwidget_event_string (struct xwidget *xw,
|
||||
const char *eventname,
|
||||
const char *eventstr);
|
||||
|
||||
void store_xwidget_js_callback_event (struct xwidget *xw,
|
||||
Lisp_Object proc,
|
||||
Lisp_Object argument);
|
||||
#else
|
||||
INLINE_HEADER_BEGIN
|
||||
INLINE void syms_of_xwidget (void) {}
|
||||
|
|
Loading…
Add table
Reference in a new issue