diff --git a/index.html b/index.html index d7eda7e..d545129 100644 --- a/index.html +++ b/index.html @@ -65,11 +65,11 @@ implementationReportURI: "https://wpt.fyi/results/gamepad", caniuse: "gamepad", - xref: ["HTML", "DOM", "PERMISSIONS-POLICY", "HR-TIME", "Infra", "geometry-1"], + xref: ["ecmascript", "HTML", "DOM", "PERMISSIONS-POLICY", "HR-TIME", "Infra", "geometry-1", "webcrypto", "WebDriver", "WebDriver-BiDi", "RFC4122"], }; - +

The Gamepad specification defines a low-level interface that represents @@ -136,6 +136,204 @@

sensing, depth sensing, video analysis, gesture recognition, and so on.

+
+

+ Model +

+

+ A gamepad is a collection of input controls and output controls. + An input control has a collection of input values that may update over time. + Input controls include the buttons, triggers, joysticks, thumbsticks, and touch surfaces of the [=gamepad=]. + An output control is a feature that changes the behavior of the [=gamepad=] to provide feedback to the user interacting with the [=gamepad=]. + Output controls include the haptic actuators of the [=gamepad=]. + A [=gamepad=] is available if the [=user agent=] can read the current state of its [=input controls=]. + A [=gamepad=] that is not [=available=] is unavailable. + The [=input controls=] and [=output controls=] for a [=gamepad=] cannot change while the [=gamepad=] is [=available=]. +

+

+ The [=user agent=] is responsible for: +

+

+

+ A [=gamepad=] has a gamepad identifier string, a + human-readable string that identifies the brand or style of + [=gamepad=]. The content is decided by the [=user agent=]. +

+
+

+ Input control layout +

+

+ A [=gamepad=] may have an input control layout that describes the position, orientation and type of each [=input control=] on the [=gamepad=]. + The [=user agent=] is responsible for recognizing when a [=gamepad=] corresponds with a standard layout, meaning the [=gamepad=] has an [=input control layout=] that enables it to be used interchangeably with other [=gamepads=] that correspond with the same standard layout. + The [=user agent=] SHOULD consider a layout to correspond with a standard layout if its [=input controls=] have approximately the same relative positions and orientations as [=input controls=] described in the standard layout. +

+

+ The [=user agent=] typically cannot directly inspect the [=input control layout=] for a [=gamepad=] and MAY use heuristics to decide the layout. + The [=user agent=] SHOULD consider the device identifiers when deciding whether a [=gamepad=] [=corresponds with a standard layout=]. + If the system assigns a label to each [=input control=] and the labels imply a particular layout then the [=user agent=] SHOULD consider the [=gamepad=] to have that layout. + When there is a standard model and an accessible model with the same [=input controls=], the [=user agent=] SHOULD consider the accessible model to have the same [=input control layout=] as the standard model. +

+
+
+

+ Input values +

+

+ Each input control has one or more associated [=input values=], which are numerical values that represent the current state of the control. + [=Input values=] can update at any time. + The [=user agent=] is responsible for detecting when [=input values=] have updated and SHOULD try to minimize the delay between the update and when the updated values are read. +

+

+ Reading an [=input value=] returns its logical value, an unscaled numerical representation of the current state. + An [=input value=] also has a logical minimum and logical maximum which define the minimum and maximum in-bounds [=logical values=]. +

+

+ An [=input value=] may have an associated HID usage identifier, a 32-bit value that identifies the type of data represented by the input value. + A HID usage does not precisely describe the [=input control layout=], but by convention many [=gamepads=] with similar layouts use similar usages. + The [=user agent=] SHOULD rely on conventions around [=HID usage identifiers=] when deciding the [=input control layout=]. +

+
+
+

+ Axes +

+

+ A [=gamepad=] may have [=axis=] inputs. An axis is an [=input value=] that represents the current displacement of the control from a reference position. +

+ A [=gamepad=] has an axis list which is a [=list=] + containing all the [=axis=] inputs for the [=gamepad=] in some order + determined by the [=user agent=]. +

+

+ An [=input control=] may be designed to automatically return an [=axis=] to + a center position when the user stops interacting with the [=input + control=]. If so, the [=axis=] has a preferred axis state. + An [=axis=] with a [=preferred axis state=] may also have an additional + [=input value=], the center position value, which is the [=logical value=] when the [=axis=] is centered. +

+
+
+

+ Buttons +

+

+ A [=gamepad=] may have [=button=] inputs. A button is an + [=input control=] that can be pressed to activate. A [=gamepad=] has a + button list which is a [=list=] containing all the + [=button=] inputs for the [=gamepad=] in some order determined by the + [=user agent=]. +

+

+ An [=input control=] may be designed to automatically return a [=button=] + to an unpressed state when the user stops interacting with the [=input + control=]. If so, the [=button=] has a preferred button state. +

+

+ A [=button=] may have a digital switch to indicate when the [=button=] + is activated. If so, the [=button=] has an additional [=input value=], + the digital button value, that is `true` when the [=button=] is + activated and `false` otherwise. +

+

+ A [=button=] may have an analog sensor enabling the [=button=] to + report the degree to which the [=button=] is activated. If so, the + [=button=] has: +

+ +

+ A [=button=] may be capable of detecting touch. If so, the [=button=] + has an additional [=input value=], the button touch value, that is `true` when + the [=button=] is touched and `false` otherwise. +

+
+
+

+ Touch surfaces +

+

+ A [=gamepad=] may have [=touch surfaces=]. A touch surface + is an input control that provides 2D position data representing points + of contact. A [=gamepad=] has a touch surface list which is + a [=list=] containing the [=touch surfaces=] for the [=gamepad=]. The + list is ordered such that [=touch surfaces=] closer to the left side of + the [=gamepad=] appear closer to the start of the list. +

