[Script] KeypressOSD - Display key press on screen

Post your working scripts, libraries and tools
robodesign
Posts: 371
Joined: 30 Sep 2017, 03:59
Facebook: marius.sucan
GitHub: mariussucan
Location: Romania
Contact:

Re: [Script] KeypressOSD - Display key press on screen

Post by robodesign » 04 Oct 2017, 14:44

Hello, ppl!

Another new version:

; CHANGELOG:
; by Marius Sucan
; v2.72 (2017-10-04)
; - new option: make beeps on mouse clicks
; - new option: distinct beeper for modifier keys: Shift, Ctrl, Alt, WinKey.
; - new option: beep hidden keys; if you want it to beep even if the keys are not displayed
; - fix: now it always counts mouse clicks and "wheeling"

Script also at: http://p.ahkscript.org/?p=867f287b

Code: Select all

; KeypressOSD.ahk
;--------------------------------------------------------------------------------------------------------------------------
;
; AVAILABLE SHORTCUTS:
; Ctrl+Alt+Shift+F8  - toggles "Show single key" option. Useful when you must reliably use dead keys.
; Ctrl+Alt+Shift+F9  - toggles between two OSD offsets: GuiYa and GuiYb
; Ctrl+Alt+Shift+F12 - Reinitialize OSD. Useful when it no longer appears on top. To have it appear on top of elevated apps, run it in administrator mode.
;
; FEATURES:
; - Show previously pressed key if fired quickly
; - Count key presses or key fires and mouse clicks
; - Automatic resizing of OSD/HUD or fixed size
; - Generate beeps for key presses, modifiers, mouse clicks or just when typing with Capslock
; - Indicators for CapsLock, NumLock and ScrollLock states
; - Typing mode; shows what you are typing in an expanding text area
; - Dead keys support (partial), option to turn it off
; - Easy to configure with many options:
;   - to toggle features: key beepers, key counting or previous key
;   - to hide modifiers, mouse clicks or single key presses (which disables typing mode)
;   - or hide keys that usually get in the way: Left Click and Print Screen [HideAnnoyingKeys]
;   - differ between left and right modifiers
;   - OSD/HUD position, size and display time
;   - beep key presses even if keys are not displayed
;
; CHANGELOG:
; by Marius Sucan (robodesign.ro)
;             v2.72 (2017-10-04) - new option: make beeps on mouse clicks
;                                - new option: beeper for modifier keys: Shift, Ctrl, Alt, WinKey.
;                                - new option: beep hidden keys; if you want it to beep even if the keys are not displayed
;                                - fix: now it always counts mouse clicks
;             v2.70 (2017-10-03) - added option to toggle dead keys support / work-around.
;                                - added option to hide annoying keys that usually get in the way: Left Click and Print Screen.
;                                - redraw improvements, reduced flickering
;                                - disabled OSD transparency to reduce flickering
;             v2.69 (2017-09-30) - OSD / GUI hides when mouse is over it. Many thanks to phaleth!
;             v2.68 (2017-09-29) - numpad keys now work in typing mode as expected; they appear as symbols or numbers
;
; by phaleth from irc.freenode.net #ahk
;             v2.67 (2017-09-28) - dead keys improvements
;
; by Marius Sucan (robodesign.ro)
;             v2.66 (2017-09-28) - key combinations with Shift work better.
;             v2.65 (2017-09-27) - Fixed a bug with counting modifier keys;
;                                - improved the dead keys work-around
;                                - friendly names for mouse clicks
;                                - when pressed, volume keys  always generate beeps
;                                - added option to differentiate between left and right modifiers
;                                - now it detects AltGr key
;                                - the key beeper now also makes a beep for modifiers.
;                                - capslock no longer erases text you are typing displayed by the OSD
;                                - now you can toggle between two different OSD positions with Ctrl + Alt + Shift + F9
;             v2.60 (2017-09-26) - Fixed many bugs with counting keys;
;                                - reimplemented the feature to see the previous key combination, if quickly a new one is pressed;
;                                - added options/settings to toggle previous keys, counting keys and delay;
;                                - added shortcuts to toggle ShowSingleKey option and to reinitialize the OSD;
;                                - added option for automatic resizing of the OSD; it can be turned off in the settings section; it is a fishy implementation, but if one adjusts it, can make it to suit personal needs;
;                                - new option: beep when key is released or when writing with capslock
;             v2.58 (2017-09-23) - Numpad keys have friendly naming, based on the numlock state.
;                                - Combinations with space and backspace work again.
;             v2.56 (2017-09-22) - more fixes for space usage and key combinations;
;                                - now it indicates when ScrollLock, NumLock and Capslock are activated.
;             v2.55 (2017-09-21) - minor fixes for space usage and key combinations.
;
; by Saiapatsu from irc.freenode.net #ahk
;             v2.54 (2017-09-21) - Scrolls through n recently typed characters instead of just the latest word
;             v2.53 (2017-09-21) - Case change effect limited to the loop 95 letters only.
;             v2.52 (2017-09-21) - Now supports backspace. Commented out CapsLock beeper.
;             v2.51 (2017-09-21) - Changed labels to functions, added ToolWindow style to window, changed DisplayTime
;                                  calculation, made it show last word typed, hid spacebar presses
;             todo: make Shift look less ugly
;
; by Marius Sucan (robodesign.ro)
;             v2.50 (2017-09-20) - Changed the OSD positioning and sizing. It was based on the current window. Now it is always fixed in a specific place. Added a Capslock beeper.
;
; by tmplinshi from https://autohotkey.com/boards/viewtopic.php?f=6&t=225
;             v2.22 (2017-02-25) - Now pressing same combination keys continuously more than 2 times,
;                                  for example press Ctrl+V 3 times, will displayed as "Ctrl + v (3)"
;             v2.21 (2017-02-24) - Fixed LWin/RWin not poping up start menu
;             v2.20 (2017-02-24) - Added displaying continuous-pressed combination keys.
;                                  e.g.: With CTRL key held down, pressing K and U continuously will shown as "Ctrl + k, u"
;             v2.10 (2017-01-22) - Added ShowStickyModKeyCount option
;             v2.09 (2017-01-22) - Added ShowModifierKeyCount option
;             v2.08 (2017-01-19) - Fixed a bug
;             v2.07 (2017-01-19) - Added ShowSingleModifierKey option (default is True)
;             v2.06 (2016-11-23) - Added more keys. Thanks to SashaChernykh.
;             v2.05 (2016-10-01) - Fixed not detecting "Ctrl + ScrollLock/NumLock/Pause". Thanks to lexikos.
;             v2.04 (2016-10-01) - Added NumpadDot and AppsKey
;             v2.03 (2016-09-17) - Added displaying "Double-Click" of the left mouse button.
;             v2.02 (2016-09-16) - Added displaying mouse button, and 3 settings (ShowMouseButton, FontSize, GuiHeight)
;             v2.01 (2016-09-11) - Display non english keyboard layout characters when combine with modifer keys.
;             v2.00 (2016-09-01) - Removed the "Fade out" effect because of its buggy.
;                                - Added support for non english keyboard layout.
;                                - Added GuiPosition setting.
;             v1.00 (2013-10-11) - First release by tmplinshi based on RaptorX. Function keys, numpad keys and mouse clicks support. Popups at mouse position.
;             v0.50 (2010-03-18) - Released by RaptorX.
;--------------------------------------------------------------------------------------------------------------------------
;
; TO-DO:::::::::::::::::::::::::::::::::::
; features to implement:
; - support for non-english keyboards
; - when user types single letters, display Shift + (sign or numeric keys) as signs/symbols, eg. Shift + 0, should be displayed as );
; - after pressing Shift + [letter] if the following key is a single letter, remember the first Shift + [letter];
; - after a Numpad key is pressed, typing mode should begin if the second one is another numpad key;
; - show a generic symbol for ignored dead keys; helps to clarify one was pressed;
;
; glitches to fix [by priority]:
; - [regression since v2.52] show Shift as a modifier, as Ctrl and Alt; Shift should behave as the other two; it is never displayed alone and countable;
; - once a key is pressed, counting of key fires is not initiated after the OSD was hidden for a little awhile;
; - make dead keys always work; with the current implementation, the special keys show up seemingly random in the OSD and sometimes in the text field the user types in;
;   - if dead keys support / work-around enabled, once you typed a caret [^], sometimes user gets randomly inserted carets when typing;
; - automatic resizing of the OSD/GUI is just a silly hack based on the default font size and the number of typed chars; it often fails to resize properly;
; - redraw issues; it still flickers;
; - make it work reliably with sticky keys; if user presses once Ctrl and another key afterwards, it rarely detects the combination on Winndows 7;
;----------------------------------------------------------------------------

; Initialization
#SingleInstance force
#NoEnv
#MaxHotkeysPerInterval 500
SetBatchLines, -1
ListLines, Off

; Settings.
    global DeadKeys              := 1     ; a toggle for a partial dead keys support. Zero [0] means no dead keys. See CreateHotkey() and char2skip to define the dead keys.
    global ShowSingleKey         := 1     ; show only key combinations
    global ShowMouseButton       := 1     ; if disabled, mouse click beeps will never occur
    global HideAnnoyingKeys      := 1     ; Left click and PrintScreen can easily get in the way.
    global ShowSingleModifierKey := 1     ; make it display Ctrl, Alt, Shift when pressed alone
    global DifferModifiers       := 0     ; differentiate between left and right modifiers
    global ShowPrevKey           := 1     ; show previously pressed key, if pressed quickly in succession
    global ShowPrevKeyDelay      := 300
    global ShowKeyCount          := 1     ; count how many times a key is pressed
    global ShowKeyCountFired     := 1     ; show only key presses (0) or catch key fires as well (1)
    global DisplayTime           := 3000  ; in milliseconds
    global GuiWidth              := 360
    global GuiHeight             := 50
    global GuiX                  := 50
    global GuiYa                 := 250   ; toggle between GuiYa and GuiYb with Ctrl + Alt + Shift + F9
    global GuiYb                 := 800
    global FontSize              := 19
    global OSDautosize           := 1     ; make adjustments in ShowHotkey() to match your font size
    global NumLetters            := 25    ; amount of recently typed letters to display
    global NumLettersAutosize    := 60    ; ...when OSD resizes automatically
    global CapslockBeeper        := 1     ; only when the key is released
    global KeyBeeper             := 0     ; only when the key is released
    global ModBeeper             := 0     ; beeps for every modifier, when released
    global MouseBeeper           := 0
    global BeepHiddenKeys        := 0     ; [when any beeper enabled] to beep or not when keys are not displayed by OSD/HUD
;   global TransparencyLevel     := 220   ; OSD transparency; range from 0 to 255. feature disabled, to reduce flickering

; Initialization variables. Altering these may lead to undesired results.
    global typed := ""
    global visible := 0
    global GuiY := GuiYb
    global prefixed := 0 ; hack used to determine if last keypress had a modifier
    global zcSCROL := "SCROLL LOCK"
    global tickcount_start := 0   ; timer to count repeated key presses
    global keyCount := 0
    global ShowKeyCountDelay := (ShowKeyCountFired = 0) ? 700 : 6000
    NumLetters := (OSDautosize=1) ? NumLettersAutosize : NumLetters
    global text_width := 60

    CreateGUI()
    CreateHotkey()
    return

; The script

GetSpecialKeysStates() {
    GetKeyState, ScrollState, ScrollLock, T   
    If ScrollState = D
    {
       global zcSCROL := "SCROLL LOCK ON"
    }
    else {
       global zcSCROL := "Scroll lock off"
    }
}

TypedLetter(key) {
    return typed := SubStr(typed key, -NumLetters)
}

OnMousePressed() {
    global tickcount_start := A_TickCount-500

    try {
        key := GetKeyStr()
        typed := "" ; concerning TypedLetter(" ") - it resets the content of the OSD
        ShowHotkey(key)
        SetTimer, HideGUI, % -DisplayTime
    }

    If (MouseBeeper = 1) && (ShowMouseButton = 1) && (ShowSingleKey = 1) || (MouseBeeper = 1) && (ShowSingleKey = 0) && (BeepHiddenKeys = 1)
       soundbeep, 2500, 65

}

