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

Inconsistent char output using Alt key binds in macOS #2017

Closed
spamwax opened this issue Jan 19, 2019 · 62 comments
Closed

Inconsistent char output using Alt key binds in macOS #2017

spamwax opened this issue Jan 19, 2019 · 62 comments

Comments

@spamwax
Copy link

spamwax commented Jan 19, 2019

Which operating system does the issue occur on? macOS (10.13.6)
Alacritty: 0.2.5

I have following in my settings file

  - { key: F,        mods: Alt,         chars: "\x1bf"                  }
  - { key: I,        mods: Alt,         chars: "\x1bi"                  }

Doing a /bin/cat and typing Option+F followed by a B produces ^[fb while Option +I followed by a B produces ^[iˆb

I came across this issue while using kakoune editor. I reported the issue there but it seems it is alacritty's issue (both iTerm & Terminal print ^[ib)

@chrisduerr
Copy link
Member

What happens if you remove these bindings? This should be the case by default, since the alt modifier will automatically send escape codes.

Also does it produce ^[i^b or ^[i^B?

@spamwax
Copy link
Author

spamwax commented Jan 19, 2019

@chrisduerr with my settings it produced small letter b. Please note that the carrot before the small letter b (mentioned in my original post above) is different than actual character that you would get by pressing it on the keyboard!

I removed my alacrity.yml file & restarted, which I believe causes alacrity to start with alt_send_esc: true as default.
Option+F followed by B produces ƒb
Option+I followed by B produces ˆb (again the carrot is not standard here)

So it seems, at least on my end, the recent changes to automatically send escape code is not working!

More examples (with default settings):
Option+O followed by B produces øb
Option+A followed by B produces åb

@chrisduerr
Copy link
Member

It should send ^[f and ^[i, can someone else repro this on macOS?

@spamwax
Copy link
Author

spamwax commented Jan 19, 2019

I just built alacrity on my laptop (macOS) too and it has the same issue as above!

@chrisduerr
Copy link
Member

Might be interesting to show the output of alacritty --print-events when those characters are input.

In theory, it should print that the alt key is held down.

@spamwax
Copy link
Author

spamwax commented Jan 20, 2019

Alacritty is run from iTerm with default settings:

Option+I followed by B produces this output (produces ˆb )

Option+F followed by B produces this output (produces ƒb )

Alacritty is run from iTerm with my key bind settings:

Option+I followed by B produces this output (produces ^[iˆb )

Option+F followed by B produces this output (produces ^[fb )

@chrisduerr
Copy link
Member

@spamwax Is this with alt_send_esc: true in both scenarios?

@spamwax
Copy link
Author

spamwax commented Jan 20, 2019

in first one, I'm using the default settings where as you mentioned that setting is true.
in second one I do explicitly set it to true.

@chrisduerr
Copy link
Member

It shouldn't matter for the second one since keybindings automatically suppress alt escape characters anyways.

I've got a little diff which should help with debugging this. It prints the exact characters that are written to the pty and bypasses potential issues with shell/cat. I'm interested in what this outputs when you use the default config without modified key bindings.

diff --git a/src/input.rs b/src/input.rs
index 65b7b6c..a9d3207 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -748,7 +748,10 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
                 && self.ctx.last_modifiers().alt
                 && utf8_len == 1
             {
+                println!("WRITING ^[{}", c);
                 bytes.insert(0, b'\x1b');
+            } else {
+                println!("WRITING {}", c);
             }
 
             self.ctx.write_to_pty(bytes);

In theory this should print the following for you when pressing Option+I -> B:

WRITING ^[^
WRITING b

And I'd assume this is output when pressing Option+F -> B:

WRITING ^[f
WRITING b

@spamwax
Copy link
Author

spamwax commented Jan 20, 2019

I get identical outcomes! (Option+I -> B)

with default settings:

[2019-01-19 21:11] [INFO] glutin event: WindowEvent { window_id: WindowId(Id(140402958181952)), event: ReceivedCharacter('ˆ') }
WRITING ˆ
[2019-01-19 21:11] [INFO] glutin event: WindowEvent { window_id: WindowId(Id(140402958181952)), event: ReceivedCharacter('b') }
WRITING b

with my key bindings I get:

[2019-01-19 21:08] [INFO] glutin event: WindowEvent { window_id: WindowId(Id(140196043588400)), event: ReceivedCharacter('ˆ') }
WRITING ˆ
[2019-01-19 21:08] [INFO] glutin event: WindowEvent { window_id: WindowId(Id(140196043588400)), event: ReceivedCharacter('b') }
WRITING b

for Option+F -> B (default settings)

[2019-01-19 21:16] [INFO] glutin event: WindowEvent { window_id: WindowId(Id(140549080384736)), event: KeyboardInput { device_id: DeviceId(DeviceId), input: KeyboardInput { scancode: 58, state: Pressed, virtual_keycode: Some(LAlt), modifiers: ModifiersState { shift: false, ctrl: false, alt: true, logo: false } } } }
[2019-01-19 21:16] [INFO] glutin event: WindowEvent { window_id: WindowId(Id(140549080384736)), event: KeyboardInput { device_id: DeviceId(DeviceId), input: KeyboardInput { scancode: 3, state: Pressed, virtual_keycode: Some(F), modifiers: ModifiersState { shift: false, ctrl: false, alt: true, logo: false } } } }
[2019-01-19 21:16] [INFO] glutin event: WindowEvent { window_id: WindowId(Id(140549080384736)), event: ReceivedCharacter('ƒ') }
WRITING ƒ
[2019-01-19 21:16] [INFO] glutin event: Awakened
[2019-01-19 21:16] [INFO] glutin event: WindowEvent { window_id: WindowId(Id(140549080384736)), event: KeyboardInput { device_id: DeviceId(DeviceId), input: KeyboardInput { scancode: 3, state: Released, virtual_keycode: Some(F), modifiers: ModifiersState { shift: false, ctrl: false, alt: true, logo: false } } } }
[2019-01-19 21:16] [INFO] glutin event: WindowEvent { window_id: WindowId(Id(140549080384736)), event: KeyboardInput { device_id: DeviceId(DeviceId), input: KeyboardInput { scancode: 58, state: Released, virtual_keycode: Some(LAlt), modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: false } } } }
[2019-01-19 21:16] [INFO] glutin event: WindowEvent { window_id: WindowId(Id(140549080384736)), event: KeyboardInput { device_id: DeviceId(DeviceId), input: KeyboardInput { scancode: 11, state: Pressed, virtual_keycode: Some(B), modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: false } } } }
[2019-01-19 21:16] [INFO] glutin event: WindowEvent { window_id: WindowId(Id(140549080384736)), event: ReceivedCharacter('b') }
WRITING b

Option+F -> B (my settings)

[2019-01-19 21:17] [INFO] glutin event: WindowEvent { window_id: WindowId(Id(140313842782624)), event: KeyboardInput { device_id: DeviceId(DeviceId), input: KeyboardInput { scancode: 58, state: Pressed, virtual_keycode: Some(LAlt), modifiers: ModifiersState { shift: false, ctrl: false, alt: true, logo: false } } } }
[2019-01-19 21:17] [INFO] glutin event: WindowEvent { window_id: WindowId(Id(140313842782624)), event: KeyboardInput { device_id: DeviceId(DeviceId), input: KeyboardInput { scancode: 3, state: Pressed, virtual_keycode: Some(F), modifiers: ModifiersState { shift: false, ctrl: false, alt: true, logo: false } } } }
[2019-01-19 21:17] [INFO] glutin event: WindowEvent { window_id: WindowId(Id(140313842782624)), event: ReceivedCharacter('ƒ') }
[2019-01-19 21:17] [INFO] glutin event: WindowEvent { window_id: WindowId(Id(140313842782624)), event: KeyboardInput { device_id: DeviceId(DeviceId), input: KeyboardInput { scancode: 58, state: Released, virtual_keycode: Some(LAlt), modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: false } } } }
[2019-01-19 21:17] [INFO] glutin event: WindowEvent { window_id: WindowId(Id(140313842782624)), event: KeyboardInput { device_id: DeviceId(DeviceId), input: KeyboardInput { scancode: 3, state: Released, virtual_keycode: Some(F), modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: false } } } }
[2019-01-19 21:17] [INFO] glutin event: WindowEvent { window_id: WindowId(Id(140313842782624)), event: KeyboardInput { device_id: DeviceId(DeviceId), input: KeyboardInput { scancode: 11, state: Pressed, virtual_keycode: Some(B), modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: false } } } }
[2019-01-19 21:17] [INFO] glutin event: WindowEvent { window_id: WindowId(Id(140313842782624)), event: ReceivedCharacter('b') }
WRITING b

@chrisduerr
Copy link
Member

chrisduerr commented Jan 20, 2019

Okay, so I can see two issues here.

The first issue is that the keys you're pressing are automatically being converted to modified keys. So LAlt + F -> ƒ. And LAlt + i -> ^. This is a more complicated issue and I'd like to put this on hold until I've figured out the other one.

The second issue is that for some reason alt isn't sending escape sequences. To figure out why exactly that's not the case, I've created another small diff which should print even more debug info in the case that no escapes are written:

diff --git a/src/input.rs b/src/input.rs
index 65b7b6c..b9efd0e 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -748,7 +748,14 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
                 && self.ctx.last_modifiers().alt
                 && utf8_len == 1
             {
+                println!("WRITING ^[{}", c);
                 bytes.insert(0, b'\x1b');
+            } else {
+                println!("RECEIVED COUNT: {}", self.ctx.received_count());
+                println!("ALT SEND ESC: {}", self.alt_send_esc);
+                println!("UTF8 LEN: {}", utf8_len);
+                println!("ALT: {}", self.ctx.last_modifiers().alt);
+                println!("WRITING {}", c);
             }
 
             self.ctx.write_to_pty(bytes);

Thanks a ton for your cooperation. Hopefully this can be quickly resolved.

@spamwax
Copy link
Author

spamwax commented Jan 20, 2019

while i apply the path, i'd like to mention that behavior like LAlt + F -> ƒ seems to be mac specific and is related to diacritic as fas as I can tell. According to some, that PR has caused issues with Meta/Option key.

@chrisduerr
Copy link
Member

The issues discussed in #209 should be resolved with the new alt_send_esc option. Since that allows not sending an escape sequence anymore.

The problem with your setup though is that it's not actually sending f, but ƒ. And that it's sending no escape sequence. So I think it's a bit different, though I'm not super familiar with macOS.

Ideally we'd want it to send ^[ƒ, that much I would understand. Since you're pressing alt it should send the escape sequence. The ƒ vs f issue would still be present, but that's something that might need discussion with the window library Alacritty is using.

@spamwax
Copy link
Author

spamwax commented Jan 20, 2019

Default settings:

Option+I -> B gives this
Option+F -> B gives this

@chrisduerr
Copy link
Member

Ah, so it turns out that these characters have a utf8 length of 2, so that's why the escape characters aren't printed.

So the problem is just that when pressing alt and hitting f/i, that it's putting down modified characters. I think the only way to resolve this problem would be to request with our window library to not send combined characters whenever the alt key is held down and the alt_send_esc option is enabled.

@spamwax
Copy link
Author

spamwax commented Jan 20, 2019

so other than filing a bug, what would help motivate them to resolve this faster? :)

@chrisduerr
Copy link
Member

I'm not sure if this is something that will be included in winit actually since most applications do not have this problem.

This is kinda a mess macOS has created by using alt instead of something like an AltGr key. I'm not sure if there's a good solution for this problem.

Reading through this whole thread again though, there are some new insights I have obtained. My main focus so far was making this work without having to add any key bindings, which should be the goal, however I kinda ignored that it didn't work with keybindings either.

In theory, this should work great when manually adding key bindings. However it clearly does not. Alacritty currently ignores all characters while a keybinding is triggered. However that doesn't seem to be working here.

This is the relevant part of your debug output (paraphrased):

LAlt Down
I Down, Alt pressed
I Up, Alt pressed
LAlt Up
B Down
Received character ˆ
Received character b

Looking at this, it seems like the weird ^ character is received after the B character has been pressed. However it should be received after I Down, Alt pressed has been received.

Would you mind verifying that this is really the case? That would be a separate issue in winit and I'm not sure what's going on. I'd assume it's true that Option + I is what should create the ^ character, right? Does the ReceivedCharacter('^') really only pop up when B is pressed? Or does it pop up when B is not pressed, but it's just always after LAlt Up?

@spamwax
Copy link
Author

spamwax commented Jan 21, 2019

Based on alacritty output, this is what's happening:

LAlt Down
I Down, Alt pressed
I Up, Alt pressed
LAlt Up
# No ReceivedCharacter message in output

at this point i waited one minute then pressed B, then this is what got printed:

B Down, No modifier
ReceivedCharacter('ˆ')
# ... your patch debug outputs
ReceivedCharacter('b')
# ... your patch debug outputs
B Up, No modifier

However you may be right that wininit is messing the order here, as in a native macOS app when I press Option+I, the app actually shows a weird ^ indicating it's waiting for next stroke:

untitled

So it seems wininit is following macOS convention and waiting for next key before actually sending stuff to its clients.

@chrisduerr
Copy link
Member

Okay, so I think I've got this figured out now. The ^ is actually a dead key which goes over other characters, so the only reason why the bindings won't work is because this character isn't received until the next one is pressed.

Since I now actually know what exactly is going on, I'll open an issue upstream and link it here. Maybe we can figure something out.

@chrisduerr
Copy link
Member

I've created an issue upstream to track a solution for this problem:
rust-windowing/winit#768

@chrisduerr chrisduerr added this to the Version 1.0 milestone Feb 5, 2019
@eraserhd
Copy link

This is a duplicate of #1610 .

@chrisduerr
Copy link
Member

@eraserhd Thanks a lot for letting me know. Closing the other issue as duplicate since this one contains a lot more information about the issue at hand.

@kjmph
Copy link

kjmph commented Feb 14, 2020

Great news that your issue is fixed @spamwax! I had a request upstream to move the change into configuration, so that when option_as_alt set to true it will toggle this behavior on. I held on modifying the pull request till you had tested using the original with_ignore_alt_modifier patch. Are you good with me modifying the upstream pull request? There is no change to the event processing, it is purely a cosmetic change.

@spamwax
Copy link
Author

spamwax commented Feb 14, 2020

yes I am. thanks for your PR.
can winit distinguish between L-Option, R-Option?
If answer is yes, then maybe it's possible to keep both behavior of alt, i.e.
L-Option can be set to send alt and R-Option continue to modify characters before sending it to alacritty (more acting like a AltGr?

@kjmph
Copy link

kjmph commented Feb 15, 2020

I don't believe it can distinguish at the event time. The ALT modifier is set when the alphanumeric characters are pressed, and to know if it was L-Option or R-Option there would need to be state captured; which is error prone. I'll go ahead and push to the winit PR.

@jkp
Copy link

jkp commented Nov 20, 2020

I just found out about Alacritty, and while excited to use it, found that my fingers have the key bindings so ingrained in them. So, I fixed this problem solely in my alacritty.yml file. If this helps anyone else, here is my config. This makes Alacritty behave like Terminal.app. Key repeats and diacritics are all fixed.

There may be a few key combinations that others use, is there anyway we can make a standard MacOS config?

Hi there. Where are we with all this? I'm still rather confused and although the mappings listed in the referenced post work for most cases they don't cover all key-combos and I'm running into more and more things that don't work as expected (especially in emacs which relies heavily on all kinds of key combos).

Are the patches to the upstream library integrated yet? Is there a branch of this code that fixes things more permenently?

Thanks in advance for your help.

Jamie

@chrisduerr
Copy link
Member

Are the patches to the upstream library integrated yet? Is there a branch of this code that fixes things more permenently?

No to both of those. It's work in progress, but there's very little macOS development capacity.

@kjmph
Copy link

kjmph commented Nov 20, 2020

Hello @jkp, I live in emacs and running the patch listed in rust-windowing/winit#1449 has covered all emacs bindings that I can realistically test as one person. Would you mind testing out the branch listed in the PR? I can always update against the latest master if you have problems building. I've been running the same custom binaries for most of this year, so the branch may need to be refreshed.

@jkp
Copy link

jkp commented Nov 21, 2020 via email

@jkp
Copy link

jkp commented Nov 21, 2020

OK I did three things to get a build with this patch included:

  • Pulled your branch of winit and merged in the v0.23.0 branch from the origin to make the library compatible with the version being used in the master branch of alacritty.
  • Added a patch to Cargo.toml to pull in the my locally patched winit branch.
  • Added the hard-coded line to window.rs (.with_ignore_alt_modifier(true);).

I've built the code and am running the binary but things still don't work as I expect. Maybe I just don't understand what you've done here (to be fair I haven't read the patches properly so I'm unversed). I'm expecting these fixes to make it possible to have my "alt" key act as a meta key (as per terminal.app). Do I need to do something in my config? Do I still need the key mappings that you originally listed above (those seemed like workarounds so i was assuming those could be removed once things work properly).

Thanks for the pointers. Would love to help get this tested and merged and am at your disposal to try to make that happen.

Jamie

@jkp
Copy link

jkp commented Nov 21, 2020

OK - I added one more line to window.rs to hard code .set_option_as_alt(true) and now this works as expected and I can remove all the custom mappings in my config.

I'll run with this for a while but initially it looks good. One question that you may know the answer to @kjmph - I have some emacs bindings that use both ctl and meta (eg C-M-) but with this patch it seems that emacs only sees M-\ when I press all three keys together. I captured the events that alacritty sees and they look correct:

[2020-11-21 15:36:51.497506000] [INFO ] [alacritty] glutin event: WindowEvent { window_id: WindowId(Id(140448397406320)), event: KeyboardInput { device_id: DeviceId(DeviceId), input: KeyboardInput { scancode: 42, state: Pressed, virtual_keycode: Some(Backslash), modifiers: CTRL | ALT }, is_synthetic: false } }
[2020-11-21 15:36:51.584908000] [INFO ] [alacritty] glutin event: WindowEvent { window_id: WindowId(Id(140448397406320)), event: KeyboardInput { device_id: DeviceId(DeviceId), input: KeyboardInput { scancode: 42, state: Released, virtual_keycode: Some(Backslash), modifiers: CTRL | ALT }, is_synthetic: false } }
[2020-11-21 15:36:53.009238000] [INFO ] [alacritty] glutin event: WindowEvent { window_id: WindowId(Id(140448397406320)), event: KeyboardInput { device_id: DeviceId(DeviceId), input: KeyboardInput { scancode: 59, state: Released, virtual_keycode: Some(LControl), modifiers: ALT }, is_synthetic: false } }
[2020-11-21 15:36:53.014028000] [INFO ] [alacritty] glutin event: WindowEvent { window_id: WindowId(Id(140448397406320)), event: KeyboardInput { device_id: DeviceId(DeviceId), input: KeyboardInput { scancode: 58, state: Released, virtual_keycode: Some(LAlt), modifiers: (empty) }, is_synthetic: false } }

Any ideas whats going on?

@jkp
Copy link

jkp commented Nov 21, 2020

Some further info pertinent to the issue i mentioned above.

  • This is certainly due to an issue in the terminal emulation layer. The cocoa emacs app behaves correctly, and it also works under iterm2.
  • I had some other issues under iterm2 and today I discovered an option to use a different library for keyboard event handling under that terminal emulator seems to have fixed all my issues. Could it be worth looking at integrating it into alacritty? Details are here.

@kjmph
Copy link

kjmph commented Nov 23, 2020

Thanks for the test case! I can reproduce it. It looks at first glance that if you have an Escape key on a Mac the escape key is sent through as ReceivedCharacter('\u{1b}') and then CTRL is a modifier for the \ input, yet sent through only as a modifiers: CTRL | ALT when Alt is used as Meta. Let me look into the code and see if this is a straight-forward change.

@kjmph
Copy link

kjmph commented Nov 23, 2020

Sorry for the delay, day job.. The underlying problem is that when CTRL is used, the diacritical marks are not applied to the characters. LOGO and SHIFT don't exhibit this, so charactersIgnoringModifiers will return \ instead of \u{1c}. Thus, CTRL is a special case. emacs picks up the key combination with the latest commit I just pushed to the proposed branch.

@jkp
Copy link

jkp commented Dec 1, 2020

@kjmph Just built and tested your updated branch and it fixed the ctl-meta-{foo} issue. I will keep testing with this branch for a while and report back any new issues I find. Thanks for all your work on this!

@jkp
Copy link

jkp commented Feb 1, 2021

Bump: I've been running this for a couple of months now with no issues. Is there anything I can do to help get this merged in for good so I can get back to running a vanilla release?

@kjmph
Copy link

kjmph commented Oct 1, 2021

Hello @jkp, my apologies on my delayed response, I lost steam trying to get winit to accept my patches. They were focused on having virtual keyboards work as well, which the patch didn't make any worse than what was previously existing. I moved back to Terminal.app myself, maintaining the patches for about a year got time consuming for me.

@jkp
Copy link

jkp commented Oct 4, 2021

@kjmph do you know if they merged your patches to winit in the end?

@kjmph
Copy link

kjmph commented Oct 4, 2021

They did not. One reviewer marked a change request for the name of the paramter and field, I could do that and see if one last push could get it accepted..

@jkp
Copy link

jkp commented Oct 4, 2021

@kjmph that would be ace. I could try to pick up the patch here and take it the last mile then.

@kjmph
Copy link

kjmph commented Oct 4, 2021

I'll invite you to the repository.

@l4zygreed
Copy link

hi, people

@kjmph any news about fix merge?

@dxlr8r
Copy link

dxlr8r commented Jan 17, 2022

Would love to see this happen as well.

@kchibisov
Copy link
Member

Likely fixed on master given that it looks like duplicate of #62.

@benjaminbauer
Copy link
Contributor

With alacritty 0.12.0 (5a72819) on MacOs with a custom keyboard layout some combinations, e.g. alt+q work now! 🥳

Others do not. E.g. when I press alt+. still nothing happens.
When I add extra config, it works as expected (get last argument of previous command)

key_bindings:
  - { key: Period,    mods: Alt,       chars: "\x1b." } 

Is this expected, or should I open a separate issue?

@chrisduerr
Copy link
Member

Is this expected, or should I open a separate issue?

Likely just receiving the wrong character sequence or something like that. I'd imagine it would get fixed by #458.

raphamorim added a commit to raphamorim/rio that referenced this issue May 19, 2023
Sets whether the window should get IME events. When IME is not allowed, the window won’t receive Ime events, and will receive KeyboardInput events for every keypress instead. Without allowing IME, the window will also get ReceivedCharacter events for certain keyboard input. Not allowing IME is useful for games for example.

https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_ime_purpose

rust-windowing/winit#518
rust-windowing/winit#625
alacritty/alacritty#2017
servo/servo#20770
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

10 participants