diff --git a/index.html b/index.html index 0368aa4..3c7401e 100644 --- a/index.html +++ b/index.html @@ -63,10 +63,9 @@ implementationReportURI: "https://wpt.fyi/results/gamepad", caniuse: "gamepad", - xref: ["HTML", "DOM"], + xref: ["HTML", "DOM", "PERMISSIONS-POLICY", "HR-TIME", "Infra"], }; -
@@ -85,14 +84,14 @@

Introduction

- Some user agents have connected gamepad devices. These devices - are desirable and suited to input for gaming applications, and for "10 + Some [=user agent=]s have connected gamepad devices. These devices are + desirable and suited to input for gaming applications, and for "10 foot" user interfaces (presentations, media viewers).

Currently, the only way for a gamepad to be used as input would be to emulate mouse or keyboard events, however this would lose information - and require additional software outside of the user agent to + and require additional software outside of the [=user agent=] to accomplish emulation.

@@ -104,22 +103,6 @@

interfaces that allow web applications to directly act on gamepad data.

-
-

- Dependencies -

-

- This specification references interfaces from a number of other - specifications: -

- -

Scope @@ -170,21 +153,178 @@

readonly attribute FrozenArray<GamepadButton> buttons; }; +

+ The algorithms used to communicate with the system typically complete + asynchronously, queuing work on the gamepad task source. +

+

+ Instances of {{Gamepad}} are created with the internal slots described + in the following table: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Internal slot + + Initial value + + Description (non-normative) +
+ [[\connected]] + + `false` + + A flag indicating that the device is connected to the system +
+ [[\timestamp]] + + undefined + + The last time data for this {{Gamepad}} was updated +
+ [[\axes]] + + An empty [=sequence=] + + A [=sequence=] of {{double}} values representing the current state + of axes exposed by this device +
+ [[\buttons]] + + An empty [=sequence=] + + A [=sequence=] of {{GamepadButton}} objects representing the + current state of buttons exposed by this device +
+ [[\exposed]] + + `false` + + A flag indicating that the {{Gamepad}} object has been exposed to + script +
+ [[\axisMapping]] + + An empty [=ordered map=] + + Mapping from unmapped axis index to an index in the + {{Gamepad/axes}} array +
+ [[\axisMinimums]] + + An empty [=list=] + + A [=list=] containing the minimum logical value for each axis +
+ [[\axisMaximums]] + + An empty [=list=] + + A [=list=] containing the maximum logical value for each axis +
+ [[\buttonMapping]] + + An empty [=ordered map=] + + Mapping from unmapped button index to an index in the + {{Gamepad/buttons}} array +
+ [[\buttonMinimums]] + + An empty [=list=] + + A [=list=] containing the minimum logical value for each button. +
+ [[\buttonMaximums]] + + An empty [=list=] + + A [=list=] containing the maximum logical value for each button +
id attribute
- An identification string for the gamepad. This string identifies the - brand or style of connected gamepad device. Typically, this will - include the USB vendor and a product ID. +

+ An identification string for the gamepad. This string identifies + the brand or style of connected gamepad device. +

+

+ The exact format of the {{Gamepad/id}} string is left unspecified. + It is RECOMMENDED that the [=user agent=] select a string that + identifies the product without uniquely identifying the device. For + example, a USB gamepad may be identified by its `idVendor` and + `idProduct` values. Unique identifiers like serial numbers or + Bluetooth device addresses MUST NOT be included in the + {{Gamepad/id}} string. +

index attribute
The index of the gamepad in the {{Navigator}}. When multiple gamepads - are connected to a user agent, indices MUST be assigned on a + are connected to a [=user agent=], indices MUST be assigned on a first-come, first-serve basis, starting at zero. If a gamepad is disconnected, previously assigned indices MUST NOT be reassigned to gamepads that continue to be connected. However, if a gamepad is @@ -195,69 +335,521 @@

connected attribute
- Indicates whether the physical device represented by this object is - still connected to the system. When a gamepad becomes unavailable, - whether by being physically disconnected, powered off or otherwise - unusable, the `connected` attribute MUST be set to false. +

+ Indicates whether the physical device represented by this object is + still connected to the system. When a gamepad becomes unavailable, + whether by being physically disconnected, powered off or otherwise + unusable, the {{Gamepad/connected}} attribute MUST be set to + `false`. +

+

+ The {{Gamepad/connected}} getter steps are: +

+
    +
  1. Return [=this=].{{Gamepad/[[connected]]}}. +
  2. +
timestamp attribute
- Last time the data for this gamepad was updated. Timestamp is a - monotonically increasing value that allows the author to determine if - the axes and button data have been updated from the - hardware. The value must be relative to the - navigationStart attribute of the - PerformanceTiming interface. Since values are monotonically - increasing they can be compared to determine the ordering of updates, - as newer values will always be greater than or equal to older values. - If no data has been received from the hardware, the value of the - timestamp attribute should be the time relative to - navigationStart when the Gamepad object was first - made available to script. +

+ The {{Gamepad/timestamp}} allows the author to determine the last + time the {{Gamepad/axes}} or {{Gamepad/buttons}} attribute for this + gamepad was updated. The value MUST be set to the [=current high + resolution time=] each time the system [=receives new button or + axis input values=] from the device. If no data has been received + from the hardware, {{Gamepad/timestamp}} MUST be the [=current high + resolution time=] at the time when the {{Gamepad}} was first made + available to script. +

