@@ -21,8 +21,11 @@ internal static class Program
2121 private const int VK_CAPITAL = 0x14 ;
2222 private const int VK_SHIFT = 0x10 ;
2323 private const int VK_CONTROL = 0x11 ;
24+ private const int VK_LWIN = 0x5B ;
25+ private const int VK_RWIN = 0x5C ;
2426 private const int WM_INPUTLANGCHANGEREQUEST = 0x0050 ;
2527 private const int INPUTLANGCHANGE_FORWARD = 0x0002 ;
28+ private const int LLKHF_INJECTED = 0x10 ;
2629 private static readonly IntPtr HKL_NEXT = new ( 1 ) ;
2730
2831 private static IntPtr hookId = IntPtr . Zero ;
@@ -167,43 +170,51 @@ private static IntPtr SetHook(LowLevelKeyboardProc proc)
167170
168171 private static IntPtr HookCallback ( int nCode , IntPtr wParam , IntPtr lParam )
169172 {
173+ if ( nCode < 0 || IsWindowsKeyDown ( ) )
174+ {
175+ return CallNextHookEx ( hookId , nCode , wParam , lParam ) ;
176+ }
177+
170178 if ( ! appSettings . IsCapsLangEnabled )
171179 {
172180 return CallNextHookEx ( hookId , nCode , wParam , lParam ) ;
173181 }
174182
175- if ( nCode >= 0 )
183+ var message = wParam . ToInt32 ( ) ;
184+ var keyboardEvent = Marshal . PtrToStructure < KBDLLHOOKSTRUCT > ( lParam ) ;
185+ var vkCode = keyboardEvent . vkCode ;
186+
187+ if ( vkCode == VK_CAPITAL )
176188 {
177- var message = wParam . ToInt32 ( ) ;
178- var vkCode = Marshal . ReadInt32 ( lParam ) ;
189+ if ( ( keyboardEvent . flags & LLKHF_INJECTED ) != 0 )
190+ {
191+ return CallNextHookEx ( hookId , nCode , wParam , lParam ) ;
192+ }
179193
180- if ( vkCode == VK_CAPITAL )
194+ if ( message is WM_KEYDOWN or WM_SYSKEYDOWN )
181195 {
182- if ( message is WM_KEYDOWN or WM_SYSKEYDOWN )
196+ if ( IsKeyDown ( VK_CONTROL ) )
183197 {
184- if ( IsKeyDown ( VK_CONTROL ) )
185- {
186- ForceCapsLockOff ( ) ;
187- ShowIndicator ( "CAPS OFF" , force : true ) ;
188- }
189- else if ( IsKeyDown ( VK_SHIFT ) )
190- {
191- ToggleCapsLock ( ) ;
192- ShowIndicator ( IsCapsLockOn ( ) ? "CAPS ON" : "CAPS OFF" , force : true ) ;
193- }
194- else
195- {
196- ForceCapsLockOff ( ) ;
197- SwitchToNextInputLanguage ( ) ;
198- languagePopupTimer ? . Stop ( ) ;
199- languagePopupTimer ? . Start ( ) ;
200- }
198+ ForceCapsLockOff ( ) ;
199+ ShowIndicator ( "CAPS OFF" , force : true ) ;
201200 }
202-
203- if ( message is WM_KEYDOWN or WM_KEYUP or WM_SYSKEYDOWN or WM_SYSKEYUP )
201+ else if ( IsKeyDown ( VK_SHIFT ) )
204202 {
205- return new IntPtr ( 1 ) ;
203+ SetCapsLockState ( ! IsCapsLockOn ( ) ) ;
204+ ShowIndicator ( IsCapsLockOn ( ) ? "CAPS ON" : "CAPS OFF" , force : true ) ;
206205 }
206+ else
207+ {
208+ ForceCapsLockOff ( ) ;
209+ SwitchToNextInputLanguage ( ) ;
210+ languagePopupTimer ? . Stop ( ) ;
211+ languagePopupTimer ? . Start ( ) ;
212+ }
213+ }
214+
215+ if ( message is WM_KEYDOWN or WM_KEYUP or WM_SYSKEYDOWN or WM_SYSKEYUP )
216+ {
217+ return new IntPtr ( 1 ) ;
207218 }
208219 }
209220
@@ -221,10 +232,7 @@ private static void SwitchToNextInputLanguage()
221232
222233 private static void ForceCapsLockOff ( )
223234 {
224- if ( IsCapsLockOn ( ) )
225- {
226- ToggleCapsLock ( ) ;
227- }
235+ SetCapsLockState ( false ) ;
228236 }
229237
230238 private static bool IsCapsLockOn ( )
@@ -237,7 +245,20 @@ private static bool IsKeyDown(int virtualKey)
237245 return ( GetAsyncKeyState ( virtualKey ) & 0x8000 ) != 0 ;
238246 }
239247
240- private static void ToggleCapsLock ( )
248+ private static bool IsWindowsKeyDown ( )
249+ {
250+ return IsKeyDown ( VK_LWIN ) || IsKeyDown ( VK_RWIN ) ;
251+ }
252+
253+ private static void SetCapsLockState ( bool enabled )
254+ {
255+ if ( IsCapsLockOn ( ) != enabled )
256+ {
257+ SetKeyboardStateCapsLock ( enabled ) ;
258+ }
259+ }
260+
261+ private static void SetKeyboardStateCapsLock ( bool enabled )
241262 {
242263 keybd_event ( VK_CAPITAL , 0x45 , KEYEVENTF_EXTENDEDKEY , UIntPtr . Zero ) ;
243264 keybd_event ( VK_CAPITAL , 0x45 , KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP , UIntPtr . Zero ) ;
@@ -507,6 +528,16 @@ private static Point GetPointerPopupAnchor()
507528
508529 private delegate IntPtr LowLevelKeyboardProc ( int nCode , IntPtr wParam , IntPtr lParam ) ;
509530
531+ [ StructLayout ( LayoutKind . Sequential ) ]
532+ private struct KBDLLHOOKSTRUCT
533+ {
534+ public int vkCode ;
535+ public int scanCode ;
536+ public int flags ;
537+ public int time ;
538+ public UIntPtr dwExtraInfo ;
539+ }
540+
510541 [ StructLayout ( LayoutKind . Sequential ) ]
511542 private struct GUITHREADINFO
512543 {
@@ -550,10 +581,10 @@ private struct RECT
550581 private static extern short GetAsyncKeyState ( int vKey ) ;
551582
552583 [ DllImport ( "user32.dll" ) ]
553- private static extern void keybd_event ( int bVk , byte bScan , uint dwFlags , UIntPtr dwExtraInfo ) ;
584+ private static extern IntPtr GetForegroundWindow ( ) ;
554585
555586 [ DllImport ( "user32.dll" ) ]
556- private static extern IntPtr GetForegroundWindow ( ) ;
587+ private static extern void keybd_event ( int bVk , byte bScan , uint dwFlags , UIntPtr dwExtraInfo ) ;
557588
558589 [ DllImport ( "user32.dll" ) ]
559590 private static extern uint GetWindowThreadProcessId ( IntPtr hWnd , out uint lpdwProcessId ) ;
0 commit comments