blob: ffd5be1dbace899482afc86fc3584a20352255e4 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/xr/xr_canvas_input_provider.h"
#include "device/vr/public/mojom/vr_service.mojom-blink.h"
#include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
#include "third_party/blink/renderer/core/events/pointer_event.h"
#include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
#include "third_party/blink/renderer/modules/xr/xr_frame_provider.h"
#include "third_party/blink/renderer/modules/xr/xr_input_source.h"
#include "third_party/blink/renderer/modules/xr/xr_session.h"
#include "third_party/blink/renderer/modules/xr/xr_system.h"
#include "third_party/blink/renderer/modules/xr/xr_view.h"
namespace blink {
namespace {
class XRCanvasInputEventListener : public NativeEventListener {
public:
XRCanvasInputEventListener(XRCanvasInputProvider* input_provider)
: input_provider_(input_provider) {}
void Invoke(ExecutionContext* execution_context, Event* event) override {
if (!input_provider_->ShouldProcessEvents())
return;
auto* pointer_event = To<PointerEvent>(event);
DCHECK(pointer_event);
if (!pointer_event->isPrimary())
return;
if (event->type() == event_type_names::kPointerdown) {
input_provider_->OnPointerDown(pointer_event);
} else if (event->type() == event_type_names::kPointerup ||
event->type() == event_type_names::kPointercancel) {
input_provider_->OnPointerUp(pointer_event);
}
}
void Trace(Visitor* visitor) const override {
visitor->Trace(input_provider_);
EventListener::Trace(visitor);
}
private:
Member<XRCanvasInputProvider> input_provider_;
};
} // namespace
XRCanvasInputProvider::XRCanvasInputProvider(XRSession* session,
HTMLCanvasElement* canvas)
: session_(session), canvas_(canvas) {
listener_ = MakeGarbageCollected<XRCanvasInputEventListener>(this);
canvas->addEventListener(event_type_names::kPointerdown, listener_);
canvas->addEventListener(event_type_names::kPointerup, listener_);
canvas->addEventListener(event_type_names::kPointercancel, listener_);
}
XRCanvasInputProvider::~XRCanvasInputProvider() {}
void XRCanvasInputProvider::Stop() {
if (!listener_) {
return;
}
canvas_->removeEventListener(event_type_names::kPointerdown, listener_);
canvas_->removeEventListener(event_type_names::kPointerup, listener_);
canvas_->removeEventListener(event_type_names::kPointercancel, listener_);
canvas_ = nullptr;
listener_ = nullptr;
}
bool XRCanvasInputProvider::ShouldProcessEvents() {
// Don't process canvas gestures if there's an active immersive session.
return !(session_->xr()->frameProvider()->immersive_session());
}
void XRCanvasInputProvider::OnPointerDown(PointerEvent* event) {
UpdateInputSource(event);
input_source_->OnSelectStart();
}
void XRCanvasInputProvider::OnPointerUp(PointerEvent* event) {
UpdateInputSource(event);
input_source_->OnSelect();
ClearInputSource();
}
XRInputSource* XRCanvasInputProvider::GetInputSource() {
return input_source_;
}
void XRCanvasInputProvider::UpdateInputSource(PointerEvent* event) {
if (!canvas_)
return;
if (!input_source_) {
// XRSession doesn't like source ID's of 0. We should only be processing
// Canvas Input events in non-immersive sessions anyway, where we don't
// expect other controllers, so this number is somewhat arbitrary anyway.
input_source_ = MakeGarbageCollected<XRInputSource>(
session_, 1, device::mojom::XRTargetRayMode::TAPPING);
session_->AddTransientInputSource(input_source_);
}
// Get the event location relative to the canvas element.
double element_x = event->pageX() - canvas_->OffsetLeft();
double element_y = event->pageY() - canvas_->OffsetTop();
// Unproject the event location into a pointer matrix. This takes the 2D
// position of the screen interaction and shoves it backwards through the
// projection matrix to get a 3D point in space, which is then returned in
// matrix form so we can use it as an XRInputSource's pointerMatrix.
XRViewData* view = session_->views()[0];
TransformationMatrix viewer_from_pointer = view->UnprojectPointer(
element_x, element_y, canvas_->OffsetWidth(), canvas_->OffsetHeight());
// Update the pointer pose in input space. For screen tapping, input
// space is equivalent to viewer space.
input_source_->SetInputFromPointer(&viewer_from_pointer);
}
void XRCanvasInputProvider::ClearInputSource() {
session_->RemoveTransientInputSource(input_source_);
input_source_ = nullptr;
}
void XRCanvasInputProvider::Trace(Visitor* visitor) const {
visitor->Trace(session_);
visitor->Trace(canvas_);
visitor->Trace(listener_);
visitor->Trace(input_source_);
}
} // namespace blink