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