Accumulate smooth scroll into full wheel clicks on server
fix(platform/ei): accumulate smooth scroll into full wheel clicks on server On Wayland, two-finger smooth scroll events are delivered as per-frame pixel deltas (e.g. 0.26, 0.53, 1.05 px per event). The previous code quantized these at the pixel level via `modf()` — any accumulated integer pixel (even just 1) triggered a wheel event whose value was `pixels * 12`, i.e. 12, 24 or 36 units. Because the deskflow protocol uses 120 units per standard wheel click, these fractional clicks were silently dropped by the client compositor and the user saw nothing on the secondary screen until a very large/fast stroke pushed enough pixels through. Switch to wheel-click-level accumulation: smooth scroll pixel deltas are now scaled to fractional wheel-clicks (0.1 per pixel, keeping the 10 px == 1 click convention) and only dispatched to the client when at least one full 120-unit click has accumulated. This uses `std::trunc()` (toward zero) instead of `std::floor()` to avoid spurious clicks on negative scroll values. Physical mouse wheels (discrete scroll events, EI_EVENT_SCROLL_DISCRETE) are unaffected and continue to be forwarded immediately as-is. - `EiScreen.cpp`: replace `modf()` pixel quantization with fractional wheel-click accumulation using `std::trunc()`. - `ScrollRemainder` struct comment: clarify the unit is now fractional wheel-clicks rather than pixels.
This commit is contained in:
parent
dd3944cabc
commit
22a4b135f0
1 changed files with 23 additions and 22 deletions
|
|
@ -28,7 +28,7 @@
|
|||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
// Values are in pixels
|
||||
// Values are in fractional wheel-click units (1.0 == one full 120-unit click)
|
||||
struct ScrollRemainder
|
||||
{
|
||||
double x;
|
||||
|
|
@ -762,14 +762,12 @@ void EiScreen::onButtonEvent(ei_event *event)
|
|||
|
||||
void EiScreen::onPointerScrollEvent(ei_event *event)
|
||||
{
|
||||
// Ratio of 10 pixels == one wheel click because that's what mutter/gtk
|
||||
// use (for historical reasons).
|
||||
static const int s_pixelsPerWheelClick = 10;
|
||||
// Our logical wheel clicks are multiples 120, so we
|
||||
// convert between the two and keep the remainders because
|
||||
// we will very likely get subpixel scroll events.
|
||||
// This means a single pixel is 120/s_pixelToWheelRation in wheel values.
|
||||
const int s_pixelToWheelRatio = s_scrollDelta / s_pixelsPerWheelClick;
|
||||
// Smooth scroll deltas are in pixels. We accumulate them as fractional
|
||||
// wheel-click units and only send full wheel clicks (120 units each)
|
||||
// to the client. Sub-120 fractional clicks are silently ignored by
|
||||
// compositors on the receiving end, so accumulating full clicks avoids
|
||||
// flooding the network with events that the client drops anyway.
|
||||
static const double s_wheelClicksPerPixel = 0.1; // 10 pixels == 1 full wheel click
|
||||
|
||||
assert(m_isPrimary);
|
||||
|
||||
|
|
@ -785,28 +783,31 @@ void EiScreen::onPointerScrollEvent(ei_event *event)
|
|||
ei_device_set_user_data(device, remainder);
|
||||
}
|
||||
|
||||
dx += remainder->x;
|
||||
dy += remainder->y;
|
||||
// Accumulate smooth scroll as fractional wheel clicks (1.0 == 120 units)
|
||||
double accX = remainder->x + dx * s_wheelClicksPerPixel;
|
||||
double accY = remainder->y + dy * s_wheelClicksPerPixel;
|
||||
|
||||
double x;
|
||||
double y;
|
||||
double rx = modf(dx, &x);
|
||||
double ry = modf(dy, &y);
|
||||
|
||||
assert(!std::isnan(x) && !std::isinf(x));
|
||||
assert(!std::isnan(y) && !std::isinf(y));
|
||||
// Only dispatch full wheel clicks. Use trunc (toward zero) not floor,
|
||||
// because floor(-0.3) == -1 which would fire a spurious click.
|
||||
double fullClicksX = std::trunc(accX);
|
||||
double fullClicksY = std::trunc(accY);
|
||||
|
||||
// libei and deskflow seem to use opposite directions, so we have
|
||||
// to send the opposite of the value reported by EI if we want to
|
||||
// remain compatible with other platforms (including X11).
|
||||
if (x != 0 || y != 0)
|
||||
if (fullClicksX != 0 || fullClicksY != 0) {
|
||||
sendEvent(
|
||||
EventTypes::PrimaryScreenWheel,
|
||||
WheelInfo::alloc((int32_t)-x * s_pixelToWheelRatio, (int32_t)-y * s_pixelToWheelRatio)
|
||||
WheelInfo::alloc(
|
||||
static_cast<int32_t>(-fullClicksX) * s_scrollDelta, static_cast<int32_t>(-fullClicksY) * s_scrollDelta
|
||||
)
|
||||
);
|
||||
accX -= fullClicksX;
|
||||
accY -= fullClicksY;
|
||||
}
|
||||
|
||||
remainder->x = rx;
|
||||
remainder->y = ry;
|
||||
remainder->x = accX;
|
||||
remainder->y = accY;
|
||||
}
|
||||
|
||||
void EiScreen::onPointerScrollDiscreteEvent(ei_event *event)
|
||||
|
|
|
|||
Loading…
Reference in a new issue