From e93ef99f1f991497bdf370193b96306f80167f3d Mon Sep 17 00:00:00 2001 From: Matt Reynolds Date: Thu, 10 Jun 2021 12:45:31 -0700 Subject: [PATCH 1/4] Add buttondown, buttonup, buttonchange, axischange events Closes #4 --- index.html | 340 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 335 insertions(+), 5 deletions(-) diff --git a/index.html b/index.html index 6820d0e..c1b3665 100644 --- a/index.html +++ b/index.html @@ -147,6 +147,11 @@

         [Exposed=Window, SecureContext]
         interface Gamepad {
+          attribute EventHandler onbuttondown;
+          attribute EventHandler onbuttonup;
+          attribute EventHandler onbuttonchange;
+          attribute EventHandler onaxischange;
+
           readonly attribute DOMString id;
           readonly attribute long index;
           readonly attribute boolean connected;
@@ -304,6 +309,34 @@ 

+
+ onbuttondown attribute +
+
+ {{Gamepad/onbuttondown}} is an [=event handler IDL attribute=] for + the {{buttondown}} event type. +
+
+ onbuttonup attribute +
+
+ {{Gamepad/onbuttonup}} is an [=event handler IDL attribute=] for the + {{buttonup}} event type. +
+
+ onbuttonchange attribute +
+
+ {{Gamepad/onbuttonchange}} is an [=event handler IDL attribute=] for + the {{buttonchange}} event type. +
+
+ onaxischange attribute +
+
+ {{Gamepad/onaxischange}} is an [=event handler IDL attribute=] for + the {{axischange}} event type. +
id attribute
@@ -470,6 +503,27 @@

following steps:

    +
  1. Initialize |oldAxisValues:list| to be an empty [=list=]. +
  2. +
  3. [=list/For each=] |axis:double| of + |gamepad|.{{Gamepad/[[axes]]}}, [=list/append=] |axis| to + |oldAxisValues|. +
  4. +
  5. Initialize |oldButtonValues:list| to be an empty [=list=]. +
  6. +
  7. Initialize |oldButtonPressed:list| to be an empty [=list=]. +
  8. +
  9. [=list/For each=] |button:GamepadButton| of + |gamepad|.{{Gamepad/[[buttons]]}}: +
      +
    1. [=list/Append=] |button|.{{GamepadButton/value}} to + |oldButtonValues|. +
    2. +
    3. [=list/Append=] |button|.{{GamepadButton/pressed}} to + |oldButtonPressed|. +
    4. +
    +
  10. Let |now:DOMHighResTimeStamp| be the [=current high resolution time=].
  11. @@ -516,6 +570,99 @@

+
  • If |navigator|.{{Navigator/[[hasGamepadGesture]]}} is `false`, + abort these steps. +
  • +
  • [=list/For each=] |axisIndex:long| of [=the range=] from 0 to the + [=list/size=] of |gamepad|.{{Gamepad/axes}} − 1: +
      +
    1. If |oldAxisValues|[|axisIndex|] is not equal to + |gamepad|.{{Gamepad/[[axes]]}}[|axisIndex|], [=queue a task=] on + the [=gamepad task source=] to [=fire an event=] named + {{axischange}} at |gamepad| using {{GamepadAxisEvent}} with its + {{GamepadAxisEvent/gamepadIndex}} attribute initialized to + |gamepad|.{{Gamepad/index}}, its {{GamepadAxisEvent/axisIndex}} + attribute initialized to |axisIndex|, its + {{GamepadAxisEvent/axisSnapshot}} attribute initialized to + |newValue|, and its {{GamepadAxisEvent/gamepadTimestamp}} + attribute initialized to |now|. +
    2. +
    +
  • +
  • [=list/For each=] |buttonIndex:long| of [=the range=] from 0 to + the [=list/size=] of |gamepad|.{{Gamepad/buttons}} − 1: +
      +
    1. Let |button:GamepadButton| be + |gamepad|.{{Gamepad/[[buttons]]}}[|buttonIndex|]. +
    2. +
    3. If |oldButtonValue|[|buttonIndex|] is not equal to + |button|.{{GamepadButton/value}}: +
        +
      1. Let |buttonCopy| be a [=new=] {{GamepadButton}} instance + with its {{GamepadButton/value}} attribute initialized to + |button|.{{GamepadButton/value}}, its + {{GamepadButton/pressed}} attribute initialized to + |button|.{{GamepadButton/pressed}}, and its + {{GamepadButton/touched}} attribute initialized to + |button|.{{GamepadButton/touched}}. +
      2. +
      3. [=Queue a task=] on the [=gamepad task source=] to [=fire + an event=] named {{buttonchange}} at |gamepad| using + {{GamepadButtonEvent}} with its + {{GamepadButtonEvent/gamepadIndex}} attribute initialized to + |gamepad|.{{Gamepad/index}}, its + {{GamepadButtonEvent/buttonIndex}} attribute initialized to + |buttonIndex|, its {{GamepadButtonEvent/buttonSnapshot}} + attribute initialized to |buttonCopy|, and its + {{GamepadButtonEvent/gamepadTimestamp}} attribute initialized + to |now|. +
      4. +
      +
    4. +
    5. If |oldButtonPressed|[|buttonIndex|] is not equal to + |button|.{{GamepadButton/pressed}}: +
        +
      1. Let |buttonCopy| be a [=new=] {{GamepadButton}} instance + with its {{GamepadButton/value}} attribute initialized to + |button|.{{GamepadButton/value}}, its + {{GamepadButton/pressed}} attribute initialized to + |button|.{{GamepadButton/pressed}}, and its + {{GamepadButton/touched}} attribute initialized to + |button|.{{GamepadButton/touched}}. +
      2. +
      3. +

        + If |button|.{{GamepadButton/pressed}} is `true`, [=queue + a task=] on the [=gamepad task source=] to [=fire an + event=] named {{buttondown}} at |gamepad| using + {{GamepadButtonEvent}} with its + {{GamepadButtonEvent/gamepadIndex}} attribute initialized + to |gamepad|.{{Gamepad/index}}, its + {{GamepadButtonEvent/buttonIndex}} attribute initialized + to |buttonIndex|, its + {{GamepadButtonEvent/buttonSnapshot}} attribute + initialized to |buttonCopy|, and its + {{GamepadButtonEvent/gamepadTimestamp}} attribute + initialized to |now|. +

        +

        + Otherwise, [=queue a task=] on the [=gamepad task + source=] to [=fire an event=] named {{buttonup}} at + |gamepad| using {{GamepadButtonEvent}} with its + {{GamepadButtonEvent/gamepadIndex}} attribute initialized + to |gamepad|.{{Gamepad/index}}, its + {{GamepadButtonEvent/buttonIndex}} attribute initialized + to |buttonIndex|, its + {{GamepadButtonEvent/buttonSnapshot}} attribute + initialized to |buttonCopy|, and its + {{GamepadButtonEvent/gamepadTimestamp}} attribute + initialized to |now|. +

        +
      4. +
      +
    6. +
    +
  • To map and normalize axes for |gamepad:Gamepad|, run the @@ -1207,6 +1354,173 @@

    +
    +

    + GamepadAxisEvent Interface +

    +
    +        [Exposed=Window, SecureContext]
    +
    +        interface GamepadAxisEvent: Event {
    +          constructor(DOMString type, GamepadAxisEventInit eventInitDict);
    +          readonly attribute long gamepadIndex;
    +          readonly attribute long axisIndex;
    +          readonly attribute double axisSnapshot;
    +          readonly attribute DOMHighResTimeStamp gamepadTimestamp;
    +        };
    +      
    +
    +
    + gamepadIndex attribute +
    +
    + The {{Gamepad/index}} attribute of the {{Gamepad}} associated with + this event. +
    +
    + axisIndex attribute +
    +
    + The index of the axis in the {{Gamepad/axes}} array. +
    +
    + axisSnapshot attribute +
    +
    + The axis value at the time when this event was created. +
    +
    + gamepadTimestamp attribute +
    +
    + The {{Gamepad/timestamp}} of the {{Gamepad}} associated with this + event at the time when the event was created. +
    +
    +
    +

    + GamepadAxisEventInit dictionary +

    +
    +        dictionary GamepadAxisEventInit : EventInit {
    +          required long gamepadIndex;
    +          required long axisIndex;
    +          required double axisSnapshot;
    +          required DOMHighResTimeStamp gamepadTimestamp;
    +        };
    +      
    +
    +
    + gamepadIndex member +
    +
    + The gamepad index for the event. +
    +
    + axisIndex member +
    +
    + The button index for the event. +
    +
    + axisSnapshot member +
    +
    + A copy of the current button state. +
    +
    + gamepadTimestamp member +
    +
    + The gamepad timestamp for the event. +
    +
    +
    +
    +
    +

    + GamepadButtonEvent Interface +

    +
    +        [Exposed=Window, SecureContext]
    +
    +        interface GamepadButtonEvent: Event {
    +          constructor(DOMString type, GamepadButtonEventInit eventInitDict);
    +          readonly attribute long gamepadIndex;
    +          readonly attribute long buttonIndex;
    +          readonly attribute GamepadButton buttonSnapshot;
    +          readonly attribute DOMHighResTimeStamp gamepadTimestamp;
    +        };
    +      
    +
    +
    + gamepadIndex attribute +
    +
    + The {{Gamepad/index}} attribute of the {{Gamepad}} associated with + this event. +
    +
    + buttonIndex attribute +
    +
    + The index of the {{GamepadButton}} in the {{Gamepad/buttons}} array. +
    +
    + buttonSnapshot attribute +
    +
    + A copy of the {{GamepadButton}} at the time when this event was + created. +
    +
    + gamepadTimestamp attribute +
    +
    + The {{Gamepad/timestamp}} of the {{Gamepad}} associated with this + event at the time when the event was created. +
    +
    +
    +

    + GamepadButtonEventInit dictionary +

    +
    +        dictionary GamepadButtonEventInit : EventInit {
    +          required long gamepadIndex;
    +          required long buttonIndex;
    +          required GamepadButton buttonSnapshot;
    +          required DOMHighResTimeStamp gamepadTimestamp;
    +        };
    +      
    +
    +
    + gamepadIndex member +
    +
    + The gamepad index for the event. +
    +
    + buttonIndex member +
    +
    + The button index for the event. +
    +
    + buttonSnapshot member +
    +
    + A copy of the current button state. +
    +
    + gamepadTimestamp member +
    +
    + The gamepad timestamp for the event. +
    +
    +
    +

    Remapping @@ -1596,13 +1910,29 @@

    - Other events + The axischange, buttonchange, buttondown, and buttonup events

    - 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. + [=User agent=]s implementing this specification MUST provide new DOM + events named axischange, buttonchange, buttondown, and + buttonup. The corresponding event MUST be of + type {{GamepadAxisEvent}} for {{axischange}}, or {{GamepadButtonEvent}} + for {{buttonchange}}, {{buttondown}}, and {{buttonup}}. All events MUST + fire on the {{Gamepad}} object. +

    +

    + When the [=user agent=] receives new button or axis input values from a + gamepad, the [=user agent=] MUST compare the current button and axis + values with the previous values and dispatch {{axischange}} and + {{buttonchange}} events for each axis and button with a changed value. + The [=user agent=] MUST also compare the current button pressed state + with the previous button pressed state and dispatch {{buttondown}} and + {{buttonup}} events. +

    +

    + These events MUST NOT be dispatched before the [=user agent=] has + dispatched a {{gamepadconnected}} event for that gamepad.

    From bf1e7b2e167938eae5960de512a622f306fcdcc4 Mon Sep 17 00:00:00 2001 From: Matt Reynolds Date: Tue, 27 Jul 2021 13:52:54 -0700 Subject: [PATCH 2/4] changes for reillyg --- index.html | 57 +++++++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/index.html b/index.html index c1b3665..72289bf 100644 --- a/index.html +++ b/index.html @@ -595,17 +595,9 @@

  • Let |button:GamepadButton| be |gamepad|.{{Gamepad/[[buttons]]}}[|buttonIndex|].
  • -
  • If |oldButtonValue|[|buttonIndex|] is not equal to +
  • If |oldButtonValues|[|buttonIndex|] is not equal to |button|.{{GamepadButton/value}}:
      -
    1. Let |buttonCopy| be a [=new=] {{GamepadButton}} instance - with its {{GamepadButton/value}} attribute initialized to - |button|.{{GamepadButton/value}}, its - {{GamepadButton/pressed}} attribute initialized to - |button|.{{GamepadButton/pressed}}, and its - {{GamepadButton/touched}} attribute initialized to - |button|.{{GamepadButton/touched}}. -
    2. [=Queue a task=] on the [=gamepad task source=] to [=fire an event=] named {{buttonchange}} at |gamepad| using {{GamepadButtonEvent}} with its @@ -613,7 +605,8 @@

      |gamepad|.{{Gamepad/index}}, its {{GamepadButtonEvent/buttonIndex}} attribute initialized to |buttonIndex|, its {{GamepadButtonEvent/buttonSnapshot}} - attribute initialized to |buttonCopy|, and its + attribute initialized to the result of [=creating a button + snapshot=] of |button|, and its {{GamepadButtonEvent/gamepadTimestamp}} attribute initialized to |now|.

    3. @@ -622,14 +615,6 @@

    4. If |oldButtonPressed|[|buttonIndex|] is not equal to |button|.{{GamepadButton/pressed}}:
        -
      1. Let |buttonCopy| be a [=new=] {{GamepadButton}} instance - with its {{GamepadButton/value}} attribute initialized to - |button|.{{GamepadButton/value}}, its - {{GamepadButton/pressed}} attribute initialized to - |button|.{{GamepadButton/pressed}}, and its - {{GamepadButton/touched}} attribute initialized to - |button|.{{GamepadButton/touched}}. -
      2. If |button|.{{GamepadButton/pressed}} is `true`, [=queue @@ -641,7 +626,8 @@

        {{GamepadButtonEvent/buttonIndex}} attribute initialized to |buttonIndex|, its {{GamepadButtonEvent/buttonSnapshot}} attribute - initialized to |buttonCopy|, and its + initialized to the result of [=creating a button + snapshot=] of |button|, and its {{GamepadButtonEvent/gamepadTimestamp}} attribute initialized to |now|.

        @@ -654,7 +640,8 @@

        {{GamepadButtonEvent/buttonIndex}} attribute initialized to |buttonIndex|, its {{GamepadButtonEvent/buttonSnapshot}} attribute - initialized to |buttonCopy|, and its + initialized to the result of [=creating a button + snapshot=] of |button|, and its {{GamepadButtonEvent/gamepadTimestamp}} attribute initialized to |now|.

        @@ -763,6 +750,27 @@

    +

    + To create a button + snapshot for |button:GamepadButton|, run the following steps: +

    +
      +
    1. Let |buttonCopy| be a newly created {{GamepadButton}} instance: +
        +
      1. Initialize |buttonCopy|'s {{GamepadButton/value}} attribute + to |button|.{{GamepadButton/value}}. +
      2. +
      3. Initialize |buttonCopy|'s {{GamepadButton/pressed}} attribute + to |button|.{{GamepadButton/pressed}}. +
      4. +
      5. Initialize |buttonCopy|'s {{GamepadButton/touched}} attribute + to |button|.{{GamepadButton/touched}}. +
      6. +
      +
    2. +
    3. Return |buttonCopy|. +
    4. +
  • @@ -1320,8 +1328,7 @@

             [Exposed=Window, SecureContext]
    -
    -        interface GamepadEvent: Event {
    +        interface GamepadEvent : Event {
               constructor(DOMString type, GamepadEventInit eventInitDict);
               [SameObject] readonly attribute Gamepad gamepad;
             };
    @@ -1360,8 +1367,7 @@ 

             [Exposed=Window, SecureContext]
    -
    -        interface GamepadAxisEvent: Event {
    +        interface GamepadAxisEvent : Event {
               constructor(DOMString type, GamepadAxisEventInit eventInitDict);
               readonly attribute long gamepadIndex;
               readonly attribute long axisIndex;
    @@ -1443,8 +1449,7 @@ 

             [Exposed=Window, SecureContext]
    -
    -        interface GamepadButtonEvent: Event {
    +        interface GamepadButtonEvent : Event {
               constructor(DOMString type, GamepadButtonEventInit eventInitDict);
               readonly attribute long gamepadIndex;
               readonly attribute long buttonIndex;
    
    From a5def3d5fef25f281ce25de2dab64b57fe0b3502 Mon Sep 17 00:00:00 2001
    From: Matt Reynolds 
    Date: Thu, 5 Aug 2021 15:44:44 -0700
    Subject: [PATCH 3/4] Make Gamepad an EventTarget
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    Co-authored-by: Marcos Cáceres 
    ---
     index.html | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/index.html b/index.html
    index 72289bf..ee1f8f3 100644
    --- a/index.html
    +++ b/index.html
    @@ -146,7 +146,7 @@ 

             [Exposed=Window, SecureContext]
    -        interface Gamepad {
    +        interface Gamepad : EventTarget {
               attribute EventHandler onbuttondown;
               attribute EventHandler onbuttonup;
               attribute EventHandler onbuttonchange;
    
    From 059111d7d3aa1f5dd3baa6a4c9ed1b0c43ee3c32 Mon Sep 17 00:00:00 2001
    From: Matt Reynolds 
    Date: Tue, 27 Jul 2021 13:52:54 -0700
    Subject: [PATCH 4/4] gamepadchange and rawgamepadchange
    
    ---
     index.html | 505 ++++++++++++++++++++++++++++++++++++++++-------------
     1 file changed, 379 insertions(+), 126 deletions(-)
    
    diff --git a/index.html b/index.html
    index ee1f8f3..4137a01 100644
    --- a/index.html
    +++ b/index.html
    @@ -149,8 +149,8 @@ 

    interface Gamepad : EventTarget { attribute EventHandler onbuttondown; attribute EventHandler onbuttonup; - attribute EventHandler onbuttonchange; - attribute EventHandler onaxischange; + attribute EventHandler ongamepadchange; + attribute EventHandler onrawgamepadchange; readonly attribute DOMString id; readonly attribute long index; @@ -307,6 +307,17 @@

    A [=list=] containing the maximum logical value for each button + + + [[\pendingChanges]] + + + An empty [=list=] + + + A [=list=] containing pending {{GamepadChangeEvent}}s. + +
    @@ -324,18 +335,18 @@

    {{buttonup}} event type.
    - onbuttonchange attribute + ongamepadchange attribute
    - {{Gamepad/onbuttonchange}} is an [=event handler IDL attribute=] for - the {{buttonchange}} event type. + {{Gamepad/ongamepadchange}} is an [=event handler IDL attribute=] for + the {{gamepadchange}} event type.
    - onaxischange attribute + onrawgamepadchange attribute
    - {{Gamepad/onaxischange}} is an [=event handler IDL attribute=] for - the {{axischange}} event type. + {{Gamepad/onrawgamepadchange}} is an [=event handler IDL attribute=] + for the {{rawgamepadchange}} event type.
    id attribute @@ -505,16 +516,15 @@

    1. Initialize |oldAxisValues:list| to be an empty [=list=].
    2. -
    3. [=list/For each=] |axis:double| of - |gamepad|.{{Gamepad/[[axes]]}}, [=list/append=] |axis| to - |oldAxisValues|. +
    4. [=list/For each=] |axis:double| of |gamepad|.{{Gamepad/axes}}, + [=list/append=] |axis| to |oldAxisValues|.
    5. Initialize |oldButtonValues:list| to be an empty [=list=].
    6. Initialize |oldButtonPressed:list| to be an empty [=list=].
    7. [=list/For each=] |button:GamepadButton| of - |gamepad|.{{Gamepad/[[buttons]]}}: + |gamepad|.{{Gamepad/buttons}}:
      1. [=list/Append=] |button|.{{GamepadButton/value}} to |oldButtonValues|. @@ -558,11 +568,11 @@

        `null`.

      2. 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|. + active=], then [=fire an event=] named + {{gamepadconnected}} at |gamepad|'s [=relevant global + object=] using {{GamepadEvent}} with its + {{GamepadEvent/gamepad}} attribute initialized to + |connectedGamepad|.
    8. @@ -573,84 +583,208 @@

    9. If |navigator|.{{Navigator/[[hasGamepadGesture]]}} is `false`, abort these steps.
    10. +
    11. Let |axesChanged| be an empty [=list=]. +
    12. [=list/For each=] |axisIndex:long| of [=the range=] from 0 to the [=list/size=] of |gamepad|.{{Gamepad/axes}} − 1:
      1. If |oldAxisValues|[|axisIndex|] is not equal to - |gamepad|.{{Gamepad/[[axes]]}}[|axisIndex|], [=queue a task=] on - the [=gamepad task source=] to [=fire an event=] named - {{axischange}} at |gamepad| using {{GamepadAxisEvent}} with its - {{GamepadAxisEvent/gamepadIndex}} attribute initialized to - |gamepad|.{{Gamepad/index}}, its {{GamepadAxisEvent/axisIndex}} - attribute initialized to |axisIndex|, its - {{GamepadAxisEvent/axisSnapshot}} attribute initialized to - |newValue|, and its {{GamepadAxisEvent/gamepadTimestamp}} - attribute initialized to |now|. + |gamepad|.{{Gamepad/axes}}[|axisIndex|], [=list/append=] + |axisIndex| to |axesChanged|.
    13. +
    14. Let |buttonsChanged| be an empty [=list=]. +
    15. +
    16. Let |buttonsPressed| be an empty [=list=]. +
    17. +
    18. Let |buttonsReleased| be an empty [=list=]. +
    19. [=list/For each=] |buttonIndex:long| of [=the range=] from 0 to the [=list/size=] of |gamepad|.{{Gamepad/buttons}} − 1:
      1. Let |button:GamepadButton| be - |gamepad|.{{Gamepad/[[buttons]]}}[|buttonIndex|]. + |gamepad|.{{Gamepad/buttons}}[|buttonIndex|].
      2. If |oldButtonValues|[|buttonIndex|] is not equal to - |button|.{{GamepadButton/value}}: -
          -
        1. [=Queue a task=] on the [=gamepad task source=] to [=fire - an event=] named {{buttonchange}} at |gamepad| using - {{GamepadButtonEvent}} with its - {{GamepadButtonEvent/gamepadIndex}} attribute initialized to - |gamepad|.{{Gamepad/index}}, its - {{GamepadButtonEvent/buttonIndex}} attribute initialized to - |buttonIndex|, its {{GamepadButtonEvent/buttonSnapshot}} - attribute initialized to the result of [=creating a button - snapshot=] of |button|, and its - {{GamepadButtonEvent/gamepadTimestamp}} attribute initialized - to |now|. -
        2. -
        + |button|.{{GamepadButton/value}}, [=list/append=] |buttonIndex| + to |buttonsChanged|.
      3. -
      4. If |oldButtonPressed|[|buttonIndex|] is not equal to - |button|.{{GamepadButton/pressed}}: -
          -
        1. -

          - If |button|.{{GamepadButton/pressed}} is `true`, [=queue - a task=] on the [=gamepad task source=] to [=fire an - event=] named {{buttondown}} at |gamepad| using - {{GamepadButtonEvent}} with its - {{GamepadButtonEvent/gamepadIndex}} attribute initialized - to |gamepad|.{{Gamepad/index}}, its - {{GamepadButtonEvent/buttonIndex}} attribute initialized - to |buttonIndex|, its - {{GamepadButtonEvent/buttonSnapshot}} attribute - initialized to the result of [=creating a button - snapshot=] of |button|, and its - {{GamepadButtonEvent/gamepadTimestamp}} attribute - initialized to |now|. -

          -

          - Otherwise, [=queue a task=] on the [=gamepad task - source=] to [=fire an event=] named {{buttonup}} at - |gamepad| using {{GamepadButtonEvent}} with its - {{GamepadButtonEvent/gamepadIndex}} attribute initialized - to |gamepad|.{{Gamepad/index}}, its - {{GamepadButtonEvent/buttonIndex}} attribute initialized - to |buttonIndex|, its - {{GamepadButtonEvent/buttonSnapshot}} attribute - initialized to the result of [=creating a button - snapshot=] of |button|, and its - {{GamepadButtonEvent/gamepadTimestamp}} attribute - initialized to |now|. -

          -
        2. -
        +
      5. If |oldButtonPressed|[|buttonIndex|] is `false` and + |button|.{{GamepadButton/pressed}} is `true`, [=list/append=] + |buttonIndex| to |buttonsPressed|. +
      6. +
      7. If |oldButtonPressed|[|buttonIndex|] is `true` and + |button|.{{GamepadButton/pressed}} is `false`, [=list/append=] + |buttonIndex| to |buttonsReleased|. +
      8. +
      +
    20. +
    21. If any of |axesChanged| or |buttonsChanged|, |buttonsPressed|, or + |buttonsReleased| [=list/is not empty=]: +
        +
      1. Let |gamepadSnapshot| be the result of [=creating a + snapshot=] for |gamepad|. +
      2. +
      3. [=Fire an event=] named {{rawgamepadchange}} at |gamepad| + using {{GamepadChangeEvent}} with its + {{GamepadChangeEvent/gamepadSnapshot}} attribute initialized to + |gamepadSnapshot|, its {{GamepadChangeEvent/axesChanged}} + attribute initialized to |axesChanged|, its + {{GamepadChangeEvent/buttonsChanged}} attribute initialized to + |buttonsChanged|, its {{GamepadChangeEvent/buttonsPressed}} + attribute initialized to |buttonsPressed|, and its + {{GamepadChangeEvent/buttonsReleased}} attribute initialized to + |buttonsReleased|. +
      4. +
      5. Let |event:GamepadChangeEvent| be a newly created + {{GamepadChangeEvent}} with its + {{GamepadChangeEvent/gamepadSnapshot}} attribute initialized to + |gamepadSnapshot|, its {{GamepadChangeEvent/axesChanged}} + attribute initialized to |axesChanged|, its + {{GamepadChangeEvent/buttonsChanged}} attribute initialized to + |buttonsChanged|, its {{GamepadChangeEvent/buttonsPressed}} + attribute initialized to |buttonsPressed|, and its + {{GamepadChangeEvent/buttonsReleased}} attribute initialized to + |buttonsReleased|. +
      6. +
      7. Append |event| to |gamepad|.{{Gamepad/[[pendingChanges]]}}. +
      8. +
      +
    22. +
    23. If |buttonsPressed| or |buttonsReleased| [=list/is not empty=], + [=flush pending change events=] for |gamepad|. +
    24. +
    25. [=list/For each=] |buttonIndex:long| of |buttonsPressed|: +
        +
      1. [=Fire an event=] named {{buttondown}} at |gamepad| using + {{GamepadButtonEvent}} with its + {{GamepadButtonEvent/gamepadIndex}} attribute initialized to + |gamepad|.{{Gamepad/index}}, its + {{GamepadButtonEvent/buttonIndex}} attribute initialized to + |buttonIndex|, its {{GamepadButtonEvent/buttonSnapshot}} + attribute initialized to the result of [=creating a button + snapshot=] of |gamepad|.{{Gamepad/buttons}}[|buttonIndex|], and + its {{GamepadButtonEvent/gamepadTimestamp}} attribute initialized + to |now|. +
      2. +
      +
    26. +
    27. [=list/For each=] |buttonIndex:long| of |buttonsReleased|: +
        +
      1. [=Fire an event=] named {{buttonup}} at |gamepad| using + {{GamepadButtonEvent}} with its + {{GamepadButtonEvent/gamepadIndex}} attribute initialized to + |gamepad|.{{Gamepad/index}}, its + {{GamepadButtonEvent/buttonIndex}} attribute initialized to + |buttonIndex|, its {{GamepadButtonEvent/buttonSnapshot}} + attribute initialized to the result of [=creating a button + snapshot=] of |gamepad|.{{Gamepad/buttons}}[|buttonIndex|], and + its {{GamepadButtonEvent/gamepadTimestamp}} attribute initialized + to |now|. +
      2. +
      +
    28. +

    +

    + To flush pending change events for |gamepad:Gamepad|, run + the following steps: +

    +
      +
    1. If |gamepad|.{{Gamepad/[[pendingChanges]]}} [=list/is empty=], + abort these steps. +
    2. +
    3. Let |axesChanged:ordered set| be an empty [=ordered set=]. +
    4. +
    5. Let |buttonsChanged:ordered set| be an empty [=ordered set=]. +
    6. +
    7. Let |buttonsPressed:ordered set| be an empty [=ordered set=]. +
    8. +
    9. Let |buttonsReleased:ordered set| be an empty [=ordered set=]. +
    10. +
    11. Let |coalescedEvents:list| be an empty [=list=]. +
    12. +
    13. [=list/For each=] |event:GamepadChangeEvent| of + |gamepad|.{{Gamepad/[[pendingChanges]]}}: +
        +
      1. [=list/For each=] |axisIndex:long| of + |event|.{{GamepadChangeEvent/axesChanged}}, [=list/append=] + |axisIndex| to |axesChanged|. +
      2. +
      3. [=list/For each=] |buttonIndex:long| of + |event|.{{GamepadChangeEvent/buttonsChanged}}, [=list/append=] + |buttonIndex| to |buttonsChanged|. +
      4. +
      5. [=list/For each=] |buttonIndex:long| of + |event|.{{GamepadChangeEvent/buttonsPressed}}, [=list/append=] + |buttonIndex| to |buttonsPressed|. +
      6. +
      7. [=list/For each=] |buttonIndex:long| of + |event|.{{GamepadChangeEvent/buttonsReleased}}, [=list/append=] + |buttonIndex| to |buttonsReleased|. +
      8. +
      9. [=list/Append=] |event| to |coalescedEvents|. +
      10. +
      +
    14. +
    15. Let |lastChange:GamepadChangeEvent| be the last [=list/item=] of + |gamepad|.{{Gamepad/[[pendingChanges]]}}. +
    16. +
    17. Let |event:GamepadChangeEvent| be a newly created + {{GamepadChangeEvent}} instance: +
        +
      1. Initialize |event|.{{GamepadChangeEvent/gamepadSnapshot}} to + |lastChange|.{{GamepadChangeEvent/gamepadSnapshot}}. +
      2. +
      3. Initialize |event|.{{GamepadChangeEvent/axesChanged}} to the + result of [=list/sorting in ascending order=] |axesChanged|. +
      4. +
      5. Initialize |event|.{{GamepadChangeEvent/buttonsChanged}} to + the result of [=list/sorting in ascending order=] + |buttonsChanged|. +
      6. +
      7. Initialize |event|.{{GamepadChangeEvent/buttonsPressed}} to + the result of [=list/sorting in ascending order=] + |buttonsPressed|. +
      8. +
      9. Initialize |event|.{{GamepadChangeEvent/buttonsReleased}} to + the result of [=list/sorting in ascending order=] + |buttonsReleased|. +
      10. +
      11. Initialize |event|.{{GamepadChangeEvent/[[coalescedEvents]]}} + to |coalescedEvents|. +
      12. +
      +
    18. +
    19. [=list/Empty=] |gamepad|.{{Gamepad/[[pendingChanges]]}}. +
    20. +
    21. [=Dispatch=] |event| to |gamepad|. +
    22. +
    +

    + To run the gamepad steps for a [=document=] + |document:document|, run these steps: +

    +
      +
    1. Let |navigator:Navigator| be |document|'s [=relevant global + object=]'s {{Navigator}} object. +
    2. +
    3. If |navigator|.{{Navigator/[[hasGamepadGesture]]}} is `false`, + abort these steps. +
    4. +
    5. [=list/For each=] |gamepad:Gamepad?| of + |navigator|.{{Navigator/[[gamepads]]}}: +
        +
      1. If |gamepad| is not `null` and + |gamepad|.{{Gamepad/[[exposed]]}} is `true`, [=flush pending + change events=] for |gamepad|.
    +
    + These steps integrate with the event loop defined in HTML. [HTML] +

    To map and normalize axes for |gamepad:Gamepad|, run the following steps: @@ -718,7 +852,7 @@

    |logicalMinimum|) / (|logicalMaximum| − |logicalMinimum|).
  • Let |button:GamepadButton| be - |gamepad|.{{Gamepad/[[buttons]]}}[|mappedIndex|]. + |gamepad|.{{Gamepad/buttons}}[|mappedIndex|].
  • Set |button|.{{GamepadButton/[[value]]}} to |normalizedValue|. @@ -771,6 +905,45 @@

  • Return |buttonCopy|.
  • +

    + To create a snapshot for + |gamepad:Gamepad|, run the following steps: +

    +
      +
    1. Let |gamepadCopy| be a newly created {{Gamepad}} instance: +
        +
      1. Initialize |gamepadCopy|.{{Gamepad/id}} to + |gamepad|.{{Gamepad/id}}. +
      2. +
      3. Initialize |gamepadCopy|.{{Gamepad/index}} to + |gamepad|.{{Gamepad/index}}. +
      4. +
      5. Initialize |gamepadCopy|.{{Gamepad/mapping}} to + |gamepad|.{{Gamepad/mapping}}. +
      6. +
      7. Initialize |gamepadCopy|.{{Gamepad/[[connected]]}} to + |gamepad|.{{Gamepad/connected}}. +
      8. +
      9. Initialize |gamepadCopy|.{{Gamepad/[[timestamp]]}} to + |gamepad|.{{Gamepad/timestamp}}. +
      10. +
      11. Initialize |gamepadCopy|.{{Gamepad/[[axes]]}} to an empty + [=sequence=]. +
      12. +
      13. [=list/For each=] |axisValue:double| of + |gamepad|.{{Gamepad/axes}}, [=list/append=] |axisValue| to + |gamepadCopy|.{{Gamepad/[[axes]]}}. +
      14. +
      15. [=list/For each=] |button:GamepadButton| of + |gamepad|.{{Gamepad/buttons}}, [=create a button snapshot=] for + |button| and [=list/append=] it to + |gamepadCopy|.{{Gamepad/[[buttons]]}}. +
      16. +
      +
    2. +
    3. Return |gamepadCopy|. +
    4. +

  • @@ -1361,84 +1534,138 @@

    -
    +

    - GamepadAxisEvent Interface + GamepadChangeEvent Interface

             [Exposed=Window, SecureContext]
    -        interface GamepadAxisEvent : Event {
    -          constructor(DOMString type, GamepadAxisEventInit eventInitDict);
    -          readonly attribute long gamepadIndex;
    -          readonly attribute long axisIndex;
    -          readonly attribute double axisSnapshot;
    -          readonly attribute DOMHighResTimeStamp gamepadTimestamp;
    +        interface GamepadChangeEvent : Event {
    +          constructor(DOMString type, GamepadChangeEventInit eventInitDict);
    +          readonly attribute Gamepad gamepadSnapshot;
    +          readonly attribute FrozenArray<long> axesChanged;
    +          readonly attribute FrozenArray<long> buttonsChanged;
    +          readonly attribute FrozenArray<long> buttonsPressed;
    +          readonly attribute FrozenArray<long> buttonsReleased;
    +          sequence<GamepadChangeEvent> getCoalescedEvents();
             };
           
    +

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

    + + + + + + + + + + + +
    + Internal slot + + Initial value + + Description (non-normative) +
    + [[\coalescedEvents]] + + An empty [=sequence=] + + A [=sequence=] of {{GamepadChangeEvent}} instances that were + coalesced to create this event. +
    - gamepadIndex attribute + gamepadSnapshot attribute
    - The {{Gamepad/index}} attribute of the {{Gamepad}} associated with - this event. + A snapshot of the {{Gamepad}} when the event was created.
    - axisIndex attribute + axesChanged attribute
    - The index of the axis in the {{Gamepad/axes}} array. + An array of indices of items in the {{Gamepad/axes}} array + representing the axes that changed.
    - axisSnapshot attribute + buttonsChanged attribute
    - The axis value at the time when this event was created. + An array of indices of items in the {{Gamepad/buttons}} array + representing the buttons that changed.
    - gamepadTimestamp attribute + buttonsPressed attribute
    - The {{Gamepad/timestamp}} of the {{Gamepad}} associated with this - event at the time when the event was created. + An array of indices of items in the {{Gamepad/buttons}} array + representing the buttons that were pressed. +
    +
    + buttonsReleased attribute +
    +
    + An array of indices of items in the {{Gamepad/buttons}} array + representing the buttons that were released. +
    +
    + getCoalescedEvents() method +
    +
    + If this is a {{gamepadchange}} event, returns the [=sequence=] of + {{GamepadChangeEvent}} instances that were used to create this event. + Otherwise, returns an empty [=sequence=].

    - GamepadAxisEventInit dictionary + GamepadChangeEventInit dictionary

    -        dictionary GamepadAxisEventInit : EventInit {
    -          required long gamepadIndex;
    -          required long axisIndex;
    -          required double axisSnapshot;
    -          required DOMHighResTimeStamp gamepadTimestamp;
    +        dictionary GamepadChangeEventInit : EventInit {
    +          required Gamepad gamepadSnapshot;
    +          required sequence<long> axesChanged;
    +          required sequence<long> buttonsChanged;
    +          required sequence<long> buttonsPressed;
    +          required sequence<long> buttonsReleased;
             };
    -      
    -
    +

    +
    - gamepadIndex member + gamepadSnapshot member
    - The gamepad index for the event. + The gamepad snapshot for the event.
    - axisIndex member + axesChanged member
    - The button index for the event. + The changed axes for the event.
    - axisSnapshot member + buttonsChanged member
    - A copy of the current button state. + The changed buttons for the event.
    - gamepadTimestamp member + buttonsPressed member
    - The gamepad timestamp for the event. + The pressed buttons for the event. +
    +
    + buttonsReleased member +
    +
    + The released buttons for the event.
    @@ -1915,29 +2142,55 @@

    - The axischange, buttonchange, buttondown, and buttonup events + The gamepadchange and rawgamepadchange events

    [=User agent=]s implementing this specification MUST provide new DOM - events named axischange, buttonchange, buttondown, and - buttonup. The corresponding event MUST be of - type {{GamepadAxisEvent}} for {{axischange}}, or {{GamepadButtonEvent}} - for {{buttonchange}}, {{buttondown}}, and {{buttonup}}. All events MUST - fire on the {{Gamepad}} object. + events named gamepadchange and rawgamepadchange. These events MUST be of type + {{GamepadChangeEvent}} and MUST fire on the {{Gamepad}} object.

    When the [=user agent=] receives new button or axis input values from a gamepad, the [=user agent=] MUST compare the current button and axis - values with the previous values and dispatch {{axischange}} and - {{buttonchange}} events for each axis and button with a changed value. - The [=user agent=] MUST also compare the current button pressed state - with the previous button pressed state and dispatch {{buttondown}} and - {{buttonup}} events. + state with the previous state. If any button or axis has changed, a + {{rawgamepadchange}} event MUST be dispatched. +

    +

    + If any button or axis has changed, a pending {{gamepadchange}} event + MUST be created and enqueued. The [=user agent=] MAY [=flush pending + change events=] at any time after the {{rawgamepadchange}} has been + dispatched, but MUST do so before the animation frame callbacks are run + in the event loop. [[HTML]] +

    +

    + The {{gamepadchange}} and {{rawgamepadchange}} events MUST NOT be + dispatched before the [=user agent=] has dispatched a + {{gamepadconnected}} event for the gamepad. +

    +
    +
    +

    + The buttondown and buttonup events +

    +

    + [=User agent=]s implementing this specification MUST provide new DOM + events named buttondown and buttonup. The corresponding events MUST be of type + {{GamepadButtonEvent}} and MUST fire on the {{Gamepad}} object. +

    +

    + When the [=user agent=] receives new button or axis input values from a + gamepad, the [=user agent=] MUST compare the current button pressed + state with the previous button pressed state and dispatch + {{buttondown}} and {{buttonup}} events.

    - These events MUST NOT be dispatched before the [=user agent=] has - dispatched a {{gamepadconnected}} event for that gamepad. + The {{buttondown}} and {{buttonup}} events MUST NOT be dispatched + before the [=user agent=] has dispatched a {{gamepadconnected}} event + for the gamepad. The [=user agent=] MUST [=flush pending change + events=] for a gamepad before firing {{buttondown}} or {{buttonup}} for + a button on that gamepad.