From 2f90ceb0d2e556cad2802cdca5ef8324a8dc5523 Mon Sep 17 00:00:00 2001
From: Sungbin Jo <goranmoomin@daum.net>
Date: Tue, 4 Oct 2022 08:51:33 +0900
Subject: [PATCH] Handle sharing Cocoa xwidgets more gracefully

* src/nsxwidget.h (nsxwidget_resize, XwWindow, XvWindow): Remove
unused subclasses and functions.
* src/nsxwidget.m:
([XwWebView mouseDown:], [XwWebView mouseUp:])
([XwWebView keyDown:])
([XwWebView userContentController:didReceiveScriptMessage:]):
Rename field of xwidget_view from emacswindow to emacsFrame to
better match emacs terminology.
(nsxwidget_init, nsxwidget_resize_view)
(nsxwidget_move_widget_in_view): Simplify logic by removing
field xwWindow and using the xvWindow as the container.
(nsxwidget_resize, XwWindow, XvWindow): Remove unused subclasses
and functions.
(nsxwidget_init_view, nsxwidget_delete_view): Handle creating
non-primary xwidget views.
(nsxwidget_show_view, nsxwidget_hide_view): Remove hack to hide
views.
* src/xwidget.c (xwidget_init_view): Update formatting.
(x_draw_xwidget_glyph_string): Handle displaying non-primary
xwidget views and remove previous message warning.
(Fxwidget_resize): Remove useless call to nsxwidget_resize, as
the subsequent redisplay handles them via nsxwidget_resize_view.
* src/xwidget.h (struct xwidget): Remove field xwWindow and
update comments to be more accurate.
(struct xwidget_view): Add field xvWidget and rename field
emacswindow to emacsFrame to better match emacs terminology.
---
 src/nsxwidget.h | 14 --------
 src/nsxwidget.m | 86 ++++++++++++++++++++-----------------------------
 src/xwidget.c   | 11 ++-----
 src/xwidget.h   | 15 ++++-----
 4 files changed, 44 insertions(+), 82 deletions(-)

diff --git a/src/nsxwidget.h b/src/nsxwidget.h
index 666509744a..f7c744d405 100644
--- a/src/nsxwidget.h
+++ b/src/nsxwidget.h
@@ -42,26 +42,12 @@ #define NSXWIDGET_H_INCLUDED
 
 /* 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,
diff --git a/src/nsxwidget.m b/src/nsxwidget.m
index be0eba0bcb..2a6d5cdb54 100644
--- a/src/nsxwidget.m
+++ b/src/nsxwidget.m
@@ -199,13 +199,13 @@ - (void)webView:(WKWebView *)webView
 
 - (void)mouseDown:(NSEvent *)event
 {
-  [self.xw->xv->emacswindow mouseDown:event];
+  [self.xw->xv->emacsFrame mouseDown:event];
   [super mouseDown:event];
 }
 
 - (void)mouseUp:(NSEvent *)event
 {
-  [self.xw->xv->emacswindow mouseUp:event];
+  [self.xw->xv->emacsFrame mouseUp:event];
   [super mouseUp:event];
 }
 
@@ -220,15 +220,15 @@ - (void)keyDown:(NSEvent *)event
   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];
+      [self.window makeFirstResponder:self.xw->xv->emacsFrame];
+      [self.xw->xv->emacsFrame keyDown:event];
       return;
     }
 
   /* Emacs handles keyboard events when javascript is blocked.  */
   if ([self.urlScriptBlocked[self.URL] boolValue])
     {
-      [self.xw->xv->emacswindow keyDown:event];
+      [self.xw->xv->emacsFrame keyDown:event];
       return;
     }
 
@@ -237,13 +237,13 @@ - (void)keyDown:(NSEvent *)event
       if (error)
         {
           NSLog (@"xwHasFocus: %@", error);
-          [self.xw->xv->emacswindow keyDown:event];
+          [self.xw->xv->emacsFrame keyDown:event];
         }
       else if (result)
         {
           NSNumber *hasFocus = result; /* __NSCFBoolean */
           if (!hasFocus.boolValue)
-            [self.xw->xv->emacswindow keyDown:event];
+            [self.xw->xv->emacsFrame keyDown:event];
           else
             [super keyDown:event];
         }
