AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Keyboard layout switcher

 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
wOxxOm



Joined: 09 Feb 2006
Posts: 319

PostPosted: Fri Dec 28, 2007 12:52 am    Post subject: Keyboard layout switcher Reply with quote

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
View user's profile Send private message Send e-mail Visit poster's website
sashabe



Joined: 09 Oct 2006
Posts: 14

PostPosted: Sat Dec 29, 2007 7:08 pm    Post subject: Reply with quote

Thanks!!! Спасибо))) Бесценная вещь)
Back to top
View user's profile Send private message
wOxxOm



Joined: 09 Feb 2006
Posts: 319

PostPosted: Mon Dec 31, 2007 4:57 am    Post subject: Reply with quote

Ооо наши везде!

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
View user's profile Send private message Send e-mail Visit poster's website
JDN



Joined: 24 Mar 2004
Posts: 126

PostPosted: Mon Dec 31, 2007 5:28 am    Post subject: Reply with quote

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
View user's profile Send private message
wOxxOm



Joined: 09 Feb 2006
Posts: 319

PostPosted: Mon Dec 31, 2007 5:30 am    Post subject: Reply with quote

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
View user's profile Send private message Send e-mail Visit poster's website
JDN



Joined: 24 Mar 2004
Posts: 126

PostPosted: Mon Dec 31, 2007 5:31 am    Post subject: Reply with quote

OK. Thanks.
Back to top
View user's profile Send private message
sashabe



Joined: 09 Oct 2006
Posts: 14

PostPosted: Mon Dec 31, 2007 1:03 pm    Post subject: Reply with quote

А нельзя ли присобачить к нему возможность транслитерации? Я пока не разобрался в скрипте...
Back to top
View user's profile Send private message
rogal



Joined: 23 Dec 2007
Posts: 51
Location: Austin

PostPosted: Mon Dec 31, 2007 7:52 pm    Post subject: Reply with quote

Great script. Thank you a lot!
Back to top
View user's profile Send private message
wOxxOm



Joined: 09 Feb 2006
Posts: 319

PostPosted: Mon Dec 31, 2007 11:40 pm    Post subject: Reply with quote

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
View user's profile Send private message Send e-mail Visit poster's website
Lobotomie
Guest





PostPosted: Thu May 22, 2008 5:35 pm    Post subject: Thank you ! Reply with quote

Thank you, this is really useful for me ! Very Happy

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 ? Embarassed
Back to top
Lobotomie
Guest





PostPosted: Thu May 22, 2008 6:52 pm    Post subject: .. I found ! Reply with quote

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
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Page 1 of 1

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group