-
Notifications
You must be signed in to change notification settings - Fork 29.8k
[linux] Receives the unmodified characters obtained from GLFW #34752
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
Conversation
|
Comments addressed, PTAL! |
goderbauer
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure about the implications of this change for our other platforms. Greg would probably be the best person to know.
| dataText.add(Text('keyCode: ${data.keyCode} (${_asHex(data.keyCode)})')); | ||
| dataText.add(Text('scanCode: ${data.scanCode}')); | ||
| dataText.add(Text('codePoint: ${data.codePoint}')); | ||
| dataText.add(Text('charactersIgnoringModifiers: ${data.charactersIgnoringModifiers}')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this also work for all the other platforms we support? I remember @gspencergoog saying that not all data is available everywhere...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(or should we provide both properties?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Each platform has their own RawKeyEventData with different properties, so this change wouldn't affect them. There's no need to send both since codePoint is just the decimal representation of the characters (which we were converting to string anyway).
|
This PR only affects the Linux raw keyboard data, which is not really being used at all. The glfw shell was sending the key data as Android events, due to some limitations with the GLFW apis. With this PR and this one (flutter/engine#9386), the shell will be able to send the key data as GLFW events. It's safe to land this PR, since this code isn't really being used, but also not urgent. |
| @required this.keyHelper, | ||
| this.charactersIgnoringModifiers = '', | ||
| this.scanCode = 0, | ||
| this.codePoint = 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need a breaking change proposal? I wonder if it's an exception since it's related to engine/platform.
+cc @Hixie
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keyboard mapping is still a WIP, the linux part hasn't been implemented by the engine (will be after this PR is merged). I don't think it'd be worth it to add a proposal for this since these apis are still subject to change quickly and desktop is still not a stable platform.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should still announce it on flutter-announce and label this as a break if it is a break.
cbracken
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM for the code change, though I wonder if there's a less ambiguous name for the charactersIgnoringModifiers parameter.
| /// [modifiers], arguments must not be null. | ||
| const RawKeyEventDataLinux({ | ||
| @required this.keyHelper, | ||
| this.charactersIgnoringModifiers = '', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be characterIncludingModifiers (character singular)? The doc comment below describes this as "the name of the printable key" -- is this something like e, E, é, ¸ (cédille), return (for 0x0d)?
I wonder if there's a way to avoid the use of the word 'character' which is pretty ambiguous when you get down to the level of differentiating between codepoints, glyphs, bytes and other things frequently referred to as 'characters' in discussions at this level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got that name from the macos implementation, which is the name Apple uses for their own API. So maybe it's good to keep for consistency? I agree that it being plural makes it a bit more ambiguous since it can be one character, but two codepoints. I could make it singular, but don't really have any other suggestions to change the word "character" :) WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On a second thought, given that the macOS implementation has the same name, I'd like to keep the same name for consistency. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree it's a good idea to keep ourselves consistent across platforms, but we don't need to necessarily leak macOS concepts into Flutter APIs. To be honest, I'm still confused as to which meaning of 'character' this refers to and what that would look like in the presence of e.g. an IME, even after reading Apple's docs. I'd guess (but am not at all sure), that a keypress of the 'c' key when using a Japanese keyboard layout in 'romaji' input mode would emit 'c', but that in 'kana' input mode it might emit 'そ' (see my profile picture).
As noted above the word 'character' is really ambiguous and feels like a leftover from the old days where byte/grapheme/glyph/codepoint were regularly conflated, and APIs that don't communicate concepts precisely to users result in assumptions + bugs.
In any case, depending on what the behaviour is, I wonder if we can find a better name to expose than 'character'. @Hixie almost certainly has thoughts on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely don't use the word "character". It's a meaningless word.
cbracken
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a reply. I'd like @Hixie's thoughts on character-related terminology here.
|
I would use "codepoint" rather than "characterIgnoringModifier". It's not at all clear to me what "characterIgnoringModifier" means ("character" is meaningless, "modifier" means many things depending on context, "ignoring" is ambiguous, and even if you get past all of that, I'm convinced that it'd be wrong in many cases, e.g. presumably hitting a dead key on layouts with dead keys would be reported as only the "modifier", and hitting a non-dead key like the backquote on a US 101-key layout would be a "modifier" too). |
| if (charactersIgnoringModifiers.length == 2) { | ||
| final int secondCode = charactersIgnoringModifiers.codeUnitAt(1); | ||
| codeUnit = (codeUnit << 16) | secondCode; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this code at all.
It appears this code is untested. We should definitely test whatever this code is doing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Greg suggested this code as a safeguard against any really rare/custom keyboard that would send a unicode character with more than one code unit on a single key press. I can add a test and documentation to clarify this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what you mean by "unicode character" or "code unit" or how a keyboard could do this. Can you elaborate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By "unicode character" I meant the code point. By "code unit" I was referring to the UTF-16 representation of the code point. This check covers the case were charactersIgnoringModifiers is a high code point and so it would be a surrogate pair.
This check might be a bit paranoid since it's highly unlikely that a keyboard would do this, but wanted to cover it anyway given the possibility in the future. I can remove it if you think it just adds complexity and little value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's use the terms "Unicode Scalar Value" and "UTF-16 words" for those two terms, to maximally avoid ambiguity.
Regarding the code itself, IMHO we should either support arbitrary length strings, or we should only allow one Unicode scalar value, and if the latter, we must document this and assert it throughout (ideally by changing the API to not accept an arbitrary string at all, e.g. take a Unicode scalar value). We should definitely not have an API that looks like it takes an arbitrary string but doesn't.
FWIW, I think the code you have here is equivalent to just codeUnit = codePoint.runes.single.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By suggesting the addition of handling the extra scalar value in the macOS code, I was trying to handle the possible situation when a single keystroke returns multiple scalar values. The docs for macOS's charactersIgnoringModifiers aren't very clear about how many values could be stuffed into the field, but our id scheme doesn't allow for more than two scalars, since it's only 32 bits. Since we could accept up to two, I thought we should support up to the limit we could accept. We should assert throughout that it doesn't contain more than two, however (the reading code does assert already if it's more than two). Unfortunately, Dart doesn't have fixed-length arrays, or I'd also use one of those to indicate that the member can't have more than two characters, but we should document it. Using a packed int could work, however.
I'd rather not limit us to one value, since there may be some common cases (I'm thinking of combining characters here) where there are more than one, depending on how they handle those.
I'm not sure how clear the Linux docs are about this same situation, but if it's supplying a string, then it's probably similar enough to macOS to use a similar strategy.
And yes, it's equivalent to the first rune of the string (although I try and avoid working with runes, since it's such Dart specific terminology).
| 'keyCode': 0x04, | ||
| 'scanCode': 0x01, | ||
| 'codePoint': 0x10, | ||
| 'charactersIgnoringModifiers': 'a', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how are 0x10 and 'a' related?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume they're not -- this is test data. My assumption stems from the fact that a key-event for a tap on the 'a' key is something likely, whereas I'm not aware of any keyboard that'll emit keydown events for 0x10 (DLE: "data link escape" according to my local ASCII table).
That said, I'm sure that at some point such a keyboard has existed and I'd be very curious to know what the value would be according to Apple's APIs. 0x1b (ESC), on the other hand, seems like something we could easily test their APIs against to answer what their APIs currently return.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are not. This test only really cares about modifiers. The rest of the key data is there just to show all the fields in object. IIRC, 0x10 is just a random value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case, this entire change appears to have had no practical impact on the tests at all, which is quite concerning! We should make sure to test this code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This particular test is not affected by the codePoint since it's only testing for the modifiers. The tests for key translations are below.
I think the keyCode and codePoint from the previous patch were just random numbers but I changed them now to mimic an actual 'A' key press in all fields.
|
Comments addressed, PTAL! |
| 'codePoint': '', | ||
| }); | ||
| final RawKeyEventDataLinux data = shiftLeftKeyEvent.data; | ||
| expect(data.physicalKey, equals(PhysicalKeyboardKey.shiftLeft)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we still need to add tests for this new code. As far as I can tell the tests don't test the new code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test that checks for this code is on line 534 https://github.com/flutter/flutter/pull/34752/files#diff-1e6ff69b318c8b31b82b2086c0f8a12eR534. Is this what you are referring to?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I think he means it's not testing the "multiple code points" part of the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, totally missed that. Added test for handling 2 and 3 values
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is now an int, there's no need for these tests. Existing tests cover the creation of the logical key.
|
Comments addressed, PTAL! @Hixie. @gspencergoog |
| /// (GLFW, GTK, QT, etc) may have a different key code mapping. | ||
| final KeyHelper keyHelper; | ||
|
|
||
| /// The name of the printable key. This is typically the character that [keyCode] would |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't really a name. It's the set of Unicode scalar values generated by a single keystroke.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| /// The name of the printable key. This is typically the character that [keyCode] would | ||
| /// produce without any modifier keys. For dead keys, it is typically the diacritic | ||
| /// it would add to a character. Defaults to an empty string, asserted to be not null. | ||
| final String codePoint; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
codePoint isn't a great name, since it's a string (and could contain multiple "code points").
I'd call it something like unicodeScalarValuesProduced.
You could also just leave it as an int, and pack the first two scalars into it. Then it would represent exactly the amount of data we can accept. Definitely document it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed the name and documented that we handle two at most, but it's typically one value
| if (charactersIgnoringModifiers.length == 2) { | ||
| final int secondCode = charactersIgnoringModifiers.codeUnitAt(1); | ||
| codeUnit = (codeUnit << 16) | secondCode; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By suggesting the addition of handling the extra scalar value in the macOS code, I was trying to handle the possible situation when a single keystroke returns multiple scalar values. The docs for macOS's charactersIgnoringModifiers aren't very clear about how many values could be stuffed into the field, but our id scheme doesn't allow for more than two scalars, since it's only 32 bits. Since we could accept up to two, I thought we should support up to the limit we could accept. We should assert throughout that it doesn't contain more than two, however (the reading code does assert already if it's more than two). Unfortunately, Dart doesn't have fixed-length arrays, or I'd also use one of those to indicate that the member can't have more than two characters, but we should document it. Using a packed int could work, however.
I'd rather not limit us to one value, since there may be some common cases (I'm thinking of combining characters here) where there are more than one, depending on how they handle those.
I'm not sure how clear the Linux docs are about this same situation, but if it's supplying a string, then it's probably similar enough to macOS to use a similar strategy.
And yes, it's equivalent to the first rune of the string (although I try and avoid working with runes, since it's such Dart specific terminology).
| 'codePoint': '', | ||
| }); | ||
| final RawKeyEventDataLinux data = shiftLeftKeyEvent.data; | ||
| expect(data.physicalKey, equals(PhysicalKeyboardKey.shiftLeft)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I think he means it's not testing the "multiple code points" part of the code.
|
@gspencergoog ping |
| /// (GLFW, GTK, QT, etc) may have a different key code mapping. | ||
| final KeyHelper keyHelper; | ||
|
|
||
| /// A string with one or more unicode scalar values in a single keystroke. Two values are handled |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// A string with one or more unicode scalar values in a single keystroke. Two values are handled | |
| /// A string with one or more Unicode scalar values in a single keystroke. Two values are handled |
| /// at most. Otherwise, an assertion will be raised. This is typically the character | ||
| /// that [keyCode] would produce without any modifier keys. For dead keys, it is typically | ||
| /// the diacritic it would add to a character. Defaults to an empty string, asserted to be not null. | ||
| final String unicodeScalarValuesProduced; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be a 32-bit int that packs the first two values into it. It's all we can handle anyhow, and it would make it seem less like you can pack any number of values into it (which is one of Ian's concerns). I realize it would mean changing the engine, but then it definitely wouldn't give the impression that it can be an arbitrary-length string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to an int. Made the relevant changes on the engine repo as well flutter/engine#9386
|
@franciscojma86 Any plans to follow-up on the review? |
|
Just finished my rotation, working on this right now. Should be ready soon. |
| /// at most. Otherwise, an assertion will be raised. This is typically the character | ||
| /// that [keyCode] would produce without any modifier keys. For dead keys, it is typically | ||
| /// the diacritic it would add to a character. Defaults to 0, asserted to be not null. | ||
| final int unicodeScalarValuesProduced; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know, I'm the one that suggested it, but would it be better to just be unicodeScalarValues? Reading it now, I feel like the "Produced" is redundant: we know it's being produced.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| 'keyCode': 65, | ||
| 'scanCode': 0x00000026, | ||
| 'codePoint': 97, | ||
| 'unicodeScalarValuesProduced': 113, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also add back in some tests that test two and three scalar values encoded in the unicodeScalarValuesProduced member? (the "three" scenario should assert).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Co-Authored-By: Greg Spencer <[email protected]>
Co-Authored-By: Greg Spencer <[email protected]>
gspencergoog
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.

Description
Prefer using the unmodified unicode character obtained from GLFW, instead of a code point, which is obtained from a separate callback. This new logic now mimics the macOS implementation.
Part of #33395
Tests
Update raw_keyboard_test.dart
Checklist
Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes (
[x]). This will ensure a smooth and quick review process.///).flutter analyze --flutter-repo) does not report any problems on my PR.Breaking Change
Does your PR require Flutter developers to manually update their apps to accommodate your change?