rbreaves / kinto

Mac-style shortcut keys for Linux & Windows.
http://kinto.sh
GNU General Public License v2.0
4.25k stars 213 forks source link

Consider switch to keyszer keymapper (xkeysnail fork) #718

Open joshgoebel opened 2 years ago

joshgoebel commented 2 years ago

I wanted to create an issue to track the progress/larger meta-discussion on this. I've written at length in https://github.com/joshgoebel/keyszer/issues/9 at where I see the one "last" big problem being. My plan is to add these tuning knobs and then as you're testing you can play with them and make your own decision about what is best for Kinto (or perhaps you'd expose different options in your UI). TLDR: The decision is between false positives and sending tons of wasted press/release events to output (which can cause their own issues) or breaking/slowing things like Cmd-click...

I've done more testing myself in the "no false positives" setup but I guess now I should switch that and test out the other config and see how that works (since false positives aren't a big problem for me anyways)... I do think @RedBearAK has done more testing with fast Cmd-click though by manually hacking the existing delay quite low.


Regarding upgrading itself:

See my detailed writeup for more detail. My plan the next few days:

So you could pip install it or I suppose fetch it from Git... though if you're using an actual mainline version I'm not sure why you wouldn't want an actual local --user install vs pulling from git. I'm not sure how you're doing the actual install now once you fetch your patched version from git. I think for long-term use you'd want to pin it to major versions of keyszer and at least quickly test before bumping minor versions. The whole point of semantic versioning (once we hit 1.) is that generally you should be able to trust going from 1.1 to 1.2 to 1.3 that I won't break anything on you, etc...

The risks:

I assume you plan to test it yourself quite a bit before it winds up in a large Kinto release... I'm happy to help anyway I can. Just let me know what you need.

rbreaves commented 2 years ago

I do plan to dog food this soon, whether that period is 1 to 4 weeks depends on how it goes but I expect I’ll be testing it in a branch for up to 1 week minimum before making an official release w/ the switch.

joshgoebel commented 2 years ago

0.5.0 released https://pypi.org/project/keyszer/

RedBearAK commented 1 year ago

@rbreaves

New Kinto config for keyszer

It's gotten to the point that my Kinto config was so different, with the changes I've made to take advantage of keyszer's development branch, that I broke down and started a massive reorganization, labeling and cleanup of my config file. A substantial amount of new features and capabilities have been implemented already, and when keyszer v0.7 is deemed stable enough for release there will even be a few more. I suggest waiting for that release before migrating Kinto.

There are so many changes to my config that it no longer makes much sense to try and submit additions to the current Kinto config one little section at a time in separate PRs. So I'd like to have a discussion about using my version of the Kinto config file, that I've modified heavily to work with keyszer, as a new jumping-off point to go along with the planned transition of Kinto from xkeysnail to keyszer.

Although the basic set of shortcuts that exist in the current default Kinto config are probably 99% the same, I've done a lot of moving certain blocks around to make more sense (such as moving General GUI to the end of the file so terminals can more easily override certain things). As far as I know everything that existed already is still there and in perfect working order.

I've put significant effort into making sure that I isolated anything specific to me so that it's easy to remove and there should be minimal effort required to "generalize" the config for distribution. Very little.

It's going to be hard to list all the improvements that would come with adopting this version of the config, but here goes...

There is only one new set of shortcuts that I know of that are specific to GNOME, and they're tagged with "(gnome)" at the end, similar to other lines that are so tagged for the installer to deal with. The rest of my config is either effectively the same as the existing Kinto config, just in a somewhat different order at times, or has been rewritten to take full advantage of keyszer or otherwise offer a feature that should work the same way regardless of what DE or distro the user is on.

Technically Kinto's existing config should be completely compatible with keyszer, since it has kept a lot of backward compatibility with xkeysnail. But there are a lot of good things that the changes in keyszer have enabled to be implemented already, and I think it would be a shame to not immediately take advantage of that as soon as the migration happens.

Questions? Let me know.

kinto-keyszer.py.txt

joshgoebel commented 1 year ago

Now would be great timing to explore this switch. I'm starting a new job in 3 weeks and will likely have reduced time to work on things. If there are any big bugs that require some in-depth debugging... now would be a great time window to tackle them. I have one or two more bugs to dig into in the next day or two but I hope to push an official 0.7.

rbreaves commented 1 year ago

@RedBearAK @joshgoebel I know I won't be doing anything that'll get in the way of attempting a switch over this weekend - so yea I am on board with trying to make this happen over the labor day weekend.

RedBearAK commented 1 year ago

@rbreaves

I am on board with trying to make this happen over the labor day weekend

Regarding the change to modmaps, there are some things to consider. I hesitate to bring this up since it's not actually essential to the transition, but it's something to think about and probably work on later.

With the modmaps being concurrent now rather than mutually exclusive, it's become very easy to disable/enable them "on-the-fly" without restarting Kinto/keyszer, by just toggling a variable state. This could be expanded to include some conditions that I can already think of, such as: A) toggling the multi-language option, B) disabling the main modifier remappings (GUI and terminal) for things like Synergy, or C) changing the keyboard type. All without restarting the keymapper process.