+

+ A [=touch surface=] has an active touch point list [=input + value=], a [=list=] of zero or more [=touch points=] representing the + points of contact currently detected by the sensor. A touch + point represents a single point of contact at a single point in + time. A [=touch point=] has a touch x coordinate + and a touch y coordinate representing the position in the [=touch surface=]'s coordinate system. + If a [=touch surface=] is on the top, bottom, front, or back side of the [=gamepad=] then the [=touch x coordinate=] is measured along the left-right axis, otherwise it is measured along the top-bottom axis. + The [=touch y coordinate=] is measured along the perpendicular axis. +

+

+ If a [=touch surface=] is on the left or right side of a [=gamepad=] then neither of its dimensions will align with the horizontal axis. +

+

+ A [=touch surface=] may have surface dimension [=input values=]. The + surface width and surface height [=input values=] + are the dimensions of the [=touch surface=] in the same units as the + [=touch x coordinate=] and [=touch y coordinate=]. A [=touch surface=] + has both dimension values or neither value. +

+

+ A [=touch point=] may be a new contact point or a continuation of an + earlier contact. A [=touch point=] is part of an existing active + touch point if the [=user agent=] identifies that it is a + continuation of a [=touch point=] represented by an earlier + {{GamepadTouch}}. The active touch point id for a [=touch + point=] that is [=part of an existing active touch point=] is the {{GamepadTouch/touchId}} of the earlier + {{GamepadTouch}}. +

+
+
+

+ Output controls +

+

+ A [=gamepad=] may have [=haptic actuators=]. A haptic + actuator is an output control capable of moving the [=gamepad=] + in a way that can be felt by the user. + A [=haptic actuator=] can be used to generate a haptic effect that provides feedback to the user. + Vibrations from multiple actuators are combined to generate more complex effects. + The [=user agent=] is responsible for commanding [=haptic + actuators=] to play and stop [=haptic effects=] on [=available=] + [=gamepads=]. +

+

+ A [=gamepad=] may have a vibration actuator which is a + [=haptic actuator=] capable of playing a [=haptic effect=] to vibrate + the whole [=gamepad=]. +

+

+ A [=haptic actuator=] has a [=list=] of supported effect + types containing one or more {{GamepadHapticEffectType}} values, + which cannot change while the [=gamepad=] is [=available=]. +

+
+

