diff options
author | yzshen@chromium.org <yzshen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-22 03:36:37 +0000 |
---|---|---|
committer | yzshen@chromium.org <yzshen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-22 03:36:37 +0000 |
commit | 67bfb83fbdd401bba2cfdbe330edebf2b3105a6f (patch) | |
tree | a7ed2dc89b3161efdb13cf5b163aa1a61d2e07b6 | |
parent | 88de6c013ddad04c6896f10714c787ce21cf858a (diff) | |
download | chromium_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
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(¢er); + 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) { |