@@ -291,7 +291,7 @@ - (void)userContentController:(WKUserContentController *)userContentController
     {
       /* 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];
+      [self.window makeFirstResponder:self.xw->xv->emacsFrame];
     }
 }
 
@@ -437,16 +437,10 @@ - (void)userContentController:(WKUserContentController *)userContentController
     }];
 }
 
-/* Window containing an xwidget.  */
-
-@implementation XwWindow
-- (BOOL)isFlipped { return YES; }
-@end
-
 /* Xwidget model, macOS Cocoa part.  */
 
 void
-nsxwidget_init(struct xwidget *xw)
+nsxwidget_init (struct xwidget *xw)
 {
   block_input ();
   NSRect rect = NSMakeRect (0, 0, xw->width, xw->height);
@@ -454,9 +448,6 @@ - (BOOL)isFlipped { return YES; }
                    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 ();
 }
@@ -481,22 +472,10 @@ - (BOOL)isFlipped { return YES; }
       [((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)
 {
@@ -504,12 +483,6 @@ - (BOOL)isFlipped { return YES; }
                  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,
@@ -526,16 +499,28 @@ - (BOOL)isFlipped { return YES; }
   xv->clip_top = 0;
   xv->clip_bottom = xw->height;
 
-  xv->xvWindow = [[XvWindow alloc]
+  xv->xvWindow = [[NSView 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];
+  if (!xw->xv)
+    {
+      xw->xv = xv; /* For 1 to 1 relationship of webkit2.  */
+      /* This seems like it should be retained, but tests show otherwise.  */
+      xv->xvWidget = xw->xwWidget;
+    }
+  else
+    {
+      NSString *warningString = @"Cocoa Xwidgets do not support sharing widgets.";
+      NSTextField *warningLabel = [NSTextField labelWithString:warningString];
+      [warningLabel setFrameSize:NSMakeSize (xw->width, xw->height)];
+      warningLabel.drawsBackground = YES;
+      warningLabel.backgroundColor = [NSColor textBackgroundColor];
+      xv->xvWidget = warningLabel;
+    }
 
-  xv->emacswindow = FRAME_NS_VIEW (s->f);
-  [xv->emacswindow addSubview:xv->xvWindow];
+  [xv->xvWindow addSubview:xv->xvWidget];
+  xv->emacsFrame = FRAME_NS_VIEW (s->f);
+  [xv->emacsFrame addSubview:xv->xvWindow];
 }
 
 void
@@ -544,8 +529,8 @@ - (BOOL)isFlipped { return YES; }
   if (!EQ (xv->model, Qnil))
     {
       struct xwidget *xw = XXWIDGET (xv->model);
-      [xw->xwWindow removeFromSuperviewWithoutNeedingDisplay];
-      xw->xv = NULL; /* Now model has no view.  */
+      if (xw->xv == xv)
+        xw->xv = NULL; /* Now model has no view.  */
     }
   [xv->xvWindow removeFromSuperviewWithoutNeedingDisplay];
   [xv->xvWindow release];
@@ -555,21 +540,21 @@ - (BOOL)isFlipped { return YES; }
 nsxwidget_show_view (struct xwidget_view *xv)
 {
   xv->hidden = NO;
-  [xv->xvWindow setFrameOrigin:NSMakePoint(xv->x + xv->clip_left,
-                                           xv->y + xv->clip_top)];
+  [xv->xvWindow setHidden:NO];
 }
 
 void
 nsxwidget_hide_view (struct xwidget_view *xv)
 {
   xv->hidden = YES;
-  [xv->xvWindow setFrameOrigin:NSMakePoint(10000, 10000)];
+  [xv->xvWindow setHidden:YES];
 }
 
 void
 nsxwidget_resize_view (struct xwidget_view *xv, int width, int height)
 {
-  [xv->xvWindow setFrameSize:NSMakeSize(width, height)];
+  [xv->xvWindow setFrameSize:NSMakeSize (width, height)];
+  [xv->xvWidget setFrameSize:NSMakeSize (width, height)];
 }
 
 void
@@ -582,8 +567,7 @@ - (BOOL)isFlipped { return YES; }
 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)];
+  [xv->xvWidget setFrameOrigin:NSMakePoint (x, y)];
 }
 
 void
diff --git a/src/xwidget.c b/src/xwidget.c
index 8bdfab02fd..013b773a90 100644
--- a/src/xwidget.c
+++ b/src/xwidget.c
@@ -2762,7 +2762,7 @@ xwidget_init_view (struct xwidget *xww,
   xv->just_resized = false;
 #elif defined NS_IMPL_COCOA
   nsxwidget_init_view (xv, xww, s, x, y);
-  nsxwidget_resize_view(xv, xww->width, xww->height);
+  nsxwidget_resize_view (xv, xww->width, xww->height);
 #endif
 
   return xv;
@@ -2794,7 +2794,7 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
 
   xv->just_resized = false;
 #elif defined NS_IMPL_COCOA
-  if (!xv)
+  if (!xv || xww->xv != xv)
     {
       /* Enforce 1 to 1, model and view for macOS Cocoa webkit2.  */
       if (xww->xv)
@@ -2805,11 +2805,6 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
               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);
     }
@@ -3243,8 +3238,6 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0,
 
       gtk_widget_queue_allocate (GTK_WIDGET (xw->widget_osr));
     }
-#elif defined NS_IMPL_COCOA
-  nsxwidget_resize (xw);
 #endif
   unblock_input ();
 
diff --git a/src/xwidget.h b/src/xwidget.h
index 502beb6765..3943148e3f 100644
--- a/src/xwidget.h
+++ b/src/xwidget.h
@@ -81,16 +81,13 @@ #define XWIDGET_H_INCLUDED
   guint hit_result;
 #elif defined (NS_IMPL_COCOA)
 # ifdef __OBJC__
-  /* For offscreen widgets, unused if not osr.  */
+  /* The primary widget.  */
   NSView *xwWidget;
-  XwWindow *xwWindow;
 
-  /* Used only for xwidget types (such as webkit2) enforcing 1 to 1
-     relationship between model and view.  */
+  /* The current view that displays the primary widget, if exists.  */
   struct xwidget_view *xv;
 # else
   void *xwWidget;
-  void *xwWindow;
   struct xwidget_view *xv;
 # endif
 #endif
@@ -136,11 +133,13 @@ #define XWIDGET_H_INCLUDED
   int just_resized;
 #elif defined (NS_IMPL_COCOA)
 # ifdef __OBJC__
-  XvWindow *xvWindow;
-  NSView *emacswindow;
+  NSView *xvWindow;
+  NSView *xvWidget;
+  NSView *emacsFrame;
 # else
   void *xvWindow;
-  void *emacswindow;
+  void *xvWidget;
+  void *emacsFrame;
 # endif
 #endif
 
-- 
2.37.0 (Apple Git-136)