+

+ [=User agent=]s SHOULD set a minimum resolution of |gamepad|'s + {{Gamepad/timestamp}} attribute to 5 microseconds, following + [[HR-TIME]]'s clock resolution recommendation. +

+

+ The {{Gamepad/timestamp}} getter steps are: +

+
    +
  1. Return [=this=].{{Gamepad/[[timestamp]]}}. +
  2. +
mapping attribute
- The mapping in use for this device. If the user agent has knowledge - of the layout of the device, then it SHOULD indicate that a mapping - is in use by setting this property to a known mapping name. Currently - the only known mapping is {{GamepadMappingType["standard"]}}, which - corresponds to the Standard Gamepad layout. If the user agent - does not have knowledge of the device layout and is simply providing - the controls as represented by the driver in use, then it MUST set - the `mapping` property to the empty string. +

+ The mapping in use for this device. If the user agent has knowledge + of the layout of the device, then it SHOULD indicate that a mapping + is in use by setting {{Gamepad/mapping}} to the corresponding + {{GamepadMappingType}} value. +

+

+ To select a mapping for a + gamepad device, run the following steps: +

+
    +
  1. If the button and axis layout of the gamepad device corresponds + with the [=Standard Gamepad=] layout, then return + {{GamepadMappingType/"standard"}}. +
  2. +
  3. Return {{GamepadMappingType/""}}. +
  4. +
axes attribute
- Array of values for all axes of the gamepad. All axis values MUST be - linearly normalized to the range [-1.0 .. 1.0]. If the controller is - perpendicular to the ground with the directional stick pointing up, - -1.0 SHOULD correspond to "forward" or "left", and 1.0 SHOULD - correspond to "backward" or "right". Axes that are drawn from a 2D - input device SHOULD appear next to each other in the axes array, X - then Y. It is RECOMMENDED that axes appear in decreasing order of - importance, such that element 0 and 1 typically represent the X and Y - axis of a directional stick. The same object MUST be returned until - the user agent needs to return different values (or values in - a different order). +

+ Array of values for all axes of the gamepad. All axis values MUST + be linearly normalized to the range [-1.0 .. 1.0]. If the + controller is perpendicular to the ground with the directional + stick pointing up, -1.0 SHOULD correspond to "forward" or "left", + and 1.0 SHOULD correspond to "backward" or "right". Axes that are + drawn from a 2D input device SHOULD appear next to each other in + the axes array, X then Y. It is RECOMMENDED that axes appear in + decreasing order of importance, such that element 0 and 1 typically + represent the X and Y axis of a directional stick. The same object + MUST be returned until the [=user agent=] needs to return different + values (or values in a different order). +

+

+ The {{Gamepad/axes}} getter steps are: +

+
    +
  1. Return [=this=].{{Gamepad/[[axes]]}}. +
  2. +
buttons attribute
- Array of button states for all buttons of the gamepad. It is - RECOMMENDED that buttons appear in decreasing importance such that - the primary button, secondary button, tertiary button, and so on - appear as elements 0, 1, 2, ... in the buttons array. The same object - MUST be returned until the user agent needs to return - different values (or values in a different order). +

+ Array of button states for all buttons of the gamepad. It is + RECOMMENDED that buttons appear in decreasing importance such that + the primary button, secondary button, tertiary button, and so on + appear as elements 0, 1, 2, ... in the buttons array. The same + object MUST be returned until the [=user agent=] needs to return + different values (or values in a different order). +

+

+ The {{Gamepad/buttons}} getter steps are: +

+
    +
  1. Return [=this=].{{Gamepad/[[buttons]]}}. +
  2. +

+
+

+ Receiving inputs +

+

+ When the system receives new button or axis input values, + run the following steps: +

+
    +
  1. Let |gamepad:Gamepad| be the {{Gamepad}} object representing the + device that received new button or axis input values. +
  2. +
  3. [=Queue a task=] on the [=gamepad task source=] to [=update + gamepad state=] for |gamepad|. +
  4. +
+

+ To update gamepad state for |gamepad:Gamepad|, run the + following steps: +

+
    +
  1. Let |now:DOMHighResTimeStamp| be the [=current high resolution + time=]. +
  2. +
  3. Set |gamepad|.{{Gamepad/[[timestamp]]}} to |now|. +
  4. +
  5. Run the steps to [=map and normalize axes=] for |gamepad|. +
  6. +
  7. Run the steps to [=map and normalize buttons=] for |gamepad|. +
  8. +
  9. Let |navigator:Navigator| be |gamepad|'s [=relevant global + object=]'s {{Navigator}} object. +
  10. +
  11. If |navigator|.{{Navigator/[[hasGamepadGesture]]}} is `false` and + |gamepad| [=contains a gamepad user gesture=]: +
      +
    1. Set |navigator|.{{Navigator/[[hasGamepadGesture]]}} to + `true`. +
    2. +
    3. [=list/For each=] |connectedGamepad:Gamepad?| of + |navigator|.{{Navigator/[[gamepads]]}}: +
        +
      1. If |connectedGamepad| is not equal to `null`: +
          +
        1. Set |connectedGamepad|.{{Gamepad/[[exposed]]}} to + `true`. +
        2. +
        3. Set |connectedGamepad|.{{Gamepad/[[timestamp]]}} to + |now|. +
        4. +
        5. Let |document:Document?| be |gamepad|'s [=relevant + global object=]'s [=associated `Document`=]; otherwise + `null`. +
        6. +
        7. If |document| is not `null` and is [=Document/fully + active=], then [=queue a task=] on the [=gamepad task + source=] to [=fire an event=] named {{gamepadconnected}} + at |gamepad|'s [=relevant global object=] using + {{GamepadEvent}} with its {{GamepadEvent/gamepad}} + attribute initialized to |connectedGamepad|. +
        8. +
        +
      2. +
      +
    4. +
    +
  12. +
