AutoHotkey Community

It is currently May 26th, 2012, 1:51 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 24 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Keyboard layout switcher
PostPosted: December 28th, 2007, 12:52 am 
Offline

Joined: February 9th, 2006, 8:36 pm
Posts: 338
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 January 1st, 2008, 9:37 am, edited 7 times in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 29th, 2007, 7:08 pm 
Offline

Joined: October 9th, 2006, 9:27 am
Posts: 21
Thanks!!! Спасибо))) Бесценная вещь)


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 31st, 2007, 4:57 am 
Offline

Joined: February 9th, 2006, 8:36 pm
Posts: 338
Ооо наши везде!

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


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 31st, 2007, 5:28 am 
Offline

Joined: March 24th, 2004, 2:34 pm
Posts: 299
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 December 31st, 2007, 5:31 am, edited 1 time in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 31st, 2007, 5:30 am 
Offline

Joined: February 9th, 2006, 8:36 pm
Posts: 338
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.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 31st, 2007, 5:31 am 
Offline

Joined: March 24th, 2004, 2:34 pm
Posts: 299
OK. Thanks.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 31st, 2007, 1:03 pm 
Offline

Joined: October 9th, 2006, 9:27 am
Posts: 21
А нельзя ли присобачить к нему возможность транслитерации? Я пока не разобрался в скрипте...


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 31st, 2007, 7:52 pm 
Offline

Joined: December 23rd, 2007, 7:47 pm
Posts: 62
Location: Austin
Great script. Thank you a lot!


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 31st, 2007, 11:40 pm 
Offline

Joined: February 9th, 2006, 8:36 pm
Posts: 338
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


Report this post
Top
 Profile  
Reply with quote  
 Post subject: Thank you !
PostPosted: May 22nd, 2008, 5:35 pm 
Thank you, this is really useful for me ! :D

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 ? :oops:


Report this post
Top
  
Reply with quote  
 Post subject: .. I found !
PostPosted: May 22nd, 2008, 6:52 pm 
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


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: September 14th, 2008, 2:57 pm 
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


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: February 16th, 2009, 9:49 pm 
Offline

Joined: February 16th, 2009, 9:45 pm
Posts: 2
Does anyone know how one could obtain the actual Keyboard Layout Name? Here's my system config:

Image

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?


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 16th, 2009, 11:19 pm 
Offline

Joined: February 9th, 2006, 8:36 pm
Posts: 338
the code in the first post, the block right after this comment - ;show traytip


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 19th, 2009, 2:14 pm 
Offline

Joined: February 19th, 2009, 1:29 pm
Posts: 1
My dumb way of doing this translation+switching task: :roll:
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


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 24 posts ]  Go to page 1, 2  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: No registered users and 20 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group