Skip to content

Commit 14c8f3c

Browse files
author
Alex Vallat
committed
v2.0: Chrome Support
1 parent fc7c32b commit 14c8f3c

File tree

6 files changed

+96
-7
lines changed

6 files changed

+96
-7
lines changed

KeyLayoutAutoSwitch/AccessibleObjectHelper.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@ namespace KeyLayoutAutoSwitch
99
{
1010
internal static class AccessibleObjectHelper
1111
{
12+
[DllImport("oleacc.dll", ExactSpelling = true, PreserveSig = false)]
13+
[return: MarshalAs(UnmanagedType.Interface)]
14+
private static extern object AccessibleObjectFromWindow(
15+
IntPtr hwnd,
16+
uint dwObjectID,
17+
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid);
18+
19+
private static readonly Guid IID_IAccessible = new Guid("{618736E0-3C3D-11CF-810C-00AA00389B71}");
20+
1221
[DllImport("oleacc.dll")]
1322
private static extern uint AccessibleChildren(IAccessible paccContainer, int iChildStart, int cChildren, [Out] object[] rgvarChildren, out int pcObtained);
1423
private const int NAVDIR_FIRSTCHILD = 7;
@@ -115,5 +124,10 @@ public static AccessibleRole GetRole(IAccessible accessibleObject)
115124
return AccessibleRole.None;
116125
}
117126
}
127+
128+
public static IAccessible GetAccessibleObjectFromWindow(IntPtr hwnd, uint objectID = 0)
129+
{
130+
return (IAccessible)AccessibleObjectFromWindow(hwnd, objectID, IID_IAccessible);
131+
}
118132
}
119133
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using System;
2+
using System.Runtime.InteropServices;
3+
4+
namespace KeyLayoutAutoSwitch
5+
{
6+
/// <summary>
7+
/// Handles accessibility events as if a screen reader were running. Chrome detects this to automatically enable accessibility features.
8+
/// </summary>
9+
internal class ChromeAccessibilityWinEventHook : IDisposable
10+
{
11+
private delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
12+
13+
[DllImport("user32.dll")]
14+
private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
15+
16+
[DllImport("user32.dll")]
17+
private static extern bool UnhookWinEvent(IntPtr hWinEventHook);
18+
19+
[DllImport("User32.dll")]
20+
private static extern IntPtr SendMessage(IntPtr hWnd, uint nMsg, int wParam, int lParam);
21+
22+
private const uint WINEVENT_OUTOFCONTEXT = 0;
23+
private const uint EVENT_SYSTEM_ALERT = 0x0002;
24+
25+
private const uint WM_GETOBJECT = 0x3D;
26+
27+
// ref: http://www.chromium.org/developers/design-documents/accessibility
28+
private const int GOOGLE_CHROME_ACCESSIBILITY_OBJECT_ID = 1;
29+
30+
private readonly IntPtr mWinEventHook;
31+
private readonly WinEventDelegate mEventDelegate;
32+
33+
public ChromeAccessibilityWinEventHook()
34+
{
35+
mEventDelegate = OnEventReceived;
36+
mWinEventHook = SetWinEventHook(EVENT_SYSTEM_ALERT, EVENT_SYSTEM_ALERT, IntPtr.Zero, mEventDelegate, 0, 0, WINEVENT_OUTOFCONTEXT);
37+
}
38+
39+
private void OnEventReceived(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
40+
{
41+
if (idObject == GOOGLE_CHROME_ACCESSIBILITY_OBJECT_ID)
42+
{
43+
EventReceived = true;
44+
SendMessage(hwnd, WM_GETOBJECT, 0, idObject);
45+
46+
// Chrome only enables accessibility if it gets a top-level IAccessible request, so let's make one first
47+
var _ = AccessibleObjectHelper.GetAccessibleObjectFromWindow(hwnd).accName;
48+
}
49+
}
50+
51+
public void Dispose()
52+
{
53+
UnhookWinEvent(mWinEventHook);
54+
}
55+
56+
/// <summary>
57+
/// This flag is set true whenever an event is received.
58+
/// Set it false before the period of interest.
59+
/// </summary>
60+
public bool EventReceived { get; set; }
61+
}
62+
}

KeyLayoutAutoSwitch/KeyLayoutAutoSwitch.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
<ItemGroup>
5656
<Compile Include="AccessibleObjectHelper.cs" />
5757
<Compile Include="Browser.cs" />
58+
<Compile Include="ChromeAccessibilityWinEventHook.cs" />
5859
<Compile Include="ChromeWidgets.cs" />
5960
<Compile Include="ExtensionMethods.cs" />
6061
<Compile Include="Chrome.cs" />

KeyLayoutAutoSwitch/Program.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,12 @@ internal class BackgroundApplicationContext : ApplicationContext
148148
{
149149
private readonly NotifyIcon mNotifyIcon;
150150
private readonly IDisposable mFocusEventHook;
151+
private readonly ChromeAccessibilityWinEventHook mChromeAccessibilityWinEventHook;
151152

152153
public BackgroundApplicationContext(bool showInitialConfigDialog)
153154
{
154155
mFocusEventHook = NativeMethods.AddFocusEventHook(OnFocusChanged);
156+
mChromeAccessibilityWinEventHook = new ChromeAccessibilityWinEventHook();
155157

156158
Application.ApplicationExit += OnApplicationExit;
157159

@@ -241,7 +243,7 @@ private void OnFocusChanged(IntPtr hwnd, uint idObject, uint idChild)
241243
{
242244
var className = NativeMethods.GetWindowClassName(hwnd);
243245

244-
Debug.WriteLine($"Focus changed to window: {hwnd} ({className}), object {idObject}, child {idChild}, layout {NativeMethods.GetKeyboardLayout(hwnd)}");
246+
//Debug.WriteLine($"Focus changed to window: {hwnd} ({className}), object {idObject}, child {idChild}, layout {NativeMethods.GetKeyboardLayout(hwnd)}");
245247

246248
Browser browser = null;
247249
switch (className)
@@ -252,8 +254,11 @@ private void OnFocusChanged(IntPtr hwnd, uint idObject, uint idChild)
252254
case "Chrome_RenderWidgetHostHWND":
253255
browser = new Chrome();
254256
break;
255-
case "Chrome_WidgetWin_1":
256-
browser = new ChromeWidgets();
257+
default:
258+
if (className.StartsWith("Chrome_WidgetWin_"))
259+
{
260+
browser = new ChromeWidgets();
261+
}
257262
break;
258263
}
259264
if (browser != null)
@@ -338,6 +343,7 @@ private void OnApplicationExit(object sender, EventArgs eventArgs)
338343
{
339344
mFocusEventHook.Dispose();
340345
mNotifyIcon.Dispose();
346+
mChromeAccessibilityWinEventHook.Dispose();
341347
}
342348
}
343349
}

KeyLayoutAutoSwitch/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,5 @@
3030
// You can specify all the values or you can default the Build and Revision Numbers
3131
// by using the '*' as shown below:
3232
// [assembly: AssemblyVersion("1.0.*")]
33-
[assembly: AssemblyVersion("1.2.0.0")]
34-
[assembly: AssemblyFileVersion("1.2.0.0")]
33+
[assembly: AssemblyVersion("2.0.0.0")]
34+
[assembly: AssemblyFileVersion("2.0.0.0")]

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,14 @@ Note that the visited URLs are stored hashed, and in memory only. They are never
3333

3434
To exit KeyLayoutAutoSwitch entirely, right click on the notification area icon and choose "Exit". KeyLayoutAutoSwitch will no longer be running in the background, and will not change keyboard layouts until it is next run.
3535

36+
## Chrome Support
37+
KeyLayoutAutoSwitch also supports Chrome and most Chromium-based browsers, however the Find in Page rule "Match web page" will not work (the URL is not know for the Find in Page popup). Instead, use the "Do not change" rule, which is similar.
38+
39+
If Chrome is not automatically enabling accessibility then this can be forced by adding the "--force-renderer-accessibility" flag on the command line. This will not generally be required, but if you are finding that the site rules are having no effect then it is worth trying.
40+
3641
## Limitations
3742
KeyLayoutAutoSwitch uses the Accessibility interfaces to obtain information from the web browser about which element is focused. If Accessibility services have been disabled in the browser it will not be able to function properly.
3843

39-
At present only Firefox is supported, although it is anticipated that support for other browsers may be added in the future.
40-
4144
KeyLayoutAutoSwitch is Windows only. Linux support would be nice to have, but is unlikely to happen soon, unless someone with knowledge of Linux accessibility services and Mono is willing to contribute development work to the project.
4245

4346
## Translation
@@ -53,6 +56,9 @@ KeyLayoutAutoSwitch will use the default windows locale, but this may be overrid
5356
Donations are very welcome, and may be made through PayPal by using the [Donate](http://keylayoutautoswitch.byalexv.co.uk/donate) link. Or you can send directly to me in any currency using <https://www.paypal.me/toalexv>.
5457

5558
## Changelog
59+
v2.0:
60+
* Added support for Chrome and Chromium-based browsers
61+
5662
v1.3:
5763
* Added new option to Previously visited web pages rule, to apply the last used keyboard layout for the whole site (domain)
5864
* Added context menu command to clear the previously visited web pages, forgetting all last used layouts

0 commit comments

Comments
 (0)