[CLASS] Lyt - Keyboard layout (language) operation

Post your working scripts, libraries and tools for AHK v1.1 and older
stealzy
Posts: 91
Joined: 01 Nov 2015, 13:43

[CLASS] Lyt - Keyboard layout (language) operation

Post by stealzy » 17 Feb 2017, 13:24

Class and example usage:

Code: Select all

#SingleInstance force
Loop % Lyt.GetList().MaxIndex()
	str .= A_Index ": " Lyt.GetList()[A_Index].LocName " - " Lyt.GetList()[A_Index].LayoutName
	. "`n" Format("{:#010x}", Lyt.GetList()[A_Index].hkl) "`n"

MsgBox,, Your system loaded layout list, % str "`n" defaulSystemLayout
F1::ToolTip % Lyt.Set("EN")           ; set first EN locale layout in system loaded layout list
F2::ToolTip % Lyt.Set("switch")       ; switch input locale. Lyt.Set() do the same.
F3::ToolTip % Lyt.Set("forward")      ; move forward (cycle) in layout list
F4::ToolTip % Lyt.Set(2)              ; set second layout in list
F7::ToolTip % Lyt.Set("-EN")          ; set first non-english layout in list
F8::ToolTip % Lyt.Set(0x4090409)      ; set en-US layout by HKL
F9::Lyt.Set("forward", WinExist("AutoHotkey Help ahk_class HH Parent")) ; in AutoHotkey.chm windows
F10::Lyt.Set("en", "AutoHotkey Help ahk_class HH Parent")
F11::MsgBox % Lyt.GetLayoutName("AutoHotkey Help ahk_class HH Parent")
F12::MsgBox % "HKL: " Format("{:#010x}", Lyt.GetInputHKL()) "`n№: " Lyt.GetNum() " in system loaded list"
 . "`nLayoutName: " Lyt.GetLayoutName() "`nLocale: " Lyt.GetLocaleName()
 . " (" Lyt.GetLocaleName(,, true) ")`n" Lyt.GetList()[3].KLID
; . "`n" Lyt.GetList()[2].LayoutName " " Lyt.GetList()[2].LocName " " Lyt.GetList()[2].LocFullName
Esc::ExitApp

