Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / native_client_sdk / src / examples / api / mouse_lock / mouse_lock.cc
blob6c02b8f13b016aa7175abb59aed4cff4dcebba20
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.
5 #include <stdarg.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <cmath>
9 #include <cstdlib>
11 #include <algorithm>
13 #include "mouse_lock.h"
15 #ifdef WIN32
16 #undef min
17 #undef max
18 #undef PostMessage
19 #endif
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
23 // in.
24 typedef enum {
25 kLeft = 0,
26 kRight = 1,
27 kUp = 2,
28 kDown = 3
29 } MouseDirection;
31 namespace {
32 const int kCentralSpotRadius = 5;
33 const uint32_t kReturnKeyCode = 13;
34 const uint32_t kBackgroundColor = 0xff606060;
35 const uint32_t kForegroundColor = 0xfff08080;
36 } // namespace
38 MouseLockInstance::~MouseLockInstance() {
39 free(background_scanline_);
40 background_scanline_ = NULL;
43 bool MouseLockInstance::Init(uint32_t argc,
44 const char* argn[],
45 const char* argv[]) {
46 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_KEYBOARD);
47 return true;
50 bool MouseLockInstance::HandleInputEvent(const pp::InputEvent& event) {
51 switch (event.GetType()) {
52 case PP_INPUTEVENT_TYPE_MOUSEDOWN: {
53 if (mouse_locked_) {
54 UnlockMouse();
55 } else {
56 LockMouse(
57 callback_factory_.NewCallback(&MouseLockInstance::DidLockMouse));
59 return true;
62 case PP_INPUTEVENT_TYPE_MOUSEMOVE: {
63 pp::MouseInputEvent mouse_event(event);
64 mouse_movement_ = mouse_event.GetMovement();
65 Paint();
66 return true;
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_)
76 return true;
78 if (fullscreen_.IsFullscreen()) {
79 if (!fullscreen_.SetFullscreen(false)) {
80 Log("Could not leave fullscreen mode\n");
81 } else {
82 is_context_bound_ = false;
84 } else {
85 if (!fullscreen_.SetFullscreen(true)) {
86 Log("Could not enter fullscreen mode\n");
87 } else {
88 is_context_bound_ = false;
92 return true;
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:
112 default:
113 return false;
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");
128 return;
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.");
144 return;
145 } else {
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
161 Paint();
164 void MouseLockInstance::MouseLockLost() {
165 if (mouse_locked_) {
166 Log("Mouselock unlocked.\n");
167 mouse_locked_ = false;
168 Paint();
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);
179 Paint();
182 void MouseLockInstance::DidFlush(int32_t result) {
183 if (result != 0)
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_) {
191 return;
194 pp::ImageData image = PaintImage(size_);
195 if (image.is_null()) {
196 Log("Could not create image data\n");
197 return;
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");
210 return image;
213 ClearToBackground(&image);
215 DrawCenterSpot(&image, kForegroundColor);
216 DrawNeedle(&image, kForegroundColor);
217 return image;
220 void MouseLockInstance::ClearToBackground(pp::ImageData* image) {
221 if (image == NULL) {
222 Log("ClearToBackground with NULL image.");
223 return;
225 if (background_scanline_ == NULL) {
226 Log("ClearToBackground with no scanline.");
227 return;
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));
234 memcpy(scanline,
235 background_scanline_,
236 image_width * sizeof(*background_scanline_));
240 void MouseLockInstance::DrawCenterSpot(pp::ImageData* image,
241 uint32_t spot_color) {
242 if (image == NULL) {
243 Log("DrawCenterSpot with NULL image");
244 return;
246 // Draw the center spot. The ROI is bounded by the size of the spot, plus
247 // one pixel.
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) {
268 if (image == NULL) {
269 Log("DrawNeedle with NULL image");
270 return;
272 if (GetDistance(mouse_movement_.x(), mouse_movement_.y(), 0, 0) <=
273 kCentralSpotRadius) {
274 return;
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);
283 pp::Point anchor_1;
284 pp::Point anchor_2;
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);
295 } else {
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);
333 if (NULL == console)
334 return;
335 va_list args;
336 va_start(args, format);
337 char buf[512];
338 vsnprintf(buf, sizeof(buf) - 1, format, args);
339 buf[sizeof(buf) - 1] = '\0';
340 va_end(args);
342 pp::Var value(buf);
343 console->Log(pp_instance(), PP_LOGLEVEL_ERROR, value.pp_var());
346 // This object is the global object representing this plugin library as long
347 // as it is loaded.
348 class MouseLockModule : public pp::Module {
349 public:
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);
359 namespace pp {
361 // Factory function for your specialization of the Module object.
362 Module* CreateModule() { return new MouseLockModule(); }
364 } // namespace pp