1

Тема: AutoHotkey: Определение языка ввода, переключение раскладок клавиатуры

Иногда хочется заменить стандартные Ctrl+Shift или Alt+Shift для переключения языков на свои произвольные комбинации клавиш.
Следующий скрипт обеспечивает переключение на английский с помощью Win+1, на русский - с помощью Win+2. Можно при желании на Win+3 добавить третью раскладку (если такая есть) и т.д.:

#1:: SendMessage, 0x50,, 0x4090409,, A ; английский
#2:: SendMessage, 0x50,, 0x4190419,, A ; русский

Следующий скрипт обеспечивает переключение на английский с помощью двойного нажатия левого Ctrl, на русский - двойного нажатия правого Ctrl:

~LControl::
If LControl_Presses > 0
{
    LControl_Presses += 1
    Return
}
LControl_Presses = 1
SetTimer, KeyLControl, 300
Return

KeyLControl:
SetTimer, KeyLControl, Off
If LControl_Presses = 2
{
    SendMessage, 0x50,, 0x4090409,, A ; английский
}
LControl_Presses = 0
Return

~RControl::
If RControl_Presses > 0
{
    RControl_Presses += 1
    Return
}
RControl_Presses = 1
SetTimer, KeyRControl, 300
Return

KeyRControl:
SetTimer, KeyRControl, Off
If RControl_Presses = 2
{
    SendMessage, 0x50,, 0x4190419,, A ; русский
}
RControl_Presses = 0
Return
Предложения в русском языке начинаются с большой буквы и заканчиваются точкой.
В названии ветки всегда должен быть указан язык программирования или среда исполнения скрипта, если это возможно.

2

Re: AutoHotkey: Определение языка ввода, переключение раскладок клавиатуры

Циклическое переключение между двумя выбранными языками ввода по клавише Control:

~^Shift:: Exit
~+Ctrl:: Exit
Ctrl::
  SetFormat, Integer, H
  Locale1=0x4090409  ; Английский (американский).
  Locale2=0x4190419  ; Русский.
  WinGet, WinID,, A
  ThreadID:=DllCall("GetWindowThreadProcessId", "Int", WinID, "Int", "0")
  InputLocaleID:=DllCall("GetKeyboardLayout", "Int", ThreadID)
  if(InputLocaleID=Locale1)
    SendMessage, 0x50,, % Locale2,, A
  else if(InputLocaleID=Locale2)
    SendMessage, 0x50,, % Locale1,, A
Exit

Две горячих клавиши в начале скрипта нужны для предотвращения ложных срабатываний скрипта от нажатий стандартных клавиш Windows для переключения языка. При этом эти стандартные клавиши продолжают работать.
Чтобы определить идентификатор локали для произвольного установленного языка, используйте скрипт ниже (клавиша F11). При этом активное окно должно быть переключено на нужный язык:

F11::
  SetFormat, Integer, H
  WinGet, WinID,, A
  ThreadID:=DllCall("GetWindowThreadProcessId", "Int", WinID, "Int", "0")
  InputLocaleID:=DllCall("GetKeyboardLayout", "Int", ThreadID)
  MsgBox, %InputLocaleID%
Exit

Скрипты опубликовал YMP.

Предложения в русском языке начинаются с большой буквы и заканчиваются точкой.
В названии ветки всегда должен быть указан язык программирования или среда исполнения скрипта, если это возможно.

3

Re: AutoHotkey: Определение языка ввода, переключение раскладок клавиатуры

Переключение раскладки при удержании клавиши Right Win (по аналогии с клавишей Shift для изменения регистра символов). Работает с двумя языками. Автор скрипта - YMP.

RWin::
  PostMessage, 0x50, 2,,, A  ; Переключить язык.
  KeyWait, RWin              ; Ждать отпускания.
  PostMessage, 0x50, 2,,, A  ; Переключить.
Return
Предложения в русском языке начинаются с большой буквы и заканчиваются точкой.
В названии ветки всегда должен быть указан язык программирования или среда исполнения скрипта, если это возможно.

4

Re: AutoHotkey: Определение языка ввода, переключение раскладок клавиатуры

