fix(host/linux): don't normalize absolute coords into gamescope's degenerate INT32_MAX EIS region
The previous commit's no-region fallback never fired: gamescope DOES advertise a region — (0,0,INT32_MAX,INT32_MAX), meaning "coordinates are raw". Mapping normalized positions into it explodes a center tap to x≈1e9, clamped by gamescope to the far corner — the cursor pinned at (1279,799) and every degraded-touch tap landing there. Treat a region as an output geometry only when plausibly sized (≤16384 px); otherwise emit raw client pixels. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -391,6 +391,15 @@ struct EiState {
|
|||||||
degraded_touch: Option<u32>,
|
degraded_touch: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is this EIS region a plausible OUTPUT geometry — something to map normalized coordinates
|
||||||
|
/// into? gamescope advertises a degenerate `(0,0,INT32_MAX,INT32_MAX)` "everything" region on
|
||||||
|
/// its virtual input device, meaning "absolute coordinates are raw"; normalizing into it
|
||||||
|
/// explodes a center tap to x≈1e9, which the compositor clamps to the far corner. 16384
|
||||||
|
/// comfortably covers real multi-monitor layouts while rejecting the sentinel.
|
||||||
|
fn sane_region(r: &reis::event::Region) -> bool {
|
||||||
|
r.width > 0 && r.height > 0 && r.width <= 16_384 && r.height <= 16_384
|
||||||
|
}
|
||||||
|
|
||||||
/// Stable small index per [`InputKind`] for the `seen_kinds` bitmask.
|
/// Stable small index per [`InputKind`] for the `seen_kinds` bitmask.
|
||||||
fn kind_bit(kind: InputKind) -> u32 {
|
fn kind_bit(kind: InputKind) -> u32 {
|
||||||
let i = match kind {
|
let i = match kind {
|
||||||
@@ -694,14 +703,17 @@ impl EiState {
|
|||||||
let h = (ev.flags & 0xffff) as f32;
|
let h = (ev.flags & 0xffff) as f32;
|
||||||
match slot.interface::<ei::PointerAbsolute>() {
|
match slot.interface::<ei::PointerAbsolute>() {
|
||||||
Some(p) if w > 0.0 && h > 0.0 => {
|
Some(p) 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
|
// but only when the region looks like a real output geometry.
|
||||||
// "Gamescope Virtual Input") — there the managed session runs at the
|
// gamescope's "Gamescope Virtual Input" advertises a degenerate
|
||||||
// client's mode, so client pixels ARE output pixels: emit them raw
|
// (0,0,INT32_MAX,INT32_MAX) region meaning "coordinates are raw":
|
||||||
// rather than silently dropping every absolute move.
|
// normalizing into it explodes a center tap to x≈1e9, which gamescope
|
||||||
|
// clamps to the far corner (the observed cursor-parked-at-1279,799).
|
||||||
|
// There the managed session runs at the client's mode, so client
|
||||||
|
// pixels ARE output pixels: emit them raw.
|
||||||
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, y) = match slot.regions().first() {
|
let (x, y) = match slot.regions().first().filter(|r| sane_region(r)) {
|
||||||
Some(region) => (
|
Some(region) => (
|
||||||
region.x as f32 + nx * region.width as f32,
|
region.x as f32 + nx * region.width as f32,
|
||||||
region.y as f32 + ny * region.height as f32,
|
region.y as f32 + ny * region.height as f32,
|
||||||
@@ -777,8 +789,8 @@ impl EiState {
|
|||||||
Some(t) 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);
|
||||||
// Same no-region fallback as MouseMoveAbs: raw client pixels.
|
// Same degenerate-region fallback as MouseMoveAbs: raw client pixels.
|
||||||
let (x, y) = match slot.regions().first() {
|
let (x, y) = match slot.regions().first().filter(|r| sane_region(r)) {
|
||||||
Some(region) => (
|
Some(region) => (
|
||||||
region.x as f32 + nx * region.width as f32,
|
region.x as f32 + nx * region.width as f32,
|
||||||
region.y as f32 + ny * region.height as f32,
|
region.y as f32 + ny * region.height as f32,
|
||||||
|
|||||||
Reference in New Issue
Block a user