I haven't gone in this direction yet because I know this would require some changes to the Kinto installer script as well as the mechanism (i.e., using sed to toggle the commented state of various lines and restarting the service) that the GUI and tray apps currently use to allow the user to change options like keyboard type with a click.

I did change the modmaps just enough to make them happy with no longer being exclusive, but I've left all the tagging in place so that things should work as they always have while installing or changing options with the Kinto GUI tools.

I'm still too inexperienced with Python to know the simplest/best way to get the GUI tools to toggle a variable state in the running config, so I'm not sure how involved it will be to implement this method of controlling the behavior of the modmaps. I'm imagining a temporary "socket" file or something like that.

Some of the modmaps in my config file already use this mechanism of changing a variable to turn on and off, like the Mac Numpad feature (responds to Opt+Numlock or Fn+Numlock) and the Media Arrows Fix (Shift+Opt+Cmd+M, just like the AHK version that also has a menu item available).

Oh, and of course similar to keymaps the modmaps now "cascade" from top to bottom, so the order within the file if you want them to "overlap" and need to override something further down is important. That's what the terminal modmap does at the moment. But everything could also be broken up into separate modmaps with mutually exclusive conditions to the point where the order wouldn't matter. I think that would be necessary to support switching keyboard types with just a variable change.

I suppose you could also say that the terminal modmap could go back to being fully redundant with the "global" modmap and the "global" modmap could have have the condition of "not in terminals" re-attached instead of being condition-less, so they would be basically in the same mutually-exclusive state they were in originally. That would be true. But unlike before the new independent modmaps would keep working instead of being clobbered. So, lots of different ways to do it.

