diff options
author | Po Lu <luangruo@yahoo.com> | 2022-01-27 21:36:52 +0800 |
---|---|---|
committer | Po Lu <luangruo@yahoo.com> | 2022-01-27 21:38:07 +0800 |
commit | 0991e8686cd90a7678346b7608c438fcb7e06bc6 (patch) | |
tree | 5c5ff468fc7d4acdc3e23a8cdd5d267bd4e1646c /src/xwidget.c | |
parent | 63255de48b498a8fab4cec5da244998f1ed23f8e (diff) | |
download | emacs-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.c | 184 |
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); |