1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
13 #include "mouse_lock.h"
21 // Indicate the direction of the mouse location relative to the center of the
22 // view. These values are used to determine which 2D quadrant the needle lies
32 const int kCentralSpotRadius
= 5;
33 const uint32_t kReturnKeyCode
= 13;
34 const uint32_t kBackgroundColor
= 0xff606060;
35 const uint32_t kForegroundColor
= 0xfff08080;
38 MouseLockInstance::~MouseLockInstance() {
39 free(background_scanline_
);
40 background_scanline_
= NULL
;
43 bool MouseLockInstance::Init(uint32_t argc
,
46 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE
| PP_INPUTEVENT_CLASS_KEYBOARD
);
50 bool MouseLockInstance::HandleInputEvent(const pp::InputEvent
& event
) {
51 switch (event
.GetType()) {
52 case PP_INPUTEVENT_TYPE_MOUSEDOWN
: {
57 callback_factory_
.NewCallback(&MouseLockInstance::DidLockMouse
));
62 case PP_INPUTEVENT_TYPE_MOUSEMOVE
: {
63 pp::MouseInputEvent
mouse_event(event
);
64 mouse_movement_
= mouse_event
.GetMovement();
69 case PP_INPUTEVENT_TYPE_KEYDOWN
: {
70 pp::KeyboardInputEvent
key_event(event
);
72 // Switch in and out of fullscreen when 'Enter' is hit
73 if (key_event
.GetKeyCode() == kReturnKeyCode
) {
74 // Ignore switch if in transition
75 if (!is_context_bound_
)
78 if (fullscreen_
.IsFullscreen()) {
79 if (!fullscreen_
.SetFullscreen(false)) {
80 Log("Could not leave fullscreen mode\n");
82 is_context_bound_
= false;
85 if (!fullscreen_
.SetFullscreen(true)) {
86 Log("Could not enter fullscreen mode\n");
88 is_context_bound_
= false;
95 case PP_INPUTEVENT_TYPE_MOUSEUP
:
96 case PP_INPUTEVENT_TYPE_MOUSEENTER
:
97 case PP_INPUTEVENT_TYPE_MOUSELEAVE
:
98 case PP_INPUTEVENT_TYPE_WHEEL
:
99 case PP_INPUTEVENT_TYPE_RAWKEYDOWN
:
100 case PP_INPUTEVENT_TYPE_KEYUP
:
101 case PP_INPUTEVENT_TYPE_CHAR
:
102 case PP_INPUTEVENT_TYPE_CONTEXTMENU
:
103 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START
:
104 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE
:
105 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END
:
106 case PP_INPUTEVENT_TYPE_IME_TEXT
:
107 case PP_INPUTEVENT_TYPE_UNDEFINED
:
108 case PP_INPUTEVENT_TYPE_TOUCHSTART
:
109 case PP_INPUTEVENT_TYPE_TOUCHMOVE
:
110 case PP_INPUTEVENT_TYPE_TOUCHEND
:
111 case PP_INPUTEVENT_TYPE_TOUCHCANCEL
:
117 void MouseLockInstance::DidChangeView(const pp::View
& view
) {
118 // DidChangeView can get called for many reasons, so we only want to
119 // rebuild the device context if we really need to.
121 if ((size_
== view
.GetRect().size()) &&
122 (was_fullscreen_
== view
.IsFullscreen()) && is_context_bound_
) {
123 Log("DidChangeView SKIP %d,%d FULL=%s CTX Bound=%s",
124 view
.GetRect().width(),
125 view
.GetRect().height(),
126 view
.IsFullscreen() ? "true" : "false",
127 is_context_bound_
? "true" : "false");
131 Log("DidChangeView DO %d,%d FULL=%s CTX Bound=%s",
132 view
.GetRect().width(),
133 view
.GetRect().height(),
134 view
.IsFullscreen() ? "true" : "false",
135 is_context_bound_
? "true" : "false");
137 size_
= view
.GetRect().size();
138 device_context_
= pp::Graphics2D(this, size_
, false);
139 waiting_for_flush_completion_
= false;
141 is_context_bound_
= BindGraphics(device_context_
);
142 if (!is_context_bound_
) {
143 Log("Could not bind to 2D context\n.");
146 Log("Bound to 2D context size %d,%d.\n", size_
.width(), size_
.height());
149 // Create a scanline for fill.
150 delete[] background_scanline_
;
151 background_scanline_
= new uint32_t[size_
.width()];
152 uint32_t* bg_pixel
= background_scanline_
;
153 for (int x
= 0; x
< size_
.width(); ++x
) {
154 *bg_pixel
++ = kBackgroundColor
;
157 // Remember if we are fullscreen or not
158 was_fullscreen_
= view
.IsFullscreen();
160 // Paint this context
164 void MouseLockInstance::MouseLockLost() {
166 Log("Mouselock unlocked.\n");
167 mouse_locked_
= false;
172 void MouseLockInstance::DidLockMouse(int32_t result
) {
173 mouse_locked_
= result
== PP_OK
;
174 if (result
!= PP_OK
) {
175 Log("Mouselock failed with failed with error number %d.\n", result
);
177 mouse_movement_
.set_x(0);
178 mouse_movement_
.set_y(0);
182 void MouseLockInstance::DidFlush(int32_t result
) {
184 Log("Flushed failed with error number %d.\n", result
);
185 waiting_for_flush_completion_
= false;
188 void MouseLockInstance::Paint() {
189 // If we are already waiting to paint...
190 if (waiting_for_flush_completion_
) {
194 pp::ImageData image
= PaintImage(size_
);
195 if (image
.is_null()) {
196 Log("Could not create image data\n");
200 device_context_
.ReplaceContents(&image
);
201 waiting_for_flush_completion_
= true;
202 device_context_
.Flush(
203 callback_factory_
.NewCallback(&MouseLockInstance::DidFlush
));
206 pp::ImageData
MouseLockInstance::PaintImage(const pp::Size
& size
) {
207 pp::ImageData
image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL
, size
, false);
208 if (image
.is_null() || image
.data() == NULL
) {
209 Log("Skipping image.\n");
213 ClearToBackground(&image
);
215 DrawCenterSpot(&image
, kForegroundColor
);
216 DrawNeedle(&image
, kForegroundColor
);
220 void MouseLockInstance::ClearToBackground(pp::ImageData
* image
) {
222 Log("ClearToBackground with NULL image.");
225 if (background_scanline_
== NULL
) {
226 Log("ClearToBackground with no scanline.");
229 int image_height
= image
->size().height();
230 int image_width
= image
->size().width();
232 for (int y
= 0; y
< image_height
; ++y
) {
233 uint32_t* scanline
= image
->GetAddr32(pp::Point(0, y
));
235 background_scanline_
,
236 image_width
* sizeof(*background_scanline_
));
240 void MouseLockInstance::DrawCenterSpot(pp::ImageData
* image
,
241 uint32_t spot_color
) {
243 Log("DrawCenterSpot with NULL image");
246 // Draw the center spot. The ROI is bounded by the size of the spot, plus
248 int center_x
= image
->size().width() / 2;
249 int center_y
= image
->size().height() / 2;
250 int region_of_interest_radius
= kCentralSpotRadius
+ 1;
252 pp::Point
left_top(std::max(0, center_x
- region_of_interest_radius
),
253 std::max(0, center_y
- region_of_interest_radius
));
254 pp::Point
right_bottom(
255 std::min(image
->size().width(), center_x
+ region_of_interest_radius
),
256 std::min(image
->size().height(), center_y
+ region_of_interest_radius
));
257 for (int y
= left_top
.y(); y
< right_bottom
.y(); ++y
) {
258 for (int x
= left_top
.x(); x
< right_bottom
.x(); ++x
) {
259 if (GetDistance(x
, y
, center_x
, center_y
) < kCentralSpotRadius
) {
260 *image
->GetAddr32(pp::Point(x
, y
)) = spot_color
;
266 void MouseLockInstance::DrawNeedle(pp::ImageData
* image
,
267 uint32_t needle_color
) {
269 Log("DrawNeedle with NULL image");
272 if (GetDistance(mouse_movement_
.x(), mouse_movement_
.y(), 0, 0) <=
273 kCentralSpotRadius
) {
277 int abs_mouse_x
= std::abs(mouse_movement_
.x());
278 int abs_mouse_y
= std::abs(mouse_movement_
.y());
279 int center_x
= image
->size().width() / 2;
280 int center_y
= image
->size().height() / 2;
281 pp::Point
vertex(mouse_movement_
.x() + center_x
,
282 mouse_movement_
.y() + center_y
);
285 MouseDirection direction
= kLeft
;
287 if (abs_mouse_x
>= abs_mouse_y
) {
288 anchor_1
.set_x(center_x
);
289 anchor_1
.set_y(center_y
- kCentralSpotRadius
);
290 anchor_2
.set_x(center_x
);
291 anchor_2
.set_y(center_y
+ kCentralSpotRadius
);
292 direction
= (mouse_movement_
.x() < 0) ? kLeft
: kRight
;
293 if (direction
== kLeft
)
294 anchor_1
.swap(anchor_2
);
296 anchor_1
.set_x(center_x
+ kCentralSpotRadius
);
297 anchor_1
.set_y(center_y
);
298 anchor_2
.set_x(center_x
- kCentralSpotRadius
);
299 anchor_2
.set_y(center_y
);
300 direction
= (mouse_movement_
.y() < 0) ? kUp
: kDown
;
301 if (direction
== kUp
)
302 anchor_1
.swap(anchor_2
);
305 pp::Point
left_top(std::max(0, center_x
- abs_mouse_x
),
306 std::max(0, center_y
- abs_mouse_y
));
307 pp::Point
right_bottom(
308 std::min(image
->size().width(), center_x
+ abs_mouse_x
),
309 std::min(image
->size().height(), center_y
+ abs_mouse_y
));
310 for (int y
= left_top
.y(); y
< right_bottom
.y(); ++y
) {
311 for (int x
= left_top
.x(); x
< right_bottom
.x(); ++x
) {
312 bool within_bound_1
= ((y
- anchor_1
.y()) * (vertex
.x() - anchor_1
.x())) >
313 ((vertex
.y() - anchor_1
.y()) * (x
- anchor_1
.x()));
314 bool within_bound_2
= ((y
- anchor_2
.y()) * (vertex
.x() - anchor_2
.x())) <
315 ((vertex
.y() - anchor_2
.y()) * (x
- anchor_2
.x()));
316 bool within_bound_3
= (direction
== kUp
&& y
< center_y
) ||
317 (direction
== kDown
&& y
> center_y
) ||
318 (direction
== kLeft
&& x
< center_x
) ||
319 (direction
== kRight
&& x
> center_x
);
321 if (within_bound_1
&& within_bound_2
&& within_bound_3
) {
322 *image
->GetAddr32(pp::Point(x
, y
)) = needle_color
;
328 void MouseLockInstance::Log(const char* format
, ...) {
329 static PPB_Console
* console
=
330 (PPB_Console
*)pp::Module::Get()->GetBrowserInterface(
331 PPB_CONSOLE_INTERFACE
);
336 va_start(args
, format
);
338 vsnprintf(buf
, sizeof(buf
) - 1, format
, args
);
339 buf
[sizeof(buf
) - 1] = '\0';
343 console
->Log(pp_instance(), PP_LOGLEVEL_ERROR
, value
.pp_var());
346 // This object is the global object representing this plugin library as long
348 class MouseLockModule
: public pp::Module
{
350 MouseLockModule() : pp::Module() {}
351 virtual ~MouseLockModule() {}
353 // Override CreateInstance to create your customized Instance object.
354 virtual pp::Instance
* CreateInstance(PP_Instance instance
) {
355 return new MouseLockInstance(instance
);
361 // Factory function for your specialization of the Module object.
362 Module
* CreateModule() { return new MouseLockModule(); }