[TOY/FUNC] Combine Keys

Post your working scripts, libraries and tools for AHK v1.1 and older
just me
Posts: 9464
Joined: 02 Oct 2013, 08:51
Location: Germany

[TOY/FUNC] Combine Keys

09 Jan 2014, 01:30

Update 1.0.01.00 - 2014-01-11:
  • Fixed some bugs.
  • Added additional modifiers for the dead-key.
  • The specified locale will be loaded and unloaded now.


As requested here a made this little function with some help from stackoverflow.

It combines keys in the manner of keyboard input and returns the combined character, if any.

You may pass the following parameters.
  • A (unshifted) key name.
  • A string containing the modifiers for this key.
  • A (unshifted) dead-key to combine with.
  • A string containing the modifiers for this dead-key.
  • The locale to use.

Code: Select all

#NoEnv
; Put in additional locales you want to use here.
; Language identifiers are documented in the help file (http://www.ahkscript.org/docs/misc/Languages.htm).
; You have to use the 4 hex digits of the language code as the string value for the locale name.
; The "Default" entry will be added to the DDL internally and will use the current keyboard locale.
Locales := {"en-US": "0409", "fr-FR": "040C"}
; Locales := {}
LocDDL := "Default"
For Locale In Locales
   LocDDL .= "|" . Locale
; ----------------------------------------------------------------------------------------------------------------------
Gui, Margin, 20, 20
Gui, Add, Text, cNavy, Enter the unshiftet key:
Gui, Add, Edit, y+5 vKey Center Limit1 Lowercase, "W"
GuiControl, , Key
Gui, Add, CheckBox, x+20 yp hp vShift, Shift
Gui, Add, CheckBox, x+10 yp hp vCtrl, Ctrl
Gui, Add, CheckBox, x+10 yp hp vAlt, Alt
Gui, Add, CheckBox, x+10 yp hp vAltGr, AltGr
Gui, Add, Text, xm y+20 cNavy, Enter the unshifted dead-key:
Gui, Add, Edit, xm y+5 vDead Center Limit1 Lowercase, "W"
GuiControl, , Dead
Gui, Add, CheckBox, x+20 yp hp vDeadShift, Shift
Gui, Add, CheckBox, x+10 yp hp vDeadCtrl, Ctrl
Gui, Add, CheckBox, x+10 yp hp vDeadAlt, Alt
Gui, Add, CheckBox, x+10 yp hp vDeadAltGr, AltGr
Gui, Add, Text, xm y+20 cNavy, Choose the locale:
Gui, Add, DDL, xm y+5 r5 vLoc Choose1, % LocDDL
Gui, Add, Button, xm gShowResult, Show Result
Gui, Add, Button, x+20 yp vBtnClear gClear, Clear
Gui, Font, s24 Bold
Gui, Add, Text, xm w200 h60 Border Center +0x200 cNavy vResult
Gui, Show, , Combine Keys
Return
; ----------------------------------------------------------------------------------------------------------------------
GuiClose:
ExitApp
; ----------------------------------------------------------------------------------------------------------------------
Clear:
   GuiControl, , Key
   GuiControl, , Shift, 0
   GuiControl, , Ctrl, 0
   GuiControl, , Alt, 0
   GuiControl, , AltGr, 0
   GuiControl, , Dead
   GuiControl, , DeadShift, 0
   GuiControl, , DeadCtrl, 0
   GuiControl, , DeadAlt, 0
   GuiControl, , DeadAltGr, 0
   GuiControl, Choose, Loc, 1
   GuiControl, , Result
   GuiControl, Focus, Key
Return
; ----------------------------------------------------------------------------------------------------------------------
ShowResult:
   Gui, Submit, NoHide
   KeyMods := ""
   If (Shift)
      KeyMods .= "+"
   If (Ctrl)
      KeyMods .= "^"
   If (Alt)
      KeyMods .= "!"
   If (AltGr)
      KeyMods .= "^!"
   DeadMods := ""
   If (DeadShift)
      DeadMods .= "+"
   If (DeadCtrl)
      DeadMods .= "^"
   If (DeadAlt)
      DeadMods .= "!"
   If (DeadAltGr)
      DeadMods .= "^!"
   Locale := Loc = "Default" ? 0 : Locales[Loc]
   Error := ""
   GuiControl, , Result
   Try GuiControl, , Result, % CombineKeys(Key, KeyMods, Dead, DeadMods, Locale)
   Catch Error {
   }
   SB_SetText("   " . Error)
   GuiControl, Focus, BtnClear
Return
; ======================================================================================================================
; CombineKeys() - combine keys in the manner of keyboard input.
; Changelog:
;     1.0.01.00/2014-01-11/just me - bug fixes and additional modifier keys for the dead-key.
;     1.0.00.00/2014-01-09/just me - initial release.
; ======================================================================================================================
CombineKeys(Key, KeyModifiers := "", DeadKey := "", DeadKeyModifiers := "", Locale := 0) {
   ; -------------------------------------------------------------------------------------------------------------------
   ; Key                -  Unshifted key
   ; KeyModifiers       -  A string containing a combination of modifier keys in the format of the Send command.
   ;                       Valid values: ! (Alt), ^ (Ctrl), and/or + (Shift). Default: "" - no modifiers.
   ;                       Specify "^!" for the AltGR key.
   ; DeadKey            -  Unshifted dead-key to compose with key. Default: "" - no dead-key.
   ; DeadKeyModifiers   -  A string containing a combination of modifiers for the dead-key (see KeyModifiers).
   ; Locale             -  The input locale identifier (4-digit hex string) to be used to translate the specified keys.
   ;                       Default: 0 - current locale.
   ; Return values:     -  "" (empty string):
   ;                       -  the specified locale could not be loaded, or
   ;                       -  the specified dead-key is not valid for the specified keyboard layout, or
   ;                       -  the specified combination has no translation for the specified keyboard layout, or
   ;                       -  the specified combination results in a dead-key.
   ;                    -  1 character - the combined character.
   ;                    -  2 or more characters - the most common cause for this is that the dead-key could not be
   ;                       combined with the specified key to form a single character.
   ; Notes:             Many of the values needed to specify the locale are listed in the help file:
   ;                       A_Language -> Language Codes (these 4-digit codes).
   ;                    You have to pass the four hex digits as a quoted string (i.e. "0436" for Afrikaans).
   ; -------------------------------------------------------------------------------------------------------------------
   Static VK_Mod := {"+": 0x10, "^": 0x11, "!": 0x12}
   HKL := 0 ; initialize HKL
   If (Locale) {
      Locale := SubStr("0000000" . Locale, -7)
      If !(HKL := DllCall("User32.dll\LoadKeyboardLayout", "Str", Locale, "UInt", 0x81, "UInt"))
         Return ""
   }
   If (DeadKey) {
      VarSetCapacity(Chars, 32, 0)
      VarSetCapacity(ModStates, 256, 0)
      DeadKey := SubStr(DeadKey, 1, 1)
      VK := GetKeyVK(DeadKey)
      SC := GetKeySC(DeadKey)
      For Each, Modifier In StrSplit(DeadKeyModifiers)
         If VK_Mod.HasKey(Modifier)
            NumPut(0x80, ModStates, VK_Mod[Modifier], "UChar")
      CharCount :=  DllCall("User32.dll\ToUnicodeEx", "UInt", VK, "UInt", SC, "Ptr", &ModStates, "Str", Chars
                          , "Int", 16, "UInt", 0 , "UInt", HKL, "Int")
      If (CharCount <> -1) { ; the specified dead-key is invalid
         If (HKL)
            DllCall("User32.dll\UnloadKeyboardLayout", "UInt", HKL)
         Return ""
      }
   }
   VarSetCapacity(Chars, 32, 0)
   VarSetCapacity(ModStates, 256, 0)
   Key := SubStr(Key, 1, 1)
   VK := GetKeyVK(Key)
   SC := GetKeySC(Key)
   For Each, Modifier In StrSplit(KeyModifiers)
      If VK_Mod.HasKey(Modifier)
         NumPut(0x80, ModStates, VK_Mod[Modifier], "UChar")
   CharCount := DllCall("User32.dll\ToUnicodeEx", "UInt", VK, "UInt", SC, "Ptr", &ModStates, "Str", Chars
                      , "Int", 16, "UInt", 0, "UInt", HKL, "Int")
   If (CharCount < 0) { ; the specified key is a dead-key, remove it from the keyboard buffer and return ""
      VarSetCapacity(ModStates, 256, 0)
      DllCall("User32.dll\ToUnicodeEx", "UInt", 0x20, "UInt", 0, "Ptr", &ModStates, "Str", Chars
            , "Int", 16, "UInt", 0, "UInt", HKL, "Int")
      If (HKL)
         DllCall("User32.dll\UnloadKeyboardLayout", "UInt", HKL)
      Return ""
   }
   If (HKL)
      DllCall("User32.dll\UnloadKeyboardLayout", "UInt", HKL)
   If (CharCount < 1) ; the specified combination has no translation for the current state of the keyboard
      Return ""
   Return StrGet(&Chars, CharCount, "UTF-16")
}
Last edited by just me on 11 Jan 2014, 03:10, edited 1 time in total.
Zelio
Posts: 278
Joined: 30 Sep 2013, 00:45
Location: France

Re: [TOY/FUNC] Combine Keys

09 Jan 2014, 15:58

Very Nice !
ToUnicodeEx seems perfect, that works for altgr+e = alt+ctrl+e = €, not like ToAscii ?

However, I found some bugs or problems :
1/ Static VK_Mod := {"+": 0x10, "^": 0x11, "!": 0x12} you have to add ,"Shift": 0x10 else you can't have a good ShiftDeadKey because of NumPut(0x80, ModStates, VK_Mod.Shift, "UChar") who call or refer to nothing
2/ Create a shortcut to merge altgr = ctrl + alt, for noob user
3/ On other keyboards (ex: french) some dead keys are triggered with altgr, so you have to Loop, Parse, ShiftDeadKey as you do for Modifiers so NumPut(0x80, ModStates, VK_Mod[A_LoopField], "UChar") will can have "Ctrl" and "Alt" value.
4/ for noob and readability, to add all constant as static of this page http://msdn.microsoft.com/en-us/library ... 18693.aspx

In other word, my suggest is to do something like this:
CombineKeys(Key, KeyModifiers, DeadKey, DeadKeyModifiers, LayoutName)
CombineKeys("e", "shift", "è", "altgr", "fr-FR") ; 0x080c È
just me
Posts: 9464
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [TOY/FUNC] Combine Keys

10 Jan 2014, 00:54

Hi Zelio,

when I read your post there I started some more testing and found even more bugs (at least on Win XP). ;)
  1. This was caused by a "last-minute change" trying to make the passing of modifiers more easy.
  2. Well, I think an additional check box in the GUI and a note in the function header should be enough.
  3. Yes, I've got it now.
  4. I'm not sure. Most of the locale identifiers are documented (http://www.ahkscript.org/docs/misc/Languages.htm).
Also, I found that the used locale has to be (pre)loaded, otherwise the function will fail. And, the function crashes AHK when called using the AHK ANSI version if the parameter 'Chars' of ToUnicode() is passed as 'WStr' (I've no idea why).

I'm working on the fixes.

BTW: The prior used ToAscii() function is retrieving "€" for e + Alt + Ctrl combination for me.

Thanks!
just me
Posts: 9464
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [TOY/FUNC] Combine Keys

11 Jan 2014, 03:14

*Update* (more work than expected for such a little toy)

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 171 guests