Watch-Keyboard.ps1

function Watch-Keyboard
{
    <#
    .Synopsis
        Watches for Keyboard Input
    .Description
        Watches for Keyboard Input without blocking the script.
 
        When input arrives, it can be caught be any handler in -OnKey,
        and the results of that handler will be returned.
 
        If no input handler catches the key, it will be returned from Watch-Keyboard.
 
        If no keys are currently pressed, nothing will be returned.
    .Example
        # Watches the keys until you hit CTRL+C
        do { Watch-Keyboard | Select-Object -ExpandProperty Key } while ($true)
    .Link
        Watch-Game
    #>

    [OutputType([PSObject])]
    param(
    # A dictionary of key handlers.
    # The key should be the name of a key, and the value should be a script block.
    [Parameter(ValueFromPipelineByPropertyName)]
    [Collections.IDictionary]
    $OnKey,

    # The read key options.
    [Parameter(ValueFromPipelineByPropertyName)]
    [Alias('ReadKeyOptions')]
    [Management.Automation.Host.ReadKeyOptions]
    $ReadKeyOption = 'NoEcho,IncludeKeyDown,IncludeKeyUp'
    )

    process {
        :KeyLoop while ($Host.UI.RawUI.KeyAvailable) {
            #region Read the Keys
            $KeyRead = $Host.UI.RawUI.ReadKey($ReadKeyOption)
            $KeyReadAt = [DateTime]::Now
            if (-not $OnKey) {
                [PSCustomObject][Ordered]@{
                    PSTypeName='PowerArcade.Keypress'
                    Key = $KeyRead
                    TimeStamp = $KeyReadAt
                }
                continue
            }
            #endregion Read the Keys

            #region Deal with the Handlers
            :NextOnKey foreach ($kv in $(if ($OnKey) {$OnKey.GetEnumerator()})) {
                $key = "$($kv.Key)"
                if ($key.Contains('+'))
                {
                    # check for modifiers, and continue if not found
                    # if found, strip the modifiers from $key
                }
                $IsMatch =
                    ($key -eq 'Any') -or
                    ($key -eq 'All') -or
                    ($key -eq 'left' -and $KeyRead.virtualkeyCode -eq 37) -or
                    ($key -eq 'up' -and $KeyRead.virtualkeyCode -eq 38) -or
                    ($key -eq 'right' -and $KeyRead.virtualkeyCode -eq 39) -or
                    ($key -eq 'down' -and $KeyRead.virtualkeyCode -eq 40) -or
                    ($key -eq 'space' -and $keyRead.virtualKeyCode -eq 32) -or
                    (('esc', 'escape' -contains $key) -and $keyRead.virtualKeyCode -eq 27) -or
                    (('enter', 'return' -contains $key) -and $keyRead.VirtualKeyCode -eq 13) -or
                    (('back', 'backspace' -contains $key) -and $keyRead.VirtualKeyCode -eq 8) -or
                    ($KeyRead.character -eq $key)


                if ($IsMatch) {
                    if ($kv.Value -is [ScriptBlock]) {
                        . $kv.Value $keyRead
                    }
                    break KeyLoop
                }
            }
            #endregion Deal with the Handlers

            [PSCustomObject][Ordered]@{
                PSTypeName='PowerArcade.Keypress'
                Key = $KeyRead
                TimeStamp = $KeyReadAt
            }
        }
    }
}