logiops icon indicating copy to clipboard operation
logiops copied to clipboard

MX Master 3 Thumbwheel: Very inconsistent if Interval set above 1

Open gdiShun opened this issue 3 years ago • 10 comments

Basically, Interval: 1 is way too fast for me. So I've tried setting it to 2 or 3, but both result in behavior that's way too inconsistent and unpredictable. The easiest way to illustrate this is setting it to those values, then simply scrolling from bottom-to-top as fast as you can. And then, do the same thing as slow as you can. For me, I use it for volume, I can go from 0% to 80+% in that one fast scroll, but the slow scroll won't move the volume at all. Despite traveling the same distance, the scroll wheel registers extremely different values.

I was able to fix this in a hacky way by setting Interval to 1, and then changing Thumbwheel.cpp from this:

            if(direction > 0)
                scroll_action = _config.rightAction();
            else
                scroll_action = _config.leftAction();

to this:

            if(direction > 0) {
            	count += direction;       	
	        if(count > 3) {
               		scroll_action = _config.rightAction();
			count = 0;
		}
            }
            else {
            	count += direction;
	        if(count < -3) {
	                scroll_action = _config.leftAction();
		        count = 0;
		}
            }     

I imagine there's a better, less hacky, and more user-friendly way of solving it however.

gdiShun avatar Apr 12 '21 14:04 gdiShun

I doubled down on your hack. I take no pride in this, it's amateur stack over flow copy-pasta I added to your idea, but damn if my quality of life hasn't improved with it! I just kinda reset the counter when you switch directions or start a new spin to keep the behavior consistent.

outside _handleEvent method
#include <chrono>
using namespace std::chrono;

int8_t count = 0;
milliseconds last_thumb_scroll_event = duration_cast< milliseconds >(
    system_clock::now().time_since_epoch()
);
inside _handleEvent method
            milliseconds this_event = duration_cast< milliseconds >(
                system_clock::now().time_since_epoch()
            );

            auto since_last_event = std::chrono::duration_cast<std::chrono::milliseconds>(this_event - last_thumb_scroll_event);

            if(since_last_event.count() > 250) {
                count = 0;
            }

            last_thumb_scroll_event = this_event;

            if(direction > 0) {
                if (count < 0) {
                    count = 1;
                }
                else {
                    count += direction;
                    if(count > 5) {
                        scroll_action = _config.rightAction();
                        count = 0;
                    }
                }
            }
            else {
                if (count > 0) {
                    count = -1;
                }
                else {
                    count += direction;
                    if(count < -5) {
                        scroll_action = _config.leftAction();
                        count = 0;
                    }
                }
            }

I too plead for someone to do a better job of this, but I'm content for the meantime!

dhitchcock avatar Apr 19 '21 04:04 dhitchcock

I've tried mapping Left/Right gestures with thumb button on MX Master 2S to workspace switching in Gnome 40:

                { direction: "Right"; mode: "OnInterval"; interval: 10; action: { type: "Keypress"; keys: ["KEY_LEFTMETA", "KEY_LEFTALT", "KEY_RIGHT"]; }; },
                { direction: "Left"; mode: "OnInterval"; interval: 10; action: { type: "Keypress"; keys: ["KEY_LEFTMETA", "KEY_LEFTALT", "KEY_LEFT"]; }; },

It works ok as long as I only have two workspaces, which is problematic because Gnome creates workspaces dynamically to always have another empty one. If I have more than two, then it's really hard to switch to specific one as it goes straight to first or last. I think the main issue is that the interval doesn't really work as interval. It behaves more like a dead zone. If I set it to 10, I need to first move 10 pixels to start the first event, but after that it doesn't wait for another 10 pixels, but fires more events every 1 px instead. The interval counter should reset after each event.

yawor avatar Apr 22 '21 10:04 yawor

@yawor is right. Now it's works poor. I try to bind to thumbwheel Alt+Tab and Alt+Shift+Tab, But it is impossible to work with it

meugr avatar May 03 '21 13:05 meugr

@dhitchcock I've used your hack to do what @yawor is trying to do and it works perfectly! Now I can comfortably switch between as many workspaces as I need.

Great job! Super useful!

my config:


