keyszer icon indicating copy to clipboard operation
keyszer copied to clipboard

Collapse `multipurpose_modmap` into regular `modmap`

Open joshgoebel opened this issue 2 years ago • 44 comments

Update: Everywhere I say conditional what I really mean is multi-modmaps.

Right now multi modmaps are applied AFTER regular modmaps - creating a lot of complex code and the possibility of a double remap to happen... why? It seems that conditional modmaps (esp once we add suspend) are a lot more like regular modmaps... there keys are suspended when pressed and then held until either the timeout is reached (or another key is added, confirming that the modifier variant is necessary). So conditional modmaps invented suspend before I did... but it's based on a stored time, not a try async timer....

The system has no way to intervene 1 second later if you continue to hold the key... it can only take action if you press or release another key. This isn't quite correct... if the potential modifier is pressed and held past the limit then it should immediately assert itself as a modifier (and not the plain key)... it seems all of this could be brought under a single suspend system and hence a single modmap... so there would be no double mapping opportunity.... it might look like this:

modmap("default",
    Key.RIGHT_CTRL: Key.ESC,

    Key.ENTER: { "momentary": Key.ENTER, "held": Key.RIGHT_CTRL },
)

I use the dict style for clarity, we could also still allow the harder to read typle/array style if we so chose. So now at any given moment only a SINGLE conditional modmap would apply. Every key would either be mapped multipurpose or single purpose. Upon secondary input suspension would end.

Given the timeout settings it might be possible for a multi-purpose key suspension to timeout BEFORE other held keys suspensions timed out... if that happened I imagine the ambiguous momentary state could be marked as not having happened (ie the key is now a modifier for sure) and it could still remain suspended unto the other keys woke. Or it could immediately wake all keys.

I think this would be a vast simplification of what we have now and make things a lot more understandable.

joshgoebel avatar Jun 08 '22 19:06 joshgoebel

@RedBearAK any thoughts? I'm not really sure how to do this in a backwards compatible way but pretty sure this is the right direction. Maintaining both old and new behavior side by side isn't something I want to do.

joshgoebel avatar Jun 09 '22 05:06 joshgoebel

Was looking at Kinto config now:

define_multipurpose_modmap(
    # {Key.ENTER: [Key.ENTER, Key.RIGHT_CTRL]   # Enter2Cmd
    # {Key.CAPSLOCK: [Key.ESC, Key.RIGHT_CTRL]  # Caps2Esc
    # {Key.LEFT_META: [Key.ESC, Key.RIGHT_CTRL] # Caps2Esc - Chromebook
    {                                         # Placeholder
})

And I bet some people are using multipurpose modmaps as a "default" modmap that's "ever present"... ie, its rules can apply REGARDLESS of the conditional modmap... if we merge the two this will no longer be possible...

This creates the need for an "always on" modmap - which I think is a better way to conceptualize this anyways. IE, a modmap that always applies - unless a conditional rule would overwrite it...

Right now a conditional modmap super-cedes and entirely disables a non-conditional modmap.

joshgoebel avatar Jun 09 '22 13:06 joshgoebel

I wonder if this would break anything if we just changed non-conditoinal mod-maps to be "always active"... if someone REALLY wanted to disable a modmap in a condition then they could do it by hand:

modmap { 
  A = > B
}

conditional( when Terminal,
# disable A => B when in Terminal
 {  A => A }
)

This would make modmaps behave the same as keymaps.

joshgoebel avatar Jun 09 '22 13:06 joshgoebel

Allowing multiple modmaps to be active at once would also sort of restore the current backwards compatible behavior of allowing one to have BOTH a modmap and multimodmap active simultaneously.

So the only thing we'd be breaking is allowing modmaps to interact with each other. (ie where a multi-modmap could remap a key already remapped by a regular modmap)

joshgoebel avatar Jun 09 '22 13:06 joshgoebel

@joshgoebel

