- add sources.
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / examples / api / mouse_lock / mouse_lock.cc
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.
4
5 #include <stdarg.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <cmath>
9 #include <cstdlib>
10
11 #include <algorithm>
12
13 #include "mouse_lock.h"
14
15 #ifdef WIN32
16 #undef min
17 #undef max
18 #undef PostMessage
19 #endif
20
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;
30
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
37
38 MouseLockInstance::~MouseLockInstance() {
39   free(background_scanline_);
40   background_scanline_ = NULL;
41 }
42
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;
48 }
49
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));
58       }
59       return true;
60     }
61
62     case PP_INPUTEVENT_TYPE_MOUSEMOVE: {
63       pp::MouseInputEvent mouse_event(event);
64       mouse_movement_ = mouse_event.GetMovement();
65       Paint();
66       return true;
67     }
68
69     case PP_INPUTEVENT_TYPE_KEYDOWN: {
70       pp::KeyboardInputEvent key_event(event);
71
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;
77
78         if (fullscreen_.IsFullscreen()) {
79           if (!fullscreen_.SetFullscreen(false)) {
80             Log("Could not leave fullscreen mode\n");
81           } else {
82             is_context_bound_ = false;
83           }
84         } else {
85           if (!fullscreen_.SetFullscreen(true)) {
86             Log("Could not enter fullscreen mode\n");
87           } else {
88             is_context_bound_ = false;
89           }
90         }
91       }
92       return true;
93     }
94
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;
114   }
115 }
116
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.
120
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;
129   }
130
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");
136
137   size_ = view.GetRect().size();
138   device_context_ = pp::Graphics2D(this, size_, false);
139   waiting_for_flush_completion_ = false;
140
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());
147   }
148
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;
155   }
156
157   // Remember if we are fullscreen or not
158   was_fullscreen_ = view.IsFullscreen();
159
160   // Paint this context
161   Paint();
162 }
163
164 void MouseLockInstance::MouseLockLost() {
165   if (mouse_locked_) {
166     Log("Mouselock unlocked.\n");
167     mouse_locked_ = false;
168     Paint();
169   }
170 }
171
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);
176   }
177   mouse_movement_.set_x(0);
178   mouse_movement_.set_y(0);
179   Paint();
180 }
181
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;
186 }
187
188 void MouseLockInstance::Paint() {
189   // If we are already waiting to paint...
190   if (waiting_for_flush_completion_) {
191     return;
192   }
193
194   pp::ImageData image = PaintImage(size_);
195   if (image.is_null()) {
196     Log("Could not create image data\n");
197     return;
198   }
199
200   device_context_.ReplaceContents(&image);
201   waiting_for_flush_completion_ = true;
202   device_context_.Flush(
203       callback_factory_.NewCallback(&MouseLockInstance::DidFlush));
204 }
205
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;
211   }
212
213   ClearToBackground(&image);
214
215   DrawCenterSpot(&image, kForegroundColor);
216   DrawNeedle(&image, kForegroundColor);
217   return image;
218 }
219
220 void MouseLockInstance::ClearToBackground(pp::ImageData* image) {
221   if (image == NULL) {
222     Log("ClearToBackground with NULL image.");
223     return;
224   }
225   if (background_scanline_ == NULL) {
226     Log("ClearToBackground with no scanline.");
227     return;
228   }
229   int image_height = image->size().height();
230   int image_width = image->size().width();
231
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_));
237   }
238 }
239
240 void MouseLockInstance::DrawCenterSpot(pp::ImageData* image,
241                                        uint32_t spot_color) {
242   if (image == NULL) {
243     Log("DrawCenterSpot with NULL image");
244     return;
245   }
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;
251
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;
261       }
262     }
263   }
264 }
265
266 void MouseLockInstance::DrawNeedle(pp::ImageData* image,
267                                    uint32_t needle_color) {
268   if (image == NULL) {
269     Log("DrawNeedle with NULL image");
270     return;
271   }
272   if (GetDistance(mouse_movement_.x(), mouse_movement_.y(), 0, 0) <=
273       kCentralSpotRadius) {
274     return;
275   }
276
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;
286
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);
303   }
304
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);
320
321       if (within_bound_1 && within_bound_2 && within_bound_3) {
322         *image->GetAddr32(pp::Point(x, y)) = needle_color;
323       }
324     }
325   }
326 }
327
328 void MouseLockInstance::Log(const char* format, ...) {
329   static PPB_Console* console =
330       (PPB_Console*)pp::Module::Get()->GetBrowserInterface(
331           PPB_CONSOLE_INTERFACE);
332
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);
341
342   pp::Var value(buf);
343   console->Log(pp_instance(), PP_LOGLEVEL_ERROR, value.pp_var());
344 }
345
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() {}
352
353   // Override CreateInstance to create your customized Instance object.
354   virtual pp::Instance* CreateInstance(PP_Instance instance) {
355     return new MouseLockInstance(instance);
356   }
357 };
358
359 namespace pp {
360
361 // Factory function for your specialization of the Module object.
362 Module* CreateModule() { return new MouseLockModule(); }
363
364 }  // namespace pp