NOTICE: add Alexandre Blondin Massé
[nit.git] / lib / pnacl.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2014 Johan Kayser <kayser.johan@gmail.com>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Provides PNaCl support for Nit.
18 #
19 # To use this module and compile for PNaCl, you must install the
20 # NaCl SDK (This file is based on Pepper 33).
21 # If NACL_SDK_ROOT is not set in your PATH, you have to work in
22 # 'nacl_sdk/pepper_your_pepper_version/getting_started/your_project_folder'.
23 module pnacl is platform
24
25 import standard
26 intrude import standard::stream
27
28 in "C Header" `{
29 #include "ppapi/c/pp_errors.h"
30 #include "ppapi/c/ppp.h"
31 #include "ppapi/c/ppp_instance.h"
32 #include "ppapi/c/pp_bool.h"
33 #include "ppapi/c/ppb_var.h"
34 #include "ppapi/c/ppb_messaging.h"
35 #include "ppapi/c/ppp_messaging.h"
36 #include "ppapi/c/ppb_var_dictionary.h"
37 #include "ppapi/c/ppb_var_array.h"
38 `}
39
40 `{
41 #include <unistd.h>
42 #include <stddef.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <stdlib.h>
46 #include <pthread.h>
47
48 #define MAX_DICTIONARY_QUEUE_SIZE 200
49 #define MAX_MESSAGE_QUEUE_SIZE 10
50
51 extern int nit_main(int, char**);
52
53 /* A working thread for Nit. */
54 static pthread_t g_nit_thread;
55
56 /* Mutex that guards the queues. */
57 static pthread_mutex_t g_dictionary_queue_mutex;
58 static pthread_mutex_t g_message_queue_mutex;
59
60 /* Condition variables that are signalled when the queues are not empty. */
61 static pthread_cond_t g_dictionary_queue_not_empty_cond;
62 static pthread_cond_t g_message_queue_not_empty_cond;
63
64 /** Circular queues of dictionaries and messages from JavaScript to be handled.
65 *
66 * If g_queue_start < g_queue_end:
67 * all elements in the range [g_queue_start, g_queue_end) are valid.
68 * If g_queue_start > g_queue_end:
69 * all elements in the ranges [0, g_queue_end) and
70 * [g_queue_start, MAX_QUEUE_SIZE) are valid.
71 * If g_queue_start == g_queue_end, and g_queue_size > 0:
72 * all elements in the g_queue are valid.
73 * If g_queue_start == g_queue_end, and g_queue_size == 0:
74 * No elements are valid. */
75 static struct PP_Var g_dictionary_queue[MAX_DICTIONARY_QUEUE_SIZE];
76 static char* g_message_queue[MAX_MESSAGE_QUEUE_SIZE];
77
78 /* The index of the head of the queues. */
79 static int g_dictionary_queue_start = 0;
80 static int g_message_queue_start = 0;
81
82 /* The index of the tail of the queues, non-inclusive. */
83 static int g_dictionary_queue_end = 0;
84 static int g_message_queue_end = 0;
85
86 /* The size of the queues. */
87 static int g_dictionary_queue_size = 0;
88 static int g_message_queue_size = 0;
89
90 /* PNaCl interfaces. */
91 const PPB_Messaging* g_varMessagingInterface;
92 const PPB_Var* g_varInterface;
93 const PPB_VarDictionary* g_varDictionaryInterface;
94 const PPB_VarArray* g_varArrayInterface;
95
96 PP_Instance g_instance;
97 PnaclApp app;
98
99 /* A wrapper to launch the Nit main on a new thread. */
100 void* WrapperNitMain(void* arg) {
101 nit_main(0, NULL);
102 return NULL;
103 }
104
105 /** Return whether the queues are empty.
106 *
107 * NOTE: this function assumes g_queue_mutex lock is held.
108 * @return non-zero if the queue is empty. */
109 static int IsDictionaryQueueEmpty() { return g_dictionary_queue_size == 0; }
110 static int IsMessageQueueEmpty() { return g_message_queue_size == 0; }
111
112 /** Return whether the queues are full.
113 *
114 * NOTE: this function assumes g_queue_mutex lock is held.
115 * @return non-zero if the queue is full. */
116 static int IsDictionaryQueueFull() { return g_dictionary_queue_size == MAX_DICTIONARY_QUEUE_SIZE; }
117 static int IsMessageQueueFull() { return g_message_queue_size == MAX_MESSAGE_QUEUE_SIZE; }
118
119 /* Initialize the queues. */
120 void InitializeQueues() {
121 pthread_mutex_init(&g_dictionary_queue_mutex, NULL);
122 pthread_cond_init(&g_dictionary_queue_not_empty_cond, NULL);
123 pthread_mutex_init(&g_message_queue_mutex, NULL);
124 pthread_cond_init(&g_message_queue_not_empty_cond, NULL);
125 }
126
127 /** Enqueue a dictionary (i.e. add to the end)
128 *
129 * If the queue is full, the dictionary will be dropped.
130 *
131 * NOTE: this function assumes g_dictionary_queue_mutex is _NOT_ held.
132 * @param[in] dictionary, the dictionary to enqueue.
133 * @return non-zero if the dictionary was added to the queue. */
134 int EnqueueDictionary(struct PP_Var dictionary) {
135 pthread_mutex_lock(&g_dictionary_queue_mutex);
136
137 /* We shouldn't block the main thread waiting for the queue to not be full,
138 * so just drop the dictionary. */
139 if (IsDictionaryQueueFull()) {
140 pthread_mutex_unlock(&g_dictionary_queue_mutex);
141 return 0;
142 }
143
144 g_dictionary_queue[g_dictionary_queue_end] = dictionary;
145 g_dictionary_queue_end = (g_dictionary_queue_end + 1) % MAX_DICTIONARY_QUEUE_SIZE;
146 g_dictionary_queue_size++;
147
148 pthread_cond_signal(&g_dictionary_queue_not_empty_cond);
149
150 pthread_mutex_unlock(&g_dictionary_queue_mutex);
151
152 return 1;
153 }
154
155 /** Enqueue a message (i.e. add to the end)
156 *
157 * If the queue is full, the message will be dropped.
158 *
159 * NOTE: this function assumes g_message_queue_mutex is _NOT_ held.
160 * @param[in] message The message to enqueue.
161 * @return non-zero if the message was added to the queue. */
162 int EnqueueMessage(char* message) {
163 pthread_mutex_lock(&g_message_queue_mutex);
164
165 /* We shouldn't block the main thread waiting for the queue to not be full,
166 * so just drop the message. */
167 if (IsMessageQueueFull()) {
168 pthread_mutex_unlock(&g_message_queue_mutex);
169 return 0;
170 }
171
172 g_message_queue[g_message_queue_end] = message;
173 g_message_queue_end = (g_message_queue_end + 1) % MAX_MESSAGE_QUEUE_SIZE;
174 g_message_queue_size++;
175
176 pthread_cond_signal(&g_message_queue_not_empty_cond);
177
178 pthread_mutex_unlock(&g_message_queue_mutex);
179
180 return 1;
181 }
182
183 /** Dequeue a dictionary and return it.
184 *
185 * This function blocks until a dictionary is available. It should not be called
186 * on the main thread.
187 *
188 * NOTE: this function assumes g_dictionary_queue_mutex is _NOT_ held.
189 * @return The dictionary at the head of the queue. */
190 struct PP_Var DequeueDictionary() {
191 struct PP_Var dictionary = g_varDictionaryInterface->Create();
192
193 pthread_mutex_lock(&g_dictionary_queue_mutex);
194
195 while (IsDictionaryQueueEmpty()) {
196 pthread_cond_wait(&g_dictionary_queue_not_empty_cond, &g_dictionary_queue_mutex);
197 }
198
199 dictionary = g_dictionary_queue[g_dictionary_queue_start];
200 g_dictionary_queue_start = (g_dictionary_queue_start + 1) % MAX_DICTIONARY_QUEUE_SIZE;
201 g_dictionary_queue_size--;
202
203 pthread_mutex_unlock(&g_dictionary_queue_mutex);
204
205 return dictionary;
206 }
207
208 /** Dequeue a message and return it.
209 *
210 * This function blocks until a message is available. It should not be called
211 * on the main thread.
212 *
213 * NOTE: this function assumes g_queue_mutex is _NOT_ held.
214 * @return The message at the head of the queue. */
215 char* DequeueMessage() {
216 char* message = NULL;
217
218 pthread_mutex_lock(&g_message_queue_mutex);
219
220 while (IsMessageQueueEmpty()) {
221 pthread_cond_wait(&g_message_queue_not_empty_cond, &g_message_queue_mutex);
222 }
223
224 message = g_message_queue[g_message_queue_start];
225 g_message_queue_start = (g_message_queue_start + 1) % MAX_MESSAGE_QUEUE_SIZE;
226 g_message_queue_size--;
227
228 pthread_mutex_unlock(&g_message_queue_mutex);
229
230 return message;
231 }
232
233 /* Posts a string message to JS. */
234 void PostMessage(char* message) {
235 /* Create PP_Var containing the message body. */
236 struct PP_Var varString = g_varInterface->VarFromUtf8(message, strlen(message));
237
238 /* Post message to the JavaScript layer. */
239 g_varMessagingInterface->PostMessage(g_instance, varString);
240 }
241
242 /* Posts a Dictionary (JS like object) to JS. */
243 void PostDictionary(struct PP_Var dictionary) {
244 g_varMessagingInterface->PostMessage(g_instance, dictionary);
245 }
246
247 /* Posts a Variable (aka PepperVar) to JS.
248 Should only be used for testing, conventional conversation is made
249 with Strings or Dictionaries. */
250 void PostVar(struct PP_Var v) {
251 g_varMessagingInterface->PostMessage(g_instance, v);
252 }
253
254 /* char* to PP_Var. */
255 static struct PP_Var CStrToVar(const char* str) {
256 if (g_varInterface != NULL) {
257 return g_varInterface->VarFromUtf8(str, strlen(str));
258 }
259 return PP_MakeUndefined();
260 }
261
262 static PP_Bool Instance_DidCreate(PP_Instance instance, uint32_t argc, const char* argn[], const char* argv[]) {
263 g_instance = instance;
264
265 /* Initialization of the queues and creation of the thread for Nit. */
266 InitializeQueues();
267 pthread_create(&g_nit_thread, NULL, &WrapperNitMain, NULL);
268
269 return PP_TRUE;
270 }
271
272 static void Instance_DidDestroy(PP_Instance instance) {
273 // TODO
274 }
275
276 static void Instance_DidChangeView(PP_Instance pp_instance, PP_Resource view) {
277 // TODO
278 }
279
280 static void Instance_DidChangeFocus(PP_Instance pp_instance, PP_Bool has_focus) {
281 // TODO
282 }
283
284 static PP_Bool Instance_HandleDocumentLoad(PP_Instance pp_instance, PP_Resource pp_url_loader) {
285 // TODO
286 return PP_FALSE;
287 }
288
289 /* Called when JS sends something, is set to accept Strings or Dictionaries,
290 returns an error if received object is not a String or Dictionary. */
291 void Messaging_HandleMessage(PP_Instance instance, struct PP_Var varMessage) {
292 if(varMessage.type == PP_VARTYPE_DICTIONARY) {
293 if(!EnqueueDictionary(varMessage)) {
294 struct PP_Var errorMessage = CStrToVar("QueueFull : dropped dictionary because the queue was full.");
295 g_varMessagingInterface->PostMessage(g_instance, errorMessage);
296 }
297 }
298 else if(varMessage.type == PP_VARTYPE_STRING) {
299 uint32_t len;
300 char* message = (char*)g_varInterface->VarToUtf8(varMessage, &len);
301 if(!EnqueueMessage(message)) {
302 struct PP_Var errorMessage = CStrToVar("QueueFull : dropped message because the queue was full.");
303 g_varMessagingInterface->PostMessage(g_instance, errorMessage);
304 }
305 }
306 else {
307 struct PP_Var errorMessage = CStrToVar("TypeError : only accepts JS objects or Strings");
308 g_varMessagingInterface->PostMessage(g_instance, errorMessage);
309 }
310 }
311
312 /* This function is called by Nit when using check_dictionary,
313 returns the dictionary at the head of the queue. */
314 void* NitHandleDictionary() {
315 struct PP_Var dictionary = DequeueDictionary();
316 PnaclApp_handle_dictionary(app, &dictionary);
317 return 0;
318 }
319
320 /* This function is called By Nit when waiting for a user input. */
321 char* NitHandleMessage() {
322 return DequeueMessage();
323 }
324
325 /* Entry point */
326 PP_EXPORT int32_t PPP_InitializeModule(PP_Module module_id, PPB_GetInterface get_browser_interface) {
327 /* Initializing global pointers. */
328 g_varMessagingInterface = (const PPB_Messaging*) get_browser_interface(PPB_MESSAGING_INTERFACE);
329 g_varInterface = (const PPB_Var*) get_browser_interface(PPB_VAR_INTERFACE);
330 g_varDictionaryInterface = (const PPB_VarDictionary*) get_browser_interface(PPB_VAR_DICTIONARY_INTERFACE);
331 g_varArrayInterface = (const PPB_VarArray*) get_browser_interface(PPB_VAR_ARRAY_INTERFACE);
332 return PP_OK;
333 }
334
335 PP_EXPORT void PPP_ShutdownModule() {
336 // TODO
337 }
338
339 PP_EXPORT const void* PPP_GetInterface(const char* interface_name) {
340 if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0)
341 {
342 static PPP_Instance instance_interface = {
343 &Instance_DidCreate,
344 &Instance_DidDestroy,
345 &Instance_DidChangeView,
346 &Instance_DidChangeFocus,
347 &Instance_HandleDocumentLoad
348 };
349 return &instance_interface;
350 }
351 else if (strcmp(interface_name, PPP_MESSAGING_INTERFACE) == 0) {
352 static PPP_Messaging messaging_interface = {
353 &Messaging_HandleMessage
354 };
355 return &messaging_interface;
356 }
357 return NULL;
358 }
359
360 /* Hack in order to avoid the problem with file. */
361 int poll(void *fds, int nfds, int timeout) { return 0; }
362 `}
363
364 # Nit class representing a Pepper C API PP_Var typed as a Dictionary.
365 extern class PepperDictionary `{ struct PP_Var* `}
366
367 new `{
368 struct PP_Var* recv = malloc( sizeof( struct PP_Var ) );
369 *recv = g_varDictionaryInterface->Create();
370 return recv;
371 `}
372
373 # Get fonction using PepperVars.
374 #
375 # Returns the value that is associated with 'key'.
376 # If 'key' is not a String typed PepperVar, or doesn't exist in the Dictionary, an undefined PepperVar is returned.
377 fun native_get(key: PepperVar): PepperVar `{
378 struct PP_Var* value = malloc( sizeof ( struct PP_Var ) );
379 *value = g_varDictionaryInterface->Get(*recv, *key);
380 return value;
381 `}
382
383 # Returns the value associated with 'key'.
384 #
385 # 'key' must be a String.
386 # If 'key' is not a String or doesn't exist in the Dictionary, 'null' is returned.
387 fun [](key: nullable Pepperable): nullable Pepperable
388 do
389 var native_key = key.to_pepper
390 var native_value = native_get(native_key)
391 return native_value.to_nit
392 end
393
394 # Set function using PepperVars.
395 #
396 # Sets the value associated with the specified key.
397 # 'key' must be a String typed PepperVar.
398 # If 'key' hasn't existed in the Dictionary, it is added and associated with 'value'.
399 # Otherwise, the previous value is replaced with 'value'.
400 # Returns a Boolean indicating whether the operation succeeds.
401 fun native_set(key: PepperVar, value: PepperVar): Bool `{
402 PP_Bool b;
403 b = g_varDictionaryInterface->Set(*recv, *key, *value);
404 return b;
405 `}
406
407 # Sets the value associated with the specified key.
408 #
409 # 'key' must be a String.
410 # If 'key' hasn't existed in the Dictionary, it is added and associated with 'value'.
411 # Otherwise, the previous value is replaced with 'value'.
412 # Returns a Boolean indicating whether the operation succeeds.
413 fun []=(key: nullable Pepperable, value: nullable Pepperable): Bool
414 do
415 var native_key = key.to_pepper
416 var native_value = value.to_pepper
417 return native_set(native_key, native_value)
418 end
419
420 # Deletes the specified key and its associated value, if the key exists.
421 #
422 # Takes a String typed PepperVar.
423 fun native_delete(key: PepperVar) `{
424 g_varDictionaryInterface->Delete(*recv, *key);
425 `}
426
427 # Deletes the specified key and its associated value, if the key exists.
428 #
429 # Takes a String.
430 fun delete(key: String)
431 do
432 var native_key = key.to_pepper
433 native_delete native_key
434 end
435
436 # Checks whether a key exists.
437 #
438 # Takes a String typed PepperVar.
439 fun native_has_key(key: PepperVar): Bool `{
440 PP_Bool b;
441 b = g_varDictionaryInterface->HasKey(*recv, *key);
442 return b;
443 `}
444
445 # Checks whether a key exists.
446 #
447 # Takes a String.
448 fun has_key(key: String): Bool
449 do
450 var native_key = key.to_pepper
451 return native_has_key(native_key)
452 end
453
454 # Gets all the keys in a dictionary.
455 #
456 # Returns a PepperArray which contains all the keys of the Dictionary. The elements are string vars.
457 fun get_keys: PepperArray `{
458 struct PP_Var* array = malloc( sizeof( struct PP_Var ) );
459 *array = g_varDictionaryInterface->GetKeys(*recv);
460 return array;
461 `}
462
463 # Use this function to copy a dictionary.
464 fun copy: PepperDictionary `{
465 struct PP_Var* varDictionary = malloc( sizeof( struct PP_Var ) );
466 *varDictionary = g_varDictionaryInterface->Create();
467 *varDictionary = *recv;
468 return varDictionary;
469 `}
470 end
471
472 # Nit class representing a Pepper C API PP_Var typed as an Array.
473 extern class PepperArray `{ struct PP_Var* `}
474
475 new `{
476 struct PP_Var* recv = malloc( sizeof( struct PP_Var ) );
477 *recv = g_varArrayInterface->Create();
478 return recv;
479 `}
480
481 # Returns the element at the specified position as a PepperVar.
482 #
483 # If 'index' is larger than or equal to the array length, an undefined PepperVar is returned.
484 fun native_get(index: Int): PepperVar `{
485 struct PP_Var* value = malloc( sizeof( struct PP_Var ) );
486 *value = g_varArrayInterface->Get(*recv, index);
487 return value;
488 `}
489
490 # Returns the element at the specified position.
491 #
492 # If 'index' is larger than or equal to the array length, 'null' is returned.
493 fun [](index: Int): nullable Pepperable
494 do
495 var native_value = native_get(index)
496 return native_value.to_nit
497 end
498
499 # Returns an int containing the length of the PepperArray.
500 fun length: Int `{
501 int length = g_varArrayInterface->GetLength(*recv);
502 return length;
503 `}
504
505 # Takes a PepperVar for the 'value' param.
506 #
507 # Sets the value of an element in the array at indicated index.
508 # If 'index' is larger than or equal to the array length, the length is updated to be 'index' + 1.
509 # Any position in the array that hasn't been set before is set to undefined, i.e., PepperVar of C type PP_VARTYPE_UNDEFINED.
510 # Returns a Boolean indicating whether the operation succeeds.
511 fun native_set(index: Int, value: PepperVar): Bool `{
512 PP_Bool b;
513 b = g_varArrayInterface->Set(*recv, index, *value);
514 return b;
515 `}
516
517 # Sets the value of an element in the array at indicated index.
518 #
519 # If 'index' is larger than or equal to the array length, the length is updated to be 'index' + 1.
520 # Any position in the array that hasn't been set before is set to undefined, i.e., PepperVar of C type PP_VARTYPE_UNDEFINED.
521 # Returns a Boolean indicating whether the operation succeeds.
522 fun []=(index: Int, value: nullable Pepperable): Bool
523 do
524 var native_value = value.to_pepper
525 return native_set(index, native_value)
526 end
527
528 # Sets the array length.
529 #
530 # If 'length' is smaller than its current value, the array is truncated to the new length.
531 # Any elements that no longer fit are removed and the references to them will be released.
532 # If 'length' is larger than its current value, undefined PepperVars are appended to increase the array to the specified length.
533 # Returns a Boolean indicating whether the operation succeeds.
534 fun length=(length: Int): Bool `{
535 PP_Bool b;
536 b = g_varArrayInterface->SetLength(*recv, length);
537 return b;
538 `}
539 end
540
541 # Nit class representing a Pepper C API PP_Var.
542 extern class PepperVar `{ struct PP_Var* `}
543
544 new `{
545 return malloc( sizeof( struct PP_Var ) );
546 `}
547
548 # Converts PepperVar to standard types.
549 #
550 # Actually supports bools, ints, floats, strings. To be used with 'isa'.
551 fun to_nit: nullable Pepperable
552 do
553 if isa_null then return null
554 if isa_bool then return as_bool
555 if isa_int then return as_int
556 if isa_float then return as_float
557 if isa_string then return as_string
558 if is_undefined then return null
559
560 return null
561 end
562
563 private fun isa_null: Bool `{ return recv->type == PP_VARTYPE_NULL; `}
564 private fun isa_bool: Bool `{ return recv->type == PP_VARTYPE_BOOL; `}
565 private fun isa_int: Bool `{ return recv->type == PP_VARTYPE_INT32; `}
566 private fun isa_float: Bool `{ return recv->type == PP_VARTYPE_DOUBLE; `}
567 private fun isa_string: Bool `{ return recv->type == PP_VARTYPE_STRING; `}
568 private fun is_undefined: Bool `{ return recv->type == PP_VARTYPE_UNDEFINED; `}
569
570 private fun as_bool: Bool `{ return recv->value.as_bool; `}
571 private fun as_int: Int `{ return recv->value.as_int; `}
572 private fun as_float: Float `{ return recv->value.as_double; `}
573 private fun as_string: String import NativeString.to_s_with_length `{
574 uint32_t len;
575 char* str = (char*)g_varInterface->VarToUtf8(*recv, &len);
576 return NativeString_to_s_with_length(str, len);
577 `}
578 end
579
580 # Provides a method to convert in PepperVars.
581 interface Pepperable
582 fun to_pepper: PepperVar is abstract
583 end
584
585 redef class Int
586 super Pepperable
587
588 # Converts a Int into a PepperVar with Int type.
589 redef fun to_pepper `{
590 struct PP_Var* var = malloc( sizeof( struct PP_Var ) );
591 *var = PP_MakeInt32(recv);
592 return var;
593 `}
594 end
595
596 redef class Float
597 super Pepperable
598
599 # Converts a Float into a PepperVar with Float type.
600 redef fun to_pepper `{
601 struct PP_Var* var = malloc( sizeof( struct PP_Var ) );
602 *var = PP_MakeDouble(recv);
603 return var;
604 `}
605 end
606
607 redef class Bool
608 super Pepperable
609
610 # Converts a Bool into a PepperVar with Bool type.
611 redef fun to_pepper `{
612 struct PP_Var* var = malloc( sizeof( struct PP_Var ) );
613 *var = PP_MakeBool(recv);
614 return var;
615 `}
616 end
617
618 redef class String
619 super Pepperable
620
621 # Converts a String into a PepperVar with String type.
622 redef fun to_pepper: PepperVar import String.to_cstring, String.length `{
623 char *str = String_to_cstring(recv);
624 struct PP_Var* var = malloc( sizeof( struct PP_Var ) );
625 *var = g_varInterface->VarFromUtf8(str, String_length(recv));
626 return var;
627 `}
628 end
629
630 # A stream for PNaCl, redefines basic input and output methods.
631 class PnaclStream
632 super PollableIStream
633 super OStream
634 super BufferedIStream
635
636 init do prepare_buffer(10)
637
638 redef var end_reached: Bool = false
639
640 redef fun eof do return end_reached
641
642 # Redefintion of 'write' to send messages to the browser.
643 redef fun write(s: Text) do app.post_message s.to_s
644
645 redef fun is_writable: Bool do return true
646
647 # Checks if there is a message in the queue, and if so the message is handled automatically.
648 fun check_message: NativeString `{
649 return NitHandleMessage();
650 `}
651
652 # fill_buffer now checks for a message in the message queue which is filled by user inputs.
653 redef fun fill_buffer
654 do
655 _buffer.clear
656 _buffer_pos = 0
657 _buffer.append check_message.to_s
658 end
659 end
660
661 # For a PNaCl app, Sys uses PnaclStreams.
662 redef class Sys
663 fun pnacl_stdstr: PnaclStream do return once new PnaclStream
664
665 # NaCl input.
666 redef fun stdin do return pnacl_stdstr
667
668 # NaCl output.
669 redef fun stdout do return pnacl_stdstr
670
671 # NaCl output for errors.
672 redef fun stderr do return pnacl_stdstr
673 end
674
675 # Class that provides the tools to interact with PNaCl.
676 class PnaclApp
677
678 # Sets everything up to work, need to be called at first.
679 fun initialize import PnaclApp.handle_message, PnaclApp.handle_dictionary, NativeString.to_s_with_length `{
680 app = recv;
681 `}
682
683 # Posts a message to JS.
684 fun post_message(message: String) import String.to_cstring `{
685 char* str = String_to_cstring(message);
686 PostMessage(str);
687 `}
688
689 # Posts a dictionary to JS.
690 fun post_dictionary(dictionary: PepperDictionary) `{
691 PostDictionary(*dictionary);
692 `}
693
694 # Posts a PepperVar to JS.
695 #
696 # Should be used for testing, not recommanded for conventional conversation.
697 private fun post_var(v: PepperVar) `{
698 PostVar(*v);
699 `}
700
701 # Is called when a message is received from JS.
702 #
703 # Is set to be redefined in your application to handle like you want.
704 fun handle_message(message: String)
705 do
706 # To be Implemented by user.
707 end
708
709 # Is called when a Dictionary is received from JS.
710 #
711 # Is set to be redefined in your application to handle like you want.
712 # The dictionary is freed after this method returns.
713 fun handle_dictionary(dictionary: PepperDictionary)
714 do
715 # To be Implemented by user.
716 end
717
718 # Checks if there is a dictionary in the queue, and if so the dictionary is handled automatically.
719 fun check_dictionary `{
720 NitHandleDictionary();
721 `}
722
723 # Infinite loop on check_dictionary
724 fun run
725 do
726 loop
727 check_dictionary
728 end
729 end
730 end
731
732 # Creates a new thread for Nit.
733 #
734 # This function launches the Nit main on a new thread.
735 # Its purpose is to allow Nit to be still operational after an exit when needed,
736 # because reloading the page may not be an option.
737 #
738 # Should only be used within the 'exit' before stopping the current thread
739 # when the Nit execution causes a crash.
740 #
741 # REQUIRE: g_nit_thread and WrapperNitMain are set.
742 fun create_thread `{
743 pthread_create(&g_nit_thread, NULL, &WrapperNitMain, NULL);
744 `}
745
746 # Calls 'pthread_exit on current thread.
747 fun exit_thread(exit_value: Int) `{
748 pthread_exit((void*) exit_value);
749 `}
750
751 # Redef of exit in order to avoid the module to crash by terminating only the Nit thread.
752 redef fun exit(exit_value: Int)
753 do
754 var dictionary = new PepperDictionary
755 dictionary["exit"] = exit_value
756 app.post_dictionary dictionary
757 exit_thread exit_value
758 end
759
760 fun app: PnaclApp do return once new PnaclApp