+

+ To map and normalize axes for |gamepad:Gamepad|, run the + following steps: +

+
    +
  1. Let |axisValues:list| be a [=list=] of {{unsigned long}} values + representing the most recent logical axis input values for each axis + input of the device represented by |gamepad|. +
  2. +
  3. Let |maxRawAxisIndex:long| be the [=list/size=] of |axisValues| − + 1. +
  4. +
  5. [=list/For each=] |rawAxisIndex:long| of [=the range=] from 0 to + |maxRawAxisIndex|: +
      +
    1. Let |mappedIndex:long| be + |gamepad|.{{Gamepad/[[axisMapping]]}}[|rawAxisIndex|]. +
    2. +
    3. Let |logicalValue:unsigned long| be + |axisValues|[|rawAxisIndex|]. +
    4. +
    5. Let |logicalMinimum:unsigned long| be + |gamepad|.{{Gamepad/[[axisMinimums]]}}[|rawAxisIndex|]. +
    6. +
    7. Let |logicalMaximum:unsigned long| be + |gamepad|.{{Gamepad/[[axisMaximums]]}}[|rawAxisIndex|]. +
    8. +
    9. Let |normalizedValue:double| be 2 (|logicalValue| − + |logicalMinimum|) / (|logicalMaximum| − |logicalMinimum|) − 1. +
    10. +
    11. Set |gamepad|.{{Gamepad/[[axes]]}}[|axisIndex|] to be + |normalizedValue|. +
    12. +
    +
  6. +
+

+ To map and normalize buttons for |gamepad:Gamepad|, run + the following steps: +

+
    +
  1. Let |buttonValues:list| be a [=list=] of {{unsigned long}} values + representing the most recent logical button input values for each + button input of the device represented by |gamepad|. +
  2. +
  3. Let |maxRawButtonIndex:long| be the [=list/size=] of + |buttonValues| − 1. +
  4. +
  5. [=list/For each=] |rawButtonIndex:long| of [=the range=] from 0 + to |maxRawButtonIndex|: +
      +
    1. Let |mappedIndex:long| be + |gamepad|.{{Gamepad/[[buttonMapping]]}}[|rawButtonIndex|]. +
    2. +
    3. Let |logicalValue:unsigned long| be + |buttonValues|[|rawButtonIndex|]. +
    4. +
    5. Let |logicalMinimum:unsigned long| be + |gamepad|.{{Gamepad/[[buttonMinimums]]}}[|rawButtonIndex|]. +
    6. +
    7. Let |logicalMaximum:unsigned long| be + |gamepad|.{{Gamepad/[[buttonMaximums]]}}[|rawButtonIndex|]. +
    8. +
    9. Let |normalizedValue:double| be (|logicalValue| − + |logicalMinimum|) / (|logicalMaximum| − |logicalMinimum|). +
    10. +
    11. Let |button:GamepadButton| be + |gamepad|.{{Gamepad/[[buttons]]}}[|mappedIndex|]. +
    12. +
    13. Set |button|.{{GamepadButton/[[value]]}} to + |normalizedValue|. +
    14. +
    15. +

      + If the button has a digital switch to indicate a pure pressed + or released state, set |button|.{{GamepadButton/[[pressed]]}} + to `true` if the button is pressed or `false` if it is not + pressed. +

      +

      + Otherwise, set |button|.{{GamepadButton/[[pressed]]}} to + `true` if the value is above the [=button press threshold=] + or `false` if it is not above the threshold. +

      +
    16. +
    17. +

      + If the button is capable of detecting touch, set + |button|.{{GamepadButton/[[touched]]}} to `true` if the + button is currently being touched. +

      +

      + Otherwise, set |button|.{{GamepadButton/[[touched]]}} to + |button|.{{GamepadButton/[[pressed]]}}. +

      +
    18. +
    +
  6. +
+
+
+

+ Constructing a `Gamepad` +

+

+ A new `Gamepad` representing a connected gamepad device is + constructed by performing the following steps: +

+
    +
  1. Let |gamepad:Gamepad| be a newly created {{Gamepad}} instance: +
      +
    1. Initialize |gamepad|'s {{Gamepad/id}} attribute to an + identification string for the gamepad. +
    2. +
    3. Initialize |gamepad|'s {{Gamepad/index}} attribute to the + result of [=selecting an unused gamepad index=] for |gamepad|. +
    4. +
    5. Initialize |gamepad|'s {{Gamepad/mapping}} attribute to the + result of [=selecting a mapping=] for the gamepad device. +
    6. +
    7. Initialize |gamepad|.{{Gamepad/[[connected]]}} to `true`. +
    8. +
    9. Initialize |gamepad|.{{Gamepad/[[timestamp]]}} to the + [=current high resolution time=]. +
    10. +
    11. Initialize |gamepad|.{{Gamepad/[[axes]]}} to the result of + [=initializing axes=] for |gamepad|. +
    12. +
    13. Initialize |gamepad|.{{Gamepad/[[buttons]]}} to the result of + [=initializing buttons=] for |gamepad|. +
    14. +
    +
  2. +
  3. Return |gamepad|. +
  4. +
