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