1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
|
{{+bindTo:partials.standard_nacl_article}}
<section id="view-change-focus-and-input-events">
<span id="view-focus-input-events"></span><h1 id="view-change-focus-and-input-events"><span id="view-focus-input-events"></span>View Change, Focus, and Input Events</h1>
<div class="contents local" id="contents" style="display: none">
<ul class="small-gap">
<li><a class="reference internal" href="#overview" id="id2">Overview</a></li>
<li><p class="first"><a class="reference internal" href="#handling-browser-events" id="id3">Handling browser events</a></p>
<ul class="small-gap">
<li><a class="reference internal" href="#didchangeview" id="id4">DidChangeView()</a></li>
<li><a class="reference internal" href="#didchangefocus" id="id5">DidChangeFocus()</a></li>
</ul>
</li>
<li><p class="first"><a class="reference internal" href="#handling-input-events" id="id6">Handling input events</a></p>
<ul class="small-gap">
<li><a class="reference internal" href="#registering-a-module-to-accept-input-events" id="id7">Registering a module to accept input events</a></li>
<li><a class="reference internal" href="#determining-and-branching-on-event-types" id="id8">Determining and branching on event types</a></li>
<li><a class="reference internal" href="#threading-and-blocking" id="id9">Threading and blocking</a></li>
</ul>
</li>
</ul>
</div><p>This section describes view change, focus, and input event handling for a
Native Client module. The section assumes you are familiar with the
material presented in the <a class="reference internal" href="/native-client/overview.html"><em>Technical Overview</em></a>.</p>
<p>There are two examples used in this section to illustrate basic
programming techniques. The <code>input_events</code> example is used to
illustrate how your module can react to keyboard and mouse input
event. The <code>mouse_lock</code> example is used to illustrate how your module
can react to view change events. You can find these examples in the
<code>/pepper_<version>/examples/api/input_event</code> and
<code>/pepper_<version>/examples/api/mouse_lock</code> directories in the Native Client
SDK. There is also the ppapi_simple library that can be used to to implement
most of the boiler plate. The <code>pi_generator</code> example in
<code>/pepper_<version>/examples/demo/pi_generator</code> uses ppapi_simple to manage
view change events and 2D graphics.</p>
<h2 id="overview">Overview</h2>
<p>When a user interacts with the web page using a keyboard, mouse or some other
input device, the browser generates input events. In a traditional web
application, these input events are passed to and handled in JavaScript,
typically through event listeners and event handlers. In a Native Client
application, user interaction with an instance of a module (e.g., clicking
inside the rectangle managed by a module) also generates input events, which
are passed to the module. The browser also passes view change and focus events
that affect a module’s instance to the module. Native Client modules can
override certain functions in the <a class="reference external" href="/native-client/pepper_stable/cpp/classpp_1_1_instance">pp::Instance</a> class to handle input
and browser events. These functions are listed in the table below:</p>
<table border="1" class="docutils">
<colgroup>
</colgroup>
<thead valign="bottom">
<tr class="row-odd"><th class="head">Function</th>
<th class="head">Use</th>
</tr>
</thead>
<tbody valign="top">
<tr class="row-even"><td><dl class="first last docutils">
<dt><code>DidChangeView</code></dt>
<dd>Called when the position, size, or
clip rectangle of the module’s
instance in the browser has
changed. This event also occurs
when the browser window is resized
or the mouse wheel is scrolled.</dd>
</dl>
</td>
<td>An implementation of this function might
check the size of the module instance’s
rectangle has changed and reallocate the
graphcs context when a different size is
received.</td>
</tr>
<tr class="row-odd"><td><dl class="first last docutils">
<dt><code>DidChangeFocus</code></dt>
<dd>Called when the module’s instance
in the browser has gone in or out
of focus (usually by clicking
inside or outside the module
instance). Having focus means that
keyboard events will be sent to the
module instance. An instance’s
default condition is that it does
not have focus.</dd>
</dl>
</td>
<td>An implementation of this function might
start or stop an animation or a blinking
cursor.</td>
</tr>
<tr class="row-even"><td><dl class="first last docutils">
<dt><code>HandleDocumentLoad</code></dt>
<dd><code>pp::Instance::Init()</code> for a
full-frame module instance that was
instantiated based on the MIME
type of a DOMWindow navigation.
This situation only applies to
modules that are pre-registered to
handle certain MIME types. If you
haven’t specifically registered to
handle a MIME type or aren’t
positive this applies to you, your
implementation of this function can
just return false.</dd>
</dl>
</td>
<td>This API is only applicable when you are
writing an extension to enhance the
abilities of the Chrome web browser. For
example, a PDF viewer might implement
this function to download and display a
PDF file.</td>
</tr>
<tr class="row-odd"><td><dl class="first last docutils">
<dt><code>HandleInputEvent</code></dt>
<dd>Called when a user interacts with
the module’s instance in the
browser using an input device such
as a mouse or keyboard. You must
register your module to accept
input events using
<code>RequestInputEvents()</code>
for mouse events and
<code>RequestFilteringInputEvents()</code>
for keyboard events prior to
overriding this function.</dd>
</dl>
</td>
<td>An implementation of this function
examines the input event type and
branches accordingly.</td>
</tr>
</tbody>
</table>
<p>These interfaces are found in the <a class="reference external" href="/native-client/pepper_stable/cpp/classpp_1_1_instance">pp::Instance class</a>. The sections below
provide examples of how to handle these events.</p>
<h2 id="handling-browser-events">Handling browser events</h2>
<h3 id="didchangeview">DidChangeView()</h3>
<p>In the <code>mouse_lock</code> example, <code>DidChangeView()</code> checks the previous size
of instance’s rectangle versus the new size. It also compares
other state such as whether or not the app is running in full screen mode.
If none of the state has actually changed, no action is needed.
However, if the size of the view or other state has changed, it frees the
old graphics context and allocates a new one.</p>
<pre class="prettyprint">
void MouseLockInstance::DidChangeView(const pp::View& view) {
// DidChangeView can get called for many reasons, so we only want to
// rebuild the device context if we really need to.
if ((size_ == view.GetRect().size()) &&
(was_fullscreen_ == view.IsFullscreen()) && is_context_bound_) {
return;
}
// ...
// Reallocate the graphics context.
size_ = view.GetRect().size();
device_context_ = pp::Graphics2D(this, size_, false);
waiting_for_flush_completion_ = false;
is_context_bound_ = BindGraphics(device_context_);
// ...
// Remember if we are fullscreen or not
was_fullscreen_ = view.IsFullscreen();
// ...
}
</pre>
<p>For more information about graphics contexts and how to manipulate images, see:</p>
<ul class="small-gap">
<li><a class="reference external" href="/native-client/pepper_stable/cpp/classpp_1_1_image_data">pp::ImageData class</a></li>
<li><a class="reference external" href="/native-client/pepper_stable/cpp/classpp_1_1_graphics2_d">pp::Graphics2D class</a></li>
</ul>
<h3 id="didchangefocus">DidChangeFocus()</h3>
<p><code>DidChangeFocus()</code> is called when you click inside or outside of a
module’s instance in the web page. When the instance goes out
of focus (click outside of the instance), you might do something
like stop an animation. When the instance regains focus, you can
restart the animation.</p>
<pre class="prettyprint">
void DidChangeFocus(bool focus) {
// Do something like stopping animation or a blinking cursor in
// the instance.
}
</pre>
<h2 id="handling-input-events">Handling input events</h2>
<p>Input events are events that occur when the user interacts with a
module instance using the mouse, keyboard, or other input device
(e.g., touch screen). This section describes how the <code>input_events</code>
example handles input events.</p>
<h3 id="registering-a-module-to-accept-input-events">Registering a module to accept input events</h3>
<p>Before your module can handle these events, you must register your
module to accept input events using <code>RequestInputEvents()</code> for mouse
events and <code>RequestFilteringInputEvents()</code> for keyboard events. For the
<code>input_events</code> example, this is done in the constructor of the
<code>InputEventInstance</code> class:</p>
<pre class="prettyprint">
class InputEventInstance : public pp::Instance {
public:
explicit InputEventInstance(PP_Instance instance)
: pp::Instance(instance), event_thread_(NULL), callback_factory_(this) {
RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_WHEEL |
PP_INPUTEVENT_CLASS_TOUCH);
RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD);
}
// ...
};
</pre>
<p><code>RequestInputEvents()</code> and <code>RequestFilteringInputEvents()</code> accept a
combination of flags that identify the class of events that the instance is
requesting to receive. Input event classes are defined in the
<a class="reference external" href="/native-client/pepper_stable/c/group___enums.html#gafe68e3c1031daa4a6496845ff47649cd">PP_InputEvent_Class</a>
enumeration in <a class="reference external" href="/native-client/pepper_stable/c/ppb__input__event_8h">ppb_input_event.h</a>.</p>
<h3 id="determining-and-branching-on-event-types">Determining and branching on event types</h3>
<p>In a typical implementation, the <code>HandleInputEvent()</code> function determines the
type of each event using the <code>GetType()</code> function found in the <code>InputEvent</code>
class. The <code>HandleInputEvent()</code> function then uses a switch statement to
branch on the type of input event. Input events are defined in the
<a class="reference external" href="/native-client/pepper_stable/c/group___enums.html#gaca7296cfec99fcb6646b7144d1d6a0c5">PP_InputEvent_Type</a>
enumeration in <a class="reference external" href="/native-client/pepper_stable/c/ppb__input__event_8h">ppb_input_event.h</a>.</p>
<pre class="prettyprint">
virtual bool HandleInputEvent(const pp::InputEvent& event) {
Event* event_ptr = NULL;
switch (event.GetType()) {
case PP_INPUTEVENT_TYPE_UNDEFINED:
break;
case PP_INPUTEVENT_TYPE_MOUSEDOWN:
case PP_INPUTEVENT_TYPE_MOUSEUP:
case PP_INPUTEVENT_TYPE_MOUSEMOVE:
case PP_INPUTEVENT_TYPE_MOUSEENTER:
case PP_INPUTEVENT_TYPE_MOUSELEAVE:
case PP_INPUTEVENT_TYPE_CONTEXTMENU: {
pp::MouseInputEvent mouse_event(event);
PP_InputEvent_MouseButton pp_button = mouse_event.GetButton();
MouseEvent::MouseButton mouse_button = MouseEvent::kNone;
switch (pp_button) {
case PP_INPUTEVENT_MOUSEBUTTON_NONE:
mouse_button = MouseEvent::kNone;
break;
case PP_INPUTEVENT_MOUSEBUTTON_LEFT:
mouse_button = MouseEvent::kLeft;
break;
case PP_INPUTEVENT_MOUSEBUTTON_MIDDLE:
mouse_button = MouseEvent::kMiddle;
break;
case PP_INPUTEVENT_MOUSEBUTTON_RIGHT:
mouse_button = MouseEvent::kRight;
break;
}
event_ptr =
new MouseEvent(ConvertEventModifier(mouse_event.GetModifiers()),
mouse_button,
mouse_event.GetPosition().x(),
mouse_event.GetPosition().y(),
mouse_event.GetClickCount(),
mouse_event.GetTimeStamp(),
event.GetType() == PP_INPUTEVENT_TYPE_CONTEXTMENU);
} break;
case PP_INPUTEVENT_TYPE_WHEEL: {
pp::WheelInputEvent wheel_event(event);
event_ptr =
new WheelEvent(ConvertEventModifier(wheel_event.GetModifiers()),
wheel_event.GetDelta().x(),
wheel_event.GetDelta().y(),
wheel_event.GetTicks().x(),
wheel_event.GetTicks().y(),
wheel_event.GetScrollByPage(),
wheel_event.GetTimeStamp());
} break;
case PP_INPUTEVENT_TYPE_RAWKEYDOWN:
case PP_INPUTEVENT_TYPE_KEYDOWN:
case PP_INPUTEVENT_TYPE_KEYUP:
case PP_INPUTEVENT_TYPE_CHAR: {
pp::KeyboardInputEvent key_event(event);
event_ptr = new KeyEvent(ConvertEventModifier(key_event.GetModifiers()),
key_event.GetKeyCode(),
key_event.GetTimeStamp(),
key_event.GetCharacterText().DebugString());
} break;
default: {
// For any unhandled events, send a message to the browser
// so that the user is aware of these and can investigate.
std::stringstream oss;
oss << "Default (unhandled) event, type=" << event.GetType();
PostMessage(oss.str());
} break;
}
event_queue_.Push(event_ptr);
return true;
}
</pre>
<p>Notice that the generic <code>InputEvent</code> received by <code>HandleInputEvent()</code> is
converted into a specific type after the event type is
determined. The event types handled in the example code are
<code>MouseInputEvent</code>, <code>WheelInputEvent</code>, and <code>KeyboardInputEvent</code>.
There are also <code>TouchInputEvents</code>. For the latest list of event types,
see the <a class="reference external" href="/native-client/pepper_stable/c/classpp_1_1_input_event">InputEvent documentation</a>.
For reference information related to the these event classes, see the
following documentation:</p>
<ul class="small-gap">
<li><a class="reference external" href="/native-client/pepper_stable/c/classpp_1_1_mouse_input_event">pp::MouseInputEvent class</a></li>
<li><a class="reference external" href="/native-client/pepper_stable/c/classpp_1_1_wheel_input_event">pp::WheelInputEvent class</a></li>
<li><a class="reference external" href="/native-client/pepper_stable/c/classpp_1_1_keyboard_input_event">pp::KeyboardInputEvent class</a></li>
</ul>
<h3 id="threading-and-blocking">Threading and blocking</h3>
<p><code>HandleInputEvent()</code> in this example runs on the main module thread.
However, the bulk of the work happens on a separate worker thread (see
<code>ProcessEventOnWorkerThread</code>). <code>HandleInputEvent()</code> puts events in
the <code>event_queue_</code> and the worker thread takes events from the
<code>event_queue_</code>. This processing happens independently of the main
thread, so as not to slow down the browser.</p>
</section>
{{/partials.standard_nacl_article}}
|