blob: cb3ad75c30415809f319aa68dd738b79dd41dffb [file] [edit]
/*
* GPAC - Multimedia Framework C SDK
*
* Authors: Jean Le Feuvre
* Copyright (c) Telecom ParisTech 2000-2026
* All rights reserved
*
* This file is part of GPAC / Scene Graph sub-project
*
* GPAC is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* GPAC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <gpac/internal/scenegraph_dev.h>
#ifdef GPAC_HAS_QJS
#include <gpac/internal/compositor_dev.h>
#include <gpac/modules/compositor_ext.h>
#include "qjs_common.h"
typedef struct
{
u8 is_setup;
/*set to 1 for proto IS fields*/
u8 IS_route;
/*set to 1 for JS route to fun*/
u8 script_route;
u32 ID;
char *name;
/*scope of this route*/
GF_SceneGraph *graph;
u32 lastActivateTime;
GF_Node *FromNode;
GF_FieldInfo FromField;
GF_Node *ToNode;
GF_FieldInfo ToField;
JSValue obj;
JSValue fun;
} GF_RouteToScript;
typedef struct __gf_js_field
{
GF_FieldInfo field;
GF_Node *owner;
JSValue obj;
/*list of JSValue * for MFFields (except MFNode) or NULL
we need this to keep the link between an MF slot and the parent node since everything is passed by reference*/
JSValue *mfvals;
u32 mfvals_count;
/*pointer to the SFNode if this is an SFNode or MFNode[i] field */
GF_Node *node;
/*when creating MFnode from inside the script, the node list is stored here until attached to an object*/
GF_ChildNodeItem *temp_list;
/*only set when not owned by a node, in which case field.far_ptr is also set to this value*/
void *field_ptr;
/*context in which the field was created*/
struct JSContext *js_ctx;
} GF_JSField;
JSValue js_throw_err_msg(JSContext *ctx, s32 err, const char *fmt, ...)
{
JSValue obj = JS_NewError(ctx);
if (JS_IsException(obj)) {
obj = JS_NULL;
} else {
JS_DefinePropertyValueStr(ctx, obj, "code", JS_NewInt32(ctx, err), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
if (fmt) {
char szMsg[2050];
va_list vl;
va_start(vl, fmt);
vsnprintf(szMsg, 2048, fmt, vl);
va_end(vl);
JS_DefinePropertyValueStr(ctx, obj, "message", JS_NewString(ctx, szMsg), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
}
}
return JS_Throw(ctx, obj);
}
JSValue js_throw_err(JSContext *ctx, s32 err)
{
JSValue obj = JS_NewError(ctx);
if (JS_IsException(obj)) {
obj = JS_NULL;
} else {
JS_DefinePropertyValueStr(ctx, obj, "code", JS_NewInt32(ctx, err), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
}
return JS_Throw(ctx, obj);
}
#define _ScriptMessage(_c, _msg) { \
GF_Node *_n = (GF_Node *) JS_GetContextOpaque(_c); \
if (_n->sgprivate->scenegraph->script_action) {\
GF_JSAPIParam par; \
par.info.e = GF_SCRIPT_INFO; \
par.info.msg = (_msg); \
_n->sgprivate->scenegraph->script_action(_n->sgprivate->scenegraph->script_action_cbck, GF_JSAPI_OP_MESSAGE, NULL, &par);\
} \
}
static Bool ScriptAction(JSContext *c, GF_SceneGraph *scene, u32 type, GF_Node *node, GF_JSAPIParam *param)
{
#ifndef GPAC_DISABLE_COMPOSITOR
if (!scene) {
GF_Node *n = (GF_Node *) JS_GetContextOpaque(c);
scene = n->sgprivate->scenegraph;
}
if (scene->script_action)
return scene->script_action(scene->script_action_cbck, type, node, param);
#endif// GPAC_DISABLE_COMPOSITOR
return 0;
}
GF_JSClass SFNodeClass;
#ifndef GPAC_DISABLE_VRML
GF_JSClass globalClass;
GF_JSClass browserClass;
GF_JSClass SFVec2fClass;
GF_JSClass SFVec3fClass;
GF_JSClass SFRotationClass;
GF_JSClass SFColorClass;
GF_JSClass SFImageClass;
GF_JSClass MFInt32Class;
GF_JSClass MFBoolClass;
GF_JSClass MFFloatClass;
GF_JSClass MFTimeClass;
GF_JSClass MFVec2fClass;
GF_JSClass MFVec3fClass;
GF_JSClass MFRotationClass;
GF_JSClass MFColorClass;
GF_JSClass MFStringClass;
GF_JSClass MFUrlClass;
GF_JSClass MFNodeClass;
GF_JSClass AnyClass;
#endif
#ifndef GPAC_DISABLE_SVG
/*SVG tags for script handling*/
#include <gpac/nodes_svg.h>
GF_Node *dom_get_element(JSContext *c, JSValue obj);
#endif
void gf_sg_script_to_node_field(struct JSContext *c, JSValue v, GF_FieldInfo *field, GF_Node *owner, GF_JSField *parent);
JSValue gf_sg_script_to_qjs_field(GF_ScriptPriv *priv, GF_FieldInfo *field, GF_Node *parent, Bool force_evaluate);
static void JSScript_NodeModified(GF_SceneGraph *sg, GF_Node *node, GF_FieldInfo *info, GF_Node *script);
Bool JSScriptFromFile(GF_Node *node, const char *opt_file, Bool no_complain, JSValue *rval);
#ifndef GPAC_DISABLE_SVG
static JSValue vrml_event_add_listener(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv);
static JSValue vrml_event_remove_listener(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv);
#endif // GPAC_DISABLE_SVG
void do_js_gc(JSContext *c, GF_Node *node)
{
#ifdef FORCE_GC
node->sgprivate->scenegraph->trigger_gc = GF_TRUE;
#endif
if (node->sgprivate->scenegraph->trigger_gc) {
node->sgprivate->scenegraph->trigger_gc = GF_FALSE;
gf_js_call_gc(c);
}
}
#ifndef GPAC_DISABLE_VRML
/*MPEG4 & X3D tags (for node tables & script handling)*/
#include <gpac/nodes_mpeg4.h>
#include <gpac/nodes_x3d.h>
static void SFColor_fromHSV(SFColor *col)
{
Fixed f, q, t, p, hue, sat, val;
u32 i;
hue = col->red;
sat = col->green;
val = col->blue;
if (sat==0) {
col->red = col->green = col->blue = val;
return;
}
if (hue == FIX_ONE) hue = 0;
else hue *= 6;
i = FIX2INT( gf_floor(hue) );
f = hue-i;
p = gf_mulfix(val, FIX_ONE - sat);
q = gf_mulfix(val, FIX_ONE - gf_mulfix(sat,f));
t = gf_mulfix(val, FIX_ONE - gf_mulfix(sat, FIX_ONE - f));
switch (i) {
case 0:
col->red = val;
col->green = t;
col->blue = p;
break;
case 1:
col->red = q;
col->green = val;
col->blue = p;
break;
case 2:
col->red = p;
col->green = val;
col->blue = t;
break;
case 3:
col->red = p;
col->green = q;
col->blue = val;
break;
case 4:
col->red = t;
col->green = p;
col->blue = val;
break;
case 5:
col->red = val;
col->green = p;
col->blue = q;
break;
}
}
static void SFColor_toHSV(SFColor *col)
{
Fixed h, s;
Fixed _max = MAX(col->red, MAX(col->green, col->blue));
Fixed _min = MIN(col->red, MAX(col->green, col->blue));
s = (_max == 0) ? 0 : gf_divfix(_max - _min, _max);
if (s != 0) {
Fixed rl = gf_divfix(_max - col->red, _max - _min);
Fixed gl = gf_divfix(_max - col->green, _max - _min);
Fixed bl = gf_divfix(_max - col->blue, _max - _min);
if (_max == col->red) {
if (_min == col->green) h = 60*(5+bl);
else h = 60*(1-gl);
} else if (_max == col->green) {
if (_min == col->blue) h = 60*(1+rl);
else h = 60*(3-bl);
} else {
if (_min == col->red) h = 60*(3+gl);
else h = 60*(5-rl);
}
} else {
h = 0;
}
col->red = h;
col->green = s;
col->blue = _max;
}
static GFINLINE GF_JSField *NewJSField(JSContext *c)
{
GF_JSField *ptr;
GF_SAFEALLOC(ptr, GF_JSField);
if (!ptr) return NULL;
ptr->js_ctx = c;
ptr->obj = JS_UNDEFINED;
return ptr;
}
static GFINLINE M_Script *JS_GetScript(JSContext *c)
{
return (M_Script *) JS_GetContextOpaque(c);
}
static GFINLINE GF_ScriptPriv *JS_GetScriptStack(JSContext *c)
{
M_Script *script = (M_Script *) JS_GetContextOpaque(c);
return script->sgprivate->UserPrivate;
}
static JSValue getName(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
return JS_NewString(ctx, "GPAC");
}
static JSValue getVersion(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
return JS_NewString(ctx, gf_gpac_version());
}
static JSValue getCurrentSpeed(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
GF_JSAPIParam par;
GF_Node *node = JS_GetContextOpaque(ctx);
par.time = 0;
ScriptAction(ctx, NULL, GF_JSAPI_OP_GET_SPEED, node->sgprivate->scenegraph->RootNode, &par);
return JS_NewFloat64(ctx, par.time);
}
static JSValue getCurrentFrameRate(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
GF_JSAPIParam par;
GF_Node *node = JS_GetContextOpaque(ctx);
par.time = 0;
ScriptAction(ctx, NULL, GF_JSAPI_OP_GET_FPS, node->sgprivate->scenegraph->RootNode, &par);
return JS_NewFloat64(ctx, par.time);
}
static JSValue getWorldURL(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
GF_JSAPIParam par;
GF_Node *node = JS_GetContextOpaque(ctx);
par.uri.url = NULL;
par.uri.nb_params = 0;
if (ScriptAction(ctx, NULL, GF_JSAPI_OP_RESOLVE_URI, node->sgprivate->scenegraph->RootNode, &par)) {
JSValue ret = JS_NewString(ctx, par.uri.url);
gf_free(par.uri.url);
return ret;
}
return JS_UNDEFINED;
}
static JSValue node_get_binding(GF_ScriptPriv *priv, GF_Node *node)
{
GF_JSField *field;
if (!node) return JS_NULL;
if (node->sgprivate->interact && node->sgprivate->interact->js_binding && node->sgprivate->interact->js_binding->pf) {
field = node->sgprivate->interact->js_binding->pf;
gf_assert(JS_IsObject(field->obj));
gf_assert(JS_GetOpaque(field->obj, SFNodeClass.class_id)!=NULL);
return field->obj;
}
field = NewJSField(priv->js_ctx);
field->field.fieldType = GF_SG_VRML_SFNODE;
field->node = node;
field->field.far_ptr = &field->node;
node->sgprivate->flags |= GF_NODE_HAS_BINDING;
gf_node_register(node, NULL);
field->obj = JS_NewObjectClass(priv->js_ctx, SFNodeClass.class_id);
JS_SetOpaque(field->obj, field);
gf_list_add(priv->jsf_cache, field);
/*remember the object*/
if (!node->sgprivate->interact) {
GF_SAFEALLOC(node->sgprivate->interact, struct _node_interactive_ext);
if (!node->sgprivate->interact) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRMLJS] Failed to create interact storage\n"));
return GF_JS_EXCEPTION(priv->js_ctx);
}
}
if (!node->sgprivate->interact->js_binding) {
GF_SAFEALLOC(node->sgprivate->interact->js_binding, struct _node_js_binding);
if (!node->sgprivate->interact->js_binding) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRMLJS] Failed to create JS bindings storage\n"));
return GF_JS_EXCEPTION(priv->js_ctx);
}
node->sgprivate->interact->js_binding->fields = gf_list_new();
}
node->sgprivate->flags |= GF_NODE_HAS_BINDING;
node->sgprivate->interact->js_binding->pf = field;
return field->obj;
}
static JSValue getScript(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
{
GF_ScriptPriv *priv = JS_GetScriptStack(c);
GF_Node *node = JS_GetContextOpaque(c);
return JS_DupValue(c, node_get_binding(priv, node) );
}
static JSValue loadScript(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
{
Bool no_complain = 0;
const char *url;
GF_Node *node = JS_GetContextOpaque(c);
JSValue aval = JS_UNDEFINED;
if (!node || !argc || !JS_IsString(argv[0])) return GF_JS_EXCEPTION(c);
if ((argc>1) && JS_IsBool(argv[1])) no_complain = (JS_ToBool(c,argv[1])) ? 1 : 0;
url = JS_ToCString(c, argv[0]);
if (url) {
JSScriptFromFile(node, url, no_complain, &aval);
}
JS_FreeCString(c, url);
return aval;
}
static JSValue getProto(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
{
GF_ScriptPriv *priv = JS_GetScriptStack(c);
GF_Node *node = JS_GetContextOpaque(c);
if (!node->sgprivate->scenegraph->pOwningProto) {
return JS_NULL;
}
node = (GF_Node *) node->sgprivate->scenegraph->pOwningProto;
return JS_DupValue(c, node_get_binding(priv, node));
}
#ifndef GPAC_DISABLE_SVG
GF_Node *gf_sm_load_svg_from_string(GF_SceneGraph *sg, char *svg_str);
#endif
static JSValue vrml_parse_xml(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
{
#ifndef GPAC_DISABLE_SVG
GF_SceneGraph *sg;
GF_Node *node;
const char *str;
str = JS_ToCString(c, argv[0]);
if (!str) return JS_TRUE;
node = JS_GetContextOpaque(c);
sg = node->sgprivate->scenegraph;
node = gf_sm_load_svg_from_string(sg, (char *) str);
JS_FreeCString(c, str);
return dom_element_construct(c, node);
#else
return GF_JS_EXCEPTION(c);
#endif
}
static JSValue getElementById(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
{
GF_Node *elt;
const char *name = NULL;
u32 ID = 0;
GF_ScriptPriv *priv = JS_GetScriptStack(c);
GF_Node *sc = JS_GetContextOpaque(c);
if (JS_IsString(argv[0])) name = JS_ToCString(c, argv[0]);
else if (JS_IsInteger(argv[0])) {
if (JS_ToInt32(c, &ID, argv[0]))
return GF_JS_EXCEPTION(c);
}
if (!ID && !name) return GF_JS_EXCEPTION(c);
elt = NULL;
if (ID) elt = gf_sg_find_node(sc->sgprivate->scenegraph, ID);
else elt = gf_sg_find_node_by_name(sc->sgprivate->scenegraph, (char *) name);
JS_FreeCString(c, name);
return JS_DupValue(c, node_get_binding(priv, elt));
}
static JSValue replaceWorld(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
{
return JS_TRUE;
}
static void on_route_to_object(GF_Node *node, GF_Route *_r)
{
JSValue argv[2], rval;
Double time;
GF_FieldInfo t_info;
GF_ScriptPriv *priv;
JSValue obj;
GF_RouteToScript *r = (GF_RouteToScript *)_r;
if (!node)
return;
priv = gf_node_get_private(node);
if (!priv)
return;
if (!r->FromNode) {
JS_FreeValue(priv->js_ctx, r->fun);
r->fun = JS_UNDEFINED;
JS_FreeValue(priv->js_ctx, r->obj);
r->obj = JS_UNDEFINED;
return;
}
obj = r->obj;
if (JS_IsUndefined(obj) || JS_IsNull(obj))
obj = priv->js_obj;
memset(&t_info, 0, sizeof(GF_FieldInfo));
time = gf_node_get_scene_time(node);
t_info.far_ptr = &time;
t_info.fieldType = GF_SG_VRML_SFTIME;
t_info.fieldIndex = -1;
t_info.name = "timestamp";
gf_js_lock(priv->js_ctx, 1);
argv[1] = gf_sg_script_to_qjs_field(priv, &t_info, node, 1);
argv[0] = gf_sg_script_to_qjs_field(priv, &r->FromField, r->FromNode, 1);
rval = JS_Call(priv->js_ctx, r->fun, obj, 2, argv);
if (JS_IsException(rval))
js_dump_error(priv->js_ctx);
JS_FreeValue(priv->js_ctx, argv[0]);
JS_FreeValue(priv->js_ctx, argv[1]);
JS_FreeValue(priv->js_ctx, rval);
js_std_loop(priv->js_ctx);
gf_js_lock(priv->js_ctx, 0);
do_js_gc(priv->js_ctx, node);
}
static JSValue addRoute(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
{
GF_JSField *ptr;
GF_Node *n1, *n2;
const char *f1;
GF_FieldInfo info;
u32 f_id1, f_id2;
GF_Err e;
if (argc!=4) return GF_JS_EXCEPTION(c);
ptr = (GF_JSField *) JS_GetOpaque(argv[0], SFNodeClass.class_id);
if (!ptr) return GF_JS_EXCEPTION(c);
gf_assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
n1 = * ((GF_Node **)ptr->field.far_ptr);
if (!n1) return GF_JS_EXCEPTION(c);
n2 = NULL;
if (!JS_IsString(argv[1])) return GF_JS_EXCEPTION(c);
f1 = JS_ToCString(c, argv[1]);
if (!f1) return JS_FALSE;
if (!strnicmp(f1, "_field", 6)) {
f_id1 = atoi(f1+6);
e = gf_node_get_field(n1, f_id1, &info);
} else {
e = gf_node_get_field_by_name(n1, (char *) f1, &info);
f_id1 = info.fieldIndex;
}
JS_FreeCString(c, f1);
if (e != GF_OK) return GF_JS_EXCEPTION(c);
if (!JS_IsObject(argv[2])) return GF_JS_EXCEPTION(c);
ptr = (GF_JSField *) JS_GetOpaque(argv[2], SFNodeClass.class_id);
/*regular route*/
if (ptr && JS_IsString(argv[3]) ) {
const char *f2;
GF_Route *r;
gf_assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
n2 = * ((GF_Node **)ptr->field.far_ptr);
if (!n2) return GF_JS_EXCEPTION(c);
f2 = JS_ToCString(c, argv[3]);
if (!f2) return GF_JS_EXCEPTION(c);
if (!strnicmp(f2, "_field", 6)) {
f_id2 = atoi(f2+6);
e = gf_node_get_field(n2, f_id2, &info);
} else {
if ((n2->sgprivate->tag==TAG_MPEG4_Script)
#ifndef GPAC_DISABLE_X3D
|| (n2->sgprivate->tag==TAG_X3D_Script)
#endif
) {
GF_FieldInfo src = info;
if (gf_node_get_field_by_name(n2, (char *) f2, &info) != GF_OK) {
gf_sg_script_field_new(n2, GF_SG_SCRIPT_TYPE_EVENT_IN, src.fieldType, f2);
}
}
e = gf_node_get_field_by_name(n2, (char *) f2, &info);
f_id2 = info.fieldIndex;
}
JS_FreeCString(c, f2);
if (e != GF_OK) return GF_JS_EXCEPTION(c);
r = gf_sg_route_new(n1->sgprivate->scenegraph, n1, f_id1, n2, f_id2);
if (!r) return GF_JS_EXCEPTION(c);
}
/*route to object*/
else {
u32 i = 0;
const char *fun_name;
char *fun_name_dup=NULL;
JSAtom atom;
GF_RouteToScript *r = NULL;
if (!JS_IsFunction(c, argv[3]) ) return GF_JS_EXCEPTION(c);
atom = JS_ValueToAtom(c, argv[3]);
fun_name = JS_AtomToCString(c, atom);
if (fun_name && n1->sgprivate->interact && n1->sgprivate->interact->routes ) {
while ( (r = (GF_RouteToScript*)gf_list_enum(n1->sgprivate->interact->routes, &i) )) {
if ( (r->FromNode == n1)
&& (r->FromField.fieldIndex == f_id1)
&& (r->ToNode == (GF_Node*)JS_GetScript(c))
&& !stricmp(r->ToField.name, fun_name)
)
break;
}
}
if (!r) fun_name_dup = gf_strdup(fun_name);
JS_FreeCString(c, fun_name);
JS_FreeAtom(c, atom);
if ( !r ) {
GF_SAFEALLOC(r, GF_RouteToScript)
if (!r) {
if (fun_name_dup) gf_free(fun_name_dup);
return JS_FALSE;
}
r->script_route = 1;
r->FromNode = n1;
r->FromField.fieldIndex = f_id1;
gf_node_get_field(r->FromNode, f_id1, &r->FromField);
r->ToNode = (GF_Node*)JS_GetScript(c);
r->ToField.fieldType = GF_SG_VRML_SCRIPT_FUNCTION;
r->ToField.on_event_in = on_route_to_object;
r->ToField.eventType = GF_SG_EVENT_IN;
r->ToField.far_ptr = NULL;
//store fun text val in name, route to obj cannot have their ID set
r->name = fun_name_dup;
r->ToField.name = r->name;
r->obj = JS_DupValue(c, argv[2]);
r->fun = JS_DupValue(c, argv[3]);
r->is_setup = 1;
r->graph = n1->sgprivate->scenegraph;
if (!n1->sgprivate->interact) {
GF_SAFEALLOC(n1->sgprivate->interact, struct _node_interactive_ext);
if (!n1->sgprivate->interact) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRMLJS] Failed to create interact storage\n"));
if (fun_name_dup) gf_free(fun_name_dup);
gf_free(r);
return GF_JS_EXCEPTION(c);
}
}
if (!n1->sgprivate->interact->routes) n1->sgprivate->interact->routes = gf_list_new();
gf_list_add(n1->sgprivate->interact->routes, r);
gf_list_add(n1->sgprivate->scenegraph->Routes, r);
}
}
return JS_UNDEFINED;
}
static JSValue deleteRoute(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
{
GF_JSField *ptr;
GF_Node *n1, *n2;
const char *f1, *f2;
GF_FieldInfo info;
GF_RouteToScript *rts;
GF_Err e;
u32 f_id1, f_id2, i;
if (argc!=4) return JS_FALSE;
if (!JS_IsObject(argv[0]) || JS_IsNull(argv[0]))
return GF_JS_EXCEPTION(c);
ptr = JS_GetOpaque(argv[0], SFNodeClass.class_id);
if (!ptr) return GF_JS_EXCEPTION(c);
gf_assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
if (JS_IsString(argv[1]) && JS_IsNull(argv[2]) && JS_IsNull(argv[3])) {
n1 = * ((GF_Node **)ptr->field.far_ptr);
f1 = JS_ToCString(c, argv[1]);
if (!strcmp(f1, "ALL")) {
while (n1->sgprivate->interact && n1->sgprivate->interact->routes && gf_list_count(n1->sgprivate->interact->routes) ) {
GF_Route *r = gf_list_get(n1->sgprivate->interact->routes, 0);
gf_sg_route_del(r);
}
}
JS_FreeCString(c, f1);
return JS_UNDEFINED;
}
if (!JS_IsString(argv[1]) || !JS_IsString(argv[3])) return GF_JS_EXCEPTION(c);
n1 = * ((GF_Node **)ptr->field.far_ptr);
ptr = JS_GetOpaque(argv[2], SFNodeClass.class_id);
if (!ptr) return GF_JS_EXCEPTION(c);
gf_assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
n2 = * ((GF_Node **)ptr->field.far_ptr);
if (!n1 || !n2) return GF_JS_EXCEPTION(c);
if (!n1->sgprivate->interact) return JS_UNDEFINED;
f1 = JS_ToCString(c, argv[1]);
f2 = JS_ToCString(c, argv[3]);
if (!f1 || !f2) {
JS_FreeCString(c, f1);
JS_FreeCString(c, f2);
return GF_JS_EXCEPTION(c);
}
if (!strnicmp(f1, "_field", 6)) {
f_id1 = atoi(f1+6);
e = gf_node_get_field(n1, f_id1, &info);
} else {
e = gf_node_get_field_by_name(n1, (char *)f1, &info);
f_id1 = info.fieldIndex;
}
JS_FreeCString(c, f1);
if (e != GF_OK) return GF_JS_EXCEPTION(c);
if (!strnicmp(f2, "_field", 6)) {
f_id2 = atoi(f2+6);
e = gf_node_get_field(n2, f_id2, &info);
} else {
e = gf_node_get_field_by_name(n2, (char *)f2, &info);
f_id2 = info.fieldIndex;
}
JS_FreeCString(c, f2);
if (e != GF_OK) return GF_JS_EXCEPTION(c);
i=0;
while ((rts = gf_list_enum(n1->sgprivate->interact->routes, &i))) {
if (rts->FromField.fieldIndex != f_id1) continue;
if (rts->ToNode != n2) continue;
if (rts->ToField.fieldIndex != f_id2) continue;
JS_FreeValue(c, rts->fun);
JS_FreeValue(c, rts->obj);
gf_sg_route_del((GF_Route *) rts);
return JS_UNDEFINED;
}
return JS_UNDEFINED;
}
static JSValue loadURL(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
{
JSClassID _classID;
u32 i;
GF_JSAPIParam par;
GF_JSField *f;
M_Script *script = (M_Script *) JS_GetContextOpaque(c);
if (argc < 1) return GF_JS_EXCEPTION(c);
if (JS_IsString(argv[0])) {
Bool res;
par.uri.url = (char *) JS_ToCString(c, argv[0]);
/*TODO add support for params*/
par.uri.nb_params = 0;
res = ScriptAction(c, NULL, GF_JSAPI_OP_LOAD_URL, (GF_Node *)script, &par);
JS_FreeCString(c, par.uri.url);
return res ? JS_TRUE : JS_FALSE;
}
if (!JS_IsObject(argv[0])) return GF_JS_EXCEPTION(c);
f = (GF_JSField *) JS_GetAnyOpaque(argv[0], &_classID);
if (!f || !f->mfvals) return GF_JS_EXCEPTION(c);
for (i=0; i<f->mfvals_count; i++) {
Bool res=GF_FALSE;
JSValue item = f->mfvals[i];
if (JS_IsString(item)) {
par.uri.url = (char *) JS_ToCString(c, item);
/*TODO add support for params*/
par.uri.nb_params = 0;
res = ScriptAction(c, NULL, GF_JSAPI_OP_LOAD_URL, (GF_Node*)script, &par);
JS_FreeCString(c, par.uri.url);
}
if (res) return JS_TRUE;
}
return JS_FALSE;
}
static JSValue setDescription(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
{
GF_JSAPIParam par;
GF_Node *node = JS_GetContextOpaque(c);
if (!argc || !JS_IsString(argv[0])) return GF_JS_EXCEPTION(c);
par.uri.url = (char *) JS_ToCString(c, argv[0]);
ScriptAction(c, NULL, GF_JSAPI_OP_SET_TITLE, node->sgprivate->scenegraph->RootNode, &par);
JS_FreeCString(c, par.uri.url);
return JS_TRUE;
}
static JSValue createVrmlFromString(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
{
#ifndef GPAC_DISABLE_LOADER_BT
GF_ScriptPriv *priv;
GF_FieldInfo field;
JSValue res;
/*BT/VRML from string*/
GF_List *gf_sm_load_bt_from_string(GF_SceneGraph *in_scene, char *node_str, Bool force_wrl);
const char *str;
GF_List *nlist;
GF_Node *sc_node = JS_GetContextOpaque(c);
if (!sc_node || (argc < 1)) return GF_JS_EXCEPTION(c);
if (!JS_IsString(argv[0])) return GF_JS_EXCEPTION(c);
str = JS_ToCString(c, argv[0]);
nlist = gf_sm_load_bt_from_string(sc_node->sgprivate->scenegraph, (char *)str, 1);
JS_FreeCString(c, str);
if (!nlist) return GF_JS_EXCEPTION(c);
priv = JS_GetScriptStack(c);
memset(&field, 0, sizeof(GF_FieldInfo));
field.fieldType = GF_SG_VRML_MFNODE;
field.far_ptr = &nlist;
res = gf_sg_script_to_qjs_field(priv, &field, NULL, 0);
/*don't forget to unregister all this stuff*/
while (gf_list_count(nlist)) {
GF_Node *n = gf_list_get(nlist, 0);
gf_list_rem(nlist, 0);
gf_node_unregister(n, NULL);
}
gf_list_del(nlist);
return res;
#else
return GF_JS_EXCEPTION(c);
#endif
}
void gf_node_event_out_proto(GF_Node *node, u32 FieldIndex);
void Script_FieldChanged(JSContext *c, GF_Node *parent, GF_JSField *parent_owner, GF_FieldInfo *field)
{
u32 script_field;
u32 i;
if (!parent && parent_owner) {
parent = parent_owner->owner;
field = &parent_owner->field;
}
if (!parent) return;
script_field = 0;
if ((parent->sgprivate->tag == TAG_MPEG4_Script)
#ifndef GPAC_DISABLE_X3D
|| (parent->sgprivate->tag == TAG_X3D_Script)
#endif
) {
script_field = 1;
if ( (GF_Node *) JS_GetContextOpaque(c) == parent) script_field = 2;
}
if (script_field!=2) {
if (field->on_event_in) field->on_event_in(parent, NULL);
else if (script_field && (field->eventType==GF_SG_EVENT_IN) ) {
gf_sg_script_event_in(parent, field);
gf_node_changed_internal(parent, field, 0);
return;
}
/*field has changed, set routes...*/
if (parent->sgprivate->tag == TAG_ProtoNode) {
GF_ProtoInstance *inst = (GF_ProtoInstance *)parent;
gf_sg_proto_propagate_event(parent, field->fieldIndex, (GF_Node*)JS_GetScript(c));
/* Node exposedField can also be routed to another field */
gf_node_event_out_proto(parent, field->fieldIndex);
//hardcoded protos be implemented in ways not inspecting the node_dirty propagation scheme (eg defining an SFNode in their interface, not linked with the graph).
//in this case handle the node as a regular one
if (inst->flags & GF_SG_PROTO_HARDCODED) {
gf_node_changed_internal(parent, field, 0);
}
} else {
gf_node_event_out(parent, field->fieldIndex);
gf_node_changed_internal(parent, field, 0);
}
return;
}
/*otherwise mark field if eventOut*/
if (parent_owner || parent) {
GF_ScriptPriv *priv = parent ? parent->sgprivate->UserPrivate : parent_owner->owner->sgprivate->UserPrivate;
GF_ScriptField *sf;
i=0;
while ((sf = gf_list_enum(priv->fields, &i))) {
if (sf->ALL_index == field->fieldIndex) {
/*queue eventOut*/
if (sf->eventType == GF_SG_EVENT_OUT) {
sf->activate_event_out = 1;
}
}
}
}
}
static JSValue gf_sg_script_eventout_set_prop(JSContext *c, JSValueConst this_val, JSValueConst val, int magic)
{
u32 i;
GF_ScriptPriv *script;
GF_Node *n;
GF_ScriptField *sf;
script = JS_GetScriptStack(c);
if (!script) return GF_JS_EXCEPTION(c);
n = (GF_Node *) JS_GetScript(c);
if (!n) return GF_JS_EXCEPTION(c);
i=0;
while ((sf = gf_list_enum(script->fields, &i))) {
if (sf->magic==magic) {
GF_FieldInfo info;
gf_node_get_field(n, sf->ALL_index, &info);
gf_sg_script_to_node_field(c, val, &info, n, NULL);
sf->activate_event_out = 1;
return JS_UNDEFINED;
}
}
return GF_JS_EXCEPTION(c);
}
/*generic ToString method*/
static GFINLINE void sffield_toString(char **str, void *f_ptr, u32 fieldType)
{
char temp[1000];
switch (fieldType) {
case GF_SG_VRML_SFVEC2F:
{
SFVec2f val = * ((SFVec2f *) f_ptr);
sprintf(temp, "%f %f", FIX2FLT(val.x), FIX2FLT(val.y));
gf_dynstrcat(str, temp, NULL);
break;
}
case GF_SG_VRML_SFVEC3F:
{
SFVec3f val = * ((SFVec3f *) f_ptr);
sprintf(temp, "%f %f %f", FIX2FLT(val.x), FIX2FLT(val.y), FIX2FLT(val.z));
gf_dynstrcat(str, temp, NULL);
break;
}
case GF_SG_VRML_SFROTATION:
{
SFRotation val = * ((SFRotation *) f_ptr);
sprintf(temp, "%f %f %f %f", FIX2FLT(val.x), FIX2FLT(val.y), FIX2FLT(val.z), FIX2FLT(val.q));
gf_dynstrcat(str, temp, NULL);
break;
}
case GF_SG_VRML_SFCOLOR:
{
SFColor val = * ((SFColor *) f_ptr);
sprintf(temp, "%f %f %f", FIX2FLT(val.red), FIX2FLT(val.green), FIX2FLT(val.blue));
gf_dynstrcat(str, temp, NULL);
break;
}
case GF_SG_VRML_SFIMAGE:
{
SFImage *val = ((SFImage *)f_ptr);
sprintf(temp, "%dx%dx%d", val->width, val->height, val->numComponents);
gf_dynstrcat(str, temp, NULL);
break;
}
}
}
static void JS_ObjectDestroyed(JSRuntime *rt, JSValue obj, GF_JSField *ptr, Bool is_js_call)
{
JS_SetOpaque(obj, NULL);
if (!ptr) return;
/*if ptr is a node, remove node binding*/
if (ptr->node
&& ptr->node->sgprivate->interact
&& ptr->node->sgprivate->interact->js_binding
&& (ptr->node->sgprivate->interact->js_binding->pf == ptr)
) {
ptr->node->sgprivate->interact->js_binding->pf = NULL;
}
/*if ptr is a field, remove field binding from parent*/
if (ptr->owner && ptr->owner->sgprivate->interact && ptr->owner->sgprivate->interact->js_binding) {
gf_list_del_item(ptr->owner->sgprivate->interact->js_binding->fields, ptr);
}
/*
If object is still registered, remove it from the js_cache
*/
if (!JS_IsUndefined(ptr->obj) && is_js_call) {
if (ptr->js_ctx) {
GF_ScriptPriv *priv;
if (!gf_js_context_is_valid(ptr->js_ctx))
return;
priv = JS_GetScriptStack(ptr->js_ctx);
gf_list_del_item(priv->jsf_cache, ptr);
}
ptr->obj = JS_UNDEFINED;
}
}
static JSValue field_toString(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
{
u32 i;
JSValue item;
Double d;
JSClassID _classID;
char *str = NULL;
GF_JSField *f = JS_GetAnyOpaque(this_val, &_classID);
if (!f) return JS_FALSE;
if (gf_sg_vrml_is_sf_field(f->field.fieldType)) {
sffield_toString(&str, f->field.far_ptr, f->field.fieldType);
} else {
if (f->field.fieldType == GF_SG_VRML_MFNODE) return JS_TRUE;
gf_dynstrcat(&str, "[", NULL);
for (i=0; i<f->mfvals_count; i++) {
char temp[1000];
s32 ival;
item = f->mfvals[i];
switch (f->field.fieldType) {
case GF_SG_VRML_MFBOOL:
sprintf(temp, "%s", JS_ToBool(c, item) ? "TRUE" : "FALSE");
gf_dynstrcat(&str, temp, NULL);
break;
case GF_SG_VRML_MFINT32:
JS_ToInt32(c, &ival, item);
sprintf(temp, "%d", ival);
gf_dynstrcat(&str, temp, NULL);
break;
case GF_SG_VRML_MFFLOAT:
case GF_SG_VRML_MFTIME:
JS_ToFloat64(c, &d, item);
sprintf(temp, "%g", d);
gf_dynstrcat(&str, temp, NULL);
break;
case GF_SG_VRML_MFSTRING:
case GF_SG_VRML_MFURL:
{
char *str_val = (char *) JS_ToCString(c, item);
gf_dynstrcat(&str, str_val, NULL);
JS_FreeCString(c, str_val);
}
break;
default:
if (JS_IsObject(item)) {
GF_JSField *sf = (GF_JSField *) JS_GetAnyOpaque(item, &_classID);
sffield_toString(&str, sf->field.far_ptr, sf->field.fieldType);
}
break;
}
if (i < f->mfvals_count-1) gf_dynstrcat(&str, ", ", NULL);
}
gf_dynstrcat(&str, "]", NULL);
}
item = JS_NewString(c, str ? str : "");
if (str) gf_free(str);
return item;
}
static JSValue SFNodeConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
u32 tag, ID;
GF_Node *new_node;
GF_JSField *field;
GF_Proto *proto;
GF_SceneGraph *sg;
char *node_name;
GF_ScriptPriv *priv = JS_GetScriptStack(c);
M_Script *sc = JS_GetScript(c);
if (argc && !JS_IsString(argv[0]))
return GF_JS_EXCEPTION(c);
tag = 0;
if (!argc) {
JSValue obj = JS_NewObjectClass(c, SFNodeClass.class_id);
if (JS_IsException(obj)) return obj;
field = NewJSField(c);
field->field.fieldType = GF_SG_VRML_SFNODE;
field->node = NULL;
field->field.far_ptr = &field->node;
JS_SetOpaque(obj, field);
return obj;
}
ID = 0;
node_name = (char *) JS_ToCString(c, argv[0]);
if (!node_name) return GF_JS_EXCEPTION(c);
if (!strnicmp(node_name, "_proto", 6)) {
ID = atoi(node_name+6);
JS_FreeCString(c, node_name);
node_name = NULL;
locate_proto:
/*locate proto in current graph and all parents*/
sg = sc->sgprivate->scenegraph;
while (1) {
proto = gf_sg_find_proto(sg, ID, node_name);
if (proto) break;
if (!sg->parent_scene) break;
sg = sg->parent_scene;
}
if (!proto) {
JS_FreeCString(c, node_name);
return JS_FALSE;
}
/* create interface and load code in current graph*/
new_node = gf_sg_proto_create_instance(sc->sgprivate->scenegraph, proto);
if (!new_node) {
JS_FreeCString(c, node_name);
return JS_FALSE;
}
/*OK, instantiate proto code*/
if (gf_sg_proto_load_code(new_node) != GF_OK) {
gf_node_unregister(new_node, NULL);
JS_FreeCString(c, node_name);
return JS_FALSE;
}
} else {
switch (sc->sgprivate->tag) {
case TAG_MPEG4_Script:
tag = gf_node_mpeg4_type_by_class_name(node_name);
break;
#ifndef GPAC_DISABLE_X3D
case TAG_X3D_Script:
tag = gf_node_x3d_type_by_class_name(node_name);
break;
#endif
}
if (!tag) goto locate_proto;
new_node = gf_node_new(sc->sgprivate->scenegraph, tag);
if (!new_node) {
JS_FreeCString(c, node_name);
return JS_FALSE;
}
gf_node_init(new_node);
}
JS_FreeCString(c, node_name);
return JS_DupValue(c, node_get_binding(priv, new_node));
}
static void node_finalize_ex(JSRuntime *rt, JSValue obj, Bool is_js_call)
{
GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(obj, SFNodeClass.class_id);
JS_ObjectDestroyed(rt, obj, ptr, is_js_call);
if (!ptr) return;
if (ptr->node
/*num_instances may be 0 if the node is the script being destroyed*/
&& ptr->node->sgprivate->num_instances
) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] unregistering node %s (%s)\n", gf_node_get_name(ptr->node), gf_node_get_class_name(ptr->node)));
gf_node_unregister(ptr->node, NULL);
}
gf_free(ptr);
}
static void node_finalize(JSRuntime *rt, JSValue val)
{
node_finalize_ex(rt, val, GF_TRUE);
}
static JSValue node_toString(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
char str[1000];
u32 id;
GF_Node *n;
GF_JSField *f;
const char *name;
f = (GF_JSField *) JS_GetOpaque(obj, SFNodeClass.class_id);
if (!f) return GF_JS_EXCEPTION(c);
str[0] = 0;
n = * ((GF_Node **)f->field.far_ptr);
if (!n) return GF_JS_EXCEPTION(c);
name = gf_node_get_name_and_id(n, &id);
if (id) {
if (name) {
snprintf(str, 500, "DEF %s ", name);
} else {
snprintf(str, 500, "DEF %d ", id - 1);
}
}
gf_strcat(str, gf_node_get_class_name(n));
return JS_NewString(c, (const char *) str);
}
static JSValue node_getTime(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_Node *n;
GF_JSField *f;
f = (GF_JSField *) JS_GetOpaque(obj, SFNodeClass.class_id);
if (!f) return GF_JS_EXCEPTION(c);
n = * ((GF_Node **)f->field.far_ptr);
if (!n) return GF_JS_EXCEPTION(c);
return JS_NewFloat64(c, gf_node_get_scene_time(n));
}
static JSValue node_getProperty(JSContext *c, JSValueConst obj, JSAtom atom, JSValueConst receiver)
{
GF_Node *n;
u32 index;
JSValue res;
const char *fieldName;
GF_FieldInfo info;
GF_JSField *ptr;
GF_ScriptPriv *priv;
ptr = (GF_JSField *) JS_GetOpaque(obj, SFNodeClass.class_id);
if (!ptr) return GF_JS_EXCEPTION(c);
gf_assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
n = * ((GF_Node **)ptr->field.far_ptr);
if (!n) return GF_JS_EXCEPTION(c);
priv = JS_GetScriptStack(c);
fieldName = JS_AtomToCString(c, atom);
if (!fieldName) return GF_JS_EXCEPTION(c);
if (!strcmp(fieldName, "toString")) {
JS_FreeCString(c, fieldName);
return JS_DupValue(priv->js_ctx, priv->node_toString_fun);
}
if (!strcmp(fieldName, "getTime")) {
JS_FreeCString(c, fieldName);
return JS_DupValue(priv->js_ctx, priv->node_getTime_fun);
}
#if !defined(GPAC_DISABLE_SVG)
if (!strcmp(fieldName, "addEventListener") || !strcmp(fieldName, "addEventListenerNS")) {
JS_FreeCString(c, fieldName);
return JS_DupValue(priv->js_ctx, priv->node_addEventListener_fun);
}
if (!strcmp(fieldName, "removeEventListener") || !strcmp(fieldName, "removeEventListenerNS")) {
JS_FreeCString(c, fieldName);
return JS_DupValue(priv->js_ctx, priv->node_removeEventListener_fun);
}
#endif
/*fieldID indexing*/
if (!strnicmp(fieldName, "_field", 6)) {
index = atoi(fieldName+6);
if ( gf_node_get_field(n, index, &info) == GF_OK) {
res = gf_sg_script_to_qjs_field(priv, &info, n, 0);
JS_FreeCString(c, fieldName);
return res;
}
} else if ( gf_node_get_field_by_name(n, (char *) fieldName, &info) == GF_OK) {
res = gf_sg_script_to_qjs_field(priv, &info, n, 0);
JS_FreeCString(c, fieldName);
return res;
}
if (!strcmp(fieldName, "_bounds")) {
GF_JSAPIParam par;
par.bbox.is_set = 0;
if (ScriptAction(c, n->sgprivate->scenegraph, GF_JSAPI_OP_GET_LOCAL_BBOX, (GF_Node *)n, &par) ) {
JSValue _obj = JS_NewObjectClass(priv->js_ctx, AnyClass.class_id);
Float x, y, w, h;
x = y = w = h = 0;
if (par.bbox.is_set) {
x = FIX2FLT(par.bbox.min_edge.x);
y = FIX2FLT(par.bbox.min_edge.y);
w = FIX2FLT(par.bbox.max_edge.x - par.bbox.min_edge.x);
h = FIX2FLT(par.bbox.max_edge.y - par.bbox.min_edge.y);
}
JS_SetPropertyStr(priv->js_ctx, _obj, "x", JS_NewFloat64(c, x));
JS_SetPropertyStr(priv->js_ctx, _obj, "y", JS_NewFloat64(c, y));
JS_SetPropertyStr(priv->js_ctx, _obj, "width", JS_NewFloat64(c, w));
JS_SetPropertyStr(priv->js_ctx, _obj, "height", JS_NewFloat64(c, h));
JS_FreeCString(c, fieldName);
return _obj;
}
}
JS_FreeCString(c, fieldName);
return JS_UNDEFINED;
}
static int node_setProperty(JSContext *c, JSValueConst obj, JSAtom atom, JSValueConst value, JSValueConst receiver, int flags)
{
GF_Node *n;
GF_FieldInfo info;
u32 index;
const char *fieldname;
Bool isOK = GF_FALSE;
GF_JSField *ptr;
ptr = (GF_JSField *) JS_GetOpaque(obj, SFNodeClass.class_id);
if (! ptr) return -1;
fieldname = JS_AtomToCString(c, atom);
if (!fieldname) return -1;
gf_assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
n = * ((GF_Node **)ptr->field.far_ptr);
/*fieldID indexing*/
if (!strnicmp(fieldname, "_field", 6)) {
index = atoi(fieldname+6);
JS_FreeCString(c, fieldname);
if ( gf_node_get_field(n, index, &info) == GF_OK) {
isOK = GF_TRUE;
}
} else {
if (gf_node_get_field_by_name(n, (char *)fieldname, &info) == GF_OK) {
isOK = GF_TRUE;
} else {
/*VRML style*/
if (!strnicmp(fieldname, "set_", 4)) {
if (gf_node_get_field_by_name(n, (char *) fieldname + 4, &info) == GF_OK) {
isOK = GF_TRUE;
}
}
}
}
if (!isOK) {
JS_FreeCString(c, fieldname);
JS_DefinePropertyValue(c, obj, atom, JS_DupValue(c, value), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
return 0;
}
JS_FreeCString(c, fieldname);
if (gf_node_get_tag(n)==TAG_ProtoNode)
gf_sg_proto_mark_field_loaded(n, &info);
gf_sg_script_to_node_field(c, value, &info, n, ptr);
return 1;
}
/* Generic field destructor */
static void field_finalize(JSRuntime *rt, JSValue obj)
{
JSClassID _classID;
GF_JSField *ptr = (GF_JSField *) JS_GetAnyOpaque(obj, &_classID);
JS_ObjectDestroyed(rt, obj, ptr, 1);
if (!ptr) return;
if (ptr->field_ptr) gf_sg_vrml_field_pointer_del(ptr->field_ptr, ptr->field.fieldType);
gf_free(ptr);
}
/*SFImage class functions */
static GFINLINE GF_JSField *SFImage_Create(JSContext *c, JSValue obj, u32 w, u32 h, u32 nbComp, MFInt32 *pixels)
{
u32 i, len;
GF_JSField *field;
SFImage *v;
field = NewJSField(c);
v = gf_sg_vrml_field_pointer_new(GF_SG_VRML_SFIMAGE);
field->field_ptr = field->field.far_ptr = v;
field->field.fieldType = GF_SG_VRML_SFIMAGE;
v->width = w;
v->height = h;
v->numComponents = nbComp;
v->pixels = (u8 *) gf_malloc(sizeof(u8) * nbComp * w * h);
len = MIN(nbComp * w * h, pixels->count);
for (i=0; i<len; i++) v->pixels[i] = (u8) pixels->vals[i];
JS_SetOpaque(obj, field);
return field;
}
static JSValue SFImageConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
u32 w, h, nbComp;
MFInt32 *pixels;
JSValue obj;
if (argc<4) return GF_JS_EXCEPTION(c);
if (!JS_IsInteger(argv[0]) || !JS_IsInteger(argv[1]) || !JS_IsInteger(argv[2]) || !JS_IsObject(argv[3]) )
return GF_JS_EXCEPTION(c);
pixels = JS_GetOpaque(argv[3], MFInt32Class.class_id);
if (!pixels) return GF_JS_EXCEPTION(c);
obj = JS_NewObjectClass(c, SFImageClass.class_id);
if (JS_IsException(obj)) return obj;
JS_ToInt32(c, &w, argv[0]);
JS_ToInt32(c, &h, argv[1]);
JS_ToInt32(c, &nbComp, argv[2]);
SFImage_Create(c, obj, w, h, nbComp, pixels);
return obj;
}
static JSValue image_getProperty(JSContext *c, JSValueConst this_val, int magic)
{
SFImage *sfi;
GF_ScriptPriv *priv = JS_GetScriptStack(c);
GF_JSField *val = (GF_JSField *) JS_GetOpaque(this_val, SFImageClass.class_id);
if (!val) return GF_JS_EXCEPTION(c);
sfi = (SFImage*)val->field.far_ptr;
switch (magic) {
case 0: return JS_NewInt32(c, sfi->width);
case 1: return JS_NewInt32(c, sfi->height);
case 2: return JS_NewInt32(c, sfi->numComponents);
case 3:
{
u32 i, len;
JSValue an_obj = JS_NewObjectClass(c, MFInt32Class.class_id);
len = sfi->width*sfi->height*sfi->numComponents;
for (i=0; i<len; i++) {
JS_SetPropertyUint32(priv->js_ctx, an_obj, i, JS_NewInt32(c, sfi->pixels[i]) );
}
}
break;
default:
break;
}
return JS_UNDEFINED;
}
static JSValue image_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
{
u32 ival;
Bool changed = 0;
SFImage *sfi;
GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(obj, SFImageClass.class_id);
if (!ptr) return GF_JS_EXCEPTION(c);
sfi = (SFImage*)ptr->field.far_ptr;
switch (magic) {
case 0:
JS_ToInt32(c, &ival, value);
changed = ! (sfi->width == ival);
sfi->width = ival;
if (changed && sfi->pixels) {
gf_free(sfi->pixels);
sfi->pixels = NULL;
}
break;
case 1:
JS_ToInt32(c, &ival, value);
changed = ! (sfi->height == ival);
sfi->height = ival;
if (changed && sfi->pixels) {
gf_free(sfi->pixels);
sfi->pixels = NULL;
}
break;
case 2:
JS_ToInt32(c, &ival, value);
changed = ! (sfi->numComponents == ival);
sfi->numComponents = ival;
if (changed && sfi->pixels) {
gf_free(sfi->pixels);
sfi->pixels = NULL;
}
break;
case 3:
{
MFInt32 *pixels;
GF_JSField *sf;
u32 len, i;
sf = JS_GetOpaque(value, MFInt32Class.class_id);
if (!sf) return GF_JS_EXCEPTION(c);
pixels = (MFInt32 *) sf->field.far_ptr;
if (sfi->pixels) gf_free(sfi->pixels);
len = sfi->width*sfi->height*sfi->numComponents;
sfi->pixels = (unsigned char *) gf_malloc(sizeof(char)*len);
len = MAX(len, pixels->count);
for (i=0; i<len; i++) sfi->pixels[i] = (u8) pixels->vals[i];
changed = 1;
break;
}
default:
return JS_UNDEFINED;
}
if (changed) Script_FieldChanged(c, NULL, ptr, NULL);
return JS_UNDEFINED;
}
/*SFVec2f class functions */
static GFINLINE GF_JSField *SFVec2f_Create(JSContext *c, JSValue obj, Fixed x, Fixed y)
{
GF_JSField *field;
SFVec2f *v;
field = NewJSField(c);
v = gf_sg_vrml_field_pointer_new(GF_SG_VRML_SFVEC2F);
field->field_ptr = field->field.far_ptr = v;
field->field.fieldType = GF_SG_VRML_SFVEC2F;
v->x = x;
v->y = y;
JS_SetOpaque(obj, field);
return field;
}
static JSValue SFVec2fConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
Double x = 0.0, y = 0.0;
JSValue obj = JS_NewObjectClass(c, SFVec2fClass.class_id);
if (argc > 0) JS_ToFloat64(c, &x, argv[0]);
if (argc > 1) JS_ToFloat64(c, &y, argv[1]);
SFVec2f_Create(c, obj, FLT2FIX( x), FLT2FIX( y));
return obj;
}
static JSValue vec2f_getProperty(JSContext *c, JSValueConst obj, int magic)
{
GF_JSField *val = (GF_JSField *) JS_GetOpaque(obj, SFVec2fClass.class_id);
if (!val) return GF_JS_EXCEPTION(c);
switch (magic) {
case 0:
return JS_NewFloat64(c, FIX2FLT( ((SFVec2f*)val->field.far_ptr)->x));
case 1:
return JS_NewFloat64(c, FIX2FLT( ((SFVec2f*)val->field.far_ptr)->y));
default:
break;
}
return JS_UNDEFINED;
}
static JSValue vec2f_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
{
Double d;
Fixed v;
Bool changed = 0;
GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(obj, SFVec2fClass.class_id);
if (!ptr) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &d, value)) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[VRML JS] Value is not a number while assigning SFVec2f\n"));
return JS_FALSE;
}
switch (magic) {
case 0:
v = FLT2FIX( d);
changed = ! ( ((SFVec2f*)ptr->field.far_ptr)->x == v);
((SFVec2f*)ptr->field.far_ptr)->x = v;
break;
case 1:
v = FLT2FIX( d);
changed = ! ( ((SFVec2f*)ptr->field.far_ptr)->y == v);
((SFVec2f*)ptr->field.far_ptr)->y = v;
break;
default:
return JS_UNDEFINED;
}
if (changed) Script_FieldChanged(c, NULL, ptr, NULL);
return JS_UNDEFINED;
}
static JSValue vec2f_operand(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, u32 op)
{
SFVec2f *v1, *v2=NULL;
Double d = 0.0;
JSValue pNew;
Fixed v;
GF_JSField *p1 = (GF_JSField *) JS_GetOpaque(obj, SFVec2fClass.class_id);
if (!p1) return GF_JS_EXCEPTION(c);
GF_JSField *p2 = NULL;
if (argc) {
if (JS_IsObject(argv[0])) {
p2 = (GF_JSField *) JS_GetOpaque(argv[0], SFVec2fClass.class_id);
} else if (JS_ToFloat64(c, &d, argv[0]))
return GF_JS_EXCEPTION(c);
}
v1 = ((GF_JSField *) p1)->field.far_ptr;
if (p2)
v2 = ((GF_JSField *) p2)->field.far_ptr;
switch (op) {
case 5:
return JS_NewFloat64(c, FIX2FLT(gf_v2d_len(v1)) );
case 7:
if (!p2) return GF_JS_EXCEPTION(c);
return JS_NewFloat64(c, FIX2FLT( gf_mulfix(v1->x, v2->x) + gf_mulfix(v1->y, v2->y) ) );
case 0:
case 1:
if (!p2) return GF_JS_EXCEPTION(c);
}
pNew = JS_NewObjectClass(c, SFVec2fClass.class_id);
switch (op) {
case 0:
SFVec2f_Create(c, pNew, v1->x + v2->x, v1->y + v2->y);
break;
case 1:
SFVec2f_Create(c, pNew, v1->x - v2->x, v1->y - v2->y);
break;
case 2:
SFVec2f_Create(c, pNew, -v1->x , -v1->y );
break;
case 3:
v = FLT2FIX(d);
SFVec2f_Create(c, pNew, gf_mulfix(v1->x , v), gf_mulfix(v1->y, v) );
break;
case 4:
v = FLT2FIX(d);
SFVec2f_Create(c, pNew, gf_divfix(v1->x, v), gf_divfix(v1->y, v));
break;
case 6:
v = gf_v2d_len(v1);
SFVec2f_Create(c, pNew, gf_divfix(v1->x, v), gf_divfix(v1->y, v) );
break;
}
return pNew;
}
static JSValue vec2f_add(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return vec2f_operand(c, obj, argc, argv, 0);
}
static JSValue vec2f_subtract(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return vec2f_operand(c, obj, argc, argv, 1);
}
static JSValue vec2f_negate(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return vec2f_operand(c, obj, argc, argv, 2);
}
static JSValue vec2f_multiply(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return vec2f_operand(c, obj, argc, argv, 3);
}
static JSValue vec2f_divide(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return vec2f_operand(c, obj, argc, argv, 4);
}
static JSValue vec2f_length(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return vec2f_operand(c, obj, argc, argv, 5);
}
static JSValue vec2f_normalize(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return vec2f_operand(c, obj, argc, argv, 6);
}
static JSValue vec2f_dot(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return vec2f_operand(c, obj, argc, argv, 7);
}
/*SFVec3f class functions */
static GFINLINE GF_JSField *SFVec3f_Create(JSContext *c, JSValue obj, Fixed x, Fixed y, Fixed z)
{
GF_JSField *field;
SFVec3f *v;
field = NewJSField(c);
v = gf_sg_vrml_field_pointer_new(GF_SG_VRML_SFVEC3F);
field->field_ptr = field->field.far_ptr = v;
field->field.fieldType = GF_SG_VRML_SFVEC3F;
v->x = x;
v->y = y;
v->z = z;
JS_SetOpaque(obj, field);
return field;
}
static JSValue SFVec3fConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
Double x = 0.0, y = 0.0, z = 0.0;
JSValue obj = JS_NewObjectClass(c, SFVec3fClass.class_id);
if (argc > 0) JS_ToFloat64(c, &x, argv[0]);
if (argc > 1) JS_ToFloat64(c, &y, argv[1]);
if (argc > 2) JS_ToFloat64(c, &z, argv[2]);
SFVec3f_Create(c, obj, FLT2FIX( x), FLT2FIX( y), FLT2FIX( z));
return obj;
}
static JSValue vec3f_getProperty(JSContext *c, JSValueConst obj, int magic)
{
GF_JSField *val = (GF_JSField *) JS_GetOpaque(obj, SFVec3fClass.class_id);
if (!val) return GF_JS_EXCEPTION(c);
switch (magic) {
case 0:
return JS_NewFloat64(c, FIX2FLT( ((SFVec3f*)val->field.far_ptr)->x));
case 1:
return JS_NewFloat64(c, FIX2FLT( ((SFVec3f*)val->field.far_ptr)->y));
case 2:
return JS_NewFloat64(c, FIX2FLT( ((SFVec3f*)val->field.far_ptr)->z));
}
return JS_UNDEFINED;
}
static JSValue vec3f_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
{
Double d;
Fixed v;
Bool changed = 0;
GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(obj, SFVec3fClass.class_id);
if (!ptr) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &d, value)) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[VRML JS] Value is not a number while assigning SFVec3f\n"));
return JS_FALSE;
}
switch (magic) {
case 0:
v = FLT2FIX( d);
changed = ! ( ((SFVec3f*)ptr->field.far_ptr)->x == v);
((SFVec3f*)ptr->field.far_ptr)->x = v;
break;
case 1:
v = FLT2FIX( d);
changed = ! ( ((SFVec3f*)ptr->field.far_ptr)->y == v);
((SFVec3f*)ptr->field.far_ptr)->y = v;
break;
case 2:
v = FLT2FIX( d);
changed = ! ( ((SFVec3f*)ptr->field.far_ptr)->z == v);
((SFVec3f*)ptr->field.far_ptr)->z = v;
break;
default:
return JS_UNDEFINED;
}
if (changed) Script_FieldChanged(c, NULL, ptr, NULL);
return JS_UNDEFINED;
}
static JSValue vec3f_operand(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, u32 op)
{
SFVec3f vec, wvec, *v1, *v2=NULL;
Double d=0;
JSValue pNew;
Fixed v;
GF_JSField *p1 = (GF_JSField *) JS_GetOpaque(obj, SFVec3fClass.class_id);
if (!p1) return GF_JS_EXCEPTION(c);
GF_JSField *p2 = NULL;
if (argc) {
if (JS_IsObject(argv[0])) {
p2 = (GF_JSField *) JS_GetOpaque(argv[0], SFVec3fClass.class_id);
} else if (JS_ToFloat64(c, &d, argv[0]))
return GF_JS_EXCEPTION(c);
}
v1 = ((GF_JSField *) p1)->field.far_ptr;
if (p2)
v2 = ((GF_JSField *) p2)->field.far_ptr;
switch (op) {
case 0:
case 1:
case 8:
if (!p2) return GF_JS_EXCEPTION(c);
case 5:
return JS_NewFloat64(c, FIX2FLT(gf_vec_len(*v1)) );
case 7:
vec = *v1;
if (!p2) return GF_JS_EXCEPTION(c);
wvec = *v2;
return JS_NewFloat64(c, FIX2FLT(gf_vec_dot(vec, wvec)) );
}
pNew = JS_NewObjectClass(c, SFVec3fClass.class_id);
switch (op) {
case 0:
SFVec3f_Create(c, pNew, v1->x + v2->x, v1->y + v2->y, v1->z + v2->z);
break;
case 1:
SFVec3f_Create(c, pNew, v1->x - v2->x, v1->y - v2->y, v1->z - v2->z);
break;
case 2:
SFVec3f_Create(c, pNew, -v1->x , -v1->y , -v1->z );
break;
case 3:
v = FLT2FIX(d);
SFVec3f_Create(c, pNew, gf_mulfix(v1->x, v), gf_mulfix(v1->y, v), gf_mulfix(v1->z, v) );
break;
case 4:
v = FLT2FIX(d);
SFVec3f_Create(c, pNew, gf_divfix(v1->x, v), gf_divfix(v1->y, v), gf_divfix(v1->z, v));
break;
case 6:
vec = *v1;
gf_vec_norm(&vec);
SFVec3f_Create(c, pNew, vec.x, vec.y, vec.z);
break;
case 8:
if (v2) {
vec = gf_vec_cross(*v1, *v2);
SFVec3f_Create(c, pNew, vec.x, vec.y, vec.z);
}
break;
}
return pNew;
}
static JSValue vec3f_add(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return vec3f_operand(c, obj, argc, argv, 0);
}
static JSValue vec3f_subtract(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return vec3f_operand(c, obj, argc, argv, 1);
}
static JSValue vec3f_negate(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return vec3f_operand(c, obj, argc, argv, 2);
}
static JSValue vec3f_multiply(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return vec3f_operand(c, obj, argc, argv, 3);
}
static JSValue vec3f_divide(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return vec3f_operand(c, obj, argc, argv, 4);
}
static JSValue vec3f_length(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return vec3f_operand(c, obj, argc, argv, 5);
}
static JSValue vec3f_normalize(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return vec3f_operand(c, obj, argc, argv, 6);
}
static JSValue vec3f_dot(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return vec3f_operand(c, obj, argc, argv, 7);
}
static JSValue vec3f_cross(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return vec3f_operand(c, obj, argc, argv, 8);
}
/*SFRotation class*/
static GFINLINE GF_JSField *SFRotation_Create(JSContext *c, JSValue obj, Fixed x, Fixed y, Fixed z, Fixed q)
{
GF_JSField *field;
SFRotation *v;
field = NewJSField(c);
v = gf_sg_vrml_field_pointer_new(GF_SG_VRML_SFROTATION);
field->field_ptr = field->field.far_ptr = v;
field->field.fieldType = GF_SG_VRML_SFROTATION;
v->x = x;
v->y = y;
v->z = z;
v->q = q;
JS_SetOpaque(obj, field);
return field;
}
static JSValue SFRotationConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
GF_JSField *f;
SFVec3f v1, v2;
Fixed l1, l2, dot;
Double x = 0.0, y = 0.0, z = 0.0, a = 0.0;
JSValue obj = JS_NewObjectClass(c, SFRotationClass.class_id);
if (!argc) {
SFRotation_Create(c, obj, FLT2FIX(x), FLT2FIX(y), FIX_ONE, FLT2FIX(a) );
return obj;
}
if (JS_IsNumber(argv[0])) {
if (argc > 0) JS_ToFloat64(c, &x, argv[0]);
if (argc > 1) JS_ToFloat64(c, &y, argv[1]);
if (argc > 2) JS_ToFloat64(c, &z, argv[2]);
if (argc > 3) JS_ToFloat64(c, &a, argv[3]);
SFRotation_Create(c, obj, FLT2FIX(x), FLT2FIX(y), FLT2FIX(z), FLT2FIX(a));
return obj;
}
if (argc!=2) return GF_JS_EXCEPTION(c);
if (!JS_IsObject(argv[0])) return GF_JS_EXCEPTION(c);
f = JS_GetOpaque(argv[0], SFVec3fClass.class_id);
if (!f) return GF_JS_EXCEPTION(c);
v1 = * (SFVec3f *) (f)->field.far_ptr;
if (JS_IsNumber(argv[1])) {
JS_ToFloat64(c, &a, argv[1]);
SFRotation_Create(c, obj, v1.x, v1.y, v1.z, FLT2FIX(a));
return obj;
}
if (!JS_IsObject(argv[1])) return JS_FALSE;
f = JS_GetOpaque(argv[1], SFVec3fClass.class_id);
if (!f) return GF_JS_EXCEPTION(c);
v2 = * (SFVec3f *) (f)->field.far_ptr;
l1 = gf_vec_len(v1);
l2 = gf_vec_len(v2);
dot = gf_divfix(gf_vec_dot(v1, v2), gf_mulfix(l1, l2) );
a = gf_atan2(gf_sqrt(FIX_ONE - gf_mulfix(dot, dot)), dot);
SFRotation_Create(c, obj, gf_mulfix(v1.y, v2.z) - gf_mulfix(v2.y, v1.z),
gf_mulfix(v1.z, v2.x) - gf_mulfix(v2.z, v1.x),
gf_mulfix(v1.x, v2.y) - gf_mulfix(v2.x, v1.y),
FLT2FIX(a));
return obj;
}
static JSValue rot_getProperty(JSContext *c, JSValueConst obj, int magic)
{
GF_JSField *val = (GF_JSField *) JS_GetOpaque(obj, SFRotationClass.class_id);
if (!val) return GF_JS_EXCEPTION(c);
switch (magic) {
case 0:
return JS_NewFloat64(c, FIX2FLT( ((SFRotation*)val->field.far_ptr)->x));
case 1:
return JS_NewFloat64(c, FIX2FLT( ((SFRotation*)val->field.far_ptr)->y));
case 2:
return JS_NewFloat64(c, FIX2FLT( ((SFRotation*)val->field.far_ptr)->z));
case 3:
return JS_NewFloat64(c, FIX2FLT( ((SFRotation*)val->field.far_ptr)->q));
}
return JS_UNDEFINED;
}
static JSValue rot_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
{
Double d;
Fixed v;
Bool changed = 0;
GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(obj, SFRotationClass.class_id);
if (!ptr) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &d, value)) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[VRML JS] Value is not a number while assigning SFRotation\n"));
return JS_FALSE;
}
switch (magic) {
case 0:
v = FLT2FIX(d);
changed = ! ( ((SFRotation*)ptr->field.far_ptr)->x == v);
((SFRotation*)ptr->field.far_ptr)->x = v;
break;
case 1:
v = FLT2FIX(d);
changed = ! ( ((SFRotation*)ptr->field.far_ptr)->y == v);
((SFRotation*)ptr->field.far_ptr)->y = v;
break;
case 2:
v = FLT2FIX(d);
changed = ! ( ((SFRotation*)ptr->field.far_ptr)->z == v);
((SFRotation*)ptr->field.far_ptr)->z = v;
break;
case 3:
v = FLT2FIX(d);
changed = ! ( ((SFRotation*)ptr->field.far_ptr)->q == v);
((SFRotation*)ptr->field.far_ptr)->q = v;
break;
default:
return JS_UNDEFINED;
}
if (changed) Script_FieldChanged(c, NULL, ptr, NULL);
return JS_UNDEFINED;
}
static JSValue rot_getAxis(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
SFRotation r;
JSValue pNew;
GF_JSField *p = JS_GetOpaque(obj, SFRotationClass.class_id);
if (!p) return GF_JS_EXCEPTION(c);
r = * (SFRotation *) (p)->field.far_ptr;
pNew = JS_NewObjectClass(c, SFVec3fClass.class_id);
SFVec3f_Create(c, pNew, r.x, r.y, r.z);
return pNew;
}
static JSValue rot_inverse(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
SFRotation r;
JSValue pNew;
GF_JSField *p = JS_GetOpaque(obj, SFRotationClass.class_id);
if (!p) return GF_JS_EXCEPTION(c);
r = * (SFRotation *) (p)->field.far_ptr;
pNew = JS_NewObjectClass(c, SFRotationClass.class_id);
SFRotation_Create(c, pNew, r.x, r.y, r.z, r.q-GF_PI);
return pNew;
}
static JSValue rot_multiply(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
SFRotation r1, r2;
SFVec4f q1, q2;
JSValue pNew;
if (argc<=0 || !JS_IsObject(argv[0]))
return GF_JS_EXCEPTION(c);
GF_JSField *p = JS_GetOpaque(obj, SFRotationClass.class_id);
if (!p) return GF_JS_EXCEPTION(c);
r1 = * (SFRotation *) (p)->field.far_ptr;
p = JS_GetOpaque(argv[0], SFRotationClass.class_id);
if (!p) return GF_JS_EXCEPTION(c);
r2 = * (SFRotation *) (p)->field.far_ptr;
q1 = gf_quat_from_rotation(r1);
q2 = gf_quat_from_rotation(r2);
q1 = gf_quat_multiply(&q1, &q2);
r1 = gf_quat_to_rotation(&q1);
pNew = JS_NewObjectClass(c, SFRotationClass.class_id);
SFRotation_Create(c, pNew, r1.x, r1.y, r1.z, r1.q);
return pNew;
}
static JSValue rot_multVec(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
SFVec3f v;
SFRotation r;
GF_Matrix mx;
JSValue pNew;
if (argc<=0 || !JS_IsObject(argv[0]))
return GF_JS_EXCEPTION(c);
GF_JSField *p = JS_GetOpaque(obj, SFRotationClass.class_id);
if (!p) return GF_JS_EXCEPTION(c);
r = * (SFRotation *) (p)->field.far_ptr;
p = JS_GetOpaque(argv[0], SFVec3fClass.class_id);
if (!p) return GF_JS_EXCEPTION(c);
v = * (SFVec3f *) (p)->field.far_ptr;
gf_mx_init(mx);
gf_mx_add_rotation(&mx, r.q, r.x, r.y, r.z);
gf_mx_apply_vec(&mx, &v);
pNew = JS_NewObjectClass(c, SFVec3fClass.class_id);
SFVec3f_Create(c, pNew, v.x, v.y, v.z);
return pNew;
}
static JSValue rot_setAxis(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
SFVec3f v;
SFRotation *r;
GF_JSField *ptr;
if (argc<=0 || !JS_IsObject(argv[0]))
return GF_JS_EXCEPTION(c);
ptr = JS_GetOpaque(obj, SFRotationClass.class_id);
if (!ptr) return GF_JS_EXCEPTION(c);
r = (SFRotation *) (ptr)->field.far_ptr;
GF_JSField *p = JS_GetOpaque(argv[0], SFVec3fClass.class_id);
if (!p) return GF_JS_EXCEPTION(c);
v = * (SFVec3f *) (p)->field.far_ptr;
r->x = v.x;
r->y = v.y;
r->z = v.z;
Script_FieldChanged(c, NULL, ptr, NULL);
return JS_TRUE;
}
static JSValue rot_slerp(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
SFRotation v1, v2, res;
SFVec4f q1, q2;
JSValue pNew;
Double d;
GF_JSField *p;
if ((argc<2) || !JS_IsObject(argv[0]))
return GF_JS_EXCEPTION(c);
p = JS_GetOpaque(obj, SFRotationClass.class_id);
if (!p) return GF_JS_EXCEPTION(c);
v1 = * (SFRotation *) (p)->field.far_ptr;
p = JS_GetOpaque(argv[0], SFRotationClass.class_id);
if (!p) return GF_JS_EXCEPTION(c);
v2 = * (SFRotation *) (p)->field.far_ptr;
if (JS_ToFloat64(c, &d, argv[1])) return GF_JS_EXCEPTION(c);
q1 = gf_quat_from_rotation(v1);
q2 = gf_quat_from_rotation(v2);
q1 = gf_quat_slerp(q1, q2, FLT2FIX( d));
res = gf_quat_to_rotation(&q1);
pNew = JS_NewObjectClass(c, SFRotationClass.class_id);
SFRotation_Create(c, pNew, res.x, res.y, res.z, res.q);
return pNew;
}
/* SFColor class functions */
static GFINLINE GF_JSField *SFColor_Create(JSContext *c, JSValue obj, Fixed r, Fixed g, Fixed b)
{
GF_JSField *field;
SFColor *v;
field = NewJSField(c);
v = gf_sg_vrml_field_pointer_new(GF_SG_VRML_SFCOLOR);
field->field_ptr = field->field.far_ptr = v;
field->field.fieldType = GF_SG_VRML_SFCOLOR;
v->red = r;
v->green = g;
v->blue = b;
JS_SetOpaque(obj, field);
return field;
}
static JSValue SFColorConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
Double r = 0.0, g = 0.0, b = 0.0;
JSValue obj = JS_NewObjectClass(c, SFColorClass.class_id);
if (argc > 0) JS_ToFloat64(c, &r, argv[0]);
if (argc > 1) JS_ToFloat64(c, &g, argv[1]);
if (argc > 2) JS_ToFloat64(c, &b, argv[2]);
SFColor_Create(c, obj, FLT2FIX( r), FLT2FIX( g), FLT2FIX( b));
return obj;
}
static JSValue color_getProperty(JSContext *c, JSValueConst obj, int magic)
{
GF_JSField *val = (GF_JSField *) JS_GetOpaque(obj, SFColorClass.class_id);
if (!val) return GF_JS_EXCEPTION(c);
switch (magic) {
case 0:
return JS_NewFloat64(c, FIX2FLT( ((SFColor*)val->field.far_ptr)->red));
case 1:
return JS_NewFloat64(c, FIX2FLT( ((SFColor*)val->field.far_ptr)->green));
break;
case 2:
return JS_NewFloat64(c, FIX2FLT( ((SFColor*)val->field.far_ptr)->blue));
break;
default:
return JS_UNDEFINED;
}
return JS_UNDEFINED;
}
static JSValue color_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
{
Double d;
Fixed v;
Bool changed = 0;
GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(obj, SFColorClass.class_id);
if (!ptr) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &d, value)) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[VRML JS] Value is not a number while assigning SFColor\n"));
return JS_FALSE;
}
switch (magic) {
case 0:
v = FLT2FIX(d);
changed = ! ( ((SFColor*)ptr->field.far_ptr)->red == v);
((SFColor*)ptr->field.far_ptr)->red = v;
break;
case 1:
v = FLT2FIX(d);
changed = ! ( ((SFColor*)ptr->field.far_ptr)->green == v);
((SFColor*)ptr->field.far_ptr)->green = v;
break;
case 2:
v = FLT2FIX(d);
changed = ! ( ((SFColor*)ptr->field.far_ptr)->blue == v);
((SFColor*)ptr->field.far_ptr)->blue = v;
break;
default:
return JS_UNDEFINED;
}
if (changed) Script_FieldChanged(c, NULL, ptr, NULL);
return JS_UNDEFINED;
}
static JSValue color_setHSV(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
SFColor *v1, hsv;
Double h=0, s=0, v=0;
GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(obj, SFColorClass.class_id);
if (!ptr) return GF_JS_EXCEPTION(c);
if (argc != 3) return JS_FALSE;
v1 = (ptr)->field.far_ptr;
if (JS_ToFloat64(c, &h, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &s, argv[1])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &v, argv[2])) return GF_JS_EXCEPTION(c);
hsv.red = FLT2FIX( h);
hsv.green = FLT2FIX( s);
hsv.blue = FLT2FIX( v);
SFColor_fromHSV(&hsv);
gf_sg_vrml_field_copy(v1, &hsv, GF_SG_VRML_SFCOLOR);
Script_FieldChanged(c, NULL, ptr, NULL);
return JS_UNDEFINED;
}
static JSValue color_getHSV(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
SFColor *v1, hsv;
JSValue arr;
GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(obj, SFColorClass.class_id);
if (!ptr) return GF_JS_EXCEPTION(c);
v1 = (ptr)->field.far_ptr;
hsv = *v1;
SFColor_toHSV(&hsv);
arr = JS_NewArray(c);
if (JS_IsException(arr)) return arr;
JS_SetPropertyUint32(c, arr, 0, JS_NewFloat64(c, FIX2FLT(hsv.red)) );
JS_SetPropertyUint32(c, arr, 1, JS_NewFloat64(c, FIX2FLT(hsv.green)) );
JS_SetPropertyUint32(c, arr, 2, JS_NewFloat64(c, FIX2FLT(hsv.blue)) );
return arr;
}
static JSValue genmf_Constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv, JSClassID mf_class_id, JSClassID sf_class_id, u32 fieldType)
{
u32 i;
GF_JSField *ptr;
JSValue obj = JS_NewObjectClass(c, mf_class_id);
ptr = NewJSField(c);
ptr->field.fieldType = fieldType;
ptr->obj = obj;
JS_SetOpaque(obj, ptr);
if (!argc || (fieldType==GF_SG_VRML_MFNODE)) return obj;
ptr->mfvals = gf_realloc(ptr->mfvals, sizeof(JSValue)*argc);
ptr->mfvals_count = argc;
for (i=0; i<(u32) argc; i++) {
if (sf_class_id) {
if (JS_IsObject(argv[i]) && JS_GetOpaque(argv[i], sf_class_id)) {
ptr->mfvals[i] = JS_DupValue(c, argv[i]);
} else {
ptr->mfvals[i] = JS_NewObjectClass(c, sf_class_id);
}
} else {
ptr->mfvals[i] = JS_DupValue(c, argv[i]);
}
}
//don't dup value since this may go directly in script
return obj;
}
static JSValue MFBoolConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
return genmf_Constructor(c, new_target, argc, argv, MFBoolClass.class_id, 0, GF_SG_VRML_MFBOOL);
}
static JSValue MFInt32Constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
return genmf_Constructor(c, new_target, argc, argv, MFInt32Class.class_id, 0, GF_SG_VRML_MFINT32);
}
static JSValue MFFloatConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
return genmf_Constructor(c, new_target, argc, argv, MFFloatClass.class_id, 0, GF_SG_VRML_MFFLOAT);
}
static JSValue MFTimeConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
return genmf_Constructor(c, new_target, argc, argv, MFTimeClass.class_id, 0, GF_SG_VRML_MFTIME);
}
static JSValue MFStringConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
return genmf_Constructor(c, new_target, argc, argv, MFStringClass.class_id, 0, GF_SG_VRML_MFSTRING);
}
static JSValue MFURLConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
return genmf_Constructor(c, new_target, argc, argv, MFUrlClass.class_id, 0, GF_SG_VRML_MFURL);
}
static JSValue MFNodeConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
return genmf_Constructor(c, new_target, argc, argv, MFNodeClass.class_id, 0, GF_SG_VRML_MFNODE);
}
static void array_finalize_ex(JSRuntime *rt, JSValue obj, Bool is_js_call)
{
u32 i;
JSClassID _classID;
GF_JSField *ptr = JS_GetAnyOpaque(obj, &_classID);
JS_ObjectDestroyed(rt, obj, ptr, 1);
if (!ptr) return;
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] unregistering MFField %s\n", ptr->field.name));
/*MF values*/
if (ptr->mfvals) {
for (i=0; i<ptr->mfvals_count; i++)
JS_FreeValueRT(rt, ptr->mfvals[i] );
gf_free(ptr->mfvals);
}
/*MFNode*/
if (ptr->temp_list) {
gf_node_unregister_children(ptr->owner, ptr->temp_list);
}
if (ptr->field_ptr) {
gf_sg_vrml_field_pointer_del(ptr->field_ptr, ptr->field.fieldType);
}
gf_free(ptr);
}
static void array_finalize(JSRuntime *rt, JSValue obj)
{
array_finalize_ex(rt, obj, GF_TRUE);
}
static JSValue array_getElement(JSContext *c, JSValueConst obj, JSAtom atom, JSValueConst receiver)
{
u32 idx;
JSClassID _classID;
GF_JSField *ptr = JS_GetAnyOpaque(obj, &_classID);
if (!JS_AtomIsArrayIndex(c, &idx, atom)) {
JSValue ret = JS_UNDEFINED;
const char *str = JS_AtomToCString(c, atom);
if (!str) return ret;
if (!strcmp(str, "length")) {
GF_JSField *f_ptr = JS_GetAnyOpaque(obj, &_classID);
if (!f_ptr) {
ret = GF_JS_EXCEPTION(c);
} else if (f_ptr->field.fieldType==GF_SG_VRML_MFNODE) {
ret = JS_NewInt32(c, gf_node_list_get_count(*(GF_ChildNodeItem **)f_ptr->field.far_ptr) );
} else {
ret = JS_NewInt32(c, f_ptr->mfvals_count);
}
} else if (!strcmp(str, "toString")) {
ret = JS_NewCFunction(c, field_toString, "toString", 0);
}
JS_FreeCString(c, str);
return ret;
}
if (ptr->field.fieldType==GF_SG_VRML_MFNODE) {
GF_Node *node = gf_node_list_get_child(*(GF_ChildNodeItem **)ptr->field.far_ptr, idx);
if (!node) return JS_NULL;
return JS_DupValue(c, node_get_binding(JS_GetScriptStack(c), node));
} else {
JSValue val;
if (idx>=ptr->mfvals_count) return JS_NULL;
val = ptr->mfvals[idx];
// GF_JSField *sf = JS_GetAnyOpaque(val, &_classID);
return JS_DupValue(c, val);
}
return JS_NULL;
}
static int array_setLength(JSContext *c, GF_JSField *ptr, JSValueConst value)
{
u32 len, i, sftype, old_len;
GF_JSClass *the_sf_class;
if (!JS_IsInteger(value) || !ptr) return -1;
len=-1;
JS_ToInt32(c, &len, value);
if ((s32)len<0) return -1;
if (!len) {
if (ptr->field.fieldType==GF_SG_VRML_MFNODE) {
gf_node_unregister_children(ptr->owner, *(GF_ChildNodeItem**)ptr->field.far_ptr);
*(GF_ChildNodeItem**)ptr->field.far_ptr = NULL;
} else {
gf_sg_vrml_mf_reset(ptr->field.far_ptr, ptr->field.fieldType);
}
while (ptr->mfvals_count) {
ptr->mfvals_count--;
JS_FreeValue(c, ptr->mfvals[ptr->mfvals_count]);
}
gf_free(ptr->mfvals);
ptr->mfvals = NULL;
return 1;
}
old_len = ptr->mfvals_count;
ptr->mfvals_count = len;
if (len == old_len) return 1;
the_sf_class = NULL;
switch (ptr->field.fieldType) {
case GF_SG_VRML_MFVEC2F:
the_sf_class = &SFVec2fClass;
break;
case GF_SG_VRML_MFVEC3F:
the_sf_class = &SFVec3fClass;
break;
case GF_SG_VRML_MFCOLOR:
the_sf_class = &SFColorClass;
break;
case GF_SG_VRML_MFROTATION:
the_sf_class = &SFRotationClass;
break;
case GF_SG_VRML_MFNODE:
{
u32 nb_nodes = gf_node_list_get_count(*(GF_ChildNodeItem**)ptr->field.far_ptr);
while (len < nb_nodes) {
GF_Node *n = gf_node_list_del_child_idx((GF_ChildNodeItem**)ptr->field.far_ptr, nb_nodes - 1);
if (n) gf_node_unregister(n, ptr->owner);
nb_nodes--;
}
if (len>nb_nodes) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[VRML] MFARRAY EXPANSION NOT SUPPORTED!!!\n"));
}
}
return 1;
}
ptr->mfvals = gf_realloc(ptr->mfvals, sizeof(JSValue)*ptr->mfvals_count);
sftype = gf_sg_vrml_get_sf_type(ptr->field.fieldType);
for (i=old_len; i<len; i++) {
JSValue a_val;
if (the_sf_class) {
GF_JSField *slot;
a_val = JS_NewObjectClass(c, the_sf_class->class_id);
slot = SFVec2f_Create(c, a_val, 0, 0);
if (slot) {
slot->owner = ptr->owner;
}
} else {
switch (sftype) {
case GF_SG_VRML_SFBOOL:
a_val = JS_FALSE;
break;
case GF_SG_VRML_SFFLOAT:
case GF_SG_VRML_SFTIME:
a_val = JS_NewFloat64(c, 0);
break;
case GF_SG_VRML_SFSTRING:
case GF_SG_VRML_SFURL:
a_val = JS_NewString(c, "");
break;
case GF_SG_VRML_SFINT32:
default:
a_val = JS_NewInt32(c, 0);
break;
}
}
ptr->mfvals[i] = a_val;
gf_sg_vrml_mf_append(ptr->field.far_ptr, ptr->field.fieldType, NULL);
}
return 1;
}
//this could be overloaded for each MF type...
static int array_setElement(JSContext *c, JSValueConst obj, JSAtom atom, JSValueConst value, JSValueConst receiver, int flags)
{
u32 ind;
u32 len;
Double d;
s32 ival;
GF_JSField *from;
GF_JSClass *the_sf_class = NULL;
char *str_val;
void *sf_slot;
JSClassID _classID;
Bool is_append = 0;
GF_JSField *ptr = (GF_JSField *) JS_GetAnyOpaque(obj, &_classID);
if (!ptr) return -1;
if (!JS_AtomIsArrayIndex(c, &ind, atom)) {
int ret = 0;
const char *str = JS_AtomToCString(c, atom);
if (str && !strcmp(str, "length")) {
ret = array_setLength(c, ptr, value);
}
JS_FreeCString(c, str);
return ret;
}
if (ptr->field.fieldType!=GF_SG_VRML_MFNODE) {
len = ptr->mfvals_count;
} else {
len = gf_node_list_get_count(*(GF_ChildNodeItem **)ptr->field.far_ptr);
}
if (gf_sg_vrml_is_sf_field(ptr->field.fieldType))
return -1;
switch (ptr->field.fieldType) {
case GF_SG_VRML_MFVEC2F:
the_sf_class = &SFVec2fClass;
break;
case GF_SG_VRML_MFVEC3F:
the_sf_class = &SFVec3fClass;
break;
case GF_SG_VRML_MFCOLOR:
the_sf_class = &SFColorClass;
break;
case GF_SG_VRML_MFROTATION:
the_sf_class = &SFRotationClass;
break;
}
/*dynamic expend*/
if (ind>=len) {
is_append = 1;
}
if (is_append && (ptr->field.fieldType!=GF_SG_VRML_MFNODE)) {
ptr->mfvals = gf_realloc(ptr->mfvals, sizeof(JSValue)*(ind+1));
ptr->mfvals_count = ind+1;
while (len<ind) {
JSValue a_val;
switch (ptr->field.fieldType) {
case GF_SG_VRML_MFBOOL:
a_val = JS_NewBool(c, 0);
break;
case GF_SG_VRML_MFINT32:
a_val = JS_NewInt32(c, 0);
break;
case GF_SG_VRML_MFFLOAT:
case GF_SG_VRML_MFTIME:
a_val = JS_NewFloat64(c, 0);
break;
case GF_SG_VRML_MFSTRING:
case GF_SG_VRML_MFURL:
a_val = JS_NewString(c, "");
break;
case GF_SG_VRML_MFVEC2F:
case GF_SG_VRML_MFVEC3F:
case GF_SG_VRML_MFCOLOR:
case GF_SG_VRML_MFROTATION:
a_val = JS_NewObjectClass(c, the_sf_class->class_id);
break;
default:
a_val = JS_NULL;
break;
}
gf_sg_vrml_mf_insert(ptr->field.far_ptr, ptr->field.fieldType, &sf_slot, len);
ptr->mfvals[len] = a_val;
len++;
}
if (ptr->field.far_ptr)
gf_sg_vrml_mf_insert(ptr->field.far_ptr, ptr->field.fieldType, &sf_slot, ind);
ptr->mfvals[ind] = JS_NULL;
}
if (ptr->field.far_ptr && (ptr->field.fieldType!=GF_SG_VRML_MFNODE)) {
u32 items = ((GenMFField *)ptr->field.far_ptr)->count;
while (ind>=items) {
gf_sg_vrml_mf_insert(ptr->field.far_ptr, ptr->field.fieldType, &sf_slot, ind);
items++;
}
}
/*assign object*/
if (ptr->field.fieldType==GF_SG_VRML_MFNODE) {
if (JS_IsUndefined(value)) return -1;
if (JS_IsNull(value) ) return -1;
if (!JS_GetOpaque(value, SFNodeClass.class_id) ) return -1;
} else if (the_sf_class) {
if (JS_IsUndefined(value)) return -1;
if (!JS_GetOpaque(value, the_sf_class->class_id) ) return -1;
} else if (ptr->field.fieldType==GF_SG_VRML_MFBOOL) {
if (!JS_IsBool(value)) return -1;
} else if (ptr->field.fieldType==GF_SG_VRML_MFINT32) {
if (!JS_IsInteger(value)) return -1;
} else if (ptr->field.fieldType==GF_SG_VRML_MFFLOAT) {
if (!JS_IsNumber(value)) return -1;
} else if (ptr->field.fieldType==GF_SG_VRML_MFTIME) {
if (!JS_IsNumber(value)) return -1;
} else if (ptr->field.fieldType==GF_SG_VRML_MFSTRING) {
if (!JS_IsString(value)) return -1;
} else if (ptr->field.fieldType==GF_SG_VRML_MFURL) {
if (!JS_IsString(value)) return -1;
}
/*rewrite MFNode entry*/
if (ptr->field.fieldType==GF_SG_VRML_MFNODE) {
GF_Node *prev_n, *new_n;
if (!ptr->owner) return 1;
/*get new node*/
from = (GF_JSField *) JS_GetOpaque(value, SFNodeClass.class_id);
new_n = *(GF_Node**)from->field.far_ptr;
prev_n = NULL;
if (!is_append) {
/*get and delete previous node if any, but unregister later*/
prev_n = gf_node_list_del_child_idx( (GF_ChildNodeItem **)ptr->field.far_ptr, ind);
}
if (new_n) {
gf_node_list_insert_child( (GF_ChildNodeItem **)ptr->field.far_ptr , new_n, ind);
gf_node_register(new_n, ptr->owner);
/*node created from script and inserted in the tree, create binding*/
node_get_binding(JS_GetScriptStack(c), new_n);
}
/*unregister previous node*/
if (prev_n) gf_node_unregister(prev_n, ptr->owner);
Script_FieldChanged(c, NULL, ptr, NULL);
return 1;
}
//since this value comes from the script, ref count it
JS_FreeValue(c, ptr->mfvals[ind]);
ptr->mfvals[ind] = JS_DupValue(c, value);
if (!ptr->owner) return 1;
if (!ptr->field.far_ptr) return -1;
/*rewrite MF slot*/
switch (ptr->field.fieldType) {
case GF_SG_VRML_MFBOOL:
((MFBool *)ptr->field.far_ptr)->vals[ind] = (Bool) JS_ToBool(c, value);
break;
case GF_SG_VRML_MFINT32:
JS_ToInt32(c, &ival, value);
((MFInt32 *)ptr->field.far_ptr)->vals[ind] = ival;
break;
case GF_SG_VRML_MFFLOAT:
JS_ToFloat64(c, &d, value);
((MFFloat *)ptr->field.far_ptr)->vals[ind] = FLT2FIX(d);
break;
case GF_SG_VRML_MFTIME:
JS_ToFloat64(c, &d, value);
((MFTime *)ptr->field.far_ptr)->vals[ind] = d;
break;
case GF_SG_VRML_MFSTRING:
if (((MFString *)ptr->field.far_ptr)->vals[ind]) {
gf_free(((MFString *)ptr->field.far_ptr)->vals[ind]);
((MFString *)ptr->field.far_ptr)->vals[ind] = NULL;
}
str_val = (char *) JS_ToCString(c, value);
((MFString *)ptr->field.far_ptr)->vals[ind] = gf_strdup(str_val);
JS_FreeCString(c, str_val);
break;
case GF_SG_VRML_MFURL:
if (((MFURL *)ptr->field.far_ptr)->vals[ind].url) {
gf_free(((MFURL *)ptr->field.far_ptr)->vals[ind].url);
((MFURL *)ptr->field.far_ptr)->vals[ind].url = NULL;
}
str_val = (char *) JS_ToCString(c, value);
((MFURL *)ptr->field.far_ptr)->vals[ind].url = gf_strdup(str_val);
((MFURL *)ptr->field.far_ptr)->vals[ind].OD_ID = 0;
JS_FreeCString(c, str_val);
break;
case GF_SG_VRML_MFVEC2F:
from = (GF_JSField *) JS_GetOpaque(value, the_sf_class->class_id);
gf_sg_vrml_field_copy(& ((MFVec2f *)ptr->field.far_ptr)->vals[ind], from->field.far_ptr, from->field.fieldType);
break;
case GF_SG_VRML_MFVEC3F:
from = (GF_JSField *) JS_GetOpaque(value, the_sf_class->class_id);
gf_sg_vrml_field_copy(& ((MFVec3f *)ptr->field.far_ptr)->vals[ind], from->field.far_ptr, from->field.fieldType);
break;
case GF_SG_VRML_MFROTATION:
from = (GF_JSField *) JS_GetOpaque(value, the_sf_class->class_id);
gf_sg_vrml_field_copy(& ((MFRotation *)ptr->field.far_ptr)->vals[ind], from->field.far_ptr, from->field.fieldType);
break;
case GF_SG_VRML_MFCOLOR:
from = (GF_JSField *) JS_GetOpaque(value, the_sf_class->class_id);
gf_sg_vrml_field_copy(& ((MFColor *)ptr->field.far_ptr)->vals[ind], from->field.far_ptr, from->field.fieldType);
break;
}
Script_FieldChanged(c, NULL, ptr, NULL);
return 1;
}
/* MFVec2f class constructor */
static JSValue MFVec2fConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
return genmf_Constructor(c, new_target, argc, argv, MFVec2fClass.class_id, SFVec2fClass.class_id, GF_SG_VRML_MFVEC2F);
}
/* MFVec3f class constructor */
static JSValue MFVec3fConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
return genmf_Constructor(c, new_target, argc, argv, MFVec3fClass.class_id, SFVec3fClass.class_id, GF_SG_VRML_MFVEC3F);
}
/* MFRotation class constructor */
static JSValue MFRotationConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
return genmf_Constructor(c, new_target, argc, argv, MFRotationClass.class_id, SFRotationClass.class_id, GF_SG_VRML_MFROTATION);
}
/*MFColor class constructor */
static JSValue MFColorConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
return genmf_Constructor(c, new_target, argc, argv, MFColorClass.class_id, SFColorClass.class_id, GF_SG_VRML_MFCOLOR);
}
#ifndef GPAC_DISABLE_SVG
JSValue gf_sg_js_event_add_listener(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv, GF_Node *node);
JSValue gf_sg_js_event_remove_listener(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv, GF_Node *node);
static JSValue vrml_event_add_listener(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
{
GF_Node *node;
GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(this_val, SFNodeClass.class_id);
if (!ptr)
return GF_JS_EXCEPTION(c);
gf_assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
node = * ((GF_Node **)ptr->field.far_ptr);
return gf_sg_js_event_add_listener(c, this_val, argc, argv, node);
}
static JSValue vrml_event_remove_listener(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
{
GF_Node *node;
GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(this_val, SFNodeClass.class_id);
if (!ptr) return GF_JS_EXCEPTION(c);
gf_assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
node = * ((GF_Node **)ptr->field.far_ptr);
return gf_sg_js_event_remove_listener(c, this_val, argc, argv, node);
}
#endif
static const JSCFunctionListEntry Browser_funcs[] =
{
JS_CFUNC_DEF("getName", 0, getName),
JS_CFUNC_DEF("getVersion", 0, getVersion),
JS_CFUNC_DEF("getCurrentSpeed", 0, getCurrentSpeed),
JS_CFUNC_DEF("getCurrentFrameRate", 0, getCurrentFrameRate),
JS_CFUNC_DEF("getWorldURL", 0, getWorldURL),
JS_CFUNC_DEF("replaceWorld", 1, replaceWorld),
JS_CFUNC_DEF("addRoute", 4, addRoute),
JS_CFUNC_DEF("deleteRoute", 4, deleteRoute),
JS_CFUNC_DEF("loadURL", 1, loadURL),
JS_CFUNC_DEF("createVrmlFromString", 1, createVrmlFromString),
JS_CFUNC_DEF("setDescription", 1, setDescription),
JS_CFUNC_DEF("print", 1, js_print),
JS_CFUNC_DEF("alert", 1, js_print),
JS_CFUNC_DEF("getScript", 0, getScript),
JS_CFUNC_DEF("getProto", 0, getProto),
JS_CFUNC_DEF("loadScript", 1, loadScript),
JS_CFUNC_DEF("getElementById", 1, getElementById),
};
static const JSCFunctionListEntry SFNode_funcs[] =
{
JS_CFUNC_DEF("toString", 0, node_toString),
};
static JSClassExoticMethods SFNode_exotic =
{
.get_property = node_getProperty,
.set_property = node_setProperty,
};
static const JSCFunctionListEntry SFVec2f_funcs[] =
{
JS_CGETSET_MAGIC_DEF("x", vec2f_getProperty, vec2f_setProperty, 0),
JS_CGETSET_MAGIC_DEF("y", vec2f_getProperty, vec2f_setProperty, 1),
JS_CFUNC_DEF("add", 1, vec2f_add),
JS_CFUNC_DEF("divide", 1, vec2f_divide),
JS_CFUNC_DEF("dot", 1, vec2f_dot),
JS_CFUNC_DEF("length", 0, vec2f_length),
JS_CFUNC_DEF("multiply", 1, vec2f_multiply),
JS_CFUNC_DEF("normalize", 0, vec2f_normalize),
JS_CFUNC_DEF("subtract", 1, vec2f_subtract),
JS_CFUNC_DEF("negate", 0, vec2f_negate),
JS_CFUNC_DEF("toString", 0, field_toString),
};
static const JSCFunctionListEntry SFVec3f_funcs[] =
{
JS_CGETSET_MAGIC_DEF("x", vec3f_getProperty, vec3f_setProperty, 0),
JS_CGETSET_MAGIC_DEF("y", vec3f_getProperty, vec3f_setProperty, 1),
JS_CGETSET_MAGIC_DEF("z", vec3f_getProperty, vec3f_setProperty, 2),
JS_CFUNC_DEF("add", 1, vec3f_add),
JS_CFUNC_DEF("divide", 1, vec3f_divide),
JS_CFUNC_DEF("dot", 1, vec3f_dot),
JS_CFUNC_DEF("length", 0, vec3f_length),
JS_CFUNC_DEF("multiply", 1, vec3f_multiply),
JS_CFUNC_DEF("normalize", 0, vec3f_normalize),
JS_CFUNC_DEF("subtract", 1, vec3f_subtract),
JS_CFUNC_DEF("cross", 1, vec3f_cross),
JS_CFUNC_DEF("negate", 0, vec3f_negate),
JS_CFUNC_DEF("toString", 0, field_toString),
};
static const JSCFunctionListEntry SFRotation_funcs[] =
{
JS_CGETSET_MAGIC_DEF("xAxis", rot_getProperty, rot_setProperty, 0),
JS_CGETSET_MAGIC_DEF("yAxis", rot_getProperty, rot_setProperty, 1),
JS_CGETSET_MAGIC_DEF("zAxis", rot_getProperty, rot_setProperty, 2),
JS_CGETSET_MAGIC_DEF("angle", rot_getProperty, rot_setProperty, 3),
JS_CFUNC_DEF("getAxis", 1, rot_getAxis),
JS_CFUNC_DEF("inverse", 1, rot_inverse),
JS_CFUNC_DEF("multiply", 1, rot_multiply),
JS_CFUNC_DEF("multVec", 0, rot_multVec),
JS_CFUNC_DEF("setAxis", 1, rot_setAxis),
JS_CFUNC_DEF("slerp", 0, rot_slerp),
JS_CFUNC_DEF("toString", 0, field_toString),
};
static const JSCFunctionListEntry SFColor_funcs[] =
{
JS_CGETSET_MAGIC_DEF("r", color_getProperty, color_setProperty, 0),
JS_CGETSET_MAGIC_DEF("g", color_getProperty, color_setProperty, 1),
JS_CGETSET_MAGIC_DEF("b", color_getProperty, color_setProperty, 2),
JS_CFUNC_DEF("setHSV", 3, color_setHSV),
JS_CFUNC_DEF("getHSV", 0, color_getHSV),
JS_CFUNC_DEF("toString", 0, field_toString),
};
static const JSCFunctionListEntry SFImage_funcs[] =
{
JS_CGETSET_MAGIC_DEF("x", image_getProperty, image_setProperty, 0),
JS_CGETSET_MAGIC_DEF("y", image_getProperty, image_setProperty, 1),
JS_CGETSET_MAGIC_DEF("comp", image_getProperty, image_setProperty, 2),
JS_CGETSET_MAGIC_DEF("array", image_getProperty, image_setProperty, 3),
JS_CFUNC_DEF("toString", 0, field_toString),
};
static const JSCFunctionListEntry MFArray_funcs[] =
{
//ignored, kept for MSVC
JS_CGETSET_MAGIC_DEF("length", NULL, NULL, 0),
};
static JSClassExoticMethods MFArray_exotic =
{
.get_property = array_getElement,
.set_property = array_setElement,
};
static void field_gc_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
{
JSClassID _classID;
GF_JSField *jsf = JS_GetAnyOpaque(val, &_classID);
if (!jsf) return;
if (!JS_IsUndefined(jsf->obj) && jsf->owner) {
JS_MarkValue(rt, jsf->obj, mark_func);
}
if (jsf->node && jsf->node->sgprivate->interact->routes) {
u32 i=0;
GF_RouteToScript *r;
while ( (r = gf_list_enum(jsf->node->sgprivate->interact->routes, &i))) {
if (r->script_route) {
JS_MarkValue(rt, r->fun, mark_func);
JS_MarkValue(rt, r->obj, mark_func);
}
}
}
if (jsf->mfvals) {
u32 i;
gf_assert(jsf->mfvals_count);
for (i=0; i<jsf->mfvals_count; i++)
JS_MarkValue(rt, jsf->mfvals[i], mark_func);
}
}
#define SETUP_JSCLASS_BASIC(_class, _name) \
/*classes are global to runtime*/\
if (!_class.class_id) {\
JS_NewClassID(&(_class.class_id)); \
_class.class.class_name = _name; \
JS_NewClass(js_runtime, _class.class_id, &(_class.class));\
}\
#define SETUP_JSCLASS(_class, _name, _proto_funcs, _construct, _finalize, _exotic) \
/*classes are global to runtime*/\
if (!_class.class_id) {\
JS_NewClassID(&(_class.class_id)); \
_class.class.class_name = _name; \
_class.class.finalizer = _finalize;\
_class.class.exotic = _exotic;\
_class.class.gc_mark = field_gc_mark;\
JS_NewClass(js_runtime, _class.class_id, &(_class.class));\
}\
{ \
JSValue _proto_obj = JS_NewObjectClass(sc->js_ctx, _class.class_id);\
JS_SetPropertyFunctionList(sc->js_ctx, _proto_obj, _proto_funcs, countof(_proto_funcs));\
JS_SetClassProto(sc->js_ctx, _class.class_id, _proto_obj);\
sc->_class = JS_NewCFunction2(sc->js_ctx, _construct, _name, 1, JS_CFUNC_constructor, 0);\
JS_SetPropertyStr(sc->js_ctx, sc->js_obj, _name, sc->_class);\
}\
static void vrml_js_init_api(GF_ScriptPriv *sc, GF_Node *script)
{
sc->js_obj = JS_GetGlobalObject(sc->js_ctx);
JSRuntime *js_runtime = JS_GetRuntime(sc->js_ctx);
//init all our classes
SETUP_JSCLASS_BASIC(globalClass, "global");
SETUP_JSCLASS_BASIC(AnyClass, "AnyClass");
SETUP_JSCLASS_BASIC(browserClass, "Browser");
SETUP_JSCLASS(SFNodeClass, "SFNode", SFNode_funcs, SFNodeConstructor, node_finalize, &SFNode_exotic);
SETUP_JSCLASS(SFVec2fClass, "SFVec2f", SFVec2f_funcs, SFVec2fConstructor, field_finalize, NULL);
SETUP_JSCLASS(SFVec3fClass, "SFVec3f", SFVec3f_funcs, SFVec3fConstructor, field_finalize, NULL);
SETUP_JSCLASS(SFRotationClass, "SFRotation", SFRotation_funcs, SFRotationConstructor, field_finalize, NULL);
SETUP_JSCLASS(SFColorClass, "SFColor", SFColor_funcs, SFColorConstructor, field_finalize, NULL);
SETUP_JSCLASS(SFImageClass, "SFImage", SFImage_funcs, SFImageConstructor, field_finalize, NULL);
SETUP_JSCLASS(MFInt32Class, "MFInt32", MFArray_funcs, MFInt32Constructor, array_finalize, &MFArray_exotic);
SETUP_JSCLASS(MFBoolClass, "MFBool", MFArray_funcs, MFBoolConstructor, array_finalize, &MFArray_exotic);
SETUP_JSCLASS(MFTimeClass, "MFTime", MFArray_funcs, MFTimeConstructor, array_finalize, &MFArray_exotic);
SETUP_JSCLASS(MFFloatClass, "MFFloat", MFArray_funcs, MFFloatConstructor, array_finalize, &MFArray_exotic);
SETUP_JSCLASS(MFUrlClass, "MFUrl", MFArray_funcs, MFURLConstructor, array_finalize, &MFArray_exotic);
SETUP_JSCLASS(MFStringClass, "MFString", MFArray_funcs, MFStringConstructor, array_finalize, &MFArray_exotic);
SETUP_JSCLASS(MFNodeClass, "MFNode", MFArray_funcs, MFNodeConstructor, array_finalize, &MFArray_exotic);
SETUP_JSCLASS(MFVec2fClass, "MFVec2f", MFArray_funcs, MFVec2fConstructor, array_finalize, &MFArray_exotic);
SETUP_JSCLASS(MFVec3fClass , "MFVec3f", MFArray_funcs, MFVec3fConstructor, array_finalize, &MFArray_exotic);
SETUP_JSCLASS(MFRotationClass, "MFRotation", MFArray_funcs, MFRotationConstructor, array_finalize, &MFArray_exotic);
SETUP_JSCLASS(MFColorClass, "MFColor", MFArray_funcs, MFColorConstructor, array_finalize, &MFArray_exotic);
JS_SetPropertyStr(sc->js_ctx, sc->js_obj, "print", JS_NewCFunction(sc->js_ctx, js_print, "print", 1));
JS_SetPropertyStr(sc->js_ctx, sc->js_obj, "alert", JS_NewCFunction(sc->js_ctx, js_print, "print", 1));
JS_SetPropertyStr(sc->js_ctx, sc->js_obj, "parseXML", JS_NewCFunction(sc->js_ctx, vrml_parse_xml, "parseXML", 1));
/*remember pointer to scene graph - do NOT use global object private, might be owned by QuickJS !*/
JSValue sg_obj = JS_NewObject(sc->js_ctx);
JS_SetOpaque(sg_obj, script->sgprivate->scenegraph);
JS_SetPropertyStr(sc->js_ctx, sc->js_obj, "__scenegraph", sg_obj);
JS_SetPropertyStr(sc->js_ctx, sc->js_obj, "FALSE", JS_FALSE);
JS_SetPropertyStr(sc->js_ctx, sc->js_obj, "TRUE", JS_TRUE);
JS_SetPropertyStr(sc->js_ctx, sc->js_obj, "NULL", JS_NULL);
JS_SetPropertyStr(sc->js_ctx, sc->js_obj, "_this", JS_NULL);
// JS_DefineProperty(sc->js_ctx, sc->js_obj, "_this", PRIVATE_TO_JSVAL(script), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT );
JSValue browser = JS_NewObjectClass(sc->js_ctx, browserClass.class_id);
JS_SetPropertyFunctionList(sc->js_ctx, browser, Browser_funcs, countof(Browser_funcs));
JS_SetPropertyStr(sc->js_ctx, sc->js_obj, "Browser", browser);
/*TODO ? SFVec3f / MFVec4f support */
sc->node_toString_fun = JS_NewCFunction(sc->js_ctx, node_toString, "toString", 0);
sc->node_getTime_fun = JS_NewCFunction(sc->js_ctx, node_getTime, "getTime", 0);
#ifndef GPAC_DISABLE_SVG
sc->node_addEventListener_fun = JS_NewCFunction(sc->js_ctx, vrml_event_add_listener, "addEventListener", 0);
sc->node_removeEventListener_fun = JS_NewCFunction(sc->js_ctx, vrml_event_remove_listener, "removeEventListener", 0);
#endif
}
void gf_sg_script_to_node_field(JSContext *c, JSValue val, GF_FieldInfo *field, GF_Node *owner, GF_JSField *parent)
{
Double d;
JSClassID _classID;
Bool changed;
const char *str_val;
GF_JSField *p, *from;
u32 len;
s32 ival;
JSValue item;
u32 i;
if (JS_IsUndefined(val)) return;
if ((field->fieldType != GF_SG_VRML_SFNODE) && JS_IsNull(val)) return;
switch (field->fieldType) {
case GF_SG_VRML_SFBOOL:
*((SFBool *) field->far_ptr) = JS_ToBool(c,val);
Script_FieldChanged(c, owner, parent, field);
return;
case GF_SG_VRML_SFINT32:
if (!JS_ToInt32(c, ((SFInt32 *) field->far_ptr), val)) {
Script_FieldChanged(c, owner, parent, field);
return;
case GF_SG_VRML_SFFLOAT:
if (!JS_ToFloat64(c, &d, val)) {
*((SFFloat *) field->far_ptr) = FLT2FIX( d);
Script_FieldChanged(c, owner, parent, field);
}
return;
case GF_SG_VRML_SFTIME:
if (!JS_ToFloat64(c, &d, val)) {
*((SFTime *) field->far_ptr) = (Double) d;
Script_FieldChanged(c, owner, parent, field);
}
return;
}
case GF_SG_VRML_SFSTRING:
{
SFString *s = (SFString*)field->far_ptr;
const char *sval = JS_ToCString(c, val);
/*we do filter strings since rebuilding a text is quite slow, so let's avoid killing the compositors*/
if (!s->buffer || strcmp(sval, s->buffer)) {
if ( s->buffer) gf_free(s->buffer);
s->buffer = gf_strdup(sval);
Script_FieldChanged(c, owner, parent, field);
}
JS_FreeCString(c, sval);
return;
}
case GF_SG_VRML_SFURL:
str_val = JS_ToCString(c, val);
if (((SFURL*)field->far_ptr)->url) gf_free(((SFURL*)field->far_ptr)->url);
((SFURL*)field->far_ptr)->url = gf_strdup(str_val);
((SFURL*)field->far_ptr)->OD_ID = 0;
Script_FieldChanged(c, owner, parent, field);
JS_FreeCString(c, str_val);
return;
case GF_SG_VRML_MFSTRING:
{
GF_JSField *src = JS_GetOpaque(val, MFStringClass.class_id);
gf_sg_vrml_mf_reset(field->far_ptr, field->fieldType);
if (src && src->mfvals_count) {
gf_sg_vrml_mf_alloc(field->far_ptr, field->fieldType, src->mfvals_count);
for (i=0; i<src->mfvals_count; i++) {
str_val = JS_ToCString(c, src->mfvals[i]);
((MFString*)field->far_ptr)->vals[i] = gf_strdup(str_val);
JS_FreeCString(c, str_val);
}
} else {
gf_sg_vrml_mf_alloc(field->far_ptr, field->fieldType, 1);
str_val = JS_ToCString(c, val);
((MFString*)field->far_ptr)->vals[0] = gf_strdup(str_val);
JS_FreeCString(c, str_val);
}
Script_FieldChanged(c, owner, parent, field);
}
return;
case GF_SG_VRML_MFURL:
gf_sg_vrml_mf_reset(field->far_ptr, field->fieldType);
gf_sg_vrml_mf_alloc(field->far_ptr, field->fieldType, 1);
str_val = JS_ToCString(c, val);
((MFURL*)field->far_ptr)->vals[0].url = gf_strdup(str_val);
((MFURL*)field->far_ptr)->vals[0].OD_ID = 0;
Script_FieldChanged(c, owner, parent, field);
JS_FreeCString(c, str_val);
return;
default:
break;
}
//from here we must have an object
if (! JS_IsObject(val)) return;
switch (field->fieldType) {
case GF_SG_VRML_SFVEC2F:
p = (GF_JSField *) JS_GetOpaque(val, SFVec2fClass.class_id);
if (p) {
gf_sg_vrml_field_copy(field->far_ptr, p->field.far_ptr, GF_SG_VRML_SFVEC2F);
Script_FieldChanged(c, owner, parent, field);
}
return;
case GF_SG_VRML_SFVEC3F:
p = (GF_JSField *) JS_GetOpaque(val, SFVec3fClass.class_id);
if (p) {
gf_sg_vrml_field_copy(field->far_ptr, p->field.far_ptr, GF_SG_VRML_SFVEC3F);
Script_FieldChanged(c, owner, parent, field);
}
return;
case GF_SG_VRML_SFROTATION:
p = (GF_JSField *) JS_GetOpaque(val, SFRotationClass.class_id);
if (p) {
gf_sg_vrml_field_copy(field->far_ptr, p->field.far_ptr, GF_SG_VRML_SFROTATION);
Script_FieldChanged(c, owner, parent, field);
}
return;
case GF_SG_VRML_SFCOLOR:
p = (GF_JSField *) JS_GetOpaque(val, SFColorClass.class_id);
if (p) {
gf_sg_vrml_field_copy(field->far_ptr, p->field.far_ptr, GF_SG_VRML_SFCOLOR);
Script_FieldChanged(c, owner, parent, field);
}
return;
case GF_SG_VRML_SFIMAGE:
p = (GF_JSField *) JS_GetOpaque(val, SFImageClass.class_id);
if (p) {
gf_sg_vrml_field_copy(field->far_ptr, p->field.far_ptr, GF_SG_VRML_SFIMAGE);
Script_FieldChanged(c, owner, parent, field);
}
return;
case GF_SG_VRML_SFNODE:
/*replace object*/
if (*((GF_Node**)field->far_ptr)) {
gf_node_unregister(*((GF_Node**)field->far_ptr), owner);
*((GF_Node**)field->far_ptr) = NULL;
}
p = (GF_JSField *) JS_GetOpaque(val, SFNodeClass.class_id);
if (JS_IsNull(val)) {
Script_FieldChanged(c, owner, parent, field);
} else if (p) {
GF_Node *n = * (GF_Node**) (p)->field.far_ptr;
* ((GF_Node **)field->far_ptr) = n;
gf_node_register(n, owner);
Script_FieldChanged(c, owner, parent, field);
}
return;
default:
break;
}
p = (GF_JSField *) JS_GetAnyOpaque(val, &_classID);
if (!p) return;
len = p->mfvals_count;
/*special handling for MF node, reset list first*/
if (field->fieldType == GF_SG_VRML_MFNODE) {
GF_Node *child;
GF_ChildNodeItem *last = NULL;
gf_node_unregister_children(owner, * (GF_ChildNodeItem **) field->far_ptr);
* (GF_ChildNodeItem **) field->far_ptr = NULL;
for (i=0; i<len; i++) {
item = p->mfvals[i];
from = (GF_JSField *) JS_GetOpaque(item, SFNodeClass.class_id);
if (!from) continue;
child = * ((GF_Node**)from->field.far_ptr);
gf_node_list_add_child_last( (GF_ChildNodeItem **) field->far_ptr , child, &last);
gf_node_register(child, owner);
}
Script_FieldChanged(c, owner, parent, field);
/*and mark the field as changed*/
JSScript_NodeModified(owner->sgprivate->scenegraph, owner, field, NULL);
return;
}
/*again, check text changes*/
changed = (field->fieldType == GF_SG_VRML_MFSTRING) ? 0 : 1;
/*gf_realloc*/
if (len != ((GenMFField *)field->far_ptr)->count) {
gf_sg_vrml_mf_reset(field->far_ptr, field->fieldType);
gf_sg_vrml_mf_alloc(field->far_ptr, field->fieldType, len);
changed = 1;
}
/*assign each slot*/
for (i=0; i<len; i++) {
item = p->mfvals[i];
switch (field->fieldType) {
case GF_SG_VRML_MFBOOL:
((MFBool*)field->far_ptr)->vals[i] = (Bool) JS_ToBool(c,item);
break;
case GF_SG_VRML_MFINT32:
if (JS_ToInt32(c, &ival, item)) {
((MFInt32 *)field->far_ptr)->vals[i] = ival;
}
break;
case GF_SG_VRML_MFFLOAT:
if (JS_ToFloat64(c, &d, item)) {
((MFFloat *)field->far_ptr)->vals[i] = FLT2FIX( d);
}
break;
case GF_SG_VRML_MFTIME:
if (JS_ToFloat64(c, &d, item)) {
((MFTime *)field->far_ptr)->vals[i] = d;
}
break;
case GF_SG_VRML_MFSTRING:
{
MFString *mfs = (MFString *) field->far_ptr;
str_val = JS_ToCString(c, item);
if (!mfs->vals[i] || strcmp(str_val, mfs->vals[i]) ) {
if (mfs->vals[i]) gf_free(mfs->vals[i]);
mfs->vals[i] = gf_strdup(str_val);
changed = 1;
}
JS_FreeCString(c, str_val);
}
break;
case GF_SG_VRML_MFURL:
{
MFURL *mfu = (MFURL *) field->far_ptr;
if (mfu->vals[i].url) gf_free(mfu->vals[i].url);
str_val = JS_ToCString(c, item);
mfu->vals[i].url = gf_strdup(str_val);
mfu->vals[i].OD_ID = 0;
JS_FreeCString(c, str_val);
}
break;
case GF_SG_VRML_MFVEC2F:
from = (GF_JSField *) JS_GetOpaque(item, SFVec2fClass.class_id);
if (from) {
gf_sg_vrml_field_copy(& ((MFVec2f*)field->far_ptr)->vals[i], from->field.far_ptr, GF_SG_VRML_SFVEC2F);
}
break;
case GF_SG_VRML_MFVEC3F:
from = (GF_JSField *) JS_GetOpaque(item, SFVec3fClass.class_id);
if (from) {
gf_sg_vrml_field_copy(& ((MFVec3f*)field->far_ptr)->vals[i], from->field.far_ptr, GF_SG_VRML_SFVEC3F);
}
break;
case GF_SG_VRML_MFROTATION:
from = (GF_JSField *) JS_GetOpaque(item, SFRotationClass.class_id);
if (from) {
gf_sg_vrml_field_copy(& ((MFRotation*)field->far_ptr)->vals[i], from->field.far_ptr, GF_SG_VRML_SFROTATION);
}
break;
case GF_SG_VRML_MFCOLOR:
from = (GF_JSField *) JS_GetOpaque(item, SFColorClass.class_id);
if (from) {
gf_sg_vrml_field_copy(& ((MFColor*)field->far_ptr)->vals[i], from->field.far_ptr, GF_SG_VRML_SFCOLOR);
}
break;
default:
break;
}
}
if (changed) Script_FieldChanged(c, owner, parent, field);
}
static void gf_sg_script_update_cached_object(GF_ScriptPriv *priv, JSValue obj, GF_JSField *jsf, GF_FieldInfo *field, GF_Node *parent)
{
u32 i;
if ((jsf->field.fieldType != GF_SG_VRML_MFNODE) && ! gf_sg_vrml_is_sf_field(jsf->field.fieldType)) {
for (i=0; i<jsf->mfvals_count; i++) {
JS_FreeValue(priv->js_ctx, jsf->mfvals[i]);
jsf->mfvals[i] = JS_NULL;
}
}
/*we need to rebuild MF types where SF is a native type.*/
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] Recomputing cached jsobj\n") );
switch (jsf->field.fieldType) {
case GF_SG_VRML_MFBOOL:
{
MFBool *f = (MFBool *) field->far_ptr;
for (i=0; i<f->count; i++) {
jsf->mfvals[i] = JS_NewBool(priv->js_ctx, f->vals[i]);
}
}
break;
case GF_SG_VRML_MFINT32:
{
MFInt32 *f = (MFInt32 *) field->far_ptr;
for (i=0; i<f->count; i++) {
jsf->mfvals[i] = JS_NewInt32(priv->js_ctx, f->vals[i]);
}
}
break;
case GF_SG_VRML_MFFLOAT:
{
MFFloat *f = (MFFloat *) field->far_ptr;
for (i=0; i<f->count; i++) {
jsf->mfvals[i] = JS_NewFloat64(priv->js_ctx, FIX2FLT(f->vals[i]));
}
}
break;
case GF_SG_VRML_MFTIME:
{
MFTime *f = (MFTime *) field->far_ptr;
for (i=0; i<f->count; i++) {
jsf->mfvals[i] = JS_NewFloat64(priv->js_ctx, f->vals[i]);
}
}
break;
case GF_SG_VRML_MFSTRING:
{
MFString *f = (MFString *) field->far_ptr;
for (i=0; i<f->count; i++) {
jsf->mfvals[i] = JS_NewString(priv->js_ctx, f->vals[i] ? f->vals[i] : "");
}
}
break;
case GF_SG_VRML_MFURL:
{
MFURL *f = (MFURL *) field->far_ptr;
for (i=0; i<f->count; i++) {
if (f->vals[i].OD_ID > 0) {
char msg[30];
sprintf(msg, "od:%d", f->vals[i].OD_ID);
jsf->mfvals[i] = JS_NewString(priv->js_ctx, (const char *) msg);
} else {
jsf->mfvals[i] = JS_NewString(priv->js_ctx, f->vals[i].url);
}
}
}
break;
/*
MFNode is tricky because in VRML/MPEG-4, SFNode are assigned by referenced, not copy.
We therefore need to make sure we reuse existing SFNode object rather than
blindly recreating them
*/
case GF_SG_VRML_MFNODE:
{
#if 0
GF_ChildNodeItem *f = *(GF_ChildNodeItem **) field->far_ptr;
u32 j, count;
JSValue val;
/*1: find all existing objs for each node*/
val = JS_GetPropertyStr(priv->js_ctx, jsf->js_list, "length");
JS_ToInt32(priv->js_ctx, &count, val);
JS_FreeValue(priv->js_ctx, val);
/*this may introduce bugs when a child is being replaced through an update command, but it is way
too costly to handle in script*/
if (gf_node_list_get_count(f)==count) return;
JS_SetPropertyStr(priv->js_ctx, jsf->js_list, "length", JS_NewInt32(priv->js_ctx, 0));
count = 0;
while (f) {
GF_JSField *slot = NULL;
/*first look in the original array*/
for (j=0; j<count; j++) {
val = JS_GetPropertyUint32(priv->js_ctx, jsf->js_list, j);
slot = JS_GetOpaque(val, SFNodeClass.class_id);
if (slot && (slot->node==f->node)) {
JS_SetPropertyUint32(priv->js_ctx, jsf->js_list, count, JS_DupValue(priv->js_ctx, val));
count++;
break;
}
JS_FreeValue(priv->js_ctx, val);
slot = NULL;
}
if (!slot) {
JS_SetPropertyUint32(priv->js_ctx, jsf->js_list, count, node_get_binding(priv, f->node, 0) );
count++;
}
f = f->next;
}
JS_SetPropertyStr(priv->js_ctx, jsf->js_list, "length", JS_NewInt32(priv->js_ctx, count));
#endif
}
break;
}
jsf->field.NDTtype = 0;
}
#define SETUP_FIELD \
jsf = NewJSField(priv->js_ctx); \
jsf->owner = parent; \
if(parent) gf_node_get_field(parent, field->fieldIndex, &jsf->field); \
#define SETUP_MF_FIELD(_class) \
obj = JS_CallConstructor(priv->js_ctx, priv->_class, 0, NULL);\
if (JS_IsException(obj) ) return obj; \
jsf = (GF_JSField *) JS_GetOpaque(obj, _class.class_id); \
jsf->owner = parent; \
if (parent) gf_node_get_field(parent, field->fieldIndex, &jsf->field); \
static GF_JSClass *get_sf_class(u32 mftype)
{
switch (mftype) {
case GF_SG_VRML_MFNODE: return &SFNodeClass;
case GF_SG_VRML_MFVEC2F: return &SFVec2fClass;
case GF_SG_VRML_MFVEC3F: return &SFVec3fClass;
case GF_SG_VRML_MFROTATION: return &SFRotationClass;
case GF_SG_VRML_MFCOLOR: return &SFColorClass;
case GF_SG_VRML_MFIMAGE: return &SFImageClass;
}
return NULL;
}
JSValue gf_sg_script_to_qjs_field(GF_ScriptPriv *priv, GF_FieldInfo *field, GF_Node *parent, Bool force_evaluate)
{
u32 i;
GF_JSField *jsf = NULL;
GF_JSField *slot = NULL;
JSValue obj;
Bool was_found = GF_FALSE;
/*native types*/
switch (field->fieldType) {
case GF_SG_VRML_SFBOOL:
return JS_NewBool(priv->js_ctx, * ((SFBool *) field->far_ptr) );
case GF_SG_VRML_SFINT32:
return JS_NewInt32(priv->js_ctx, * ((SFInt32 *) field->far_ptr));
case GF_SG_VRML_SFFLOAT:
return JS_NewFloat64(priv->js_ctx, FIX2FLT(* ((SFFloat *) field->far_ptr) ));
case GF_SG_VRML_SFTIME:
return JS_NewFloat64(priv->js_ctx, * ((SFTime *) field->far_ptr));
case GF_SG_VRML_SFSTRING:
return JS_NewString(priv->js_ctx, ((SFString *) field->far_ptr)->buffer);
case GF_SG_VRML_SFURL:
{
SFURL *url = (SFURL *)field->far_ptr;
if (url->OD_ID > 0) {
char msg[30];
sprintf(msg, "od:%d", url->OD_ID);
return JS_NewString(priv->js_ctx, (const char *) msg);
} else {
return JS_NewString(priv->js_ctx, (const char *) url->url);
}
}
}
obj = JS_NULL;
/*look into object bank in case we already have this object*/
if (parent && parent->sgprivate->interact && parent->sgprivate->interact->js_binding) {
i=0;
while ((jsf = gf_list_enum(parent->sgprivate->interact->js_binding->fields, &i))) {
was_found = GF_TRUE;
obj = jsf->obj;
if (
/*make sure we use the same JS context*/
(jsf->js_ctx == priv->js_ctx)
&& (jsf->owner == parent)
&& (jsf->field.far_ptr==field->far_ptr)
) {
Bool do_rebuild = GF_FALSE;
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] found cached jsobj (field %s) in script %s bank (%d entries)\n", field->name, gf_node_get_log_name((GF_Node*)JS_GetScript(priv->js_ctx)), gf_list_count(priv->jsf_cache) ) );
if (!force_evaluate && !jsf->field.NDTtype)
return JS_DupValue(priv->js_ctx, jsf->obj);
switch (field->fieldType) {
//we need to rewrite these
case GF_SG_VRML_MFVEC2F:
case GF_SG_VRML_MFVEC3F:
case GF_SG_VRML_MFROTATION:
case GF_SG_VRML_MFCOLOR:
if (force_evaluate) {
while (jsf->mfvals_count) {
JS_FreeValue(priv->js_ctx, jsf->mfvals[i]);
jsf->mfvals_count--;
}
gf_free(jsf->mfvals);
jsf->mfvals = NULL;
do_rebuild = GF_TRUE;
break;
}
default:
break;
}
if (do_rebuild) {
break;
}
gf_sg_script_update_cached_object(priv, jsf->obj, jsf, field, parent);
return JS_DupValue(priv->js_ctx, jsf->obj);
}
was_found = GF_FALSE;
obj = JS_NULL;
}
}
if (!was_found) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] creating jsobj %s.%s\n", gf_node_get_name(parent), field->name) );
}
switch (field->fieldType) {
case GF_SG_VRML_SFVEC2F:
SETUP_FIELD
obj = JS_NewObjectClass(priv->js_ctx, SFVec2fClass.class_id);
break;
case GF_SG_VRML_SFVEC3F:
SETUP_FIELD
obj = JS_NewObjectClass(priv->js_ctx, SFVec3fClass.class_id);
break;
case GF_SG_VRML_SFROTATION:
SETUP_FIELD
obj = JS_NewObjectClass(priv->js_ctx, SFRotationClass.class_id);
break;
case GF_SG_VRML_SFCOLOR:
SETUP_FIELD
obj = JS_NewObjectClass(priv->js_ctx, SFColorClass.class_id);
break;
case GF_SG_VRML_SFIMAGE:
SETUP_FIELD
obj = JS_NewObjectClass(priv->js_ctx, SFImageClass.class_id);
break;
case GF_SG_VRML_SFNODE:
if (! *(GF_Node**) field->far_ptr)
return JS_NULL;
obj = node_get_binding(priv, *(GF_Node**) field->far_ptr);
jsf = JS_GetOpaque(obj, SFNodeClass.class_id);
if (!jsf->owner)
jsf->owner = parent;
else
return JS_DupValue(priv->js_ctx, obj);
//return obj;
break;
case GF_SG_VRML_MFBOOL:
{
MFBool *f = (MFBool *) field->far_ptr;
SETUP_MF_FIELD(MFBoolClass)
jsf->mfvals_count = f->count;
jsf->mfvals = gf_realloc(jsf->mfvals, sizeof(JSValue)*f->count);
for (i = 0; i<f->count; i++) {
jsf->mfvals[i] = JS_NewBool(priv->js_ctx, f->vals[i]);
}
break;
}
case GF_SG_VRML_MFINT32:
{
MFInt32 *f = (MFInt32 *) field->far_ptr;
SETUP_MF_FIELD(MFInt32Class)
jsf->mfvals_count = f->count;
jsf->mfvals = gf_realloc(jsf->mfvals, sizeof(JSValue)*f->count);
for (i=0; i<f->count; i++) {
jsf->mfvals[i] = JS_NewInt32(priv->js_ctx, f->vals[i]);
}
break;
}
case GF_SG_VRML_MFFLOAT:
{
MFFloat *f = (MFFloat *) field->far_ptr;
SETUP_MF_FIELD(MFFloatClass)
jsf->mfvals_count = f->count;
jsf->mfvals = gf_realloc(jsf->mfvals, sizeof(JSValue)*f->count);
for (i=0; i<f->count; i++) {
jsf->mfvals[i] = JS_NewFloat64(priv->js_ctx, FIX2FLT(f->vals[i]) );
}
break;
}
case GF_SG_VRML_MFTIME:
{
MFTime *f = (MFTime *) field->far_ptr;
SETUP_MF_FIELD(MFTimeClass)
jsf->mfvals_count = f->count;
jsf->mfvals = gf_realloc(jsf->mfvals, sizeof(JSValue)*f->count);
for (i=0; i<f->count; i++) {
jsf->mfvals[i] = JS_NewFloat64(priv->js_ctx, f->vals[i] );
}
break;
}
case GF_SG_VRML_MFSTRING:
{
MFString *f = (MFString *) field->far_ptr;
SETUP_MF_FIELD(MFStringClass)
jsf->mfvals_count = f->count;
jsf->mfvals = gf_realloc(jsf->mfvals, sizeof(JSValue)*f->count);
for (i=0; i<f->count; i++) {
char *str = f->vals[i];
if (!str) str= "";
jsf->mfvals[i] = JS_NewString(priv->js_ctx, str );
}
break;
}
case GF_SG_VRML_MFURL:
{
MFURL *f = (MFURL *) field->far_ptr;
SETUP_MF_FIELD(MFUrlClass)
jsf->mfvals_count = f->count;
jsf->mfvals = gf_realloc(jsf->mfvals, sizeof(JSValue)*f->count);
for (i=0; i<f->count; i++) {
if (f->vals[i].OD_ID > 0) {
char msg[30];
sprintf(msg, "od:%d", f->vals[i].OD_ID);
jsf->mfvals[i] = JS_NewString(priv->js_ctx, (const char *) msg);
} else {
jsf->mfvals[i] = JS_NewString(priv->js_ctx, f->vals[i].url);
}
}
break;
}
case GF_SG_VRML_MFVEC2F:
{
MFVec2f *f = (MFVec2f *) field->far_ptr;
if (!was_found) {
SETUP_MF_FIELD(MFVec2fClass)
}
jsf->mfvals_count = f->count;
jsf->mfvals = gf_realloc(jsf->mfvals, sizeof(JSValue)*f->count);
for (i=0; i<f->count; i++) {
JSValue pf = JS_NewObjectClass(priv->js_ctx, SFVec2fClass.class_id);
slot = SFVec2f_Create(priv->js_ctx, pf, f->vals[i].x, f->vals[i].y);
slot->owner = parent;
jsf->mfvals[i] = pf;
}
break;
}
case GF_SG_VRML_MFVEC3F:
{
MFVec3f *f = (MFVec3f *) field->far_ptr;
if (!was_found) {
SETUP_MF_FIELD(MFVec3fClass)
}
jsf->mfvals_count = f->count;
jsf->mfvals = gf_realloc(jsf->mfvals, sizeof(JSValue)*f->count);
for (i=0; i<f->count; i++) {
JSValue pf = JS_NewObjectClass(priv->js_ctx, SFVec3fClass.class_id);
slot = SFVec3f_Create(priv->js_ctx, pf, f->vals[i].x, f->vals[i].y, f->vals[i].z);
slot->owner = parent;
jsf->mfvals[i] = pf;
}
break;
}
case GF_SG_VRML_MFROTATION:
{
MFRotation *f = (MFRotation*) field->far_ptr;
if (!was_found) {
SETUP_MF_FIELD(MFRotationClass)
}
jsf->mfvals_count = f->count;
jsf->mfvals = gf_realloc(jsf->mfvals, sizeof(JSValue)*f->count);
for (i=0; i<f->count; i++) {
JSValue pf = JS_NewObjectClass(priv->js_ctx, SFRotationClass.class_id);
slot = SFRotation_Create(priv->js_ctx, pf, f->vals[i].x, f->vals[i].y, f->vals[i].z, f->vals[i].q);
slot->owner = parent;
jsf->mfvals[i] = pf;
}
break;
}
case GF_SG_VRML_MFCOLOR:
{
MFColor *f = (MFColor *) field->far_ptr;
if (!was_found) {
SETUP_MF_FIELD(MFColorClass)
}
jsf->mfvals_count = f->count;
jsf->mfvals = gf_realloc(jsf->mfvals, sizeof(JSValue)*f->count);
for (i=0; i<f->count; i++) {
JSValue pf = JS_NewObjectClass(priv->js_ctx, SFColorClass.class_id);
slot = SFColor_Create(priv->js_ctx, pf, f->vals[i].red, f->vals[i].green, f->vals[i].blue);
slot->owner = parent;
jsf->mfvals[i] = pf;
}
break;
}
case GF_SG_VRML_MFNODE:
{
// GF_ChildNodeItem *f = * ((GF_ChildNodeItem **)field->far_ptr);
SETUP_MF_FIELD(MFNodeClass)
gf_assert(!jsf->mfvals);
jsf->mfvals_count = 0;
/*we don't get the binding until the node is requested, and we don't store it in the MFVals value*/
break;
}
//not supported
default:
return JS_NULL;
}
if (JS_IsNull(obj))
return obj;
if (!jsf) {
JSClassID _classID;
jsf = JS_GetAnyOpaque(obj, &_classID);
gf_assert(jsf);
}
//store field associated with object if needed
JS_SetOpaque(obj, jsf);
if (!was_found) {
jsf->obj = obj;
}
//for createVRMLFromString only, no parent
if (!parent)
return obj;
gf_assert(jsf->owner);
/*obj corresponding to an existing field/node, store it and prevent GC on object*/
/*remember the object*/
if (!parent->sgprivate->interact) GF_SAFEALLOC(parent->sgprivate->interact, struct _node_interactive_ext);
if (!parent->sgprivate->interact) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRMLJS] Failed to create interact storage\n"));
return JS_NULL;
}
if (!parent->sgprivate->interact->js_binding) {
GF_SAFEALLOC(parent->sgprivate->interact->js_binding, struct _node_js_binding);
if (!parent->sgprivate->interact->js_binding) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRMLJS] Failed to create JS bindings storage\n"));
return JS_NULL;
}
parent->sgprivate->interact->js_binding->fields = gf_list_new();
if (!parent->sgprivate->interact->js_binding->fields) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRMLJS] Failed to create JS bindings storage\n"));
return JS_NULL;
}
}
if ( gf_list_find(parent->sgprivate->interact->js_binding->fields, jsf) < 0) {
gf_assert(jsf->owner == parent);
gf_list_add(parent->sgprivate->interact->js_binding->fields, jsf);
}
if (!was_found) {
if (gf_list_find(priv->jsf_cache, jsf)<0)
gf_list_add(priv->jsf_cache, jsf);
} else {
gf_assert (gf_list_find(priv->jsf_cache, jsf)>=0);
}
/*our JS Array object (MFXXX) are always rooted and added to the cache upon construction*/
if (jsf->mfvals) {
GF_JSClass *sf_class;
u32 count = jsf->mfvals_count;
sf_class = get_sf_class(jsf->field.fieldType);
if (!sf_class) count=0;
for (i=0; i<count; i++) {
JSValue item = jsf->mfvals[i];
GF_JSField *afield = JS_GetOpaque(item, sf_class->class_id);
if (afield) {
if (afield->owner != parent) {
continue;
}
if ( gf_list_find(parent->sgprivate->interact->js_binding->fields, afield) < 0) {
gf_list_add(parent->sgprivate->interact->js_binding->fields, afield);
}
}
}
}
parent->sgprivate->flags |= GF_NODE_HAS_BINDING;
return JS_DupValue(priv->js_ctx, jsf->obj);
}
static void JS_ReleaseRootObjects(GF_ScriptPriv *priv)
{
JSRuntime *js_runtime = gf_js_get_rt();
/*pop the list rather than walk through it since unprotecting an element could trigger GC which in turn could modify this list content*/
while (gf_list_count(priv->jsf_cache)) {
JSValue obj;
GF_JSField *jsf = gf_list_pop_back(priv->jsf_cache);
gf_assert(jsf);
/* !!! WARNING !!!
GC is handled at the JSRuntime level, not at the JSContext level.
Objects may not be finalized until the runtime is destroyed/GC'ed, which is not what we want.
We therefore destroy by hand all SFNode (obj rooted) and MFNode (for js_list)
*/
obj = jsf->obj;
jsf->obj = JS_UNDEFINED;
if (jsf->mfvals && js_runtime)
array_finalize_ex(js_runtime, obj, 0);
else if (jsf->node && js_runtime)
node_finalize_ex(js_runtime, obj, 0);
else
jsf->js_ctx=NULL;
JS_FreeValue(priv->js_ctx, obj);
}
}
static void JS_PreDestroy(GF_Node *node)
{
GF_SceneGraph *scene;
GF_ScriptPriv *priv = node->sgprivate->UserPrivate;
if (!priv) return;
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[Script] Destroying script node %s", gf_node_get_log_name(node) ));
/*"shutdown" is no longer supported, as it is typically called when one of a parent node is destroyed through
a GC call.*/
gf_js_lock(priv->js_ctx, 1);
JS_FreeValue(priv->js_ctx, priv->the_event);
JS_FreeValue(priv->js_ctx, priv->node_toString_fun);
JS_FreeValue(priv->js_ctx, priv->node_getTime_fun);
#ifndef GPAC_DISABLE_SVG
JS_FreeValue(priv->js_ctx, priv->node_addEventListener_fun);
JS_FreeValue(priv->js_ctx, priv->node_removeEventListener_fun);
#endif
/*unprotect all cached objects from GC*/
JS_ReleaseRootObjects(priv);
#ifndef GPAC_DISABLE_SVG
gf_sg_js_dom_pre_destroy(JS_GetRuntime(priv->js_ctx), node->sgprivate->scenegraph, NULL);
#endif
JS_FreeValue(priv->js_ctx, priv->js_obj);
scene = JS_GetContextOpaque(priv->js_ctx);
if (scene && scene->__reserved_null) {
GF_Node *n = JS_GetContextOpaque(priv->js_ctx);
scene = n->sgprivate->scenegraph;
}
if (scene && scene->attached_session) {
void gf_fs_unload_js_api(JSContext *c, GF_FilterSession *fs);
gf_fs_unload_js_api(priv->js_ctx, scene->attached_session);
}
gf_js_lock(priv->js_ctx, 0);
gf_js_delete_context(priv->js_ctx);
#ifndef GPAC_DISABLE_SVG
dom_js_unload();
#endif
gf_list_del(priv->jsf_cache);
priv->js_ctx = NULL;
/*unregister script from parent scene (cf base_scenegraph::sg_reset) */
gf_list_del_item(node->sgprivate->scenegraph->scripts, node);
}
static void JS_InitScriptFields(GF_ScriptPriv *priv, GF_Node *sc)
{
u32 i;
GF_ScriptField *sf;
GF_FieldInfo info;
JSValue val;
JSAtom atom;
i=0;
while ((sf = gf_list_enum(priv->fields, &i))) {
switch (sf->eventType) {
case GF_SG_EVENT_IN:
//nothing to do
break;
case GF_SG_EVENT_OUT:
gf_node_get_field(sc, sf->ALL_index, &info);
//do not assign a value, eventOut are write-only fields
/*create a setter function for this field to be notified whenever modified*/
sf->magic = i;
JSValue setter = JS_NewCFunction2(priv->js_ctx, (JSCFunction *) gf_sg_script_eventout_set_prop, sf->name, 1, JS_CFUNC_setter_magic, sf->magic);
atom = JS_NewAtom(priv->js_ctx, sf->name);
JS_DefinePropertyGetSet(priv->js_ctx, priv->js_obj, atom, JS_UNDEFINED, setter, 0);
JS_FreeAtom(priv->js_ctx, atom);
break;
default:
/*get field value and define in in global scope*/
gf_node_get_field(sc, sf->ALL_index, &info);
val = gf_sg_script_to_qjs_field(priv, &info, sc, 0);
JS_SetPropertyStr(priv->js_ctx, priv->js_obj, (const char *) sf->name, val);
break;
}
}
}
GF_EXPORT
void gf_js_vrml_flush_event_out(GF_Node *node, GF_ScriptPriv *priv)
{
u32 i;
GF_ScriptField *sf;
/*flush event out*/
i=0;
while ((sf = gf_list_enum(priv->fields, &i))) {
if (sf->activate_event_out) {
sf->activate_event_out = 0;
gf_node_event_out(node, sf->ALL_index);
#ifndef GPAC_DISABLE_SVG
if (node->sgprivate->interact && node->sgprivate->interact->dom_evt) {
GF_FieldInfo info;
if (gf_node_get_field(node, sf->ALL_index, &info)==GF_OK)
gf_node_changed(node, &info);
}
#endif
}
}
}
static void JS_EventIn(GF_Node *node, GF_FieldInfo *in_field)
{
JSValue fval, rval;
Double time;
JSValue argv[2];
GF_ScriptField *sf;
GF_FieldInfo t_info;
GF_ScriptPriv *priv;
u32 i;
priv = node->sgprivate->UserPrivate;
/*no support for change of static fields*/
if (in_field->fieldIndex<3) return;
i = (node->sgprivate->tag==TAG_MPEG4_Script) ? 3 : 4;
sf = gf_list_get(priv->fields, in_field->fieldIndex - i);
time = gf_node_get_scene_time(node);
/*
if (sf->last_route_time == time) return;
*/
sf->last_route_time = time;
gf_js_lock(priv->js_ctx, 1);
fval = JS_GetPropertyStr(priv->js_ctx, priv->js_obj, sf->name);
if (JS_IsUndefined(fval) || JS_IsNull(fval) || !JS_IsFunction(priv->js_ctx, fval)) {
gf_js_lock(priv->js_ctx, 0);
JS_FreeValue(priv->js_ctx, fval);
return;
}
argv[0] = gf_sg_script_to_qjs_field(priv, in_field, node, 1);
memset(&t_info, 0, sizeof(GF_FieldInfo));
t_info.far_ptr = &sf->last_route_time;
t_info.fieldType = GF_SG_VRML_SFTIME;
t_info.fieldIndex = -1;
t_info.name = "timestamp";
argv[1] = gf_sg_script_to_qjs_field(priv, &t_info, node, 1);
rval = JS_Call(priv->js_ctx, fval, priv->js_obj, 2, argv);
if (JS_IsException(rval)) {
js_dump_error(priv->js_ctx);
}
JS_FreeValue(priv->js_ctx, fval);
JS_FreeValue(priv->js_ctx, rval);
JS_FreeValue(priv->js_ctx, argv[0]);
JS_FreeValue(priv->js_ctx, argv[1]);
js_std_loop(priv->js_ctx);
gf_js_lock(priv->js_ctx, 0);
gf_js_vrml_flush_event_out(node, priv);
do_js_gc(priv->js_ctx, node);
}
static Bool vrml_js_load_script(M_Script *script, char *file, Bool primary_script, JSValue *rval)
{
char *jsscript;
u32 fsize;
Bool success = 1;
u32 flags = JS_EVAL_TYPE_GLOBAL;
JSValue ret;
GF_Err e;
GF_ScriptPriv *priv = (GF_ScriptPriv *) script->sgprivate->UserPrivate;
*rval = JS_UNDEFINED;
e = gf_file_load_data(file, (u8 **) &jsscript, &fsize);
if (e) {
return GF_FALSE;
}
if (!gf_opts_get_bool("core", "no-js-mods") && JS_DetectModule((const char *) jsscript, fsize )) {
flags = JS_EVAL_TYPE_MODULE;
priv->use_strict = GF_TRUE;
}
//we use JS_EVAL_TYPE_MODULE only for GUI at the current time. However the GUI script is still old and not
//compliant to strict mode, using a lot of global functions
//the code below is correct (we should always stick to strict if modules) but deactivated until we rewrite the GUI
// if (priv->use_strict)
// flags = JS_EVAL_TYPE_MODULE;
ret = JS_Eval(priv->js_ctx, jsscript, fsize, file, flags);
if (JS_IsException(ret)) {
success = 0;
js_dump_error(priv->js_ctx);
}
if (!success || primary_script)
JS_FreeValue(priv->js_ctx, ret);
else
*rval = ret;
if (success && primary_script) {
JSValue fun = JS_GetPropertyStr(priv->js_ctx, priv->js_obj, "initialize");
if (JS_IsFunction(priv->js_ctx, fun)) {
ret = JS_Call(priv->js_ctx, fun, priv->js_obj, 0, NULL);
if (JS_IsException(ret)) {
js_dump_error(priv->js_ctx);
}
gf_js_vrml_flush_event_out((GF_Node *)script, priv);
JS_FreeValue(priv->js_ctx, ret);
}
JS_FreeValue(priv->js_ctx, fun);
}
gf_free(jsscript);
if (success)
js_std_loop(priv->js_ctx);
return success;
}
#endif
#ifndef GPAC_DISABLE_SVG
Bool svg_js_load_script(GF_Node *script, char *file);
#endif
typedef struct
{
GF_Node *script;
//type: 0: VRML main JS, 1: imported JS from VRML JS, 2: SVG JS
u32 type;
GF_DownloadSession *sess;
} AsyncFetcher;
#ifdef GPAC_USE_DOWNLOADER
void async_script_sess_io(void *usr_cbk, GF_NETIO_Parameter *parameter)
{
AsyncFetcher *as = usr_cbk;
GF_Err e=GF_OK;
if (parameter->msg_type == GF_NETIO_DATA_TRANSFERED) {
const char *szCache = gf_dm_sess_get_cache_name(parameter->sess);
if (as->type<=1) {
#ifndef GPAC_DISABLE_VRML
M_Script *script = (M_Script *) as->script;
GF_ScriptPriv *priv = (GF_ScriptPriv *) as->script->sgprivate->UserPrivate;
JSValue rval;
if (!vrml_js_load_script(script, (char *) szCache, (as->type==1) ? 0 : 1, &rval))
e = GF_SCRIPT_ERROR;
JS_FreeValue(priv->js_ctx, rval);
#else
e = GF_NOT_SUPPORTED;
#endif
} else {
#ifndef GPAC_DISABLE_SVG
if (!svg_js_load_script(as->script, (char *) szCache))
e = GF_SCRIPT_ERROR;
#else
e = GF_NOT_SUPPORTED;
#endif
}
gf_dm_sess_del(as->sess);
gf_free(as);
}
else if (parameter->msg_type == GF_NETIO_STATE_ERROR) {
e = parameter->error;
gf_dm_sess_del(as->sess);
gf_free(as);
}
if (e) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("Failed to load script: %s\n", gf_error_to_string(e)));
}
}
#endif
GF_Err vrml_svg_js_async_load(GF_DownloadManager *dnld_man, char *url, u32 type, GF_Node *script, JSValue *rval)
{
#ifdef GPAC_USE_DOWNLOADER
AsyncFetcher *as;
GF_Err e;
if (rval) *rval = JS_UNDEFINED;
GF_SAFEALLOC(as, AsyncFetcher);
if (!as) return GF_OUT_OF_MEM;
//GF_ScriptPriv *priv = (GF_ScriptPriv *) script->sgprivate->UserPrivate;
as->script = script;
as->type = type;
as->sess = gf_dm_sess_new(dnld_man, url, GF_NETIO_SESSION_MEMORY_CACHE, async_script_sess_io, as, &e);
if (e) {
gf_free(as);
return e;
}
return gf_dm_sess_process(as->sess);
#else
return GF_NOT_SUPPORTED;
#endif
}
#ifndef GPAC_DISABLE_VRML
/*fetches each listed URL and attempts to load the script - this is SYNCHRONOUS*/
Bool JSScriptFromFile(GF_Node *node, const char *opt_file, Bool no_complain, JSValue *rval)
{
GF_JSAPIParam par;
u32 i;
GF_DownloadManager *dnld_man;
GF_Err e;
M_Script *script = (M_Script *)node;
e = GF_SCRIPT_ERROR;
par.dnld_man = NULL;
ScriptAction(NULL, node->sgprivate->scenegraph, GF_JSAPI_OP_GET_DOWNLOAD_MANAGER, NULL, &par);
if (!par.dnld_man) return 0;
dnld_man = par.dnld_man;
for (i=0; i<script->url.count; i++) {
char *url;
const char *ext;
char *_url = script->url.vals[i].script_text;
if (opt_file) {
if (strnicmp(_url+4, "script:", 7) && strnicmp(_url+5, "script:", 5)) {
_url = gf_url_concatenate(script->url.vals[i].script_text, opt_file);
} else {
_url = gf_strdup(opt_file);
}
}
par.uri.url = _url;
par.uri.nb_params = 0;
ScriptAction(NULL, node->sgprivate->scenegraph, GF_JSAPI_OP_RESOLVE_URI, node, &par);
if (opt_file) gf_free(_url);
url = (char *)par.uri.url;
ext = gf_file_ext_start(url);
if (ext && strnicmp(ext, ".js", 3)) {
gf_free(url);
continue;
}
if (!strstr(url, "://") || !strnicmp(url, "file://", 7)) {
Bool res = vrml_js_load_script(script, url, opt_file ? 0 : 1, rval);
gf_free(url);
if (res) return 1;
if (no_complain) return 0;
} else {
#ifdef GPAC_USE_DOWNLOADER
e = vrml_svg_js_async_load(dnld_man, url, opt_file ? 1 : 0, node, rval);
gf_free(url);
if (!e) return 1;
#endif
}
}
par.info.e = GF_SCRIPT_ERROR;
par.info.msg = "Cannot fetch script";
ScriptAction(NULL, node->sgprivate->scenegraph, GF_JSAPI_OP_MESSAGE, NULL, &par);
return 0;
}
static void JSScript_LoadVRML(GF_Node *node)
{
char *str;
u32 i;
u32 flags = JS_EVAL_TYPE_GLOBAL;
Bool local_script;
JSValue rval;
M_Script *script = (M_Script *)node;
GF_ScriptPriv *priv = (GF_ScriptPriv *) node->sgprivate->UserPrivate;
if (!priv || priv->is_loaded) return;
if (!script->url.count) return;
priv->is_loaded = 1;
/*register script width parent scene (cf base_scenegraph::sg_reset) */
gf_list_add(node->sgprivate->scenegraph->scripts, node);
str = NULL;
for (i=0; i<script->url.count; i++) {
str = script->url.vals[i].script_text;
if (!str) continue;
while (str[0] && strchr("\n\t ", str[0])) str++;
if (!strnicmp(str, "javascript:", 11)) str += 11;
else if (!strnicmp(str, "vrmlscript:", 11)) str += 11;
else if (!strnicmp(str, "ecmascript:", 11)) str += 11;
else if (!strnicmp(str, "mpeg4script:", 12)) str += 12;
else str = NULL;
if (str) break;
}
local_script = str ? 1 : 0;
/*lock runtime and set current thread before creating the context*/
priv->js_ctx = gf_js_create_context();
if (!priv->js_ctx) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[VRML JS] Cannot allocate ECMAScript context for node\n"));
return;
}
gf_js_lock(priv->js_ctx, 1);
JS_SetContextOpaque(priv->js_ctx, node);
vrml_js_init_api(priv, node);
qjs_init_all_modules(priv->js_ctx, GF_TRUE, GF_TRUE);
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] Built-in classes initialized\n"));
#ifndef GPAC_DISABLE_SVG
/*initialize DOM*/
dom_js_load(node->sgprivate->scenegraph, priv->js_ctx);
/*create event object, and remember it*/
priv->the_event = dom_js_define_event(priv->js_ctx);
priv->the_event = JS_DupValue(priv->js_ctx, priv->the_event);
#endif
#ifdef GPAC_USE_DOWNLOADER
qjs_module_init_xhr_global(priv->js_ctx, priv->js_obj);
#endif
priv->jsf_cache = gf_list_new();
/*setup fields interfaces*/
JS_InitScriptFields(priv, node);
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] script fields initialized\n"));
priv->JS_PreDestroy = JS_PreDestroy;
priv->JS_EventIn = JS_EventIn;
if (!local_script) {
JSScriptFromFile(node, NULL, 0, &rval);
gf_js_lock(priv->js_ctx, 0);
return;
}
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] Evaluating script %s\n", str));
if (!gf_opts_get_bool("core", "no-js-mods") && JS_DetectModule((const char *) str, (u32) strlen(str) ))
flags = JS_EVAL_TYPE_MODULE;
JSValue ret = JS_Eval(priv->js_ctx, str, (u32) strlen(str), "inline_script", flags);
if (!JS_IsException(ret)) {
JSValue fval = JS_GetPropertyStr(priv->js_ctx, priv->js_obj, "initialize");
if (JS_IsFunction(priv->js_ctx, fval) && !JS_IsUndefined(fval)) {
JSValue r, args[2];
args[0] = JS_TRUE;
args[1] = JS_NewFloat64(priv->js_ctx, gf_node_get_scene_time(node) );
r = JS_Call(priv->js_ctx, fval, priv->js_obj, 2, args);
if (JS_IsException(r)) {
js_dump_error(priv->js_ctx);
}
JS_FreeValue(priv->js_ctx, r);
JS_FreeValue(priv->js_ctx, args[0]);
JS_FreeValue(priv->js_ctx, args[1]);
gf_js_vrml_flush_event_out(node, priv);
}
JS_FreeValue(priv->js_ctx, fval);
} else {
js_dump_error(priv->js_ctx);
}
JS_FreeValue(priv->js_ctx, ret);
js_std_loop(priv->js_ctx);
gf_js_lock(priv->js_ctx, 0);
do_js_gc(priv->js_ctx, node);
}
static void JSScript_Load(GF_Node *node)
{
#ifndef GPAC_DISABLE_SVG
void JSScript_LoadSVG(GF_Node *node);
#endif
switch (node->sgprivate->tag) {
case TAG_MPEG4_Script:
#ifndef GPAC_DISABLE_X3D
case TAG_X3D_Script:
#endif
JSScript_LoadVRML(node);
break;
#ifndef GPAC_DISABLE_SVG
case TAG_SVG_script:
case TAG_SVG_handler:
JSScript_LoadSVG(node);
break;
#endif
default:
break;
}
}
static void JSScript_NodeModified(GF_SceneGraph *sg, GF_Node *node, GF_FieldInfo *info, GF_Node *script)
{
u32 i;
GF_JSField *jsf;
/*this is REPLACE NODE signaling*/
if (script) {
u32 count;
GF_ScriptPriv *priv = gf_node_get_private(script);
count = gf_list_count(priv->jsf_cache);
for (i=0; i<count; i++) {
jsf = gf_list_get(priv->jsf_cache, i);
gf_assert(jsf);
if (jsf->node && (jsf->node==node)) {
//detach node and its js binding
jsf->node = NULL;
node->sgprivate->interact->js_binding->pf = NULL;
node->sgprivate->interact->js_binding->obj = JS_UNDEFINED;
}
}
return;
}
if (!info) {
/*handle DOM case*/
if ((node->sgprivate->tag>=GF_NODE_FIRST_PARENT_NODE_TAG)
&& node->sgprivate->interact
&& node->sgprivate->interact->js_binding
&& !JS_IsUndefined(node->sgprivate->interact->js_binding->obj))
{
if (gf_list_del_item(sg->objects, node->sgprivate->interact->js_binding)>=0) {
#ifndef GPAC_DISABLE_SVG
void svg_free_node_binding(struct __tag_svg_script_ctx *svg_js, GF_Node *node);
svg_free_node_binding(sg->svg_js, node);
#endif
}
return;
}
/*this is unregister signaling*/
if (!node->sgprivate->parents && node->sgprivate->interact && node->sgprivate->interact->js_binding && node->sgprivate->interact->js_binding->pf) {
GF_JSField *field = node->sgprivate->interact->js_binding->pf;
/*if this is the last occurence of the obj (no longer in JS), remove node binding*/
if (JS_VALUE_HAS_REF_COUNT(field->obj)) {
JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(field->obj);
if (p->ref_count==1) {
//store obj before calling JS_ObjectDestroyed which will set field->obj to JS_UNDEFINED
JSValue obj = field->obj;
JS_ObjectDestroyed(JS_GetRuntime(field->js_ctx), obj, field, 1);
JS_FreeValue(field->js_ctx, obj);
gf_free(field);
gf_assert( node->sgprivate->interact->js_binding->pf==NULL);
//unregister node since we destroyed the binding
gf_node_unregister(node, NULL);
}
}
return;
}
/*final destroy*/
if (!node->sgprivate->num_instances) {
i=0;
while (node->sgprivate->interact && node->sgprivate->interact->js_binding && (jsf = gf_list_enum(node->sgprivate->interact->js_binding->fields, &i))) {
jsf->owner = NULL;
if (jsf->mfvals) {
u32 j;
if (jsf->field.fieldType != GF_SG_VRML_MFNODE) {
u32 count = jsf->mfvals_count;
GF_JSClass *sf_class = get_sf_class(jsf->field.fieldType);
if (!sf_class) count=0;
for (j=0; j<count; j++) {
JSValue item = jsf->mfvals[j];
GF_JSField *afield = JS_GetOpaque(item, sf_class->class_id);
if (afield) {
afield->owner = NULL;
}
}
}
while (jsf->mfvals_count) {
jsf->mfvals_count--;
JS_FreeValue(jsf->js_ctx, jsf->mfvals[jsf->mfvals_count]);
}
gf_free(jsf->mfvals);
jsf->mfvals = NULL;
}
}
}
return;
}
/*this is field modification signaling*/
if (!node->sgprivate->interact || !node->sgprivate->interact->js_binding) return;
i=0;
while ((jsf = gf_list_enum(node->sgprivate->interact->js_binding->fields, &i))) {
if ((jsf->field.fieldIndex == info->fieldIndex) && (jsf->field.fieldType == info->fieldType)) {
jsf->field.NDTtype = 1;
/*if field is a script field, rewrite it right away because script fields are exposed
as global variables within the script
We also have a special value '-1' for the NDT for addChildren and removeChildren*/
#if 0
if ((info->NDTtype == (u32) -1) || (node == (GF_Node*)JS_GetScript(jsf->js_ctx))) {
gf_sg_script_update_cached_object( JS_GetScriptStack(jsf->js_ctx), jsf->obj, jsf, &jsf->field, node);
}
#endif
return;
}
}
}
GF_EXPORT
void gf_sg_handle_dom_event_for_vrml(GF_Node *node, GF_DOM_Event *event, GF_Node *observer)
{
#ifndef GPAC_DISABLE_SVG
GF_ScriptPriv *priv;
Bool prev_type;
JSClassID _classID;
//JSBool ret = JS_FALSE;
GF_DOM_Event *prev_event = NULL;
SVG_handlerElement *hdl;
JSValue ret;
JSValue evt;
JSValue argv[1];
hdl = (SVG_handlerElement *) node;
if (!hdl->js_data) return;
if (!JS_IsFunction(hdl->js_data->ctx, hdl->js_data->fun_val) && !JS_IsUndefined(hdl->js_data->evt_listen_obj)) {
return;
}
GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[DOM Events] Executing script code from VRML handler\n"));
priv = JS_GetScriptStack(hdl->js_data->ctx);
gf_js_lock(priv->js_ctx, 1);
prev_event = JS_GetAnyOpaque(priv->the_event, &_classID);
/*break loops*/
if (prev_event && (prev_event->type==event->type) && (prev_event->target==event->target)) {
gf_js_lock(priv->js_ctx, 0);
return;
}
evt = gf_dom_new_event(priv->js_ctx);
if (JS_IsUndefined(evt) || JS_IsNull(evt)) {
gf_js_lock(priv->js_ctx, 0);
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DOM Events] Cannot create JavaScript dom event for event type %d\n", event->type));
return;
}
prev_type = event->is_vrml;
event->is_vrml = 1;
JS_SetOpaque(priv->the_event, event);
JS_SetOpaque(evt, event);
argv[0] = evt;
if (JS_IsFunction(hdl->js_data->ctx, hdl->js_data->fun_val)) {
JSValue listenObj = priv->js_obj;
if (!JS_IsNull(hdl->js_data->evt_listen_obj) && !JS_IsUndefined(hdl->js_data->evt_listen_obj))
listenObj = hdl->js_data->evt_listen_obj;
ret = JS_Call(hdl->js_data->ctx, hdl->js_data->fun_val, listenObj, 1, argv);
} else {
JSValue fun = JS_GetPropertyStr(priv->js_ctx, hdl->js_data->evt_listen_obj, "handleEvent");
if (JS_IsFunction(priv->js_ctx, fun)) {
ret = JS_Call(priv->js_ctx, fun, hdl->js_data->evt_listen_obj, 1, argv);
} else {
ret = JS_UNDEFINED;
}
JS_FreeValue(priv->js_ctx, fun);
}
if (JS_IsException(ret)) {
js_dump_error(priv->js_ctx);
}
JS_FreeValue(priv->js_ctx, ret);
JS_FreeValue(priv->js_ctx, evt);
event->is_vrml = prev_type;
JS_SetOpaque(priv->the_event, prev_event);
js_std_loop(priv->js_ctx);
gf_js_lock(priv->js_ctx, 0);
#endif
}
#endif /*GPAC_DISABLE_VRML*/
#endif /*GPAC_HAS_QJS*/
/*set JavaScript interface*/
void gf_sg_set_script_action(GF_SceneGraph *scene, gf_sg_script_action script_act, void *cbk)
{
if (!scene) return;
scene->script_action = script_act;
scene->script_action_cbck = cbk;
#if defined(GPAC_HAS_QJS) && !defined(GPAC_DISABLE_VRML)
scene->script_load = JSScript_Load;
scene->on_node_modified = JSScript_NodeModified;
#endif
}
#ifdef GPAC_HAS_QJS
GF_EXPORT
GF_Node *gf_sg_js_get_node(JSContext *c, JSValue obj)
{
#ifndef GPAC_DISABLE_VRML
GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(obj, SFNodeClass.class_id);
if (ptr && (ptr->field.fieldType==GF_SG_VRML_SFNODE))
return * ((GF_Node **)ptr->field.far_ptr);
#endif
#ifndef GPAC_DISABLE_SVG
{
Bool has_p = 0;
JSValue ns = JS_GetPropertyStr(c, obj, "namespaceURI");
if (!JS_IsNull(ns) && !JS_IsUndefined(ns)) has_p = 1;
JS_FreeValue(c, ns);
if (has_p) return dom_get_element(c, obj);
}
#endif
return NULL;
}
#endif
GF_EXPORT
Bool gf_sg_has_scripting()
{
#ifdef GPAC_HAS_QJS
return 1;
#else
return 0;
#endif
}