rbreaves / kinto

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

[Linux] Unicode and dead keys character entry support #701

Open RedBearAK opened 2 years ago

RedBearAK commented 2 years ago

@rbreaves @joshgoebel I'm looking at some different ways of implementing a scheme to enter accented characters and special symbols with the Option key, the way Apple does it. There are a couple of different methods to do this on Linux.

Method 1: Compose key macros. This doesn't seem to work correctly unless the DE is set up to use the Menu key as the compose key. At this time I don't know how to make this reliable without making the user change the relevant setting on their system.

Method 2: Hold Ctrl+Shift, press u, then enter the Unicode character code (e.g., 00A1) and release Ctrl+Shift.

This second method seems to work fine without changing any settings, but it won't work with Kinto's branch of xkeysnail in its current state. A macro in xkeysnail doesn't have the ability to hold down some modifier keys and then type an entire sequence of other characters before releasing the modifiers. Unless I'm missing something.

With AutoHotKey on Windows it has ways to say {Shift down}{Ctrl down}u00A1{Shift up}{Ctrl up} to complete a sequence like this as if you were typing the "u00A1" part while holding the modifier keys down, then lifting afterward.

Then again, AHK also has the ability to input Unicode characters with a very simple Send format:

!1::Send, {U+00A1}    ; Inverted Exclamation Mark

This seems to work even in Windows apps like Notepad.exe that refuse to accept Alt Codes.

The Option-key special symbol and accented character scheme for the Windows version of Kinto is nearing completion. But without some solution for a reliable way to enter Unicode characters in the Linux config, I can't work on bringing it over to the Linux side.

Maybe there could be a special function added that recognizes the {U+0000} format and translates it into the proper Shift+Ctrl+u0000 key presses and releases?

K({U+000A1})    # Inverted Exclamation Mark

The other issue that will stop me is that multi-key nested shortcuts, which seems to work in mainline xkeysnail, is broken in Kinto's fork. So it will be impossible to implement the "dead keys" method of entering accented characters until that gets repaired.

RedBearAK commented 2 years ago

hardcore facepalm

This is happening because the shortcut doesn't exist. I stopped using the script to launch keyszer, so it stopped copying my config file and stripping out the xkeysnail import each time. Newly added shortcuts simply aren't going to be there in the "local" copy of the config file until I run the script again. Stupid.

Really stupid. Now I have to strip this stuff out because it won't run normally, but once I do I'm sure it will work perfectly.

joshgoebel commented 2 years ago

Told ya it'd be something stupid... ;-p You owe me a beer/coffee now one day! LOL

RedBearAK commented 2 years ago

Ya, you bet.

But at least as a saving grace the long macro is still majorly screwed up when I'm on your branch:

Now is the time for all good men to come to the aid of their country.
N good men to come to the aid of their country.
Now is the time for all goof their country.
Now is the time for all good men to come to the aid of their country.

≈
joshgoebel commented 2 years ago

Give me this macro...

joshgoebel commented 2 years ago

But at least as a saving grace the long macro is still majorly screwed up when I'm on your branch:

And how is that a saving grace? Ugh.

