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