|
1 | 1 | using System;
|
2 | 2 | using System.ComponentModel;
|
| 3 | +using System.Linq; |
3 | 4 | using System.Runtime.InteropServices;
|
4 | 5 | using System.Windows;
|
5 | 6 | using System.Windows.Interop;
|
6 | 7 | using System.Windows.Media;
|
7 | 8 | using Windows.Win32;
|
8 | 9 | using Windows.Win32.Foundation;
|
9 | 10 | using Windows.Win32.Graphics.Dwm;
|
| 11 | +using Windows.Win32.UI.Input.KeyboardAndMouse; |
10 | 12 | using Windows.Win32.UI.WindowsAndMessaging;
|
11 | 13 | using Flow.Launcher.Infrastructure.UserSettings;
|
| 14 | +using Point = System.Windows.Point; |
12 | 15 |
|
13 | 16 | namespace Flow.Launcher.Infrastructure
|
14 | 17 | {
|
@@ -317,5 +320,81 @@ internal static HWND GetWindowHandle(Window window, bool ensure = false)
|
317 | 320 | }
|
318 | 321 |
|
319 | 322 | #endregion
|
| 323 | + |
| 324 | + #region Keyboard Layout |
| 325 | + |
| 326 | + // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/70feba9f-294e-491e-b6eb-56532684c37f |
| 327 | + private static readonly uint[] EnglishLanguageIds = |
| 328 | + { |
| 329 | + 0x0009, 0x0409, 0x0809, 0x0C09, 0x1000, 0x1009, 0x1409, 0x1809, 0x1C09, 0x2009, 0x2409, 0x2809, 0x2C09, |
| 330 | + 0x3009, 0x3409, 0x3C09, 0x4009, 0x4409, 0x4809, 0x4C09, |
| 331 | + }; |
| 332 | + |
| 333 | + // Store the previous keyboard layout |
| 334 | + private static HKL _previousLayout; |
| 335 | + |
| 336 | + private static unsafe HKL FindEnglishKeyboardLayout() |
| 337 | + { |
| 338 | + // Get the number of keyboard layouts |
| 339 | + int count = PInvoke.GetKeyboardLayoutList(0, null); |
| 340 | + if (count <= 0) return HKL.Null; |
| 341 | + |
| 342 | + // Get all keyboard layouts |
| 343 | + var handles = new HKL[count]; |
| 344 | + fixed (HKL* h = handles) |
| 345 | + { |
| 346 | + _ = PInvoke.GetKeyboardLayoutList(count, h); |
| 347 | + } |
| 348 | + |
| 349 | + // Look for any English keyboard layout |
| 350 | + foreach (var hkl in handles) |
| 351 | + { |
| 352 | + // The lower word contains the language identifier |
| 353 | + var langId = (uint)hkl.Value & 0xFFFF; |
| 354 | + |
| 355 | + // Check if it's an English layout |
| 356 | + if (EnglishLanguageIds.Contains(langId)) |
| 357 | + { |
| 358 | + return hkl; |
| 359 | + } |
| 360 | + } |
| 361 | + |
| 362 | + return HKL.Null; |
| 363 | + } |
| 364 | + |
| 365 | + public static unsafe void SetEnglishKeyboardLayout(bool backupPrevious) |
| 366 | + { |
| 367 | + // Find an installed English layout |
| 368 | + var enHKL = FindEnglishKeyboardLayout(); |
| 369 | + |
| 370 | + // No installed English layout found |
| 371 | + if (enHKL == HKL.Null) return; |
| 372 | + |
| 373 | + // Get the current window thread ID |
| 374 | + uint threadId = 0; |
| 375 | + var result = PInvoke.GetWindowThreadProcessId(PInvoke.GetForegroundWindow(), &threadId); |
| 376 | + if (result == 0 || threadId == 0) throw new Win32Exception(Marshal.GetLastWin32Error()); |
| 377 | + |
| 378 | + // Backup current keyboard layout |
| 379 | + if (backupPrevious) |
| 380 | + { |
| 381 | + _previousLayout = PInvoke.GetKeyboardLayout(threadId); |
| 382 | + } |
| 383 | + |
| 384 | + // Switch to English layout |
| 385 | + PInvoke.ActivateKeyboardLayout(enHKL, 0); |
| 386 | + } |
| 387 | + |
| 388 | + public static void SetPreviousKeyboardLayout() |
| 389 | + { |
| 390 | + if (_previousLayout != HKL.Null) |
| 391 | + { |
| 392 | + PInvoke.ActivateKeyboardLayout(_previousLayout, 0); |
| 393 | + |
| 394 | + _previousLayout = HKL.Null; |
| 395 | + } |
| 396 | + } |
| 397 | + |
| 398 | + #endregion |
320 | 399 | }
|
321 | 400 | }
|
0 commit comments