srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/tools/virtual-pointer-pin-test/src/main.rs
blob: 9e46939c1c2849bc9ece8f060ec164b071bd424b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// Minimal zwlr_virtual_pointer_unstable_v1 client: prints its own pid (so
// an external script can `srd dispatch pin input <pid> <window-id>` while
// it waits), then on a line from stdin sends motion_absolute/button/frame
// requests to drag from one point to another - built to live-verify
// srdwm's Phase 2 pinned-virtual-pointer delivery (see
// crates/wayland/src/virtual_pointer.rs's own module doc comment) against
// a real client, the same "purpose-built protocol test client, not a
// guess from source reading" precedent ../toplevel-activate already set
// for zwlr_foreign_toplevel_handle_v1.
//
// Usage:
//   vptest <x1> <y1> <x2> <y2>
//     Prints "pid <n>", then blocks on one line of stdin ("go\n").
//     Sends motion_absolute(x1,y1) [0..10000 extent], button press,
//     motion_absolute(x2,y2), button release, frame, flush, exits.

use std::io::BufRead;

use wayland_client::protocol::{wl_registry, wl_seat::WlSeat};
use wayland_client::{Connection, Dispatch, EventQueue, QueueHandle};
use wayland_protocols_wlr::virtual_pointer::v1::client::zwlr_virtual_pointer_manager_v1::ZwlrVirtualPointerManagerV1;
use wayland_protocols_wlr::virtual_pointer::v1::client::zwlr_virtual_pointer_v1::{self, ZwlrVirtualPointerV1};

const EXTENT: u32 = 10000;
const BTN_LEFT: u32 = 0x110;

struct State {
    seat: Option<WlSeat>,
    manager: Option<ZwlrVirtualPointerManagerV1>,
}

impl Dispatch<wl_registry::WlRegistry, ()> for State {
    fn event(state: &mut Self, registry: &wl_registry::WlRegistry, event: wl_registry::Event, _: &(), _: &Connection, qh: &QueueHandle<Self>) {
        if let wl_registry::Event::Global { name, interface, version } = event {
            if interface == "wl_seat" {
                state.seat = Some(registry.bind::<WlSeat, _, _>(name, version.min(9), qh, ()));
            } else if interface == "zwlr_virtual_pointer_manager_v1" {
                state.manager = Some(registry.bind::<ZwlrVirtualPointerManagerV1, _, _>(name, version.min(2), qh, ()));
            }
        }
    }
}

impl Dispatch<WlSeat, ()> for State {
    fn event(_: &mut Self, _: &WlSeat, _: wayland_client::protocol::wl_seat::Event, _: &(), _: &Connection, _: &QueueHandle<Self>) {}
}

impl Dispatch<ZwlrVirtualPointerManagerV1, ()> for State {
    fn event(_: &mut Self, _: &ZwlrVirtualPointerManagerV1, _: wayland_protocols_wlr::virtual_pointer::v1::client::zwlr_virtual_pointer_manager_v1::Event, _: &(), _: &Connection, _: &QueueHandle<Self>) {
    }
}

impl Dispatch<ZwlrVirtualPointerV1, ()> for State {
    fn event(_: &mut Self, _: &ZwlrVirtualPointerV1, _: zwlr_virtual_pointer_v1::Event, _: &(), _: &Connection, _: &QueueHandle<Self>) {}
}

fn main() {
    let args: Vec<String> = std::env::args().skip(1).collect();
    if args.len() != 4 {
        eprintln!("usage: vptest <x1> <y1> <x2> <y2>  (fractions of EXTENT={EXTENT}, e.g. 5000 5000 is dead center)");
        std::process::exit(1);
    }
    let x1: u32 = args[0].parse().expect("x1 must be a number");
    let y1: u32 = args[1].parse().expect("y1 must be a number");
    let x2: u32 = args[2].parse().expect("x2 must be a number");
    let y2: u32 = args[3].parse().expect("y2 must be a number");

    let conn = Connection::connect_to_env().expect("failed to connect to the Wayland compositor - is WAYLAND_DISPLAY set?");
    let display = conn.display();
    let mut event_queue: EventQueue<State> = conn.new_event_queue();
    let qh = event_queue.handle();
    let _registry = display.get_registry(&qh, ());

    let mut state = State { seat: None, manager: None };
    event_queue.roundtrip(&mut state).expect("initial roundtrip failed");

    let Some(manager) = state.manager.clone() else {
        eprintln!("compositor does not advertise zwlr_virtual_pointer_manager_v1");
        std::process::exit(1);
    };
    let Some(seat) = state.seat.clone() else {
        eprintln!("compositor does not advertise wl_seat");
        std::process::exit(1);
    };

    let pointer = manager.create_virtual_pointer(Some(&seat), &qh, ());
    event_queue.roundtrip(&mut state).expect("create_virtual_pointer roundtrip failed");

    println!("pid {}", std::process::id());
    println!("waiting for a line on stdin before sending any motion...");
    let mut line = String::new();
    std::io::stdin().lock().read_line(&mut line).expect("failed to read stdin");

    let time = 0;
    pointer.motion_absolute(time, x1, y1, EXTENT, EXTENT);
    pointer.frame();
    event_queue.roundtrip(&mut state).expect("motion_absolute(1) roundtrip failed");

    pointer.button(time, BTN_LEFT, wayland_client::protocol::wl_pointer::ButtonState::Pressed);
    pointer.frame();
    event_queue.roundtrip(&mut state).expect("button press roundtrip failed");

    pointer.motion_absolute(time, x2, y2, EXTENT, EXTENT);
    pointer.frame();
    event_queue.roundtrip(&mut state).expect("motion_absolute(2) roundtrip failed");

    pointer.button(time, BTN_LEFT, wayland_client::protocol::wl_pointer::ButtonState::Released);
    pointer.frame();
    event_queue.roundtrip(&mut state).expect("button release roundtrip failed");

    println!("done.");
}