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
Goto page 1, 2  Next
 
Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
wOxxOm



Joined: 09 Feb 2006
Posts: 322

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: 19

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: 322

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: 258

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: 322

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: 258

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: 19

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: 62
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: 322

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
Cyberion
Guest





PostPosted: Sun Sep 14, 2008 2:57 pm    Post subject: Reply with quote

Congratulations wOxxOm! Very nice work.

Two things, though.

1. You won't see the nifty tooltips if you have tooltips disabled (through TweakUI, for instance).
2. What's the advantage of your tool over the built-in Windows switcher?

If you don't mind, I would like to use the part where you get the current keyboard layout. Looks pretty complicated, though (I'm not a very experienced coder).
What do I need it for? I'm using the Accents script to enter accentuated characters in one of my languages. Unfortunately, when I switch to Russian, 'Accents' messes up my Russian keyboard layout. That is why I want to add an If statement that would disable the script whenever my keyboard layout is set to anything than the language in which I need the accents.

I'll let you know how it goes.
Congrats again. Your second script is pure genious, by the way
Back to top
mihaibirsan



Joined: 16 Feb 2009
Posts: 2

PostPosted: Mon Feb 16, 2009 9:49 pm    Post subject: Reply with quote

Does anyone know how one could obtain the actual Keyboard Layout Name? Here's my system config:



The strings that I want to retrieve are "US" and "Romanian (Standard, cu sedile)". I can't find a way to do that, and I've been googling for quite a while now. Any hints?
Back to top
View user's profile Send private message
wOxxOm



Joined: 09 Feb 2006
Posts: 322

PostPosted: Mon Feb 16, 2009 11:19 pm    Post subject: Reply with quote

the code in the first post, the block right after this comment - ;show traytip
Back to top
View user's profile Send private message Send e-mail Visit poster's website
cargo



Joined: 19 Feb 2009
Posts: 1

PostPosted: Thu Feb 19, 2009 2:14 pm    Post subject: Reply with quote

My dumb way of doing this translation+switching task: Rolling Eyes
Code:
; Translates selected text RU<>EN, cycles input language
Pause::Translate() ; Hotkey - Pause button

Translate()
{
Eng=qwertyuiop[]asdfghjkl;'zxcvbnm,.QWERTYUIOP{}ASDFGHJKL:"ZXCVBNM<>
Rus=йцукенгшщзхъфывапролджэячсмитьбюЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ

Send ^{Ins} ; Copy selection to clipboard
ClipWait
r =
Loop, parse, clipboard
{
   p := InStr(Eng, A_LoopField, true)
   if p > 0
      r := r . SubStr(Rus, p, 1)
   else
   {
      p := InStr(Rus, A_LoopField, true)
      if p > 0
         r := r . SubStr(Eng, p, 1)
      else
         r := r . A_LoopField
   }
}
PostMessage, 0x50, 2, 0,, A ; Switch lang to next
SendInput {Del}%r% ; Delete selection, print translated text
}

Limitations:
- only RU<>EN translation
- there must be only two input languages configured, as they are cycled each time
Back to top
View user's profile Send private message
Display posts from previous:   
Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
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