Skip to content

Latest commit

 

History

History
253 lines (195 loc) · 12.1 KB

File metadata and controls

253 lines (195 loc) · 12.1 KB
description Learn more about: Screen readers and hardware system buttons
title Screen readers and hardware button events
label Screen readers and hardware button events
template detail.hbs
ms.date 02/20/2020
ms.topic how-to
keywords windows 10, uwp, accessibility, narrator, screen reader
ms.localizationpriority medium

Screen readers and hardware system buttons

Screen-readers, such as Narrator, must be able to recognize and handle hardware system button events and communicate their state to users. In some cases, the screen reader might need to handle these hardware button events exclusively and not let them bubble up to other handlers.

Beginning with Windows 10 version 2004, UWP applications can listen for and handle the Fn hardware system button events in the same way as other hardware buttons. Previously, this system button acted only as a modifier for how other hardware buttons reported their events and state.

Beginning with Windows 10 version 1909, assistive technologies such as Narrator can detect the location of the Fn key and determine if it is in a locked or unlocked state. This capability is supported on Surface devices and other OEM hardware that implements the required firmware support.

Note

Fn button support is OEM-specific and can include features such as the ability to toggle/lock on or off (vs. a press-and-hold key combination), along with a corresponding lock indicator light (which might not be helpful to users who are blind or have a vision impairment). Surface devices fully support Fn key state detection and reporting.

Fn button events are exposed through a new SystemButtonEventController Class in the Windows.UI.Input namespace. The SystemButtonEventController object supports the following events:

Important

The SystemButtonEventController cannot receive these events if they have already been handled by a higher priority handler.

Accessing current Fn key state

While the events above notify your application when the Fn key state changes, you may also need to determine the current state of the Fn key when your application starts or at any point during execution.

Detecting Fn key availability

To detect if the current device has an Fn key with lock capability (such as Surface devices), you can create a SystemButtonEventController and check if it successfully receives events. Devices that don't support Fn key detection will not fire the related events.

Getting current lock state

The current Fn lock state can be determined by handling the initial events when creating the SystemButtonEventController. When the controller is created, it immediately fires events for the current state, allowing you to capture the initial Fn lock status.

bool _currentFnLockState = false;
bool _fnKeySupported = false;

void InitializeFnKeyState()
{
    // Create controller to detect current state
    _controller = winrt::SystemButtonEventController::CreateForDispatcherQueue(_queue);
    
    // Handle the immediate state events to capture current state
    _fnLockToken = _controller->FunctionLockChanged(
        [this](const winrt::SystemButtonEventController& /*sender*/, const winrt::FunctionLockChangedEventArgs& args)
        {
            _currentFnLockState = args.IsLocked();
            _fnKeySupported = true;
            
            // Announce current state for assistive technology
            std::wstring message = _currentFnLockState ? L"Fn lock is currently enabled" : L"Fn lock is currently disabled";
            // Use your preferred method to announce this state
        });
}

// Function to query current state
bool IsFnKeyLocked()
{
    return _currentFnLockState;
}

bool IsFnKeySupported()
{
    return _fnKeySupported;
}

Surface device compatibility

Surface devices provide full support for Fn key state detection. The following Surface devices are known to support this capability:

  • Surface Pro (6th generation and later)
  • Surface Laptop (all generations)
  • Surface Book (all generations)
  • Surface Studio (all generations)

On these devices, both the Fn lock state and the indicator light state can be detected and monitored through the SystemButtonEventController API.

Examples

In the following examples, we show how to create a SystemButtonEventController based on a DispatcherQueue, handle the four events supported by this object, and access the current state of the Fn key.

