// Virtual-pointer injector mode for the F11 demo:
//
// f11_scroll inject <hold_ms> <steps> <a> <b> <interval_ms> [wheel]
//
// Plugs a `zwlr_virtual_pointer_v1` device in (which is what makes a headless
// wlroots seat gain the pointer capability — the F12 lesson) and keeps it
// alive for `hold_ms` after the steps complete. Two uses here:
//
// - steps=0: a pure capability HOLD. While the held device is up, the seat
// keeps its pointer capability, the demo's wl_pointer survives, and the
// transient `wlrctl pointer scroll` bursts land on a live resource.
// - mode `wheel`: per step send axis_source(wheel) + axis_discrete(axis=
// vertical, value=a×15, discrete=a) + frame — a notched-wheel click.
// wlroots converts the discrete count to wl_pointer.axis_value120
// (discrete × 120) for v8+ clients, which is the only way to exercise
// the v120/detent path on a headless host (wlrctl never sends discrete).
// The 15-units-per-detent value follows the libinput wheel convention.
//
// Without a mode argument, streams `steps` relative motions of (a, b) every
// `interval_ms` (unused by ./run.sh; kept aligned with the F10 injector).
module (module) injectinject;
import c; // ImportC shim — wsi_vpm_* / wsi_vptr_* are hand-marshalled (see ./c.c)
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.stdioD header file for C99 <stdio.h>
pubs.opengroup.org/onlinepubs/009695399/basedefs/stdio.h.html, stdio.h
Source
core/stdc/stdio.d
stdio : (alias) inject.fprintf = int core.stdc.stdio.fprintf(shared(core.stdc.stdio._IO_FILE)* stream, scope const(char*) format, scope const ...) nothrow @nogcfprintf, (alias) inject.printf = int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf, (alias shared global) inject.stderr = shared(core.stdc.stdio._IO_FILE*) core.stdc.stdio.stderrstderr;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.stringD header file for C99.
pubs.opengroup.org/onlinepubs/009695399/basedefs/string.h.html, string.h
Source
core/stdc/string.d
string : (alias) inject.strcmp = int core.stdc.string.strcmp(scope const(char*) s1, scope const(char*) s2) pure nothrow @nogcstrcmp;
__gshared wl_proxy* _error_ inject.gi_mgrgi_mgr;
__gshared wl_seat* _error_ inject.gi_seatgi_seat;
extern (C) void inject.injGlobalinjGlobal(void* data, wl_registry* (parameter) wl_registry* regreg, uint name,
const(char)* iface, uint ver) nothrow @nogc
{
if (strcmp(iface, "zwlr_virtual_pointer_manager_v1") == 0)
gi_mgr = cast(wl_proxy*) wsi_registry_bind(reg, name,
&wsi_virtual_pointer_manager_interface, 1);
else if (strcmp(iface, wl_seat_interface.name) == 0 && gi_seat is null)
gi_seat = cast(wl_seat*) wsi_registry_bind(reg, name, &wl_seat_interface, 1);
}
extern (C) void inject.injGlobalRemoveinjGlobalRemove(void* data, wl_registry* (parameter) wl_registry* regreg, uint name) nothrow @nogc {}
__gshared wl_registry_listener _error_ inject.gi_registryListenergi_registryListener = {&injGlobal, &injGlobalRemove};
int int inject.injectMain(int holdMs, int steps, int dx, int dy, int intervalMs, bool wheel)injectMain(int (parameter) int holdMsholdMs, int (parameter) int stepssteps, int (parameter) int dxdx, int (parameter) int dydy, int (parameter) int intervalMsintervalMs, bool (parameter) bool wheelwheel)
{
wl_display* (local variable) _error_ dpydpy = wl_display_connect(null);
if (dpy is null)
{
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf("SKIP: inject: no Wayland compositor\n");
return 0;
}
wl_registry* (local variable) _error_ regreg = wsi_display_get_registry(dpy);
wsi_registry_add_listener(reg, &gi_registryListener, null);
wl_display_roundtrip(dpy);
if (gi_mgr is null)
{
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf("SKIP: inject: no zwlr_virtual_pointer_manager_v1\n");
wl_display_disconnect(dpy);
return 0;
}
wl_proxy* (local variable) _error_ ptrptr = wsi_vpm_create_virtual_pointer(gi_mgr, gi_seat);
wl_display_roundtrip(dpy); // device plugged → seat gains the pointer capability
foreach ((local variable) int ii; 0 .. (parameter) int stepssteps)
{
if ((parameter) bool wheelwheel)
{
// One notched-wheel click of `dx` detents: 15 axis units per
// detent (the libinput convention); wlroots turns `discrete`
// into wl_pointer.axis_value120 = discrete × 120 for v8+ binds.
wsi_vptr_axis_source(ptr, 0 /* wheel */);
wsi_vptr_axis_discrete(ptr, 0, 0 /* vertical */, dx * 15 * 256, dx);
}
else
wsi_vptr_motion(ptr, 0, dx * 256, dy * 256);
wsi_vptr_frame(ptr);
wl_display_flush(dpy);
usleep(intervalMs * 1000);
}
usleep(holdMs * 1000);
wsi_vptr_destroy(ptr); // device unplugged → capability drop on the seat
wsi_vpm_destroy(gi_mgr);
wl_display_roundtrip(dpy);
wl_display_disconnect(dpy);
int core.stdc.stdio.fprintf(shared(core.stdc.stdio._IO_FILE)* stream, scope const(char*) format, scope const ...) nothrow @nogcfprintf((shared global) shared(core.stdc.stdio._IO_FILE*) core.stdc.stdio.stderrstderr, "inject: done steps=%d delta=%d,%d interval_ms=%d hold_ms=%d\n",
(parameter) int stepssteps, (parameter) int dxdx, (parameter) int dydy, (parameter) int intervalMsintervalMs, (parameter) int holdMsholdMs);
return 0;
}