Lazarus

Programming => General => Topic started by: CM630 on September 28, 2018, 08:26:04 am

Title: [SOLVED] How to detect keyboard layout?
Post by: CM630 on September 28, 2018, 08:26:04 am
I want to make ScrollLock to be On when a kirrilic layout is activated and Off when latin is activated. So I wonder how to „read‟ which layout is selected?
Windows only solution would be enough for me, but it wold be better if it is applicable for Linux.
Title: Re: How to detect keyboard layout?
Post by: ASerge on September 28, 2018, 04:02:39 pm
I want to make ScrollLock to be On when a kirrilic layout is activated and Off when latin is activated.
Only in your app?
Title: Re: How to detect keyboard layout?
Post by: CM630 on November 21, 2018, 11:03:47 am
No, I want it to behave this way in every app.
Title: Re: How to detect keyboard layout?
Post by: ASerge on November 21, 2018, 04:46:53 pm
No, I want it to behave this way in every app.
To determine: periodically by timer, get the foreground window, its thread ID, and use GetKeyboardLayout.
For setup use SendInput with simulated pressing the Scrolllock.
Title: Re: How to detect keyboard layout?
Post by: skalogryz on November 21, 2018, 05:07:47 pm
To determine: periodically by timer, get the foreground window, its thread ID, and use GetKeyboardLayout.
For setup use SendInput with simulated pressing the Scrolllock.
tracking WM_INPUTLANGCHANGE (https://docs.microsoft.com/en-us/windows/desktop/winmsg/wm-inputlangchange) could be a better approach for Windows, rather than timer.

...actually no. It's only being sent to the topmost window.
Title: Re: How to detect keyboard layout?
Post by: balazsszekely on November 21, 2018, 06:27:16 pm
Try @Serge suggestion(not tested):
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Timer1Timer(Sender: TObject);
  2. var
  3.   ActiveWindow: HWND;
  4.   ActiveThreadID: DWord;
  5.   KeyBoardLayOut: HKL;
  6.   Lang: array[0..2] of Char;
  7.   KeyState : TKeyBoardState;
  8.   NeedToChange: Boolean;
  9. begin
  10.   ActiveWindow := GetForegroundWindow;
  11.   ActiveThreadID := GetWindowThreadProcessId(ActiveWindow, nil);
  12.   KeyBoardLayOut := GetKeyboardLayout(ActiveThreadID);
  13.   GetKeyboardstate(KeyState);
  14.   GetLocaleInfo(LoWord(KeyBoardLayOut), LOCALE_SENGLANGUAGE, Lang, 2);
  15.   if UpperCase(Lang) = 'EN' then
  16.     NeedToChange := KeyState[VK_SCROLL] = 0
  17.   else
  18.     NeedToChange := KeyState[VK_SCROLL] = 1;
  19.   if NeedToChange then
  20.   begin
  21.     Keybd_Event(VK_SCROLL, 45, KEYEVENTF_EXTENDEDKEY, 0);
  22.     Keybd_Event(VK_SCROLL, 45, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP, 0);
  23.   end;
  24. end;  


PS: Timer interval is 1000. Don't forget to change "EN" to whatever language you like. Now the scroll lock will be switched on for English. Windows only...
Title: Re: How to detect keyboard layout?
Post by: sash on November 21, 2018, 09:48:28 pm
I want to make ScrollLock to be On .... but it wold be better if it is applicable for Linux.

If you're about LED indicator, on my Linux (XFCE, although it is configurable in xorg) this works right out of the box: whenever I have Cyrillic layout on, I also have ScrollLock LED on.
Title: Re: How to detect keyboard layout?
Post by: CM630 on November 22, 2018, 12:14:02 pm
...Don't forget to change "EN" to whatever language you like. Now the scroll lock will be switched on for English. Windows only...
I added Windows in Uses.
And as you wrote, I changed if UpperCase(Lang) = 'EN' then to if LeftStr(UpperCase(Lang),2) = 'EN' then
Not Scroll Lock flickers when English layout is selected. I have not debugged, but flickering seems even better ;)

Edit: It dos not flicker when the Lazarus application itself is shown.
Title: Re: How to detect keyboard layout?
Post by: CM630 on November 28, 2018, 12:00:03 pm
Thanks to everyone!I have marked this is solved since I got an answer „How to detect keyboard layout‟.
Still I cannot figure out how to set CapsLock on and off, but I will create a separate thread for that if necessary.
Anyway a GetKeyboardState(KeyState);  seems to be missing in @GetMem's code.
Title: Re: [SOLVED] How to detect keyboard layout?
Post by: balazsszekely on November 28, 2018, 12:03:13 pm
Anyway a GetKeyboardState(KeyState);  seems to be missing in @GetMem's code.
Look at line 13.  :)


Still I cannot figure out how to set CapsLock on and off
Same way you did with Scroll Lock:
Code: Pascal  [Select][+][-]
  1. uses windows;
  2.  
  3. procedure TForm1.Button1Click(Sender: TObject);
  4. begin
  5.   Keybd_Event(VK_CAPITAL, 45, KEYEVENTF_EXTENDEDKEY, 0);
  6.   Keybd_Event(VK_CAPITAL, 45, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP, 0);
  7. end;