RedBearAK commented 2 years ago
    K("RC-Shift-Alt-t"):          [K("Shift-N"),K("o"),K("w"),K("Space"),K("i"),K("s"),K("Space"),K("t"),K("h"),K("e"),K("Space"),
    K("t"),K("i"),K("m"),K("e"),K("Space"),K("f"),K("o"),K("r"),K("Space"),K("a"),K("l"),K("l"),K("Space"),K("g"),K("o"),K("o"),
    K("d"),K("Space"),K("m"),K("e"),K("n"),K("Space"),K("t"),K("o"),K("Space"),K("c"),K("o"),K("m"),K("e"),K("Space"),K("t"),K("o"),
    K("Space"),K("t"),K("h"),K("e"),K("Space"),K("a"),K("i"),K("d"),K("Space"),K("o"),K("f"),K("Space"),K("t"),K("h"),K("e"),K("i"),
    K("r"),K("Space"),K("c"),K("o"),K("u"),K("n"),K("t"),K("r"),K("y"),K("dot"),K("Enter")

Seems kind of pointless. It will work perfectly for you, I'm sure.

And how is that a saving grace? Ugh.

Means I'm not the only thing in the world that's totally screwed up? LOL.

RedBearAK commented 2 years ago

≈≈≈≈≈≈≈≈≈≈≈≈≈≈ ≈≈≈≈≈≈≈≈≈≈≈≈≈≈ ≈≈≈≈≈≈≈≈≈≈≈≈≈≈

The Unicode shortcut seems pretty reliable. Slow enough that I can often kind of see it typing, but it works.

Wonder if it would be possible to construct the Unicode character in the background and then just directly insert it without the visible typing.

Anyway, sorry for all the trouble. I should have known none of that made any sense.

joshgoebel commented 2 years ago

Well I was expecting to watch it type but I guess that's silly... it just pops up all at once.

joshgoebel commented 2 years ago

It's very fast here (the wavey thingy), though I have to let up to get the output.

joshgoebel commented 2 years ago

Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country. Now is the time for all good men to come to the aid of their country.

joshgoebel commented 2 years ago

Really laggy if I do it over and over in rapid succession, but it comes thru eventually.

joshgoebel commented 2 years ago

SO... you need to confirm with xev and evtest where it's getting lost and like I suggest befoe if it's on the X side we take it to the libinput peoples.

RedBearAK commented 2 years ago

≈≈≈≈≈≈≈≈2248 ≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈

I just see the number flashing, mostly. As you can see here, I got it to fail.

I really suspect that this machine has some sort of hardware interference on the USB bus or something.

Yeah, the long macros don't come out until you stop typing for a bit, if you do it repeatedly.

Looks like I should try putting a delay in your output the same why I did with the Kinto branch.

The libinput procedure for submitting new issues is fairly involved. I don't know if I can motivate myself to do that anytime soon.

For now I've got to get some sleep. Will take this up another time.

joshgoebel commented 2 years ago

I really suspect that this machine has some sort of hardware interference on the USB bus or something.

That would lead to problems, but not this problem... all this is happening inside the machine.

joshgoebel commented 2 years ago

Really? It's just a simple form on Gitlab...

RedBearAK commented 2 years ago

Read their prep procedures. It's quite extensive. If you actually want them to pay any attention to the issue, that is. But I'm sure I could submit a simple issue without following all the rules.

There are times when the mouse cursor acts like there is a rubber band between my fingertip and the cursor. Other times it acts fine. The whole machine has just always acted a little odd. The problems appeared in multiple operating systems and distro installs, which points to hardware, not software. I have a nearly identical unit that has never exhibited such issues, regardless of OS. It has a ghost, basically.

I'm out for now. Signing off.

RedBearAK commented 2 years ago

Stuck a 1ms delay in your output.py and it seems to cure the macros issue, just like with the Kinto branch:

Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
Now is the time for all good men to come to the aid of their country.
joshgoebel commented 2 years ago

1ms delay in your output.py and it seems to cure the macros issue,

Very strange.

rbreaves commented 2 years ago

And they are meaningless if you don't have the users modmap in your head.

Hence why I push and urge users to focus on letting Kinto be the primary remapping means and possibly starting their distro fresh with Kinto..

@RedBearAK Also to really offer mac like support don't you need a modal to popup sorta like this one for emoji's? https://github.com/tom-james-watson/Emote

Imo triggering an external GUI app with accent options would be the way to go and emote provides an interesting modal to base the work off of I would think.

joshgoebel commented 2 years ago

I'm very interested in this emoji pop up but only if someone can explain the security model to me. I'm wondering if we need a helper process that runs as the user to communicate to the keymapper that's running as a semi privileged user?

RedBearAK commented 2 years ago

@rbreaves

Also to really offer mac like support don't you need a modal to popup sorta like this one for emoji's?

Sure, but at the level where you're wanting to access the entire set of Emoji or all Unicode characters, there are apps for that on every platform. Like the Emoji extension for Ulauncher. And the support for launch within xkeysnail remains very fragile. I just got one of those weird semi-crashes yesterday from my screenshot launch shortcuts that I thought were fully working. If the user wants to have a separate app pop up on a shortcut it seems more like something that should be triggered with a hotkey app like sxhkd. (Which I'm just beginning to look into.)

My focus for now is entirely on bringing the more elegant "dead keys" Option key method of typing the accented characters, which I think is still better than the relatively intuitive Compose-key method, and certainly worlds better than what Windows offers. And I went ahead and cataloged the rest of the symbols on the Apple keyboard with their Unicode and Alt Codes. There are a number of fairly commonly needed characters on the numbers and letters, often in very intuitive places, so they are easy to learn and remember. Currency, trademark, copyright, some common math symbols and other things.

It looks like the only easy way to bring this to Linux with Kinto shortcuts is the Unicode entry method, since the compose key setup is finicky and would apparently require messing with the user's keyboard settings. So it's really unfortunate that method is currently not working.

The basic Option and Shift+Option set of characters has been on the Apple keyboard for decades, and I just think many Mac users would really appreciate having access to it, where for more advanced needs they can go ahead and use whatever tool is available to get the full Unicode table. I have seen a number of questions online from users trying to do some basic Apple Option key character input with AutoHotKey in Windows, and there was the user who made their own Umlaut function and put it in a Kinto PR.

The little pop-up over the letter they currently do in macOS (and iOS/iPadOS) when you hold a key is really neat, and better for visual people, but I would have no idea how to make that work across the various Linux DE graphics toolkits. By the way, this works so easily in macOS because they've never allowed most keys to repeat when you hold them down. I didn't even realize that for the longest time. That makes macOS decidedly different from Windows and Linux, where if you want to let regular keys do something special when held down you'll have to intercept the key presses and count them.

joshgoebel commented 2 years ago

