Input Filtering

When streaming media, the host grants an InputLevel bitmask, indicating which kinds of interactive control the client has over the remote desktop.

Rainway offers two callbacks to further process or filter inputs as they go from the client to the host.

(Inputs are first filtered by InputLevel, and then by the callbacks.)

Outgoing input filter

Using the RainwayStream.setOutgoingInputFilter(), a Rainway web app can process input events that happen on the page before they get sent to the host.

The filter returns true to let an input through unfiltered, or false to block it. In either case, the filter can also trigger application logic, so this feature is useful for adding keybinds to a Rainway application.

It should not be used to securely "forbid" inputs like Alt+Tab or the Windows key, as the filtering only happens client-side, and can be bypassed by users with malicious intent.

stream.setOutgoingInputFilter((input, heldKeys) => {
    if (
        input.discriminator === KeyboardInput.discriminator &&
        heldKeys.shift &&
        input.value.keycode === VirtualKey.K
    ) {
        // Block Shift+K by returning false...
        stream.host.send("Control", "Send an application message instead.");
        return false;
    }
    // Allow everything else that's allowed by InputLevel.
    return true;
});

Incoming input filter

The host can also supply a filter on incoming inputs in RainwayStreamConfig when accepting a stream request.

The incoming input filter is also a function (Input, HeldKeys) → boolean, but the checking happens on the host side, making this a more appropriate place for "secure" checks like preventing Alt-Tab, right click, etc.

var config = new RainwayConfig() {
    ...
    OnStreamRequest = (runtime, request) => request.Accept(
        new RainwayStreamConfig()
        {
            InputLevel = RainwayInputLevel.Mouse | RainwayInputLevel.Keyboard,
            IncomingInputFilter = (input, heldKeys) =>
                !(input is RainwayInput.KeyboardInput { Keycode: RainwayVirtualKey.K } &&
                    (heldKeys.Contains(RainwayVirtualKey.LeftShift) ||
                        heldKeys.Contains(RainwayVirtualKey.RightShift))),
        }
    ),
    ...
};
const config = new RainwayConfig({
    ...
    onStreamRequest: (runtime, request) => request.accept({
        inputLevel: RainwayInputLevel.Mouse | RainwayInputLevel.Keyboard,
        incomingInputFilter: (input, heldKeys) =>
            !(input.type === InputType.Keyboard && input.key === RainwayVirtualKey.K &&
                (heldKeys.includes(RainwayVirtualKey.LeftShift) ||
                    heldKeys.includes(RainwayVirtualKey.RightShift))),
    }),
    ...
});
bool my_filter(
    RainwayPeerId peer,
    RainwayStreamId stream,
    const struct RainwayInput *input,
    const RainwayVirtualKey *held_keys,
    uintptr_t num_held_keys
) {
    // ...
    return true;
}

RainwayStreamConfig config;
config.incomingInputFilter = my_filter;

Did this page help you?