FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat(injection): Add keyboard input injection on Skia by MartinZikmund · Pull Request #23970 · unoplatform/uno · GitHub

feat(injection): Add keyboard input injection on Skia - #23970

Open
MartinZikmund wants to merge 10 commits into
masterfrom
dev/mazi/keyboardinjection
Open

feat(injection): Add keyboard input injection on Skia#23970
MartinZikmund wants to merge 10 commits into
masterfrom
dev/mazi/keyboardinjection

Conversation

MartinZikmund commented Aug 5, 2026
edited
Loading

Copy link
Copy Markdown
Member

GitHub Issue: closes #12962

PR Type:

✨ Feature

What changed? 🚀

InputInjector.InjectKeyboardInput was unimplemented on every Uno target — the generated stub threw. This implements it for all Skia targets.

The implementation joins the input pipeline at InputManager.KeyboardManager, the single chokepoint every Skia host already feeds through IUnoKeyboardInputSource. Injected keys therefore get focus routing, accelerators, Tab navigation and text input for free, with no per-host changes.

Keyboard injection targets the active window, mirroring the foreground-window behavior of the Windows implementation. The injector registration moves from the managed pointer manager into the InputManager constructor and now tracks every ContentRoot on the thread, so a second window is reachable.

Also included:

  • Held modifiers now reach injected pointer events. A modifier latched by an injected key-down is carried into subsequently injected pointer events, so Ctrl+Click behaves as it does for a user.
  • Unpaired surrogates no longer throw in Skia text layout. UnicodeText assumed every surrogate was part of a valid pair, so char.ConvertToUtf32 threw whenever a lone surrogate reached layout — which happens transiently whenever a surrogate pair arrives one code unit at a time, i.e. exactly how a pair is typed, injected, or committed by an IME. Unpaired surrogates now render as the replacement character.
  • A design note in specs/keyboard-injection-design.md covering the pipeline entry point, window targeting, and the documented differences from Windows (in-process only, no IME composition, invariant US layout unless Unicode is used, no automatic key repeat).

Non-Skia targets keep the previous behavior: the new IInputInjectorTarget keyboard members follow the existing pointer pattern (cross-platform explicit interface implementation delegating to a partial void that only the Skia file implements), so they compile to a no-op everywhere else.

PR Checklist ✅

github-actions Bot added area/skia ✏️ Categorizes an issue or PR as relevant to Skia kind/documentation labels Aug 5, 2026

github-actions Bot commented Aug 5, 2026
edited
Loading

Copy link
Copy Markdown
Contributor

Claude finished @MartinZikmund's task in 4m 25s —— View job


PR Review: feat(injection): Add keyboard input injection on Skia

Tasks:

  • Gather context and analyze PR structure
  • Read changed source files
  • Review InputInjector.cs (main logic)
  • Review InjectedInputKeyboardInfo.cs and InjectedInputKeyboardCharacterMap.cs
  • Review InputManager.Keyboard.skia.cs and InputManager.Keyboard.cs
  • Review IInputInjectorTarget.cs interface changes
  • Review runtime tests
  • Post inline comments
  • Write summary

Review Summary

Verdict: ship — the architecture is sound and the approach of joining at KeyboardManager.OnKey is correct. A handful of small issues below.

Inline findings posted (4 substantive, 1 informational):

Cross-cutting notes (no matching diff line):

  • IInputInjectorTarget.cs carries a pre-existing using System.Linq; import that is never used — remove it.
  • The InjectedInputKeyboardCharacterMap omits numpad operators (Add, Subtract, Multiply, Divide) from the shift-aware path (they correctly fall through to the unshifted switch arm), but consider a note explaining they are unaffected by shift for future readers.
  • Test coverage is excellent: 13 targeted tests covering character synthesis, modifier chaining, Caps Lock, Tab nav, Unicode/surrogate injection, KeyStatus fields, and the Ctrl+Click cross-device modifier test. The design doc in specs/ is a useful addition.

Copy link
Copy Markdown
Contributor

🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-23970/wasm-skia-net9/index.html

Copy link
Copy Markdown
Contributor

🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-23970/docs/index.html

Copy link
Copy Markdown
Contributor

The build 225873 found UI Test snapshots differences: skia-linux-screenshots: 57, skia-windows-screenshots: 2348, wasm: 11

