emscripten-core / emscripten

Emscripten: An LLVM-to-WebAssembly Compiler
Other
25.89k stars 3.32k forks source link

Mouse scroll delta is not scaled properly #6283

Open ghost opened 6 years ago

ghost commented 6 years ago

I've done some digging and I think this is true for all scrolling in all libraries.

Using GLFW to listen to scroll events (glfwSetScrollCallback) gives me two doubles: Y and X delta. These are majorly differing between Firefox and Chrome (because as I have found, you should scale the delta according to if you get PIXELS or LINES).

In library_glfw.js, glfwInit function you listen to 'wheel' event (and others), handler is GLFW.onMouseWheel. There you call Browser.getMouseWheelDelta which for the 'wheel' event simply returns deltaY. Then this delta is just passed up to GLFW handlers.

What needs to be done is to properly handle the WheelEvent.deltaMode which can be DOM_DELTA_PIXEL or DOM_DELTA_LINE or even DOM_DELTA_PAGE.

When Firefox gives delta in LINES and Chrome gives delta in PIXELS there's a major inconsistency for apps run in those two browsers. Browser.getMouseWheelDelta is used by all libraries it seems to this issue should affect all scrolling.

ghost commented 6 years ago

I made this HTML to display and test the behavior:

<html>

<head>
<script>

// I found this somewhere
function getScrollLineHeight() {
    var r;
    var iframe = document.createElement('iframe');
    iframe.src = '#';
    document.body.appendChild(iframe);
    var iwin = iframe.contentWindow;
    var idoc = iwin.document;
    idoc.open();
    idoc.write('<!DOCTYPE html><html><head></head><body><span>a</span></body></html>');
    idoc.close();
    var span = idoc.body.firstElementChild;
    r = span.offsetHeight;
    document.body.removeChild(iframe);
    return r;
}

function handler(e) {
// pixels or lines?
if (e.deltaMode == 0) {
console.log(e.deltaY);
} else if (e.deltaMode == 1) {
console.log(e.deltaY * getScrollLineHeight());
}

}

function main() {
var canvas = document.getElementById('canvas');

canvas.addEventListener("wheel", handler, false);
}
</script>
</head>

<body onload="main();">

<canvas id="canvas" style="width: 600px; height 600px; background-color: red;"></canvas>

</body>

</html>
centurn commented 5 years ago

I've encountered the same problem while using SDL API: Y delta is different about ~30 times between Firefox and Chrome. The Firefox values are similar to what I get when running native SDL on Linux.

I've also came across this 4-year old issue. Looks like it was closed without normalizing the values between browsers.

feliwir commented 5 years ago

Having this issue aswell

flostellbrink commented 5 years ago

Looks like the specific issue is in line 568 of library_browser.js:

getMouseWheelDelta: function(event) {
var delta = 0;
  switch (event.type) {
    case 'DOMMouseScroll':
      delta = event.detail;
      break;
    case 'mousewheel':
      delta = event.wheelDelta;
      break;
    case 'wheel':
      delta = event['deltaY'];
      break;
    default:
      throw 'unrecognized mouse wheel event: ' + event.type;
  }
  return delta;
},

I just found this solution from facebook. They essentially convert all values into pixels.

I would suggest that we also return those estimated pixels by default. Specific libraries like library_glfw.js could then attempt to recreate the behaviour of their originals.

malytomas commented 5 years ago

Does the fix apply to SDL2 too? I have this same issue now with 1.39.3.

flostellbrink commented 5 years ago

@malytomas Sorry to disappoint. This was really just a legacy patch. SDL1 and GLFW2 should work, but neither GLFW3 nor SDL2 are affected by this. This also only supports vertical scrolling.

I think the html5 api would be the place to fix issues with SDL2. https://github.com/emscripten-core/emscripten/blob/1abc0c76764e33e1f14fe7a8e5f44eedec8ddb41/src/library_html5.js#L552-L617

feliwir commented 4 years ago

Are there any updates on a SDL2 fix? This is causing major issues for us

kripken commented 4 years ago

I'm not aware of someone working on it. cc @Daft-Freak who might remember more though.

If someone is interested to investigate and work on a patch for this, let me know if there is anything I can do to speed that along.

Daft-Freak commented 4 years ago

I think I remember this being mentioned at some point, but doesn't look like anything was implemented...

Looks like we need to check wheelEvent->deltaMode here: https://github.com/emscripten-ports/SDL2/blob/master/src/video/emscripten/SDL_emscriptenevents.c#L425. I don't think SDL2 defines what the units should be though.

feliwir commented 4 years ago

@Daft-Freak is there a library / implementation that handles that correctly?

ziocleto commented 4 years ago

Having the same issue, a super hacky fix, for now, it's to normalize everything [-1.0, 1.0]. Which it's pants. But at least it "works" for us somehow.

I do not understand were the root of the problem is, in the GLFW and SDL callbacks? Or in the general HTML5 wheel handling code? I won't mind having a go if you can confirm where exactly the issue arises.

feliwir commented 4 years ago

@ziocleto i'm not sure either why this can't be fixed. This is literally breaking our entire application on some browsers

ziocleto commented 4 years ago