+

+ To select an unused + gamepad index for |gamepad:Gamepad|, run the following steps: +

+
    +
  1. Let |navigator:Navigator| be |gamepad|'s [=relevant global + object=]'s {{Navigator}} object. +
  2. +
  3. Let |maxGamepadIndex:long| be the [=list/size=] of + |navigator|.{{Navigator/[[gamepads]]}} − 1. +
  4. +
  5. [=list/For each=] |gamepadIndex:long| of [=the range=] from 0 to + |maxGamepadIndex|: +
      +
    1. If |navigator|.{{Navigator/[[gamepads]]}}[|gamepadIndex|] is + `null`, then return |gamepadIndex|. +
    2. +
    +
  6. +
  7. [=list/Append=] `null` to |navigator|.{{Navigator/[[gamepads]]}}. +
  8. +
  9. Return the [=list/size=] of + |navigator|.{{Navigator/[[gamepads]]}} − 1. +
  10. +
+

+ To initialize axes for + |gamepad:Gamepad|, run the following steps: +

+
    +
  1. Let |inputCount:long| be the number of axis inputs exposed by the + device represented by |gamepad|. +
  2. +
  3. Set |gamepad|.{{Gamepad/[[axisMinimums]]}} to a [=list=] of + {{unsigned long}} values with [=list/size=] equal to |inputCount| + containing minimum logical values for each of the axis inputs. +
  4. +
  5. Set |gamepad|.{{Gamepad/[[axisMaximums]]}} to a [=list=] of + {{unsigned long}} values with [=list/size=] equal to |inputCount| + containing maximum logical values for each of the axis inputs. +
  6. +
  7. Initialize |unmappedInputList| to be an empty [=list=]. +
  8. +
  9. Initialize |mappedIndexList| to be an empty [=list=]. +
  10. +
  11. Initialize |axesSize:long| to be 0. +
  12. +
  13. [=list/For each=] |rawInputIndex:long| of [=the range=] from 0 to + |inputCount| − 1: +
      +
    1. If the the gamepad axis at index |rawInputIndex| [=represents + a Standard Gamepad axis=]: +
        +
      1. Let |canonicalIndex:long| be the [=canonical index=] for + the axis. +
      2. +
      3. If |mappedIndexList| [=list/contain=]s |canonicalIndex|, + then append |rawInputIndex| to |unmappedInputList|. +

        + Otherwise: +

        +
          +
        1. Set + |gamepad|.{{Gamepad/[[axisMapping]]}}[|rawInputIndex|] to + |canonicalIndex|. +
        2. +
        3. [=list/Append=] |canonicalIndex| to + |mappedIndexList|. +
        4. +
        5. If |canonicalIndex| + 1 is greater than |axesSize|, + then set |axesSize| to |canonicalIndex| + 1. +
        6. +
        +
      4. +
      +

      + Otherwise, [=list/append=] |rawInputIndex| to + |unmappedInputList|. +

      +
    2. +
    +
  14. +
  15. Initialize |axisIndex:long| to be 0. +
  16. +
  17. [=list/For each=] |rawInputIndex:long| of |unmappedInputList|: +
      +
    1. While |mappedIndexList| [=list/contain=]s |axisIndex|: +
        +
      1. Increment |axisIndex|. +
      2. +
      +
    2. +
    3. Set |gamepad|.{{Gamepad/[[axisMapping]]}}[|rawInputIndex|] to + |axisIndex|. +
    4. +
    5. [=list/Append=] |axisIndex| to |mappedIndexList|. +
    6. +
    7. If |axisIndex| + 1 is greater than |axesSize|, then set + |axesSize| to |axisIndex| + 1. +
    8. +
    +
  18. +
  19. Initialize |axes| to be an empty [=list=]. +
  20. +
  21. [=list/For each=] |axisIndex:long| of [=the range=] from 0 to + |axesSize| − 1, [=list/append=] 0 to |axes|. +
  22. +
  23. Return |axes|. +
  24. +
+

+ To initialize buttons for a + gamepad, run the following steps: +