My understandings of modmaps is just from studying and using (and to a minor extent, contributing new shortcuts to) the Kinto config file for the past couple of year. Where, other than the fact that Alt is "passed through" unmodified for Alt+Tabbing (if I understand correctly) instead of acting like Ctrl, I've gotten very used to adjusting shortcuts to adapt to the "permanent" repositioning of the modifiers that Kinto does to get all the most basic shortcuts working without specific remaps, with the variation of no Super key in terminals. (I actually never really understood why that choice was made, rather than just rewriting a bunch of basic Super shortcuts to remap back to LCtrl in terminals. That way there could be just a single modmap. Which would not solve but kind of sidestep the issue you're having with the modmap changing while using the screen paging shortcuts. I would think.)

In a general keymapper it does seem rational to solve the unwanted modifier behavior with the suspension period, and then to have the attributes of "held" and "momentary" to allow for different behaviors. But from the perspective of mimicking macOS I don't know of any shortcut or modifier that reacts that way in macOS, changing its behavior if you hold it. My focus is always going to be just wanting something that makes Kinto more reliable at making Linux and Windows behave like macOS, so that all Mac users can have a smoother transition between the three platforms. AutoHotKey seems to have things covered pretty thoroughly on the Windows side, it's extremely powerful.

With Kinto I've never really felt the need to change a modmap "on the fly", but maybe I've just been thinking about the remapper in a limited way. Other than that I'm not sure what I could contribute to the conversation without having a better grasp of the specific cases where the lack of flexibility in the modmaps might be causing a problem. Or even exactly what's happening in the chain between modmaps, multipurpose and conditionals.

any thoughts?

I try not to have too many of those. They make my head hurt.

RedBearAK avatar Jun 09 '22 18:06 RedBearAK

I've gotten very used to adjusting shortcuts to adapt to the "permanent" repositioning of the modifiers that Kinto does to get all the most basic shortcuts working without specific remaps

That annoyed me at first - I wish in your config you could write against mapped and unmapped keys, but that would probably just make most people more confused. :-)

But from the perspective of mimicking macOS I don't know of any shortcut or modifier that reacts that way in macOS,

Yeah I think it's definitely more of a Linux power tool functionality but I was trying to keep all the functionally I can - unless it seemed outright bad/broken.

With Kinto I've never really felt the need to change a modmap "on the fly",

Oh I'm certain most of the time it's a thing one does unintentionally... as I found out when I ran into an actual case. In any case that bug is fixed for the short-term... modmaps themselves are "sticky" now... they will follow you around if you keep a key held so if you push Enter and it maps to ESC... no matter what you do it will remain ESC until released. Other conditional modmaps cannot change it.

IE, the modmapping happens only at keypress. (well, key-release also for multi-mapped, but the two possibilities are mapped at the time of the press)

This may prove incorrect in the future, but if so we'll deal with it then. Step 1 needed to happen anyways to track the real/modified key so we'd even know if they changed out from under us at all.

joshgoebel avatar Jun 09 '22 19:06 joshgoebel

That annoyed me at first - I wish in your config you could write against mapped and unmapped keys, but that would probably just make most people more confused. :-)

Just so we're clear, you know I'm not in any way an original dev or maintainer of Kinto, right? Or is that "you" in the general sense?

Beyond contributing some shortcuts[*] and a few minor fixes to the installer script, which were graciously accepted by the dev, I'm basically just another end user. I just happen to want Kinto to be as usable as possible on as many platforms as possible, to benefit as many [fellow Mac users] as possible, as well as myself. So I try to help out when users have issues, and report as many issues as I can, while looking for solutions with my limited skill set.

*Well, quite a lot of shortcuts, I guess, with the Finder Mods that became part of the default config, and hopefully soon the full Option-key special character entry scheme, and all the fixes for keypad nav keys in GTK and weird laptop arrow keys with media functions instead of the usual Fn+PgUp/PgDn/Home/End. The latter two have still not been accepted into the default config. Although I think they should be, as they don't cause any harm that I know of, and fix a rather annoying problem with missing PgUp/PgDn/Home/End functionality in (GTK-based) Linux apps.

