sdl2-compat icon indicating copy to clipboard operation
sdl2-compat copied to clipboard

SDL_MOUSEMOTION events report incorrect relative motion

Open cenomla opened this issue 10 months ago • 10 comments

I'm on Arch Linux using sdl2-compat 2.30.52-1, X11. Tested that this works correctly on sdl2 2.16.0.

When moving the cursor single pixels at a time, the xrel and yrel fields report 0 instead of 1. Other cases are incorrect as well, there seems to be some accumulation going on that isn't reset/rounded properly. If you move the cursor large amounts and then by single pixels, xrel/yrel will still report those large coordinates in future mouse motion events.

cenomla avatar Feb 20 '25 07:02 cenomla

I can take this one.

cgutman avatar Feb 20 '25 23:02 cgutman

@cenomla, this should be fixed with the latest code, can you confirm?

slouken avatar Feb 22 '25 01:02 slouken

@slouken the behavior is better but still not 1:1 with SDL2. Sometimes the mouse motion window coordinate change does not match the relative coordinate change. You can see in this image the coordinate (third line, right most values) increments before the relative values indicate the mouse has moved. Also SDL2 never reports a mouse motion event with a xrel/yrel value of 0

Image

cenomla avatar Feb 26 '25 01:02 cenomla

Yeah, there is going to be some tiny differences from SDL2 behavior due to the move to floating point values for all those in SDL3. For example, a scenario where the cursor moved from (429.2, 786) to (430, 786) creates a relative delta of (0.8, 0), so you'd see a change in the integer absolute X coordinate (429 -> 430) but not the relative X delta until another +0.2 delta accumulated.

Is this behavior actually causing problems for your app or are you just noting the difference from SDL2?

cgutman avatar Feb 26 '25 02:02 cgutman

Makes sense.

The change in behavior causes a minor bug in my app, which is why I noticed it. I'll be upgrading to SDL3 at some point so it's not a huge issue for me personally as I can work around it for now.

It's probably a good idea to document this change if the behavior remains different, as it's quite subtle and any resulting bugs would be annoying to track down.

cenomla avatar Feb 27 '25 02:02 cenomla

Are you able to share the source code that wasn't behaving well with the current sdl2-compat behavior?

sdl2-compat tries to be close enough that most SDL2 apps shouldn't need to be aware of it. If there's some change we can make to cover the case your code is hitting, it would probably help other apps too.

cgutman avatar Feb 27 '25 07:02 cgutman

I can't share the full source code but I'll give some pseudo code since the issue is fairly isolated

float gridSize;
float posX, posY;
float cursorStartX, cursorStartY;
float cursorDeltaX, cursorDeltaY;
float cursorX, cursorY;

void do_frame() {
	cursorDeltaX = 0;
	cursorDeltaY = 0;

	SDL_Event e;
	while (SDL_PollEvent(&e)) {
		switch (e.type) {
		case SDL_MOUSEMOTION:
			cursorX = static_cast<float>(e.motion.x);
			cursorY = static_cast<float>(e.motion.y);
			cursorDeltaX += static_cast<float>(e.motion.xrel);
			cursorDeltaY += static_cast<float>(e.motion.yrel);
			break;
		}
	} 

	if (/*was mouse button pressed*/) {
		cursorStartX = cursorX;
		cursorStartY = cursorY;
	}

	if (/*is mouse button down*/) {
		posX += cursorDeltaX;
		posY += cursorDeltaY;
	}

	if (/*was mouse button released*/) {
		float dx = cursorX - cursorStartX;
		float dy = cursorY - cursorStartY;

		posX += round(dx/gridSize)*gridSize - dx;
		posY += round(dy/gridSize)*gridSize - dy;
	}

}

The issue is with the delta vector (dx, dy) not being the same vector as the offset vector for the position from when the mouse button was pressed to when it was released. If the sum of the cursorDelta vector over all of the frames the mouse button was held does not equal the delta vector then the relative grid snapping will be incorrect. In my app this periodically causes objects to snap very slightly off of the grid.

cenomla avatar Mar 01 '25 03:03 cenomla

Related issue, should be already fixed by the linked PR: coarse scrolling events from SDL_EVENT_MOUSE_WHEEL never reporting non-zero when scrolling slowly on a touchpad.

v1993 avatar Mar 02 '25 03:03 v1993

After looking into this more to try to solve the issue in https://github.com/libsdl-org/sdl2-compat/issues/372#issuecomment-2691888221, the current approach in #378 isn't correct for applications that register event filters because we're calling Event3To2() more than once per event and event conversion is no longer idempotent due to those accumulator variables we have to maintain.

@slouken what are your thoughts creating a hidden hint in SDL3 that will provide integer mouse values for sdl2-compat, so we can make Event3To2() stateless again?

The only alternative I can think of would be moving the accumulators into separate event state contexts for each codepath where we call Event3To2() (at least one for the filter path and one for the peep/wait/poll path) to ensure the values remain consistent, or radically changing the sdl2-compat event processing model to push converted SDL2 events into the SDL3 queue from the event filter instead of converting them multiple times for event filters and peep/wait/poll.

cgutman avatar Mar 09 '25 00:03 cgutman

@slouken what are your thoughts creating a hidden hint in SDL3 that will provide integer mouse values for sdl2-compat, so we can make Event3To2() stateless again?

The hint in SDL3 would be fine, we'd have to re-add the accumulator logic there.

slouken avatar Mar 09 '25 01:03 slouken