Переключатель раскладки клавиатуры по принципу OS X

Я достаточно давно пользуюсь OS X и привык к его удобной системе переключения языков. Отличие от переключения в Windows состоит в том, что на маке переключение языков происходит между двумя последними использовавшимися. До тех пор, пока в Windows два языка — это не создает проблем, но лично я знаю людей, у которых 4 языка это норма и для них переключение языков доставляет некоторые неудобства из-за чего выбирать нужный язык приходится кликом мышки, а не комбинацией кнопок. И вот так, после очередного удаления/установки третьего языка, было решено написать простой переключатель раскладки клавиатуры для себя, а заодно получить полезный опыт.

Исходники программы доступны по адресу https://bitbucket.org/Ezbar/languageswitcher/overview. Для тех кто хочет попробовать программу и не заниматься компиляцией найдут бинарник в разделе Downloads.

Какие задачи пришлось решать:
 — перехват нажатий комбинации Win + Пробел
 — смена раскладки для активной программы
 — пропадание фокуса активной программы, после смены раскладки

Рассмотрим каждый случай отдельно. Для перехвата нажатий мы будем использовать функции API и установим хук для WH_KEYBOARD_LL. Значение констант и всю обвязку вызова хука можно посмотреть в коде.

Функция проверки нажатия комбинации для переключения языка
static IntPtr IgnoreWin_Space(int nCode, IntPtr wParam, IntPtr lParam)
        {
            Boolean spacePressed = false;
            var keyInfo = (KbHookParam)Marshal.PtrToStructure(lParam, typeof(KbHookParam));

            if (nCode == HC_ACTION)
            {                
                if ((int)wParam == WM_KEYDOWN)
                {
                    if (keyInfo.VkCode == (int)Keys.Space)
                    {
                        spacePressed = true;                        
                        kSpace = true;
                    }
                    else
                        kSpace = false;

                    // нажат одновременно левый виндовс
                    if (GetAsyncKeyState(Keys.LWin) < 0)
                        kWin = true;
                    else
                    {
                        kWin = false;
                    }

                    if (kWin && kSpace)
                    {                        
                        if (spacePressed)
                        {
                            Bar.SetLanguage("");
                            Bar.Show(); // сбивает фокус, пофиксим в конструкторе
                            return (IntPtr)1; //just ignore the key press
                        }
                    }
                }
            }
            if ((int)wParam == WM_KEYUP)
            {
                if (keyInfo.VkCode == (int)Keys.LWin)
                {                    
                    kWin = false;
                    Bar.DoHide();
                    string HEX = Bar.getHex();
                    uint WM_INPUTLANGCHANGEREQUEST = 0x0050;
                    uint KLF_ACTIVATE = 1;
                    PostMessage(GetForegroundWindow(), WM_INPUTLANGCHANGEREQUEST, IntPtr.Zero, LoadKeyboardLayout(HEX, KLF_ACTIVATE));
                }                
            }

            return CallNextHookEx(HookHandle, nCode, wParam, lParam);
        }       

Смена раскладки для активной программы так же производится через вызов API функцией PostMessage. Для этого мы получаем дискриптор активного приложения и отправляем ему сообщение для смены раскладки. Шестнадцатеричный код для кодировки языка мы получаем из функции ответственной за получение всех установленных языков в систему и их порядок переключения.

Смена раскладки для активной программы
string HEX = Bar.getHex();
uint WM_INPUTLANGCHANGEREQUEST = 0x0050;
uint KLF_ACTIVATE = 1;
PostMessage(GetForegroundWindow(), WM_INPUTLANGCHANGEREQUEST, IntPtr.Zero, LoadKeyboardLayout(HEX, KLF_ACTIVATE));

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

Код для конструктора
// Show a Form without stealing focus
protected override bool ShowWithoutActivation
{
     get { return true; }
}

В итоге я получил хороший опыт и маленькую, но полезную программу, которая облегчает жизнь, если у вас установлено больше двух ракладок клавиатуры. Буду признателен за любые комментарии и советы и прошу прощение за допущенные мною орфографические ошибки, а так же за возможно сумбурное изложение мыслей. Автор самоучка и, возможно, некоторые вещи называет не своими именами. Всем спасибо за внимание. Надеюсь, публикация будет кому-то полезной, например у кого на одном компьютере живет OSX+Windows или кто просто изучает программирование.

Комментарии (2)

  • 25 августа 2016 в 08:05

    0

    MacSwitch.exe не является приложением Win32.

    Это только у меня?

  • 25 августа 2016 в 08:32

    0

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

    Не знаю, как вы пользуетесь тремя и более языками, но способ OS X мне кажется как раз неудобным. У меня, например, три активных языка, и в то короткое время, когда я пользовался маком, весьма раздражало, что я не мог произвольно переключать между языками простым сочетанием клавиш — нужно смотреть на экран и выбирать язык (если он не в списке последних двух).

    В Windows сочетание типа alt+shift переключает языки по кругу, а ещё можно задать конкретное сочетание для конкретного языка, например, LeftAlt+Shift+1.

© Habrahabr.ru