+
    +
  1. Let |inputCount:long| be the number of button inputs exposed by + the device represented by |gamepad|. +
  2. +
  3. Set |gamepad|.{{Gamepad/[[buttonMinimums]]}} to be a [=list=] of + {{unsigned long}} values with [=list/size=] equal to |inputCount| + containing minimum logical values for each of the button inputs. +
  4. +
  5. Set |gamepad|.{{Gamepad/[[buttonMaximums]]}} to be a [=list=] of + {{unsigned long}} values with [=list/size=] equal to |inputCount| + containing maximum logical values for each of the button inputs. +
  6. +
  7. Initialize |unmappedInputList| to be an empty [=list=]. +
  8. +
  9. Initialize |mappedIndexList| to be an empty [=list=]. +
  10. +
  11. Initialize |buttonsSize:long| to be 0. +
  12. +
  13. [=list/For each=] |rawInputIndex:long| of [=the range=] from 0 to + |inputCount| − 1: +
      +
    1. If the the gamepad button at index |rawInputIndex| + [=represents a Standard Gamepad button=]: +
        +
      1. Let |canonicalIndex:long| be the [=canonical index=] for + the button. +
      2. +
      3. If |mappedIndexList| [=list/contain=]s |canonicalIndex|, + then append |rawInputIndex| to |unmappedInputList|. +

        + Otherwise: +

        +
          +
        1. Set + |gamepad|.{{Gamepad/[[buttonMapping]]}}[|rawInputIndex|] + to |canonicalIndex|. +
        2. +
        3. [=list/Append=] |canonicalIndex| to + |mappedIndexList|. +
        4. +
        5. If |canonicalIndex| + 1 is greater than + |buttonsSize|, then set |buttonsSize| to |canonicalIndex| + + 1. +
        6. +
        +
      4. +
      +

      + Otherwise, [=list/append=] |rawInputIndex| to + |unmappedInputList|. +

      +
    2. +
    3. Increment |rawInputIndex|. +
    4. +
    +
  14. +
  15. Initialize |buttonIndex:long| to be 0. +
  16. +
  17. [=list/For each=] |rawInputIndex:long| of |unmappedInputList|: +
      +
    1. While |mappedIndexList| [=list/contain=]s |buttonIndex|: +
        +
      1. Increment |buttonIndex|. +
      2. +
      +
    2. +
    3. Set |gamepad|.{{Gamepad/[[buttonMapping]]}}[|rawInputIndex|] + to |buttonIndex|. +
    4. +
    5. [=list/Append=] |buttonIndex| to |mappedIndexList|. +
    6. +
    7. If |buttonIndex| + 1 is greater than |buttonsSize|, then set + |buttonsSize| to |buttonIndex| + 1. +
    8. +
    +
  18. +
  19. Initialize |buttons| to be an empty [=list=]. +
  20. +
  21. [=list/For each=] |buttonIndex:long| of [=the range=] from 0 to + |buttonsSize| − 1, [=list/append=] a [=new=] {{GamepadButton}} to + |buttons|. +
  22. +
  23. Return |buttons|. +
  24. +
+

@@ -275,43 +867,121 @@

readonly attribute double value; }; +

+ Instances of {{GamepadButton}} are created with the internal slots + described in the following table: +

+ + + + + + + + + + + + + + + + + + + + + +
+ Internal slot + + Initial value + + Description (non-normative) +
+ [[\pressed]] + + `false` + + A flag indicating that the button is pressed +
+ [[\touched]] + + `false` + + A flag indicating that the button is touched +
+ [[\value]] + + 0.0 + + A {{double}} representing the button value scaled to the range [0.0 + .. 1.0] +
pressed attribute
- The pressed state of the button. This property MUST be true if the - button is currently pressed, and false if it is not pressed. For - buttons which do not have a digital switch to indicate a pure pressed - or released state, the user agent MUST choose a threshold - value to indicate the button as pressed when its value is above a - certain amount. If the platform API gives a recommended value, the - user agent SHOULD use that. In other cases, the user agent SHOULD - choose some other reasonable value. +

+ The pressed state of the button. This property MUST be `true` if + the button is currently pressed, and `false` if it is not pressed. + For buttons which do not have a digital switch to indicate a pure + pressed or released state, the [=user agent=] MUST choose a + button press threshold to indicate the button as pressed + when its value is above a certain amount. If the platform API gives + a recommended value, the user agent SHOULD use that. In other + cases, the user agent SHOULD choose some other reasonable value. +

+

+ The {{GamepadButton/pressed}} getter steps are: +

+
    +
  1. Return [=this=].{{GamepadButton/[[pressed]]}}. +
  2. +
touched attribute
- The touched state of the button. If the button is capable of - detecting touch, this property MUST be true if the button is - currently being touched, and false otherwise. If the button is not - capable of detecting touch and is capable of reporting an analog - value, this property MUST be true if the value property is greater - than 0, and false if the value is 0. If the button is not capable of - detecting touch and can only report a digital value, this property - MUST mirror the pressed attribute. +

+ The touched state of the button. If the button is capable of + detecting touch, this property MUST be `true` if the button is + currently being touched, and `false` otherwise. If the button is + not capable of detecting touch and is capable of reporting an + analog value, this property MUST be `true` if the value property is + greater than 0, and `false` if the value is 0. If the button is not + capable of detecting touch and can only report a digital value, + this property MUST mirror the {{GamepadButton/pressed}} attribute. +

+

+ The {{GamepadButton/touched}} getter steps are: +

+
    +
  1. Return [=this=].{{GamepadButton/[[touched]]}}. +
  2. +
value attribute
- For buttons that have an analog sensor, this property MUST represent - the amount which the button has been pressed. All button values MUST - be linearly normalized to the range [0.0 .. 1.0]. 0.0 MUST mean fully - unpressed, and 1.0 MUST mean fully pressed. For buttons without an - analog sensor, only the values 0.0 and 1.0 for fully unpressed and - fully pressed respectively, MUST be provided. +

