diff options
| author | Po Lu | 2022-01-27 21:36:52 +0800 |
|---|---|---|
| committer | Po Lu | 2022-01-27 21:38:07 +0800 |
| commit | 0991e8686cd90a7678346b7608c438fcb7e06bc6 (patch) | |
| tree | 5c5ff468fc7d4acdc3e23a8cdd5d267bd4e1646c /src | |
| parent | 63255de48b498a8fab4cec5da244998f1ed23f8e (diff) | |
| download | emacs-0991e8686cd90a7678346b7608c438fcb7e06bc6.tar.gz emacs-0991e8686cd90a7678346b7608c438fcb7e06bc6.zip | |
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')
| -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, | |||
| 1416 | *out_y = y_out; | 1416 | *out_y = y_out; |
| 1417 | } | 1417 | } |
| 1418 | 1418 | ||
| 1419 | static GdkWindow * | ||
| 1420 | xw_find_common_ancestor (GdkWindow *window, | ||
| 1421 | GdkWindow *other, | ||
| 1422 | GdkWindow *toplevel) | ||
| 1423 | { | ||
| 1424 | GdkWindow *tem; | ||
| 1425 | GList *l1 = NULL; | ||
| 1426 | GList *l2 = NULL; | ||
| 1427 | GList *i1, *i2; | ||
| 1428 | |||
| 1429 | tem = window; | ||
| 1430 | while (tem && tem != toplevel) | ||
| 1431 | { | ||
| 1432 | l1 = g_list_prepend (l1, tem); | ||
| 1433 | tem = gdk_window_get_parent (tem); | ||
| 1434 | } | ||
| 1435 | |||
| 1436 | tem = other; | ||
| 1437 | while (tem && tem != toplevel) | ||
| 1438 | { | ||
| 1439 | l2 = g_list_prepend (l2, tem); | ||
| 1440 | tem = gdk_window_get_parent (tem); | ||
| 1441 | } | ||
| 1442 | |||
| 1443 | tem = NULL; | ||
| 1444 | i1 = l1; | ||
| 1445 | i2 = l2; | ||
| 1446 | |||
| 1447 | while (i1 && i2 && (i1->data == i2->data)) | ||
| 1448 | { | ||
| 1449 | tem = i1->data; | ||
| 1450 | i1 = i1->next; | ||
| 1451 | i2 = i2->next; | ||
| 1452 | } | ||
| 1453 | |||
| 1454 | g_list_free (l1); | ||
| 1455 | g_list_free (l2); | ||
| 1456 | |||
| 1457 | return tem; | ||
| 1458 | } | ||
| 1459 | |||
| 1460 | static void | ||
| 1461 | xw_notify_virtual_upwards_until (struct xwidget_view *xv, | ||
| 1462 | GdkWindow *window, | ||
| 1463 | GdkWindow *until, | ||
| 1464 | GdkWindow *toplevel, | ||
| 1465 | unsigned int state, | ||
| 1466 | int x, int y, Time time, | ||
| 1467 | GdkEventType type, | ||
| 1468 | bool nonlinear_p) | ||
| 1469 | { | ||
| 1470 | GdkEvent *xg_event; | ||
| 1471 | GdkWindow *tem; | ||
| 1472 | int cx, cy; | ||
| 1473 | |||
| 1474 | for (tem = gdk_window_get_parent (window); | ||
| 1475 | tem && (tem != until); | ||
| 1476 | tem = gdk_window_get_parent (tem)) | ||
| 1477 | { | ||
| 1478 | xg_event = gdk_event_new (type); | ||
| 1479 | |||
| 1480 | gdk_event_set_device (xg_event, find_suitable_pointer (xv->frame)); | ||
| 1481 | window_coords_from_toplevel (tem, toplevel, x, y, &cx, &cy); | ||
| 1482 | xg_event->crossing.x = cx; | ||
| 1483 | xg_event->crossing.y = cy; | ||
| 1484 | xg_event->crossing.time = time; | ||
| 1485 | xg_event->crossing.focus = FALSE; | ||
| 1486 | xg_event->crossing.detail = (nonlinear_p | ||
| 1487 | ? GDK_NOTIFY_NONLINEAR_VIRTUAL | ||
| 1488 | : GDK_NOTIFY_VIRTUAL); | ||
| 1489 | xg_event->crossing.mode = GDK_CROSSING_NORMAL; | ||
| 1490 | xg_event->crossing.window = g_object_ref (tem); | ||
| 1491 | |||
| 1492 | gtk_main_do_event (xg_event); | ||
| 1493 | gdk_event_free (xg_event); | ||
| 1494 | } | ||
| 1495 | } | ||
| 1496 | |||
| 1497 | static void | ||
| 1498 | xw_notify_virtual_downwards_until (struct xwidget_view *xv, | ||
| 1499 | GdkWindow *window, | ||
| 1500 | GdkWindow *until, | ||
| 1501 | GdkWindow *toplevel, | ||
| 1502 | unsigned int state, | ||
| 1503 | int x, int y, Time time, | ||
| 1504 | GdkEventType type, | ||
| 1505 | bool nonlinear_p) | ||
| 1506 | { | ||
| 1507 | GdkEvent *xg_event; | ||
| 1508 | GdkWindow *tem; | ||
| 1509 | int cx, cy; | ||
| 1510 | GList *path = NULL, *it; | ||
| 1511 | |||
| 1512 | tem = gdk_window_get_parent (window); | ||
| 1513 | |||
| 1514 | while (tem && tem != until) | ||
| 1515 | { | ||
| 1516 | path = g_list_prepend (path, tem); | ||
| 1517 | tem = gdk_window_get_parent (tem); | ||
| 1518 | } | ||
| 1519 | |||
| 1520 | for (it = path; it; it = it->next) | ||
| 1521 | { | ||
| 1522 | tem = it->data; | ||
| 1523 | xg_event = gdk_event_new (type); | ||
| 1524 | |||
| 1525 | gdk_event_set_device (xg_event, find_suitable_pointer (xv->frame)); | ||
| 1526 | window_coords_from_toplevel (tem, toplevel, x, y, &cx, &cy); | ||
| 1527 | xg_event->crossing.x = cx; | ||
| 1528 | xg_event->crossing.y = cy; | ||
| 1529 | xg_event->crossing.time = time; | ||
| 1530 | xg_event->crossing.focus = FALSE; | ||
| 1531 | xg_event->crossing.detail = (nonlinear_p | ||
| 1532 | ? GDK_NOTIFY_NONLINEAR_VIRTUAL | ||
| 1533 | : GDK_NOTIFY_VIRTUAL); | ||
| 1534 | xg_event->crossing.mode = GDK_CROSSING_NORMAL; | ||
| 1535 | xg_event->crossing.window = g_object_ref (tem); | ||
| 1536 | |||
| 1537 | gtk_main_do_event (xg_event); | ||
| 1538 | gdk_event_free (xg_event); | ||
| 1539 | } | ||
| 1540 | |||
| 1541 | g_list_free (path); | ||
| 1542 | } | ||
| 1543 | |||
| 1419 | static bool | 1544 | static bool |
| 1420 | xw_maybe_synthesize_crossing (struct xwidget_view *view, | 1545 | xw_maybe_synthesize_crossing (struct xwidget_view *view, |
| 1421 | GdkWindow *current_window, | 1546 | GdkWindow *current_window, |
| 1422 | int x, int y, int crossing, | 1547 | int x, int y, int crossing, |
| 1423 | Time time, unsigned int state) | 1548 | Time time, unsigned int state) |
| 1424 | { | 1549 | { |
| 1425 | GdkWindow *last_crossing; | 1550 | GdkWindow *last_crossing, *toplevel, *ancestor; |
| 1426 | GdkEvent *xg_event; | 1551 | GdkEvent *xg_event; |
| 1427 | GdkWindow *toplevel; | ||
| 1428 | int cx, cy; | 1552 | int cx, cy; |
| 1553 | bool nonlinear_p; | ||
| 1554 | |||
| 1555 | toplevel = gtk_widget_get_window (XXWIDGET (view->model)->widgetwindow_osr); | ||
| 1556 | |||
| 1557 | if (crossing == XW_CROSSING_LEFT | ||
| 1558 | && (view->last_crossing_window | ||
| 1559 | && !gdk_window_is_destroyed (view->last_crossing_window))) | ||
| 1560 | { | ||
| 1561 | xw_notify_virtual_upwards_until (view, view->last_crossing_window, | ||
| 1562 | toplevel, toplevel, | ||
| 1563 | state, x, y, time, | ||
| 1564 | GDK_LEAVE_NOTIFY, false); | ||
| 1565 | } | ||
| 1429 | 1566 | ||
| 1430 | if (view->last_crossing_window | 1567 | if (view->last_crossing_window |
| 1431 | && (gdk_window_is_destroyed (view->last_crossing_window) | 1568 | && (gdk_window_is_destroyed (view->last_crossing_window) |
| @@ -1437,15 +1574,33 @@ xw_maybe_synthesize_crossing (struct xwidget_view *view, | |||
| 1437 | if (!last_crossing) | 1574 | if (!last_crossing) |
| 1438 | { | 1575 | { |
| 1439 | view->last_crossing_window = g_object_ref (current_window); | 1576 | view->last_crossing_window = g_object_ref (current_window); |
| 1577 | |||
| 1578 | xw_notify_virtual_downwards_until (view, current_window, | ||
| 1579 | toplevel, toplevel, | ||
| 1580 | state, x, y, time, | ||
| 1581 | GDK_ENTER_NOTIFY, | ||
| 1582 | false); | ||
| 1440 | return false; | 1583 | return false; |
| 1441 | } | 1584 | } |
| 1442 | 1585 | ||
| 1443 | toplevel = gtk_widget_get_window (XXWIDGET (view->model)->widgetwindow_osr); | ||
| 1444 | |||
| 1445 | if (last_crossing != current_window) | 1586 | if (last_crossing != current_window) |
| 1446 | { | 1587 | { |
| 1447 | view->last_crossing_window = g_object_ref (current_window); | 1588 | view->last_crossing_window = g_object_ref (current_window); |
| 1448 | 1589 | ||
| 1590 | ancestor = xw_find_common_ancestor (last_crossing, current_window, toplevel); | ||
| 1591 | |||
| 1592 | if (!ancestor) | ||
| 1593 | emacs_abort (); | ||
| 1594 | |||
| 1595 | nonlinear_p = (last_crossing != ancestor) && (current_window != ancestor); | ||
| 1596 | |||
| 1597 | if (nonlinear_p || (last_crossing != ancestor)) | ||
| 1598 | xw_notify_virtual_upwards_until (view, last_crossing, | ||
| 1599 | ancestor, toplevel, | ||
| 1600 | state, x, y, time, | ||
| 1601 | GDK_LEAVE_NOTIFY, | ||
| 1602 | nonlinear_p); | ||
| 1603 | |||
| 1449 | xg_event = gdk_event_new (GDK_LEAVE_NOTIFY); | 1604 | xg_event = gdk_event_new (GDK_LEAVE_NOTIFY); |
| 1450 | gdk_event_set_device (xg_event, find_suitable_pointer (view->frame)); | 1605 | gdk_event_set_device (xg_event, find_suitable_pointer (view->frame)); |
| 1451 | window_coords_from_toplevel (last_crossing, toplevel, | 1606 | window_coords_from_toplevel (last_crossing, toplevel, |
| @@ -1455,14 +1610,25 @@ xw_maybe_synthesize_crossing (struct xwidget_view *view, | |||
| 1455 | xg_event->crossing.time = time; | 1610 | xg_event->crossing.time = time; |
| 1456 | xg_event->crossing.focus = FALSE; | 1611 | xg_event->crossing.focus = FALSE; |
| 1457 | xg_event->crossing.state = state; | 1612 | xg_event->crossing.state = state; |
| 1458 | /* TODO: actually calculate the event detail and mode. */ | 1613 | /* TODO: actually calculate the event mode. */ |
| 1459 | xg_event->crossing.detail = GDK_NOTIFY_NONLINEAR; | 1614 | xg_event->crossing.detail = (nonlinear_p |
| 1615 | ? GDK_NOTIFY_NONLINEAR | ||
| 1616 | : (last_crossing == ancestor | ||
| 1617 | ? GDK_NOTIFY_INFERIOR | ||
| 1618 | : GDK_NOTIFY_ANCESTOR)); | ||
| 1460 | xg_event->crossing.mode = GDK_CROSSING_NORMAL; | 1619 | xg_event->crossing.mode = GDK_CROSSING_NORMAL; |
| 1461 | xg_event->crossing.window = g_object_ref (last_crossing); | 1620 | xg_event->crossing.window = g_object_ref (last_crossing); |
| 1462 | 1621 | ||
| 1463 | gtk_main_do_event (xg_event); | 1622 | gtk_main_do_event (xg_event); |
| 1464 | gdk_event_free (xg_event); | 1623 | gdk_event_free (xg_event); |
| 1465 | 1624 | ||
| 1625 | if (nonlinear_p || (current_window != ancestor)) | ||
| 1626 | xw_notify_virtual_downwards_until (view, current_window, | ||
| 1627 | ancestor, toplevel, | ||
| 1628 | state, x, y, time, | ||
| 1629 | GDK_ENTER_NOTIFY, | ||
| 1630 | nonlinear_p); | ||
| 1631 | |||
| 1466 | xg_event = gdk_event_new (GDK_ENTER_NOTIFY); | 1632 | xg_event = gdk_event_new (GDK_ENTER_NOTIFY); |
| 1467 | gdk_event_set_device (xg_event, find_suitable_pointer (view->frame)); | 1633 | gdk_event_set_device (xg_event, find_suitable_pointer (view->frame)); |
| 1468 | window_coords_from_toplevel (current_window, toplevel, | 1634 | window_coords_from_toplevel (current_window, toplevel, |
| @@ -1472,7 +1638,11 @@ xw_maybe_synthesize_crossing (struct xwidget_view *view, | |||
| 1472 | xg_event->crossing.time = time; | 1638 | xg_event->crossing.time = time; |
| 1473 | xg_event->crossing.focus = FALSE; | 1639 | xg_event->crossing.focus = FALSE; |
| 1474 | xg_event->crossing.state = state; | 1640 | xg_event->crossing.state = state; |
| 1475 | xg_event->crossing.detail = GDK_NOTIFY_NONLINEAR; | 1641 | xg_event->crossing.detail = (nonlinear_p |
| 1642 | ? GDK_NOTIFY_NONLINEAR | ||
| 1643 | : (current_window == ancestor | ||
| 1644 | ? GDK_NOTIFY_INFERIOR | ||
| 1645 | : GDK_NOTIFY_ANCESTOR)); | ||
| 1476 | xg_event->crossing.mode = GDK_CROSSING_NORMAL; | 1646 | xg_event->crossing.mode = GDK_CROSSING_NORMAL; |
| 1477 | xg_event->crossing.window = g_object_ref (current_window); | 1647 | xg_event->crossing.window = g_object_ref (current_window); |
| 1478 | 1648 | ||