Облагороженный скрипт из поста №2, для определения идентификатора локали ввода.
Тогда я ещё был молодой и глупый.
Последний параметр в вызове GetKeyboardLayout указывает, что возвращаемое функцией число - беззнаковое (UInt). Без этого некоторые идентификаторы отображаются отрицательными числами.

F11::
  SetFormat, Integer, H
  WinGet, WinID,, A
  ThreadID:=DllCall("GetWindowThreadProcessId", UInt, WinID, UInt, 0)
  InputLocaleID:=DllCall("GetKeyboardLayout", UInt, ThreadID, UInt)
  MsgBox, %InputLocaleID%
Return

Этот код выводит список идентификаторов локалей ввода, между которыми возможно переключение. Вышеупомянутой проблемы здесь нет, т.к. функция NumGet по умолчанию трактует считываемые числа как беззнаковые целые (UInt).

F11::
  Count:=DllCall("GetKeyboardLayoutList", UInt, 0, UInt, 0)
  VarSetCapacity(Buf, Count*4)
  DllCall("GetKeyboardLayoutList", UInt, Count, UInt, &Buf)
  List:=""
  SetFormat, Integer, H
  Loop, % Count
    List.=NumGet(Buf, (A_Index-1)*4) . "`n"
  MsgBox, % List
Return

5 (изменено: architech, 2009-10-24 16:22:53)

Re: AutoHotkey: Определение языка ввода, переключение раскладок клавиатуры

Выкладываю код скрипта, умеющего делать следующее:

- Переключать раскладку клавиатуры, а именно: по нажатию клавиши Caps Lock - в другую раскладку, правой клавиши Alt - в английскую раскладку, правой клавиши Win - в русскую раскладку.

- Выводить tooltip с указанием текущей раскладки, расположенный чуть правее и чуть выше текстового курсора (правда, в некоторых программах tooltip появляется не совсем там (или совсем не там), где этого ожидает пользователь )

- Подавать звуковой сигнал при переключении раскладки (вполне возможно, что многих пользователей он будет только раздражать, так что рано или поздно придется придумать способ его отключения без ручного редактирования скрипта )

- Показывать текущую раскладку с помощью иконок в системном трее

- Обрабатывать нажатия клавиши Alt примерно так (с большой натяжкой ), как это делает Punto Switcher (правда, пока эта фича работает только в нескольких программах, специально прописанных в скрипте; новые придется добавлять вручную прямо в код скрипта)

; 2009-10-11  
; very simple Russian-English keyboard layout switcher by Lazytech 