Gamepad interface @@ -543,11 +741,21 @@

  • Set |gamepad|.{{Gamepad/[[timestamp]]}} to |now|.
  • -
  • Run the steps to [=map and normalize axes=] for |gamepad|. -
  • -
  • Run the steps to [=map and normalize buttons=] for |gamepad|. -
  • -
  • Run the steps to [=record touches=] for |gamepad|. +
  • +

    + If the [=gamepad=] represented by |gamepad| is a [=simulated gamepad=], run the steps to [=update simulated gamepad state=] for |gamepad|. +

    +

    + Otherwise: +

    +
      +
    1. Run the steps to [=map and normalize axes=] for |gamepad|. +
    2. +
    3. Run the steps to [=map and normalize buttons=] for |gamepad|. +
    4. +
    5. Run the steps to [=record touches=] for |gamepad|. +
    6. +
  • Let |navigator:Navigator| be |gamepad|'s [=relevant global object=]'s {{Navigator}} object. @@ -587,6 +795,14 @@

  • +

    + To normalize a logical axis value given |value|, |minimum|, and |maximum|: +

    +
      +
    1. + Return 2 (|value| − |minimum|) / (|maximum| − |minimum|) − 1. +
    2. +

    To map and normalize axes for |gamepad:Gamepad|, run the following steps: @@ -614,8 +830,7 @@

  • Let |logicalMaximum:unsigned long| be |gamepad|.{{Gamepad/[[axisMaximums]]}}[|rawAxisIndex|].
  • -
  • Let |normalizedValue:double| be 2 (|logicalValue| − - |logicalMinimum|) / (|logicalMaximum| − |logicalMinimum|) − 1. +
  • Let |normalizedValue:double| be the result of [=normalize a logical axis value=] with |logicalValue|, |logicalMinimum|, and |logicalMaximum|.
  • Set |gamepad|.{{Gamepad/[[axes]]}}[|axisIndex|] to be |normalizedValue|. @@ -623,6 +838,14 @@

  • +

    + To normalize a logical button value given |value|, |minimum|, and |maximum|: +

    +
      +
    1. + Return (|logicalValue| − |logicalMinimum|) / (|logicalMaximum| − |logicalMinimum|). +
    2. +

    To map and normalize buttons for |gamepad:Gamepad|, run the following steps: @@ -650,8 +873,7 @@

  • Let |logicalMaximum:unsigned long| be |gamepad|.{{Gamepad/[[buttonMaximums]]}}[|rawButtonIndex|].
  • -
  • Let |normalizedValue:double| be (|logicalValue| − - |logicalMinimum|) / (|logicalMaximum| − |logicalMinimum|). +
  • Let |normalizedValue:double| be the result of [=normalize a logical button value=] with |logicalValue|, |logicalMinimum|, and |logicalMaximum|.
  • Let |button:GamepadButton| be |gamepad|.{{Gamepad/[[buttons]]}}[|mappedIndex|]. @@ -1317,7 +1539,7 @@

    {{GamepadEffectParameters}} |params:GamepadEffectParameters |, are:

      -
    1. If |params:GamepadEffectParameters| does not describe a [=valid +
    2. If |params:GamepadEffectParameters| does not describe a [=valid effect=] of type |type:GamepadHapticEffectType|, return [=a promise rejected with=] a {{TypeError}}.
    3. @@ -1361,6 +1583,8 @@

    4. [=Issue a haptic effect=] to the actuator with |type|, |params|, and the |playEffectTimestamp|.
    5. +
    6. If |this|'s [=gamepad=] is a [=simulated gamepad=], run the steps to [=vibrate a simulated gamepad=] for |this|'s [=gamepad=] with |params|. +
    7. When the effect completes, if [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}} is not `null`, [=queue a global task=] on the [=relevant global @@ -1413,6 +1637,8 @@

    8. [=Stop haptic effects=] on [=this=]'s gamepad's actuator.
    9. +
    10. If [=this=]'s [=gamepad=] is a [=simulated gamepad=], run the steps to [=stop vibration for a simulated gamepad=] for [=this=]'s [=gamepad=]. +
    11. If the effect has been successfully stopped, do:
      1. If |effectPromise| and @@ -2255,7 +2481,7 @@

        The gamepadconnected event

        - When a gamepad becomes available on the system, run the following + When a gamepad becomes available on the system, run the following steps:

          @@ -2310,7 +2536,7 @@

          The gamepaddisconnected event

          - When a gamepad becomes unavailable on the system, run the following + When a gamepad becomes unavailable on the system, run the following steps:

            @@ -2406,6 +2632,1425 @@

  • +
    +

    + Automation +

    +

    + For the purposes of [=user agent=] automation and application testing, this document defines extensions to the [[WebDriver-BiDi]] expecification. + It is OPTIONAL for a [=user agent=] to support them. +

    +

    + Each [=top-level traversable=] has a simulated gamepad map which is an [=ordered map=] of [=version 4 UUIDs=] to [=gamepad input sources=]. +

    +

    + A simulated gamepad is a software-defined [=gamepad=] that behaves like a physical gamepad and has [=input controls=], [=output controls=], and a [=gamepad identifier string=]. +

    +

    + A [=simulated gamepad=] is not a physical device and does not have an [=input control layout=]. + If a [=simulated gamepad=] has device identifiers that match a [=gamepad=] with a known [=input control layout=], the [=user agent=] SHOULD process inputs on the [=simulated gamepad=] as if it had the same [=input control layout=]. +

    +
    +

    + The emulation module +

    +
    +

    + Types +

    +
    +
    + The emulation.GamepadBounds Type +
    +
    +              emulation.GamepadBounds = {
    +                max: float64,
    +                ? min: float64,
    +              }
    +            
    +
    +
    + max +
    +
    + is the [=logical maximum=] for an [=input value=]. +
    +
    + min +
    +
    + is the [=logical minimum=] for an [=input value=]. +
    +
    +
    +
    +
    + The emulation.SurfaceBounds Type +
    +
    +              emulation.SurfaceBounds = {
    +                x: emulation.GamepadBounds,
    +                y: emulation.GamepadBounds,
    +              }
    +            
    +
    +
    + x +
    +
    + is the logical bounds for the [=touch x coordinate=]. +
    +
    + y +
    +
    + is the logical bounds for the [=touch y coordinate=]. +
    +
    +
    +
    +
    + The emulation.GamepadVibrationEffectType Type +
    +
    +
    + "dual-rumble" +
    +
    + represents the {{GamepadHapticEffectType/"dual-rumble"}} {{GamepadHapticEffectType}}. +
    +
    + "trigger-rumble" +
    +
    + represents the {{GamepadHapticEffectType/"trigger-rumble"}} {{GamepadHapticEffectType}}. +
    +
    +
    +              emulation.GamepadVibrationEffectType = ("dual-rumble", "trigger-rumble")
    +            
    +
    +
    +
    + The emulation.SimulateGamepadParams Type +
    +
    +              emulation.SimulateGamepadParams = {
    +                type: "gamepad",
    +                context: browsingContext.BrowsingContext,
    +                ? axes: [* emulation.GamepadBounds],
    +                ? buttons: [* emulation.GamepadBounds],
    +                ? surfaces: [* emulation.SurfaceBounds],
    +                ? vibration: [* emulation.GamepadVibrationEffectType]
    +              }
    +            
    +
    +
    + type +
    +
    + must be `"gamepad"`. +
    +
    + context +
    +
    + is the [=browsing context=]. +
    +
    + axes +
    +
    + is the logical bounds for each [=axis=]. +
    +
    + buttons +
    +
    + is the logical bounds for each [=button=]. +
    +
    + surfaces +
    +
    + is the logical bounds for each [=touch surface=]. +
    +
    + vibration +
    +
    + is the [=supported effect types=] for the [=vibration actuator=]. +
    +
    +
    +
    +
    + The emulation.RemoveSimulatedDeviceParams Type +
    +
    +              emulation.RemoveSimulatedDeviceParams = {
    +                deviceId: text
    +              }
    +            
    +
    +
    + deviceId +
    +
    + is a random [=version 4 UUID=] identifying the [=simulated gamepad=]. +
    +
    +
    +
    +
    + The emulation.SimulatedDeviceEventParams Type +
    +
    +              emulation.SimulatedDeviceEventParams = {
    +                type: "gamepadVibration",
    +                deviceId: text,
    +                vibration: bool,
    +                strongMagnitude: (0.0..1.0),
    +                weakMagntiude: (0.0..1.0),
    +                leftTrigger: (0.0..1.0),
    +                rightTrigger: (0.0..1.0),
    +              }
    +            
    +
    +
    + type +
    +
    + must be `"gamepadVibration"`. +
    +
    + deviceId +
    +
    + is a [=version 4 UUID=] identifying the [=simulated gamepad=]. +
    +
    + vibration +
    +
    + is `true` if the [=simulated gamepad=]'s [=vibration actuator=] is vibrating, or `false` otherwise. +
    +
    + strongMagnitude +
    +
    + is the intensity level for low-frequency vibration. +
    +
    + weakMagnitude +
    +
    + is the intensity level for high-frequency vibration. +
    +
    + leftTrigger +
    +
    + is the intensity level for vibration localized to the bottom left front button. +
    +
    + rightTrigger +
    +
    + is the intensity level for vibration localized to the bottom right front button. +
    +
    +
    +
    +
    +

    + Commands +

    +
    +
    + The emulation.simulateDevice Command +
    +
    +              emulation.SimulateDevice = {
    +                method: "emulation.simulateDevice",
    +                params: emulation.SimulateGamepadParams,
    +              }
    +              Returns: deviceId: text
    +            
    +

    + The [=remote end steps=] with command parameters |params| are: +

    +
      +
    1. + Let |source| be the result of [=create a gamepad input source=] with |params|. +
    2. +
    3. + Let |simulatedGamepads| be the [=simulated gamepad map=]. +
    4. +
    5. + Let |deviceId:string| be the result of calling [=generate a random UUID=]. +
    6. +
    7. + [=map/Set=] |simulatedGamepads|[|deviceId|] to |source|. +
    8. +
    9. + Run the steps [=when a gamepad becomes available on the system=] for |gamepad|. +
    10. +
    11. + Return |deviceId|. +
    12. +
    +
    +
    +
    + The emulation.removeSimulatedDevice Command +
    +
    +              emulation.RemoveSimulatedDevice = {
    +                method: "emulation.removeSimulatedDevice",
    +                params: emulation.RemoveSimulatedDeviceParams,
    +              }
    +              Returns: void
    +            
    +

    + The [=remote end steps=] with command parameters |params| are: +

    +
      +
    1. + Let |simulatedGamepads| be the [=simulated gamepad map=]. +
    2. +
    3. + If |simulatedGamepads|[|params|.|deviceId|] does not [=map/exist=], abort these steps. +
    4. +
    5. + Let |gamepad| be |simulatedGamepads|[|params|.|deviceId|]. +
    6. +
    7. + Run the steps [=when a gamepad becomes unavailable on the system=] with |gamepad|. +
    8. +
    9. + [=map/Remove=] |simulatedGamepads|[|params|.|deviceId|]. +
    10. +
    +
    +
    +
    +

    + Events +

    +
    +
    + The emulation.simulatedDeviceEvent Event +
    +
    +              emulation.SimulatedDeviceEvent = {
    +                method: "emulation.removeSimulatedDevice",
    +                params: emulation.SimulatedDeviceEventParams,
    +              }
    +            
    +

    + To get the identifier for a simulated gamepad |gamepad|: +

    +
      +
    1. + Let |simulatedGamepads| be the [=simulated gamepad map=]. +
    2. +
    3. + [=map/For each=] |key| → |value| of |simulatedGamepads|: +
        +
      1. + If |value| is |gamepad|, return |key|. +
      2. +
      +
    4. +
    5. + Return `null`. +
    6. +
    +

    + To vibrate a simulated gamepad |gamepad| given a [=navigable=] |navigable| and {{GamepadEffectParameters}} |effectParams|: +

    +
      +
    1. + Let |deviceId:string| be [=the identifier for a simulated gamepad=] |gamepad|. +
    2. +
    3. + If |deviceId| is `null`, abort these steps. +
    4. +
    5. + Let |navigableId| be |navigable|'s [=navigable id=]. +
    6. +
    7. + Let |eventParams| be a [=map=] matching the `emulation.SimulatedDeviceEventParameters` production with the `deviceId` field set to |deviceId|. +
    8. +
    9. + Let |vibration| be `false`. +
    10. +
    11. + Let |channels| be the [=list=] « `"strongMagnitude"`, `"weakMagnitude"`, `"leftTrigger"`, `"rightTrigger"` ». +
    12. +
    13. + [=list/For each=] |channel| of |channels|: +
        +
      1. + If |effectParams|[|channel|] [=map/exists=]: +
          +
        1. + If |effectParams|[|channel|] is greater than 0.0, set |vibration| to `true`. +
        2. +
        3. + [=map/Set=] |eventParams|[|channel|] to |effectParams|[|channel|]. +
        4. +
        +
      2. +
      +
    14. +
    15. + [=map/Set=] |eventParams|[`"vibration"`] to |vibration|. +
    16. +
    17. + Let |body| be a [=map=] matching the `emulation.SimulatedDeviceEvent` production with the `params` field set to |eventParams|. +
    18. +
    19. + Let |relatedNavigables| be a [=set=] containing |navigable|. +
    20. +
    21. + [=set/For each=] |session| in the [=set of sessions for which an event is enabled=] given `"emulation.simulatedDeviceEvent"` and |relatedNavigables|: +
        +
      1. + [=Emit an event=] with |session| and |body|. +
      2. +
      +
    22. +
    +

    + To stop vibration for a simulated gamepad |gamepad| given a [=navigable=] |navigable|: +

    +
      +
    1. + Let |deviceId| be [=the identifier for a simulated gamepad=] |gamepad|. +
    2. +
    3. + If |deviceId| is `null`, abort these steps. +
    4. +
    5. + Let |navigableId| be |navigable|'s [=navigable id=]. +
    6. +
    7. + Let |eventParams| be a [=map=] matching the `emulation.SimulatedDeviceEventParameters` production with the `deviceId` field set to |deviceId| and the `vibration` field set to `false`. +
    8. +
    9. + Let |body| be a [=map=] matching the `emulation.SimulatedDeviceEvent` production with the `params` field set to |eventParams|. +
    10. +
    11. + Let |relatedNavigables| be a [=set=] containing |navigable|. +
    12. +
    13. + [=set/For each=] |session| in the [=set of sessions for which an event is enabled=] given `"emulation.simulatedDeviceEvent"` and |relatedNavigables|: +
        +
      1. + [=Emit an event=] with |session| and |body|. +
      2. +
      +
    14. +
    +
    +
    +
    +
    +

    + The input module +

    +
    +

    + Gamepad Input Source +

    +

    + A gamepad input source is an [=input source=] that is associated with a [=gamepad=]-like device. +

    +

    + A [=gamepad input source=] has the following items: +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Item + + Non-normative Description + + Default Value +
    + axes + + The logical bounds for each [=axis=] input. + +
    + buttons + + The logical bounds for each [=button=] input. + +
    + surfaces + + The logical bounds for each [=touch surface=]. + +
    + vibration + + A [=set=] of `emulation.GamepadVibrationEffectType` containing supported vibration efffect types. + + Empty [=set=] +
    + pendingAxisInputs + + A [=map=] of [=axis=] indices to updated [=axis=] [=input values=]. + + Empty [=map=] +
    + pendingButtonInputs + + A [=map=] of [=button=] indices to updated [=button=] [=input values=]. + + Empty [=map=] +
    + activeTouches + + A [=list=] containing all the [=touch points=] on the [=active touch point list=] for each [=touch surface=]. + + Empty [=list=] +
    +

    + A [=gamepad input source=] supports the same [=pause=] action as a [=null input source=] plus the following actions: +

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Action + + Non-normative Description +
    + GamepadAxisInput + + Used to indicate that the [=input value=] for an [=axis=] has updated. +
    + GamepadButtonInput + + Used to indicate that the [=input values=] for a [=button=] have updated. +
    + GamepadTouchStartAction + + Used to indicate that there is a new [=touch point=] on a [=touch surface=]. +
    + GamepadTouchMoveAction + + Used to indicate that an active [=touch point=] has updated coordinates. +
    + GamepadTouchEndAction + + Used to indicate that a [=touch point=] is no longer active. +
    +

    + To create a gamepad input source object given |params|, return a new [=gamepad input source=] with: +

    +
      +
    • + `axes` set to |params|.axes, +
    • +
    • + `buttons` set to |params|.buttons, +
    • +
    • + `surfaces` set to |params|.surfaces, and +
    • +
    • + `vibration` set to |params|.vibration. +
    • +
    +
    +
    +

    + Actions +

    +

    + To process a gamepad action given |id| and |action item|: +

    +
      +
    1. + Let |subtype| be the result of getting the property named "type" from |action item|. +
    2. +
    3. + If |subtype| is not one of the values `"axis"`, `"button"`, `"touchStart"`, `"touchMove"`, `"touchEnd"`, or `"pause"`, return an [=error=] with [=error code=] [=invalid argument=]. +
    4. +
    5. + Let |action| be an [=action object=] constructed with arguments |id|, `"gamepad"`, and |subtype|. +
    6. +
    7. + If |subtype| is `"pause"`, let |result| be the result of [=trying=] to [=process a pause action=] with arguments |action item| and |action|, and return |result|. +
    8. +
    9. + If |subtype| is `"axis"`, let |result| be the result of [=trying=] to [=process a gamepad axis input action=] with arguments |action item| and |action|, and return |result|. +
    10. +
    11. + If |subtype| is `"button"`, let |result| be the result of [=trying=] to [=process a gamepad button input action=] with arguments |action item| and |action|, and return |result|. +
    12. +
    13. + If |subtype| is `"touchStart"`, let |result| be the result of [=trying=] to [=process a gamepad touch start action=] with arguments |action item| and |action|, and return |result|. +
    14. +
    15. + If |subtype| is `"touchMove"`, let |result| be the result of [=trying=] to [=process a gamepad touch move action=] with arguments |action item| and |action|, and return |result|. +
    16. +
    17. + If |subtype| is `"touchEnd"`, let |result| be the result of [=trying=] to [=process a gamepad touch end action=] with arguments |action item| and |action|, and return |result|. +
    18. +
    19. + Return [=success=] with data |action|. +
    20. +
    +

    + To process the logical input value given |action item| and |action|: +

    +
      +
    1. + Let |value| be the result of getting the property named `"value"` from |action item|. +
    2. +
    3. + If |value| is not a {{Number}}, return [=error=] with [=error code=] [=invalid argument=]. +
    4. +
    5. + Set the `value` property of |action| to |value|. +
    6. +
    +

    + To process a gamepad axis input action given |action item| and |action|: +

    +
      +
    1. + Let |axisIndex| be the result of getting the property named `"axisIndex"` from |action item|. +
    2. +
    3. + If |axisIndex| is not an integer greater than or equal to 0, return [=error=] with [=error code=] [=invalid argument=]. +
    4. +
    5. + Set the `axisIndex` property of |action| to |axisIndex|. +
    6. +
    7. + [=Process the logical input value=] with |action item| and |action|. +
    8. +
    9. + Return [=success=] with data |action|. +
    10. +
    +

    + To process a gamepad button input action given |action item| and |action|: +

    +
      +
    1. + Let |buttonIndex| be the result of getting the property named `"buttonIndex"` from |action item|. +
    2. +
    3. + If |buttonIndex| is not an integer greater than or equal to 0, return [=error=] with [=error code=] [=invalid argument=]. +
    4. +
    5. + Set the `buttonIndex` property of |action| to |buttonIndex|. +
    6. +
    7. + [=Process the logical input value=] with |action item| and |action|. +
    8. +
    9. + Let |pressed| be the result of getting the property named `"pressed"` from |action item|. +
    10. +
    11. + If |pressed| is not a {{Boolean}} or `undefined`, return [=error=] with [=error code=] [=invalid argument=]. +
    12. +
    13. + Set the `pressed` property of |action| to |pressed|. +
    14. +
    15. + Let |touched| be the result of getting the property named `"touched"` from |action item|. +
    16. +
    17. + If |touched| is not a {{Boolean}} or `undefined`, return [=error=] with [=error code=] [=invalid argument=]. +
    18. +
    19. + Set the `touched` property of |action| to |touched|. +
    20. +
    21. + Return [=success=] with data |action|. +
    22. +
    +

    + To process touch id given |action item| and |action|: +

    +
      +
    1. + Let |touchId| be the result of getting the property named `"touchId"` from |action item|. +
    2. +
    3. + If |touchId| is not a {{String}}, return [=error=] with [=error code=] [=invalid argument=]. +
    4. +
    5. + Set the `touchId` property of |action| to |touchId|. +
    6. +
    +

    + To process touch values given |action item| and |action|: +

    +
      +
    1. + Let |x| be the result of getting the property named `"x"` from |action item|. +
    2. +
    3. + If |x| is not a {{Number}}, return [=error=] with [=error code=] [=invalid argument=]. +
    4. +
    5. + Set the `x` property of |action| to |x|. +
    6. +
    7. + Let |y| be the result of getting the property named `"y"` from |action item|. +
    8. +
    9. + If |y| is not a {{Number}}, return [=error=] with [=error code=] [=invalid argument=]. +
    10. +
    11. + Set the `y` property of |action| to |y|. +
    12. +
    +

    + To process a gamepad touch start action given |action item| and |action|: +

    +
      +
    1. + Let |surfaceId| be the result of getting the property named `"surfaceId"` from |action item|. +
    2. +
    3. + If |surfaceId| is not a {{Number}} greater than or equal to 0, return [=error=] with [=error code=] [=invalid argument=]. +
    4. +
    5. + Set the `surfaceId` property of |action| to |surfaceId|. +
    6. +
    7. + [=Process touch id=] given |action item| and |action|. +
    8. +
    9. + [=Process touch values=] with |action item| and |action|. +
    10. +
    11. + Return [=success=] with data |action|. +
    12. +
    +

    + To process a gamepad touch move action given |action item| and |action|: +

    +
      +
    1. + [=Process touch id=] given |action item| and |action|. +
    2. +
    3. + [=Process touch values=] with |action item| and |action|. +
    4. +
    5. + Return [=success=] with data |action|. +
    6. +
    +

    + To process a gamepad touch end action given |action item| and |action|: +

    +
      +
    1. + [=Process touch id=] given |action item| and |action|. +
    2. +
    3. + Return [=success=] with data |action|. +
    4. +
    +
    +            input.GamepadValueProperties = (
    +              value: float64,
    +            )
    +          
    +
    +
    + value +
    +
    + is the [=logical value=] for an [=input value=]. +
    +
    +
    +            input.GamepadTouchCommonProperties = (
    +              touchId: text,
    +            )
    +          
    +
    +
    + touchId +
    +
    + is the identifier for a simulated [=touch point=]. +
    +
    +
    +            input.GamepadTouchValueProperties = (
    +              x: float64,
    +              y: float64,
    +            )
    +          
    +
    +
    + x +
    +
    + is the [=logical value=] for the [=touch x coordinate=]. +
    +
    + y +
    +
    + is the [=logical value=] for the [=touch y coordinate=]. +
    +
    +
    +            input.GamepadSourceAction = (
    +              input.PauseAction /
    +              input.GamepadAxisInput /
    +              input.GamepadButtonInput /
    +              input.GamepadTouchStartAction /
    +              input.GamepadTouchMoveAction /
    +              input.GamepadTouchEndAction
    +            )
    +
    +            input.GamepadSourceActions = {
    +              type: "gamepad",
    +              deviceId: text,
    +              actions: [* input.GamepadSourceAction],
    +            }
    +          
    +
    +
    + type +
    +
    + must be `"gamepad"`. +
    +
    + deviceId +
    +
    + is a [=version 4 UUID=] identifying a [=simulated gamepad=]. +
    +
    + actions +
    +
    + is a [=list=] of actions representing the [=simulated gamepad=]'s updated [=input values=]. +
    +
    +
    +
    + The input.GamepadAxisInput Action +
    +
    +              input.GamepadAxisInput = {
    +                type: "axis",
    +                axisIndex: uint,
    +                value: float64,
    +              }
    +            
    +
    +
    + type +
    +
    + must be `"axis"`. +
    +
    + axisIndex +
    +
    + is the index of the updated [=axis=] in the [=axis list=]. +
    +
    + value +
    +
    + is the updated [=logical value=]. +
    +
    +
    +
    +
    + The input.GamepadButtonInput Action +
    +
    +              input.GamepadButtonInput = {
    +                type: "button",
    +                buttonIndex: uint,
    +                value: float64,
    +                ? pressed: bool,
    +                ? touched: bool,
    +              }
    +            
    +
    +
    + type +
    +
    + must be `"button"`. +
    +
    + buttonIndex +
    +
    + is the index of the updated [=button=] in the [=button list=]. +
    +
    + value +
    +
    + is the updated [=logical value=]. +
    +
    +
    +
    +
    + The input.GamepadTouchStartAction Action +
    +
    +              input.GamepadTouchStartAction = {
    +                type: "touchStart",
    +                surfaceId: uint,
    +                input.GamepadTouchCommonProperties,
    +                input.GamepadTouchValueProperties,
    +              }
    +            
    +
    +
    + type +
    +
    + must be `"touchStart"`. +
    +
    + surfaceId +
    +
    + is the {{GamepadTouch/surfaceId}} of the [=touch surface=] containing the simulated [=touch point=]. +
    +
    +
    +
    +
    + The input.GamepadTouchMoveAction Action +
    +
    +              input.GamepadTouchMoveAction = {
    +                type: "touchMove",
    +                input.GamepadTouchCommonProperties,
    +                input.GamepadTouchValueProperties,
    +              }
    +            
    +
    +
    + type +
    +
    + must be `"touchMove"`. +
    +
    +
    +
    +
    + The input.GamepadTouchEndAction Action +
    +
    +              input.GamepadTouchEndAction = {
    +                type: "touchEnd",
    +                input.GamepadTouchCommonProperties,
    +              }
    +            
    +
    +
    + type +
    +
    + must be `"touchEnd"`. +
    +
    +
    +

    + Extend the dispatch action algorithm table in [=dispatch tick actions=] with the following table: +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + source type + + subtype + + Dispatch action algorithm +
    + `"gamepad"` + + `"axis"` + + [=Dispatch a gamepad axis action=] +
    + `"gamepad"` + + `"button"` + + [=Dispatch a gamepad button action=] +
    + `"gamepad"` + + `"touchStart"` + + [=Dispatch a gamepad touch start action=] +
    + `"gamepad"` + + `"touchMove"` + + [=Dispatch a gamepad touch move action=] +
    + `"gamepad"` + + `"touchEnd"` + + [=Dispatch a gamepad touch end action=] +
    + `"gamepad"` + + `"pause"` + + [=Dispatch a gamepad pause action=] +
    +

    + To normalize a simulated input value given |logical value| and |bounds|: +

    +
      +
    1. + Let |logical minimum| be equal to |bounds|'s `min` property. +
    2. +
    3. + If |logical minimum| is `undefined`, set |logical minimum| to 0. +
    4. +
    5. + Let |logical maximum| be equal to |bounds|'s `max` property. +
    6. +
    7. + Return the result of [=normalize a logical axis value=] with |logical value|, |logical minimum|, and |logical maximum|. +
    8. +
    +

    + To dispatch a gamepad axis action given |action object|, |source|, |global key state|, |tick duration|, |browsing context|, and |actions options|: +

    +
      +
    1. + Let |device id| be equal to |action object|'s `deviceId` property. +
    2. +
    3. + Let |axis index| be equal to |action object|'s `axisIndex` property. +
    4. +
    5. + Let |logical value| be equal to |action object|'s `value` property. +
    6. +
    7. + Let |axes| be equal to |source|'s axes. +
    8. +
    9. + Let |bounds| be equal to |axes|[|axis index|]. +
    10. +
    11. + If |bounds| is `undefined`, return [=success=] with data `null`. +
    12. +
    13. + Let |simulated gamepads| be the [=simulated gamepad map=]. +
    14. +
    15. + If |simulated gamepads|[|device id|] does not [=map/exist=], abort these steps. +
    16. +
    17. + Let |pending axis inputs| be |source|'s pendingAxisInputs. +
    18. +
    19. + Set |pending axis inputs|[|axis index|] to the result of [=normalize a simulated input value=] given |logical value| and |bounds|. +
    20. +
    21. + Return [=success=] with data `null`. +
    22. +
    +

    + To dispatch a gamepad button action given |action object|, |source|, |global key state|, |tick duration|, |browsing context|, and |actions options|: +

    +
      +
    1. + Let |device id| be equal to |action object|'s `deviceId` property. +
    2. +
    3. + Let |button index| be equal to |action object|'s `buttonIndex` property. +
    4. +
    5. + Let |logical value| be equal to |action object|'s `value` property. +
    6. +
    7. + Let |pressed| be equal to |action object|'s `pressed` property. +
    8. +
    9. + Let |touched| be equal to |action object|'s `touched` property. +
    10. +
    11. + Let |buttons| be equal to |source|'s buttons. +
    12. +
    13. + Let |bounds| be equal to |buttons|[|button index|]. +
    14. +
    15. + If |bounds| is `undefined`, return [=success=] with data `null`. +
    16. +
    17. + Let |pending button inputs| be |source|'s pendingButtonInputs. +
    18. +
    19. + Set |pending button inputs|[|button index|].value to the result of [=normalize a simulated input value=] given |logical value| and |bounds|. +
    20. +
    21. + Set |pending button inputs|[|button index|].value to |pressed|. +
    22. +
    23. + Set |pending button inputs|[|button index|].value to |touched|. +
    24. +
    25. + Return [=success=] with data `null`. +
    26. +
    +

    + To compute the surface dimension given |bounds|: +

    +
      +
    1. + Let |logical minimum| be equal to |bounds|'s `min` property. +
    2. +
    3. + If |logical minimum| is `undefined`, set |logical minimum| to 0. +
    4. +
    5. + Let |logical maximum| be equal to |bounds|'s `max` property. +
    6. +
    7. + Return |logical maximum| - |logical minimum|. +
    8. +
    +

    + To dispatch a gamepad touch start action given |action object|, |source|, |global key state|, |tick duration|, |browsing context|, and |actions options|: +

    +
      +
    1. + Let |device id| be equal to |action object|'s `deviceId` property. +
    2. +
    3. + Let |surface id| be equal to |action object|'s `surfaceId` property. +
    4. +
    5. + Let |touch id| be equal to |action object|'s `touchId` property. +
    6. +
    7. + Let |x logical value| be equal to |action object|'s `x` property. +
    8. +
    9. + Let |y logical value| be equal to |action object|'s `y` property. +
    10. +
    11. + Let |surfaces| be |source|'s surfaces. +
    12. +
    13. + Let |bounds| be |surfaces|[|surface id|]. +
    14. +
    15. + If |bounds| is `undefined`, return [=success=] with data `null`. +
    16. +
    17. + Let |x bounds| be equal to |bounds|'s `x` property. +
    18. +
    19. + Let |y bounds| be equal to |bounds|'s `y` property. +
    20. +
    21. + Let |touch| be a new {{Object}}. +
    22. +
    23. + Set |touch|.surfaceId to |surface id|. +
    24. +
    25. + Set |touch|.touchId to |touch id|. +
    26. +
    27. + Set |touch|.x to the result of [=normalize a simulated input value=] given |x logical value| and |x bounds|. +
    28. +
    29. + Set |touch|.y to the result of [=normalize a simulated input value=] given |y logical value| and |y bounds|. +
    30. +
    31. + Set |touch|.width to the result of [=compute the surface dimension=] given |x bounds|. +
    32. +
    33. + Set |touch|.height to the result of [=compute the surface dimension=] given |y bounds|. +
    34. +
    35. + Let |active touches| be |source|'s activeTouches. +
    36. +
    37. + [=list/Append=] |touch| to |active touches|. +
    38. +
    39. + Return [=success=] with data `null`. +
    40. +
    +

    + To find the active touch given |source| and |touch id|: +

    +
      +
    1. + Let |activeTouches| be |source|'s `activeTouches` property. +
    2. +
    3. + [=list/For each=] |touch| in |activeTouches|: +
        +
      1. + If |touch|'s `touchId` property is equal to |touch id|, return |touch|. +
      2. +
      +
    4. +
    5. + Return `null`. +
    6. +
    +

    + To dispatch a gamepad touch move action given |action object|, |source|, |global key state|, |tick duration|, |browsing context|, and |actions options|: +

    +
      +
    1. + Let |device id| be equal to |action object|'s `deviceId` property. +
    2. +
    3. + Let |touch id| be equal to |action object|'s `touchId` property. +
    4. +
    5. + Let |x logical value| be equal to |action object|'s `x` property. +
    6. +
    7. + Let |y logical value| be equal to |action object|'s `y` property. +
    8. +
    9. + Let |touch| be the result of [=find the active touch=] given |source| and |touch id|. +
    10. +
    11. + If |touch| is `null`, return [=success=] with data `null`. +
    12. +
    13. + Let |surfaces| be |source|'s surfaces. +
    14. +
    15. + Let |bounds| be |surfaces|[|touch|.surfaceId]. +
    16. +
    17. + If |bounds| is `undefined`, return [=success=] with data `null`. +
    18. +
    19. + Let |x bounds| be equal to |bounds|'s `x` property. +
    20. +
    21. + Let |y bounds| be equal to |bounds|'s `y` property. +
    22. +
    23. + Set |touch|.x to the result of [=normalize a simulated input value=] given |x logical value| and |x bounds|. +
    24. +
    25. + Set |touch|.y to the result of [=normalize a simulated input value=] given |y logical value| and |y bounds|. +
    26. +
    27. + Set |touch|.width to the result of [=compute the surface dimension=] given |x bounds|. +
    28. +
    29. + Set |touch|.height to the result of [=compute the surface dimension=] given |y bounds|. +
    30. +
    31. + Return [=success=] with data `null`. +
    32. +
    +

    + To dispatch a gamepad touch end action given |action object|, |source|, |global key state|, |tick duration|, |browsing context|, and |actions options|: +

    +
      +
    1. + Let |device id| be equal to |action object|'s `deviceId` property. +
    2. +
    3. + Let |touch id| be equal to |action object|'s `touchId` property. +
    4. +
    5. + Let |active touches| be |source|'s activeTouches. +
    6. +
    7. + [=list/Remove=] all items with `touchId` property equal to |touch id| from |active touches|. +
    8. +
    9. + Return [=success=] with data `null`. +
    10. +
    +

    + To dispatch a gamepad pause action given |action object|, |source|, |global key state|, |tick duration|, |browsing context|, and |actions options|: +

    +
      +
    1. + Let |device id| be equal to |action object|'s `deviceId` property. +
    2. +
    3. + Run the steps for when the system [=receives new button or axis input values=] with |gamepad|. +
    4. +
    5. + Return [=success=] with data `null`. +
    6. +
    +

    + To update simulated gamepad state given |gamepad|: +

    +
      +
    1. + Let |source| be the [=gamepad input source=] for |gamepad|. +
    2. +
    3. + [=map/For each=] |index| → |value| of |source|.pendingAxisInputs, set |gamepad|.{{Gamepad/[[axes]]}}[|index|] to |value|. +
    4. +
    5. + [=map/For each=] |index| → |value| of |source|.pendingButtonInputs, set |gamepad|.{{Gamepad/[[buttons]]}}[|index|] to |value|. +
    6. +
    7. + [=list/Empty=] |gamepad|.{{Gamepad/[[touches]]}}. +
    8. +
    9. +

      + [=list/For each=] |touch| of |source|.activeTouches: +

      +
        +
      1. + Let |position| be a [=new=] {{DOMPointReadOnly}} with {{DOMPointReadOnly/x}} initialized to |touch|.x and {{DOMPointReadOnly/y}} initialized to |touch|.y. +
      2. +
      3. +

        + If |touch|.width and |touch|.height are not `undefined`, let |surfaceDimensions| be a new {{DOMRectReadOnly}} with {{DOMRectReadOnly/width}} initialized to |touch|.width and {{DOMRectReadOnly/height}} initialized to |touch|.height. +

        +

        + Otherwise, let |surfaceDimensions| be `null`. +

        +
      4. + Let |gamepadTouch| be a [=new=] {{GamepadTouch}} with its {{GamepadTouch/touchId}} set to |touch|.touchId, its {{GamepadTouch/surfaceId}} set to |touch|.surfaceId, its {{GamepadTouch/position}} set to |position|, and its {{GamepadTouch/surfaceDimensions}} set to |surfaceDimensions|. +
      5. +
      6. + [=list/Append=] |gamepadTouch| to |gamepad|.{{Gamepad/[[touches]]}}. +
      7. +
      +
    10. +
    +
    +
    +