Fix a bunch of preferences errors
[vlc.git] / modules / video_filter / wall.c
1 /*****************************************************************************
2  * wall.c : Wall video plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000, 2001, 2002, 2003 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>                                      /* malloc(), free() */
28 #include <string.h>
29
30 #include <vlc/vlc.h>
31 #include <vlc/vout.h>
32
33 #include "filter_common.h"
34
35 /*****************************************************************************
36  * Local prototypes
37  *****************************************************************************/
38 static int  Create    ( vlc_object_t * );
39 static void Destroy   ( vlc_object_t * );
40
41 static int  Init      ( vout_thread_t * );
42 static void End       ( vout_thread_t * );
43 static void Render    ( vout_thread_t *, picture_t * );
44
45 static void RemoveAllVout  ( vout_thread_t *p_vout );
46
47 static int  SendEvents( vlc_object_t *, char const *,
48                         vlc_value_t, vlc_value_t, void * );
49
50 /*****************************************************************************
51  * Module descriptor
52  *****************************************************************************/
53 #define COLS_TEXT N_("Number of columns")
54 #define COLS_LONGTEXT N_("Select the number of horizontal video windows in " \
55     "which to split the video.")
56
57 #define ROWS_TEXT N_("Number of rows")
58 #define ROWS_LONGTEXT N_("Select the number of vertical video windows in " \
59     "which to split the video.")
60
61 #define ACTIVE_TEXT N_("Active windows")
62 #define ACTIVE_LONGTEXT N_("Comma separated list of active windows, " \
63     "defaults to all")
64
65 #define ASPECT_TEXT N_("Element aspect ratio")
66 #define ASPECT_LONGTEXT N_("The aspect ratio of the individual displays building the display wall")
67
68 vlc_module_begin();
69     set_description( _("Wall video filter") );
70     set_shortname( N_("Image wall" ));
71     set_capability( "video filter", 0 );
72     set_category( CAT_VIDEO );
73     set_subcategory( SUBCAT_VIDEO_VFILTER );
74
75     add_integer( "wall-cols", 3, NULL, COLS_TEXT, COLS_LONGTEXT, VLC_FALSE );
76     add_integer( "wall-rows", 3, NULL, ROWS_TEXT, ROWS_LONGTEXT, VLC_FALSE );
77     add_string( "wall-active", NULL, NULL, ACTIVE_TEXT, ACTIVE_LONGTEXT,
78                  VLC_TRUE );
79     add_string( "wall-element-aspect", "4:3", NULL, ASPECT_TEXT, ASPECT_LONGTEXT, VLC_FALSE );
80
81     add_shortcut( "wall" );
82     set_callbacks( Create, Destroy );
83 vlc_module_end();
84
85 /*****************************************************************************
86  * vout_sys_t: Wall video output method descriptor
87  *****************************************************************************
88  * This structure is part of the video output thread descriptor.
89  * It describes the Wall specific properties of an output thread.
90  *****************************************************************************/
91 struct vout_sys_t
92 {
93     int    i_col;
94     int    i_row;
95     int    i_vout;
96     struct vout_list_t
97     {
98         vlc_bool_t b_active;
99         int i_width;
100         int i_height;
101         int i_left;
102         int i_top;
103         vout_thread_t *p_vout;
104     } *pp_vout;
105 };
106
107 /*****************************************************************************
108  * Control: control facility for the vout (forwards to child vout)
109  *****************************************************************************/
110 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
111 {
112     int i_row, i_col, i_vout = 0;
113
114     for( i_row = 0; i_row < p_vout->p_sys->i_row; i_row++ )
115     {
116         for( i_col = 0; i_col < p_vout->p_sys->i_col; i_col++ )
117         {
118             vout_vaControl( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
119                             i_query, args );
120             i_vout++;
121         }
122     }
123     return VLC_SUCCESS;
124 }
125
126 /*****************************************************************************
127  * Create: allocates Wall video thread output method
128  *****************************************************************************
129  * This function allocates and initializes a Wall vout method.
130  *****************************************************************************/
131 static int Create( vlc_object_t *p_this )
132 {
133     vout_thread_t *p_vout = (vout_thread_t *)p_this;
134     char *psz_method, *psz_tmp, *psz_method_tmp;
135     int i_vout;
136
137     /* Allocate structure */
138     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
139     if( p_vout->p_sys == NULL )
140     {
141         msg_Err( p_vout, "out of memory" );
142         return VLC_ENOMEM;
143     }
144
145     p_vout->pf_init = Init;
146     p_vout->pf_end = End;
147     p_vout->pf_manage = NULL;
148     p_vout->pf_render = Render;
149     p_vout->pf_display = NULL;
150     p_vout->pf_control = Control;
151
152     /* Look what method was requested */
153     p_vout->p_sys->i_col = config_GetInt( p_vout, "wall-cols" );
154     p_vout->p_sys->i_row = config_GetInt( p_vout, "wall-rows" );
155
156     p_vout->p_sys->i_col = __MAX( 1, __MIN( 15, p_vout->p_sys->i_col ) );
157     p_vout->p_sys->i_row = __MAX( 1, __MIN( 15, p_vout->p_sys->i_row ) );
158
159     msg_Dbg( p_vout, "opening a %i x %i wall",
160              p_vout->p_sys->i_col, p_vout->p_sys->i_row );
161
162     p_vout->p_sys->pp_vout = malloc( p_vout->p_sys->i_row *
163                                      p_vout->p_sys->i_col *
164                                      sizeof(struct vout_list_t) );
165     if( p_vout->p_sys->pp_vout == NULL )
166     {
167         msg_Err( p_vout, "out of memory" );
168         free( p_vout->p_sys );
169         return VLC_ENOMEM;
170     }
171
172     psz_method_tmp = psz_method = config_GetPsz( p_vout, "wall-active" );
173
174     /* If no trailing vout are specified, take them all */
175     if( psz_method == NULL )
176     {
177         for( i_vout = p_vout->p_sys->i_row * p_vout->p_sys->i_col;
178              i_vout--; )
179         {
180             p_vout->p_sys->pp_vout[i_vout].b_active = 1;
181         }
182     }
183     /* If trailing vout are specified, activate only the requested ones */
184     else
185     {
186         for( i_vout = p_vout->p_sys->i_row * p_vout->p_sys->i_col;
187              i_vout--; )
188         {
189             p_vout->p_sys->pp_vout[i_vout].b_active = 0;
190         }
191
192         while( *psz_method )
193         {
194             psz_tmp = psz_method;
195             while( *psz_tmp && *psz_tmp != ',' )
196             {
197                 psz_tmp++;
198             }
199
200             if( *psz_tmp )
201             {
202                 *psz_tmp = '\0';
203                 i_vout = atoi( psz_method );
204                 psz_method = psz_tmp + 1;
205             }
206             else
207             {
208                 i_vout = atoi( psz_method );
209                 psz_method = psz_tmp;
210             }
211
212             if( i_vout >= 0 &&
213                 i_vout < p_vout->p_sys->i_row * p_vout->p_sys->i_col )
214             {
215                 p_vout->p_sys->pp_vout[i_vout].b_active = 1;
216             }
217         }
218     }
219
220     free( psz_method_tmp );
221
222     return VLC_SUCCESS;
223 }
224
225 /*****************************************************************************
226  * Init: initialize Wall video thread output method
227  *****************************************************************************/
228 static int Init( vout_thread_t *p_vout )
229 {
230     int i_index, i_row, i_col, i_width, i_height, i_left, i_top;
231     unsigned int i_target_width,i_target_height;
232     picture_t *p_pic;
233     video_format_t fmt = {0};
234     int i_aspect = 4*VOUT_ASPECT_FACTOR/3;
235     int i_align = 0;
236     unsigned int i_hstart, i_hend, i_vstart, i_vend;
237     unsigned int w1,h1,w2,h2;
238     int i_xpos, i_ypos;
239     int i_vstart_rounded = 0, i_hstart_rounded = 0;
240     char *psz_aspect;
241
242     psz_aspect = config_GetPsz( p_vout, "wall-element-aspect" );
243     if( psz_aspect && *psz_aspect )
244     {
245         char *psz_parser = strchr( psz_aspect, ':' );
246         if( psz_parser )
247         {
248             *psz_parser++ = '\0';
249             i_aspect = atoi( psz_aspect ) * VOUT_ASPECT_FACTOR
250                 / atoi( psz_parser );
251         }
252         else
253         {
254             msg_Warn( p_vout, "invalid aspect ratio specification" );
255         }
256         free( psz_aspect );
257     }
258     
259
260     i_xpos = var_CreateGetInteger( p_vout, "video-x" );
261     i_ypos = var_CreateGetInteger( p_vout, "video-y" );
262     if( i_xpos < 0 ) i_xpos = 0;
263     if( i_ypos < 0 ) i_ypos = 0;
264
265     I_OUTPUTPICTURES = 0;
266
267     /* Initialize the output structure */
268     p_vout->output.i_chroma = p_vout->render.i_chroma;
269     p_vout->output.i_width  = p_vout->render.i_width;
270     p_vout->output.i_height = p_vout->render.i_height;
271     p_vout->output.i_aspect = p_vout->render.i_aspect;
272     var_Create( p_vout, "align", VLC_VAR_INTEGER );
273
274     fmt.i_width = fmt.i_visible_width = p_vout->render.i_width;
275     fmt.i_height = fmt.i_visible_height = p_vout->render.i_height;
276     fmt.i_x_offset = fmt.i_y_offset = 0;
277     fmt.i_chroma = p_vout->render.i_chroma;
278     fmt.i_aspect = p_vout->render.i_aspect;
279     fmt.i_sar_num = p_vout->render.i_aspect * fmt.i_height / fmt.i_width;
280     fmt.i_sar_den = VOUT_ASPECT_FACTOR;
281
282     w1 = p_vout->output.i_width / p_vout->p_sys->i_col;
283     w1 &= ~1;
284     h1 = w1 * VOUT_ASPECT_FACTOR / i_aspect&~1;
285     h1 &= ~1;
286     
287     h2 = p_vout->output.i_height / p_vout->p_sys->i_row&~1;
288     h2 &= ~1;
289     w2 = h2 * i_aspect / VOUT_ASPECT_FACTOR&~1;
290     w2 &= ~1;
291     
292     if ( h1 * p_vout->p_sys->i_row < p_vout->output.i_height )
293     {
294         unsigned int i_tmp;
295         i_target_width = w2;        
296         i_target_height = h2;
297         i_vstart = 0;
298         i_vend = p_vout->output.i_height;
299         i_tmp = i_target_width * p_vout->p_sys->i_col;
300         while( i_tmp < p_vout->output.i_width ) i_tmp += p_vout->p_sys->i_col;
301         i_hstart = (( i_tmp - p_vout->output.i_width ) / 2)&~1;
302         i_hstart_rounded  = ( ( i_tmp - p_vout->output.i_width ) % 2 ) ||
303             ( ( ( i_tmp - p_vout->output.i_width ) / 2 ) & 1 );
304         i_hend = i_hstart + p_vout->output.i_width;
305     }
306     else
307     {
308         unsigned int i_tmp;
309         i_target_height = h1;
310         i_target_width = w1;
311         i_hstart = 0;
312         i_hend = p_vout->output.i_width;
313         i_tmp = i_target_height * p_vout->p_sys->i_row;
314         while( i_tmp < p_vout->output.i_height ) i_tmp += p_vout->p_sys->i_row;
315         i_vstart = ( ( i_tmp - p_vout->output.i_height ) / 2 ) & ~1;
316         i_vstart_rounded  = ( ( i_tmp - p_vout->output.i_height ) % 2 ) ||
317             ( ( ( i_tmp - p_vout->output.i_height ) / 2 ) & 1 );
318         i_vend = i_vstart + p_vout->output.i_height;
319     }
320     msg_Dbg( p_vout, "target resolution %dx%d", i_target_width, i_target_height );
321
322     /* Try to open the real video output */
323     msg_Dbg( p_vout, "spawning the real video outputs" );
324
325     p_vout->p_sys->i_vout = 0;
326     msg_Dbg( p_vout, "target window (%d,%d)-(%d,%d)", i_hstart,i_vstart,i_hend,i_vend );
327     
328
329     i_top = 0;
330     i_height = 0;
331     for( i_row = 0; i_row < p_vout->p_sys->i_row; i_row++ )
332     {
333         i_left = 0;
334         i_top += i_height;
335         for( i_col = 0; i_col < p_vout->p_sys->i_col; i_col++ )
336         {
337             i_align = 0;
338
339             if( i_col*i_target_width >= i_hstart &&
340                 (i_col+1)*i_target_width <= i_hend )
341             {
342                 i_width = i_target_width;
343             }
344             else if( ( i_col + 1 ) * i_target_width < i_hstart ||
345                      ( i_col * i_target_width ) > i_hend )
346             {
347                 i_width = 0;                
348             }
349             else
350             {
351                 i_width = ( i_target_width - i_hstart % i_target_width );
352                 if( i_col >= ( p_vout->p_sys->i_col / 2 ) )
353                 {
354                     i_align |= VOUT_ALIGN_LEFT;
355                     i_width -= i_hstart_rounded ? 2: 0;
356                 }
357                 else
358                 {
359                     i_align |= VOUT_ALIGN_RIGHT;
360                 }
361             }
362             
363             if( i_row * i_target_height >= i_vstart &&
364                 ( i_row + 1 ) * i_target_height <= i_vend )
365             {
366                 i_height = i_target_height;
367             }
368             else if( ( i_row + 1 ) * i_target_height < i_vstart ||
369                      ( i_row * i_target_height ) > i_vend )
370             {
371                 i_height = 0;
372             }
373             else
374             {
375                 i_height = ( i_target_height -
376                              i_vstart%i_target_height );
377                 if(  i_row >= ( p_vout->p_sys->i_row / 2 ) )
378                 {
379                     i_align |= VOUT_ALIGN_TOP;
380                     i_height -= i_vstart_rounded ? 2: 0;
381                 }
382                 else
383                 {
384                     i_align |= VOUT_ALIGN_BOTTOM;
385                 }
386             }
387             if( i_height == 0 || i_width == 0 )
388             {
389                 p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].b_active = VLC_FALSE;
390             }
391
392             p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].i_width = i_width;
393             p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].i_height = i_height;
394             p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].i_left = i_left;
395             p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].i_top = i_top;
396             i_left += i_width;
397
398             if( !p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].b_active )
399             {
400                 p_vout->p_sys->i_vout++;
401                 continue;
402             }
403             var_SetInteger( p_vout, "align", i_align );
404             var_SetInteger( p_vout, "video-x", i_left + i_xpos - i_width);
405             var_SetInteger( p_vout, "video-y", i_top + i_ypos );
406
407             fmt.i_width = fmt.i_visible_width = i_width;
408             fmt.i_height = fmt.i_visible_height = i_height;
409             fmt.i_aspect = i_aspect * i_target_height / i_height *
410                 i_width / i_target_width;
411
412             p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].p_vout =
413                 vout_Create( p_vout, &fmt );
414             if( !p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].p_vout )
415             {
416                 msg_Err( p_vout, "failed to get %ix%i vout threads",
417                                  p_vout->p_sys->i_col, p_vout->p_sys->i_row );
418                 RemoveAllVout( p_vout );
419                 return VLC_EGENERIC;
420             }
421             ADD_CALLBACKS(
422                 p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].p_vout,
423                 SendEvents );
424             p_vout->p_sys->i_vout++;
425         }
426     }
427
428     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
429
430     ADD_PARENT_CALLBACKS( SendEventsToChild );
431
432     return VLC_SUCCESS;
433 }
434
435 /*****************************************************************************
436  * End: terminate Wall video thread output method
437  *****************************************************************************/
438 static void End( vout_thread_t *p_vout )
439 {
440     int i_index;
441
442     /* Free the fake output buffers we allocated */
443     for( i_index = I_OUTPUTPICTURES ; i_index ; )
444     {
445         i_index--;
446         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
447     }
448 }
449
450 /*****************************************************************************
451  * Destroy: destroy Wall video thread output method
452  *****************************************************************************
453  * Terminate an output method created by WallCreateOutputMethod
454  *****************************************************************************/
455 static void Destroy( vlc_object_t *p_this )
456 {
457     vout_thread_t *p_vout = (vout_thread_t *)p_this;
458
459     RemoveAllVout( p_vout );
460
461     DEL_PARENT_CALLBACKS( SendEventsToChild );
462
463     free( p_vout->p_sys->pp_vout );
464     free( p_vout->p_sys );
465 }
466
467 /*****************************************************************************
468  * Render: displays previously rendered output
469  *****************************************************************************
470  * This function send the currently rendered image to Wall image, waits
471  * until it is displayed and switch the two rendering buffers, preparing next
472  * frame.
473  *****************************************************************************/
474 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
475 {
476     picture_t *p_outpic = NULL;
477     int i_col, i_row, i_vout, i_plane;
478     int pi_left_skip[VOUT_MAX_PLANES], pi_top_skip[VOUT_MAX_PLANES];
479
480     i_vout = 0;
481
482     for( i_row = 0; i_row < p_vout->p_sys->i_row; i_row++ )
483     {
484         for( i_col = 0; i_col < p_vout->p_sys->i_col; i_col++ )
485         {
486             for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
487             {
488                 pi_left_skip[i_plane] = p_vout->p_sys->pp_vout[ i_vout ].i_left * p_pic->p[ i_plane ].i_pitch / p_vout->output.i_width;
489                 pi_top_skip[i_plane] = (p_vout->p_sys->pp_vout[ i_vout ].i_top * p_pic->p[ i_plane ].i_lines / p_vout->output.i_height)*p_pic->p[i_plane].i_pitch;
490             }
491
492             if( !p_vout->p_sys->pp_vout[ i_vout ].b_active )
493             {
494                 i_vout++;
495                 continue;
496             }
497
498             while( ( p_outpic =
499                 vout_CreatePicture( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
500                                     0, 0, 0 )
501                    ) == NULL )
502             {
503                 if( p_vout->b_die || p_vout->b_error )
504                 {
505                     vout_DestroyPicture(
506                         p_vout->p_sys->pp_vout[ i_vout ].p_vout, p_outpic );
507                     return;
508                 }
509
510                 msleep( VOUT_OUTMEM_SLEEP );
511             }
512
513             vout_DatePicture( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
514                               p_outpic, p_pic->date );
515             vout_LinkPicture( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
516                               p_outpic );
517
518             for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
519             {
520                 uint8_t *p_in, *p_in_end, *p_out;
521                 int i_in_pitch = p_pic->p[i_plane].i_pitch;
522                 int i_out_pitch = p_outpic->p[i_plane].i_pitch;
523                 int i_copy_pitch = p_outpic->p[i_plane].i_visible_pitch;
524
525                 p_in = p_pic->p[i_plane].p_pixels
526                         + pi_top_skip[i_plane] + pi_left_skip[i_plane];
527
528                 p_in_end = p_in + p_outpic->p[i_plane].i_visible_lines
529                                    * p_pic->p[i_plane].i_pitch;
530
531                 p_out = p_outpic->p[i_plane].p_pixels;
532
533                 while( p_in < p_in_end )
534                 {
535                     p_vout->p_vlc->pf_memcpy( p_out, p_in, i_copy_pitch );
536                     p_in += i_in_pitch;
537                     p_out += i_out_pitch;
538                 }
539
540 //                pi_left_skip[i_plane] += i_copy_pitch;
541             }
542
543             vout_UnlinkPicture( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
544                                 p_outpic );
545             vout_DisplayPicture( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
546                                  p_outpic );
547
548             i_vout++;
549         }
550
551 /*         for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ ) */
552 /*         { */
553 /*             pi_top_skip[i_plane] += p_vout->p_sys->pp_vout[ i_vout ].i_height */
554 /*                                      * p_pic->p[i_plane].i_visible_lines */
555 /*                                      / p_vout->output.i_height */
556 /*                                      * p_pic->p[i_plane].i_pitch; */
557 /*         } */
558     }
559 }
560
561 /*****************************************************************************
562  * RemoveAllVout: destroy all the child video output threads
563  *****************************************************************************/
564 static void RemoveAllVout( vout_thread_t *p_vout )
565 {
566     while( p_vout->p_sys->i_vout )
567     {
568          --p_vout->p_sys->i_vout;
569          if( p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].b_active )
570          {
571              DEL_CALLBACKS(
572                  p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].p_vout,
573                  SendEvents );
574              vlc_object_detach(
575                  p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].p_vout );
576              vout_Destroy(
577                  p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].p_vout );
578          }
579     }
580 }
581
582 /*****************************************************************************
583  * SendEvents: forward mouse and keyboard events to the parent p_vout
584  *****************************************************************************/
585 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
586                        vlc_value_t oldval, vlc_value_t newval, void *_p_vout )
587 {
588     vout_thread_t *p_vout = (vout_thread_t *)_p_vout;
589     int i_vout;
590     vlc_value_t sentval = newval;
591
592     /* Find the video output index */
593     for( i_vout = 0; i_vout < p_vout->p_sys->i_vout; i_vout++ )
594     {
595         if( p_this == (vlc_object_t *)p_vout->p_sys->pp_vout[ i_vout ].p_vout )
596         {
597             break;
598         }
599     }
600
601     if( i_vout == p_vout->p_sys->i_vout )
602     {
603         return VLC_EGENERIC;
604     }
605
606     /* Translate the mouse coordinates */
607     if( !strcmp( psz_var, "mouse-x" ) )
608     {
609         sentval.i_int += p_vout->output.i_width
610                           * (i_vout % p_vout->p_sys->i_col)
611                           / p_vout->p_sys->i_col;
612     }
613     else if( !strcmp( psz_var, "mouse-y" ) )
614     {
615         sentval.i_int += p_vout->output.i_height
616                           * (i_vout / p_vout->p_sys->i_row)
617                           / p_vout->p_sys->i_row;
618     }
619
620     var_Set( p_vout, psz_var, sentval );
621
622     return VLC_SUCCESS;
623 }
624
625 /*****************************************************************************
626  * SendEventsToChild: forward events to the child/children vout
627  *****************************************************************************/
628 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
629                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
630 {
631     vout_thread_t *p_vout = (vout_thread_t *)p_this;
632     int i_row, i_col, i_vout = 0;
633
634     for( i_row = 0; i_row < p_vout->p_sys->i_row; i_row++ )
635     {
636         for( i_col = 0; i_col < p_vout->p_sys->i_col; i_col++ )
637         {
638             var_Set( p_vout->p_sys->pp_vout[ i_vout ].p_vout, psz_var, newval);
639             if( !strcmp( psz_var, "fullscreen" ) ) break;
640             i_vout++;
641         }
642     }
643
644     return VLC_SUCCESS;
645 }