Skip to content

Instantly share code, notes, and snippets.

@krissen
Last active December 30, 2024 23:47
Show Gist options
  • Save krissen/dd27082e7ab0575619c7a31f4d2ec7ae to your computer and use it in GitHub Desktop.
Save krissen/dd27082e7ab0575619c7a31f4d2ec7ae to your computer and use it in GitHub Desktop.

Edit 2024-12-18:

Support for mouse emulation has now been implemented. See https://zmk.dev/docs/keymaps/behaviors/mouse-emulation

The instructions below are outdated.

Outdated instructions. Do ignore. Edit 2024-02-05:

The instructions below are outdated.

There is an official PR (#2027) for mouse functionality. You should use that rather than what is described below.

See comments [1, 2] below.

ZMK mouse support

When looking to add mouse support, I originally found instructions for how to go about it on Reddit. I was later pointed out to this message on the ZMK Discord (invite).

Repository with mouse support: FTC.

Documentation for for beta testing, including how to make a build off of a different repo such as the one mentioned above. Some documentation about mouse support found here; can be complemented by looking at the source code, here.

EDIT 2022-12-10: Added additional configuration options for scroll wheel speed tweaks.
EDIT 2022-12-09: Added additional configuration options which can be used to tweak mouse movement speed.

How-to

Step one, use the custom repository mentioned above in config/west.yml:

manifest:
  remotes:
    - name: zmkfirmware
      url-base: https://github.com/ftc/     # <--- CHANGE REPO
  projects:
    - name: zmk
      remote: zmkfirmware
      revision: mouse-ftc                   # <--- CHANGE REVISION
      import: app/west.yml
  self:
    path: config

Step 2, add necessary config line in config/YOURBOARD.conf:

CONFIG_ZMK_SLEEP=y
CONFIG_ZMK_MOUSE=y                          /* <--- ADD MOUSE SUPPORT */

Step 3, add support in the keyboard layout, config/YOURBOARD.keymap:

a) Necessary include in the header:

#include <dt-bindings/zmk/mouse.h>

b) Binds, like for instance:

&mkp LCLK       &mkp RCLK
&mmv MOVE_UP    &mmv MOVE_DOWN
&mmv MOVE_LEFT  &mmv MOVE_RIGHT

Step 4, build it; install on the board.

Step 5, unpair and repair the keyboard for it to be recognised as a pointer device as well.

Additional, optional configuration

To adjust mouse movement speed, the following can be used in config/YOURBOARD.keymap, before the keymap {}.

#define U_MOUSE_MOVE_MAX 1400                     /* <--- New max speed setting (default: 600) */
#undef MOVE_UP
#undef MOVE_DOWN
#undef MOVE_LEFT
#undef MOVE_RIGHT
#define MOVE_UP MOVE_VERT(-U_MOUSE_MOVE_MAX)
#define MOVE_DOWN MOVE_VERT(U_MOUSE_MOVE_MAX)
#define MOVE_LEFT MOVE_HOR(-U_MOUSE_MOVE_MAX)
#define MOVE_RIGHT MOVE_HOR(U_MOUSE_MOVE_MAX)

&mmv {
	time-to-max-speed-ms = <400>;             /* <--- How long time until max speed is reached (default: 500) */
};

To adjust scroll wheel, use something like the following:

#define U_MOUSE_SCROLL_MAX 100                    /* <--- New max speed setting (default: 10) */

#undef SCROLL_UP
#undef SCROLL_DOWN
#undef SCROLL_LEFT
#undef SCROLL_RIGHT
#define SCROLL_UP SCROLL_VERT(U_MOUSE_SCROLL_MAX)
#define SCROLL_DOWN SCROLL_VERT(-U_MOUSE_SCROLL_MAX)
#define SCROLL_LEFT SCROLL_HOR(-U_MOUSE_SCROLL_MAX)
#define SCROLL_RIGHT SCROLL_HOR(U_MOUSE_SCROLL_MAX)

&mwh {
  time-to-max-speed-ms = <500>;                 /* <--- How long time until max speed is reached */
};
						</details>
@krissen
Copy link
Author

krissen commented Nov 7, 2023

Hi all,

Looks like mouse emulation is poised to be merged into zmk master. There’s a testing branch available. Follow this GitHub issue for more information and updates.

@Synow
Copy link

Synow commented Nov 7, 2023

Hi all,

Looks like mouse emulation is poised to be merged into zmk master. There’s a testing branch available. Follow this GitHub issue for more information and updates.

That's lit! Can't wait!

@FilipParyz
Copy link

