imcplugin demo: Extend to support stat() call
[nativeclient.git] / npapi_plugin / srpc / video.cc
blob24ad10ac422d246bbf511f7653ded6e8138b2bfe
1 /*
2 * Copyright 2008, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #include <new>
34 #if NACL_OSX
35 #include <Carbon/Carbon.h>
36 #endif // NACL_OSX
37 #if NACL_WINDOWS
38 #include <windows.h>
39 #include <windowsx.h>
40 #endif // NACL_WINDOWS
41 #if NACL_LINUX && defined(MOZ_X11)
42 #include <sched.h>
43 #include <X11/Xlib.h>
44 #include <X11/Intrinsic.h>
45 #include <X11/keysym.h>
46 #endif // NACL_LINUX && defined(MOZ_X11)
48 #include "native_client/tools/npapi_runtime/nacl_npapi.h"
49 #include "native_client/npapi_plugin/srpc/npapi_native.h"
50 #include "native_client/npapi_plugin/srpc/srpc.h"
51 #include "native_client/npapi_plugin/srpc/video.h"
54 namespace nacl {
56 class GlobalVideoMutex {
57 private:
58 NaClMutex mutex_;
59 public:
60 void Lock() { NaClMutexLock(&mutex_); }
61 void Unlock() { NaClMutexUnlock(&mutex_); }
62 GlobalVideoMutex() { NaClMutexCtor(&mutex_); }
63 ~GlobalVideoMutex() { NaClMutexDtor(&mutex_); }
66 static GlobalVideoMutex globalVideoMutex;
68 void VideoGlobalLock() {
69 globalVideoMutex.Lock();
72 void VideoGlobalUnlock() {
73 globalVideoMutex.Unlock();
77 #if NACL_LINUX && defined(MOZ_X11)
78 static int XKeysymToNaCl(KeySym xsym) {
79 if ((xsym & 0xFF00) == 0x0000)
80 return xsym;
81 switch (xsym) {
82 case XK_Alt_L: return NACL_KEY_LALT;
83 case XK_Alt_R: return NACL_KEY_RALT;
84 case XK_BackSpace: return NACL_KEY_BACKSPACE;
85 case XK_Break: return NACL_KEY_BREAK;
86 case XK_Caps_Lock: return NACL_KEY_CAPSLOCK;
87 case XK_Clear: return NACL_KEY_CLEAR;
88 case XK_Control_L: return NACL_KEY_LCTRL;
89 case XK_Control_R: return NACL_KEY_RCTRL;
90 case XK_Delete: return NACL_KEY_DELETE;
91 case XK_Down: return NACL_KEY_DOWN;
92 case XK_End: return NACL_KEY_END;
93 case XK_Escape: return NACL_KEY_ESCAPE;
94 case XK_F1: return NACL_KEY_F1;
95 case XK_F2: return NACL_KEY_F2;
96 case XK_F3: return NACL_KEY_F3;
97 case XK_F4: return NACL_KEY_F4;
98 case XK_F5: return NACL_KEY_F5;
99 case XK_F6: return NACL_KEY_F6;
100 case XK_F7: return NACL_KEY_F7;
101 case XK_F8: return NACL_KEY_F8;
102 case XK_F9: return NACL_KEY_F9;
103 case XK_F10: return NACL_KEY_F10;
104 case XK_F11: return NACL_KEY_F11;
105 case XK_F12: return NACL_KEY_F12;
106 case XK_F13: return NACL_KEY_F13;
107 case XK_F14: return NACL_KEY_F14;
108 case XK_F15: return NACL_KEY_F15;
109 case XK_Help: return NACL_KEY_HELP;
110 case XK_Home: return NACL_KEY_HOME;
111 case XK_Hyper_R: return NACL_KEY_MENU;
112 case XK_Insert: return NACL_KEY_INSERT;
113 case XK_KP_0: return NACL_KEY_KP0;
114 case XK_KP_1: return NACL_KEY_KP1;
115 case XK_KP_2: return NACL_KEY_KP2;
116 case XK_KP_3: return NACL_KEY_KP3;
117 case XK_KP_4: return NACL_KEY_KP4;
118 case XK_KP_5: return NACL_KEY_KP5;
119 case XK_KP_6: return NACL_KEY_KP6;
120 case XK_KP_7: return NACL_KEY_KP7;
121 case XK_KP_8: return NACL_KEY_KP8;
122 case XK_KP_9: return NACL_KEY_KP9;
123 case XK_KP_Add: return NACL_KEY_KP_PLUS;
124 case XK_KP_Begin: return NACL_KEY_KP5;
125 case XK_KP_Decimal: return NACL_KEY_KP_PERIOD;
126 case XK_KP_Delete: return NACL_KEY_KP_PERIOD;
127 case XK_KP_Divide: return NACL_KEY_KP_DIVIDE;
128 case XK_KP_Down: return NACL_KEY_KP2;
129 case XK_KP_End: return NACL_KEY_KP1;
130 case XK_KP_Enter: return NACL_KEY_KP_ENTER;
131 case XK_KP_Equal: return NACL_KEY_KP_EQUALS;
132 case XK_KP_Home: return NACL_KEY_KP7;
133 case XK_KP_Insert: return NACL_KEY_KP0;
134 case XK_KP_Left: return NACL_KEY_KP4;
135 case XK_KP_Multiply: return NACL_KEY_KP_MULTIPLY;
136 case XK_KP_Page_Down: return NACL_KEY_KP3;
137 case XK_KP_Page_Up: return NACL_KEY_KP9;
138 case XK_KP_Right: return NACL_KEY_KP6;
139 case XK_KP_Subtract: return NACL_KEY_KP_MINUS;
140 case XK_KP_Up: return NACL_KEY_KP8;
141 case XK_Left: return NACL_KEY_LEFT;
142 case XK_Num_Lock: return NACL_KEY_NUMLOCK;
143 case XK_Menu: return NACL_KEY_MENU;
144 case XK_Meta_R: return NACL_KEY_RMETA;
145 case XK_Meta_L: return NACL_KEY_LMETA;
146 case XK_Mode_switch: return NACL_KEY_MODE;
147 case XK_Multi_key: return NACL_KEY_COMPOSE;
148 case XK_Page_Down: return NACL_KEY_PAGEDOWN;
149 case XK_Page_Up: return NACL_KEY_PAGEUP;
150 case XK_Pause: return NACL_KEY_PAUSE;
151 case XK_Print: return NACL_KEY_PRINT;
152 case XK_Return: return NACL_KEY_RETURN;
153 case XK_Right: return NACL_KEY_RIGHT;
154 case XK_Scroll_Lock: return NACL_KEY_SCROLLOCK;
155 case XK_Shift_R: return NACL_KEY_RSHIFT;
156 case XK_Shift_L: return NACL_KEY_LSHIFT;
157 case XK_Super_L: return NACL_KEY_LSUPER;
158 case XK_Super_R: return NACL_KEY_RSUPER;
159 case XK_Sys_Req: return NACL_KEY_SYSREQ;
160 case XK_Tab: return NACL_KEY_TAB;
161 case XK_Up: return NACL_KEY_UP;
163 return NACL_KEY_UNKNOWN;
166 void VideoMap::RedrawAsync(void *platform_parm) {
167 dprintf(("VideoMap::RedrawAsync %p\n", platform_parm));
168 if (0 != untrusted_video_share_->u.h.video_ready) {
169 Display *display = reinterpret_cast<Display *>(platform_parm);
170 Drawable window = reinterpret_cast<Drawable>(window_->window);
171 GC gc = XCreateGC(display, window, 0, NULL);
172 XImage image;
173 memset(&image, 0, sizeof image);
174 image.format = ZPixmap;
175 image.data = reinterpret_cast<char*>
176 (&untrusted_video_share_->video_pixels[0]);
177 image.width = window_->width;
178 image.height = window_->height;
179 image.xoffset = 0;
180 image.byte_order = LSBFirst;
181 image.bitmap_bit_order = MSBFirst;
182 image.bits_per_pixel = 32;
183 image.bytes_per_line = 4 * window_->width;
184 image.bitmap_unit = 32;
185 image.bitmap_pad = 32;
186 image.depth = 24;
187 XPutImage(display, window, gc, &image, 0, 0, 0, 0,
188 window_->width, window_->height);
189 XFreeGC(display, gc);
190 XFlush(display);
192 sched_yield();
195 void VideoMap::Redraw() {
196 Display* display =
197 static_cast<NPSetWindowCallbackStruct*>(window_->ws_info)->display;
198 this->RedrawAsync(reinterpret_cast<void *>(display));
201 void VideoMap::XEventHandler(Widget widget,
202 VideoMap* video,
203 XEvent* xevent,
204 Boolean* b) {
205 VideoScopedGlobalLock video_lock;
206 if ((NULL == video) || (NULL == video->window_) ||
207 (NULL == video->untrusted_video_share_))
208 return;
209 Window xwin = reinterpret_cast<Window>(video->window_->window);
210 NPSetWindowCallbackStruct* wcbs =
211 reinterpret_cast<NPSetWindowCallbackStruct*>(video->window_->ws_info);
212 Display* dpy = wcbs->display;
213 union NaClMultimediaEvent nacl_event;
214 KeySym xsym;
215 int button;
216 int nsym;
217 uint16_t x;
218 uint16_t y;
220 switch (xevent->type) {
221 case Expose:
222 // Exposure events come in multiples, one per rectangle uncovered.
223 // We just look at one and redraw the whole region.
224 while (XCheckTypedWindowEvent(dpy, xwin, Expose, xevent));
225 video->Redraw();
226 break;
227 case KeyPress:
228 nacl_event.type = NACL_EVENT_KEY_DOWN;
229 nacl_event.key.which = 0;
230 nacl_event.key.state = kSet;
231 nacl_event.key.keysym.scancode = xevent->xkey.keycode;
232 xsym = XKeycodeToKeysym(dpy, xevent->xkey.keycode, 0);
233 nsym = XKeysymToNaCl(xsym);
234 video->SetKeyMod(nsym, kSet);
235 nacl_event.key.keysym.sym = nsym;
236 nacl_event.key.keysym.mod = video->event_state_key_mod_;
237 nacl_event.key.keysym.unicode = 0;
238 video->EventQueuePut(&nacl_event);
239 break;
240 case KeyRelease:
241 nacl_event.type = NACL_EVENT_KEY_UP;
242 nacl_event.key.which = 0;
243 nacl_event.key.state = kClear;
244 nacl_event.key.keysym.scancode = xevent->xkey.keycode;
245 xsym = XKeycodeToKeysym(dpy, xevent->xkey.keycode, 0);
246 nsym = XKeysymToNaCl(xsym);
247 video->SetKeyMod(nsym, kClear);
248 nacl_event.key.keysym.sym = nsym;
249 nacl_event.key.keysym.mod = video->event_state_key_mod_;
250 nacl_event.key.keysym.unicode = 0;
251 video->EventQueuePut(&nacl_event);
252 break;
253 case MotionNotify:
254 x = static_cast<uint16_t>(xevent->xmotion.x);
255 y = static_cast<uint16_t>(xevent->xmotion.y);
256 nacl_event.type = NACL_EVENT_MOUSE_MOTION;
257 nacl_event.motion.which = 0;
258 nacl_event.motion.state = video->GetButton();
259 nacl_event.motion.x = x;
260 nacl_event.motion.y = y;
261 video->GetRelativeMotion(x, y,
262 &nacl_event.motion.xrel,
263 &nacl_event.motion.yrel);
264 video->SetMotion(xevent->xmotion.x, xevent->xmotion.y, 1);
265 video->EventQueuePut(&nacl_event);
266 break;
267 case ButtonPress:
268 button = xevent->xbutton.button;
269 nacl_event.type = NACL_EVENT_MOUSE_BUTTON_DOWN;
270 nacl_event.button.which = 0;
271 nacl_event.button.button = button;
272 nacl_event.button.state = kSet;
273 video->GetMotion(&nacl_event.button.x, &nacl_event.button.y);
274 video->SetButton(button, 1);
275 video->EventQueuePut(&nacl_event);
276 break;
277 case ButtonRelease:
278 button = xevent->xbutton.button;
279 nacl_event.type = NACL_EVENT_MOUSE_BUTTON_UP;
280 nacl_event.button.which = 0;
281 nacl_event.button.button = button;
282 nacl_event.button.state = kClear;
283 video->GetMotion(&nacl_event.button.x, &nacl_event.button.y);
284 video->SetButton(button, 0);
285 video->EventQueuePut(&nacl_event);
286 break;
287 case LeaveNotify:
288 nacl_event.type = NACL_EVENT_ACTIVE;
289 nacl_event.active.state = kClear;
290 video->EventQueuePut(&nacl_event);
291 break;
292 case EnterNotify:
293 nacl_event.type = NACL_EVENT_ACTIVE;
294 nacl_event.active.state = kSet;
295 video->SetMotion(0, 0, 0);
296 video->EventQueuePut(&nacl_event);
297 break;
298 default:
299 // Other types of events should be handled here.
300 dprintf(("VideoMap::XEventHandler() Other\n"));
301 break;
304 #endif // NACL_LINUX && defined(MOZ_X11)
306 #if NACL_OSX
307 // convert global mouse coordinate to local space
308 // TODO: why do these only work in the paint event?
309 static void GlobalToLocalHelper(NPWindow* window, Point *point) {
310 NP_Port* npport = static_cast<NP_Port*>(window->window);
311 CGrafPtr our_port = npport->port;
312 GrafPtr save;
313 GetPort(&save);
314 SetPort(reinterpret_cast<GrafPtr>(our_port));
315 GlobalToLocal(point);
316 dprintf(("mouse local pos %d %d\n", point->h, point->v));
317 SetPort(save);
320 // looks for first changed key
321 static bool FindChangedKey(uint32_t *keys, uint32_t *previous,
322 int *which, int *state) {
323 if ((keys[0] != previous[0]) ||
324 (keys[1] != previous[1]) ||
325 (keys[2] != previous[2]) ||
326 (keys[3] != previous[3])) {
327 for (int i = 0; i < 128; ++i) {
328 int idx = (i >> 5);
329 int bit = (1 << (i & 31));
330 int keys_bit = ((keys[idx] & bit) != 0);
331 int previous_bit = ((previous[idx] & bit) != 0);
332 if (keys_bit != previous_bit) {
333 *which = i;
334 *state = keys_bit ? kSet : kClear;
335 previous[idx] = keys[idx];
336 return true;
340 return false;
343 // converts mac raw key to nacl key
344 static int MacKeyToNaCl(int mk) {
345 switch (mk) {
346 case 0x00: return NACL_KEY_a;
347 case 0x0B: return NACL_KEY_b;
348 case 0x08: return NACL_KEY_c;
349 case 0x02: return NACL_KEY_d;
350 case 0x0E: return NACL_KEY_e;
351 case 0x03: return NACL_KEY_f;
352 case 0x05: return NACL_KEY_g;
353 case 0x04: return NACL_KEY_h;
354 case 0x22: return NACL_KEY_i;
355 case 0x26: return NACL_KEY_j;
356 case 0x28: return NACL_KEY_k;
357 case 0x25: return NACL_KEY_l;
358 case 0x2E: return NACL_KEY_m;
359 case 0x2D: return NACL_KEY_n;
360 case 0x1F: return NACL_KEY_o;
361 case 0x23: return NACL_KEY_p;
362 case 0x0C: return NACL_KEY_q;
363 case 0x0F: return NACL_KEY_r;
364 case 0x01: return NACL_KEY_s;
365 case 0x11: return NACL_KEY_t;
366 case 0x20: return NACL_KEY_u;
367 case 0x09: return NACL_KEY_v;
368 case 0x0D: return NACL_KEY_w;
369 case 0x07: return NACL_KEY_x;
370 case 0x10: return NACL_KEY_y;
371 case 0x06: return NACL_KEY_z;
372 case 0x12: return NACL_KEY_1;
373 case 0x13: return NACL_KEY_2;
374 case 0x14: return NACL_KEY_3;
375 case 0x15: return NACL_KEY_4;
376 case 0x17: return NACL_KEY_5;
377 case 0x16: return NACL_KEY_6;
378 case 0x1A: return NACL_KEY_7;
379 case 0x1C: return NACL_KEY_8;
380 case 0x19: return NACL_KEY_9;
381 case 0x1D: return NACL_KEY_0;
382 case 0x32: return NACL_KEY_BACKQUOTE;
383 case 0x2A: return NACL_KEY_BACKSLASH;
384 case 0x33: return NACL_KEY_BACKSPACE;
385 case 0x39: return NACL_KEY_CAPSLOCK;
386 case 0x2B: return NACL_KEY_COMMA;
387 case 0x75: return NACL_KEY_DELETE;
388 case 0x7D: return NACL_KEY_DOWN;
389 case 0x77: return NACL_KEY_END;
390 case 0x18: return NACL_KEY_EQUALS;
391 case 0x35: return NACL_KEY_ESCAPE;
392 case 0x73: return NACL_KEY_HOME;
393 case 0x72: return NACL_KEY_INSERT;
394 case 0x52: return NACL_KEY_KP0;
395 case 0x53: return NACL_KEY_KP1;
396 case 0x54: return NACL_KEY_KP2;
397 case 0x55: return NACL_KEY_KP3;
398 case 0x56: return NACL_KEY_KP4;
399 case 0x57: return NACL_KEY_KP5;
400 case 0x58: return NACL_KEY_KP6;
401 case 0x59: return NACL_KEY_KP7;
402 case 0x5B: return NACL_KEY_KP8;
403 case 0x5C: return NACL_KEY_KP9;
404 case 0x4B: return NACL_KEY_KP_DIVIDE;
405 case 0x34: return NACL_KEY_KP_ENTER;
406 case 0x4C: return NACL_KEY_KP_ENTER;
407 case 0x51: return NACL_KEY_KP_EQUALS;
408 case 0x4E: return NACL_KEY_KP_MINUS;
409 case 0x43: return NACL_KEY_KP_MULTIPLY;
410 case 0x41: return NACL_KEY_KP_PERIOD;
411 case 0x45: return NACL_KEY_KP_PLUS;
412 case 0x3A: return NACL_KEY_LALT;
413 case 0x3B: return NACL_KEY_LCTRL;
414 case 0x7B: return NACL_KEY_LEFT;
415 case 0x21: return NACL_KEY_LEFTBRACKET;
416 case 0x37: return NACL_KEY_LMETA;
417 case 0x38: return NACL_KEY_LSHIFT;
418 case 0x1B: return NACL_KEY_MINUS;
419 case 0x47: return NACL_KEY_NUMLOCK;
420 case 0x79: return NACL_KEY_PAGEDOWN;
421 case 0x74: return NACL_KEY_PAGEUP;
422 case 0x71: return NACL_KEY_PAUSE;
423 case 0x2F: return NACL_KEY_PERIOD;
424 case 0x69: return NACL_KEY_PRINT;
425 case 0x27: return NACL_KEY_QUOTE;
426 case 0x3D: return NACL_KEY_RALT;
427 case 0x3E: return NACL_KEY_RCTRL;
428 case 0x24: return NACL_KEY_RETURN;
429 case 0x7C: return NACL_KEY_RIGHT;
430 case 0x1E: return NACL_KEY_RIGHTBRACKET;
431 case 0x36: return NACL_KEY_RMETA;
432 case 0x3C: return NACL_KEY_RSHIFT;
433 case 0x6B: return NACL_KEY_SCROLLOCK;
434 case 0x29: return NACL_KEY_SEMICOLON;
435 case 0x30: return NACL_KEY_TAB;
436 case 0x2C: return NACL_KEY_SLASH;
437 case 0x31: return NACL_KEY_SPACE;
438 case 0x7E: return NACL_KEY_UP;
440 return NACL_KEY_UNKNOWN;
443 int16_t VideoMap::HandleEvent(void *param) {
444 dprintf(("VideoMap::HandleEvent(%p, %p)\n", this, param));
445 uint16_t x;
446 uint16_t y;
447 uint16_t button;
448 int scancode;
449 const int rightMouseButtonMod = 4096;
450 VideoScopedGlobalLock video_lock;
451 if ((NULL == window_) || (NULL == untrusted_video_share_))
452 return 0;
454 // MacOS delivers its events through the NPAPI HandleEvent API.
455 // TODO: switch this to a carbon event handler
456 EventRecord* event = static_cast<EventRecord*>(param);
457 NaClMultimediaEvent nacl_event;
459 if (!event) {
460 return 0;
462 dprintf(("Event type %d\n", event->what));
464 // poll the mouse and see if it moved within our window
465 // TODO: only mouse events during updateEvt seem to
466 // TODO: provide good local coordinates.
467 // TODO: hmm... osEvt+18 works much better in FF3
468 // osEvt + 18 is NPAPIs mousemove event
469 const int mouseMoveBoundToEvent = osEvt + 18;
470 if (event->what == mouseMoveBoundToEvent) {
471 Point mouse_position;
472 Point global_mouse_position;
473 GetGlobalMouse(&global_mouse_position);
474 mouse_position = global_mouse_position;
475 if (NULL != window_) {
476 GlobalToLocalHelper(window_, &mouse_position);
477 if ((mouse_position.h >= 0) && (mouse_position.v >= 0) &&
478 (mouse_position.h < window_->width) &&
479 (mouse_position.v < window_->height)) {
480 uint16_t last_x, last_y;
481 x = static_cast<uint16_t>(mouse_position.h);
482 y = static_cast<uint16_t>(mouse_position.v);
483 GetMotion(&last_x, &last_y);
484 if ((x != last_x) ||
485 (y != last_y)) {
486 dprintf(("The mouse moved %d %d (event %d)\n",
487 (int)x, (int)y, event->what));
488 nacl_event.type = NACL_EVENT_MOUSE_MOTION;
489 nacl_event.motion.which = 0;
490 nacl_event.motion.state = GetButton();
491 nacl_event.motion.x = x;
492 nacl_event.motion.y = y;
493 GetRelativeMotion(x, y,
494 &nacl_event.motion.xrel, &nacl_event.motion.yrel);
495 SetMotion(x, y, 1);
496 EventQueuePut(&nacl_event);
502 //#define FIREFOX2
503 #if defined(FIREFOX2)
504 // Firefox2's keyDown keyUp events don't seem to report
505 // ctrl/alt/meta/shift keypresses, so we need to
506 // poll the keyboard CTRL, SHIFT, ALT keys
507 // Unfortunately, this is a global poll, which is bad
508 // because it can see these presses even without focus.
509 // TODO: look into getting a better solution for FF2
510 KeyMap keymap;
511 static uint32_t previous_keys[4] = {0, 0, 0, 0};
512 uint32_t *keys = reinterpret_cast<uint32_t*>(keymap);
513 int which_key;
514 int key_state;
515 GetKeys(keymap);
516 if (FindChangedKey(keys, previous_keys, &which_key, &key_state)) {
517 int nsym = MacKeyToNaCl(which_key);
518 bool doScan =
519 (nsym == NACL_KEY_LCTRL) ||
520 (nsym == NACL_KEY_LALT) ||
521 (nsym == NACL_KEY_LSHIFT);
522 if (doScan) {
523 nacl_event.type = key_state ? NACL_EVENT_KEY_DOWN : NACL_EVENT_KEY_UP;
524 nacl_event.key.which = 0;
525 nacl_event.key.state = key_state;
526 nacl_event.key.keysym.scancode = which_key;
527 SetKeyMod(nsym, kSet);
528 nacl_event.key.keysym.sym = nsym;
529 nacl_event.key.keysym.mod = vs->event_state_key_mod;
530 nacl_event.key.keysym.unicode = 0;
531 EventQueuePut(&nacl_event);
532 dprintf(("A special key has changed scancode: %d sym: %d state: %d\n",
533 which_key, nsym, key_state));
536 #endif
538 if ((event->what != 0) && (event->what != updateEvt))
539 dprintf(("an event happened %d\n", event->what));
540 switch (event->what) {
541 case (osEvt + 16): // NPAPI getFocusEvent:
542 nacl_event.type = NACL_EVENT_ACTIVE;
543 nacl_event.active.state = kSet;
544 EventQueuePut(&nacl_event);
545 dprintf(("Focus gained\n"));
546 break;
547 case (osEvt + 17): // NPAPI loseFocusEvent:
548 nacl_event.type = NACL_EVENT_ACTIVE;
549 nacl_event.active.state = kClear;
550 EventQueuePut(&nacl_event);
551 dprintf(("Focus lost\n"));
552 break;
553 case keyUp:
554 case keyDown:
555 int scancode = (event->message & keyCodeMask) >> 8;
556 int nsym = MacKeyToNaCl(scancode);
557 if (keyDown == event->what) {
558 nacl_event.type = NACL_EVENT_KEY_DOWN;
559 nacl_event.key.state = kSet;
560 } else {
561 nacl_event.type = NACL_EVENT_KEY_UP;
562 nacl_event.key.state = kClear;
564 nacl_event.key.which = 0;
565 nacl_event.key.keysym.scancode = scancode;
566 SetKeyMod(nsym, nacl_event.key.state);
567 nacl_event.key.keysym.sym = nsym;
568 nacl_event.key.keysym.mod = event_state_key_mod_;
569 nacl_event.key.keysym.unicode = 0;
570 EventQueuePut(&nacl_event);
571 dprintf(("A key press (scan: %d sym: %d state: %d)\n",
572 scancode, nsym, nacl_event.key.state));
573 break;
574 case mouseDown:
575 dprintf(("A mouse down happened\n"));
576 button = 1;
577 if (event->modifiers & rightMouseButtonMod) {
578 button = 3;
580 nacl_event.type = NACL_EVENT_MOUSE_BUTTON_DOWN;
581 nacl_event.button.which = 0;
582 nacl_event.button.button = button;
583 nacl_event.button.state = kSet;
584 GetMotion(&nacl_event.button.x, &nacl_event.button.y);
585 SetButton(button, kSet);
586 EventQueuePut(&nacl_event);
587 break;
588 case mouseUp:
589 dprintf(("A mouse up happened\n"));
590 button = 1;
591 if (event->modifiers & rightMouseButtonMod) {
592 button = 3;
594 nacl_event.type = NACL_EVENT_MOUSE_BUTTON_UP;
595 nacl_event.button.which = 0;
596 nacl_event.button.button = button;
597 nacl_event.button.state = kClear;
598 GetMotion(&nacl_event.button.x, &nacl_event.button.y);
599 SetButton(button, kClear);
600 EventQueuePut(&nacl_event);
601 break;
602 case nullEvent:
603 // Null events are set up by the browser to occur every ~16ms.
604 // We use this to redraw whenever an upcall has requested one.
605 if (request_redraw_ && (NULL != window_)) {
606 request_redraw_ = false;
607 // invalidate rect to generate an updateEvt
608 Invalidate();
610 break;
611 case updateEvt:
612 Redraw();
613 break;
614 default:
615 // we didn't handle this event...
616 return 0;
618 return 1;
621 void VideoMap::RedrawAsync(void *platform_parm) {
622 dprintf(("VideoMap::RedrawAsync(%p)\n", platform_parm));
623 if (NULL != platform_parm) {
624 if (!window_ || !window_->window || !untrusted_video_share_ ||
625 window_->clipRect.right <= window_->clipRect.left) {
626 return;
629 RgnHandle saved_clip = NewRgn();
630 if (!saved_clip) {
631 return;
633 GrafPtr saved_port;
634 Rect revealed_rect;
635 short saved_port_top;
636 short saved_port_left;
638 NP_Port* npport = static_cast<NP_Port*>(window_->window);
639 CGrafPtr our_port = npport->port;
640 GetPort(&saved_port);
641 SetPort(reinterpret_cast<GrafPtr>(our_port));
643 Rect port_rect;
644 GetPortBounds(our_port, &port_rect);
645 saved_port_top = port_rect.top;
646 saved_port_left = port_rect.left;
647 GetClip(saved_clip);
649 revealed_rect.top = window_->clipRect.top + npport->porty;
650 revealed_rect.left = window_->clipRect.left + npport->portx;
651 revealed_rect.bottom = window_->clipRect.bottom + npport->porty;
652 revealed_rect.right = window_->clipRect.right + npport->portx;
653 SetOrigin(npport->portx, npport->porty);
654 ClipRect(&revealed_rect);
656 Rect bounds;
657 // NB: the asymmetry in bounding box sizes is due to the fact that a pixmap
658 // appears not to contain its last row value. Hence we need one more
659 // in height than in width.
660 // TODO: get to the bottom of this
661 SetRect(&bounds, 0, 0, window_->width - 1, window_->height);
662 GWorldPtr gworld;
663 OSErr result = NewGWorld(&gworld, 32, &bounds, 0, 0, 0);
664 if (result == noErr) {
665 PixMapHandle pixmap;
666 pixmap = GetGWorldPixMap(gworld);
667 if (LockPixels(pixmap)) {
668 // TODO: Find a way to avoid bitmap copy and conversion
669 // (ARGB to RGBA) operations.
670 uint32_t* dst = reinterpret_cast<uint32_t*>(GetPixBaseAddr(pixmap));
671 uint32_t* src = reinterpret_cast<uint32_t*>
672 (&untrusted_video_share_->video_pixels[0]);
673 for (unsigned int n = 0; n < window_->width * window_->height; ++n) {
674 // Convert from ARGB to RGBA.
675 uint32_t color = *src++;
676 color = (0x000000ff & (color >> 24) |
677 (0x0000ff00 & (color >> 8)) |
678 (0x00ff0000 & (color << 8)) |
679 (0xff000000 & (color << 24)));
680 *dst++ = color;
682 UnlockPixels(pixmap);
684 LockPortBits(gworld);
685 CopyBits(GetPortBitMapForCopyBits(gworld),
686 GetPortBitMapForCopyBits(our_port),
687 &bounds,
688 &bounds,
689 srcCopy,
690 NULL);
691 UnlockPortBits(gworld);
692 DisposeGWorld(gworld);
695 SetOrigin(saved_port_left, saved_port_top);
696 SetClip(saved_clip);
697 SetPort(saved_port);
698 DisposeRgn(saved_clip);
702 void VideoMap::Redraw() {
703 int i = 0;
704 // non-null to tell it we're from npapi thread
705 // TODO: figure out a thread safe way to render on mac
706 this->RedrawAsync(&i);
708 #endif // NACL_OSX
710 #if NACL_WINDOWS
711 static int WinKeyToNacl(int vk) {
712 switch(vk) {
713 case VK_ADD: return NACL_KEY_KP_PLUS;
714 case VK_BACK: return NACL_KEY_BACKSPACE;
715 case VK_CAPITAL: return NACL_KEY_CAPSLOCK;
716 case VK_CLEAR: return NACL_KEY_CLEAR;
717 case VK_DECIMAL: return NACL_KEY_KP_PERIOD;
718 case VK_DELETE: return NACL_KEY_DELETE;
719 case VK_DIVIDE: return NACL_KEY_KP_DIVIDE;
720 case VK_DOWN: return NACL_KEY_DOWN;
721 case VK_END: return NACL_KEY_END;
722 case VK_ESCAPE: return NACL_KEY_ESCAPE;
723 case VK_F1: return NACL_KEY_F1;
724 case VK_F2: return NACL_KEY_F2;
725 case VK_F3: return NACL_KEY_F3;
726 case VK_F4: return NACL_KEY_F4;
727 case VK_F5: return NACL_KEY_F5;
728 case VK_F6: return NACL_KEY_F6;
729 case VK_F7: return NACL_KEY_F7;
730 case VK_F8: return NACL_KEY_F8;
731 case VK_F9: return NACL_KEY_F9;
732 case VK_F10: return NACL_KEY_F10;
733 case VK_F11: return NACL_KEY_F11;
734 case VK_F12: return NACL_KEY_F12;
735 case VK_F13: return NACL_KEY_F13;
736 case VK_F14: return NACL_KEY_F14;
737 case VK_F15: return NACL_KEY_F15;
738 case VK_HELP: return NACL_KEY_HELP;
739 case VK_HOME: return NACL_KEY_HOME;
740 case VK_INSERT: return NACL_KEY_INSERT;
741 case VK_LCONTROL: return NACL_KEY_LCTRL;
742 case VK_LEFT: return NACL_KEY_LEFT;
743 case VK_LMENU: return NACL_KEY_LALT;
744 case VK_LSHIFT: return NACL_KEY_LSHIFT;
745 case VK_MULTIPLY: return NACL_KEY_KP_MULTIPLY;
746 case VK_NEXT: return NACL_KEY_PAGEDOWN;
747 case VK_NUMLOCK: return NACL_KEY_NUMLOCK;
748 case VK_NUMPAD0: return NACL_KEY_KP0;
749 case VK_NUMPAD1: return NACL_KEY_KP1;
750 case VK_NUMPAD2: return NACL_KEY_KP2;
751 case VK_NUMPAD3: return NACL_KEY_KP3;
752 case VK_NUMPAD4: return NACL_KEY_KP4;
753 case VK_NUMPAD5: return NACL_KEY_KP5;
754 case VK_NUMPAD6: return NACL_KEY_KP6;
755 case VK_NUMPAD7: return NACL_KEY_KP7;
756 case VK_NUMPAD8: return NACL_KEY_KP8;
757 case VK_NUMPAD9: return NACL_KEY_KP9;
758 case VK_OEM_1: return NACL_KEY_SEMICOLON;
759 case VK_OEM_2: return NACL_KEY_SLASH;
760 case VK_OEM_3: return NACL_KEY_BACKQUOTE;
761 case VK_OEM_4: return NACL_KEY_LEFTBRACKET;
762 case VK_OEM_5: return NACL_KEY_BACKSLASH;
763 case VK_OEM_6: return NACL_KEY_RIGHTBRACKET;
764 case VK_OEM_7: return NACL_KEY_QUOTE;
765 case VK_OEM_8: return NACL_KEY_BACKQUOTE;
766 case VK_OEM_COMMA: return NACL_KEY_COMMA;
767 case VK_OEM_MINUS: return NACL_KEY_MINUS;
768 case VK_OEM_PERIOD: return NACL_KEY_PERIOD;
769 case VK_OEM_PLUS: return NACL_KEY_EQUALS;
770 case VK_OEM_102: return NACL_KEY_LESS;
771 case VK_PAUSE: return NACL_KEY_PAUSE;
772 case VK_PRIOR: return NACL_KEY_PAGEUP;
773 case VK_RCONTROL: return NACL_KEY_RCTRL;
774 case VK_RETURN: return NACL_KEY_RETURN;
775 case VK_RIGHT: return NACL_KEY_RIGHT;
776 case VK_RMENU: return NACL_KEY_RALT;
777 case VK_RSHIFT: return NACL_KEY_RSHIFT;
778 case VK_SCROLL: return NACL_KEY_SCROLLOCK;
779 case VK_SPACE: return NACL_KEY_SPACE;
780 case VK_SUBTRACT: return NACL_KEY_KP_MINUS;
781 case VK_TAB: return NACL_KEY_TAB;
782 case VK_UP: return NACL_KEY_UP;
783 case '0': return NACL_KEY_0;
784 case '1': return NACL_KEY_1;
785 case '2': return NACL_KEY_2;
786 case '3': return NACL_KEY_3;
787 case '4': return NACL_KEY_4;
788 case '5': return NACL_KEY_5;
789 case '6': return NACL_KEY_6;
790 case '7': return NACL_KEY_7;
791 case '8': return NACL_KEY_8;
792 case '9': return NACL_KEY_9;
793 case 'A': return NACL_KEY_a;
794 case 'B': return NACL_KEY_b;
795 case 'C': return NACL_KEY_c;
796 case 'D': return NACL_KEY_d;
797 case 'E': return NACL_KEY_e;
798 case 'F': return NACL_KEY_f;
799 case 'G': return NACL_KEY_g;
800 case 'H': return NACL_KEY_h;
801 case 'I': return NACL_KEY_i;
802 case 'J': return NACL_KEY_j;
803 case 'K': return NACL_KEY_k;
804 case 'L': return NACL_KEY_l;
805 case 'M': return NACL_KEY_m;
806 case 'N': return NACL_KEY_n;
807 case 'O': return NACL_KEY_o;
808 case 'P': return NACL_KEY_p;
809 case 'Q': return NACL_KEY_q;
810 case 'R': return NACL_KEY_r;
811 case 'S': return NACL_KEY_s;
812 case 'T': return NACL_KEY_t;
813 case 'U': return NACL_KEY_u;
814 case 'V': return NACL_KEY_v;
815 case 'W': return NACL_KEY_w;
816 case 'X': return NACL_KEY_x;
817 case 'Y': return NACL_KEY_y;
818 case 'Z': return NACL_KEY_z;
820 return NACL_KEY_UNKNOWN;
823 LRESULT CALLBACK VideoMap::WindowProcedure(HWND hwnd,
824 UINT msg,
825 WPARAM wparam,
826 LPARAM lparam) {
827 dprintf(("VideoMap::VideoWindowProcedure()\n"));
828 VideoScopedGlobalLock video_lock;
829 uint16_t x;
830 uint16_t y;
831 uint8_t button;
832 union NaClMultimediaEvent nacl_event;
833 int nsym;
834 const int KEY_REPEAT_BIT = (1 << 30);
835 const int KEY_EXTENDED_BIT = (1 << 24);
836 VideoMap *video = reinterpret_cast<VideoMap*>(
837 GetWindowLong(hwnd, GWL_USERDATA));
838 if ((NULL == video) || (NULL == video->untrusted_video_share_)) {
839 return DefWindowProc(hwnd, msg, wparam, lparam);
842 switch (msg) {
843 case WM_ERASEBKGND:
844 // return non-zero value to indicate no further erasing is required
845 return 1;
846 case WM_PAINT: {
847 PAINTSTRUCT ps;
848 HDC hdc = BeginPaint(hwnd, &ps);
849 if (NULL != video->untrusted_video_share_) {
850 uint32_t* pixel_bits = reinterpret_cast<uint32_t*>
851 (&video->untrusted_video_share_->video_pixels[0]);
852 BITMAPINFO bmp_info;
853 bmp_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
854 bmp_info.bmiHeader.biWidth = video->window_->width;
855 bmp_info.bmiHeader.biHeight =
856 -static_cast<LONG>(video->window_->height); // top-down
857 bmp_info.bmiHeader.biPlanes = 1;
858 bmp_info.bmiHeader.biBitCount = 32;
859 bmp_info.bmiHeader.biCompression = BI_RGB;
860 bmp_info.bmiHeader.biSizeImage = 0;
861 bmp_info.bmiHeader.biXPelsPerMeter = 0;
862 bmp_info.bmiHeader.biYPelsPerMeter = 0;
863 bmp_info.bmiHeader.biClrUsed = 0;
864 bmp_info.bmiHeader.biClrImportant = 0;
865 SetDIBitsToDevice(hdc,
868 video->window_->width,
869 video->window_->height,
873 video->window_->height,
874 pixel_bits,
875 &bmp_info,
876 DIB_RGB_COLORS);
878 EndPaint(hwnd, &ps);
879 break;
881 case WM_MOUSEMOVE:
882 x = static_cast<uint16_t>(LOWORD(lparam));
883 y = static_cast<uint16_t>(HIWORD(lparam));
884 nacl_event.type = NACL_EVENT_MOUSE_MOTION;
885 nacl_event.motion.which = 0;
886 nacl_event.motion.state = video->GetButton();
887 nacl_event.motion.x = x;
888 nacl_event.motion.y = y;
889 video->GetRelativeMotion(x, y,
890 &nacl_event.motion.xrel, &nacl_event.motion.yrel);
891 video->SetMotion(x, y, 1);
892 video->EventQueuePut(&nacl_event);
893 break;
894 case WM_LBUTTONDOWN:
895 case WM_LBUTTONDBLCLK:
896 case WM_RBUTTONDOWN:
897 case WM_RBUTTONDBLCLK:
898 case WM_MBUTTONDOWN:
899 case WM_MBUTTONDBLCLK:
900 SetFocus(hwnd);
901 switch(msg) {
902 case WM_LBUTTONDOWN:
903 case WM_LBUTTONDBLCLK:
904 button = 1;
905 break;
906 case WM_MBUTTONDOWN:
907 case WM_MBUTTONDBLCLK:
908 button = 2;
909 break;
910 case WM_RBUTTONDOWN:
911 case WM_RBUTTONDBLCLK:
912 button = 3;
913 break;
914 default:
915 button = 4;
916 break;
918 nacl_event.type = NACL_EVENT_MOUSE_BUTTON_DOWN;
919 nacl_event.button.which = 0;
920 nacl_event.button.button = button;
921 nacl_event.button.state = kSet;
922 video->GetMotion(&nacl_event.button.x, &nacl_event.button.y);
923 video->SetButton(button, 1);
924 video->EventQueuePut(&nacl_event);
925 break;
926 case WM_LBUTTONUP:
927 case WM_RBUTTONUP:
928 case WM_MBUTTONUP:
929 switch(msg) {
930 case WM_LBUTTONUP:
931 button = 1;
932 break;
933 case WM_MBUTTONUP:
934 button = 2;
935 break;
936 case WM_RBUTTONUP:
937 button = 3;
938 break;
939 default:
940 button = 4;
941 break;
943 nacl_event.type = NACL_EVENT_MOUSE_BUTTON_UP;
944 nacl_event.button.which = 0;
945 nacl_event.button.button = button;
946 nacl_event.button.state = kClear;
947 video->GetMotion(&nacl_event.button.x, &nacl_event.button.y);
948 video->SetButton(button, 0);
949 video->EventQueuePut(&nacl_event);
950 break;
951 case WM_KEYUP:
952 case WM_KEYDOWN:
953 if (wparam == VK_CONTROL) {
954 if (lparam & KEY_EXTENDED_BIT) {
955 wparam = VK_RCONTROL;
956 } else {
957 wparam = VK_LCONTROL;
960 if (msg == WM_KEYDOWN) {
961 if (lparam & KEY_REPEAT_BIT) return 0; // disable repeat
962 nacl_event.type = NACL_EVENT_KEY_DOWN;
963 nacl_event.key.state = kSet;
964 } else {
965 nacl_event.type = NACL_EVENT_KEY_UP;
966 nacl_event.key.state = kClear;
968 nacl_event.key.which = 0;
969 nacl_event.key.keysym.scancode = HIWORD(lparam) & 0x1FF;
970 nsym = WinKeyToNacl(wparam);
971 nacl_event.key.keysym.sym = nsym;
972 nacl_event.key.keysym.mod = video->event_state_key_mod_;
973 nacl_event.key.keysym.unicode = 0;
974 video->EventQueuePut(&nacl_event);
975 break;
977 default:
978 break;
980 return DefWindowProc(hwnd, msg, wparam, lparam);
983 void VideoMap::RedrawAsync(void *platform_parm) {
984 dprintf(("VideoMap::RedrawAsync()\n"));
985 HWND hwnd = static_cast<HWND>(window_->window);
986 HDC hdc = GetDC(hwnd);
987 VideoMap *video = reinterpret_cast<VideoMap*>(
988 GetWindowLong(hwnd, GWL_USERDATA));
989 if ((NULL == video) || (NULL == video->untrusted_video_share_)) {
990 return;
993 uint32_t* pixel_bits = reinterpret_cast<uint32_t*>
994 (&video->untrusted_video_share_->video_pixels[0]);
995 BITMAPINFO bmp_info;
996 bmp_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
997 bmp_info.bmiHeader.biWidth = video->window_->width;
998 bmp_info.bmiHeader.biHeight =
999 -static_cast<LONG>(video->window_->height); // top-down
1000 bmp_info.bmiHeader.biPlanes = 1;
1001 bmp_info.bmiHeader.biBitCount = 32;
1002 bmp_info.bmiHeader.biCompression = BI_RGB;
1003 bmp_info.bmiHeader.biSizeImage = 0;
1004 bmp_info.bmiHeader.biXPelsPerMeter = 0;
1005 bmp_info.bmiHeader.biYPelsPerMeter = 0;
1006 bmp_info.bmiHeader.biClrUsed = 0;
1007 bmp_info.bmiHeader.biClrImportant = 0;
1008 SetDIBitsToDevice(hdc,
1011 video->window_->width,
1012 video->window_->height,
1016 video->window_->height,
1017 pixel_bits,
1018 &bmp_info,
1019 DIB_RGB_COLORS);
1020 ReleaseDC(hwnd, hdc);
1023 void VideoMap::Redraw() {
1024 this->RedrawAsync(NULL);
1026 #endif // NACL_WINDOWS
1028 // assumes event is already normalized to NaClMultimediaEvent
1029 int VideoMap::EventQueuePut(union NaClMultimediaEvent *event) {
1030 int windex;
1031 if (EventQueueIsFull()) {
1032 return -1;
1034 // apply mask to untrusted event_write_index
1035 windex = (untrusted_video_share_->u.h.event_write_index &
1036 (NACL_EVENT_RING_BUFFER_SIZE - 1));
1037 untrusted_video_share_->u.h.event_queue[windex] = *event;
1038 windex = (windex + 1) & (NACL_EVENT_RING_BUFFER_SIZE - 1);
1039 untrusted_video_share_->u.h.event_write_index = windex;
1040 return 0;
1043 // tests event queue to see if it is full
1044 int VideoMap::EventQueueIsFull() {
1045 int windex;
1046 windex = untrusted_video_share_->u.h.event_write_index;
1047 windex = (windex + 1) & (NACL_EVENT_RING_BUFFER_SIZE - 1);
1048 return (windex == untrusted_video_share_->u.h.event_read_index);
1051 // Gets the last recorded mouse motion (position)
1052 // note: outputs normalized for NaCl events
1053 void VideoMap::GetMotion(uint16_t *x, uint16_t *y) {
1054 *x = event_state_motion_last_x_;
1055 *y = event_state_motion_last_y_;
1058 // Sets the mouse motion (position)
1059 // note: the inputs must be normalized for NaCl events
1060 void VideoMap::SetMotion(uint16_t x, uint16_t y, int last_valid) {
1061 event_state_motion_last_x_ = x;
1062 event_state_motion_last_y_ = y;
1063 event_state_motion_last_valid_ = last_valid;
1066 // Gets the relative mouse motion and updates position
1067 // note: the inputs must be normalized for NaCl events
1068 void VideoMap::GetRelativeMotion(uint16_t x, uint16_t y,
1069 int16_t *rel_x, int16_t *rel_y) {
1070 if (event_state_motion_last_valid_) {
1071 *rel_x = x - event_state_motion_last_x_;
1072 *rel_y = y - event_state_motion_last_y_;
1073 event_state_motion_last_x_ = x;
1074 event_state_motion_last_y_ = y;
1075 event_state_motion_last_valid_ = 1;
1076 } else {
1077 *rel_x = 0;
1078 *rel_y = 0;
1082 // Gets the current mouse button state
1083 // note: output is normalized for NaCl events
1084 int VideoMap::GetButton() {
1085 return event_state_button_;
1088 // Modifies the mouse button state
1089 // note: input must be normalized for NaCl events
1090 void VideoMap::SetButton(int button, int state) {
1091 if (kClear == state) {
1092 event_state_button_ &= ~(1 << button);
1093 } else {
1094 event_state_button_ |= (1 << button);
1098 // Modifies the keyboard modifier state (shift, ctrl, alt, etc)
1099 // note: input must be normalized for NaCl events
1100 void VideoMap::SetKeyMod(int nsym, int state) {
1101 int mask = 0;
1102 switch (nsym) {
1103 // fill in with actual key modifiers
1104 case NACL_KEY_LCTRL: mask = NACL_KEYMOD_LCTRL; break;
1105 case NACL_KEY_RCTRL: mask = NACL_KEYMOD_RCTRL; break;
1106 case NACL_KEY_LSHIFT: mask = NACL_KEYMOD_LSHIFT; break;
1107 case NACL_KEY_RSHIFT: mask = NACL_KEYMOD_RSHIFT; break;
1108 case NACL_KEY_LALT: mask = NACL_KEYMOD_LALT; break;
1109 case NACL_KEY_RALT: mask = NACL_KEYMOD_RALT; break;
1110 default: mask = NACL_KEYMOD_NONE; break;
1112 if (kClear == state) {
1113 event_state_key_mod_ &= ~(mask);
1114 } else {
1115 event_state_key_mod_ |= (mask);
1119 // Gets the current keyboard modifier state
1120 // note: output is normalized for NaCl Events
1121 int VideoMap::GetKeyMod() {
1122 return event_state_key_mod_;
1125 // handle video map specific NPAPI SetWindow
1126 NPError VideoMap::SetWindow(NPWindow *window) {
1127 dprintf(("VideoMap::SetWindow(%p)\n", window));
1128 // first check window->width & window->height...
1129 if ((NULL == window_) && (window->window) &&
1130 (window->width > 0) && (window->height > 0)) {
1131 NPVariant variant;
1132 int width, height;
1133 // then check width & height properties... (needed for Safari)
1134 nacl_srpc::Plugin *plugin = srpc_plugin_->plugin();
1135 if (nacl_srpc::Plugin::GetProperty(plugin,
1136 nacl_srpc::Plugin::kWidthIdent, &variant) == false) {
1137 return NPERR_NO_ERROR;
1139 if (!nacl_srpc::NPVariantToScalar(&variant, &width)) {
1140 return NPERR_NO_ERROR;
1142 if (nacl_srpc::Plugin::GetProperty(plugin,
1143 nacl_srpc::Plugin::kWidthIdent, &variant) == false) {
1144 return NPERR_NO_ERROR;
1146 if (!nacl_srpc::NPVariantToScalar(&variant, &height)) {
1147 return NPERR_NO_ERROR;
1149 if ((width > 0) && (height > 0)) {
1150 dprintf(("VideoMap::SetWindow calling Initialize(%p)\n", window));
1151 // don't allow invalid window sizes
1152 if ((width < kNaClVideoMinWindowSize) ||
1153 (width > kNaClVideoMaxWindowSize) ||
1154 (height < kNaClVideoMinWindowSize) ||
1155 (height > kNaClVideoMaxWindowSize)) {
1156 dprintf(("VideoMap::SetWindow got invalid window size (%d, %d)\n",
1157 width, height));
1158 return NPERR_GENERIC_ERROR;
1160 if (-1 == InitializeSharedMemory(window)) {
1161 return NPERR_GENERIC_ERROR;
1163 #if NACL_WINDOWS
1164 dprintf(("VideoMap::SetWindow adding Windows event listener\n"));
1165 HWND hwnd = static_cast<HWND>(window->window);
1166 original_window_procedure_ = SubclassWindow(hwnd,
1167 reinterpret_cast<WNDPROC>(VideoMap::WindowProcedure));
1168 SetWindowLong(hwnd, GWL_USERDATA, reinterpret_cast<LONG>(this));
1169 #endif // NACL_WINDOWS
1170 #if NACL_LINUX && defined(MOZ_X11)
1171 // open X11 display, add X11 event listener
1172 dprintf(("VideoMap::SetWindow adding X11 display\n"));
1173 set_platform_specific(XOpenDisplay(NULL));
1174 dprintf(("VideoMap::SetWindow adding X11 event listener\n"));
1175 Window xwin = reinterpret_cast<Window>(window->window);
1176 NPSetWindowCallbackStruct* npsw =
1177 reinterpret_cast<NPSetWindowCallbackStruct*>(window->ws_info);
1178 Widget widget = XtWindowToWidget(npsw->display, xwin);
1179 if (widget) {
1180 long event_mask = StructureNotifyMask | KeyPressMask |
1181 KeyReleaseMask | ExposureMask | PointerMotionMask |
1182 ButtonPressMask | ButtonReleaseMask |
1183 LeaveWindowMask | EnterWindowMask;
1184 XSelectInput(npsw->display, xwin, event_mask);
1185 XtAddEventHandler(widget, event_mask, False,
1186 reinterpret_cast<XtEventHandler>(&VideoMap::XEventHandler), this);
1188 #endif // NACL_LINUX && defined(MOZ_X11)
1191 return NPERR_NO_ERROR;
1194 #if !NACL_OSX
1195 int16_t VideoMap::HandleEvent(void *param) {
1196 dprintf(("VideoMap::HandleEvent(%p, %p)\n", this, param));
1197 return 0;
1199 #endif // !NACL_OSX
1201 nacl_srpc::SharedMemory* VideoMap::VideoSharedMemorySetup() {
1202 dprintf(("VideoMap::VideoSharedMemorySetup(%p, %p, %p)\n",
1203 this, video_shared_memory_, video_handle_));
1204 if (NULL == video_shared_memory_) {
1205 if (kInvalidHtpHandle == video_handle_) {
1206 // The plugin has not set up a shared memory object for the display.
1207 // This indicates either the plugin was set to be invisible, or had
1208 // an error when SetWindow was invoked. In either case, it is an
1209 // error to get the shared memory object.
1210 dprintf(("VideoMap::VideoSharedMemorySetup returning NULL\n"));
1211 return NULL;
1212 } else {
1213 // Create a SharedMemory object that the script can get at.
1214 // SRPC_Plugin initially takes exclusive ownership
1215 dprintf(("VideoMap::VideoSharedMemorySetup srpc_plugin_ (%p)\n",
1216 srpc_plugin_));
1217 dprintf(("VideoMap::VideoSharedMemorySetup plugin (%p)\n",
1218 srpc_plugin_->plugin()));
1219 video_shared_memory_ = nacl_srpc::SharedMemory::
1220 New(srpc_plugin_->plugin(), video_handle_);
1223 dprintf(("VideoMap::VideoSharedMemorySetup returns %p\n",
1224 video_shared_memory_));
1225 // Anyone requesting video_shared_memory_ begins sharing ownership.
1226 NPN_RetainObject(video_shared_memory_);
1227 return video_shared_memory_;
1230 // Note: Graphics functionality similar to npapi_bridge.
1231 // TODO: Make only one copy of the graphics code.
1232 void VideoMap::Invalidate() {
1233 if (window_ && window_->window) {
1234 #if NACL_WINDOWS
1235 HWND hwnd = static_cast<HWND>(window_->window);
1236 RECT rect;
1237 rect.left = 0;
1238 rect.top = 0;
1239 rect.right = window_->width;
1240 rect.bottom = window_->height;
1241 ::InvalidateRect(hwnd, &rect, FALSE);
1242 #endif
1243 #if NACL_OSX
1244 NPRect rect;
1245 rect.left = 0;
1246 rect.top = 0;
1247 rect.right = window_->width;
1248 rect.bottom = window_->height;
1249 ::NPN_InvalidateRect(srpc_plugin_->GetNPP(), const_cast<NPRect*>(&rect));
1250 #endif
1254 VideoCallbackData* VideoMap::InitCallbackData(NaClHandle h,
1255 SRPC_Plugin *p,
1256 nacl_srpc::MultimediaSocket *msp) {
1257 // initialize with refcount set to 2
1258 video_callback_data_ = new(std::nothrow) VideoCallbackData(h, p, 2, msp);
1259 return video_callback_data_;
1262 // static method
1263 VideoCallbackData* VideoMap::ReleaseCallbackData(VideoCallbackData* vcd) {
1264 dprintf(("VideoMap::ReleaseCallbackData (%p), refcount was %d\n",
1265 vcd, vcd->refcount));
1266 --vcd->refcount;
1267 if (0 == vcd->refcount) {
1268 delete vcd;
1269 return NULL;
1271 return vcd;
1274 // static method
1275 // normally, the Release version is preferred, which reference counts.
1276 // in some cases we may just need to forcefully delete the callback data,
1277 // and ignore the refcount.
1278 void VideoMap::ForceDeleteCallbackData(VideoCallbackData* vcd) {
1279 dprintf(("VideoMap::ForceDeleteCallbackData (%p)\n", vcd));
1280 delete vcd;
1283 // opens shared memory map into untrusted space
1284 // returns 0 success, -1 failure
1285 int VideoMap::InitializeSharedMemory(NPWindow *window) {
1286 int width = window->width;
1287 int height = window->height;
1288 const int bytes_per_pixel = 4;
1289 int image_size = width * height * bytes_per_pixel;
1290 int vps_size = sizeof(struct NaClVideoShare) + image_size;
1292 dprintf(("VideoMap::Initialize(%p) this: %p\n", window, this));
1293 dprintf(("VideoMap::Initialize width, height: %d, %d\n", width, height));
1294 if (NULL == window_) {
1295 // verify event struct size
1296 if (sizeof(union NaClMultimediaEvent) >
1297 sizeof(struct NaClMultimediaPadEvent)) {
1298 dprintf(("VideoMap::Initialize event struct size failed\n"));
1299 return -1;
1301 // verify header size
1302 if ((NACL_VIDEO_SHARE_HEADER_SIZE + NACL_VIDEO_SHARE_PIXEL_PAD) !=
1303 sizeof(struct NaClVideoShare)) {
1304 dprintf(("VideoMap::Initialize video share size failed\n"));
1305 return -1;
1307 dprintf(("VideoMap::Initialize assigning window to this(%p)\n", this));
1308 // map video & event shared memory structure between trusted & untrusted
1309 window_ = window;
1310 vps_size = NaClRoundAllocPage(vps_size);
1311 video_size_ = vps_size;
1312 video_handle_ = CreateShmDesc(CreateMemoryObject(video_size_),
1313 video_size_);
1314 if (kInvalidHtpHandle == video_handle_) {
1315 video_size_ = 0;
1316 return -1;
1318 dprintf(("VideoMap::Initialize about to Map...\n"));
1319 untrusted_video_share_ = (NaClVideoShare *)Map(NULL,
1320 video_size_,
1321 kProtRead | kProtWrite,
1322 kMapShared,
1323 video_handle_,
1325 if (kMapFailed == untrusted_video_share_) {
1326 untrusted_video_share_ = NULL;
1327 nacl::Close(video_handle_);
1328 untrusted_video_share_ = NULL;
1329 video_size_ = 0;
1330 window_ = NULL;
1331 return -1;
1334 // clear entire untrusted memory window
1335 memset(untrusted_video_share_, 0, video_size_);
1337 // pass some information to untrusted code via the shared memory window
1338 // note: everything untrusted_video_share_ points to is untrusted, and could
1339 // be maliciously modified at any time.
1340 untrusted_video_share_->u.h.map_size = video_size_;
1341 untrusted_video_share_->u.h.video_width = width;
1342 untrusted_video_share_->u.h.video_height = height;
1343 untrusted_video_share_->u.h.video_size = image_size;
1345 // explicitly init states
1346 event_state_button_ = kClear;
1347 event_state_key_mod_ = kClear;
1348 event_state_motion_last_x_ = 0;
1349 event_state_motion_last_y_ = 0;
1350 event_state_motion_last_valid_ = 0;
1352 return 0;
1355 // this is the draw method the upcall thread should invoke
1356 void VideoMap::RequestRedraw() {
1357 if (!IsEnabled()) {
1358 return;
1360 // TODO: based on video_update_ member variable,
1361 // either render from the paint/expose event, or asynchronously
1362 // (at the moment, MacOSX always renders from paint event, but the
1363 // other platforms always render asynchronously)
1364 #if NACL_OSX
1365 request_redraw_ = true;
1366 #else
1367 RedrawAsync(platform_specific());
1368 #endif
1371 VideoMap::VideoMap(SRPC_Plugin *srpc_plugin) :
1372 event_state_button_(kClear),
1373 event_state_key_mod_(kClear),
1374 event_state_motion_last_x_(0),
1375 event_state_motion_last_y_(0),
1376 event_state_motion_last_valid_(0),
1377 platform_specific_(NULL),
1378 request_redraw_(false),
1379 untrusted_video_share_(NULL),
1380 video_handle_(kInvalidHtpHandle),
1381 video_size_(0),
1382 video_enabled_(false),
1383 video_callback_data_(NULL),
1384 video_shared_memory_(NULL),
1385 video_update_mode_(0) {
1386 srpc_plugin_ = srpc_plugin;
1387 window_ = NULL;
1388 // retrieve update property from plugin
1389 if (NULL != srpc_plugin_) {
1390 nacl_srpc::Plugin *plugin = srpc_plugin_->plugin();
1391 if (NULL != plugin) {
1392 NPVariant variant;
1393 nacl_srpc::Plugin::GetProperty(plugin,
1394 nacl_srpc::Plugin::kVideoUpdateModeIdent, &variant);
1395 if (!nacl_srpc::NPVariantToScalar(&variant, &video_update_mode_)) {
1396 // BUG: unhandled type error in the constructor.
1397 // TODO: Break the constructor to handle errors.
1403 VideoMap::~VideoMap() {
1404 Disable();
1405 dprintf(("VideoMap::~VideoMap() releasing video_callback_data_ (%p)\n",
1406 video_callback_data_));
1407 if (NULL != video_callback_data_) {
1408 video_callback_data_->srpc_plugin = NULL;
1409 video_callback_data_ = ReleaseCallbackData(video_callback_data_);
1411 if (NULL != platform_specific()) {
1412 #if NACL_LINUX
1413 XCloseDisplay(static_cast<Display *>(platform_specific()));
1414 #endif
1415 set_platform_specific(NULL);
1417 #if NACL_WINDOWS
1418 if (window_ && window_->window) {
1419 SubclassWindow(reinterpret_cast<HWND>(window_->window),
1420 original_window_procedure_);
1422 #endif // NACL_WINDOWS
1425 } // namespace nacl