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