+ For buttons that have an analog sensor, this property MUST + represent the amount which the button has been pressed. All button + values MUST be linearly normalized to the range [0.0 .. 1.0]. 0.0 + MUST mean fully unpressed, and 1.0 MUST mean fully pressed. For + buttons without an analog sensor, only the values 0.0 and 1.0 for + fully unpressed and fully pressed respectively, MUST be provided. +

+

+ The {{GamepadButton/value}} getter steps are: +

+
    +
  1. Return [=this=].{{GamepadButton/[[value]]}}. +
  2. +

@@ -338,79 +1008,96 @@

gamepad.
- standard + `"standard"`
- The Gamepad's controls have been mapped to the Standard Gamepad layout. + The Gamepad's controls have been mapped to the [=Standard Gamepad=] + layout.
- xr-standard + `"xr-standard"`
The Gamepad's controls have been mapped to the [="xr-standard" gamepad mapping=]. This mapping is reserved for use by the [[[webxr-gamepads-module-1]]]. Gamepads returned by {{Navigator/getGamepads()}} MUST NOT report a {{Gamepad/mapping}} of - {{GamepadMappingType["xr-standard"]}}. + {{GamepadMappingType/"xr-standard"}}.

- Navigator Interface extension + Extensions to the `Navigator` interface

-

- This partial interface defines an extension to the Navigator interface. -

         [Exposed=Window]
         partial interface Navigator {
           sequence<Gamepad?> getGamepads();
         };
       
-
-
- getGamepads() -
-
-

- The gamepad state returned from {{Navigator/getGamepads()}} does - not reflect disconnection or connection until after the - gamepaddisconnected or gamepadconnected events have - fired. -

-

- If the current settings object's [=environment settings - object / responsible document=] is not allowed to use the - "`gamepad`" permission, then [=exception/throw=] a - {{"SecurityError"}} {{DOMException}}. -

-
-
- Retrieve a snapshot of the data for the the currently connected and - interacted-with gamepads. Gamepads MUST only appear in the list if - they are currently connected to the user agent, and at least - one device has been interacted with by the user. If no devices have - been interacted with, devices MUST NOT appear in the list to avoid a - malicious page from fingerprinting the user. The length of the array - returned MUST be one more than the maximum index value of the Gamepad - objects returned in the array. The entries in the array MUST be the - set of Gamepad objects that are visible to the current page, with - each Gamepad present at the index in the array specified by its - {{Gamepad/index}} attribute. Array indices for which there is no - connected Gamepad with the corresponding index should return null. -

- The gamepad state returned from getGamepads() does not reflect - disconnection or connection until after the - gamepaddisconnected or gamepadconnected events have - fired. -

-

- As an example, if there is one connected gamepad with an index of - 1, then the following code snippet describes the expected behavior: -

-
+      

+ Instances of {{Navigator}} are created with the internal slots + described in the following table: +

+ + + + + + + + + + + + + + + + +
+ Internal slot + + Initial value + + Description (non-normative) +
+ [[\hasGamepadGesture]] + + `false` + + A flag indicating that a [=gamepad user gesture=] has been observed +
+ [[\gamepads]] + + `[]` + + A `sequence<Gamepad?>` with each {{Gamepad}} present at the + index specified by its {{Gamepad/index}} attribute, or `null` for + unassigned indices. +
+
+

+ getGamepads() method +

+

+ The gamepad state returned from {{Navigator/getGamepads()}} does not + reflect disconnection or connection until after the + {{gamepaddisconnected}} or {{gamepadconnected}} events have fired. +

+

+ To mitigate fingerprinting, {{Navigator/getGamepads()}} returns an + empty [=list=] before a [=gamepad user gesture=] has been seen. + [[FINGERPRINTING-GUIDANCE]] +

+
-
+ +

+ The {{Navigator/getGamepads()}} method steps are: +

+
    +
  1. If the [=current settings object=]'s [=environment settings + object / responsible document=] is not [=allowed to use=] the + `"gamepad"` permission, then [=exception/throw=] a + {{"SecurityError"}} {{DOMException}} and abort these steps. +
  2. +
  3. If [=this=].{{Navigator/[[hasGamepadGesture]]}} is `false`, then + return an empty [=list=]. +
  4. +
  5. Let |now:DOMHighResTimeStamp| be the [=current high resolution + time=]. +
  6. +
  7. [=list/For each=] |gamepad:Gamepad| of + [=this=].{{Navigator/[[gamepads]]}}: +
      +
    1. If |gamepad| is not `null` and + |gamepad|.{{Gamepad/[[exposed]]}} is `false`: +
        +
      1. Set |gamepad|.{{Gamepad/[[exposed]]}} to `true`. +
      2. +
      3. Set |gamepad|.{{Gamepad/[[timestamp]]}} to |now|. +
      4. +
      +
    2. +
    +
  8. +
  9. Return |gamepads|. +
  10. +
+

+ A |gamepad:Gamepad| contains a + gamepad user gesture if the current input state indicates that + the user is currently interacting with the gamepad. The [=user + agent=] MUST provide an algorithm to check if the input state + contains a gamepad user gesture. For buttons that support a neutral + default value and have reported a {{GamepadButton/pressed}} value of + `false` at least once, a {{GamepadButton/pressed}} value of `true` + SHOULD be considered interaction. If a button does not support a + neutral default value (for example, a toggle switch), then a + {{GamepadButton/pressed}} value of `true` SHOULD NOT be considered + interaction. If a button has never reported a + {{GamepadButton/pressed}} value of `false` then it SHOULD NOT be + considered interaction. Axis movements SHOULD be considered + interaction if the axis supports a neutral default value, the current + displacement from neutral is greater than a threshold chosen by the + [=user agent=], and the axis has reported a value below the threshold + at least once. If an axis does not support a neutral default value + (for example, an axis for a joystick that does not self-center), or + an axis has never reported a value below the axis gesture threshold, + then the axis SHOULD NOT be considered when checking for interaction. + The axis gesture threshold SHOULD be large enough that random jitter + is not considered interaction. +

