MouseKeys Plugin - separate keys for press and release?

Is there a way to have separate “press” and “release” keys for the MouseKeys plugin?

Right now it looks like there is just Key_mouseBtnL, Key_mouseBtnM, and Key_mouseBtnR.

I’d like to have Key_mouseBtnLpress and Key_mouseBtnLrelease (and same for middle and right).

1 Like

You can do it with a pair of macros, something along these lines:

// Add MBL_P, MBL_R, etc to the macro enum
// MBL_P/_R stand for Mouse Button Left Press / Release

static struct {
 bool left, middle, right;
} mouse_buttons;

static void mousePressMacro(uint8_t macro_index, uint8_t key_state) {
  if (keyToggledOn(key_state)) {
    switch (macro_index) {
      case MBL_P:
        mouse_buttons.left = true;
        break;
      case MBM_P:
        mouse_buttons.middle = true;
        break;
      case MBR_P:
        mouse_buttons.right = true;
        break;
    }
  }

  switch (macro_index) {
    case MBL_P:
      if (mouse_buttons.left)
        kaleidoscope::hid::pressMouseButtons(KEY_MOUSE_BTN_L);
      break;
    case MBM_P:
      if (mouse_buttons.middle)
        kaleidoscope::hid::pressMouseButtons(KEY_MOUSE_BTN_M);
      break;
    case MBR_P:
      if (mouse_buttons.right)
        kaleidoscope::hid::pressMouseButtons(KEY_MOUSE_BTN_R);
      break;
  }
}

static void mouseReleaseMacro(uint8_t mouse_index, uint8_t key_state) {
  if (keyToggledOn(key_state)) {
    switch (macro_index) {
      case MBL_P:
        mouse_buttons.left = false;
        break;
      case MBM_P:
        mouse_buttons.middle = false;
        break;
      case MBR_P:
        mouse_buttons.right = false;
        break;
    }
  }

  // No need to explicitly release, we clear the report each cycle. 
  // Just mark the buttons released so the other macro doesn't re-add 
  // them to the report.
}

// Use M(MBL_P), M(MBL_R), etc on the keymap.

// And something like this in macroAction:
case MBL_P:
case MBM_P:
case MBR_P:
  mousePressMacro(macroIndex, keyState);
  break;
case MBL_R:
case MBM_R:
case MBR_R:
  mouseReleaseMacro(macroIndex, keyState);
  break;

And now some explanation: the mouse_buttons struct will be used to keep track of what state each button should be at. The mousePressMacro will set the appropriate button’s state to true whenever the key toggles on. As long as the state is true, this macro will add the button press to the report that gets sent to the host, thus, keep the button pressed. As macros are executed every cycle, even when there were no key events, this will keep the button pressed. The mouseReleaseMacro simply turns the state off. It does not release the button, because we clear the report every cycle. Therefore, if we do not re-add the press, it will get released.

Hope this helps.

2 Likes

I’ll try it out, thanks!

algernon, I tried this and I had to add in a call to releaseMouseButtons to get the mouse buttons to release. Otherwise, even after fixing the typo where you used P instead of R in mouseReleaseMacro, the buttons wouldn’t release.

So it looks like the mouse buttons don’t get cleared each cycle the way the keys do.

Here’s the working code:

static struct {
 bool left, middle, right;
} mouse_buttons;

const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) {
  switch (macroIndex) {

  case MBL_P:
  case MBM_P:
  case MBR_P:
    mousePressMacro(macroIndex, keyState);
  break;

  case MBL_R:
  case MBM_R:
  case MBR_R:
    mouseReleaseMacro(macroIndex, keyState);
  break;


static void mousePressMacro(uint8_t macro_index, uint8_t key_state) {
  if (keyToggledOn(key_state)) {
    switch (macro_index) {
      case MBL_P:
        mouse_buttons.left = true;
        break;
      case MBM_P:
        mouse_buttons.middle = true;
        break;
      case MBR_P:
        mouse_buttons.right = true;
        break;
    }
  }

  switch (macro_index) {
    case MBL_P:
      if (mouse_buttons.left)
        kaleidoscope::hid::pressMouseButtons(KEY_MOUSE_BTN_L);
      break;
    case MBM_P:
      if (mouse_buttons.middle)
        kaleidoscope::hid::pressMouseButtons(KEY_MOUSE_BTN_M);
      break;
    case MBR_P:
      if (mouse_buttons.right)
        kaleidoscope::hid::pressMouseButtons(KEY_MOUSE_BTN_R);
      break;
  }
}

static void mouseReleaseMacro(uint8_t mouse_index, uint8_t key_state) {
  
 switch (mouse_index) {
    case MBL_R:
      if (mouse_buttons.left)
        kaleidoscope::hid::releaseMouseButtons(KEY_MOUSE_BTN_L);
      break;
    case MBM_R:
      if (mouse_buttons.middle)
        kaleidoscope::hid::releaseMouseButtons(KEY_MOUSE_BTN_M);
      break;
    case MBR_R:
      if (mouse_buttons.right)
        kaleidoscope::hid::releaseMouseButtons(KEY_MOUSE_BTN_R);
      break;
  }
 if (keyToggledOn(key_state)) {
        switch (mouse_index) {
          case MBL_R:
            mouse_buttons.left = false;
            break;
          case MBM_R:
            mouse_buttons.middle = false;
            break;
          case MBR_R:
            mouse_buttons.right = false;
            break;
        }
      }
    }

Oh. Did you install the libraries via the Arduino IDE, or via the Arduino-Boards git repo? If the former, then the libraries may be out of date. In the past, MouseKeys did not clear the report like keys do, but as of recently, they do. The updated code will work with both versions, mind you, and I’m happy to hear you got it working!

1 Like

This may explain the problem I’ve been having. I think I’m in the 3rd manufacturing run - would that have the pre-mousekeys fix code?

Hello. Can you explain why this is so? I mean in my mind a macro is something that gets executed when you do something, not even when you don’t do something?

Is there any utility to that behaviour apart from this usecase (which would otherwise have required a plugin that is called upon each cycle)?

It’s not like that anymore. =)

It was like that because that was easy, and we had a few use-cases that were easier this way. But in the end, we opted to optimize out the idle case, and for the few cases that relied on it, we went with different solutions.

OK so does this mean that this macro you “prescribed” earlier will work as desired or not? I am looking for something like one-shot double tap stickiness to be released by a third tap or such for my mouse button (left will do) since I have much (diagrammatic) graphical work to do and a Kensington trackball for it but am now trying to use my KbdIO’s mouse abilities to avoid moving my hands around and wondering if it will suffice. (Only starting to get the hang of the warp drive today!) Obviously drag-drop is an important mouse usecase…

No, it will not work anymore, not as is. The AppSwitcher example in the Kaleidoscope repo may be useful to see how to keep a key pressed nowadays.

As for one-shot double-tap sticky for a mouse key… hm. Should be doable with a thin, specialised plugin. On onKeyswitchEvent, it’d record the last pressed key. If that’s the left mouse button - again - flip a flag, and keep it pressed in beforeReportingState. If the flag is flipped, and the pressed key is the mouse button, unflip the flag.

That should be the gist of it, I think. If you need further help, please open a new thread, and I’ll try to help more.

Specifically this line in Macros.cpp and this one in the sketch’s loop function?

Yep, exactly those two lines.