LCOV - code coverage report
Current view: top level - compositor - mpeg4_sensors.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 588 918 64.1 %
Date: 2021-04-29 23:48:07 Functions: 43 49 87.8 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2000-2012
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / Scene Compositor sub-project
       9             :  *
      10             :  *  GPAC is free software; you can redistribute it and/or modify
      11             :  *  it under the terms of the GNU Lesser General Public License as published by
      12             :  *  the Free Software Foundation; either version 2, or (at your option)
      13             :  *  any later version.
      14             :  *
      15             :  *  GPAC is distributed in the hope that it will be useful,
      16             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :  *  GNU Lesser General Public License for more details.
      19             :  *
      20             :  *  You should have received a copy of the GNU Lesser General Public
      21             :  *  License along with this library; see the file COPYING.  If not, write to
      22             :  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
      23             :  *
      24             :  */
      25             : 
      26             : 
      27             : 
      28             : #include "nodes_stacks.h"
      29             : #include "visual_manager.h"
      30             : /*for anchor*/
      31             : #include "mpeg4_grouping.h"
      32             : 
      33             : #ifndef GPAC_DISABLE_VRML
      34             : 
      35             : /*for event DOM filtering type ...*/
      36             : #include <gpac/scenegraph_svg.h>
      37             : 
      38             : 
      39        1465 : static void mpeg4_sensor_deleted(GF_Node *node, GF_SensorHandler *hdl)
      40             : {
      41        1465 :         GF_Compositor *compositor = gf_sc_get_compositor(node);
      42        1465 :         if (compositor) {
      43             :                 GF_VisualManager *visual;
      44        1465 :                 u32 i=0;
      45        1465 :                 gf_list_del_item(compositor->sensors, hdl);
      46        1465 :                 gf_list_del_item(compositor->previous_sensors, hdl);
      47        1465 :                 if (compositor->interaction_sensors) compositor->interaction_sensors--;
      48        3100 :                 while ( (visual=gf_list_enum(compositor->visuals, &i)) ) {
      49        1635 :                         if (visual->offscreen)
      50         170 :                                 compositor_compositetexture_sensor_delete(visual->offscreen, hdl);
      51             :                 }
      52             : 
      53             : #ifndef GPAC_DISABLE_SVG
      54        1465 :                 gf_sg_unregister_event_type(gf_node_get_graph(node), GF_DOM_EVENT_MOUSE|GF_DOM_EVENT_KEY);
      55             : #endif
      56             :         }
      57        1465 : }
      58             : 
      59        1465 : static void mpeg4_sensor_created(GF_Compositor *compositor, GF_Node *node)
      60             : {
      61        1465 :         compositor->interaction_sensors--;
      62             : #ifndef GPAC_DISABLE_SVG
      63        1465 :         gf_sg_register_event_type(gf_node_get_graph(node), GF_DOM_EVENT_MOUSE|GF_DOM_EVENT_KEY);
      64             : #endif
      65        1465 : }
      66             : 
      67             : 
      68             : typedef struct
      69             : {
      70             :         GROUPING_MPEG4_STACK_2D
      71             : 
      72             :         Bool enabled, active, over;
      73             :         GF_SensorHandler hdl;
      74             :         GF_Compositor *compositor;
      75             : } AnchorStack;
      76             : 
      77         689 : static void TraverseAnchor(GF_Node *node, void *rs, Bool is_destroy)
      78             : {
      79         689 :         AnchorStack *st = (AnchorStack *) gf_node_get_private(node);
      80             :         GF_TraverseState *tr_state = (GF_TraverseState *)rs;
      81             : 
      82         689 :         if (is_destroy) {
      83           6 :                 mpeg4_sensor_deleted(node, &st->hdl);
      84           6 :                 gf_sc_check_focus_upon_destroy(node);
      85           6 :                 if (st->sensors) gf_list_del(st->sensors);
      86           6 :                 gf_free(st);
      87           6 :                 return;
      88             :         }
      89             : 
      90         683 :         if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) {
      91             :                 MFURL *url = NULL;
      92           6 :                 switch (gf_node_get_tag(node)) {
      93           6 :                 case TAG_MPEG4_Anchor:
      94           6 :                         url = & ((M_Anchor *)node)->url;
      95           6 :                         break;
      96             : #ifndef GPAC_DISABLE_X3D
      97           0 :                 case TAG_X3D_Anchor:
      98           0 :                         url = & ((X_Anchor *)node)->url;
      99           0 :                         break;
     100             : #endif
     101             :                 }
     102           6 :                 st->enabled = 0;
     103           6 :                 if (url && url->count && url->vals[0].url && strlen(url->vals[0].url) )
     104           5 :                         st->enabled = 1;
     105             : 
     106           6 :                 gf_node_dirty_clear(node, GF_SG_NODE_DIRTY);
     107             :         }
     108             : 
     109         683 :         group_2d_traverse(node, (GroupingNode2D*)st, tr_state);
     110             : }
     111             : 
     112           0 : static void anchor_activation(GF_Node *node, AnchorStack *st, GF_Compositor *compositor)
     113             : {
     114             :         GF_Event evt;
     115             :         u32 i;
     116             :         MFURL *url = NULL;
     117           0 :         switch (gf_node_get_tag(node)) {
     118           0 :         case TAG_MPEG4_Anchor:
     119           0 :                 url = & ((M_Anchor *)node)->url;
     120           0 :                 evt.navigate.param_count = ((M_Anchor *)node)->parameter.count;
     121           0 :                 evt.navigate.parameters = (const char **) ((M_Anchor *)node)->parameter.vals;
     122             :                 break;
     123             : #ifndef GPAC_DISABLE_X3D
     124           0 :         case TAG_X3D_Anchor:
     125           0 :                 url = & ((X_Anchor *)node)->url;
     126           0 :                 evt.navigate.param_count = ((X_Anchor *)node)->parameter.count;
     127           0 :                 evt.navigate.parameters = (const char **) ((X_Anchor *)node)->parameter.vals;
     128             :                 break;
     129             : #endif
     130             :         }
     131           0 :         evt.type = GF_EVENT_NAVIGATE;
     132             :         i=0;
     133           0 :         while (url && i<url->count) {
     134           0 :                 evt.navigate.to_url = url->vals[i].url;
     135           0 :                 if (!evt.navigate.to_url) break;
     136             :                 /*current scene navigation*/
     137           0 :                 if (evt.navigate.to_url[0] == '#') {
     138             :                         GF_Node *bindable;
     139           0 :                         evt.navigate.to_url++;
     140           0 :                         bindable = gf_sg_find_node_by_name(gf_node_get_graph(node), (char *) evt.navigate.to_url);
     141           0 :                         if (bindable) {
     142           0 :                                 Bindable_SetSetBind(bindable, 1);
     143             :                                 break;
     144             :                         }
     145             :                 } else {
     146           0 :                         if (gf_scene_process_anchor(node, &evt))
     147             :                                 break;
     148             :                 }
     149           0 :                 i++;
     150             :         }
     151           0 : }
     152             : 
     153           0 : static Bool OnAnchor(GF_SensorHandler *sh, Bool is_over, Bool is_cancel, GF_Event *ev, GF_Compositor *compositor)
     154             : {
     155             :         GF_Event evt;
     156             :         MFURL *url = NULL;
     157           0 :         AnchorStack *st = (AnchorStack *) gf_node_get_private(sh->sensor);
     158             : 
     159           0 :         if ((ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT)) st->active = 1;
     160           0 :         else if ((ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)) st->active = 1;
     161           0 :         else if (st->active && (
     162           0 :                      /*mouse*/ ((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT))
     163           0 :                      || /*mouse*/((ev->type==GF_EVENT_KEYUP) && (ev->key.key_code==GF_KEY_ENTER))
     164             :                  ) ) {
     165           0 :                 if (!is_cancel) anchor_activation(sh->sensor, st, compositor);
     166           0 :         } else if (is_over && !st->over) {
     167           0 :                 st->over = 1;
     168           0 :                 evt.type = GF_EVENT_NAVIGATE_INFO;
     169           0 :                 switch (gf_node_get_tag(sh->sensor)) {
     170           0 :                 case TAG_MPEG4_Anchor:
     171           0 :                         evt.navigate.to_url = ((M_Anchor *)sh->sensor)->description.buffer;
     172           0 :                         url = & ((M_Anchor *)sh->sensor)->url;
     173           0 :                         break;
     174             : #ifndef GPAC_DISABLE_X3D
     175           0 :                 case TAG_X3D_Anchor:
     176           0 :                         evt.navigate.to_url = ((X_Anchor *)sh->sensor)->description.buffer;
     177           0 :                         url = & ((X_Anchor *)sh->sensor)->url;
     178           0 :                         break;
     179             : #endif
     180             :                 }
     181           0 :                 if (url && (!evt.navigate.to_url || !strlen(evt.navigate.to_url))) evt.navigate.to_url = url->vals[0].url;
     182           0 :                 gf_sc_send_event(compositor, &evt);
     183             : 
     184           0 :         } else if (!is_over) {
     185           0 :                 st->over = 0;
     186             :         }
     187           0 :         return 0;
     188             : }
     189             : 
     190           0 : static Bool anchor_is_enabled(GF_Node *node)
     191             : {
     192           0 :         AnchorStack *st = (AnchorStack *) gf_node_get_private(node);
     193           0 :         return st->enabled;
     194             : }
     195             : 
     196           0 : static void on_activate_anchor(GF_Node *node, GF_Route *route)
     197             : {
     198           0 :         AnchorStack *st = (AnchorStack *) gf_node_get_private(node);
     199           0 :         if (!((M_Anchor *)node)->on_activate) return;
     200             : 
     201           0 :         anchor_activation(node, st, st->compositor);
     202             : }
     203             : 
     204           6 : GF_SensorHandler *gf_sc_anchor_get_handler(GF_Node *n)
     205             : {
     206           6 :         AnchorStack *st = (AnchorStack *) gf_node_get_private(n);
     207           6 :         return &st->hdl;
     208             : }
     209             : 
     210             : 
     211           6 : void compositor_init_anchor(GF_Compositor *compositor, GF_Node *node)
     212             : {
     213             :         AnchorStack *stack;
     214           6 :         GF_SAFEALLOC(stack, AnchorStack);
     215           6 :         if (!stack) {
     216           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate anchor stack\n"));
     217             :                 return;
     218             :         }
     219             : 
     220           6 :         stack->hdl.IsEnabled = anchor_is_enabled;
     221           6 :         stack->hdl.OnUserEvent = OnAnchor;
     222           6 :         stack->hdl.sensor = node;
     223           6 :         if (gf_node_get_tag(node)==TAG_MPEG4_Anchor) {
     224           6 :                 ((M_Anchor *)node)->on_activate = on_activate_anchor;
     225             :         }
     226           6 :         stack->compositor = compositor;
     227           6 :         mpeg4_sensor_created(compositor, node);
     228           6 :         gf_node_set_private(node, stack);
     229           6 :         gf_node_set_callback_function(node, TraverseAnchor);
     230             : }
     231             : 
     232             : 
     233             : typedef struct
     234             : {
     235             :         GF_SensorHandler hdl;
     236             :         GF_Compositor *compositor;
     237             :         Fixed start_angle;
     238             :         GF_Matrix initial_matrix;
     239             : } DiscSensorStack;
     240             : 
     241         385 : static void DestroyDiscSensor(GF_Node *node, void *rs, Bool is_destroy)
     242             : {
     243         385 :         if (is_destroy) {
     244           9 :                 DiscSensorStack *st = (DiscSensorStack *) gf_node_get_private(node);
     245           9 :                 mpeg4_sensor_deleted(node, &st->hdl);
     246           9 :                 gf_free(st);
     247             :         }
     248         385 : }
     249             : 
     250          29 : static Bool ds_is_enabled(GF_Node *n)
     251             : {
     252             :         M_DiscSensor *ds = (M_DiscSensor *)n;
     253          29 :         return (ds->enabled || ds->isActive);
     254             : }
     255             : 
     256             : 
     257          46 : static Bool OnDiscSensor(GF_SensorHandler *sh, Bool is_over, Bool is_cancel, GF_Event *ev, GF_Compositor *compositor)
     258             : {
     259          46 :         Bool is_mouse = (ev->type<=GF_EVENT_MOUSEWHEEL) ? 1 : 0;
     260          46 :         M_DiscSensor *ds = (M_DiscSensor *)sh->sensor;
     261          46 :         DiscSensorStack *stack = (DiscSensorStack *) gf_node_get_private(sh->sensor);
     262             : 
     263          82 :         if (ds->isActive &&
     264          36 :                 (!ds->enabled
     265          36 :                  || /*mouse*/((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT))
     266          35 :                  || /*keyboar*/(!is_mouse && (!is_over|| ((ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER))) )
     267             :                 ) ) {
     268           1 :                 if (ds->autoOffset) {
     269           1 :                         ds->offset = ds->rotation_changed;
     270             :                         /*that's an exposedField*/
     271           1 :                         if (!is_cancel) gf_node_event_out(sh->sensor, 4/*"offset"*/);
     272             :                 }
     273           1 :                 ds->isActive = 0;
     274           1 :                 if (!is_cancel) gf_node_event_out(sh->sensor, 5/*"isActive"*/);
     275           1 :                 sh->grabbed = 0;
     276           1 :                 return is_cancel ? 0 : 1;
     277          45 :         } else if (is_mouse) {
     278          45 :                 if (!ds->isActive && (ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT)) {
     279             :                         /*store inverse matrix*/
     280           2 :                         gf_mx_copy(stack->initial_matrix, compositor->hit_local_to_world);
     281           2 :                         stack->start_angle = gf_atan2(compositor->hit_local_point.y, compositor->hit_local_point.x);
     282           2 :                         ds->isActive = 1;
     283           2 :                         gf_node_event_out(sh->sensor, 5/*"isActive"*/);
     284           2 :                         sh->grabbed = 1;
     285           2 :                         return 1;
     286             :                 }
     287          43 :                 else if (ds->isActive) {
     288             :                         GF_Ray loc_ray;
     289             :                         Fixed rot;
     290             :                         SFVec3f res;
     291          35 :                         loc_ray = compositor->hit_world_ray;
     292          35 :                         gf_mx_apply_ray(&stack->initial_matrix, &loc_ray);
     293          35 :                         compositor_get_2d_plane_intersection(&loc_ray, &res);
     294             : 
     295          35 :                         rot = gf_atan2(res.y, res.x) - stack->start_angle + ds->offset;
     296          35 :                         if (ds->minAngle < ds->maxAngle) {
     297             :                                 /*FIXME this doesn't work properly*/
     298           0 :                                 if (rot < ds->minAngle) rot = ds->minAngle;
     299           0 :                                 if (rot > ds->maxAngle) rot = ds->maxAngle;
     300             :                         }
     301          35 :                         ds->rotation_changed = rot;
     302          35 :                         gf_node_event_out(sh->sensor, 6/*"rotation_changed"*/);
     303          35 :                         ds->trackPoint_changed.x = res.x;
     304          35 :                         ds->trackPoint_changed.y = res.y;
     305          35 :                         gf_node_event_out(sh->sensor, 7/*"trackPoint_changed"*/);
     306             :                         return 1;
     307             :                 }
     308             :         } else {
     309           0 :                 if (!ds->isActive && is_over && (ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)) {
     310           0 :                         ds->isActive = 1;
     311           0 :                         stack->start_angle = ds->offset;
     312           0 :                         gf_node_event_out(sh->sensor, 5/*"isActive"*/);
     313           0 :                         return 1;
     314             :                 }
     315           0 :                 else if (ds->isActive && (ev->type==GF_EVENT_KEYDOWN)) {
     316             :                         Fixed res;
     317           0 :                         Fixed diff = (ev->key.flags & GF_KEY_MOD_SHIFT) ? GF_PI/8 : GF_PI/64;
     318           0 :                         res = stack->start_angle;
     319           0 :                         switch (ev->key.key_code) {
     320           0 :                         case GF_KEY_LEFT:
     321             :                         case GF_KEY_UP:
     322           0 :                                 res += -diff;
     323           0 :                                 break;
     324           0 :                         case GF_KEY_RIGHT:
     325             :                         case GF_KEY_DOWN:
     326           0 :                                 res += diff;
     327           0 :                                 break;
     328           0 :                         case GF_KEY_HOME:
     329           0 :                                 res = ds->offset;
     330           0 :                                 break;
     331             :                         default:
     332             :                                 return 0;
     333             :                         }
     334           0 :                         if (ds->minAngle < ds->maxAngle) {
     335             :                                 /*FIXME this doesn't work properly*/
     336           0 :                                 if (res < ds->minAngle) res = ds->minAngle;
     337           0 :                                 if (res > ds->maxAngle) res = ds->maxAngle;
     338             :                         }
     339           0 :                         stack->start_angle = res;
     340           0 :                         ds->rotation_changed = res;
     341           0 :                         gf_node_event_out(sh->sensor, 6/*"rotation_changed"*/);
     342           0 :                         return 1;
     343             :                 }
     344             :         }
     345             :         return 0;
     346             : }
     347             : 
     348             : static GF_SensorHandler *disc_sensor_get_handler(GF_Node *n)
     349             : {
     350          29 :         DiscSensorStack *st = (DiscSensorStack *)gf_node_get_private(n);
     351          29 :         return &st->hdl;
     352             : }
     353             : 
     354           9 : void compositor_init_disc_sensor(GF_Compositor *compositor, GF_Node *node)
     355             : {
     356             :         DiscSensorStack *st;
     357           9 :         GF_SAFEALLOC(st, DiscSensorStack);
     358           9 :         if (!st) {
     359           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate disc sensor stack\n"));
     360             :                 return;
     361             :         }
     362             : 
     363           9 :         st->hdl.IsEnabled = ds_is_enabled;
     364           9 :         st->hdl.OnUserEvent = OnDiscSensor;
     365           9 :         st->hdl.sensor = node;
     366           9 :         st->compositor = compositor;
     367           9 :         mpeg4_sensor_created(compositor, node);
     368           9 :         gf_node_set_private(node, st);
     369           9 :         gf_node_set_callback_function(node, DestroyDiscSensor);
     370             : }
     371             : 
     372             : 
     373             : typedef struct
     374             : {
     375             :         SFVec2f start_drag;
     376             :         GF_Matrix initial_matrix;
     377             :         GF_Compositor *compositor;
     378             :         GF_SensorHandler hdl;
     379             : } PS2DStack;
     380             : 
     381        3001 : static void DestroyPlaneSensor2D(GF_Node *node, void *rs, Bool is_destroy)
     382             : {
     383        3001 :         if (is_destroy) {
     384          39 :                 PS2DStack *st = (PS2DStack *) gf_node_get_private(node);
     385          39 :                 mpeg4_sensor_deleted(node, &st->hdl);
     386          39 :                 gf_free(st);
     387             :         }
     388        3001 : }
     389             : 
     390         755 : static Bool ps2D_is_enabled(GF_Node *n)
     391             : {
     392             :         M_PlaneSensor2D *ps2d = (M_PlaneSensor2D *)n;
     393         755 :         return (ps2d->enabled || ps2d->isActive);
     394             : }
     395             : 
     396         503 : static Bool OnPlaneSensor2D(GF_SensorHandler *sh, Bool is_over, Bool is_cancel, GF_Event *ev, GF_Compositor *compositor)
     397             : {
     398         503 :         Bool is_mouse = (ev->type<=GF_EVENT_MOUSEWHEEL) ? 1 : 0;
     399         503 :         M_PlaneSensor2D *ps = (M_PlaneSensor2D *)sh->sensor;
     400         503 :         PS2DStack *stack = (PS2DStack *) gf_node_get_private(sh->sensor);
     401             : 
     402             : 
     403         886 :         if (ps->isActive &&
     404         383 :                 (!ps->enabled
     405         383 :                  || /*mouse*/((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT))
     406         373 :                  || /*keyboar*/(!is_mouse && (!is_over|| ((ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER))) )
     407             :                 ) ) {
     408          10 :                 if (ps->autoOffset) {
     409          10 :                         ps->offset = ps->translation_changed;
     410          10 :                         if (!is_cancel) gf_node_event_out(sh->sensor, 4/*"offset"*/);
     411             :                 }
     412             : 
     413          10 :                 ps->isActive = 0;
     414          10 :                 if (!is_cancel) gf_node_event_out(sh->sensor, 5/*"isActive"*/);
     415          10 :                 sh->grabbed = 0;
     416          10 :                 return is_cancel ? 0 : 1;
     417         493 :         } else if (is_mouse) {
     418         490 :                 if (!ps->isActive && (ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT)) {
     419          14 :                         gf_mx_copy(stack->initial_matrix, compositor->hit_local_to_world);
     420          14 :                         stack->start_drag.x = compositor->hit_local_point.x - ps->offset.x;
     421          14 :                         stack->start_drag.y = compositor->hit_local_point.y - ps->offset.y;
     422          14 :                         ps->isActive = 1;
     423          14 :                         gf_node_event_out(sh->sensor, 5/*"isActive"*/);
     424          14 :                         sh->grabbed = 1;
     425             :                         /*fallthrough to fire mouse coords*/
     426             :                         //return 1;
     427             :                 }
     428         490 :                 if (ps->isActive) {
     429             :                         SFVec3f res;
     430             :                         GF_Ray loc_ray;
     431         387 :                         loc_ray = compositor->hit_world_ray;
     432         387 :                         gf_mx_apply_ray(&stack->initial_matrix, &loc_ray);
     433             : 
     434         387 :                         compositor_get_2d_plane_intersection(&loc_ray, &res);
     435             : 
     436         387 :                         ps->trackPoint_changed.x = res.x;
     437         387 :                         ps->trackPoint_changed.y = res.y;
     438         387 :                         gf_node_event_out(sh->sensor, 6/*"trackPoint_changed"*/);
     439             : 
     440         387 :                         res.x -= stack->start_drag.x;
     441         387 :                         res.y -= stack->start_drag.y;
     442             :                         /*clip*/
     443         387 :                         if (ps->minPosition.x <= ps->maxPosition.x) {
     444         330 :                                 if (res.x < ps->minPosition.x) res.x = ps->minPosition.x;
     445         330 :                                 if (res.x > ps->maxPosition.x) res.x = ps->maxPosition.x;
     446             :                         }
     447         387 :                         if (ps->minPosition.y <= ps->maxPosition.y) {
     448         330 :                                 if (res.y < ps->minPosition.y)
     449           0 :                                         res.y = ps->minPosition.y;
     450         330 :                                 if (res.y > ps->maxPosition.y)
     451           0 :                                         res.y = ps->maxPosition.y;
     452             :                         }
     453         387 :                         ps->translation_changed.x = res.x;
     454         387 :                         ps->translation_changed.y = res.y;
     455         387 :                         gf_node_event_out(sh->sensor, 7/*"translation_changed"*/);
     456             :                         return 1;
     457             :                 }
     458             :         } else {
     459           3 :                 if (!ps->isActive && is_over && (ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)) {
     460           0 :                         ps->isActive = 1;
     461           0 :                         stack->start_drag = ps->offset;
     462           0 :                         gf_node_event_out(sh->sensor, 5/*"isActive"*/);
     463           0 :                         return 1;
     464             :                 }
     465           3 :                 else if (ps->isActive && (ev->type==GF_EVENT_KEYDOWN)) {
     466             :                         SFVec2f res;
     467           0 :                         Fixed diff = (ev->key.flags & GF_KEY_MOD_SHIFT) ? 5*FIX_ONE : FIX_ONE;
     468           0 :                         if (!gf_sg_use_pixel_metrics(gf_node_get_graph(sh->sensor)))
     469           0 :                                 diff = gf_divfix(diff, INT2FIX(compositor->vp_width/2));
     470           0 :                         res = stack->start_drag;
     471           0 :                         switch (ev->key.key_code) {
     472           0 :                         case GF_KEY_LEFT:
     473           0 :                                 res.x += -diff;
     474           0 :                                 break;
     475           0 :                         case GF_KEY_RIGHT:
     476           0 :                                 res.x += diff;
     477           0 :                                 break;
     478           0 :                         case GF_KEY_UP:
     479           0 :                                 res.y += diff;
     480           0 :                                 break;
     481           0 :                         case GF_KEY_DOWN:
     482           0 :                                 res.y += -diff;
     483           0 :                                 break;
     484           0 :                         case GF_KEY_HOME:
     485           0 :                                 res = ps->offset;
     486           0 :                                 break;
     487             :                         default:
     488             :                                 return 0;
     489             :                         }
     490             :                         /*clip*/
     491           0 :                         if (ps->minPosition.x <= ps->maxPosition.x) {
     492           0 :                                 if (res.x < ps->minPosition.x) res.x = ps->minPosition.x;
     493           0 :                                 if (res.x > ps->maxPosition.x) res.x = ps->maxPosition.x;
     494             :                         }
     495           0 :                         if (ps->minPosition.y <= ps->maxPosition.y) {
     496           0 :                                 if (res.y < ps->minPosition.y) res.y = ps->minPosition.y;
     497           0 :                                 if (res.y > ps->maxPosition.y) res.y = ps->maxPosition.y;
     498             :                         }
     499           0 :                         ps->translation_changed = res;
     500           0 :                         gf_node_event_out(sh->sensor, 7/*"translation_changed"*/);
     501           0 :                         ps->trackPoint_changed.x = res.x + stack->start_drag.x;
     502           0 :                         ps->trackPoint_changed.y = res.y + stack->start_drag.y;
     503           0 :                         gf_node_event_out(sh->sensor, 6/*"trackPoint_changed"*/);
     504           0 :                         stack->start_drag = res;
     505           0 :                         return 1;
     506             :                 }
     507             :         }
     508             :         return 0;
     509             : }
     510             : 
     511             : static GF_SensorHandler *plane_sensor2d_get_handler(GF_Node *n)
     512             : {
     513         755 :         PS2DStack *st = (PS2DStack *)gf_node_get_private(n);
     514         755 :         return &st->hdl;
     515             : }
     516             : 
     517          39 : void compositor_init_plane_sensor2d(GF_Compositor *compositor, GF_Node *node)
     518             : {
     519             :         PS2DStack *st;
     520          39 :         GF_SAFEALLOC(st, PS2DStack);
     521          39 :         if (!st) {
     522           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate plane sensor 2d stack\n"));
     523             :                 return;
     524             :         }
     525             : 
     526          39 :         st->hdl.IsEnabled = ps2D_is_enabled;
     527          39 :         st->hdl.OnUserEvent = OnPlaneSensor2D;
     528          39 :         st->hdl.sensor = node;
     529          39 :         st->compositor = compositor;
     530          39 :         mpeg4_sensor_created(compositor, node);
     531          39 :         gf_node_set_private(node, st);
     532          39 :         gf_node_set_callback_function(node, DestroyPlaneSensor2D);
     533             : }
     534             : 
     535             : 
     536             : typedef struct
     537             : {
     538             :         Double last_time;
     539             :         GF_Compositor *compositor;
     540             :         GF_SensorHandler hdl;
     541             : } Prox2DStack;
     542             : 
     543         308 : static void DestroyProximitySensor2D(GF_Node *node, void *rs, Bool is_destroy)
     544             : {
     545         308 :         if (is_destroy) {
     546           8 :                 Prox2DStack *st = (Prox2DStack *) gf_node_get_private(node);
     547           8 :                 mpeg4_sensor_deleted(node, &st->hdl);
     548           8 :                 gf_free(st);
     549             :         }
     550         308 : }
     551             : 
     552          31 : static Bool prox2D_is_enabled(GF_Node *n)
     553             : {
     554          31 :         return ((M_ProximitySensor2D *) n)->enabled;
     555             : }
     556             : 
     557             : static Bool prox2D_is_in_sensor(Prox2DStack *st, M_ProximitySensor2D *ps, Fixed X, Fixed Y)
     558             : {
     559         106 :         if (X < ps->center.x - ps->size.x/2) return 0;
     560          87 :         if (X > ps->center.x + ps->size.x/2) return 0;
     561          44 :         if (Y < ps->center.y - ps->size.y/2) return 0;
     562          44 :         if (Y > ps->center.y + ps->size.y/2) return 0;
     563             :         return 1;
     564             : }
     565             : 
     566         110 : static Bool OnProximitySensor2D(GF_SensorHandler *sh, Bool is_over, Bool is_cancel, GF_Event *ev, GF_Compositor *compositor)
     567             : {
     568         110 :         M_ProximitySensor2D *ps = (M_ProximitySensor2D *)sh->sensor;
     569         110 :         Prox2DStack *stack = (Prox2DStack *) gf_node_get_private(sh->sensor);
     570             : 
     571             :         assert(ps->enabled);
     572             : 
     573         110 :         if (is_over) {
     574         106 :                 stack->last_time = gf_node_get_scene_time(sh->sensor);
     575         106 :                 if (is_cancel) return 0;
     576         106 :                 if (prox2D_is_in_sensor(stack, ps, compositor->hit_local_point.x, compositor->hit_local_point.y)) {
     577          44 :                         ps->position_changed.x = compositor->hit_local_point.x;
     578          44 :                         ps->position_changed.y = compositor->hit_local_point.y;
     579          44 :                         gf_node_event_out(sh->sensor, 4/*"position_changed"*/);
     580             : 
     581          44 :                         if (!ps->isActive) {
     582           2 :                                 ps->isActive = 1;
     583           2 :                                 gf_node_event_out(sh->sensor, 3/*"isActive"*/);
     584           2 :                                 ps->enterTime = stack->last_time;
     585           2 :                                 gf_node_event_out(sh->sensor, 6/*"enterTime"*/);
     586             :                         }
     587             :                         return 1;
     588             :                 }
     589             :         }
     590             :         /*either we're not over the shape or we're not in sensor*/
     591          66 :         if (ps->isActive) {
     592           2 :                 ps->exitTime = stack->last_time;
     593           2 :                 gf_node_event_out(sh->sensor, 7/*"exitTime"*/);
     594           2 :                 ps->isActive = 0;
     595           2 :                 gf_node_event_out(sh->sensor, 3/*"isActive"*/);
     596           2 :                 return 1;
     597             :         }
     598             :         return 0;
     599             : }
     600             : 
     601             : static GF_SensorHandler *proximity_sensor2d_get_handler(GF_Node *n)
     602             : {
     603          31 :         Prox2DStack *st = (Prox2DStack *)gf_node_get_private(n);
     604          31 :         return &st->hdl;
     605             : }
     606             : 
     607             : 
     608           8 : void compositor_init_proximity_sensor2d(GF_Compositor *compositor, GF_Node *node)
     609             : {
     610             :         Prox2DStack *st;
     611           8 :         GF_SAFEALLOC(st, Prox2DStack);
     612           8 :         if (!st) {
     613           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate proximity sensor 2d stack\n"));
     614             :                 return;
     615             :         }
     616             : 
     617           8 :         st->hdl.IsEnabled = prox2D_is_enabled;
     618           8 :         st->hdl.OnUserEvent = OnProximitySensor2D;
     619           8 :         st->hdl.sensor = node;
     620           8 :         st->compositor = compositor;
     621           8 :         mpeg4_sensor_created(compositor, node);
     622           8 :         gf_node_set_private(node, st);
     623           8 :         gf_node_set_callback_function(node, DestroyProximitySensor2D);
     624             : }
     625             : 
     626             : typedef struct
     627             : {
     628             :         GF_SensorHandler hdl;
     629             :         Bool mouse_down;
     630             :         GF_Compositor *compositor;
     631             : } TouchSensorStack;
     632             : 
     633      127149 : static void DestroyTouchSensor(GF_Node *node, void *rs, Bool is_destroy)
     634             : {
     635      127149 :         if (is_destroy) {
     636        1382 :                 TouchSensorStack *st = (TouchSensorStack *) gf_node_get_private(node);
     637        1382 :                 mpeg4_sensor_deleted(node, &st->hdl);
     638        1382 :                 gf_free(st);
     639             :         }
     640      127149 : }
     641             : 
     642        8407 : static Bool ts_is_enabled(GF_Node *n)
     643             : {
     644        8407 :         return ((M_TouchSensor *) n)->enabled;
     645             : }
     646             : 
     647        1758 : static Bool OnTouchSensor(GF_SensorHandler *sh, Bool is_over, Bool is_cancel, GF_Event *ev, GF_Compositor *compositor)
     648             : {
     649        1758 :         Bool is_mouse = (ev->type<=GF_EVENT_MOUSEWHEEL);
     650        1758 :         M_TouchSensor *ts = (M_TouchSensor *)sh->sensor;
     651             : 
     652             :         /*this is not specified in VRML, however we consider that a de-enabled sensor will not sent deactivation events*/
     653        1758 :         if (!ts->enabled) {
     654          24 :                 if (ts->isActive) {
     655           0 :                         sh->grabbed = 0;
     656             :                 }
     657             :                 return 0;
     658             :         }
     659             : 
     660             :         /*isActive becomes false, send touch time*/
     661        1734 :         if (ts->isActive) {
     662         271 :                 if (
     663         104 :                     /*mouse*/ ((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT) )
     664         167 :                     || /*keyboard*/ ((ev->type==GF_EVENT_KEYUP) && (ev->key.key_code==GF_KEY_ENTER) )
     665             :                 ) {
     666         104 :                         ts->touchTime = gf_node_get_scene_time(sh->sensor);
     667         104 :                         if (!is_cancel) gf_node_event_out(sh->sensor, 6/*"touchTime"*/);
     668         104 :                         ts->isActive = 0;
     669         104 :                         if (!is_cancel) gf_node_event_out(sh->sensor, 4/*"isActive"*/);
     670         104 :                         sh->grabbed = 0;
     671         104 :                         return is_cancel ? 0 : 1;
     672             :                 }
     673             :         }
     674        1630 :         if (is_over != ts->isOver) {
     675         485 :                 ts->isOver = is_over;
     676         485 :                 if (!is_cancel) gf_node_event_out(sh->sensor, 5/*"isOver"*/);
     677         485 :                 return is_cancel ? 0 : 1;
     678             :         }
     679        1145 :         if (!ts->isActive && is_over) {
     680         986 :                 if (/*mouse*/ ((ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT))
     681         870 :                               || /*keyboard*/ ((ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER))
     682             :                    ) {
     683         116 :                         ts->isActive = 1;
     684         116 :                         gf_node_event_out(sh->sensor, 4/*"isActive"*/);
     685         116 :                         sh->grabbed = 1;
     686         116 :                         return 1;
     687             :                 }
     688         870 :                 if (ev->type==GF_EVENT_MOUSEUP) return 0;
     689             :         }
     690        1029 :         if (is_over && is_mouse && (ev->type==GF_EVENT_MOUSEMOVE) ) {
     691             :                 /*THIS IS NOT CONFORMANT, the hitpoint should be in the touchsensor coordinate system, eg we
     692             :                 should store the matrix from TS -> shape and apply that ...*/
     693         962 :                 ts->hitPoint_changed = compositor->hit_local_point;
     694         962 :                 gf_node_event_out(sh->sensor, 1/*"hitPoint_changed"*/);
     695         962 :                 ts->hitNormal_changed = compositor->hit_normal;
     696         962 :                 gf_node_event_out(sh->sensor, 2/*"hitNormal_changed"*/);
     697         962 :                 ts->hitTexCoord_changed = compositor->hit_texcoords;
     698         962 :                 gf_node_event_out(sh->sensor, 3/*"hitTexCoord_changed"*/);
     699         962 :                 return 1;
     700             :         }
     701             :         return 0;
     702             : }
     703             : 
     704             : static GF_SensorHandler *touch_sensor_get_handler(GF_Node *n)
     705             : {
     706        7788 :         TouchSensorStack *ts = (TouchSensorStack *)gf_node_get_private(n);
     707        7788 :         return &ts->hdl;
     708             : }
     709             : 
     710             : 
     711        1382 : void compositor_init_touch_sensor(GF_Compositor *compositor, GF_Node *node)
     712             : {
     713             :         TouchSensorStack *st;
     714        1382 :         GF_SAFEALLOC(st, TouchSensorStack);
     715        1382 :         if (!st) {
     716           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate touch sensor stack\n"));
     717             :                 return;
     718             :         }
     719             : 
     720        1382 :         st->hdl.IsEnabled = ts_is_enabled;
     721        1382 :         st->hdl.OnUserEvent = OnTouchSensor;
     722        1382 :         st->hdl.sensor = node;
     723        1382 :         st->compositor = compositor;
     724        1382 :         mpeg4_sensor_created(compositor, node);
     725        1382 :         gf_node_set_private(node, st);
     726        1382 :         gf_node_set_callback_function(node, DestroyTouchSensor);
     727             : }
     728             : 
     729             : #ifndef GPAC_DISABLE_3D
     730             : 
     731         113 : void TraverseProximitySensor(GF_Node *node, void *rs, Bool is_destroy)
     732             : {
     733             :         SFVec3f user_pos, dist, up;
     734             :         SFRotation ori;
     735             :         GF_Matrix mx;
     736             :         GF_TraverseState *tr_state = (GF_TraverseState *)rs;
     737             :         M_ProximitySensor *ps = (M_ProximitySensor *)node;
     738         223 :         if (is_destroy) return;
     739             : 
     740         112 :         if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) {
     741             :                 /*work with twice bigger bbox to get sure we're notify when culled out*/
     742           1 :                 gf_vec_add(tr_state->bbox.max_edge, ps->center, ps->size);
     743           1 :                 gf_vec_diff(tr_state->bbox.min_edge, ps->center, ps->size);
     744           1 :                 gf_bbox_refresh(&tr_state->bbox);
     745           1 :                 return;
     746         111 :         } else if (!ps->enabled || (tr_state->traversing_mode != TRAVERSE_SORT) ) return;
     747             : 
     748             :         /*TODO FIXME - find a way to cache inverted matrix*/
     749           3 :         gf_mx_copy(mx, tr_state->model_matrix);
     750           3 :         gf_mx_inverse(&mx);
     751             :         /*get use pos in local coord system*/
     752           3 :         user_pos = tr_state->camera->position;
     753           3 :         gf_mx_apply_vec(&mx, &user_pos);
     754           3 :         gf_vec_diff(dist, user_pos, ps->center);
     755             : 
     756           3 :         if (dist.x<0) dist.x *= -1;
     757           3 :         if (dist.y<0) dist.y *= -1;
     758           3 :         if (dist.z<0) dist.z *= -1;
     759             : 
     760           3 :         if ((2*dist.x <= ps->size.x)
     761           3 :                 && (2*dist.y <= ps->size.y)
     762           3 :                 && (2*dist.z <= ps->size.z) ) {
     763             : 
     764           0 :                 if (!ps->isActive) {
     765           0 :                         ps->isActive = 1;
     766           0 :                         gf_node_event_out(node, 3/*"isActive"*/);
     767           0 :                         ps->enterTime = gf_node_get_scene_time(node);
     768           0 :                         gf_node_event_out(node, 6/*"enterTime"*/);
     769             :                 }
     770           0 :                 if ((ps->position_changed.x != user_pos.x)
     771           0 :                         || (ps->position_changed.y != user_pos.y)
     772           0 :                         || (ps->position_changed.z != user_pos.z) )
     773             :                 {
     774           0 :                         ps->position_changed = user_pos;
     775           0 :                         gf_node_event_out(node, 4/*"position_changed"*/);
     776             :                 }
     777           0 :                 dist = tr_state->camera->target;
     778           0 :                 gf_mx_apply_vec(&mx, &dist);
     779           0 :                 up = tr_state->camera->up;
     780           0 :                 gf_mx_apply_vec(&mx, &up);
     781           0 :                 ori = camera_get_orientation(user_pos, dist, tr_state->camera->up);
     782           0 :                 if ((ori.q != ps->orientation_changed.q)
     783           0 :                         || (ori.x != ps->orientation_changed.x)
     784           0 :                         || (ori.y != ps->orientation_changed.y)
     785           0 :                         || (ori.z != ps->orientation_changed.z) ) {
     786           0 :                         ps->orientation_changed = ori;
     787           0 :                         gf_node_event_out(node, 5/*"orientation_changed"*/);
     788             :                 }
     789           3 :         } else if (ps->isActive) {
     790           0 :                 ps->isActive = 0;
     791           0 :                 gf_node_event_out(node, 3/*"isActive"*/);
     792           0 :                 ps->exitTime = gf_node_get_scene_time(node);
     793           0 :                 gf_node_event_out(node, 7/*"exitTime"*/);
     794             :         }
     795             : }
     796             : 
     797           1 : void compositor_init_proximity_sensor(GF_Compositor *compositor, GF_Node *node)
     798             : {
     799           1 :         gf_node_set_callback_function(node, TraverseProximitySensor);
     800           1 : }
     801             : 
     802             : 
     803             : typedef struct
     804             : {
     805             :         SFVec3f start_drag;
     806             :         GF_Plane tracker;
     807             :         GF_Matrix initial_matrix;
     808             :         GF_Compositor *compositor;
     809             :         GF_SensorHandler hdl;
     810             : } PSStack;
     811             : 
     812         379 : static void DestroyPlaneSensor(GF_Node *node, void *rs, Bool is_destroy)
     813             : {
     814         379 :         if (is_destroy) {
     815           7 :                 PSStack *st = (PSStack *) gf_node_get_private(node);
     816           7 :                 mpeg4_sensor_deleted(node, &st->hdl);
     817           7 :                 gf_free(st);
     818             :         }
     819         379 : }
     820             : 
     821         289 : static Bool ps_is_enabled(GF_Node *n)
     822             : {
     823             :         M_PlaneSensor *ps = (M_PlaneSensor *)n;
     824         289 :         return (ps->enabled || ps->isActive);
     825             : }
     826             : 
     827          71 : static Bool OnPlaneSensor(GF_SensorHandler *sh, Bool is_over, Bool is_cancel, GF_Event *ev, GF_Compositor *compositor)
     828             : {
     829          71 :         Bool is_mouse = (ev->type<=GF_EVENT_MOUSEWHEEL) ? 1 : 0;
     830          71 :         M_PlaneSensor *ps = (M_PlaneSensor *)sh->sensor;
     831          71 :         PSStack *stack = (PSStack *) gf_node_get_private(sh->sensor);
     832             : 
     833             : 
     834         137 :         if (ps->isActive &&
     835          66 :                 ( /*mouse*/((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT))
     836          66 :                            || /*keyboar*/(!is_mouse && (!is_over|| ((ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER))) )
     837             :                 ) ) {
     838           0 :                 if (ps->autoOffset) {
     839           0 :                         ps->offset = ps->translation_changed;
     840           0 :                         if (!is_cancel) gf_node_event_out(sh->sensor, 4/*"offset"*/);
     841             :                 }
     842           0 :                 ps->isActive = 0;
     843           0 :                 if (!is_cancel) gf_node_event_out(sh->sensor, 5/*"isActive"*/);
     844           0 :                 sh->grabbed = 0;
     845           0 :                 return is_cancel ? 0 : 1;
     846             :         }
     847             :         /*mouse*/
     848          71 :         else if (is_mouse) {
     849          71 :                 if (!ps->isActive && (ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT) ) {
     850           1 :                         gf_mx_copy(stack->initial_matrix, compositor->hit_local_to_world);
     851           1 :                         gf_vec_diff(stack->start_drag, compositor->hit_local_point, ps->offset);
     852           1 :                         stack->tracker.normal.x = stack->tracker.normal.y = 0;
     853           1 :                         stack->tracker.normal.z = FIX_ONE;
     854           1 :                         stack->tracker.d = - gf_vec_dot(stack->start_drag, stack->tracker.normal);
     855           1 :                         ps->isActive = 1;
     856           1 :                         gf_node_event_out(sh->sensor, 5/*"isActive"*/);
     857           1 :                         sh->grabbed = 1;
     858           1 :                         return 1;
     859             :                 }
     860          70 :                 else if (ps->isActive) {
     861             :                         GF_Ray loc_ray;
     862             :                         SFVec3f res;
     863          66 :                         loc_ray = compositor->hit_world_ray;
     864          66 :                         gf_mx_apply_ray(&stack->initial_matrix, &loc_ray);
     865          66 :                         gf_plane_intersect_line(&stack->tracker, &loc_ray.orig, &loc_ray.dir, &res);
     866          66 :                         ps->trackPoint_changed = res;
     867          66 :                         gf_node_event_out(sh->sensor, 6/*"trackPoint_changed"*/);
     868             : 
     869          66 :                         gf_vec_diff(res, res, stack->start_drag);
     870             :                         /*clip*/
     871          66 :                         if (ps->minPosition.x <= ps->maxPosition.x) {
     872          66 :                                 if (res.x < ps->minPosition.x) res.x = ps->minPosition.x;
     873          66 :                                 if (res.x > ps->maxPosition.x) res.x = ps->maxPosition.x;
     874             :                         }
     875          66 :                         if (ps->minPosition.y <= ps->maxPosition.y) {
     876          66 :                                 if (res.y < ps->minPosition.y) res.y = ps->minPosition.y;
     877          66 :                                 if (res.y > ps->maxPosition.y) res.y = ps->maxPosition.y;
     878             :                         }
     879          66 :                         ps->translation_changed = res;
     880          66 :                         gf_node_event_out(sh->sensor, 7/*"translation_changed"*/);
     881             :                         return 1;
     882             :                 }
     883             :         } else {
     884           0 :                 if (!ps->isActive && is_over && (ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)) {
     885           0 :                         ps->isActive = 1;
     886           0 :                         stack->start_drag = ps->offset;
     887           0 :                         gf_node_event_out(sh->sensor, 5/*"isActive"*/);
     888           0 :                         return 1;
     889             :                 }
     890           0 :                 else if (ps->isActive && (ev->type==GF_EVENT_KEYDOWN)) {
     891             :                         SFVec3f res;
     892           0 :                         Fixed diff = (ev->key.flags & GF_KEY_MOD_SHIFT) ? 5*FIX_ONE : FIX_ONE;
     893           0 :                         if (!gf_sg_use_pixel_metrics(gf_node_get_graph(sh->sensor)))
     894           0 :                                 diff = gf_divfix(diff, INT2FIX(compositor->vp_width/2));
     895             : 
     896           0 :                         res = stack->start_drag;
     897           0 :                         switch (ev->key.key_code) {
     898           0 :                         case GF_KEY_LEFT:
     899           0 :                                 res.x -= diff;
     900           0 :                                 break;
     901           0 :                         case GF_KEY_RIGHT:
     902           0 :                                 res.x += diff;
     903           0 :                                 break;
     904           0 :                         case GF_KEY_UP:
     905           0 :                                 res.y += diff;
     906           0 :                                 break;
     907           0 :                         case GF_KEY_DOWN:
     908           0 :                                 res.y -= diff;
     909           0 :                                 break;
     910           0 :                         case GF_KEY_HOME:
     911           0 :                                 res = ps->offset;
     912           0 :                                 break;
     913             :                         default:
     914             :                                 return 0;
     915             :                         }
     916             :                         /*clip*/
     917           0 :                         if (ps->minPosition.x <= ps->maxPosition.x) {
     918           0 :                                 if (res.x < ps->minPosition.x) res.x = ps->minPosition.x;
     919           0 :                                 if (res.x > ps->maxPosition.x) res.x = ps->maxPosition.x;
     920             :                         }
     921           0 :                         if (ps->minPosition.y <= ps->maxPosition.y) {
     922           0 :                                 if (res.y < ps->minPosition.y) res.y = ps->minPosition.y;
     923           0 :                                 if (res.y > ps->maxPosition.y) res.y = ps->maxPosition.y;
     924             :                         }
     925           0 :                         stack->start_drag = res;
     926           0 :                         ps->translation_changed = res;
     927           0 :                         gf_node_event_out(sh->sensor, 7/*"translation_changed"*/);
     928           0 :                         return 1;
     929             :                 }
     930             :         }
     931             :         return 0;
     932             : }
     933             : 
     934             : static GF_SensorHandler *plane_sensor_get_handler(GF_Node *n)
     935             : {
     936         216 :         PSStack *st = (PSStack *)gf_node_get_private(n);
     937         216 :         return &st->hdl;
     938             : }
     939             : 
     940           7 : void compositor_init_plane_sensor(GF_Compositor *compositor, GF_Node *node)
     941             : {
     942             :         PSStack *st;
     943           7 :         GF_SAFEALLOC(st, PSStack);
     944           7 :         if (!st) {
     945           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate plane sensor stack\n"));
     946             :                 return;
     947             :         }
     948             : 
     949           7 :         st->hdl.IsEnabled = ps_is_enabled;
     950           7 :         st->hdl.OnUserEvent = OnPlaneSensor;
     951           7 :         st->hdl.sensor = node;
     952           7 :         st->compositor = compositor;
     953           7 :         mpeg4_sensor_created(compositor, node);
     954           7 :         gf_node_set_private(node, st);
     955           7 :         gf_node_set_callback_function(node, DestroyPlaneSensor);
     956             : }
     957             : 
     958             : typedef struct
     959             : {
     960             :         GF_SensorHandler hdl;
     961             :         GF_Compositor *compositor;
     962             :         GF_Matrix init_matrix;
     963             :         Bool disk_mode;
     964             :         SFVec3f grab_start;
     965             :         GF_Plane yplane, zplane, xplane;
     966             : } CylinderSensorStack;
     967             : 
     968         303 : static void DestroyCylinderSensor(GF_Node *node, void *rs, Bool is_destroy)
     969             : {
     970         303 :         if (is_destroy) {
     971           7 :                 CylinderSensorStack *st = (CylinderSensorStack *) gf_node_get_private(node);
     972           7 :                 mpeg4_sensor_deleted(node, &st->hdl);
     973           7 :                 gf_free(st);
     974             :         }
     975         303 : }
     976             : 
     977         285 : static Bool cs_is_enabled(GF_Node *n)
     978             : {
     979             :         M_CylinderSensor *cs = (M_CylinderSensor *)n;
     980         285 :         return (cs->enabled || cs->isActive);
     981             : }
     982             : 
     983          56 : static Bool OnCylinderSensor(GF_SensorHandler *sh, Bool is_over, Bool is_cancel, GF_Event *ev, GF_Compositor *compositor)
     984             : {
     985          56 :         Bool is_mouse = (ev->type<=GF_EVENT_MOUSEWHEEL) ? 1 : 0;
     986          56 :         M_CylinderSensor *cs = (M_CylinderSensor *)sh->sensor;
     987          56 :         CylinderSensorStack *st = (CylinderSensorStack *) gf_node_get_private(sh->sensor);
     988             : 
     989          56 :         if (cs->isActive && (!cs->enabled
     990          49 :                              || /*mouse*/((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT))
     991          48 :                              || /*keyboar*/(!is_mouse && (!is_over|| ((ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER))))
     992             :                             ) ) {
     993           1 :                 if (cs->autoOffset) {
     994           1 :                         cs->offset = cs->rotation_changed.q;
     995           1 :                         if (!is_cancel) gf_node_event_out(sh->sensor, 5/*"offset"*/);
     996             :                 }
     997           1 :                 cs->isActive = 0;
     998           1 :                 if (!is_cancel) gf_node_event_out(sh->sensor, 6/*"isActive"*/);
     999           1 :                 sh->grabbed = 0;
    1000           1 :                 return is_cancel ? 0 : 1;
    1001             :         }
    1002          55 :         else if (is_mouse) {
    1003          55 :                 if (!cs->isActive && (ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT)) {
    1004             :                         GF_Ray r;
    1005             :                         SFVec3f yaxis;
    1006             :                         Fixed acute, reva;
    1007             :                         SFVec3f bearing;
    1008             : 
    1009           1 :                         gf_mx_copy(st->init_matrix, compositor->hit_world_to_local);
    1010             :                         /*get initial angle & check disk mode*/
    1011           1 :                         r = compositor->hit_world_ray;
    1012           1 :                         gf_vec_add(r.dir, r.orig, r.dir);
    1013           1 :                         gf_mx_apply_vec(&compositor->hit_world_to_local, &r.orig);
    1014           1 :                         gf_mx_apply_vec(&compositor->hit_world_to_local, &r.dir);
    1015           1 :                         gf_vec_diff(bearing, r.orig, r.dir);
    1016           1 :                         gf_vec_norm(&bearing);
    1017           1 :                         yaxis.x = yaxis.z = 0;
    1018           1 :                         yaxis.y = FIX_ONE;
    1019           1 :                         acute = gf_vec_dot(bearing, yaxis);
    1020           1 :                         if (acute < -FIX_ONE) acute = -FIX_ONE;
    1021           1 :                         else if (acute > FIX_ONE) acute = FIX_ONE;
    1022           1 :                         acute = gf_acos(acute);
    1023           1 :                         reva = ABS(GF_PI - acute);
    1024           1 :                         if (reva<acute) acute = reva;
    1025           1 :                         st->disk_mode = (acute < cs->diskAngle) ? 1 : 0;
    1026             : 
    1027           1 :                         st->grab_start = compositor->hit_local_point;
    1028             :                         /*cos we're lazy*/
    1029           1 :                         st->yplane.d = 0;
    1030           1 :                         st->yplane.normal.x = st->yplane.normal.z = st->yplane.normal.y = 0;
    1031           1 :                         st->zplane = st->xplane = st->yplane;
    1032           1 :                         st->xplane.normal.x = FIX_ONE;
    1033           1 :                         st->yplane.normal.y = FIX_ONE;
    1034           1 :                         st->zplane.normal.z = FIX_ONE;
    1035             : 
    1036           1 :                         cs->rotation_changed.x = 0;
    1037           1 :                         cs->rotation_changed.y = FIX_ONE;
    1038           1 :                         cs->rotation_changed.z = 0;
    1039             : 
    1040           1 :                         cs->isActive = 1;
    1041           1 :                         gf_node_event_out(sh->sensor, 6/*"isActive"*/);
    1042           1 :                         sh->grabbed = 1;
    1043             :                         return 1;
    1044             :                 }
    1045          54 :                 else if (cs->isActive) {
    1046             :                         GF_Ray r;
    1047             :                         Fixed radius, rot;
    1048             :                         SFVec3f dir1, dir2, cx;
    1049             : 
    1050          48 :                         if (is_over) {
    1051          24 :                                 cs->trackPoint_changed = compositor->hit_local_point;
    1052          24 :                                 gf_node_event_out(sh->sensor, 8/*"trackPoint_changed"*/);
    1053             :                         } else {
    1054             :                                 GF_Plane project_to;
    1055          24 :                                 r = compositor->hit_world_ray;
    1056          24 :                                 gf_mx_apply_ray(&st->init_matrix, &r);
    1057             : 
    1058             :                                 /*no intersection, use intersection with "main" fronting plane*/
    1059          24 :                                 if ( ABS(r.dir.z) > ABS(r.dir.y)) {
    1060          24 :                                         if (ABS(r.dir.z) > ABS(r.dir.x)) project_to = st->xplane;
    1061           0 :                                         else project_to = st->yplane;
    1062             :                                 } else {
    1063           0 :                                         if (ABS(r.dir.z) > ABS(r.dir.x)) project_to = st->xplane;
    1064           0 :                                         else project_to = st->zplane;
    1065             :                                 }
    1066          24 :                                 if (!gf_plane_intersect_line(&project_to, &r.orig, &r.dir, &compositor->hit_local_point)) return 0;
    1067             :                         }
    1068             : 
    1069          24 :                         dir1.x = compositor->hit_local_point.x;
    1070          24 :                         dir1.y = 0;
    1071          24 :                         dir1.z = compositor->hit_local_point.z;
    1072          24 :                         if (st->disk_mode) {
    1073             :                                 radius = FIX_ONE;
    1074             :                         } else {
    1075          24 :                                 radius = gf_vec_len(dir1);
    1076             :                         }
    1077          24 :                         gf_vec_norm(&dir1);
    1078          24 :                         dir2.x = st->grab_start.x;
    1079          24 :                         dir2.y = 0;
    1080          24 :                         dir2.z = st->grab_start.z;
    1081          24 :                         gf_vec_norm(&dir2);
    1082          24 :                         cx = gf_vec_cross(dir2, dir1);
    1083          24 :                         gf_vec_norm(&cx);
    1084          24 :                         if (gf_vec_len(cx)<FIX_EPSILON) return 0;
    1085          24 :                         rot = gf_mulfix(radius, gf_acos(gf_vec_dot(dir2, dir1)) );
    1086          24 :                         if (fabs(cx.y + FIX_ONE) < FIX_EPSILON) rot = -rot;
    1087          24 :                         if (cs->autoOffset) rot += cs->offset;
    1088             : 
    1089          24 :                         if (cs->minAngle < cs->maxAngle) {
    1090           0 :                                 if (rot < cs->minAngle) rot = cs->minAngle;
    1091           0 :                                 else if (rot > cs->maxAngle) rot = cs->maxAngle;
    1092             :                         }
    1093          24 :                         cs->rotation_changed.q = rot;
    1094          24 :                         gf_node_event_out(sh->sensor, 7/*"rotation_changed"*/);
    1095          24 :                         return 1;
    1096             :                 }
    1097             :         } else {
    1098           0 :                 if (!cs->isActive && is_over && (ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)) {
    1099           0 :                         cs->isActive = 1;
    1100           0 :                         cs->rotation_changed.q = cs->offset;
    1101           0 :                         cs->rotation_changed.x = cs->rotation_changed.z = 0;
    1102           0 :                         cs->rotation_changed.y = FIX_ONE;
    1103           0 :                         gf_node_event_out(sh->sensor, 6/*"isActive"*/);
    1104           0 :                         return 1;
    1105             :                 }
    1106           0 :                 else if (cs->isActive && (ev->type==GF_EVENT_KEYDOWN)) {
    1107             :                         SFFloat res;
    1108           0 :                         Fixed diff = (ev->key.flags & GF_KEY_MOD_SHIFT) ? GF_PI/8 : GF_PI/64;
    1109             : 
    1110           0 :                         res = cs->rotation_changed.q;
    1111           0 :                         switch (ev->key.key_code) {
    1112           0 :                         case GF_KEY_LEFT:
    1113           0 :                                 res -= diff;
    1114           0 :                                 break;
    1115           0 :                         case GF_KEY_RIGHT:
    1116           0 :                                 res += diff;
    1117           0 :                                 break;
    1118           0 :                         case GF_KEY_HOME:
    1119           0 :                                 res = cs->offset;
    1120           0 :                                 break;
    1121             :                         default:
    1122             :                                 return 0;
    1123             :                         }
    1124             :                         /*clip*/
    1125           0 :                         if (cs->minAngle <= cs->maxAngle) {
    1126           0 :                                 if (res < cs->minAngle) res = cs->minAngle;
    1127           0 :                                 if (res > cs->maxAngle) res = cs->maxAngle;
    1128             :                         }
    1129           0 :                         cs->rotation_changed.q = res;
    1130           0 :                         gf_node_event_out(sh->sensor, 7/*"rotation_changed"*/);
    1131           0 :                         return 1;
    1132             :                 }
    1133             :         }
    1134             :         return 0;
    1135             : }
    1136             : 
    1137             : static GF_SensorHandler *cylinder_sensor_get_handler(GF_Node *n)
    1138             : {
    1139         214 :         CylinderSensorStack *st = (CylinderSensorStack  *)gf_node_get_private(n);
    1140         214 :         return &st->hdl;
    1141             : }
    1142             : 
    1143           7 : void compositor_init_cylinder_sensor(GF_Compositor *compositor, GF_Node *node)
    1144             : {
    1145             :         CylinderSensorStack *st;
    1146           7 :         GF_SAFEALLOC(st, CylinderSensorStack);
    1147           7 :         if (!st) {
    1148           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate cylinder sensor 2d stack\n"));
    1149             :                 return;
    1150             :         }
    1151             : 
    1152           7 :         st->hdl.IsEnabled = cs_is_enabled;
    1153           7 :         st->hdl.OnUserEvent = OnCylinderSensor;
    1154           7 :         st->hdl.sensor = node;
    1155           7 :         st->compositor = compositor;
    1156           7 :         mpeg4_sensor_created(compositor, node);
    1157           7 :         gf_node_set_private(node, st);
    1158           7 :         gf_node_set_callback_function(node, DestroyCylinderSensor);
    1159             : }
    1160             : 
    1161             : 
    1162             : typedef struct
    1163             : {
    1164             :         GF_SensorHandler hdl;
    1165             :         GF_Compositor *compositor;
    1166             :         Fixed radius;
    1167             :         /*center in world coords */
    1168             :         SFVec3f grab_vec, center;
    1169             : } SphereSensorStack;
    1170             : 
    1171         382 : static void DestroySphereSensor(GF_Node *node, void *rs, Bool is_destroy)
    1172             : {
    1173         382 :         if (is_destroy) {
    1174           7 :                 SphereSensorStack *st = (SphereSensorStack *) gf_node_get_private(node);
    1175           7 :                 mpeg4_sensor_deleted(node, &st->hdl);
    1176           7 :                 gf_free(st);
    1177             :         }
    1178         382 : }
    1179             : 
    1180         285 : static Bool sphere_is_enabled(GF_Node *n)
    1181             : {
    1182             :         M_SphereSensor *ss = (M_SphereSensor *)n;
    1183         285 :         return (ss->enabled || ss->isActive);
    1184             : }
    1185             : 
    1186          70 : static Bool OnSphereSensor(GF_SensorHandler *sh, Bool is_over, Bool is_cancel, GF_Event *ev, GF_Compositor *compositor)
    1187             : {
    1188          70 :         Bool is_mouse = (ev->type<=GF_EVENT_MOUSEWHEEL) ? 1 : 0;
    1189          70 :         M_SphereSensor *sphere = (M_SphereSensor *)sh->sensor;
    1190          70 :         SphereSensorStack *st = (SphereSensorStack *) gf_node_get_private(sh->sensor);
    1191             : 
    1192             : 
    1193          70 :         if (sphere->isActive && (!sphere->enabled
    1194          62 :                                  || /*mouse*/((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT))
    1195          61 :                                  || /*keyboar*/(!is_mouse && (!is_over|| ((ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER))))
    1196             :                                 ) ) {
    1197           1 :                 if (sphere->autoOffset) {
    1198           1 :                         sphere->offset = sphere->rotation_changed;
    1199           1 :                         if (!is_cancel) gf_node_event_out(sh->sensor, 2/*"offset"*/);
    1200             :                 }
    1201           1 :                 sphere->isActive = 0;
    1202           1 :                 if (!is_cancel) gf_node_event_out(sh->sensor, 3/*"isActive"*/);
    1203           1 :                 sh->grabbed = 0;
    1204           1 :                 return is_cancel ? 0 : 1;
    1205             :         }
    1206          69 :         else if (is_mouse) {
    1207          69 :                 if (!sphere->isActive && (ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT)) {
    1208           1 :                         st->center.x = st->center.y = st->center.z = 0;
    1209           1 :                         gf_mx_apply_vec(&compositor->hit_local_to_world, &st->center);
    1210           1 :                         st->radius = gf_vec_len(compositor->hit_local_point);
    1211           1 :                         if (!st->radius) st->radius = FIX_ONE;
    1212           1 :                         st->grab_vec = gf_vec_scale(compositor->hit_local_point, gf_invfix(st->radius));
    1213             : 
    1214           1 :                         sphere->isActive = 1;
    1215           1 :                         gf_node_event_out(sh->sensor, 3/*"isActive"*/);
    1216           1 :                         sh->grabbed = 1;
    1217           1 :                         return 1;
    1218             :                 }
    1219          68 :                 else if (sphere->isActive) {
    1220             :                         SFVec3f vec, axis;
    1221             :                         SFVec4f q1, q2;
    1222             :                         SFRotation r;
    1223             :                         Fixed cl;
    1224          61 :                         if (is_over) {
    1225          58 :                                 sphere->trackPoint_changed = compositor->hit_local_point;
    1226          58 :                                 gf_node_event_out(sh->sensor, 5/*"trackPoint_changed"*/);
    1227             :                         } else {
    1228           3 :                                 GF_Ray ray = compositor->hit_world_ray;
    1229           3 :                                 gf_mx_apply_ray(&compositor->hit_world_to_local, &ray);
    1230           3 :                                 if (!gf_ray_hit_sphere(&ray, NULL, st->radius, &compositor->hit_local_point)) {
    1231           3 :                                         vec.x = vec.y = vec.z = 0;
    1232             :                                         /*doesn't work properly...*/
    1233           3 :                                         compositor->hit_local_point = gf_closest_point_to_line(ray.orig, ray.dir, vec);
    1234             :                                 }
    1235             :                         }
    1236             : 
    1237          61 :                         vec = gf_vec_scale(compositor->hit_local_point, gf_invfix(st->radius));
    1238          61 :                         axis = gf_vec_cross(st->grab_vec, vec);
    1239          61 :                         cl = gf_vec_len(axis);
    1240             : 
    1241          61 :                         if (cl < -FIX_ONE) cl = -FIX_ONE;
    1242          61 :                         else if (cl > FIX_ONE) cl = FIX_ONE;
    1243          61 :                         r.q = gf_asin(cl);
    1244          61 :                         if (gf_vec_dot(st->grab_vec, vec) < 0) r.q += GF_PI / 2;
    1245             : 
    1246          61 :                         gf_vec_norm(&axis);
    1247          61 :                         r.x = axis.x;
    1248          61 :                         r.y = axis.y;
    1249          61 :                         r.z = axis.z;
    1250          61 :                         q1 = gf_quat_from_rotation(r);
    1251          61 :                         if (sphere->autoOffset) {
    1252          61 :                                 q2 = gf_quat_from_rotation(sphere->offset);
    1253          61 :                                 q1 = gf_quat_multiply(&q1, &q2);
    1254             :                         }
    1255          61 :                         sphere->rotation_changed = gf_quat_to_rotation(&q1);
    1256          61 :                         gf_node_event_out(sh->sensor, 4/*"rotation_changed"*/);
    1257             :                         return 1;
    1258             :                 }
    1259             :         } else {
    1260           0 :                 if (!sphere->isActive && is_over && (ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)) {
    1261           0 :                         sphere->isActive = 1;
    1262           0 :                         sphere->rotation_changed = sphere->offset;
    1263           0 :                         gf_node_event_out(sh->sensor, 3/*"isActive"*/);
    1264           0 :                         return 1;
    1265             :                 }
    1266           0 :                 else if (sphere->isActive && (ev->type==GF_EVENT_KEYDOWN)) {
    1267             :                         SFVec4f res, rot;
    1268             :                         Fixed diff = GF_PI/64;
    1269             : 
    1270           0 :                         res = sphere->rotation_changed;
    1271           0 :                         switch (ev->key.key_code) {
    1272           0 :                         case GF_KEY_LEFT:
    1273             :                                 diff = -diff;
    1274           0 :                         case GF_KEY_RIGHT:
    1275           0 :                                 rot.x = 0;
    1276           0 :                                 rot.y = FIX_ONE;
    1277           0 :                                 rot.z = 0;
    1278           0 :                                 rot.q = diff;
    1279           0 :                                 res = gf_quat_from_rotation(res);
    1280           0 :                                 rot = gf_quat_from_rotation(rot);
    1281           0 :                                 rot = gf_quat_multiply(&rot, &res);
    1282           0 :                                 res = gf_quat_to_rotation(&rot);
    1283           0 :                                 break;
    1284           0 :                         case GF_KEY_DOWN:
    1285             :                                 diff = -diff;
    1286           0 :                         case GF_KEY_UP:
    1287           0 :                                 if (ev->key.flags & GF_KEY_MOD_SHIFT) {
    1288           0 :                                         rot.x = 0;
    1289           0 :                                         rot.z = FIX_ONE;
    1290             :                                 } else {
    1291           0 :                                         rot.x = FIX_ONE;
    1292           0 :                                         rot.z = 0;
    1293             :                                 }
    1294           0 :                                 rot.y = 0;
    1295           0 :                                 rot.q = diff;
    1296           0 :                                 res = gf_quat_from_rotation(res);
    1297           0 :                                 rot = gf_quat_from_rotation(rot);
    1298           0 :                                 rot = gf_quat_multiply(&rot, &res);
    1299           0 :                                 res = gf_quat_to_rotation(&rot);
    1300           0 :                                 break;
    1301           0 :                         case GF_KEY_HOME:
    1302           0 :                                 res = sphere->offset;
    1303           0 :                                 break;
    1304             :                         default:
    1305             :                                 return 0;
    1306             :                         }
    1307           0 :                         sphere->rotation_changed = res;
    1308           0 :                         gf_node_event_out(sh->sensor, 4/*"rotation_changed"*/);
    1309           0 :                         return 1;
    1310             :                 }
    1311             :         }
    1312             :         return 0;
    1313             : }
    1314             : 
    1315             : static GF_SensorHandler *sphere_get_handler(GF_Node *n)
    1316             : {
    1317         214 :         SphereSensorStack *st = (SphereSensorStack *)gf_node_get_private(n);
    1318         214 :         return &st->hdl;
    1319             : }
    1320             : 
    1321           7 : void compositor_init_sphere_sensor(GF_Compositor *compositor, GF_Node *node)
    1322             : {
    1323             :         SphereSensorStack *st;
    1324           7 :         GF_SAFEALLOC(st, SphereSensorStack);
    1325           7 :         if (!st) {
    1326           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate sphere sensor 2d stack\n"));
    1327             :                 return;
    1328             :         }
    1329             : 
    1330           7 :         st->hdl.IsEnabled = sphere_is_enabled;
    1331           7 :         st->hdl.OnUserEvent = OnSphereSensor;
    1332           7 :         st->hdl.sensor = node;
    1333           7 :         st->compositor = compositor;
    1334           7 :         mpeg4_sensor_created(compositor, node);
    1335           7 :         gf_node_set_private(node, st);
    1336           7 :         gf_node_set_callback_function(node, DestroySphereSensor);
    1337             : }
    1338             : 
    1339         578 : void TraverseVisibilitySensor(GF_Node *node, void *rs, Bool is_destroy)
    1340             : {
    1341             :         GF_TraverseState *tr_state = (GF_TraverseState *)rs;
    1342             :         M_VisibilitySensor *vs = (M_VisibilitySensor *)node;
    1343             : 
    1344         578 :         if (is_destroy || !vs->enabled) return;
    1345             : 
    1346         571 :         if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) {
    1347             :                 /*work with twice bigger bbox to get sure we're notify when culled out*/
    1348           1 :                 gf_vec_add(tr_state->bbox.max_edge, vs->center, vs->size);
    1349           1 :                 gf_vec_diff(tr_state->bbox.min_edge, vs->center, vs->size);
    1350           1 :                 gf_bbox_refresh(&tr_state->bbox);
    1351             : 
    1352         570 :         } else if (tr_state->traversing_mode==TRAVERSE_SORT) {
    1353             :                 Bool visible;
    1354             :                 u32 cull_flag;
    1355             :                 GF_BBox bbox;
    1356             :                 SFVec3f s;
    1357         294 :                 s = gf_vec_scale(vs->size, FIX_ONE/2);
    1358             :                 /*cull with normal bbox*/
    1359         294 :                 gf_vec_add(bbox.max_edge, vs->center, s);
    1360         294 :                 gf_vec_diff(bbox.min_edge, vs->center, s);
    1361         294 :                 gf_bbox_refresh(&bbox);
    1362         294 :                 cull_flag = tr_state->cull_flag;
    1363         294 :                 tr_state->cull_flag = CULL_INTERSECTS;
    1364         294 :                 visible = visual_3d_node_cull(tr_state, &bbox, 0);
    1365         294 :                 tr_state->cull_flag = cull_flag;
    1366             : 
    1367         294 :                 if (visible && !vs->isActive) {
    1368           7 :                         vs->isActive = 1;
    1369           7 :                         gf_node_event_out(node, 5/*"isActive"*/);
    1370           7 :                         vs->enterTime = gf_node_get_scene_time(node);
    1371           7 :                         gf_node_event_out(node, 3/*"enterTime"*/);
    1372             :                 }
    1373         287 :                 else if (!visible && vs->isActive) {
    1374           0 :                         vs->isActive = 0;
    1375           0 :                         gf_node_event_out(node, 5/*"isActive"*/);
    1376           0 :                         vs->exitTime = gf_node_get_scene_time(node);
    1377           0 :                         gf_node_event_out(node, 4/*"exitTime"*/);
    1378             :                 }
    1379             :         }
    1380             : }
    1381             : 
    1382           7 : void compositor_init_visibility_sensor(GF_Compositor *compositor, GF_Node *node)
    1383             : {
    1384           7 :         gf_node_set_callback_function(node, TraverseVisibilitySensor);
    1385           7 : }
    1386             : 
    1387             : #endif
    1388             : 
    1389         141 : GF_SensorHandler *compositor_mpeg4_get_sensor_handler(GF_Node *n)
    1390             : {
    1391         141 :         return compositor_mpeg4_get_sensor_handler_ex(n, GF_FALSE);
    1392             : }
    1393      254182 : GF_SensorHandler *compositor_mpeg4_get_sensor_handler_ex(GF_Node *n, Bool skip_anchors)
    1394             : {
    1395             :         GF_SensorHandler *hs;
    1396             : 
    1397      254182 :         switch (gf_node_get_tag(n)) {
    1398             :         /*anchor is not considered as a child sensor node when picking sensors*/
    1399         428 :         case TAG_MPEG4_Anchor:
    1400         428 :                 if (skip_anchors) return NULL;
    1401             :                 hs = gf_sc_anchor_get_handler(n);
    1402           0 :                 break;
    1403             : #ifndef GPAC_DISABLE_X3D
    1404           0 :         case TAG_X3D_Anchor:
    1405           0 :                 if (skip_anchors) return NULL;
    1406             :                 hs = gf_sc_anchor_get_handler(n);
    1407           0 :                 break;
    1408             : #endif
    1409             :         case TAG_MPEG4_DiscSensor:
    1410             :                 hs = disc_sensor_get_handler(n);
    1411          29 :                 break;
    1412             :         case TAG_MPEG4_PlaneSensor2D:
    1413             :                 hs = plane_sensor2d_get_handler(n);
    1414         755 :                 break;
    1415             :         case TAG_MPEG4_ProximitySensor2D:
    1416             :                 hs = proximity_sensor2d_get_handler(n);
    1417          31 :                 break;
    1418             :         case TAG_MPEG4_TouchSensor:
    1419             :                 hs = touch_sensor_get_handler(n);
    1420        7788 :                 break;
    1421             : #ifndef GPAC_DISABLE_X3D
    1422             :         case TAG_X3D_TouchSensor:
    1423             :                 hs = touch_sensor_get_handler(n);
    1424           0 :                 break;
    1425             : #endif
    1426             : #ifndef GPAC_DISABLE_3D
    1427             :         case TAG_MPEG4_CylinderSensor:
    1428             :                 hs = cylinder_sensor_get_handler(n);
    1429         214 :                 break;
    1430             :         case TAG_MPEG4_PlaneSensor:
    1431             :                 hs = plane_sensor_get_handler(n);
    1432         216 :                 break;
    1433             :         case TAG_MPEG4_SphereSensor:
    1434             :                 hs = sphere_get_handler(n);
    1435         214 :                 break;
    1436             : #ifndef GPAC_DISABLE_X3D
    1437             :         case TAG_X3D_CylinderSensor:
    1438             :                 hs = cylinder_sensor_get_handler(n);
    1439           0 :                 break;
    1440             :         case TAG_X3D_PlaneSensor:
    1441             :                 hs = plane_sensor_get_handler(n);
    1442           0 :                 break;
    1443             :         case TAG_X3D_SphereSensor:
    1444             :                 hs = sphere_get_handler(n);
    1445           0 :                 break;
    1446             : #endif
    1447             : #endif /*GPAC_DISABLE_3D*/
    1448             :         default:
    1449             :                 return NULL;
    1450             :         }
    1451        9247 :         if (hs && hs->IsEnabled(n)) return hs;
    1452             :         return NULL;
    1453             : }
    1454             : 
    1455           0 : Bool compositor_mpeg4_is_sensor_node(GF_Node *node)
    1456             : {
    1457             :         GF_SensorHandler *sh = compositor_mpeg4_get_sensor_handler(node);
    1458           0 :         if (sh && sh->IsEnabled(node)) return 1;
    1459             :         return 0;
    1460             : }
    1461             : 
    1462         211 : static void traverse_envtest(GF_Node *node, void *rs, Bool is_destroy)
    1463             : {
    1464         211 :         if (is_destroy) {
    1465           9 :                 GF_Compositor *compositor = gf_node_get_private(node);
    1466           9 :                 gf_list_del_item(compositor->env_tests, node);
    1467             :         }
    1468         211 : }
    1469             : 
    1470          24 : void envtest_evaluate(GF_Node *node, GF_Route *_route)
    1471             : {
    1472             :         Bool smaller, larger, equal;
    1473             :         Float ar, arft;
    1474             :         u32 par;
    1475             :         char par_value[50];
    1476             :         const char *opt;
    1477             :         M_EnvironmentTest *envtest = (M_EnvironmentTest *)node;
    1478          24 :         GF_Compositor *compositor = (GF_Compositor *)gf_node_get_private(node);
    1479             : 
    1480          24 :         if (envtest->parameterValue.buffer) gf_free(envtest->parameterValue.buffer);
    1481          24 :         envtest->parameterValue.buffer=NULL;
    1482             : 
    1483             :         smaller = larger = equal = 0;
    1484          24 :         switch (envtest->parameter) {
    1485             :         /*screen aspect ratio*/
    1486           0 :         case 0:
    1487           0 :                 if (compositor->display_width>compositor->display_height) {
    1488           0 :                         ar = (Float) compositor->display_width;
    1489           0 :                         ar /= compositor->display_height;
    1490             :                 } else {
    1491           0 :                         ar = (Float) compositor->display_height;
    1492           0 :                         ar /= compositor->display_width;
    1493             :                 }
    1494           0 :                 if (envtest->compareValue.buffer && (sscanf(envtest->compareValue.buffer, "%f", &arft)==1)) {
    1495           0 :                         if (ar==arft) equal=1;
    1496           0 :                         else if (ar>arft) smaller=1;
    1497             :                         else larger=1;
    1498             :                 }
    1499           0 :                 sprintf(par_value, "%f", ar);
    1500             :                 break;
    1501             :         /*screen is portrait */
    1502           2 :         case 1:
    1503           2 :                 equal = (compositor->display_width < compositor->display_height) ? 1 : 2;
    1504             :                 strcpy(par_value, (equal==1) ? "TRUE" : "FALSE");
    1505             :                 break;
    1506             :         /*screen width */
    1507          20 :         case 2:
    1508          20 :                 if (envtest->compareValue.buffer && (sscanf(envtest->compareValue.buffer, "%u", &par)==1)) {
    1509           0 :                         if (compositor->display_width==par) equal=1;
    1510           0 :                         else if (compositor->display_width>par) smaller=1;
    1511             :                         else larger=1;
    1512             :                 }
    1513          20 :                 sprintf(par_value, "%u", compositor->display_width);
    1514             :                 break;
    1515             :         /*screen width */
    1516           2 :         case 3:
    1517           2 :                 if (envtest->compareValue.buffer && (sscanf(envtest->compareValue.buffer, "%u", &par)==1)) {
    1518           0 :                         if (compositor->display_height==par) equal=1;
    1519           0 :                         else if (compositor->display_height>par) smaller=1;
    1520             :                         else larger=1;
    1521             :                 }
    1522           2 :                 sprintf(par_value, "%u", compositor->display_height);
    1523             :                 break;
    1524             :         /*screen dpi horizontal */
    1525           0 :         case 4:
    1526           0 :                 if (envtest->compareValue.buffer && (sscanf(envtest->compareValue.buffer, "%u", &par)==1)) {
    1527           0 :                         if (compositor->video_out->dpi_x==par) equal=1;
    1528           0 :                         else if (compositor->video_out->dpi_x>par) smaller=1;
    1529             :                         else larger=1;
    1530             :                 }
    1531           0 :                 sprintf(par_value, "%u", compositor->video_out->dpi_x);
    1532             :                 break;
    1533             :         /*screen dpi vertical*/
    1534           0 :         case 5:
    1535           0 :                 if (envtest->compareValue.buffer && (sscanf(envtest->compareValue.buffer, "%u", &par)==1)) {
    1536           0 :                         if (compositor->video_out->dpi_y==par) equal=1;
    1537           0 :                         else if (compositor->video_out->dpi_y>par) smaller=1;
    1538             :                         else larger=1;
    1539             :                 }
    1540           0 :                 sprintf(par_value, "%u", compositor->video_out->dpi_y);
    1541             :                 break;
    1542             :         /*automotive situation - fixme we should use a profile doc ?*/
    1543           0 :         case 6:
    1544           0 :                 opt = gf_opts_get_key("Profile", "Automotive");
    1545           0 :                 equal = (opt && !strcmp(opt, "yes")) ? 1 : 2;
    1546             :                 strcpy(par_value, (equal==1) ? "TRUE" : "FALSE");
    1547             :                 break;
    1548             :         /*visually challenged - fixme we should use a profile doc ?*/
    1549           0 :         case 7:
    1550           0 :                 opt = gf_opts_get_key("Profile", "VisuallyChallenged");
    1551           0 :                 equal = (opt && !strcmp(opt, "yes")) ? 1 : 2;
    1552             :                 strcpy(par_value, (equal==1) ? "TRUE" : "FALSE");
    1553             :                 break;
    1554             :         /*has touch - fixme we should find out by ourselves*/
    1555           0 :         case 8:
    1556           0 :                 opt = gf_opts_get_key("Profile", "HasTouchScreen");
    1557           0 :                 equal = (!opt || !strcmp(opt, "yes")) ? 1 : 2;
    1558             :                 strcpy(par_value, (equal==1) ? "TRUE" : "FALSE");
    1559             :                 break;
    1560             :         /*has key - fixme we should find out by ourselves*/
    1561           0 :         case 9:
    1562           0 :                 opt = gf_opts_get_key("Profile", "HasKeyPad");
    1563           0 :                 equal = (!opt || !strcmp(opt, "yes")) ? 1 : 2;
    1564             :                 strcpy(par_value, (equal==1) ? "TRUE" : "FALSE");
    1565             :                 break;
    1566             :         }
    1567             : 
    1568          24 :         if (equal) {
    1569           2 :                 envtest->valueEqual=(equal==1) ? 1 : 0;
    1570           2 :                 gf_node_event_out(node, 6/*"valueEqual"*/);
    1571             :         }
    1572          22 :         else if (smaller) {
    1573           0 :                 envtest->valueSmaller=1;
    1574           0 :                 gf_node_event_out(node, 7/*"valueSmaller"*/);
    1575             :         }
    1576          22 :         else if (larger) {
    1577           0 :                 envtest->valueLarger=1;
    1578           0 :                 gf_node_event_out(node, 5/*"valueLarger"*/);
    1579             :         }
    1580          24 :         envtest->parameterValue.buffer = gf_strdup(par_value);
    1581          24 :         gf_node_event_out(node, 8/*"parameterValue"*/);
    1582          24 : }
    1583             : 
    1584         699 : void compositor_evaluate_envtests(GF_Compositor *compositor, u32 param_type)
    1585             : {
    1586             :         u32 i, count;
    1587         699 :         count = gf_list_count(compositor->env_tests);
    1588         714 :         for (i=0; i<count; i++) {
    1589          15 :                 GF_Node *envtest = gf_list_get(compositor->env_tests, i);
    1590          15 :                 if (!((M_EnvironmentTest *)envtest)->evaluateOnChange) continue;
    1591             : 
    1592          15 :                 switch (((M_EnvironmentTest *)envtest)->parameter) {
    1593             :                 /*screen-size related*/
    1594          15 :                 case 0:
    1595             :                 case 1:
    1596             :                 case 2:
    1597             :                 case 3:
    1598          15 :                         if (param_type==0) envtest_evaluate(envtest, NULL);
    1599             :                         break;
    1600             :                 /*DPI related*/
    1601           0 :                 case 4:
    1602             :                 case 5:
    1603           0 :                         if (param_type==1) envtest_evaluate(envtest, NULL);
    1604             :                         break;
    1605             :                 /*automotive situation*/
    1606           0 :                 case 6:
    1607           0 :                         if (param_type==2) envtest_evaluate(envtest, NULL);
    1608             :                         break;
    1609             :                         /*the rest are static events*/
    1610             :                 }
    1611             :         }
    1612         699 : }
    1613             : 
    1614           0 : void compositor_envtest_modified(GF_Node *node)
    1615             : {
    1616           9 :         envtest_evaluate(node, NULL);
    1617           0 : }
    1618             : 
    1619           9 : void compositor_init_envtest(GF_Compositor *compositor, GF_Node *node)
    1620             : {
    1621             :         M_EnvironmentTest *envtest = (M_EnvironmentTest *)node;
    1622           9 :         gf_list_add(compositor->env_tests, node);
    1623           9 :         gf_node_set_private(node, compositor);
    1624           9 :         gf_node_set_callback_function(node, traverse_envtest);
    1625             : 
    1626           9 :         envtest->on_evaluate = envtest_evaluate;
    1627             : 
    1628             : #ifdef GPAC_ENABLE_COVERAGE
    1629           9 :         if (gf_sys_is_cov_mode()) {
    1630             :                 compositor_envtest_modified(node);
    1631             :         }
    1632             : #endif
    1633           9 : }
    1634             : 
    1635             : 
    1636             : 
    1637             : #endif /*GPAC_DISABLE_VRML*/

Generated by: LCOV version 1.13