Ctrl + scroll for zoom not working from keyboard but working from mouse

Hello. Since using my mouse’s scroll wheel up/down with Ctrl pressed zooms in and out in my KDE environment, I tried configuring two keys on my Fn layer to LCTRL(Key_mouseScrollUp) and LCTRL(Key_mouseScrollDn). However, they are not producing the desired zooming effect although using the actual mouse’s scroll wheel with Ctrl pressed on the keyboard does.

Please advise. Thank you!

The LCTRL() macro only works for traditional keyboard keys. The reason for this is purely technical: we have keycodes on the keymap, which the firmware interprets. There’s only so much bits (16) per key, to describe what it does, and if we wanted to support LCTRL with mouse keys, we wouldn’t fit into 16 bits.

To achieve what you want, you can use a macro, like the following:

const macro_t *ctrlScroll(uint8_t keyState, uint8_t macroIndex) {
  Key direction = Key_mouseScrollDn;
  if (macroIndex == M_CTRL_SCROLLUP)
    direction = Key_mouseScrollUp;

  handleKeyswitchEvent(Key_LeftControl, Macros.row, Macros.col, keyState);
  handleKeyswitchEvent(direction, Macros.row, Macros.col, keyState);

  return MACRO_NONE;
}

…or something along these lines. I haven’t tested the above code, so it may be a little buggy. If it is, let me know, and I’ll help fixing it up.

2 Likes

Thanks for your reply and help as always! I sorta adapted your code into the following inlined into the macroAction function (somehow I don’t like to clutter the individual macros into separate functions):

    case MACRO_ZOOM_IN:
        if (keyToggledOn(keyState))
        {
            handleKeyswitchEvent(Key_LeftControl, Macros.row, Macros.col, keyState);
            handleKeyswitchEvent(Key_mouseScrollUp, Macros.row, Macros.col, keyState);
        }
        break;

    case MACRO_ZOOM_OUT:
        if (keyToggledOn(keyState))
        {
            handleKeyswitchEvent(Key_LeftControl, Macros.row, Macros.col, keyState);
            handleKeyswitchEvent(Key_mouseScrollDn, Macros.row, Macros.col, keyState);
        }
        break;
1 Like

As a warning to other wannabe programmers here, I initially forgot the two break statements above and due to that triggering either (or one, I’m not really sure) of these macros would send my system into a non-GUI terminal from which only Ctrl+Alt+Del was possible. :slightly_frowning_face:

1 Like

We are all wannabe programmers. We will never be true programmers as we are learning every day something new (at least I do). Actually, there are no complete C++ programmers. Before you can reach this point there will always come a new version of the ISO standard that you have to adopt :wink:

Hello @algernon. How different is the above from using MACROSDOWN? And it seems to be sequential so how does it make it that Control modifies the following mouse scroll key?

Thanks!

You can’t use a variable like direction with Macro.play(), nor return a macro with a variable, because the argument of Macro.play, and the return value of macroActions must be computed at compile-time. The above code does essentially the same, but allowing run-time conditions.

There’s no report sent between the two, no clearing reports, etc. It’s like you pressed both at the exact same time, as far as the host is concerned.

Hello. Re the above, seeing as we are only sending a “toggled on” event to mimic the pressing, don’t we have to send a “toggled off” event to mimic the releasing?

So if I don’t use a variable, I still am not able to use MACROSDOWN and:

return MACRODOWN(D(LeftControl), T(mouseScrollUp), U(LeftControl));

doesn’t work. Why? Is this because the mouse key can’t really be modified by the control key and the host only responds to both being pressed at the same time? But even then, the above macro should ensure that both are seen as pressed together, no?

We’re not sending events, we’re reporting states. We also clear the report each cycle (but not during the cycle, normally). So if we stop setting the “pressed” state, that will count as toggling off.

It doesn’t work, because of a limitation of Kaleidoscope-Macros, something that is reasonably easy to fix, so much so that I just made a PR:

https://github.com/keyboardio/Kaleidoscope-Macros/pull/24

The issue was that when playing back macros, we forced sending a keyboard report, but we didn’t do the same for the mouse report. So Control going down and up was sent during macro playback, and a scrollup was sent at the end of the cycle, with Control already off.

With the pull request, we send a mouse report too, and the macro appears to work fine.

Nice catch, thank you!

