Chromium Code Reviews| Index: remoting/client/plugin/chromoting_instance.cc |
| diff --git a/remoting/client/plugin/chromoting_instance.cc b/remoting/client/plugin/chromoting_instance.cc |
| index 37bceee87bf26631c5e5b5735bff033fb6cd9594..12c9e7a95df125a54b8bfb90049dc3d43de5364e 100644 |
| --- a/remoting/client/plugin/chromoting_instance.cc |
| +++ b/remoting/client/plugin/chromoting_instance.cc |
| @@ -4,6 +4,7 @@ |
| #include "remoting/client/plugin/chromoting_instance.h" |
| +#include <algorithm> |
| #include <string> |
| #include <vector> |
| @@ -55,6 +56,12 @@ namespace { |
| // 32-bit BGRA is 4 bytes per pixel. |
| const int kBytesPerPixel = 4; |
| +#if defined(ARCH_CPU_LITTLE_ENDIAN) |
| +const uint32_t kPixelAlphaMask = 0xff000000; |
| +#else // !defined(ARCH_CPU_LITTLE_ENDIAN) |
| +const uint32_t kPixelAlphaMask = 0x000000ff; |
| +#endif // !defined(ARCH_CPU_LITTLE_ENDIAN) |
| + |
| // Default DPI to assume for old clients that use notifyClientResolution. |
| const int kDefaultDPI = 96; |
| @@ -125,6 +132,11 @@ std::string ConnectionErrorToString(protocol::ErrorCode error) { |
| return std::string(); |
| } |
| +// Returns true |pixel| is not completely transparent. |
|
Wez
2013/09/05 20:24:45
nit: "... true if ..."
alexeypa (please no reviews)
2013/09/06 20:00:17
Done.
|
| +bool IsVisiblePixel(uint32_t pixel) { |
| + return (pixel & kPixelAlphaMask) != 0; |
| +} |
| + |
| // This flag blocks LOGs to the UI if we're already in the middle of logging |
| // to the UI. This prevents a potential infinite loop if we encounter an error |
| // while sending the log message to the UI. |
| @@ -144,7 +156,7 @@ logging::LogMessageHandlerFunction g_logging_old_handler = NULL; |
| const char ChromotingInstance::kApiFeatures[] = |
| "highQualityScaling injectKeyEvent sendClipboardItem remapKey trapKey " |
| "notifyClientResolution pauseVideo pauseAudio asyncPin thirdPartyAuth " |
| - "pinlessAuth extensionMessage"; |
| + "pinlessAuth extensionMessage allowMouseLock"; |
| const char ChromotingInstance::kRequestedCapabilities[] = ""; |
| const char ChromotingInstance::kSupportedCapabilities[] = "desktopShape"; |
| @@ -170,14 +182,18 @@ bool ChromotingInstance::ParseAuthMethods(const std::string& auth_methods_str, |
| ChromotingInstance::ChromotingInstance(PP_Instance pp_instance) |
| : pp::Instance(pp_instance), |
| + pp::MouseLock(this), |
| initialized_(false), |
| plugin_task_runner_(new PluginThreadTaskRunner(&plugin_thread_delegate_)), |
| context_(plugin_task_runner_.get()), |
| + focused_(false), |
| + mouse_lock_state_(MouseLockDisabled), |
| input_tracker_(&mouse_input_filter_), |
| key_mapper_(&input_tracker_), |
| normalizing_input_filter_(CreateNormalizingInputFilter(&key_mapper_)), |
| input_handler_(normalizing_input_filter_.get()), |
| use_async_pin_dialog_(false), |
| + callback_factory_(this), |
| weak_factory_(this) { |
| RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_WHEEL); |
| RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD); |
| @@ -304,9 +320,18 @@ void ChromotingInstance::HandleMessage(const pp::Var& message) { |
| HandleRequestPairing(*data); |
| } else if (method == "extensionMessage") { |
| HandleExtensionMessage(*data); |
| + } else if (method == "allowMouseLock") { |
| + HandleAllowMouseLockMessage(); |
| } |
| } |
| +void ChromotingInstance::DidChangeFocus(bool has_focus) { |
| + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); |
| + |
| + focused_ = has_focus; |
| + RequestMouseLock(); |
|
Wez
2013/09/05 20:24:45
nit: It looks strange to call RequestMouseLock() i
alexeypa (please no reviews)
2013/09/06 20:00:17
RequestMouseLock() checks whether the plugin is fo
|
| +} |
| + |
| void ChromotingInstance::DidChangeView(const pp::View& view) { |
| DCHECK(plugin_task_runner_->BelongsToCurrentThread()); |
| @@ -323,7 +348,17 @@ bool ChromotingInstance::HandleInputEvent(const pp::InputEvent& event) { |
| if (!IsConnected()) |
| return false; |
| - return input_handler_.HandleInputEvent(event); |
| + return input_handler_.HandleInputEvent(event, |
| + mouse_lock_state_ == MouseLockOn); |
| +} |
| + |
| +void ChromotingInstance::MouseLockLost() { |
| + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); |
| + DCHECK(mouse_lock_state_ == MouseLockOn || |
| + mouse_lock_state_ == MouseLockCancelling); |
| + |
| + mouse_lock_state_ = MouseLockOff; |
| + EnableMousePointer(); |
| } |
| void ChromotingInstance::SetDesktopSize(const SkISize& size, |
| @@ -464,6 +499,8 @@ void ChromotingInstance::InjectClipboardEvent( |
| void ChromotingInstance::SetCursorShape( |
| const protocol::CursorShapeInfo& cursor_shape) { |
| + COMPILE_ASSERT(sizeof(uint32_t) == kBytesPerPixel, rgba_pixels_are_32bit); |
| + |
| if (!cursor_shape.has_data() || |
| !cursor_shape.has_width() || |
| !cursor_shape.has_height() || |
| @@ -500,47 +537,58 @@ void ChromotingInstance::SetCursorShape( |
| int hotspot_x = cursor_shape.hotspot_x(); |
| int hotspot_y = cursor_shape.hotspot_y(); |
| - |
| int bytes_per_row = width * kBytesPerPixel; |
| - const uint8* src_row_data = reinterpret_cast<const uint8*>( |
| + int src_stride = width; |
| + const uint32_t* src_row_data = reinterpret_cast<const uint32_t*>( |
| cursor_shape.data().data()); |
| - int stride = bytes_per_row; |
| - |
| - // If the cursor exceeds the size permitted by PPAPI then crop it, keeping |
| - // the hotspot as close to the center of the new cursor shape as possible. |
| - if (height > kMaxCursorHeight) { |
| - int y = hotspot_y - (kMaxCursorHeight / 2); |
| - y = std::max(y, 0); |
| - y = std::min(y, height - kMaxCursorHeight); |
| - |
| - src_row_data += stride * y; |
| - height = kMaxCursorHeight; |
| - hotspot_y -= y; |
| - } |
| - if (width > kMaxCursorWidth) { |
| - int x = hotspot_x - (kMaxCursorWidth / 2); |
| - x = std::max(x, 0); |
| - x = std::min(x, height - kMaxCursorWidth); |
| - |
| - src_row_data += x * kBytesPerPixel; |
| - width = kMaxCursorWidth; |
| - bytes_per_row = width * kBytesPerPixel; |
| - hotspot_x -= x; |
| - } |
| + const uint32_t* src_row_data_end = src_row_data + src_stride * height; |
| + |
| + // See if the cursor image completely consists of transparent pixels. |
|
Wez
2013/09/05 20:24:45
nit: consists completely
alexeypa (please no reviews)
2013/09/06 20:00:17
Done.
|
| + bool mouse_lock_requested = (mouse_lock_state_ != MouseLockDisabled) && |
| + (std::find_if(src_row_data, |
| + src_row_data_end, |
| + &IsVisiblePixel) == src_row_data_end); |
|
Wez
2013/09/05 20:24:45
nit: Move this find_if() into an IsVisibleRow() he
alexeypa (please no reviews)
2013/09/06 20:00:17
Done.
|
| + if (mouse_lock_requested) { |
| + // Request mouse lock. |
| + cursor_image_.reset(); |
| + RequestMouseLock(); |
| + } else { |
| + // If the cursor exceeds the size permitted by PPAPI then crop it, keeping |
| + // the hotspot as close to the center of the new cursor shape as possible. |
|
Wez
2013/09/05 20:24:45
Nit, can we move this crop logic into a CropCursor
alexeypa (please no reviews)
2013/09/06 20:00:17
Not sure. This code updates a bunch of local varia
|
| + if (height > kMaxCursorHeight) { |
| + int y = hotspot_y - (kMaxCursorHeight / 2); |
| + y = std::max(y, 0); |
| + y = std::min(y, height - kMaxCursorHeight); |
| + |
| + src_row_data += src_stride * y; |
| + height = kMaxCursorHeight; |
| + hotspot_y -= y; |
| + } |
| + if (width > kMaxCursorWidth) { |
| + int x = hotspot_x - (kMaxCursorWidth / 2); |
| + x = std::max(x, 0); |
| + x = std::min(x, height - kMaxCursorWidth); |
| + |
| + src_row_data += x; |
| + width = kMaxCursorWidth; |
| + bytes_per_row = width * kBytesPerPixel; |
| + hotspot_x -= x; |
| + } |
| - pp::ImageData cursor_image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, |
| - pp::Size(width, height), false); |
| + cursor_image_.reset(new pp::ImageData(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, |
| + pp::Size(width, height), false)); |
| + cursor_hotspot_ = pp::Point(hotspot_x, hotspot_y); |
| - uint8* dst_row_data = reinterpret_cast<uint8*>(cursor_image.data()); |
| - for (int row = 0; row < height; row++) { |
| - memcpy(dst_row_data, src_row_data, bytes_per_row); |
| - src_row_data += stride; |
| - dst_row_data += cursor_image.stride(); |
| - } |
| + uint8* dst_row_data = reinterpret_cast<uint8*>(cursor_image_->data()); |
| + for (int row = 0; row < height; row++) { |
| + memcpy(dst_row_data, src_row_data, bytes_per_row); |
| + src_row_data += src_stride; |
| + dst_row_data += cursor_image_->stride(); |
| + } |
| - pp::MouseCursor::SetCursor(this, PP_MOUSECURSOR_TYPE_CUSTOM, |
| - cursor_image, |
| - pp::Point(hotspot_x, hotspot_y)); |
| + // Cancel mouse lock if any and apply the new cursor image. |
|
Wez
2013/09/05 20:24:45
nit: if any and -> if active, and
alexeypa (please no reviews)
2013/09/06 20:00:17
Done.
|
| + CancelMouseLock(); |
| + } |
| } |
| void ChromotingInstance::OnFirstFrameReceived() { |
| @@ -860,6 +908,11 @@ void ChromotingInstance::HandleExtensionMessage( |
| host_connection_->host_stub()->DeliverClientMessage(message); |
| } |
| +void ChromotingInstance::HandleAllowMouseLockMessage() { |
| + DCHECK_EQ(mouse_lock_state_, MouseLockDisabled); |
| + mouse_lock_state_ = MouseLockOff; |
| +} |
| + |
| ChromotingStats* ChromotingInstance::GetStats() { |
| if (!client_.get()) |
| return NULL; |
| @@ -1036,4 +1089,86 @@ bool ChromotingInstance::IsConnected() { |
| (host_connection_->state() == protocol::ConnectionToHost::CONNECTED); |
| } |
| +void ChromotingInstance::EnableMousePointer() { |
|
Wez
2013/09/05 20:24:45
This name doesn't feel right; you're setting the c
alexeypa (please no reviews)
2013/09/06 20:00:17
Done.
|
| + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); |
| + DCHECK(mouse_lock_state_ == MouseLockDisabled || |
| + mouse_lock_state_ == MouseLockOff); |
| + |
| + if (cursor_image_) { |
| + pp::MouseCursor::SetCursor(this, PP_MOUSECURSOR_TYPE_CUSTOM, |
| + *cursor_image_, |
| + cursor_hotspot_); |
| + } else { |
| + // THe browser cancelled mouse lock but we don't have a cursor to show. Use |
| + // the standard arrow pointer instead. |
|
Wez
2013/09/05 20:24:45
Will we also be in this state if the host never se
alexeypa (please no reviews)
2013/09/06 20:00:17
Done.
|
| + pp::MouseCursor::SetCursor(this, PP_MOUSECURSOR_TYPE_POINTER); |
| + } |
| +} |
| + |
| +void ChromotingInstance::RequestMouseLock() { |
| + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); |
| + |
| + // Request mouse lock if the plugin is focused, mouse lock requested by |
|
Wez
2013/09/05 20:24:45
nit: lock if -> lock only if
alexeypa (please no reviews)
2013/09/06 20:00:17
Done.
|
| + // the host and no callback is pending. |
|
Wez
2013/09/05 20:24:45
nit: mouse lock requested by the host -> the host-
alexeypa (please no reviews)
2013/09/06 20:00:17
Done.
|
| + if (focused_ && !cursor_image_ && mouse_lock_state_ == MouseLockOff) { |
|
Wez
2013/09/05 20:24:45
nit: I think this function would be more readable
alexeypa (please no reviews)
2013/09/06 20:00:17
Six lines instead of one? I don't think so.
|
| + pp::CompletionCallback callback = |
| + callback_factory_.NewCallback(&ChromotingInstance::OnMouseLocked); |
| + int result = pp::MouseLock::LockMouse(callback); |
| + DCHECK_EQ(result, PP_OK_COMPLETIONPENDING); |
| + |
| + // Hide the cursor to workaround http://crbug.com/285809. |
|
Wez
2013/09/05 20:24:45
nit: Suggest: "Hide cursor to avoid it becoming a
alexeypa (please no reviews)
2013/09/06 20:00:17
Done.
|
| + pp::MouseCursor::SetCursor(this, PP_MOUSECURSOR_TYPE_NONE); |
| + mouse_lock_state_ = MouseLockPending; |
|
Wez
2013/09/05 20:24:45
nit: Since the comment "Hide the cursor" doesn't a
alexeypa (please no reviews)
2013/09/06 20:00:17
Done.
|
| + } |
| +} |
| + |
| +void ChromotingInstance::CancelMouseLock() { |
| + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); |
| + |
| + switch (mouse_lock_state_) { |
| + case MouseLockDisabled: |
| + case MouseLockOff: |
| + EnableMousePointer(); |
| + break; |
| + |
| + case MouseLockCancelling: |
| + break; |
| + |
| + case MouseLockPending: |
| + // Let the callback know that the operation should be cancelled. |
|
Wez
2013/09/05 20:24:45
nit: Clarify here that we can't call UnlockMouse()
alexeypa (please no reviews)
2013/09/06 20:00:17
Done.
|
| + mouse_lock_state_ = MouseLockCancelling; |
| + break; |
| + |
| + case MouseLockOn: |
| + pp::MouseLock::UnlockMouse(); |
| + |
| + // Wait until MouseLockLost() is called. |
|
Wez
2013/09/05 20:24:45
nit: Suggest "Note that mouse-lock has been cancel
alexeypa (please no reviews)
2013/09/06 20:00:17
Done.
|
| + mouse_lock_state_ = MouseLockCancelling; |
| + break; |
| + |
| + default: |
| + NOTREACHED(); |
| + } |
| +} |
| + |
| +void ChromotingInstance::OnMouseLocked(int error) { |
| + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); |
| + DCHECK(mouse_lock_state_ == MouseLockPending || |
| + mouse_lock_state_ == MouseLockCancelling); |
| + |
| + bool cancel = (mouse_lock_state_ == MouseLockCancelling); |
|
Wez
2013/09/05 20:24:45
nit: cancel -> should_cancel?
alexeypa (please no reviews)
2013/09/06 20:00:17
Done.
|
| + |
| + // See if the operation succeeded. |
| + if (error == PP_OK) { |
| + mouse_lock_state_ = MouseLockOn; |
| + } else { |
| + mouse_lock_state_ = MouseLockOff; |
| + EnableMousePointer(); |
| + } |
| + |
| + // Cancel as needed. |
| + if (cancel) |
| + CancelMouseLock(); |
|
Wez
2013/09/05 20:24:45
So we can't dispatch the mouse-lock cancel until a
alexeypa (please no reviews)
2013/09/06 20:00:17
I don't know. I implemented it this way because th
|
| +} |
| + |
| } // namespace remoting |