Title: Re: [SOLVED] How to detect keyboard layout?
Post by: Guva on November 28, 2018, 02:11:52 pm
for linux https://github.com/GuvaCode/LedKey
Title: Re: [SOLVED] How to detect keyboard layout?
Post by: CM630 on November 30, 2018, 10:02:27 am

Still I cannot figure out how to set CapsLock on and off

Same way you did with Scroll Lock:
Code: Pascal  [Select][+][-]
  1. uses windows;
  2.  
  3.  
  4. procedure TForm1.Button1Click(Sender: TObject);
  5. begin
  6.   Keybd_Event(VK_CAPITAL, 45, KEYEVENTF_EXTENDEDKEY, 0);
  7.   Keybd_Event(VK_CAPITAL, 45, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP, 0);
  8. end;


This code toggles CapsLock.
So I need to know current state of CapsLock, in order to decide if I should toggle it.

But, GetKeyboardState(KeyState);  returns 4 values: 0;1; 128 or 129.
0 = Off
1 = On
128 = Scroll lock is pressed. It was ON before being pressed.
129 = Scroll lock is pressed. It was OFF before being pressed.

But
Code: Pascal  [Select][+][-]
  1.   Keybd_Event(VK_CAPITAL, 45, KEYEVENTF_EXTENDEDKEY, 0);
  2.   Keybd_Event(VK_CAPITAL, 45, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP, 0);

leaves KeyState in 128 or 129, still I have not found how 128 or 129 are set, but KeyState might be both 128 and 129 in ON state (and vice versa).


Thanks, @indigo80, but I still do not know how to detect keyboard layout in Linux. And currently, Windows is what bothers me, Linux has support for what I want (as @Sash wrote).

Title: Re: [SOLVED] How to detect keyboard layout?
Post by: balazsszekely on November 30, 2018, 10:24:49 pm
@CM630
Quote
This code toggles CapsLock.
So I need to know current state of CapsLock, in order to decide if I should toggle it.

But, GetKeyboardState(KeyState);  returns 4 values: 0;1; 128 or 129.
0 = Off
1 = On
128 = Scroll lock is pressed. It was ON before being pressed.
129 = Scroll lock is pressed. It was OFF before being pressed.
I'm not following you, what are you saying is not making sense. Attached project works for you or not?
Title: Re: [SOLVED] How to detect keyboard layout?
Post by: CM630 on December 04, 2018, 04:51:34 pm
I have added a couple of lines and a label to your code.
Leftmost bit (bit 7) in KeyState seems to indicate if key is pressed.
You can try your own app- when key is pressed indicator on the keyboard and indicator on the app mismatch in one of the direction.


When application itself is working- everything is ok, but when ScrollLock is toggled outside it (another app is focused)- bit 7 starts to make troubles.
I will try to ignore bit 7.
Title: Re: [SOLVED] How to detect keyboard layout?
Post by: balazsszekely on December 04, 2018, 05:54:14 pm
One more try...
Title: Re: [SOLVED] How to detect keyboard layout?
Post by: CM630 on December 06, 2018, 12:50:30 pm
Here is my code. It works fine as long as I do not focus another window. Then it starts acting oddly.
When I change layouts in another windows, indication in Label1 stays unchanged (for example 128). When I mouse mouse over it, it changes... sometimes.
I tried to change KetKeyboardState with GetKeyState- still not working.

I change layouts with alt+shift. I also tried changing with the mouse- so it is not an issue of Alt+Shift modifying something.

Also, there is a huge bug (possibly labelled as a „feature‟) in Windows- it does not remember key layout globally, but separately for each window.
Maybe that adds to the keyboard detection issue.

But it seems to me that GetKeyboardState does not work globally, but only for the focuses/active app.
Title: Re: [SOLVED] How to detect keyboard layout?
Post by: balazsszekely on December 06, 2018, 05:38:17 pm
OK, the issue was far more complex then I initially thought. To correctly get the keyboard layout, we must attach the input processing mechanism of our main thread to the thread that gets keyboard input, usually the one belonging to foreground window. Please test attached project, as far as I can tell it's working correctly now. If the input language differ from English, the "Scroll Lock" is activated(ON). I commented the code as much as possible:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.tmWaitTimer(Sender: TObject);
  2. var
  3.   ActiveWindow: HWND;
  4.   ActiveThreadID: DWord;
  5.   KeyBoardLayOut: HKL;
  6.   Lang: array[0..2] of Char;
  7.   KeyState: Integer;
  8.   LangStr: String;
  9. begin
  10.   //turn on/off our "leds", similar to the keyboard leds for Num Lock, Caps Lock, Scroll Lock
  11.   im.GetBitmap(GetState(VK_NUMLOCK), imNumLock.Picture.Bitmap);
  12.   im.GetBitmap(GetState(VK_CAPITAL), imCapsLock.Picture.Bitmap);
  13.   im.GetBitmap(GetState(VK_SCROLL), imScrollLock.Picture.Bitmap);
  14.  
  15.   //get foreground window, the window attached to the calling thread that gets input
  16.   ActiveWindow := GetForegroundWindow;
  17.   //get active thread ID
  18.   ActiveThreadID := GetWindowThreadProcessId(ActiveWindow, nil);
  19.   if (ActiveThreadID <> OldActiveThreadID) and (ActiveThreadID <> GetCurrentThreadID) then
  20.   begin
  21.     //detach old
  22.     AttachThreadInput(GetCurrentThreadID, OldActiveThreadID, False);
  23.     //attach thread input, for system processes this is not possible(ActiveThreadID --> 0)
  24.     if not AttachThreadInput(GetCurrentThreadID, ActiveThreadID, True) then
  25.       ActiveThreadID := 0;
  26.     //reset old threadid
  27.     OldActiveThreadID := ActiveThreadID;
  28.   end;
  29.  
  30.   if ActiveThreadID > 0 then
  31.   begin
  32.     //get kebyoard layout and language with the
  33.     KeyBoardLayOut := GetKeyboardLayout(ActiveThreadID);
  34.     GetLocaleInfo(LoWord(KeyBoardLayOut), LOCALE_SENGLANGUAGE, Lang, 2);
  35.     if Length(Lang) > 0 then
  36.     begin
  37.       SetString(LangStr, PChar(@Lang[0]), Length(Lang));
  38.       LangStr := LeftStr(UpperCase(LangStr), 2);
  39.     end;
  40.   end;
  41.   //get keystate
  42.   KeyState := GetKeyState(VK_SCROLL);
  43.  
  44.   //togle scroll lock according to the specification
  45.   if ((OldLangStr = 'EN') and (LangStr <> 'EN') and (KeyState = 0)) or
  46.       ((OldLangStr <> 'EN') and (LangStr= 'EN') and (KeyState = 1)) or
  47.        ((OldLangStr <> 'EN') and (LangStr <> 'EN') and (KeyState = 0)) or
  48.         ((OldLangStr = 'EN') and (LangStr = 'EN') and (KeyState = 1)) then
  49.            SetState(VK_SCROLL, 45);
  50.   //reset old langugage string
  51.   if OldLangStr <> LangStr then
  52.     OldLangStr := LangStr;
  53. end;
Title: Re: [SOLVED] How to detect keyboard layout?
Post by: CM630 on December 07, 2018, 11:23:08 am
Thanks, as far as I tried it it seems to work fine.
But all seems to be in vain, because... pressing a Lock key when Alt+Tab dialog is open closes the dialog !?! and makes Alt+Tab unusable  >:(
Title: Re: [SOLVED] How to detect keyboard layout?
Post by: balazsszekely on December 07, 2018, 12:09:24 pm
Thanks, as far as I tried it it seems to work fine.
But all seems to be in vain, because... pressing a Lock key when Alt+Tab dialog is open closes the dialog !?! and makes Alt+Tab unusable  >:(
On my win10 nothing happens when I press Alt +Tab combined with a Lock key. Everything works as it should. What is your OS win7 or xp?
Title: Re: [SOLVED] How to detect keyboard layout?
Post by: CM630 on December 07, 2018, 12:44:44 pm
It is Win7. Also- when I try to open an application with a popup menu from the taskbar (for example MS Word with more than 2 documents open in it)- the popup is gone when a lock key is toggled, so I have no way to show the desired application.
Title: Re: [SOLVED] How to detect keyboard layout?
Post by: balazsszekely on December 07, 2018, 05:41:23 pm
But all seems to be in vain, because... pressing a Lock key when Alt+Tab dialog is open closes the dialog !?! and makes Alt+Tab unusable  >:(
I can reproduce the issue with win7, but the same is true when the test application(the one I attached in my previous post) is not running. So I guess it's a standard behaviour in win7. Setting Scroll Lock in a remote application is an "abusive" move, personally I don't like it. By the way you can detect with a low level keyboard hook when Alt+Tab is pressed, then stop the timer in your application.
Title: Re: [SOLVED] How to detect keyboard layout?
Post by: CM630 on December 10, 2018, 12:19:54 pm

But all seems to be in vain, because... pressing a Lock key when Alt+Tab dialog is open closes the dialog !?! and makes Alt+Tab unusable  >:(

...but the same is true when the test application(the one I attached in my previous post) is not running...

Exactly.

So I guess it's a standard behaviour in win7. Setting Scroll Lock in a remote application is an "abusive" move, personally I don't like it. By the way you can detect with a low level keyboard hook when Alt+Tab is pressed, then stop the timer in your application.

Actually, the only right decision I could think of is:
   1. Disable Window's own keyboard switched.
   2. Assign alt+shift to a (Lazarus) app.

   3. When alt+shift is pressed, keyboard layout is changed. Either:
       3,1. all active apps shall be sought and their layout should be changed to the selected one.
       3,3. when app is changed/ timer ticks selected layout shall be applied to the active window/app.
   Solution 3,1 won't probably work, because it won't be useful for newly open app.
   4. When layout is changed by step 3, ScrollLock might me toggled.


I will leave it for some future times.
TinyPortal © 2005-2018