Hmm. There still seems to be a problem. The relevant lines of my sketch are here. As mentioned here, Inkscape allows for Shift + Wheel Up/Dn to work as wheel left/right, and since the horizontal wheel keys aren’t working right now, I figured on using the substitute…

But I find that on pressing the wheel left macro (which is Shift + Wheel Up) the display actually goes to the left and up and likewise on pressing the wheel right macro (which is Shift + Wheel Dn) the display actually goes to the right and down.

It would seem that the mouse report is getting sent twice, once with the modifier and once without?

(In the case of Ctrl + wheel, since zooming in/out changes the centre of the picture, I wasn’t earlier able to identify the extra scroll…)

Hmm my earlier reply wasn’t so clear and somewhat misleading.

The zoom-in/out feature is working correctly if implemented as said earlier:

#define PRESS(X) handleKeyswitchEvent(X, Macros.row, Macros.col, keyState)
        if (keyToggledOn(keyState))
        {
            PRESS(Key_LeftControl); PRESS(Key_mouseScrollUp);
        }

but in the case of the macro:

        if (keyToggledOn(keyState))
            return MACRODOWN(D(LeftControl), T(mouseScrollUp), U(LeftControl));
        break;

… the extra unmodified scroll event is indeed sent. This in confirmed by observing that a zoom in/out pair implemented as a macro doesn’t restore the original state, whereas it should, and indeed does so in the case of the handleKeyswitchEvent version.

This is obviously because the extra scrolling when zoomed out does not counteract the extra scrolling when zoomed in as it is opposite in direction but not equal in magnitude.

Likewise the Shift + Wheel Up/Down for wheel left/right also doesn’t generate the extra scrolling when implemented by handleKeyswitchEvent and correctly scrolls left/right only.

Just recording an interim observation that one may want to use if (keyIsPressed(keyState)) rather than if (keyToggledOn(keyState)) in the macro for zooming. For me I prefer the stepped zooming so I retained the keyToggledOn wording but others may prefer differently.

Ah, I see the issue. When a mouse key is released, we do not explicitly clear the relevant direction in the report, but rely on the main loop to clear it for us (for keys, we explicitly do a release). So the macro will correctly send Ctrl+WheelUp (or Shift+WheelUp), but since the mouse report isn’t cleared at this stage, the main cycle will send another mouse report, and that’ll come out as WheelUp.

This is reasonably easy to fix, I think, I should have a patch up in a few hours or so. It’s non-trivial, because we need to touch not only MouseKeys, but likely HIDAdaptor-KeyboardioHID too.

Thanks for the great description and the debugging! We… didn’t think of using MouseKeys with macros, I guess. O:)

This PR (and its dependencies) should fix the issue:

https://github.com/keyboardio/Kaleidoscope-MouseKeys/pull/25

1 Like

It only became necessary because one cannot apply modifiers to the mouse keys using LSHIFT etc. I cannot think of any other macro usages involving mouse keys. :slightly_smiling_face:

OK next problem. As I mentioned here, it is found to be preferable to use keyIsPressed for the horizontal scroll at least and maybe some may prefer it for zooming too. But even if keyIsPressed is used, the macro fires only once and repeated horizontal scrolling is not seen and still requires the use of handleKeyswitchEvent only:

#define PRESS(X) handleKeyswitchEvent(X, Macros.row, Macros.col, keyState)

    case M_WHEEL_LEFT:
        if (keyIsPressed(keyState))
//             return MACRODOWN(D(LeftShift), T(mouseScrollUp), U(LeftShift));
        {
            PRESS(Key_LeftShift); PRESS(Key_mouseScrollUp);
        }
        break;

    case M_WHEEL_RIGHT:
        if (keyIsPressed(keyState))
//             return MACRODOWN(D(LeftShift), T(mouseScrollDn), U(LeftShift));
        {
            PRESS(Key_LeftShift); PRESS(Key_mouseScrollDn);
        }
        break;

I’m starting to wonder if it was worth trying to enable the use of mouse keys in macros (since there exists a non-problematic alternate solution)…

Interesting… added it to my list of things to investigate.

Well, I now have a nice Zoom in/out macro. Granted, I could’ve done it differently, but the macro was smaller, and I’m tight on space… so there’s at least one person who’s happy about this development! :smiley:

Are you saying that the macro version consumes a few lesser bytes than the handleKeyswitchEvent version?

Well I’m most appreciative of the work you’re putting into this but it seemed to me “much ado about nothing” as I didn’t know about the size benefit…