Yeah I think it's definitely more of a Linux power tool functionality but I was trying to keep all the functionally I can - unless it seemed outright bad/broken.

Yes, an eminently reasonable goal for a general keymapper utility. In fact I keep putting off an attempt to solidify the support in Kinto for maintaining the Linux window-moving shortcuts (left side, right side, corners, etc.), which usually get pretty scrambled or even disabled after installing Kinto. But it works a little different in each DE/WM, so I've never been quite sure what shortcuts to try to standardize on, since there's really nothing related in macOS without 3rd party extensions. But it would be nice to get Kinto to retain that functionality as much as possible.

RedBearAK avatar Jun 09 '22 20:06 RedBearAK

Or is that "you" in the general sense?

General sense, "your" as in "any users copy of"... :-)

The latter two have still not been accepted into the default config. Although I think they should be,

Well another thing I think that could be good is that the kinto config is split up... the "core" and then you might have extensions that people could just add, either by importing them or just by dragging and dropping them into the configuration folder...

So if there is some question about your config being right for EVERYONE but it's easy to see that some would love it it could be merged as a plugin - and then people could use it -or not use it - at their discetion.

I'm not sure how much @rbreaves wants to cater to power users though vs just shipping a "it works out of the box, trust me with the config" type package. Because you could also say "that's a great IE, but only if we build a UI for it"... and I'm not interested in that (the config UI)... I'm interested in the level below that - a most solid keymapper foundation.

joshgoebel avatar Jun 09 '22 20:06 joshgoebel

General sense, "your" as in "any users copy of"... :-)

Good old ambiguous English. We should all be using Lojban, it would make people on the ASD spectrum much happier dealing with the rest of us. Heh.

Well another thing I think that could be good is that the kinto config is split up... the "core" and then you might have extensions that people could just add, either by importing them or just by dragging and dropping them into the configuration folder...

Yeah, I've had some discussions about that with Ben a few times. Mostly centered around the fact that my customizations would get overwritten whenever I reinstalled Kinto, which I did fairly frequently early on. There doesn't seem to be a simple way to pull in an external config with a python import, at least not without needing to run through a lot of verifications on the imported file(s) for syntax errors to avoid crashing the whole thing. He wants to move to a yaml-based config setup that would then be interpreted into whatever the keymapper and distro/DE needs. But that seems like a far-future project.

wants to cater to power users though vs just shipping a "it works out of the box, trust me with the config" type package

It's pretty much the latter, so far. Which I appreciate, as it has kept the Kinto config relatively simple. I have expanded the default functionality as much as he's allowed me to, keeping within the bounds of "Does this make it act more like macOS?"

Anyway, gotta get back to finishing my insane Option-key character entry scheme. I think it's making up about 30% of the length of the AutoHotKey Kinto config file at this point. LOL.

RedBearAK avatar Jun 09 '22 21:06 RedBearAK

There doesn't seem to be a simple way to pull in an external config with a python import, at least not without needing to run through a lot of verifications on the imported file(s) for syntax errors to avoid crashing the whole thing.

This is a trivial problem to solve. You just wrap the eval in a try block... I don't see this as an issue though, you'd have syntax errors if you edited the HUGE config in the first place... either way - syntax errors - crashes. Or perhaps you can try/catch imports also, I'm unsure. Python isn't one of my best languages.

I'd also be totally open to a "--check-config` option or some such that just tried to load the config and told you if it was good or not. The kind of thing you'd use in a restart wrapper to avoid restarting if the config was bad, etc... pretty common that most server software can tell you if the config is good just as a side feature.

He wants to move to a yaml-based config setup that would then be interpreted into whatever the keymapper and distro/DE needs. But that seems like a far-future project.