Hey everyone! I was successful in using @urob's clone to compile the mouse compatible keymap.
I can't wait to see native support in the main repo!

@Machione
Copy link

Machione commented Nov 28, 2023

I also had success with @urob's fork. It's worth noting that changing the max speed setting from 10 to 100 (as documented above) was necessary for me to get scroll wheel movements working with rotary encoders. You can see that in action here.

@tauzahmd
Copy link

tauzahmd commented Dec 3, 2023

A part of mouse emulation is in the main repo, Look at the latest doc

@caksoylar
Copy link

caksoylar commented Feb 2, 2024

FYI for anyone using this as a reference: You should now be using the official PR zmkfirmware/zmk#2027 if you want mouse keys functionality with moving and scrolling (see beta testing instructions).

Compared to above instructions, you also need to make these changes:

  • Use &msc instead of &mwh
  • Use *_X/Y instead of *_HOR/VERT (e.g. SCROLL_X instead of SCROLL_HOR

The branch ftc/mouse-ftc that is documented here is massively outdated and has already caused issues when people try to use it with other ZMK functionality or hardware that is recent.

PS: If you are creating documentation outside the official docs and sharing with others, keeping it up-to-date or at least putting an "this is not updated" notice at the beginning would be good practice.

@Synow
Copy link

Synow commented Feb 5, 2024

FYI for anyone using this as a reference: You should now be using the official PR zmkfirmware/zmk#2027 if you want mouse keys functionality with moving and scrolling (see beta testing instructions).

Compared to above instructions, you also need to make these changes:

  • Use &msc instead of &mwh
  • Use *_X/Y instead of *_HOR/VERT (e.g. SCROLL_X instead of SCROLL_HOR

The branch ftc/mouse-ftc that is documented here is massively outdated and has already caused issues when people try to use it with other ZMK functionality or hardware that is recent.

PS: If you are creating documentation outside the official docs and sharing with others, keeping it up-to-date or at least putting an "this is not updated" notice at the beginning would be good practice.

Thnx! Are there any docs regarding the implementation details that you know of?

@krissen
Copy link
Author

krissen commented Feb 5, 2024

PS: If you are creating documentation outside the official docs and sharing with others, keeping it up-to-date or at least putting an "this is not updated" notice at the beginning would be good practice.

Excellent advice! Thank you for taking the time to mention it. Have updated.

@caksoylar
Copy link

@Synow There isn't official docs at the moment, but gist's examples mostly apply (besides the changes I noted above). I am planning to contribute the docs update to the official PR when I get a chance, at which point we can point people to the PR docs preview page.

@delabere
Copy link

delabere commented Apr 7, 2024

You also need to add this into your kepmap if using zmkfirmware/zmk#2027

#include <behaviors/mouse_keys.dtsi>

This gives you the use of mmv, msc, and mkp

@Micmonta
Copy link

You also need to add this into your kepmap if using zmkfirmware/zmk#2027

#include <behaviors/mouse_keys.dtsi>

This gives you the use of mmv, msc, and mkp

Additionally to all the comments above, &mwh SCROLL_UP become &msc SCRL_UP and so on.

And if adjusting scroll speeds use #define SCRL_UP MOVE_Y(ZMK_MOUSE_DEFAULT_SCRL_VAL) and such.

@redmasters
Copy link

This work wireless?

@krissen
Copy link
Author

krissen commented Oct 5, 2024

This work wireless?

Yes

@dee-tree
Copy link

Hi! Is there an ability to assign keys to change the movement and scroll speed instead of hardcoding it in the config?

@krissen
Copy link
Author

krissen commented Dec 17, 2024

Hi! Is there an ability to assign keys to change the movement and scroll speed instead of hardcoding it in the config?

These instructions are dated. See links at the top of the page. Haven’t checked if support for changing speed on the fly has been added. Would be great and should be easy!

@urob
Copy link

urob commented Dec 17, 2024

Hi! Is there an ability to assign keys to change the movement and scroll speed instead of hardcoding it in the config?

These instructions are dated. See links at the top of the page. Haven’t checked if support for changing speed on the fly has been added. Would be great and should be easy!

Even the updated instructions are dated. Pointer & scrolling is now in upstream ZMK. There have been some important API changes just before it got merged. The official ZMK website provides thorough & clear documentation.

@dee-tree
Copy link

Hi! Is there an ability to assign keys to change the movement and scroll speed instead of hardcoding it in the config?

These instructions are dated. See links at the top of the page. Haven’t checked if support for changing speed on the fly has been added. Would be great and should be easy!

Even the updated instructions are dated. Pointer & scrolling is now in upstream ZMK. There have been some important API changes just before it got merged. The official ZMK website provides thorough & clear documentation.

Thanks for the replies! Looks like there is no changing speed on the fly support yet

@hugomaiavieira
Copy link

hugomaiavieira commented Dec 17, 2024

Hi! Is there an ability to assign keys to change the movement and scroll speed instead of hardcoding it in the config?

These instructions are dated. See links at the top of the page. Haven’t checked if support for changing speed on the fly has been added. Would be great and should be easy!

Even the updated instructions are dated. Pointer & scrolling is now in upstream ZMK. There have been some important API changes just before it got merged. The official ZMK website provides thorough & clear documentation.

Thanks for the replies! Looks like there is no changing speed on the fly support yet

I believe we can use this https://zmk.dev/docs/keymaps/input-processors/scaler#pre-defined-instances. I also need this and will try it myself later today or tomorrow.

@caksoylar
Copy link

Yes, you can implement it via the scaling processor: caksoylar/zmk-config@0020c23

Can we clean up this gist? This is officially documented at https://zmk.dev/docs/keymaps/behaviors/mouse-emulation and it is confusing for users to have outdated unofficial documentation around.

@FilipParyz
Copy link

I agree, I see way too much discussion on this thread.
@krissen could you update the information on the top, with links to the official documentation?
Maybe making the whole instruction into a collapsible section would prevent people from not reading the update.

@krissen
Copy link
Author

krissen commented Dec 18, 2024

@FilipParyz Done! 👍

@FilipParyz
Copy link

Thank you very much! Both for the original work you did and on the maintenance of this gist 😄

@dee-tree
Copy link

Yes, you can implement it via the scaling processor: caksoylar/zmk-config@0020c23

Can we clean up this gist? This is officially documented at https://zmk.dev/docs/keymaps/behaviors/mouse-emulation and it is confusing for users to have outdated unofficial documentation around.

Oh, thanks! But how does it work? Is it like constant coefficient applied to the default speed only when the layer specified (NAV) is active? Or is it incrementally multiplies when that layer is active? I think I misunderstand the behavior

@caksoylar
Copy link

caksoylar commented Dec 18, 2024

Oh, thanks! But how does it work? Is it like constant coefficient applied to the default speed only when the layer specified (NAV) is active? Or is it incrementally multiplies when that layer is active? I think I misunderstand the behavior

The scaler input processor that it uses scales the current speed, rather than setting it to a fixed value. Since we add it to a layer-specific override, it will multiply the current speed by 1/4 when NAV layer is active, yes. The speed will go back to normal if you deactivate the layer.

@hugomaiavieira
Copy link

Oh, thanks! But how does it work? Is it like constant coefficient applied to the default speed only when the layer specified (NAV) is active? Or is it incrementally multiplies when that layer is active? I think I misunderstand the behavior

The scaler input processor that it uses scales the current speed, rather than setting it to a fixed value. Since we add it to a layer-specific override, it will multiply the current speed by 1/4 when NAV layer is active, yes. The speed will go back to normal if you deactivate the layer.

For having a speed mouse movement, a regular and a slow, would I have to create 3 different mouse layers? Or would be possible to have a single layer with 3 different keys to switch the speed?

PS: Thank you for the work on your projects and for sharing it! 🫶🏼

@caksoylar
Copy link

Thanks!

You can always place multiple instances of &mmv on a same layer, with different max speeds. The max speed is determined by the behavior parameter.
You can also create different instances of mouse move behaviors with different acceleration characteristics and use them instead of &mmv.
Note that you will want to release the key for one speed before pressing another, otherwise their speeds will be accumulated.

I find that pressing an additional key to adjust speed is a bit better than having separate keys, like a precision mode in trackballs etc. For that you need to use a layer because input processors are either always on, or tied to layer status. (In my case I didn't have to create a new layer, since I could just use a lower-indexed layer instead.)

@dee-tree
Copy link

Thanks!

You can always place multiple instances of &mmv on a same layer, with different max speeds. The max speed is determined by the behavior parameter. You can also create different instances of mouse move behaviors with different acceleration characteristics and use them instead of &mmv. Note that you will want to release the key for one speed before pressing another, otherwise their speeds will be accumulated.

I find that pressing an additional key to adjust speed is a bit better than having separate keys, like a precision mode in trackballs etc. For that you need to use a layer because input processors are either always on, or tied to layer status. (In my case I didn't have to create a new layer, since I could just use a lower-indexed layer instead.)

Great, thanks for the reply! I will try your way with the layer to adjust speed. At the first glance I waited for dedicated keys to be responsible for speeding up or slowing down right in the mouse layer, but I will check if your case will be comfortable for me

@hugomaiavieira
Copy link

Thank you again @caksoylar!

@dee-tree it worked perfectly for me!

I have a layer for the mouse and three layers for changing the speed, which I trigger with a &mo for each speed layer on the mouse layer.

You can check the code here.
#include <input/processors.dtsi>
#include <dt-bindings/zmk/pointing.h>

#define ZMK_POINTING_DEFAULT_MOVE_VAL 1000  // default: 600
#define ZMK_POINTING_DEFAULT_SCRL_VAL 80    // default: 10

// Layer aliases
#define MOUSE 7
#define MOUSEFAST 8
#define MOUSESLOW 9
#define MOUSEXSLOW 10

&mmv {
    time-to-max-speed-ms = <120>;
    acceleration-exponent = <6>;
};

&msc {
    time-to-max-speed-ms = <80>;
    acceleration-exponent = <10>;
};

&mmv_input_listener {
    set_mouse_fast {
        layers = <MOUSEFAST>;
        input-processors = <&zip_xy_scaler 2 1>;
    };
};

&mmv_input_listener {
    set_mouse_slow {
        layers = <MOUSESLOW>;
        input-processors = <&zip_xy_scaler 1 2>;
    };
};

&mmv_input_listener {
    set_mouse_extra_slow {
        layers = <MOUSEXSLOW>;
        input-processors = <&zip_xy_scaler 1 4>;
    };
};

# other things...

/ {
    keymap {
        compatible = "zmk,keymap";

        # other layers...

        MOUSE {
            bindings = <
&none  &kp LEFT_SHIFT  &kp LEFT_GUI  &kp LEFT_ALT  &none    &none           &none           &mmv MOVE_UP    &none            &none
&none  &mo 8           &mo 9         &mo 10        &none    &msc SCRL_UP    &mmv MOVE_LEFT  &mmv MOVE_DOWN  &mmv MOVE_RIGHT  &mkp RCLK
&none  &none           &none         &none         &none    &msc SCRL_DOWN  &msc SCRL_LEFT  &mkp MCLK       &msc SCRL_RIGHT  &none
                                     &none         &none    &none           &mkp LCLK
            >;
        };

        MOUSEFAST {
            bindings = <
&none  &none  &none  &none  &none    &none           &none           &mmv MOVE_UP    &none            &none
&none  &none  &none  &none  &none    &msc SCRL_UP    &mmv MOVE_LEFT  &mmv MOVE_DOWN  &mmv MOVE_RIGHT  &mkp RCLK
&none  &none  &none  &none  &none    &msc SCRL_DOWN  &msc SCRL_LEFT  &mkp MCLK       &msc SCRL_RIGHT  &none
                     &none  &none    &none           &mkp LCLK
            >;
        };

        MOUSESLOW {
            bindings = <
&none  &none  &none  &none  &none    &none           &none           &mmv MOVE_UP    &none            &none
&none  &none  &none  &none  &none    &msc SCRL_UP    &mmv MOVE_LEFT  &mmv MOVE_DOWN  &mmv MOVE_RIGHT  &mkp RCLK
&none  &none  &none  &none  &none    &msc SCRL_DOWN  &msc SCRL_LEFT  &mkp MCLK       &msc SCRL_RIGHT  &none
                     &none  &none    &none           &mkp LCLK
            >;
        };

        MOUSEXSLOW {
            bindings = <
&none  &none  &none  &none  &none    &none           &none           &mmv MOVE_UP    &none            &none
&none  &none  &none  &none  &none    &msc SCRL_UP    &mmv MOVE_LEFT  &mmv MOVE_DOWN  &mmv MOVE_RIGHT  &mkp RCLK
&none  &none  &none  &none  &none    &msc SCRL_DOWN  &msc SCRL_LEFT  &mkp MCLK       &msc SCRL_RIGHT  &none
                     &none  &none    &none           &mkp LCLK
            >;
        };
    };
};

I still need to refactor it (use the aliases on &mo; I guess the speed layers could be &trans, etc) and fine tune the speed, but it is working pretty well! 🎉

I tried to do the same for the scroll movement (click to see the code), but it didn't work (but I did read the docs yet about it).
&msc_input_listener {
    set_mouse_extra_slow {
        layers = <MOUSEXSLOW>;
        input-processors = <&zip_xy_scaler 1 4>;
    };
};

When I have some extra time I will check that as well 🙂

@dee-tree
Copy link

Thank you again @caksoylar!

@dee-tree it worked perfectly for me!

I have a layer for the mouse and three layers for changing the speed, which I trigger with a &mo for each speed layer on the mouse layer.

You can check the code here.

#include <input/processors.dtsi>
#include <dt-bindings/zmk/pointing.h>

#define ZMK_POINTING_DEFAULT_MOVE_VAL 1000  // default: 600
#define ZMK_POINTING_DEFAULT_SCRL_VAL 80    // default: 10

// Layer aliases
#define MOUSE 7
#define MOUSEFAST 8
#define MOUSESLOW 9
#define MOUSEXSLOW 10

&mmv {
    time-to-max-speed-ms = <120>;
    acceleration-exponent = <6>;
};

&msc {
    time-to-max-speed-ms = <80>;
    acceleration-exponent = <10>;
};

&mmv_input_listener {
    set_mouse_fast {
        layers = <MOUSEFAST>;
        input-processors = <&zip_xy_scaler 2 1>;
    };
};

&mmv_input_listener {
    set_mouse_slow {
        layers = <MOUSESLOW>;
        input-processors = <&zip_xy_scaler 1 2>;
    };
};

&mmv_input_listener {
    set_mouse_extra_slow {
        layers = <MOUSEXSLOW>;
        input-processors = <&zip_xy_scaler 1 4>;
    };
};

# other things...

/ {
    keymap {
        compatible = "zmk,keymap";

        # other layers...

        MOUSE {
            bindings = <
&none  &kp LEFT_SHIFT  &kp LEFT_GUI  &kp LEFT_ALT  &none    &none           &none           &mmv MOVE_UP    &none            &none
&none  &mo 8           &mo 9         &mo 10        &none    &msc SCRL_UP    &mmv MOVE_LEFT  &mmv MOVE_DOWN  &mmv MOVE_RIGHT  &mkp RCLK
&none  &none           &none         &none         &none    &msc SCRL_DOWN  &msc SCRL_LEFT  &mkp MCLK       &msc SCRL_RIGHT  &none
                                     &none         &none    &none           &mkp LCLK
            >;
        };

        MOUSEFAST {
            bindings = <
&none  &none  &none  &none  &none    &none           &none           &mmv MOVE_UP    &none            &none
&none  &none  &none  &none  &none    &msc SCRL_UP    &mmv MOVE_LEFT  &mmv MOVE_DOWN  &mmv MOVE_RIGHT  &mkp RCLK
&none  &none  &none  &none  &none    &msc SCRL_DOWN  &msc SCRL_LEFT  &mkp MCLK       &msc SCRL_RIGHT  &none
                     &none  &none    &none           &mkp LCLK
            >;
        };

        MOUSESLOW {
            bindings = <
&none  &none  &none  &none  &none    &none           &none           &mmv MOVE_UP    &none            &none
&none  &none  &none  &none  &none    &msc SCRL_UP    &mmv MOVE_LEFT  &mmv MOVE_DOWN  &mmv MOVE_RIGHT  &mkp RCLK
&none  &none  &none  &none  &none    &msc SCRL_DOWN  &msc SCRL_LEFT  &mkp MCLK       &msc SCRL_RIGHT  &none
                     &none  &none    &none           &mkp LCLK
            >;
        };

        MOUSEXSLOW {
            bindings = <
&none  &none  &none  &none  &none    &none           &none           &mmv MOVE_UP    &none            &none
&none  &none  &none  &none  &none    &msc SCRL_UP    &mmv MOVE_LEFT  &mmv MOVE_DOWN  &mmv MOVE_RIGHT  &mkp RCLK
&none  &none  &none  &none  &none    &msc SCRL_DOWN  &msc SCRL_LEFT  &mkp MCLK       &msc SCRL_RIGHT  &none
                     &none  &none    &none           &mkp LCLK
            >;
        };
    };
};

I still need to refactor it (use the aliases on &mo; I guess the speed layers could be &trans, etc) and fine tune the speed, but it is working pretty well! 🎉

I tried to do the same for the scroll movement (click to see the code), but it didn't work (but I did read the docs yet about it).

&msc_input_listener {
    set_mouse_extra_slow {
        layers = <MOUSEXSLOW>;
        input-processors = <&zip_xy_scaler 1 4>;
    };
};

When I have some extra time I will check that as well 🙂

It's an idea! Hope these extra layers will not take too much memory. Thanks for your sources, I will try it.
Relating to mouse scroll speed, have you tried to assign a different layer for it?

@caksoylar
Copy link

I have not, no. But I haven't felt the need yet, but a layer that speeds it up might be nice. I could probably use the same layer I use for move precision.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment