1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/modules/xr/xr_canvas_input_provider.h"
6 
7 #include "device/vr/public/mojom/vr_service.mojom-blink.h"
8 #include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
9 #include "third_party/blink/renderer/core/events/pointer_event.h"
10 #include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
11 #include "third_party/blink/renderer/modules/xr/xr_frame_provider.h"
12 #include "third_party/blink/renderer/modules/xr/xr_input_source.h"
13 #include "third_party/blink/renderer/modules/xr/xr_session.h"
14 #include "third_party/blink/renderer/modules/xr/xr_system.h"
15 #include "third_party/blink/renderer/modules/xr/xr_view.h"
16 
17 namespace blink {
18 
19 namespace {
20 
21 class XRCanvasInputEventListener : public NativeEventListener {
22  public:
XRCanvasInputEventListener(XRCanvasInputProvider * input_provider)23   XRCanvasInputEventListener(XRCanvasInputProvider* input_provider)
24       : input_provider_(input_provider) {}
25 
Invoke(ExecutionContext * execution_context,Event * event)26   void Invoke(ExecutionContext* execution_context, Event* event) override {
27     if (!input_provider_->ShouldProcessEvents())
28       return;
29 
30     auto* pointer_event = To<PointerEvent>(event);
31     DCHECK(pointer_event);
32     if (!pointer_event->isPrimary())
33       return;
34 
35     if (event->type() == event_type_names::kPointerdown) {
36       input_provider_->OnPointerDown(pointer_event);
37     } else if (event->type() == event_type_names::kPointerup ||
38                event->type() == event_type_names::kPointercancel) {
39       input_provider_->OnPointerUp(pointer_event);
40     }
41   }
42 
Trace(Visitor * visitor)43   void Trace(Visitor* visitor) override {
44     visitor->Trace(input_provider_);
45     EventListener::Trace(visitor);
46   }
47 
48  private:
49   Member<XRCanvasInputProvider> input_provider_;
50 };
51 
52 }  // namespace
53 
XRCanvasInputProvider(XRSession * session,HTMLCanvasElement * canvas)54 XRCanvasInputProvider::XRCanvasInputProvider(XRSession* session,
55                                              HTMLCanvasElement* canvas)
56     : session_(session), canvas_(canvas) {
57   listener_ = MakeGarbageCollected<XRCanvasInputEventListener>(this);
58   canvas->addEventListener(event_type_names::kPointerdown, listener_);
59   canvas->addEventListener(event_type_names::kPointerup, listener_);
60   canvas->addEventListener(event_type_names::kPointercancel, listener_);
61 }
62 
~XRCanvasInputProvider()63 XRCanvasInputProvider::~XRCanvasInputProvider() {}
64 
Stop()65 void XRCanvasInputProvider::Stop() {
66   if (!listener_) {
67     return;
68   }
69   canvas_->removeEventListener(event_type_names::kPointerdown, listener_);
70   canvas_->removeEventListener(event_type_names::kPointerup, listener_);
71   canvas_->removeEventListener(event_type_names::kPointercancel, listener_);
72   canvas_ = nullptr;
73   listener_ = nullptr;
74 }
75 
ShouldProcessEvents()76 bool XRCanvasInputProvider::ShouldProcessEvents() {
77   // Don't process canvas gestures if there's an active immersive session.
78   return !(session_->xr()->frameProvider()->immersive_session());
79 }
80 
OnPointerDown(PointerEvent * event)81 void XRCanvasInputProvider::OnPointerDown(PointerEvent* event) {
82   UpdateInputSource(event);
83   input_source_->OnSelectStart();
84 }
85 
OnPointerUp(PointerEvent * event)86 void XRCanvasInputProvider::OnPointerUp(PointerEvent* event) {
87   UpdateInputSource(event);
88   input_source_->OnSelect();
89   ClearInputSource();
90 }
91 
GetInputSource()92 XRInputSource* XRCanvasInputProvider::GetInputSource() {
93   return input_source_;
94 }
95 
UpdateInputSource(PointerEvent * event)96 void XRCanvasInputProvider::UpdateInputSource(PointerEvent* event) {
97   if (!canvas_)
98     return;
99 
100   if (!input_source_) {
101     // XRSession doesn't like source ID's of 0.  We should only be processing
102     // Canvas Input events in non-immersive sessions anyway, where we don't
103     // expect other controllers, so this number is somewhat arbitrary anyway.
104     input_source_ = MakeGarbageCollected<XRInputSource>(
105         session_, 1, device::mojom::XRTargetRayMode::TAPPING);
106     session_->AddTransientInputSource(input_source_);
107   }
108 
109   // Get the event location relative to the canvas element.
110   double element_x = event->pageX() - canvas_->OffsetLeft();
111   double element_y = event->pageY() - canvas_->OffsetTop();
112 
113   // Unproject the event location into a pointer matrix. This takes the 2D
114   // position of the screen interaction and shoves it backwards through the
115   // projection matrix to get a 3D point in space, which is then returned in
116   // matrix form so we can use it as an XRInputSource's pointerMatrix.
117   XRViewData& view = session_->views()[0];
118   TransformationMatrix viewer_from_pointer = view.UnprojectPointer(
119       element_x, element_y, canvas_->OffsetWidth(), canvas_->OffsetHeight());
120 
121   // Update the pointer pose in input space. For screen tapping, input
122   // space is equivalent to viewer space.
123   input_source_->SetInputFromPointer(&viewer_from_pointer);
124 }
125 
ClearInputSource()126 void XRCanvasInputProvider::ClearInputSource() {
127   session_->RemoveTransientInputSource(input_source_);
128   input_source_ = nullptr;
129 }
130 
Trace(Visitor * visitor)131 void XRCanvasInputProvider::Trace(Visitor* visitor) {
132   visitor->Trace(session_);
133   visitor->Trace(canvas_);
134   visitor->Trace(listener_);
135   visitor->Trace(input_source_);
136 }
137 
138 }  // namespace blink
139