I assume that's to better support windows (yaml is the source and it generates autokey and keyszer configs from that) otherwise I don't see the point and it sounds like MORE work.

joshgoebel avatar Jun 09 '22 21:06 joshgoebel

I'd also be totally open to a "--check-config` option or some such that just tried to load the config and told you if it was good or not. The kind of thing you'd use in a restart wrapper to avoid restarting if the config was bad, etc... pretty common that most server software can tell you if the config is good just as a side feature.

That could really be helpful. Especially if it were to show the error location/line number like you see in the terminal when there's a crash. That could even be popped up in a notify-send notification so the user can see info about the issue location without even needing to open a terminal.

RedBearAK avatar Jun 11 '22 01:06 RedBearAK

e1c3d4e --check @RedBearAK

joshgoebel avatar Jun 11 '22 13:06 joshgoebel

So to sum this up you’re wanting to do 2 things?

  • have layered remaps reference the original modifiers vs the newly mapped location?
  • introduce momentary & held/sticky?

Personally I think emit is shorter & conveys the same but maybe you disagree. Also prefer held over sticky, but none of that really matters to me lol.

On the confusion from layered remaps.. I’m not sure how I feel about, as like Redbear I’ve grown used to how it generally works & I believe AHK is largely & surprisingly similar so diverging too much actually creates some complexity for users trying to keep the 2 config in sync from a selfish Kinto perspective.

What I think might be better (or needed addition) is optional aliasing modifier modmap remaps.. so when I move Ctrl to Super’s location I would then have a newly created ‘Cmd’ key. To me that would make the most sense & clarity.

Now aliasing the moved Super key (Physical Ctrl) as Ctrl (during GUI apps remap) after the fact might be odd, but I guess if you implemented things to remap based on original location vs it’s actual value then that’s already resolved.

rbreaves avatar Jun 11 '22 17:06 rbreaves

Mentally I’m quite used to interpreting Ctrl as Cmd, Alt as Alt & Super as Ctrl during GUI apps.

Ctrl-Shift as Cmd, Alt as Alt & Ctrl as Ctrl for Terminal apps.

It makes complete sense when looked at from an order of operations.. order of importance perspective. Granted it must not have been that intuitive as I hadn’t seen anyone else attempt much more than simplistic xmodmap remaps before writing kinto 😅.

If you make a change I’d suggest optional modifier key aliases post remap is my only real suggestion on this. I don’t mind adapting but an alias would allow my project to have clarity I feel like.

rbreaves avatar Jun 11 '22 17:06 rbreaves

So to sum this up you’re wanting to do 2 things?

Nope. :-)

introduce momentary & held/sticky?

This already exists in multi-modmaps, I was just writing it out longer for clarity in the new side by side modmap. The length of the keys isn't so much a concern as I don't know that we have to kill the shorter form... and this isn't the other sticky I'm always talking about I was just finding words for the two sides of a multimap:

  • if_momentary (if you quickly press and release)
  • if_held (if you hold it down, it becomes a modifier)

emit doesn't work since both sides emit [keys]... And sticky (for Alt-tab) is something else entirely lets not mix that up in this discussion.


have layered remaps reference the original modifiers vs the newly mapped location?