@feliwir Me neither, IIRC I had a go to try to understand the issue but I really gave up as it seems nobody really knows all the details of this mess. And yes, even for us, it broke everything, well it still does, so annoying... If you have any updates, let us know!

feliwir commented 4 years ago

@ziocleto i investigated a bit further and found out that the deltaMode is different for Chrome & Firefox (thanks @Daft-Freak ). However i'm not certain what's the correct formula to fix this properly. This worked for me:

static EM_BOOL
Emscripten_HandleWheel(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData)
{
    SDL_WindowData *window_data = userData;
    // Default mode is in pixels
    float deltaX = wheelEvent->deltaX;
    float deltaY = wheelEvent->deltaY;
    // If delta mode is in lines
    if(wheelEvent->deltaMode == DOM_DELTA_LINE)
    {
        deltaX *= 20.0f;
        deltaY *= 20.0f;
    }
    else if(wheelEvent->deltaMode == DOM_DELTA_PAGE)
    {
        //TODO:
    }

    SDL_SendMouseWheel(window_data->window, wheelEvent->deltaMode, deltaX, -deltaY, SDL_MOUSEWHEEL_NORMAL);
    return SDL_GetEventState(SDL_MOUSEWHEEL) == SDL_ENABLE;
}

However this will probably break for Retinna etc. Can't we just pass the deltaMode to the callback aswell?

ghost commented 4 years ago

@ziocleto i investigated a bit further and found out that the deltaMode is different for Chrome & Firefox (thanks @Daft-Freak ).

I said exactly this in my very first report 2 years ago in the top of this very issue report if you care to read it...

feliwir commented 4 years ago

Indeed, thanks to @alexhultman aswell, but can we fix this now?

ghost commented 4 years ago

You need to know the height of a "row" and scale it accordingly. That's in the initial report. [On] Firefox [emscripten] treats the input given as "rows" as if they were pixels. That's why the scrolling is wildly incorrect. I guess you need to read the font metrics or the font-size or something like that.

feliwir commented 4 years ago

@alexhultman well, we display text ourselves (with WebGL), so it would be enough for us to get a notification by the SDL port that this is LINES or PIXELS, so we can handle the difference on our side.

Maybe we could add new wheel types?

Daft-Freak commented 4 years ago

I did look at this for SDL2, but the values seem to vary between platforms. (And possibly system settings?)

To get scroll events matching native on Windows, I needed /3 for LINE(Firefox) and /200 for PIXEL(Chrome). But on my Linux machine Chrome was 106...

ziocleto commented 4 years ago

BTW As I see that you guys are talking about SDL, I do not think the problem is related to SDL per se, we use GLFW for example, so fixing it should be agnostic from the graphics framework.

Daft-Freak commented 4 years ago

Hmm, SDL2 should probably be an issue in the SDL2 repo. (The fix would be in SDL as it's using the low-level HTML5 API).

You could even argue that it doesn't need fixed as the docs for SDL_WheelEvent don't specify a unit and native platforms seem to only ever return +/-1...

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because there has been no activity in the past year. It will be closed automatically if no further activity occurs in the next 30 days. Feel free to re-open at any time if this issue is still relevant.

thomasballinger commented 3 years ago

I've posted PR to the SDL2 port which fixes for me: https://github.com/emscripten-ports/SDL2/pull/154

kripken commented 3 years ago

Thanks @thomasballinger !

If people can test that PR and verify it works on their codebases, and on multiple browsers, that would help move this forward.

thomasballinger commented 3 years ago

One way to test is to make these changes to emsdk/upstream/emscripten/tools/ports/sdl2.py:

rm -rf emsdk/upstream/emscripten/cache/ports/sdl2.zip emsdk/upstream/emscripten/cache/ports/sdl2

(I've updated the hash 3x now, you can also set the hash to '')

8,9c8,9
< TAG = 'version_24'
< HASH = '5a8181acdcce29cdda7e7a4cc876602740f5b9deebd366ecec71ae15c4bbf1f352da4dd0e3c5e0ba8160709dda0270566d64a6cd3892da894463ecf8502836aa'
---
> TAG = 'scroll-speed'
> HASH = 'fc3a4aaffb57ec63ef2e2a6a0a562f99ce7b7e03e7b68762cc6105883f9766d214f50043cf0de2edd8ff1a0556a5d684719001b136d8a58101f43a9617ddcc9b'

23c23
<   ports.fetch_project('sdl2', 'https://github.com/emscripten-ports/SDL2/archive/' + TAG + '.zip', SUBDIR, sha512hash=HASH)
---
>   ports.fetch_project('sdl2', 'https://github.com/thomasballinger/SDL2/archive/refs/heads/scroll-speed.zip', SUBDIR, sha512hash=HASH)

I've tried this with https://play-endless-web.com/ (deployed version does not have the fix) in Firefox and Chrome and it feels much better to me.

trusktr commented 8 months ago

These are majorly differing between Firefox and Chrome (because as I have found, you should scale the delta according to if you get PIXELS or LINES).

Even in situations where we get PIXELS, the delta values still vary wildly across browser/OS combinations (even across the same browser in different OSes).

Related: