]> git.eshelyaron.com Git - emacs.git/commitdiff
Allow xwidgets to accept motion and button events
authorPo Lu <luangruo@yahoo.com>
Wed, 3 Nov 2021 11:02:59 +0000 (19:02 +0800)
committerLars Ingebrigtsen <larsi@gnus.org>
Sun, 7 Nov 2021 01:59:40 +0000 (02:59 +0100)
* src/xterm.c (handle_one_xevent): Pass through some events to
xwidgets.

* src/xwidget.c (synthesize_focus_in_event)
(find_suitable_pointer, xwidget_button_1)
(xwidget_button, xwidget_motion_or_crossing): New functions.
(xwidget_view_from_window): Draw offscreen window instead of the
widget.
(x_draw_xwidget_glyph_string): Set appropriate event masks.

* src/xwidget.h (xwidget_button, xwidget_motion_or_crossing): New
functions.

src/xterm.c
src/xwidget.c
src/xwidget.h

index 3e8cfb8b2971c1a0a643507bd93d4e9d48a6c9f9..12dad6ae8064adcc9f5ce36562cc5bf5a70e5ff7 100644 (file)
@@ -8893,6 +8893,18 @@ handle_one_xevent (struct x_display_info *dpyinfo,
       x_display_set_last_user_time (dpyinfo, event->xcrossing.time);
       x_detect_focus_change (dpyinfo, any, event, &inev.ie);
 
+#ifdef HAVE_XWIDGETS
+      {
+       struct xwidget_view *xvw = xwidget_view_from_window (event->xcrossing.window);
+
+       if (xvw)
+         {
+           xwidget_motion_or_crossing (xvw, event);
+           goto OTHER;
+         }
+      }
+#endif
+
       f = any;
 
       if (f && x_mouse_click_focus_ignore_position)
@@ -8936,6 +8948,17 @@ handle_one_xevent (struct x_display_info *dpyinfo,
       goto OTHER;
 
     case LeaveNotify:
+#ifdef HAVE_XWIDGETS
+      {
+       struct xwidget_view *xvw = xwidget_view_from_window (event->xcrossing.window);
+
+       if (xvw)
+         {
+           xwidget_motion_or_crossing (xvw, event);
+           goto OTHER;
+         }
+      }
+#endif
       x_display_set_last_user_time (dpyinfo, event->xcrossing.time);
       x_detect_focus_change (dpyinfo, any, event, &inev.ie);
 
@@ -8985,6 +9008,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 #ifdef USE_GTK
         if (f && xg_event_is_for_scrollbar (f, event))
           f = 0;
+#endif
+#ifdef HAVE_XWIDGETS
+       struct xwidget_view *xvw = xwidget_view_from_window (event->xmotion.window);
+
+       if (xvw)
+         xwidget_motion_or_crossing (xvw, event);
 #endif
         if (f)
           {
@@ -9240,6 +9269,18 @@ handle_one_xevent (struct x_display_info *dpyinfo,
     case ButtonRelease:
     case ButtonPress:
       {
+#ifdef HAVE_XWIDGETS
+       struct xwidget_view *xvw = xwidget_view_from_window (event->xmotion.window);
+
+       if (xvw)
+         {
+           xwidget_button (xvw, event->type == ButtonPress,
+                           event->xbutton.x, event->xbutton.y,
+                           event->xbutton.button, event->xbutton.state,
+                           event->xbutton.time);
+
+         }
+#endif
         /* If we decide we want to generate an event to be seen
            by the rest of Emacs, we put it here.  */
         Lisp_Object tab_bar_arg = Qnil;
index fe6640171cbff5d3e6767f2e1add3aa8b0f9270b..b25f95c7fafef267d34411109e4c46070131cd85 100644 (file)
@@ -47,6 +47,7 @@ static uint32_t xwidget_counter = 0;
 #ifdef USE_GTK
 static Lisp_Object x_window_to_xwv_map;
 static gboolean offscreen_damage_event (GtkWidget *, GdkEvent *, gpointer);
+static void synthesize_focus_in_event (GtkWidget *offscreen_window);
 #endif
 
 static struct xwidget *
@@ -170,6 +171,7 @@ Returns the newly constructed xwidget, or nil if construction fails.  */)
 
       gtk_widget_show (xw->widget_osr);
       gtk_widget_show (xw->widgetwindow_osr);
+      synthesize_focus_in_event (xw->widgetwindow_osr);
 
       /* Store some xwidget data in the gtk widgets for convenient
          retrieval in the event handlers.  */
@@ -251,6 +253,143 @@ xwidget_from_id (uint32_t id)
 
 #ifdef USE_GTK
 
+static GdkDevice *
+find_suitable_pointer (struct frame *f)
+{
+  GdkSeat *seat = gdk_display_get_default_seat
+    (gtk_widget_get_display (FRAME_GTK_WIDGET (f)));
+
+  if (!seat)
+    return NULL;
+
+  return gdk_seat_get_pointer (seat);
+}
+
+static void
+xwidget_button_1 (struct xwidget_view *view,
+                 bool down_p, int x, int y, int button,
+                 int modifier_state, Time time)
+{
+  GdkEvent *xg_event = gdk_event_new (down_p ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE);
+  struct xwidget *model = XXWIDGET (view->model);
+
+  /* X and Y should be relative to the origin of view->wdesc.  */
+  x += view->clip_left;
+  y += view->clip_top;
+
+  xg_event->any.window = gtk_widget_get_window (model->widget_osr);
+  g_object_ref (xg_event->any.window); /* The window will be unrefed
+                                         later by gdk_event_free. */
+
+  xg_event->button.x = x;
+  xg_event->button.x_root = x;
+  xg_event->button.y = y;
+  xg_event->button.y_root = y;
+  xg_event->button.button = button;
+  xg_event->button.state = modifier_state;
+  xg_event->button.time = time;
+  xg_event->button.device = find_suitable_pointer (view->frame);
+
+  gtk_main_do_event (xg_event);
+  gdk_event_free (xg_event);
+}
+
+void
+xwidget_button (struct xwidget_view *view,
+               bool down_p, int x, int y, int button,
+               int modifier_state, Time time)
+{
+  if (button < 4 || button > 8)
+    xwidget_button_1 (view, down_p, x, y, button, modifier_state, time);
+  else
+    {
+      GdkEvent *xg_event = gdk_event_new (GDK_SCROLL);
+      struct xwidget *model = XXWIDGET (view->model);
+
+      xg_event->any.window = gtk_widget_get_window (model->widget_osr);
+      g_object_ref (xg_event->any.window); /* The window will be unrefed
+                                             later by gdk_event_free. */
+      if (button == 4)
+       xg_event->scroll.direction = GDK_SCROLL_UP;
+      else if (button == 5)
+       xg_event->scroll.direction = GDK_SCROLL_DOWN;
+      else if (button == 6)
+       xg_event->scroll.direction = GDK_SCROLL_LEFT;
+      else
+       xg_event->scroll.direction = GDK_SCROLL_RIGHT;
+
+      xg_event->scroll.device = find_suitable_pointer (view->frame);
+
+      xg_event->scroll.x = x;
+      xg_event->scroll.x_root = x;
+      xg_event->scroll.y = y;
+      xg_event->scroll.y_root = y;
+      xg_event->scroll.state = modifier_state;
+      xg_event->scroll.time = time;
+
+      xg_event->scroll.delta_x = 0;
+      xg_event->scroll.delta_y = 0;
+
+      gtk_main_do_event (xg_event);
+      gdk_event_free (xg_event);
+    }
+}
+
+void
+xwidget_motion_or_crossing (struct xwidget_view *view, const XEvent *event)
+{
+  GdkEvent *xg_event = gdk_event_new (event->type == MotionNotify ? GDK_MOTION_NOTIFY :
+                                     (event->type == LeaveNotify ? GDK_LEAVE_NOTIFY :
+                                      GDK_ENTER_NOTIFY));
+  struct xwidget *model = XXWIDGET (view->model);
+
+  xg_event->any.window = gtk_widget_get_window (model->widget_osr);
+  g_object_ref (xg_event->any.window); /* The window will be unrefed
+                                         later by gdk_event_free. */
+
+  if (event->type == MotionNotify)
+    {
+      xg_event->motion.x = event->xmotion.x + view->clip_left;
+      xg_event->motion.y = event->xmotion.y + view->clip_top;
+      xg_event->motion.x_root = event->xmotion.x_root;
+      xg_event->motion.y_root = event->xmotion.y_root;
+      xg_event->motion.time = event->xmotion.time;
+      xg_event->motion.device = find_suitable_pointer (view->frame);
+    }
+  else
+    {
+      xg_event->crossing.detail = min (5, event->xcrossing.detail);
+      xg_event->crossing.time = event->xcrossing.time;
+      xg_event->crossing.x = event->xcrossing.x + view->clip_left;
+      xg_event->crossing.y = event->xcrossing.y + view->clip_top;
+      xg_event->crossing.x_root = event->xcrossing.x_root;
+      xg_event->crossing.y_root = event->xcrossing.y_root;
+    }
+
+  gtk_main_do_event (xg_event);
+  gdk_event_free (xg_event);
+}
+
+static void
+synthesize_focus_in_event (GtkWidget *offscreen_window)
+{
+  GdkWindow *wnd;
+  GdkEvent *focus_event;
+
+  if (!gtk_widget_get_realized (offscreen_window))
+    gtk_widget_realize (offscreen_window);
+
+  wnd = gtk_widget_get_window (offscreen_window);
+
+  focus_event = gdk_event_new (GDK_FOCUS_CHANGE);
+  focus_event->any.window = wnd;
+  focus_event->focus_change.in = TRUE;
+  g_object_ref (wnd);
+
+  gtk_main_do_event (focus_event);
+  gdk_event_free (focus_event);
+}
+
 struct xwidget_view *
 xwidget_view_from_window (Window wdesc)
 {
@@ -291,7 +430,7 @@ xv_do_draw (struct xwidget_view *xw, struct xwidget *w)
   cairo_save (xw->cr_context);
   cairo_translate (xw->cr_context, -xw->clip_left,
                   -xw->clip_top);
-  gtk_widget_draw (w->widget_osr, xw->cr_context);
+  gtk_widget_draw (w->widgetwindow_osr, xw->cr_context);
   cairo_restore (xw->cr_context);
 
   unblock_input ();
@@ -704,7 +843,8 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
       Lisp_Object xvw;
       XSETXWIDGET_VIEW (xvw, xv);
       XSetWindowAttributes a;
-      a.event_mask = ExposureMask;
+      a.event_mask = (ExposureMask | ButtonPressMask | ButtonReleaseMask
+                     | PointerMotionMask);
 
       xv->wdesc = XCreateWindow (xv->dpy, FRAME_X_WINDOW (s->f),
                                 x + clip_left, y + clip_top,
index f51921dbef02e31d8464bd075a6ffe8ea6a2adac..50d8271db19d2dfcd5e59e59a3cfe3f9ede98acc 100644 (file)
@@ -172,6 +172,10 @@ void xwidget_expose (struct xwidget_view *xv);
 
 extern struct xwidget *xwidget_from_id (uint32_t id);
 extern void kill_frame_xwidget_views (struct frame *f);
+extern void xwidget_button (struct xwidget_view *, bool, int,
+                           int, int, int, Time);
+extern void xwidget_motion_or_crossing (struct xwidget_view *,
+                                       const XEvent *);
 #else
 INLINE_HEADER_BEGIN
 INLINE void syms_of_xwidget (void) {}