No, but that does bug me... but this is about eliminating the triple remapping (which you aren't using except for one spot) to reduce loads of complexity... We'd still have your beloved double remapping - first with modmap, then with keymaps...

Here is the current process:

# - on_event
#   - forward non key events
#   - modmapping
#   - multi-mapping

First modmaps are searched for and applied, THEN multi-modmaps are ALSO searched for and applied so you could do something like:

- modmap maps Ctrl -> Cmd
- multi-modmap maps Cmd -> Alt
- combo maps Alt -> ESC

So hitting Ctrl on the input would generate ESC on the output. With:

define_modmap(
# Ctrl -> Cmd
)

define_multipurpose_modmap(
# Cmd -> moment: Cmd, held: Alt
)

This results in a lot of complexity and duplicate code. If someone wants Ctrl -> Alt they should just collapse that directly in a single layer modmap...

define_modmap(
# Ctrl -> moment: Cmd, held: Alt
)

So once we say "it's all just modmaps" then things get much simpler:

# - on_event
#   - forward non key events
#   - modmapping (simple and multi)

And then it can all collapse into a single modmap as a result:

modmap("default",
    Key.RIGHT_CTRL: { "if_momentary": Key.LEFT_META, "if_held": Key.LEFT_ALT },
    # short-form
    Key.RIGHT_CTRL: [ Key.LEFT_META, Key.LEFT_ALT],
)

Is that clearer? I dislike the short form because of it's ambiguity when read like this.

joshgoebel avatar Jun 11 '22 21:06 joshgoebel

This might affect one line of your config:

define_multipurpose_modmap(
    # {Key.ENTER: [Key.ENTER, Key.RIGHT_CTRL]   # Enter2Cmd
    # {Key.CAPSLOCK: [Key.ESC, Key.RIGHT_CTRL]  # Caps2Esc
    # {Key.LEFT_META: [Key.ESC, Key.RIGHT_CTRL] # Caps2Esc - Chromebook  <------------
)

This is the only place I see you might be engaging in triple modmapping. After the change I'm suggesting here the incoming key would be the REAL key, not the result of a prior modmap...

You'd have to instead figure out the actual direct mapping and integrate this into both your "terminal" and "not a terminal" modmaps.

joshgoebel avatar Jun 11 '22 21:06 joshgoebel

It's hard to say what it should be afterwards because I don't understand what you're trying to accomplish with that last mapping. (given the other conditional modmaps you already have)

joshgoebel avatar Jun 11 '22 22:06 joshgoebel

This is the only place I see you might be engaging in triple modmapping.

None of your commented out Chromebook lines actually modmap to LEFT_META though, so it could be this doesn't apply to you at all? If the "Chromebook" comments are accurate.

joshgoebel avatar Jun 11 '22 22:06 joshgoebel

What I think might be better (or needed addition) is optional aliasing modifier modmap remaps.. so when I move Ctrl to Super’s location I would then have a newly created ‘Cmd’ key. To me that would make the most sense & clarity.

I'm not sure I follow, could you throw some rough code (from the config) of what that might look like so I can get a better picture?

joshgoebel avatar Jun 11 '22 22:06 joshgoebel

I would then have a newly created ‘Cmd’ key.

I just added Cmd and Command aliases (to Super/Meta)... if you wanted to use aliases differently by some documented convention alone that made things clearer you could already do this in your K() macros.

joshgoebel avatar Jun 11 '22 22:06 joshgoebel

@rbreaves @joshgoebel

Personally I think emit is shorter & conveys the same but maybe you disagree. Also prefer held over sticky, but none of that really matters to me lol.

Yes, as I've argued "sticky" reminds me of the accessibility feature of treating a modifier key as being held down when it was already released.

I'll argue further that a keymapper should actually support all three use modes:

  1. Emit: Match shortcut, see releases, immediately send remapped keys with releases.
  2. Held: Match shortcut, respect and pass on "repeat" events for any key that's actually being held down.
  3. Sticky: Match keys, send press, send artificial repeats, do not send release event until the matching combo (or a "release" key) is press/released again.

I can imagine some users finding the true "sticky" mode very useful. For instance, you could match Shift+Ctrl+u, then act like those keys are being held down by continually sending repeats while the user enters the Unicode sequence, then release the held keys when the user taps the original keys, or just a designated "release" modifier like Ctrl. Or the user might want to send Shift+Ctrl+u but only need to hold down Ctrl during the rest of the sequence. That's just an example.

I'd have to look into the existing accessibility features of X11/Xorg to know how many users might also be helped by a global "sticky" mode that just treated all single modifier presses by themselves as held until they are pressed again.

And... looks like "sticky" keys is an existing paradigm with tools to enable the feature in X11/Xorg environments. Probably been around for decades.

https://superuser.com/questions/410657/enabling-sticky-keys-under-xorg-awesome-desktop-manager

RedBearAK avatar Jun 11 '22 22:06 RedBearAK

I just added Cmd and Command aliases (to Super/Meta)... if you wanted to use aliases differently by some documented convention alone that made things clearer you could already do this in your K() macros.

What I meant is that when the initial remapping of either Alt or Super to Ctrl that I'd like to alias that as "Cmd". So if someone used the Windows config and the modmap remap of Alt to Ctrl is being using I would then alias that to Cmd so all other specific app remaps would just use the Cmd alias instead of the C or Control name when remapping.

If the user is using a mac/apple keyboard then the Cmd alias would refer to the Super to Ctrl modmap remap. And again it just makes all of the C/Control references in Kinto.py literally say Cmd instead.

So yea alias Super itself as Cmd wouldn't do any good as it would be more of an alias on C/Control than Super - at least in practical terms.

rbreaves avatar Jun 11 '22 23:06 rbreaves

I'd like to alias that as "Cmd"

My fork already allows this (nothing magic about aliases anymore, they are just strings that can point to any modifier - modifiers are created at runtime, not hard-coded), though I admit I'd never thought of just re-aliasing Ctrl -> Cmd. That would clean up like 90% of the confusion when reading keymaps, wouldn't it?

https://github.com/joshgoebel/keyszer/blob/main/src/keyszer/models/modifier.py#L76

joshgoebel avatar Jun 12 '22 00:06 joshgoebel

You could even invent your own thing... KCmd or CMD (all caps vs Cmd)... with a tiny bit of documentation at the top of the config to explain the naming conventions you were using.

joshgoebel avatar Jun 12 '22 00:06 joshgoebel

If you repurposed Cmd how would you map tot he real Cmd though, just use one of the other aliases always on the right side of the mapping, super, etc?

What about calling it the Apl or Apple key? :-) Old school.

