]> git.sesse.net Git - vlc/blob - modules/video_output/opengl.c
90c80f28091cf7f0f341c4852cb703cd203389c4
[vlc] / modules / video_output / opengl.c
1 /*****************************************************************************
2  * opengl.c: OpenGL video output
3  *****************************************************************************
4  * Copyright (C) 2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Cyril Deguet <asmax@videolan.org>
8  *          Gildas Bazin <gbazin@videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program 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 General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <errno.h>                                                 /* ENOMEM */
29 #include <stdlib.h>                                      /* malloc(), free() */
30 #include <string.h>
31
32 #include <vlc/vlc.h>
33 #include <vlc/vout.h>
34
35 #ifdef SYS_DARWIN
36 #include <OpenGL/gl.h>
37 #include <OpenGL/glext.h>
38
39 /* On OS X, use GL_TEXTURE_RECTANGLE_EXT instead of GL_TEXTURE_2D.
40    This allows sizes which are not powers of 2 */
41 #define VLCGL_TARGET GL_TEXTURE_RECTANGLE_EXT
42
43 /* OS X OpenGL supports YUV. Hehe. */
44 #define VLCGL_FORMAT GL_YCBCR_422_APPLE
45 #define VLCGL_TYPE   GL_UNSIGNED_SHORT_8_8_APPLE
46 #else
47
48 #include <GL/gl.h>
49 #define VLCGL_TARGET GL_TEXTURE_2D
50
51 /* RV16 */
52 #ifndef GL_UNSIGNED_SHORT_5_6_5
53 #define GL_UNSIGNED_SHORT_5_6_5 0x8363
54 #endif
55 //#define VLCGL_RGB_FORMAT GL_RGB
56 //#define VLCGL_RGB_TYPE GL_UNSIGNED_SHORT_5_6_5
57
58 /* RV24 */
59 //#define VLCGL_RGB_FORMAT GL_RGB
60 //#define VLCGL_RGB_TYPE GL_UNSIGNED_BYTE
61
62 /* RV32 */
63 #define VLCGL_RGB_FORMAT GL_RGBA
64 #define VLCGL_RGB_TYPE GL_UNSIGNED_BYTE
65
66 /* Use RGB on Win32/GLX */
67 #define VLCGL_FORMAT VLCGL_RGB_FORMAT
68 #define VLCGL_TYPE   VLCGL_RGB_TYPE
69 #endif
70
71 /* OpenGL effects */
72 #define OPENGL_EFFECT_NONE             1
73 #define OPENGL_EFFECT_CUBE             2
74 #define OPENGL_EFFECT_TRANSPARENT_CUBE 4
75
76 /*****************************************************************************
77  * Vout interface
78  *****************************************************************************/
79 static int  CreateVout   ( vlc_object_t * );
80 static void DestroyVout  ( vlc_object_t * );
81 static int  Init         ( vout_thread_t * );
82 static void End          ( vout_thread_t * );
83 static int  Manage       ( vout_thread_t * );
84 static void Render       ( vout_thread_t *, picture_t * );
85 static void DisplayVideo ( vout_thread_t *, picture_t * );
86 static int  Control      ( vout_thread_t *, int, va_list );
87
88 static inline int GetAlignedSize( int );
89
90 static int SendEvents( vlc_object_t *, char const *,
91                        vlc_value_t, vlc_value_t, void * );
92
93 /*****************************************************************************
94  * Module descriptor
95  *****************************************************************************/
96 #define EFFECT_TEXT N_("Select effect")
97 #define EFFECT_LONGTEXT N_( \
98     "Allows you to select different visual effects.")
99
100 vlc_module_begin();
101     set_description( _("OpenGL video output") );
102     set_capability( "video output", 20 );
103     add_shortcut( "opengl" );
104     set_callbacks( CreateVout, DestroyVout );
105
106     add_string( "opengl-effect", "none", NULL, EFFECT_TEXT,
107                  EFFECT_LONGTEXT, VLC_TRUE );
108 vlc_module_end();
109
110 /*****************************************************************************
111  * vout_sys_t: video output method descriptor
112  *****************************************************************************
113  * This structure is part of the video output thread descriptor.
114  * It describes the OpenGL specific properties of the output thread.
115  *****************************************************************************/
116 struct vout_sys_t
117 {
118     vout_thread_t *p_vout;
119
120     uint8_t    *pp_buffer[2];
121     int         i_index;
122     int         i_tex_width;
123     int         i_tex_height;
124     GLuint      p_textures[2];
125
126     int         i_effect;
127 };
128
129 /*****************************************************************************
130  * CreateVout: This function allocates and initializes the OpenGL vout method.
131  *****************************************************************************/
132 static int CreateVout( vlc_object_t *p_this )
133 {
134     vout_thread_t *p_vout = (vout_thread_t *)p_this;
135     vout_sys_t *p_sys;
136
137     /* Allocate structure */
138     p_vout->p_sys = p_sys = malloc( sizeof( vout_sys_t ) );
139     if( p_sys == NULL )
140     {
141         msg_Err( p_vout, "out of memory" );
142         return VLC_EGENERIC;
143     }
144
145     var_Create( p_vout, "opengl-effect", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
146
147 #ifdef SYS_DARWIN
148     p_sys->i_tex_width  = p_vout->render.i_width;
149     p_sys->i_tex_height = p_vout->render.i_height;
150 #else
151     /* A texture must have a size aligned on a power of 2 */
152     p_sys->i_tex_width  = GetAlignedSize( p_vout->render.i_width );
153     p_sys->i_tex_height = GetAlignedSize( p_vout->render.i_height );
154 #endif
155
156     msg_Dbg( p_vout, "Texture size: %dx%d", p_sys->i_tex_width,
157              p_sys->i_tex_height );
158
159     /* Get window */
160     p_sys->p_vout =
161         (vout_thread_t *)vlc_object_create( p_this, VLC_OBJECT_OPENGL );
162     if( p_sys->p_vout == NULL )
163     {
164         msg_Err( p_vout, "out of memory" );
165         return VLC_ENOMEM;
166     }
167     vlc_object_attach( p_sys->p_vout, p_this );
168
169     p_sys->p_vout->i_window_width = p_vout->i_window_width;
170     p_sys->p_vout->i_window_height = p_vout->i_window_height;
171     p_sys->p_vout->b_fullscreen = p_vout->b_fullscreen;
172     p_sys->p_vout->render.i_width = p_vout->render.i_width;
173     p_sys->p_vout->render.i_height = p_vout->render.i_height;
174     p_sys->p_vout->render.i_aspect = p_vout->render.i_aspect;
175     p_sys->p_vout->b_scale = p_vout->b_scale;
176     p_sys->p_vout->i_alignment = p_vout->i_alignment;
177
178     p_sys->p_vout->p_module =
179         module_Need( p_sys->p_vout, "opengl provider", NULL, 0 );
180     if( p_sys->p_vout->p_module == NULL )
181     {
182         msg_Err( p_vout, "No OpenGL provider found" );
183         vlc_object_detach( p_sys->p_vout );
184         vlc_object_destroy( p_sys->p_vout );
185         return VLC_ENOOBJ;
186     }
187
188     p_vout->pf_init = Init;
189     p_vout->pf_end = End;
190     p_vout->pf_manage = Manage;
191     p_vout->pf_render = Render;
192     p_vout->pf_display = DisplayVideo;
193     p_vout->pf_control = Control;
194
195     /* Forward events from the opengl provider */
196     var_AddCallback( p_sys->p_vout, "mouse-x", SendEvents, p_vout );
197     var_AddCallback( p_sys->p_vout, "mouse-y", SendEvents, p_vout );
198     var_AddCallback( p_sys->p_vout, "mouse-moved", SendEvents, p_vout );
199     var_AddCallback( p_sys->p_vout, "mouse-clicked", SendEvents, p_vout );
200
201     return VLC_SUCCESS;
202 }
203
204 /*****************************************************************************
205  * Init: initialize the OpenGL video thread output method
206  *****************************************************************************/
207 static int Init( vout_thread_t *p_vout )
208 {
209     vout_sys_t *p_sys = p_vout->p_sys;
210     int i_pixel_pitch, i_index;
211     vlc_value_t val;
212
213     p_sys->p_vout->pf_init( p_sys->p_vout );
214
215 #ifdef SYS_DARWIN
216     p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
217     p_vout->output.i_rmask = 0x00ff0000;
218     p_vout->output.i_gmask = 0x0000ff00;
219     p_vout->output.i_bmask = 0x000000ff;
220     i_pixel_pitch = 2;
221 #else
222 #if VLCGL_RGB_FORMAT == GL_RGB
223 #   if VLCGL_RGB_TYPE == GL_UNSIGNED_BYTE
224     p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4');
225     p_vout->output.i_rmask = 0x000000ff;
226     p_vout->output.i_gmask = 0x0000ff00;
227     p_vout->output.i_bmask = 0x00ff0000;
228     i_pixel_pitch = 3;
229 #   else
230     p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6');
231     p_vout->output.i_rmask = 0xf800;
232     p_vout->output.i_gmask = 0x07e0;
233     p_vout->output.i_bmask = 0x001f;
234     i_pixel_pitch = 2;
235 #   endif
236 #else
237     p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2');
238     p_vout->output.i_rmask = 0x000000ff;
239     p_vout->output.i_gmask = 0x0000ff00;
240     p_vout->output.i_bmask = 0x00ff0000;
241     i_pixel_pitch = 4;
242 #endif
243 #endif
244
245     /* Since OpenGL can do rescaling for us, stick to the default
246      * coordinates and aspect. */
247     p_vout->output.i_width  = p_vout->render.i_width;
248     p_vout->output.i_height = p_vout->render.i_height;
249     p_vout->output.i_aspect = p_vout->render.i_aspect;
250
251     /* We know the chroma, allocate one buffer which will be used
252      * directly by the decoder */
253     p_sys->pp_buffer[0] =
254         malloc( p_sys->i_tex_width * p_sys->i_tex_height * i_pixel_pitch );
255     if( !p_sys->pp_buffer[0] )
256     {
257         msg_Err( p_vout, "Out of memory" );
258         return -1;
259     }
260     p_sys->pp_buffer[1] =
261         malloc( p_sys->i_tex_width * p_sys->i_tex_height * i_pixel_pitch );
262     if( !p_sys->pp_buffer[1] )
263     {
264         msg_Err( p_vout, "Out of memory" );
265         return -1;
266     }
267
268     p_vout->p_picture[0].i_planes = 1;
269     p_vout->p_picture[0].p->p_pixels = p_sys->pp_buffer[0];
270     p_vout->p_picture[0].p->i_lines = p_vout->output.i_height;
271     p_vout->p_picture[0].p->i_pixel_pitch = i_pixel_pitch;
272     p_vout->p_picture[0].p->i_pitch = p_vout->output.i_width *
273         p_vout->p_picture[0].p->i_pixel_pitch;
274     p_vout->p_picture[0].p->i_visible_pitch = p_vout->output.i_width *
275         p_vout->p_picture[0].p->i_pixel_pitch;
276
277     p_vout->p_picture[0].i_status = DESTROYED_PICTURE;
278     p_vout->p_picture[0].i_type   = DIRECT_PICTURE;
279
280     PP_OUTPUTPICTURE[ 0 ] = &p_vout->p_picture[0];
281
282     I_OUTPUTPICTURES = 1;
283
284     glGenTextures( 2, p_sys->p_textures );
285
286     for( i_index = 0; i_index < 2; i_index++ )
287     {
288         glBindTexture( VLCGL_TARGET, p_sys->p_textures[i_index] );
289     
290         /* Set the texture parameters */
291         glTexParameterf( VLCGL_TARGET, GL_TEXTURE_PRIORITY, 1.0 );
292     
293         glTexParameteri( VLCGL_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
294         glTexParameteri( VLCGL_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
295     
296         glTexParameteri( VLCGL_TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP );
297         glTexParameteri( VLCGL_TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP );
298     
299         glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
300
301 #ifdef SYS_DARWIN
302         /* Tell the driver not to make a copy of the texture but to use
303            our buffer */
304         glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
305         glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
306     
307 #if 0
308         /* Use VRAM texturing */
309         glTexParameteri( VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
310                          GL_STORAGE_CACHED_APPLE );
311 #else
312         /* Use AGP texturing */
313         glTexParameteri( VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
314                          GL_STORAGE_SHARED_APPLE );
315 #endif
316 #endif
317
318         /* Call glTexImage2D only once, and use glTexSubImage2D later */
319         glTexImage2D( VLCGL_TARGET, 0, 3, p_sys->i_tex_width,
320                       p_sys->i_tex_height, 0, VLCGL_FORMAT, VLCGL_TYPE,
321                       p_sys->pp_buffer[i_index] );
322     }
323
324     glDisable(GL_BLEND);
325     glDisable(GL_DEPTH_TEST);
326     glDepthMask(GL_FALSE);
327     glDisable(GL_CULL_FACE);
328     glClear( GL_COLOR_BUFFER_BIT );
329
330     /* Check if the user asked for useless visual effects */
331     var_Get( p_vout, "opengl-effect", &val );
332     if( !val.psz_string || !strcmp( val.psz_string, "none" ))
333     {
334         p_sys->i_effect = OPENGL_EFFECT_NONE;
335     }
336     else if( !strcmp( val.psz_string, "cube" ) )
337     {
338         p_sys->i_effect = OPENGL_EFFECT_CUBE;
339
340         glEnable( GL_CULL_FACE);
341         //glEnable( GL_DEPTH_TEST );
342     }
343     else if( !strcmp( val.psz_string, "transparent-cube" ) )
344     {
345         p_sys->i_effect = OPENGL_EFFECT_TRANSPARENT_CUBE;
346
347         glDisable( GL_DEPTH_TEST );
348         glEnable( GL_BLEND );
349         glBlendFunc( GL_SRC_ALPHA, GL_ONE );
350     }
351     else
352     {
353         msg_Warn( p_vout, "no valid opengl effect provided, using "
354                   "\"none\"" );
355         p_sys->i_effect = OPENGL_EFFECT_NONE;
356     }
357     if( val.psz_string ) free( val.psz_string );
358
359     if( p_sys->i_effect & ( OPENGL_EFFECT_CUBE |
360                 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
361     {
362         /* Set the perpective */
363         glMatrixMode( GL_PROJECTION );
364         glLoadIdentity();
365         glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
366         glMatrixMode( GL_MODELVIEW );
367         glLoadIdentity();
368         glTranslatef( 0.0, 0.0, - 5.0 );
369     }
370
371     return 0;
372 }
373
374 /*****************************************************************************
375  * End: terminate GLX video thread output method
376  *****************************************************************************/
377 static void End( vout_thread_t *p_vout )
378 {
379     glFinish();
380     glFlush();
381 }
382
383 /*****************************************************************************
384  * Destroy: destroy GLX video thread output method
385  *****************************************************************************
386  * Terminate an output method created by CreateVout
387  *****************************************************************************/
388 static void DestroyVout( vlc_object_t *p_this )
389 {
390     vout_thread_t *p_vout = (vout_thread_t *)p_this;
391     vout_sys_t *p_sys = p_vout->p_sys;
392
393     module_Unneed( p_sys->p_vout, p_sys->p_vout->p_module );
394     vlc_object_detach( p_sys->p_vout );
395     vlc_object_destroy( p_sys->p_vout );
396
397     /* Free the texture buffer*/
398     if( p_sys->pp_buffer[0] ) free( p_sys->pp_buffer[0] );
399     if( p_sys->pp_buffer[1] ) free( p_sys->pp_buffer[1] );
400
401     free( p_sys );
402 }
403
404 /*****************************************************************************
405  * Manage: handle Sys events
406  *****************************************************************************
407  * This function should be called regularly by video output thread. It returns
408  * a non null value if an error occured.
409  *****************************************************************************/
410 static int Manage( vout_thread_t *p_vout )
411 {
412     vout_sys_t *p_sys = p_vout->p_sys;
413     return p_sys->p_vout->pf_manage( p_sys->p_vout );
414 }
415
416 /*****************************************************************************
417  * Render: render previously calculated output
418  *****************************************************************************/
419 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
420 {
421     vout_sys_t *p_sys = p_vout->p_sys;
422     float f_width, f_height;
423
424     /* glTexCoord works differently with GL_TEXTURE_2D and
425        GL_TEXTURE_RECTANGLE_EXT */
426 #ifdef SYS_DARWIN
427     f_width = (float)p_vout->output.i_width;
428     f_height = (float)p_vout->output.i_height;
429 #else
430     f_width = (float)p_vout->output.i_width / p_sys->i_tex_width;
431     f_height = (float)p_vout->output.i_height / p_sys->i_tex_height;
432 #endif
433
434     glClear( GL_COLOR_BUFFER_BIT );
435
436     /* On Win32/GLX, we do this the usual way:
437        + Fill the buffer with new content,
438        + Reload the texture,
439        + Use the texture.
440
441        On OS X with VRAM or AGP texturing, the order has to be:
442        + Reload the texture,
443        + Fill the buffer with new content,
444        + Use the texture.
445
446        (Thanks to gcc from the Arstechnica forums for the tip)
447
448        Therefore, we have to use two buffers and textures. On Win32/GLX,
449        we reload the texture to be displayed and use it right away. On
450        OS X, we first render, then reload the texture to be used next
451        time. */
452
453 #ifdef SYS_DARWIN
454     glBindTexture( VLCGL_TARGET, p_sys->p_textures[p_sys->i_index] );
455 #else
456
457     /* Update the texture */
458     glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0,
459                      p_vout->render.i_width, p_vout->render.i_height,
460                      VLCGL_RGB_FORMAT, VLCGL_RGB_TYPE, p_sys->pp_buffer[0] );
461 #endif
462
463     if( p_sys->i_effect == OPENGL_EFFECT_NONE )
464     {
465         glEnable( VLCGL_TARGET );
466         glBegin( GL_POLYGON );
467         glTexCoord2f( 0.0, 0.0 ); glVertex2f( -1.0, 1.0 );
468         glTexCoord2f( f_width, 0.0 ); glVertex2f( 1.0, 1.0 );
469         glTexCoord2f( f_width, f_height ); glVertex2f( 1.0, -1.0 );
470         glTexCoord2f( 0.0, f_height ); glVertex2f( -1.0, -1.0 );
471         glEnd();
472     }
473     else
474     {
475         glRotatef( 1.0, 0.3, 0.5, 0.7 );
476
477         glEnable( VLCGL_TARGET );
478         glBegin( GL_QUADS );
479
480         /* Front */
481         glTexCoord2f( 0, 0 ); glVertex3f( - 1.0, 1.0, 1.0 );
482         glTexCoord2f( 0, f_height ); glVertex3f( - 1.0, - 1.0, 1.0 );
483         glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, - 1.0, 1.0 );
484         glTexCoord2f( f_width, 0 ); glVertex3f( 1.0, 1.0, 1.0 );
485
486         /* Left */
487         glTexCoord2f( 0, 0 ); glVertex3f( - 1.0, 1.0, - 1.0 );
488         glTexCoord2f( 0, f_height ); glVertex3f( - 1.0, - 1.0, - 1.0 );
489         glTexCoord2f( f_width, f_height ); glVertex3f( - 1.0, - 1.0, 1.0 );
490         glTexCoord2f( f_width, 0 ); glVertex3f( - 1.0, 1.0, 1.0 );
491
492         /* Back */
493         glTexCoord2f( 0, 0 ); glVertex3f( 1.0, 1.0, - 1.0 );
494         glTexCoord2f( 0, f_height ); glVertex3f( 1.0, - 1.0, - 1.0 );
495         glTexCoord2f( f_width, f_height ); glVertex3f( - 1.0, - 1.0, - 1.0 );
496         glTexCoord2f( f_width, 0 ); glVertex3f( - 1.0, 1.0, - 1.0 );
497
498         /* Right */
499         glTexCoord2f( 0, 0 ); glVertex3f( 1.0, 1.0, 1.0 );
500         glTexCoord2f( 0, f_height ); glVertex3f( 1.0, - 1.0, 1.0 );
501         glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, - 1.0, - 1.0 );
502         glTexCoord2f( f_width, 0 ); glVertex3f( 1.0, 1.0, - 1.0 );
503
504         /* Top */
505         glTexCoord2f( 0, 0 ); glVertex3f( - 1.0, 1.0, - 1.0 );
506         glTexCoord2f( 0, f_height ); glVertex3f( - 1.0, 1.0, 1.0 );
507         glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, 1.0, 1.0 );
508         glTexCoord2f( f_width, 0 ); glVertex3f( 1.0, 1.0, - 1.0 );
509
510         /* Bottom */
511         glTexCoord2f( 0, 0 ); glVertex3f( - 1.0, - 1.0, 1.0 );
512         glTexCoord2f( 0, f_height ); glVertex3f( - 1.0, - 1.0, - 1.0 );
513         glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, - 1.0, - 1.0 );
514         glTexCoord2f( f_width, 0 ); glVertex3f( 1.0, - 1.0, 1.0 );
515         glEnd();
516     }
517
518     glDisable( VLCGL_TARGET );
519
520 #ifdef SYS_DARWIN
521     /* Switch buffers */
522     p_sys->i_index = ( p_sys->i_index + 1 ) & 1;
523     p_pic->p->p_pixels = p_sys->pp_buffer[p_sys->i_index];
524
525     /* Update the texture */
526     glBindTexture( VLCGL_TARGET, p_sys->p_textures[p_sys->i_index] );
527     glTexSubImage2D( VLCGL_TARGET, 0, 0, 0, p_sys->i_tex_width,
528                      p_sys->i_tex_height, VLCGL_FORMAT, VLCGL_TYPE,
529                      p_sys->pp_buffer[p_sys->i_index] );
530 #endif
531 }
532
533 /*****************************************************************************
534  * DisplayVideo: displays previously rendered output
535  *****************************************************************************/
536 static void DisplayVideo( vout_thread_t *p_vout, picture_t *p_pic )
537 {
538     vout_sys_t *p_sys = p_vout->p_sys;
539     p_sys->p_vout->pf_swap( p_sys->p_vout );
540 }
541
542 int GetAlignedSize( int i_size )
543 {
544     /* Return the nearest power of 2 */
545     int i_result = 1;
546     while( i_result < i_size )
547     {
548         i_result *= 2;
549     }
550     return i_result;
551 }
552
553 /*****************************************************************************
554  * Control: control facility for the vout
555  *****************************************************************************/
556 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
557 {
558     vout_sys_t *p_sys = p_vout->p_sys;
559
560     if( p_sys->p_vout->pf_control )
561         return p_sys->p_vout->pf_control( p_sys->p_vout, i_query, args );
562     else
563         return vout_vaControlDefault( p_vout, i_query, args );
564 }
565
566 /*****************************************************************************
567  * SendEvents: forward mouse and keyboard events to the parent p_vout
568  *****************************************************************************/
569 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
570                        vlc_value_t oldval, vlc_value_t newval, void *_p_vout )
571 {
572     return var_Set( (vlc_object_t *)_p_vout, psz_var, newval );
573 }