Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(52)

Unified Diff: remoting/client/plugin/chromoting_instance.cc

Issue 23484015: Added support of relative mouse motion in Chromoting. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698