devices: ({
  name: "Wireless Mouse MX Master 3";

  smartshift: { on: true; threshold: 10; };

  hiresscroll: {
    hires: true;
    invert: false;
    target: false;
  };
thumbwheel:
{
    divert: true;
    invert: false;
    right:
    {
        interval: 3;
        direction: "Left";
        mode: "OnInterval";
        action =
        {
            type: "Keypress";
            keys: ["KEY_LEFTCTRL", "KEY_LEFTALT", "KEY_DOWN"];
        };
    };
    left:
    {
        interval: 3;
        direction: "Right";
        mode: "OnInterval";
        action =
        {
            type: "Keypress";
            keys: ["KEY_LEFTCTRL", "KEY_LEFTALT", "KEY_UP"];
        };
    };
};

  // 4000 max for MX Master 3.
  dpi: 1800;

  buttons: (

    // Thumb button.
    { cid: 0xc3; action = { type: "Keypress"; keys: ["KEY_LEFTCTRL", "KEY_W"]; }; },
    // Top button.
    { cid: 0xc4; action = { type: "ToggleSmartshift"; }; }

  );
});

dszmaj avatar May 22 '21 15:05 dszmaj

I've give it a shot and as far as I can tell the best fix for me is just commenting out (ThumbWheel.cpp:152)

            if(scroll_action) {
                //scroll_action->press(true);
                scroll_action->move(direction * event.rotation);
            }

press(true) resets the accumulator for axis in IntervalGesture.cpp. Reset should only occur at the end of the movement, not during as is now. Works good for me :)

gfduszynski avatar Aug 16 '21 21:08 gfduszynski

gfduszynki's fix did not work for me on arch linux with MX Master 3. When using an interval greater than 1, the event fires randomly and when it does fire, it runs the action multiple times. An interval of 1 is too sensitive to be useful.

zyoungdev avatar Oct 17 '21 01:10 zyoungdev

@zyoungdev Can you share your config for thumbwheel ? Mine is as follows:

    thumbwheel:
    {
        divert: true;
        invert: false;
        left:
        {
            threshold: 1;
            interval: 10;
            direction: "Right";
            mode: "OnInterval";
            action = { type: "Keypress"; keys: ["KEY_LEFTCTRL", "KEY_PAGEUP"]; };
        };
        right:
        {
            threshold: 1;
            interval: 10;
            direction: "Left";
            mode: "OnInterval";
            action = { type: "Keypress"; keys: ["KEY_LEFTCTRL", "KEY_PAGEDOWN"]; };
        };
    };

Also, the last commit I have is: 6bb470000952405b6817032c43c7215f7d070428

gfduszynski avatar Oct 17 '21 18:10 gfduszynski

As my previous explanation was not clear I'll try to do a better job. (@iFwu) Call to scroll_action->press(true); with pretty much any gesture handler resets the state.

Relevant code from IntervalGesture.cpp

void IntervalGesture::press(bool init_threshold)
{
    _axis = init_threshold ? _config.threshold() : 0;
    _interval_pass_count = 0;
}

ThumbWheel.cpp:152 event handler for event.rotation triggers press for every movement of the wheel. This makes the config unusable.

By the way I'm still running logiops with commented out //scroll_action->press(true); and it works fine. However once I've got it working it turned out to not be such useful as I initially thought.

gfduszynski avatar Oct 17 '21 19:10 gfduszynski

My mistake! I compiled it incorrectly**. After compiling with the change from gfduszynski's fix above, I was able to utilize the threshold value for the thumbwheel. Here is my thumbwheel config:

// other config...
  thumbwheel: {
    divert: true;
    left: {
      mode: "OnInterval";
      interval: 15;
      action = { type: "Keypress"; keys: ["KEY_LEFTCTRL", "KEY_TAB"]; };
    };
    right: {
      mode: "OnInterval";
      interval: 15;
      action = { type: "Keypress"; keys: ["KEY_LEFTCTRL", "KEY_LEFTSHIFT", "KEY_TAB"]; };
    };
  };
// other config...

** I compiled using the AUR package but forgot to use -e flag on makepkg to make the changes persist.

zyoungdev avatar Oct 17 '21 19:10 zyoungdev