It is common for more than one of the supported events to fire when the Fn button is pressed. For example, pressing the Fn button on a Surface keyboard fires SystemFunctionButtonPressed, SystemFunctionLockChanged, and SystemFunctionLockIndicatorChanged at the same time.

  1. In this first snippet, we simply include the required namespaces and specify some global objects, including the DispatcherQueue and the DispatcherQueueController objects for managing the SystemButtonEventController thread.

    We then specify the event tokens returned when registering the SystemButtonEventController event-handling delegates.

    namespace winrt
    {
        using namespace Windows::System;
        using namespace Windows::UI::Input;
    }
    
    ...
    
    // Declare related members
    winrt::DispatcherQueueController _queueController;
    winrt::DispatcherQueue _queue;
    winrt::SystemButtonEventController _controller;
    winrt::event_token _fnKeyDownToken;
    winrt::event_token _fnKeyUpToken;
    winrt::event_token _fnLockToken;
    
  2. We also specify an event token for the SystemFunctionLockIndicatorChanged event along with a bool to indicate whether the application is in "Learning Mode" (where the user is simply trying to explore the keyboard without performing any functions).

    winrt::event_token _fnLockIndicatorToken;
    bool _isLearningMode = false;
    
    // State tracking variables
    bool _currentFnLockState = false;
    bool _currentIndicatorState = false;
    bool _fnKeySupported = false;
    
  3. This third snippet includes the corresponding event handler delegates for each event supported by the SystemButtonEventController object.

    Each event handler announces the event that has occurred. In addition, the FunctionLockIndicatorChanged handler also controls whether the app is in "Learning" mode (_isLearningMode = true), which prevents the event from bubbling to other handlers and lets the user explore keyboard features without actually performing the action.

    void SetupSystemButtonEventController()
    {
        // Create dispatcher queue controller and dispatcher queue
        _queueController = winrt::DispatcherQueueController::CreateOnDedicatedThread();
        _queue = _queueController.DispatcherQueue();
    
        // Create controller based on new created dispatcher queue
        _controller = winrt::SystemButtonEventController::CreateForDispatcherQueue(_queue);
    
        // Add Event Handler for each different event
        _fnKeyDownToken = _controller->FunctionButtonPressed(
            [](const winrt::SystemButtonEventController& /*sender*/, const winrt:: FunctionButtonEventArgs& args)
            {
                // Mock function to read the sentence "Fn button is pressed"
                PronounceFunctionButtonPressedMock();
                // Set Handled as true means this event is consumed by this controller
                // no more targets will receive this event
                args.Handled(true);
            });
    
            _fnKeyUpToken = _controller->FunctionButtonReleased(
                [](const winrt::SystemButtonEventController& /*sender*/, const winrt:: FunctionButtonEventArgs& args)
                {
                    // Mock function to read the sentence "Fn button is up"
                    PronounceFunctionButtonReleasedMock();
                    // Set Handled as true means this event is consumed by this controller
                    // no more targets will receive this event
                    args.Handled(true);
                });
    
        _fnLockToken = _controller->FunctionLockChanged(
            [this](const winrt::SystemButtonEventController& /*sender*/, const winrt:: FunctionLockChangedEventArgs& args)
            {
                // Update current state tracking
                _currentFnLockState = args.IsLocked();
                _fnKeySupported = true;
                
                // Mock function to read the sentence "Fn shift is locked/unlocked"
                PronounceFunctionLockMock(args.IsLocked());
                // Set Handled as true means this event is consumed by this controller
                // no more targets will receive this event
                args.Handled(true);
            });
    
        _fnLockIndicatorToken = _controller->FunctionLockIndicatorChanged(
            [this](const winrt::SystemButtonEventController& /*sender*/, const winrt:: FunctionLockIndicatorChangedEventArgs& args)
            {
                // Update current indicator state
                _currentIndicatorState = args.IsIndicatorOn();
                
                // Mock function to read the sentence "Fn lock indicator is on/off"
                PronounceFunctionLockIndicatorMock(args.IsIndicatorOn());
                // In learning mode, the user is exploring the keyboard. They expect the program
                // to announce what the key they just pressed WOULD HAVE DONE, without actually
                // doing it. Therefore, handle the event when in learning mode so the key is ignored
                // by the system.
                args.Handled(_isLearningMode);
            });
    }
    
  4. Finally, you can add methods to query the current state at any time after the controller has been initialized:

    // Methods to query current Fn key state
    bool IsFnKeySupported() const
    {
        return _fnKeySupported;
    }
    
    bool IsFnKeyLocked() const
    {
        return _currentFnLockState;
    }
    
    bool IsFnIndicatorOn() const
    {
        return _currentIndicatorState;
    }
    
    // Example usage: announce current state on demand
    void AnnounceCurrentFnState()
    {
        if (!_fnKeySupported)
        {
            // Device doesn't support Fn key detection
            return;
        }
        
        if (_currentFnLockState)
        {
            // Mock function to announce "Fn lock is currently enabled"
            PronounceFunctionLockMock(true);
        }
        else
        {
            // Mock function to announce "Fn lock is currently disabled"  
            PronounceFunctionLockMock(false);
        }
        
        if (_currentIndicatorState)
        {
            // Mock function to announce "Fn lock indicator is on"
            PronounceFunctionLockIndicatorMock(true);
        }
    }
    

See also

SystemButtonEventController Class

FunctionLockChangedEventArgs Class

FunctionLockIndicatorChangedEventArgs Class

Windows accessibility documentation

Keyboard accessibility