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 "mouselock.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;
40 MouseLockInstance::~MouseLockInstance() {
41 free(background_scanline_
);
42 background_scanline_
= NULL
;
45 bool MouseLockInstance::Init(uint32_t argc
,
48 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE
|
49 PP_INPUTEVENT_CLASS_KEYBOARD
);
53 bool MouseLockInstance::HandleInputEvent(const pp::InputEvent
& event
) {
54 switch (event
.GetType()) {
55 case PP_INPUTEVENT_TYPE_MOUSEDOWN
: {
59 LockMouse(callback_factory_
.NewCallback(
60 &MouseLockInstance::DidLockMouse
));
65 case PP_INPUTEVENT_TYPE_MOUSEMOVE
: {
66 pp::MouseInputEvent
mouse_event(event
);
67 mouse_movement_
= mouse_event
.GetMovement();
72 case PP_INPUTEVENT_TYPE_KEYDOWN
: {
73 pp::KeyboardInputEvent
key_event(event
);
75 // Switch in and out of fullscreen when 'Enter' is hit
76 if (key_event
.GetKeyCode() == kReturnKeyCode
) {
77 // Ignore switch if in transition
78 if (!is_context_bound_
)
81 if (fullscreen_
.IsFullscreen()) {
82 if (!fullscreen_
.SetFullscreen(false)) {
83 Log("Could not leave fullscreen mode\n");
85 is_context_bound_
= false;
88 if (!fullscreen_
.SetFullscreen(true)) {
89 Log("Could not enter fullscreen mode\n");
91 is_context_bound_
= false;
98 case PP_INPUTEVENT_TYPE_MOUSEUP
:
99 case PP_INPUTEVENT_TYPE_MOUSEENTER
:
100 case PP_INPUTEVENT_TYPE_MOUSELEAVE
:
101 case PP_INPUTEVENT_TYPE_WHEEL
:
102 case PP_INPUTEVENT_TYPE_RAWKEYDOWN
:
103 case PP_INPUTEVENT_TYPE_KEYUP
:
104 case PP_INPUTEVENT_TYPE_CHAR
:
105 case PP_INPUTEVENT_TYPE_CONTEXTMENU
:
106 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START
:
107 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE
:
108 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END
:
109 case PP_INPUTEVENT_TYPE_IME_TEXT
:
110 case PP_INPUTEVENT_TYPE_UNDEFINED
:
111 case PP_INPUTEVENT_TYPE_TOUCHSTART
:
112 case PP_INPUTEVENT_TYPE_TOUCHMOVE
:
113 case PP_INPUTEVENT_TYPE_TOUCHEND
:
114 case PP_INPUTEVENT_TYPE_TOUCHCANCEL
:
120 void MouseLockInstance::DidChangeView(const pp::View
& view
) {
121 // DidChangeView can get called for many reasons, so we only want to
122 // rebuild the device context if we really need to.
124 if ((size_
== view
.GetRect().size()) &&
125 (was_fullscreen_
== view
.IsFullscreen()) && is_context_bound_
) {
126 Log("DidChangeView SKIP %d,%d FULL=%s CTX Bound=%s",
127 view
.GetRect().width(), view
.GetRect().height(),
128 view
.IsFullscreen() ? "true" : "false",
129 is_context_bound_
? "true" : "false");
133 Log("DidChangeView DO %d,%d FULL=%s CTX Bound=%s",
134 view
.GetRect().width(), view
.GetRect().height(),
135 view
.IsFullscreen() ? "true" : "false",
136 is_context_bound_
? "true" : "false");
138 size_
= view
.GetRect().size();
139 device_context_
= pp::Graphics2D(this, size_
, false);
140 waiting_for_flush_completion_
= false;
142 is_context_bound_
= BindGraphics(device_context_
);
143 if (!is_context_bound_
) {
144 Log("Could not bind to 2D context\n.");
147 Log("Bound to 2D context size %d,%d.\n", size_
.width(), size_
.height());
150 // Create a scanline for fill.
151 delete[] background_scanline_
;
152 background_scanline_
= new uint32_t[size_
.width()];
153 uint32_t* bg_pixel
= background_scanline_
;
154 for (int x
= 0; x
< size_
.width(); ++x
) {
155 *bg_pixel
++ = kBackgroundColor
;
158 // Remember if we are fullscreen or not
159 was_fullscreen_
= view
.IsFullscreen();
161 // Paint this context
165 void MouseLockInstance::MouseLockLost() {
167 Log("Mouselock unlocked.\n");
168 mouse_locked_
= false;
173 void MouseLockInstance::DidLockMouse(int32_t result
) {
174 mouse_locked_
= result
== PP_OK
;
175 if (result
!= PP_OK
) {
176 Log("Mouselock failed with failed with error number %d.\n", result
);
178 mouse_movement_
.set_x(0);
179 mouse_movement_
.set_y(0);
183 void MouseLockInstance::DidFlush(int32_t result
) {
184 if (result
!= 0) 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
));
207 pp::ImageData
MouseLockInstance::PaintImage(const pp::Size
& size
) {
208 pp::ImageData
image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL
, size
, false);
209 if (image
.is_null() || image
.data() == NULL
) {
210 Log("Skipping image.\n");
214 ClearToBackground(&image
);
216 DrawCenterSpot(&image
, kForegroundColor
);
217 DrawNeedle(&image
, kForegroundColor
);
221 void MouseLockInstance::ClearToBackground(pp::ImageData
* image
) {
223 Log("ClearToBackground with NULL image.");
226 if (background_scanline_
== NULL
) {
227 Log("ClearToBackground with no scanline.");
230 int image_height
= image
->size().height();
231 int image_width
= image
->size().width();
233 for (int y
= 0; y
< image_height
; ++y
) {
234 uint32_t* scanline
= image
->GetAddr32(pp::Point(0, y
));
236 background_scanline_
,
237 image_width
* sizeof(*background_scanline_
));
241 void MouseLockInstance::DrawCenterSpot(pp::ImageData
* image
,
242 uint32_t spot_color
) {
244 Log("DrawCenterSpot with NULL image");
247 // Draw the center spot. The ROI is bounded by the size of the spot, plus
249 int center_x
= image
->size().width() / 2;
250 int center_y
= image
->size().height() / 2;
251 int region_of_interest_radius
= kCentralSpotRadius
+ 1;
253 pp::Point
left_top(std::max(0, center_x
- region_of_interest_radius
),
254 std::max(0, center_y
- region_of_interest_radius
));
255 pp::Point
right_bottom(std::min(image
->size().width(),
256 center_x
+ region_of_interest_radius
),
257 std::min(image
->size().height(),
258 center_y
+ region_of_interest_radius
));
259 for (int y
= left_top
.y(); y
< right_bottom
.y(); ++y
) {
260 for (int x
= left_top
.x(); x
< right_bottom
.x(); ++x
) {
261 if (GetDistance(x
, y
, center_x
, center_y
) < kCentralSpotRadius
) {
262 *image
->GetAddr32(pp::Point(x
, y
)) = spot_color
;
268 void MouseLockInstance::DrawNeedle(pp::ImageData
* image
,
269 uint32_t needle_color
) {
271 Log("DrawNeedle with NULL image");
274 if (GetDistance(mouse_movement_
.x(), mouse_movement_
.y(), 0, 0) <=
275 kCentralSpotRadius
) {
279 int abs_mouse_x
= std::abs(mouse_movement_
.x());
280 int abs_mouse_y
= std::abs(mouse_movement_
.y());
281 int center_x
= image
->size().width() / 2;
282 int center_y
= image
->size().height() / 2;
283 pp::Point
vertex(mouse_movement_
.x() + center_x
,
284 mouse_movement_
.y() + center_y
);
287 MouseDirection direction
= kLeft
;
289 if (abs_mouse_x
>= abs_mouse_y
) {
290 anchor_1
.set_x(center_x
);
291 anchor_1
.set_y(center_y
- kCentralSpotRadius
);
292 anchor_2
.set_x(center_x
);
293 anchor_2
.set_y(center_y
+ kCentralSpotRadius
);
294 direction
= (mouse_movement_
.x() < 0) ? kLeft
: kRight
;
295 if (direction
== kLeft
)
296 anchor_1
.swap(anchor_2
);
298 anchor_1
.set_x(center_x
+ kCentralSpotRadius
);
299 anchor_1
.set_y(center_y
);
300 anchor_2
.set_x(center_x
- kCentralSpotRadius
);
301 anchor_2
.set_y(center_y
);
302 direction
= (mouse_movement_
.y() < 0) ? kUp
: kDown
;
303 if (direction
== kUp
)
304 anchor_1
.swap(anchor_2
);
307 pp::Point
left_top(std::max(0, center_x
- abs_mouse_x
),
308 std::max(0, center_y
- abs_mouse_y
));
309 pp::Point
right_bottom(std::min(image
->size().width(),
310 center_x
+ abs_mouse_x
),
311 std::min(image
->size().height(),
312 center_y
+ abs_mouse_y
));
313 for (int y
= left_top
.y(); y
< right_bottom
.y(); ++y
) {
314 for (int x
= left_top
.x(); x
< right_bottom
.x(); ++x
) {
315 bool within_bound_1
=
316 ((y
- anchor_1
.y()) * (vertex
.x() - anchor_1
.x())) >
317 ((vertex
.y() - anchor_1
.y()) * (x
- anchor_1
.x()));
318 bool within_bound_2
=
319 ((y
- anchor_2
.y()) * (vertex
.x() - anchor_2
.x())) <
320 ((vertex
.y() - anchor_2
.y()) * (x
- anchor_2
.x()));
321 bool within_bound_3
=
322 (direction
== kUp
&& y
< center_y
) ||
323 (direction
== kDown
&& y
> center_y
) ||
324 (direction
== kLeft
&& x
< center_x
) ||
325 (direction
== kRight
&& x
> center_x
);
327 if (within_bound_1
&& within_bound_2
&& within_bound_3
) {
328 *image
->GetAddr32(pp::Point(x
, y
)) = needle_color
;
335 void MouseLockInstance::Log(const char* format
, ...) {
336 static PPB_Console
* console
= (PPB_Console
*)
337 pp::Module::Get()->GetBrowserInterface(PPB_CONSOLE_INTERFACE
);
339 if (NULL
== console
) return;
341 va_start(args
, format
);
343 vsnprintf(buf
, sizeof(buf
) - 1, format
, args
);
344 buf
[sizeof(buf
) - 1] = '\0';
348 console
->Log(pp_instance(), PP_LOGLEVEL_ERROR
, value
.pp_var());
351 } // namespace mouselock
353 // This object is the global object representing this plugin library as long
355 class MouseLockModule
: public pp::Module
{
357 MouseLockModule() : pp::Module() {}
358 virtual ~MouseLockModule() {}
360 // Override CreateInstance to create your customized Instance object.
361 virtual pp::Instance
* CreateInstance(PP_Instance instance
) {
362 return new mouselock::MouseLockInstance(instance
);
368 // Factory function for your specialization of the Module object.
369 Module
* CreateModule() {
370 return new MouseLockModule();