summaryrefslogtreecommitdiff
path: root/src/xwidget.c
diff options
context:
space:
mode:
authorPo Lu <luangruo@yahoo.com>2022-01-27 21:36:52 +0800
committerPo Lu <luangruo@yahoo.com>2022-01-27 21:38:07 +0800
commit0991e8686cd90a7678346b7608c438fcb7e06bc6 (patch)
tree5c5ff468fc7d4acdc3e23a8cdd5d267bd4e1646c /src/xwidget.c
parent63255de48b498a8fab4cec5da244998f1ed23f8e (diff)
downloademacs-0991e8686cd90a7678346b7608c438fcb7e06bc6.tar.gz
Improve xwidget window ancestry calculations
* src/xwidget.c (xw_find_common_ancestor): (xw_notify_virtual_upwards_until) (xw_notify_virtual_downwards_until): New functions. (xw_maybe_synthesize_crossing): Synthesize virtual events like GTK does for non-linear changes.
Diffstat (limited to 'src/xwidget.c')
-rw-r--r--src/xwidget.c184
1 files changed, 177 insertions, 7 deletions
diff --git a/src/xwidget.c b/src/xwidget.c
index c42d1609d7a..175a289a007 100644
--- a/src/xwidget.c
+++ b/src/xwidget.c
@@ -1416,16 +1416,153 @@ window_coords_from_toplevel (GdkWindow *window, GdkWindow *toplevel,
*out_y = y_out;
}
+static GdkWindow *
+xw_find_common_ancestor (GdkWindow *window,
+ GdkWindow *other,
+ GdkWindow *toplevel)
+{
+ GdkWindow *tem;
+ GList *l1 = NULL;
+ GList *l2 = NULL;
+ GList *i1, *i2;
+
+ tem = window;
+ while (tem && tem != toplevel)
+ {
+ l1 = g_list_prepend (l1, tem);
+ tem = gdk_window_get_parent (tem);
+ }
+
+ tem = other;
+ while (tem && tem != toplevel)
+ {
+ l2 = g_list_prepend (l2, tem);
+ tem = gdk_window_get_parent (tem);
+ }
+
+ tem = NULL;
+ i1 = l1;
+ i2 = l2;
+
+ while (i1 && i2 && (i1->data == i2->data))
+ {
+ tem = i1->data;
+ i1 = i1->next;
+ i2 = i2->next;
+ }
+
+ g_list_free (l1);
+ g_list_free (l2);
+
+ return tem;
+}
+
+static void
+xw_notify_virtual_upwards_until (struct xwidget_view *xv,
+ GdkWindow *window,
+ GdkWindow *until,
+ GdkWindow *toplevel,
+ unsigned int state,
+ int x, int y, Time time,
+ GdkEventType type,
+ bool nonlinear_p)
+{
+ GdkEvent *xg_event;
+ GdkWindow *tem;
+ int cx, cy;
+
+ for (tem = gdk_window_get_parent (window);
+ tem && (tem != until);
+ tem = gdk_window_get_parent (tem))
+ {
+ xg_event = gdk_event_new (type);
+
+ gdk_event_set_device (xg_event, find_suitable_pointer (xv->frame));
+ window_coords_from_toplevel (tem, toplevel, x, y, &cx, &cy);
+ xg_event->crossing.x = cx;
+ xg_event->crossing.y = cy;
+ xg_event->crossing.time = time;
+ xg_event->crossing.focus = FALSE;
+ xg_event->crossing.detail = (nonlinear_p
+ ? GDK_NOTIFY_NONLINEAR_VIRTUAL
+ : GDK_NOTIFY_VIRTUAL);
+ xg_event->crossing.mode = GDK_CROSSING_NORMAL;
+ xg_event->crossing.window = g_object_ref (tem);
+
+ gtk_main_do_event (xg_event);
+ gdk_event_free (xg_event);
+ }
+}
+
+static void
+xw_notify_virtual_downwards_until (struct xwidget_view *xv,
+ GdkWindow *window,
+ GdkWindow *until,
+ GdkWindow *toplevel,
+ unsigned int state,
+ int x, int y, Time time,
+ GdkEventType type,
+ bool nonlinear_p)
+{
+ GdkEvent *xg_event;
+ GdkWindow *tem;
+ int cx, cy;
+ GList *path = NULL, *it;
+
+ tem = gdk_window_get_parent (window);
+
+ while (tem && tem != until)
+ {
+ path = g_list_prepend (path, tem);
+ tem = gdk_window_get_parent (tem);
+ }
+
+ for (it = path; it; it = it->next)
+ {
+ tem = it->data;
+ xg_event = gdk_event_new (type);
+
+ gdk_event_set_device (xg_event, find_suitable_pointer (xv->frame));
+ window_coords_from_toplevel (tem, toplevel, x, y, &cx, &cy);
+ xg_event->crossing.x = cx;
+ xg_event->crossing.y = cy;
+ xg_event->crossing.time = time;
+ xg_event->crossing.focus = FALSE;
+ xg_event->crossing.detail = (nonlinear_p
+ ? GDK_NOTIFY_NONLINEAR_VIRTUAL
+ : GDK_NOTIFY_VIRTUAL);
+ xg_event->crossing.mode = GDK_CROSSING_NORMAL;
+ xg_event->crossing.window = g_object_ref (tem);
+
+ gtk_main_do_event (xg_event);
+ gdk_event_free (xg_event);
+ }
+
+ g_list_free (path);
+}
+
static bool
xw_maybe_synthesize_crossing (struct xwidget_view *view,
GdkWindow *current_window,
int x, int y, int crossing,
Time time, unsigned int state)
{
- GdkWindow *last_crossing;
+ GdkWindow *last_crossing, *toplevel, *ancestor;
GdkEvent *xg_event;
- GdkWindow *toplevel;
int cx, cy;
+ bool nonlinear_p;
+
+ toplevel = gtk_widget_get_window (XXWIDGET (view->model)->widgetwindow_osr);
+
+ if (crossing == XW_CROSSING_LEFT
+ && (view->last_crossing_window
+ && !gdk_window_is_destroyed (view->last_crossing_window)))
+ {
+ xw_notify_virtual_upwards_until (view, view->last_crossing_window,
+ toplevel, toplevel,
+ state, x, y, time,
+ GDK_LEAVE_NOTIFY, false);
+ }
if (view->last_crossing_window
&& (gdk_window_is_destroyed (view->last_crossing_window)
@@ -1437,15 +1574,33 @@ xw_maybe_synthesize_crossing (struct xwidget_view *view,
if (!last_crossing)
{
view->last_crossing_window = g_object_ref (current_window);
+
+ xw_notify_virtual_downwards_until (view, current_window,
+ toplevel, toplevel,
+ state, x, y, time,
+ GDK_ENTER_NOTIFY,
+ false);
return false;
}
- toplevel = gtk_widget_get_window (XXWIDGET (view->model)->widgetwindow_osr);
-
if (last_crossing != current_window)
{
view->last_crossing_window = g_object_ref (current_window);
+ ancestor = xw_find_common_ancestor (last_crossing, current_window, toplevel);
+
+ if (!ancestor)
+ emacs_abort ();
+
+ nonlinear_p = (last_crossing != ancestor) && (current_window != ancestor);
+
+ if (nonlinear_p || (last_crossing != ancestor))
+ xw_notify_virtual_upwards_until (view, last_crossing,
+ ancestor, toplevel,
+ state, x, y, time,
+ GDK_LEAVE_NOTIFY,
+ nonlinear_p);
+
xg_event = gdk_event_new (GDK_LEAVE_NOTIFY);
gdk_event_set_device (xg_event, find_suitable_pointer (view->frame));
window_coords_from_toplevel (last_crossing, toplevel,
@@ -1455,14 +1610,25 @@ xw_maybe_synthesize_crossing (struct xwidget_view *view,
xg_event->crossing.time = time;
xg_event->crossing.focus = FALSE;
xg_event->crossing.state = state;
- /* TODO: actually calculate the event detail and mode. */
- xg_event->crossing.detail = GDK_NOTIFY_NONLINEAR;
+ /* TODO: actually calculate the event mode. */
+ xg_event->crossing.detail = (nonlinear_p
+ ? GDK_NOTIFY_NONLINEAR
+ : (last_crossing == ancestor
+ ? GDK_NOTIFY_INFERIOR
+ : GDK_NOTIFY_ANCESTOR));
xg_event->crossing.mode = GDK_CROSSING_NORMAL;
xg_event->crossing.window = g_object_ref (last_crossing);
gtk_main_do_event (xg_event);
gdk_event_free (xg_event);
+ if (nonlinear_p || (current_window != ancestor))
+ xw_notify_virtual_downwards_until (view, current_window,
+ ancestor, toplevel,
+ state, x, y, time,
+ GDK_ENTER_NOTIFY,
+ nonlinear_p);
+
xg_event = gdk_event_new (GDK_ENTER_NOTIFY);
gdk_event_set_device (xg_event, find_suitable_pointer (view->frame));
window_coords_from_toplevel (current_window, toplevel,
@@ -1472,7 +1638,11 @@ xw_maybe_synthesize_crossing (struct xwidget_view *view,
xg_event->crossing.time = time;
xg_event->crossing.focus = FALSE;
xg_event->crossing.state = state;
- xg_event->crossing.detail = GDK_NOTIFY_NONLINEAR;
+ xg_event->crossing.detail = (nonlinear_p
+ ? GDK_NOTIFY_NONLINEAR
+ : (current_window == ancestor
+ ? GDK_NOTIFY_INFERIOR
+ : GDK_NOTIFY_ANCESTOR));
xg_event->crossing.mode = GDK_CROSSING_NORMAL;
xg_event->crossing.window = g_object_ref (current_window);