-
-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Emscripten mouse wheeling (was: GLFW Emscripten fixes) #6096
Conversation
…sonable In my browser, I noticed that zooming seemed jumpy and unreliable. Turns out that my fingers were moving horizontally on my trackpad, and the x-scroll was causing the unexpected behavior.
…#6096) This is used to populate the monitor array, which is only a requirement for multi-viewport support (which is not supported by Emscripten anyway).
Thank you for the PR! I have reworded and pushed the first change as 6342275, feels simpler this way. About the mis-caled horizontal event, would you be able to do some extra research on this? The "flipped" component is a bit confusing and #4019 is still open. |
If you go to https://tracy.nereid.pl/ and try to scroll horizontally using a laptop touchpad, you will see that the way input behaves makes this uncontrollable. |
Bartosz: I don't have access to one right now, it is the same issue (scrolling being way too fast)? If so, does the scaling suggested by the PR seems reasonable to your case. Worth mentioning if this is on Mac or not? Mac drivers can emit simultaneous 2D events, which used to be problematic #3795 #4559 but 1.89.2 made changes to this, which may include side-effects. Could be unrelated and you may be referring 100% to the scaling issue. As for the X direction: the crux of #4019 is we need to clarify/standardize who exactly is responsible for direction flipping. As you know Macs application eends to scroll the "opposite" way of Windows. Some low-level libraries (SDL/GLFW) tends to apply this flipping on Mac, our backends also tends to do it, and our scrolling functions also do it, and it's currently a bit inconsistent. This is the essence of #4019 to untangle this. What should we report to end-user wanting to use horizontal wheeling to alter values that are not scrolling? Probably something consistent across platforms Add to the mix that I don't think an Emscripten target is considered an EDITED |
This is upstreaming of a PR that OP originally submitted to Tracy: wolfpld/tracy#510
It seems to be OK on Windows, sorta-kinda OK-ish on Mac and unusable on Linux (with Wayland).
I do not think tying this with MacOS is the right solution. I use "natural" scroll direction on Linux, and #5167 suggests it's also an option on Windows. FWIW, I had to reverse the scroll direction in my Wayland backed, for the scroll to be consistent with what should happen both when the "natural" scroll is enabled (view contents are moved in the same direction as finger movement, as if you were moving a paper sheet on a table) and when it is disabled (when the viewport itself, or an imaginary camera looking down at the contents, is following finger movement). More generally, I have been thinking in context of Wayland how ImGui represents the possible inputs space, and it seems to be lacking many things. For example, mouse click events and touch inputs should be handled separately, but currently both are mapped to mouse click events. Or how scroll events are emulating the digital way of mouse wheel event reporting, while with touchpad hardware you also get "scroll start" and "scroll end" events. Handling these last two events would, for example, enable things like fast-swipe-and-release, to do kinetic scrolling, as seen in mobile phone UIs. |
It would make sense at least to create a separate flag for "natural" scroll.
Why? (Serious question). My assumption is that for touch events (#3453, #2650 etc) we'll probably tag existing mouse events with additional metadata, and let them be handled the same way in a majority of code paths. But it's good to know specifically how we can better cater to touch inputs. My new Windows laptop has a touch-screen so I should be able to investigate this better. I'm not sure I fully understand the purpose/need of scroll-start/scroll-end and what can be done. This probably ought to have its own issue but realistically we still need to make progress on better touch support. |
Most basically, the touch and mouse events are not exclusive wrt each other. What should happen, when you have a touch event active, mapped to left mouse button, and at the same time you press the left mouse button on your mouse? More specifically, with touch events the button ID has no special meaning. It's just an ID that you use to track the event. So, you can have touches 0 and 1, both scrolling contents in independent windows, and then touch 2 clicks on some button, which should execute the assigned LMB action. Consider that all these touches are active at the same time, and common hardware typically supports tracking of 10 touches. If this would be with a mouse, the button ID 2 would indicate the right mouse button, which may have completely different meaning, such as opening a context menu.
Please see the following video for visual explanation of the kinetic scrolling: https://www.youtube.com/watch?v=b68mskFTHX0 In Tracy, I map mouse wheel events to zoom in / zoom out. Since wheel events are impulses, I can set the target view width to e.g. 90% of the current view and start a short-running lerp, for extra smoothness. Since wheel impulses arrive infrequently, due to the hardware design of scroll wheel encoder, this works fine. However, if wheel events are coming from an analog touchpad, the stream of events may be continuous, in which case the discrete processing described above does not work as expected, either under- or over-shooting the actual finger movement. In this case a more natural solution would be to switch to a 1:1 mapping of finger movement to zoom, triggered when the scroll event starts, and then maybe add kinetic scroll when the event ends. (And of course, you can have touchpad scrolling active and mouse wheel impulses arriving at the same time.)
Yes, this is out-of-scope for this PR. |
Multi-touch is a whole different beast that simply "better support for basic touch". But simultaneous and separate touch interactions (e.g. scrolling two windows at the same time) I cannot imagine happening with the current codebase and scope of Dear ImGui, unfortunately. It's way far off what would be reasonable with the codebase. Some of the most-important multi-touch handling (transforming drags of three fingers into a scrolling event) would already be handled by the OS or backend and transparent to Dear ImGui. We can even possibly make backends report more multi-touch-specific events (e.g. zooming gesture). I'm just saying: (A) touch should be handled by default by widget codes and this should report as mouse-click + metadata for optional custom processing (B) separate touch interactions are unlikely to be supported.
Thanks for the reference.
Likewise if the mouse event is tagged you can have specific code to select a behavior.
But for that: 🙈🙉 |
I don't have all that much experience with Emscripten's GLFW implementation unfortunately. Emscripten basically has its own GLFW emulation, so it's not guaranteed to behave like native GLFW and probably also doesn't keep track of bug fixes and new features in GLFW. The Emscripten emulation layer is here: https://github.com/emscripten-core/emscripten/blob/main/src/library_glfw.js ...and specifically the mouse wheel event handler (which interestingly flips the scroll axis): https://github.com/emscripten-core/emscripten/blob/373b282c8fb785927032f90d4a105badb6ebb035/src/library_glfw.js#L518-L543 ...the rest of the code (Browser.getMouseWheelDelta) is here, this takes care of the different 'scroll deltas' in browser wheel events: For comparison this is my Emscripten mouse wheel handler in sokol_app.h: I'm also flipping the direction there, but this then passed as is to Dear ImGui, I don't understand why a 'double flip' is necessary with Emscripten's GLFW emulation. I'm afraid that's all I can help with :) For a "proper application" I wouldn't use Emscripten's GLFW emulation, also since these emulation layers in Emscripten are only minimally maintained anymore (AFAIK!). I probably would still accept the PR since the changes are not all that "intrusive" :) |
I fully understand your reluctance to make changes here, especially considering the target audience of ImGui. However, in my experience, going against the available APIs by inventing common abstractions creates more problems than it solves.
Right now, I see the following problems when using touch on emscripten (with GLFW):
It may be easy to assume that touch input is a mobile thing, and therefore not something to worry about too much. Even if you have it available on a laptop, it's more or less a gimmick, since you also have the touchpad and the ability to connect a mouse, so why bother? It's easy to think that way. Then you realize that your laptop is a 2-in-1 and can be physically reconfigured to act as a tablet, and now all the input you have is just touch.
It is not something that I would want to support, but the design of the API does not preclude it from happening. |
@ocornut Thanks for the revised commit and review so far I'm happy to do more research if needed. Can you let me know what still needs looking into, based on the latest discussion? |
I don't disagree we should better support touch and those specific issues can be brought one by one in issues and improving.
I need to test/confirm values on Windows and OSX on my side for the scale and perhaps flip. |
FYI no topic is too small. I am afraid even this mouse-wheeling issue may be a relatively large mess to untangle. I ran dozens tests for raw value we are getting across backends and browsers. I added them to the existing thread about this (which already had a couple of infos) I have also added a preliminary Emscripten+GLFW example but not committed it as currently it doesn't resize dislay properly, however for the purpose of testing mouse events I tested that along with Emscripten+SDL and found the wheel values were mostly identical. About Wheel Magnitude:
About Horizontal Wheel Sign:
|
Sorry, but this is completely wrong. Clamping this to unit values would completely destroy interaction through touchpad.
The emscripten html5.h interface is what all wrapper libraries have to rely on internally, for example here's how SDL does things: (You gotta love that dy is negated there, but dx isn't.) The API is explained in the emscripten's html5.h interface documentation: https://emscripten.org/docs/api_reference/html5.h.html#wheel The API is very simple to understand. You have the mouse wheel callback function, which delivers events:
The interesting data is passed in EmscriptenWheelEvent struct:
Now, https://w3c.github.io/uievents/#events-wheelevents DOM_DELTA_PIXEL - The units of measurement for the delta MUST be pixels. This is the most typical case in most operating system and implementation configurations. What does this mean exactly is defined in another castle. I'm not even sure if a pixel is a pixel, as Hi-DPI displays and UI scaling is a thing, and you have multiple possible coordinate spaces to consider. The simplest way to inspect the raw data provided by the browser is to run the following program: #include <emscripten.h>
#include <emscripten/html5.h>
#include <stdio.h>
int main()
{
emscripten_set_wheel_callback( EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, false, []( int, const EmscriptenWheelEvent* ev, void* ) {
printf( "mode: %lu dx: %g, dy: %g, dz: %g\n", ev->deltaMode, ev->deltaX, ev->deltaY, ev->deltaZ );
return EM_TRUE;
} );
emscripten_set_main_loop( []{}, 0, false );
} You can compile it using the emscripten SDK with For your convenience I have placed this test program at https://nereid.pl/wheel/ |
I understand it is wrong but it would currently be less wrong than interpreting GLFW/SDL mouse wheel inputs coming via Emscripten that have widely different magnitude. That /20 scaling factor seems like it means something different depending on each browser. Thanks for the links and details. I’ll check the testbed tomorrow. First I’m going to try fixing the sign inconsistencies. Obtaining usable Magnitudes will need more investigation on Emscripten side |
I have pushed 3617a96, see details and excel sheet in #4019 (comment) I went through the data many times on many configuration and couldn't quite understand why you didn't flip nor scale the vertical wheel data. According to all my data it should be done just as well. I wondered if it was because Tracy mostly uses Horizontal scrollbars and not much Vertical ones. About Magnitude The magnitude of wheel inputs under Emscripten (with SDL or GLFW backends) is still a problem which I have merely used a dumb scale factor for. We should continue this specific discussion here. Based on the data I have obtained ON MY DEAR IMGUI TESTBEDS I have scaled the magnitude down by 120.0f and not 20.0f (as 20.0f was still very unusable). However it is possible based on details posted above that this depends on the container HTML/JS. Someone who need to dig deeper to figure out what the right solution is (I'm sure there is one), perhaps replacing the container HTML/JS of imgui's example_emscripten_opengl3/ with one closer to yours we can validate this hypothesis. I did run https://nereid.pl/wheel and recorded some values but since presently our aim is to support SDL and/or GLFW I can't do much with this, as I am not sure what value transforms are applied by the emulation layers for SDL and GLFW. This is however a good reference of what the raw data should be. About Shift+Vertical Wheel on OSX When running on Emscripten under OSX, Shift+WheelY with a mouse will be cancelled by our own handling of Shift+WheelY, this is because ** About Tracy being a savy used a horizontal scrolling ** We could consider a feature where vertical wheel could be redirect to horizontal scroll under some conditions. If interested please open a dedicated issue. |
Note that it is also perfectly possible that the "flipping" is part of a HTML/CSS/JS directive for scroll direction, which would explain the difference between your version and mine. |
Effectively both my fix and your fix being so different suggest they are both wrong, so this issue is entirely Open, but hopefully now that backends are better standardized and we have the data in a sheet it should be easier to reason about the Emscripten parts without noise. (I also now have a Mac at hands, a Windows with touchpad and a mouse with dual-wheel emulation.) |
Browser user-agent string. https://developer.mozilla.org/en-US/docs/Web/API/Navigator/userAgent |
I would have no idea how to sanely surface this data into the C++ side, or at least massage it on JS side into a bool for C++ side, whichever makes more sense. |
Refer to https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html#call-compiled-c-c-code-directly-from-javascript for information on javascript string to char ptr conversion, and the immediately following section for information on executing javascript snippets from within C/C++ code. Example code: float Backend::GetDpiScale()
{
#ifdef __EMSCRIPTEN__
return EM_ASM_DOUBLE( { return window.devicePixelRatio; } );
#endif
...
} |
I have noticed that my Emscripten was not up to date, and updating it to latest I started getting mouse wheel values closer to 1.0f (with still values such as 3.75f on some browser). At some point emscripten repo changed from using I am currently working on refactoring all examples in the codebase to use a |
FYI : I've pushed the example refactors mentioned as 96ab68e and ce6e6da making the SDL+GL and GLFW+GL examples also build and run with Emscripten, without the need for a dedicated Emscripten example. This is effectively adding the "missing" GLFW+Emscripten example that doesn't rely on WebGPU. Apologies for the yak-shaving/spam. TL;DR;
SDL
GLFW
I think we should:
OR
|
I'll work toward have our CPP backend simply use emscripten.h raw events, it seems like the sane way out. Zooming affects the |
I think the values the browser is producing are better explained here: https://www.w3.org/TR/uievents/#events-wheelevents
The exact coordinate system of axis is also specified:
Emscripten has code to fix this for browsers that are not compliant with the DOM Level 3 wheel event. I wouldn't expect there to be any processing of the raw values returned by the browser, so I don't think it's an emscripten bug. For the SDL implementation, refer to the source code I have linked before. The adjustments there are somebody's random guess at what would feel fine, I guess. See emscripten-ports/SDL2#149 and the Rube Goldberg machine of referenced wheel issues. As for GLFW, I was wondering where the implementation is, as there is no emscripten-specific code in the GLFW repo. It seems that emscripten has a pure javascript implementation of the GLFW API. https://github.com/emscripten-core/emscripten/blob/main/src/library_glfw.js
FWIW, emscripten's html5.h API is very nice to work with. You need to use OpenAL for sound and EGL to setup OpenGL, but you remove all that intermediate layer of SDL/GLFW code, which may be doing unexpected things, as is evident in this issue. |
Thanks for your help and patience with this, this is clearly not my comfort zone (both Emscripten and doing anything on a Mac). I wired Emscripten raw calls into our GLFW backend, and could visualize the difference. I think the bug in the GLFW emulation JS code is: https://github.com/emscripten-core/emscripten/blob/main/src/library_glfw.js#L518
This quantization is causing my issue that I can't get good values for both touchpad and stepping mouse, it makes small movement on the later end up having too much effect, as slow wheeling on a stepping mouse will emit series of -1/+1. I think I can now workaround using |
Here's a tentative fix for the magnitude GLFW backend: Could you cherry-pick (after updating) and see if things works for you? |
Firefox on Linux + touchpad is ok. I can't comment on how mouse wheel behaves, as I don't have handling of smooth vertical wheel input. Impulses (be it from the wheel, or the touchpad) work as expected. The build I was testing on is available at https://tracy.nereid.pl/ |
To give more context for this, the reason is that I treat zoom in and zoom out events as impulses, as discussed earlier. I want the zoom to be animated so that the UI feels smooth. Touchpad control of the zoom requires mapping the touchpad finger motion directly to the zoom action, but this requires the touch start/end events. Emitting events on both axes is still a bad idea from a UX point of view. There should be some dead zone to allow the user to perform vertical-only or horizontal-only scrolling. Windows and Linux get this, MacOS apparently doesn't. |
Mac OS emit dual-axis events for the first few events then seem to lock an axis. We use that and a heuristic to make our scrolling code select and lock a single axis. Are you polling wheel data or talking about scrolling here? |
I handle dx and dy separately. I think both actions should be available to the user at the same time, if they want to, and this works reasonably well on Linux. On Mac you have to fight against this.
On the principle, I would be against fixing this in client code. |
I have pushed the GLFW backend change as d0b1aaa. Most OSX apps are using "main axis" heuristic for scrolling. It is useful and sometimes required when you have nested view (e.g. child can scroll in one direction, parent in the other direction). This is what was addressed in #3795. However we never alter |
…iding by 100.0f on Emscripten. (#4019, #6096, #1463) Ref libsdl-org/SDL#10454 (comment)
A couple of small fixes for targeting GLFW with Emscripten.
Please let me know if anything needs improving 🙂