2
2
Copyright (c) Microsoft Corporation. All rights reserved.
3
3
--********************************************************************/
4
4
5
+ using System . Diagnostics ;
5
6
using System . Runtime . InteropServices ;
6
7
7
8
namespace Microsoft . PowerShell . Internal
@@ -10,14 +11,82 @@ internal class Accessibility
10
11
{
11
12
internal static bool IsScreenReaderActive ( )
12
13
{
13
- bool returnValue = false ;
14
-
15
14
if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
16
15
{
17
- PlatformWindows . SystemParametersInfo ( PlatformWindows . SPI_GETSCREENREADER , 0 , ref returnValue , 0 ) ;
16
+ return IsAnyWindowsScreenReaderEnabled ( ) ;
17
+ }
18
+
19
+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) )
20
+ {
21
+ return IsVoiceOverEnabled ( ) ;
22
+ }
23
+
24
+ // TODO: Support Linux per https://code.visualstudio.com/docs/configure/accessibility/accessibility
25
+ return false ;
26
+ }
27
+
28
+ private static bool IsAnyWindowsScreenReaderEnabled ( )
29
+ {
30
+ // The supposedly official way to check for a screen reader on
31
+ // Windows is SystemParametersInfo(SPI_GETSCREENREADER, ...) but it
32
+ // doesn't detect the in-box Windows Narrator and is otherwise known
33
+ // to be problematic.
34
+ //
35
+ // Unfortunately, the alternative method used by Electron and
36
+ // Chromium, where the relevant screen reader libraries (modules)
37
+ // are checked for does not work in the context of PowerShell
38
+ // because it relies on those applications injecting themselves into
39
+ // the app. Which they do not because PowerShell is not a windowed
40
+ // app, so we're stuck using the known-to-be-buggy way.
41
+ bool spiScreenReader = false ;
42
+ PlatformWindows . SystemParametersInfo ( PlatformWindows . SPI_GETSCREENREADER , 0 , ref spiScreenReader , 0 ) ;
43
+ if ( spiScreenReader )
44
+ {
45
+ return true ;
46
+ }
47
+
48
+ // At least we can correctly check for Windows Narrator using the
49
+ // NarratorRunning mutex. Windows Narrator is mostly not broken with
50
+ // PSReadLine, not in the way that NVDA and VoiceOver are.
51
+ if ( PlatformWindows . IsMutexPresent ( "NarratorRunning" ) )
52
+ {
53
+ return true ;
54
+ }
55
+
56
+ return false ;
57
+ }
58
+
59
+ private static bool IsVoiceOverEnabled ( )
60
+ {
61
+ try
62
+ {
63
+ // Use the 'defaults' command to check if VoiceOver is enabled
64
+ // This checks the com.apple.universalaccess preference for voiceOverOnOffKey
65
+ ProcessStartInfo startInfo = new ( )
66
+ {
67
+ FileName = "defaults" ,
68
+ Arguments = "read com.apple.universalaccess voiceOverOnOffKey" ,
69
+ UseShellExecute = false ,
70
+ RedirectStandardOutput = true ,
71
+ RedirectStandardError = true ,
72
+ CreateNoWindow = true
73
+ } ;
74
+
75
+ using Process process = Process . Start ( startInfo ) ;
76
+ process . WaitForExit ( 250 ) ;
77
+ if ( process . HasExited && process . ExitCode == 0 )
78
+ {
79
+ string output = process . StandardOutput . ReadToEnd ( ) . Trim ( ) ;
80
+ // VoiceOver is enabled if the value is 1
81
+ return output == "1" ;
82
+ }
83
+ }
84
+ catch
85
+ {
86
+ // If we can't determine the status, assume VoiceOver is not enabled
18
87
}
19
88
20
- return returnValue ;
89
+ return false ;
21
90
}
22
91
}
23
92
}
0 commit comments