OnKeyPressed() {
    try {
        key := GetKeyStr()
        typed := "" ; concerning TypedLetter(" ") - it resets the content of the OSD
        ShowHotkey(key)
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnLetterPressed() {
    try {
        if typed 
        {
            sleep, 25    ; this delay helps with dead keys,  but it generates errors; the following actions: stringleft,1 and stringlower help correct these
        } else
        {
        }

        key := GetKeyStr(1)     ; consider it a letter

        if prefixed
        {
            ShowHotkey(key)
        } else
        {
            StringLeft, key, key, 1
            Stringlower, key, key

            GetKeyState, CapsState, CapsLock, T
            If CapsState != D
            {
                 if GetKeyState("Shift", "P")
                {
                  StringUpper, key, key
                }
            } else
                StringUpper, key, key

            TypedLetter(key)
            ShowHotkey(typed)
        }
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnSpacePressed() {
    try {
        if typed {
            if (visible)
            {
                TypedLetter("_")
                ShowHotkey(typed)
            } else
            {
                TypedLetter("_")
                ShowHotkey(typed)
                SetTimer, HideGUI, % -DisplayTime
            }
        } else if (!typed)
        {
          key := GetKeyStr()
          ShowHotkey(key)
          SetTimer, HideGUI, % -DisplayTime
        }
    }
}

OnBspPressed() {
    try
    {
        if typed
        {
            typed := SubStr(typed, 1, StrLen(typed) - 1)
            ShowHotkey(typed)
        } else if (!typed)
        {
            key := GetKeyStr()
            ShowHotkey(key)
            SetTimer, HideGUI, % -DisplayTime
        }
    }
}

OnCapsPressed() {
    try
    {
        if typed
        {
            ShowHotkey(typed)
        } else if (!typed)
        {
            key := GetKeyStr()
            GetKeyState, CapsState, CapsLock, T
            if CapsState = D
            {
                key := prefixed ? key : "CAPSLOCK ON"
            } else
                key := prefixed ? key : "CapsLock off"
            ShowHotkey(key)
            SetTimer, HideGUI, % -DisplayTime
        }
    }

    If (CapslockBeeper = 1) && (ShowSingleKey = 1) || (BeepHiddenKeys = 1)
       {
        soundbeep, 450, 200
       }
}

OnNumpadPressed()
{
    GetKeyState, NumState, NumLock, T
    try {
        if NumState != D
        {
            key := GetKeyStr()
            ShowHotkey(key)
            SetTimer, HideGUI, % -DisplayTime
        } else if !typed
        {
            key := GetKeyStr()
            ShowHotkey(key)
        } else if NumState = D
        {
            key := GetKeyStr(1) ; consider it a letter
            StringLeft, key, key, 3
            StringRight, key, key, 1
            TypedLetter(key)
            ShowHotkey(typed)
        }
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnKeyUp() {
    global tickcount_start := A_TickCount

    if typed && (CapslockBeeper = 1) && (ShowSingleKey = 1)
    {
        GetKeyState, CapsState, CapsLock, T
        If CapsState = D
           {
             soundbeep, 450, 25
           }
           else if (KeyBeeper = 1) && (ShowSingleKey = 1)
           {
             soundbeep, 1900, 45
           }
    }

    If (CapslockBeeper = 0) && (KeyBeeper = 1) && (ShowSingleKey = 1)
       {
         soundbeep, 1900, 45
       }
       else if (CapslockBeeper = 1) && (KeyBeeper = 0)
       {
       }
       else if !typed && (CapslockBeeper = 1) && (ShowSingleKey = 1)
       {
         soundbeep, 1900, 45
       }

    if (BeepHiddenKeys = 1) && (KeyBeeper = 1) && (ShowSingleKey = 0)
         soundbeep, 1900, 45  
}

OnModUp() {
    global tickcount_start := A_TickCount

    If (ModBeeper = 1) && (ShowSingleKey = 1) && (ShowSingleModifierKey = 1) || (ModBeeper = 1) && (BeepHiddenKeys = 1)
       soundbeep, 1000, 65

}

; ===================================================================================

CreateGUI() {
    global

    Gui, +AlwaysOnTop -Caption +Owner +LastFound +ToolWindow +E0x20
    Gui, Margin, 10, 10
    Gui, Color, 111111
    Gui, Font, cWhite s%FontSize% bold, Arial, -wrap
    Gui, Add, Text, vHotkeyText left x10 y10 -wrap

;    if TransparencyLevel between 10 and 245
;       WinSet, Transparent, %TransparencyLevel%

}

CreateHotkey() {

    Loop, 95
    {
        k := Chr(A_Index + 31)

        if (DeadKeys=1)
        {
            for each, char2skip in StrSplit("``,^,',"",~", ",")  ; dead keys to ignore
            {
                if (k = char2skip && DeadKeys=1)
                {
                    continue, 2
                }
            }
        }

        if (k = " ")
        {
            Hotkey, % "~*Space", OnSpacePressed
            Hotkey, % "~*Space Up", OnKeyUp
        }
        else
        {
            Hotkey, % "~*" k, OnLetterPressed
            Hotkey, % "~*" k " Up", OnKeyUp
        }
    }

    Hotkey, % "~*Backspace", OnBspPressed
    Hotkey, % "~*Backspace Up", OnKeyUp
    Hotkey, % "~*CapsLock", OnCapsPressed
    Hotkey, % "~*CapsLock Up", OnKeyUp

    Loop, 24 ; F1-F24
    {
        Hotkey, % "~*F" A_Index, OnKeyPressed
        Hotkey, % "~*F" A_Index " Up", OnKeyUp
    }

    Loop, 10 ; Numpad0 - Numpad9
    {
        Hotkey, % "~*Numpad" A_Index - 1, OnNumpadPressed
        Hotkey, % "~*Numpad" A_Index - 1 " Up", OnKeyUp
    }

    NumpadKeysList := "NumpadDot|NumpadDiv|NumpadMult|NumpadAdd|NumpadSub|sc04E|sc04A|sc052|sc04F|sc050|sc051|sc04B|sc04C|sc04D|sc047|sc048|sc049|sc053|sc037|sc135"

    Loop, parse, NumpadKeysList, |
    {
       Hotkey, % "~*" A_LoopField, OnNumpadPressed
       Hotkey, % "~*" A_LoopField " Up", OnKeyUp
    }

    Otherkeys := "WheelDown|WheelUp|WheelLeft|WheelRight|XButton1|XButton2|Browser_Forward|Browser_Back|Browser_Refresh|Browser_Stop|Browser_Search|Browser_Favorites|Browser_Home|Volume_Mute|Volume_Down|Volume_Up|Media_Next|Media_Prev|Media_Stop|Media_Play_Pause|Launch_Mail|Launch_Media|Launch_App1|Launch_App2|Help|Sleep|PrintScreen|CtrlBreak|Break|AppsKey|Tab|Enter|Esc"
               . "|Insert|Home|End|Up|Down|Left|Right|ScrollLock|NumLock|Pause|sc145|sc146|sc046|sc123|sc11C|sc149|sc151|sc122|sc153"
    Loop, parse, Otherkeys, |
    {
        Hotkey, % "~*" A_LoopField, OnKeyPressed
        Hotkey, % "~*" A_LoopField " Up", OnKeyUp
    }

    If ShowMouseButton=1
    {
        Loop, Parse, % "LButton|MButton|RButton", |
        Hotkey, % "~*" A_LoopField, OnMousePressed
    }

    for i, mod in ["LCtrl", "RCtrl", "LAlt", "RAlt"] {
        Hotkey, % "~*" mod, OnKeyPressed
        Hotkey, % "~*" mod " Up", OnModUp
    }

    for i, mod in ["LWin", "RWin"]
        Hotkey, % "~*" mod, OnKeyPressed
        Hotkey, % "~*" mod " Up", OnModUp

    If typed {
    for i, mod in ["LShift", "RShift"]
        Hotkey, % "~*" mod, OnKeyPressed
        Hotkey, % "~*" mod " Up", OnModUp
    }
}

ShowHotkey(HotkeyStr) {

    if GetKeyState("Shift", "P") && (HotkeyStr ~= "\^") && typed && DeadKeys && ShowSingleKey
    {
        SendRaw, ^
        return
    }

    if (OSDautosize=1) && (A_TickCount-tickcount_start > 70)
    {
        text_width := (StrLen(HotkeyStr)/1.3)*FontSize
        text_width := (text_width<70) ? 70 : text_width+15
    } else if OSDautosize=0
    {
        text_width := GuiWidth
    }

    if !typed
    {
        StringUpper, HotkeyStr, HotkeyStr, T
    }


    GuiControl,     , HotkeyText, %HotkeyStr%
    GuiControl, Move, HotkeyText, w%text_width% left
    Gui, Show, NoActivate x%GuiX% y%GuiY% AutoSize, KeypressOSD
    visible := 1
    SetTimer, runThisRepeatedly, on, 800
}

GetKeyStr(letter := 0) {
    static modifiers := ["LCtrl", "RCtrl", "LAlt", "RAlt", "LShift", "RShift", "LWin", "RWin"]
    static repeatCount := 1

    ; If any mod but shift, go ; If shift, check if not letter

    for i, mod in modifiers
    {
        if (mod = "LShift" && typed || mod = "RShift" && typed ? (!letter && GetKeyState(mod)) : GetKeyState(mod))
            prefix .= mod " + "
    }

    if (!prefix && !ShowSingleKey)
        throw

    key := SubStr(A_ThisHotkey, 3)

    if (key ~= "i)^(LCtrl|RCtrl|LShift|RShift|LAlt|RAlt|LWin|RWin)$") {
        if (ShowSingleModifierKey = 0) || (ShowSingleKey = 0) || (A_TickCount-tickcount_start > 2000) && visible
        {
            throw
        } else
        {
             key := ""
             prefix := RTrim(prefix, "+ ")
        }

        prefix := CompactModifiers(prefix)

        if (ShowKeyCount=1) {
            if !InStr(prefix, "+") && IsDoubleClickEx() {
                if (A_ThisHotKey != A_PriorHotKey) || (ShowKeyCount=1) && (A_TickCount-tickcount_start < 700) {
                    if (++repeatCount > 1) {
                        prefix .= " {" repeatCount "}"
                    }
                } else {
                    repeatCount := 0
                }
            } else {
                repeatCount := 1
            }
        }
    } else {
        if ( StrLen(key) = 1 ) {
            key := GetKeyChar(key, "A")
        } else if ( SubStr(key, 1, 2) = "sc" ) {
            key := SpecialSC(key)
        } else if (key = "Volume_Up") || (key = "Volume_Down") {
            soundbeep, 150, 40
        } else if (key = "PrintScreen") {
            if HideAnnoyingKeys
                throw
            key := "Print Screen"
        } else if (key = "MButton") {
            key := "Middle Click"
        } else if (key = "RButton") {
            key := "Right Click"
        } else if (key = "LButton") && IsDoubleClick() {
            key := "Double-Click"
        } else if (key = "LButton") {
            if HideAnnoyingKeys
                throw
            key := "Left Click"
        }
        {
            _key := (key = "Double-Click") ? "Left Click" : key
        }

        prefix := CompactModifiers(prefix)

        static pre_prefix, pre_key, keyCount := 1
        keyCount := (key=pre_key) && (prefix = pre_prefix) ? keyCount : 1
        global ShowKeyCountDelay := (ShowKeyCountFired = 0) ? 700 : 6000
        ShowKeyCountDelay := (ShowKeyCountFired=1) ? (ShowKeyCountDelay+keyCount*100) : ShowKeyCountDelay

        if (InStr(prefix, "+")) && (A_TickCount-tickcount_start < ShowKeyCountDelay) || (!letter) && (A_TickCount-tickcount_start < ShowKeyCountDelay)
        {
            if (ShowPrevKey=1) && (A_TickCount-tickcount_start < ShowPrevKeyDelay) && !typed
            {
                ShowPrevKeyValid := 1
            } else
            {
                ShowPrevKeyValid := 0
            }
            if (prefix != pre_prefix) {
                result := (ShowPrevKeyValid=1) ? prefix key " {" pre_prefix pre_key "}" : prefix key
            } else if (ShowPrevKeyValid=1) && (key != pre_key) || (ShowKeyCount=1) && (ShowPrevKeyValid=1)
            {
                keyCount := (key=pre_key) && (ShowKeyCount=1) ? (keyCount+1) : 1
                key := (keyCount>1) && (ShowKeyCount=1) ? (key " {" keyCount "}") : (key ", " pre_key)
            } else if (ShowPrevKeyValid=0)
            {
                keyCount := (key=pre_key) && (ShowKeyCount=1) ? (keyCount+1) : 1
                key := (keyCount>1) ? (key " {" keyCount "}") : (key)
            }
        } else {
            keyCount := 1
        }

        pre_prefix := prefix
        pre_key := _key

        repeatCount := 1
    }

    prefixed := prefix ? 1 : 0
    return result ? result : prefix . key
}

CompactModifiers(stringy)
{
    if DifferModifiers = 1
    {
        StringReplace, stringy, stringy, LCtrl + RAlt, AltGr, All
        StringReplace, stringy, stringy, LCtrl + RCtrl + RAlt, RCtrl + AltGr, All
        StringReplace, stringy, stringy, RAlt, AltGr, All
        StringReplace, stringy, stringy, LAlt, Alt, All
    } else if (DifferModifiers = 0)
    {
        StringReplace, stringy, stringy, LCtrl + RAlt, AltGr, All
        ; StringReplace, stringy, stringy, LCtrl + RCtrl + RAlt, RCtrl + AltGr, All
        StringReplace, stringy, stringy, LCtrl, Ctrl, All
        StringReplace, stringy, stringy, RCtrl, Ctrl, All
        StringReplace, stringy, stringy, LShift, Shift, All
        StringReplace, stringy, stringy, RShift, Shift, All
        StringReplace, stringy, stringy, LAlt, Alt, All
        StringReplace, stringy, stringy, LWin, WinKey, All
        StringReplace, stringy, stringy, RWin, WinKey, All
        StringReplace, stringy, stringy, Ctrl + Ctrl, Ctrl, All
        StringReplace, stringy, stringy, Shift + Shift, Shift, All
        StringReplace, stringy, stringy, WinKey + WinKey, WinKey, All
        StringReplace, stringy, stringy, RAlt, AltGr, All
    }
    return stringy
}

SpecialSC(sc) {
    GetSpecialKeysStates()

    GetKeyState, NumState, NumLock, T
    If NumState = D
    {
       k := {sc046: zcSCROL, sc145: "NUM LOCK ON", sc146: "Pause", sc123: "Genius LuxeMate Scroll", sc04E: "[ + ]", sc04A: "[ - ]", sc052: "[ 0 ]", sc04F: "[ 1 ]", sc050: "[ 2 ]", sc051: "[ 3 ]", sc04B: "[ 4 ]", sc04C: "[ 5 ]", sc04D: "[ 6 ]", sc047: "[ 7 ]", sc048: "[ 8 ]", sc049: "[ 9 ]", sc053: "[ . ]", sc037: "[ * ]", sc135: "[ / ]", sc11C: "[Enter]", sc149: "Page Up", sc151: "Page Down", sc153: "Delete", sc122: "Media_Play/Pause"}
    }
    else {
       k := {sc046: zcSCROL, sc145: "Num lock off", sc146: "Pause", sc123: "Genius LuxeMate Scroll", sc04E: "[ + ]", sc04A: "[ - ]", sc052: "[Insert]", sc04F: "[End]", sc050: "[Down]", sc051: "[Page Down]", sc04B: "[Left]", sc04C: "[Undefined]", sc04D: "[Right]", sc047: "[Home]", sc048: "[Up]", sc049: "[Page Up]", sc053: "[Delete]", sc037: "[ * ]", sc135: "[ / ]", sc11C: "[Enter]", sc149: "Page Up", sc151: "Page Down", sc153: "Delete", sc122: "Media_Play/Pause"}
    }
    return k[sc]
}

; by Lexikos -- https://autohotkey.com/board/topic/110808-getkeyname-for-other-languages/#entry682236
GetKeyChar(Key, WinTitle:=0) {
    thread := WinTitle=0 ? 0
        : DllCall("GetWindowThreadProcessId", "ptr", WinExist(WinTitle), "ptr", 0)
    hkl := DllCall("GetKeyboardLayout", "uint", thread, "ptr")
    vk := GetKeyVK(Key), sc := GetKeySC(Key)
    VarSetCapacity(state, 256, 0)
    VarSetCapacity(char, 4, 0)
    n := DllCall("ToUnicodeEx", "uint", vk, "uint", sc
        , "ptr", &state, "ptr", &char, "int", 2, "uint", 0, "ptr", hkl)
    return StrGet(&char, n, "utf-16")
}

IsDoubleClick(MSec = 300) {
    Return (A_ThisHotKey = A_PriorHotKey) && (A_TimeSincePriorHotkey < MSec)
}

IsDoubleClickEx(MSec = 300) {
    preHotkey := RegExReplace(A_PriorHotkey, "i) Up$")
    Return (A_ThisHotKey = preHotkey) && (A_TimeSincePriorHotkey < MSec)
}

HideGUI() {
    visible := 0
    Gui, Hide
    SetTimer, runThisRepeatedly, off, 300

}

!+^F8::
    ShowSingleKey := (!ShowSingleKey) ? true : false
    ShowHotkey("Show single keys = " ShowSingleKey)
    SetTimer, HideGUI, % -DisplayTime
return

!+^F9::
    GuiY := (GuiY = GuiYb) ? GuiYa : GuiYb
    ShowHotkey("OSD position changed")
    SetTimer, HideGUI, % -DisplayTime
return

!+^F12::
    Gui, Destroy
    sleep, 100
    CreateGUI()
    sleep, 100
    ShowHotkey("OSD reinitialized")
    SetTimer, HideGUI, % -DisplayTime
return

runThisRepeatedly() {
    id := mouseIsOver()
    title := getWinTitleFromID(id)
    if title = KeypressOSD
        HideGUI()
}

mouseIsOver() {
    MouseGetPos,,, id
    return id
}

getWinTitleFromID(id) {
    WinGetTitle, title, % "ahk_id " id
    return title
}
; AVAILABLE SHORTCUTS:
; Ctrl+Alt+Shift+F8 - toggles "Show single key" option. Useful when you must reliably use dead keys.
; Ctrl+Alt+Shift+F9 - toggles between two OSD offsets: GuiYa and GuiYb
; Ctrl+Alt+Shift+F12 - Reinitialize OSD. Useful when it no longer appears on top. To have it appear on top of elevated apps, run it in administrator mode.
;
; FEATURES:
; - Show previously pressed key if fired quickly
; - Count key presses or key fires and mouse clicks
; - Automatic resizing of OSD/HUD or fixed size
; - Generate beeps for key presses, modifiers, mouse clicks or just when typing with Capslock
; - Indicators for CapsLock, NumLock and ScrollLock states
; - Hides automatically when mouse runs over it
; - Typing mode; shows what you are typing in an expanding text area
; - Dead keys support (partial), option to turn it off
; - Easy to configure with many options:
; - to toggle features: key beepers, key counting or previous key
; - to hide modifiers, mouse clicks or single key presses (which disables typing mode)
; - or hide keys that usually get in the way: Left Click and Print Screen [HideAnnoyingKeys]
; - differ between left and right modifiers
; - OSD/HUD position, size and display time
; - beep key presses even if keys are not displayed

; TO-DO:::::::::::::::::::::::::::::::::::
; features to implement:
; - support for non-english keyboards done
; - when user types single letters, display Shift + (sign or numeric keys) as signs/symbols, eg. Shift + 0, should be displayed as ); done
; - after pressing Shift + [letter] if the following key is a single letter, remember the first Shift + [letter]; done
; - after a Numpad key is pressed, typing mode should begin if the second one is another numpad key; done
; - show a generic symbol for ignored dead keys; helps to clarify one was pressed;done
;
; glitches to fix [by priority]:
; - [regression since v2.52] show Shift as a modifier, as Ctrl and Alt; Shift should behave as the other two; it is never displayed alone and countable; done
; - once a key is pressed, counting of key fires is not initiated after the OSD was hidden for a little awhile; done/fixed
; - make dead keys always work; with the current implementation, the special keys show up seemingly random in the OSD and sometimes in the text field the user types in; done
; - if dead keys support / work-around enabled, once you typed a caret [^], sometimes user gets randomly inserted carets when typing; done/fixed
; - automatic resizing of the OSD/GUI is just a silly hack based on the default font size and the number of typed chars; it often fails to resize properly; done/much improved
; - redraw issues; it still flickers;
; - make it work reliably with sticky keys; if user presses once Ctrl and another key afterwards, it rarely detects the combination on Winndows 7;

PS. Drugwash/Dragoș. I tested the Romanian keyboard layout with the script. No Romanian keys are displayed in the OSD. However, it does not prevent one from using the keys.

Best regards, Marius
Last edited by robodesign on 16 Nov 2017, 05:27, edited 2 times in total.
-------------------------
KeyPress OSD v4: GitHub or forum. (presentation video)
My home page.
User avatar
Drugwash
Posts: 567
Joined: 29 May 2014, 21:07
Location: Ploieşti, Romania

Re: [Script] KeypressOSD - Display key press on screen

Post by Drugwash » 05 Oct 2017, 08:24

PS. Drugwash/Dragoș. I tested the Romanian keyboard layout with the script. No Romanian keys are displayed in the OSD. However, it does not prevent one from using the keys.
Well then, since this script's main function is to display the pressed keys, I'd say it's buggy/incomplete.
Any users of other keyboard layouts here (italian, french, turkish, danish, whatever) to confirm the issue?
I've deleted my CloudMe account because of GDPR - the now legal base for privacy invasion and data theft.
Guest

Re: [Script] KeypressOSD - Display key press on screen

Post by Guest » 05 Oct 2017, 08:51

It seems rather logical to me when you study the CreateHotkey() function - it doesn't create any non-English keyboard hotkeys.
So if Ș for example is in your keyboard layout it needs to be added as a hotkey. And that for each language / keyboard layout you wish to add support for.
robodesign
Posts: 371
Joined: 30 Sep 2017, 03:59
Facebook: marius.sucan
GitHub: mariussucan
Location: Romania
Contact:

Re: [Script] KeypressOSD - Display key press on screen

Post by robodesign » 05 Oct 2017, 15:22

Yes, there is no real international support.

I just learned that it does not cover a wide range of keys.

I could make (even did so) a huge list of letters/chars specific to Romanian, French, Turkish or other languages, to assign hotkeys to all, in CreateHotkey() function, but...

The problem is AHK gives the error [not a valid key name] when the script tries to assign hotkeys to inexistent keys. So, I would need another function to check if they exist. Can anyone help me with the DLLCalls for this? I need to circumvent getting the error and I was told DLL calls are required for this.

I identified 50 different characters in the alphabet of Turkish, French, German, Romanian, Czech, Polish, Hungarian, Danish, Swedish: à, ă, ą, á, â, å, ä, æ, ß, ć, č, ç, ď, ę, ě, è, é, ê, ë, ğ, i̇, í, î, ï, ń, ň, œ, ő, ø, ó, ô, ö, ů, ű, ù, ú, û, ü, ý, ÿ, ř, ś, ş, š, ș, ț, ť, ź, ż, ž,

Thank you.

Best regards, Marius
-------------------------
KeyPress OSD v4: GitHub or forum. (presentation video)
My home page.
robodesign
Posts: 371
Joined: 30 Sep 2017, 03:59
Facebook: marius.sucan
GitHub: mariussucan
Location: Romania
Contact:

Re: [Script] KeypressOSD - Display key press on screen

Post by robodesign » 06 Oct 2017, 03:09

okay, thank you. I will try/check those, but I also thought of adding a settings option, where users can simply add a list of chars specific to their language. After all, one needs to edit the settings section to make it suitable to one's needs. I think this way I could safely enable international support and not overly complicate it. What do you guys think?
-------------------------
KeyPress OSD v4: GitHub or forum. (presentation video)
My home page.
User avatar
Drugwash
Posts: 567
Joined: 29 May 2014, 21:07
Location: Ploieşti, Romania

Re: [Script] KeypressOSD - Display key press on screen

Post by Drugwash » 06 Oct 2017, 06:54

Things are not that simple. User may switch layouts at any time for any given open application. As you may know, each open application retains its own layout and switching applications will also switch layouts. There is a notification message when layouts are switched (0x51=WM_INPUTLANGCHANGE) and you should hook that notification, retrieve the current layout pertaining to the currently focused application and adjust the script to display the correct corresponding keys.
Here's a piece of code, originally by SKAN, which I've modified for testing purposes - maybe you'll find something useful in there:

Code: Select all

; originally by SKAN at  https://autohotkey.com/board/topic/21105-crazy-scripting-scriptlet-to-find-scancode-of-a-key/
; modified by Drugwash using info from 'About keyboard input'  https://msdn.microsoft.com/en-us/library/windows/desktop/ms646267%28v=vs.85%29.aspx 
; GetKeyNameText()  https://msdn.microsoft.com/en-us/library/windows/desktop/ms646300%28v=vs.85%29.aspx
SetFormat, Integer, Hex
Gui +ToolWindow -SysMenu +AlwaysOnTop
Gui, Font, s8, Arial
Gui, Add, Text, w200 h16 vkbd 0x200,
Gui, Add, Text, w200 h16 vlo 0x200,
Gui, Add, Text, w200 h16 vlo2 0x200,
Gui, Font, s14 Bold, Arial
Gui, Add, Text, w200 h33 vSC 0x201 +Border, {key?}
msgbox, % "Installed layouts:`n" GetKeyboardLayoutList()
Gui, Show,, % "// ScanCode //////////"
Loop 9
  OnMessage( 255+A_Index, "ScanCode" ) ; 0x100 to 0x108
OnMessage( 0x51, "lang" ) ; WM_INPUTLANGCHANGE
GetKBType(), layout()
Return
;================================================================
ScanCode( wParam, lParam ) {
;================================================================
; Clipboard := "SC" SubStr((((lParam>>16) & 0xFF)+0xF000),-2)
; GuiControl,, SC, %Clipboard%
;sc := (lParam>>16) & 0xFF
sc := "SC" SubStr((((lParam>>16) & 0xFF)+0xF000),-2), vk := SubStr(wParam, 3)
r1 := (lParam>>26) & 0x1, r2 := (lParam>>27) & 0x1, r3 := (lParam>>28) & 0x1
VarSetCapacity(keyname, sz:=260, 0)
DllCall("GetKeyNameText", "UInt", lParam, "Str", keyname, "UInt", sz)	; user32.dll, A/W
if !GetKeyState("Shift", "P") && StrLen(keyname)=1
	StringLower, keyname, keyname
else if GetKeyState("Shift", "P") && StrLen(keyname)=1
	StringUpper, keyname, keyname
 GuiControl,, SC, %keyname%
;GuiControl,, lo2, Scan code: %sc% [%r1% %r2% %r3%]
GuiControl,, lo2, % GetKeyName(sc) " vk " wParam " " GetKeyName("VK" vk)
}
;================================================================
lang(wP, lP)
;================================================================
{
layout()
GuiControl,, lo2, Charset: %wP% LCID: %lP%
}
;================================================================
layout()
;================================================================
{
VarSetCapacity(lo, 32, 0)
DllCall("GetKeyboardLayoutName", "Str", lo)
GuiControl,, lo, Layout: %lo%
}
;================================================================
GetKBType()
;================================================================
{	; user32.dll
t := DllCall("GetKeyboardType", "UInt", 0)	; type
s := DllCall("GetKeyboardType", "UInt", 1)	; subtype
k := DllCall("GetKeyboardType", "UInt", 2)	; no. of function keys
GuiControl,, kbd, Kbd. Type: %t% Subtype: %s% Fn: %k%
}
;================================================================
GetKeyboardLayoutList()
;================================================================
{
n := DllCall("GetKeyboardLayoutList", "UInt", 0, Ptr, &buf)
VarSetCapacity(buf, n*4, 0)
if n := DllCall("GetKeyboardLayoutList", "UInt", n, Ptr, &buf)
	Loop, %n%
		list .= NumGet(buf, 4*(A_Index-1), "UInt") "`n"
return list
}
;================================================================
GetCurrentLayout(hAwin=0)
;================================================================
{
hAwin := hAwin ? hAwin : "A"
WinGet, wID,, %hAwin%
pID:= DllCall("GetWindowThreadProcessId", Ptr, wID, "UInt", 0)
return DllCall("GetKeyboardLayout", Ptr, pID, "UInt")
}

/*
lParam bits in ScanCode (WM_ key messages):
0-15=key repeat count
16-23=scan code
24 extended-key flag (true for R-ALT, R-CTRL)
25-'do not care' flag (no left/right distinction)
26-28=RESERVED
29=context code
30=previous key-state flag
31=transition-state flag
========
keyboard types:
1=83 keys (IBM PC/XT or compatible)
2=102 keys (Olivetti "ICO")
3=84 keys (IBM PC/AT or similar)
4=101/102 keys (IBM enhanced)
5=Nokia 1050 and similar
6=Nokia 9140 and similar
7=Japanese

subtype is OEM-dependent
With single USB keyboard connected, function returns code 81. ???
=============
to do: GetKeyboardLayout(UInt idThread)
=============
key messages:
0x7B=WM_CONTEXTMENU
0x100=WM_KEYDOWN
0x101=WM_KEYUP
0x102=WM_CHAR
0x103=WM_DEADCHAR
0x104=WM_SYSKEYDOWN
0x105=WM_SYSKEYUP
0x106=WM_SYSCHAR
0x107=WM_SYSDEADCHAR
0x109=WM_UNICHAR
0x112=WM_SYSCOMMAND
0x319=WM_APPCOMMAND
*/
I've deleted my CloudMe account because of GDPR - the now legal base for privacy invasion and data theft.
robodesign
Posts: 371
Joined: 30 Sep 2017, 03:59
Facebook: marius.sucan
GitHub: mariussucan
Location: Romania
Contact:

Re: [Script] KeypressOSD - Display key press on screen

Post by robodesign » 08 Oct 2017, 09:24

Hello, guys!

I made a new version. This include support for about 35-40 keyboard layouts, or more, if we include variants.

It has automatic detection at start or continously, as you choose, or when you press a button/shortcut.

Source at: http://p.ahkscript.org/?p=daa02b61

; AVAILABLE SHORTCUTS:
; Ctrl+Alt+Shift+F8 - Toggles "Show single key" option. Useful when you must reliably use dead keys.
; Ctrl+Alt+Shift+F9 - Toggles between two OSD offsets: GuiYa and GuiYb.
; Ctrl+Alt+Shift+F10 - Toggles personal/regional keys support.
; Ctrl+Alt+Shift+F11 - Detect keyboard language.
; Ctrl+Alt+Shift+F12 - Reinitialize OSD. Useful when it no longer appears on top. To have it appear on top of elevated apps, run it in administrator mode.
;
; FEATURES:
; - Show previously pressed key if fired quickly.
; - Count key presses or key fires and mouse clicks.
; - Automatic resizing of OSD/HUD or fixed size.
; - Hides automatically when mouse runs over it.
; - Generate beeps for key presses, modifiers, mouse clicks or just when typing with Capslock.
; - Indicators for CapsLock, NumLock and ScrollLock states.
; - Typing mode; shows what you are typing in an expanding text area.
; - Partial dead keys support, option to turn it off; The work-around is only for the English International keyboard layout.
; - Partial support non-English keyboards. 40 keyboard layouts defined.
; - limited automatic detection of keyboard layouts.
; - the user also has the option to define his regional keys.
; - Easy to configure with many options:
; - to toggle features: key beepers, key counting or previous key;
; - to hide modifiers, mouse clicks or single key presses (which disables typing mode);
; - or hide keys that usually get in the way: Left Click and Print Screen [HideAnnoyingKeys];
; - differ between left and right modifiers;
; - OSD/HUD position, size and display time;
; - beep key presses even if keys are not displayed;
; - some settings toggled by shortcuts are stored permanently in an INI file;
;
; CHANGELOG:
; by Marius Sucan (robodesign.ro)
; v2.85 (2017-10-08)
; - minor improvements for the dead keys work-around
; - added support for 40 foreign keyboard layouts
; - automatic detection of keyboard layouts, at start or continously
; - user also can define personal/regional keys in the settings section
; - new keyboard shortcut, to toggle regional keys
; - new setting: to enable or disable system-wide keyboard shortcuts
; - settings toggled by shortcuts are stored permanently in an INI file
; - except for the option of regional keys support
; v2.73 (2017-10-05)
; - improvements for automatic resize calculation

Code: Select all

;
KeypressOSD.ahk
;--------------------------------------------------------------------------------------------------------------------------
;
; AVAILABLE SHORTCUTS:
; Ctrl+Alt+Shift+F8  - Toggles "Show single key" option. Useful when you must reliably use dead keys.
; Ctrl+Alt+Shift+F9  - Toggles between two OSD offsets: GuiYa and GuiYb.
; Ctrl+Alt+Shift+F10 - Toggles personal/regional keys support.
; Ctrl+Alt+Shift+F11 - Detect keyboard language.
; Ctrl+Alt+Shift+F12 - Reinitialize OSD. Useful when it no longer appears on top. To have it appear on top of elevated apps, run it in administrator mode.
;
; FEATURES:
; - Show previously pressed key if fired quickly.
; - Count key presses or key fires and mouse clicks.
; - Automatic resizing of OSD/HUD or fixed size.
; - Hides automatically when mouse runs over it.
; - Generate beeps for key presses, modifiers, mouse clicks or just when typing with Capslock.
; - Indicators for CapsLock, NumLock and ScrollLock states.
; - Typing mode; shows what you are typing in an expanding text area.
; - Partial dead keys support, option to turn it off; The work-around is only for the English International keyboard layout.
; - Partial support non-English keyboards. 40 keyboard layouts defined.
;   - limited automatic detection of keyboard layouts.
;   - the user also has the option to define his regional keys.
; - Easy to configure with many options:
;   - to toggle features: key beepers, key counting or previous key;
;   - to hide modifiers, mouse clicks or single key presses (which disables typing mode);
;   - or hide keys that usually get in the way: Left Click and Print Screen [HideAnnoyingKeys];
;   - differ between left and right modifiers;
;   - OSD/HUD position, size and display time;
;   - beep key presses even if keys are not displayed;
;   - some settings toggled by shortcuts are stored permanently in an INI file;
;
; CHANGELOG:
; by Marius Sucan (robodesign.ro)
;   v2.85 (2017-10-08)
;   - minor improvements for the dead keys work-around
;   - added support for 40 foreign keyboard layouts
;   - automatic detection of keyboard layouts, at start or continously
;   - user also can define personal/regional keys in the settings section
;   - new keyboard shortcut, to toggle regional keys
;   - new setting: to enable or disable system-wide keyboard shortcuts
;   - settings toggled by shortcuts are stored permanently in an INI file
;     - except for the option of regional keys support
;   v2.73 (2017-10-05)
;   - improvements for automatic resize calculation
;   v2.72 (2017-10-04)
;   - new option: make beeps on mouse clicks
;   - new option: dstinct beeper for modifier keys: Shift, Ctrl, Alt, WinKey.
;   - new option: beep hidden keys; if you want it to beep even if the keys are not displayed
;   - fix: now it always counts mouse clicks
;   v2.70 (2017-10-03)
;   - added option to toggle dead keys support / work-around.
;   - added option to hide annoying keys that usually get in the way: Left Click and Print Screen.
;   - redraw improvements, reduced flickering
;   - disabled OSD transparency to reduce flickering
;   v2.69 (2017-09-30)
;   - OSD / GUI hides when mouse is over it. Many thanks to phaleth!
;   v2.68 (2017-09-29)
;   - numpad keys now work in typing mode as expected; they appear as symbols or numbers
;
; by phaleth from irc.freenode.net #ahk
;   v2.67 (2017-09-28)
;   - dead keys improvements
;
; by Marius Sucan (robodesign.ro)
;   v2.66 (2017-09-28)
;   - key combinations with Shift work better.
;   v2.65 (2017-09-27)
;   - Fixed a bug with counting modifier keys;
;   - improved the dead keys work-around
;   - friendly names for mouse clicks
;   - when pressed, volume keys  always generate beeps
;   - added option to differentiate between left and right modifiers
;   - now it detects AltGr key
;   - the key beeper now also makes a beep for modifiers.
;   - capslock no longer erases text you are typing displayed by the OSD
;   - now you can toggle between two different OSD positions with Ctrl + Alt + Shift + F9
;   v2.60 (2017-09-26)
;   - Fixed many bugs with counting keys;
;   - reimplemented the feature to see the previous key combination, if quickly a new one is pressed;
;   - added options/settings to toggle previous keys, counting keys and delay;
;   - added shortcuts to toggle ShowSingleKey option and to reinitialize the OSD;
;   - added option for automatic resizing of the OSD; it can be turned off in the settings section; it is a fishy implementation, but if one adjusts it, can make it to suit personal needs;
;   - new option: beep when key is released or when writing with capslock
;   v2.58 (2017-09-23)
;   - Numpad keys have friendly naming, based on the numlock state.
;   - Combinations with space and backspace work again.
;   v2.56 (2017-09-22)
;   - more fixes for space usage and key combinations;
;   - now it indicates when ScrollLock, NumLock and Capslock are activated.
;   v2.55 (2017-09-21)
;   - minor fixes for space usage and key combinations.
;
; by Saiapatsu from irc.freenode.net #ahk
;   v2.54 (2017-09-21)
;   - Scrolls through n recently typed characters instead of just the latest word
;   v2.53 (2017-09-21)
;   - Case change effect limited to the loop 95 letters only.
;   v2.52 (2017-09-21)
;   - Now supports backspace. Commented out CapsLock beeper.
;   v2.51 (2017-09-21)
;   - Changed labels to functions, added ToolWindow style to window, changed DisplayTime
;   calculation, made it show last word typed, hid spacebar presses
;   todo: make Shift look less ugly
;
; by Marius Sucan (robodesign.ro)
;   v2.50 (2017-09-20)
;   - Changed the OSD positioning and sizing. It was based on the current window. Now it is always fixed in a specific place. Added a Capslock beeper.
;
; by tmplinshi from https://autohotkey.com/boards/viewtopic.php?f=6&t=225
;   v2.22 (2017-02-25)
;   - Now pressing same combination keys continuously more than 2 times,
;   for example press Ctrl+V 3 times, will displayed as "Ctrl + v (3)"
;   v2.21 (2017-02-24)
;   - Fixed LWin/RWin not poping up start menu
;   v2.20 (2017-02-24)
;   - Added displaying continuous-pressed combination keys.
;   e.g.: With CTRL key held down, pressing K and U continuously will shown as "Ctrl + k, u"
;   v2.10 (2017-01-22)
;   - Added ShowStickyModKeyCount option
;   v2.09 (2017-01-22)
;   - Added ShowModifierKeyCount option
;   v2.08 (2017-01-19)
;   - Fixed a bug
;   v2.07 (2017-01-19)
;   - Added ShowSingleModifierKey option (default is True)
;   v2.06 (2016-11-23)
;   - Added more keys. Thanks to SashaChernykh.
;   v2.05 (2016-10-01)
;   - Fixed not detecting "Ctrl + ScrollLock/NumLock/Pause". Thanks to lexikos.
;   v2.04 (2016-10-01)
;   - Added NumpadDot and AppsKey
;   v2.03 (2016-09-17)
;   - Added displaying "Double-Click" of the left mouse button.
;   v2.02 (2016-09-16)
;   - Added displaying mouse button, and 3 settings (ShowMouseButton, FontSize, GuiHeight)
;   v2.01 (2016-09-11)
;   - Display non english keyboard layout characters when combine with modifer keys.
;   v2.00 (2016-09-01)
;   - Removed the "Fade out" effect because of its buggy.
;   - Added support for non english keyboard layout.
;   - Added GuiPosition setting.
;   v1.00 (2013-10-11)
;   - First release by tmplinshi based on RaptorX. Function keys, numpad keys and mouse clicks support. Popups at mouse position.
;   v0.50 (2010-03-18)
;   - Released by RaptorX.
;--------------------------------------------------------------------------------------------------------------------------
;
; TO-DO:::::::::::::::::::::::::::::::::::
; features to implement:
; - visual mouse clicks
; - languages in an external file
; - when user types single letters, display Shift + (sign or numeric keys) as signs/symbols, eg. Shift + 0, should be displayed as );
; - after pressing Shift + [letter] if the following key is a single letter, remember the first Shift + [letter];
; - after a Numpad key is pressed, typing mode should begin if the second one is another numpad key;
; - show a generic symbol for ignored dead keys; helps to clarify one was pressed;
;
; glitches to fix [by priority]:
; - [regression since v2.52] show Shift as a modifier, as Ctrl and Alt; Shift should behave as the other two; it is never displayed alone and countable;
; - once a key is pressed, counting of key fires is not initiated after the OSD was hidden for a little awhile;
; - make dead keys work better
; - automatic resizing of the OSD/GUI is just a silly hack based on the default font size and the number of typed chars; it often fails to resize properly;
; - redraw issues; it still flickers;
; - make it work reliably with sticky keys; if user presses once Ctrl and another key afterwards, it rarely detects the combination on Winndows 7;
;----------------------------------------------------------------------------

; Initialization
#SingleInstance force
#NoEnv
#MaxHotkeysPerInterval 500
SetBatchLines, -1
ListLines, Off

; Settings:
    global DeadKeys              := 0     ; a toggle for a partial dead keys support. Zero [0] means no dead keys. See CreateHotkey() and char2skip to define the dead keys.
    global CustomRegionalKeys    := 0     ; if you want to add support to a regional keyboard
    global RegionalKeysList      := "a|b|c"  ; add the characters in this list, separated by |
    global AutoDetectKBD         := 1     ; at start, detect keyboard layout
    global ConstantAutoDetect    := 1     ; continously check if the keyboard layout changed; if AutoDetectKBD=0, this is ignored
    global SilentDetection       := 0     ; do not display information about language switching

    global ShowSingleKey         := 1     ; show only key combinations
    global ShowMouseButton       := 1     ; if disabled, mouse click beeps will never occur
    global HideAnnoyingKeys      := 1     ; Left click and PrintScreen can easily get in the way.
    global ShowSingleModifierKey := 1     ; make it display Ctrl, Alt, Shift when pressed alone
    global DifferModifiers       := 0     ; differentiate between left and right modifiers
    global ShowPrevKey           := 1     ; show previously pressed key, if pressed quickly in succession
    global ShowPrevKeyDelay      := 300
    global ShowKeyCount          := 1     ; count how many times a key is pressed
    global ShowKeyCountFired     := 1     ; show only key presses (0) or catch key fires as well (1)

;   global TransparencyLevel     := 220   ; OSD transparency; range from 0 to 255. feature disabled, to reduce flickering
    global DisplayTime           := 3000  ; in milliseconds
    global GuiWidth              := 360
    global GuiHeight             := 50
    global GuiX                  := 50
    global GuiYa                 := 250   ; toggle between GuiYa and GuiYb with Ctrl + Alt + Shift + F9
    global GuiYb                 := 800
    global FontSize              := 19
    global OSDautosize           := 1     ; make adjustments in ShowHotkey() to match your font size
    global NumLetters            := 25    ; amount of recently typed letters to display
    global NumLettersAutosize    := 60    ; ...when OSD resizes automatically

    global CapslockBeeper        := 1     ; only when the key is released
    global KeyBeeper             := 0     ; only when the key is released
    global ModBeeper             := 0     ; beeps for every modifier, when released
    global MouseBeeper           := 0
    global BeepHiddenKeys        := 0     ; [when any beeper enabled] to beep or not when keys are not displayed by OSD/HUD

    global KeyboardShortcuts     := 1     ; system-wide shortcuts


; Initialization variables. Altering these may lead to undesired results.
    global typed := ""
    global visible := 0
    global GuiY := GuiYb
    global prefixed := 0 ; hack used to determine if last keypress had a modifier
    global zcSCROL := "SCROLL LOCK"
    global tickcount_start := 0   ; timer to count repeated key presses
    global keyCount := 0
    global ShowKeyCountDelay := (ShowKeyCountFired = 0) ? 700 : 6000
    NumLetters := (OSDautosize=1) ? NumLettersAutosize : NumLetters
    global text_width := 60
    global InputLocaleID := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")

    IniRead, GuiY, keypress-osd.ini, PermanentSettings, GuiY
    GuiY := (GuiY=GuiYb || GuiY=GuiYa) ? GuiY : GuiYb
    IniRead, ShowSingleKey, keypress-osd.ini, PermanentSettings, ShowSingleKey
    IniRead, CustomMultiLangToggled, keypress-osd.ini, PermanentSettings, CustomMultiLangToggled
    if (CustomMultiLangToggled=1) {
        IniRead, CustomRegionalKeys, keypress-osd.ini, PermanentSettings, CustomRegionalKeys
        CustomMultiLangToggled := 0
        IniWrite, %CustomMultiLangToggled%, keypress-osd.ini, PermanentSettings, CustomMultiLangToggled
    }

    IniRead, AutoDetectKBDToggled, keypress-osd.ini, PermanentSettings, AutoDetectKBDToggled
    if (AutoDetectKBDToggled=1) {
        IniRead, AutoDetectKBD, keypress-osd.ini, PermanentSettings, AutoDetectKBD
        IniRead, ConstantAutoDetect, keypress-osd.ini, PermanentSettings, ConstantAutoDetect
        AutoDetectKBDToggled := 0
        IniWrite, %AutoDetectKBDToggled%, keypress-osd.ini, PermanentSettings, AutoDetectKBDToggled
    }

    CreateGUI()
    CreateGlobalShortcuts()
    CreateHotkey()
    return

; The script

GetSpecialKeysStates() {
    GetKeyState, ScrollState, ScrollLock, T   
    If ScrollState = D
    {
       global zcSCROL := "SCROLL LOCK ON"
    }
    else {
       global zcSCROL := "Scroll lock off"
    }
}

TypedLetter(key) {
    return typed := SubStr(typed key, -NumLetters)
}

OnMousePressed() {
    global tickcount_start := A_TickCount-500

    try {
        key := GetKeyStr()
        typed := "" ; concerning TypedLetter(" ") - it resets the content of the OSD
        ShowHotkey(key)
        SetTimer, HideGUI, % -DisplayTime
    }

    If (MouseBeeper = 1) && (ShowMouseButton = 1) && (ShowSingleKey = 1) || (MouseBeeper = 1) && (ShowSingleKey = 0) && (BeepHiddenKeys = 1)
       soundbeep, 2500, 65

}

OnKeyPressed() {
    try {
        key := GetKeyStr()
        typed := "" ; concerning TypedLetter(" ") - it resets the content of the OSD
        ShowHotkey(key)
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnLetterPressed() {
    try {
        if typed && DeadKeys=1
        {
            sleep, 25    ; this delay helps with dead keys, but it generates errors; the following actions: stringleft,1 and stringlower help correct these
        } else
        {
        }

        key := GetKeyStr(1)     ; consider it a letter

        if prefixed
        {
            ShowHotkey(key)
        } else
        {
            StringLeft, key, key, 1
            Stringlower, key, key

            GetKeyState, CapsState, CapsLock, T
            If CapsState != D
            {
                 if GetKeyState("Shift", "P")
                {
                  StringUpper, key, key
                }
            } else
                StringUpper, key, key

            TypedLetter(key)
            ShowHotkey(typed)
        }
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnSpacePressed() {
    try {
        if typed {
            if (visible)
            {
                TypedLetter("_")
                ShowHotkey(typed)
            } else
            {
                TypedLetter("_")
                ShowHotkey(typed)
                SetTimer, HideGUI, % -DisplayTime
            }
        } else if (!typed)
        {
          key := GetKeyStr()
          ShowHotkey(key)
          SetTimer, HideGUI, % -DisplayTime
        }
    }
}

OnBspPressed() {
    try
    {
        if typed
        {
            typed := SubStr(typed, 1, StrLen(typed) - 1)
            ShowHotkey(typed)
        } else if (!typed)
        {
            key := GetKeyStr()
            ShowHotkey(key)
            SetTimer, HideGUI, % -DisplayTime
        }
    }
    if (BeepHiddenKeys = 1) && (KeyBeeper = 1) && (ShowSingleKey = 0)
       soundbeep, 1900, 45

    if (KeyBeeper = 1) && (ShowSingleKey = 1)
       soundbeep, 1900, 45
}

OnCapsPressed() {
    try
    {
        if typed
        {
            ShowHotkey(typed)
        } else if (!typed)
        {
            key := GetKeyStr()
            GetKeyState, CapsState, CapsLock, T
            if CapsState = D
            {
                key := prefixed ? key : "CAPSLOCK ON"
            } else
                key := prefixed ? key : "CapsLock off"
            ShowHotkey(key)
            SetTimer, HideGUI, % -DisplayTime
        }
    }

    If (CapslockBeeper = 1) && (ShowSingleKey = 1) || (BeepHiddenKeys = 1)
       {
        soundbeep, 450, 200
       }
}

OnNumpadPressed()
{
    GetKeyState, NumState, NumLock, T
    try {
        if NumState != D
        {
            key := GetKeyStr()
            ShowHotkey(key)
            SetTimer, HideGUI, % -DisplayTime
        } else if !typed
        {
            key := GetKeyStr()
            ShowHotkey(key)
        } else if NumState = D
        {
            key := GetKeyStr(1) ; consider it a letter
            StringLeft, key, key, 3
            StringRight, key, key, 1
            TypedLetter(key)
            ShowHotkey(typed)
        }
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnKeyUp() {
    global tickcount_start := A_TickCount

    if typed && (CapslockBeeper = 1) && (ShowSingleKey = 1)
    {
        GetKeyState, CapsState, CapsLock, T
        If CapsState = D
           {
             soundbeep, 450, 25
           }
           else if (KeyBeeper = 1) && (ShowSingleKey = 1)
           {
             soundbeep, 1900, 45
           }
    }

    If (CapslockBeeper = 0) && (KeyBeeper = 1) && (ShowSingleKey = 1)
       {
         soundbeep, 1900, 45
       }
       else if (CapslockBeeper = 1) && (KeyBeeper = 0)
       {
       }
       else if !typed && (CapslockBeeper = 1) && (ShowSingleKey = 1)
       {
         soundbeep, 1900, 45
       }

    if (BeepHiddenKeys = 1) && (KeyBeeper = 1) && (ShowSingleKey = 0)
         soundbeep, 1900, 45  
}

OnModUp() {
    global tickcount_start := A_TickCount

    If (ModBeeper = 1) && (ShowSingleKey = 1) && (ShowSingleModifierKey = 1) || (ModBeeper = 1) && (BeepHiddenKeys = 1)
       soundbeep, 1000, 65

}

; ===================================================================================

CreateGUI() {
    global

    Gui, +AlwaysOnTop -Caption +Owner +LastFound +ToolWindow +E0x20
    Gui, Margin, 10, 10
    Gui, Color, 111111
    Gui, Font, cWhite s%FontSize% bold, Arial, -wrap
    Gui, Add, Text, vHotkeyText left x10 y10 -wrap

;    if TransparencyLevel between 10 and 245
;       WinSet, Transparent, %TransparencyLevel%

}

CreateHotkey() {

    if (CustomRegionalKeys=1) && (AutoDetectKBD=0)
    {
        Loop, parse, RegionalKeysList, |
        {
           Hotkey, % "~*" A_LoopField, OnLetterPressed
           Hotkey, % "~*" A_LoopField " Up", OnKeyUp
        }
    }

   if (AutoDetectKBD=1)
   {
       IdentifyKBDlayout()
   }

    Loop, 95
    {
        k := Chr(A_Index + 31)

        if (DeadKeys=1)
        {
            for each, char2skip in StrSplit("``,^,6,',"",~", ",")        ; dead keys to ignore
            {
                if (k = char2skip && DeadKeys=1)
                {
                    continue, 2
                }
            }
        }

        if (k = " ")
        {
            Hotkey, % "~*Space", OnSpacePressed
            Hotkey, % "~*Space Up", OnKeyUp
        }
        else
        {
            Hotkey, % "~*" k, OnLetterPressed
            Hotkey, % "~*" k " Up", OnKeyUp
        }
    }

    Hotkey, % "~*Backspace", OnBspPressed
    Hotkey, % "~*CapsLock", OnCapsPressed
    Hotkey, % "~*CapsLock Up", OnKeyUp

    Loop, 24 ; F1-F24
    {
        Hotkey, % "~*F" A_Index, OnKeyPressed
        Hotkey, % "~*F" A_Index " Up", OnKeyUp
    }

    Loop, 10 ; Numpad0 - Numpad9
    {
        Hotkey, % "~*Numpad" A_Index - 1, OnNumpadPressed
        Hotkey, % "~*Numpad" A_Index - 1 " Up", OnKeyUp
    }

    NumpadKeysList := "NumpadDot|NumpadDiv|NumpadMult|NumpadAdd|NumpadSub|sc04E|sc04A|sc052|sc04F|sc050|sc051|sc04B|sc04C|sc04D|sc047|sc048|sc049|sc053|sc037|sc135"

    Loop, parse, NumpadKeysList, |
    {
       Hotkey, % "~*" A_LoopField, OnNumpadPressed
       Hotkey, % "~*" A_LoopField " Up", OnKeyUp
    }

    Otherkeys := "WheelDown|WheelUp|WheelLeft|WheelRight|XButton1|XButton2|Browser_Forward|Browser_Back|Browser_Refresh|Browser_Stop|Browser_Search|Browser_Favorites|Browser_Home|Volume_Mute|Volume_Down|Volume_Up|Media_Next|Media_Prev|Media_Stop|Media_Play_Pause|Launch_Mail|Launch_Media|Launch_App1|Launch_App2|Help|Sleep|PrintScreen|CtrlBreak|Break|AppsKey|Tab|Enter|Esc"
               . "|Insert|Home|End|Up|Down|Left|Right|ScrollLock|NumLock|Pause|sc145|sc146|sc046|sc123|sc11C|sc149|sc151|sc122|sc153"
    Loop, parse, Otherkeys, |
    {
        Hotkey, % "~*" A_LoopField, OnKeyPressed
        Hotkey, % "~*" A_LoopField " Up", OnKeyUp
    }

    If ShowMouseButton=1
    {
        Loop, Parse, % "LButton|MButton|RButton", |
        Hotkey, % "~*" A_LoopField, OnMousePressed
    }

    for i, mod in ["LCtrl", "RCtrl", "LAlt", "RAlt"] {
        Hotkey, % "~*" mod, OnKeyPressed
        Hotkey, % "~*" mod " Up", OnModUp
    }

    for i, mod in ["LWin", "RWin"]
        Hotkey, % "~*" mod, OnKeyPressed
        Hotkey, % "~*" mod " Up", OnModUp

    If typed {
    for i, mod in ["LShift", "RShift"]
        Hotkey, % "~*" mod, OnKeyPressed
        Hotkey, % "~*" mod " Up", OnModUp
    }

}

ShowHotkey(HotkeyStr) {

    if (OSDautosize=1)
    {
        HotkeyTextTrimmed := RegExReplace(HotkeyStr, "[^a-zA-Z]", "")
        StringLeft, HotkeyTextTrimmed, HotkeyTextTrimmed, 5
        growthFactor := 1.3
        if HotkeyTextTrimmed is upper
           growthFactor := 1.05
        text_width := (StrLen(HotkeyStr)/growthFactor)*FontSize
        text_width := (text_width<70) ? 70 : text_width+15
    } else if OSDautosize=0
    {
        text_width := GuiWidth
    }

    if !typed
    {
        StringUpper, HotkeyStr, HotkeyStr, T
    }

    GuiControl,     , HotkeyText, %HotkeyStr%
    GuiControl, Move, HotkeyText, w%text_width% left
    Gui, Show, NoActivate x%GuiX% y%GuiY% AutoSize, KeypressOSD
    visible := 1
    SetTimer, checkMousePresence, on, 400
}

GetKeyStr(letter := 0) {
    static modifiers := ["LCtrl", "RCtrl", "LAlt", "RAlt", "LShift", "RShift", "LWin", "RWin"]
    static repeatCount := 1

    ; If any mod but shift, go ; If shift, check if not letter

    for i, mod in modifiers
    {
        if (mod = "LShift" && typed || mod = "RShift" && typed ? (!letter && GetKeyState(mod)) : GetKeyState(mod))
            prefix .= mod " + "
    }

    if (!prefix && !ShowSingleKey)
        throw

    key := SubStr(A_ThisHotkey, 3)

    if (key ~= "i)^(LCtrl|RCtrl|LShift|RShift|LAlt|RAlt|LWin|RWin)$") {
        if (ShowSingleModifierKey = 0) || (ShowSingleKey = 0) || (A_TickCount-tickcount_start > 2000) && visible
        {
            throw
        } else
        {
             key := ""
             prefix := RTrim(prefix, "+ ")
        }

        prefix := CompactModifiers(prefix)

        if (ShowKeyCount=1) {
            if !InStr(prefix, "+") && IsDoubleClickEx() {
                if (A_ThisHotKey != A_PriorHotKey) || (ShowKeyCount=1) && (A_TickCount-tickcount_start < 700) {
                    if (++repeatCount > 1) {
                        prefix .= " (" repeatCount ")"
                    }
                } else {
                    repeatCount := 0
                }
            } else {
                repeatCount := 1
            }
        }
    } else {
        if StrLen(key)=1
        {
            key := GetKeyChar(key, "A")
        } else if ( SubStr(key, 1, 2) = "sc" ) {
            key := SpecialSC(key)
        } else if (key = "Volume_Up") || (key = "Volume_Down") {
            soundbeep, 150, 40
        } else if (key = "PrintScreen") {
            if HideAnnoyingKeys
                throw
            key := "Print Screen"
        } else if (key = "MButton") {
            key := "Middle Click"
        } else if (key = "RButton") {
            key := "Right Click"
        } else if (key = "LButton") && IsDoubleClick() {
            key := "Double-Click"
        } else if (key = "LButton") {
            if HideAnnoyingKeys
                throw
            key := "Left Click"
        }
        {
            _key := (key = "Double-Click") ? "Left Click" : key
        }

        prefix := CompactModifiers(prefix)

        static pre_prefix, pre_key, keyCount := 1
        keyCount := (key=pre_key) && (prefix = pre_prefix) ? keyCount : 1
        global ShowKeyCountDelay := (ShowKeyCountFired = 0) ? 700 : 6000
        ShowKeyCountDelay := (ShowKeyCountFired=1) ? (ShowKeyCountDelay+keyCount*100) : ShowKeyCountDelay

        if (InStr(prefix, "+")) && (A_TickCount-tickcount_start < ShowKeyCountDelay) || (!letter) && (A_TickCount-tickcount_start < ShowKeyCountDelay)
        {
            if (ShowPrevKey=1) && (A_TickCount-tickcount_start < ShowPrevKeyDelay) && !typed && !letter
            {
                ShowPrevKeyValid := 1
            } else
            {
                ShowPrevKeyValid := 0
            }
            if (prefix != pre_prefix) {
                result := (ShowPrevKeyValid=1) ? prefix key " {" pre_prefix pre_key "}" : prefix key
            } else if (ShowPrevKeyValid=1) && (key != pre_key) || (ShowKeyCount=1) && (ShowPrevKeyValid=1)
            {
                keyCount := (key=pre_key) && (ShowKeyCount=1) ? (keyCount+1) : 1
                key := (keyCount>1) && (ShowKeyCount=1) ? (key " (" keyCount ")") : (key ", " pre_key)
            } else if (ShowPrevKeyValid=0)
            {
                keyCount := (key=pre_key) && (ShowKeyCount=1) ? (keyCount+1) : 1
                key := (keyCount>1) ? (key " (" keyCount ")") : (key)
            }
        } else {
            keyCount := 1
        }

        pre_prefix := prefix
        pre_key := _key

        repeatCount := 1
    }

    prefixed := prefix ? 1 : 0
    return result ? result : prefix . key
}

CompactModifiers(stringy)
{
    if DifferModifiers = 1
    {
        StringReplace, stringy, stringy, LCtrl + RAlt, AltGr, All
        StringReplace, stringy, stringy, LCtrl + RCtrl + RAlt, RCtrl + AltGr, All
        StringReplace, stringy, stringy, RAlt, AltGr, All
        StringReplace, stringy, stringy, LAlt, Alt, All
    } else if (DifferModifiers = 0)
    {
        StringReplace, stringy, stringy, LCtrl + RAlt, AltGr, All
        ; StringReplace, stringy, stringy, LCtrl + RCtrl + RAlt, RCtrl + AltGr, All
        StringReplace, stringy, stringy, LCtrl, Ctrl, All
        StringReplace, stringy, stringy, RCtrl, Ctrl, All
        StringReplace, stringy, stringy, LShift, Shift, All
        StringReplace, stringy, stringy, RShift, Shift, All
        StringReplace, stringy, stringy, LAlt, Alt, All
        StringReplace, stringy, stringy, LWin, WinKey, All
        StringReplace, stringy, stringy, RWin, WinKey, All
        StringReplace, stringy, stringy, Ctrl + Ctrl, Ctrl, All
        StringReplace, stringy, stringy, Shift + Shift, Shift, All
        StringReplace, stringy, stringy, WinKey + WinKey, WinKey, All
        StringReplace, stringy, stringy, RAlt, AltGr, All
    }
    return stringy
}

SpecialSC(sc) {
    GetSpecialKeysStates()

    GetKeyState, NumState, NumLock, T
    If NumState = D
    {
       k := {sc046: zcSCROL, sc145: "NUM LOCK ON", sc146: "Pause", sc123: "Genius LuxeMate Scroll", sc04E: "[ + ]", sc04A: "[ - ]", sc052: "[ 0 ]", sc04F: "[ 1 ]", sc050: "[ 2 ]", sc051: "[ 3 ]", sc04B: "[ 4 ]", sc04C: "[ 5 ]", sc04D: "[ 6 ]", sc047: "[ 7 ]", sc048: "[ 8 ]", sc049: "[ 9 ]", sc053: "[ . ]", sc037: "[ * ]", sc135: "[ / ]", sc11C: "[Enter]", sc149: "Page Up", sc151: "Page Down", sc153: "Delete", sc122: "Media_Play/Pause"}
    }
    else {
       k := {sc046: zcSCROL, sc145: "Num lock off", sc146: "Pause", sc123: "Genius LuxeMate Scroll", sc04E: "[ + ]", sc04A: "[ - ]", sc052: "[Insert]", sc04F: "[End]", sc050: "[Down]", sc051: "[Page Down]", sc04B: "[Left]", sc04C: "[Undefined]", sc04D: "[Right]", sc047: "[Home]", sc048: "[Up]", sc049: "[Page Up]", sc053: "[Delete]", sc037: "[ * ]", sc135: "[ / ]", sc11C: "[Enter]", sc149: "Page Up", sc151: "Page Down", sc153: "Delete", sc122: "Media_Play/Pause"}
    }
    return k[sc]
}

; <tmplinshi>: thanks to Lexikos: https://autohotkey.com/board/topic/110808-getkeyname-for-other-languages/#entry682236
; This enables partial support for non-English keyboard layouts.
; If the script initializes with the English keyboard layout, but then used with another one, this function gets proper key names,

GetKeyChar(Key, WinTitle:=0)
{
    thread := WinTitle=0 ? 0
        : DllCall("GetWindowThreadProcessId", "ptr", WinExist(WinTitle), "ptr", 0)
    hkl := DllCall("GetKeyboardLayout", "uint", thread, "ptr")
    vk := GetKeyVK(Key), sc := GetKeySC(Key)
    VarSetCapacity(state, 256, 0)
    VarSetCapacity(char, 4, 0)
    n := DllCall("ToUnicodeEx", "uint", vk, "uint", sc
        , "ptr", &state, "ptr", &char, "int", 2, "uint", 0, "ptr", hkl)
    return StrGet(&char, n, "utf-16")
}
global LangsBinded := 0

IdentifyKBDlayout() {
  VarSetCapacity(kbLayoutRaw, 32, 0)
  DllCall("GetKeyboardLayoutName", "Str", kbLayoutRaw)
  StringRight, kbLayout, kbLayoutRaw, 4

; supported languages
LangRaw_00020409 = ENGLISH_US_INTERNATIONAL
LangRaw_00000409 = ENGLISH_US
LangRaw_00000452 = ENGLISH_UK_EXTENDED
LangRaw_00000809 = ENGLISH_UK
LangRaw_00004009 = ENGLISH_INDIA
LangName_1809 = ENGLISH_IRISH
LangName_0c09 = ENGLISH_AUSTRALIAN
LangName_1409 = ENGLISH_NEW_ZEALAND
LangName_0409 = ENGLISH_US
LangName_0452 = ENGLISH_UK_EXTENDED
LangName_0809 = ENGLISH_UK
LangName_4009 = ENGLISH_INDIA
LangRaw_00020426 = LATVIAN_STANDARD_QWERTY
LangName_0426 = LATVIAN_STANDARD_QWERTY
LangRaw_00010402 = BULGARIAN_LATIN
LangName_0402 = BULGARIAN_LATIN
LangRaw_00050408 = GREEK_LATIN
LangName_0408 = GREEK_LATIN
LangRaw_0000040c = FRENCH_STANDARD
LangName_040c = FRENCH_STANDARD
  LangChars_040c := "&|-|!|²|à|é|è|ç|ù"
LangRaw_00001009 = FRENCH_CANADIAN
LangName_1009 = FRENCH_CANADIAN
  LangChars_1009 := "&|°|-|!|²|é"
LangRaw_00000c0c = FRENCH_CANADIAN_TRADITIONAL
LangName_0c0c = FRENCH_CANADIAN_TRADITIONAL
  LangChars_0c0c := "&|°|-|!|²|à|é|è|ç|ù"
LangRaw_0000080c = FRENCH_BELGIAN
LangName_080c = FRENCH_BELGIAN
  LangChars_080c := "&|°|-|§|!|²|à|é|è|ç|ù|µ"
LangRaw_00000407 = GERMAN_STANDARD
LangName_0407 = GERMAN_STANDARD
  LangChars_0407 := "Ä|ä|Ö|ö|Ü|ü|ẞ|ß"
LangRaw_00010418 = ROMANIAN_STANDARD
LangName_0418 = ROMANIAN_STANDARD
  LangChars_0418 := "„|”|Ă|ă|Â|â|Î|î|Ș|ș|Ț|ț"
LangRaw_00000425 = ESTONIAN
LangName_0425 = ESTONIAN
  LangChars_0425 := "ü|õ|ö|ä|Ü|Õ|Ö|Ä"
LangRaw_00010427 = LITHUANIAN
LangName_0427 = LITHUANIAN
  LangChars_0427 := "ą|č|ę|ė|į|š|ų|ū|ž|Ą|Č|Ę|Ė|Į|Š|Ų|Ū|Ž|x|w|q|f"
LangRaw_00000414 = NORWEGIAN_BOKMAL
LangName_0414 = NORWEGIAN_BOKMAL
  LangChars_0414 := "å|ø|æ|Å|Ø|Æ"
LangRaw_0000043b = NORWEGIAN
LangName_043b = NORWEGIAN
  LangChars_043b := "å|ø|æ|Å|Ø|Æ|å|Å|ŋ|đ"
LangRaw_00000413 = DUTCH_STANDARD
LangName_0413 = DUTCH_STANDARD
  LangChars_0413 := "@|°"
LangRaw_00000816 = PORTUGUESE_STANDARD
LangName_0816 = PORTUGUESE_STANDARD
  LangChars_0816 := "«|ç|º"
LangRaw_0000081a = SERBIAN_LATIN
LangName_081a = SERBIAN_LATIN
  LangChars_081a := "š|đ|ž|č|ć|Š|Đ|Ž|Č|Ć"
LangRaw_00000424 = SLOVENIAN
LangName_0424 = SLOVENIAN
  LangChars_0424 := "š|đ|č|ć|ž|Š|Đ|Č|Ć|Ž"
LangRaw_0000042a = VIETNAMESE
LangName_042a = VIETNAMESE
  LangChars_042a := "ă|â|ê|ô|đ|₫|ư|ơ|Ă|Â|Ê|Ô|Đ|₫|Ư|Ơ"
LangRaw_0001041f = TURKISH_F
LangName_041f = TURKISH_F
  LangChars_041f := "ğ|ı|ş|ç|ö|ü|i|Ğ|I|Ş|Ç|Ö|Ü|I|i|İ"
LangRaw_00000405 = CZECH
LangName_0405 = CZECH
  LangChars_0405 := ")|§|ů|ú|ě|š|č|ř|ž|ý|á|í|é|Ů|Ú|Ě|Š|Č|Ř|Ž|Ý|Á|Í|É"
LangRaw_00000406 = DANISH
LangName_0406 = DANISH
  LangChars_0406 := "å|æ|ø|Å|Æ|Ø|½"
LangRaw_0000046f = GREENLAND
LangName_046f = GREENLAND
  LangChars_046f := "å|æ|ø|Å|Æ|Ø|½"
LangRaw_0000040e = HUNGARIAN
LangName_040e = HUNGARIAN
  LangChars_040e := "ö|ü|ó|ő|ú|ű|é|á|Ö|Ü|Ó|Ő|Ú|Ű|É|Á"
LangRaw_0000040a = SPANISH_TRADITIONAL
LangName_040a = SPANISH_TRADITIONAL
  LangChars_040a := "¿|ñ|Ñ|ç|Ç|¡|º"
LangRaw_0000080a = SPANISH_MEXICAN
LangName_080a = SPANISH_MEXICAN
  LangChars_080a := "¿|ñ|Ñ"
LangRaw_0000041b = SLOVAK
LangName_041b = SLOVAK
  LangChars_041b := "š|č|ť|ž|ý|á|í|é|ň|ä|ú|ô|§|ľ"
LangRaw_0000040f = ICELANDIC
LangName_040f = ICELANDIC
  LangChars_040f := "ö|ð|æ|þ|Ö|Ð|Æ|Þ"
LangRaw_0002083b = SWEDISH_FINLAND_EXTENDED
LangName_083b = SWEDISH_FINLAND
  LangChars_083b := "§|á|š|č|ŧ|å|ŋ|đ|ö|ä|Á|Š|Č|Ŧ|Å|Ŋ|Đ|Ö|Ä"
LangRaw_0000040b = FINNISH
LangName_040b = FINNISH
  LangChars_040b := "§|å|ö|ä|Å|Ö|Ä"
LangRaw_00010410 = ITALIAN_142
LangName_0410 = ITALIAN_142
  LangChars_0410 := "è|ò|à|ì"
LangRaw_0000041d = SWEDISH
LangName_041d = SWEDISH
  LangChars_041d := "§|å|ö|ä|Å|Ö|Ä"
LangRaw_00000416 = PORTUGUESE_BRAZILIAN
LangName_0416 = PORTUGUESE_BRAZILIAN
  LangChars_0416 := "ç|Ç"


; unsupported languages
LangName_041c = -ALBANIAN
LangName_0436 = -AFRIKAANS
LangName_0401 = -ARABIC_SAUDI_ARABIA
LangName_0801 = -ARABIC_IRAQ
LangName_0c01 = -ARABIC_EGYPT
LangName_0401 = -ARABIC_SAUDI_ARABIA
LangName_0801 = -ARABIC_IRAQ
LangName_0c01 = -ARABIC_EGYPT
LangName_1001 = -ARABIC_LIBYA
LangName_1401 = -ARABIC_ALGERIA
LangName_1801 = -ARABIC_MOROCCO
LangName_1c01 = -ARABIC_TUNISIA
LangName_2001 = -ARABIC_OMAN
LangName_2401 = -ARABIC_YEMEN
LangName_2801 = -ARABIC_SYRIA
LangName_2c01 = -ARABIC_JORDAN
LangName_3001 = -ARABIC_LEBANON
LangName_3401 = -ARABIC_KUWAIT
LangName_3801 = -ARABIC_UAE
LangName_3c01 = -ARABIC_BAHRAIN
LangName_4001 = -ARABIC_QATAR
LangName_042b = -ARMENIAN
LangName_042c = -AZERI_LATIN
LangName_082c = -AZERI_CYRILLIC
LangName_042d = -BASQUE
LangName_0423 = -BELARUSIAN
LangName_0402 = -BULGARIAN
LangName_0403 = -CATALAN
LangName_0404 = -CHINESE_TAIWAN
LangName_0804 = -CHINESE_PRC
LangName_0c04 = -CHINESE_HONG_KONG
LangName_1004 = -CHINESE_SINGAPORE
LangName_1404 = -CHINESE_MACAU
LangName_041a = -CROATIAN
LangName_0813 = -DUTCH_BELGIAN
LangName_1009 = -ENGLISH_CANADIAN
LangName_1c09 = -ENGLISH_SOUTH_AFRICA
LangName_2009 = -ENGLISH_JAMAICA
LangName_2409 = -ENGLISH_CARIBBEAN
LangName_2809 = -ENGLISH_BELIZE
LangName_2c09 = -ENGLISH_TRINIDAD
LangName_3009 = -ENGLISH_ZIMBABWE
LangName_3409 = -ENGLISH_PHILIPPINES
LangName_0438 = -FAEROESE
LangName_0429 = -FARSI
LangName_100c = -FRENCH_SWISS
LangName_140c = -FRENCH_LUXEMBOURG
LangName_180c = -FRENCH_MONACO
LangName_0437 = -GEORGIAN
LangName_0c07 = -GERMAN_AUSTRIAN
LangName_0807 = -GERMAN_SWISS
LangName_1007 = -GERMAN_LUXEMBOURG
LangName_1407 = -GERMAN_LIECHTENSTEIN
LangName_040d = -HEBREW
LangName_0439 = -HINDI
LangName_0421 = -INDONESIAN
LangName_0810 = -ITALIAN_SWISS
LangName_0411 = -JAPANESE
LangName_043f = -KAZAKH
LangName_0457 = -KONKANI
LangName_0412 = -KOREAN
LangName_042f = -MACEDONIAN
LangName_043e = -MALAY_MALAYSIA
LangName_083e = -MALAY_BRUNEI_DARUSSALAM
LangName_044e = -MARATHI
LangName_0814 = -NORWEGIAN_NYNORSK
LangName_0415 = -POLISH
LangName_0419 = -RUSSIAN
LangName_044f = -SANSKRIT
LangName_0c1a = -SERBIAN_CYRILLIC
LangName_2c0a = -SPANISH_ARGENTINA
LangName_400a = -SPANISH_BOLIVIA
LangName_340a = -SPANISH_CHILE
LangName_240a = -SPANISH_COLOMBIA
LangName_140a = -SPANISH_COSTA_RICA
LangName_1c0a = -SPANISH_DOMINICAN_REPUBLIC
LangName_300a = -SPANISH_ECUADOR
LangName_440a = -SPANISH_EL_SALVADOR
LangName_100a = -SPANISH_GUATEMALA
LangName_480a = -SPANISH_HONDURAS
LangName_0c0a = -SPANISH_MODERN_SORT
LangName_4c0a = -SPANISH_NICARAGUA
LangName_180a = -SPANISH_PANAMA
LangName_3c0a = -SPANISH_PARAGUAY
LangName_500a = -SPANISH_PUERTO_RICO
LangName_280a = -SPANISH_PERU
LangName_380a = -SPANISH_URUGUAY
LangName_200a = -SPANISH_VENEZUELA
LangName_0441 = -SWAHILI
LangName_081d = -SWEDISH_FINLAND
LangName_0449 = -TAMIL
LangName_0444 = -TATAR
LangName_041e = -THAI
LangName_0422 = -UKRAINIAN
LangName_0420 = -URDU
LangName_0443 = -UZBEK_LATIN
LangName_0843 = -UZBEK_CYRILLIC
LangName_0417 = -RAETO-ROMANCE
LangName_0428 = -TAJIK
LangName_0431 = -TSONGA
LangName_0432 = -SETSUANA
LangName_0433 = -VENDA
LangName_0434 = -XHOSA
LangName_0435 = -ZULU
LangName_0440 = -KYRGYZ_CYRILLIC
LangName_0442 = -TURKMEN
LangName_0445 = -BENGALI_INDIA
LangName_0446 = -PUNJABI
LangName_0447 = -GUJARATI
LangName_0448 = -ORIYA
LangName_0450 = -MONGOLIAN
LangName_0451 = -TIBETAN
LangName_0438 = -FAROESE
LangName_0452 = -WELSH
LangName_0453 = -KHMER
LangName_0454 = -LAO
LangName_0455 = -BURMESE
LangName_0456 = -GALICIAN
LangName_0458 = -MANIPURI
LangName_0459 = -SINDHI
LangName_0460 = -KASHMIRI
LangName_0461 = -NEPALI
LangName_0462 = -FRISIAN_NETHERLANDS
LangName_0464 = -FILIPINO
LangName_0466 = -EDO
LangName_0470 = -IGBO_NIGERIA
LangName_0474 = -GUARANI_PARAGUAY
LangName_0476 = -LATIN
LangName_0477 = -SOMALI
LangName_0481 = -MAORI
LangName_0819 = -RUSSIAN_MOLDOVA
LangName_0845 = -BENGALI_BANGLADESH
LangName_0850 = -MONGOLIAN
LangName_1115 = -SINHALA

  check_kbd := StrLen(LangName_%kbLayout%)>2 ? 1 : 0
  check_kbd_exact := StrLen(LangRaw_%kbLayoutRaw%)>2 ? 1 : 0
  if (check_kbd_exact=0)
  {
      partialKBDmatch = (Partial match)
  }

  if (check_kbd=0)
  {
      ShowHotkey("Unrecognized layout: (kbd " kbLayoutRaw ").")
      SetTimer, HideGUI, % -DisplayTime
      soundbeep, 500, 900
  }

  StringLeft, kbLayoutSupport, LangName_%kbLayout%, 1
  if (kbLayoutSupport="-") && (check_kbd=1)
  {
      ShowHotkey("Unsupported layout: " LangName_%kbLayout% " (kbd" kbLayout ").")
      SetTimer, HideGUI, % -DisplayTime
      soundbeep, 500, 900
  }

  IfInString, LangsBinded, %kbLayout%
  {
      KBDbinded := 1
  } else
  {
      LangsBinded := kbLayout "|" LangsBinded 
      StringLeft, LangsBinded, LangsBinded, 20
      KBDbinded := 0
  }

  if (kbLayoutSupport!="-") && (check_kbd=1) && (KBDbinded=0)
  {

      Loop, parse, LangChars_%kbLayout%, |
      {
         Hotkey, % "~*" A_LoopField, OnLetterPressed
         Hotkey, % "~*" A_LoopField " Up", OnKeyUp
      }
      if SilentDetection=0
      {
          ShowHotkey("Keyboard layout: " LangName_%kbLayout% " (kbd" kbLayout "). " partialKBDmatch)
          SetTimer, HideGUI, % -DisplayTime/4
      }
  }

   if (ConstantAutoDetect=1) && (AutoDetectKBD=1)
      SetTimer, ConstantKBDchecker, 1500
}

ConstantKBDchecker() {
  SetFormat, Integer, H
  WinGet, WinID,, A
  ThreadID := DllCall("GetWindowThreadProcessId", "UInt", WinID, "UInt", 0)
  NewInputLocaleID := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")
    if (InputLocaleID != NewInputLocaleID)
    {
        InputLocaleID := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")
        
        if SilentDetection=0
        ShowHotkey("Changed keyboard layout: " InputLocaleID)
        
        sleep, 350
        Reload
        ;WinActivate, KeypressOSD
        ;SetTimer, HideGUI, % -DisplayTime/3
        ;IdentifyKBDlayout()
    } 
}

IsDoubleClick(MSec = 300) {
    Return (A_ThisHotKey = A_PriorHotKey) && (A_TimeSincePriorHotkey < MSec)
}

IsDoubleClickEx(MSec = 300) {
    preHotkey := RegExReplace(A_PriorHotkey, "i) Up$")
    Return (A_ThisHotKey = preHotkey) && (A_TimeSincePriorHotkey < MSec)
}

HideGUI() {
    visible := 0
    Gui, Hide
    SetTimer, checkMousePresence, off, 300

}

checkMousePresence() {
    id := mouseIsOver()
    title := getWinTitleFromID(id)
    if title = KeypressOSD
    {
        HideGUI()
        sleep, 1000
    }
}

mouseIsOver() {
    MouseGetPos,,, id
    return id
}

getWinTitleFromID(id) {
    WinGetTitle, title, % "ahk_id " id
    return title
}

CreateGlobalShortcuts() {
   if (KeyboardShortcuts=1) {
      Hotkey, !+^F8, ToggleShowSingleKey
      Hotkey, !+^F9, TogglePosition
      Hotkey, !+^F10, EnableCustomKeys
      Hotkey, !+^F11, DetectLangNow
      Hotkey, !+^F12, ReloadScript
    }
}

ToggleShowSingleKey:
    ShowSingleKey := (!ShowSingleKey) ? true : false
    Gui, Destroy
    sleep, 100
    CreateGUI()
    sleep, 100
    IniWrite, %ShowSingleKey%, keypress-osd.ini, PermanentSettings, ShowSingleKey
    ShowHotkey("Show single keys = " ShowSingleKey)
    SetTimer, HideGUI, % -DisplayTime
return

TogglePosition:
    GuiY := (GuiY = GuiYb) ? GuiYa : GuiYb
    Gui, Destroy
    sleep, 100
    CreateGUI()
    sleep, 100
    IniWrite, %GuiY%, keypress-osd.ini, PermanentSettings, GuiY
    ShowHotkey("OSD position changed")
    SetTimer, HideGUI, % -DisplayTime
return

EnableCustomKeys:
    global CustomRegionalKeys := CustomRegionalKeys = 1 ? 0 : 1
    CustomMultiLangToggled := 1
    IniWrite, %CustomMultiLangToggled%, keypress-osd.ini, PermanentSettings, CustomMultiLangToggled
    IniWrite, %CustomRegionalKeys%, keypress-osd.ini, PermanentSettings, CustomRegionalKeys
    ShowHotkey("Custom Regional keys = " RegionalKeys)
    sleep, 1000
    Reload
return

DetectLangNow:
    global AutoDetectKBD := 1
    global ConstantAutoDetect := 0
    AutoDetectKBDToggled := 1
    IniWrite, %AutoDetectKBDToggled%, keypress-osd.ini, PermanentSettings, AutoDetectKBDToggled
    IniWrite, %AutoDetectKBD%, keypress-osd.ini, PermanentSettings, AutoDetectKBD
    IniWrite, %ConstantAutoDetect%, keypress-osd.ini, PermanentSettings, ConstantAutoDetect
    ShowHotkey("Detecting keyboard layout...")
    sleep, 800
    Reload
return

ReloadScript:
    Gui, Destroy
    sleep, 100
    CreateGUI()
    sleep, 100
    ShowHotkey("OSD reinitializing...")
    sleep, 1500
    Reload
return
Critique and feedback, always welcomed.

I tested it only on Windows 10 x64, which no longer switches keyboards based on apps, only when you want it.

Best regards, Marius.
Last edited by robodesign on 08 Oct 2017, 11:56, edited 1 time in total.
-------------------------
KeyPress OSD v4: GitHub or forum. (presentation video)
My home page.
User avatar
Drugwash
Posts: 567
Joined: 29 May 2014, 21:07
Location: Ploieşti, Romania

Re: [Script] KeypressOSD - Display key press on screen

Post by Drugwash » 08 Oct 2017, 10:03

This error comes up at launch, nothing else would work:
20171008180030.png
20171008180030.png (30.13 KiB) Viewed 1217 times
I've deleted my CloudMe account because of GDPR - the now legal base for privacy invasion and data theft.
robodesign
Posts: 371
Joined: 30 Sep 2017, 03:59
Facebook: marius.sucan
GitHub: mariussucan
Location: Romania
Contact:

Re: [Script] KeypressOSD - Display key press on screen

Post by robodesign » 08 Oct 2017, 11:55

remove the quotes from line 811, LangChars_0418 := "„|”|Ă|ă|Â|â|Î|î|Ș|ș|Ț|ț"
. Here it works with them.

I tested it only on Win10. Things are different here. Detection may also be tricky on your system, if it changes language for every app.

I must do some error control :-) .
-------------------------
KeyPress OSD v4: GitHub or forum. (presentation video)
My home page.
User avatar
Drugwash
Posts: 567
Joined: 29 May 2014, 21:07
Location: Ploieşti, Romania

Re: [Script] KeypressOSD - Display key press on screen

Post by Drugwash » 08 Oct 2017, 12:30

There is no 'lower doublequote' on RO layout.
My current system is XP-SP3 x86 and the only installed layout is RO; EN is not needed since it's a subset of RO. So there is no app-specific layout change at all.
Also please note the last four chars in the string (Ş|ş|Ţ|ţ) appear as question marks here. You may wanna use a different text editor that does not screw up characters.
Correct string should be LangChars_0418 := """|Ă|ă|Â|â|Î|î|Ş|ş|Ţ|ţ"

Other issues:
- script shows Shift+] when I type [, I'd really like it to show the real character instead of the key combo.
- sometimes it shows Shift+A (which should actually be Shift+a) while other times it just shows capital A (as it always should).
I've deleted my CloudMe account because of GDPR - the now legal base for privacy invasion and data theft.
robodesign
Posts: 371
Joined: 30 Sep 2017, 03:59
Facebook: marius.sucan
GitHub: mariussucan
Location: Romania
Contact:

Re: [Script] KeypressOSD - Display key press on screen

Post by robodesign » 08 Oct 2017, 13:01

Please read the to-do list. I want to have that implemented as well, hence it is in my to-do.

In Windows 10, Romanian keyboard is different :) ...and I suspect many other languages changed as well.. I will stick to the Win10 keyboard layout which has those special quotes. There are 3 variations for the Romanian layout: inherited, standard and programmers. I chose the standard one.

Top priority is to make it handle/catch errors when trying to bind keys.
-------------------------
KeyPress OSD v4: GitHub or forum. (presentation video)
My home page.
User avatar
Drugwash
Posts: 567
Joined: 29 May 2014, 21:07
Location: Ploieşti, Romania

Re: [Script] KeypressOSD - Display key press on screen

Post by Drugwash » 09 Oct 2017, 10:59

The layout that matches the one in Win95 through XP is 'legacy' (or inherited, as you say). There should be a way to distinguish each variant and adjust accordingly.
Good luck!
I've deleted my CloudMe account because of GDPR - the now legal base for privacy invasion and data theft.
robodesign
Posts: 371
Joined: 30 Sep 2017, 03:59
Facebook: marius.sucan
GitHub: mariussucan
Location: Romania
Contact:

Re: [Script] KeypressOSD - Display key press on screen

Post by robodesign » 09 Oct 2017, 13:31

Hello!

I made it now to catch/handle errors. It should be more efficient at assigning keys. Failures are limited now only to the key that cannot be assigned, no longer all in the same raw fail. It beeps for each key that failed to bind, but that should not worry you if it works :).

Languages are now in an external file. Place it in the same folder with the script. Make sure both files are UTF-8 with BOM.

Drugwash, it should work on WinXP as well, with your kbd layout.

Source at http://p.ahkscript.org/?p=465b53a1
Source of languages file: http://p.ahkscript.org/?p=fe8603b5

; CHANGELOG:
; by Marius Sucan (robodesign.ro)
; v2.87 (2017-10-09)
; - improvements for assigning keys;
; - errors are now managed, it will beep when key bindings fail
; - [new] option to disable audio alerts :)
; - languages are now in an external file
; - new keyboard layouts for Polish, Azerbaijian, Turkmen

Code: Select all

; KeypressOSD.ahk
; Charset for this file must be UTF 8 with BOM.
; it will not function properly otherwise.
;--------------------------------------------------------------------------------------------------------------------------
;
; AVAILABLE SHORTCUTS:
; Ctrl+Alt+Shift+F8  - Toggles "Show single key" option. Useful when you must reliably use dead keys.
; Ctrl+Alt+Shift+F9  - Toggles between two OSD offsets: GuiYa and GuiYb.
; Ctrl+Alt+Shift+F10 - Toggles personal/regional keys support.
; Ctrl+Alt+Shift+F11 - Detect keyboard language.
; Ctrl+Alt+Shift+F12 - Reinitialize OSD. Useful when it no longer appears on top. To have it appear on top of elevated apps, run it in administrator mode.
;
; NOTES:
; This script was made for, and on Windows 10.
; The keyboard layouts have changed since Win XP or Win 98.
; Windows 10 also no longer switches keyboard layouts based
; on the currently active app. As such, automatic keyboard
; layout detection may not work for you.
; 
; I do not intend to offer support for older Windows versions.
; 
; This script has support only for Latin-based keyboards.
; Thus, it has no support for Chinese, Japanese, chirilic, 
; It is too complex for me to implement support for other alphabets or writing systems.
; If other programmers willing to invest the time in this script,
; are welcomed to do so, and even to transform it into anything they wish. 
;
; I offer numerous options/settings in the script such that
; everyone can find a way to adapt it to personal needs.
; - you can edit, in the code, what are the dead keys
;   - see Loop, 95, char2skip from CreateHotkey() function
; - you can also define personal regional keys, while autodetect is disabled
; - disable dead keys if you do not have such keys
; 
; Read the messages you get:
; - it indicates when your keyboard layout is unsupported or Unrecognized
; - it also indicates if it made a partial match;
;   - in such cases, you will likely not have all the keys
;   - or simply AHK will give errors trying to bind to inexistent keys
; - if the external file is missing, languages.ini, it will always report
; that it did not detect your keyboard.
;
; Default/built in language support is for English International.
;
; For the layouts I added support, I avoided binding to dead keys
; such that you no longer have to add them manually, as indicated previously.
; 
; If you rely only on the "vanilla" version, you will likely 
; not be able to use it.
;
; I am no programmer and the script is still quite quirky, but I am trying to
; make it better and better with each version.
;
; FEATURES:
; - Show previously pressed key if fired quickly.
; - Count key presses or key fires and mouse clicks.
; - Automatic resizing of OSD/HUD or fixed size.
; - Hides automatically when mouse runs over it.
; - Generate beeps for key presses, modifiers, mouse clicks or just when typing with Capslock.
; - Indicators for CapsLock, NumLock and ScrollLock states.
; - Typing mode; shows what you are typing in an expanding text area.
; - Partial dead keys support, option to turn it off; The work-around is only for the English International keyboard layout.
; - Partial support for many non-English keyboards. 40 keyboard layouts defined.
;   - limited automatic detection of keyboard layouts.
;   - the user also has the option to define his regional keys.
; - Easy to configure with many options:
;   - to toggle features: key beepers, key counting or previous key;
;   - to hide modifiers, mouse clicks or single key presses (which disables typing mode);
;   - or hide keys that usually get in the way: Left Click and Print Screen [HideAnnoyingKeys];
;   - differ between left and right modifiers;
;   - OSD/HUD position, size and display time;
;   - beep key presses even if keys are not displayed;
;   - some settings toggled by shortcuts are stored permanently in an INI file;
;
; CHANGELOG:
; by Marius Sucan (robodesign.ro)
;   v2.87 (2017-10-09)
;   - improvements for assigning keys;
;     - errors are now managed, it will beep when key bindings fail
;   - [new] option to disable audio alerts :)
;   - languages are now in an external file
;   - new keyboard layouts for Polish, Azerbaijian, Turkmen
;   v2.85 (2017-10-08)
;   - minor improvements for the dead keys work-around
;   - added support for 40 foreign keyboard layouts
;   - automatic detection of keyboard layouts, at start or continously
;   - user also can define personal/regional keys in the settings section
;   - new keyboard shortcut, to toggle regional keys
;   - new setting: to enable or disable system-wide keyboard shortcuts
;   - settings toggled by shortcuts are stored permanently in an INI file
;     - except for the option of regional keys support
;   v2.73 (2017-10-05)
;   - improvements for automatic resize calculation
;   v2.72 (2017-10-04)
;   - new option: make beeps on mouse clicks
;   - new option: dstinct beeper for modifier keys: Shift, Ctrl, Alt, WinKey.
;   - new option: beep hidden keys; if you want it to beep even if the keys are not displayed
;   - fix: now it always counts mouse clicks
;   v2.70 (2017-10-03)
;   - added option to toggle dead keys support / work-around.
;   - added option to hide annoying keys that usually get in the way: Left Click and Print Screen.
;   - redraw improvements, reduced flickering
;   - disabled OSD transparency to reduce flickering
;   v2.69 (2017-09-30)
;   - OSD / GUI hides when mouse is over it. Many thanks to phaleth!
;   v2.68 (2017-09-29)
;   - numpad keys now work in typing mode as expected; they appear as symbols or numbers
;
; by phaleth from irc.freenode.net #ahk
;   v2.67 (2017-09-28)
;   - dead keys improvements
;
; by Marius Sucan (robodesign.ro)
;   v2.66 (2017-09-28)
;   - key combinations with Shift work better.
;   v2.65 (2017-09-27)
;   - Fixed a bug with counting modifier keys;
;   - improved the dead keys work-around
;   - friendly names for mouse clicks
;   - when pressed, volume keys  always generate beeps
;   - added option to differentiate between left and right modifiers
;   - now it detects AltGr key
;   - the key beeper now also makes a beep for modifiers.
;   - capslock no longer erases text you are typing displayed by the OSD
;   - now you can toggle between two different OSD positions with Ctrl + Alt + Shift + F9
;   v2.60 (2017-09-26)
;   - Fixed many bugs with counting keys;
;   - reimplemented the feature to see the previous key combination, if quickly a new one is pressed;
;   - added options/settings to toggle previous keys, counting keys and delay;
;   - added shortcuts to toggle ShowSingleKey option and to reinitialize the OSD;
;   - added option for automatic resizing of the OSD; it can be turned off in the settings section; it is a fishy implementation, but if one adjusts it, can make it to suit personal needs;
;   - new option: beep when key is released or when writing with capslock
;   v2.58 (2017-09-23)
;   - Numpad keys have friendly naming, based on the numlock state.
;   - Combinations with space and backspace work again.
;   v2.56 (2017-09-22)
;   - more fixes for space usage and key combinations;
;   - now it indicates when ScrollLock, NumLock and Capslock are activated.
;   v2.55 (2017-09-21)
;   - minor fixes for space usage and key combinations.
;
; by Saiapatsu from irc.freenode.net #ahk
;   v2.54 (2017-09-21)
;   - Scrolls through n recently typed characters instead of just the latest word
;   v2.53 (2017-09-21)
;   - Case change effect limited to the loop 95 letters only.
;   v2.52 (2017-09-21)
;   - Now supports backspace. Commented out CapsLock beeper.
;   v2.51 (2017-09-21)
;   - Changed labels to functions, added ToolWindow style to window, changed DisplayTime
;   calculation, made it show last word typed, hid spacebar presses
;   todo: make Shift look less ugly
;
; by Marius Sucan (robodesign.ro)
;   v2.50 (2017-09-20)
;   - Changed the OSD positioning and sizing. It was based on the current window. Now it is always fixed in a specific place. Added a Capslock beeper.
;
; by tmplinshi from https://autohotkey.com/boards/viewtopic.php?f=6&t=225
;   v2.22 (2017-02-25)
;   - Now pressing same combination keys continuously more than 2 times,
;   for example press Ctrl+V 3 times, will displayed as "Ctrl + v (3)"
;   v2.21 (2017-02-24)
;   - Fixed LWin/RWin not poping up start menu
;   v2.20 (2017-02-24)
;   - Added displaying continuous-pressed combination keys.
;   e.g.: With CTRL key held down, pressing K and U continuously will shown as "Ctrl + k, u"
;   v2.10 (2017-01-22)
;   - Added ShowStickyModKeyCount option
;   v2.09 (2017-01-22)
;   - Added ShowModifierKeyCount option
;   v2.08 (2017-01-19)
;   - Fixed a bug
;   v2.07 (2017-01-19)
;   - Added ShowSingleModifierKey option (default is True)
;   v2.06 (2016-11-23)
;   - Added more keys. Thanks to SashaChernykh.
;   v2.05 (2016-10-01)
;   - Fixed not detecting "Ctrl + ScrollLock/NumLock/Pause". Thanks to lexikos.
;   v2.04 (2016-10-01)
;   - Added NumpadDot and AppsKey
;   v2.03 (2016-09-17)
;   - Added displaying "Double-Click" of the left mouse button.
;   v2.02 (2016-09-16)
;   - Added displaying mouse button, and 3 settings (ShowMouseButton, FontSize, GuiHeight)
;   v2.01 (2016-09-11)
;   - Display non english keyboard layout characters when combine with modifer keys.
;   v2.00 (2016-09-01)
;   - Removed the "Fade out" effect because of its buggy.
;   - Added support for non english keyboard layout.
;   - Added GuiPosition setting.
;   v1.00 (2013-10-11)
;   - First release by tmplinshi based on RaptorX. Function keys, numpad keys and mouse clicks support. Popups at mouse position.
;   v0.50 (2010-03-18)
;   - Released by RaptorX.
;--------------------------------------------------------------------------------------------------------------------------
;
; TO-DO:::::::::::::::::::::::::::::::::::
; features to implement:
; - visual mouse clicks
; - when user types single letters, display Shift + (sign or numeric keys) as signs/symbols, eg. Shift + 0, should be displayed as );
; - after pressing Shift + [letter] if the following key is a single letter, remember the first Shift + [letter];
; - after a Numpad key is pressed, typing mode should begin if the second one is another numpad key;
; - show a generic symbol for ignored dead keys; helps to clarify one was pressed;
;
; glitches to fix [by priority]:
; - [regression since v2.52] show Shift as a modifier, as Ctrl and Alt; Shift should behave as the other two; it is never displayed alone and countable;
; - once a key is pressed, counting of key fires is not initiated after the OSD was hidden for a little awhile;
; - make dead keys work better
; - automatic resizing of the OSD/GUI is just a silly hack based on the default font size and the number of typed chars; it often fails to resize properly;
; - redraw issues; it still flickers;
; - make it work reliably with sticky keys; if user presses once Ctrl and another key afterwards, it rarely detects the combination on Winndows 7;
;----------------------------------------------------------------------------

; Initialization
#SingleInstance force
#NoEnv
#MaxHotkeysPerInterval 500
SetBatchLines, -1
ListLines, Off

; Settings:
    global DeadKeys              := 0     ; a toggle for a partial dead keys support. Zero [0] means no dead keys. See CreateHotkey() and char2skip to define the dead keys.
    global CustomRegionalKeys    := 0     ; if you want to add support to a regional keyboard
    global RegionalKeysList      := "a|b|c"  ; add the characters in this list, separated by |
    global AutoDetectKBD         := 1     ; at start, detect keyboard layout
    global ConstantAutoDetect    := 0     ; continously check if the keyboard layout changed; if AutoDetectKBD=0, this is ignored
    global SilentDetection       := 0     ; do not display information about language switching
    global audioAlerts           := 1     ; generate beeps when key bindings fail

    global ShowSingleKey         := 1     ; show only key combinations
    global ShowMouseButton       := 1     ; if disabled, mouse click beeps will never occur
    global HideAnnoyingKeys      := 1     ; Left click and PrintScreen can easily get in the way.
    global ShowSingleModifierKey := 1     ; make it display Ctrl, Alt, Shift when pressed alone
    global DifferModifiers       := 0     ; differentiate between left and right modifiers
    global ShowPrevKey           := 1     ; show previously pressed key, if pressed quickly in succession
    global ShowPrevKeyDelay      := 300
    global ShowKeyCount          := 1     ; count how many times a key is pressed
    global ShowKeyCountFired     := 1     ; show only key presses (0) or catch key fires as well (1)

;   global TransparencyLevel     := 220   ; OSD transparency; range from 0 to 255. feature disabled, to reduce flickering
    global DisplayTime           := 3000  ; in milliseconds
    global GuiWidth              := 360
    global GuiHeight             := 50
    global GuiX                  := 50
    global GuiYa                 := 250   ; toggle between GuiYa and GuiYb with Ctrl + Alt + Shift + F9
    global GuiYb                 := 800
    global FontSize              := 19
    global OSDautosize           := 1     ; make adjustments in ShowHotkey() to match your font size
    global NumLetters            := 25    ; amount of recently typed letters to display
    global NumLettersAutosize    := 60    ; ...when OSD resizes automatically

    global CapslockBeeper        := 1     ; only when the key is released
    global KeyBeeper             := 0     ; only when the key is released
    global ModBeeper             := 0     ; beeps for every modifier, when released
    global MouseBeeper           := 0
    global BeepHiddenKeys        := 0     ; [when any beeper enabled] to beep or not when keys are not displayed by OSD/HUD

    global KeyboardShortcuts     := 1     ; system-wide shortcuts


; Initialization variables. Altering these may lead to undesired results.
    global typed := ""
    global visible := 0
    global GuiY := GuiYb
    global prefixed := 0 ; hack used to determine if last keypress had a modifier
    global zcSCROL := "SCROLL LOCK"
    global tickcount_start := 0   ; timer to count repeated key presses
    global keyCount := 0
    global ShowKeyCountDelay := (ShowKeyCountFired = 0) ? 700 : 6000
    NumLetters := (OSDautosize=1) ? NumLettersAutosize : NumLetters
    global text_width := 60
    global InputLocaleID := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")

    IniRead, GuiY, keypress-osd.ini, PermanentSettings, GuiY
    GuiY := (GuiY=GuiYb || GuiY=GuiYa) ? GuiY : GuiYb
    IniRead, ShowSingleKey, keypress-osd.ini, PermanentSettings, ShowSingleKey
    IniRead, CustomMultiLangToggled, keypress-osd.ini, PermanentSettings, CustomMultiLangToggled
    if (CustomMultiLangToggled=1) {
        IniRead, CustomRegionalKeys, keypress-osd.ini, PermanentSettings, CustomRegionalKeys
        CustomMultiLangToggled := 0
        IniWrite, %CustomMultiLangToggled%, keypress-osd.ini, PermanentSettings, CustomMultiLangToggled
    }

    IniRead, AutoDetectKBDToggled, keypress-osd.ini, PermanentSettings, AutoDetectKBDToggled
    if (AutoDetectKBDToggled=1) {
        IniRead, AutoDetectKBD, keypress-osd.ini, PermanentSettings, AutoDetectKBD
        IniRead, ConstantAutoDetect, keypress-osd.ini, PermanentSettings, ConstantAutoDetect
        AutoDetectKBDToggled := 0
        IniWrite, %AutoDetectKBDToggled%, keypress-osd.ini, PermanentSettings, AutoDetectKBDToggled
    }

    CreateGUI()
    CreateGlobalShortcuts()
    CreateHotkey()
    return

; The script

GetSpecialKeysStates() {
    GetKeyState, ScrollState, ScrollLock, T   
    If ScrollState = D
    {
       global zcSCROL := "SCROLL LOCK ON"
    }
    else {
       global zcSCROL := "Scroll lock off"
    }
}

TypedLetter(key) {
    return typed := SubStr(typed key, -NumLetters)
}

OnMousePressed() {
    global tickcount_start := A_TickCount-500

    try {
        key := GetKeyStr()
        typed := "" ; concerning TypedLetter(" ") - it resets the content of the OSD
        ShowHotkey(key)
        SetTimer, HideGUI, % -DisplayTime
    }

    If (MouseBeeper = 1) && (ShowMouseButton = 1) && (ShowSingleKey = 1) || (MouseBeeper = 1) && (ShowSingleKey = 0) && (BeepHiddenKeys = 1)
       soundbeep, 2500, 65

}

OnKeyPressed() {
    try {
        key := GetKeyStr()
        typed := "" ; concerning TypedLetter(" ") - it resets the content of the OSD
        ShowHotkey(key)
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnLetterPressed() {
    try {
        if typed && DeadKeys=1
        {
            sleep, 25    ; this delay helps with dead keys, but it generates errors; the following actions: stringleft,1 and stringlower help correct these
        } else
        {
        }

        key := GetKeyStr(1)     ; consider it a letter

        if prefixed
        {
            ShowHotkey(key)
        } else
        {
            StringLeft, key, key, 1
            Stringlower, key, key

            GetKeyState, CapsState, CapsLock, T
            If CapsState != D
            {
                 if GetKeyState("Shift", "P")
                {
                  StringUpper, key, key
                }
            } else
                StringUpper, key, key

            TypedLetter(key)
            ShowHotkey(typed)
        }
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnSpacePressed() {
    try {
        if typed {
            if (visible)
            {
                TypedLetter("_")
                ShowHotkey(typed)
            } else
            {
                TypedLetter("_")
                ShowHotkey(typed)
                SetTimer, HideGUI, % -DisplayTime
            }
        } else if (!typed)
        {
          key := GetKeyStr()
          ShowHotkey(key)
          SetTimer, HideGUI, % -DisplayTime
        }
    }
}

OnBspPressed() {
    try
    {
        if typed
        {
            typed := SubStr(typed, 1, StrLen(typed) - 1)
            ShowHotkey(typed)
        } else if (!typed)
        {
            key := GetKeyStr()
            ShowHotkey(key)
            SetTimer, HideGUI, % -DisplayTime
        }
    }
    if (BeepHiddenKeys = 1) && (KeyBeeper = 1) && (ShowSingleKey = 0)
       soundbeep, 1900, 45

    if (KeyBeeper = 1) && (ShowSingleKey = 1)
       soundbeep, 1900, 45
}

OnCapsPressed() {
    try
    {
        if typed
        {
            ShowHotkey(typed)
        } else if (!typed)
        {
            key := GetKeyStr()
            GetKeyState, CapsState, CapsLock, T
            if CapsState = D
            {
                key := prefixed ? key : "CAPSLOCK ON"
            } else
                key := prefixed ? key : "CapsLock off"
            ShowHotkey(key)
            SetTimer, HideGUI, % -DisplayTime
        }
    }

    If (CapslockBeeper = 1) && (ShowSingleKey = 1) || (BeepHiddenKeys = 1)
       {
        soundbeep, 450, 200
       }
}

OnNumpadPressed()
{
    GetKeyState, NumState, NumLock, T
    try {
        if NumState != D
        {
            key := GetKeyStr()
            ShowHotkey(key)
            SetTimer, HideGUI, % -DisplayTime
        } else if !typed
        {
            key := GetKeyStr()
            ShowHotkey(key)
        } else if NumState = D
        {
            key := GetKeyStr(1) ; consider it a letter
            StringLeft, key, key, 3
            StringRight, key, key, 1
            TypedLetter(key)
            ShowHotkey(typed)
        }
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnKeyUp() {
    global tickcount_start := A_TickCount

    if typed && (CapslockBeeper = 1) && (ShowSingleKey = 1)
    {
        GetKeyState, CapsState, CapsLock, T
        If CapsState = D
           {
             soundbeep, 450, 25
           }
           else if (KeyBeeper = 1) && (ShowSingleKey = 1)
           {
             soundbeep, 1900, 45
           }
    }

    If (CapslockBeeper = 0) && (KeyBeeper = 1) && (ShowSingleKey = 1)
       {
         soundbeep, 1900, 45
       }
       else if (CapslockBeeper = 1) && (KeyBeeper = 0)
       {
       }
       else if !typed && (CapslockBeeper = 1) && (ShowSingleKey = 1)
       {
         soundbeep, 1900, 45
       }

    if (BeepHiddenKeys = 1) && (KeyBeeper = 1) && (ShowSingleKey = 0)
         soundbeep, 1900, 45  
}

OnModUp() {
    global tickcount_start := A_TickCount

    If (ModBeeper = 1) && (ShowSingleKey = 1) && (ShowSingleModifierKey = 1) || (ModBeeper = 1) && (BeepHiddenKeys = 1)
       soundbeep, 1000, 65

}

; ===================================================================================

CreateGUI() {
    global

    Gui, +AlwaysOnTop -Caption +Owner +LastFound +ToolWindow +E0x20
    Gui, Margin, 10, 10
    Gui, Color, 111111
    Gui, Font, cWhite s%FontSize% bold, Arial, -wrap
    Gui, Add, Text, vHotkeyText left x10 y10 -wrap

;    if TransparencyLevel between 10 and 245
;       WinSet, Transparent, %TransparencyLevel%

}

CreateHotkey() {

    if (CustomRegionalKeys=1) && (AutoDetectKBD=0)
    {
        Loop, parse, RegionalKeysList, |
        {
           Hotkey, % "~*" A_LoopField, OnLetterPressed, useErrorLevel
           Hotkey, % "~*" A_LoopField " Up", OnKeyUp, useErrorLevel
           if (errorlevel!=0) && (audioAlerts=1)
              soundbeep, 1900, 50
        }
    }
   if (AutoDetectKBD=1)
   {
       IdentifyKBDlayout()
   }

    Loop, 95
    {
        k := Chr(A_Index + 31)

        if (DeadKeys=1)
        {
            for each, char2skip in StrSplit("``,^,6,',"",~", ",")        ; dead keys to ignore
            {
                if (k = char2skip && DeadKeys=1)
                {
                    continue, 2
                }
            }
        }

        if (k = " ")
        {
            Hotkey, % "~*Space", OnSpacePressed, useErrorLevel
            Hotkey, % "~*Space Up", OnKeyUp, useErrorLevel
        }
        else
        {
            Hotkey, % "~*" k, OnLetterPressed, useErrorLevel
            Hotkey, % "~*" k " Up", OnKeyUp, useErrorLevel
            if (errorlevel!=0) && (audioAlerts=1)
               soundbeep, 1900, 50
        }
    }

    Hotkey, % "~*Backspace", OnBspPressed, useErrorLevel
    Hotkey, % "~*CapsLock", OnCapsPressed, useErrorLevel
    Hotkey, % "~*CapsLock Up", OnKeyUp, useErrorLevel

    Loop, 24 ; F1-F24
    {
        Hotkey, % "~*F" A_Index, OnKeyPressed, useErrorLevel
        Hotkey, % "~*F" A_Index " Up", OnKeyUp, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    Loop, 10 ; Numpad0 - Numpad9
    {
        Hotkey, % "~*Numpad" A_Index - 1, OnNumpadPressed, useErrorLevel
        Hotkey, % "~*Numpad" A_Index - 1 " Up", OnKeyUp, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    NumpadKeysList := "NumpadDot|NumpadDiv|NumpadMult|NumpadAdd|NumpadSub|sc04E|sc04A|sc052|sc04F|sc050|sc051|sc04B|sc04C|sc04D|sc047|sc048|sc049|sc053|sc037|sc135"

    Loop, parse, NumpadKeysList, |
    {
       Hotkey, % "~*" A_LoopField, OnNumpadPressed, useErrorLevel
       Hotkey, % "~*" A_LoopField " Up", OnKeyUp, useErrorLevel
       if (errorlevel!=0) && (audioAlerts=1)
          soundbeep, 1900, 50
    }

    Otherkeys := "WheelDown|WheelUp|WheelLeft|WheelRight|XButton1|XButton2|Browser_Forward|Browser_Back|Browser_Refresh|Browser_Stop|Browser_Search|Browser_Favorites|Browser_Home|Volume_Mute|Volume_Down|Volume_Up|Media_Next|Media_Prev|Media_Stop|Media_Play_Pause|Launch_Mail|Launch_Media|Launch_App1|Launch_App2|Help|Sleep|PrintScreen|CtrlBreak|Break|AppsKey|Tab|Enter|Esc"
               . "|Insert|Home|End|Up|Down|Left|Right|ScrollLock|NumLock|Pause|sc145|sc146|sc046|sc123|sc11C|sc149|sc151|sc122|sc153"
    Loop, parse, Otherkeys, |
    {
        Hotkey, % "~*" A_LoopField, OnKeyPressed, useErrorLevel
        Hotkey, % "~*" A_LoopField " Up", OnKeyUp, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    If ShowMouseButton=1
    {
        Loop, Parse, % "LButton|MButton|RButton", |
        Hotkey, % "~*" A_LoopField, OnMousePressed, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    for i, mod in ["LCtrl", "RCtrl", "LAlt", "RAlt"] {
        Hotkey, % "~*" mod, OnKeyPressed, useErrorLevel
        Hotkey, % "~*" mod " Up", OnModUp, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    for i, mod in ["LWin", "RWin"] {
        Hotkey, % "~*" mod, OnKeyPressed, useErrorLevel
        Hotkey, % "~*" mod " Up", OnModUp, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    If typed {
    for i, mod in ["LShift", "RShift"]
        Hotkey, % "~*" mod, OnKeyPressed, useErrorLevel
        Hotkey, % "~*" mod " Up", OnModUp, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }
}

ShowHotkey(HotkeyStr) {

    if (OSDautosize=1)
    {
        HotkeyTextTrimmed := RegExReplace(HotkeyStr, "[^a-zA-Z]", "")
        StringLeft, HotkeyTextTrimmed, HotkeyTextTrimmed, 5
        growthFactor := 1.3
        if HotkeyTextTrimmed is upper
           growthFactor := 1.05
        text_width := (StrLen(HotkeyStr)/growthFactor)*FontSize
        text_width := (text_width<70) ? 70 : text_width+15
    } else if OSDautosize=0
    {
        text_width := GuiWidth
    }

    if !typed
    {
        StringUpper, HotkeyStr, HotkeyStr, T
    }

    GuiControl,     , HotkeyText, %HotkeyStr%
    GuiControl, Move, HotkeyText, w%text_width% left
    Gui, Show, NoActivate x%GuiX% y%GuiY% AutoSize, KeypressOSD
    visible := 1
    SetTimer, checkMousePresence, on, 400
}

GetKeyStr(letter := 0) {
    static modifiers := ["LCtrl", "RCtrl", "LAlt", "RAlt", "LShift", "RShift", "LWin", "RWin"]
    static repeatCount := 1

    ; If any mod but shift, go ; If shift, check if not letter

    for i, mod in modifiers
    {
        if (mod = "LShift" && typed || mod = "RShift" && typed ? (!letter && GetKeyState(mod)) : GetKeyState(mod))
            prefix .= mod " + "
    }

    if (!prefix && !ShowSingleKey)
        throw

    key := SubStr(A_ThisHotkey, 3)

    if (key ~= "i)^(LCtrl|RCtrl|LShift|RShift|LAlt|RAlt|LWin|RWin)$") {
        if (ShowSingleModifierKey = 0) || (ShowSingleKey = 0) || (A_TickCount-tickcount_start > 2000) && visible
        {
            throw
        } else
        {
             key := ""
             prefix := RTrim(prefix, "+ ")
        }

        prefix := CompactModifiers(prefix)

        if (ShowKeyCount=1) {
            if !InStr(prefix, "+") && IsDoubleClickEx() {
                if (A_ThisHotKey != A_PriorHotKey) || (ShowKeyCount=1) && (A_TickCount-tickcount_start < 700) {
                    if (++repeatCount > 1) {
                        prefix .= " (" repeatCount ")"
                    }
                } else {
                    repeatCount := 0
                }
            } else {
                repeatCount := 1
            }
        }
    } else {
        if StrLen(key)=1
        {
            key := GetKeyChar(key, "A")
        } else if ( SubStr(key, 1, 2) = "sc" ) {
            key := SpecialSC(key)
        } else if (key = "Volume_Up") || (key = "Volume_Down") {
            soundbeep, 150, 40
        } else if (key = "PrintScreen") {
            if HideAnnoyingKeys
                throw
            key := "Print Screen"
        } else if (key = "MButton") {
            key := "Middle Click"
        } else if (key = "RButton") {
            key := "Right Click"
        } else if (key = "LButton") && IsDoubleClick() {
            key := "Double-Click"
        } else if (key = "LButton") {
            if HideAnnoyingKeys
                throw
            key := "Left Click"
        }
        {
            _key := (key = "Double-Click") ? "Left Click" : key
        }

        prefix := CompactModifiers(prefix)

        static pre_prefix, pre_key, keyCount := 1
        keyCount := (key=pre_key) && (prefix = pre_prefix) ? keyCount : 1
        global ShowKeyCountDelay := (ShowKeyCountFired = 0) ? 700 : 6000
        ShowKeyCountDelay := (ShowKeyCountFired=1) ? (ShowKeyCountDelay+keyCount*100) : ShowKeyCountDelay

        if (InStr(prefix, "+")) && (A_TickCount-tickcount_start < ShowKeyCountDelay) || (!letter) && (A_TickCount-tickcount_start < ShowKeyCountDelay)
        {
            if (ShowPrevKey=1) && (A_TickCount-tickcount_start < ShowPrevKeyDelay) && !typed && !letter
            {
                ShowPrevKeyValid := 1
            } else
            {
                ShowPrevKeyValid := 0
            }
            if (prefix != pre_prefix) {
                result := (ShowPrevKeyValid=1) ? prefix key " {" pre_prefix pre_key "}" : prefix key
            } else if (ShowPrevKeyValid=1) && (key != pre_key) || (ShowKeyCount=1) && (ShowPrevKeyValid=1)
            {
                keyCount := (key=pre_key) && (ShowKeyCount=1) ? (keyCount+1) : 1
                key := (keyCount>1) && (ShowKeyCount=1) ? (key " (" keyCount ")") : (key ", " pre_key)
            } else if (ShowPrevKeyValid=0)
            {
                keyCount := (key=pre_key) && (ShowKeyCount=1) ? (keyCount+1) : 1
                key := (keyCount>1) ? (key " (" keyCount ")") : (key)
            }
        } else {
            keyCount := 1
        }

        pre_prefix := prefix
        pre_key := _key

        repeatCount := 1
    }

    prefixed := prefix ? 1 : 0
    return result ? result : prefix . key
}

CompactModifiers(stringy)
{
    if DifferModifiers = 1
    {
        StringReplace, stringy, stringy, LCtrl + RAlt, AltGr, All
        StringReplace, stringy, stringy, LCtrl + RCtrl + RAlt, RCtrl + AltGr, All
        StringReplace, stringy, stringy, RAlt, AltGr, All
        StringReplace, stringy, stringy, LAlt, Alt, All
    } else if (DifferModifiers = 0)
    {
        StringReplace, stringy, stringy, LCtrl + RAlt, AltGr, All
        ; StringReplace, stringy, stringy, LCtrl + RCtrl + RAlt, RCtrl + AltGr, All
        StringReplace, stringy, stringy, LCtrl, Ctrl, All
        StringReplace, stringy, stringy, RCtrl, Ctrl, All
        StringReplace, stringy, stringy, LShift, Shift, All
        StringReplace, stringy, stringy, RShift, Shift, All
        StringReplace, stringy, stringy, LAlt, Alt, All
        StringReplace, stringy, stringy, LWin, WinKey, All
        StringReplace, stringy, stringy, RWin, WinKey, All
        StringReplace, stringy, stringy, Ctrl + Ctrl, Ctrl, All
        StringReplace, stringy, stringy, Shift + Shift, Shift, All
        StringReplace, stringy, stringy, WinKey + WinKey, WinKey, All
        StringReplace, stringy, stringy, RAlt, AltGr, All
    }
    return stringy
}

SpecialSC(sc) {
    GetSpecialKeysStates()

    GetKeyState, NumState, NumLock, T
    If NumState = D
    {
       k := {sc046: zcSCROL, sc145: "NUM LOCK ON", sc146: "Pause", sc123: "Genius LuxeMate Scroll", sc04E: "[ + ]", sc04A: "[ - ]", sc052: "[ 0 ]", sc04F: "[ 1 ]", sc050: "[ 2 ]", sc051: "[ 3 ]", sc04B: "[ 4 ]", sc04C: "[ 5 ]", sc04D: "[ 6 ]", sc047: "[ 7 ]", sc048: "[ 8 ]", sc049: "[ 9 ]", sc053: "[ . ]", sc037: "[ * ]", sc135: "[ / ]", sc11C: "[Enter]", sc149: "Page Up", sc151: "Page Down", sc153: "Delete", sc122: "Media_Play/Pause"}
    }
    else {
       k := {sc046: zcSCROL, sc145: "Num lock off", sc146: "Pause", sc123: "Genius LuxeMate Scroll", sc04E: "[ + ]", sc04A: "[ - ]", sc052: "[Insert]", sc04F: "[End]", sc050: "[Down]", sc051: "[Page Down]", sc04B: "[Left]", sc04C: "[Undefined]", sc04D: "[Right]", sc047: "[Home]", sc048: "[Up]", sc049: "[Page Up]", sc053: "[Delete]", sc037: "[ * ]", sc135: "[ / ]", sc11C: "[Enter]", sc149: "Page Up", sc151: "Page Down", sc153: "Delete", sc122: "Media_Play/Pause"}
    }
    return k[sc]
}

; <tmplinshi>: thanks to Lexikos: https://autohotkey.com/board/topic/110808-getkeyname-for-other-languages/#entry682236
; This enables partial support for non-English keyboard layouts.
; If the script initializes with the English keyboard layout, but then used with another one, this function gets proper key names,

GetKeyChar(Key, WinTitle:=0)
{
    thread := WinTitle=0 ? 0
        : DllCall("GetWindowThreadProcessId", "ptr", WinExist(WinTitle), "ptr", 0)
    hkl := DllCall("GetKeyboardLayout", "uint", thread, "ptr")
    vk := GetKeyVK(Key), sc := GetKeySC(Key)
    VarSetCapacity(state, 256, 0)
    VarSetCapacity(char, 4, 0)
    n := DllCall("ToUnicodeEx", "uint", vk, "uint", sc
        , "ptr", &state, "ptr", &char, "int", 2, "uint", 0, "ptr", hkl)
    return StrGet(&char, n, "utf-16")
}
global LangsBinded := 0

IdentifyKBDlayout() {
  VarSetCapacity(kbLayoutRaw, 32, 0)
  DllCall("GetKeyboardLayoutName", "Str", kbLayoutRaw)
  StringRight, kbLayout, kbLayoutRaw, 4

  #Include *i keypress-osd-languages.ini

  check_kbd := StrLen(LangName_%kbLayout%)>2 ? 1 : 0
  check_kbd_exact := StrLen(LangRaw_%kbLayoutRaw%)>2 ? 1 : 0
  if (check_kbd_exact=0)
  {
      partialKBDmatch = (Partial match)
  }

  if (check_kbd=0)
  {
      ShowHotkey("Unrecognized layout: (kbd " kbLayoutRaw ").")
      SetTimer, HideGUI, % -DisplayTime
      soundbeep, 500, 900
  }

  StringLeft, kbLayoutSupport, LangName_%kbLayout%, 1
  if (kbLayoutSupport="-") && (check_kbd=1)
  {
      ShowHotkey("Unsupported layout: " LangName_%kbLayout% " (kbd" kbLayout ").")
      SetTimer, HideGUI, % -DisplayTime
      soundbeep, 500, 900
  }

  IfInString, LangsBinded, %kbLayout%
  {
      KBDbinded := 1
  } else
  {
      LangsBinded := kbLayout "|" LangsBinded 
      StringLeft, LangsBinded, LangsBinded, 20
      KBDbinded := 0
  }

  if (kbLayoutSupport!="-") && (check_kbd=1) && (KBDbinded=0)
  {

      Loop, parse, LangChars_%kbLayout%, |
      {
         Hotkey, % "~*" A_LoopField, OnLetterPressed, useErrorLevel
         Hotkey, % "~*" A_LoopField " Up", OnKeyUp, useErrorLevel
         if (errorlevel!=0) && (audioAlerts=1)
             soundbeep, 1900, 50
      }
      if SilentDetection=0
      {
          ShowHotkey("Keyboard layout: " LangName_%kbLayout% " (kbd" kbLayout "). " partialKBDmatch)
          SetTimer, HideGUI, % -DisplayTime/4
      }
  }

   if (ConstantAutoDetect=1) && (AutoDetectKBD=1)
      SetTimer, ConstantKBDchecker, 1500
}

ConstantKBDchecker() {
  SetFormat, Integer, H
  WinGet, WinID,, A
  ThreadID := DllCall("GetWindowThreadProcessId", "UInt", WinID, "UInt", 0)
  NewInputLocaleID := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")
    if (InputLocaleID != NewInputLocaleID)
    {
        InputLocaleID := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")
        
        if SilentDetection=0
        ShowHotkey("Changed keyboard layout: " InputLocaleID)
        
        sleep, 350
        Reload
        ;WinActivate, KeypressOSD
        ;SetTimer, HideGUI, % -DisplayTime/3
        ;IdentifyKBDlayout()
    } 
}

IsDoubleClick(MSec = 300) {
    Return (A_ThisHotKey = A_PriorHotKey) && (A_TimeSincePriorHotkey < MSec)
}

IsDoubleClickEx(MSec = 300) {
    preHotkey := RegExReplace(A_PriorHotkey, "i) Up$")
    Return (A_ThisHotKey = preHotkey) && (A_TimeSincePriorHotkey < MSec)
}

HideGUI() {
    visible := 0
    Gui, Hide
    SetTimer, checkMousePresence, off, 300
}

checkMousePresence() {
    id := mouseIsOver()
    title := getWinTitleFromID(id)
    if title = KeypressOSD
       HideGUI()
}

mouseIsOver() {
    MouseGetPos,,, id
    return id
}

getWinTitleFromID(id) {
    WinGetTitle, title, % "ahk_id " id
    return title
}

CreateGlobalShortcuts() {
   if (KeyboardShortcuts=1) {
      Hotkey, !+^F8, ToggleShowSingleKey
      Hotkey, !+^F9, TogglePosition
      Hotkey, !+^F10, EnableCustomKeys
      Hotkey, !+^F11, DetectLangNow
      Hotkey, !+^F12, ReloadScript
    }
}

ToggleShowSingleKey:
    ShowSingleKey := (!ShowSingleKey) ? true : false
    Gui, Destroy
    sleep, 100
    CreateGUI()
    sleep, 100
    IniWrite, %ShowSingleKey%, keypress-osd.ini, PermanentSettings, ShowSingleKey
    ShowHotkey("Show single keys = " ShowSingleKey)
    SetTimer, HideGUI, % -DisplayTime
return

TogglePosition:
    GuiY := (GuiY = GuiYb) ? GuiYa : GuiYb
    Gui, Destroy
    sleep, 100
    CreateGUI()
    sleep, 100
    IniWrite, %GuiY%, keypress-osd.ini, PermanentSettings, GuiY
    ShowHotkey("OSD position changed")
    SetTimer, HideGUI, % -DisplayTime
return

EnableCustomKeys:
    global CustomRegionalKeys := CustomRegionalKeys = 1 ? 0 : 1
    CustomMultiLangToggled := 1
    IniWrite, %CustomMultiLangToggled%, keypress-osd.ini, PermanentSettings, CustomMultiLangToggled
    IniWrite, %CustomRegionalKeys%, keypress-osd.ini, PermanentSettings, CustomRegionalKeys
    ShowHotkey("Custom Regional keys = " RegionalKeys)
    sleep, 1000
    Reload
return

DetectLangNow:
    global AutoDetectKBD := 1
    global ConstantAutoDetect := 0
    AutoDetectKBDToggled := 1
    IniWrite, %AutoDetectKBDToggled%, keypress-osd.ini, PermanentSettings, AutoDetectKBDToggled
    IniWrite, %AutoDetectKBD%, keypress-osd.ini, PermanentSettings, AutoDetectKBD
    IniWrite, %ConstantAutoDetect%, keypress-osd.ini, PermanentSettings, ConstantAutoDetect
    ShowHotkey("Detecting keyboard layout...")
    sleep, 800
    Reload
return

ReloadScript:
    Gui, Destroy
    sleep, 100
    CreateGUI()
    sleep, 100
    ShowHotkey("OSD reinitializing...")
    sleep, 1500
    Reload
return

Code: Select all

; keypress-osd-languages.ini
; (AHK script) KeyPress OSD v2.86 language definitions.
;
; Charset for this file must be UTF 8 with BOM.
; it will not function properly otherwise.
;
; supported languages
LangRaw_00020409 = ENGLISH_US_INTERNATIONAL
LangRaw_00000409 = ENGLISH_US
LangRaw_00000452 = ENGLISH_UK_EXTENDED
LangRaw_00000809 = ENGLISH_UK
LangRaw_00004009 = ENGLISH_INDIA
LangName_0c09 = ENGLISH_AUSTRALIAN
LangName_1809 = ENGLISH_IRISH
LangName_1409 = ENGLISH_NEW_ZEALAND
LangName_0409 = ENGLISH_US
LangName_0452 = ENGLISH_UK_EXTENDED
LangName_0809 = ENGLISH_UK
LangName_4009 = ENGLISH_INDIA
LangRaw_00020426 = LATVIAN_STANDARD_QWERTY
LangName_0426 = LATVIAN_STANDARD_QWERTY
LangRaw_00010402 = BULGARIAN_LATIN
LangName_0402 = BULGARIAN_LATIN
LangRaw_00050408 = GREEK_LATIN
LangName_0408 = GREEK_LATIN
LangRaw_0000040c = FRENCH_STANDARD
LangName_040c = FRENCH_STANDARD
  LangChars_040c := "&|-|!|²|à|é|è|ç|ù"
LangRaw_00011009 = FRENCH_CANADIAN
LangName_1009 = FRENCH_CANADIAN
  LangChars_1009 := "&|°|-|!|²|é|è|ç|à|É|È|Ç|À"
LangRaw_00000c0c = FRENCH_CANADIAN_TRADITIONAL
LangName_0c0c = FRENCH_CANADIAN_TRADITIONAL
  LangChars_0c0c := "&|°|-|!|²|à|é|è|ç|ù"
LangRaw_0000080c = FRENCH_BELGIAN
LangName_080c = FRENCH_BELGIAN
  LangChars_080c := "&|°|-|§|!|²|à|é|è|ç|ù|µ"
LangRaw_00000407 = GERMAN_STANDARD
LangName_0407 = GERMAN_STANDARD
  LangChars_0407 := "Ä|ä|Ö|ö|Ü|ü|ẞ|ß"
LangRaw_00010418 = ROMANIAN_STANDARD
LangName_0418 = ROMANIAN_STANDARD
  LangChars_0418 := "„|”|Ă|ă|Â|â|Î|î|Ș|ș|Ț|ț|Ş|ş|Ţ|ţ"
LangRaw_00000425 = ESTONIAN
LangName_0425 = ESTONIAN
  LangChars_0425 := "ü|õ|ö|ä|Ü|Õ|Ö|Ä"
LangRaw_00010427 = LITHUANIAN
LangName_0427 = LITHUANIAN
  LangChars_0427 := "ą|č|ę|ė|į|š|ų|ū|ž|Ą|Č|Ę|Ė|Į|Š|Ų|Ū|Ž|x|w|q|f"
LangRaw_00000414 = NORWEGIAN_BOKMAL
LangName_0414 = NORWEGIAN_BOKMAL
  LangChars_0414 := "å|ø|æ|Å|Ø|Æ"
LangRaw_0000043b = NORWEGIAN
LangName_043b = NORWEGIAN
  LangChars_043b := "å|ø|æ|Å|Ø|Æ|å|Å|ŋ|đ"
LangRaw_00000413 = DUTCH_STANDARD
LangName_0413 = DUTCH_STANDARD
  LangChars_0413 := "@|°"
LangRaw_00000816 = PORTUGUESE_STANDARD
LangName_0816 = PORTUGUESE_STANDARD
  LangChars_0816 := "«|ç|º"
LangRaw_0000081a = SERBIAN_LATIN
LangName_081a = SERBIAN_LATIN
  LangChars_081a := "š|đ|ž|č|ć|Š|Đ|Ž|Č|Ć"
LangRaw_00000424 = SLOVENIAN
LangName_0424 = SLOVENIAN
  LangChars_0424 := "š|đ|č|ć|ž|Š|Đ|Č|Ć|Ž"
LangRaw_0000042a = VIETNAMESE
LangName_042a = VIETNAMESE
  LangChars_042a := "ă|â|ê|ô|đ|₫|ư|ơ|Ă|Â|Ê|Ô|Đ|₫|Ư|Ơ"
LangRaw_0001041f = TURKISH_F
LangName_041f = TURKISH_F
  LangChars_041f := "ğ|ı|ş|ç|ö|ü|i|Ğ|I|Ş|Ç|Ö|Ü|I|i|İ"
LangRaw_00000405 = CZECH
LangName_0405 = CZECH
  LangChars_0405 := "§|ů|ú|ě|š|č|ř|ž|ý|á|í|é|Ů|Ú|Ě|Š|Č|Ř|Ž|Ý|Á|Í|É|`=|`)"
LangRaw_00000406 = DANISH
LangName_0406 = DANISH
  LangChars_0406 := "å|æ|ø|Å|Æ|Ø|½"
LangRaw_0000046f = GREENLAND
LangName_046f = GREENLAND
  LangChars_046f := "å|æ|ø|Å|Æ|Ø|½"
LangRaw_0000040e = HUNGARIAN
LangName_040e = HUNGARIAN
  LangChars_040e := "ö|ü|ó|ő|ú|ű|é|á|Ö|Ü|Ó|Ő|Ú|Ű|É|Á"
LangRaw_0000040a = SPANISH_TRADITIONAL
LangName_040a = SPANISH_TRADITIONAL
  LangChars_040a := "¿|ñ|Ñ|ç|Ç|¡|º"
LangRaw_0000080a = SPANISH_MEXICAN
LangName_080a = SPANISH_MEXICAN
  LangChars_080a := "¿|ñ|Ñ"
LangRaw_0000041b = SLOVAK
LangName_041b = SLOVAK
  LangChars_041b := "š|č|ť|ž|ý|á|í|é|ň|ä|ú|ô|§|ľ"
LangRaw_0000040f = ICELANDIC
LangName_040f = ICELANDIC
  LangChars_040f := "ö|ð|æ|þ|Ö|Ð|Æ|Þ"
LangRaw_0002083b = SWEDISH_FINLAND_EXTENDED
LangName_083b = SWEDISH_FINLAND
  LangChars_083b := "§|á|š|č|ŧ|å|ŋ|đ|ö|ä|Á|Š|Č|Ŧ|Å|Ŋ|Đ|Ö|Ä"
LangRaw_0000040b = FINNISH
LangName_040b = FINNISH
  LangChars_040b := "§|å|ö|ä|Å|Ö|Ä"
LangRaw_00010410 = ITALIAN_142
LangName_0410 = ITALIAN_142
  LangChars_0410 := "è|ò|à|ì|ù"
LangRaw_0000041d = SWEDISH
LangName_041d = SWEDISH
  LangChars_041d := "§|å|ö|ä|Å|Ö|Ä"
LangRaw_00000416 = PORTUGUESE_BRAZILIAN
LangName_0416 = PORTUGUESE_BRAZILIAN
  LangChars_0416 := "ç|Ç"
LangRaw_0001042c = AZERBAIJANI_LATIN
LangName_042c = AZERBAIJANI_LATIN
  LangChars_042c := "ü|ş|ı|i|c|ğ|ç|z|ö|x|j|ə|Ü|Ş|I|I|C|Ğ|Ç|Z|Ö|X|J|Ə"
LangRaw_00010415 = POLISH
LangName_0415 = POLISH
  LangChars_0415 := "ó|ś|ż|ą|ł"
LangRaw_00000442 = TURKMEN
LangName_0442 = TURKMEN
  LangChars_0442 := "ž|ä|ü|ç|ý|ň|ö|ş|Ž|Ä|Ü|Ç|Ý|Ň|Ö|Ş"


; unsupported, but recognized languages
LangName_041c = -ALBANIAN
LangName_0436 = -AFRIKAANS
LangName_0401 = -ARABIC_SAUDI_ARABIA
LangName_0801 = -ARABIC_IRAQ
LangName_0c01 = -ARABIC_EGYPT
LangName_0401 = -ARABIC_SAUDI_ARABIA
LangName_0801 = -ARABIC_IRAQ
LangName_0c01 = -ARABIC_EGYPT
LangName_1001 = -ARABIC_LIBYA
LangName_1401 = -ARABIC_ALGERIA
LangName_1801 = -ARABIC_MOROCCO
LangName_1c01 = -ARABIC_TUNISIA
LangName_2001 = -ARABIC_OMAN
LangName_2401 = -ARABIC_YEMEN
LangName_2801 = -ARABIC_SYRIA
LangName_2c01 = -ARABIC_JORDAN
LangName_3001 = -ARABIC_LEBANON
LangName_3401 = -ARABIC_KUWAIT
LangName_3801 = -ARABIC_UAE
LangName_3c01 = -ARABIC_BAHRAIN
LangName_4001 = -ARABIC_QATAR
LangName_042b = -ARMENIAN
LangName_082c = -AZERBAIJANI_CYRILLIC
LangName_042d = -BASQUE
LangName_0423 = -BELARUSIAN
LangName_0402 = -BULGARIAN
LangName_0403 = -CATALAN
LangName_0404 = -CHINESE_TAIWAN
LangName_0804 = -CHINESE_PRC
LangName_0c04 = -CHINESE_HONG_KONG
LangName_1004 = -CHINESE_SINGAPORE
LangName_1404 = -CHINESE_MACAU
LangName_041a = -CROATIAN
LangName_0813 = -DUTCH_BELGIAN
LangName_1c09 = -ENGLISH_SOUTH_AFRICA
LangName_2009 = -ENGLISH_JAMAICA
LangName_2409 = -ENGLISH_CARIBBEAN
LangName_2809 = -ENGLISH_BELIZE
LangName_2c09 = -ENGLISH_TRINIDAD
LangName_3009 = -ENGLISH_ZIMBABWE
LangName_3409 = -ENGLISH_PHILIPPINES
LangName_0438 = -FAEROESE
LangName_0429 = -FARSI
LangName_100c = -FRENCH_SWISS
LangName_140c = -FRENCH_LUXEMBOURG
LangName_180c = -FRENCH_MONACO
LangName_0437 = -GEORGIAN
LangName_0c07 = -GERMAN_AUSTRIAN
LangName_0807 = -GERMAN_SWISS
LangName_1007 = -GERMAN_LUXEMBOURG
LangName_1407 = -GERMAN_LIECHTENSTEIN
LangName_040d = -HEBREW
LangName_0439 = -HINDI
LangName_0421 = -INDONESIAN
LangName_0810 = -ITALIAN_SWISS
LangName_0411 = -JAPANESE
LangName_043f = -KAZAKH
LangName_0457 = -KONKANI
LangName_0412 = -KOREAN
LangName_042f = -MACEDONIAN
LangName_043e = -MALAY_MALAYSIA
LangName_083e = -MALAY_BRUNEI_DARUSSALAM
LangName_044e = -MARATHI
LangName_0814 = -NORWEGIAN_NYNORSK
LangName_0419 = -RUSSIAN
LangName_044f = -SANSKRIT
LangName_0c1a = -SERBIAN_CYRILLIC
LangName_2c0a = -SPANISH_ARGENTINA
LangName_400a = -SPANISH_BOLIVIA
LangName_340a = -SPANISH_CHILE
LangName_240a = -SPANISH_COLOMBIA
LangName_140a = -SPANISH_COSTA_RICA
LangName_1c0a = -SPANISH_DOMINICAN_REPUBLIC
LangName_300a = -SPANISH_ECUADOR
LangName_440a = -SPANISH_EL_SALVADOR
LangName_100a = -SPANISH_GUATEMALA
LangName_480a = -SPANISH_HONDURAS
LangName_0c0a = -SPANISH_MODERN_SORT
LangName_4c0a = -SPANISH_NICARAGUA
LangName_180a = -SPANISH_PANAMA
LangName_3c0a = -SPANISH_PARAGUAY
LangName_500a = -SPANISH_PUERTO_RICO
LangName_280a = -SPANISH_PERU
LangName_380a = -SPANISH_URUGUAY
LangName_200a = -SPANISH_VENEZUELA
LangName_0441 = -SWAHILI
LangName_081d = -SWEDISH_FINLAND
LangName_0449 = -TAMIL
LangName_0444 = -TATAR
LangName_041e = -THAI
LangName_0422 = -UKRAINIAN
LangName_0420 = -URDU
LangName_0443 = -UZBEK_LATIN
LangName_0843 = -UZBEK_CYRILLIC
LangName_0417 = -RAETO-ROMANCE
LangName_0428 = -TAJIK
LangName_0431 = -TSONGA
LangName_0432 = -SETSUANA
LangName_0433 = -VENDA
LangName_0434 = -XHOSA
LangName_0435 = -ZULU
LangName_0440 = -KYRGYZ_CYRILLIC
LangName_0445 = -BENGALI_INDIA
LangName_0446 = -PUNJABI
LangName_0447 = -GUJARATI
LangName_0448 = -ORIYA
LangName_0450 = -MONGOLIAN
LangName_0451 = -TIBETAN
LangName_0438 = -FAROESE
LangName_0452 = -WELSH
LangName_0453 = -KHMER
LangName_0454 = -LAO
LangName_0455 = -BURMESE
LangName_0456 = -GALICIAN
LangName_0458 = -MANIPURI
LangName_0459 = -SINDHI
LangName_0460 = -KASHMIRI
LangName_0461 = -NEPALI
LangName_0462 = -FRISIAN_NETHERLANDS
LangName_0464 = -FILIPINO
LangName_0466 = -EDO
LangName_0470 = -IGBO_NIGERIA
LangName_0474 = -GUARANI_PARAGUAY
LangName_0476 = -LATIN
LangName_0477 = -SOMALI
LangName_0481 = -MAORI
LangName_0819 = -RUSSIAN_MOLDOVA
LangName_0845 = -BENGALI_BANGLADESH
LangName_0850 = -MONGOLIAN
LangName_1115 = -SINHALA
Please read the notes section.

Best regards, Marius.
-------------------------
KeyPress OSD v4: GitHub or forum. (presentation video)
My home page.
User avatar
Drugwash
Posts: 567
Joined: 29 May 2014, 21:07
Location: Ploieşti, Romania

Re: [Script] KeypressOSD - Display key press on screen

Post by Drugwash » 10 Oct 2017, 08:37

There are improvements and indeed it works better. There is room for more though. :)
Personally I'm a mouse guy, I like buttons, checkboxes, radiobuttons and so on in a nicely designed GUI, when it comes to options. Strangely and amazingly I try to stay away from hotkeys as much as possible because they always interfere with applications (try to type a simple @ in AOL Mail's web interface with a Romanian Legacy keyboard layout, see if you can! That's because @ on RO (legacy) is issued through AltGr+V or Ctrl+Alt+V, while the imbecils at AOL insist on assigning that hotkey to some stupid command and their hotkeys cannot be disabled). Here I right-click the systray icon and I see no Options item so for me this script has no options at all. Editing the script manually is an ugly job. If/when you get some time it would be great if you could build an Options GUI. ;)

There's also one thing that jumped to bite me: spaces appear as underscores (_____) and I personally feel that's wrong. Or maybe it's my layout's fault, because underscores appear as dashes (-----) instead.

Anyway, your efforts are truly appreciated. Keep up the good work! ;)
I've deleted my CloudMe account because of GDPR - the now legal base for privacy invasion and data theft.
robodesign
Posts: 371
Joined: 30 Sep 2017, 03:59
Facebook: marius.sucan
GitHub: mariussucan
Location: Romania
Contact:

Re: [Script] KeypressOSD - Display key press on screen

Post by robodesign » 10 Oct 2017, 14:52

Hello!

Thank you very much. Today, I actually decided to try to implement a Tray menu with options or a settings window easy to invoke. I have not yet did anything about it yet, I just added it in my to-do list. Today I decided to make more improvements in regards to typing , in general. So, I am still working on it. ;) Spaces are _ (underscore) because they are visible on the OSD, so when you press backspace, you see you deleted the space.

Now, my main priority is to make it show proper signs when using Shift, eg. Shift+0=). I hope to succed in a way it works with various keyboards.

; CHANGELOG:
; by Marius Sucan (robodesign.ro)
; v2.90 (2017-10-10)
; - from now, numpad keys always initiate typing mode
; - modifiers (Ctrl, Shift, Alt, WinKey) and Tab, Insert no longer clear typed input in the OSD, if pressed once, without a combo
; - unified the behaviour of modifiers
; - fixed some counting bugs :)
; - added option to disable PermanentSettings

Code: Select all

; KeypressOSD.ahk
; Charset for this file must be UTF 8 with BOM.
; it will not function properly otherwise.
;--------------------------------------------------------------------------------------------------------------------------
;
; AVAILABLE SHORTCUTS:
 ; Ctrl+Alt+Shift+F8  - Toggles "Show single key" option. Useful when you must reliably use dead keys.
 ; Ctrl+Alt+Shift+F9  - Toggles between two OSD offsets: GuiYa and GuiYb.
 ; Ctrl+Alt+Shift+F10 - Toggles personal/regional keys support.
 ; Ctrl+Alt+Shift+F11 - Detect keyboard language.
 ; Ctrl+Alt+Shift+F12 - Reinitialize OSD. Useful when it no longer appears on top. To have it appear on top of elevated apps, run it in administrator mode.
;
; NOTES:
 ; This script was made for, and on Windows 10.
 ; The keyboard layouts have changed since Win XP or Win 98.
 ; Windows 10 also no longer switches keyboard layouts based
 ; on the currently active app. As such, automatic keyboard
 ; layout detection may not work for you.
 ; 
 ; I do not intend to offer support for older Windows versions.
 ; 
 ; This script has support only for Latin-based keyboards.
 ; Thus, it has no support for Chinese, Japanese, chirilic, 
 ; It is too complex for me to implement support for other alphabets or writing systems.
 ; If other programmers willing to invest the time in this script,
 ; are welcomed to do so, and even to transform it into anything they wish. 
 ;
 ; I offer numerous options/settings in the script such that
 ; everyone can find a way to adapt it to personal needs.
 ; - you can edit, in the code, what are the dead keys
 ;   - see Loop, 95, char2skip from CreateHotkey() function
 ; - you can also define personal regional keys, while autodetect is disabled
 ; - disable dead keys if you do not have such keys
 ; 
 ; Read the messages you get:
 ; - it indicates when your keyboard layout is unsupported or Unrecognized
 ; - it also indicates if it made a partial match;
 ;   - in such cases, you will likely not have all the keys
 ;   - or simply AHK will give errors trying to bind to inexistent keys
 ; - if the external file is missing, languages.ini, it will always report
 ; that it did not detect your keyboard.
 ;
 ; Default/built in language support is for English International.
 ;
 ; For the layouts I added support, I avoided binding to dead keys
 ; such that you no longer have to add them manually, as indicated previously.
 ; 
 ; If you rely only on the "vanilla" version, you will likely 
 ; not be able to use it.
 ;
 ; I am no programmer and the script is still quite quirky, but I am trying to
 ; make it better and better with each version.
;
; FEATURES:
 ; - Show previously pressed key if fired quickly.
 ; - Count key presses or key fires and mouse clicks.
 ; - Automatic resizing of OSD/HUD or fixed size.
 ; - Hides automatically when mouse runs over it.
 ; - Generate beeps for key presses, modifiers, mouse clicks or just when typing with Capslock.
 ; - Indicators for CapsLock, NumLock and ScrollLock states.
 ; - Typing mode; shows what you are typing in an expanding text area.
 ; - Partial dead keys support, option to turn it off; The work-around is only for the English International keyboard layout.
 ; - Partial support for many non-English keyboards. 40 keyboard layouts defined.
 ;   - limited automatic detection of keyboard layouts.
 ;   - the user also has the option to define his regional keys.
 ; - Easy to configure with many options:
 ;   - to toggle features: key beepers, key counting or previous key;
 ;   - to hide modifiers, mouse clicks or single key presses (which disables typing mode);
 ;   - or hide keys that usually get in the way: Left Click and Print Screen [HideAnnoyingKeys];
 ;   - differ between left and right modifiers;
 ;   - OSD/HUD position, size and display time;
 ;   - beep key presses even if keys are not displayed;
 ;   - some settings toggled by shortcuts are stored permanently in an INI file;
;
; CHANGELOG:
 ; by Marius Sucan (robodesign.ro)
 ;   v2.90 (2017-10-10)
 ;   - from now, numpad keys always initiate typing mode
 ;   - modifiers (Ctrl, Shift, Alt, WinKey) and Tab, Insert no longer clear typed input in the OSD, if pressed once, without a combo
 ;   - unified the behaviour of modifiers
 ;   - fixed counting bugs :)
 ;   - added option to disable PermanentSettings
 ;   v2.87 (2017-10-09)
 ;   - improvements for assigning keys;
 ;     - errors are now managed, it will beep when key bindings fail
 ;   - [new] option to disable audio alerts :)
 ;   - languages are now in an external file
 ;   - new keyboard layouts for Polish, Azerbaijian, Turkmen
 ;   v2.85 (2017-10-08)
 ;   - minor improvements for the dead keys work-around
 ;   - added support for 40 foreign keyboard layouts
 ;   - automatic detection of keyboard layouts, at start or continously
 ;   - user also can define personal/regional keys in the settings section
 ;   - new keyboard shortcut, to toggle regional keys
 ;   - new setting: to enable or disable system-wide keyboard shortcuts
 ;   - settings toggled by shortcuts are stored permanently in an INI file
 ;     - except for the option of regional keys support
 ;   v2.73 (2017-10-05)
 ;   - improvements for automatic resize calculation
 ;   v2.72 (2017-10-04)
 ;   - new option: make beeps on mouse clicks
 ;   - new option: dstinct beeper for modifier keys: Shift, Ctrl, Alt, WinKey.
 ;   - new option: beep hidden keys; if you want it to beep even if the keys are not displayed
 ;   - fix: now it always counts mouse clicks
 ;   v2.70 (2017-10-03)
 ;   - added option to toggle dead keys support / work-around.
 ;   - added option to hide annoying keys that usually get in the way: Left Click and Print Screen.
 ;   - redraw improvements, reduced flickering
 ;   - disabled OSD transparency to reduce flickering
 ;   v2.69 (2017-09-30)
 ;   - OSD / GUI hides when mouse is over it. Many thanks to phaleth!
 ;   v2.68 (2017-09-29)
 ;   - numpad keys now work in typing mode as expected; they appear as symbols or numbers
 ;
 ; by phaleth from irc.freenode.net #ahk
 ;   v2.67 (2017-09-28)
 ;   - dead keys improvements
 ;
 ; by Marius Sucan (robodesign.ro)
 ;   v2.66 (2017-09-28)
 ;   - key combinations with Shift work better.
 ;   v2.65 (2017-09-27)
 ;   - Fixed a bug with counting modifier keys;
 ;   - improved the dead keys work-around
 ;   - friendly names for mouse clicks
 ;   - when pressed, volume keys  always generate beeps
 ;   - added option to differentiate between left and right modifiers
 ;   - now it detects AltGr key
 ;   - the key beeper now also makes a beep for modifiers.
 ;   - capslock no longer erases text you are typing displayed by the OSD
 ;   - now you can toggle between two different OSD positions with Ctrl + Alt + Shift + F9
 ;   v2.60 (2017-09-26)
 ;   - Fixed many bugs with counting keys;
 ;   - reimplemented the feature to see the previous key combination, if quickly a new one is pressed;
 ;   - added options/settings to toggle previous keys, counting keys and delay;
 ;   - added shortcuts to toggle ShowSingleKey option and to reinitialize the OSD;
 ;   - added option for automatic resizing of the OSD; it can be turned off in the settings section; it is a fishy implementation, but if one adjusts it, can make it to suit personal needs;
 ;   - new option: beep when key is released or when writing with capslock
 ;   v2.58 (2017-09-23)
 ;   - Numpad keys have friendly naming, based on the numlock state.
 ;   - Combinations with space and backspace work again.
 ;   v2.56 (2017-09-22)
 ;   - more fixes for space usage and key combinations;
 ;   - now it indicates when ScrollLock, NumLock and Capslock are activated.
 ;   v2.55 (2017-09-21)
 ;   - minor fixes for space usage and key combinations.
 ;
 ; by Saiapatsu from irc.freenode.net #ahk
 ;   v2.54 (2017-09-21)
 ;   - Scrolls through n recently typed characters instead of just the latest word
 ;   v2.53 (2017-09-21)
 ;   - Case change effect limited to the loop 95 letters only.
 ;   v2.52 (2017-09-21)
 ;   - Now supports backspace. Commented out CapsLock beeper.
 ;   v2.51 (2017-09-21)
 ;   - Changed labels to functions, added ToolWindow style to window, changed DisplayTime
 ;   calculation, made it show last word typed, hid spacebar presses
 ;   todo: make Shift look less ugly
 ;
 ; by Marius Sucan (robodesign.ro)
 ;   v2.50 (2017-09-20)
 ;   - Changed the OSD positioning and sizing. It was based on the current window. Now it is always fixed in a specific place. Added a Capslock beeper.
 ;
 ; by tmplinshi from https://autohotkey.com/boards/viewtopic.php?f=6&t=225
 ;   v2.22 (2017-02-25)
 ;   - Now pressing same combination keys continuously more than 2 times,
 ;   for example press Ctrl+V 3 times, will displayed as "Ctrl + v (3)"
 ;   v2.21 (2017-02-24)
 ;   - Fixed LWin/RWin not poping up start menu
 ;   v2.20 (2017-02-24)
 ;   - Added displaying continuous-pressed combination keys.
 ;   e.g.: With CTRL key held down, pressing K and U continuously will shown as "Ctrl + k, u"
 ;   v2.10 (2017-01-22)
 ;   - Added ShowStickyModKeyCount option
 ;   v2.09 (2017-01-22)
 ;   - Added ShowModifierKeyCount option
 ;   v2.08 (2017-01-19)
 ;   - Fixed a bug
 ;   v2.07 (2017-01-19)
 ;   - Added ShowSingleModifierKey option (default is True)
 ;   v2.06 (2016-11-23)
 ;   - Added more keys. Thanks to SashaChernykh.
 ;   v2.05 (2016-10-01)
 ;   - Fixed not detecting "Ctrl + ScrollLock/NumLock/Pause". Thanks to lexikos.
 ;   v2.04 (2016-10-01)
 ;   - Added NumpadDot and AppsKey
 ;   v2.03 (2016-09-17)
 ;   - Added displaying "Double-Click" of the left mouse button.
 ;   v2.02 (2016-09-16)
 ;   - Added displaying mouse button, and 3 settings (ShowMouseButton, FontSize, GuiHeight)
 ;   v2.01 (2016-09-11)
 ;   - Display non english keyboard layout characters when combine with modifer keys.
 ;   v2.00 (2016-09-01)
 ;   - Removed the "Fade out" effect because of its buggy.
 ;   - Added support for non english keyboard layout.
 ;   - Added GuiPosition setting.
 ;   v1.00 (2013-10-11)
 ;   - First release by tmplinshi based on RaptorX. Function keys, numpad keys and mouse clicks support. Popups at mouse position.
 ;   v0.50 (2010-03-18)
 ;   - Released by RaptorX.
;--------------------------------------------------------------------------------------------------------------------------
;
; TO-DO:
 ; features to implement:
 ; - after pressing Shift + [letter] if the following key is a single letter, remember the first Shift + [letter];
 ; - when user types single letters, display Shift + (sign or numeric keys) as signs/symbols, eg. Shift + 0, should be displayed as );
 ; - visual mouse clicks
 ; - clipboard monitoring
 ; - tray menu, with options
 ; - show a generic symbol for ignored dead keys; helps to clarify one was pressed;
 ;
 ; glitches to fix [by priority]:
 ; - [regression since v2.52] show Shift as a modifier, as Ctrl and Alt; Shift should behave as the other two; it is never displayed alone and countable;
 ; - [bug, since v1.00] Shift + [numpad] does not work; no idea why
 ; - [bug] once a key is pressed, counting of key fires is not initiated after the OSD was hidden for a little awhile;
 ; - make dead keys work better, even detect them ;)
 ; - automatic resizing of the OSD/GUI is just a silly hack based on the default font size and the number of typed chars; it often fails to resize properly;
 ; - redraw issues; it still flickers;
 ; - make it work reliably with sticky keys; if user presses once Ctrl and another key afterwards, it rarely detects the combination on Winndows 7;
;----------------------------------------------------------------------------

; Initialization
#SingleInstance force
#NoEnv
#MaxHotkeysPerInterval 500
SetBatchLines, -1
ListLines, Off

; Settings:
    global DeadKeys              := 0     ; a toggle for a partial dead keys support. Zero [0] means no dead keys. See CreateHotkey() and char2skip to define the dead keys.
    global CustomRegionalKeys    := 0     ; if you want to add support to a regional keyboard
    global RegionalKeysList      := "a|b|c"  ; add the characters in this list, separated by |
    global AutoDetectKBD         := 1     ; at start, detect keyboard layout
    global ConstantAutoDetect    := 0     ; continously check if the keyboard layout changed; if AutoDetectKBD=0, this is ignored
    global SilentDetection       := 0     ; do not display information about language switching
    global audioAlerts           := 1     ; generate beeps when key bindings fail

    global ShowSingleKey         := 1     ; show only key combinations
    global ShowMouseButton       := 1     ; if disabled, mouse click beeps will never occur
    global HideAnnoyingKeys      := 1     ; Left click and PrintScreen can easily get in the way.
    global ShowSingleModifierKey := 0     ; make it display Ctrl, Alt, Shift when pressed alone
    global DifferModifiers       := 0     ; differentiate between left and right modifiers
    global ShowPrevKey           := 1     ; show previously pressed key, if pressed quickly in succession
    global ShowPrevKeyDelay      := 300
    global ShowKeyCount          := 1     ; count how many times a key is pressed
    global ShowKeyCountFired     := 1     ; show only key presses (0) or catch key fires as well (1)

;   global TransparencyLevel     := 220   ; OSD transparency; range from 0 to 255. feature disabled, to reduce flickering
    global DisplayTime           := 3000  ; in milliseconds
    global GuiWidth              := 360
    global GuiHeight             := 50
    global GuiX                  := 50
    global GuiYa                 := 250   ; toggle between GuiYa and GuiYb with Ctrl + Alt + Shift + F9
    global GuiYb                 := 800
    global FontSize              := 19
    global OSDautosize           := 1     ; make adjustments in ShowHotkey() to match your font size
    global NumLetters            := 25    ; amount of recently typed letters to display
    global NumLettersAutosize    := 60    ; ...when OSD resizes automatically

    global CapslockBeeper        := 1     ; only when the key is released
    global KeyBeeper             := 0     ; only when the key is released
    global ModBeeper             := 0     ; beeps for every modifier, when released
    global MouseBeeper           := 0
    global BeepHiddenKeys        := 0     ; [when any beeper enabled] to beep or not when keys are not displayed by OSD/HUD

    global KeyboardShortcuts     := 1     ; system-wide shortcuts
    global PermanentSettings     := 1     ; settings stored in keypress-osd.ini override the settings from here


; Initialization variables. Altering these may lead to undesired results.
    global typed := ""
    global visible := 0
    global GuiY := GuiYb
    global prefixed := 0 ; hack used to determine if last keypress had a modifier
    global zcSCROL := "SCROLL LOCK"
    global tickcount_start := 0   ; timer to count repeated key presses
    global keyCount := 0
    global ShowKeyCountDelay := (ShowKeyCountFired = 0) ? 700 : 6000
    NumLetters := (OSDautosize=1) ? NumLettersAutosize : NumLetters
    global text_width := 60
    global InputLocaleID := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")

    if PermanentSettings=1
    {
        IniRead, ShowSingleKey, keypress-osd.ini, PermanentSettings, ShowSingleKey
        IniRead, GuiY, keypress-osd.ini, PermanentSettings, GuiY
        GuiY := (GuiY=GuiYb || GuiY=GuiYa) ? GuiY : GuiYb
    }

    IniRead, CustomMultiLangToggled, keypress-osd.ini, PermanentSettings, CustomMultiLangToggled
    if (CustomMultiLangToggled=1) {
        IniRead, CustomRegionalKeys, keypress-osd.ini, PermanentSettings, CustomRegionalKeys
        CustomMultiLangToggled := 0
        IniWrite, %CustomMultiLangToggled%, keypress-osd.ini, PermanentSettings, CustomMultiLangToggled
    }

    IniRead, AutoDetectKBDToggled, keypress-osd.ini, PermanentSettings, AutoDetectKBDToggled
    if (AutoDetectKBDToggled=1) {
        IniRead, AutoDetectKBD, keypress-osd.ini, PermanentSettings, AutoDetectKBD
        IniRead, ConstantAutoDetect, keypress-osd.ini, PermanentSettings, ConstantAutoDetect
        AutoDetectKBDToggled := 0
        IniWrite, %AutoDetectKBDToggled%, keypress-osd.ini, PermanentSettings, AutoDetectKBDToggled
    }

    CreateGUI()
    CreateGlobalShortcuts()
    CreateHotkey()
    return

; The script

GetSpecialKeysStates() {
    GetKeyState, ScrollState, ScrollLock, T   
    If ScrollState = D
    {
       global zcSCROL := "SCROLL LOCK ON"
    }
    else {
       global zcSCROL := "Scroll lock off"
    }
}

TypedLetter(key) {
    return typed := SubStr(typed key, -NumLetters)
}

OnMousePressed() {
    global tickcount_start := A_TickCount-500

    try {
        key := GetKeyStr()
        typed := "" ; concerning TypedLetter(" ") - it resets the content of the OSD
        ShowHotkey(key)
        SetTimer, HideGUI, % -DisplayTime
    }

    If (MouseBeeper = 1) && (ShowMouseButton = 1) && (ShowSingleKey = 1) || (MouseBeeper = 1) && (ShowSingleKey = 0) && (BeepHiddenKeys = 1)
       soundbeep, 2500, 65

}

OnKeyPressed() {
    try {
        key := GetKeyStr()
        if (!(key ~= "i)^(Ctrl|LCtrl|RCtrl|Shift|LShift|RShift|Alt|Altgr|LAlt|RAlt|LWin|RWin|Winkey|Insert|Tab)$")) {
           typed := ""
        }
        ShowHotkey(key)
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnLetterPressed() {
    try {
        if typed && DeadKeys=1
        {
            sleep, 25    ; this delay helps with dead keys, but it generates errors; the following actions: stringleft,1 and stringlower help correct these
        } else
        {
        }

        key := GetKeyStr(1)     ; consider it a letter

        if prefixed
        {
            typed := ""
            ShowHotkey(key)
        } else
        {
            StringLeft, key, key, 1
            Stringlower, key, key

            GetKeyState, CapsState, CapsLock, T
            If CapsState != D
            {
                 if GetKeyState("Shift", "P")
                {
                  StringUpper, key, key
                }
            } else
                StringUpper, key, key

            TypedLetter(key)
            ShowHotkey(typed)
        }
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnSpacePressed() {
    try {
        key := GetKeyStr()
        if prefixed
        {
            typed := ""
            ShowHotkey(key)
        } else if typed
        {
            TypedLetter("_")
            ShowHotkey(typed)
        } else if (!typed)
        {
            ShowHotkey(key)
            typed := ""
        }
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnBspPressed() {
    try
    {
        key := GetKeyStr()
        if prefixed
        {
            typed := ""
            ShowHotkey(key)
        } else if typed
        {
            typed := SubStr(typed, 1, StrLen(typed) - 1)
            ShowHotkey(typed)
        } else if !typed
        {
            typed := ""
            ShowHotkey(key)
        }
        SetTimer, HideGUI, % -DisplayTime
    }
    if (BeepHiddenKeys = 1) && (KeyBeeper = 1) && (ShowSingleKey = 0)
       soundbeep, 1900, 45

    if (KeyBeeper = 1) && (ShowSingleKey = 1)
       soundbeep, 1900, 45
}

OnCapsPressed() {
    try
    {
        if typed
        {
            ShowHotkey(typed)
        } else if (!typed)
        {
            key := GetKeyStr()
            GetKeyState, CapsState, CapsLock, T
            if CapsState = D
            {
                key := prefixed ? key : "CAPSLOCK ON"
            } else
                key := prefixed ? key : "CapsLock off"
            ShowHotkey(key)
            SetTimer, HideGUI, % -DisplayTime
        }
    }

    If (CapslockBeeper = 1) && (ShowSingleKey = 1) || (BeepHiddenKeys = 1)
       {
        soundbeep, 450, 200
       }
}

OnNumpadPressed()
{
    GetKeyState, NumState, NumLock, T
    try {
        key := GetKeyStr()
        if NumState != D
        {
            typed := "" ; reset typed content
            ShowHotkey(key)
        } else if prefixed
        {
            typed := ""
            ShowHotkey(key)
        } else if NumState = D
        {
            key := GetKeyStr(1)
            if (StrLen(key)=5)
            {
              StringLeft, key, key, 3
              StringRight, key, key, 1
            }
            TypedLetter(key)
            ShowHotkey(typed)
        }
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnModPressed() {
    try {
        keyCount := keyCount+1
        key := GetKeyStr()
        ShowHotkey(key)
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnKeyUp() {
    global tickcount_start := A_TickCount

    if typed && (CapslockBeeper = 1) && (ShowSingleKey = 1)
    {
        GetKeyState, CapsState, CapsLock, T
        If CapsState = D
           {
             soundbeep, 450, 25
           }
           else if (KeyBeeper = 1) && (ShowSingleKey = 1)
           {
             soundbeep, 1900, 45
           }
    }

    If (CapslockBeeper = 0) && (KeyBeeper = 1) && (ShowSingleKey = 1)
       {
         soundbeep, 1900, 45
       }
       else if (CapslockBeeper = 1) && (KeyBeeper = 0)
       {
       }
       else if !typed && (CapslockBeeper = 1) && (ShowSingleKey = 1)
       {
         soundbeep, 1900, 45
       }

    if (BeepHiddenKeys = 1) && (KeyBeeper = 1) && (ShowSingleKey = 0)
         soundbeep, 1900, 45  
}

OnModUp() {
    global tickcount_start := A_TickCount

    If (ModBeeper = 1) && (ShowSingleKey = 1) && (ShowSingleModifierKey = 1) || (ModBeeper = 1) && (BeepHiddenKeys = 1)
       soundbeep, 1000, 65

}

; ===================================================================================

CreateGUI() {
    global

    Gui, +AlwaysOnTop -Caption +Owner +LastFound +ToolWindow +E0x20
    Gui, Margin, 10, 10
    Gui, Color, 111111
    Gui, Font, cWhite s%FontSize% bold, Arial, -wrap
    Gui, Add, Text, vHotkeyText left x10 y10 -wrap

;    if TransparencyLevel between 10 and 245
;       WinSet, Transparent, %TransparencyLevel%

}

CreateHotkey() {

    if (CustomRegionalKeys=1) && (AutoDetectKBD=0)
    {
        Loop, parse, RegionalKeysList, |
        {
           Hotkey, % "~*" A_LoopField, OnLetterPressed, useErrorLevel
           Hotkey, % "~*" A_LoopField " Up", OnKeyUp, useErrorLevel
           if (errorlevel!=0) && (audioAlerts=1)
              soundbeep, 1900, 50
        }
    }
   if (AutoDetectKBD=1)
   {
       IdentifyKBDlayout()
   }

    Loop, 95
    {
        k := Chr(A_Index + 31)

        if (DeadKeys=1)
        {
            for each, char2skip in StrSplit("``,^,6,',"",~", ",")        ; dead keys to ignore
            {
                if (k = char2skip && DeadKeys=1)
                {
                    continue, 2
                }
            }
        }

        if (k = " ")
        {
            Hotkey, % "~*Space", OnSpacePressed, useErrorLevel
            Hotkey, % "~*Space Up", OnKeyUp, useErrorLevel
        }
        else
        {
            Hotkey, % "~*" k, OnLetterPressed, useErrorLevel
            Hotkey, % "~*" k " Up", OnKeyUp, useErrorLevel
            if (errorlevel!=0) && (audioAlerts=1)
               soundbeep, 1900, 50
        }
    }

    Hotkey, % "~*Backspace", OnBspPressed, useErrorLevel
    Hotkey, % "~*CapsLock", OnCapsPressed, useErrorLevel
    Hotkey, % "~*CapsLock Up", OnKeyUp, useErrorLevel

    Loop, 24 ; F1-F24
    {
        Hotkey, % "~*F" A_Index, OnKeyPressed, useErrorLevel
        Hotkey, % "~*F" A_Index " Up", OnKeyUp, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    NumpadKeysList := "sc04E|sc04A|sc052|sc04F|sc050|sc051|sc04B|sc04C|sc04D|sc047|sc048|sc049|sc053|sc037|sc135"

    Loop, parse, NumpadKeysList, |
    {
       Hotkey, % "~*" A_LoopField, OnNumpadPressed, useErrorLevel
       Hotkey, % "~*" A_LoopField " Up", OnKeyUp, useErrorLevel
       if (errorlevel!=0) && (audioAlerts=1)
          soundbeep, 1900, 50
    }

    Otherkeys := "WheelDown|WheelUp|WheelLeft|WheelRight|XButton1|XButton2|Browser_Forward|Browser_Back|Browser_Refresh|Browser_Stop|Browser_Search|Browser_Favorites|Browser_Home|Volume_Mute|Volume_Down|Volume_Up|Media_Next|Media_Prev|Media_Stop|Media_Play_Pause|Launch_Mail|Launch_Media|Launch_App1|Launch_App2|Help|Sleep|PrintScreen|CtrlBreak|Break|AppsKey|Tab|Enter|Esc"
               . "|Insert|Home|End|Up|Down|Left|Right|ScrollLock|NumLock|Pause|sc145|sc146|sc046|sc123|sc11C|sc149|sc151|sc122|sc153"
    Loop, parse, Otherkeys, |
    {
        Hotkey, % "~*" A_LoopField, OnKeyPressed, useErrorLevel
        Hotkey, % "~*" A_LoopField " Up", OnKeyUp, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    If ShowMouseButton=1
    {
        Loop, Parse, % "LButton|MButton|RButton", |
        Hotkey, % "~*" A_LoopField, OnMousePressed, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    for i, mod in ["LCtrl", "RCtrl", "LAlt", "RAlt"] {
        Hotkey, % "~*" mod, OnKeyPressed, useErrorLevel
        Hotkey, % "~*" mod " Up", OnModUp, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    for i, mod in ["LWin", "RWin"] {
        Hotkey, % "~*" mod, OnKeyPressed, useErrorLevel
        Hotkey, % "~*" mod " Up", OnModUp, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    If typed {
    for i, mod in ["LShift", "RShift"]
        Hotkey, % "~*" mod, OnKeyPressed, useErrorLevel
        Hotkey, % "~*" mod " Up", OnModUp, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    If (ShowSingleModifierKey=1)
    {
      for i, mod in ["LShift", "RShift"]   ; , "LCtrl", "RCtrl", "LAlt", "RAlt", "LWin", "RWin"]
        Hotkey, % "~*" mod, OnModPressed, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }
}

ShowHotkey(HotkeyStr) {

    if (OSDautosize=1)
    {
        HotkeyTextTrimmed := RegExReplace(HotkeyStr, "[^a-zA-Z]", "")
        StringLeft, HotkeyTextTrimmed, HotkeyTextTrimmed, 5
        growthFactor := 1.3
        if HotkeyTextTrimmed is upper
           growthFactor := 1.05
        text_width := (StrLen(HotkeyStr)/growthFactor)*FontSize
        text_width := (text_width<70) ? 70 : text_width+15
    } else if OSDautosize=0
    {
        text_width := GuiWidth
    }

    if !typed
    {
        StringUpper, HotkeyStr, HotkeyStr, T
    }

    GuiControl,     , HotkeyText, %HotkeyStr%
    GuiControl, Move, HotkeyText, w%text_width% left
    Gui, Show, NoActivate x%GuiX% y%GuiY% AutoSize, KeypressOSD
    visible := 1
    SetTimer, checkMousePresence, on, 400
}

GetKeyStr(letter := 0) {
    static modifiers := ["LCtrl", "RCtrl", "LAlt", "RAlt", "LShift", "RShift", "LWin", "RWin"]
    static repeatCount := 1

    ; If any mod but shift, go ; If shift, check if not letter

    for i, mod in modifiers
    {
        if (mod = "LShift" && typed || mod = "RShift" && typed ? (!letter && GetKeyState(mod)) : GetKeyState(mod))
     ;  if GetKeyState(mod)
           prefix .= mod " + "
    }

    if (!prefix && !ShowSingleKey)
        throw

    key := SubStr(A_ThisHotkey, 3)

    if (key ~= "i)^(LCtrl|RCtrl|LShift|RShift|LAlt|RAlt|LWin|RWin)$") {
        if (ShowSingleModifierKey = 0) || (ShowSingleKey = 0) || (A_TickCount-tickcount_start > 2000) && visible
        {
            throw
        } else
        {
             key := ""
             prefix := RTrim(prefix, "+ ")
        }

        prefix := CompactModifiers(prefix)

        if (ShowKeyCount=1) {
            if !InStr(prefix, "+") && IsDoubleClickEx() {
                if (A_ThisHotKey != A_PriorHotKey) || (ShowKeyCount=1) && (A_TickCount-tickcount_start < 700) {
                    if (++repeatCount > 1) {
                        prefix .= " (" repeatCount ")"
                    }
                } else {
                    repeatCount := 0
                }
            } else {
                repeatCount := 1
            }
        }
    } else {
        if StrLen(key)=1
        {
            key := GetKeyChar(key, "A")
        } else if ( SubStr(key, 1, 2) = "sc" ) {
            key := SpecialSC(key)
        } else if (key = "Volume_Up") || (key = "Volume_Down") {
            soundbeep, 150, 40
        } else if (key = "PrintScreen") {
            if HideAnnoyingKeys
                throw
            key := "Print Screen"
        } else if (key = "MButton") {
            key := "Middle Click"
        } else if (key = "RButton") {
            key := "Right Click"
        } else if (key = "LButton") && IsDoubleClick() {
            key := "Double-Click"
        } else if (key = "LButton") {
            if HideAnnoyingKeys
                throw
            key := "Left Click"
        }
        {
            _key := (key = "Double-Click") ? "Left Click" : key
        }

        prefix := CompactModifiers(prefix)

        static pre_prefix, pre_key, keyCount := 1
        keyCount := (key=pre_key) && (prefix = pre_prefix) && (repeatCount<1.5)? keyCount : 1
        global ShowKeyCountDelay := (ShowKeyCountFired = 0) ? 700 : 6000
        ShowKeyCountDelay := (ShowKeyCountFired=1) ? (ShowKeyCountDelay+keyCount*100) : ShowKeyCountDelay

        if (InStr(prefix, "+")) && (A_TickCount-tickcount_start < ShowKeyCountDelay) || (!letter) && (A_TickCount-tickcount_start < ShowKeyCountDelay)
        {
            if (ShowPrevKey=1) && (A_TickCount-tickcount_start < ShowPrevKeyDelay) && !typed && !letter
            {
                ShowPrevKeyValid := 1
            } else
            {
                ShowPrevKeyValid := 0
            }
            if (prefix != pre_prefix) {
                result := (ShowPrevKeyValid=1) ? prefix key " {" pre_prefix pre_key "}" : prefix key
            } else if (ShowPrevKeyValid=1) && (key != pre_key) || (ShowKeyCount=1) && (ShowPrevKeyValid=1)
            {
                keyCount := (key=pre_key) && (ShowKeyCount=1) ? (keyCount+1) : 1
                key := (keyCount>1) && (ShowKeyCount=1) ? (key " (" keyCount ")") : (key ", " pre_key)
            } else if (ShowPrevKeyValid=0)
            {
                keyCount := (key=pre_key) && (ShowKeyCount=1) ? (keyCount+1) : 1
                key := (keyCount>1) ? (key " (" keyCount ")") : (key)
            }
        } else {
            keyCount := 1
        }

        pre_prefix := prefix
        pre_key := _key

        repeatCount := 1
    }

    prefixed := prefix ? 1 : 0
    return result ? result : prefix . key
}

CompactModifiers(stringy)
{
    if DifferModifiers = 1
    {
        StringReplace, stringy, stringy, LCtrl + RAlt, AltGr, All
        StringReplace, stringy, stringy, LCtrl + RCtrl + RAlt, RCtrl + AltGr, All
        StringReplace, stringy, stringy, RAlt, AltGr, All
        StringReplace, stringy, stringy, LAlt, Alt, All
    } else if (DifferModifiers = 0)
    {
        StringReplace, stringy, stringy, LCtrl + RAlt, AltGr, All
        ; StringReplace, stringy, stringy, LCtrl + RCtrl + RAlt, RCtrl + AltGr, All
        StringReplace, stringy, stringy, LCtrl, Ctrl, All
        StringReplace, stringy, stringy, RCtrl, Ctrl, All
        StringReplace, stringy, stringy, LShift, Shift, All
        StringReplace, stringy, stringy, RShift, Shift, All
        StringReplace, stringy, stringy, LAlt, Alt, All
        StringReplace, stringy, stringy, LWin, WinKey, All
        StringReplace, stringy, stringy, RWin, WinKey, All
        StringReplace, stringy, stringy, Ctrl + Ctrl, Ctrl, All
        StringReplace, stringy, stringy, Shift + Shift, Shift, All
        StringReplace, stringy, stringy, WinKey + WinKey, WinKey, All
        StringReplace, stringy, stringy, RAlt, AltGr, All
    }
    return stringy
}

SpecialSC(sc) {
    GetSpecialKeysStates()

    GetKeyState, NumState, NumLock, T
    If NumState = D
    {
       k := {sc046: zcSCROL, sc145: "NUM LOCK ON", sc146: "Pause", sc123: "Genius LuxeMate Scroll", sc04E: "[ + ]", sc04A: "[ - ]", sc052: "[ 0 ]", sc04F: "[ 1 ]", sc050: "[ 2 ]", sc051: "[ 3 ]", sc04B: "[ 4 ]", sc04C: "[ 5 ]", sc04D: "[ 6 ]", sc047: "[ 7 ]", sc048: "[ 8 ]", sc049: "[ 9 ]", sc053: "[ . ]", sc037: "[ * ]", sc135: "[ / ]", sc11C: "[Enter]", sc149: "Page Up", sc151: "Page Down", sc153: "Delete", sc122: "Media_Play/Pause"}
    }
    else {
       k := {sc046: zcSCROL, sc145: "Num lock off", sc146: "Pause", sc123: "Genius LuxeMate Scroll", sc04E: "[ + ]", sc04A: "[ - ]", sc052: "[Insert]", sc04F: "[End]", sc050: "[Down]", sc051: "[Page Down]", sc04B: "[Left]", sc04C: "[Undefined]", sc04D: "[Right]", sc047: "[Home]", sc048: "[Up]", sc049: "[Page Up]", sc053: "[Delete]", sc037: "[ * ]", sc135: "[ / ]", sc11C: "[Enter]", sc149: "Page Up", sc151: "Page Down", sc153: "Delete", sc122: "Media_Play/Pause"}
    }
    return k[sc]
}

; <tmplinshi>: thanks to Lexikos: https://autohotkey.com/board/topic/110808-getkeyname-for-other-languages/#entry682236
; This enables partial support for non-English keyboard layouts.
; If the script initializes with the English keyboard layout, but then used with another one, this function gets proper key names,

GetKeyChar(Key, WinTitle:=0)
{
    thread := WinTitle=0 ? 0
        : DllCall("GetWindowThreadProcessId", "ptr", WinExist(WinTitle), "ptr", 0)
    hkl := DllCall("GetKeyboardLayout", "uint", thread, "ptr")
    vk := GetKeyVK(Key), sc := GetKeySC(Key)
    VarSetCapacity(state, 256, 0)
    VarSetCapacity(char, 4, 0)
    n := DllCall("ToUnicodeEx", "uint", vk, "uint", sc
        , "ptr", &state, "ptr", &char, "int", 2, "uint", 0, "ptr", hkl)
    return StrGet(&char, n, "utf-16")
}
global LangsBinded := 0

IdentifyKBDlayout() {
  VarSetCapacity(kbLayoutRaw, 32, 0)
  DllCall("GetKeyboardLayoutName", "Str", kbLayoutRaw)
  StringRight, kbLayout, kbLayoutRaw, 4

  #Include *i keypress-osd-languages.ini

  check_kbd := StrLen(LangName_%kbLayout%)>2 ? 1 : 0
  check_kbd_exact := StrLen(LangRaw_%kbLayoutRaw%)>2 ? 1 : 0
  if (check_kbd_exact=0)
  {
      partialKBDmatch = (Partial match)
  }

  if (check_kbd=0)
  {
      ShowHotkey("Unrecognized layout: (kbd " kbLayoutRaw ").")
      SetTimer, HideGUI, % -DisplayTime
      soundbeep, 500, 900
  }

  StringLeft, kbLayoutSupport, LangName_%kbLayout%, 1
  if (kbLayoutSupport="-") && (check_kbd=1)
  {
      ShowHotkey("Unsupported layout: " LangName_%kbLayout% " (kbd" kbLayout ").")
      SetTimer, HideGUI, % -DisplayTime
      soundbeep, 500, 900
  }

  IfInString, LangsBinded, %kbLayout%
  {
      KBDbinded := 1
  } else
  {
      LangsBinded := kbLayout "|" LangsBinded 
      StringLeft, LangsBinded, LangsBinded, 20
      KBDbinded := 0
  }

  if (kbLayoutSupport!="-") && (check_kbd=1) && (KBDbinded=0)
  {

      Loop, parse, LangChars_%kbLayout%, |
      {
         Hotkey, % "~*" A_LoopField, OnLetterPressed, useErrorLevel
         Hotkey, % "~*" A_LoopField " Up", OnKeyUp, useErrorLevel
         if (errorlevel!=0) && (audioAlerts=1)
             soundbeep, 1900, 50
      }
      if SilentDetection=0
      {
          ShowHotkey("Keyboard layout: " LangName_%kbLayout% " (kbd" kbLayout "). " partialKBDmatch)
          SetTimer, HideGUI, % -DisplayTime/4
      }
  }

   if (ConstantAutoDetect=1) && (AutoDetectKBD=1)
      SetTimer, ConstantKBDchecker, 1500
}

ConstantKBDchecker() {
  SetFormat, Integer, H
  WinGet, WinID,, A
  ThreadID := DllCall("GetWindowThreadProcessId", "UInt", WinID, "UInt", 0)
  NewInputLocaleID := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")
    if (InputLocaleID != NewInputLocaleID)
    {
        InputLocaleID := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")
        
        if SilentDetection=0
        ShowHotkey("Changed keyboard layout: " InputLocaleID)
        
        sleep, 350
        Reload
        ;WinActivate, KeypressOSD
        ;SetTimer, HideGUI, % -DisplayTime/3
        ;IdentifyKBDlayout()
    } 
}

IsDoubleClick(MSec = 300) {
    Return (A_ThisHotKey = A_PriorHotKey) && (A_TimeSincePriorHotkey < MSec)
}

IsDoubleClickEx(MSec = 300) {
    preHotkey := RegExReplace(A_PriorHotkey, "i) Up$")
    Return (A_ThisHotKey = preHotkey) && (A_TimeSincePriorHotkey < MSec)
}

HideGUI() {
    visible := 0
    Gui, Hide
    SetTimer, checkMousePresence, off, 300
}

checkMousePresence() {
    id := mouseIsOver()
    title := getWinTitleFromID(id)
    if title = KeypressOSD
       HideGUI()
}

mouseIsOver() {
    MouseGetPos,,, id
    return id
}

getWinTitleFromID(id) {
    WinGetTitle, title, % "ahk_id " id
    return title
}

CreateGlobalShortcuts() {
   if (KeyboardShortcuts=1) {
      Hotkey, !+^F8, ToggleShowSingleKey
      Hotkey, !+^F9, TogglePosition
      Hotkey, !+^F10, EnableCustomKeys
      Hotkey, !+^F11, DetectLangNow
      Hotkey, !+^F12, ReloadScript
    }
}

ToggleShowSingleKey:
    ShowSingleKey := (!ShowSingleKey) ? true : false
    Gui, Destroy
    sleep, 100
    CreateGUI()
    sleep, 100
    IniWrite, %ShowSingleKey%, keypress-osd.ini, PermanentSettings, ShowSingleKey
    ShowHotkey("Show single keys = " ShowSingleKey)
    SetTimer, HideGUI, % -DisplayTime
return

TogglePosition:
    GuiY := (GuiY = GuiYb) ? GuiYa : GuiYb
    Gui, Destroy
    sleep, 100
    CreateGUI()
    sleep, 100
    IniWrite, %GuiY%, keypress-osd.ini, PermanentSettings, GuiY
    ShowHotkey("OSD position changed")
    SetTimer, HideGUI, % -DisplayTime
return

EnableCustomKeys:
    global CustomRegionalKeys := CustomRegionalKeys = 1 ? 0 : 1
    CustomMultiLangToggled := 1
    IniWrite, %CustomMultiLangToggled%, keypress-osd.ini, PermanentSettings, CustomMultiLangToggled
    IniWrite, %CustomRegionalKeys%, keypress-osd.ini, PermanentSettings, CustomRegionalKeys
    ShowHotkey("Custom Regional keys = " RegionalKeys)
    sleep, 1000
    Reload
return

DetectLangNow:
    global AutoDetectKBD := 1
    global ConstantAutoDetect := 0
    AutoDetectKBDToggled := 1
    IniWrite, %AutoDetectKBDToggled%, keypress-osd.ini, PermanentSettings, AutoDetectKBDToggled
    IniWrite, %AutoDetectKBD%, keypress-osd.ini, PermanentSettings, AutoDetectKBD
    IniWrite, %ConstantAutoDetect%, keypress-osd.ini, PermanentSettings, ConstantAutoDetect
    ShowHotkey("Detecting keyboard layout...")
    sleep, 800
    Reload
return

ReloadScript:
    Gui, Destroy
    sleep, 100
    CreateGUI()
    sleep, 100
    ShowHotkey("OSD reinitializing...")
    sleep, 1500
    Reload
return
; TO-DO:
; features to implement:
; - after pressing Shift + [letter] if the following key is a single letter, remember the first Shift + [letter]; done
; - when user types single letters, display Shift + (sign or numeric keys) as signs/symbols, eg. Shift + 0, should be displayed as ); done
; - visual mouse clicks done
; - clipboard monitoring done
; - tray menu, with options done
; - show a generic symbol for ignored dead keys; helps to clarify one was pressed;
;
; glitches to fix [by priority]:
; - display previous key for keys pertaining to loop, 95 done
; - [regression since v2.52] show Shift as a modifier, as Ctrl and Alt; Shift should behave as the other two; it is never displayed alone and countable; done
; - [bug, since v1.00] Shift + [numpad] does not work; no idea why
; - [bug] once a key is pressed, counting of key fires is not initiated after the OSD was hidden for a little awhile;
; - make dead keys work better, even detect them ;)
; - automatic resizing of the OSD/GUI is just a silly hack based on the default font size and the number of typed chars; it often fails to resize properly;
; - redraw issues; it still flickers;
; - make it work reliably with sticky keys; if user presses once Ctrl and another key afterwards, it rarely detects the combination on Winndows 7;

Also at http://p.ahkscript.org/?p=8498fb0e

Best regards, Marius
Last edited by robodesign on 29 Oct 2017, 06:27, edited 1 time in total.
-------------------------
KeyPress OSD v4: GitHub or forum. (presentation video)
My home page.
User avatar
Drugwash
Posts: 567
Joined: 29 May 2014, 21:07
Location: Ploieşti, Romania

Re: [Script] KeypressOSD - Display key press on screen

Post by Drugwash » 11 Oct 2017, 09:04

Displaying spaces as underscores can be confusing in certain situations. for example, if I type _ __ _ ___ the OSD would display -___--_-_--- which is not in the least similar to the actual characters typed. Can't find a suitable replacement for this issue though.

The modifiers (Shift/Ctrl/Alt/AltGr) should be displayed when pressed, mainly as a proof that the keyboard does function correctly, and when any other keys are pressed the combo - if accepted by the current layout - should replace the single modifier. Yes, this may prove to be tricky but should be the correct way to handle keypresses IMHO.

The OSK (in XP, at least) does detect pressing of Shift and AltGr (on RO layout) and switches key symbols accordingly in its graphic interface. Shift is also visibly displayed as pressed. This could be important at least for users of unreliable keyboards.

Dunno if I already stated this but I feel such kind of script definitely needs a system-wide keyboard/mouse hook similar to a keylogger. I know this falls at best into a grey area but considering all the issues with different layouts and the difficulties in capturing and displaying the key presses I believe it's the only reliable method. Of course, a handy option to temporarily hide the OSD (during typing of passwords or other sensible information) should be implemented (if not already present), but otherwise this script would never get close to perfection if it only relies on assigning hotkeys.

Please, other respectable members of the community share your opinion on this. Thank you.
I've deleted my CloudMe account because of GDPR - the now legal base for privacy invasion and data theft.
robodesign
Posts: 371
Joined: 30 Sep 2017, 03:59
Facebook: marius.sucan
GitHub: mariussucan
Location: Romania
Contact:

Re: [Script] KeypressOSD - Display key press on screen

Post by robodesign » 11 Oct 2017, 13:10

Hello!

To type passwords, you must toggle Show Single Keys. Ctrl+Alt+Shift+F8 - Toggles "Show single key" option.

Modifers are displayed when activated, except for Shift. I will have it fixed.

I will implement a settings window too.

Today's progresses are minimal. Lots to fiddle with....

Here's the script: http://p.ahkscript.org/?p=a08fb51c

; CHANGELOG:
; by Marius Sucan (robodesign.ro)
; v2.92 (2017-10-11)
; - rolled back the changes for modifier keys; it resulted in many regressions noticeable for those using StickyKeys; I will insist on this :)
; - clipboard monitoring; it displays briefly what text user copied to clipboard
; - fixed bugs related to showing previous keys

Code: Select all

; KeypressOSD.ahk
; Charset for this file must be UTF 8 with BOM.
; it will not function properly otherwise.
;--------------------------------------------------------------------------------------------------------------------------
;
; AVAILABLE SHORTCUTS:
 ; Ctrl+Alt+Shift+F8  - Toggles "Show single key" option. Useful when you must reliably use dead keys.
 ; Ctrl+Alt+Shift+F9  - Toggles between two OSD offsets: GuiYa and GuiYb.
 ; Ctrl+Alt+Shift+F10 - Toggles personal/regional keys support.
 ; Ctrl+Alt+Shift+F11 - Detect keyboard language.
 ; Ctrl+Alt+Shift+F12 - Reinitialize OSD. Useful when it no longer appears on top. To have it appear on top of elevated apps, run it in administrator mode.
;
; NOTES:
 ; This script was made for, and on Windows 10.
 ; The keyboard layouts have changed since Win XP or Win 98.
 ; Windows 10 also no longer switches keyboard layouts based
 ; on the currently active app. As such, automatic keyboard
 ; layout detection may not work for you.
 ; 
 ; I do not intend to offer support for older Windows versions.
 ; 
 ; This script has support only for Latin-based keyboards.
 ; Thus, it has no support for Chinese, Japanese, chirilic, 
 ; It is too complex for me to implement support for other alphabets or writing systems.
 ; If other programmers willing to invest the time in this script,
 ; are welcomed to do so, and even to transform it into anything they wish. 
 ;
 ; I offer numerous options/settings in the script such that
 ; everyone can find a way to adapt it to personal needs.
 ; - you can edit, in the code, what are the dead keys
 ;   - see Loop, 95, char2skip from CreateHotkey() function
 ; - you can also define personal regional keys, while autodetect is disabled
 ; - disable dead keys if you do not have such keys
 ; 
 ; Read the messages you get:
 ; - it indicates when your keyboard layout is unsupported or Unrecognized
 ; - it also indicates if it made a partial match;
 ;   - in such cases, you will likely not have all the keys
 ;   - or simply AHK will give errors trying to bind to inexistent keys
 ; - if the external file is missing, languages.ini, it will always report
 ; that it did not detect your keyboard.
 ;
 ; Default/built in language support is for English International.
 ;
 ; For the layouts I added support, I avoided binding to dead keys
 ; such that you no longer have to add them manually, as indicated previously.
 ; 
 ; If you rely only on the "vanilla" version, you will likely 
 ; not be able to use it.
 ;
 ; I am no programmer and the script is still quite quirky, but I am trying to
 ; make it better and better with each version.
;
; FEATURES:
 ; - Show previously pressed key if fired quickly.
 ; - Count key presses or key fires and mouse clicks.
 ; - Automatic resizing of OSD/HUD or fixed size.
 ; - Hides automatically when mouse runs over it.
 ; - Generate beeps for key presses, modifiers, mouse clicks or just when typing with Capslock.
 ; - Clipboard monitor. It displays briefly texts copied to clipboard.
 ; - Indicators for CapsLock, NumLock and ScrollLock states.
 ; - Typing mode; shows what you are typing in an expanding text area.
 ; - Partial dead keys support, option to turn it off; The work-around is only for the English International keyboard layout.
 ; - Partial support for many non-English keyboards. 40 keyboard layouts defined.
 ;   - limited automatic detection of keyboard layouts.
 ;   - the user also has the option to define his regional keys.
 ; - Easy to configure with many options:
 ;   - to toggle features: key beepers, key counting or previous key;
 ;   - to hide modifiers, mouse clicks or single key presses (which disables typing mode);
 ;   - or hide keys that usually get in the way: Left Click and Print Screen [HideAnnoyingKeys];
 ;   - differ between left and right modifiers;
 ;   - OSD/HUD position, size and display time;
 ;   - beep key presses even if keys are not displayed;
 ;   - some settings toggled by shortcuts are stored permanently in an INI file;
;
; CHANGELOG:
 ; by Marius Sucan (robodesign.ro)
 ;   v2.92 (2017-10-11)
 ;   - rolled back with the changes for modifier keys; it resulted in many regressions; will insist on this :)
 ;   - clipboard monitoring; it displays what text was copied
 ;   - fixed bugs related to showing previous keys
 ;   v2.90 (2017-10-10)
 ;   - from now, numpad keys always initiate typing mode
 ;   - modifiers (Ctrl, Shift, Alt, WinKey) and Tab, Insert no longer clear typed input in the OSD, if pressed once, without a combo
 ;   - unified the behaviour of modifiers
 ;   - fixed some counting bugs :)
 ;   - added option to disable PermanentSettings
 ;   v2.87 (2017-10-09)
 ;   - improvements for assigning keys;
 ;     - errors are now managed, it will beep when key bindings fail
 ;   - [new] option to disable audio alerts :)
 ;   - languages are now in an external file
 ;   - new keyboard layouts for Polish, Azerbaijian, Turkmen
 ;   v2.85 (2017-10-08)
 ;   - minor improvements for the dead keys work-around
 ;   - added support for 40 foreign keyboard layouts
 ;   - automatic detection of keyboard layouts, at start or continously
 ;   - user also can define personal/regional keys in the settings section
 ;   - new keyboard shortcut, to toggle regional keys
 ;   - new setting: to enable or disable system-wide keyboard shortcuts
 ;   - settings toggled by shortcuts are stored permanently in an INI file
 ;     - except for the option of regional keys support
 ;   v2.73 (2017-10-05)
 ;   - improvements for automatic resize calculation
 ;   v2.72 (2017-10-04)
 ;   - new option: make beeps on mouse clicks
 ;   - new option: distinct beeper for modifier keys: Shift, Ctrl, Alt, WinKey.
 ;   - new option: beep hidden keys; if you want it to beep even if the keys are not displayed
 ;   - fix: now it always counts mouse clicks
 ;   v2.70 (2017-10-03)
 ;   - added option to toggle dead keys support / work-around.
 ;   - added option to hide annoying keys that usually get in the way: Left Click and Print Screen.
 ;   - redraw improvements, reduced flickering
 ;   - disabled OSD transparency to reduce flickering
 ;   v2.69 (2017-09-30)
 ;   - OSD / GUI hides when mouse is over it. Many thanks to phaleth!
 ;   v2.68 (2017-09-29)
 ;   - numpad keys now work in typing mode as expected; they appear as symbols or numbers
 ;
 ; by phaleth from irc.freenode.net #ahk
 ;   v2.67 (2017-09-28)
 ;   - dead keys improvements
 ;
 ; by Marius Sucan (robodesign.ro)
 ;   v2.66 (2017-09-28)
 ;   - key combinations with Shift work better.
 ;   v2.65 (2017-09-27)
 ;   - Fixed a bug with counting modifier keys;
 ;   - improved the dead keys work-around
 ;   - friendly names for mouse clicks
 ;   - when pressed, volume keys  always generate beeps
 ;   - added option to differentiate between left and right modifiers
 ;   - now it detects AltGr key
 ;   - the key beeper now also makes a beep for modifiers.
 ;   - capslock no longer erases text you are typing displayed by the OSD
 ;   - now you can toggle between two different OSD positions with Ctrl + Alt + Shift + F9
 ;   v2.60 (2017-09-26)
 ;   - Fixed many bugs with counting keys;
 ;   - reimplemented the feature to see the previous key combination, if quickly a new one is pressed;
 ;   - added options/settings to toggle previous keys, counting keys and delay;
 ;   - added shortcuts to toggle ShowSingleKey option and to reinitialize the OSD;
 ;   - added option for automatic resizing of the OSD; it can be turned off in the settings section; it is a fishy implementation, but if one adjusts it, can make it to suit personal needs;
 ;   - new option: beep when key is released or when writing with capslock
 ;   v2.58 (2017-09-23)
 ;   - Numpad keys have friendly naming, based on the numlock state.
 ;   - Combinations with space and backspace work again.
 ;   v2.56 (2017-09-22)
 ;   - more fixes for space usage and key combinations;
 ;   - now it indicates when ScrollLock, NumLock and Capslock are activated.
 ;   v2.55 (2017-09-21)
 ;   - minor fixes for space usage and key combinations.
 ;
 ; by Saiapatsu from irc.freenode.net #ahk
 ;   v2.54 (2017-09-21)
 ;   - Scrolls through n recently typed characters instead of just the latest word
 ;   v2.53 (2017-09-21)
 ;   - Case change effect limited to the loop 95 letters only.
 ;   v2.52 (2017-09-21)
 ;   - Now supports backspace. Commented out CapsLock beeper.
 ;   v2.51 (2017-09-21)
 ;   - Changed labels to functions, added ToolWindow style to window, changed DisplayTime
 ;   calculation, made it show last word typed, hid spacebar presses
 ;   todo: make Shift look less ugly
 ;
 ; by Marius Sucan (robodesign.ro)
 ;   v2.50 (2017-09-20)
 ;   - Changed the OSD positioning and sizing. It was based on the current window. Now it is always fixed in a specific place. Added a Capslock beeper.
 ;
 ; by tmplinshi from https://autohotkey.com/boards/viewtopic.php?f=6&t=225
 ;   v2.22 (2017-02-25)
 ;   - Now pressing same combination keys continuously more than 2 times,
 ;   for example press Ctrl+V 3 times, will displayed as "Ctrl + v (3)"
 ;   v2.21 (2017-02-24)
 ;   - Fixed LWin/RWin not poping up start menu
 ;   v2.20 (2017-02-24)
 ;   - Added displaying continuous-pressed combination keys.
 ;   e.g.: With CTRL key held down, pressing K and U continuously will shown as "Ctrl + k, u"
 ;   v2.10 (2017-01-22)
 ;   - Added ShowStickyModKeyCount option
 ;   v2.09 (2017-01-22)
 ;   - Added ShowModifierKeyCount option
 ;   v2.08 (2017-01-19)
 ;   - Fixed a bug
 ;   v2.07 (2017-01-19)
 ;   - Added ShowSingleModifierKey option (default is True)
 ;   v2.06 (2016-11-23)
 ;   - Added more keys. Thanks to SashaChernykh.
 ;   v2.05 (2016-10-01)
 ;   - Fixed not detecting "Ctrl + ScrollLock/NumLock/Pause". Thanks to lexikos.
 ;   v2.04 (2016-10-01)
 ;   - Added NumpadDot and AppsKey
 ;   v2.03 (2016-09-17)
 ;   - Added displaying "Double-Click" of the left mouse button.
 ;   v2.02 (2016-09-16)
 ;   - Added displaying mouse button, and 3 settings (ShowMouseButton, FontSize, GuiHeight)
 ;   v2.01 (2016-09-11)
 ;   - Display non english keyboard layout characters when combine with modifer keys.
 ;   v2.00 (2016-09-01)
 ;   - Removed the "Fade out" effect because of its buggy.
 ;   - Added support for non english keyboard layout.
 ;   - Added GuiPosition setting.
 ;   v1.00 (2013-10-11)
 ;   - First release by tmplinshi based on RaptorX. Function keys, numpad keys and mouse clicks support. Popups at mouse position.
 ;   v0.50 (2010-03-18)
 ;   - Released by RaptorX.
;--------------------------------------------------------------------------------------------------------------------------
;
; TO-DO:
 ; features to implement:
 ; - after pressing Shift + [letter] if the following key is a single letter, remember the first Shift + [letter];
 ; - modifiers should not erase typed text from OSD
 ; - when user types single letters, display Shift + (sign or numeric keys) as signs/symbols, eg. Shift + 0, should be displayed as );
 ; - visual mouse clicks
 ; - tray menu or window with options/settings
 ; - show a generic symbol for ignored dead keys; helps to clarify one was pressed;
 ;
 ; glitches to fix [by priority]:
 ; - [regression since v2.52] show Shift as a modifier, Shift should behave just like the other modifiers; now, it is never displayed alone and countable;
 ; - [bug, since v1.00] Shift + [numpad] does not work; no idea why
 ; - [bug] once a key is pressed, counting of key fires is not initiated after the OSD was hidden for a little awhile;
 ; - make dead keys work better, even detect them ;)
 ; - automatic resizing of the OSD/GUI is just a silly hack based on the default font size and the number of typed chars; it often fails to resize properly;
 ; - redraw issues; it still flickers;
 ; - make it work reliably with sticky keys; if user presses once Ctrl and another key afterwards, it rarely detects the combination on Winndows 7;
;----------------------------------------------------------------------------

; Initialization
#SingleInstance force
#NoEnv
#MaxHotkeysPerInterval 500
SetBatchLines, -1
ListLines, Off

; Settings:
    global DeadKeys              := 0     ; a toggle for a partial dead keys support. Zero [0] means no dead keys. See CreateHotkey() and char2skip to define the dead keys.
    global CustomRegionalKeys    := 0     ; if you want to add support to a regional keyboard
    global RegionalKeysList      := "a|b|c"  ; add the characters in this list, separated by |
    global AutoDetectKBD         := 1     ; at start, detect keyboard layout
    global ConstantAutoDetect    := 0     ; continously check if the keyboard layout changed; if AutoDetectKBD=0, this is ignored
    global SilentDetection       := 0     ; do not display information about language switching
    global audioAlerts           := 1     ; generate beeps when key bindings fail

    global ShowSingleKey         := 1     ; show only key combinations
    global ShowMouseButton       := 1     ; if disabled, mouse click beeps will never occur
    global HideAnnoyingKeys      := 1     ; Left click and PrintScreen can easily get in the way.
    global ShowSingleModifierKey := 1     ; make it display Ctrl, Alt, Shift when pressed alone
    global DifferModifiers       := 0     ; differentiate between left and right modifiers
    global ShowPrevKey           := 1     ; show previously pressed key, if pressed quickly in succession
    global ShowPrevKeyDelay      := 300
    global ShowKeyCount          := 1     ; count how many times a key is pressed
    global ShowKeyCountFired     := 1     ; show only key presses (0) or catch key fires as well (1)

;   global TransparencyLevel     := 220   ; OSD transparency; range from 0 to 255. feature disabled, to reduce flickering
    global DisplayTime           := 3000  ; in milliseconds
    global GuiWidth              := 360
    global GuiHeight             := 50
    global GuiX                  := 50
    global GuiYa                 := 250   ; toggle between GuiYa and GuiYb with Ctrl + Alt + Shift + F9
    global GuiYb                 := 800
    global FontSize              := 19
    global OSDautosize           := 1     ; make adjustments in ShowHotkey() to match your font size
    global NumLetters            := 25    ; amount of recently typed letters to display
    global NumLettersAutosize    := 55    ; ...when OSD resizes automatically

    global CapslockBeeper        := 1     ; only when the key is released
    global KeyBeeper             := 0     ; only when the key is released
    global ModBeeper             := 0     ; beeps for every modifier, when released
    global MouseBeeper           := 0
    global BeepHiddenKeys        := 0     ; [when any beeper enabled] to beep or not when keys are not displayed by OSD/HUD

    global KeyboardShortcuts     := 1     ; system-wide shortcuts
    global PermanentSettings     := 1     ; settings stored in keypress-osd.ini override the settings from here
    global ClipMonitor           := 1     ; show clipboard changes


; Initialization variables. Altering these may lead to undesired results.
    global typed := ""
    global visible := 0
    global GuiY := GuiYb
    global prefixed := 0 ; hack used to determine if last keypress had a modifier
    global zcSCROL := "SCROLL LOCK"
    global tickcount_start := 0   ; timer to count repeated key presses
    global keyCount := 0
    global ShowKeyCountDelay := (ShowKeyCountFired = 0) ? 700 : 6000
    NumLetters := (OSDautosize=1) ? NumLettersAutosize : NumLetters
    global text_width := 60
    global InputLocaleID := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")

    if PermanentSettings=1
    {
        IniRead, ShowSingleKey, keypress-osd.ini, PermanentSettings, ShowSingleKey
        IniRead, GuiY, keypress-osd.ini, PermanentSettings, GuiY
        GuiY := (GuiY=GuiYb || GuiY=GuiYa) ? GuiY : GuiYb
    }

    IniRead, CustomMultiLangToggled, keypress-osd.ini, PermanentSettings, CustomMultiLangToggled
    if (CustomMultiLangToggled=1) {
        IniRead, CustomRegionalKeys, keypress-osd.ini, PermanentSettings, CustomRegionalKeys
        CustomMultiLangToggled := 0
        IniWrite, %CustomMultiLangToggled%, keypress-osd.ini, PermanentSettings, CustomMultiLangToggled
    }

    IniRead, AutoDetectKBDToggled, keypress-osd.ini, PermanentSettings, AutoDetectKBDToggled
    if (AutoDetectKBDToggled=1) {
        IniRead, AutoDetectKBD, keypress-osd.ini, PermanentSettings, AutoDetectKBD
        IniRead, ConstantAutoDetect, keypress-osd.ini, PermanentSettings, ConstantAutoDetect
        AutoDetectKBDToggled := 0
        IniWrite, %AutoDetectKBDToggled%, keypress-osd.ini, PermanentSettings, AutoDetectKBDToggled
    }

    CreateGUI()
    CreateGlobalShortcuts()
    CreateHotkey()
    if ClipMonitor=1
        OnClipboardChange("ClipChanged")
    return

; The script

GetSpecialKeysStates() {
    GetKeyState, ScrollState, ScrollLock, T   
    If ScrollState = D
    {
       global zcSCROL := "SCROLL LOCK ON"
    }
    else {
       global zcSCROL := "Scroll lock off"
    }
}

TypedLetter(key) {
    StringLeft, key, key, 1
    Stringlower, key, key

    GetKeyState, CapsState, CapsLock, T
    If CapsState != D
    {
         if GetKeyState("Shift")
        {
          StringUpper, key, key
        }
    } else
        StringUpper, key, key

    return typed := SubStr(typed key, -NumLetters)
}

OnMousePressed() {
    global tickcount_start := A_TickCount-500

    try {
        key := GetKeyStr()
        typed := "" ; concerning TypedLetter(" ") - it resets the content of the OSD
        ShowHotkey(key)
        SetTimer, HideGUI, % -DisplayTime
    }

    If (MouseBeeper = 1) && (ShowMouseButton = 1) && (ShowSingleKey = 1) || (MouseBeeper = 1) && (ShowSingleKey = 0) && (BeepHiddenKeys = 1)
       soundbeep, 2500, 65

}

OnKeyPressed() {
    try {
        key := GetKeyStr()
        if (!(key ~= "i)^(Insert|Tab)$")) {
           typed := ""
        }
        ShowHotkey(key)
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnLetterPressed() {
    try {
        if typed && DeadKeys=1
            sleep, 25    ; this delay helps with dead keys, but it generates errors; the following actions: stringleft,1 and stringlower help correct these

        key := GetKeyStr(1)     ; consider it a letter

        if prefixed
        {
            typed := ""
            ShowHotkey(key)
        } else
        {
            TypedLetter(key)
            ShowHotkey(typed)
        }
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnSpacePressed() {
    try {
        if typed {
            TypedLetter("_")
            ShowHotkey(typed)
        } else if (!typed)
        {
          key := GetKeyStr()
          ShowHotkey(key)
        }
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnBspPressed() {
    try
    {
        if typed
        {
            typed := SubStr(typed, 1, StrLen(typed) - 1)
            ShowHotkey(typed)
        } else if (!typed)
        {
            key := GetKeyStr()
            ShowHotkey(key)
        }
        SetTimer, HideGUI, % -DisplayTime
    }

    if (BeepHiddenKeys = 1) && (KeyBeeper = 1) && (ShowSingleKey = 0)
       soundbeep, 1900, 45

    if (KeyBeeper = 1) && (ShowSingleKey = 1)
       soundbeep, 1900, 45
}

OnCapsPressed() {
    try
    {
        if typed
        {
            ShowHotkey(typed)
        } else if (!typed)
        {
            key := GetKeyStr()
            GetKeyState, CapsState, CapsLock, T
            if CapsState = D
            {
                key := prefixed ? key : "CAPSLOCK ON"
            } else
                key := prefixed ? key : "CapsLock off"
            ShowHotkey(key)
        }
        SetTimer, HideGUI, % -DisplayTime
    }

    If (CapslockBeeper = 1) && (ShowSingleKey = 1) || (BeepHiddenKeys = 1)
       {
        soundbeep, 450, 200
       }
}

OnNumpadPressed()
{
    GetKeyState, NumState, NumLock, T
    try {
        key := GetKeyStr()
        if NumState != D
        {
            typed := "" ; reset typed content
            ShowHotkey(key)
        } else if prefixed
        {
            typed := ""
            ShowHotkey(key)
        } else if NumState = D
        {
            key2 := GetKeyStr(1)
            if (StrLen(key2)=5)
            {
              StringLeft, key2, key2, 3
              StringRight, key2, key2, 1
              TypedLetter(key2)
              ShowHotkey(typed)
            }
        }
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnKeyUp() {
    global tickcount_start := A_TickCount

    if typed && (CapslockBeeper = 1) && (ShowSingleKey = 1)
    {
        GetKeyState, CapsState, CapsLock, T
        If CapsState = D
           {
             soundbeep, 450, 25
           }
           else if (KeyBeeper = 1) && (ShowSingleKey = 1)
           {
             soundbeep, 1900, 45
           }
    }

    If (CapslockBeeper = 0) && (KeyBeeper = 1) && (ShowSingleKey = 1)
       {
         soundbeep, 1900, 45
       }
       else if (CapslockBeeper = 1) && (KeyBeeper = 0)
       {
       }
       else if !typed && (CapslockBeeper = 1) && (ShowSingleKey = 1)
       {
         soundbeep, 1900, 45
       }

    if (BeepHiddenKeys = 1) && (KeyBeeper = 1) && (ShowSingleKey = 0)
         soundbeep, 1900, 45  
}

; OnModPressed() {
;     try {
;         key := GetKeyStr()
;         if (!(key ~= "i)^(Ctrl|LCtrl|RCtrl|Shift|LShift|RShift|Alt|Altgr|LAlt|RAlt|LWin|RWin|Winkey)$")) {
;            typed := ""
;         }
;         ShowHotkey(key)
;         SetTimer, HideGUI, % -DisplayTime
;     }
; }


OnModUp() {
    global tickcount_start := A_TickCount

    If (ModBeeper = 1) && (ShowSingleKey = 1) && (ShowSingleModifierKey = 1) || (ModBeeper = 1) && (BeepHiddenKeys = 1)
       soundbeep, 1000, 65

}

; ===================================================================================

CreateGUI() {
    global

    Gui, +AlwaysOnTop -Caption +Owner +LastFound +ToolWindow +E0x20
    Gui, Margin, 10, 10
    Gui, Color, 111111
    Gui, Font, cWhite s%FontSize% bold, Arial, -wrap
    Gui, Add, Text, vHotkeyText left x10 y10 -wrap

;    if TransparencyLevel between 10 and 245
;       WinSet, Transparent, %TransparencyLevel%

}

CreateHotkey() {

    if (CustomRegionalKeys=1) && (AutoDetectKBD=0)
    {
        Loop, parse, RegionalKeysList, |
        {
           Hotkey, % "~*" A_LoopField, OnLetterPressed, useErrorLevel
           Hotkey, % "~*" A_LoopField " Up", OnKeyUp, useErrorLevel
           if (errorlevel!=0) && (audioAlerts=1)
              soundbeep, 1900, 50
        }
    }
   if (AutoDetectKBD=1)
   {
       IdentifyKBDlayout()
   }

    Loop, 95
    {
        k := Chr(A_Index + 31)

        if (DeadKeys=1)
        {
            for each, char2skip in StrSplit("``,^,6,',"",~", ",")        ; dead keys to ignore
            {
                if (k = char2skip && DeadKeys=1)
                {
                    continue, 2
                }
            }
        }

        if (k = " ")
        {
            Hotkey, % "~*Space", OnSpacePressed, useErrorLevel
            Hotkey, % "~*Space Up", OnKeyUp, useErrorLevel
        }
        else
        {
            Hotkey, % "~*" k, OnLetterPressed, useErrorLevel
            Hotkey, % "~*" k " Up", OnKeyUp, useErrorLevel
            if (errorlevel!=0) && (audioAlerts=1)
               soundbeep, 1900, 50
        }
    }

    Hotkey, % "~*Backspace", OnBspPressed, useErrorLevel
    Hotkey, % "~*CapsLock", OnCapsPressed, useErrorLevel
    Hotkey, % "~*CapsLock Up", OnKeyUp, useErrorLevel

    Loop, 24 ; F1-F24
    {
        Hotkey, % "~*F" A_Index, OnKeyPressed, useErrorLevel
        Hotkey, % "~*F" A_Index " Up", OnKeyUp, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    NumpadKeysList := "NumpadDot|NumpadDiv|NumpadMult|NumpadAdd|NumpadSub|sc04E|sc04A|sc052|sc04F|sc050|sc051|sc04B|sc04C|sc04D|sc047|sc048|sc049|sc053|sc037|sc135"

    Loop, parse, NumpadKeysList, |
    {
       Hotkey, % "~*" A_LoopField, OnNumpadPressed, useErrorLevel
       Hotkey, % "~*" A_LoopField " Up", OnKeyUp, useErrorLevel
       if (errorlevel!=0) && (audioAlerts=1)
          soundbeep, 1900, 50
    }

    Otherkeys := "WheelDown|WheelUp|WheelLeft|WheelRight|XButton1|XButton2|Browser_Forward|Browser_Back|Browser_Refresh|Browser_Stop|Browser_Search|Browser_Favorites|Browser_Home|Volume_Mute|Volume_Down|Volume_Up|Media_Next|Media_Prev|Media_Stop|Media_Play_Pause|Launch_Mail|Launch_Media|Launch_App1|Launch_App2|Help|Sleep|PrintScreen|CtrlBreak|Break|AppsKey|Tab|Enter|Esc"
               . "|Insert|Home|End|Up|Down|Left|Right|ScrollLock|NumLock|Pause|sc145|sc146|sc046|sc123|sc11C|sc149|sc151|sc122|sc153"
    Loop, parse, Otherkeys, |
    {
        Hotkey, % "~*" A_LoopField, OnKeyPressed, useErrorLevel
        Hotkey, % "~*" A_LoopField " Up", OnKeyUp, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    If ShowMouseButton=1
    {
        Loop, Parse, % "LButton|MButton|RButton", |
        Hotkey, % "~*" A_LoopField, OnMousePressed, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    for i, mod in ["LCtrl", "RCtrl", "LAlt", "RAlt", "LWin", "RWin"]
    {
        Hotkey, % "~*" mod, OnKeyPressed, useErrorLevel
        Hotkey, % "~*" mod " Up", OnModUp, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    If typed {
    for i, mod in ["LShift", "RShift"]
        Hotkey, % "~*" mod, OnKeyPressed, useErrorLevel
        Hotkey, % "~*" mod " Up", OnModUp, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

;    If (ShowSingleModifierKey=1)
;    {
;      for i, mod in ["LShift", "RShift", "LCtrl", "RCtrl", "LAlt", "RAlt", "LWin", "RWin"]
;        Hotkey, % "~*" mod, OnModPressed, useErrorLevel
;        Hotkey, % "~*" mod " Up", OnModUp, useErrorLevel
;        if (errorlevel!=0) && (audioAlerts=1)
;           soundbeep, 1900, 50
;    }
}

ShowHotkey(HotkeyStr) {

    if (OSDautosize=1)
    {
        HotkeyTextTrimmed := RegExReplace(HotkeyStr, "[^a-zA-Z]", "")
        StringLeft, HotkeyTextTrimmed, HotkeyTextTrimmed, 5
        growthFactor := 1.35
        if HotkeyTextTrimmed is upper
           growthFactor := 1.0
        text_width := (StrLen(HotkeyStr)/growthFactor)*FontSize
        text_width := (text_width<70) ? 70 : text_width+15
    } else if OSDautosize=0
    {
        text_width := GuiWidth
    }

    GuiControl,     , HotkeyText, %HotkeyStr%
    GuiControl, Move, HotkeyText, w%text_width% left
    Gui, Show, NoActivate x%GuiX% y%GuiY% AutoSize, KeypressOSD
    visible := 1
    SetTimer, checkMousePresence, on, 400
}

GetKeyStr(letter := 0) {
    static modifiers := ["LCtrl", "RCtrl", "LAlt", "RAlt", "LShift", "RShift", "LWin", "RWin"]
    static repeatCount := 1

    ; If any mod but shift, go ; If shift, check if not letter

    for i, mod in modifiers
    {
        if (mod = "LShift" && typed || mod = "RShift" && typed ? (!letter && GetKeyState(mod)) : GetKeyState(mod))
;        if GetKeyState(mod)
            prefix .= mod " + "
    }

    if (!prefix && !ShowSingleKey)
        throw

    key := SubStr(A_ThisHotkey, 3)

    if (key ~= "i)^(LCtrl|RCtrl|LShift|RShift|LAlt|RAlt|LWin|RWin)$") {
        if (ShowSingleModifierKey = 0) || (ShowSingleKey = 0) || (A_TickCount-tickcount_start > 2000) && visible
        {
            throw
        } else
        {
             key := ""
             prefix := RTrim(prefix, "+ ")
        }

        prefix := CompactModifiers(prefix)

        if (ShowKeyCount=1) {
            if !InStr(prefix, "+") && IsDoubleClickEx() {
                if (A_ThisHotKey != A_PriorHotKey) || (ShowKeyCount=1) && (A_TickCount-tickcount_start < 700) {
                    if (++repeatCount > 1) {
                        prefix .= " (" repeatCount ")"
                    }
                } else {
                    repeatCount := 0
                }
            } else {
                repeatCount := 1
            }
        }
    } else {
        if StrLen(key)=1 || InStr(key, " up") && StrLen(key)=4 && typed
        {
            StringLeft, key, key, 1
            key := GetKeyChar(key, "A")
        } else if ( SubStr(key, 1, 2) = "sc" ) {
            key := SpecialSC(key)
        } else if (key = "Volume_Up") || (key = "Volume_Down") {
            soundbeep, 150, 40
        } else if (key = "PrintScreen") {
            if HideAnnoyingKeys=1
                throw
            key := "Print Screen"
        } else if (key = "MButton") {
            key := "Middle Click"
        } else if (key = "RButton") {
            key := "Right Click"
        } else if (key = "LButton") && IsDoubleClick() {
            key := "Double-Click"
        } else if (key = "LButton") {
            if HideAnnoyingKeys=1
                throw
            key := "Left Click"
        }
        {
            _key := (key = "Double-Click") ? "Left Click" : key
        }

        prefix := CompactModifiers(prefix)

        static pre_prefix, pre_key, keyCount := 1
        StringUpper, key, key, T
        StringUpper, pre_key, pre_key, T
        keyCount := (key=pre_key) && (prefix = pre_prefix) && (repeatCount<1.5) ? keyCount : 1
        global ShowKeyCountDelay := (ShowKeyCountFired = 0) ? 700 : 6000
        ShowKeyCountDelay := (ShowKeyCountFired=1) ? (ShowKeyCountDelay+keyCount*100) : ShowKeyCountDelay

        if (InStr(prefix, "+")) && (A_TickCount-tickcount_start < ShowKeyCountDelay) || (!letter) && (A_TickCount-tickcount_start < ShowKeyCountDelay)
        {
            if (ShowPrevKey=1) && (A_TickCount-tickcount_start < ShowPrevKeyDelay)
            {
                ShowPrevKeyValid := 1
                if InStr(pre_key, " up")  && StrLen(pre_key)=4
                {
                    StringLeft, pre_key, pre_key, 1
                }
            } else
            {
                ShowPrevKeyValid := 0
            }
            if (prefix != pre_prefix) {
                result := (ShowPrevKeyValid=1) ? prefix key " {" pre_prefix pre_key "}" : prefix key
            } else if (ShowPrevKeyValid=1) && (key != pre_key) || (ShowKeyCount=1) && (ShowPrevKeyValid=1)
            {
                keyCount := (key=pre_key) && (ShowKeyCount=1) ? (keyCount+1) : 1
                key := (keyCount>1) && (ShowKeyCount=1) ? (key " (" keyCount ")") : (key ", " pre_key)
            } else if (ShowPrevKeyValid=0)
            {
                keyCount := (key=pre_key) && (ShowKeyCount=1) ? (keyCount+1) : 1
                key := (keyCount>1) ? (key " (" keyCount ")") : (key)
            }
        } else {
            keyCount := 1
        }

        pre_prefix := prefix
        pre_key := _key

        repeatCount := 1
    }

    prefixed := prefix ? 1 : 0
    return result ? result : prefix . key
}

CompactModifiers(stringy)
{
    if DifferModifiers = 1
    {
        StringReplace, stringy, stringy, LCtrl + RAlt, AltGr, All
        StringReplace, stringy, stringy, LCtrl + RCtrl + RAlt, RCtrl + AltGr, All
        StringReplace, stringy, stringy, RAlt, AltGr, All
        StringReplace, stringy, stringy, LAlt, Alt, All
    } else if (DifferModifiers = 0)
    {
        StringReplace, stringy, stringy, LCtrl + RAlt, AltGr, All
        ; StringReplace, stringy, stringy, LCtrl + RCtrl + RAlt, RCtrl + AltGr, All
        StringReplace, stringy, stringy, LCtrl, Ctrl, All
        StringReplace, stringy, stringy, RCtrl, Ctrl, All
        StringReplace, stringy, stringy, LShift, Shift, All
        StringReplace, stringy, stringy, RShift, Shift, All
        StringReplace, stringy, stringy, LAlt, Alt, All
        StringReplace, stringy, stringy, LWin, WinKey, All
        StringReplace, stringy, stringy, RWin, WinKey, All
        StringReplace, stringy, stringy, Ctrl + Ctrl, Ctrl, All
        StringReplace, stringy, stringy, Shift + Shift, Shift, All
        StringReplace, stringy, stringy, WinKey + WinKey, WinKey, All
        StringReplace, stringy, stringy, RAlt, AltGr, All
    }
    return stringy
}

SpecialSC(sc) {
    GetSpecialKeysStates()

    GetKeyState, NumState, NumLock, T
    If NumState = D
    {
       k := {sc046: zcSCROL, sc145: "NUM LOCK ON", sc146: "Pause", sc123: "Genius LuxeMate Scroll", sc04E: "[ + ]", sc04A: "[ - ]", sc052: "[ 0 ]", sc04F: "[ 1 ]", sc050: "[ 2 ]", sc051: "[ 3 ]", sc04B: "[ 4 ]", sc04C: "[ 5 ]", sc04D: "[ 6 ]", sc047: "[ 7 ]", sc048: "[ 8 ]", sc049: "[ 9 ]", sc053: "[ . ]", sc037: "[ * ]", sc135: "[ / ]", sc11C: "[Enter]", sc149: "Page Up", sc151: "Page Down", sc153: "Delete", sc122: "Media_Play/Pause"}
    }
    else {
       k := {sc046: zcSCROL, sc145: "Num lock off", sc146: "Pause", sc123: "Genius LuxeMate Scroll", sc04E: "[ + ]", sc04A: "[ - ]", sc052: "[Insert]", sc04F: "[End]", sc050: "[Down]", sc051: "[Page Down]", sc04B: "[Left]", sc04C: "[Undefined]", sc04D: "[Right]", sc047: "[Home]", sc048: "[Up]", sc049: "[Page Up]", sc053: "[Delete]", sc037: "[ * ]", sc135: "[ / ]", sc11C: "[Enter]", sc149: "Page Up", sc151: "Page Down", sc153: "Delete", sc122: "Media_Play/Pause"}
    }
    return k[sc]
}

; <tmplinshi>: thanks to Lexikos: https://autohotkey.com/board/topic/110808-getkeyname-for-other-languages/#entry682236
; This enables partial support for non-English keyboard layouts.
; If the script initializes with the English keyboard layout, but then used with another one, this function gets proper key names,

GetKeyChar(Key, WinTitle:=0)
{
    thread := WinTitle=0 ? 0
        : DllCall("GetWindowThreadProcessId", "ptr", WinExist(WinTitle), "ptr", 0)
    hkl := DllCall("GetKeyboardLayout", "uint", thread, "ptr")
    vk := GetKeyVK(Key), sc := GetKeySC(Key)
    VarSetCapacity(state, 256, 0)
    VarSetCapacity(char, 4, 0)
    n := DllCall("ToUnicodeEx", "uint", vk, "uint", sc
        , "ptr", &state, "ptr", &char, "int", 2, "uint", 0, "ptr", hkl)
    return StrGet(&char, n, "utf-16")
}
global LangsBinded := 0

IdentifyKBDlayout() {
  VarSetCapacity(kbLayoutRaw, 32, 0)
  DllCall("GetKeyboardLayoutName", "Str", kbLayoutRaw)
  StringRight, kbLayout, kbLayoutRaw, 4

  #Include *i keypress-osd-languages.ini

  check_kbd := StrLen(LangName_%kbLayout%)>2 ? 1 : 0
  check_kbd_exact := StrLen(LangRaw_%kbLayoutRaw%)>2 ? 1 : 0
  if (check_kbd_exact=0)
  {
      partialKBDmatch = (Partial match)
  }

  if (check_kbd=0)
  {
      ShowHotkey("Unrecognized layout: (kbd " kbLayoutRaw ").")
      SetTimer, HideGUI, % -DisplayTime
      soundbeep, 500, 900
  }

  StringLeft, kbLayoutSupport, LangName_%kbLayout%, 1
  if (kbLayoutSupport="-") && (check_kbd=1)
  {
      ShowHotkey("Unsupported layout: " LangName_%kbLayout% " (kbd" kbLayout ").")
      SetTimer, HideGUI, % -DisplayTime
      soundbeep, 500, 900
  }

  IfInString, LangsBinded, %kbLayout%
  {
      KBDbinded := 1
  } else
  {
      LangsBinded := kbLayout "|" LangsBinded 
      StringLeft, LangsBinded, LangsBinded, 20
      KBDbinded := 0
  }

  if (kbLayoutSupport!="-") && (check_kbd=1) && (KBDbinded=0)
  {
      Loop, parse, LangChars_%kbLayout%, |
      {
         Hotkey, % "~*" A_LoopField, OnLetterPressed, useErrorLevel
         Hotkey, % "~*" A_LoopField " Up", OnKeyUp, useErrorLevel
         if (errorlevel!=0) && (audioAlerts=1)
             soundbeep, 1900, 50
      }
      if SilentDetection=0
      {
          ShowHotkey("Keyboard layout: " LangName_%kbLayout% " (kbd" kbLayout "). " partialKBDmatch)
          SetTimer, HideGUI, % -DisplayTime/4
      }
  }

   if (ConstantAutoDetect=1) && (AutoDetectKBD=1)
      SetTimer, ConstantKBDchecker, 1500
}

ConstantKBDchecker() {
  SetFormat, Integer, H
  WinGet, WinID,, A
  ThreadID := DllCall("GetWindowThreadProcessId", "UInt", WinID, "UInt", 0)
  NewInputLocaleID := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")
    if (InputLocaleID != NewInputLocaleID)
    {
        InputLocaleID := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")
        
        if SilentDetection=0
        ShowHotkey("Changed keyboard layout: " InputLocaleID)
      
        sleep, 350
        Reload
    } 
}

IsDoubleClick(MSec = 300) {
    Return (A_ThisHotKey = A_PriorHotKey) && (A_TimeSincePriorHotkey < MSec)
}

IsDoubleClickEx(MSec = 300) {
    preHotkey := RegExReplace(A_PriorHotkey, "i) Up$")
    Return (A_ThisHotKey = preHotkey) && (A_TimeSincePriorHotkey < MSec)
}

HideGUI() {
    visible := 0
    Gui, Hide
    SetTimer, checkMousePresence, off, 300
}

checkMousePresence() {
    id := mouseIsOver()
    title := getWinTitleFromID(id)
    if title = KeypressOSD
       HideGUI()
}

mouseIsOver() {
    MouseGetPos,,, id
    return id
}

getWinTitleFromID(id) {
    WinGetTitle, title, % "ahk_id " id
    return title
}

CreateGlobalShortcuts() {
   if (KeyboardShortcuts=1) {
      Hotkey, !+^F8, ToggleShowSingleKey
      Hotkey, !+^F9, TogglePosition
      Hotkey, !+^F10, EnableCustomKeys
      Hotkey, !+^F11, DetectLangNow
      Hotkey, !+^F12, ReloadScript
    }
}

ToggleShowSingleKey:
    ShowSingleKey := (!ShowSingleKey) ? true : false
    Gui, Destroy
    sleep, 100
    CreateGUI()
    sleep, 100
    IniWrite, %ShowSingleKey%, keypress-osd.ini, PermanentSettings, ShowSingleKey
    ShowHotkey("Show single keys = " ShowSingleKey)
    SetTimer, HideGUI, % -DisplayTime
return

TogglePosition:
    GuiY := (GuiY = GuiYb) ? GuiYa : GuiYb
    Gui, Destroy
    sleep, 100
    CreateGUI()
    sleep, 100
    IniWrite, %GuiY%, keypress-osd.ini, PermanentSettings, GuiY
    ShowHotkey("OSD position changed")
    SetTimer, HideGUI, % -DisplayTime
return

EnableCustomKeys:
    global CustomRegionalKeys := CustomRegionalKeys = 1 ? 0 : 1
    CustomMultiLangToggled := 1
    IniWrite, %CustomMultiLangToggled%, keypress-osd.ini, PermanentSettings, CustomMultiLangToggled
    IniWrite, %CustomRegionalKeys%, keypress-osd.ini, PermanentSettings, CustomRegionalKeys
    ShowHotkey("Custom Regional keys = " RegionalKeys)
    sleep, 1000
    Reload
return

DetectLangNow:
    global AutoDetectKBD := 1
    global ConstantAutoDetect := 0
    AutoDetectKBDToggled := 1
    IniWrite, %AutoDetectKBDToggled%, keypress-osd.ini, PermanentSettings, AutoDetectKBDToggled
    IniWrite, %AutoDetectKBD%, keypress-osd.ini, PermanentSettings, AutoDetectKBD
    IniWrite, %ConstantAutoDetect%, keypress-osd.ini, PermanentSettings, ConstantAutoDetect
    ShowHotkey("Detecting keyboard layout...")
    sleep, 800
    Reload
return

ReloadScript:
    Gui, Destroy
    sleep, 100
    CreateGUI()
    sleep, 100
    ShowHotkey("OSD reinitializing...")
    sleep, 1500
    Reload
return

ClipChanged(Type)
{
    sleep, 300
    if type=1
    {
       troll := clipboard
       Stringleft, troll, troll, NumLetters*2
       StringReplace, troll, troll, `r`n, %A_SPACE%, All
       StringReplace, troll, troll, %A_SPACE%%A_SPACE%, , All
       ShowHotkey(troll)
       SetTimer, HideGUI, % -DisplayTime
    } else if type=2
    {
       ShowHotkey("Clipboard data changed")
       SetTimer, HideGUI, % -DisplayTime
    }
}
; TO-DO:
; features to implement:
; - after pressing Shift + [letter] if the following key is a single letter, remember the first Shift + [letter]; done
; - modifiers should not erase typed text from OSD done
; - when user types single letters, display Shift + (sign or numeric keys) as signs/symbols, eg. Shift + 0, should be displayed as ); done
; - visual mouse clicks done
; - tray menu or window with options/settings done
; - show a generic symbol for ignored dead keys; helps to clarify one was pressed;done
;
; glitches to fix [by priority]:
; - [regression since v2.52] show Shift as a modifier, Shift should behave just like the other modifiers; now, it is never displayed alone and countable; done
; - [bug, since v1.00] Shift + [numpad] does not work; no idea why
; - [bug] once a key is pressed, counting of key fires is not initiated after the OSD was hidden for a little awhile; fixed
; - make dead keys work better, even detect them ;) done
; - automatic resizing of the OSD/GUI is just a silly hack based on the default font size and the number of typed chars; it often fails to resize properly; done/much improved
; - redraw issues; it still flickers;
; - make it work reliably with sticky keys; if user presses once Ctrl and another key afterwards, it rarely detects the combination on Winndows 7;


Best regards, Marius
Last edited by robodesign on 16 Nov 2017, 05:31, edited 2 times in total.
-------------------------
KeyPress OSD v4: GitHub or forum. (presentation video)
My home page.
ozzii
Posts: 308
Joined: 30 Oct 2013, 06:04

Re: [Script] KeypressOSD - Display key press on screen

Post by ozzii » 12 Oct 2017, 01:58

Thanks Marius for your work...
robodesign
Posts: 371
Joined: 30 Sep 2017, 03:59
Facebook: marius.sucan
GitHub: mariussucan
Location: Romania
Contact:

Re: [Script] KeypressOSD - Display key press on screen

Post by robodesign » 12 Oct 2017, 07:57

Hello!

Ezzii, my pleasure; if you have any questions or suggestions, please tell me. IWhat is what you desire the most for it? See the to-do list and tell me.

I am doing my best to make it as usable as possible. I use it constantly and I make it such that I like to use it.

Next is... More work on Shift related connundrums and typing, or visual mouse clicks feature.

New version:
; CHANGELOG:
; by Marius Sucan (robodesign.ro)
; v2.93 (2017-10-12)
; - new option: StickyKeys
; - back again, modifiers behave as I want
; - if StickKeys is off, modifiers (Ctrl, Shift, Alt, WinKey) do not clear typed input in the OSD, if pressed once, without a combo
; - if StickyKeys is on, they erase what is written in the OSD, except for Shift, of course

Code: Select all

; KeypressOSD.ahk
; Charset for this file must be UTF 8 with BOM.
; it may not function properly otherwise.
;--------------------------------------------------------------------------------------------------------------------------
;
; AVAILABLE SHORTCUTS:
 ; Ctrl+Alt+Shift+F8  - Toggles "Show single key" option. Useful when you type passwords or must reliably use dead keys.
 ; Ctrl+Alt+Shift+F9  - Toggles between two OSD offsets: GuiYa and GuiYb.
 ; Ctrl+Alt+Shift+F10 - Toggles personal/regional keys support.
 ; Ctrl+Alt+Shift+F11 - Detect keyboard language.
 ; Ctrl+Alt+Shift+F12 - Reinitialize OSD. Useful when it no longer appears on top. To have it appear on top of elevated apps, run it in administrator mode.
;
; NOTES:
 ; This script was made for, and on Windows 10.
 ; The keyboard layouts have changed since Win XP or Win 98.
 ; Windows 10 also no longer switches keyboard layouts based
 ; on the currently active app. As such, automatic keyboard
 ; layout detection may not work for you.
 ; 
 ; I do not intend to offer support for older Windows versions.
 ; 
 ; This script has support only for Latin-based keyboards.
 ; Thus, it has no support for Chinese, Japanese, chirilic, 
 ; It is too complex for me to implement support for other alphabets or writing systems.
 ; If other programmers willing to invest the time in this script,
 ; are welcomed to do so, and even to transform it into anything they wish. 
 ;
 ; I offer numerous options/settings in the script such that
 ; everyone can find a way to adapt it to personal needs.
 ; - you can edit, in the code, what are the dead keys
 ;   - see Loop, 95, char2skip from CreateHotkey() function
 ; - you can also define personal regional keys, while autodetect is disabled
 ; - disable dead keys if you do not have such keys
 ; 
 ; Read the messages you get:
 ; - it indicates when your keyboard layout is unsupported or Unrecognized
 ; - it also indicates if it made a partial match;
 ;   - in such cases, you will likely not have all the keys
 ;   - or simply AHK will give errors trying to bind to inexistent keys
 ; - if the external file is missing, languages.ini, it will always report
 ; that it did not detect your keyboard.
 ;
 ; Default/built in language support is for English International.
 ;
 ; For the layouts I added support, I avoided binding to dead keys
 ; such that you no longer have to add them manually, as indicated previously.
 ; 
 ; If you rely only on the "vanilla" version, you will likely 
 ; not be able to use it.
 ;
 ; I am no programmer and the script is still quite quirky, but I am trying to
 ; make it better and better with each version.

 ; I made it for people like me, with poor eye sight or low vision.
 ; It is meant to aid desktop computer usage.
;
; FEATURES:
 ; - Show previously pressed key if fired quickly.
 ; - Count key presses or key fires and mouse clicks.
 ; - Automatic resizing of OSD/HUD or fixed size.
 ; - Hides automatically when mouse runs over it.
 ; - Generate beeps for key presses, modifiers, mouse clicks or just when typing with Capslock.
 ; - Clipboard monitor. It displays briefly texts copied to clipboard.
 ; - Indicators for CapsLock, NumLock and ScrollLock states.
 ; - Typing mode; shows what you are typing in an expanding text area.
 ; - Partial dead keys support, option to turn it off; The work-around is only for the English International keyboard layout.
 ; - Partial support for many non-English keyboards. 40 keyboard layouts defined.
 ;   - limited automatic detection of keyboard layouts.
 ;   - the user also has the option to define his regional keys.
 ; - Easy to configure with many options:
 ;   - to toggle features: key beepers, key counting or previous key;
 ;   - to hide modifiers, mouse clicks or single key presses (which disables typing mode);
 ;   - or hide keys that usually get in the way: Left Click and Print Screen [HideAnnoyingKeys];
 ;   - differ between left and right modifiers;
 ;   - OSD/HUD position, size and display time;
 ;   - beep key presses even if keys are not displayed;
 ;   - some settings toggled by shortcuts are stored permanently in an INI file;
;
; CHANGELOG:
 ; by Marius Sucan (robodesign.ro)
 ;   v2.93 (2017-10-12)
 ;   - new option: StickyKeys
 ;   - back again, modifiers behave as I want
 ;     - if StickKeys is off, modifiers do not erase typed text
 ;     - if StickyKeys is on, they erase what is written in the OSD
 ;   v2.92 (2017-10-11)
 ;   - rolled back the changes for modifier keys; it resulted in many regressions noticeable for those using StickyKeys; I will insist on this :)
 ;   - clipboard monitoring; it displays briefly what text user copied to clipboard
 ;   - fixed bugs related to showing previous keys
 ;   v2.90 (2017-10-10)
 ;   - from now, numpad keys always initiate typing mode
 ;   - modifiers (Ctrl, Shift, Alt, WinKey) and Tab, Insert no longer clear typed input in the OSD, if pressed once, without a combo
 ;   - unified the behaviour of modifiers
 ;   - fixed some counting bugs :)
 ;   - added option to disable PermanentSettings
 ;   v2.87 (2017-10-09)
 ;   - improvements for assigning keys;
 ;     - errors are now managed, it will beep when key bindings fail
 ;   - [new] option to disable audio alerts :)
 ;   - languages are now in an external file
 ;   - new keyboard layouts for Polish, Azerbaijian, Turkmen
 ;   v2.85 (2017-10-08)
 ;   - minor improvements for the dead keys work-around
 ;   - added support for 40 foreign keyboard layouts
 ;   - automatic detection of keyboard layouts, at start or continously
 ;   - user also can define personal/regional keys in the settings section
 ;   - new keyboard shortcut, to toggle regional keys
 ;   - new setting: to enable or disable system-wide keyboard shortcuts
 ;   - settings toggled by shortcuts are stored permanently in an INI file
 ;     - except for the option of regional keys support
 ;   v2.73 (2017-10-05)
 ;   - improvements for automatic resize calculation
 ;   v2.72 (2017-10-04)
 ;   - new option: make beeps on mouse clicks
 ;   - new option: distinct beeper for modifier keys: Shift, Ctrl, Alt, WinKey.
 ;   - new option: beep hidden keys; if you want it to beep even if the keys are not displayed
 ;   - fix: now it always counts mouse clicks
 ;   v2.70 (2017-10-03)
 ;   - added option to toggle dead keys support / work-around.
 ;   - added option to hide annoying keys that usually get in the way: Left Click and Print Screen.
 ;   - redraw improvements, reduced flickering
 ;   - disabled OSD transparency to reduce flickering
 ;   v2.69 (2017-09-30)
 ;   - OSD / GUI hides when mouse is over it. Many thanks to phaleth!
 ;   v2.68 (2017-09-29)
 ;   - numpad keys now work in typing mode as expected; they appear as symbols or numbers
 ;
 ; by phaleth from irc.freenode.net #ahk
 ;   v2.67 (2017-09-28)
 ;   - dead keys improvements
 ;
 ; by Marius Sucan (robodesign.ro)
 ;   v2.66 (2017-09-28)
 ;   - key combinations with Shift work better.
 ;   v2.65 (2017-09-27)
 ;   - Fixed a bug with counting modifier keys;
 ;   - improved the dead keys work-around
 ;   - friendly names for mouse clicks
 ;   - when pressed, volume keys  always generate beeps
 ;   - added option to differentiate between left and right modifiers
 ;   - now it detects AltGr key
 ;   - the key beeper now also makes a beep for modifiers.
 ;   - capslock no longer erases text you are typing displayed by the OSD
 ;   - now you can toggle between two different OSD positions with Ctrl + Alt + Shift + F9
 ;   v2.60 (2017-09-26)
 ;   - Fixed many bugs with counting keys;
 ;   - reimplemented the feature to see the previous key combination, if quickly a new one is pressed;
 ;   - added options/settings to toggle previous keys, counting keys and delay;
 ;   - added shortcuts to toggle ShowSingleKey option and to reinitialize the OSD;
 ;   - added option for automatic resizing of the OSD; it can be turned off in the settings section; it is a fishy implementation, but if one adjusts it, can make it to suit personal needs;
 ;   - new option: beep when key is released or when writing with capslock
 ;   v2.58 (2017-09-23)
 ;   - Numpad keys have friendly naming, based on the numlock state.
 ;   - Combinations with space and backspace work again.
 ;   v2.56 (2017-09-22)
 ;   - more fixes for space usage and key combinations;
 ;   - now it indicates when ScrollLock, NumLock and Capslock are activated.
 ;   v2.55 (2017-09-21)
 ;   - minor fixes for space usage and key combinations.
 ;
 ; by Saiapatsu from irc.freenode.net #ahk
 ;   v2.54 (2017-09-21)
 ;   - Scrolls through n recently typed characters instead of just the latest word
 ;   v2.53 (2017-09-21)
 ;   - Case change effect limited to the loop 95 letters only.
 ;   v2.52 (2017-09-21)
 ;   - Now supports backspace. Commented out CapsLock beeper.
 ;   v2.51 (2017-09-21)
 ;   - Changed labels to functions, added ToolWindow style to window, changed DisplayTime
 ;   calculation, made it show last word typed, hid spacebar presses
 ;   todo: make Shift look less ugly
 ;
 ; by Marius Sucan (robodesign.ro)
 ;   v2.50 (2017-09-20)
 ;   - Changed the OSD positioning and sizing. It was based on the current window. Now it is always fixed in a specific place. Added a Capslock beeper.
 ;
 ; by tmplinshi from https://autohotkey.com/boards/viewtopic.php?f=6&t=225
 ;   v2.22 (2017-02-25)
 ;   - Now pressing same combination keys continuously more than 2 times,
 ;   for example press Ctrl+V 3 times, will displayed as "Ctrl + v (3)"
 ;   v2.21 (2017-02-24)
 ;   - Fixed LWin/RWin not poping up start menu
 ;   v2.20 (2017-02-24)
 ;   - Added displaying continuous-pressed combination keys.
 ;   e.g.: With CTRL key held down, pressing K and U continuously will shown as "Ctrl + k, u"
 ;   v2.10 (2017-01-22)
 ;   - Added ShowStickyModKeyCount option
 ;   v2.09 (2017-01-22)
 ;   - Added ShowModifierKeyCount option
 ;   v2.08 (2017-01-19)
 ;   - Fixed a bug
 ;   v2.07 (2017-01-19)
 ;   - Added ShowSingleModifierKey option (default is True)
 ;   v2.06 (2016-11-23)
 ;   - Added more keys. Thanks to SashaChernykh.
 ;   v2.05 (2016-10-01)
 ;   - Fixed not detecting "Ctrl + ScrollLock/NumLock/Pause". Thanks to lexikos.
 ;   v2.04 (2016-10-01)
 ;   - Added NumpadDot and AppsKey
 ;   v2.03 (2016-09-17)
 ;   - Added displaying "Double-Click" of the left mouse button.
 ;   v2.02 (2016-09-16)
 ;   - Added displaying mouse button, and 3 settings (ShowMouseButton, FontSize, GuiHeight)
 ;   v2.01 (2016-09-11)
 ;   - Display non english keyboard layout characters when combine with modifer keys.
 ;   v2.00 (2016-09-01)
 ;   - Removed the "Fade out" effect because of its buggy.
 ;   - Added support for non english keyboard layout.
 ;   - Added GuiPosition setting.
 ;   v1.00 (2013-10-11)
 ;   - First release by tmplinshi based on RaptorX. Function keys, numpad keys and mouse clicks support. Popups at mouse position.
 ;   v0.50 (2010-03-18)
 ;   - Released by RaptorX.
;--------------------------------------------------------------------------------------------------------------------------
;
; TO-DO:
 ; features to implement:
 ; - after pressing Shift + [letter] if the following key is a single letter, remember the first Shift + [letter];
 ; - when user types single letters, display Shift + (sign or numeric keys) as signs/symbols, eg. Shift + 0, should be displayed as );
 ; - visual mouse clicks and highlighter/locator
 ; - tray menu or window with options/settings
 ; - text caret locator/highlighter
 ; - show a generic symbol for ignored dead keys; helps to clarify one was pressed;
 ; - window spy, get texts underneath the mouse
 ;
 ; glitches to fix [by priority]:
 ; - [bug, since v1.00] Shift + [numpad] does not work; wrong behaviour when user presses Shift + [numpad]
 ; - [minor bug] once a key is pressed, counting of key fires is not initiated after the OSD was hidden for a little awhile;
 ; - make dead keys work better, even detect them ;)
 ; - automatic resizing of the OSD/GUI is just a silly hack based on the default font size and the number of typed chars; it often fails to resize properly;
 ; - redraw issues; it still flickers;
 ; - make it work reliably with sticky keys; if user presses once Ctrl and another key afterwards, it rarely detects the combination on Winndows 7;
;----------------------------------------------------------------------------

; Initialization
 #SingleInstance force
 #NoEnv
 #MaxHotkeysPerInterval 500
 SetBatchLines, -1
 ListLines, Off
 SetWorkingDir %A_ScriptDir%

; Settings / Customize:

 global DeadKeys         := 0     ; a toggle for a partial dead keys support. Zero [0] means no dead keys. See CreateHotkey() and char2skip to define the dead keys.
 , CustomRegionalKeys    := 0     ; if you want to add support to a regional keyboard
 , RegionalKeysList      := "a|b|c"  ; add the characters in this list, separated by |
 , AutoDetectKBD         := 1     ; at start, detect keyboard layout
 , ConstantAutoDetect    := 0     ; continously check if the keyboard layout changed; if AutoDetectKBD=0, this is ignored
 , SilentDetection       := 0     ; do not display information about language switching
 , audioAlerts           := 1     ; generate beeps when key bindings fail
 
 , ShowSingleKey         := 1     ; show only key combinations
 , HideAnnoyingKeys      := 1     ; Left click and PrintScreen can easily get in the way.
 , ShowMouseButton       := 1     ; if disabled, mouse click beeps will never occur
 , StickyKeys            := 0     ; how modifiers behave; set it to 1 if you use StickyKeys in Windows
 , ShowSingleModifierKey := 1     ; make it display Ctrl, Alt, Shift when pressed alone
 , DifferModifiers       := 0     ; differentiate between left and right modifiers
 , ShowPrevKey           := 1     ; show previously pressed key, if pressed quickly in succession
 , ShowPrevKeyDelay      := 300
 , ShowKeyCount          := 1     ; count how many times a key is pressed
 , ShowKeyCountFired     := 1     ; show only key presses (0) or catch key fires as well (1)
 
 , DisplayTime           := 3000  ; in milliseconds
 , GuiWidth              := 360
 , GuiHeight             := 50
 , GuiX                  := 50
 , GuiYa                 := 250   ; toggle between GuiYa and GuiYb with Ctrl + Alt + Shift + F9
 , GuiYb                 := 800
 , FontSize              := 19
 , OSDautosize           := 1     ; make adjustments in ShowHotkey() to match your font size
 , NumLetters            := 25    ; amount of recently typed letters to display
 , NumLettersAutosize    := 55    ; ...when OSD resizes automatically
 
 , CapslockBeeper        := 1     ; only when the key is released
 , KeyBeeper             := 0     ; only when the key is released
 , ModBeeper             := 0     ; beeps for every modifier, when released
 , MouseBeeper           := 0
 , BeepHiddenKeys        := 0     ; [when any beeper enabled] to beep or not when keys are not displayed by OSD/HUD
 
 , KeyboardShortcuts     := 1     ; system-wide shortcuts
 , PermanentSettings     := 1     ; settings stored in keypress-osd.ini override the settings from here
 , ClipMonitor           := 1     ; show clipboard changes
 
 ; global TransparencyLevel  := 220   ; OSD transparency; range from 0 to 255. feature disabled, to reduce flickering

; Initialization variables. Altering these may lead to undesired results.

 global IniFile := "keypress-osd.ini"
 , typed := "" ; hack used to determine if user is writing
 , visible := 0
 , GuiY := GuiYb
 , prefixed := 0 ; hack used to determine if last keypress had a modifier
 , zcSCROL := "SCROLL LOCK"
 , tickcount_start := 0 ; timer to count repeated key presses
 , keyCount := 0
 , ShowKeyCountDelay := (ShowKeyCountFired = 0) ? 700 : 6000
 , text_width := 60
 , modifiers_temp := 0
 , InputLocaleID := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")

NumLetters := (OSDautosize=1) ? NumLettersAutosize : NumLetters

    if PermanentSettings=1
    {
        IniRead, ShowSingleKey, IniFile, PermanentSettings, ShowSingleKey
        IniRead, GuiY, IniFile, PermanentSettings, GuiY
        GuiY := (GuiY=GuiYb || GuiY=GuiYa) ? GuiY : GuiYb
    }

    IniRead, CustomMultiLangToggled, IniFile, PermanentSettings, CustomMultiLangToggled
    if (CustomMultiLangToggled=1) {
        IniRead, CustomRegionalKeys, IniFile, PermanentSettings, CustomRegionalKeys
        CustomMultiLangToggled := 0
        IniWrite, %CustomMultiLangToggled%, IniFile, PermanentSettings, CustomMultiLangToggled
    }

    IniRead, AutoDetectKBDToggled, IniFile, PermanentSettings, AutoDetectKBDToggled
    if (AutoDetectKBDToggled=1) {
        IniRead, AutoDetectKBD, IniFile, PermanentSettings, AutoDetectKBD
        IniRead, ConstantAutoDetect, IniFile, PermanentSettings, ConstantAutoDetect
        AutoDetectKBDToggled := 0
        IniWrite, %AutoDetectKBDToggled%, IniFile, PermanentSettings, AutoDetectKBDToggled
    }

CreateGUI()
CreateGlobalShortcuts()
CreateHotkey()
if ClipMonitor=1
    OnClipboardChange("ClipChanged")
return

; The script

GetSpecialKeysStates() {
    GetKeyState, ScrollState, ScrollLock, T   
    If ScrollState = D
    {
       global zcSCROL := "SCROLL LOCK ON"
    }
    else {
       global zcSCROL := "Scroll lock off"
    }
}

TypedLetter(key) {
    StringLeft, key, key, 1
    Stringlower, key, key

    GetKeyState, CapsState, CapsLock, T
    If CapsState != D
    {
         if GetKeyState("Shift")
        {
          StringUpper, key, key
        }
    } else
        StringUpper, key, key

    return typed := SubStr(typed key, -NumLetters)
}

OnMousePressed() {
    global tickcount_start := A_TickCount-500

    try {
        key := GetKeyStr()
        typed := "" ; concerning TypedLetter(" ") - it resets the content of the OSD
        ShowHotkey(key)
        SetTimer, HideGUI, % -DisplayTime
    }

    If (MouseBeeper = 1) && (ShowMouseButton = 1) && (ShowSingleKey = 1) || (MouseBeeper = 1) && (ShowSingleKey = 0) && (BeepHiddenKeys = 1)
       soundbeep, 2500, 65

}

OnKeyPressed() {
    try {
        key := GetKeyStr()
        if (!(key ~= "i)^(Insert|Tab)$")) {
           typed := ""
        }
        ShowHotkey(key)
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnLetterPressed() {
    try {
        if typed && DeadKeys=1
            sleep, 25    ; this delay helps with dead keys, but it generates errors; the following actions: stringleft,1 and stringlower help correct these

        key := GetKeyStr(1)     ; consider it a letter

        if prefixed
        {
            typed := ""
            ShowHotkey(key)
        } else
        {
            TypedLetter(key)
            ShowHotkey(typed)
        }
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnSpacePressed() {
    try {
        if typed {
            TypedLetter("_")
            ShowHotkey(typed)
        } else if (!typed)
        {
          key := GetKeyStr()
          ShowHotkey(key)
        }
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnBspPressed() {
    try
    {
        if typed
        {
            typed := SubStr(typed, 1, StrLen(typed) - 1)
            ShowHotkey(typed)
        } else if (!typed)
        {
            key := GetKeyStr()
            ShowHotkey(key)
        }
        SetTimer, HideGUI, % -DisplayTime
    }

    if (BeepHiddenKeys = 1) && (KeyBeeper = 1) && (ShowSingleKey = 0)
       soundbeep, 1900, 45

    if (KeyBeeper = 1) && (ShowSingleKey = 1)
       soundbeep, 1900, 45
}

OnCapsPressed() {
    try
    {
        if typed
        {
            ShowHotkey(typed)
        } else if (!typed)
        {
            key := GetKeyStr()
            GetKeyState, CapsState, CapsLock, T
            if CapsState = D
            {
                key := prefixed ? key : "CAPSLOCK ON"
            } else
                key := prefixed ? key : "CapsLock off"
            ShowHotkey(key)
        }
        SetTimer, HideGUI, % -DisplayTime
    }

    If (CapslockBeeper = 1) && (ShowSingleKey = 1) || (BeepHiddenKeys = 1)
       {
        soundbeep, 450, 200
       }
}

OnNumpadPressed()
{
    GetKeyState, NumState, NumLock, T

    try {
        key := GetKeyStr()
        if NumState != D
        {
            typed := "" ; reset typed content
            ShowHotkey(key)
        } else if prefixed
        {
            typed := ""
            ShowHotkey(key)
        } else if NumState = D
        {
            key2 := GetKeyStr(1)
            sleep, 30    ; stupid hack12
            if (StrLen(key2)=5)
            {
              StringLeft, key2, key2, 3
              StringRight, key2, key2, 1
            }
            TypedLetter(key2)
            ShowHotkey(typed)
        }
        SetTimer, HideGUI, % -DisplayTime
    }
}

OnKeyUp() {
    global tickcount_start := A_TickCount

    if typed && (CapslockBeeper = 1) && (ShowSingleKey = 1)
    {
        GetKeyState, CapsState, CapsLock, T
        If CapsState = D
           {
             soundbeep, 450, 25
           }
           else if (KeyBeeper = 1) && (ShowSingleKey = 1)
           {
             soundbeep, 1900, 45
           }
    }

    If (CapslockBeeper = 0) && (KeyBeeper = 1) && (ShowSingleKey = 1)
       {
         soundbeep, 1900, 45
       }
       else if (CapslockBeeper = 1) && (KeyBeeper = 0)
       {
       }
       else if !typed && (CapslockBeeper = 1) && (ShowSingleKey = 1)
       {
         soundbeep, 1900, 45
       }

    if (BeepHiddenKeys = 1) && (KeyBeeper = 1) && (ShowSingleKey = 0)
         soundbeep, 1900, 45  
}

OnModPressed() {
    static modifierz := ["LCtrl", "RCtrl", "LAlt", "RAlt", "LShift", "RShift", "LWin", "RWin"]
    static repeatCount := 1

    for i, mod in modifierz
    {
        if GetKeyState(mod)
           fl_prefix .= mod " + "
    }

    if StickyKeys=0
       fl_prefix := RTrim(fl_prefix, "+ ")

    fl_prefix := CompactModifiers(fl_prefix)
    
    if InStr(fl_prefix, modifiers_temp)
    {
        keyCount := a
        valid_count := 1
    } else
    {
        keyCount := a
        valid_count := 0
        modifiers_temp := fl_prefix
    }

    if (ShowKeyCount=1) && (StickyKeys=0) {
        if !InStr(fl_prefix, "+") {
            if (valid_count=1) {
                if (++repeatCount > 1) {
                    modifiers_temp := fl_prefix
                    fl_prefix .= " (" repeatCount ")"
                }
            } else {
                repeatCount := 1
            }
        } else {
            repeatCount := 1
        }
   }

   if ShowSingleKey=1
   {
      ShowHotkey(fl_prefix)
      SetTimer, HideGUI, % -DisplayTime/2
   }
}

OnModUp() {
    global tickcount_start := A_TickCount

    If (ModBeeper = 1) && (ShowSingleKey = 1) && (ShowSingleModifierKey = 1) || (ModBeeper = 1) && (BeepHiddenKeys = 1)
       soundbeep, 1000, 65

}

; ===================================================================================

CreateGUI() {
    global

    Gui, +AlwaysOnTop -Caption +Owner +LastFound +ToolWindow +E0x20
    Gui, Margin, 10, 10
    Gui, Color, 111111
    Gui, Font, cWhite s%FontSize% bold, Arial, -wrap
    Gui, Add, Text, vHotkeyText left x10 y10 -wrap

;    if TransparencyLevel between 10 and 245
;       WinSet, Transparent, %TransparencyLevel%

}

CreateHotkey() {

    if (CustomRegionalKeys=1) && (AutoDetectKBD=0)
    {
        Loop, parse, RegionalKeysList, |
        {
           Hotkey, % "~*" A_LoopField, OnLetterPressed, useErrorLevel
           Hotkey, % "~*" A_LoopField " Up", OnKeyUp, useErrorLevel
           if (errorlevel!=0) && (audioAlerts=1)
              soundbeep, 1900, 50
        }
    }
   if (AutoDetectKBD=1)
   {
       IdentifyKBDlayout()
   }

    Loop, 95
    {
        k := Chr(A_Index + 31)

        if (DeadKeys=1)
        {
            for each, char2skip in StrSplit("``,^,6,',"",~", ",")        ; dead keys to ignore
            {
                if (k = char2skip && DeadKeys=1)
                {
                    continue, 2
                }
            }
        }

        if (k = " ")
        {
            Hotkey, % "~*Space", OnSpacePressed, useErrorLevel
            Hotkey, % "~*Space Up", OnKeyUp, useErrorLevel
        }
        else
        {
            Hotkey, % "~*" k, OnLetterPressed, useErrorLevel
            Hotkey, % "~*" k " Up", OnKeyUp, useErrorLevel
            if (errorlevel!=0) && (audioAlerts=1)
               soundbeep, 1900, 50
        }
    }

    Hotkey, % "~*Backspace", OnBspPressed, useErrorLevel
    Hotkey, % "~*CapsLock", OnCapsPressed, useErrorLevel
    Hotkey, % "~*CapsLock Up", OnKeyUp, useErrorLevel

    Loop, 24 ; F1-F24
    {
        Hotkey, % "~*F" A_Index, OnKeyPressed, useErrorLevel
        Hotkey, % "~*F" A_Index " Up", OnKeyUp, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    NumpadKeysList := "NumpadDot|NumpadDiv|NumpadMult|NumpadAdd|NumpadSub|sc04E|sc04A|sc052|sc04F|sc050|sc051|sc04B|sc04C|sc04D|sc047|sc048|sc049|sc053|sc037|sc135"

    Loop, parse, NumpadKeysList, |
    {
       Hotkey, % "~*" A_LoopField, OnNumpadPressed, useErrorLevel
       Hotkey, % "~*" A_LoopField " Up", OnKeyUp, useErrorLevel
       if (errorlevel!=0) && (audioAlerts=1)
          soundbeep, 1900, 50
    }

    Otherkeys := "WheelDown|WheelUp|WheelLeft|WheelRight|XButton1|XButton2|Browser_Forward|Browser_Back|Browser_Refresh|Browser_Stop|Browser_Search|Browser_Favorites|Browser_Home|Volume_Mute|Volume_Down|Volume_Up|Media_Next|Media_Prev|Media_Stop|Media_Play_Pause|Launch_Mail|Launch_Media|Launch_App1|Launch_App2|Help|Sleep|PrintScreen|CtrlBreak|Break|AppsKey|Tab|Enter|Esc"
               . "|Insert|Home|End|Up|Down|Left|Right|ScrollLock|NumLock|Pause|sc145|sc146|sc046|sc123|sc11C|sc149|sc151|sc122|sc153"
    Loop, parse, Otherkeys, |
    {
        Hotkey, % "~*" A_LoopField, OnKeyPressed, useErrorLevel
        Hotkey, % "~*" A_LoopField " Up", OnKeyUp, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    If ShowMouseButton=1
    {
        Loop, Parse, % "LButton|MButton|RButton", |
        Hotkey, % "~*" A_LoopField, OnMousePressed, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    for i, mod in ["LCtrl", "RCtrl", "LAlt", "RAlt", "LWin", "RWin"]
    {
        Hotkey, % "~*" mod, OnKeyPressed, useErrorLevel
        Hotkey, % "~*" mod " Up", OnModUp, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    If typed {
    for i, mod in ["LShift", "RShift"]
        Hotkey, % "~*" mod, OnKeyPressed, useErrorLevel
        Hotkey, % "~*" mod " Up", OnModUp, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    If (ShowSingleModifierKey=1) && (StickyKeys=0)
    {
      for i, mod in ["LShift", "RShift", "LCtrl", "RCtrl", "LAlt", "RAlt", "LWin", "RWin"]
        Hotkey, % "~*" mod, OnModPressed, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }

    If (ShowSingleModifierKey=1) && (StickyKeys=1)
    {
      for i, mod in ["LShift", "RShift"]
        Hotkey, % "~*" mod, OnModPressed, useErrorLevel
        if (errorlevel!=0) && (audioAlerts=1)
           soundbeep, 1900, 50
    }
}

ShowHotkey(HotkeyStr) {

    if (OSDautosize=1)
    {
        HotkeyTextTrimmed := RegExReplace(HotkeyStr, "[^a-zA-Z]", "")
        StringLeft, HotkeyTextTrimmed, HotkeyTextTrimmed, 5
        growthFactor := 1.25
        if HotkeyTextTrimmed is upper
           growthFactor := 1.0
        text_width := (StrLen(HotkeyStr)/growthFactor)*FontSize
        text_width := (text_width<70) ? 70 : text_width+15
    } else if OSDautosize=0
    {
        text_width := GuiWidth
    }

    GuiControl,     , HotkeyText, %HotkeyStr%
    GuiControl, Move, HotkeyText, w%text_width% left
    Gui, Show, NoActivate x%GuiX% y%GuiY% AutoSize, KeypressOSD
    visible := 1
    SetTimer, checkMousePresence, on, 400
}

GetKeyStr(letter := 0) {
    modifiers_temp := 0
    static modifiers := ["LCtrl", "RCtrl", "LAlt", "RAlt", "LShift", "RShift", "LWin", "RWin"]

    ; If any mod but shift, go ; If shift, check if not letter

    for i, mod in modifiers
    {
        if (mod = "LShift" && typed || mod = "RShift" && typed ? (!letter && GetKeyState(mod)) : GetKeyState(mod))
    ;    if GetKeyState(mod)
            prefix .= mod " + "
    }

    if (!prefix && !ShowSingleKey)
        throw

    key := SubStr(A_ThisHotkey, 3)

    if (key ~= "i)^(LCtrl|RCtrl|LShift|RShift|LAlt|RAlt|LWin|RWin)$") {
        if (ShowSingleModifierKey = 0) || (ShowSingleKey = 0) || (A_TickCount-tickcount_start > 2000) && visible
        {
            throw
        } else
        {
;           prefix := RTrim(prefix, "+ ")
            key := ""
            keyCount := a
            if StickyKeys=0
               throw
        }

    prefix := CompactModifiers(prefix)

    } else
    {
        if StrLen(key)=1 || InStr(key, " up") && StrLen(key)=4 && typed
        {
            StringLeft, key, key, 1
            key := GetKeyChar(key, "A")
        } else if ( SubStr(key, 1, 2) = "sc" ) {
            key := SpecialSC(key)
        } else if (key = "Volume_Up") || (key = "Volume_Down") {
            soundbeep, 150, 40
        } else if (key = "PrintScreen") {
            if HideAnnoyingKeys=1
                throw
            key := "Print Screen"
        } else if (key = "MButton") {
            key := "Middle Click"
        } else if (key = "RButton") {
            key := "Right Click"
        } else if (key = "LButton") && IsDoubleClick() {
            key := "Double-Click"
        } else if (key = "LButton") {
            if HideAnnoyingKeys=1
                throw
            key := "Left Click"
        }
        {
            _key := (key = "Double-Click") ? "Left Click" : key
        }

        prefix := CompactModifiers(prefix)

        static pre_prefix, pre_key
        StringUpper, key, key, T
        StringUpper, pre_key, pre_key, T
        keyCount := (key=pre_key) && (prefix = pre_prefix) && (repeatCount<1.5) ? keyCount : 1
        global ShowKeyCountDelay := (ShowKeyCountFired = 0) ? 700 : 6000
        ShowKeyCountDelay := (ShowKeyCountFired=1) ? (ShowKeyCountDelay+keyCount*100) : ShowKeyCountDelay

        if (InStr(prefix, "+")) && (A_TickCount-tickcount_start < ShowKeyCountDelay) || (!letter) && (A_TickCount-tickcount_start < ShowKeyCountDelay)
        {
            if (ShowPrevKey=1) && (A_TickCount-tickcount_start < ShowPrevKeyDelay)
            {
                ShowPrevKeyValid := 1
                if InStr(pre_key, " up")  && StrLen(pre_key)=4
                {
                    StringLeft, pre_key, pre_key, 1
                }
            } else
            {
                ShowPrevKeyValid := 0
            }
            if (prefix != pre_prefix) {
                result := (ShowPrevKeyValid=1) ? prefix key " {" pre_prefix pre_key "}" : prefix key
            } else if (ShowPrevKeyValid=1) && (key != pre_key) || (ShowKeyCount=1) && (ShowPrevKeyValid=1)
            {
                keyCount := (key=pre_key) && (ShowKeyCount=1) ? (keyCount+1) : 1
                key := (keyCount>1) && (ShowKeyCount=1) ? (key " (" keyCount ")") : (key ", " pre_key)
            } else if (ShowPrevKeyValid=0)
            {
                keyCount := (key=pre_key) && (ShowKeyCount=1) ? (keyCount+1) : 1
                key := (keyCount>1) ? (key " (" keyCount ")") : (key)
            }
        } else {
            keyCount := 1
        }
        pre_prefix := prefix
        pre_key := _key
    }

    prefixed := prefix ? 1 : 0
    return result ? result : prefix . key
}

CompactModifiers(stringy)
{
    if DifferModifiers = 1
    {
        StringReplace, stringy, stringy, LCtrl + RAlt, AltGr, All
        StringReplace, stringy, stringy, LCtrl + RCtrl + RAlt, RCtrl + AltGr, All
        StringReplace, stringy, stringy, RAlt, AltGr, All
        StringReplace, stringy, stringy, LAlt, Alt, All
    } else if (DifferModifiers = 0)
    {
        StringReplace, stringy, stringy, LCtrl + RAlt, AltGr, All
        ; StringReplace, stringy, stringy, LCtrl + RCtrl + RAlt, RCtrl + AltGr, All
        StringReplace, stringy, stringy, LCtrl, Ctrl, All
        StringReplace, stringy, stringy, RCtrl, Ctrl, All
        StringReplace, stringy, stringy, LShift, Shift, All
        StringReplace, stringy, stringy, RShift, Shift, All
        StringReplace, stringy, stringy, LAlt, Alt, All
        StringReplace, stringy, stringy, LWin, WinKey, All
        StringReplace, stringy, stringy, RWin, WinKey, All
        StringReplace, stringy, stringy, Ctrl + Ctrl, Ctrl, All
        StringReplace, stringy, stringy, Shift + Shift, Shift, All
        StringReplace, stringy, stringy, WinKey + WinKey, WinKey, All
        StringReplace, stringy, stringy, RAlt, AltGr, All
    }
    return stringy
}

SpecialSC(sc) {
    GetSpecialKeysStates()

    GetKeyState, NumState, NumLock, T
    If NumState = D
    {
       k := {sc046: zcSCROL, sc145: "NUM LOCK ON", sc146: "Pause", sc123: "Genius LuxeMate Scroll", sc04E: "[ + ]", sc04A: "[ - ]", sc052: "[ 0 ]", sc04F: "[ 1 ]", sc050: "[ 2 ]", sc051: "[ 3 ]", sc04B: "[ 4 ]", sc04C: "[ 5 ]", sc04D: "[ 6 ]", sc047: "[ 7 ]", sc048: "[ 8 ]", sc049: "[ 9 ]", sc053: "[ . ]", sc037: "[ * ]", sc135: "[ / ]", sc11C: "[Enter]", sc149: "Page Up", sc151: "Page Down", sc153: "Delete", sc122: "Media_Play/Pause"}
    }
    else {
       k := {sc046: zcSCROL, sc145: "Num lock off", sc146: "Pause", sc123: "Genius LuxeMate Scroll", sc04E: "[ + ]", sc04A: "[ - ]", sc052: "[Insert]", sc04F: "[End]", sc050: "[Down]", sc051: "[Page Down]", sc04B: "[Left]", sc04C: "[Undefined]", sc04D: "[Right]", sc047: "[Home]", sc048: "[Up]", sc049: "[Page Up]", sc053: "[Delete]", sc037: "[ * ]", sc135: "[ / ]", sc11C: "[Enter]", sc149: "Page Up", sc151: "Page Down", sc153: "Delete", sc122: "Media_Play/Pause"}
    }
    return k[sc]
}

; <tmplinshi>: thanks to Lexikos: https://autohotkey.com/board/topic/110808-getkeyname-for-other-languages/#entry682236
; This enables partial support for non-English keyboard layouts.
; If the script initializes with the English keyboard layout, but then used with another one, this function gets proper key names,

GetKeyChar(Key, WinTitle:=0)
{
    thread := WinTitle=0 ? 0
        : DllCall("GetWindowThreadProcessId", "ptr", WinExist(WinTitle), "ptr", 0)
    hkl := DllCall("GetKeyboardLayout", "uint", thread, "ptr")
    vk := GetKeyVK(Key), sc := GetKeySC(Key)
    VarSetCapacity(state, 256, 0)
    VarSetCapacity(char, 4, 0)
    n := DllCall("ToUnicodeEx", "uint", vk, "uint", sc
        , "ptr", &state, "ptr", &char, "int", 2, "uint", 0, "ptr", hkl)
    return StrGet(&char, n, "utf-16")
}
global LangsBinded := 0

IdentifyKBDlayout() {
  VarSetCapacity(kbLayoutRaw, 32, 0)
  DllCall("GetKeyboardLayoutName", "Str", kbLayoutRaw)
  StringRight, kbLayout, kbLayoutRaw, 4

  #Include *i keypress-osd-languages.ini

  check_kbd := StrLen(LangName_%kbLayout%)>2 ? 1 : 0
  check_kbd_exact := StrLen(LangRaw_%kbLayoutRaw%)>2 ? 1 : 0
  if (check_kbd_exact=0)
  {
      partialKBDmatch = (Partial match)
  }

  if (check_kbd=0)
  {
      ShowHotkey("Unrecognized layout: (kbd " kbLayoutRaw ").")
      SetTimer, HideGUI, % -DisplayTime
      soundbeep, 500, 900
  }

  StringLeft, kbLayoutSupport, LangName_%kbLayout%, 1
  if (kbLayoutSupport="-") && (check_kbd=1)
  {
      ShowHotkey("Unsupported layout: " LangName_%kbLayout% " (kbd" kbLayout ").")
      SetTimer, HideGUI, % -DisplayTime
      soundbeep, 500, 900
  }

  IfInString, LangsBinded, %kbLayout%
  {
      KBDbinded := 1
  } else
  {
      LangsBinded := kbLayout "|" LangsBinded 
      StringLeft, LangsBinded, LangsBinded, 20
      KBDbinded := 0
  }

  if (kbLayoutSupport!="-") && (check_kbd=1) && (KBDbinded=0)
  {
      Loop, parse, LangChars_%kbLayout%, |
      {
         Hotkey, % "~*" A_LoopField, OnLetterPressed, useErrorLevel
         Hotkey, % "~*" A_LoopField " Up", OnKeyUp, useErrorLevel
         if (errorlevel!=0) && (audioAlerts=1)
             soundbeep, 1900, 50
      }
      if SilentDetection=0
      {
          ShowHotkey("Keyboard layout: " LangName_%kbLayout% " (kbd" kbLayout "). " partialKBDmatch)
          SetTimer, HideGUI, % -DisplayTime/4
      }
  }

   if (ConstantAutoDetect=1) && (AutoDetectKBD=1)
      SetTimer, ConstantKBDchecker, 1500
}

ConstantKBDchecker() {
  SetFormat, Integer, H
  WinGet, WinID,, A
  ThreadID := DllCall("GetWindowThreadProcessId", "UInt", WinID, "UInt", 0)
  NewInputLocaleID := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")
    if (InputLocaleID != NewInputLocaleID)
    {
        InputLocaleID := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")
        
        if SilentDetection=0
        ShowHotkey("Changed keyboard layout: " InputLocaleID)
      
        sleep, 350
        Reload
    } 
}

IsDoubleClick(MSec = 300) {
    Return (A_ThisHotKey = A_PriorHotKey) && (A_TimeSincePriorHotkey < MSec)
}

IsDoubleClickEx(MSec = 300) {
    preHotkey := RegExReplace(A_PriorHotkey, "i) Up$")
    Return (A_ThisHotKey = preHotkey) && (A_TimeSincePriorHotkey < MSec)
}

HideGUI() {
    visible := 0
    Gui, Hide
    SetTimer, checkMousePresence, off, 300
}

checkMousePresence() {
    id := mouseIsOver()
    title := getWinTitleFromID(id)
    if title = KeypressOSD
       HideGUI()
}

mouseIsOver() {
    MouseGetPos,,, id
    return id
}

getWinTitleFromID(id) {
    WinGetTitle, title, % "ahk_id " id
    return title
}

CreateGlobalShortcuts() {
   if (KeyboardShortcuts=1) {
      Hotkey, !+^F8, ToggleShowSingleKey
      Hotkey, !+^F9, TogglePosition
      Hotkey, !+^F10, EnableCustomKeys
      Hotkey, !+^F11, DetectLangNow
      Hotkey, !+^F12, ReloadScript
    }
}

ToggleShowSingleKey:
    ShowSingleKey := (!ShowSingleKey) ? true : false
    Gui, Destroy
    sleep, 100
    CreateGUI()
    sleep, 100
    IniWrite, %ShowSingleKey%, IniFile, PermanentSettings, ShowSingleKey
    ShowHotkey("Show single keys = " ShowSingleKey)
    SetTimer, HideGUI, % -DisplayTime
return

TogglePosition:
    GuiY := (GuiY = GuiYb) ? GuiYa : GuiYb
    Gui, Destroy
    sleep, 100
    CreateGUI()
    sleep, 100
    IniWrite, %GuiY%, IniFile, PermanentSettings, GuiY
    ShowHotkey("OSD position changed")
    SetTimer, HideGUI, % -DisplayTime
return

EnableCustomKeys:
    global CustomRegionalKeys := CustomRegionalKeys = 1 ? 0 : 1
    CustomMultiLangToggled := 1
    IniWrite, %CustomMultiLangToggled%, IniFile, PermanentSettings, CustomMultiLangToggled
    IniWrite, %CustomRegionalKeys%, IniFile, PermanentSettings, CustomRegionalKeys
    ShowHotkey("Custom Regional keys = " RegionalKeys)
    sleep, 1000
    Reload
return

DetectLangNow:
    global AutoDetectKBD := 1
    global ConstantAutoDetect := 0
    AutoDetectKBDToggled := 1
    IniWrite, %AutoDetectKBDToggled%, IniFile, PermanentSettings, AutoDetectKBDToggled
    IniWrite, %AutoDetectKBD%, IniFile, PermanentSettings, AutoDetectKBD
    IniWrite, %ConstantAutoDetect%, IniFile, PermanentSettings, ConstantAutoDetect
    ShowHotkey("Detecting keyboard layout...")
    sleep, 800
    Reload
return

ReloadScript:
    Gui, Destroy
    sleep, 100
    CreateGUI()
    sleep, 100
    ShowHotkey("OSD reinitializing...")
    sleep, 1500
    Reload
return

ClipChanged(Type)
{
    sleep, 300
    if type=1
    {
       troll := clipboard
       Stringleft, troll, troll, NumLetters*2
       StringReplace, troll, troll, `r`n, %A_SPACE%, All
       StringReplace, troll, troll, %A_SPACE%%A_SPACE%, , All
       ShowHotkey(troll)
       SetTimer, HideGUI, % -DisplayTime*1.5
    } else if type=2
    {
       ShowHotkey("Clipboard data changed")
       SetTimer, HideGUI, % -DisplayTime/2
    }
}

Source at: http://p.ahkscript.org/?p=4fab8086

; TO-DO:
; features to implement:
; - after pressing Shift + [letter] if the following key is a single letter, remember the first Shift + [letter]; done
; - when user types single letters, display Shift + (sign or numeric keys) as signs/symbols, eg. Shift + 0, should be displayed as ); done
; - visual mouse clicks and highlighter/locator done
; - tray menu or window with options/settings done
; - text caret locator/highlighter
; - show a generic symbol for ignored dead keys; helps to clarify one was pressed; done
; - window spy, get texts underneath the mouse
;
; glitches to fix [by priority]:
; - [bug, since v1.00] Shift + [numpad] does not work; wrong behaviour when user presses Shift + [numpad]

Best regards, Marius.
Last edited by robodesign on 16 Nov 2017, 05:29, edited 2 times in total.
-------------------------
KeyPress OSD v4: GitHub or forum. (presentation video)
My home page.
Post Reply

Return to “Scripts and Functions”