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*/
|