+

@@ -438,8 +1181,8 @@

gamepad
- The single gamepad attribute provides access to the associated - gamepad data for this event. + The {{GamepadEvent/gamepad}} attribute provides access to the + associated gamepad data for this event.
@@ -456,43 +1199,64 @@

gamepad member
- The gamepad associated with this event. + The {{Gamepad}} associated with this event.

- Remapping + Remapping

Each device manufacturer creates many different products and each has unique styles and layouts of buttons and axes. It is intended that the - user agent support as many of these as possible. + [=user agent=] support as many of these as possible.

Additionally there are de facto standard layouts that have - been made popular by game consoles. When the user agent - recognizes the attached device, it is RECOMMENDED that it be remapped - to a canonical ordering when possible. Devices that are not recognized + been made popular by game consoles. When the [=user agent=] recognizes + the attached device, it is RECOMMENDED that it be remapped to a + canonical ordering when possible. Devices that are not recognized should still be exposed in their raw form.

- There is currently one canonical device, the "Standard Gamepad". The - standard gamepad has 4 axes, and up to 17 buttons. When remapping, the - indices in axes[] and buttons[] should correspond as - closely as possible to the physical locations in the diagram below. - Additionally, the mapping property of the Gamepad SHOULD be set - to the string {{GamepadMappingType["standard"]}}. -

-

- The "Standard Gamepad" physical button locations are layed out in a - left cluster of four buttons, a right cluster of four buttons, a center - cluster of three buttons, and a pair of front facing buttons on the - left and right side of the gamepad. The four axes of the "Standard - Gamepad" are associated with a pair of analog sticks, one on the left - and one on the right. The following table describes the buttons/axes - and their physical locations. + There is currently one canonical layout, the Standard + Gamepad. When remapping, the indices in {{Gamepad/axes}} and + {{Gamepad/buttons}} should correspond as closely as possible to the + physical locations in the diagram below. Additionally, + {{Gamepad/mapping}} SHOULD be set to {{GamepadMappingType/"standard"}}. +

+

+ The [=Standard Gamepad=] buttons are laid out in a left cluster of four + buttons, a right cluster of four buttons, a center cluster of three + buttons, and a pair of front facing buttons on the left and right side + of the gamepad. The four axes of the "Standard Gamepad" are associated + with a pair of analog sticks, one on the left and one on the right. The + following table describes the buttons/axes and their physical + locations. +

+

+ An axis input represents a Standard Gamepad axis if it + reports the input value for a thumbstick axis, the thumbstick is + located in approximately the same location as the corresponding + [=Standard Gamepad=] thumbstick, and the orientation of the axis + (up-down or left-right) matches the orientation of the [=Standard + Gamepad=] axis. If there are multiple axes that represent the same + [=Standard Gamepad=] axis, then the [=user agent=] SHOULD select one to + be the [=Standard Gamepad=] axis and assign a different index to the + other axis. +

+

+ A button input represents a Standard Gamepad button if it + reports the input value for a button or trigger, and the button or + trigger is located in approximately the same location as the + corresponding [=Standard Gamepad=] button. +

+

+ If an axis or button input represents a [=Standard Gamepad=] axis or + button, then its canonical index is the index of the + corresponding [=Standard Gamepad=] axis or button.

@@ -679,7 +1443,7 @@

- Visual representation of a standard gamepad layout. + Visual representation of a [=Standard Gamepad=] layout.
@@ -723,22 +1487,55 @@

The gamepadconnected event

- User agents implementing this specification must provide a new DOM - event, named gamepadconnected. The corresponding event - MUST be of type {{GamepadEvent}} and, if allowed to use the - "`gamepad`" permission, MUST fire on the window object. - Registration for and firing of the gamepadconnected event - MUST follow the usual behavior of DOM Events. [[DOM]] + When a gamepad becomes available on the system, run the following + steps:

+
    +
  1. Let |document:Document?| be the [=current settings object=]'s + [=environment settings object / responsible document=]; otherwise + `null`. +
  2. +
  3. If |document| is not `null` and is not [=allowed to use=] the + `"gamepad"` permission, then abort these steps. +
  4. +
  5. [=Queue a task=] on the [=gamepad task source=] to perform the + following steps: +
      +
    1. Let |gamepad:Gamepad| be [=a new `Gamepad`=] representing the + gamepad. +
    2. +
    3. Let |navigator:Navigator| be |gamepad|'s [=relevant global + object=]'s {{Navigator}} object. +
    4. +
    5. Set + |navigator|.{{Navigator/[[gamepads]]}}[|gamepad|.{{Gamepad/index}}] + to |gamepad|. +
    6. +
    7. If |navigator|.{{Navigator/[[hasGamepadGesture]]}} is `true`: +
        +
      1. Set |gamepad|.{{Gamepad/[[exposed]]}} to `true`. +
      2. +
      3. If |document| is not `null` and is [=Document/fully + active=], then [=fire an event=] named {{gamepadconnected}} at + |gamepad|'s [=relevant global object=] using {{GamepadEvent}} + with its {{GamepadEvent/gamepad}} attribute initialized to + |gamepad|. +
      4. +
      +
    8. +
    +
  6. +