(Now that I think about it again I may have made a mistake in not having the main modmap not be active "in terminals", which I think also includes remotes. But if that's the case it should be easily rectified by just reverting the terminals modmap to what it was and giving the main modmap back its conditionality.)

This is probably something to save for a few months down the line, after it's all thought through logically. With the main benefit being no longer needing to restart the service for so many option changes.

Anyway, just want us all to be on the same page with the possibilities. Toodles! 👋🏽

rbreaves commented 1 year ago

@RedBearAK

I will mostly just being doing a lift & shift w/ as many minimal changes as possible for the initial push. The goal is to get something done & released by Sunday &/or Monday w/ minimal breakage. My hope is that this will fix numerous open tickets all at once.

I’ll re-read your suggestions a little later though.

joshgoebel commented 1 year ago

We may need to talk about how Kinto uses "pass_through_key" (now called ignore_key) and how that maps to the new behavior where keymaps aren't exclusive... But I suppose as long as Kinto only uses it in lower (conditional, app specific) keymaps it may not matter much.

If it were included in a higher-level keymap it would of course "block" any later keymaps from receiving that key, which would likely be bad.

RedBearAK commented 1 year ago

@joshgoebel @rbreaves

Here is an updated version of my Kinto config. The main modmaps are reverted back to being mutually exclusive by condition (in terminals vs. not in terminals). So they should work exactly as they did originally, but without interfering with the new unrelated modmaps.

This also includes a drastically improved version of the Option key special character entry scheme, thanks to very generous help from Josh getting past some sticky spots. The Option key scheme is also now enabled by default, since that is the best way to mimic a Mac keyboard. Any Alt and Shift+Alt shortcuts it may interfere with should be remapped to use the correct shortcuts for the macOS version of any particular app. For instance in VSCode the find dialog has options that use Alt+C (Match Case), Alt+W (Match Whole Word), and Alt+R (Use Regex) on Linux, but the macOS version of VSCodium uses Opt+Cmd+C/W/R for those same find options.

All other features and benefits remain the same as posted above.

kinto-keyszer.py.txt

RedBearAK commented 1 year ago

@joshgoebel @rbreaves

Update again. A more advanced version of the special characters scheme is now completed. There are two different layouts, the standard US layout and the ABC Extended layout that has, literally, hundreds of dead key special characters.

Because of the two layouts, control shortcuts for the scheme are changed to:

Shift+Opt+Cmd+O: Disable the [O]ption key scheme (turn it [o]ff) Shift+Opt+Cmd+U: Enable or disable the [U]S layout (toggle) Shift+Opt+Cmd+X: Enable or disable the ABC E[x]tended layout (toggle)

So you can completely turn it off with a single shortcut, regardless of which layout is enabled, or move back and forth between the two layouts with their specific shortcuts, or use the same shortcut that actives either layout to also deactivate that layout if used a second time while that layout is active. This seems intuitive to me, and there are GUI notifications that should make the process self-explanatory whenever you use any of the control shortcuts, announcing which layout is active or inactive and the relevant keyboard shortcuts.

The standard US layout scheme is active in this config by default. The ABC Extended layout would need to be activated by switching the variables for the layouts in its config file (if you want it on all the time), or using the Shift+Opt+Cmd+X shortcut.

There is a Markdown file in the zip with samples of the characters available on all the dead keys, showing which keys are valid for each and what the shortcut is. All other keys on the keyboard just issue a single special character, largely the same as the US layout scheme. I haven't been able to get a good count of the total number of characters on the ABC Extended layout, but it's somewhere between about 450 and 600 different characters. The standard US layout had about 137.

The main config file is now using the new "include" functionality in the latest pre-release version of keyszer to separate out the special character scheme into its own "module" file, which actually ended up being larger than the main config file. Anyone trying to use this will need to put both of the Python files from the zip in the same directory.

2022-09-10_kinto-keyszer-optspecialchars.zip

Let me know if you have any questions, concerns, problems or observations.

RedBearAK commented 1 year ago

Fixed some bad characters and added a safety function to make sure the user doesn't try to enable both the US and ABC Extended layouts at the same time. They need to be exclusive.

Included in the zip this time are the PDF formatted catalogs of all the special characters on both Option-key layouts, suitable for double-sided printing. Finally finished with the ABC Extended. Also uploaded to my documentation repo:

https://github.com/RedBearAK/optspecialchars

Now all that's left is to port the ABC Extended layout into the Windows/AHK config file. That may take some time.

2022-09-15_kinto-keyszer-optspec.zip

joshgoebel commented 1 year ago

@rbreaves Any further luck?

RedBearAK commented 1 year ago

@rbreaves

I went through a number of iterations with a completely "generic-ized" version of my config, comparing it to the existing default config with a fine-toothed comb. At this point I am confident enough (99.9%-ish range) that there would be no missing elements/features/behaviors if this was used as a drop-in replacement, that I went ahead and submitted the PR (#750).

Also seemed like a good time since the whole Option-key thing has settled down to the point where I'm comfortable with the idea of a general release of it.

I made a number of small changes that drastically simplified the "diff" that you'll see if you try to compare the two main config files. Still a number of blocks that completely change locations (IntelliJ, the General GUI block). But for the most part the diff algorithm is doing a good job just showing identical blocks together, where it's clear the only differences are things like:

Stuff like that. So the diff is looking pretty straightforward despite the rearrangement of the file compared to the original. The "General GUI" block gets broken up in the diff for reasons I can't identify, but it just moved, and the few blocks above it are just in their own section now. That should be the worst potential source of confusion when trying to do a direct comparison.

RedBearAK commented 1 year ago

I made a somewhat complex "helper" function named matchProps() to simplify matching on combinations of multiple window/context properties when using keyszer. It's documented here in the keyszer wiki:

https://github.com/joshgoebel/keyszer/wiki/Helper-Function-Examples

I'd like to see this become part of the Kinto config at some point. One of the main reasons I created the function is to enable the fix for Cmd+W with "when" condition lists like this that combine WM_CLASS and WM_NAME properties, but it can do much more than this:

### dialogs_Esc = send these windows the Escape key for Cmd+W
dialogs_Esc = [
    {cls:"^.*nautilus$",                nme:"^.*Properties$|^Preferences$|^Create Archive$"},
    {cls:"^Transmission-gtk$",          not_nme:"^Transmission$"},
    {cls:"^org.gnome.Software$",        not_nme:"^Software$"},
]

### dialogs_AltF4 = send these windows Alt+F4 combo for Cmd+W
dialogs_AltF4 = [
    {cls:"^Gnome-control-center$",      not_nme:"^Settings$"},
    {cls:"^gnome-terminal$",            nme:"^Preferences.*$"},
    {cls:"^pcloud$"},
    {cls:"^Totem$",                     not_nme:"^Videos$"},
]

The function works very well for me at this point, even serving as a replacement for the main Kinto modmaps and keymaps that need negative matching against lists such as "not in terminals" or "not in remotes". But without some more experienced eyes looking at it, I can't be sure there aren't any remaining defects, inefficiencies, or strange choices with the parameter setup that could be done in a different way to make the function easier to use.

That said, its usage seems very intuitive to me, but then again I wrote it, so I don't know if it will make much sense to others. These are the available parameters:

def matchProps(
    dpm=None,                                   # None    (dummy parameter)
    cls=None, nme=None, dvn=None,               # string  (positive match)
    not_cls=None, not_nme=None, not_dvn=None,   # string  (negative match)
    nlk=None, clk=None, cse=None,               # bool
    lst=None,                                   # list of dicts (positive)
    not_lst=None,                               # list of dicts (negative)
    dbg=None,                                   # string  (debugging info)
):

The function enforces the use of named parameters (while blocking positional arguments), and all parameters are optional, due to the fact that there is no way of knowing what kind of combination of one or more match parameters will be passed to it. It has separate parameters for positive and negative matching of the properties that are strings (wm_class, wm_name, device_name). There are a lot of internal checks to make sure the user doesn't try to do anything that doesn't make sense, like mixing positive and negative matching parameters for the same property, or trying to have it process a list of conditions that isn't a proper list of dicts containing key:value pairs of the valid parameters (as in the above examples).

I'd appreciate any kind of feedback on the function. See the wiki link for the full function and other usage examples.

joshgoebel commented 1 year ago

@rbreaves Did you make any progress with this?