Skip to content
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

How to set up a dead key? (or also, is it possible to pass the pressed any-key_code through?) #2477

Open
GiveMeZeny opened this issue Oct 27, 2020 · 6 comments

Comments

@GiveMeZeny
Copy link

I'm trying to implement a dead key with Karabiner Elements' config.
My current attempt looks like this:

  1. I set up a boolean-variable with key-code q.
{
    "type": "basic",
    "from": { "key_code": "q" },
    "to":  [{ "set_variable": { "name": "dead-q", "value": 1 } }],
    "to_delayed_action": {
        "to_if_invoked":  [{ "set_variable": { "name": "dead-q", "value": 0 } }],
        "to_if_canceled": [{ "set_variable": { "name": "dead-q", "value": 0 } }]
    }, "parameters": { "to_delayed_action_delay_milliseconds": 10000 },
}
  1. I emit b when the boolean-variable is toggled.
{
    "type": "basic",
    "from": { "key_code": "b" },
    "to":  [{ "key_code": "c" }],
    "conditions": [{ "type": "variable_if", "name": "dead-q", "value": 1 }]
}

So far this works as intended: when I press [q, b] the output is just c. However, when pressing any other key than b, the output is just that other key.
So q is consumed either way, while a proper dead key wouldn't consume the key that acts as a dead key, when no combination was found.
So the expected output of e.g. [q, x] would be [q, x], when there is no combination for x.

I thought of something like this, using any-keycode, so I could implement the dead key's combinations with the snippet above and the rest is caught by any-key_code:

{
    "type": "basic",
    "from": { "any": "key_code" },
    "to": [
        { "key_code": "q" },
        { "key_code": "$from.key_code" }, <----
    ],
    "conditions": [{ "type": "variable_if", "name": "dead-q", "value": 1 }]
}

But there doesn't seem to be a way to obtain from's key_code or pass it through. So right now I can only always consume q or always prepend it, but not conditionally emit it when there's no combination.

Does anyone know of a method implementing a dead key using Karabiner Elements?

@MuhammedZakir
Copy link

MuhammedZakir commented Oct 28, 2020

What you are looking for is from.simultaneous. [1] Don't forget to set key_down_order to strict. [2][3]

[1] https://karabiner-elements.pqrs.org/docs/json/complex-modifications-manipulator-definition/from/simultaneous/
[2] https://karabiner-elements.pqrs.org/docs/json/complex-modifications-manipulator-definition/from/simultaneous/#manipulated-input-2
[3] https://karabiner-elements.pqrs.org/docs/json/complex-modifications-manipulator-definition/from/simultaneous-options/#key_down_order


So far this works as intended: when I press [q, b] the output is just c. However, when pressing any other key than b, the output is just that other key.
So q is consumed either way, while a proper dead key wouldn't consume the key that acts as a dead key, when no combination was found.
So the expected output of e.g. [q, x] would be [q, x], when there is no combination for x.

Your modification manipulates all q to setting dead-q variable. Regardless of which combination you use, only dead-q variable is set and q won't be send. Afaik, there are 3 ways to fix this:

  1. Straightforward and easiest (recommended) -- simultaneous event
  2. Very cumbersome and redundant -- add all possible key combinations, i.e. set variable when pressing q and then when pressing X and you don't want to use it as dead key, it will send qX instead of sending just X.
  3. Simple (depends on your use case) - send q along with setting variable and delete that q when you want use it as a dead key.

send q:

{
    "type": "basic",
    "from": { "key_code": "q" },
    "to":  [{ "set_variable": { "name": "dead-q", "value": 1 } }, {"key_code": "q"}],
    "to_delayed_action": {
        "to_if_invoked":  [{ "set_variable": { "name": "dead-q", "value": 0 } }],
        "to_if_canceled": [{ "set_variable": { "name": "dead-q", "value": 0 } }]
    }
}

q as dead key for qb -> delete q and send c:

{
    "type": "basic",
    "from": { "key_code": "b" },
    "to":  [{"key_code": "delete_or_backspace"}, { "key_code": "c" }],
    "conditions": [{ "type": "variable_if", "name": "dead-q", "value": 1 }]
}

Note: 2nd is exact opposite of 3rd. In 2nd, q is send when pressing X, whereas in 3rd, q is always send and when you want to use it as dead key, q is deleted when pressing X.

Edit: 4th method: You can also use modifier keys in from.mandatory to control the behavior. E.g. with caps lock.


About from.any: It matches all key codes/mouse buttons. Only use I can think of is for modal layout, when you want to use any key to exit a mode. There could be other uses though.

