moses-palmer / pynput

Sends virtual input commands
GNU Lesser General Public License v3.0
1.77k stars 245 forks source link

What is the darwin_intercept equivalent of backspace? #478

Closed SpecialCharacter closed 2 years ago

SpecialCharacter commented 2 years ago

Description I want to delete the last character with a darwin_intercept command before replacing it by something else. The example unfortunately does not contain any non-Unicode characters that could be extrapolated...

Platform and pynput version MacOS Catalina 10.15.7 pynput version unknown

To Reproduce keyboard.Controller().type('\b') obviously does not work in darwin_intercept. Quartz.CGEventKeyboardSetUnicodeString(event, 2, '\b') fails because '\b' is not Unicode. Can I use the BACKSPACE u"\u0008" somehow?

SpecialCharacter commented 2 years ago

Edit: Actually keyboard.Controller().type('\b''\b') works and delete the last character. Now I only need to figure out how to replace it with something else...

moses-palmer commented 2 years ago

Quartz.CGEventSetIntegerValueField(event, Quartz.kCGKeyboardEventKeycode, Key.backspace.value.vk) would replace the current key event with a backspace. I think it would be possible to then send a new fake event with your replacement, if you can guarantee that your new event will not trigger the same action.

SpecialCharacter commented 2 years ago

Does for some reason not function in my program :(

However, I found this works (example: ä for ae):

keyboard.Controller().tap(Key.left)
keyboard.Controller().type('\b')
keyboard.Controller().tap(Key.right)
Quartz.CGEventKeyboardSetUnicodeString(event, 1, 'ä')

However, the execution is rather slow, and occasionally an unchanged ae can be seen.

SpecialCharacter commented 2 years ago

OK, the slow execution was my mistake, now it works.

SpecialCharacter commented 2 years ago

Wait, keyboard.Controller().type('\b') deletes both base letter and combining character (= TWO unicode characters). That cannot be right. Tried it with Quartz.CGEventSetIntegerValueField(event, Quartz.kCGKeyboardEventKeycode, Key.backspace.value.vk), same result.

SpecialCharacter commented 2 years ago

Also, when I type the second version, Quartz.CGEventKeyboardSetUnicodeString for my fake event won't work anymore… do you have a working snippet?

moses-palmer commented 2 years ago

With your last comments, I'm starting to understand what you are trying to achieve.

To make this work correctly, I think you would need to hook into the GUI framework of your platform. A user may very type an A into one edit box, move focus to another and then press E, which would produce a very confusing result given the naïve implementations possible with this library.

Sending a backspace event to delete the previous character also seems rather fragile. If you decide to attempt this using pynput, perhaps suppressing the A, setting a flag and then rewriting the E would work better? You would have to ensure fake resending the A after a delay if the second key is not an E.

SpecialCharacter commented 2 years ago

What I want to do is built a more powerful version of Apples replace function, with lots of conditionality built in:

if firstcharacter = a
   if secondcharacter = e
      keyboard.Controller().type('\b''\b''ä')
   elif secondcharacter = ...

I accept confusing results when moving focus (should not happen to often in my experience). Typing A with nothing appearing on the screen would confuse the user and make him type the character again, which would throw off the algorhythm.

The critical component is having it work in darwin_intercept... so I need my backspace.

keyboard.Controller().tap(Key.left)
keyboard.Controller().type('\b')
keyboard.Controller().tap(Key.right)
Quartz.CGEventKeyboardSetUnicodeString(event, 1, 'ä')

is kind of ugly (and slow) but at least it works. If you have a better method, I am all ears! :)

SpecialCharacter commented 2 years ago

What is strange, sometimes deleting just one unicode character works in the Python shell but not in a text document. [ ] works, ( ) does not. Could it be because you have to press shift to get ( ) ? Anyway, it does not in a text document :(

SpecialCharacter commented 2 years ago

Had a closer look at it and it seems MacOS implements backspace in a different way than Windows does (which is unfortunate). Under Windows, I am able to change combining diacritics while meandering around the base characters (a¨ --> aˉ). Contrary, MacOS will delete the combining diacritics along with its base character.

SpecialCharacter commented 2 years ago

I asked around in various forums and got this answer: "It's more likely that the backspace action deleted the entire grapheme made up of multiple codepoints rather than a single codepoint. Whatever is happening, it's probably a feature of whatever application is actually handling keyboard input (e.g., iTerm2, your IDE) or a library/framework that Python is using (e.g., GNU Readline), not Python specifically." Does that ring any bell?

SpecialCharacter commented 1 year ago

Have you had time to look into the issue? Forums suggested the faulty implementation comes from a library/framework that Python is using,