1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
using System;
using System.Runtime.InteropServices;
using AppKit;
using CoreFoundation;
using Foundation;
using ObjCRuntime;
namespace Xwt.Mac
{
public class AccerssibilityHelper
{
public event EventHandler AccessibilityInUseChanged;
static bool a11yHelperInitialized;
static AccerssibilityHelper a11yHelperInstance;
public void InitializeAccessibilityHelper()
{
// Don't swizzle twice if we have both XamMac and GtkMac running
if (a11yHelperInstance != null)
return;
a11yHelperInstance = this;
// TODO: Test if this works with VSM swizzle
SwizzleNSApplicationAccessibilitySetter();
}
[DllImport("/usr/lib/libobjc.dylib")]
private static extern IntPtr class_getInstanceMethod(IntPtr classHandle, IntPtr Selector);
[DllImport("/usr/lib/libobjc.dylib")]
private static extern IntPtr method_getImplementation(IntPtr method);
[DllImport("/usr/lib/libobjc.dylib")]
private static extern IntPtr imp_implementationWithBlock(ref BlockLiteral block);
[DllImport("/usr/lib/libobjc.dylib")]
private static extern void method_setImplementation(IntPtr method, IntPtr imp);
[MonoNativeFunctionWrapper]
delegate void AccessibilitySetValueForAttributeDelegate(IntPtr self, IntPtr selector, IntPtr valueHandle, IntPtr attributeHandle);
delegate void SwizzledAccessibilitySetValueForAttributeDelegate(IntPtr block, IntPtr self, IntPtr valueHandle, IntPtr attributeHandle);
static IntPtr originalAccessibilitySetValueForAttributeMethod;
void SwizzleNSApplicationAccessibilitySetter()
{
// Swizzle accessibilitySetValue:forAttribute: so that we can detect when VoiceOver gets enabled
var nsApplicationClassHandle = Class.GetHandle("NSApplication");
// This happens if GtkMac is loaded before XamMac
if (nsApplicationClassHandle == IntPtr.Zero)
return;
var accessibilitySetValueForAttributeSelector = Selector.GetHandle("accessibilitySetValue:forAttribute:");
var accessibilitySetValueForAttributeMethod = class_getInstanceMethod(nsApplicationClassHandle, accessibilitySetValueForAttributeSelector);
originalAccessibilitySetValueForAttributeMethod = method_getImplementation(accessibilitySetValueForAttributeMethod);
var block = new BlockLiteral();
SwizzledAccessibilitySetValueForAttributeDelegate d = accessibilitySetValueForAttribute;
block.SetupBlock(d, null);
var imp = imp_implementationWithBlock(ref block);
method_setImplementation(accessibilitySetValueForAttributeMethod, imp);
accessibilityInUse = CFPreferences.GetAppBooleanValue("voiceOverOnOffKey", "com.apple.universalaccess");
a11yHelperInitialized = true;
}
[MonoPInvokeCallback(typeof(SwizzledAccessibilitySetValueForAttributeDelegate))]
static void accessibilitySetValueForAttribute(IntPtr block, IntPtr self, IntPtr valueHandle, IntPtr attributeHandle)
{
var d = Marshal.GetDelegateForFunctionPointer(originalAccessibilitySetValueForAttributeMethod, typeof(AccessibilitySetValueForAttributeDelegate));
d.DynamicInvoke(self, Selector.GetHandle("accessibilitySetValue:forAttribute:"), valueHandle, attributeHandle);
var val = (NSNumber)ObjCRuntime.Runtime.GetNSObject(valueHandle);
bool previousValue = accessibilityInUse;
accessibilityInUse = val.BoolValue;
if (accessibilityInUse != previousValue)
a11yHelperInstance.OnAccessibilityInUseChanged(a11yHelperInstance, EventArgs.Empty);
}
void OnAccessibilityInUseChanged(object sender, EventArgs eventArgs)
{
AccessibilityInUseChanged?.Invoke(sender, eventArgs);
}
static bool accessibilityInUse;
public bool AccessibilityInUse
{
get
{
if (!a11yHelperInitialized)
InitializeAccessibilityHelper();
return accessibilityInUse;
}
}
public void MakeAnnouncement(string message, bool polite = false)
{
if (!a11yHelperInitialized)
return;
var nsObject = NSApplication.SharedApplication?.AccessibilityFocusedWindow;
if (nsObject == null)
return;
using (var msg = new NSString(message))
using (var dictionary = new NSDictionary(NSAccessibilityNotificationUserInfoKeys.AnnouncementKey, msg,
NSAccessibilityNotificationUserInfoKeys.PriorityKey, polite ? NSAccessibilityPriorityLevel.Medium : NSAccessibilityPriorityLevel.High))
{
NSAccessibility.PostNotification(nsObject, NSView.AnnouncementRequestedNotification, dictionary);
}
}
}
}
|