Keep popup drag coordinates relative to the grabbed surface

94b337838d7f1ae8092397f10a9635643cd41baa
Alexis Sellier committed ago 1 parent f986577d
swm.c +30 -4
4437 4437
    dx = lx - cursor->x;
4438 4438
    dy = ly - cursor->y;
4439 4439
    motion_notify(pointer_event_time(event->time_msec), &event->pointer->base, dx, dy, dx, dy, 1);
4440 4440
}
4441 4441
4442 +
/* Find an enabled surface's origin in layout coordinates. */
4443 +
static bool scene_surface_coords(
4444 +
    struct wlr_scene_node *node, struct wlr_surface *surface, int *lx, int *ly
4445 +
) {
4446 +
    struct wlr_scene_node    *child;
4447 +
    struct wlr_scene_surface *scene_surface;
4448 +
4449 +
    if (!node->enabled)
4450 +
        return false;
4451 +
    if (node->type == WLR_SCENE_NODE_BUFFER) {
4452 +
        scene_surface = wlr_scene_surface_try_from_buffer(wlr_scene_buffer_from_node(node));
4453 +
        return scene_surface && scene_surface->surface == surface &&
4454 +
               wlr_scene_node_coords(node, lx, ly);
4455 +
    }
4456 +
    if (node->type == WLR_SCENE_NODE_TREE) {
4457 +
        wl_list_for_each(child, &wlr_scene_tree_from_node(node)->children, link) {
4458 +
            if (scene_surface_coords(child, surface, lx, ly))
4459 +
                return true;
4460 +
        }
4461 +
    }
4462 +
    return false;
4463 +
}
4464 +
4442 4465
/* Move the pointer or grabbed window and update the surface under it. */
4443 4466
void motion_notify(
4444 4467
    uint32_t                 time,
4445 4468
    struct wlr_input_device *device,
4446 4469
    double                   dx,
4449 4472
    double                   dy_unaccel,
4450 4473
    bool                     refocus
4451 4474
) {
4452 4475
    double                            sx = 0, sy = 0, sx_confined, sy_confined;
4453 4476
    client_t                         *c = nullptr, *w = nullptr;
4454 -
    layer_surface_t                  *l          = nullptr;
4455 4477
    struct wlr_surface               *surface    = nullptr;
4456 4478
    struct wlr_pointer_constraint_v1 *constraint = active_constraint;
4479 +
    int                               surface_x, surface_y;
4457 4480
4458 4481
    /* A zero timestamp marks an internal pointer-focus update. */
4459 4482
    if (time) {
4460 4483
        wlr_relative_pointer_manager_v1_send_relative_motion(
4461 4484
            relative_pointer_mgr, seat, (uint64_t)time * 1000, dx, dy, dx_unaccel, dy_unaccel
4494 4517
    /* Hit-test at the updated cursor position. */
4495 4518
    point_to_node(cursor->x, cursor->y, &surface, &c, nullptr, &sx, &sy);
4496 4519
4497 4520
    if (cursor_mode == CURSOR_PRESSED && !seat->drag &&
4498 4521
        surface != seat->pointer_state.focused_surface &&
4499 -
        toplevel_from_wlr_surface(seat->pointer_state.focused_surface, &w, &l) >= 0) {
4522 +
        toplevel_from_wlr_surface(seat->pointer_state.focused_surface, &w, nullptr) >= 0 &&
4523 +
        scene_surface_coords(
4524 +
            &scene->tree.node, seat->pointer_state.focused_surface, &surface_x, &surface_y
4525 +
        )) {
4500 4526
        c       = w;
4501 4527
        surface = seat->pointer_state.focused_surface;
4502 -
        sx      = cursor->x - (l ? l->scene->node.x : w->geom.x);
4503 -
        sy      = cursor->y - (l ? l->scene->node.y : w->geom.y);
4528 +
        sx      = cursor->x - surface_x;
4529 +
        sy      = cursor->y - surface_y;
4504 4530
    }
4505 4531
    /* Keep the drag icon under the pointer. */
4506 4532
    wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y));
4507 4533
4508 4534
    /* Move or resize the grabbed window instead of changing pointer focus. */
test/clients.py +76 -0
999 999
    virtual.destroy()
1000 1000
    keyboard.destroy()
1001 1001
    app.close()
1002 1002
1003 1003
1004 +
def popup_pointer(arguments: list[str]) -> None:
1005 +
    """Keep drag coordinates relative to the popup that received the press."""
1006 +
1007 +
    app = Connection(
1008 +
        WlCompositor, WlShm, XdgWmBase, WlSeat,
1009 +
        ZwlrVirtualPointerManagerV1, ZwpVirtualKeyboardManagerV1,
1010 +
    )
1011 +
    seat = app.get(WlSeat)
1012 +
    virtual = app.get(ZwlrVirtualPointerManagerV1).create_virtual_pointer(seat)
1013 +
    keyboard = app.get(ZwpVirtualKeyboardManagerV1).create_virtual_keyboard(seat)
1014 +
    app.roundtrips(2)
1015 +
    pointer = seat.get_pointer()
1016 +
    window = Window(app, "popup-pointer", 0xFF335577, fullscreen=True)
1017 +
    app.roundtrips(5)
1018 +
    positioner = app.get(XdgWmBase).create_positioner()
1019 +
    positioner.set_size(120, 60)
1020 +
    positioner.set_anchor_rect(30, 20, 10, 10)
1021 +
    positioner.set_anchor(XdgPositioner.anchor.bottom_right)
1022 +
    positioner.set_gravity(XdgPositioner.gravity.bottom_right)
1023 +
    surface = app.get(WlCompositor).create_surface()
1024 +
    xdg = app.get(XdgWmBase).get_xdg_surface(surface)
1025 +
1026 +
    def configure(proxy, serial) -> None:
1027 +
        """Attach the popup buffer after its configuration arrives."""
1028 +
1029 +
        proxy.ack_configure(serial)
1030 +
        surface.attach(create_buffer(app.get(WlShm), 120, 60, 0xFF557733), 0, 0)
1031 +
        surface.damage_buffer(0, 0, 120, 60)
1032 +
        surface.commit()
1033 +
1034 +
    xdg.dispatcher["configure"] = configure
1035 +
    popup = xdg.get_popup(window.xdg_surface, positioner)
1036 +
    surface.commit()
1037 +
    app.roundtrips(4)
1038 +
    positions = []
1039 +
    entered = []
1040 +
1041 +
    def enter(proxy, serial, target, x, y) -> None:
1042 +
        """Record the surface and local position that receive pointer focus."""
1043 +
1044 +
        entered.append(target)
1045 +
        positions.append((x, y))
1046 +
1047 +
    pointer.dispatcher["enter"] = enter
1048 +
    pointer.dispatcher["motion"] = lambda proxy, time, x, y: positions.append((x, y))
1049 +
    virtual.motion_absolute(1, 60, 60, window.width, window.height)
1050 +
    virtual.frame()
1051 +
    app.roundtrips(2)
1052 +
    if not entered or entered[-1] != surface or not positions:
1053 +
        raise Failure("pointer did not enter the popup")
1054 +
    start = positions[-1]
1055 +
    virtual.button(2, 0x110, 1)
1056 +
    virtual.motion(3, 200.0, 10.0)
1057 +
    virtual.frame()
1058 +
    app.roundtrips(2)
1059 +
    expected = (start[0] + 200, start[1] + 10)
1060 +
    if positions[-1] != expected:
1061 +
        raise Failure(f"popup drag coordinates {positions[-1]} differ from {expected}")
1062 +
    virtual.motion(4, -200.0, -10.0)
1063 +
    virtual.frame()
1064 +
    app.roundtrips(2)
1065 +
    if positions[-1] != start:
1066 +
        raise Failure("popup drag coordinates changed after returning to its starting point")
1067 +
    virtual.button(5, 0x110, 0)
1068 +
    virtual.frame()
1069 +
    popup.destroy()
1070 +
    xdg.destroy()
1071 +
    surface.destroy()
1072 +
    positioner.destroy()
1073 +
    window.destroy()
1074 +
    virtual.destroy()
1075 +
    keyboard.destroy()
1076 +
    app.close()
1077 +
1078 +
1004 1079
def shortcut_repeat() -> None:
1005 1080
    """Stop a held focus shortcut when its modifier is released."""
1006 1081
1007 1082
    connection = Connection(WlCompositor, WlShm, WlSeat, XdgWmBase, ZwpVirtualKeyboardManagerV1)
1008 1083
    seat = connection.get(WlSeat)
1416 1491
    "output-power": output_power,
1417 1492
    "output-management": output_management,
1418 1493
    "layer": layer,
1419 1494
    "session-lock": session_lock,
1420 1495
    "lock-pointer": lock_pointer,
1496 +
    "popup-pointer": popup_pointer,
1421 1497
    "text-input": text_input,
1422 1498
    "x11": x11,
1423 1499
}
1424 1500
1425 1501
test/run.py +1 -0
680 680
    def lifecycle() -> None:
681 681
        terminate(one)
682 682
        terminate(two)
683 683
        protocol("xdg-lifecycle", 2)
684 684
        protocol("transient", 2)
685 +
        protocol("popup-pointer")
685 686
        protocol("layer", 2)
686 687
        protocol("lock-pointer")
687 688
        protocol("session-lock", 2)
688 689
        protocol("text-input")
689 690
        protocol("x11")