Something went wrong. Try again.
A history browser for git, a replacement for gitk, drawn with Vulkan on Wayland.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112# SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us># SPDX-License-Identifier: MIT
# A pointer for a compositor with none — a headless sway — made through# zwlr_virtual_pointer_v1 and kept for a whole script, so that a click lands# on a surface the pointer has entered, which one made afresh for each click# never has. After harrier's tests/nixos/drag.py, which only drags.## pointer.py PROTOCOLS_DIR WIDTH HEIGHT STEP...## Each step is one of:## x,y move there# left, right click that button where the pointer is# down, up press or let go of the left button, around moves: a drag# double, triple click the left button two or three times, quickly# wheel:N turn the wheel N notches, down when positive# hwheel:N tilt it sideways N notches, right when positive# sleep:S wait S seconds, for a window to draw, or a test to look## PROTOCOLS_DIR holds pywayland's bindings for the virtual-pointer protocol,# as `protocols/`.
import sysimport time
sys.path.insert(0, sys.argv[1])from pywayland.client import Display # noqa: E402from protocols.wayland import WlSeat # noqa: E402from protocols.wlr_virtual_pointer_unstable_v1 import ZwlrVirtualPointerManagerV1 # noqa: E402
width, height = int(sys.argv[2]), int(sys.argv[3])steps = sys.argv[4:]BUTTONS = {"left": 0x110, "right": 0x111, "middle": 0x112}
display = Display()display.connect()registry = display.get_registry()found = {}
def on_global(reg, name, interface, version): if interface == "wl_seat": found["seat"] = reg.bind(name, WlSeat, 1) elif interface == "zwlr_virtual_pointer_manager_v1": found["manager"] = reg.bind(name, ZwlrVirtualPointerManagerV1, 1)
registry.dispatcher["global"] = on_globaldisplay.roundtrip()pointer = found["manager"].create_virtual_pointer(found["seat"])display.roundtrip()# Long enough for the windows to see a pointer appear and bind it.time.sleep(1.0)
t = 0
def tick(): global t t += 16 return t
for step in steps: if step in BUTTONS: pointer.button(tick(), BUTTONS[step], 1) pointer.frame() display.flush() time.sleep(0.1) pointer.button(tick(), BUTTONS[step], 0) pointer.frame() display.flush() time.sleep(0.3) elif step in ("double", "triple"): for _ in range(2 if step == "double" else 3): for state in (1, 0): pointer.button(tick(), BUTTONS["left"], state) pointer.frame() display.flush() time.sleep(0.05) time.sleep(0.3) elif step in ("down", "up"): pointer.button(tick(), BUTTONS["left"], 1 if step == "down" else 0) pointer.frame() display.flush() time.sleep(0.2) elif step.startswith("wheel:") or step.startswith("hwheel:"): axis = 1 if step.startswith("h") else 0 notches = int(step.split(":")[1]) # A wheel: each notch fifteen units, as libinput reports one. pointer.axis_source(0) pointer.axis_discrete(tick(), axis, 15 * notches, notches) pointer.frame() display.flush() time.sleep(0.3) elif step.startswith("sleep:"): time.sleep(float(step[len("sleep:"):])) else: x, y = (int(v) for v in step.split(",")) # Twice, so that the first enters the surface and the second moves # in it. for _ in range(2): pointer.motion_absolute(tick(), x, y, width, height) pointer.frame() display.flush() time.sleep(0.15)
pointer.destroy()display.roundtrip()display.disconnect()