 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
wOxxOm
Joined: 09 Feb 2006 Posts: 319
|
Posted: Fri Dec 28, 2007 12:52 am Post subject: Keyboard layout switcher |
|
|
here's a very basic example of keyboard layout (input language) switcher.
- operates on the active window (any window, not limited to AHK script's own!)
- switches all available keyboard layouts in cycle
- Displays tooltip for 0.5 second with the keyboard layout name and Country name
- The default switch key is a single Right Control
- it switches languages instantly without an awkward delay seen with Punto Switcher or other utilities (IMHO)
| Code: | $~RControl::LangSwitch(1)
$~RControl up::LangSwitch(2)
LangSwitch( iKeyDownUp=0 )
{
static tickLast
IfEqual,iKeyDownUp,1
{ tickLast=%A_TickCount%
return
}
IfEqual,iKeyDownUp,2
If( A_TickCount-tickLast>200 )
return
HKL:=DllCall("GetKeyboardLayout", "uint",GetThreadOfWindow(), "uint")
HKLnum:=DllCall("GetKeyboardLayoutList","uint",0,"uint",0)
VarSetCapacity( HKLlist, HKLnum*4, 0 )
DllCall("GetKeyboardLayoutList","uint",HKLnum,"uint",&HKLlist)
loop,%HKLnum%
{ if( NumGet( HKLlist, (A_Index-1)*4 ) = HKL )
{ HKL:=NumGet( HKLlist, mod(A_Index,HKLnum)*4 )
break
}
}
ControlGetFocus,ctl,A
SendMessage,0x50,0,HKL,%ctl%,A ;WM_INPUTLANGCHANGEREQUEST
;show traytip
LOCALE_SENGLANGUAGE=0x1001
LOCALE_SENGCOUNTRY=0x1002
VarSetCapacity( sKbd, 260, 0 )
VarSetCapacity( sCountry, 260, 0 )
DllCall("GetLocaleInfo","uint",HKL>>16,"uint",LOCALE_SENGLANGUAGE, "str",sKbd, "uint",260)
DllCall("GetLocaleInfo","uint",HKL & 0xFFFF,"uint",LOCALE_SENGCOUNTRY, "str",sCountry, "uint",260)
traytip,%sKbd%,%sCountry%
SetTimer,REMOVE_TOOLTIP,500 ;0.5 second
return
REMOVE_TOOLTIP:
SetTimer,REMOVE_TOOLTIP,off
traytip
return
}
;returns first thread for the <processID>
;sets optional <List> to pipe | separated thread list for the <processID>
GetProcessThreadOrList( processID, byRef list="" )
{
;THREADENTRY32 {
THREADENTRY32_dwSize=0 ; DWORD
THREADENTRY32_cntUsage = 4 ;DWORD
THREADENTRY32_th32ThreadID = 8 ;DWORD
THREADENTRY32_th32OwnerProcessID = 12 ;DWORD
THREADENTRY32_tpBasePri = 16 ;LONG
THREADENTRY32_tpDeltaPri = 20 ;LONG
THREADENTRY32_dwFlags = 24 ;DWORD
THREADENTRY32_SIZEOF = 28
TH32CS_SNAPTHREAD=4
hProcessSnap := DllCall("CreateToolhelp32Snapshot","uint",TH32CS_SNAPTHREAD, "uint",0)
ifEqual,hProcessSnap,-1, return
VarSetCapacity( thE, THREADENTRY32_SIZEOF, 0 )
NumPut( THREADENTRY32_SIZEOF, thE )
ret=-1
if( DllCall("Thread32First","uint",hProcessSnap, "uint",&thE ))
loop
{
if( NumGet( thE ) >= THREADENTRY32_th32OwnerProcessID + 4)
if( NumGet( thE, THREADENTRY32_th32OwnerProcessID ) = processID )
{ th := NumGet( thE, THREADENTRY32_th32ThreadID )
IfEqual,ret,-1
ret:=th
list .= th "|"
}
NumPut( THREADENTRY32_SIZEOF, thE )
if( DllCall("Thread32Next","uint",hProcessSnap, "uint",&thE )=0)
break
}
DllCall("CloseHandle","uint",hProcessSnap)
StringTrimRight,list,list,1
return ret
}
; Returns thread owning specified window handle
; default = Active window
GetThreadOfWindow( hWnd=0 )
{
IfEqual,hWnd,0
hWnd:=WinExist("A")
DllCall("GetWindowThreadProcessId", "uint",hWnd, "uintp",id)
GetProcessThreadOrList( id, threads )
IfEqual,threads,
return 0
CB:=RegisterCallback("GetThreadOfWindowCallBack","Fast")
lRet=0
lParam:=hWnd
loop,parse,threads,|
{ NumPut( hWnd, lParam )
DllCall("EnumThreadWindows", "uint",A_LoopField, "uint",CB, "uint",&lParam)
if( NumGet( lParam )=true )
{ lRet:=A_LoopField
break
}
}
DllCall("GlobalFree", "uint", CB)
return lRet
}
GetThreadOfWindowCallBack( hWnd, lParam )
{
IfNotEqual,hWnd,% NumGet( 0+lParam )
return true
NumPut( true, 0+lParam )
return 0
} |
and here's just a complementary simple converter (ghbdtn <> привет) of text typed under wrong keyboard layout.
- I use it because I don't like punto switcher, keyboard ninja, and other utilities I tried.
- It works for a selection of text, the default is Pause key.
- It uses a less compatible but much faster method of pasting the changed text instead of sending it character by character, so if there are problems I manually add the window class declaration under "decide compatibility of unicode clipboard" comment.
| Code: | $~Pause::RecodeTextENRU()
RecodeTextENRU()
{
StringCaseSense On
AutoTrim,Off
clipSave:=clipAnsi()
send ^{Insert}
sleep,50
dest=
text:=clipAnsi()
StringCaseSense,On
prevCharToEN=0
; АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ
RUtoEN=F<DULT:PBQRKVYJGHCNEA{WXIO}SM">Zf,dult;pbqrkvyjghcnea[wxio]sm'.z
RUtoSP1=хъжэбюХЪЖЭБЮ.,/"№;:?
RUtoSP2=[];',.{}:"<>/?|@#$^&
;" ABCDEFGHIJKLMNOPQRSTUVWXYZ
ENtoRU=ФИСВУАПРШОЛДЬТЩЗЙКЫЕГМЦЧНЯфисвуапршолдьтщзйкыегмцчня
loop,parse,text
{
destChar=%A_LoopField%
; check explicit (non punctuations) ranges
ifGreaterOrEqual,A_LoopField,А
prevCharToEN=1
else if A_LoopField between A and Z
prevCharToEN=0
else if A_LoopField between a and z
prevCharToEN=0
; to Russian
ifEqual,prevCharToEN,0
{ StringGetPos,i,RUtoEN,%A_LoopField%
ifEqual,ErrorLevel,0
Transform,destChar,chr,% i + 0xC0
else
{ StringGetPos,i,RUtoSP2,%A_LoopField%
ifEqual,ErrorLevel,0
StringMid,destChar,RUtoSP1,% i+1, 1
}
}
; to English because nothing changed
ifEqual,destChar,%A_LoopField%
{
StringGetPos,i,ENtoRU,%A_LoopField%
ifEqual,ErrorLevel,0
Transform,destChar,chr,% i + (i>=26 ? 71 : 65)
else ; check .,;':"[]{}
{ StringGetPos,i,RUtoSP1,%A_LoopField%
ifEqual,ErrorLevel,0,StringMid,destChar,RUtoSP2,% i+1, 1
}
ifNotEqual,destChar,%A_LoopField%
prevCharToEN=1
}
dest=%dest%%destChar%
}
; decide compatibility of unicode clipboard
WinGetClass,cls,A
if cls in TMsgEditor,wndclass_desked_gsk
{
ControlGetFocus,cls,A
ifInString,cls,TXTRichEdit
clipSetUnicode(dest)
else
Clipboard=%dest%
}
else
clipSetUnicode(dest)
sleep,50
send +{Insert}
sleep 50
clipSetUnicode(clipSave)
LangSwitch()
}
; read unicode clipboard into ansi string
clipAnsi()
{
StringLen,L,Clipboard
L:=(L+1)*4
transform,ca_Clip,unicode
varSetCapacity(ca_WideText,L,0)
varSetCapacity(ca_AnsiText,L,0)
; Convert UTF-8 to UTF-16. CP_UTF8=65001
if dllCall("MultiByteToWideChar",uint,65001, uint,0, str,ca_Clip
, uint,-1, str,ca_WideText, uint,L/2)
dllCall("WideCharToMultiByte",uint,0, uint,0, str,ca_WideText
, uint,-1, str,ca_AnsiText, uint,L/2, uint,0, uint,0)
; Convert UTF-16 to ANSI. CP_ACP=0
return ca_AnsiText
}
;--------------------------------------------------------------
; copy ansi string to clipboard in unicode mode
clipSetUnicode(cu_AnsiText)
{
StringLen,L,cu_AnsiText
L:=(L+1)*4
varSetCapacity(cu_WideText,L,0)
varSetCapacity(cu_UTFtext,L,0)
; ANSI to UTF-16. CP_ACP=0
if dllCall("MultiByteToWideChar",uint,0, uint,0, str,cu_AnsiText
, uint,-1, str,cu_WideText, uint,L/2)
dllCall("WideCharToMultiByte",uint,65001, uint,0, str,cu_WideText
, uint,-1, str,cu_UTFtext, uint,L/2, uint,0, uint,0)
; Convert UTF-16 to UTF-8. CP_UTF8=65001
transform,clipboard,unicode,%cu_UTFtext%
} |
Last edited by wOxxOm on Tue Jan 01, 2008 9:37 am; edited 7 times in total |
|
| Back to top |
|
 |
sashabe
Joined: 09 Oct 2006 Posts: 14
|
Posted: Sat Dec 29, 2007 7:08 pm Post subject: |
|
|
| Thanks!!! Спасибо))) Бесценная вещь) |
|
| Back to top |
|
 |