joshgoebel avatar Jun 12 '22 00:06 joshgoebel

... I admit I'd never thought of just re-aliasing Ctrl -> Cmd. That would clean up like 90% of the confusion when reading keymaps, wouldn't it?

Yep! lol

If you repurposed Cmd how would you map tot he real Cmd though, just use one of the other aliases always on the right side of the mapping, super, etc?

And yea I would just use the other alias's on the other side and never reference Cmd on the right I don't think.

rbreaves avatar Jun 12 '22 00:06 rbreaves

define_keymap(re.compile("caja", re.IGNORECASE),{
    K("Apple-Super-o"): K("RC-Shift-Enter"),       # Open in new tab
    K("Apple-Super-o"):    K("RC-Shift-W"),        # Open in new window
},"Overrides for Caja - Finder Mods")

joshgoebel avatar Jun 12 '22 00:06 joshgoebel

Ok, it's weird to see apple next to Super though on the left...

joshgoebel avatar Jun 12 '22 00:06 joshgoebel

What about calling it the Apl or Apple key? :-) Old school.

Personally I don't know that it would ever really matter to alias or reference the "real" Cmd key.. as it is kinda pointless to do so. I mean I get some people get a warm and fuzzy feeling when they're able to remap the legitimate Super/Meta key to function like the Cmd key does on a Mac but literally 0 developers on Linux develop or think this way so it just makes 0 sense to me to accommodate that nomenclature for the actual Super key lol.

It feels more like an academic argument than anything else, to say we should reference Super/Meta as Cmd because there just isn't a Cmd for linux & the closest thing to it has a totally different key value.

rbreaves avatar Jun 12 '22 00:06 rbreaves

Yea.. I'd prefer that we just alias Ctrl as Cmd lol. You can leave a note for dear readers that says "Yes we know Cmd actually shares the same key value as Meta/Super, but we don't care &/or it doesn't matter on Linux.".

rbreaves avatar Jun 12 '22 00:06 rbreaves