Class Lyt {
	static SISO639LANGNAME            := 0x0059 ; ISO abbreviated language name, eg "EN"
	static LOCALE_SENGLANGUAGE        := 0x1001 ; Full language name, eg "English"
	static WM_INPUTLANGCHANGEREQUEST  := 0x0050
	static INPUTLANGCHANGE_FORWARD    := 0x0002
	static INPUTLANGCHANGE_BACKWARD   := 0x0004
	static KLIDsREG_PATH              := "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\"
	; =========================================================================================================
	; PUBLIC METHOD Set()
	; Parameters:     arg (optional)   - (switch / forward / backward / 2-letter locale indicator name (EN) /
	;  / number of layout in system loaded layout list / language id e.g. HKL (0x04090409)). Default: switch
	;                 win (optional)   - (ahk format WinTitle / hWnd / "global"). Default: Active Window
	; Return value:   empty or description of error
	; =========================================================================================================
	Set(arg := "switch", win := "") {
		IfEqual win, 0, Return "Window not found"
		hWnd := (win = "")
						? WinExist("A")
						: ( win + 0
								? WinExist("ahk_id" win)
								: win = "global"
									? win
									: WinExist(win) )
		IfEqual hWnd, 0, Return "Window not found" ; WinExist() return 0

		if (arg = "forward") {
			Return this.ChangeCommon(, this.INPUTLANGCHANGE_FORWARD, hWnd)
		} else if (arg = "backward") {
			Return this.ChangeCommon(, this.INPUTLANGCHANGE_BACKWARD, hWnd)
		} else if (arg = "switch") {
			tmphWnd := (hWnd = "global") ? WinExist("A") : hWnd
			HKL := this.GetInputHKL(tmphWnd)
			HKL_Number := this.GetNum(,HKL)
			LytList := this.GetList()
			Loop % HKL_Number - 1 {
				If (LytList[A_Index].hkl & 0xFFFF  !=  HKL & 0xFFFF)
					Return this.ChangeCommon(LytList[A_Index].hkl,, hWnd)
			}
			Loop % LytList.MaxIndex() - HKL_Number
				If (LytList[A_Index + HKL_Number].hkl & 0xFFFF  !=  HKL & 0xFFFF)
					Return this.ChangeCommon(LytList[A_Index + HKL_Number].hkl,, hWnd)
		} else if (arg ~= "^-?[A-z]{2}") {
			invert := ((SubStr(arg, 1, 1) = "-") && (arg := SubStr(arg, 2, 2))) ? true : false
			For index, layout in this.GetList()
				if (InStr(layout.LocName, arg) ^ invert)
					Return this.ChangeCommon(layout.hkl,, hWnd)
			Return "HKL from this locale not found in system loaded layout list"
		} else if (arg > 0 && arg <= this.GetList().MaxIndex()) { ; HKL number in system loaded layout list
			Return this.ChangeCommon(this.GetList()[arg].hkl,, hWnd)
		} else if (arg > 0x400 || arg < 0) { ; HKL handle input
			For index, layout in this.GetList()
				if layout.hkl = arg
					Return this.ChangeCommon(arg,, hWnd)
			Return "This HKL not found in system loaded layout list"
		} else
			Return "Not valid input"
	}
	ChangeCommon(HKL := 0, INPUTLANGCHANGE := 0, hWnd := 0) {
		Return (hWnd = "global")
			? this.ChangeGlobal(HKL, INPUTLANGCHANGE)
			: this.ChangeWindow(HKL, INPUTLANGCHANGE, hWnd)
	}
	ChangeGlobal(HKL, INPUTLANGCHANGE) { ; in all windows
		If (INPUTLANGCHANGE != 0)
			Return "FORWARD and BACKWARD not support with global parametr."
		IfNotEqual A_DetectHiddenWindows, On, DetectHiddenWindows % (prevDHW := "Off") ? "On" : ""
		WinGet List, List
		Loop % List
			this.Change(HKL, INPUTLANGCHANGE, List%A_Index%)
		DetectHiddenWindows % prevDHW
	}
	ChangeWindow(HKL, INPUTLANGCHANGE, hWnd) {
		static hTaskBarHwnd := WinExist("ahk_class Shell_TrayWnd ahk_exe explorer.exe")
		(hWnd = hTaskBarHwnd)
		 ? this.ChangeTaskBar(HKL, INPUTLANGCHANGE, hTaskBarHwnd)
		 : this.Change(HKL, INPUTLANGCHANGE, hWnd)
	}
	ChangeTaskBar(HKL, INPUTLANGCHANGE, hTaskBarHwnd) {
		static hStartMenu, hLangBarInd, hDV2CH
		IfNotEqual A_DetectHiddenWindows, On, DetectHiddenWindows % (prevDHW := "Off") ? "On" : ""
		hStartMenu := hStartMenu  ? hStartMenu  : WinExist("ahk_classNativeHWNDHost ahk_exeexplorer.exe")
		hLangBarInd:= hLangBarInd ? hLangBarInd : WinExist("ahk_classCiceroUIWndFrame ahk_exeexplorer.exe")
		hDV2CH     := hDV2CH      ? hDV2CH      : WinExist("ahk_classDV2ControlHost ahk_exeexplorer.exe")

		this.Change(HKL, INPUTLANGCHANGE, hTaskBarHwnd)
		this.Change(HKL, INPUTLANGCHANGE, hStartMenu)
		If INPUTLANGCHANGE {
			Sleep 20
			HKL := this.GetInputHKL(hStartMenu), INPUTLANGCHANGE := 0
		}
		; to update Language bar indicator
		this.Change(HKL, INPUTLANGCHANGE, hLangBarInd)
		this.Change(HKL, INPUTLANGCHANGE, hDV2CH)
		DetectHiddenWindows % prevDHW
	}
	Change(HKL, INPUTLANGCHANGE, hWnd) {
		PostMessage, this.WM_INPUTLANGCHANGEREQUEST, % HKL ? "" : INPUTLANGCHANGE, % HKL ? HKL : "",
		, % "ahk_id" ((hWndOwn := DllCall("GetWindow", Ptr,hWnd, UInt,GW_OWNER:=4, Ptr)) ? hWndOwn : hWnd)
	}

	GetNum(win := "", HKL := 0) { ; layout Number in system loaded layout list
		HKL ? : HKL := this.GetInputHKL(win)
		If HKL {
			For index, layout in this.GetList()
				if (layout.hkl = HKL)
					Return index
		}
		Else If (KLID := this.KLID, this.KLID := "")
			For index, layout in this.GetList()
				if (layout.KLID = KLID)
					Return index
	}
	GetList() { ; List of system loaded layouts
		static aLayouts
		If IsObject(aLayouts)
			Return aLayouts
		Else {
			aLayouts := []
			Size := DllCall("GetKeyboardLayoutList", "UInt", 0, "Ptr", 0)
			VarSetCapacity(List, A_PtrSize*Size)
			Size := DllCall("GetKeyboardLayoutList", Int, Size, Str, List)
			Loop % Size {
				aLayouts[A_Index] := {}
				aLayouts[A_Index].hkl := NumGet(List, A_PtrSize*(A_Index - 1))
				aLayouts[A_Index].LocName := this.GetLocaleName(, aLayouts[A_Index].hkl)
				aLayouts[A_Index].LocFullName := this.GetLocaleName(, aLayouts[A_Index].hkl, true)
				aLayouts[A_Index].LayoutName := this.GetLayoutName(, aLayouts[A_Index].hkl)
				aLayouts[A_Index].KLID := this.GetKLIDfromHKL(aLayouts[A_Index].hkl)
			}
			Return aLayouts
		}
	}
	GetLocaleName(win := "", HKL := false, FullName := false) { ; e.g. "EN"
		HKL ? : HKL := this.GetInputHKL(win)
		If HKL
			LocID := HKL & 0xFFFF
		Else If (HKL = 0)  ;ConsoleWindow
			LocID := "0x" . SubStr(this.KLID, -3), this.KLID := ""
		Else
			Return

		LCType := FullName ? this.LOCALE_SENGLANGUAGE : this.SISO639LANGNAME
		Size := (DllCall("GetLocaleInfo", UInt, LocID, UInt, LCType, UInt, 0, UInt, 0) * 2)
		VarSetCapacity(localeSig, Size, 0)
		DllCall("GetLocaleInfo", UInt, LocID, UInt, LCType, Str, localeSig, UInt, Size)
		Return localeSig
	}
	GetLayoutName(win := "", HKL := false) { ; Layout name in OS display lang: "US", "United States-Dvorak"
		HKL ? : HKL := this.GetInputHKL(win)
		If HKL
			KLID := this.GetKLIDfromHKL(HKL)
		Else If (HKL = 0)  ;ConsoleWindow
			KLID := this.KLID, this.KLID := ""
		Else
			Return

		RegRead LayoutName, % this.KLIDsREG_PATH KLID, Layout Display Name
		DllCall("Shlwapi.dll\SHLoadIndirectString", Ptr,&LayoutName, Ptr,&LayoutName, UInt,outBufSize:=50, UInt,0)
		if !LayoutName
			RegRead LayoutName, % this.KLIDsREG_PATH KLID, Layout Text

		Return LayoutName
	}
	; Only for loaded in system layouts
	GetKLIDfromHKL(HKL) {
		VarSetCapacity(KLID, 8 * (A_IsUnicode ? 2 : 1))

		priorHKL := DllCall("GetKeyboardLayout", Ptr,DllCall("GetWindowThreadProcessId", Ptr,0, UInt,0, Ptr), Ptr)
		if !DllCall("ActivateKeyboardLayout", Ptr,HKL, UInt,0)
		|| !DllCall("GetKeyboardLayoutName", Ptr,&KLID)
			Return false
		DllCall("ActivateKeyboardLayout", Ptr,priorHKL, UInt,0)

		Return StrGet(&KLID)
	}
	; =========================================================================================================
	; PUBLIC METHOD GetInputHKL()
	; Parameters:     win (optional)   - ("" / hWnd / WinTitle). Default: "" — Active Window
	; Return value:   HKL of window / if handle incorrect, system default layout HKL return / 0 - if KLID found
	; =========================================================================================================
	GetInputHKL(win := "") {
		If win = 0
			Return,, ErrorLevel := "Window not found"
		hWnd := (win = "")
						? WinExist("A")
						: win + 0
							? WinExist("ahk_id" win)
							: WinExist(win)
		If hWnd = 0
			Return,, ErrorLevel := "Window " win " not found"

		WinGetClass class
		if (class == "ConsoleWindowClass") {
				WinGet consolePID, PID
				DllCall("AttachConsole", Ptr, consolePID)
				VarSetCapacity(KLID, 16)
				DllCall("GetConsoleKeyboardLayoutName", Str, KLID)
				DllCall("FreeConsole")
				this.KLID := KLID
				Return 0
		} else {
			; Dvorak on OSx64 0xfffffffff0020409 = -268303351   ->   0xf0020409 = 4026663945 Dvorak on OSx86
			HKL:=DllCall("GetKeyboardLayout", Ptr,DllCall("GetWindowThreadProcessId", Ptr,hWnd, UInt,0, Ptr), Ptr)
			Return HKL & ((1 << 8*A_PtrSize) - 1)
		}
	}
}

hex(n) {
	Return Format("{:#0x}", n)
}
/*
~LShift Up::(A_PriorKey = "LShift") ? Lyt.Set("EN")
~RShift Up::(A_PriorKey = "RShift") ? Lyt.Set("RU")

#Space::PostMessage WM_INPUTLANGCHANGEREQUEST:=0x50, INPUTLANGCHANGE_FORWARD:=0x2,,, % (hWndOwn := DllCall("GetWindow", Ptr, hWnd:=WinExist("A"), UInt, GW_OWNER := 4, Ptr)) ? "ahk_id" hWndOwn : "ahk_id" hWnd
Bug: do not work in taskbar. Fixed.
Update: layout detect fully work with console windows.
Update: Drugwash fix bug with hardcode max number of layouts.
Last edited by stealzy on 06 Feb 2018, 06:21, edited 13 times in total.

Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [CLASS] Lyt - Keyboard layout (language) operation

Post by Helgef » 04 Mar 2017, 04:58

This is excellent! It seems to be very well made :bravo:
From time to time windows decides to change my language settings in some (seemingly random) windows, this is very useful for me.
Thank you!

User avatar
rommmcek
Posts: 1470
Joined: 15 Aug 2014, 15:18

Re: [CLASS] Lyt - Keyboard layout (language) operation

Post by rommmcek » 05 Mar 2017, 02:23

@Helgef: Consider this setting! (pic. form Win10)
Attachments
Advanced Language Settings.png
Advanced Language Settings.png (98.92 KiB) Viewed 10288 times

Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [CLASS] Lyt - Keyboard layout (language) operation

Post by Helgef » 05 Mar 2017, 03:02

Thank you rommmcek, I couldn't find the corresponding setting on win7, I'll take a closer look late.

lexikos
Posts: 9494
Joined: 30 Sep 2013, 04:07
Contact:

Re: [CLASS] Lyt - Keyboard layout (language) operation

Post by lexikos » 05 Mar 2017, 04:38

stealzy wrote:Remark: layout does not change on taskbar if taskbar on focus.
MS do it by design, so you can click on "Languge bar" in taskbar and layout not switching to taskbar current layout.
Obviously, you do not need change layout on taskbar, because there are no fields to print something ;-).
Right click the taskbar, select Toolbars, then Address. Now there is a field to print something...

It appears that if the taskbar has the focus, the language selector on the taskbar does in fact change the taskbar's current layout, even if there are no "fields to print something".

Clicking on the language selector on the taskbar does not focus the taskbar.

stealzy
Posts: 91
Joined: 01 Nov 2015, 13:43

Re: [CLASS] Lyt - Keyboard layout (language) operation

Post by stealzy » 16 Mar 2017, 14:31

My mistake, if the taskbar has the focus layout still change, but two-letter inscription on language selector not change until taskbar loose focus.
Workaround is only for immediate display of new layout in language selector.

lexikos
Posts: 9494
Joined: 30 Sep 2013, 04:07
Contact:

Re: [CLASS] Lyt - Keyboard layout (language) operation

Post by lexikos » 16 Mar 2017, 20:45

... but two-letter inscription on language selector not change until taskbar loose focus.
The "two-letter inscription" changes immediately if I use the built-in selector or keyboard shortcut on Windows 7 or 10. The taskbar appears to work the same as every other window in this respect.

I just tested your script on Windows 7 and it did not work with the taskbar at all. The language did not change.

I didn't test your script on Windows 10 - my initial point was merely that your explanation was incorrect. Whether your script fails because of a "by design" feature or some kind of bug, I don't know.

stealzy
Posts: 91
Joined: 01 Nov 2015, 13:43

Re: [CLASS] Lyt - Keyboard layout (language) operation

Post by stealzy » 17 Mar 2017, 00:14

I also tested on Windows 7, probably it work precariously.
I remove workaround, let it be bug, I do not know how reliably change layout on focused taskbar, using Sent/PostMessage.

formicant
Posts: 9
Joined: 21 Jan 2018, 21:06

Re: [CLASS] Lyt - Keyboard layout (language) operation

Post by formicant » 30 Jan 2018, 13:03

Great! Thanks.
(Please note that the method names in the examples differ from the ones in the class. E.g. GetLng ~ GetLocaleName.)

BTW, what is the best way to monitor the layout changing?

burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: [CLASS] Lyt - Keyboard layout (language) operation

Post by burque505 » 30 Jan 2018, 19:16

Hi stealzy, thanks, I like it.
I changed the example above as shown (or hidden) :) below so it would work (thanks formicant!).
Spoiler
Lyt.PNG
Lyt.PNG (21.69 KiB) Viewed 8412 times

User avatar
Drugwash
Posts: 850
Joined: 29 May 2014, 21:07
Location: Ploieşti, Romania
Contact:

Re: [CLASS] Lyt - Keyboard layout (language) operation

Post by Drugwash » 31 Jan 2018, 04:27

The info about console language is useful, thank you.

There is a gotcha in the code though: the script assumes the default system shell is Explorer, which may not always be the case (there were/are replacements such as BlackBox/BBLean etc). In such case it may be better to use GetShellWindow(), GetProgmanWindow(), maybe even GetDesktopWindow() - all in user32.dll.

I see each KLID is being retrieved by temporarily activating the respective layout, which even if very brief may interfere with user's typing or automation. The alternative would be to parse the Preload and Substitutes lists in registry, matching against the HKLs (and there's a trick there too) to get to the real KLIDs.

@burque505: There's a slight mistake in your code too: it should be LayoutName, not DisplayName. That will show the layout name after the language name. ;)

[EDIT] Oh shoot, just noticed one language/layout is completely missing from the list, the Swedish one:
LangLayoutList.png
LangLayoutList.png (26.46 KiB) Viewed 6957 times
[EDIT2] …and I know why: because you assumed five would be more than enough installed layouts, so you hardcoded that value instead of getting the real one. ;) Here's the correct function:

Code: Select all

	GetList() { ; List of system loaded layouts
		Static Layouts
		If IsObject(Layouts)
			Return Layouts
		Else {
			Size := DllCall("GetKeyboardLayoutList", "UInt", 0, "Ptr", 0) ; calling with both values zero will return the number of installed layouts
			VarSetCapacity(List, A_PtrSize*Size) ; and then you pass that value to buffer creation and second, real call to the API
			Size := DllCall("GetKeyboardLayoutList", "UInt", Size, "Str", List)
			Layouts := []
			Loop % Size {
				Layouts[A_Index] := {}
				Layouts[A_Index].hkl := NumGet(List, A_PtrSize*(A_Index - 1))
				Layouts[A_Index].LocName := this.GetLocaleName(, Layouts[A_Index].hkl)
				Layouts[A_Index].LocFullName := this.GetLocaleName(, Layouts[A_Index].hkl, true)
				Layouts[A_Index].LayoutName := this.GetLayoutName(, Layouts[A_Index].hkl)
				Layouts[A_Index].KLID := this.GetKLIDfromHKL(Layouts[A_Index].hkl)
			}
			Return Layouts
		}
	}
Part of my AHK work can be found here.

burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: [CLASS] Lyt - Keyboard layout (language) operation

Post by burque505 » 31 Jan 2018, 11:31

Thanks, @Drugwash! Here's how it looks on my system after your fixes:
LytDW.PNG
LytDW.PNG (21.46 KiB) Viewed 8363 times

User avatar
Drugwash
Posts: 850
Joined: 29 May 2014, 21:07
Location: Ploieşti, Romania
Contact:

Re: [CLASS] Lyt - Keyboard layout (language) operation

Post by Drugwash » 31 Jan 2018, 11:54

Yep, that is the correct output. :)

Now, since you use an x64 system (I see the long HKLs), could you please test the script below and see if it yields the same results (or better)? I don't have any x64 machines in the house.

Found a couple bugs, fixed below, and also cleaned up a bit. Edited twice since first posted. Please redownload, should work fine now in x64.

Code: Select all

#NoEnv
#SingleInstance force
#WinActivateForce
SetTitleMatchMode, 2
SetBatchLines, -1
SetWinDelay, -1
SetControlDelay, -1
SetKeyDelay, -1, -1
SetMouseDelay, -1
ListLines, Off
SetWorkingDir, %A_ScriptDir%
DetectHiddenWindows, On

currHKL := GetHKL()
;defLang := GetLocaleInfo(0x2, DllCall("GetUserDefaultLCID"))
defLang := GetLocaleInfo(0x2, DllCall("GetUserDefaultLangID"))
GetKeyboardLayoutName(KLID)
currLang := GetLocaleInfo(0x2, currHKL)
currLayout := GetLayoutDisplayName(KLID)
thr := RegisterCallback("GetKLLDetails", "F", 1)		; one parameter for thread function
if !hT := CreateThread(thr, thID, 0)						; null parameter
	MsgBox, Error creating thread.
return
;================================================================
check:
Sleep, 10		; make sure the thread exits before we execute next line so we get the thread exit code
if GetExitCodeThread(hT, res)
		if res=0x103	; STILL_ACTIVE
			if !CloseThread(hT)
				TerminateThread(hT)
if res
	{
	Gui, Margin, 2, 2
	Gui, Add, ListView, w720 R%res% Grid, HKL|KLID|ISO|Input language|Layout name (MUI)|Layout name
	Gui, Add, StatusBar,,
	SB_SetParts(60, 350)
	Loop, %res%
		LV_Add(""
				, HKL%A_Index%
				, KLID%A_Index%
				, sName%A_Index%
				, cName%A_Index%
				, lName%A_Index%
				, eName%A_Index%)
	Loop, 6
		LV_ModifyCol(A_Index, "AutoSize")
	LV_ModifyCol(3, "AutoSize Sort")
	SB_SetText("Layouts: " res, 1)
	SB_SetText("Current: " currLang " | " currLayout, 2)
	SB_SetText("Default language: " defLang, 3)
	Gui, Show,, Installed languages and layouts
	}
return
;================================================================
GuiClose:
ExitApp
;================================================================
GetKLLDetails(p:=0)
;================================================================
{
Global
if count := DllCall("GetKeyboardLayoutList", "UInt", 0, "Ptr", 0)
	{
	VarSetCapacity(hklbuf, count*A_PtrSize, 0)
	if count := DllCall("GetKeyboardLayoutList", "UInt", count, "UPtr", &hklbuf)
		{
		Loop, %count%
			{
			HKL := NumGet(hklbuf, A_PtrSize*(A_Index-1), "Ptr")
			HKL%A_Index% := Hex2Str(HKL, 16, true)
			if ActivateKeyboardLayout(HKL)
				if GetKeyboardLayoutName(KLID%A_Index%:="00000000")
					{
					cName%A_Index% := GetLocaleInfo(0x2, HKL)
					s1 := GetLocaleInfo(0x59, HKL), s2 := GetLocaleInfo(0x5A, HKL)
					sName%A_Index% := s1 "_" s2
					lName%A_Index% := GetLayoutDisplayName(KLID%A_Index%)
					eName%A_Index% := GetLayoutDisplayName(KLID%A_Index%, 0)
					}
			}
		VarSetCapacity(hklbuf, 0)
		SetTimer, check, -1
		return count
		}
	VarSetCapacity(hklbuf, 0)
	}
return 0
}
;================================================================
;		THREAD
;================================================================
CreateThread(st, ByRef tID, var=0, susp=0)
;================================================================
{
flg := susp ? 0x4 : 0
if !hT := DllCall("CreateThread"
	, "Ptr"		, 0		; lpThreadAttributes (Default security descriptor)
	, "UInt"	, 0		; dwStackSize (Default size)
	, "Ptr"		, st		; lpStartAddress
	, "Ptr"		, var		; lpParameter (handle to notification object)
	, "UInt"	, flg		; dwCreationFlags (CREATE_SUSPENDED=0x4)
	, "PtrP"		, tID		; lpThreadID
	, "Ptr")
	{
;	e := A_LastError, m := A_ThisFunc "()`nStart address=" st "`nParam=" var "`nName=" name
;	OutputDebug, % ErrorLevel := FormatMessage(m, e)
	return 0
	}
else return hT			; handle to the new thread
}
;================================================================
ResumeThread(hT)
;================================================================
{
return DllCall("ResumeThread", "Ptr", hT)
}
;================================================================
CloseThread(hT)
;================================================================
{
return DllCall("CloseHandle", "Ptr", hT)	; TRUE=success, ZERO=failure
}
;================================================================
TerminateThread(ByRef hT)
;================================================================
{
if DllCall("TerminateThread", "Ptr", hT, "UInt", 0)	; dwExitCode=0
	return DllCall("CloseHandle", "Ptr", hT)				; TRUE=success, ZERO=failure
}
;================================================================
GetExitCodeThread(hT, ByRef eCode)
;================================================================
{
return DllCall("GetExitCodeThread", "Ptr", hT, "PtrP", eCode)
}
;================================================================
;		LANGUAGE and LAYOUT
;================================================================
GetLocaleInfo(loc, hkl:=0)
;================================================================
{
; LOCALE_SLANGUAGE=0x2, LOCALE_SISO639LANGNAME=0x59, LOCALE_SISO3166CTRYNAME=0x5A
Static A_CharSize := A_IsUnicode ? 2 : 1
hkl := hkl ? hkl : GetHKL(), lcid := hkl & 0x0000FFFF
if sz := DllCall("GetLocaleInfo"
	, "UInt", lcid
	, "UInt", loc
	, "Ptr", 0
	, "UInt", 0)
	{
	VarSetCapacity(strg, sz*A_CharSize, 0)
	DllCall("GetLocaleInfo"
		, "UInt", lcid
		, "UInt", loc
		, "Str", strg
		, "UInt", sz)
	}
else strg := "Error " A_LastError " LCID: " lcid
Return strg
}
;================================================================
SHLoadIndirectString(in)	; uses WStr for both in and out
;================================================================
{
VarSetCapacity(out, 2*(sz:=256), 0)
DllCall("shlwapi\SHLoadIndirectString", "Str", in, "Str", out, "UInt", sz, "Ptr", 0)
return out
}
;================================================================
GetLayoutDisplayName(subkey, usemui := 1)
;================================================================
{
Static key := "SYSTEM\CurrentControlSet\Control\Keyboard Layouts"
RegRead, mui, HKLM, %key%\%subkey%, Layout Display Name
if (!mui OR !usemui)
	RegRead, Dname, HKLM, %key%\%subkey%, Layout Text
else
	Dname := SHLoadIndirectString(mui)
Return Dname
}
;================================================================
GetKeyboardLayoutName(ByRef KLID)
;================================================================
{
return DllCall("GetKeyboardLayoutName", "Str", KLID:="00000000")
}
;================================================================
ActivateKeyboardLayout(HKL, flags:=0)	; 0x100=KLF_SETFORPROCESS
;================================================================
{
return DllCall("ActivateKeyboardLayout", "Ptr", HKL, "UInt", flags)
}
;================================================================
GetHKL(winT:="A")
;================================================================
{
if winT is integer
	hwnd := winT
else hwnd := WinExist(winT)
r := DllCall("GetKeyboardLayout"
	, "UInt"	, DllCall("GetWindowThreadProcessId"
		, "Ptr"	, hwnd
		, "Ptr"	, 0)
	, "UPtr")
Return Hex2Str(r, 8, true)
}
;================================================================
Hex2Str(val, len, x:=false)
;================================================================
{
SetFormat, IntegerFast, D
VarSetCapacity(out, len*2, 32)
DllCall("msvcrt\sprintf", "AStr", out, "AStr", "%0" len "I64X", "UInt64", val, "CDecl")
Return x ? "0x" out : out
}
Last edited by Drugwash on 01 Feb 2018, 08:52, edited 1 time in total.
Part of my AHK work can be found here.

burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: [CLASS] Lyt - Keyboard layout (language) operation

Post by burque505 » 31 Jan 2018, 18:45

Here's what showed up for the same 7 keyboards as above.
(Having trouble getting the forum to display images at the moment ...)
LytDW2.png
LytDW2.png (40.93 KiB) Viewed 6957 times

User avatar
Drugwash
Posts: 850
Joined: 29 May 2014, 21:07
Location: Ploieşti, Romania
Contact:

Re: [CLASS] Lyt - Keyboard layout (language) operation

Post by Drugwash » 01 Feb 2018, 03:14

Dang, I desperately need an x64 machine for testing! :(
Thank you, burque. It seems to have trouble with those long negative pointers. Could you please try what's suggested above in the striked out text, that is replacing 2*A_PtrSize with 8?
But first, are you sure you redownloaded the code above? I modified it a bit after initial posting, maybe you got the old version.

Regarding images, my understanding is that images over a certain width are being attached as files rather than being rendered inline, to avoid board misalignment.

[EDIT] Code above was modified twice, should work correctly in x64 now without any modification whatsoever.
Last edited by Drugwash on 01 Feb 2018, 09:46, edited 1 time in total.
Part of my AHK work can be found here.

burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: [CLASS] Lyt - Keyboard layout (language) operation

Post by burque505 » 01 Feb 2018, 09:06

Here's some results with 2*A_PtrSize, 8, and then 16.
LytDW2.png
LytDW2.png (27.43 KiB) Viewed 8290 times
LytDW_8.png
LytDW_8.png (25.74 KiB) Viewed 8290 times
LytDW_16.png
LytDW_16.png (23.6 KiB) Viewed 8290 times

User avatar
Drugwash
Posts: 850
Joined: 29 May 2014, 21:07
Location: Ploieşti, Romania
Contact:

Re: [CLASS] Lyt - Keyboard layout (language) operation

Post by Drugwash » 01 Feb 2018, 10:01

Latest version should work fine without any modification. We should stop here until I polish it a little and start a new topic, then a moderator will hopefully move relevant posts there to remove this hijacking.
Part of my AHK work can be found here.

burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: [CLASS] Lyt - Keyboard layout (language) operation

Post by burque505 » 01 Feb 2018, 11:58

Works! :bravo:

stealzy
Posts: 91
Joined: 01 Nov 2015, 13:43

Re: [CLASS] Lyt - Keyboard layout (language) operation

Post by stealzy » 06 Feb 2018, 05:35

Hi all!
@formicant, you're right, thanks, fix all my codeboxes :facepalm:.
I think you need hook on window message to monitor the layout changing. Also, hook on window activate and creating, such every window has it's own layout.

@Drugwash, thanks for fix my hardcode :thumbup:! Update top post just now, btw feel free to modify it, i go to the forum at least once a week.
GetKLIDfromHKL() works such because WinAPI GetKeyboardLayoutName works this way. I don't like it, but it work fast (I checked it with A_TickCount) and right way.
I test it on my x64 OS with both AutoHotkeyA32.exe and AutoHotkeyU64.exe.
Drugwash wrote:It seems to have trouble with those long negative pointers
Pointer is not negative, just 8-bytes against 4, but there is nothing to worry unless you convert it to decimal view wrong way.
GetKeyboardLayout() always return full size HKL for your system, but in PostMessage you must use 4-byte HKL if AutoHotkey process is 32-byte.
Dvorak on OSx64 0xfffffffff0020409 -> 0xf0020409 Dvorak on OSx86. It's already works fine with pointers, so don't worry ;).

@Drugwash, look throw your code, DllCall("GetUserDefaultLangID") != system default layout :HeHe:.
It is system locale (time format, etc). Try change default layout in lang bar if you want proof.
Actually system default layout KLID written in registry: HKCU\Keyboard Layout\Preload\1.
Don't know why Hex2Str() look this way, maybe you find usefull this function:

Code: Select all

hex(n) {
	Return Format("{:#0" 2 + 2*A_PtrSize "x}", n)
}

User avatar
Drugwash
Posts: 850
Joined: 29 May 2014, 21:07
Location: Ploieşti, Romania
Contact:

Re: [CLASS] Lyt - Keyboard layout (language) operation

Post by Drugwash » 06 Feb 2018, 11:01

Yeah, I managed to fix a few things since posting that code above, the offline version now correctly detects Default User Layout and System User Layout, but it wasn't easy to find out the proper way. At least I think it's the proper way, but it's possible a a user with limited rights may not be able to retrieve System Default Layout. Dunno, haven't checked - I always give admin rights to users on my machines. Someday I'll post the code in a new topic, maybe someone will offer feedback.

HKLs seem to be 8 bytes even on XP, I found that out during tests. Actually, truncating them to 4 bytes will yield the wrong result when they're in the form of 0xFFFFffffXXXXYYYY.

Hex2Str() is just for prettifying the display but it erroneously interfered with the HKLs in GetHKL(). It allows for some fine customization of the output. The Format() function is so weird I can never understand it, besides I prefer basic syntax as one day I may go back to my 98SE machine after I fix it and don't wanna get bad habits in the mean time. :)
Part of my AHK work can be found here.

Post Reply

Return to “Scripts and Functions (v1)”