summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryzshen@chromium.org <yzshen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-22 03:36:37 +0000
committeryzshen@chromium.org <yzshen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-22 03:36:37 +0000
commit67bfb83fbdd401bba2cfdbe330edebf2b3105a6f (patch)
treea7ed2dc89b3161efdb13cf5b163aa1a61d2e07b6
parent88de6c013ddad04c6896f10714c787ce21cf858a (diff)
downloadchromium_src-67bfb83fbdd401bba2cfdbe330edebf2b3105a6f.zip
chromium_src-67bfb83fbdd401bba2cfdbe330edebf2b3105a6f.tar.gz
chromium_src-67bfb83fbdd401bba2cfdbe330edebf2b3105a6f.tar.bz2
Mouse lock implementation, including the renderer side and the Windows version of the browser side.
BUG=41781 TEST=Manual test in ppapi/examples/mouse_lock Review URL: http://codereview.chromium.org/7863003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@102234 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_mac.h3
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_mac.mm9
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_views.cc10
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_views.h2
-rw-r--r--content/browser/renderer_host/render_widget_host.cc42
-rw-r--r--content/browser/renderer_host/render_widget_host.h8
-rw-r--r--content/browser/renderer_host/render_widget_host_view.cc9
-rw-r--r--content/browser/renderer_host/render_widget_host_view.h15
-rw-r--r--content/browser/renderer_host/render_widget_host_view_gtk.cc9
-rw-r--r--content/browser/renderer_host/render_widget_host_view_gtk.h2
-rw-r--r--content/browser/renderer_host/render_widget_host_view_win.cc127
-rw-r--r--content/browser/renderer_host/render_widget_host_view_win.h20
-rw-r--r--content/browser/renderer_host/test_render_view_host.cc7
-rw-r--r--content/browser/renderer_host/test_render_view_host.h3
-rw-r--r--content/common/view_messages.h22
-rw-r--r--content/renderer/pepper_plugin_delegate_impl.cc131
-rw-r--r--content/renderer/pepper_plugin_delegate_impl.h31
-rw-r--r--content/renderer/render_view.cc15
-rw-r--r--content/renderer/render_view.h4
-rw-r--r--content/renderer/render_widget.cc24
-rw-r--r--content/renderer/render_widget.h7
-rwxr-xr-xppapi/examples/mouse_lock/mouse_lock.cc272
-rwxr-xr-xppapi/examples/mouse_lock/mouse_lock.html25
-rwxr-xr-x[-rw-r--r--]ppapi/ppapi_tests.gypi10
-rw-r--r--webkit/plugins/ppapi/event_conversion.cc13
-rw-r--r--webkit/plugins/ppapi/mock_plugin_delegate.cc8
-rw-r--r--webkit/plugins/ppapi/mock_plugin_delegate.h2
-rw-r--r--webkit/plugins/ppapi/plugin_delegate.h14
-rw-r--r--webkit/plugins/ppapi/ppapi_plugin_instance.cc13
29 files changed, 820 insertions, 37 deletions
diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.h b/chrome/browser/renderer_host/render_widget_host_view_mac.h
index 5b205cd..e6d6198 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_mac.h
+++ b/chrome/browser/renderer_host/render_widget_host_view_mac.h
@@ -313,6 +313,9 @@ class RenderWidgetHostViewMac : public RenderWidgetHostView {
virtual void SetScrollOffsetPinning(
bool is_pinned_to_left, bool is_pinned_to_right) OVERRIDE;
+ virtual bool LockMouse() OVERRIDE;
+ virtual void UnlockMouse() OVERRIDE;
+
void KillSelf();
void SetTextInputActive(bool active);
diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.mm b/chrome/browser/renderer_host/render_widget_host_view_mac.mm
index 46fad6e..4b959d3 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/chrome/browser/renderer_host/render_widget_host_view_mac.mm
@@ -1100,6 +1100,15 @@ void RenderWidgetHostViewMac::SetScrollOffsetPinning(
[cocoa_view_ setPinnedRight:is_pinned_to_right];
}
+bool RenderWidgetHostViewMac::LockMouse() {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+void RenderWidgetHostViewMac::UnlockMouse() {
+ NOTIMPLEMENTED();
+}
+
void RenderWidgetHostViewMac::ShutdownHost() {
shutdown_factory_.RevokeAll();
render_widget_host_->Shutdown();
diff --git a/chrome/browser/renderer_host/render_widget_host_view_views.cc b/chrome/browser/renderer_host/render_widget_host_view_views.cc
index 65346ac..3f63061 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_views.cc
+++ b/chrome/browser/renderer_host/render_widget_host_view_views.cc
@@ -8,6 +8,7 @@
#include <string>
#include "base/command_line.h"
+#include "base/logging.h"
#include "base/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/string_number_conversions.h"
@@ -434,6 +435,15 @@ void RenderWidgetHostViewViews::SetScrollOffsetPinning(
bool is_pinned_to_left, bool is_pinned_to_right) {
}
+bool RenderWidgetHostViewViews::LockMouse() {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+void RenderWidgetHostViewViews::UnlockMouse() {
+ NOTIMPLEMENTED();
+}
+
void RenderWidgetHostViewViews::SelectRect(const gfx::Point& start,
const gfx::Point& end) {
if (host_)
diff --git a/chrome/browser/renderer_host/render_widget_host_view_views.h b/chrome/browser/renderer_host/render_widget_host_view_views.h
index ad2d439..57805a7 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_views.h
+++ b/chrome/browser/renderer_host/render_widget_host_view_views.h
@@ -119,6 +119,8 @@ class RenderWidgetHostViewViews : public RenderWidgetHostView,
virtual void ShowCompositorHostWindow(bool show) OVERRIDE;
#endif
virtual gfx::PluginWindowHandle GetCompositingSurface() OVERRIDE;
+ virtual bool LockMouse() OVERRIDE;
+ virtual void UnlockMouse() OVERRIDE;
// Overridden from views::TouchSelectionClientView.
virtual void SelectRect(const gfx::Point& start,
diff --git a/content/browser/renderer_host/render_widget_host.cc b/content/browser/renderer_host/render_widget_host.cc
index 77248cb..01077f7 100644
--- a/content/browser/renderer_host/render_widget_host.cc
+++ b/content/browser/renderer_host/render_widget_host.cc
@@ -199,6 +199,8 @@ bool RenderWidgetHost::OnMessageReceived(const IPC::Message &msg) {
OnMsgImeCancelComposition)
IPC_MESSAGE_HANDLER(ViewHostMsg_DidActivateAcceleratedCompositing,
OnMsgDidActivateAcceleratedCompositing)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_LockMouse, OnMsgLockMouse)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_UnlockMouse, OnMsgUnlockMouse)
#if defined(OS_POSIX)
IPC_MESSAGE_HANDLER(ViewHostMsg_GetScreenInfo, OnMsgGetScreenInfo)
IPC_MESSAGE_HANDLER(ViewHostMsg_GetWindowRect, OnMsgGetWindowRect)
@@ -381,6 +383,9 @@ void RenderWidgetHost::Focus() {
}
void RenderWidgetHost::Blur() {
+ if (IsMouseLocked())
+ view_->UnlockMouse();
+
Send(new ViewMsg_SetFocus(routing_id_, false));
}
@@ -388,7 +393,14 @@ void RenderWidgetHost::LostCapture() {
Send(new ViewMsg_MouseCaptureLost(routing_id_));
}
+void RenderWidgetHost::LostMouseLock() {
+ Send(new ViewMsg_MouseLockLost(routing_id_));
+}
+
void RenderWidgetHost::ViewDestroyed() {
+ if (IsMouseLocked())
+ view_->UnlockMouse();
+
// TODO(evanm): tracking this may no longer be necessary;
// eliminate this function if so.
SetView(NULL);
@@ -527,7 +539,16 @@ void RenderWidgetHost::ForwardMouseEvent(const WebMouseEvent& mouse_event) {
// more WM_MOUSEMOVE events than we wish to send to the renderer.
if (mouse_event.type == WebInputEvent::MouseMove) {
if (mouse_move_pending_) {
- next_mouse_move_.reset(new WebMouseEvent(mouse_event));
+ if (!next_mouse_move_.get()) {
+ next_mouse_move_.reset(new WebMouseEvent(mouse_event));
+ } else {
+ // Accumulate movement deltas.
+ int x = next_mouse_move_->movementX;
+ int y = next_mouse_move_->movementY;
+ *next_mouse_move_ = mouse_event;
+ next_mouse_move_->movementX += x;
+ next_mouse_move_->movementY += y;
+ }
return;
}
mouse_move_pending_ = true;
@@ -780,6 +801,10 @@ void RenderWidgetHost::ImeCancelComposition() {
std::vector<WebKit::WebCompositionUnderline>(), 0, 0));
}
+bool RenderWidgetHost::IsMouseLocked() const {
+ return view_ ? view_->mouse_locked() : false;
+}
+
void RenderWidgetHost::Destroy() {
NotificationService::current()->Notify(
content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
@@ -1125,6 +1150,21 @@ void RenderWidgetHost::OnMsgDidActivateAcceleratedCompositing(bool activated) {
#endif
}
+void RenderWidgetHost::OnMsgLockMouse() {
+ // TODO(yzshen): Only allow to lock the mouse when in fullscreen mode, and
+ // make sure that the mouse is unlocked when leaving fullscreen mode.
+ if (!view_ || !view_->HasFocus() || !view_->LockMouse()) {
+ Send(new ViewMsg_LockMouse_ACK(routing_id_, false));
+ } else {
+ Send(new ViewMsg_LockMouse_ACK(routing_id_, true));
+ }
+}
+
+void RenderWidgetHost::OnMsgUnlockMouse() {
+ if (IsMouseLocked())
+ view_->UnlockMouse();
+}
+
#if defined(OS_POSIX)
void RenderWidgetHost::OnMsgGetScreenInfo(gfx::NativeViewId window_id,
WebKit::WebScreenInfo* results) {
diff --git a/content/browser/renderer_host/render_widget_host.h b/content/browser/renderer_host/render_widget_host.h
index 10463c2..9b6a14c 100644
--- a/content/browser/renderer_host/render_widget_host.h
+++ b/content/browser/renderer_host/render_widget_host.h
@@ -196,6 +196,9 @@ class CONTENT_EXPORT RenderWidgetHost : public IPC::Channel::Listener,
void Blur();
virtual void LostCapture();
+ // Called to notify the RenderWidget that it has lost the mouse lock.
+ virtual void LostMouseLock();
+
// Tells us whether the page is rendered directly via the GPU process.
bool is_accelerated_compositing_active() {
return is_accelerated_compositing_active_;
@@ -465,6 +468,8 @@ class CONTENT_EXPORT RenderWidgetHost : public IPC::Channel::Listener,
virtual void NotifyRendererUnresponsive() {}
virtual void NotifyRendererResponsive() {}
+ bool IsMouseLocked() const;
+
protected:
// true if a renderer has once been valid. We use this flag to display a sad
// tab only when we lose our renderer and not if a paint occurs during
@@ -511,6 +516,9 @@ class CONTENT_EXPORT RenderWidgetHost : public IPC::Channel::Listener,
void OnMsgDidActivateAcceleratedCompositing(bool activated);
+ void OnMsgLockMouse();
+ void OnMsgUnlockMouse();
+
#if defined(OS_POSIX)
void OnMsgGetScreenInfo(gfx::NativeViewId view,
WebKit::WebScreenInfo* results);
diff --git a/content/browser/renderer_host/render_widget_host_view.cc b/content/browser/renderer_host/render_widget_host_view.cc
index ba714df..94f287c 100644
--- a/content/browser/renderer_host/render_widget_host_view.cc
+++ b/content/browser/renderer_host/render_widget_host_view.cc
@@ -4,6 +4,7 @@
#include "content/browser/renderer_host/render_widget_host_view.h"
+#include "base/logging.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebScreenInfo.h"
#if defined(OS_MACOSX)
@@ -39,7 +40,13 @@ void RenderWidgetHostView::GetDefaultScreenInfo(
}
#endif
-RenderWidgetHostView::~RenderWidgetHostView() {}
+RenderWidgetHostView::RenderWidgetHostView()
+ : popup_type_(WebKit::WebPopupTypeNone), mouse_locked_(false) {
+}
+
+RenderWidgetHostView::~RenderWidgetHostView() {
+ DCHECK(!mouse_locked_);
+}
void RenderWidgetHostView::SetBackground(const SkBitmap& background) {
background_ = background;
diff --git a/content/browser/renderer_host/render_widget_host_view.h b/content/browser/renderer_host/render_widget_host_view.h
index 3456a67..8859d2a 100644
--- a/content/browser/renderer_host/render_widget_host_view.h
+++ b/content/browser/renderer_host/render_widget_host_view.h
@@ -313,6 +313,10 @@ class RenderWidgetHostView {
virtual void SetScrollOffsetPinning(
bool is_pinned_to_left, bool is_pinned_to_right) = 0;
+ // Return value indicates whether the mouse is locked successfully or not.
+ virtual bool LockMouse() = 0;
+ virtual void UnlockMouse() = 0;
+
void set_popup_type(WebKit::WebPopupType popup_type) {
popup_type_ = popup_type;
}
@@ -334,9 +338,11 @@ class RenderWidgetHostView {
reserved_rect_ = reserved_rect;
}
+ bool mouse_locked() const { return mouse_locked_; }
+
protected:
// Interface class only, do not construct.
- RenderWidgetHostView() : popup_type_(WebKit::WebPopupTypeNone) {}
+ RenderWidgetHostView();
// Whether this view is a popup and what kind of popup it is (select,
// autofill...).
@@ -350,6 +356,13 @@ class RenderWidgetHostView {
// rendered to draw the resize corner, sidebar mini tabs etc.
gfx::Rect reserved_rect_;
+ // While the mouse is locked, the cursor is hidden from the user. Mouse events
+ // are still generated. However, the position they report is the last known
+ // mouse position just as mouse lock was entered; the movement they report
+ // indicates what the change in position of the mouse would be had it not been
+ // locked.
+ bool mouse_locked_;
+
private:
DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostView);
};
diff --git a/content/browser/renderer_host/render_widget_host_view_gtk.cc b/content/browser/renderer_host/render_widget_host_view_gtk.cc
index d258aac..c92290c 100644
--- a/content/browser/renderer_host/render_widget_host_view_gtk.cc
+++ b/content/browser/renderer_host/render_widget_host_view_gtk.cc
@@ -1183,6 +1183,15 @@ gfx::PluginWindowHandle RenderWidgetHostViewGtk::GetCompositingSurface() {
return compositing_surface_;
}
+bool RenderWidgetHostViewGtk::LockMouse() {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+void RenderWidgetHostViewGtk::UnlockMouse() {
+ NOTIMPLEMENTED();
+}
+
void RenderWidgetHostViewGtk::ForwardKeyboardEvent(
const NativeWebKeyboardEvent& event) {
if (!host_)
diff --git a/content/browser/renderer_host/render_widget_host_view_gtk.h b/content/browser/renderer_host/render_widget_host_view_gtk.h
index 5eb9ed4..93925f4 100644
--- a/content/browser/renderer_host/render_widget_host_view_gtk.h
+++ b/content/browser/renderer_host/render_widget_host_view_gtk.h
@@ -106,6 +106,8 @@ class RenderWidgetHostViewGtk : public RenderWidgetHostView,
virtual void GetScreenInfo(WebKit::WebScreenInfo* results) OVERRIDE;
virtual gfx::Rect GetRootWindowBounds() OVERRIDE;
virtual gfx::PluginWindowHandle GetCompositingSurface() OVERRIDE;
+ virtual bool LockMouse() OVERRIDE;
+ virtual void UnlockMouse() OVERRIDE;
// ui::AnimationDelegate implementation.
virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE;
diff --git a/content/browser/renderer_host/render_widget_host_view_win.cc b/content/browser/renderer_host/render_widget_host_view_win.cc
index 1e94b60..04f8ddb 100644
--- a/content/browser/renderer_host/render_widget_host_view_win.cc
+++ b/content/browser/renderer_host/render_widget_host_view_win.cc
@@ -243,7 +243,8 @@ RenderWidgetHostViewWin::RenderWidgetHostViewWin(RenderWidgetHost* widget)
is_loading_(false),
overlay_color_(0),
text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
- is_fullscreen_(false) {
+ is_fullscreen_(false),
+ ignore_mouse_movement_(true) {
render_widget_host_->SetView(this);
registrar_.Add(this,
content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
@@ -251,6 +252,7 @@ RenderWidgetHostViewWin::RenderWidgetHostViewWin(RenderWidgetHost* widget)
}
RenderWidgetHostViewWin::~RenderWidgetHostViewWin() {
+ UnlockMouse();
ResetTooltip();
}
@@ -1100,12 +1102,14 @@ LRESULT RenderWidgetHostViewWin::OnNotify(int w_param, NMHDR* header) {
tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, kTooltipMaxWidthPixels);
SetMsgHandled(TRUE);
break;
- }
+ }
case TTN_POP:
tooltip_showing_ = false;
SetMsgHandled(TRUE);
break;
case TTN_SHOW:
+ // Tooltip shouldn't be shown when the mouse is locked.
+ DCHECK(!mouse_locked_);
tooltip_showing_ = true;
SetMsgHandled(TRUE);
break;
@@ -1226,6 +1230,15 @@ LRESULT RenderWidgetHostViewWin::OnMouseEvent(UINT message, WPARAM wparam,
LPARAM lparam, BOOL& handled) {
handled = TRUE;
+ if (message == WM_MOUSELEAVE)
+ ignore_mouse_movement_ = true;
+
+ if (mouse_locked_) {
+ HandleLockedMouseEvent(message, wparam, lparam);
+ MoveCursorToCenter();
+ return 0;
+ }
+
if (::IsWindow(tooltip_hwnd_)) {
// Forward mouse events through to the tooltip window
MSG msg;
@@ -1289,10 +1302,12 @@ LRESULT RenderWidgetHostViewWin::OnKeyEvent(UINT message, WPARAM wparam,
handled = TRUE;
// Force fullscreen windows to close on Escape.
- if (is_fullscreen_ && (message == WM_KEYDOWN || message == WM_KEYUP) &&
- wparam == VK_ESCAPE) {
- SendMessage(WM_CANCELMODE);
- return 0;
+ if ((message == WM_KEYDOWN || message == WM_KEYUP) && wparam == VK_ESCAPE) {
+ if (mouse_locked_)
+ UnlockMouse();
+ if (is_fullscreen_)
+ SendMessage(WM_CANCELMODE);
+ return 0;
}
// If we are a pop-up, forward tab related messages to our parent HWND, so
@@ -1477,6 +1492,52 @@ void RenderWidgetHostViewWin::OnAccessibilityNotifications(
browser_accessibility_manager_->OnAccessibilityNotifications(params);
}
+bool RenderWidgetHostViewWin::LockMouse() {
+ if (mouse_locked_)
+ return true;
+
+ mouse_locked_ = true;
+
+ // Hide the tooltip window if it is currently visible. When the mouse is
+ // locked, no mouse message is relayed to the tooltip window, so we don't need
+ // to worry that it will reappear.
+ if (::IsWindow(tooltip_hwnd_) && tooltip_showing_) {
+ ::SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
+ // Sending a TTM_POP message doesn't seem to actually hide the tooltip
+ // window, although we will receive a TTN_POP notification. As a result,
+ // ShowWindow() is explicitly called to hide the window.
+ ::ShowWindow(tooltip_hwnd_, SW_HIDE);
+ }
+
+ // TODO(yzshen): Show an invisible cursor instead of using
+ // ::ShowCursor(FALSE), so that MoveCursorToCenter() works with Remote
+ // Desktop.
+ ::ShowCursor(FALSE);
+
+ MoveCursorToCenter();
+
+ CRect rect;
+ GetWindowRect(&rect);
+ ::ClipCursor(&rect);
+
+ return true;
+}
+
+void RenderWidgetHostViewWin::UnlockMouse() {
+ if (!mouse_locked_)
+ return;
+
+ mouse_locked_ = false;
+
+ ::ClipCursor(NULL);
+ ::SetCursorPos(last_global_mouse_position_.x(),
+ last_global_mouse_position_.y());
+ ::ShowCursor(TRUE);
+
+ if (render_widget_host_)
+ render_widget_host_->LostMouseLock();
+}
+
void RenderWidgetHostViewWin::Observe(int type,
const NotificationSource& source,
const NotificationDetails& details) {
@@ -1795,6 +1856,31 @@ void RenderWidgetHostViewWin::ForwardMouseEventToRenderer(UINT message,
WebMouseEvent event(
WebInputEventFactory::mouseEvent(m_hWnd, message, wparam, lparam));
+ if (mouse_locked_) {
+ CPoint center = GetClientCenter();
+
+ event.movementX = event.windowX - center.x;
+ event.movementY = event.windowY - center.y;
+ event.x = last_mouse_position_.x();
+ event.y = last_mouse_position_.y();
+ event.windowX = last_mouse_position_.x();
+ event.windowY = last_mouse_position_.y();
+ event.globalX = last_global_mouse_position_.x();
+ event.globalY = last_global_mouse_position_.y();
+ } else {
+ if (ignore_mouse_movement_) {
+ ignore_mouse_movement_ = false;
+ event.movementX = 0;
+ event.movementY = 0;
+ } else {
+ event.movementX = event.globalX - last_global_mouse_position_.x();
+ event.movementY = event.globalY - last_global_mouse_position_.y();
+ }
+
+ last_mouse_position_.SetPoint(event.windowX, event.windowY);
+ last_global_mouse_position_.SetPoint(event.globalX, event.globalY);
+ }
+
// Send the event to the renderer before changing mouse capture, so that the
// capturelost event arrives after mouseup.
render_widget_host_->ForwardMouseEvent(event);
@@ -1843,3 +1929,32 @@ void RenderWidgetHostViewWin::DoPopupOrFullscreenInit(HWND parent_hwnd,
EnsureTooltip();
ShowWindow(IsActivatable() ? SW_SHOW : SW_SHOWNA);
}
+
+CPoint RenderWidgetHostViewWin::GetClientCenter() const {
+ CRect rect;
+ GetClientRect(&rect);
+ return rect.CenterPoint();
+}
+
+void RenderWidgetHostViewWin::MoveCursorToCenter() const {
+ CPoint center = GetClientCenter();
+ ClientToScreen(&center);
+ if (!::SetCursorPos(center.x, center.y))
+ LOG_GETLASTERROR(WARNING) << "Failed to set cursor position.";
+}
+
+void RenderWidgetHostViewWin::HandleLockedMouseEvent(UINT message,
+ WPARAM wparam,
+ LPARAM lparam) {
+ DCHECK(mouse_locked_);
+
+ if (message == WM_MOUSEMOVE) {
+ CPoint center = GetClientCenter();
+ // Ignore WM_MOUSEMOVE messages generated by MoveCursorToCenter().
+ if (LOWORD(lparam) == center.x && HIWORD(lparam) == center.y)
+ return;
+ }
+
+ ForwardMouseEventToRenderer(message, wparam, lparam);
+}
+
diff --git a/content/browser/renderer_host/render_widget_host_view_win.h b/content/browser/renderer_host/render_widget_host_view_win.h
index 74e58a6..6a9511f 100644
--- a/content/browser/renderer_host/render_widget_host_view_win.h
+++ b/content/browser/renderer_host/render_widget_host_view_win.h
@@ -26,6 +26,7 @@
#include "content/common/notification_registrar.h"
#include "ui/base/win/ime_input.h"
#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/point.h"
#include "webkit/glue/webcursor.h"
class BackingStore;
@@ -181,6 +182,8 @@ class RenderWidgetHostViewWin
virtual void OnAccessibilityNotifications(
const std::vector<ViewHostMsg_AccessibilityNotification_Params>& params
) OVERRIDE;
+ virtual bool LockMouse() OVERRIDE;
+ virtual void UnlockMouse() OVERRIDE;
// Implementation of NotificationObserver:
virtual void Observe(int type,
@@ -291,6 +294,11 @@ class RenderWidgetHostViewWin
const gfx::Rect& pos,
DWORD ex_style);
+ CPoint GetClientCenter() const;
+ void MoveCursorToCenter() const;
+
+ void HandleLockedMouseEvent(UINT message, WPARAM wparam, LPARAM lparam);
+
// The associated Model. While |this| is being Destroyed,
// |render_widget_host_| is NULL and the Windows message loop is run one last
// time. Message handlers must check for a NULL |render_widget_host_|.
@@ -386,6 +394,18 @@ class RenderWidgetHostViewWin
// Is the widget fullscreen?
bool is_fullscreen_;
+ // Used to record the last position of the mouse.
+ // While the mouse is locked, they store the last known position just as mouse
+ // lock was entered.
+ // Relative to the upper-left corner of the view.
+ gfx::Point last_mouse_position_;
+ // Relative to the upper-left corner of the screen.
+ gfx::Point last_global_mouse_position_;
+
+ // In the case of the mouse being moved away from the view and then moved
+ // back, we regard the mouse movement as (0, 0).
+ bool ignore_mouse_movement_;
+
DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewWin);
};
diff --git a/content/browser/renderer_host/test_render_view_host.cc b/content/browser/renderer_host/test_render_view_host.cc
index 9303f86..e15018e 100644
--- a/content/browser/renderer_host/test_render_view_host.cc
+++ b/content/browser/renderer_host/test_render_view_host.cc
@@ -252,6 +252,13 @@ gfx::PluginWindowHandle TestRenderWidgetHostView::GetCompositingSurface() {
return gfx::kNullPluginWindow;
}
+bool TestRenderWidgetHostView::LockMouse() {
+ return false;
+}
+
+void TestRenderWidgetHostView::UnlockMouse() {
+}
+
TestRenderViewHostFactory::TestRenderViewHostFactory(
RenderProcessHostFactory* rph_factory)
: render_process_host_factory_(rph_factory) {
diff --git a/content/browser/renderer_host/test_render_view_host.h b/content/browser/renderer_host/test_render_view_host.h
index 3afebf7..2952242 100644
--- a/content/browser/renderer_host/test_render_view_host.h
+++ b/content/browser/renderer_host/test_render_view_host.h
@@ -155,6 +155,9 @@ class TestRenderWidgetHostView : public RenderWidgetHostView {
virtual gfx::PluginWindowHandle GetCompositingSurface() OVERRIDE;
+ virtual bool LockMouse() OVERRIDE;
+ virtual void UnlockMouse() OVERRIDE;
+
bool is_showing() const { return is_showing_; }
private:
diff --git a/content/common/view_messages.h b/content/common/view_messages.h
index 0d4c597..f80be55 100644
--- a/content/common/view_messages.h
+++ b/content/common/view_messages.h
@@ -1335,6 +1335,14 @@ IPC_MESSAGE_ROUTED3(ViewMsg_GetSerializedHtmlDataForCurrentPageWithLocalLinks,
IPC_MESSAGE_ROUTED1(ViewMsg_UpdateRemoteAccessClientFirewallTraversal,
std::string /* traversal_data */)
+// Tells the render side that a ViewHostMsg_LockMouse message has been
+// processed. |succeeded| indicates whether the mouse has been successfully
+// locked or not.
+IPC_MESSAGE_ROUTED1(ViewMsg_LockMouse_ACK,
+ bool /* succeeded */)
+// Tells the render side that the mouse has been unlocked.
+IPC_MESSAGE_ROUTED0(ViewMsg_MouseLockLost)
+
// These three messages are sent to the parent RenderViewHost to display the
// page/widget that was created by
// CreateWindow/CreateWidget/CreateFullscreenWidget. routing_id
@@ -1900,8 +1908,8 @@ IPC_MESSAGE_ROUTED3(ViewHostMsg_UpdateZoomLimits,
// Asks the browser to create a block of shared memory for the renderer to
// fill in and pass back to the browser.
IPC_SYNC_MESSAGE_CONTROL1_1(ViewHostMsg_AllocateSharedMemoryBuffer,
- uint32 /* buffer size */,
- base::SharedMemoryHandle /* browser handle */)
+ uint32 /* buffer size */,
+ base::SharedMemoryHandle /* browser handle */)
// Notify the browser that this render process can or can't be suddenly
// terminated.
@@ -2086,3 +2094,13 @@ IPC_MESSAGE_ROUTED0(ViewHostMsg_RequestRemoteAccessClientFirewallTraversal)
// Notifies the browser of an event occurring in the media pipeline.
IPC_MESSAGE_CONTROL1(ViewHostMsg_MediaLogEvent,
media::MediaLogEvent /* event */)
+
+// Requests to lock the mouse. Will result in a ViewMsg_LockMouse_ACK message
+// being sent back.
+IPC_MESSAGE_ROUTED0(ViewHostMsg_LockMouse)
+
+// Requests to unlock the mouse. A ViewMsg_MouseLockLost message will be sent
+// whenever the mouse is unlocked (which may or may not be caused by
+// ViewHostMsg_UnlockMouse).
+IPC_MESSAGE_ROUTED0(ViewHostMsg_UnlockMouse)
+
diff --git a/content/renderer/pepper_plugin_delegate_impl.cc b/content/renderer/pepper_plugin_delegate_impl.cc
index c21353f..37a71b5 100644
--- a/content/renderer/pepper_plugin_delegate_impl.cc
+++ b/content/renderer/pepper_plugin_delegate_impl.cc
@@ -52,8 +52,10 @@
#include "ppapi/proxy/host_dispatcher.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/shared_impl/ppapi_preferences.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebCursorInfo.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFileChooserCompletion.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFileChooserParams.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebScreenInfo.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
@@ -663,10 +665,15 @@ PepperPluginDelegateImpl::PepperPluginDelegateImpl(RenderView* render_view)
has_saved_context_menu_action_(false),
saved_context_menu_action_(0),
id_generator_(0),
- is_pepper_plugin_focused_(false) {
+ is_pepper_plugin_focused_(false),
+ mouse_lock_owner_(NULL),
+ mouse_locked_(false),
+ pending_lock_request_(false),
+ pending_unlock_request_(false) {
}
PepperPluginDelegateImpl::~PepperPluginDelegateImpl() {
+ DCHECK(!mouse_lock_owner_);
}
scoped_refptr<webkit::ppapi::PluginModule>
@@ -851,6 +858,8 @@ void PepperPluginDelegateImpl::PluginCrashed(
webkit::ppapi::PluginInstance* instance) {
subscribed_to_policy_updates_.erase(instance);
render_view_->PluginCrashed(instance->module()->path());
+
+ UnlockMouse(instance);
}
void PepperPluginDelegateImpl::InstanceCreated(
@@ -865,6 +874,14 @@ void PepperPluginDelegateImpl::InstanceDeleted(
webkit::ppapi::PluginInstance* instance) {
active_instances_.erase(instance);
subscribed_to_policy_updates_.erase(instance);
+
+ if (mouse_lock_owner_ && mouse_lock_owner_ == instance) {
+ // UnlockMouse() will determine whether a ViewHostMsg_UnlockMouse needs to
+ // be sent, and set internal state properly. We only need to forget about
+ // the current |mouse_lock_owner_|.
+ UnlockMouse(mouse_lock_owner_);
+ mouse_lock_owner_ = NULL;
+ }
}
SkBitmap* PepperPluginDelegateImpl::GetSadPluginBitmap() {
@@ -1027,7 +1044,7 @@ void PepperPluginDelegateImpl::OnAsyncFileOpened(
void PepperPluginDelegateImpl::OnSetFocus(bool has_focus) {
for (std::set<webkit::ppapi::PluginInstance*>::iterator i =
- active_instances_.begin();
+ active_instances_.begin();
i != active_instances_.end(); ++i)
(*i)->SetContentAreaFocus(has_focus);
}
@@ -1036,6 +1053,64 @@ bool PepperPluginDelegateImpl::IsPluginFocused() const {
return is_pepper_plugin_focused_;
}
+void PepperPluginDelegateImpl::OnLockMouseACK(bool succeeded) {
+ DCHECK(!mouse_locked_ && pending_lock_request_);
+
+ mouse_locked_ = succeeded;
+ pending_lock_request_ = false;
+ if (pending_unlock_request_ && !succeeded) {
+ // We have sent an unlock request after the lock request. However, since
+ // the lock request has failed, the unlock request will be ignored by the
+ // browser side and there won't be any response to it.
+ pending_unlock_request_ = false;
+ }
+ // If the PluginInstance has been deleted, |mouse_lock_owner_| can be NULL.
+ if (mouse_lock_owner_) {
+ webkit::ppapi::PluginInstance* last_mouse_lock_owner = mouse_lock_owner_;
+ if (!succeeded) {
+ // Reset |mouse_lock_owner_| to NULL before calling OnLockMouseACK(), so
+ // that if OnLockMouseACK() results in calls to any mouse lock method
+ // (e.g., LockMouse()), the method will see consistent internal state.
+ mouse_lock_owner_ = NULL;
+ }
+
+ last_mouse_lock_owner->OnLockMouseACK(succeeded ? PP_OK : PP_ERROR_FAILED);
+ }
+}
+
+void PepperPluginDelegateImpl::OnMouseLockLost() {
+ DCHECK(mouse_locked_ && !pending_lock_request_);
+
+ mouse_locked_ = false;
+ pending_unlock_request_ = false;
+ // If the PluginInstance has been deleted, |mouse_lock_owner_| can be NULL.
+ if (mouse_lock_owner_) {
+ // Reset |mouse_lock_owner_| to NULL before calling OnMouseLockLost(), so
+ // that if OnMouseLockLost() results in calls to any mouse lock method
+ // (e.g., LockMouse()), the method will see consistent internal state.
+ webkit::ppapi::PluginInstance* last_mouse_lock_owner = mouse_lock_owner_;
+ mouse_lock_owner_ = NULL;
+
+ last_mouse_lock_owner->OnMouseLockLost();
+ }
+}
+
+bool PepperPluginDelegateImpl::DispatchLockedMouseEvent(
+ const WebKit::WebMouseEvent& event) {
+ if (mouse_locked_) {
+ if (mouse_lock_owner_) {
+ // |cursor_info| is ignored since it is hidden when the mouse is locked.
+ WebKit::WebCursorInfo cursor_info;
+ mouse_lock_owner_->HandleInputEvent(event, &cursor_info);
+ }
+
+ // If the mouse is locked, only the current owner of the mouse lock can
+ // process mouse events.
+ return true;
+ }
+ return false;
+}
+
bool PepperPluginDelegateImpl::OpenFileSystem(
const GURL& url,
fileapi::FileSystemType type,
@@ -1490,6 +1565,58 @@ ppapi::Preferences PepperPluginDelegateImpl::GetPreferences() {
return ppapi::Preferences(render_view_->webkit_preferences());
}
+void PepperPluginDelegateImpl::LockMouse(
+ webkit::ppapi::PluginInstance* instance) {
+ DCHECK(instance);
+ if (!MouseLockedOrPending()) {
+ DCHECK(!mouse_lock_owner_);
+ pending_lock_request_ = true;
+ mouse_lock_owner_ = instance;
+
+ render_view_->Send(
+ new ViewHostMsg_LockMouse(render_view_->routing_id()));
+ } else if (instance != mouse_lock_owner_) {
+ // Another plugin instance is using mouse lock. Fail immediately.
+ instance->OnLockMouseACK(PP_ERROR_FAILED);
+ } else {
+ if (mouse_locked_) {
+ instance->OnLockMouseACK(PP_OK);
+ } else if (pending_lock_request_) {
+ instance->OnLockMouseACK(PP_ERROR_INPROGRESS);
+ } else {
+ // The only case left here is
+ // !mouse_locked_ && !pending_lock_request_ && pending_unlock_request_,
+ // which is not possible.
+ NOTREACHED();
+ instance->OnLockMouseACK(PP_ERROR_FAILED);
+ }
+ }
+}
+
+void PepperPluginDelegateImpl::UnlockMouse(
+ webkit::ppapi::PluginInstance* instance) {
+ DCHECK(instance);
+
+ // If no one is using mouse lock or the user is not |instance|, ignore
+ // the unlock request.
+ if (MouseLockedOrPending() && mouse_lock_owner_ == instance) {
+ if (mouse_locked_ || pending_lock_request_) {
+ DCHECK(!mouse_locked_ || !pending_lock_request_);
+ if (!pending_unlock_request_) {
+ pending_unlock_request_ = true;
+
+ render_view_->Send(
+ new ViewHostMsg_UnlockMouse(render_view_->routing_id()));
+ }
+ } else {
+ // The only case left here is
+ // !mouse_locked_ && !pending_lock_request_ && pending_unlock_request_,
+ // which is not possible.
+ NOTREACHED();
+ }
+ }
+}
+
int PepperPluginDelegateImpl::GetRoutingId() const {
return render_view_->routing_id();
}
diff --git a/content/renderer/pepper_plugin_delegate_impl.h b/content/renderer/pepper_plugin_delegate_impl.h
index cf8d2c3..f8dcd56 100644
--- a/content/renderer/pepper_plugin_delegate_impl.h
+++ b/content/renderer/pepper_plugin_delegate_impl.h
@@ -45,6 +45,7 @@ class PluginModule;
namespace WebKit {
class WebFileChooserCompletion;
+class WebMouseEvent;
struct WebFileChooserParams;
}
@@ -168,6 +169,15 @@ class PepperPluginDelegateImpl
// Returns whether or not a Pepper plugin is focused.
bool IsPluginFocused() const;
+ // Notification that the request to lock the mouse has completed.
+ void OnLockMouseACK(bool succeeded);
+ // Notification that the plugin instance has lost the mouse lock.
+ void OnMouseLockLost();
+ // Dispatches mouse events directly to the owner of the mouse lock.
+ // True indicates currently the mouse is locked and the event has been
+ // dispatched to the owner.
+ bool DispatchLockedMouseEvent(const WebKit::WebMouseEvent& event);
+
// PluginDelegate implementation.
virtual void PluginFocusChanged(bool focused) OVERRIDE;
virtual void PluginCrashed(webkit::ppapi::PluginInstance* instance);
@@ -305,6 +315,8 @@ class PepperPluginDelegateImpl
virtual base::SharedMemory* CreateAnonymousSharedMemory(uint32_t size)
OVERRIDE;
virtual ::ppapi::Preferences GetPreferences() OVERRIDE;
+ virtual void LockMouse(webkit::ppapi::PluginInstance* instance) OVERRIDE;
+ virtual void UnlockMouse(webkit::ppapi::PluginInstance* instance) OVERRIDE;
CONTENT_EXPORT int GetRoutingId() const;
@@ -317,6 +329,10 @@ class PepperPluginDelegateImpl
scoped_refptr<PpapiBrokerImpl> CreatePpapiBroker(
webkit::ppapi::PluginModule* plugin_module);
+ bool MouseLockedOrPending() const {
+ return mouse_locked_ || pending_lock_request_ || pending_unlock_request_;
+ }
+
// Pointer to the RenderView that owns us.
RenderView* render_view_;
@@ -347,6 +363,21 @@ class PepperPluginDelegateImpl
// been updated.
std::set<webkit::ppapi::PluginInstance*> subscribed_to_policy_updates_;
+ // |mouse_lock_owner_| is not owned by this class. We can know about when it
+ // is destroyed via InstanceDeleted().
+ // |mouse_lock_owner_| being non-NULL doesn't indicate that currently the
+ // mouse has been locked. It is possible that a request to lock the mouse has
+ // been sent, but the response hasn't arrived yet.
+ webkit::ppapi::PluginInstance* mouse_lock_owner_;
+ bool mouse_locked_;
+ // If both |pending_lock_request_| and |pending_unlock_request_| are true,
+ // it means a lock request was sent before an unlock request and we haven't
+ // received responses for them.
+ // The logic in LockMouse() makes sure that a lock request won't be sent when
+ // there is a pending unlock request.
+ bool pending_lock_request_;
+ bool pending_unlock_request_;
+
DISALLOW_COPY_AND_ASSIGN(PepperPluginDelegateImpl);
};
diff --git a/content/renderer/render_view.cc b/content/renderer/render_view.cc
index aa45119..46e03c0 100644
--- a/content/renderer/render_view.cc
+++ b/content/renderer/render_view.cc
@@ -760,6 +760,8 @@ bool RenderView::OnMessageReceived(const IPC::Message& message) {
OnSetHistoryLengthAndPrune)
IPC_MESSAGE_HANDLER(ViewMsg_EnableViewSourceMode, OnEnableViewSourceMode)
IPC_MESSAGE_HANDLER(IntentsMsg_WebIntentReply, OnWebIntentReply);
+ IPC_MESSAGE_HANDLER(ViewMsg_LockMouse_ACK, OnLockMouseACK)
+ IPC_MESSAGE_HANDLER(ViewMsg_MouseLockLost, OnMouseLockLost)
// Have the super handle all other messages.
IPC_MESSAGE_UNHANDLED(handled = RenderWidget::OnMessageReceived(message))
@@ -4185,6 +4187,10 @@ void RenderView::DidHandleKeyEvent() {
edit_commands_.clear();
}
+bool RenderView::WillHandleMouseEvent(const WebKit::WebMouseEvent& event) {
+ return pepper_delegate_.DispatchLockedMouseEvent(event);
+}
+
void RenderView::DidHandleMouseEvent(const WebKit::WebMouseEvent& event) {
FOR_EACH_OBSERVER(RenderViewObserver, observers_, DidHandleMouseEvent(event));
}
@@ -4636,3 +4642,12 @@ void RenderView::OnEnableViewSourceMode() {
return;
main_frame->enableViewSourceMode(true);
}
+
+void RenderView::OnLockMouseACK(bool succeeded) {
+ pepper_delegate_.OnLockMouseACK(succeeded);
+}
+
+void RenderView::OnMouseLockLost() {
+ pepper_delegate_.OnMouseLockLost();
+}
+
diff --git a/content/renderer/render_view.h b/content/renderer/render_view.h
index db207a4..8f95fb3 100644
--- a/content/renderer/render_view.h
+++ b/content/renderer/render_view.h
@@ -648,6 +648,8 @@ class RenderView : public RenderWidget,
gfx::Rect* clip);
virtual gfx::Point GetScrollOffset();
virtual void DidHandleKeyEvent();
+ virtual bool WillHandleMouseEvent(
+ const WebKit::WebMouseEvent& event) OVERRIDE;
virtual void DidHandleMouseEvent(const WebKit::WebMouseEvent& event);
virtual void OnSetFocus(bool enable);
virtual void OnWasHidden();
@@ -829,8 +831,10 @@ class RenderView : public RenderWidget,
const std::vector<GURL>& links,
const std::vector<FilePath>& local_paths,
const FilePath& local_directory_name);
+ void OnLockMouseACK(bool succeeded);
void OnMediaPlayerActionAt(const gfx::Point& location,
const WebKit::WebMediaPlayerAction& action);
+ void OnMouseLockLost();
void OnMoveOrResizeStarted();
void OnNavigate(const ViewMsg_Navigate_Params& params);
void OnPaste();
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 7b2f48a..3f02e07 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -446,10 +446,16 @@ void RenderWidget::OnHandleInputEvent(const IPC::Message& message) {
if (input_event->type == WebInputEvent::RawKeyDown)
message.ReadBool(&iter, &is_keyboard_shortcut);
- bool processed = false;
+ bool prevent_default = false;
+ if (WebInputEvent::isMouseEventType(input_event->type)) {
+ prevent_default = WillHandleMouseEvent(
+ *(static_cast<const WebMouseEvent*>(input_event)));
+ }
+
+ bool processed = prevent_default;
if (input_event->type != WebInputEvent::Char || !suppress_next_char_events_) {
suppress_next_char_events_ = false;
- if (webwidget_)
+ if (!processed && webwidget_)
processed = webwidget_->handleInputEvent(*input_event);
}
@@ -482,10 +488,12 @@ void RenderWidget::OnHandleInputEvent(const IPC::Message& message) {
handling_input_event_ = false;
- if (WebInputEvent::isKeyboardEventType(input_event->type))
- DidHandleKeyEvent();
- if (WebInputEvent::isMouseEventType(input_event->type))
- DidHandleMouseEvent(*(static_cast<const WebMouseEvent*>(input_event)));
+ if (!prevent_default) {
+ if (WebInputEvent::isKeyboardEventType(input_event->type))
+ DidHandleKeyEvent();
+ if (WebInputEvent::isMouseEventType(input_event->type))
+ DidHandleMouseEvent(*(static_cast<const WebMouseEvent*>(input_event)));
+ }
}
void RenderWidget::OnMouseCaptureLost() {
@@ -1393,3 +1401,7 @@ void RenderWidget::CleanupWindowInPluginMoves(gfx::PluginWindowHandle window) {
}
}
}
+
+bool RenderWidget::WillHandleMouseEvent(const WebKit::WebMouseEvent& event) {
+ return false;
+}
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 05a7d5f..0980575 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -42,6 +42,7 @@ class PlatformCanvas;
}
namespace WebKit {
+class WebInputEvent;
class WebMouseEvent;
class WebWidget;
struct WebPopupMenuInfo;
@@ -300,6 +301,12 @@ class RenderWidget : public IPC::Channel::Listener,
// just handled.
virtual void DidHandleKeyEvent() {}
+ // Called by OnHandleInputEvent() to notify subclasses that a mouse event is
+ // about to be handled.
+ // Returns true if no further handling is needed. In that case, the event
+ // won't be sent to WebKit or trigger DidHandleMouseEvent().
+ virtual bool WillHandleMouseEvent(const WebKit::WebMouseEvent& event);
+
// Called by OnHandleInputEvent() to notify subclasses that a mouse event was
// just handled.
virtual void DidHandleMouseEvent(const WebKit::WebMouseEvent& event) {}
diff --git a/ppapi/examples/mouse_lock/mouse_lock.cc b/ppapi/examples/mouse_lock/mouse_lock.cc
new file mode 100755
index 0000000..95806cf5
--- /dev/null
+++ b/ppapi/examples/mouse_lock/mouse_lock.cc
@@ -0,0 +1,272 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cmath>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "ppapi/c/dev/ppb_console_dev.h"
+#include "ppapi/c/ppb_input_event.h"
+#include "ppapi/cpp/completion_callback.h"
+#include "ppapi/cpp/dev/mouse_lock_dev.h"
+#include "ppapi/cpp/graphics_2d.h"
+#include "ppapi/cpp/image_data.h"
+#include "ppapi/cpp/input_event.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/logging.h"
+#include "ppapi/cpp/module.h"
+#include "ppapi/cpp/rect.h"
+#include "ppapi/cpp/var.h"
+
+class MyInstance : public pp::Instance, public pp::MouseLock_Dev {
+ public:
+ explicit MyInstance(PP_Instance instance)
+ : pp::Instance(instance),
+ pp::MouseLock_Dev(this),
+ width_(0),
+ height_(0),
+ mouse_locked_(false),
+ pending_paint_(false),
+ waiting_for_flush_completion_(false),
+ callback_factory_(this),
+ console_(NULL) {
+ }
+ virtual ~MyInstance() {}
+
+ virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
+ console_ = reinterpret_cast<const PPB_Console_Dev*>(
+ pp::Module::Get()->GetBrowserInterface(PPB_CONSOLE_DEV_INTERFACE));
+ if (!console_)
+ return false;
+
+ RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE |
+ PP_INPUTEVENT_CLASS_KEYBOARD);
+ return true;
+ }
+
+ virtual bool HandleInputEvent(const pp::InputEvent& event) {
+ switch (event.GetType()) {
+ case PP_INPUTEVENT_TYPE_MOUSEDOWN: {
+ pp::MouseInputEvent mouse_event(event);
+ if (mouse_event.GetButton() == PP_INPUTEVENT_MOUSEBUTTON_LEFT &&
+ !mouse_locked_) {
+ LockMouse(
+ callback_factory_.NewRequiredCallback(&MyInstance::DidLockMouse));
+ } else if (mouse_event.GetButton() == PP_INPUTEVENT_MOUSEBUTTON_RIGHT &&
+ mouse_locked_) {
+ UnlockMouse();
+ }
+ return true;
+ }
+ case PP_INPUTEVENT_TYPE_MOUSEMOVE: {
+ pp::MouseInputEvent mouse_event(event);
+ mouse_movement_ = mouse_event.GetMovement();
+ static unsigned int i = 0;
+ Log(PP_LOGLEVEL_LOG, "[%d] movementX: %d; movementY: %d\n", i++,
+ mouse_movement_.x(), mouse_movement_.y());
+ Paint();
+ return true;
+ }
+ case PP_INPUTEVENT_TYPE_KEYDOWN: {
+ pp::KeyboardInputEvent key_event(event);
+ // Lock the mouse when the Enter key is pressed.
+ if (!mouse_locked_ && key_event.GetKeyCode() == 13) {
+ LockMouse(
+ callback_factory_.NewRequiredCallback(&MyInstance::DidLockMouse));
+ }
+ return true;
+ }
+ default:
+ return false;
+ }
+ }
+
+ virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
+ if (position.size().width() == width_ &&
+ position.size().height() == height_)
+ return; // We don't care about the position, only the size.
+
+ width_ = position.size().width();
+ height_ = position.size().height();
+
+ device_context_ = pp::Graphics2D(this, pp::Size(width_, height_), false);
+ if (!BindGraphics(device_context_))
+ return;
+
+ Paint();
+ }
+
+ virtual void MouseLockLost() {
+ if (mouse_locked_) {
+ mouse_locked_ = false;
+ Paint();
+ } else {
+ PP_NOTREACHED();
+ }
+ }
+
+ private:
+ void DidLockMouse(int32_t result) {
+ mouse_locked_ = result == PP_OK;
+ mouse_movement_.set_x(0);
+ mouse_movement_.set_y(0);
+ Paint();
+ }
+
+ void DidFlush(int32_t result) {
+ waiting_for_flush_completion_ = false;
+ if (pending_paint_) {
+ pending_paint_ = false;
+ Paint();
+ }
+ }
+
+ void Paint() {
+ if (waiting_for_flush_completion_) {
+ pending_paint_ = true;
+ return;
+ }
+
+ pp::ImageData image = PaintImage(width_, height_);
+ if (!image.is_null()) {
+ device_context_.ReplaceContents(&image);
+ waiting_for_flush_completion_ = true;
+ device_context_.Flush(
+ callback_factory_.NewRequiredCallback(&MyInstance::DidFlush));
+ }
+ }
+
+ pp::ImageData PaintImage(int width, int height) {
+ pp::ImageData image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
+ pp::Size(width, height), false);
+ if (image.is_null())
+ return image;
+
+ const static int kCenteralSpotRadius = 5;
+ const static uint32_t kBackgroundColor = 0xfff0f0f0;
+ const static uint32_t kLockedForegroundColor = 0xfff08080;
+ const static uint32_t kUnlockedForegroundColor = 0xff80f080;
+
+ int center_x = width / 2;
+ int center_y = height / 2;
+ pp::Point vertex(mouse_movement_.x() + center_x,
+ mouse_movement_.y() + center_y);
+ pp::Point anchor_1;
+ pp::Point anchor_2;
+ enum {
+ LEFT = 0,
+ RIGHT = 1,
+ UP = 2,
+ DOWN = 3
+ } direction;
+ bool draw_needle = GetDistance(mouse_movement_.x(), mouse_movement_.y(),
+ 0, 0) > kCenteralSpotRadius;
+ if (draw_needle) {
+ if (abs(mouse_movement_.x()) >= abs(mouse_movement_.y())) {
+ anchor_1.set_x(center_x);
+ anchor_1.set_y(center_y - kCenteralSpotRadius);
+ anchor_2.set_x(center_x);
+ anchor_2.set_y(center_y + kCenteralSpotRadius);
+ direction = (mouse_movement_.x() < 0) ? LEFT : RIGHT;
+ if (direction == LEFT)
+ anchor_1.swap(anchor_2);
+ } else {
+ anchor_1.set_x(center_x + kCenteralSpotRadius);
+ anchor_1.set_y(center_y);
+ anchor_2.set_x(center_x - kCenteralSpotRadius);
+ anchor_2.set_y(center_y);
+ direction = (mouse_movement_.y() < 0) ? UP : DOWN;
+ if (direction == UP)
+ anchor_1.swap(anchor_2);
+ }
+ }
+ uint32_t foreground_color = mouse_locked_ ? kLockedForegroundColor :
+ kUnlockedForegroundColor;
+ for (int y = 0; y < image.size().height(); ++y) {
+ for (int x = 0; x < image.size().width(); ++x) {
+ if (GetDistance(x, y, center_x, center_y) < kCenteralSpotRadius) {
+ *image.GetAddr32(pp::Point(x, y)) = foreground_color;
+ continue;
+ }
+ if (draw_needle) {
+ bool within_bound_1 =
+ ((y - anchor_1.y()) * (vertex.x() - anchor_1.x())) >
+ ((vertex.y() - anchor_1.y()) * (x - anchor_1.x()));
+ bool within_bound_2 =
+ ((y - anchor_2.y()) * (vertex.x() - anchor_2.x())) <
+ ((vertex.y() - anchor_2.y()) * (x - anchor_2.x()));
+ bool within_bound_3 =
+ (direction == UP && y < center_y) ||
+ (direction == DOWN && y > center_y) ||
+ (direction == LEFT && x < center_x) ||
+ (direction == RIGHT && x > center_y);
+
+ if (within_bound_1 && within_bound_2 && within_bound_3) {
+ *image.GetAddr32(pp::Point(x, y)) = foreground_color;
+ continue;
+ }
+ }
+ *image.GetAddr32(pp::Point(x, y)) = kBackgroundColor;
+ }
+ }
+
+ return image;
+ }
+
+ double GetDistance(int point_1_x, int point_1_y,
+ int point_2_x, int point_2_y) {
+ return sqrt(pow(static_cast<double>(point_1_x - point_2_x), 2) +
+ pow(static_cast<double>(point_1_y - point_2_y), 2));
+ }
+
+ void Log(PP_LogLevel_Dev level, const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ char buf[512];
+ vsnprintf(buf, sizeof(buf) - 1, format, args);
+ buf[sizeof(buf) - 1] = '\0';
+ va_end(args);
+
+ pp::Var value(buf);
+ console_->Log(pp_instance(), level, value.pp_var());
+ }
+
+ int width_;
+ int height_;
+
+ bool mouse_locked_;
+ pp::Point mouse_movement_;
+
+ bool pending_paint_;
+ bool waiting_for_flush_completion_;
+
+ pp::CompletionCallbackFactory<MyInstance> callback_factory_;
+
+ const PPB_Console_Dev* console_;
+
+ pp::Graphics2D device_context_;
+};
+
+// This object is the global object representing this plugin library as long
+// as it is loaded.
+class MyModule : public pp::Module {
+ public:
+ MyModule() : pp::Module() {}
+ virtual ~MyModule() {}
+
+ // Override CreateInstance to create your customized Instance object.
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new MyInstance(instance);
+ }
+};
+
+namespace pp {
+
+// Factory function for your specialization of the Module object.
+Module* CreateModule() {
+ return new MyModule();
+}
+
+} // namespace pp
+
diff --git a/ppapi/examples/mouse_lock/mouse_lock.html b/ppapi/examples/mouse_lock/mouse_lock.html
new file mode 100755
index 0000000..3f8841fa
--- /dev/null
+++ b/ppapi/examples/mouse_lock/mouse_lock.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <!--
+ Copyright (c) 2011 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+<head>
+ <title>Mouse Lock Example</title>
+</head>
+
+<body title="This tooltip should not be shown if the mouse is locked.">
+<ul>
+ <li>Lock mouse: left click in either of the two boxes; or right click in
+ either of the boxes to ensure that it is focused and then press Enter key.
+ </li>
+ <li>Unlock mouse: lose focus, press Esc key, or right click.</li>
+</ul>
+<object id="plugin" type="application/x-ppapi-example-mouse-lock"
+ width="300" height="300" border="2px"></object>
+<div></div>
+<object id="plugin" type="application/x-ppapi-example-mouse-lock"
+ width="300" height="300" border="2px"></object>
+</body>
+</html>
diff --git a/ppapi/ppapi_tests.gypi b/ppapi/ppapi_tests.gypi
index 382e76f..9a68fa0 100644..100755
--- a/ppapi/ppapi_tests.gypi
+++ b/ppapi/ppapi_tests.gypi
@@ -279,6 +279,16 @@
],
},
{
+ 'target_name': 'ppapi_example_mouse_lock',
+ 'dependencies': [
+ 'ppapi_example_skeleton',
+ 'ppapi.gyp:ppapi_cpp',
+ ],
+ 'sources': [
+ 'examples/mouse_lock/mouse_lock.cc',
+ ],
+ },
+ {
'target_name': 'ppapi_example_file_chooser',
'dependencies': [
'ppapi_example_skeleton',
diff --git a/webkit/plugins/ppapi/event_conversion.cc b/webkit/plugins/ppapi/event_conversion.cc
index d9ac4c6..80df1f6 100644
--- a/webkit/plugins/ppapi/event_conversion.cc
+++ b/webkit/plugins/ppapi/event_conversion.cc
@@ -135,10 +135,8 @@ void AppendMouseEvent(const WebInputEvent& event,
result.mouse_position.x = mouse_event.x;
result.mouse_position.y = mouse_event.y;
result.mouse_click_count = mouse_event.clickCount;
-
- // TODO(yzshen): Make the change after WebMouseEvent adds movementX/Y.
- result.mouse_movement.x = 0; // mouse_event.movementX
- result.mouse_movement.y = 0; // mouse_event.movementY
+ result.mouse_movement.x = mouse_event.movementX;
+ result.mouse_movement.y = mouse_event.movementY;
result_events->push_back(result);
}
@@ -229,11 +227,8 @@ WebMouseEvent* BuildMouseEvent(const InputEventData& event) {
mouse_event->x = event.mouse_position.x;
mouse_event->y = event.mouse_position.y;
mouse_event->clickCount = event.mouse_click_count;
-
- // TODO(yzshen): Uncomment the following lines after WebMouseEvent adds
- // movementX/Y.
- // mouse_event->movementX = event.mouse_position.x;
- // mouse_event->movementY = event.mouse_position.y;
+ mouse_event->movementX = event.mouse_movement.x;
+ mouse_event->movementY = event.mouse_movement.y;
return mouse_event;
}
diff --git a/webkit/plugins/ppapi/mock_plugin_delegate.cc b/webkit/plugins/ppapi/mock_plugin_delegate.cc
index c34643a..010814a 100644
--- a/webkit/plugins/ppapi/mock_plugin_delegate.cc
+++ b/webkit/plugins/ppapi/mock_plugin_delegate.cc
@@ -7,6 +7,7 @@
#include "base/message_loop_proxy.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/shared_impl/ppapi_preferences.h"
+#include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
namespace webkit {
namespace ppapi {
@@ -284,5 +285,12 @@ base::SharedMemory* MockPluginDelegate::CreateAnonymousSharedMemory(
return ::ppapi::Preferences();
}
+void MockPluginDelegate::LockMouse(PluginInstance* instance) {
+ instance->OnLockMouseACK(PP_ERROR_FAILED);
+}
+
+void MockPluginDelegate::UnlockMouse(PluginInstance* instance) {
+}
+
} // namespace ppapi
} // namespace webkit
diff --git a/webkit/plugins/ppapi/mock_plugin_delegate.h b/webkit/plugins/ppapi/mock_plugin_delegate.h
index 04382e0..82d3cff 100644
--- a/webkit/plugins/ppapi/mock_plugin_delegate.h
+++ b/webkit/plugins/ppapi/mock_plugin_delegate.h
@@ -118,6 +118,8 @@ class MockPluginDelegate : public PluginDelegate {
virtual std::string GetFlashCommandLineArgs();
virtual base::SharedMemory* CreateAnonymousSharedMemory(uint32_t size);
virtual ::ppapi::Preferences GetPreferences();
+ virtual void LockMouse(PluginInstance* instance);
+ virtual void UnlockMouse(PluginInstance* instance);
};
} // namespace ppapi
diff --git a/webkit/plugins/ppapi/plugin_delegate.h b/webkit/plugins/ppapi/plugin_delegate.h
index f63276e..193777e 100644
--- a/webkit/plugins/ppapi/plugin_delegate.h
+++ b/webkit/plugins/ppapi/plugin_delegate.h
@@ -446,6 +446,20 @@ class PluginDelegate {
// Returns the current preferences.
virtual ::ppapi::Preferences GetPreferences() = 0;
+
+ // Locks the mouse for |instance|. It will call
+ // PluginInstance::OnLockMouseACK() to notify the instance when the operation
+ // is completed. The call to OnLockMouseACK() may be synchronous (i.e., it may
+ // be called when LockMouse() is still on the stack).
+ virtual void LockMouse(PluginInstance* instance) = 0;
+
+ // Unlocks the mouse if |instance| currently owns the mouse lock. Whenever an
+ // plugin instance has lost the mouse lock, it will be notified by
+ // PluginInstance::OnMouseLockLost(). Please note that UnlockMouse() is not
+ // the only cause of losing mouse lock. For example, a user may press the Esc
+ // key to quit the mouse lock mode, which also results in an OnMouseLockLost()
+ // call to the current mouse lock owner.
+ virtual void UnlockMouse(PluginInstance* instance) = 0;
};
} // namespace ppapi
diff --git a/webkit/plugins/ppapi/ppapi_plugin_instance.cc b/webkit/plugins/ppapi/ppapi_plugin_instance.cc
index 13934ef..a31f43e 100644
--- a/webkit/plugins/ppapi/ppapi_plugin_instance.cc
+++ b/webkit/plugins/ppapi/ppapi_plugin_instance.cc
@@ -1584,20 +1584,15 @@ int32_t PluginInstance::LockMouse(PP_Instance instance,
if (lock_mouse_callback_.func)
return PP_ERROR_INPROGRESS;
- // TODO(yzshen): Uncomment the following lines after adding implementation in
- // the delegate.
- // lock_mouse_callback_ = callback;
+ lock_mouse_callback_ = callback;
// We will be notified on completion via OnLockMouseACK(), either
// synchronously or asynchronously.
- // delegate()->LockMouse(this);
- // return PP_OK_COMPLETIONPENDING;
- return PP_ERROR_FAILED;
+ delegate()->LockMouse(this);
+ return PP_OK_COMPLETIONPENDING;
}
void PluginInstance::UnlockMouse(PP_Instance instance) {
- // TODO(yzshen): Uncomment the following after adding implementation in the
- // delegate.
- // delegate()->UnlockMouse(this);
+ delegate()->UnlockMouse(this);
}
void PluginInstance::SubscribeToPolicyUpdates(PP_Instance instance) {