Details
  • skia-linux-screenshots: 57 changed over 2348

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • Buttons.png-dark
    • Buttons.png
    • DropDownButtonPage.png-dark
    • DropDownButtonPage.png
    • ElementLevelTheme.png-dark
    • ElementLevelTheme.png
    • Gamepad_CurrentReading.png-dark
    • Gamepad_CurrentReading.png
    • Gamepad_Enumeration.png-dark
    • Gamepad_Enumeration.png
    • ButtonClippingTestsControl.png-dark
    • ButtonClippingTestsControl.png
    • ClipboardTests.png-dark
    • ContextRequested.png-dark
    • ContextRequested.png
    • ImageIconPage.png-dark
    • CalendarView_Theming.png-dark
    • CalendarView_Theming.png
    • DisplayInformation.png-dark
    • Examples.png-dark
  • skia-windows-screenshots: 2348 changed over 2394

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • AppBarButtonTest.png-dark
    • Accessibility_ScreenReader.png-dark
    • AppWindowFeatures.png-dark
    • AppWindowPresenters.png
    • AutoBorderStretchwithbottommargin.png-dark
    • AuthenticationBroker_Demo.png
    • AutomationProperties_Name.png-dark
    • AutomationProperties_Name.png
    • AutoSuggestBox_Description.png
    • AutoSuggestBox_Reason.png
    • BadgeNotificationTests.png
    • BasicAutoSuggestBox.png
    • BasicThemeResources.png
    • Battery.png-dark
    • BitmapIcon_Sizing.png-dark
    • BenchmarkDotNet.png
    • Border_AntiAlias.png
    • BitmapIcon_Sizing.png
    • Border_BorderThickness.png-dark
    • BitmapImage_vs_SvgImageSource.png-dark
  • wasm: 11 changed over 1076

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • SamplesApp.Windows_UI_Xaml_Controls.ListView.ListViewSelectedItems
    • UITests.Shared.Windows_UI_Xaml_Controls.MediaPlayerElement.MediaPlayerElement_Minimal
    • UITests.Microsoft_UI_Xaml_Controls.WebView2Tests.WebView2_NavigationProperties
    • UITests.Shared.Windows_UI_Xaml_Controls.MediaPlayerElement.MediaPlayerElement_Ogg_Extension
    • UITests.Windows_UI_Xaml_Media_Animation.ColorAnimation_Background
    • SamplesApp.Wasm.Windows_UI_Xaml_Controls.ListView.ListView_IsSelected
    • UITests.Shared.Microsoft_UI_Xaml_Controls.ExpanderTests.WinUIExpanderPage
    • Uno.UI.Samples.Content.UITests.WebView.WebView_AnchorNavigation
    • SamplesApp.Microsoft_UI_Xaml_Controls.WebView2Tests.WebView2_EnableDevTools
    • UITests.Windows_UI_Xaml_Controls.CalendarView.CalendarView_Theming
    • UITests.Uno_Web.Http.CookieManagerTests

Copy link
Copy Markdown
Contributor

⚠️⚠️ The build 225873 has failed on Uno.UI - CI.

MartinZikmund changed the base branch from master to feature/breakingchanges August 22, 2026 15:32
MartinZikmund force-pushed the dev/mazi/keyboardinjection branch from 749d749 to fbe3858 Compare August 22, 2026 15:33

github-actions Bot commented Aug 22, 2026
edited
Loading

Copy link
Copy Markdown
Contributor

Claude finished @MartinZikmund's task in 3m 43s —— View job


PR Review: feat(injection): Add keyboard input injection on Skia

Tasks:

  • Load MCP tools
  • Read changed source files
  • Review InputInjector.cs (main logic)
  • Review InjectedInputKeyboardInfo.cs and InjectedInputKeyboardCharacterMap.cs
  • Review InputManager.Keyboard.cs and IInputInjectorTarget.cs
  • Review runtime tests
  • Post inline comments
  • Write summary

Review Summary

Verdict: fix-first — the architecture and approach are sound; a small cluster of issues from the prior review were not addressed and need fixing before merge.

Inline findings (5 posted):

Severity Location Issue
🔴 Bug InjectedInputKeyboardInfo.cs:59 Wrong paramName in ArgumentException — nameof(InputInjector.InjectKeyboardInput) resolves to a method name, not a parameter name; ex.ParamName returns a misleading value. Should be "input". (flagged by prior review, not yet fixed)
🟡 Test Given_InputInjector_Keyboard.cs:317 No key-up events for either surrogate code unit in the emoji test — VK 255 stays permanently "down" in KeyboardStateTracker. (flagged by prior review, not yet fixed)
🟡 Style InputManager.Keyboard.cs:168 New code uses .LogWarning(...) (MEL extension) while the surrounding file uses this.Log().Error(...) (Uno convention). Should be .Warn(...). (flagged by prior review, not yet fixed)
🟡 Style InputManager.Keyboard.cs:52 Pre-existing copy-paste: error message says "PointerManager" — should read "KeyboardManager". Good opportunity to fix while this file is open.
🟢 Nit Given_InputInjector_Keyboard.cs:32 Assert.Inconclusive is discouraged by the runtime-test guidelines — add a comment explaining why it's justified here over [PlatformCondition] or [Ignore].
🟢 Nit IInputInjectorTarget.cs:16 Pre-existing using System.Linq; is unused in this pure interface — remove it.

What's correct:

  • The join-point at KeyboardManager.OnKey is the right place to inject — injected keys get focus routing, accelerators, Tab nav, and CharacterReceived for free.
  • InjectKeyboardInput validates the full batch before dispatching any key (preventing partial injection with latched modifiers).
  • ResolveKeyboardTarget correctly falls back to the injector's own target when no window is active.
  • The static _capsLock with process-wide semantics is clearly documented and the TryCreate()-per-call pattern justifies it.
  • InjectedInputKeyboardCharacterMap is a clean US-layout invariant map — the AltGr / Apple keyboard suppression logic is correct.
  • Test coverage is excellent: 13 targeted tests covering character synthesis, modifier chaining, Caps Lock, Tab nav, Unicode/surrogate injection, KeyStatus fields, and the Ctrl+Click cross-device modifier test.

Copy link
Copy Markdown
Contributor

🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-23970/wasm-skia-net9/index.html

MartinZikmund force-pushed the dev/mazi/keyboardinjection branch from fbe3858 to 7db720c Compare August 22, 2026 16:31

github-actions Bot commented Aug 22, 2026
edited
Loading

Copy link
Copy Markdown
Contributor

Claude finished @MartinZikmund's task in 4m 10s —— View job


PR Review: feat(injection): Add keyboard input injection on Skia

Tasks:

  • Load MCP tools
  • Read changed source files
  • Check if prior review findings were addressed
  • Post inline comments for remaining issues
  • Write summary

Review Summary

Verdict: fix-first — the two new commits (7db720cb refactor: drop dead #if HAS_INPUT_INJECTOR guard; 72aef409 fix: retarget DevTools input namespace for 7.0) are both clean and introduce no new issues. However, all four substantive findings from the prior reviews remain unaddressed.

Inline findings (4 posted):

Severity Location Issue
🔴 Bug InjectedInputKeyboardInfo.cs:59 Wrong paramName in ArgumentException — nameof(InputInjector.InjectKeyboardInput) resolves to a method name, not a parameter name; ex.ParamName returns "InjectKeyboardInput" instead of "input". (flagged by both prior reviews, still unfixed)
🟡 Test Given_InputInjector_Keyboard.cs:317 No key-up events for either surrogate code unit in the emoji test — VK 255 stays permanently "down" in KeyboardStateTracker. The é test was fixed to include a key-up; the emoji test was not. (flagged by both prior reviews, still unfixed)
🟡 Style InputManager.Keyboard.cs:168 New Inject() method uses .LogWarning(...) (MEL extension) while the rest of this file uses this.Log().Error(...) (Uno convention). Should be .Warn(...). (flagged by both prior reviews, still unfixed)
🟢 Nit Given_InputInjector_Keyboard.cs:32 Assert.Inconclusive is valid here but needs a brief comment explaining why [PlatformCondition] or [Ignore] cannot be used instead, per runtime-tests.md. (flagged by both prior reviews, still unfixed)

Cross-cutting notes (not in diff, cannot post inline):

  • InputManager.Keyboard.cs:52: pre-existing copy-paste — error message says "PointerManager" but should read "KeyboardManager". Good opportunity to fix while this file is open; both prior reviews noted it.
  • IInputInjectorTarget.cs:3: pre-existing using System.Linq; is unused in this pure interface — remove it.

What's correct in the new commits:

  • Removing the #if HAS_INPUT_INJECTOR guard is correct — the comment in the commit message accurately explains why it's dead weight on the 7.0 line.
  • Retargeting the DevTools input namespace is a clean mechanical fix that depends on refactor!: Give the legacy toolkit types their natural namespaces #24148.
  • Core architecture (join-point at KeyboardManager.OnKey, weak-reference target list, process-wide _capsLock, pre-validation of batches, ResolveKeyboardTarget active-window logic) remains sound.

Copy link
Copy Markdown
Contributor

The build 229579 found UI Test snapshots differences: skia-linux-screenshots: 73, skia-windows-screenshots: 123

Details
  • skia-linux-screenshots: 73 changed over 2366

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • DynamicFont.png
    • CalendarView_Theming.png-dark
    • ButtonClippingTestsControl.png-dark
    • Buttons.png
    • ButtonClippingTestsControl.png
    • Examples.png
    • Focus_FocusVisual_Properties.png-dark
    • ImageBrushInList.png-dark
    • ImageBrushInList.png
    • ElementLevelTheme.png-dark
    • ElementLevelTheme.png
    • ExpanderColorValidationPage.png-dark
    • ExpanderColorValidationPage.png
    • ImageIconPage.png-dark
    • ImageIconPage.png
    • ContextRequested.png-dark
    • Gamepad_CurrentReading.png-dark
    • Gamepad_CurrentReading.png
    • Gamepad_Enumeration.png-dark
    • ContentControl_Inheritance.png
  • skia-windows-screenshots: 123 changed over 2366

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • Attributed_text_FontSize_Changing.png
    • Gamepad_CurrentReading.png-dark
    • Gamepad_CurrentReading.png
    • Gamepad_Enumeration.png-dark
    • Gamepad_Enumeration.png
    • DataTransferManager.png-dark
    • DataTransferManager.png
    • EmailManagerTests.png-dark
    • EmailManagerTests.png
    • Contacts_Pick.png-dark
    • Contacts_Pick.png
    • CalendarView_Theming.png-dark
    • DropDownButtonPage.png-dark
    • ClipboardTests.png-dark
    • DisplayInformation.png-dark
    • ElementLevelTheme.png-dark
    • ElementLevelTheme.png
    • Haptics.VibrationDevice.png-dark
    • Haptics.VibrationDevice.png
    • ContentPresenter_Template.png-dark

MartinZikmund changed the base branch from feature/breakingchanges to master August 26, 2026 07:48
- ArgumentException now reports "input" as paramName instead of a method name.
- InjectKeyboardInputAsync validates the whole batch before dispatching, so the
  no-partial-delivery contract of the synchronous overload holds there too.
- Caps Lock toggling moves behind a static helper, keeping the static write out
  of the instance method.
- Logging uses this.Log().Warn(...), matching the rest of the file, and the
  KeyboardManager init error no longer says "PointerManager".
- The surrogate-pair test releases VK 255 instead of leaving it latched down.
- Documented why the XamlIsland skip uses Assert.Inconclusive.
- Dropped an unused using from IInputInjectorTarget.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QmZ63vuPQNDmg8H6nSGFSq

github-actions Bot commented Aug 26, 2026
edited
Loading

Copy link
Copy Markdown
Contributor

Claude finished @MartinZikmund's task in 1m 43s —— View job


PR Review: feat(injection): Add keyboard input injection on Skia

Tasks:

  • Load MCP tools
  • Read changed source files to verify reported fixes
  • Verify InjectedInputKeyboardInfo.cs — paramName fix
  • Verify InputManager.Keyboard.skia.cs — logging style + "PointerManager" copy-paste fix
  • Verify Given_InputInjector_Keyboard.cs — surrogate key-up fix + Assert.Inconclusive comment
  • Verify InputInjector.cs — ToggleCapsLock static helper + async pre-validation
  • Verify IInputInjectorTarget.cs — unused using removed
  • Look for any new issues
  • Write summary

Review Summary

Verdict: ship — all four substantive findings from the prior two review rounds are confirmed fixed. No new issues.

Verified fixes:

Finding File Status
Wrong paramName in ArgumentException InjectedInputKeyboardInfo.cs:59 ✅ Fixed — now "input"
InjectKeyboardInputAsync partial-delivery risk InputInjector.cs:366 ✅ Fixed — pre-validates full batch before dispatching
Surrogate pair test missing key-up events Given_InputInjector_Keyboard.cs:318 ✅ Fixed — key-ups added in reverse order
Assert.Inconclusive with no justifying comment Given_InputInjector_Keyboard.cs:31 ✅ Fixed — comment explains why [PlatformCondition] can't cover it
Logging style inconsistency (.LogWarning vs .Warn) InputManager.Keyboard.skia.cs:162 ✅ Fixed — this.Log().Warn(...)
"PointerManager" copy-paste in Init error InputManager.Keyboard.skia.cs:46 ✅ Fixed — "Failed to initialize the KeyboardManager"
Static field written from instance method InputInjector.cs:101 ✅ Fixed — ToggleCapsLock() static helper
Unused using System.Linq; in IInputInjectorTarget.cs IInputInjectorTarget.cs ✅ Fixed — removed

Cross-cutting notes (no new concerns):

  • InjectedInputKeyboardCharacterMap is clean — numpad operator constants fall through to the unshifted switch arm correctly, shift-character table for digit row is accurate.
  • InjectKeyboardInputAsync now matches the synchronous overload's pre-validation guarantee.
  • _capsLock process-wide semantics and ToggleCapsLock() encapsulation are correct — TryCreate() mints a new injector per call, so per-instance latching would silently reset.
  • The IsActive property in InputManager.Keyboard.cs is a clean null-propagation chain — correct.

Copy link
Copy Markdown
Contributor

🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-23970/docs/index.html

Copy link
Copy Markdown
Contributor

🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-23970/wasm-skia-net9/index.html

Copy link
Copy Markdown
Contributor

🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-23970/docs/index.html

Copy link
Copy Markdown
Contributor

The build 230295 found UI Test snapshots differences: skia-linux-screenshots: 57, skia-windows-screenshots: 2348, wasm: 11

Details
  • skia-linux-screenshots: 57 changed over 2348

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • DisplayInformation.png-dark
    • ButtonClippingTestsControl.png-dark
    • ButtonClippingTestsControl.png
    • CalendarView_Theming.png-dark
    • CalendarView_Theming.png
    • ElementLevelTheme.png-dark
    • ElementLevelTheme.png
    • ImageIconPage.png-dark
    • ClipboardTests.png-dark
    • Focus_FocusVisual_Properties.png-dark
    • Examples.png-dark
    • Examples.png
    • ExpanderColorValidationPage.png-dark
    • ExpanderColorValidationPage.png
    • Gamepad_CurrentReading.png-dark
    • Gamepad_CurrentReading.png
    • Gamepad_Enumeration.png-dark
    • Gamepad_Enumeration.png
    • DropDownButtonPage.png-dark
    • DropDownButtonPage.png
  • skia-windows-screenshots: 2348 changed over 2396

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • AutoSuggestBox_Description.png
    • AppBarButtonWithIconTest.png-dark
    • BasicAutoSuggestBox.png
    • AppWindowClosing.png-dark
    • Battery.png
    • AppWindowTitleBar Properties.png-dark
    • BezierSegment.png
    • AutoBorderStretchwithleftmargin.png-dark
    • BitmapImage_vs_SvgImageSource.png
    • AutomationProperties_AutomationId.png-dark
    • BorderLeftwithmargins.png
    • AutoSuggestBox_Icons.png-dark
    • Border_AntiAlias.png
    • BasicEnability.png
    • Border_CornerRadius_Clipping.png-dark
    • BitmapIcon_Foreground.png-dark
    • Border_CornerRadius_Gradient.png-dark
    • BorderRightwithmargins.png-dark
    • Border_CornerRadius_Gradient.png
    • BorderRightwithmargins.png
  • wasm: 11 changed over 1076

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • UITests.Shared.Windows_UI_Xaml_Controls.MediaPlayerElement.MediaPlayerElement_Minimal
    • SamplesApp.Windows_UI_Xaml_Controls.ListView.ListViewSelectedItems
    • UITests.Shared.Windows_UI_Xaml_Controls.MediaPlayerElement.MediaPlayerElement_Ogg_Extension
    • UITests.Windows_UI_Xaml_Controls.CalendarView.CalendarView_Theming
    • UITests.Shared.Microsoft_UI_Xaml_Controls.ExpanderTests.WinUIExpanderPage
    • UITests.Uno_Web.Http.CookieManagerTests
    • SamplesApp.Wasm.Windows_UI_Xaml_Controls.ListView.ListView_IsSelected
    • UITests.Windows_UI_Xaml_Media_Animation.ColorAnimation_Background
    • SamplesApp.Microsoft_UI_Xaml_Controls.WebView2Tests.WebView2_EnableDevTools
    • UITests.Microsoft_UI_Xaml_Controls.WebView2Tests.WebView2_NavigationProperties
    • Uno.UI.Samples.Content.UITests.WebView.WebView_AnchorNavigation

MartinZikmund marked this pull request as ready for review August 26, 2026 09:57
Copilot AI lite review requested due to automatic review settings August 26, 2026 09:57

github-actions Bot commented Aug 26, 2026
edited
Loading

Copy link
Copy Markdown
Contributor

Claude finished @MartinZikmund's task in 1m 26s —— View job


PR Review: feat(injection): Add keyboard input injection on Skia

Tasks:

  • Load MCP tools
  • Read changed source files
  • Check current state of all previously-flagged findings
  • Review for any new issues
  • Post inline comments (none needed)
  • Write summary

Review Summary

Verdict: ship — all findings from three prior review rounds are confirmed fixed in the current branch HEAD. No new issues found.

Verified fixes:

Finding Location Status
Wrong paramName in ArgumentException InjectedInputKeyboardInfo.cs:59 ✅ Fixed — now "input"
Static field written from instance method InputInjector.cs:101 ✅ Fixed — ToggleCapsLock() static helper
InjectKeyboardInputAsync partial-delivery risk InputInjector.cs:368–377 ✅ Fixed — pre-validates full batch before dispatching
Surrogate pair test missing key-up events Given_InputInjector_Keyboard.cs:318–323 ✅ Fixed — key-ups added in reverse order
Assert.Inconclusive with no justifying comment Given_InputInjector_Keyboard.cs:31–33 ✅ Fixed — comment explains why [PlatformCondition] can't cover it
Logging style inconsistency (.LogWarning vs .Warn) InputManager.Keyboard.skia.cs:162 ✅ Fixed — this.Log().Warn(...)
"PointerManager" copy-paste in Init error InputManager.Keyboard.skia.cs:46 ✅ Fixed — "Failed to initialize the KeyboardManager"
Unused using System.Linq; in IInputInjectorTarget.cs IInputInjectorTarget.cs ✅ Fixed — removed

What's correct (no new concerns):

  • InjectedInputKeyboardInfo.Validate passes "input" as paramName matching the public parameter of InjectKeyboardInput(IEnumerable<InjectedInputKeyboardInfo> input).
  • ToggleCapsLock() is correctly static — process-wide semantics are intentional since TryCreate() mints a new injector per call.
  • InjectKeyboardInputAsync now matches the synchronous overload's pre-validation guarantee.
  • ResolveKeyboardTarget() uses a single TryGetTarget(out var target) && target.IsActive check — correct and race-free.
  • InputManager.Keyboard.cs (cross-platform) correctly delegates via partial void InjectKeyDown/InjectKeyUp, with only the Skia file providing the implementation.
  • 13 runtime tests covering: character synthesis, modifier chaining, Caps Lock, Tab nav, Unicode/surrogate injection, KeyStatus fields, pre-dispatch validation, and the Ctrl+Click cross-device modifier test.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Pull request overview

Adds Skia-only support for Windows.UI.Input.Preview.Injection.InputInjector.InjectKeyboardInput by routing injected key events through InputManager.KeyboardManager, enabling focus routing, accelerators, navigation, and text input, while also fixing Skia text layout to tolerate unpaired Unicode surrogates.

Changes:

  • Implement Skia keyboard injection end-to-end (new InjectedInputKeyboardInfo, character synthesis, active-window targeting via IInputInjectorTarget).
  • Propagate tracked keyboard modifiers into injected pointer events to enable composed gestures (e.g., Ctrl+Click).
  • Harden Skia text layout against lone surrogate code units and add runtime tests + design note.

Reviewed changes

Copilot reviewed 12 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/Uno.UWP/UI/Input/Preview.Injection/InputInjector.cs Tracks per-thread injector targets, implements keyboard injection, and forwards tracked modifiers into pointer injection.
src/Uno.UWP/UI/Input/Preview.Injection/InjectedInputKeyboardInfo.cs Adds real InjectedInputKeyboardInfo implementation and conversion to KeyEventArgs.
src/Uno.UWP/UI/Input/Preview.Injection/InjectedInputKeyboardCharacterMap.cs Provides invariant-US character synthesis for non-Unicode injected keys.
src/Uno.UWP/UI/Input/Preview.Injection/IInputInjectorTarget.cs Extends injector target contract with keyboard injection methods and activation state.
src/Uno.UWP/Generated/3.0.0.0/Windows.UI.Input.Preview.Injection/InputInjector.cs Generated sync update to avoid emitting a stub for an already-implemented method.
src/Uno.UWP/Generated/3.0.0.0/Windows.UI.Input.Preview.Injection/InjectedInputKeyboardInfo.cs Generated sync update to avoid emitting stubs for properties now implemented manually.
src/Uno.UI/UI/Xaml/Internal/InputManager.Pointers.Managed.cs Removes injector registration from pointer-manager construction (registration moved to InputManager ctor).
src/Uno.UI/UI/Xaml/Internal/InputManager.Keyboard.skia.cs Adds injection entry point into KeyboardManager and fixes a log message label.
src/Uno.UI/UI/Xaml/Internal/InputManager.Keyboard.cs Implements new IInputInjectorTarget keyboard members + activation targeting on InputManager.
src/Uno.UI/UI/Xaml/Internal/InputManager.cs Registers InputInjector target from InputManager constructor for Skia injector support.
src/Uno.UI/UI/Xaml/Documents/UnicodeText.skia.cs Prevents char.ConvertToUtf32 from throwing on unpaired surrogates during layout.
src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Documents/Given_UnicodeText.skia.cs Adds coverage ensuring unpaired surrogates don’t crash Skia layout paths.
src/Uno.UI.RuntimeTests/Tests/Windows_UI_Input_Preview_Injection/Given_InputInjector_Keyboard.cs Adds Skia runtime tests for injected keyboard behavior (text, accelerators, Tab navigation, Unicode, modifiers).
specs/keyboard-injection-design.md Documents measured WinUI behavior, Skia design/limitations, and test plan.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Copy link
Copy Markdown
Contributor

🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-23970/docs/index.html

Copy link
Copy Markdown
Contributor

🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-23970/wasm-skia-net9/index.html

Copy link
Copy Markdown
Contributor

The build 230310 found UI Test snapshots differences: skia-linux-screenshots: 57, skia-windows-screenshots: 2348, wasm: 11

Details
  • skia-linux-screenshots: 57 changed over 2348

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • DropDownButtonPage.png-dark
    • DropDownButtonPage.png
    • Examples.png-dark
    • Examples.png
    • ExpanderColorValidationPage.png-dark
    • ExpanderColorValidationPage.png
    • Focus_FocusVisual_Properties.png-dark
    • Buttons.png-dark
    • Buttons.png
    • ButtonClippingTestsControl.png-dark
    • ButtonClippingTestsControl.png
    • Gamepad_CurrentReading.png-dark
    • Gamepad_CurrentReading.png
    • Gamepad_Enumeration.png-dark
    • Gamepad_Enumeration.png
    • ImageIconPage.png-dark
    • ClipboardTests.png-dark
    • ElementLevelTheme.png-dark
    • ElementLevelTheme.png
    • ContextRequested.png-dark
  • skia-windows-screenshots: 2348 changed over 2396

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • AppBarToggleButtonTest.png
    • AppWindowFeatures.png-dark
    • AppWindowPositionAndSize.png-dark
    • Arrange_Performance01.png-dark
    • AutoBorderStretchwithbottommargin.png
    • AutomationProperties_Name.png
    • AutoSuggestBox_Reason.png-dark
    • BasicAutoSuggestBox.png-dark
    • BasicThemeResources.png
    • Battery.png-dark
    • BorderBottomwithmargins.png-dark
    • BorderBottomwithmargins.png
    • BorderVersusPanelClipping.png-dark
    • BorderVersusPanelClipping.png
    • Border_CornerRadius_Binding.png
    • Border_CornerRadius_BorderThickness.png-dark
    • Border_Simple_No_Background_With_TextBox.png
    • Border_Simple_with_non_Uniform_Thickness.png-dark
    • BreadcrumbBarPage.png-dark
    • BreadcrumbBarPage.png
  • wasm: 11 changed over 1076

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • UITests.Shared.Windows_UI_Xaml_Controls.MediaPlayerElement.MediaPlayerElement_Minimal
    • UITests.Windows_UI_Xaml_Controls.CalendarView.CalendarView_Theming
    • UITests.Microsoft_UI_Xaml_Controls.WebView2Tests.WebView2_NavigationProperties
    • UITests.Shared.Windows_UI_Xaml_Controls.MediaPlayerElement.MediaPlayerElement_Ogg_Extension
    • SamplesApp.Wasm.Windows_UI_Xaml_Controls.ListView.ListView_IsSelected
    • SamplesApp.Windows_UI_Xaml_Controls.ListView.ListViewSelectedItems
    • UITests.Shared.Microsoft_UI_Xaml_Controls.ExpanderTests.WinUIExpanderPage
    • UITests.Uno_Web.Http.CookieManagerTests
    • Uno.UI.Samples.Content.UITests.WebView.WebView_AnchorNavigation
    • SamplesApp.Microsoft_UI_Xaml_Controls.WebView2Tests.WebView2_EnableDevTools
    • UITests.Windows_UI_Xaml_Media_Animation.ColorAnimation_Background

Copy link
Copy Markdown
Contributor

⚠️⚠️ The build 230310 has failed on Uno.UI - CI.

MartinZikmund and others added 3 commits August 26, 2026 16:30
Real input reads the OS key state after the transition is applied, so a
Shift key-down already reports Shift. The tracker is only updated once the
routed event reaches UIElement, so injected args reported stale modifiers.

Also resolves the Unicode key (255) before querying WasKeyDown, which was
previously looked up against VirtualKey 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012PF59BzvEyASdeFtsFHrox
Select-all is Cmd+A on Apple keyboards, so the hardcoded Ctrl+A failed on
macOS and iOS.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012PF59BzvEyASdeFtsFHrox
An unpaired surrogate in a test name or assertion message made the whole
NUnit results document unwritable on WebAssembly, where it is POSTed to a
server that encodes as UTF-16. The shard then aborted with "Root element is
missing" and no diagnostics.

Escape non-XML characters when generating the document, and stop passing
lone surrogates as data-row values.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012PF59BzvEyASdeFtsFHrox
Copilot AI review requested due to automatic review settings August 26, 2026 14:32
github-actions Bot added the area/automation Categorizes an issue or PR as relevant to project automation label Aug 26, 2026

github-actions Bot commented Aug 26, 2026
edited
Loading

Copy link
Copy Markdown
Contributor

Claude finished @MartinZikmund's task in 1m 7s —— View job


PR Review: feat(injection): Add keyboard input injection on Skia

Tasks:

  • Load MCP tools
  • Read current state of InputInjector.cs and InjectedInputKeyboardInfo.cs
  • Evaluate Copilot's two new findings from the latest build
  • Post inline comments for any confirmed issues
  • Write summary

Review Summary

Verdict: ship — both new Copilot findings from the latest build are false positives. The issues they describe were already addressed in the current code.

Copilot finding 1 — stale modifiers / wrong wasKeyDown key for Unicode (line 361): Already fixed. InjectKeyboardInputCore now uses info.EffectiveVirtualKey (which returns 255 for Unicode keys, not 0), and manually folds the current key transition into modifiers via GetModifierFlag(key) before building the args. The comment in the code explains this exactly:

// Real input reads the OS key state after the transition has been applied, so pressing Shift
// already reports Shift. The tracker is only updated once the routed event reaches UIElement,
// so fold the current transition in by hand to keep injected args identical to real ones.
var modifiers = GetTrackedModifiers();
if (GetModifierFlag(key) is { } flag)
{
    modifiers = info.IsKeyUp ? modifiers & ~flag : modifiers | flag;
}
var wasKeyDown = KeyboardStateTracker.GetKeyState(key).HasFlag(CoreVirtualKeyStates.Down);

Copilot finding 2 — WasKeyDown always true for key-up (line 93): Intentional Win32 parity. The code comment explains the design: "Win32 defines the previous-key-state bit as always 1 for WM_KEYUP, so a key-up reports WasKeyDown even when no key-down preceded it." WM_KEYUP always has bit 30 of lParam set to 1 by the OS; Copilot's suggestion to pass wasKeyDown directly would break that parity.

All findings from the three prior review rounds remain confirmed fixed:

  • paramName in ArgumentException → "input" ✅
  • ToggleCapsLock() static helper ✅
  • InjectKeyboardInputAsync pre-validation ✅
  • Surrogate pair test missing key-ups ✅
  • Assert.Inconclusive justification comment ✅
  • Logging style this.Log().Warn(...) ✅
  • "PointerManager" copy-paste in Init error ✅
  • Unused using System.Linq; removed ✅

No new inline comments needed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Pull request overview

Copilot reviewed 13 out of 15 changed files in this pull request and generated no new comments.

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

src/Uno.UWP/UI/Input/Preview.Injection/InputInjector.cs:58

  • GetFirstTarget() scans the thread-static WeakReference list but never removes dead entries. Over time (open/close windows), this can leave the list full of dead WeakReferences, making TryCreate() increasingly expensive and keeping the stale list around indefinitely until another registration happens.

Consider pruning dead entries while scanning, similarly to SetTargetForCurrentThread().

	private static IInputInjectorTarget? GetFirstTarget()
	{
		if (_inputManagers is { } managers)
		{
			foreach (var weak in managers)
			{
				if (weak.TryGetTarget(out var target))
				{
					return target;
				}
			}
		}

		return null;
	}

Copy link
Copy Markdown
Contributor

🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-23970/wasm-skia-net9/index.html

Copy link
Copy Markdown
Contributor

🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-23970/docs/index.html

Copy link
Copy Markdown
Contributor

The build 230356 found UI Test snapshots differences: skia-linux-screenshots: 57, skia-windows-screenshots: 2348, wasm: 11

Details
  • skia-linux-screenshots: 57 changed over 2348

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • ButtonClippingTestsControl.png-dark
    • ButtonClippingTestsControl.png
    • ClipboardTests.png-dark
    • DisplayInformation.png-dark
    • CalendarView_Theming.png-dark
    • CalendarView_Theming.png
    • DropDownButtonPage.png-dark
    • DropDownButtonPage.png
    • Focus_FocusVisual_Properties.png-dark
    • Buttons.png-dark
    • Buttons.png
    • ContextRequested.png-dark
    • ContextRequested.png
    • ElementLevelTheme.png-dark
    • ElementLevelTheme.png
    • ImageIconPage.png-dark
    • Examples.png-dark
    • Examples.png
    • ExpanderColorValidationPage.png-dark
    • ExpanderColorValidationPage.png
  • skia-windows-screenshots: 2348 changed over 2396

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • Accessibility_ScreenReader.png-dark
    • AndroidWindowInsets.png
    • AnimatedIconPage.png
    • AppBarButtonWithIconTest.png-dark
    • AppBar_KeyBoard.png
    • AppWindowTitleBar Properties.png-dark
    • ApplicationViewSizing.png
    • ApplicationViewSizing.png-dark
    • AutoBorderStretchwithbottommargin.png-dark
    • AppWindowFeatures.png
    • Arrange_Performance01.png-dark
    • AutoBorderStretchwithtopmargin.png
    • AppWindowTitleBar Properties.png
    • AutomationProperties_AutomationId.png-dark
    • AppXamlDefinedResources.png
    • Attributed_text_Supserscript.png
    • AutoSuggestBox_BitmapIcon.png
    • Attributed_text_Simple.png
    • AutoSuggestBox_SoftKeboard.png-dark
    • AuthenticationBroker_Demo.png
  • wasm: 11 changed over 1076

    🚨🚨 Comparison Details (first 20) 🚨🚨
    • UITests.Windows_UI_Xaml_Media_Animation.ColorAnimation_Background
    • UITests.Microsoft_UI_Xaml_Controls.WebView2Tests.WebView2_NavigationProperties
    • UITests.Shared.Windows_UI_Xaml_Controls.MediaPlayerElement.MediaPlayerElement_Minimal
    • UITests.Shared.Windows_UI_Xaml_Controls.MediaPlayerElement.MediaPlayerElement_Ogg_Extension
    • UITests.Windows_UI_Xaml_Controls.CalendarView.CalendarView_Theming
    • SamplesApp.Windows_UI_Xaml_Controls.ListView.ListViewSelectedItems
    • SamplesApp.Wasm.Windows_UI_Xaml_Controls.ListView.ListView_IsSelected
    • SamplesApp.Microsoft_UI_Xaml_Controls.WebView2Tests.WebView2_EnableDevTools
    • UITests.Shared.Microsoft_UI_Xaml_Controls.ExpanderTests.WinUIExpanderPage
    • UITests.Uno_Web.Http.CookieManagerTests
    • Uno.UI.Samples.Content.UITests.WebView.WebView_AnchorNavigation

/// </remarks>
internal static class InjectedInputKeyboardCharacterMap
{
// VK codes without a Windows.System.VirtualKey name.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality
Suggested change
// VK codes without a Windows.System.VirtualKey name.
// VK codes without a Windows.System.VirtualKey name: // value (without/with)-Shift key

Comment on lines +100 to +110
VkOem1 => shift ? ':' : ';',
VkOemPlus => shift ? '+' : '=',
VkOemComma => shift ? '<' : ',',
VkOemMinus => shift ? '_' : '-',
VkOemPeriod => shift ? '>' : '.',
VkOem2 => shift ? '?' : '/',
VkOem3 => shift ? '~' : '`',
VkOem4 => shift ? '{' : '[',
VkOem5 => shift ? '|' : '\\',
VkOem6 => shift ? '}' : ']',
VkOem7 => shift ? '"' : '\'',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

consider using !shift and swap the sides?

}
}

// TODO: Move as extension method

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

todo?

modifiers |= VirtualKeyModifiers.Menu;
}

if (IsDown(VirtualKey.LeftWindows) || IsDown(VirtualKey.RightWindows))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

why only win-key has this left/right shenanigans, but not the rest/above?

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/automation Categorizes an issue or PR as relevant to project automation area/skia ✏️ Categorizes an issue or PR as relevant to Skia kind/documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add keyboard support for InputInjector

5 participants


Back | FazBrowse Home | New Git URL