diff options
| author | Sungbin Jo | 2020-08-12 12:12:34 +0200 |
|---|---|---|
| committer | Lars Ingebrigtsen | 2020-08-12 12:12:34 +0200 |
| commit | d089c4fbfc8be432dc3015a99b4044dab0a0de97 (patch) | |
| tree | d4c694ec35c3df955db8566d91fdf5b36fbe1875 /src | |
| parent | 38d70f79a613af771f73daaa6307baed0a59e7d7 (diff) | |
| download | emacs-d089c4fbfc8be432dc3015a99b4044dab0a0de97.tar.gz emacs-d089c4fbfc8be432dc3015a99b4044dab0a0de97.zip | |
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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.in | 1 | ||||
| -rw-r--r-- | src/emacs.c | 2 | ||||
| -rw-r--r-- | src/nsterm.m | 21 | ||||
| -rw-r--r-- | src/nsxwidget.h | 77 | ||||
| -rw-r--r-- | src/nsxwidget.m | 563 | ||||
| -rw-r--r-- | src/xwidget.c | 175 | ||||
| -rw-r--r-- | src/xwidget.h | 43 |
7 files changed, 862 insertions, 20 deletions
diff --git a/src/Makefile.in b/src/Makefile.in index 7141f16ec25..c5fb2ea3ab2 100644 --- a/src/Makefile.in +++ b/src/Makefile.in | |||
| @@ -433,6 +433,7 @@ SOME_MACHINE_OBJECTS = dosfns.o msdos.o \ | |||
| 433 | xterm.o xfns.o xmenu.o xselect.o xrdb.o xsmfns.o fringe.o image.o \ | 433 | xterm.o xfns.o xmenu.o xselect.o xrdb.o xsmfns.o fringe.o image.o \ |
| 434 | fontset.o dbusbind.o cygw32.o \ | 434 | fontset.o dbusbind.o cygw32.o \ |
| 435 | nsterm.o nsfns.o nsmenu.o nsselect.o nsimage.o nsfont.o macfont.o \ | 435 | nsterm.o nsfns.o nsmenu.o nsselect.o nsimage.o nsfont.o macfont.o \ |
| 436 | nsxwidget.o \ | ||
| 436 | w32.o w32console.o w32cygwinx.o w32fns.o w32heap.o w32inevt.o w32notify.o \ | 437 | w32.o w32console.o w32cygwinx.o w32fns.o w32heap.o w32inevt.o w32notify.o \ |
| 437 | w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \ | 438 | w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \ |
| 438 | w16select.o widget.o xfont.o ftfont.o xftfont.o gtkutil.o \ | 439 | w16select.o widget.o xfont.o ftfont.o xftfont.o gtkutil.o \ |
diff --git a/src/emacs.c b/src/emacs.c index d31fa2cb287..cb04de4aab6 100644 --- a/src/emacs.c +++ b/src/emacs.c | |||
| @@ -1860,7 +1860,6 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem | |||
| 1860 | syms_of_xfns (); | 1860 | syms_of_xfns (); |
| 1861 | syms_of_xmenu (); | 1861 | syms_of_xmenu (); |
| 1862 | syms_of_fontset (); | 1862 | syms_of_fontset (); |
| 1863 | syms_of_xwidget (); | ||
| 1864 | syms_of_xsettings (); | 1863 | syms_of_xsettings (); |
| 1865 | #ifdef HAVE_X_SM | 1864 | #ifdef HAVE_X_SM |
| 1866 | syms_of_xsmfns (); | 1865 | syms_of_xsmfns (); |
| @@ -1937,6 +1936,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem | |||
| 1937 | #endif /* HAVE_W32NOTIFY */ | 1936 | #endif /* HAVE_W32NOTIFY */ |
| 1938 | #endif /* WINDOWSNT */ | 1937 | #endif /* WINDOWSNT */ |
| 1939 | 1938 | ||
| 1939 | syms_of_xwidget (); | ||
| 1940 | syms_of_threads (); | 1940 | syms_of_threads (); |
| 1941 | syms_of_profiler (); | 1941 | syms_of_profiler (); |
| 1942 | syms_of_pdumper (); | 1942 | syms_of_pdumper (); |
diff --git a/src/nsterm.m b/src/nsterm.m index 572b859a982..9f5916d78ed 100644 --- a/src/nsterm.m +++ b/src/nsterm.m | |||
| @@ -49,6 +49,7 @@ GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu) | |||
| 49 | #include "nsterm.h" | 49 | #include "nsterm.h" |
| 50 | #include "systime.h" | 50 | #include "systime.h" |
| 51 | #include "character.h" | 51 | #include "character.h" |
| 52 | #include "xwidget.h" | ||
| 52 | #include "fontset.h" | 53 | #include "fontset.h" |
| 53 | #include "composite.h" | 54 | #include "composite.h" |
| 54 | #include "ccl.h" | 55 | #include "ccl.h" |
| @@ -2600,7 +2601,8 @@ frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y) | |||
| 2600 | } | 2601 | } |
| 2601 | 2602 | ||
| 2602 | static int | 2603 | static int |
| 2603 | ns_note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y) | 2604 | ns_note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y, |
| 2605 | BOOL dragging) | ||
| 2604 | /* ------------------------------------------------------------------------ | 2606 | /* ------------------------------------------------------------------------ |
| 2605 | Called by EmacsView on mouseMovement events. Passes on | 2607 | Called by EmacsView on mouseMovement events. Passes on |
| 2606 | to emacs mainstream code if we moved off of a rect of interest | 2608 | to emacs mainstream code if we moved off of a rect of interest |
| @@ -2609,17 +2611,24 @@ ns_note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y) | |||
| 2609 | { | 2611 | { |
| 2610 | struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame); | 2612 | struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame); |
| 2611 | NSRect *r; | 2613 | NSRect *r; |
| 2614 | BOOL force_update = NO; | ||
| 2612 | 2615 | ||
| 2613 | // NSTRACE ("note_mouse_movement"); | 2616 | // NSTRACE ("note_mouse_movement"); |
| 2614 | 2617 | ||
| 2615 | dpyinfo->last_mouse_motion_frame = frame; | 2618 | dpyinfo->last_mouse_motion_frame = frame; |
| 2616 | r = &dpyinfo->last_mouse_glyph; | 2619 | r = &dpyinfo->last_mouse_glyph; |
| 2617 | 2620 | ||
| 2621 | /* If the last rect is too large (ex, xwidget webkit), update at | ||
| 2622 | every move, or resizing by dragging modeline or vertical split is | ||
| 2623 | very hard to make its way. */ | ||
| 2624 | if (dragging && (r->size.width > 32 || r->size.height > 32)) | ||
| 2625 | force_update = YES; | ||
| 2626 | |||
| 2618 | /* Note, this doesn't get called for enter/leave, since we don't have a | 2627 | /* Note, this doesn't get called for enter/leave, since we don't have a |
| 2619 | position. Those are taken care of in the corresponding NSView methods. */ | 2628 | position. Those are taken care of in the corresponding NSView methods. */ |
| 2620 | 2629 | ||
| 2621 | /* Has movement gone beyond last rect we were tracking? */ | 2630 | /* Has movement gone beyond last rect we were tracking? */ |
| 2622 | if (x < r->origin.x || x >= r->origin.x + r->size.width | 2631 | if (force_update || x < r->origin.x || x >= r->origin.x + r->size.width |
| 2623 | || y < r->origin.y || y >= r->origin.y + r->size.height) | 2632 | || y < r->origin.y || y >= r->origin.y + r->size.height) |
| 2624 | { | 2633 | { |
| 2625 | ns_update_begin (frame); | 2634 | ns_update_begin (frame); |
| @@ -4368,6 +4377,10 @@ ns_draw_glyph_string (struct glyph_string *s) | |||
| 4368 | ns_unfocus (s->f); | 4377 | ns_unfocus (s->f); |
| 4369 | break; | 4378 | break; |
| 4370 | 4379 | ||
| 4380 | case XWIDGET_GLYPH: | ||
| 4381 | x_draw_xwidget_glyph_string (s); | ||
| 4382 | break; | ||
| 4383 | |||
| 4371 | case STRETCH_GLYPH: | 4384 | case STRETCH_GLYPH: |
| 4372 | ns_dumpglyphs_stretch (s); | 4385 | ns_dumpglyphs_stretch (s); |
| 4373 | break; | 4386 | break; |
| @@ -7065,6 +7078,7 @@ not_in_argv (NSString *arg) | |||
| 7065 | struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe); | 7078 | struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe); |
| 7066 | Lisp_Object frame; | 7079 | Lisp_Object frame; |
| 7067 | NSPoint pt; | 7080 | NSPoint pt; |
| 7081 | BOOL dragging; | ||
| 7068 | 7082 | ||
| 7069 | NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsView mouseMoved:]"); | 7083 | NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsView mouseMoved:]"); |
| 7070 | 7084 | ||
| @@ -7107,7 +7121,8 @@ not_in_argv (NSString *arg) | |||
| 7107 | last_mouse_window = window; | 7121 | last_mouse_window = window; |
| 7108 | } | 7122 | } |
| 7109 | 7123 | ||
| 7110 | if (!ns_note_mouse_movement (emacsframe, pt.x, pt.y)) | 7124 | dragging = (e.type == NSEventTypeLeftMouseDragged); |
| 7125 | if (!ns_note_mouse_movement (emacsframe, pt.x, pt.y, dragging)) | ||
| 7111 | help_echo_string = previous_help_echo_string; | 7126 | help_echo_string = previous_help_echo_string; |
| 7112 | 7127 | ||
| 7113 | XSETFRAME (frame, emacsframe); | 7128 | XSETFRAME (frame, emacsframe); |
diff --git a/src/nsxwidget.h b/src/nsxwidget.h new file mode 100644 index 00000000000..7e2a3e0c402 --- /dev/null +++ b/src/nsxwidget.h | |||
| @@ -0,0 +1,77 @@ | |||
| 1 | /* Header for NS Cocoa part of xwidget and webkit widget. | ||
| 2 | |||
| 3 | Copyright (C) 2019 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | This file is part of GNU Emacs. | ||
| 6 | |||
| 7 | GNU Emacs is free software: you can redistribute it and/or modify | ||
| 8 | it under the terms of the GNU General Public License as published by | ||
| 9 | the Free Software Foundation, either version 3 of the License, or (at | ||
| 10 | your option) any later version. | ||
| 11 | |||
| 12 | GNU Emacs is distributed in the hope that it will be useful, | ||
| 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | GNU General Public License for more details. | ||
| 16 | |||
| 17 | You should have received a copy of the GNU General Public License | ||
| 18 | along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | ||
| 19 | |||
| 20 | #ifndef NSXWIDGET_H_INCLUDED | ||
| 21 | #define NSXWIDGET_H_INCLUDED | ||
| 22 | |||
| 23 | /* This file can be included from non-objc files through 'xwidget.h'. */ | ||
| 24 | #ifdef __OBJC__ | ||
| 25 | #import <AppKit/NSView.h> | ||
| 26 | #endif | ||
| 27 | |||
| 28 | #include "dispextern.h" | ||
| 29 | #include "lisp.h" | ||
| 30 | #include "xwidget.h" | ||
| 31 | |||
| 32 | /* Functions for xwidget webkit. */ | ||
| 33 | |||
| 34 | bool nsxwidget_is_web_view (struct xwidget *xw); | ||
| 35 | void nsxwidget_webkit_goto_uri (struct xwidget *xw, const char *uri); | ||
| 36 | void nsxwidget_webkit_zoom (struct xwidget *xw, double zoom_change); | ||
| 37 | void nsxwidget_webkit_execute_script (struct xwidget *xw, const char *script, | ||
| 38 | Lisp_Object fun); | ||
| 39 | |||
| 40 | /* Functions for xwidget model. */ | ||
| 41 | |||
| 42 | #ifdef __OBJC__ | ||
| 43 | @interface XwWindow : NSView | ||
| 44 | @property struct xwidget *xw; | ||
| 45 | @end | ||
| 46 | #endif | ||
| 47 | |||
| 48 | void nsxwidget_init (struct xwidget *xw); | ||
| 49 | void nsxwidget_kill (struct xwidget *xw); | ||
| 50 | void nsxwidget_resize (struct xwidget *xw); | ||
| 51 | Lisp_Object nsxwidget_get_size (struct xwidget *xw); | ||
| 52 | |||
| 53 | /* Functions for xwidget view. */ | ||
| 54 | |||
| 55 | #ifdef __OBJC__ | ||
| 56 | @interface XvWindow : NSView | ||
| 57 | @property struct xwidget *xw; | ||
| 58 | @property struct xwidget_view *xv; | ||
| 59 | @end | ||
| 60 | #endif | ||
| 61 | |||
| 62 | void nsxwidget_init_view (struct xwidget_view *xv, | ||
| 63 | struct xwidget *xww, | ||
| 64 | struct glyph_string *s, | ||
| 65 | int x, int y); | ||
| 66 | void nsxwidget_delete_view (struct xwidget_view *xv); | ||
| 67 | |||
| 68 | void nsxwidget_show_view (struct xwidget_view *xv); | ||
| 69 | void nsxwidget_hide_view (struct xwidget_view *xv); | ||
| 70 | void nsxwidget_resize_view (struct xwidget_view *xv, | ||
| 71 | int widget, int height); | ||
| 72 | |||
| 73 | void nsxwidget_move_view (struct xwidget_view *xv, int x, int y); | ||
| 74 | void nsxwidget_move_widget_in_view (struct xwidget_view *xv, int x, int y); | ||
| 75 | void nsxwidget_set_needsdisplay (struct xwidget_view *xv); | ||
| 76 | |||
| 77 | #endif /* NSXWIDGET_H_INCLUDED */ | ||
diff --git a/src/nsxwidget.m b/src/nsxwidget.m new file mode 100644 index 00000000000..c5376dd311c --- /dev/null +++ b/src/nsxwidget.m | |||
| @@ -0,0 +1,563 @@ | |||
| 1 | /* NS Cocoa part implementation of xwidget and webkit widget. | ||
| 2 | |||
| 3 | Copyright (C) 2019 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | This file is part of GNU Emacs. | ||
| 6 | |||
| 7 | GNU Emacs is free software: you can redistribute it and/or modify | ||
| 8 | it under the terms of the GNU General Public License as published by | ||
| 9 | the Free Software Foundation, either version 3 of the License, or (at | ||
| 10 | your option) any later version. | ||
| 11 | |||
| 12 | GNU Emacs is distributed in the hope that it will be useful, | ||
| 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | GNU General Public License for more details. | ||
| 16 | |||
| 17 | You should have received a copy of the GNU General Public License | ||
| 18 | along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | ||
| 19 | |||
| 20 | #include <config.h> | ||
| 21 | |||
| 22 | #include "lisp.h" | ||
| 23 | #include "blockinput.h" | ||
| 24 | #include "dispextern.h" | ||
| 25 | #include "buffer.h" | ||
| 26 | #include "frame.h" | ||
| 27 | #include "nsterm.h" | ||
| 28 | #include "xwidget.h" | ||
| 29 | |||
| 30 | #import <AppKit/AppKit.h> | ||
| 31 | #import <WebKit/WebKit.h> | ||
| 32 | |||
| 33 | /* Thoughts on NS Cocoa xwidget and webkit2: | ||
| 34 | |||
| 35 | Webkit2 process architecture seems to be very hostile for offscreen | ||
| 36 | rendering techniques, which is used by GTK xwiget implementation; | ||
| 37 | Specifically NSView level view sharing / copying is not working. | ||
| 38 | |||
| 39 | *** So only one view can be associcated with a model. *** | ||
| 40 | |||
| 41 | With this decision, implementation is plain and can expect best out | ||
| 42 | of webkit2's rationale. But process and session structures will | ||
| 43 | diverge from GTK xwiget. Though, cosmetically similar usages can | ||
| 44 | be presented and will be preferred, if agreeable. | ||
| 45 | |||
| 46 | For other widget types, OSR seems possible, but will not care for a | ||
| 47 | while. */ | ||
| 48 | |||
| 49 | /* Xwidget webkit. */ | ||
| 50 | |||
| 51 | @interface XwWebView : WKWebView | ||
| 52 | <WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler> | ||
| 53 | @property struct xwidget *xw; | ||
| 54 | /* Map url to whether javascript is blocked by | ||
| 55 | 'Content-Security-Policy' sandbox without allow-scripts. */ | ||
| 56 | @property(retain) NSMutableDictionary *urlScriptBlocked; | ||
| 57 | @end | ||
| 58 | @implementation XwWebView : WKWebView | ||
| 59 | |||
| 60 | - (id)initWithFrame:(CGRect)frame | ||
| 61 | configuration:(WKWebViewConfiguration *)configuration | ||
| 62 | xwidget:(struct xwidget *)xw | ||
| 63 | { | ||
| 64 | /* Script controller to add script message handler and user script. */ | ||
| 65 | WKUserContentController *scriptor = [[WKUserContentController alloc] init]; | ||
| 66 | configuration.userContentController = scriptor; | ||
| 67 | |||
| 68 | /* Enable inspect element context menu item for debugging. */ | ||
| 69 | [configuration.preferences setValue:@YES | ||
| 70 | forKey:@"developerExtrasEnabled"]; | ||
| 71 | |||
| 72 | Lisp_Object enablePlugins = | ||
| 73 | Fintern (build_string ("xwidget-webkit-enable-plugins"), Qnil); | ||
| 74 | if (!EQ (Fsymbol_value (enablePlugins), Qnil)) | ||
| 75 | configuration.preferences.plugInsEnabled = YES; | ||
| 76 | |||
| 77 | self = [super initWithFrame:frame configuration:configuration]; | ||
| 78 | if (self) | ||
| 79 | { | ||
| 80 | self.xw = xw; | ||
| 81 | self.urlScriptBlocked = [[NSMutableDictionary alloc] init]; | ||
| 82 | self.navigationDelegate = self; | ||
| 83 | self.UIDelegate = self; | ||
| 84 | self.customUserAgent = | ||
| 85 | @"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6)" | ||
| 86 | @" AppleWebKit/603.3.8 (KHTML, like Gecko)" | ||
| 87 | @" Version/11.0.1 Safari/603.3.8"; | ||
| 88 | [scriptor addScriptMessageHandler:self name:@"keyDown"]; | ||
| 89 | [scriptor addUserScript:[[WKUserScript alloc] | ||
| 90 | initWithSource:xwScript | ||
| 91 | injectionTime: | ||
| 92 | WKUserScriptInjectionTimeAtDocumentStart | ||
| 93 | forMainFrameOnly:NO]]; | ||
| 94 | } | ||
| 95 | return self; | ||
| 96 | } | ||
| 97 | |||
| 98 | - (void)webView:(WKWebView *)webView | ||
| 99 | didFinishNavigation:(WKNavigation *)navigation | ||
| 100 | { | ||
| 101 | if (EQ (Fbuffer_live_p (self.xw->buffer), Qt)) | ||
| 102 | store_xwidget_event_string (self.xw, "load-changed", ""); | ||
| 103 | } | ||
| 104 | |||
| 105 | - (void)webView:(WKWebView *)webView | ||
| 106 | decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction | ||
| 107 | decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler | ||
| 108 | { | ||
| 109 | switch (navigationAction.navigationType) { | ||
| 110 | case WKNavigationTypeLinkActivated: | ||
| 111 | decisionHandler (WKNavigationActionPolicyAllow); | ||
| 112 | break; | ||
| 113 | default: | ||
| 114 | // decisionHandler (WKNavigationActionPolicyCancel); | ||
| 115 | decisionHandler (WKNavigationActionPolicyAllow); | ||
| 116 | break; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | - (void)webView:(WKWebView *)webView | ||
| 121 | decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse | ||
| 122 | decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler | ||
| 123 | { | ||
| 124 | decisionHandler (WKNavigationResponsePolicyAllow); | ||
| 125 | |||
| 126 | self.urlScriptBlocked[navigationResponse.response.URL] = | ||
| 127 | [NSNumber numberWithBool:NO]; | ||
| 128 | if ([navigationResponse.response isKindOfClass:[NSHTTPURLResponse class]]) | ||
| 129 | { | ||
| 130 | NSDictionary *headers = | ||
| 131 | ((NSHTTPURLResponse *) navigationResponse.response).allHeaderFields; | ||
| 132 | NSString *value = headers[@"Content-Security-Policy"]; | ||
| 133 | if (value) | ||
| 134 | { | ||
| 135 | /* TODO: Sloppy parsing of 'Content-Security-Policy' value. */ | ||
| 136 | NSRange sandbox = [value rangeOfString:@"sandbox"]; | ||
| 137 | if (sandbox.location != NSNotFound | ||
| 138 | && (sandbox.location == 0 | ||
| 139 | || [value characterAtIndex:(sandbox.location - 1)] == ' ' | ||
| 140 | || [value characterAtIndex:(sandbox.location - 1)] == ';')) | ||
| 141 | { | ||
| 142 | NSRange allowScripts = [value rangeOfString:@"allow-scripts"]; | ||
| 143 | if (allowScripts.location == NSNotFound | ||
| 144 | || allowScripts.location < sandbox.location) | ||
| 145 | self.urlScriptBlocked[navigationResponse.response.URL] = | ||
| 146 | [NSNumber numberWithBool:YES]; | ||
| 147 | } | ||
| 148 | } | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | /* No additional new webview or emacs window will be created | ||
| 153 | for <a ... target="_blank">. */ | ||
| 154 | - (WKWebView *)webView:(WKWebView *)webView | ||
| 155 | createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration | ||
| 156 | forNavigationAction:(WKNavigationAction *)navigationAction | ||
| 157 | windowFeatures:(WKWindowFeatures *)windowFeatures | ||
| 158 | { | ||
| 159 | if (!navigationAction.targetFrame.isMainFrame) | ||
| 160 | [webView loadRequest:navigationAction.request]; | ||
| 161 | return nil; | ||
| 162 | } | ||
| 163 | |||
| 164 | /* Open panel for file upload. */ | ||
| 165 | - (void)webView:(WKWebView *)webView | ||
| 166 | runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters | ||
| 167 | initiatedByFrame:(WKFrameInfo *)frame | ||
| 168 | completionHandler:(void (^)(NSArray<NSURL *> *URLs))completionHandler | ||
| 169 | { | ||
| 170 | NSOpenPanel *openPanel = [NSOpenPanel openPanel]; | ||
| 171 | openPanel.canChooseFiles = YES; | ||
| 172 | openPanel.canChooseDirectories = NO; | ||
| 173 | openPanel.allowsMultipleSelection = parameters.allowsMultipleSelection; | ||
| 174 | if ([openPanel runModal] == NSModalResponseOK) | ||
| 175 | completionHandler (openPanel.URLs); | ||
| 176 | else | ||
| 177 | completionHandler (nil); | ||
| 178 | } | ||
| 179 | |||
| 180 | /* By forwarding mouse events to emacs view (frame) | ||
| 181 | - Mouse click in webview selects the window contains the webview. | ||
| 182 | - Correct mouse hand/arrow/I-beam is displayed (TODO: not perfect yet). | ||
| 183 | */ | ||
| 184 | |||
| 185 | - (void)mouseDown:(NSEvent *)event | ||
| 186 | { | ||
| 187 | [self.xw->xv->emacswindow mouseDown:event]; | ||
| 188 | [super mouseDown:event]; | ||
| 189 | } | ||
| 190 | |||
| 191 | - (void)mouseUp:(NSEvent *)event | ||
| 192 | { | ||
| 193 | [self.xw->xv->emacswindow mouseUp:event]; | ||
| 194 | [super mouseUp:event]; | ||
| 195 | } | ||
| 196 | |||
| 197 | /* Basically we want keyboard events handled by emacs unless an input | ||
| 198 | element has focus. Especially, while incremental search, we set | ||
| 199 | emacs as first responder to avoid focus held in an input element | ||
| 200 | with matching text. */ | ||
| 201 | |||
| 202 | - (void)keyDown:(NSEvent *)event | ||
| 203 | { | ||
| 204 | Lisp_Object var = Fintern (build_string ("isearch-mode"), Qnil); | ||
| 205 | Lisp_Object val = buffer_local_value (var, Fcurrent_buffer ()); | ||
| 206 | if (!EQ (val, Qunbound) && !EQ (val, Qnil)) | ||
| 207 | { | ||
| 208 | [self.window makeFirstResponder:self.xw->xv->emacswindow]; | ||
| 209 | [self.xw->xv->emacswindow keyDown:event]; | ||
| 210 | return; | ||
| 211 | } | ||
| 212 | |||
| 213 | /* Emacs handles keyboard events when javascript is blocked. */ | ||
| 214 | if ([self.urlScriptBlocked[self.URL] boolValue]) | ||
| 215 | { | ||
| 216 | [self.xw->xv->emacswindow keyDown:event]; | ||
| 217 | return; | ||
| 218 | } | ||
| 219 | |||
| 220 | [self evaluateJavaScript:@"xwHasFocus()" | ||
| 221 | completionHandler:^(id result, NSError *error) { | ||
| 222 | if (error) | ||
| 223 | { | ||
| 224 | NSLog (@"xwHasFocus: %@", error); | ||
| 225 | [self.xw->xv->emacswindow keyDown:event]; | ||
| 226 | } | ||
| 227 | else if (result) | ||
| 228 | { | ||
| 229 | NSNumber *hasFocus = result; /* __NSCFBoolean */ | ||
| 230 | if (!hasFocus.boolValue) | ||
| 231 | [self.xw->xv->emacswindow keyDown:event]; | ||
| 232 | else | ||
| 233 | [super keyDown:event]; | ||
| 234 | } | ||
| 235 | }]; | ||
| 236 | } | ||
| 237 | |||
| 238 | - (void)interpretKeyEvents:(NSArray<NSEvent *> *)eventArray | ||
| 239 | { | ||
| 240 | /* We should do nothing and do not forward (default implementation | ||
| 241 | if we not override here) to let emacs collect key events and ask | ||
| 242 | interpretKeyEvents to its superclass. */ | ||
| 243 | } | ||
| 244 | |||
| 245 | static NSString *xwScript; | ||
| 246 | + (void)initialize | ||
| 247 | { | ||
| 248 | /* Find out if an input element has focus. | ||
| 249 | Message to script message handler when 'C-g' key down. */ | ||
| 250 | if (!xwScript) | ||
| 251 | xwScript = | ||
| 252 | @"function xwHasFocus() {" | ||
| 253 | @" var ae = document.activeElement;" | ||
| 254 | @" if (ae) {" | ||
| 255 | @" var name = ae.nodeName;" | ||
| 256 | @" return name == 'INPUT' || name == 'TEXTAREA';" | ||
| 257 | @" } else {" | ||
| 258 | @" return false;" | ||
| 259 | @" }" | ||
| 260 | @"}" | ||
| 261 | @"function xwKeyDown(event) {" | ||
| 262 | @" if (event.ctrlKey && event.key == 'g') {" | ||
| 263 | @" window.webkit.messageHandlers.keyDown.postMessage('C-g');" | ||
| 264 | @" }" | ||
| 265 | @"}" | ||
| 266 | @"document.addEventListener('keydown', xwKeyDown);" | ||
| 267 | ; | ||
| 268 | } | ||
| 269 | |||
| 270 | /* Confirming to WKScriptMessageHandler, listens concerning keyDown in | ||
| 271 | webkit. Currently 'C-g'. */ | ||
| 272 | - (void)userContentController:(WKUserContentController *)userContentController | ||
| 273 | didReceiveScriptMessage:(WKScriptMessage *)message | ||
| 274 | { | ||
| 275 | if ([message.body isEqualToString:@"C-g"]) | ||
| 276 | { | ||
| 277 | /* Just give up focus, no relay "C-g" to emacs, another "C-g" | ||
| 278 | follows will be handled by emacs. */ | ||
| 279 | [self.window makeFirstResponder:self.xw->xv->emacswindow]; | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 283 | @end | ||
| 284 | |||
| 285 | /* Xwidget webkit commands. */ | ||
| 286 | |||
| 287 | static Lisp_Object build_string_with_nsstr (NSString *nsstr); | ||
| 288 | |||
| 289 | bool | ||
| 290 | nsxwidget_is_web_view (struct xwidget *xw) | ||
| 291 | { | ||
| 292 | return xw->xwWidget != NULL && | ||
| 293 | [xw->xwWidget isKindOfClass:WKWebView.class]; | ||
| 294 | } | ||
| 295 | /* @Note ATS - Need application transport security in 'Info.plist' or | ||
| 296 | remote pages will not loaded. */ | ||
| 297 | void | ||
| 298 | nsxwidget_webkit_goto_uri (struct xwidget *xw, const char *uri) | ||
| 299 | { | ||
| 300 | XwWebView *xwWebView = (XwWebView *) xw->xwWidget; | ||
| 301 | NSString *urlString = [NSString stringWithUTF8String:uri]; | ||
| 302 | NSURL *url = [NSURL URLWithString:urlString]; | ||
| 303 | NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url]; | ||
| 304 | [xwWebView loadRequest:urlRequest]; | ||
| 305 | } | ||
| 306 | |||
| 307 | void | ||
| 308 | nsxwidget_webkit_zoom (struct xwidget *xw, double zoom_change) | ||
| 309 | { | ||
| 310 | XwWebView *xwWebView = (XwWebView *) xw->xwWidget; | ||
| 311 | xwWebView.magnification += zoom_change; | ||
| 312 | /* TODO: setMagnification:centeredAtPoint. */ | ||
| 313 | } | ||
| 314 | |||
| 315 | /* Build lisp string */ | ||
| 316 | static Lisp_Object | ||
| 317 | build_string_with_nsstr (NSString *nsstr) | ||
| 318 | { | ||
| 319 | const char *utfstr = [nsstr UTF8String]; | ||
| 320 | NSUInteger bytes = [nsstr lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; | ||
| 321 | return make_string (utfstr, bytes); | ||
| 322 | } | ||
| 323 | |||
| 324 | /* Recursively convert an objc native type JavaScript value to a Lisp | ||
| 325 | value. Mostly copied from GTK xwidget 'webkit_js_to_lisp'. */ | ||
| 326 | static Lisp_Object | ||
| 327 | js_to_lisp (id value) | ||
| 328 | { | ||
| 329 | if (value == nil || [value isKindOfClass:NSNull.class]) | ||
| 330 | return Qnil; | ||
| 331 | else if ([value isKindOfClass:NSString.class]) | ||
| 332 | return build_string_with_nsstr ((NSString *) value); | ||
| 333 | else if ([value isKindOfClass:NSNumber.class]) | ||
| 334 | { | ||
| 335 | NSNumber *nsnum = (NSNumber *) value; | ||
| 336 | char type = nsnum.objCType[0]; | ||
| 337 | if (type == 'c') /* __NSCFBoolean has type character 'c'. */ | ||
| 338 | return nsnum.boolValue? Qt : Qnil; | ||
| 339 | else | ||
| 340 | { | ||
| 341 | if (type == 'i' || type == 'l') | ||
| 342 | return make_int (nsnum.longValue); | ||
| 343 | else if (type == 'f' || type == 'd') | ||
| 344 | return make_float (nsnum.doubleValue); | ||
| 345 | /* else fall through. */ | ||
| 346 | } | ||
| 347 | } | ||
| 348 | else if ([value isKindOfClass:NSArray.class]) | ||
| 349 | { | ||
| 350 | NSArray *nsarr = (NSArray *) value; | ||
| 351 | EMACS_INT n = nsarr.count; | ||
| 352 | Lisp_Object obj; | ||
| 353 | struct Lisp_Vector *p = allocate_vector (n); | ||
| 354 | |||
| 355 | for (ptrdiff_t i = 0; i < n; ++i) | ||
| 356 | p->contents[i] = js_to_lisp ([nsarr objectAtIndex:i]); | ||
| 357 | XSETVECTOR (obj, p); | ||
| 358 | return obj; | ||
| 359 | } | ||
| 360 | else if ([value isKindOfClass:NSDictionary.class]) | ||
| 361 | { | ||
| 362 | NSDictionary *nsdict = (NSDictionary *) value; | ||
| 363 | NSArray *keys = nsdict.allKeys; | ||
| 364 | ptrdiff_t n = keys.count; | ||
| 365 | Lisp_Object obj; | ||
| 366 | struct Lisp_Vector *p = allocate_vector (n); | ||
| 367 | |||
| 368 | for (ptrdiff_t i = 0; i < n; ++i) | ||
| 369 | { | ||
| 370 | NSString *prop_key = (NSString *) [keys objectAtIndex:i]; | ||
| 371 | id prop_value = [nsdict valueForKey:prop_key]; | ||
| 372 | p->contents[i] = Fcons (build_string_with_nsstr (prop_key), | ||
| 373 | js_to_lisp (prop_value)); | ||
| 374 | } | ||
| 375 | XSETVECTOR (obj, p); | ||
| 376 | return obj; | ||
| 377 | } | ||
| 378 | NSLog (@"Unhandled type in javascript result"); | ||
| 379 | return Qnil; | ||
| 380 | } | ||
| 381 | |||
| 382 | void | ||
| 383 | nsxwidget_webkit_execute_script (struct xwidget *xw, const char *script, | ||
| 384 | Lisp_Object fun) | ||
| 385 | { | ||
| 386 | XwWebView *xwWebView = (XwWebView *) xw->xwWidget; | ||
| 387 | if ([xwWebView.urlScriptBlocked[xwWebView.URL] boolValue]) | ||
| 388 | { | ||
| 389 | message ("Javascript is blocked by 'CSP: sandbox'."); | ||
| 390 | return; | ||
| 391 | } | ||
| 392 | |||
| 393 | NSString *javascriptString = [NSString stringWithUTF8String:script]; | ||
| 394 | [xwWebView evaluateJavaScript:javascriptString | ||
| 395 | completionHandler:^(id result, NSError *error) { | ||
| 396 | if (error) | ||
| 397 | { | ||
| 398 | NSLog (@"evaluateJavaScript error : %@", error.localizedDescription); | ||
| 399 | NSLog (@"error script=%@", javascriptString); | ||
| 400 | } | ||
| 401 | else if (result && FUNCTIONP (fun)) | ||
| 402 | { | ||
| 403 | // NSLog (@"result=%@, type=%@", result, [result class]); | ||
| 404 | Lisp_Object lisp_value = js_to_lisp (result); | ||
| 405 | store_xwidget_js_callback_event (xw, fun, lisp_value); | ||
| 406 | } | ||
| 407 | }]; | ||
| 408 | } | ||
| 409 | |||
| 410 | /* Window containing an xwidget. */ | ||
| 411 | |||
| 412 | @implementation XwWindow | ||
| 413 | - (BOOL)isFlipped { return YES; } | ||
| 414 | @end | ||
| 415 | |||
| 416 | /* Xwidget model, macOS Cocoa part. */ | ||
| 417 | |||
| 418 | void | ||
| 419 | nsxwidget_init(struct xwidget *xw) | ||
| 420 | { | ||
| 421 | block_input (); | ||
| 422 | NSRect rect = NSMakeRect (0, 0, xw->width, xw->height); | ||
| 423 | xw->xwWidget = [[XwWebView alloc] | ||
| 424 | initWithFrame:rect | ||
| 425 | configuration:[[WKWebViewConfiguration alloc] init] | ||
| 426 | xwidget:xw]; | ||
| 427 | xw->xwWindow = [[XwWindow alloc] | ||
| 428 | initWithFrame:rect]; | ||
| 429 | [xw->xwWindow addSubview:xw->xwWidget]; | ||
| 430 | xw->xv = NULL; /* for 1 to 1 relationship of webkit2. */ | ||
| 431 | unblock_input (); | ||
| 432 | } | ||
| 433 | |||
| 434 | void | ||
| 435 | nsxwidget_kill (struct xwidget *xw) | ||
| 436 | { | ||
| 437 | if (xw) | ||
| 438 | { | ||
| 439 | WKUserContentController *scriptor = | ||
| 440 | ((XwWebView *) xw->xwWidget).configuration.userContentController; | ||
| 441 | [scriptor removeAllUserScripts]; | ||
| 442 | [scriptor removeScriptMessageHandlerForName:@"keyDown"]; | ||
| 443 | [scriptor release]; | ||
| 444 | if (xw->xv) | ||
| 445 | xw->xv->model = Qnil; /* Make sure related view stale. */ | ||
| 446 | |||
| 447 | /* This stops playing audio when a xwidget-webkit buffer is | ||
| 448 | killed. I could not find other solution. */ | ||
| 449 | nsxwidget_webkit_goto_uri (xw, "about:blank"); | ||
| 450 | |||
| 451 | [((XwWebView *) xw->xwWidget).urlScriptBlocked release]; | ||
| 452 | [xw->xwWidget removeFromSuperviewWithoutNeedingDisplay]; | ||
| 453 | [xw->xwWidget release]; | ||
| 454 | [xw->xwWindow removeFromSuperviewWithoutNeedingDisplay]; | ||
| 455 | [xw->xwWindow release]; | ||
| 456 | xw->xwWidget = nil; | ||
| 457 | } | ||
| 458 | } | ||
| 459 | |||
| 460 | void | ||
| 461 | nsxwidget_resize (struct xwidget *xw) | ||
| 462 | { | ||
| 463 | if (xw->xwWidget) | ||
| 464 | { | ||
| 465 | [xw->xwWindow setFrameSize:NSMakeSize(xw->width, xw->height)]; | ||
| 466 | [xw->xwWidget setFrameSize:NSMakeSize(xw->width, xw->height)]; | ||
| 467 | } | ||
| 468 | } | ||
| 469 | |||
| 470 | Lisp_Object | ||
| 471 | nsxwidget_get_size (struct xwidget *xw) | ||
| 472 | { | ||
| 473 | return list2i (xw->xwWidget.frame.size.width, | ||
| 474 | xw->xwWidget.frame.size.height); | ||
| 475 | } | ||
| 476 | |||
| 477 | /* Xwidget view, macOS Cocoa part. */ | ||
| 478 | |||
| 479 | @implementation XvWindow : NSView | ||
| 480 | - (BOOL)isFlipped { return YES; } | ||
| 481 | @end | ||
| 482 | |||
| 483 | void | ||
| 484 | nsxwidget_init_view (struct xwidget_view *xv, | ||
| 485 | struct xwidget *xw, | ||
| 486 | struct glyph_string *s, | ||
| 487 | int x, int y) | ||
| 488 | { | ||
| 489 | /* 'x_draw_xwidget_glyph_string' will calculate correct position and | ||
| 490 | size of clip to draw in emacs buffer window. Thus, just begin at | ||
| 491 | origin with no crop. */ | ||
| 492 | xv->x = x; | ||
| 493 | xv->y = y; | ||
| 494 | xv->clip_left = 0; | ||
| 495 | xv->clip_right = xw->width; | ||
| 496 | xv->clip_top = 0; | ||
| 497 | xv->clip_bottom = xw->height; | ||
| 498 | |||
| 499 | xv->xvWindow = [[XvWindow alloc] | ||
| 500 | initWithFrame:NSMakeRect (x, y, xw->width, xw->height)]; | ||
| 501 | xv->xvWindow.xw = xw; | ||
| 502 | xv->xvWindow.xv = xv; | ||
| 503 | |||
| 504 | xw->xv = xv; /* For 1 to 1 relationship of webkit2. */ | ||
| 505 | [xv->xvWindow addSubview:xw->xwWindow]; | ||
| 506 | |||
| 507 | xv->emacswindow = FRAME_NS_VIEW (s->f); | ||
| 508 | [xv->emacswindow addSubview:xv->xvWindow]; | ||
| 509 | } | ||
| 510 | |||
| 511 | void | ||
| 512 | nsxwidget_delete_view (struct xwidget_view *xv) | ||
| 513 | { | ||
| 514 | if (!EQ (xv->model, Qnil)) | ||
| 515 | { | ||
| 516 | struct xwidget *xw = XXWIDGET (xv->model); | ||
| 517 | [xw->xwWindow removeFromSuperviewWithoutNeedingDisplay]; | ||
| 518 | xw->xv = NULL; /* Now model has no view. */ | ||
| 519 | } | ||
| 520 | [xv->xvWindow removeFromSuperviewWithoutNeedingDisplay]; | ||
| 521 | [xv->xvWindow release]; | ||
| 522 | } | ||
| 523 | |||
| 524 | void | ||
| 525 | nsxwidget_show_view (struct xwidget_view *xv) | ||
| 526 | { | ||
| 527 | xv->hidden = NO; | ||
| 528 | [xv->xvWindow setFrameOrigin:NSMakePoint(xv->x + xv->clip_left, | ||
| 529 | xv->y + xv->clip_top)]; | ||
| 530 | } | ||
| 531 | |||
| 532 | void | ||
| 533 | nsxwidget_hide_view (struct xwidget_view *xv) | ||
| 534 | { | ||
| 535 | xv->hidden = YES; | ||
| 536 | [xv->xvWindow setFrameOrigin:NSMakePoint(10000, 10000)]; | ||
| 537 | } | ||
| 538 | |||
| 539 | void | ||
| 540 | nsxwidget_resize_view (struct xwidget_view *xv, int width, int height) | ||
| 541 | { | ||
| 542 | [xv->xvWindow setFrameSize:NSMakeSize(width, height)]; | ||
| 543 | } | ||
| 544 | |||
| 545 | void | ||
| 546 | nsxwidget_move_view (struct xwidget_view *xv, int x, int y) | ||
| 547 | { | ||
| 548 | [xv->xvWindow setFrameOrigin:NSMakePoint (x, y)]; | ||
| 549 | } | ||
| 550 | |||
| 551 | /* Move model window in container (view window). */ | ||
| 552 | void | ||
| 553 | nsxwidget_move_widget_in_view (struct xwidget_view *xv, int x, int y) | ||
| 554 | { | ||
| 555 | struct xwidget *xww = xv->xvWindow.xw; | ||
| 556 | [xww->xwWindow setFrameOrigin:NSMakePoint (x, y)]; | ||
| 557 | } | ||
| 558 | |||
| 559 | void | ||
| 560 | nsxwidget_set_needsdisplay (struct xwidget_view *xv) | ||
| 561 | { | ||
| 562 | xv->xvWindow.needsDisplay = YES; | ||
| 563 | } | ||
diff --git a/src/xwidget.c b/src/xwidget.c index 0347f1e6483..a3a3cd8d5bc 100644 --- a/src/xwidget.c +++ b/src/xwidget.c | |||
| @@ -23,13 +23,21 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 23 | 23 | ||
| 24 | #include "lisp.h" | 24 | #include "lisp.h" |
| 25 | #include "blockinput.h" | 25 | #include "blockinput.h" |
| 26 | #include "dispextern.h" | ||
| 26 | #include "frame.h" | 27 | #include "frame.h" |
| 27 | #include "keyboard.h" | 28 | #include "keyboard.h" |
| 28 | #include "gtkutil.h" | 29 | #include "gtkutil.h" |
| 29 | #include "sysstdio.h" | 30 | #include "sysstdio.h" |
| 31 | #include "termhooks.h" | ||
| 32 | #include "window.h" | ||
| 30 | 33 | ||
| 34 | /* Include xwidget bottom end headers. */ | ||
| 35 | #ifdef USE_GTK | ||
| 31 | #include <webkit2/webkit2.h> | 36 | #include <webkit2/webkit2.h> |
| 32 | #include <JavaScriptCore/JavaScript.h> | 37 | #include <JavaScriptCore/JavaScript.h> |
| 38 | #elif defined NS_IMPL_COCOA | ||
| 39 | #include "nsxwidget.h" | ||
| 40 | #endif | ||
| 33 | 41 | ||
| 34 | static struct xwidget * | 42 | static struct xwidget * |
| 35 | allocate_xwidget (void) | 43 | allocate_xwidget (void) |
| @@ -48,6 +56,7 @@ allocate_xwidget_view (void) | |||
| 48 | 56 | ||
| 49 | static struct xwidget_view *xwidget_view_lookup (struct xwidget *, | 57 | static struct xwidget_view *xwidget_view_lookup (struct xwidget *, |
| 50 | struct window *); | 58 | struct window *); |
| 59 | #ifdef USE_GTK | ||
| 51 | static void webkit_view_load_changed_cb (WebKitWebView *, | 60 | static void webkit_view_load_changed_cb (WebKitWebView *, |
| 52 | WebKitLoadEvent, | 61 | WebKitLoadEvent, |
| 53 | gpointer); | 62 | gpointer); |
| @@ -61,6 +70,7 @@ webkit_decide_policy_cb (WebKitWebView *, | |||
| 61 | WebKitPolicyDecision *, | 70 | WebKitPolicyDecision *, |
| 62 | WebKitPolicyDecisionType, | 71 | WebKitPolicyDecisionType, |
| 63 | gpointer); | 72 | gpointer); |
| 73 | #endif | ||
| 64 | 74 | ||
| 65 | 75 | ||
| 66 | DEFUN ("make-xwidget", | 76 | DEFUN ("make-xwidget", |
| @@ -78,8 +88,10 @@ Returns the newly constructed xwidget, or nil if construction fails. */) | |||
| 78 | Lisp_Object title, Lisp_Object width, Lisp_Object height, | 88 | Lisp_Object title, Lisp_Object width, Lisp_Object height, |
| 79 | Lisp_Object arguments, Lisp_Object buffer) | 89 | Lisp_Object arguments, Lisp_Object buffer) |
| 80 | { | 90 | { |
| 91 | #ifdef USE_GTK | ||
| 81 | if (!xg_gtk_initialized) | 92 | if (!xg_gtk_initialized) |
| 82 | error ("make-xwidget: GTK has not been initialized"); | 93 | error ("make-xwidget: GTK has not been initialized"); |
| 94 | #endif | ||
| 83 | CHECK_SYMBOL (type); | 95 | CHECK_SYMBOL (type); |
| 84 | CHECK_FIXNAT (width); | 96 | CHECK_FIXNAT (width); |
| 85 | CHECK_FIXNAT (height); | 97 | CHECK_FIXNAT (height); |
| @@ -94,10 +106,11 @@ Returns the newly constructed xwidget, or nil if construction fails. */) | |||
| 94 | xw->kill_without_query = false; | 106 | xw->kill_without_query = false; |
| 95 | XSETXWIDGET (val, xw); | 107 | XSETXWIDGET (val, xw); |
| 96 | Vxwidget_list = Fcons (val, Vxwidget_list); | 108 | Vxwidget_list = Fcons (val, Vxwidget_list); |
| 97 | xw->widgetwindow_osr = NULL; | ||
| 98 | xw->widget_osr = NULL; | ||
| 99 | xw->plist = Qnil; | 109 | xw->plist = Qnil; |
| 100 | 110 | ||
| 111 | #ifdef USE_GTK | ||
| 112 | xw->widgetwindow_osr = NULL; | ||
| 113 | xw->widget_osr = NULL; | ||
| 101 | if (EQ (xw->type, Qwebkit)) | 114 | if (EQ (xw->type, Qwebkit)) |
| 102 | { | 115 | { |
| 103 | block_input (); | 116 | block_input (); |
| @@ -152,6 +165,9 @@ Returns the newly constructed xwidget, or nil if construction fails. */) | |||
| 152 | 165 | ||
| 153 | unblock_input (); | 166 | unblock_input (); |
| 154 | } | 167 | } |
| 168 | #elif defined NS_IMPL_COCOA | ||
| 169 | nsxwidget_init (xw); | ||
| 170 | #endif | ||
| 155 | 171 | ||
| 156 | return val; | 172 | return val; |
| 157 | } | 173 | } |
| @@ -187,6 +203,7 @@ xwidget_hidden (struct xwidget_view *xv) | |||
| 187 | return xv->hidden; | 203 | return xv->hidden; |
| 188 | } | 204 | } |
| 189 | 205 | ||
| 206 | #ifdef USE_GTK | ||
| 190 | static void | 207 | static void |
| 191 | xwidget_show_view (struct xwidget_view *xv) | 208 | xwidget_show_view (struct xwidget_view *xv) |
| 192 | { | 209 | { |
| @@ -220,13 +237,14 @@ offscreen_damage_event (GtkWidget *widget, GdkEvent *event, | |||
| 220 | if (GTK_IS_WIDGET (xv_widget)) | 237 | if (GTK_IS_WIDGET (xv_widget)) |
| 221 | gtk_widget_queue_draw (GTK_WIDGET (xv_widget)); | 238 | gtk_widget_queue_draw (GTK_WIDGET (xv_widget)); |
| 222 | else | 239 | else |
| 223 | printf ("Warning, offscreen_damage_event received invalid xv pointer:%p\n", | 240 | message ("Warning, offscreen_damage_event received invalid xv pointer:%p\n", |
| 224 | xv_widget); | 241 | xv_widget); |
| 225 | 242 | ||
| 226 | return FALSE; | 243 | return FALSE; |
| 227 | } | 244 | } |
| 245 | #endif /* USE_GTK */ | ||
| 228 | 246 | ||
| 229 | static void | 247 | void |
| 230 | store_xwidget_event_string (struct xwidget *xw, const char *eventname, | 248 | store_xwidget_event_string (struct xwidget *xw, const char *eventname, |
| 231 | const char *eventstr) | 249 | const char *eventstr) |
| 232 | { | 250 | { |
| @@ -240,7 +258,7 @@ store_xwidget_event_string (struct xwidget *xw, const char *eventname, | |||
| 240 | kbd_buffer_store_event (&event); | 258 | kbd_buffer_store_event (&event); |
| 241 | } | 259 | } |
| 242 | 260 | ||
| 243 | static void | 261 | void |
| 244 | store_xwidget_js_callback_event (struct xwidget *xw, | 262 | store_xwidget_js_callback_event (struct xwidget *xw, |
| 245 | Lisp_Object proc, | 263 | Lisp_Object proc, |
| 246 | Lisp_Object argument) | 264 | Lisp_Object argument) |
| @@ -256,6 +274,7 @@ store_xwidget_js_callback_event (struct xwidget *xw, | |||
| 256 | } | 274 | } |
| 257 | 275 | ||
| 258 | 276 | ||
| 277 | #ifdef USE_GTK | ||
| 259 | void | 278 | void |
| 260 | webkit_view_load_changed_cb (WebKitWebView *webkitwebview, | 279 | webkit_view_load_changed_cb (WebKitWebView *webkitwebview, |
| 261 | WebKitLoadEvent load_event, | 280 | WebKitLoadEvent load_event, |
| @@ -486,6 +505,7 @@ xwidget_osr_event_set_embedder (GtkWidget *widget, GdkEvent *event, | |||
| 486 | gtk_widget_get_window (xv->widget)); | 505 | gtk_widget_get_window (xv->widget)); |
| 487 | return FALSE; | 506 | return FALSE; |
| 488 | } | 507 | } |
| 508 | #endif /* USE_GTK */ | ||
| 489 | 509 | ||
| 490 | 510 | ||
| 491 | /* Initializes and does initial placement of an xwidget view on screen. */ | 511 | /* Initializes and does initial placement of an xwidget view on screen. */ |
| @@ -495,8 +515,10 @@ xwidget_init_view (struct xwidget *xww, | |||
| 495 | int x, int y) | 515 | int x, int y) |
| 496 | { | 516 | { |
| 497 | 517 | ||
| 518 | #ifdef USE_GTK | ||
| 498 | if (!xg_gtk_initialized) | 519 | if (!xg_gtk_initialized) |
| 499 | error ("xwidget_init_view: GTK has not been initialized"); | 520 | error ("xwidget_init_view: GTK has not been initialized"); |
| 521 | #endif | ||
| 500 | 522 | ||
| 501 | struct xwidget_view *xv = allocate_xwidget_view (); | 523 | struct xwidget_view *xv = allocate_xwidget_view (); |
| 502 | Lisp_Object val; | 524 | Lisp_Object val; |
| @@ -507,6 +529,7 @@ xwidget_init_view (struct xwidget *xww, | |||
| 507 | XSETWINDOW (xv->w, s->w); | 529 | XSETWINDOW (xv->w, s->w); |
| 508 | XSETXWIDGET (xv->model, xww); | 530 | XSETXWIDGET (xv->model, xww); |
| 509 | 531 | ||
| 532 | #ifdef USE_GTK | ||
| 510 | if (EQ (xww->type, Qwebkit)) | 533 | if (EQ (xww->type, Qwebkit)) |
| 511 | { | 534 | { |
| 512 | xv->widget = gtk_drawing_area_new (); | 535 | xv->widget = gtk_drawing_area_new (); |
| @@ -564,6 +587,10 @@ xwidget_init_view (struct xwidget *xww, | |||
| 564 | xv->x = x; | 587 | xv->x = x; |
| 565 | xv->y = y; | 588 | xv->y = y; |
| 566 | gtk_widget_show_all (xv->widgetwindow); | 589 | gtk_widget_show_all (xv->widgetwindow); |
| 590 | #elif defined NS_IMPL_COCOA | ||
| 591 | nsxwidget_init_view (xv, xww, s, x, y); | ||
| 592 | nsxwidget_resize_view(xv, xww->width, xww->height); | ||
| 593 | #endif | ||
| 567 | 594 | ||
| 568 | return xv; | 595 | return xv; |
| 569 | } | 596 | } |
| @@ -576,6 +603,7 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) | |||
| 576 | initialization. */ | 603 | initialization. */ |
| 577 | struct xwidget *xww = s->xwidget; | 604 | struct xwidget *xww = s->xwidget; |
| 578 | struct xwidget_view *xv = xwidget_view_lookup (xww, s->w); | 605 | struct xwidget_view *xv = xwidget_view_lookup (xww, s->w); |
| 606 | int text_area_x, text_area_y, text_area_width, text_area_height; | ||
| 579 | int clip_right; | 607 | int clip_right; |
| 580 | int clip_bottom; | 608 | int clip_bottom; |
| 581 | int clip_top; | 609 | int clip_top; |
| @@ -587,13 +615,47 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) | |||
| 587 | /* Do initialization here in the display loop because there is no | 615 | /* Do initialization here in the display loop because there is no |
| 588 | other time to know things like window placement etc. Do not | 616 | other time to know things like window placement etc. Do not |
| 589 | create a new view if we have found one that is usable. */ | 617 | create a new view if we have found one that is usable. */ |
| 618 | #ifdef USE_GTK | ||
| 590 | if (!xv) | 619 | if (!xv) |
| 591 | xv = xwidget_init_view (xww, s, x, y); | 620 | xv = xwidget_init_view (xww, s, x, y); |
| 592 | 621 | #elif defined NS_IMPL_COCOA | |
| 593 | int text_area_x, text_area_y, text_area_width, text_area_height; | 622 | if (!xv) |
| 623 | { | ||
| 624 | /* Enforce 1 to 1, model and view for macOS Cocoa webkit2. */ | ||
| 625 | if (xww->xv) | ||
| 626 | { | ||
| 627 | if (xwidget_hidden (xww->xv)) | ||
| 628 | { | ||
| 629 | Lisp_Object xvl; | ||
| 630 | XSETXWIDGET_VIEW (xvl, xww->xv); | ||
| 631 | Fdelete_xwidget_view (xvl); | ||
| 632 | } | ||
| 633 | else | ||
| 634 | { | ||
| 635 | message ("You can't share an xwidget (webkit2) among windows."); | ||
| 636 | return; | ||
| 637 | } | ||
| 638 | } | ||
| 639 | xv = xwidget_init_view (xww, s, x, y); | ||
| 640 | } | ||
| 641 | #endif | ||
| 594 | 642 | ||
| 595 | window_box (s->w, TEXT_AREA, &text_area_x, &text_area_y, | 643 | window_box (s->w, TEXT_AREA, &text_area_x, &text_area_y, |
| 596 | &text_area_width, &text_area_height); | 644 | &text_area_width, &text_area_height); |
| 645 | |||
| 646 | /* Resize xwidget webkit if its container window size is changed in | ||
| 647 | some ways, for example, a buffer became hidden in small split | ||
| 648 | window, then it can appear front in merged whole window. */ | ||
| 649 | if (EQ (xww->type, Qwebkit) | ||
| 650 | && (xww->width != text_area_width || xww->height != text_area_height)) | ||
| 651 | { | ||
| 652 | Lisp_Object xwl; | ||
| 653 | XSETXWIDGET (xwl, xww); | ||
| 654 | Fxwidget_resize (xwl, | ||
| 655 | make_int (text_area_width), | ||
| 656 | make_int (text_area_height)); | ||
| 657 | } | ||
| 658 | |||
| 597 | clip_left = max (0, text_area_x - x); | 659 | clip_left = max (0, text_area_x - x); |
| 598 | clip_right = max (clip_left, | 660 | clip_right = max (clip_left, |
| 599 | min (xww->width, text_area_x + text_area_width - x)); | 661 | min (xww->width, text_area_x + text_area_width - x)); |
| @@ -616,8 +678,14 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) | |||
| 616 | 678 | ||
| 617 | /* Has it moved? */ | 679 | /* Has it moved? */ |
| 618 | if (moved) | 680 | if (moved) |
| 619 | gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)), | 681 | { |
| 620 | xv->widgetwindow, x + clip_left, y + clip_top); | 682 | #ifdef USE_GTK |
| 683 | gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)), | ||
| 684 | xv->widgetwindow, x + clip_left, y + clip_top); | ||
| 685 | #elif defined NS_IMPL_COCOA | ||
| 686 | nsxwidget_move_view (xv, x + clip_left, y + clip_top); | ||
| 687 | #endif | ||
| 688 | } | ||
| 621 | 689 | ||
| 622 | /* Clip the widget window if some parts happen to be outside | 690 | /* Clip the widget window if some parts happen to be outside |
| 623 | drawable area. An Emacs window is not a gtk window. A gtk window | 691 | 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) | |||
| 628 | || xv->clip_bottom != clip_bottom | 696 | || xv->clip_bottom != clip_bottom |
| 629 | || xv->clip_top != clip_top || xv->clip_left != clip_left) | 697 | || xv->clip_top != clip_top || xv->clip_left != clip_left) |
| 630 | { | 698 | { |
| 699 | #ifdef USE_GTK | ||
| 631 | gtk_widget_set_size_request (xv->widgetwindow, clip_right - clip_left, | 700 | gtk_widget_set_size_request (xv->widgetwindow, clip_right - clip_left, |
| 632 | clip_bottom - clip_top); | 701 | clip_bottom - clip_top); |
| 633 | gtk_fixed_move (GTK_FIXED (xv->widgetwindow), xv->widget, -clip_left, | 702 | gtk_fixed_move (GTK_FIXED (xv->widgetwindow), xv->widget, -clip_left, |
| 634 | -clip_top); | 703 | -clip_top); |
| 704 | #elif defined NS_IMPL_COCOA | ||
| 705 | nsxwidget_resize_view (xv, clip_right - clip_left, | ||
| 706 | clip_bottom - clip_top); | ||
| 707 | nsxwidget_move_widget_in_view (xv, -clip_left, -clip_top); | ||
| 708 | #endif | ||
| 635 | 709 | ||
| 636 | xv->clip_right = clip_right; | 710 | xv->clip_right = clip_right; |
| 637 | xv->clip_bottom = clip_bottom; | 711 | xv->clip_bottom = clip_bottom; |
| @@ -645,16 +719,30 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) | |||
| 645 | xwidgets background. It's just a visual glitch though. */ | 719 | xwidgets background. It's just a visual glitch though. */ |
| 646 | if (!xwidget_hidden (xv)) | 720 | if (!xwidget_hidden (xv)) |
| 647 | { | 721 | { |
| 722 | #ifdef USE_GTK | ||
| 648 | gtk_widget_queue_draw (xv->widgetwindow); | 723 | gtk_widget_queue_draw (xv->widgetwindow); |
| 649 | gtk_widget_queue_draw (xv->widget); | 724 | gtk_widget_queue_draw (xv->widget); |
| 725 | #elif defined NS_IMPL_COCOA | ||
| 726 | nsxwidget_set_needsdisplay (xv); | ||
| 727 | #endif | ||
| 650 | } | 728 | } |
| 651 | } | 729 | } |
| 652 | 730 | ||
| 653 | /* Macro that checks WEBKIT_IS_WEB_VIEW (xw->widget_osr) first. */ | 731 | static bool |
| 732 | xwidget_is_web_view (struct xwidget *xw) | ||
| 733 | { | ||
| 734 | #ifdef USE_GTK | ||
| 735 | return xw->widget_osr != NULL && WEBKIT_IS_WEB_VIEW (xw->widget_osr); | ||
| 736 | #elif defined NS_IMPL_COCOA | ||
| 737 | return nsxwidget_is_web_view (xw); | ||
| 738 | #endif | ||
| 739 | } | ||
| 740 | |||
| 741 | /* Macro that checks xwidget hold webkit web view first. */ | ||
| 654 | #define WEBKIT_FN_INIT() \ | 742 | #define WEBKIT_FN_INIT() \ |
| 655 | CHECK_XWIDGET (xwidget); \ | 743 | CHECK_XWIDGET (xwidget); \ |
| 656 | struct xwidget *xw = XXWIDGET (xwidget); \ | 744 | struct xwidget *xw = XXWIDGET (xwidget); \ |
| 657 | if (!xw->widget_osr || !WEBKIT_IS_WEB_VIEW (xw->widget_osr)) \ | 745 | if (!xwidget_is_web_view (xw)) \ |
| 658 | { \ | 746 | { \ |
| 659 | fputs ("ERROR xw->widget_osr does not hold a webkit instance\n", \ | 747 | fputs ("ERROR xw->widget_osr does not hold a webkit instance\n", \ |
| 660 | stdout); \ | 748 | stdout); \ |
| @@ -670,7 +758,11 @@ DEFUN ("xwidget-webkit-goto-uri", | |||
| 670 | WEBKIT_FN_INIT (); | 758 | WEBKIT_FN_INIT (); |
| 671 | CHECK_STRING (uri); | 759 | CHECK_STRING (uri); |
| 672 | uri = ENCODE_FILE (uri); | 760 | uri = ENCODE_FILE (uri); |
| 761 | #ifdef USE_GTK | ||
| 673 | webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr), SSDATA (uri)); | 762 | webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr), SSDATA (uri)); |
| 763 | #elif defined NS_IMPL_COCOA | ||
| 764 | nsxwidget_webkit_goto_uri (xw, SSDATA (uri)); | ||
| 765 | #endif | ||
| 674 | return Qnil; | 766 | return Qnil; |
| 675 | } | 767 | } |
| 676 | 768 | ||
| @@ -684,14 +776,19 @@ DEFUN ("xwidget-webkit-zoom", | |||
| 684 | if (FLOATP (factor)) | 776 | if (FLOATP (factor)) |
| 685 | { | 777 | { |
| 686 | double zoom_change = XFLOAT_DATA (factor); | 778 | double zoom_change = XFLOAT_DATA (factor); |
| 779 | #ifdef USE_GTK | ||
| 687 | webkit_web_view_set_zoom_level | 780 | webkit_web_view_set_zoom_level |
| 688 | (WEBKIT_WEB_VIEW (xw->widget_osr), | 781 | (WEBKIT_WEB_VIEW (xw->widget_osr), |
| 689 | webkit_web_view_get_zoom_level | 782 | webkit_web_view_get_zoom_level |
| 690 | (WEBKIT_WEB_VIEW (xw->widget_osr)) + zoom_change); | 783 | (WEBKIT_WEB_VIEW (xw->widget_osr)) + zoom_change); |
| 784 | #elif defined NS_IMPL_COCOA | ||
| 785 | nsxwidget_webkit_zoom (xw, zoom_change); | ||
| 786 | #endif | ||
| 691 | } | 787 | } |
| 692 | return Qnil; | 788 | return Qnil; |
| 693 | } | 789 | } |
| 694 | 790 | ||
| 791 | #ifdef USE_GTK | ||
| 695 | /* Save script and fun in the script/callback save vector and return | 792 | /* Save script and fun in the script/callback save vector and return |
| 696 | its index. */ | 793 | its index. */ |
| 697 | static ptrdiff_t | 794 | static ptrdiff_t |
| @@ -713,6 +810,7 @@ save_script_callback (struct xwidget *xw, Lisp_Object script, Lisp_Object fun) | |||
| 713 | ASET (cbs, idx, Fcons (make_mint_ptr (xlispstrdup (script)), fun)); | 810 | ASET (cbs, idx, Fcons (make_mint_ptr (xlispstrdup (script)), fun)); |
| 714 | return idx; | 811 | return idx; |
| 715 | } | 812 | } |
| 813 | #endif | ||
| 716 | 814 | ||
| 717 | DEFUN ("xwidget-webkit-execute-script", | 815 | DEFUN ("xwidget-webkit-execute-script", |
| 718 | Fxwidget_webkit_execute_script, Sxwidget_webkit_execute_script, | 816 | Fxwidget_webkit_execute_script, Sxwidget_webkit_execute_script, |
| @@ -729,6 +827,7 @@ argument procedure FUN.*/) | |||
| 729 | 827 | ||
| 730 | script = ENCODE_SYSTEM (script); | 828 | script = ENCODE_SYSTEM (script); |
| 731 | 829 | ||
| 830 | #ifdef USE_GTK | ||
| 732 | /* Protect script and fun during GC. */ | 831 | /* Protect script and fun during GC. */ |
| 733 | intptr_t idx = save_script_callback (xw, script, fun); | 832 | intptr_t idx = save_script_callback (xw, script, fun); |
| 734 | 833 | ||
| @@ -742,6 +841,9 @@ argument procedure FUN.*/) | |||
| 742 | NULL, /* cancelable */ | 841 | NULL, /* cancelable */ |
| 743 | webkit_javascript_finished_cb, | 842 | webkit_javascript_finished_cb, |
| 744 | (gpointer) idx); | 843 | (gpointer) idx); |
| 844 | #elif defined NS_IMPL_COCOA | ||
| 845 | nsxwidget_webkit_execute_script (xw, SSDATA (script), fun); | ||
| 846 | #endif | ||
| 745 | return Qnil; | 847 | return Qnil; |
| 746 | } | 848 | } |
| 747 | 849 | ||
| @@ -758,6 +860,7 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0, | |||
| 758 | xw->height = h; | 860 | xw->height = h; |
| 759 | 861 | ||
| 760 | /* If there is an offscreen widget resize it first. */ | 862 | /* If there is an offscreen widget resize it first. */ |
| 863 | #ifdef USE_GTK | ||
| 761 | if (xw->widget_osr) | 864 | if (xw->widget_osr) |
| 762 | { | 865 | { |
| 763 | gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width, | 866 | gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width, |
| @@ -766,6 +869,9 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0, | |||
| 766 | gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width, | 869 | gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width, |
| 767 | xw->height); | 870 | xw->height); |
| 768 | } | 871 | } |
| 872 | #elif defined NS_IMPL_COCOA | ||
| 873 | nsxwidget_resize (xw); | ||
| 874 | #endif | ||
| 769 | 875 | ||
| 770 | for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail)) | 876 | for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail)) |
| 771 | { | 877 | { |
| @@ -773,8 +879,14 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0, | |||
| 773 | { | 879 | { |
| 774 | struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail)); | 880 | struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail)); |
| 775 | if (XXWIDGET (xv->model) == xw) | 881 | if (XXWIDGET (xv->model) == xw) |
| 882 | { | ||
| 883 | #ifdef USE_GTK | ||
| 776 | gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xw->width, | 884 | gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xw->width, |
| 777 | xw->height); | 885 | xw->height); |
| 886 | #elif defined NS_IMPL_COCOA | ||
| 887 | nsxwidget_resize_view(xv, xw->width, xw->height); | ||
| 888 | #endif | ||
| 889 | } | ||
| 778 | } | 890 | } |
| 779 | } | 891 | } |
| 780 | 892 | ||
| @@ -793,9 +905,13 @@ Emacs allocated area accordingly. */) | |||
| 793 | (Lisp_Object xwidget) | 905 | (Lisp_Object xwidget) |
| 794 | { | 906 | { |
| 795 | CHECK_XWIDGET (xwidget); | 907 | CHECK_XWIDGET (xwidget); |
| 908 | #ifdef USE_GTK | ||
| 796 | GtkRequisition requisition; | 909 | GtkRequisition requisition; |
| 797 | gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition); | 910 | gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition); |
| 798 | return list2i (requisition.width, requisition.height); | 911 | return list2i (requisition.width, requisition.height); |
| 912 | #elif defined NS_IMPL_COCOA | ||
| 913 | return nsxwidget_get_size (XXWIDGET (xwidget)); | ||
| 914 | #endif | ||
| 799 | } | 915 | } |
| 800 | 916 | ||
| 801 | DEFUN ("xwidgetp", | 917 | DEFUN ("xwidgetp", |
| @@ -872,14 +988,19 @@ DEFUN ("delete-xwidget-view", | |||
| 872 | { | 988 | { |
| 873 | CHECK_XWIDGET_VIEW (xwidget_view); | 989 | CHECK_XWIDGET_VIEW (xwidget_view); |
| 874 | struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view); | 990 | struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view); |
| 991 | #ifdef USE_GTK | ||
| 875 | gtk_widget_destroy (xv->widgetwindow); | 992 | gtk_widget_destroy (xv->widgetwindow); |
| 876 | Vxwidget_view_list = Fdelq (xwidget_view, Vxwidget_view_list); | ||
| 877 | /* xv->model still has signals pointing to the view. There can be | 993 | /* xv->model still has signals pointing to the view. There can be |
| 878 | several views. Find the matching signals and delete them all. */ | 994 | several views. Find the matching signals and delete them all. */ |
| 879 | g_signal_handlers_disconnect_matched (XXWIDGET (xv->model)->widgetwindow_osr, | 995 | g_signal_handlers_disconnect_matched (XXWIDGET (xv->model)->widgetwindow_osr, |
| 880 | G_SIGNAL_MATCH_DATA, | 996 | G_SIGNAL_MATCH_DATA, |
| 881 | 0, 0, 0, 0, | 997 | 0, 0, 0, 0, |
| 882 | xv->widget); | 998 | xv->widget); |
| 999 | #elif defined NS_IMPL_COCOA | ||
| 1000 | nsxwidget_delete_view (xv); | ||
| 1001 | #endif | ||
| 1002 | |||
| 1003 | Vxwidget_view_list = Fdelq (xwidget_view, Vxwidget_view_list); | ||
| 883 | return Qnil; | 1004 | return Qnil; |
| 884 | } | 1005 | } |
| 885 | 1006 | ||
| @@ -1156,11 +1277,19 @@ xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix) | |||
| 1156 | xwidget_end_redisplay (w->current_matrix); */ | 1277 | xwidget_end_redisplay (w->current_matrix); */ |
| 1157 | struct xwidget_view *xv | 1278 | struct xwidget_view *xv |
| 1158 | = xwidget_view_lookup (glyph->u.xwidget, w); | 1279 | = xwidget_view_lookup (glyph->u.xwidget, w); |
| 1280 | #ifdef USE_GTK | ||
| 1159 | /* FIXME: Is it safe to assume xwidget_view_lookup | 1281 | /* FIXME: Is it safe to assume xwidget_view_lookup |
| 1160 | always succeeds here? If so, this comment can be removed. | 1282 | always succeeds here? If so, this comment can be removed. |
| 1161 | If not, the code probably needs fixing. */ | 1283 | If not, the code probably needs fixing. */ |
| 1162 | eassume (xv); | 1284 | eassume (xv); |
| 1163 | xwidget_touch (xv); | 1285 | xwidget_touch (xv); |
| 1286 | #elif defined NS_IMPL_COCOA | ||
| 1287 | /* In NS xwidget, xv can be NULL for the second or | ||
| 1288 | later views for a model, the result of 1 to 1 | ||
| 1289 | model view relation enforcement. */ | ||
| 1290 | if (xv) | ||
| 1291 | xwidget_touch (xv); | ||
| 1292 | #endif | ||
| 1164 | } | 1293 | } |
| 1165 | } | 1294 | } |
| 1166 | } | 1295 | } |
| @@ -1177,9 +1306,21 @@ xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix) | |||
| 1177 | if (XWINDOW (xv->w) == w) | 1306 | if (XWINDOW (xv->w) == w) |
| 1178 | { | 1307 | { |
| 1179 | if (xwidget_touched (xv)) | 1308 | if (xwidget_touched (xv)) |
| 1180 | xwidget_show_view (xv); | 1309 | { |
| 1310 | #ifdef USE_GTK | ||
| 1311 | xwidget_show_view (xv); | ||
| 1312 | #elif defined NS_IMPL_COCOA | ||
| 1313 | nsxwidget_show_view (xv); | ||
| 1314 | #endif | ||
| 1315 | } | ||
| 1181 | else | 1316 | else |
| 1182 | xwidget_hide_view (xv); | 1317 | { |
| 1318 | #ifdef USE_GTK | ||
| 1319 | xwidget_hide_view (xv); | ||
| 1320 | #elif defined NS_IMPL_COCOA | ||
| 1321 | nsxwidget_hide_view (xv); | ||
| 1322 | #endif | ||
| 1323 | } | ||
| 1183 | } | 1324 | } |
| 1184 | } | 1325 | } |
| 1185 | } | 1326 | } |
| @@ -1198,6 +1339,7 @@ kill_buffer_xwidgets (Lisp_Object buffer) | |||
| 1198 | { | 1339 | { |
| 1199 | CHECK_XWIDGET (xwidget); | 1340 | CHECK_XWIDGET (xwidget); |
| 1200 | struct xwidget *xw = XXWIDGET (xwidget); | 1341 | struct xwidget *xw = XXWIDGET (xwidget); |
| 1342 | #ifdef USE_GTK | ||
| 1201 | if (xw->widget_osr && xw->widgetwindow_osr) | 1343 | if (xw->widget_osr && xw->widgetwindow_osr) |
| 1202 | { | 1344 | { |
| 1203 | gtk_widget_destroy (xw->widget_osr); | 1345 | gtk_widget_destroy (xw->widget_osr); |
| @@ -1211,6 +1353,9 @@ kill_buffer_xwidgets (Lisp_Object buffer) | |||
| 1211 | xfree (xmint_pointer (XCAR (cb))); | 1353 | xfree (xmint_pointer (XCAR (cb))); |
| 1212 | ASET (xw->script_callbacks, idx, Qnil); | 1354 | ASET (xw->script_callbacks, idx, Qnil); |
| 1213 | } | 1355 | } |
| 1356 | #elif defined NS_IMPL_COCOA | ||
| 1357 | nsxwidget_kill (xw); | ||
| 1358 | #endif | ||
| 1214 | } | 1359 | } |
| 1215 | } | 1360 | } |
| 1216 | } | 1361 | } |
diff --git a/src/xwidget.h b/src/xwidget.h index 99fa8bbd612..29f1153206f 100644 --- a/src/xwidget.h +++ b/src/xwidget.h | |||
| @@ -29,7 +29,13 @@ struct xwidget_view; | |||
| 29 | struct window; | 29 | struct window; |
| 30 | 30 | ||
| 31 | #ifdef HAVE_XWIDGETS | 31 | #ifdef HAVE_XWIDGETS |
| 32 | # include <gtk/gtk.h> | 32 | |
| 33 | #if defined (USE_GTK) | ||
| 34 | #include <gtk/gtk.h> | ||
| 35 | #elif defined (NS_IMPL_COCOA) && defined (__OBJC__) | ||
| 36 | #import <AppKit/NSView.h> | ||
| 37 | #import "nsxwidget.h" | ||
| 38 | #endif | ||
| 33 | 39 | ||
| 34 | struct xwidget | 40 | struct xwidget |
| 35 | { | 41 | { |
| @@ -54,9 +60,25 @@ struct xwidget | |||
| 54 | int height; | 60 | int height; |
| 55 | int width; | 61 | int width; |
| 56 | 62 | ||
| 63 | #if defined (USE_GTK) | ||
| 57 | /* For offscreen widgets, unused if not osr. */ | 64 | /* For offscreen widgets, unused if not osr. */ |
| 58 | GtkWidget *widget_osr; | 65 | GtkWidget *widget_osr; |
| 59 | GtkWidget *widgetwindow_osr; | 66 | GtkWidget *widgetwindow_osr; |
| 67 | #elif defined (NS_IMPL_COCOA) | ||
| 68 | # ifdef __OBJC__ | ||
| 69 | /* For offscreen widgets, unused if not osr. */ | ||
| 70 | NSView *xwWidget; | ||
| 71 | XwWindow *xwWindow; | ||
| 72 | |||
| 73 | /* Used only for xwidget types (such as webkit2) enforcing 1 to 1 | ||
| 74 | relationship between model and view. */ | ||
| 75 | struct xwidget_view *xv; | ||
| 76 | # else | ||
| 77 | void *xwWidget; | ||
| 78 | void *xwWindow; | ||
| 79 | struct xwidget_view *xv; | ||
| 80 | # endif | ||
| 81 | #endif | ||
| 60 | 82 | ||
| 61 | /* Kill silently if Emacs is exited. */ | 83 | /* Kill silently if Emacs is exited. */ |
| 62 | bool_bf kill_without_query : 1; | 84 | bool_bf kill_without_query : 1; |
| @@ -75,9 +97,20 @@ struct xwidget_view | |||
| 75 | /* The "live" instance isn't drawn. */ | 97 | /* The "live" instance isn't drawn. */ |
| 76 | bool hidden; | 98 | bool hidden; |
| 77 | 99 | ||
| 100 | #if defined (USE_GTK) | ||
| 78 | GtkWidget *widget; | 101 | GtkWidget *widget; |
| 79 | GtkWidget *widgetwindow; | 102 | GtkWidget *widgetwindow; |
| 80 | GtkWidget *emacswindow; | 103 | GtkWidget *emacswindow; |
| 104 | #elif defined (NS_IMPL_COCOA) | ||
| 105 | # ifdef __OBJC__ | ||
| 106 | XvWindow *xvWindow; | ||
| 107 | NSView *emacswindow; | ||
| 108 | # else | ||
| 109 | void *xvWindow; | ||
| 110 | void *emacswindow; | ||
| 111 | # endif | ||
| 112 | #endif | ||
| 113 | |||
| 81 | int x; | 114 | int x; |
| 82 | int y; | 115 | int y; |
| 83 | int clip_right; | 116 | int clip_right; |
| @@ -116,6 +149,14 @@ void x_draw_xwidget_glyph_string (struct glyph_string *); | |||
| 116 | struct xwidget *lookup_xwidget (Lisp_Object spec); | 149 | struct xwidget *lookup_xwidget (Lisp_Object spec); |
| 117 | void xwidget_end_redisplay (struct window *, struct glyph_matrix *); | 150 | void xwidget_end_redisplay (struct window *, struct glyph_matrix *); |
| 118 | void kill_buffer_xwidgets (Lisp_Object); | 151 | void kill_buffer_xwidgets (Lisp_Object); |
| 152 | /* Defined in 'xwidget.c'. */ | ||
| 153 | void store_xwidget_event_string (struct xwidget *xw, | ||
| 154 | const char *eventname, | ||
| 155 | const char *eventstr); | ||
| 156 | |||
| 157 | void store_xwidget_js_callback_event (struct xwidget *xw, | ||
| 158 | Lisp_Object proc, | ||
| 159 | Lisp_Object argument); | ||
| 119 | #else | 160 | #else |
| 120 | INLINE_HEADER_BEGIN | 161 | INLINE_HEADER_BEGIN |
| 121 | INLINE void syms_of_xwidget (void) {} | 162 | INLINE void syms_of_xwidget (void) {} |