But there doesn't seem to be a way to obtain from's key_code or pass it through [...]

Currently, there is no way to "use" captured from event in to event.

@MuhammedZakir
Copy link

Why do you use 10 seconds for to_delayed_action_milliseconds? Is there a need for such a long delay?

Btw, in 2nd method above, you will have to make a small change to your modification, so that when you press q alone, it will send q. Current modification only set/unset variable, it won't send q.

{
    "type": "basic",
    "from": { "key_code": "q" },
    "to":  [{ "set_variable": { "name": "dead-q", "value": 1 } }],
    "to_delayed_action": {
        "to_if_invoked":  [{ "set_variable": { "name": "dead-q", "value": 0 } }, {"key_code": "q"}],
        "to_if_canceled": [{ "set_variable": { "name": "dead-q", "value": 0 } }]
    }
}

@GiveMeZeny
Copy link
Author

@MuhammedZakir, thanks for your response. However, doesn't mean when using from.simultaneous that at one point all of the specified keys have be pressed at the same time?
If so, dead keys can be used like that (q down, b down, both up ⟶ c), but they can also be used like: q down, q up, b down, b up ⟶ c, which wouldn't be possible with from.simultaneous I guess.

As for 3. JetBrains IDEs don't acknowledge that my dead key is not finished until I pressed a second key and thus already alter the source code on pressing the dead key (e.g. in Visual Mode) which is why I want to emulate that functionality for the IDEs to immediately emit only one key and circumvent the broken behaviour.

Also, the 10 seconds are just for testing purposes.

@MuhammedZakir
Copy link

MuhammedZakir commented Nov 3, 2020

@MuhammedZakir, thanks for your response. However, doesn't mean when using from.simultaneous that at one point all of the specified keys have be pressed at the same time?
If so, dead keys can be used like that (q down, b down, both up ⟶ c), but they can also be used like: q down, q up, b down, b up ⟶ c, which wouldn't be possible with from.simultaneous I guess.

You're right. But, if you are okay with holding down, you can extend the time using basic.simultaneous_threshold_milliseconds [1]. Otherwise, you will have to use the 2nd method described above. For that, instead of manually adding each combination, use a programming language. E.g. pqrs-org/KE-complex_modifications#697 (comment)

P.S. You may also use variables to create a mode just for this and toggle it whenever you want dead keys. A simple mode without variables can be achieved using caps lock.

[1] https://karabiner-elements.pqrs.org/docs/json/complex-modifications-manipulator-definition/from/simultaneous/#change-threshold-milliseconds

As for 3. JetBrains IDEs don't acknowledge that my dead key is not finished until I pressed a second key and thus already alter the source code on pressing the dead key (e.g. in Visual Mode) which is why I want to emulate that functionality for the IDEs to immediately emit only one key and circumvent the broken behaviour.

Can you tell me what exactly you want to do? There might be another way to achieve what you want.

Also, the 10 seconds are just for testing purposes.

I see!

@GiveMeZeny
Copy link
Author

Thanks again for the suggestions.

Having to hold down the dead key for from.simultaneous to work or making an extra mode for it is not a real option, as I already have it as a regular dead key and just want to emulate it in jetbrains' IDEs because of java/jetbrains being buggy in this regard.

Adding every single key seems like an ugly and possibly fragile solution, but it should work.
Also thanks for the script, with it it won't be as tedious!

Maybe I should feature request a forward from from to to.keycode within a macro to capture all those uninvolved keys in one case, or see if I'm able to dig into the source and do a pull request.

PS: e.g.: I have > on a dead key, so if I want to indent a selection in IdeaVim's visual mode I can't reach > without pressing the dead key, this, however, gets registered as the dead key's combination with itself (means pressing the dead key twice) immediately.
So I can't visually indent without remapping >

@MuhammedZakir
Copy link

MuhammedZakir commented Nov 6, 2020

You're welcome! :-)

Maybe I should feature request a forward from from to to.keycode within a macro to capture all those uninvolved keys in one case, or see if I'm able to dig into the source and do a pull request.

+1.

PS: e.g.: I have > on a dead key, so if I want to indent a selection in IdeaVim's visual mode I can't reach > without pressing the dead key, this, however, gets registered as the dead key's combination with itself (means pressing the dead key twice) immediately.
So I can't visually indent without remapping >

I am not sure if I understood it correctly, but have you tried remapping dead key and limit it to those specific apps?

https://karabiner-elements.pqrs.org/docs/json/complex-modifications-manipulator-definition/conditions/frontmost-application/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants