{"id":409,"date":"2015-10-01T14:44:02","date_gmt":"2015-10-01T13:44:02","guid":{"rendered":"http:\/\/www.uncovergame.com\/?p=409"},"modified":"2023-02-12T18:00:53","modified_gmt":"2023-02-12T17:00:53","slug":"building-oflive-interacting-between-javascript-and-c-code","status":"publish","type":"post","link":"https:\/\/uncovergame.com\/fr\/2015\/10\/01\/building-oflive-interacting-between-javascript-and-c-code\/","title":{"rendered":"Conception de ofLive : Interaction entre JavaScript et C"},"content":{"rendered":"<p>J&rsquo;ai r\u00e9cemment mis en ligne <a title=\"ofLive\" href=\"http:\/\/oflive-dlan.rhcloud.com\/\" target=\"_blank\" rel=\"noopener\">ofLive<\/a>, un outil permettant de coder avec OpenFrameworks directement dans un navigateur via l&rsquo;\u00e9diteur de code HTML5 <a title=\"D\u00e9p\u00f4t ACE\" href=\"https:\/\/github.com\/ajaxorg\/ace-builds\/\" target=\"_blank\" rel=\"noopener\">ACE<\/a>.<\/p>\n<p>Lors de la cr\u00e9ation de cet outil, le principal probl\u00e8me \u00e0 r\u00e9soudre \u00e9tait comment faire communiquer le code g\u00e9n\u00e9r\u00e9 par Emscripten avec du code JavaScript classique. J&rsquo;explique ci-apr\u00e8s comment je m&rsquo;y suis pris via la cr\u00e9ation d&rsquo;une librairie JavaScript pour Emscripten.<\/p>\n<p>Pour r\u00e9aliser ofLive, j&rsquo;utilise les librairies suivantes :<\/p>\n<ul>\n<li>La derni\u00e8re version nightly de OpenFrameworks et du plugin ofxEmscripten<\/li>\n<li><a title=\"ofxLua\" href=\"https:\/\/github.com\/dlan-fr\/ofxLua\" target=\"_blank\" rel=\"noopener\">Une version l\u00e9g\u00e8rement modifi\u00e9e de ofxLua<\/a><\/li>\n<li>La partie application OpenFrameworks c++ de ofLive<\/li>\n<\/ul>\n<p>J&rsquo;ai \u00e9galement cr\u00e9\u00e9 \u00e0 cette occasion un site web en PHP\/MySQL, les sources sont disponible sur <a title=\"D\u00e9p\u00f4t ofLive\" href=\"https:\/\/github.com\/dlan-fr\/ofLive\" target=\"_blank\" rel=\"noopener\">le d\u00e9p\u00f4t GitHub de ofLive<\/a><\/p>\n<div align=\"center\">\n<h2>Conception d&rsquo;une librairie, les bases<\/h2>\n<\/div>\n<p>Pour cr\u00e9er ma librairie JavaScript compatible Emscripten, je suis d&rsquo;abord parti de la base de code suivante :<\/p>\n<pre class=\"lang:javascript decode:true\" title=\"Code JavaScript modifi\u00e9\">\r\nvar LibraryOfLive = {\r\n$OFLIVE: {\r\n},\r\n\r\neditor_init: function()\r\n{\r\n},\r\n}\r\n\r\nautoAddDeps(LibraryOfLive, '$OFLIVE');\r\nmergeInto(LibraryManager.library, LibraryOfLive);\r\n<\/pre>\n<p>Le tableau $OFLIVE est pr\u00e9vu pour contenir toute les variables qui doivent \u00eatre accessibles en dehors de Emscripten, il est notamment possible d&rsquo;y stocker des pointeurs vers des fonctions C. La fonction editor_init est prise en compte par Emscripten lors de la compilation, ce qui permet de l&rsquo;appeler dans le code C afin d&rsquo;invoquer du code JavaScript. Cette fonction doit avoir une d\u00e9claration correspondante dans la partie native de l&rsquo;application au niveau d&rsquo;un fichier d&rsquo;en-t\u00eate .h :<\/p>\n<pre class=\"lang:c decode:true\" title=\"en-t\u00eate C\">#pragma once\r\n\r\nextern \"C\" {\r\n\t\/\/fonctions JavaScript utilis\u00e9es depuis C\r\n\textern void editor_init();\r\n}\r\n<\/pre>\n<p>La signature de la fonction C doit correspondre \u00e0 la signature de la fonction en JavaScript. Ceci fait, nous pouvons alors ajouter une nouvelle fonction \u00e0 notre librarie, il s&rsquo;agira cette fois d&rsquo;une fonction C utilisable par JavaScript. Voici le code de la librairie mis \u00e0 jour :<\/p>\n<pre class=\"lang:javascript decode:true\" title=\"Code JavaScript modifi\u00e9\">var LibraryOfLive = {\r\n    $OFLIVE: {\r\n        backend_loadlua: null,\r\n     },\r\n\r\n    editor_init: function()\r\n    {\r\n        \/\/bind c glue functions\r\n        OFLIVE.backend_loadlua = Module.cwrap('backend_loadlua','number',['string']);\r\n    },\r\n\r\n}\r\n\r\nautoAddDeps(LibraryOfLive, '$OFLIVE');\r\nmergeInto(LibraryManager.library, LibraryOfLive);\r\n<\/pre>\n<p>Ici, nous utilisons la fonction Module.cwrap pour obtenir un pointeur vers une fonction C nomm\u00e9e &lsquo;backend_loadlua&rsquo;, les param\u00e8tres qui suivent nous permettent de sp\u00e9cifier le type retour (&lsquo;number&rsquo; dans ce cas) et la liste des param\u00e8tres de la fonction C (ici un seul param\u00e8tre &lsquo;string&rsquo;). Le r\u00e9sultat est stock\u00e9 dans notre variable backend_loadlua du tableau $OFLIVE, ce qui nous permet d&rsquo;appeler cette fonction directement en JavaScript avec le code suivant :<\/p>\n<pre class=\"lang:javascript decode:true\" title=\"appel de fonction C\">OFLIVE.backend_loadlua('ici notre code lua')\r\n<\/pre>\n<p>Pour finir, nous devons bien \u00e9videmment ajouter cette fonction C dans notre code natif, voici l&rsquo;en-t\u00eate mis \u00e0 jour :<\/p>\n<pre class=\"lang:C decode:true\" title=\"en-t\u00eate modifi\u00e9\">#pragma once\r\n\r\nextern \"C\" {\r\n\t\/\/fonctions JavaScript utilis\u00e9es depuis C\r\n\textern void editor_init();\r\n\r\n\t\/\/fonctions C utilis\u00e9es depuis JavaScript\r\n\tint backend_loadlua(const char* scriptcontent_from_js);\r\n}\r\n<\/pre>\n<p>et le contenu de la fonction dans le fichier source :<\/p>\n<pre class=\"lang:Cpp decode:true\" title=\"impl\u00e9mentation fonction\">int backend_loadlua(const char* scriptcontent_from_js)\r\n{\r\n       std::string script_content(scriptcontent_from_js);\r\n\tofLogError() &lt;&lt; script_content;\r\n}\r\n<\/pre>\n<p>C&rsquo;est tout pour la partie code, il nous faut maintenant pr\u00e9ciser \u00e0 Emscripten o\u00f9 se trouve notre librairie et les fonctions \u00e0 exporter. Le param\u00e8tre \u00ab\u00a0&#8211;js_library\u00a0\u00bb suivi du chemin vers notre librairie permet de l&rsquo;int\u00e9grer \u00e0 l&rsquo;application Emscripten finale. Le param\u00e8tre \u00ab\u00a0-s EXPORTED_FUNCTIONS\u00a0\u00bb permet de sp\u00e9cifier la liste des fonctions C \u00e0 exporter vers JavaScript.<\/p>\n<p>N&rsquo;oubliez pas d&rsquo;ajouter un tiret-bas en face du nom de chaque fonctions et aussi d&rsquo;ajouter \u00e0 la liste la fonction \u00ab\u00a0main\u00a0\u00bb, que Emscripten n&rsquo;ajoute pas automatiquement dans notre cas. (Sinon notre application ne pourra pas d\u00e9marrer au chargement de la page Web)<\/p>\n<p><code><br \/>\n--js-library \".\\library_editor.js\" -s EXPORTED_FUNCTIONS='[\"_main\",\"_backend_loadlua\"]'<br \/>\n<\/code><\/p>\n<p>Voici le code final de la librairie et l&rsquo;en-t\u00eate .h tels qu&rsquo;ils apparaissent dans ofLive :<\/p>\n<pre class=\"lang:javascript decode:true\" title=\"Code final de la librairie\">var LibraryOfLive = {\r\n    $OFLIVE: {\r\n        editor: null,\r\n        backend_loadlua: null,\r\n        backend_newscript: null,\r\n        backend_openscript: null,\r\n        backend_savescript: null,\r\n        opened_script: \"\",\r\n        readonly_script: false,\r\n     },\r\n\r\n    editor_init: function()\r\n    {\r\n        OFLIVE.editor = ace.edit(\"editor\");\r\n        OFLIVE.editor.setTheme(\"ace\/theme\/monokai\");\r\n        OFLIVE.editor.getSession().setMode(\"ace\/mode\/lua\");\r\n        \r\n        \/\/mount read\/write filesystem\r\n        FS.mkdir('\/oflivescripts');\r\n        FS.mount(IDBFS,{},'\/oflivescripts');\r\n        Module.print(\"ofLive: Start scripts sync...\");\r\n        Module.syncdone = 0;\r\n        \r\n        FS.syncfs(true,function(err) {\r\n            assert(!err);\r\n            Module.print(\"OfLive: End scripts sync\");\r\n            Module.syncdone = 1;\r\n       });\r\n       \r\n       \/\/check if we load a shared script, in this case readonly mode\r\n       if($.trim($('#share_content').text()))\r\n       {\r\n           OFLIVE.opened_script =  $('#name_script').text();\r\n           OFLIVE.readonly_script = true;\r\n           $('#save_script').attr('class','disabled_link');\r\n           $('#shared_script').attr('class','disabled_link');\r\n       }\r\n        \r\n        \/\/bind c glue functions\r\n        OFLIVE.backend_loadlua = Module.cwrap('backend_loadlua','number',['string']);\r\n        OFLIVE.backend_newscript = Module.cwrap('backend_newscript','number',['string']);\r\n        OFLIVE.backend_openscript = Module.cwrap('backend_openscript','number',['string','number','string']);\r\n        OFLIVE.backend_savescript = Module.cwrap('backend_savescript','number',['string','string']);\r\n        \r\n        \/\/custom commands\r\n        OFLIVE.editor.commands.addCommand({\r\n            name: 'saveScript',\r\n            bindKey: {win: 'Ctrl-S', mac: 'Command-S'},\r\n            exec: function(editor) {\r\n                \/\/check first if the script is read only and if a name exist\r\n                if(OFLIVE.readonly_script == true)\r\n                    return;\r\n                \r\n                \/\/no name, open name input popup\r\n                if(OFLIVE.opened_script == \"\") {\r\n                     $(\"#save_script_name\").click();\r\n                }\r\n                else {\r\n                    OFLIVE.backend_savescript($('#name_script').text(),editor.getValue());\r\n                    OFLIVE.backend_loadlua(editor.getValue());\r\n                }\r\n            },\r\n            readOnly: false\r\n        });\r\n    },\r\n\r\n\r\n    editor_loadscript: function(scriptptr) \r\n    {\r\n        var scriptcontent = Pointer_stringify(scriptptr);\r\n        OFLIVE.editor.setValue(scriptcontent);\r\n    },\r\n    \r\n    editor_isshare: function()\r\n    {\r\n        if($.trim($('#share_content').text()))\r\n            return 1;\r\n           \r\n        return 0;\r\n    },\r\n\r\n}\r\n\r\nautoAddDeps(LibraryOfLive, '$OFLIVE');\r\nmergeInto(LibraryManager.library, LibraryOfLive);\r\n<\/pre>\n<p>Derni\u00e8re pr\u00e9cisions : Il est possible d&rsquo;utiliser du code jQuery dans notre librairie mais SEULEMENT \u00e0 l&rsquo;int\u00e9rieur d&rsquo;une fonction. Les fonctions editor_* ne sont PAS accessibles depuis JavaScript, seule les fonctions que l&rsquo;on souhaite appeler via C sont d\u00e9finies \u00e0 ce niveau.<\/p>\n<pre class=\"lang:C decode:true\" title=\"code final de l'en-t\u00eate\">#pragma once\r\n\r\nextern \"C\" {\r\n\t\/\/functions calling javascript library from c\r\n\textern void editor_init();\r\n\textern void editor_loadscript(const char* scriptcontent);\r\n\textern int editor_isshare();\r\n\r\n\t\/\/functions calling c code from javascript\r\n\tint backend_loadlua(const char* scriptcontent);\r\n\tint backend_newscript(const char* script_name);\r\n\tint backend_openscript(const char* script_name,int isExample,const char* type);\r\n\tint backend_savescript(const char* script_name,const char* scriptcontent);\r\n}\r\n<\/pre>\n<p>Plus de d\u00e9tails sur l&rsquo;utilisation de fonctions C en JavaScript dans la <a title=\"Documentation de Emscripten\" href=\"http:\/\/kripken.github.io\/emscripten-site\/docs\/api_reference\/preamble.js.html\" target=\"_blank\" rel=\"noopener\">documentation officielle (EN) <\/a>.<\/p>","protected":false},"excerpt":{"rendered":"<p>J&rsquo;ai r\u00e9cemment mis en ligne ofLive, un outil permettant de coder avec OpenFrameworks directement dans un navigateur via l&rsquo;\u00e9diteur de code HTML5 ACE. Lors de la cr\u00e9ation de cet outil, le principal probl\u00e8me \u00e0 r\u00e9soudre \u00e9tait comment faire communiquer le &hellip; <a href=\"https:\/\/uncovergame.com\/fr\/2015\/10\/01\/building-oflive-interacting-between-javascript-and-c-code\/\">Continuer la lecture <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[6,7,9],"tags":[],"_links":{"self":[{"href":"https:\/\/uncovergame.com\/fr\/wp-json\/wp\/v2\/posts\/409"}],"collection":[{"href":"https:\/\/uncovergame.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/uncovergame.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/uncovergame.com\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/uncovergame.com\/fr\/wp-json\/wp\/v2\/comments?post=409"}],"version-history":[{"count":29,"href":"https:\/\/uncovergame.com\/fr\/wp-json\/wp\/v2\/posts\/409\/revisions"}],"predecessor-version":[{"id":519,"href":"https:\/\/uncovergame.com\/fr\/wp-json\/wp\/v2\/posts\/409\/revisions\/519"}],"wp:attachment":[{"href":"https:\/\/uncovergame.com\/fr\/wp-json\/wp\/v2\/media?parent=409"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/uncovergame.com\/fr\/wp-json\/wp\/v2\/categories?post=409"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/uncovergame.com\/fr\/wp-json\/wp\/v2\/tags?post=409"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}