; (my script is largely based on other developers' code and ideas;
; special thanks to people at http://forum.script-coding.com/)

; скрипт выполняется бесплатной программой AutoHotkey, которую
; можно скачать с оф. сайта: http://www.autohotkey.com/

; для нормальной работы скрипта следует поместить в одну и ту же папку 
; файл .ahk (собственно скрипт) и все файлы .ico (иконки для индикации
; раскладки в системном трее: "EN", "RU" и "??")  

; Caps Lock   -- переключение раскладки клавиатуры для активного окна
; правая Alt  -- включение английской раскладки для активного окна
; правая Win  -- включение русской раскладки для активного окна

; функции клавиши Caps Lock берут на себя сочетания клавиш  
; Ctrl + Caps Lock  и  Shift + Caps Lock

; для того, чтобы можно было заходить в меню программы, чье окно в данный момент 
; активно, с использованием только клавиатуры (по нажатию левой клавиши Alt,
; поскольку правая клавиша Alt уже задействована в данном скрипте для включения
; английской раскладки), придется прописать в коде скрипта заголовки
; соответствующих программ.

#SingleInstance

Loop
{
    Sleep, 300
    WinGet, NewHWND, ID, A
    if (no_detect_window <> -1) and (LastHWND = NewHWND) 
    {
        Continue
    }
    else
    { 
        LastHWND = %NewHWND%
        WinGet, NewPID, PID, A
        if (LastPID = NewPID)
        {
            Continue
        }
        else
        {
            LastPID = %NewPID%
            new_layout := check_en_ru()    
            if (new_layout = "RU")
            {
                sound_RU() ; если звук не нужен, можно закомментировать
            }    
            else if (new_layout = "EN")
            {
                sound_EN() ; если звук не нужен, можно закомментировать
            }
            Sleep, 300
        }
    }    
}

~LAlt::
WinGetTitle, AWinTitle, A
WinTitleLen := StrLen(AWinTitle)
; список заголовков окон можно пополнить вручную:
if (SubStr(AWinTitle, WinTitleLen - 9) = " - Блокнот") && (check_en_ru() = "EN")
{
    switch_en_ru("RU")
}
; список заголовков окон можно пополнить вручную:
else if (SubStr(AWinTitle, 1, 11) = "Akelpad - [") || (SubStr(AWinTitle, 1, 10) = "Aditor - [") || (SubStr(AWinTitle, 1, 10) = "Lister - [") && (check_en_ru() = "RU")
{
    switch_en_ru("EN") 
}
Return

sound_RU()
{
    SoundBeep, 200, 30 ; для полного выключения звука закомментируйте
}

sound_EN()
{
    SoundBeep, 5000, 30 ; для полного выключения звука закомментируйте
}

kbd_msg(text)
{
    no_detect_window := -1
    ToolTip, %text%, A_CaretX + 10, A_CaretY - 20
    SetTimer, KbdRemoveToolTip, -1000
    return
    
    KbdRemoveToolTip:
    ToolTip
    no_detect_window := 1
    return
}

check_en_ru_by_window_id(WinID)
{
  SetFormat, Integer, H
  Locale_En := 0x4090409  ; Английский (американский).
  Locale_Ru := 0x4190419  ; Русский.
  ThreadID:=DllCall("GetWindowThreadProcessId", "Int", WinID, "Int", "0")
  InputLocaleID:=DllCall("GetKeyboardLayout", "Int", ThreadID)

    if (store_layout <> InputLocaleID)
    {
      if(InputLocaleID = Locale_En)
        {
            Menu tray, icon, en2.ico
        }
      else if(InputLocaleID = Locale_Ru)
        {
            Menu tray, icon, ru2.ico
        }
        else
        {
            Menu tray, icon, __2.ico
        }
    }
    
    store_layout = InputLocaleID

  if(InputLocaleID = Locale_En)
    {
        return "EN"
    }
  else if(InputLocaleID = Locale_Ru)
    {
        return "RU"
    }
    else
    {
      return "??"
    }
}

check_en_ru()
{
  SetFormat, Integer, H
    WinGet, WinID,, A
    return check_en_ru_by_window_id(WinID)    
}

switch_en_ru(set_layout)
{
    old_layout := check_en_ru()

    if (old_layout = set_layout)
    {
        kbd_msg(set_layout)
        EXIT
    }

    if (old_layout = "RU")
    {
        set_layout := "EN"
    }
    else
    {
        set_layout := "RU"
    }
    
    if (set_layout = "RU")
    {
        SendMessage, 0x50,, % Locale_RU,, A
        sound_RU()
    }    
    else if (set_layout = "EN")
    {
        SendMessage, 0x50,, % Locale_EN,, A
        sound_EN()
    }

    new_layout := check_en_ru()    
    kbd_msg(new_layout)
}

; переключение активного окна в другую раскладку
+Capslock::Capslock 
Capslock:: 
switch_en_ru("")
Sleep, 300
return

kbd_en()
{
    ; переключение активного окна в английскую раскладку
    PostMessage, 0x50, 0, 0x4090409,, A
    kbd_msg("EN")    
}

kbd_ru()
{
    ; переключение активного окна в русскую раскладку
    PostMessage, 0x50, 0, 0x4190419,, A     
    kbd_msg("RU")    
}

; переключаем активное окно в английскую раскладку по нажатию клавиши:
RAlt:: switch_en_ru("EN") 

; переключаем активное окно в русскую раскладку по нажатию клавиши:
RWin:: switch_en_ru("RU")

Также выкладываю Zip-архив (1,6 Кбайт) с 3 иконками ("en2.ico", "ru2.ico", "__2.ico"). Художественный вкус у меня так себе, так что можете переделать иконки под свои запросы. Для этого достаточно изменить расширение файла на bmp, отредактировать картинку в любом графическом редакторе (хоть в MS Paint ) и поменять расширение файла обратно на ico

Кстати, так и не понял, почему AHK иногда ругается на следующую строку:

SetTimer, RemoveToolTip, Off

Закомментировал, пока всё работает

P.S. Поправил скрипт - teadrinker, благодарю за подсказку!
На всякий случай показываю ниже, что именно было исправлено.

Было:

kbd_msg(text)
{
    no_detect_window := -1
    ToolTip, %text%, A_CaretX + 10, A_CaretY - 20
    SetTimer, KbdRemoveToolTip, 1000
    return
    
    KbdRemoveToolTip:
    ; SetTimer, RemoveToolTip, Off
    ToolTip
    no_detect_window := 1
    return
}

Стало:

kbd_msg(text)
{
    no_detect_window := -1
    ToolTip, %text%, A_CaretX + 10, A_CaretY - 20
    SetTimer, KbdRemoveToolTip, -1000
    return
    
    KbdRemoveToolTip:
    ToolTip
    no_detect_window := 1
    return
}
Post's attachments

ico.zip 1.62 kb, 613 downloads since 2009-10-11 

You don't have the permssions to download the attachments of this post.

6 (изменено: teadrinker, 2009-10-24 14:54:00)

Re: AutoHotkey: Определение языка ввода, переключение раскладок клавиатуры

Идентификаторы локали для сообщения WM_INPUTLANGCHANGEREQUEST (0x50).

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

7 (изменено: teadrinker, 2018-08-13 18:15:08)

Re: AutoHotkey: Определение языка ввода, переключение раскладок клавиатуры

Определение языка раскладки активного окна:

F11:: MsgBox % GetInputLangName(GetInputLangID("A"))
 
GetInputLangID(window)  {
   if !hWnd := WinExist(window)
      return

   WinGetClass, winClass
   if (winClass != "ConsoleWindowClass") || (b := SubStr(A_OSVersion, 1, 2) = "10")  {
      if b  {
         WinGet, consolePID, PID
         childConhostPID := GetCmdChildConhostPID(consolePID)
         dhw_prev := A_DetectHiddenWindows
         DetectHiddenWindows, On
         hWnd := WinExist("ahk_pid " . childConhostPID)
         DetectHiddenWindows, % dhw_prev
      }
      threadId := DllCall("GetWindowThreadProcessId", Ptr, hWnd, UInt, 0)
      lyt := DllCall("GetKeyboardLayout", Ptr, threadId, UInt)
      langID := Format("{:#x}", lyt & 0x3FFF)
   }
   else  {
      WinGet, consolePID, PID
      DllCall("AttachConsole", Ptr, consolePID)
      VarSetCapacity(lyt, 16)
      DllCall("GetConsoleKeyboardLayoutName", Str, lyt)
      DllCall("FreeConsole")
      langID := "0x" . SubStr(lyt, -4)
   }
   return langID
}

GetCmdChildConhostPID(CmdPID)  {
   static TH32CS_SNAPPROCESS := 0x2, MAX_PATH := 260
   
   h := DllCall("CreateToolhelp32Snapshot", UInt, TH32CS_SNAPPROCESS, UInt, 0, Ptr)
   VarSetCapacity(PROCESSENTRY32, size := 4*7 + A_PtrSize*2 + (MAX_PATH << !!A_IsUnicode), 0)
   NumPut(size, PROCESSENTRY32, "UInt")
   res := DllCall("Process32First", Ptr, h, Ptr, &PROCESSENTRY32)
   while res  {
      parentPid := NumGet(PROCESSENTRY32, 4*4 + A_PtrSize*2, "UInt")
      if (parentPid = CmdPID)  {
         exeName := StrGet(&PROCESSENTRY32 + 4*7 + A_PtrSize*2, "CP0")
         if (exeName = "conhost.exe" && PID := NumGet(PROCESSENTRY32, 4*2, "UInt"))
            break
      }
      res := DllCall("Process32Next", Ptr, h, Ptr, &PROCESSENTRY32)
   }
   DllCall("CloseHandle", Ptr, h)
   Return PID
}
 
GetInputLangName(langId)  {
   static LOCALE_SENGLANGUAGE := 0x1001
   charCount := DllCall("GetLocaleInfo", UInt, langId, UInt, LOCALE_SENGLANGUAGE, UInt, 0, UInt, 0)
   VarSetCapacity(localeSig, size := charCount << !!A_IsUnicode, 0)
   DllCall("GetLocaleInfo", UInt, langId, UInt, LOCALE_SENGLANGUAGE, Str, localeSig, UInt, size)
   return localeSig
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder