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

Keyboard mapping: add mapped key string to raw key event #4447

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ else {
} else {
ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath }
}

$env:PATH="$DotNetDirectory;$env:PATH"
}

Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ protected virtual void OnMessage(IAvaloniaRemoteTransportConnection transport, o
InputRoot,
key.IsDown ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
(Key)key.Key,
key.MappedKey,
GetAvaloniaRawInputModifiers(key.Modifiers)));
}, DispatcherPriority.Input);
}
Expand Down
8 changes: 4 additions & 4 deletions src/Avalonia.Headless/HeadlessWindowImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -247,14 +247,14 @@ public IRef<IWriteableBitmapImpl> GetLastRenderedFrame()

public AcrylicPlatformCompensationLevels AcrylicCompensationLevels => new AcrylicPlatformCompensationLevels(1, 1, 1);

void IHeadlessWindow.KeyPress(Key key, RawInputModifiers modifiers)
void IHeadlessWindow.KeyPress(Key key, string mappedKey, RawInputModifiers modifiers)
{
Input?.Invoke(new RawKeyEventArgs(_keyboard, Timestamp, InputRoot, RawKeyEventType.KeyDown, key, modifiers));
Input?.Invoke(new RawKeyEventArgs(_keyboard, Timestamp, InputRoot, RawKeyEventType.KeyDown, key, mappedKey, modifiers));
}

void IHeadlessWindow.KeyRelease(Key key, RawInputModifiers modifiers)
void IHeadlessWindow.KeyRelease(Key key, string mappedKey, RawInputModifiers modifiers)
{
Input?.Invoke(new RawKeyEventArgs(_keyboard, Timestamp, InputRoot, RawKeyEventType.KeyUp, key, modifiers));
Input?.Invoke(new RawKeyEventArgs(_keyboard, Timestamp, InputRoot, RawKeyEventType.KeyUp, key, mappedKey, modifiers));
}

void IHeadlessWindow.MouseDown(Point point, int button, RawInputModifiers modifiers)
Expand Down
4 changes: 2 additions & 2 deletions src/Avalonia.Headless/IHeadlessWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ namespace Avalonia.Headless
public interface IHeadlessWindow
{
IRef<IWriteableBitmapImpl> GetLastRenderedFrame();
void KeyPress(Key key, RawInputModifiers modifiers);
void KeyRelease(Key key, RawInputModifiers modifiers);
void KeyPress(Key key, string mappedKey, RawInputModifiers modifiers);
void KeyRelease(Key key, string mappedKey, RawInputModifiers modifiers);
void MouseDown(Point point, int button, RawInputModifiers modifiers = RawInputModifiers.None);
void MouseMove(Point point, RawInputModifiers modifiers = RawInputModifiers.None);
void MouseUp(Point point, int button, RawInputModifiers modifiers = RawInputModifiers.None);
Expand Down
2 changes: 2 additions & 0 deletions src/Avalonia.Input/KeyEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public class KeyEventArgs : RoutedEventArgs

public Key Key { get; set; }

public string MappedKey { get; set; }

[Obsolete("Use KeyModifiers")]
public InputModifiers Modifiers => (InputModifiers)KeyModifiers;
public KeyModifiers KeyModifiers { get; set; }
Expand Down
1 change: 1 addition & 0 deletions src/Avalonia.Input/KeyboardDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public void ProcessRawEvent(RawInputEventArgs e)
RoutedEvent = routedEvent,
Device = this,
Key = keyInput.Key,
MappedKey = keyInput.MappedKey,
KeyModifiers = KeyModifiersUtils.ConvertToKey(keyInput.Modifiers),
Source = element,
};
Expand Down
15 changes: 14 additions & 1 deletion src/Avalonia.Input/Raw/RawKeyEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,29 @@ public RawKeyEventArgs(
ulong timestamp,
IInputRoot root,
RawKeyEventType type,
Key key, RawInputModifiers modifiers)
Key key,
string mappedKey, RawInputModifiers modifiers)
: base(device, timestamp, root)
{
Key = key;
MappedKey = mappedKey;
Type = type;
Modifiers = modifiers;
}

public RawKeyEventArgs(
IKeyboardDevice device,
ulong timestamp,
IInputRoot root,
RawKeyEventType type,
Key key,
RawInputModifiers modifiers)
: this(device, timestamp, root, type, key, null, modifiers) { }

public Key Key { get; set; }

public string MappedKey { get; set; }

public RawInputModifiers Modifiers { get; set; }

public RawKeyEventType Type { get; set; }
Expand Down
4 changes: 3 additions & 1 deletion src/Avalonia.Native/WindowImplBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,9 @@ public bool RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifie
{
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);

var args = new RawKeyEventArgs(_keyboard, timeStamp, _inputRoot, (RawKeyEventType)type, (Key)key, (RawInputModifiers)modifiers);
var args = new RawKeyEventArgs(_keyboard, timeStamp, _inputRoot, (RawKeyEventType)type,
(Key)key, null, // TODO mapped key
(RawInputModifiers)modifiers);

Input?.Invoke(args);

Expand Down
1 change: 1 addition & 0 deletions src/Avalonia.Remote.Protocol/InputMessages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public class KeyEventMessage : InputEventMessageBase
{
public bool IsDown { get; set; }
public Key Key { get; set; }
public string MappedKey { get; set; }
}

