Keep popup drag coordinates relative to the grabbed surface
94b337838d7f1ae8092397f10a9635643cd41baa
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") |