OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "remoting/client/plugin/chromoting_instance.h" | 5 #include "remoting/client/plugin/chromoting_instance.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/lazy_instance.h" | 11 #include "base/lazy_instance.h" |
12 #include "base/logging.h" | 12 #include "base/logging.h" |
13 #include "base/message_loop.h" | 13 #include "base/message_loop.h" |
14 #include "base/stringprintf.h" | 14 #include "base/stringprintf.h" |
15 #include "base/synchronization/lock.h" | 15 #include "base/synchronization/lock.h" |
16 #include "base/synchronization/waitable_event.h" | 16 #include "base/synchronization/waitable_event.h" |
17 #include "base/threading/thread.h" | 17 #include "base/threading/thread.h" |
18 #include "media/base/media.h" | 18 #include "media/base/media.h" |
19 #include "ppapi/cpp/completion_callback.h" | 19 #include "ppapi/cpp/completion_callback.h" |
20 #include "ppapi/cpp/input_event.h" | 20 #include "ppapi/cpp/input_event.h" |
21 #include "ppapi/cpp/rect.h" | 21 #include "ppapi/cpp/rect.h" |
22 // TODO(wez): Remove this when crbug.com/86353 is complete. | 22 // TODO(wez): Remove this when crbug.com/86353 is complete. |
23 #include "ppapi/cpp/private/var_private.h" | 23 #include "ppapi/cpp/private/var_private.h" |
24 #include "remoting/base/util.h" | 24 #include "remoting/base/util.h" |
25 #include "remoting/client/client_config.h" | 25 #include "remoting/client/client_config.h" |
26 #include "remoting/client/chromoting_client.h" | 26 #include "remoting/client/chromoting_client.h" |
| 27 #include "remoting/client/mouse_input_filter.h" |
27 #include "remoting/client/plugin/chromoting_scriptable_object.h" | 28 #include "remoting/client/plugin/chromoting_scriptable_object.h" |
28 #include "remoting/client/plugin/pepper_input_handler.h" | 29 #include "remoting/client/plugin/pepper_input_handler.h" |
29 #include "remoting/client/plugin/pepper_view.h" | 30 #include "remoting/client/plugin/pepper_view.h" |
30 #include "remoting/client/plugin/pepper_view_proxy.h" | |
31 #include "remoting/client/plugin/pepper_xmpp_proxy.h" | 31 #include "remoting/client/plugin/pepper_xmpp_proxy.h" |
32 #include "remoting/client/rectangle_update_decoder.h" | 32 #include "remoting/client/rectangle_update_decoder.h" |
33 #include "remoting/protocol/connection_to_host.h" | 33 #include "remoting/protocol/connection_to_host.h" |
34 #include "remoting/protocol/host_stub.h" | 34 #include "remoting/protocol/host_stub.h" |
| 35 #include "remoting/protocol/key_event_tracker.h" |
35 | 36 |
36 namespace remoting { | 37 namespace remoting { |
37 | 38 |
38 // This flag blocks LOGs to the UI if we're already in the middle of logging | 39 // This flag blocks LOGs to the UI if we're already in the middle of logging |
39 // to the UI. This prevents a potential infinite loop if we encounter an error | 40 // to the UI. This prevents a potential infinite loop if we encounter an error |
40 // while sending the log message to the UI. | 41 // while sending the log message to the UI. |
41 static bool g_logging_to_plugin = false; | 42 static bool g_logging_to_plugin = false; |
42 static bool g_has_logging_instance = false; | 43 static bool g_has_logging_instance = false; |
43 static ChromotingInstance* g_logging_instance = NULL; | 44 static ChromotingInstance* g_logging_instance = NULL; |
44 static logging::LogMessageHandlerFunction g_logging_old_handler = NULL; | 45 static logging::LogMessageHandlerFunction g_logging_old_handler = NULL; |
(...skipping 30 matching lines...) Expand all Loading... |
75 // to it. This will stop all logging in all Chromoting instances. | 76 // to it. This will stop all logging in all Chromoting instances. |
76 UnregisterLoggingInstance(); | 77 UnregisterLoggingInstance(); |
77 | 78 |
78 if (client_.get()) { | 79 if (client_.get()) { |
79 base::WaitableEvent done_event(true, false); | 80 base::WaitableEvent done_event(true, false); |
80 client_->Stop(base::Bind(&base::WaitableEvent::Signal, | 81 client_->Stop(base::Bind(&base::WaitableEvent::Signal, |
81 base::Unretained(&done_event))); | 82 base::Unretained(&done_event))); |
82 done_event.Wait(); | 83 done_event.Wait(); |
83 } | 84 } |
84 | 85 |
85 // Stopping the context shutdown all chromoting threads. This is a requirement | 86 // Stopping the context shuts down all chromoting threads. |
86 // before we can call Detach() on |view_proxy_|. | |
87 context_.Stop(); | 87 context_.Stop(); |
88 | 88 |
89 if (view_proxy_.get()) { | |
90 view_proxy_->Detach(); | |
91 } | |
92 | |
93 // Delete |thread_proxy_| before we detach |plugin_message_loop_|, | 89 // Delete |thread_proxy_| before we detach |plugin_message_loop_|, |
94 // otherwise ScopedThreadProxy may DCHECK when destroying. | 90 // otherwise ScopedThreadProxy may DCHECK when being destroyed. |
95 thread_proxy_.reset(); | 91 thread_proxy_.reset(); |
96 | 92 |
97 plugin_message_loop_->Detach(); | 93 plugin_message_loop_->Detach(); |
98 } | 94 } |
99 | 95 |
100 bool ChromotingInstance::Init(uint32_t argc, | 96 bool ChromotingInstance::Init(uint32_t argc, |
101 const char* argn[], | 97 const char* argn[], |
102 const char* argv[]) { | 98 const char* argv[]) { |
103 CHECK(!initialized_); | 99 CHECK(!initialized_); |
104 initialized_ = true; | 100 initialized_ = true; |
105 | 101 |
106 VLOG(1) << "Started ChromotingInstance::Init"; | 102 VLOG(1) << "Started ChromotingInstance::Init"; |
107 | 103 |
108 // Check to make sure the media library is initialized. | 104 // Check to make sure the media library is initialized. |
109 // http://crbug.com/91521. | 105 // http://crbug.com/91521. |
110 if (!media::IsMediaLibraryInitialized()) { | 106 if (!media::IsMediaLibraryInitialized()) { |
111 LOG(ERROR) << "Media library not initialized."; | 107 LOG(ERROR) << "Media library not initialized."; |
112 return false; | 108 return false; |
113 } | 109 } |
114 | 110 |
115 // Start all the threads. | 111 // Start all the threads. |
116 context_.Start(); | 112 context_.Start(); |
117 | 113 |
118 // Create the chromoting objects that don't depend on the network connection. | 114 // Create the chromoting objects that don't depend on the network connection. |
119 view_.reset(new PepperView(this, &context_)); | 115 view_.reset(new PepperView(this, &context_)); |
120 view_proxy_ = new PepperViewProxy(this, view_.get(), plugin_message_loop_); | |
121 rectangle_decoder_ = new RectangleUpdateDecoder( | 116 rectangle_decoder_ = new RectangleUpdateDecoder( |
122 context_.decode_message_loop(), view_proxy_); | 117 context_.decode_message_loop(), view_.get()); |
123 | 118 |
124 // Default to a medium grey. | 119 // Default to a medium grey. |
125 view_->SetSolidFill(0xFFCDCDCD); | 120 view_->SetSolidFill(0xFFCDCDCD); |
126 | 121 |
127 return true; | 122 return true; |
128 } | 123 } |
129 | 124 |
130 void ChromotingInstance::Connect(const ClientConfig& config) { | 125 void ChromotingInstance::Connect(const ClientConfig& config) { |
131 DCHECK(plugin_message_loop_->BelongsToCurrentThread()); | 126 DCHECK(plugin_message_loop_->BelongsToCurrentThread()); |
132 | 127 |
133 host_connection_.reset(new protocol::ConnectionToHost( | 128 host_connection_.reset(new protocol::ConnectionToHost( |
134 context_.network_message_loop(), this, true)); | 129 context_.network_message_loop(), this, true)); |
135 | |
136 input_handler_.reset(new PepperInputHandler(&context_, | |
137 host_connection_.get(), | |
138 view_proxy_)); | |
139 | |
140 client_.reset(new ChromotingClient(config, &context_, host_connection_.get(), | 130 client_.reset(new ChromotingClient(config, &context_, host_connection_.get(), |
141 view_proxy_, rectangle_decoder_.get(), | 131 view_.get(), rectangle_decoder_.get(), |
142 input_handler_.get(), base::Closure())); | 132 base::Closure())); |
143 | 133 |
144 LOG(INFO) << "Connecting to " << config.host_jid | 134 LOG(INFO) << "Connecting to " << config.host_jid |
145 << ". Local jid: " << config.local_jid << "."; | 135 << ". Local jid: " << config.local_jid << "."; |
146 | 136 |
147 // Setup the XMPP Proxy. | 137 // Setup the XMPP Proxy. |
148 ChromotingScriptableObject* scriptable_object = GetScriptableObject(); | 138 ChromotingScriptableObject* scriptable_object = GetScriptableObject(); |
149 scoped_refptr<PepperXmppProxy> xmpp_proxy = | 139 scoped_refptr<PepperXmppProxy> xmpp_proxy = |
150 new PepperXmppProxy(scriptable_object->AsWeakPtr(), | 140 new PepperXmppProxy(scriptable_object->AsWeakPtr(), |
151 plugin_message_loop_, | 141 plugin_message_loop_, |
152 context_.network_message_loop()); | 142 context_.network_message_loop()); |
(...skipping 15 matching lines...) Expand all Loading... |
168 if (client_.get()) { | 158 if (client_.get()) { |
169 // TODO(sergeyu): Should we disconnect asynchronously? | 159 // TODO(sergeyu): Should we disconnect asynchronously? |
170 base::WaitableEvent done_event(true, false); | 160 base::WaitableEvent done_event(true, false); |
171 client_->Stop(base::Bind(&base::WaitableEvent::Signal, | 161 client_->Stop(base::Bind(&base::WaitableEvent::Signal, |
172 base::Unretained(&done_event))); | 162 base::Unretained(&done_event))); |
173 done_event.Wait(); | 163 done_event.Wait(); |
174 client_.reset(); | 164 client_.reset(); |
175 } | 165 } |
176 | 166 |
177 input_handler_.reset(); | 167 input_handler_.reset(); |
| 168 key_event_tracker_.reset(); |
| 169 mouse_input_filter_.reset(); |
178 host_connection_.reset(); | 170 host_connection_.reset(); |
179 | 171 |
180 GetScriptableObject()->SetConnectionStatus( | 172 GetScriptableObject()->SetConnectionStatus( |
181 ChromotingScriptableObject::STATUS_CLOSED, | 173 ChromotingScriptableObject::STATUS_CLOSED, |
182 ChromotingScriptableObject::ERROR_NONE); | 174 ChromotingScriptableObject::ERROR_NONE); |
183 } | 175 } |
184 | 176 |
185 void ChromotingInstance::DidChangeView(const pp::Rect& position, | 177 void ChromotingInstance::DidChangeView(const pp::Rect& position, |
186 const pp::Rect& clip) { | 178 const pp::Rect& clip) { |
187 DCHECK(plugin_message_loop_->BelongsToCurrentThread()); | 179 DCHECK(plugin_message_loop_->BelongsToCurrentThread()); |
188 | 180 |
189 view_->SetPluginSize(SkISize::Make(position.width(), position.height())); | 181 SkISize new_size = SkISize::Make(position.width(), position.height()); |
190 | 182 if (view_->SetViewSize(new_size)) { |
191 // TODO(wez): Pass the dimensions of the plugin to the RectangleDecoder | 183 if (mouse_input_filter_.get()) { |
192 // and let it generate the necessary refresh events. | 184 mouse_input_filter_->set_input_size(new_size); |
193 // If scale-to-fit is enabled then update the scaling ratios. | 185 } |
194 // We also force a full-frame refresh, in case the ratios changed. | 186 rectangle_decoder_->SetOutputSize(new_size); |
195 if (scale_to_fit_) { | |
196 rectangle_decoder_->SetScaleRatios(view_->GetHorizontalScaleRatio(), | |
197 view_->GetVerticalScaleRatio()); | |
198 rectangle_decoder_->RefreshFullFrame(); | |
199 } | 187 } |
200 | 188 |
201 // Notify the RectangleDecoder of the new clip rect. | |
202 rectangle_decoder_->UpdateClipRect( | 189 rectangle_decoder_->UpdateClipRect( |
203 SkIRect::MakeXYWH(clip.x(), clip.y(), clip.width(), clip.height())); | 190 SkIRect::MakeXYWH(clip.x(), clip.y(), clip.width(), clip.height())); |
204 } | 191 } |
205 | 192 |
206 bool ChromotingInstance::HandleInputEvent(const pp::InputEvent& event) { | 193 bool ChromotingInstance::HandleInputEvent(const pp::InputEvent& event) { |
207 DCHECK(plugin_message_loop_->BelongsToCurrentThread()); | 194 DCHECK(plugin_message_loop_->BelongsToCurrentThread()); |
208 if (!input_handler_.get()) { | 195 |
| 196 // Never inject events if the end of the input pipeline doesn't exist. |
| 197 // If it does exist but the pipeline doesn't, construct a pipeline. |
| 198 // TODO(wez): This is really ugly. We should create the pipeline when |
| 199 // the ConnectionToHost's InputStub exists. |
| 200 if (!host_connection_.get()) { |
209 return false; | 201 return false; |
| 202 } else if (!input_handler_.get()) { |
| 203 protocol::InputStub* input_stub = host_connection_->input_stub(); |
| 204 if (!input_stub) |
| 205 return false; |
| 206 mouse_input_filter_.reset(new MouseInputFilter(input_stub)); |
| 207 mouse_input_filter_->set_input_size(view_->get_view_size()); |
| 208 key_event_tracker_.reset( |
| 209 new protocol::KeyEventTracker(mouse_input_filter_.get())); |
| 210 input_handler_.reset( |
| 211 new PepperInputHandler(key_event_tracker_.get())); |
210 } | 212 } |
211 | 213 |
212 PepperInputHandler* pih | 214 // TODO(wez): When we have a good hook into Host dimensions changes, move |
213 = static_cast<PepperInputHandler*>(input_handler_.get()); | 215 // this there. |
| 216 mouse_input_filter_->set_output_size(view_->get_host_size()); |
214 | 217 |
215 switch (event.GetType()) { | 218 return input_handler_->HandleInputEvent(event); |
216 case PP_INPUTEVENT_TYPE_MOUSEDOWN: { | |
217 pih->HandleMouseButtonEvent(true, pp::MouseInputEvent(event)); | |
218 return true; | |
219 } | |
220 | |
221 case PP_INPUTEVENT_TYPE_MOUSEUP: { | |
222 pih->HandleMouseButtonEvent(false, pp::MouseInputEvent(event)); | |
223 return true; | |
224 } | |
225 | |
226 case PP_INPUTEVENT_TYPE_MOUSEMOVE: | |
227 case PP_INPUTEVENT_TYPE_MOUSEENTER: | |
228 case PP_INPUTEVENT_TYPE_MOUSELEAVE: { | |
229 pih->HandleMouseMoveEvent(pp::MouseInputEvent(event)); | |
230 return true; | |
231 } | |
232 | |
233 case PP_INPUTEVENT_TYPE_WHEEL: { | |
234 pih->HandleMouseWheelEvent(pp::WheelInputEvent(event)); | |
235 return true; | |
236 } | |
237 | |
238 case PP_INPUTEVENT_TYPE_CONTEXTMENU: { | |
239 // We need to return true here or else we'll get a local (plugin) context | |
240 // menu instead of the mouseup event for the right click. | |
241 return true; | |
242 } | |
243 | |
244 case PP_INPUTEVENT_TYPE_KEYDOWN: { | |
245 pp::KeyboardInputEvent key = pp::KeyboardInputEvent(event); | |
246 VLOG(3) << "PP_INPUTEVENT_TYPE_KEYDOWN" << " key=" << key.GetKeyCode(); | |
247 pih->HandleKeyEvent(true, key); | |
248 return true; | |
249 } | |
250 | |
251 case PP_INPUTEVENT_TYPE_KEYUP: { | |
252 pp::KeyboardInputEvent key = pp::KeyboardInputEvent(event); | |
253 VLOG(3) << "PP_INPUTEVENT_TYPE_KEYUP" << " key=" << key.GetKeyCode(); | |
254 pih->HandleKeyEvent(false, key); | |
255 return true; | |
256 } | |
257 | |
258 case PP_INPUTEVENT_TYPE_CHAR: { | |
259 pih->HandleCharacterEvent(pp::KeyboardInputEvent(event)); | |
260 return true; | |
261 } | |
262 | |
263 default: { | |
264 LOG(INFO) << "Unhandled input event: " << event.GetType(); | |
265 break; | |
266 } | |
267 } | |
268 | |
269 return false; | |
270 } | 219 } |
271 | 220 |
272 ChromotingScriptableObject* ChromotingInstance::GetScriptableObject() { | 221 ChromotingScriptableObject* ChromotingInstance::GetScriptableObject() { |
273 pp::VarPrivate object = GetInstanceObject(); | 222 pp::VarPrivate object = GetInstanceObject(); |
274 if (!object.is_undefined()) { | 223 if (!object.is_undefined()) { |
275 pp::deprecated::ScriptableObject* so = object.AsScriptableObject(); | 224 pp::deprecated::ScriptableObject* so = object.AsScriptableObject(); |
276 DCHECK(so != NULL); | 225 DCHECK(so != NULL); |
277 return static_cast<ChromotingScriptableObject*>(so); | 226 return static_cast<ChromotingScriptableObject*>(so); |
278 } | 227 } |
279 LOG(ERROR) << "Unable to get ScriptableObject for Chromoting plugin."; | 228 LOG(ERROR) << "Unable to get ScriptableObject for Chromoting plugin."; |
280 return NULL; | 229 return NULL; |
281 } | 230 } |
282 | 231 |
283 void ChromotingInstance::SetScaleToFit(bool scale_to_fit) { | |
284 DCHECK(plugin_message_loop_->BelongsToCurrentThread()); | |
285 | |
286 if (scale_to_fit == scale_to_fit_) | |
287 return; | |
288 | |
289 scale_to_fit_ = scale_to_fit; | |
290 if (scale_to_fit) { | |
291 rectangle_decoder_->SetScaleRatios(view_->GetHorizontalScaleRatio(), | |
292 view_->GetVerticalScaleRatio()); | |
293 } else { | |
294 rectangle_decoder_->SetScaleRatios(1.0, 1.0); | |
295 } | |
296 | |
297 // TODO(wez): The RectangleDecoder should generate refresh events | |
298 // as necessary in response to any scaling change. | |
299 rectangle_decoder_->RefreshFullFrame(); | |
300 } | |
301 | |
302 // static | 232 // static |
303 void ChromotingInstance::RegisterLogMessageHandler() { | 233 void ChromotingInstance::RegisterLogMessageHandler() { |
304 base::AutoLock lock(g_logging_lock.Get()); | 234 base::AutoLock lock(g_logging_lock.Get()); |
305 | 235 |
306 VLOG(1) << "Registering global log handler"; | 236 VLOG(1) << "Registering global log handler"; |
307 | 237 |
308 // Record previous handler so we can call it in a chain. | 238 // Record previous handler so we can call it in a chain. |
309 g_logging_old_handler = logging::GetLogMessageHandler(); | 239 g_logging_old_handler = logging::GetLogMessageHandler(); |
310 | 240 |
311 // Set up log message handler. | 241 // Set up log message handler. |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
396 return instance_object_; | 326 return instance_object_; |
397 } | 327 } |
398 | 328 |
399 ChromotingStats* ChromotingInstance::GetStats() { | 329 ChromotingStats* ChromotingInstance::GetStats() { |
400 if (!client_.get()) | 330 if (!client_.get()) |
401 return NULL; | 331 return NULL; |
402 return client_->GetStats(); | 332 return client_->GetStats(); |
403 } | 333 } |
404 | 334 |
405 void ChromotingInstance::ReleaseAllKeys() { | 335 void ChromotingInstance::ReleaseAllKeys() { |
406 if (!input_handler_.get()) { | 336 if (key_event_tracker_.get()) { |
407 return; | 337 key_event_tracker_->ReleaseAllKeys(); |
408 } | 338 } |
409 | |
410 input_handler_->ReleaseAllKeys(); | |
411 } | 339 } |
412 | 340 |
413 } // namespace remoting | 341 } // namespace remoting |
OLD | NEW |