Files
Jason Shirk a7af25aa37 Stop dispatching on ConsoleKeyInfo (#831)
There are likely issues introduced by this PR,
but I'm merging for a new Beta so this code gets more testing.

* Introduce PSKeyInfo type alias
* Explicit conversions b/w ConsoleKeyInfo and PSKeyInfo
* Change tests to use actual ConsoleKeyInfo data
* Add fr-FR keyboard layout tests
* Add KeyInfo-ru-RU-windows.json (#839)

All impossible without Shift shortcuts labeled as "Investigate: true"
Alt+Spacebar tracked by windows itself

* Add KeyInfo for Polish (Programmers) layout on Windows and Linux (#870)

On Windows *Alt+Spacebar* is hijacked by Windows
On Linux any *Alt+F1-12* combination was not detected
2019-03-20 16:14:05 -07:00

178 lines
5.4 KiB
PowerShell

[CmdletBinding()]
param($maxKeys = 25)
class KeyInfo
{
KeyInfo([string]$k, [ConsoleKeyInfo]$ki, [bool]$investigate)
{
$this.Key = $k
$this.KeyChar = $ki.KeyChar
$this.ConsoleKey = $ki.Key
$this.Modifiers = $ki.Modifiers
$this.Investigate = $investigate
}
[string]$Key
[string]$KeyChar
[string]$ConsoleKey
[string]$Modifiers
[bool]$Investigate
}
$quit = $false
function ReadOneKey {
param(
[string]$key,
[string]$prompt = 'Enter <{0}>'
)
function Test-ConsoleKeyInfos($k1, $k2, $key) {
if ($k1.Modifiers -ne $k2.Modifiers) {
# Some differences are OK
if ($key.Length -eq 1) {
# Don't care about Shift or Alt (because of AltGr), but
# Control should match
if ($k1.Modifiers -band [ConsoleModifiers]::Control -or
$k2.Modifiers -band [ConsoleModifiers]::Control) {
return $false
}
}
}
if ($k1.Key -ne $k2.Key) {
$keyOk = $false
switch -regex ($k1.Key,$k2.Key) {
'^Oem.*' { $keyOk = $true; break }
'^D[0-9]$' { $keyOk = $true; break }
'^NumPad[0-9]$' { $keyOk = $true; break }
}
if (!$keyOk) {
return $false
}
}
return $k1.KeyChar -eq $k2.KeyChar
}
$expectedKi = [Microsoft.PowerShell.ConsoleKeyChordConverter]::Convert($key)[0]
Write-Host -NoNewline ("`n${prompt}: " -f $key)
$ki = [Console]::ReadKey($true)
if ($ki.KeyChar -ceq 'Q') { $script:quit = $true; return }
$investigate = $false
$doubleChecks = 0
while (!(Test-ConsoleKeyInfos $ki $expectedKi $key)) {
$doubleChecks++
if ($doubleChecks -eq 1) {
Write-Host -NoNewline "`nDouble checking that last result, enter <${k}> again: "
} else {
Write-Host -NoNewline "`nLast result not confirmed, enter <Spacebar> to skip or enter <${k}> to try again: "
$investigate = $true
}
$kPrev = $ki
$ki = [Console]::ReadKey($true)
if ($ki.KeyChar -ceq 'Q') { $quit = $true; return }
if ($ki.Key -eq [ConsoleKey]::Spacebar) {
$ki = $kPrev
break
}
if (Test-ConsoleKeyInfos $ki $kPrev $key) {
break
}
}
return [KeyInfo]::new($key, $ki, $investigate)
}
$setConsoleInputMode = $false
try
{
if ($PSVersionTable.PSVersion.Major -lt 6 -or $IsWindows)
{
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class KeyInfoNativeMethods
{
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr GetStdHandle(int handleId);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool GetConsoleMode(IntPtr hConsoleOutput, out uint dwMode);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool SetConsoleMode(IntPtr hConsoleOutput, uint dwMode);
public static uint GetConsoleInputMode()
{
var handle = GetStdHandle(-10);
uint mode;
GetConsoleMode(handle, out mode);
return mode;
}
public static void SetConsoleInputMode(uint mode)
{
var handle = GetStdHandle(-10);
SetConsoleMode(handle, mode);
}
}
"@
[Flags()]
enum ConsoleModeInputFlags
{
ENABLE_PROCESSED_INPUT = 0x0001
ENABLE_LINE_INPUT = 0x0002
ENABLE_ECHO_INPUT = 0x0004
ENABLE_WINDOW_INPUT = 0x0008
ENABLE_MOUSE_INPUT = 0x0010
ENABLE_INSERT_MODE = 0x0020
ENABLE_QUICK_EDIT_MODE = 0x0040
ENABLE_EXTENDED_FLAGS = 0x0080
ENABLE_AUTO_POSITION = 0x0100
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0200
}
$prevMode = [KeyInfoNativeMethods]::GetConsoleInputMode()
$mode = $prevMode -band
-bnot ([ConsoleModeInputFlags]::ENABLE_PROCESSED_INPUT -bor
[ConsoleModeInputFlags]::ENABLE_LINE_INPUT -bor
[ConsoleModeInputFlags]::ENABLE_WINDOW_INPUT -bor
[ConsoleModeInputFlags]::ENABLE_MOUSE_INPUT)
Write-Verbose "Setting mode $mode"
[KeyInfoNativeMethods]::SetConsoleInputMode($mode)
$setConsoleInputMode = $true
}
else {
[Console]::TreatControlCAsInput = $true
}
$keyData = [System.Collections.Generic.List[KeyInfo]]::new()
$keys = Get-Content $PSScriptRoot\keydata.txt
$keys = $keys | Get-Random -Count $keys.Count
for ($i = 0; $i -lt $keys.Count; $i++) {
$k = $keys[$i]
if ($k -ceq 'Q') { continue }
$ki = ReadOneKey $k
if ($quit) { break }
$keyData.Add($ki)
}
}
finally
{
if ($setConsoleInputMode) {
[KeyInfoNativeMethods]::SetConsoleInputMode($prevMode)
}
else {
[Console]::TreatControlCAsInput = $false
}
$keyData | ConvertTo-Json | Out-File -Encoding ascii KeyInfo.json
}