fix(host/linux): emit absolute pointer/touch on region-less EIS devices + abs input-test probe
gamescope's "Gamescope Virtual Input" advertises pointer_abs but no region, so every MouseMoveAbs (and the degraded-touch moves built on it) was silently dropped (emitted=false) — clicks then landed at a stale cursor position. With no region, the managed session runs at the client's mode, so client pixels are output pixels: emit them raw. Also: log region count/dims at device-resume, and add PUNKTFUNK_INPUT_TEST_ABS=WxH to `input-test` (corners + center, 1s apart) so the degraded-touch path is verifiable with xdotool alone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -514,6 +514,8 @@ impl EiState {
|
|||||||
keyboard = dev.has_capability(DeviceCapability::Keyboard),
|
keyboard = dev.has_capability(DeviceCapability::Keyboard),
|
||||||
button = dev.has_capability(DeviceCapability::Button),
|
button = dev.has_capability(DeviceCapability::Button),
|
||||||
scroll = dev.has_capability(DeviceCapability::Scroll),
|
scroll = dev.has_capability(DeviceCapability::Scroll),
|
||||||
|
regions = dev.regions().len(),
|
||||||
|
region0 = ?dev.regions().first().map(|r| (r.x, r.y, r.width, r.height)),
|
||||||
"libei: device RESUMED (now emittable)"
|
"libei: device RESUMED (now emittable)"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -690,16 +692,22 @@ impl EiState {
|
|||||||
InputKind::MouseMoveAbs => {
|
InputKind::MouseMoveAbs => {
|
||||||
let w = ((ev.flags >> 16) & 0xffff) as f32;
|
let w = ((ev.flags >> 16) & 0xffff) as f32;
|
||||||
let h = (ev.flags & 0xffff) as f32;
|
let h = (ev.flags & 0xffff) as f32;
|
||||||
match (
|
match slot.interface::<ei::PointerAbsolute>() {
|
||||||
slot.interface::<ei::PointerAbsolute>(),
|
Some(p) if w > 0.0 && h > 0.0 => {
|
||||||
slot.regions().first(),
|
|
||||||
) {
|
|
||||||
(Some(p), Some(region)) if w > 0.0 && h > 0.0 => {
|
|
||||||
// Map the normalized client position into the device's first region.
|
// Map the normalized client position into the device's first region.
|
||||||
|
// A device may advertise pointer_abs with NO region (gamescope's
|
||||||
|
// "Gamescope Virtual Input") — there the managed session runs at the
|
||||||
|
// client's mode, so client pixels ARE output pixels: emit them raw
|
||||||
|
// rather than silently dropping every absolute move.
|
||||||
let nx = (ev.x as f32 / w).clamp(0.0, 1.0);
|
let nx = (ev.x as f32 / w).clamp(0.0, 1.0);
|
||||||
let ny = (ev.y as f32 / h).clamp(0.0, 1.0);
|
let ny = (ev.y as f32 / h).clamp(0.0, 1.0);
|
||||||
let x = region.x as f32 + nx * region.width as f32;
|
let (x, y) = match slot.regions().first() {
|
||||||
let y = region.y as f32 + ny * region.height as f32;
|
Some(region) => (
|
||||||
|
region.x as f32 + nx * region.width as f32,
|
||||||
|
region.y as f32 + ny * region.height as f32,
|
||||||
|
),
|
||||||
|
None => (ev.x as f32, ev.y as f32),
|
||||||
|
};
|
||||||
p.motion_absolute(x, y);
|
p.motion_absolute(x, y);
|
||||||
}
|
}
|
||||||
_ => emitted = false,
|
_ => emitted = false,
|
||||||
@@ -765,12 +773,18 @@ impl EiState {
|
|||||||
InputKind::TouchDown | InputKind::TouchMove => {
|
InputKind::TouchDown | InputKind::TouchMove => {
|
||||||
let w = ((ev.flags >> 16) & 0xffff) as f32;
|
let w = ((ev.flags >> 16) & 0xffff) as f32;
|
||||||
let h = (ev.flags & 0xffff) as f32;
|
let h = (ev.flags & 0xffff) as f32;
|
||||||
match (slot.interface::<ei::Touchscreen>(), slot.regions().first()) {
|
match slot.interface::<ei::Touchscreen>() {
|
||||||
(Some(t), Some(region)) if w > 0.0 && h > 0.0 => {
|
Some(t) if w > 0.0 && h > 0.0 => {
|
||||||
let nx = (ev.x as f32 / w).clamp(0.0, 1.0);
|
let nx = (ev.x as f32 / w).clamp(0.0, 1.0);
|
||||||
let ny = (ev.y as f32 / h).clamp(0.0, 1.0);
|
let ny = (ev.y as f32 / h).clamp(0.0, 1.0);
|
||||||
let x = region.x as f32 + nx * region.width as f32;
|
// Same no-region fallback as MouseMoveAbs: raw client pixels.
|
||||||
let y = region.y as f32 + ny * region.height as f32;
|
let (x, y) = match slot.regions().first() {
|
||||||
|
Some(region) => (
|
||||||
|
region.x as f32 + nx * region.width as f32,
|
||||||
|
region.y as f32 + ny * region.height as f32,
|
||||||
|
),
|
||||||
|
None => (ev.x as f32, ev.y as f32),
|
||||||
|
};
|
||||||
if ev.kind == InputKind::TouchDown {
|
if ev.kind == InputKind::TouchDown {
|
||||||
t.down(ev.code, x, y);
|
t.down(ev.code, x, y);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -30,6 +30,36 @@ pub fn input_test() -> Result<()> {
|
|||||||
y,
|
y,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
};
|
};
|
||||||
|
// `PUNKTFUNK_INPUT_TEST_ABS=WxH` (e.g. 1280x800): exercise ABSOLUTE pointer moves instead —
|
||||||
|
// steps through the corners + center of the given surface, 1s apart, so an observer
|
||||||
|
// (`DISPLAY=:0 xdotool getmouselocation`) can verify each jump. This is the degraded-touch
|
||||||
|
// path (touch → MouseMoveAbs), so it validates game-mode touch without a client.
|
||||||
|
if let Ok(dims) = std::env::var("PUNKTFUNK_INPUT_TEST_ABS") {
|
||||||
|
let (w, h) = dims
|
||||||
|
.split_once('x')
|
||||||
|
.and_then(|(w, h)| Some((w.parse::<u32>().ok()?, h.parse::<u32>().ok()?)))
|
||||||
|
.unwrap_or((1280, 800));
|
||||||
|
let flags = (w << 16) | (h & 0xffff);
|
||||||
|
let pts = [
|
||||||
|
(100, 100),
|
||||||
|
(w as i32 - 100, 100),
|
||||||
|
(w as i32 - 100, h as i32 - 100),
|
||||||
|
(100, h as i32 - 100),
|
||||||
|
(w as i32 / 2, h as i32 / 2),
|
||||||
|
];
|
||||||
|
tracing::info!(w, h, "input-test: ABS mode — corners + center, 1s apart");
|
||||||
|
for (x, y) in pts {
|
||||||
|
let mut e = ev(InputKind::MouseMoveAbs, 0, x, y);
|
||||||
|
e.flags = flags;
|
||||||
|
if let Err(err) = inj.inject(&e) {
|
||||||
|
tracing::warn!(error = %format!("{err:#}"), "input-test: abs inject failed");
|
||||||
|
}
|
||||||
|
tracing::info!(x, y, "input-test: abs move emitted");
|
||||||
|
std::thread::sleep(Duration::from_secs(1));
|
||||||
|
}
|
||||||
|
tracing::info!("input-test: done (abs)");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
"input-test: injecting a mouse square + 'A'/click taps for ~8s (watch wev / focused app)"
|
"input-test: injecting a mouse square + 'A'/click taps for ~8s (watch wev / focused app)"
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user