Skip to content

Commit c747672

Browse files
author
danalec
committed
linux/input: fix abs->rel cursor feedback (output-local coords + seqlock)
- Publish the cursor position relative to the captured output (no desktop offset); the consumer compares against touch-port coordinates, which are output-relative, so adding the offset mis-anchored the estimate on multi-monitor setups. - Make the feedback read/write consistent with a seqlock so the input path never observes a half-updated snapshot (position from one frame, extents from another).
1 parent c37ba50 commit c747672

3 files changed

Lines changed: 46 additions & 18 deletions

File tree

src/input.cpp

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -829,23 +829,43 @@ namespace input {
829829
abs_mouse_sync_estimate(const std::shared_ptr<input_t> &input, bool client_idle, bool sat_x, bool sat_y, float port_w, float port_h) {
830830
const auto &fb = platf::kms_cursor_feedback();
831831

832-
const auto seq = fb.seq.load();
832+
// Seqlock read: retry while the writer is mid-update, and validate the
833+
// sequence is unchanged after snapshotting the fields.
834+
std::uint64_t seq;
835+
std::int32_t cursor_x;
836+
std::int32_t cursor_y;
837+
std::int32_t phys_w;
838+
std::int32_t phys_h;
839+
std::int32_t logical_w;
840+
std::int32_t logical_h;
841+
for (;;) {
842+
seq = fb.seq.load();
843+
if (seq & 1) {
844+
continue;
845+
}
846+
cursor_x = fb.x.load();
847+
cursor_y = fb.y.load();
848+
phys_w = fb.desktop_w.load();
849+
phys_h = fb.desktop_h.load();
850+
logical_w = fb.logical_w.load();
851+
logical_h = fb.logical_h.load();
852+
if (seq == fb.seq.load()) {
853+
break;
854+
}
855+
}
856+
833857
if (seq == 0 || seq == input->abs_mouse.seq_last) {
834858
return;
835859
}
836860
input->abs_mouse.seq_last = seq;
837861

838-
const auto phys_w = static_cast<float>(fb.desktop_w.load());
839-
const auto phys_h = static_cast<float>(fb.desktop_h.load());
840-
const auto logical_w = static_cast<float>(fb.logical_w.load());
841-
const auto logical_h = static_cast<float>(fb.logical_h.load());
842-
if (phys_w <= 0.0f || phys_h <= 0.0f || logical_w <= 0.0f || logical_h <= 0.0f) {
862+
if (phys_w <= 0 || phys_h <= 0 || logical_w <= 0 || logical_h <= 0) {
843863
return;
844864
}
845865

846-
// Desktop physical pixels -> compositor logical pixels.
847-
const auto real_x = static_cast<float>(fb.x.load()) * (logical_w / phys_w);
848-
const auto real_y = static_cast<float>(fb.y.load()) * (logical_h / phys_h);
866+
// Output-local physical pixels -> compositor logical pixels.
867+
const auto real_x = static_cast<float>(cursor_x) * (static_cast<float>(logical_w) / static_cast<float>(phys_w));
868+
const auto real_y = static_cast<float>(cursor_y) * (static_cast<float>(logical_h) / static_cast<float>(phys_h));
849869

850870
if (client_idle) {
851871
input->abs_mouse.host_x = std::clamp(real_x, 0.0f, port_w - 1.0f);

src/platform/common.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,17 +79,23 @@ namespace platf {
7979
// The KMS backend reads the cursor plane position once per captured frame and
8080
// publishes it here so the input path can close the loop of the abs->rel
8181
// mouse conversion (see config: absolute_mouse_as_relative). Coordinates are
82-
// in desktop physical pixels; the logical extents allow rescaling to the
83-
// compositor's logical space. seq starts at 0 and is bumped on every update,
84-
// so consumers can tell fresh values from stale ones.
82+
// OUTPUT-LOCAL physical pixels (relative to the captured output, with no
83+
// desktop offset); the logical extents allow rescaling to the compositor's
84+
// logical space. The consumer compares against touch-port coordinates
85+
// (0..port), so the position must stay relative to the output, not the
86+
// desktop.
87+
//
88+
// seq doubles as a seqlock: the writer bumps it to an odd value, publishes
89+
// the fields, then bumps it back to an even value. Readers must only consume
90+
// a snapshot taken while seq is even and stable across the read.
8591
struct kms_cursor_feedback_t {
8692
std::atomic_int32_t x { -1 };
8793
std::atomic_int32_t y { -1 };
8894
std::atomic_int32_t desktop_w { 0 }; ///< Physical width of the streamed output.
8995
std::atomic_int32_t desktop_h { 0 }; ///< Physical height of the streamed output.
9096
std::atomic_int32_t logical_w { 0 }; ///< Logical width of the streamed output.
9197
std::atomic_int32_t logical_h { 0 }; ///< Logical height of the streamed output.
92-
std::atomic_uint64_t seq { 0 };
98+
std::atomic_uint64_t seq { 0 }; ///< Seqlock: odd while writing, even when consistent.
9399
};
94100

95101
/**

src/platform/linux/kmsgrab.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,12 +1253,14 @@ namespace platf {
12531253

12541254
// Publish the real cursor position for the abs->rel input conversion
12551255
// (see config: absolute_mouse_as_relative). Cursor-plane CRTC
1256-
// coordinates are CRTC-local physical pixels; add the output's desktop
1257-
// offset and publish the output extents so the consumer can rescale
1258-
// to logical touch-port units.
1256+
// coordinates are output-local physical pixels (relative to the
1257+
// captured output, no desktop offset); publish them together with the
1258+
// output extents so the consumer can rescale to logical touch-port
1259+
// units. The fields are written under a seqlock (seq odd while writing).
12591260
auto &cursor_fb = platf::kms_cursor_feedback();
1260-
cursor_fb.x.store(offset_x + *prop_crtc_x);
1261-
cursor_fb.y.store(offset_y + *prop_crtc_y);
1261+
cursor_fb.seq.fetch_add(1);
1262+
cursor_fb.x.store(*prop_crtc_x);
1263+
cursor_fb.y.store(*prop_crtc_y);
12621264
cursor_fb.desktop_w.store(width);
12631265
cursor_fb.desktop_h.store(height);
12641266
cursor_fb.logical_w.store(logical_width);

0 commit comments

Comments
 (0)