If the user wants to have a separate app pop up on a shortcut it seems more like something that should be triggered with a hotkey app like sxhkd. (Which I'm just beginning to look into.)

Right now for security I'm imagining the server process (running with escalated [but not root] privileges) perhaps opening a socket that clients could easily connect to and read off simple commands... so then that piece of software would be running in user space, with the users normal X env, etc...

Or maybe that's not needed at all and sxhkd is sufficient... I definitely don't believe in super apps... if three small more focused utilities are the way to go then I'd prefer that.

For Emoji support nothing extra was needed at all since emote has a global hotkey we can just type.

gabstv commented 2 years ago

this worked for me:

define_keymap(lambda wm_class: wm_class.casefold() not in remotes,{
    # ...
    K("Alt-e"): {
        K('e'): unicode_combo([Key.KEY_0, Key.KEY_0, Key.E, Key.KEY_9]),
        K('i'): unicode_combo([Key.KEY_0, Key.KEY_0, Key.E, Key.D]),
        # ... add more
    },
    # ... add more
}, "General GUI")

fns:

def unicode_starter():
    send_key_action(Key.LEFT_SHIFT, Action.PRESS)
    send_key_action(Key.RIGHT_CTRL, Action.PRESS)
    send_key_action(Key.U, Action.PRESS)
    time.sleep(0.01)
    send_key_action(Key.LEFT_SHIFT, Action.RELEASE)
    send_key_action(Key.RIGHT_CTRL, Action.RELEASE)
    send_key_action(Key.U, Action.RELEASE)

def unicode_combo(keys):
    def fn0():
        unicode_starter()
        for k in keys:
            send_key_action(k, Action.PRESS)
            send_key_action(k, Action.RELEASE)
        send_key_action(Key.ENTER, Action.PRESS)
        send_key_action(Key.ENTER, Action.RELEASE)
    return fn0
RedBearAK commented 2 years ago

@gabstv

That is an interesting approach. We made string and Unicode processors for keyszer that allow simplified input, but they don’t use these techniques internally. Some of this may be very useful. Are you using keyszer or still on the Kinto branch of xkeysnail?

gabstv commented 2 years ago

It's the vanilla kinto (kinto.py). I also had to disable mnemonics for it to work properly. On vscode by the user settings, and on GTK3.0 apps by the settings.ini: ~/.config/gtk-3.0/settings.ini

gtk-enable-mnemonics = 0

Then reboot. If for some reason your GTK apps still have mnemonics enabled, you can copy this ini file to:

sudo cp ~/.config/gtk-3.0/settings.ini /usr/share/themes/Default/gtk-3.0/settings.ini
# ... and other themes

on firefox: about:config -> ui.key.menuAccessKeyFocuses = false

RedBearAK commented 2 years ago

@gabstv

Fascinating that you were apparently able to bypass the issue with the repeating key presses in Kinto's xkeysnail branch. I wasn't able to get the Unicode start sequence to work with Kinto's xkeysnail.

Never heard of GTK mnemonics. But doesn't that mean you disabled the ability to navigate in GTK apps with pre-existing key press shortcuts?

Do you have any idea how exactly the GTK mnemonics were interfering?

VSCode:

window.enableMenuBarMnemonics

Wow, disabling that should help with my issue with the menu bar in VSCode constantly trying to take the keyboard focus. Just like the fix for Firefox.

Looks like it still allows an Alt key tap to highlight the menu bar, so not a complete cure.

Hmm, there is another setting, but it's description and apparent (lack of) action are confusing:

window.customMenuBarAltFocus

You can set the menu bar as always hidden and it no longer seems to steal keyboard focus since it's not visible. But that seems a bit extreme.

window.menuBarVisibility
RedBearAK commented 2 years ago

@gabstv

Have a think about contributing to keyszer development. There are a number of useful features that have been proposed in the keyszer issues. Several by me. But I don't generally have the skills to implement most of them yet.

https://github.com/joshgoebel/keyszer/issues

joshgoebel commented 2 years ago

That is an interesting approach. ... Some of this may be very useful.

I'd say you need to be careful calling send_key_action inside of a command because there is a LOT of behind the scenes state tracking going on (of which keys are held, which are released, etc)... (both on the input AND output side)... since send_key_action is a high-level function on the output side it should be updating the output state correctly - but I could imagine possible cases where one corrupts the input side somehow...

An obvious risk is you write buggy code and don't pair the press/releases exactly - which would obviously break things... that's impossible to do with the built-in helpers since they do all the work for you.

I'm speaking for keyszer but some of this would apply to xkeysnail as well... but if it works in practice it might be ok most of the time...

RedBearAK commented 2 years ago

@joshgoebel

Very true. But I think there is also a potential opportunity to reduce some of the “stuck modifiers” issues. For instance, what if a common combo like Alt-Tab was a macro that also sent only release events for the modifiers every time you switched apps.

Though I suppose just sending the whole key event should just “cycle” the key state and accomplish the same thing. I’ll have to try that.