1 // Copyright 2019 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 #include "device/vr/windows_mixed_reality/wrappers/wmr_input_manager.h"
5 
6 #include <windows.perception.h>
7 #include <windows.ui.input.spatial.h>
8 #include <wrl.h>
9 
10 #include <memory>
11 #include <vector>
12 
13 #include "base/callback_list.h"
14 #include "base/logging.h"
15 #include "base/strings/string_util.h"
16 #include "base/win/core_winrt_util.h"
17 #include "base/win/scoped_hstring.h"
18 #include "device/vr/windows_mixed_reality/mixed_reality_statics.h"
19 #include "device/vr/windows_mixed_reality/wrappers/test/mock_wmr_input_manager.h"
20 #include "device/vr/windows_mixed_reality/wrappers/wmr_input_source_state.h"
21 
22 using ABI::Windows::Foundation::ITypedEventHandler;
23 using ABI::Windows::Foundation::Collections::IVectorView;
24 using ABI::Windows::Perception::IPerceptionTimestamp;
25 using ABI::Windows::UI::Input::Spatial::ISpatialInteractionManager;
26 using ABI::Windows::UI::Input::Spatial::ISpatialInteractionSourceEventArgs;
27 using ABI::Windows::UI::Input::Spatial::ISpatialInteractionSourceEventArgs2;
28 using ABI::Windows::UI::Input::Spatial::ISpatialInteractionSourceState;
29 using ABI::Windows::UI::Input::Spatial::SpatialInteractionManager;
30 using ABI::Windows::UI::Input::Spatial::SpatialInteractionSourceEventArgs;
31 using ABI::Windows::UI::Input::Spatial::SpatialInteractionSourceState;
32 using Microsoft::WRL::Callback;
33 using Microsoft::WRL::ComPtr;
34 
35 using WMRPressKind =
36     ABI::Windows::UI::Input::Spatial::SpatialInteractionPressKind;
37 
38 typedef ITypedEventHandler<SpatialInteractionManager*,
39                            SpatialInteractionSourceEventArgs*>
40     SpatialInteractionSourceEventHandler;
41 
42 namespace device {
WMRInputSourceEventArgsImpl(ComPtr<ISpatialInteractionSourceEventArgs> args)43 WMRInputSourceEventArgsImpl::WMRInputSourceEventArgsImpl(
44     ComPtr<ISpatialInteractionSourceEventArgs> args)
45     : args_(args) {
46   DCHECK(args_);
47   HRESULT hr = args_.As(&args2_);
48   DCHECK(SUCCEEDED(hr));
49 }
50 
51 WMRInputSourceEventArgsImpl::~WMRInputSourceEventArgsImpl() = default;
52 
PressKind() const53 WMRPressKind WMRInputSourceEventArgsImpl::PressKind() const {
54   WMRPressKind press_kind;
55   HRESULT hr = args2_->get_PressKind(&press_kind);
56   DCHECK(SUCCEEDED(hr));
57   return press_kind;
58 }
59 
State() const60 std::unique_ptr<WMRInputSourceState> WMRInputSourceEventArgsImpl::State()
61     const {
62   ComPtr<ISpatialInteractionSourceState> wmr_state;
63   HRESULT hr = args_->get_State(&wmr_state);
64   DCHECK(SUCCEEDED(hr));
65   return std::make_unique<WMRInputSourceStateImpl>(wmr_state);
66 }
67 
WMRInputManagerImpl(ComPtr<ISpatialInteractionManager> manager)68 WMRInputManagerImpl::WMRInputManagerImpl(
69     ComPtr<ISpatialInteractionManager> manager)
70     : manager_(manager) {
71   DCHECK(manager_);
72   pressed_token_.value = 0;
73   released_token_.value = 0;
74   SubscribeEvents();
75 }
76 
~WMRInputManagerImpl()77 WMRInputManagerImpl::~WMRInputManagerImpl() {
78   UnsubscribeEvents();
79 }
80 
81 std::vector<std::unique_ptr<WMRInputSourceState>>
GetDetectedSourcesAtTimestamp(ComPtr<IPerceptionTimestamp> timestamp)82 WMRInputManagerImpl::GetDetectedSourcesAtTimestamp(
83     ComPtr<IPerceptionTimestamp> timestamp) {
84   std::vector<std::unique_ptr<WMRInputSourceState>> input_states;
85   ComPtr<IVectorView<SpatialInteractionSourceState*>> source_states;
86   if (FAILED(manager_->GetDetectedSourcesAtTimestamp(timestamp.Get(),
87                                                      &source_states)))
88     return input_states;
89 
90   uint32_t size;
91   HRESULT hr = source_states->get_Size(&size);
92   DCHECK(SUCCEEDED(hr));
93 
94   for (uint32_t i = 0; i < size; i++) {
95     ComPtr<ISpatialInteractionSourceState> source_state_wmr;
96     hr = source_states->GetAt(i, &source_state_wmr);
97     DCHECK(SUCCEEDED(hr));
98     input_states.push_back(
99         std::make_unique<WMRInputSourceStateImpl>(source_state_wmr));
100   }
101 
102   return input_states;
103 }
104 
105 std::unique_ptr<WMRInputManager::InputEventCallbackList::Subscription>
AddPressedCallback(const InputEventCallback & cb)106 WMRInputManagerImpl::AddPressedCallback(const InputEventCallback& cb) {
107   return pressed_callback_list_.Add(cb);
108 }
109 
110 std::unique_ptr<WMRInputManager::InputEventCallbackList::Subscription>
AddReleasedCallback(const InputEventCallback & cb)111 WMRInputManagerImpl::AddReleasedCallback(const InputEventCallback& cb) {
112   return released_callback_list_.Add(cb);
113 }
114 
OnSourcePressed(ISpatialInteractionManager * sender,ISpatialInteractionSourceEventArgs * raw_args)115 HRESULT WMRInputManagerImpl::OnSourcePressed(
116     ISpatialInteractionManager* sender,
117     ISpatialInteractionSourceEventArgs* raw_args) {
118   ComPtr<ISpatialInteractionSourceEventArgs> wmr_args(raw_args);
119   WMRInputSourceEventArgsImpl args(wmr_args);
120   pressed_callback_list_.Notify(args);
121   return S_OK;
122 }
123 
OnSourceReleased(ISpatialInteractionManager * sender,ISpatialInteractionSourceEventArgs * raw_args)124 HRESULT WMRInputManagerImpl::OnSourceReleased(
125     ISpatialInteractionManager* sender,
126     ISpatialInteractionSourceEventArgs* raw_args) {
127   ComPtr<ISpatialInteractionSourceEventArgs> wmr_args(raw_args);
128   WMRInputSourceEventArgsImpl args(wmr_args);
129   released_callback_list_.Notify(args);
130   return S_OK;
131 }
132 
SubscribeEvents()133 void WMRInputManagerImpl::SubscribeEvents() {
134   DCHECK(manager_);
135   DCHECK(pressed_token_.value == 0);
136   DCHECK(released_token_.value == 0);
137 
138   // The destructor ensures that we're unsubscribed so raw this is fine.
139   auto pressed_callback = Callback<SpatialInteractionSourceEventHandler>(
140       this, &WMRInputManagerImpl::OnSourcePressed);
141   HRESULT hr =
142       manager_->add_SourcePressed(pressed_callback.Get(), &pressed_token_);
143   DCHECK(SUCCEEDED(hr));
144 
145   // The destructor ensures that we're unsubscribed so raw this is fine.
146   auto released_callback = Callback<SpatialInteractionSourceEventHandler>(
147       this, &WMRInputManagerImpl::OnSourceReleased);
148   hr = manager_->add_SourceReleased(released_callback.Get(), &released_token_);
149   DCHECK(SUCCEEDED(hr));
150 }
151 
UnsubscribeEvents()152 void WMRInputManagerImpl::UnsubscribeEvents() {
153   if (!manager_)
154     return;
155 
156   HRESULT hr = S_OK;
157   if (pressed_token_.value != 0) {
158     hr = manager_->remove_SourcePressed(pressed_token_);
159     pressed_token_.value = 0;
160     DCHECK(SUCCEEDED(hr));
161   }
162 
163   if (released_token_.value != 0) {
164     hr = manager_->remove_SourceReleased(released_token_);
165     released_token_.value = 0;
166     DCHECK(SUCCEEDED(hr));
167   }
168 }
169 
170 }  // namespace device
171