diff --git a/index.html b/index.html index 68cfb7e..62ec6e3 100644 --- a/index.html +++ b/index.html @@ -145,7 +145,12 @@

         [Exposed=Window, SecureContext]
-        interface Gamepad {
+        interface Gamepad : EventTarget {
+          attribute EventHandler onbuttondown;
+          attribute EventHandler onbuttonup;
+          attribute EventHandler ongamepadchange;
+          attribute EventHandler onrawgamepadchange;
+
           readonly attribute DOMString id;
           readonly attribute long index;
           readonly attribute boolean connected;
@@ -301,8 +306,47 @@ 

A [=list=] containing the maximum logical value for each button + + + [[\pendingChanges]] + + + An empty [=list=] + + + A [=list=] containing pending {{GamepadChangeEvent}}s. + +
+
+ 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. +
+
+ ongamepadchange attribute +
+
+ {{Gamepad/ongamepadchange}} is an [=event handler IDL attribute=] for + the {{gamepadchange}} event type. +
+
+ onrawgamepadchange attribute +
+
+ {{Gamepad/onrawgamepadchange}} is an [=event handler IDL attribute=] + for the {{rawgamepadchange}} event type. +
id attribute
@@ -469,6 +513,26 @@

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. @@ -503,11 +567,11 @@

    `null`.
  12. 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|.
@@ -515,7 +579,211 @@

+
  • If |navigator|.{{Navigator/[[hasGamepadGesture]]}} is `false`, + abort these steps. +
  • +
  • Let |axesChanged| be an empty [=list=]. +
  • +
  • [=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|], [=list/append=] + |axisIndex| to |axesChanged|. +
    2. +
    +
  • +
  • Let |buttonsChanged| be an empty [=list=]. +
  • +
  • Let |buttonsPressed| be an empty [=list=]. +
  • +
  • Let |buttonsReleased| be an empty [=list=]. +
  • +
  • [=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 |oldButtonValues|[|buttonIndex|] is not equal to + |button|.{{GamepadButton/value}}, [=list/append=] |buttonIndex| + to |buttonsChanged|. +
    4. +
    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. +
    +
  • +
  • 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. +
    +
  • +
  • If |buttonsPressed| or |buttonsReleased| [=list/is not empty=], + [=flush pending change events=] for |gamepad|. +
  • +
  • [=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. +
    +
  • +
  • [=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. +
    +
  • +

    + 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|. +
      2. +
      +
    6. +
    +
    + These steps integrate with the event loop defined in HTML. [HTML] +

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

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

  • +

    + 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. +
    +

    + 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. +

    @@ -1177,8 +1505,7 @@

             [Exposed=Window, SecureContext]
    -
    -        interface GamepadEvent: Event {
    +        interface GamepadEvent : Event {
               constructor(DOMString type, GamepadEventInit eventInitDict);
               [SameObject] readonly attribute Gamepad gamepad;
             };
    @@ -1211,6 +1538,225 @@ 

    +
    +

    + GamepadChangeEvent Interface +

    +
    +        [Exposed=Window, SecureContext]
    +        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. +
    +
    +
    + gamepadSnapshot attribute +
    +
    + A snapshot of the {{Gamepad}} when the event was created. +
    +
    + axesChanged attribute +
    +
    + An array of indices of items in the {{Gamepad/axes}} array + representing the axes that changed. +
    +
    + buttonsChanged attribute +
    +
    + An array of indices of items in the {{Gamepad/buttons}} array + representing the buttons that changed. +
    +
    + buttonsPressed attribute +
    +
    + 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=]. +
    +
    +
    +

    + GamepadChangeEventInit dictionary +

    +
    +        dictionary GamepadChangeEventInit : EventInit {
    +          required Gamepad gamepadSnapshot;
    +          required sequence<long> axesChanged;
    +          required sequence<long> buttonsChanged;
    +          required sequence<long> buttonsPressed;
    +          required sequence<long> buttonsReleased;
    +        };
    +        
    +
    +
    + gamepadSnapshot member +
    +
    + The gamepad snapshot for the event. +
    +
    + axesChanged member +
    +
    + The changed axes for the event. +
    +
    + buttonsChanged member +
    +
    + The changed buttons for the event. +
    +
    + buttonsPressed member +
    +
    + The pressed buttons for the event. +
    +
    + buttonsReleased member +
    +
    + The released buttons 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 @@ -1600,13 +2146,55 @@

    - Other events + The gamepadchange and rawgamepadchange events +

    +

    + [=User agent=]s implementing this specification MUST provide new DOM + 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 + 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

    - 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 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. +

    +

    + 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.