- A user agent MUST dispatch this event type to indicate the user - has connected a gamepad. If a gamepad was already connected when the - page was loaded, the gamepadconnected event SHOULD be dispatched - when the user presses a button or moves an axis. + User agents implementing this specification must provide a new DOM + event, named {{gamepadconnected}}. The corresponding event MUST be of + type {{GamepadEvent}} and MUST fire on the {{Window}} object.

- A user agent MUST NOT dispatch this event type if the - environment settings object is a non-secure context. + A [=user agent=] MUST dispatch this event type to indicate the user has + connected a gamepad. If a gamepad was already connected when the page + was loaded, the {{gamepadconnected}} event SHOULD be dispatched when + the user presses a button or moves an axis.

@@ -746,23 +1543,52 @@

The gamepaddisconnected event

- User agents implementing this specification must provide a new DOM - event, named gamepaddisconnected. The corresponding event - MUST be of type GamepadEvent and, if allowed to use - the "`gamepad`" permission, MUST fire on the window - object. Registration for and firing of the - gamepaddisconnected event MUST follow the usual behavior - of DOM Events. [[DOM]] + When a gamepad becomes unavailable on the system, run the following + steps:

+
    +
  1. Let |gamepad:Gamepad| be the {{Gamepad}} representing the + unavailable device. +
  2. +
  3. [=Queue a task=] on the [=gamepad task source=] to perform the + following steps: +
      +
    1. Set |gamepad|.{{Gamepad/[[connected]]}} to `false`. +
    2. +
    3. Let |document:Document?| be |gamepad|'s [=relevant global + object=]'s [=associated `Document`=]; otherwise `null`. +
    4. +
    5. If |gamepad|.{{Gamepad/[[exposed]]}} is `true` and |document| + is not `null` and is [=Document/fully active=], then [=fire an + event=] named {{gamepaddisconnected}} at |gamepad|'s [=relevant + global object=] using {{GamepadEvent}} with its + {{GamepadEvent/gamepad}} attribute initialized to |gamepad|. +
    6. +
    7. Let |navigator:Navigator| be |gamepad|'s [=relevant global + object=]'s {{Navigator}} object. +
    8. +
    9. Set + |navigator|.{{Navigator/[[gamepads]]}}[|gamepad|.{{Gamepad/index}}] + to `null`. +
    10. +
    11. While |navigator|.{{Navigator/[[gamepads]]}} [=list/is not + empty=] and the last [=list/item=] of + |navigator|.{{Navigator/[[gamepads]]}} is `null`, [=list/remove=] + the last [=list/item=] of |navigator|.{{Navigator/[[gamepads]]}}. +
    12. +
    +
  4. +

- When a gamepad is disconnected from the user agent, if the - user agent has previously dispatched a gamepadconnected - event for that gamepad to a window, a gamepaddisconnected event - MUST be dispatched to that same window. + User agents implementing this specification must provide a new DOM + event, named {{gamepaddisconnected}}. The corresponding event MUST be + of type {{GamepadEvent}} and MUST fire on the {{Window}} object.

- A user agent MUST NOT dispatch this event type if the - environment settings object is a non-secure context. + When a gamepad is disconnected from the [=user agent=], if the [=user + agent=] has previously dispatched a {{gamepadconnected}} event for that + gamepad to a {{Window}}, a {{gamepaddisconnected}} event MUST be + dispatched to that same {{Window}}.

@@ -772,9 +1598,8 @@

More discussion needed, on whether to include or exclude axis and button changed events, and whether to roll them more together - (gamepadchanged?), separate somewhat - (gamepadaxischanged?), or separate by individual axis and - button. + (`"gamepadchanged"`?), separate somewhat (`"gamepadaxischanged"`?), or + separate by individual axis and button.

@@ -799,33 +1624,27 @@

This specification defines a policy-controlled feature identified by - the string "`gamepad`". Its [=default allowlist=] is '`self`'. + the string `"gamepad"`. Its [=default allowlist=] is `'self'`.

- A document’s [=Document/permissions policy=] determines - whether any content in that document is allowed to access + A [=document=]’s [=Document/permissions policy=] determines whether + any content in that document is allowed to access {{Navigator/getGamepads()}}. If disabled in any document, no content - in the document will be allowed to use - {{Navigator/getGamepads()}}, nor will the "gamepadconnected" and - "gamepaddisconnected" events fire. + in the document will be [=allowed to use=] + {{Navigator/getGamepads()}}, nor will the {{gamepadconnected}} and + {{gamepaddisconnected}} events fire.

-

- This specification defines conformance criteria that apply to a single - product: the user agent that implements - the interfaces that it contains. -

+

Acknowledgements -

-

- Many have made contributions in code, comments, or documentation: -

+ The following people contributed to the development of this + document.
  • David Humphrey
  • @@ -840,9 +1659,7 @@

  • Rick Waldron
-

- Please let me know if I have inadvertently omitted your name. -

+