[AvaloniaRemoteMessageGuid("C174102E-7405-4594-916F-B10B8248A17D")]
Expand Down
33 changes: 18 additions & 15 deletions src/Avalonia.X11/X11Window.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;
Expand Down Expand Up @@ -511,25 +512,27 @@ void OnEventSync(XEvent ev)
&& key > X11Key.Num_Lock && key <= X11Key.KP_9)
key = (X11Key)XKeycodeToKeysym(_x11.Display, ev.KeyEvent.keycode, index ? 0 : 1).ToInt32();


var len = Xutf8LookupString(_xic, ref ev, buffer, 40, out _, out _);
string mappedKey = null;
if (len != 0)
{
mappedKey = Encoding.UTF8.GetString(buffer, len);
if (mappedKey.Length == 1)
{
if (mappedKey[0] < ' ' || mappedKey[0] == 0x7f) //Control codes or DEL
mappedKey = null;
}
}

ScheduleInput(new RawKeyEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(), _inputRoot,
ev.type == XEventName.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
X11KeyTransform.ConvertKey(key), TranslateModifiers(ev.KeyEvent.state)), ref ev);
X11KeyTransform.ConvertKey(key), mappedKey,
TranslateModifiers(ev.KeyEvent.state)), ref ev);

if (ev.type == XEventName.KeyPress)
if (ev.type == XEventName.KeyPress && mappedKey != null)
{
var len = Xutf8LookupString(_xic, ref ev, buffer, 40, out _, out _);
if (len != 0)
{
var text = Encoding.UTF8.GetString(buffer, len);
if (text.Length == 1)
{
if (text[0] < ' ' || text[0] == 0x7f) //Control codes or DEL
return;
}
ScheduleInput(new RawTextInputEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(), _inputRoot, text),
ref ev);
}
ScheduleInput(new RawTextInputEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(), _inputRoot, mappedKey),
ref ev);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,12 +214,12 @@ protected override void OnMouseWheel(MouseWheelEventArgs e) =>

protected override void OnKeyDown(KeyEventArgs e)
=> _ttl.Input?.Invoke(new RawKeyEventArgs(_keyboard, (uint) e.Timestamp, _inputRoot, RawKeyEventType.KeyDown,
(Key) e.Key,
(Key) e.Key, null, // TODO mapped key, should be WindowsKeyboardDevice.Instance.StringFromVirtualKey(vk)
GetModifiers(null)));

protected override void OnKeyUp(KeyEventArgs e)
=> _ttl.Input?.Invoke(new RawKeyEventArgs(_keyboard, (uint)e.Timestamp, _inputRoot, RawKeyEventType.KeyUp,
(Key)e.Key,
(Key) e.Key, null, // TODO mapped key
GetModifiers(null)));

protected override void OnTextInput(TextCompositionEventArgs e)
Expand Down
1 change: 1 addition & 0 deletions src/Windows/Avalonia.Win32/Input/KeyInterop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -436,5 +436,6 @@ public static int VirtualKeyFromKey(Key key)

return result;
}

}
}
16 changes: 9 additions & 7 deletions src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,14 @@ protected virtual unsafe IntPtr AppWndProc(IntPtr hWnd, uint msg, IntPtr wParam,
case WindowsMessage.WM_KEYDOWN:
case WindowsMessage.WM_SYSKEYDOWN:
{
e = new RawKeyEventArgs(
WindowsKeyboardDevice.Instance,
timestamp,
_owner,
RawKeyEventType.KeyDown,
KeyInterop.KeyFromVirtualKey(ToInt32(wParam), ToInt32(lParam)),
WindowsKeyboardDevice.Instance.Modifiers);
e = new RawKeyEventArgs(
WindowsKeyboardDevice.Instance,
timestamp,
_owner,
RawKeyEventType.KeyDown,
KeyInterop.KeyFromVirtualKey(ToInt32(wParam), ToInt32(lParam)),
WindowsKeyboardDevice.Instance.StringFromVirtualKey((uint)ToInt32(wParam)),
Copy link
Author

Choose a reason for hiding this comment

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

This needs to be corrected. StringFromVirtualKey takes ctrl and alt into consideration and will translate a key into a control sequence.

WindowsKeyboardDevice.Instance.Modifiers);
break;
}

Expand All @@ -128,6 +129,7 @@ protected virtual unsafe IntPtr AppWndProc(IntPtr hWnd, uint msg, IntPtr wParam,
_owner,
RawKeyEventType.KeyUp,
KeyInterop.KeyFromVirtualKey(ToInt32(wParam), ToInt32(lParam)),
WindowsKeyboardDevice.Instance.StringFromVirtualKey((uint)ToInt32(wParam)),
WindowsKeyboardDevice.Instance.Modifiers);
break;
}
Expand Down
3 changes: 2 additions & 1 deletion tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ public void Impl_Input_Should_Pass_Input_To_InputManager()
0,
target,
RawKeyEventType.KeyDown,
Key.A, RawInputModifiers.None);
Key.A, "a",
RawInputModifiers.None);
impl.Object.Input(input);

inputManagerMock.Verify(x => x.ProcessInput(input));
Expand Down
4 changes: 2 additions & 2 deletions tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public void Keypresses_Should_Be_Sent_To_Root_If_No_Focused_Element()
0,
root.Object,
RawKeyEventType.KeyDown,
Key.A,
Key.A, "a",
RawInputModifiers.None));

root.Verify(x => x.RaiseEvent(It.IsAny<KeyEventArgs>()));
Expand All @@ -43,7 +43,7 @@ public void Keypresses_Should_Be_Sent_To_Focused_Element()
0,
root,
RawKeyEventType.KeyDown,
Key.A,
Key.A, "a",
RawInputModifiers.None));

focused.Verify(x => x.RaiseEvent(It.IsAny<KeyEventArgs>()));
Expand Down