1 : /*
2 : * Copyright (c) 2011 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can be
4 : * found in the LICENSE file.
5 : */
6 :
7 : // This file exports a single function used to setup the
8 : // multimedia sub-system for use with sel_universal
9 : // It was inpspired by src/trusted/plugin/srpc/multimedia_socket.cc
10 : // On the untrusted side it interface with: src/untrusted/av/nacl_av.c
11 : //
12 : // NOTE: this is experimentation and testing. We are not concerned
13 : // about descriptor and memory leaks
14 :
15 : #include <string.h>
16 : #include <fstream>
17 : #include <queue>
18 : #include <string>
19 :
20 : #include "ppapi/c/pp_errors.h"
21 : #include "ppapi/c/pp_input_event.h"
22 : #include "ppapi/c/pp_size.h"
23 :
24 : #include "native_client/src/shared/platform/nacl_check.h"
25 : #include "native_client/src/shared/platform/nacl_log.h"
26 : #include "native_client/src/shared/platform/nacl_time.h"
27 : #include "native_client/src/shared/srpc/nacl_srpc.h"
28 :
29 :
30 : #include "native_client/src/trusted/sel_universal/rpc_universal.h"
31 : #include "native_client/src/trusted/sel_universal/primitives.h"
32 : #include "native_client/src/trusted/sel_universal/parsing.h"
33 : #include "native_client/src/trusted/sel_universal/pepper_emu.h"
34 :
35 : // ======================================================================
36 : const int kInvalidInstance = 0;
37 : const int kInvalidHandle = 0;
38 : // We currently have no bookkeeping for events, so this resource handle is
39 : // used for all of them.
40 : const int kFirstEventHandle = 6000;
41 :
42 : // ======================================================================
43 :
44 : // Note: Just a bunch of fairly unrelated global variables,
45 : // we expect them to be zero initialized.
46 22 : static struct {
47 : int instance;
48 :
49 : // for event loggging and replay
50 : std::ofstream event_logger_stream;
51 : std::ifstream event_replay_stream;
52 : std::queue<UserEvent*> events_ready_to_go;
53 : UserEvent* next_sync_event;
54 :
55 : IMultimedia* sdl_engine;
56 : std::string title;
57 :
58 : std::string quit_message;
59 22 : } Global;
60 :
61 : // ======================================================================
62 : bool HandlerPepperEmuInitialize(NaClCommandLoop* ncl,
63 0 : const vector<string>& args) {
64 0 : NaClLog(LOG_INFO, "HandlerSDLInitialize\n");
65 0 : if (args.size() < 5) {
66 0 : NaClLog(LOG_ERROR, "Insufficient arguments to 'rpc' command.\n");
67 0 : return false;
68 : }
69 :
70 : UNREFERENCED_PARAMETER(ncl);
71 0 : Global.instance = ExtractInt32(args[1]);
72 0 : Global.title = args[4];
73 :
74 : // NOTE: we decide at linktime which incarnation to use here
75 : Global.sdl_engine = MakeEmuPrimitives(ExtractInt32(args[2]),
76 : ExtractInt32(args[3]),
77 0 : Global.title.c_str());
78 0 : PepperEmuInitAudio(ncl, Global.sdl_engine);
79 0 : PepperEmuInitCore(ncl, Global.sdl_engine);
80 0 : PepperEmuInitFileIO(ncl, Global.sdl_engine);
81 0 : PepperEmuInitPostMessage(ncl, Global.sdl_engine);
82 0 : PepperEmuInit3D(ncl, Global.sdl_engine);
83 0 : PepperEmuInit2D(ncl, Global.sdl_engine);
84 0 : return true;
85 : }
86 :
87 :
88 0 : static UserEvent* GetNextEvent(bool poll) {
89 0 : UserEvent* event = NULL;
90 0 : if (Global.event_replay_stream.is_open()) {
91 0 : if (Global.events_ready_to_go.size() > 0) {
92 : // empty queue while we have ready to events
93 0 : event = Global.events_ready_to_go.front();
94 0 : Global.events_ready_to_go.pop();
95 0 : return event;
96 0 : } else if (Global.next_sync_event != NULL) {
97 : // wait for the matching sync event
98 0 : event = Global.sdl_engine->EventGet();
99 :
100 0 : if (IsTerminationEvent(event)) return event;
101 :
102 : // drop all regular events on the floor;
103 0 : if (IsInputEvent(event)) return NULL;
104 :
105 : // NOTE: we only replay the recorded input events.
106 : // Recorded UserEvents are used for synchronization with the
107 : // actual UserEvents that the system generates.
108 : // TODO(robertm): We may need to refine this because, in theory,
109 : // there is no guaranteed time ordering on the UserEvents.
110 : // One solution would be to only use the screen refresh
111 : // UserEvents (CUSTOM_EVENT_FLUSH_CALLBACK) as sync events and
112 : // ignore all others.
113 : // We can delay this work until we see the check below firing.
114 0 : CHECK(event->type == Global.next_sync_event->type);
115 : // sync event has been "consumed"
116 0 : Global.next_sync_event = NULL;
117 0 : return event;
118 : } else {
119 : // refill queue
120 0 : if (Global.event_replay_stream.eof()) {
121 0 : NaClLog(LOG_INFO, "replay events depleted\n");
122 0 : Global.events_ready_to_go.push(MakeTerminationEvent());
123 0 : return NULL;
124 : }
125 0 : while (true) {
126 0 : event = new UserEvent;
127 : Global.event_replay_stream.read(
128 0 : reinterpret_cast<char*>(event), sizeof(*event));
129 0 : if (Global.event_replay_stream.fail()) return NULL;
130 0 : CHECK(!IsInvalidEvent(event));
131 0 : if (!IsInputEvent(event)) {
132 0 : Global.next_sync_event = event;
133 0 : return NULL;
134 : } else {
135 0 : Global.events_ready_to_go.push(event);
136 : }
137 : }
138 : }
139 : } else {
140 0 : if (poll) {
141 0 : return Global.sdl_engine->EventPoll();
142 : } else {
143 0 : return Global.sdl_engine->EventGet();
144 : }
145 : }
146 : }
147 :
148 :
149 : static void InvokeCompletionCallback(NaClCommandLoop* ncl,
150 : int callback,
151 : int result,
152 : void* data,
153 0 : int size) {
154 : NaClSrpcArg in[NACL_SRPC_MAX_ARGS];
155 : NaClSrpcArg* ins[NACL_SRPC_MAX_ARGS + 1];
156 : NaClSrpcArg out[NACL_SRPC_MAX_ARGS];
157 : NaClSrpcArg* outs[NACL_SRPC_MAX_ARGS + 1];
158 0 : BuildArgVec(outs, out, 0);
159 0 : BuildArgVec(ins, in, 3);
160 :
161 0 : int dummy_exception[2] = {0, 0};
162 :
163 0 : ins[0]->tag = NACL_SRPC_ARG_TYPE_INT;
164 0 : ins[0]->u.ival = callback;
165 0 : ins[1]->tag = NACL_SRPC_ARG_TYPE_INT;
166 0 : ins[1]->u.ival = result;
167 0 : ins[2]->tag = NACL_SRPC_ARG_TYPE_CHAR_ARRAY;
168 0 : if (data) {
169 0 : ins[2]->u.count = size;;
170 0 : ins[2]->arrays.carr = reinterpret_cast<char*>(data);
171 : } else {
172 0 : ins[2]->u.count = sizeof(dummy_exception);
173 0 : ins[2]->arrays.carr = reinterpret_cast<char*>(dummy_exception);
174 : }
175 :
176 0 : ncl->InvokeNexeRpc("RunCompletionCallback:iiC:", ins, outs);
177 0 : }
178 :
179 :
180 0 : static bool HandleSynthesizedEvent(NaClCommandLoop* ncl, UserEvent* event) {
181 : NaClLog(1, "Got sythesized event [%s]\n",
182 0 : StringifyEvent(event).c_str());
183 :
184 0 : switch (event->type) {
185 : case EVENT_TYPE_TERMINATION:
186 0 : NaClLog(LOG_INFO, "Got termination event\n");
187 0 : return false;
188 :
189 : // This event is created on behalf of PPB_URLLoader_Open
190 : case EVENT_TYPE_OPEN_CALLBACK:
191 : // FALL THROUGH
192 : // This event is created on behalf of PPB_Core_CallOnMainThread
193 : case EVENT_TYPE_TIMER_CALLBACK:
194 : // FALL THROUGH
195 : // This event is created on behalf of PPB_URLLoader_ReadResponseBody
196 : case EVENT_TYPE_READ_CALLBACK:
197 : // FALL THROUGH
198 : // This event gets created so that we can invoke
199 : // RunCompletionCallback after PPB_Graphics2D_Flush
200 : case EVENT_TYPE_FLUSH_CALLBACK: {
201 : InvokeCompletionCallback(ncl,
202 : event->callback,
203 : event->result,
204 : event->pointer,
205 0 : event->size);
206 0 : return true;
207 : }
208 : // This event gets created so that we can invoke
209 : // PPP_Audio_StreamCreated after PPB_Audio_Create
210 : case EVENT_TYPE_INIT_AUDIO:
211 : #if (NACL_LINUX || NACL_OSX)
212 0 : sleep(1);
213 : #elif NACL_WINDOWS
214 : Sleep(1 * 1000);
215 : #else
216 : #error "Please specify platform as NACL_LINUX, NACL_OSX or NACL_WINDOWS"
217 : #endif
218 0 : InvokeAudioStreamCreatedCallback(ncl, event);
219 0 : return true;
220 : case EVENT_TYPE_INPUT:
221 : case EVENT_TYPE_INVALID:
222 : default:
223 0 : NaClLog(LOG_FATAL, "unknown event type %d\n", event->type);
224 0 : return false;
225 : }
226 : }
227 :
228 : // The event loop below see two kinds of events. Regular input events
229 : // such as keyboard and mouse events. And synthetic events that got injected
230 : // into the event queue to trigger callbacks.
231 : // The input events are forwarded to the nexe.
232 : // The synthetic events are converted to the corresponding callback invokation.
233 : //
234 : // Having callbacks and input events going through the same queue has some
235 : // advantages. In particular we can use the non-input events
236 : // as synchronization markers (e.g. sync on every screen update) in the replay
237 : // case.
238 : bool HandlerPepperEmuEventLoop(NaClCommandLoop* ncl,
239 0 : const vector<string>& args) {
240 0 : NaClLog(LOG_INFO, "HandlerPepperEmuEventLoop\n");
241 : UNREFERENCED_PARAMETER(ncl);
242 0 : if (args.size() < 3) {
243 0 : NaClLog(LOG_ERROR, "Insufficient arguments to 'rpc' command.\n");
244 0 : return false;
245 : }
246 :
247 0 : const bool poll = ExtractInt32(args[1]) != 0;
248 0 : const int msecs = ExtractInt32(args[2]);
249 :
250 : NaClLog(LOG_INFO, "Entering event loop. Polling: %d duration_ms: %d\n",
251 0 : poll, msecs);
252 :
253 0 : int64_t termination_time = NaClGetTimeOfDayMicroseconds() + msecs * 1000;
254 0 : bool keep_going = true;
255 0 : while (keep_going && NaClGetTimeOfDayMicroseconds() < termination_time) {
256 0 : NaClLog(2, "Pepper emu event loop wait\n");
257 0 : UserEvent* event = GetNextEvent(poll);
258 0 : if (event == NULL) {
259 0 : continue;
260 : }
261 :
262 0 : if (Global.event_logger_stream.is_open() && !IsInvalidEvent(event)) {
263 : Global.event_logger_stream.write(reinterpret_cast<char*>(event),
264 0 : sizeof(*event));
265 : }
266 :
267 0 : if (IsInputEvent(event)) {
268 : // NOTE: for now always use the same event resource
269 0 : InvokeInputEventCallback(ncl, event, Global.instance, kFirstEventHandle);
270 : } else {
271 0 : keep_going = HandleSynthesizedEvent(ncl, event);
272 : }
273 : // NOTE: this is the global event sink where all events get deleted.
274 0 : delete event;
275 : }
276 :
277 : NaClLog(LOG_INFO, "Exiting event loop (%s)\n",
278 0 : keep_going ? "timeout" : "termination");
279 0 : return true;
280 : }
281 :
282 :
283 0 : void RecordPPAPIEvents(std::string filename) {
284 0 : NaClLog(LOG_INFO, "recoding events to %s\n", filename.c_str());
285 : Global.event_logger_stream.open(
286 0 : filename.c_str(), std::ios::out | std::ios::trunc | std::ios::binary);
287 0 : if (!Global.event_logger_stream.is_open()) {
288 0 : NaClLog(LOG_FATAL, "Cannot open %s\n", filename.c_str());
289 : }
290 0 : }
291 :
292 :
293 0 : void ReplayPPAPIEvents(std::string filename) {
294 0 : NaClLog(LOG_INFO, "replaying events from %s\n", filename.c_str());
295 0 : Global.next_sync_event = NULL;
296 : Global.event_replay_stream.open(filename.c_str(),
297 0 : std::ios::in | std::ios::binary);
298 0 : if (!Global.event_replay_stream.is_open()) {
299 0 : NaClLog(LOG_FATAL, "Cannot open %s\n", filename.c_str());
300 : }
301 22 : }
|