Перемикач розкладки клавіатури за принципом 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 focus stealing
protected override bool ShowWithoutActivation
{
get { return true; }
}



У підсумку я отримав хороший досвід і маленьку, але корисну програму, яка полегшує життя, якщо у вас встановлено більше двох ракладок клавіатури. Буду вдячний за будь-які коментарі та поради і прошу прощення за допущені мною орфографічні помилки, а так само за можливе сумбурний виклад думок. Автор самоучка і, можливо, деякі речі називає своїми іменами. Всім дякую за увагу. Сподіваюся, публікація буде комусь корисною, наприклад у кого на одному комп'ютері живе OSX+Windows або хто просто вивчає програмування.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.