wOxxOm
Joined: 09 Feb 2006 Posts: 319
|
Posted: Mon Dec 31, 2007 4:57 am Post subject: |
|
|
Ооо наши везде!
IMPORTANT UPDATES:
- now the LangSwitch script also works in dialog windows
(it sends WM_INPUTLANGCHANGEREQUEST to the active control of the active window)
- clipAnsi and clipSetUnicode [used by RecodeTextENRU] now can handle any amount of text
previously the allocation was fixed 1000 bytes, now it's calculated based on clipboard buffer size
|
|
| Back to top |
|
 |
JDN
Joined: 24 Mar 2004 Posts: 126
|
Posted: Mon Dec 31, 2007 5:28 am Post subject: |
|
|
I wanted to try your script but I'm afraid I don't understand it. Can you explain in a sentence or two what it is supposed to accomplish? Or better yet, can you give me some instructions I could follow that would help me see the intended effect?
I'm afraid that I don't understand about keyboard layouts and switching languages or keyboard layouts. In order to see the effect of your script, should I have loaded one or more keyboard layouts before pressing and releasing the right control key?
I loaded your hotkey and started an editor application. Then I pressed and released the right control key. But I didn't notice anything happen. Can you explain if I should have seen something happen?
Last edited by JDN on Mon Dec 31, 2007 5:31 am; edited 1 time in total |
|
| Back to top |
|
 |
wOxxOm
Joined: 09 Feb 2006 Posts: 319
|
Posted: Mon Dec 31, 2007 5:30 am Post subject: |
|
|
| just the same thing that happens when you press Ctrl-Shift or Alt-Shift which is the default Windows keyboard input language switch hotkey. However this script provides more handy control, that's it. |
|
| Back to top |
|
 |
JDN
Joined: 24 Mar 2004 Posts: 126
|
Posted: Mon Dec 31, 2007 5:31 am Post subject: |
|
|
| OK. Thanks. |
|
| Back to top |
|
 |
sashabe
Joined: 09 Oct 2006 Posts: 14
|
Posted: Mon Dec 31, 2007 1:03 pm Post subject: |
|
|
| А нельзя ли присобачить к нему возможность транслитерации? Я пока не разобрался в скрипте... |
|
| Back to top |
|
 |
rogal
Joined: 23 Dec 2007 Posts: 51 Location: Austin
|
Posted: Mon Dec 31, 2007 7:52 pm Post subject: |
|
|
| Great script. Thank you a lot! |
|
| Back to top |
|
 |
wOxxOm
Joined: 09 Feb 2006 Posts: 319
|
Posted: Mon Dec 31, 2007 11:40 pm Post subject: |
|
|
UPDATES:
1. Filtering out of RControl+other keys
| Code: | $~RControl::LangSwitch(1)
$~RControl up::LangSwitch(2) |
Synopsis: In case you use RControl also as a modifier for arrow keys or other keys, the script previously switched language as well which isn't good. Now LangSwitch checks the time interval when hotkey was pressed and released. 200 ms is the default.
2. clipSetUnicode wrong length calculation, fixed [used by RecodeTextENRU]
| Code: | clipSetUnicode(cu_AnsiText)
{
StringLen,L,cu_AnsiText
;the full code is in first post |
|
|
| Back to top |
|
 |
Lobotomie Guest
|
Posted: Thu May 22, 2008 5:35 pm Post subject: Thank you ! |
|
|
Thank you, this is really useful for me !
I'd like to customize it, but I think I need help :
I want to switch to the second layout while RControl is down, then switch back to the first layout when RControl is released ...
Can you help me please ?  |
|
| Back to top |
|
 |
Lobotomie Guest
|
Posted: Thu May 22, 2008 6:52 pm Post subject: .. I found ! |
|
|
This will do it for me with Right Alt :
RAlt::SwitchLayout(4026598409) ; switch to US International (with dead keys)
RAlt up::SwitchLayout(67699721) ; switch back to US
SwitchLayout( layoutNumber )
{
ControlGetFocus,ctl,A
SendMessage,0x50,0,layoutNumber,%ctl%,A
}
Thanks anyway |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|