]> git.sesse.net Git - vlc/blob - src/video_output/vout_intf.c
aad3799036d82342eea27daa0eee9714e2f47a8f
[vlc] / src / video_output / vout_intf.c
1 /*****************************************************************************
2  * vout_intf.c : video output interface
3  *****************************************************************************
4  * Copyright (C) 2000-2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #include <vlc/vlc.h>
29
30 #include <stdio.h>
31 #include <stdlib.h>                                                /* free() */
32 #include <sys/types.h>                                          /* opendir() */
33 #include <dirent.h>                                             /* opendir() */
34
35 #include <vlc_interface.h>
36 #include <vlc_block.h>
37 #include <vlc_playlist.h>
38
39 #include <vlc_vout.h>
40 #include <vlc_image.h>
41 #include <vlc_osd.h>
42
43 #include <vlc_strings.h>
44 #include <vlc_charset.h>
45
46 /*****************************************************************************
47  * Local prototypes
48  *****************************************************************************/
49 static void InitWindowSize( vout_thread_t *, unsigned *, unsigned * );
50
51 /* Object variables callbacks */
52 static int ZoomCallback( vlc_object_t *, char const *,
53                          vlc_value_t, vlc_value_t, void * );
54 static int CropCallback( vlc_object_t *, char const *,
55                          vlc_value_t, vlc_value_t, void * );
56 static int AspectCallback( vlc_object_t *, char const *,
57                            vlc_value_t, vlc_value_t, void * );
58 static int OnTopCallback( vlc_object_t *, char const *,
59                           vlc_value_t, vlc_value_t, void * );
60 static int FullscreenCallback( vlc_object_t *, char const *,
61                                vlc_value_t, vlc_value_t, void * );
62 static int SnapshotCallback( vlc_object_t *, char const *,
63                              vlc_value_t, vlc_value_t, void * );
64
65 /*****************************************************************************
66  * vout_RequestWindow: Create/Get a video window if possible.
67  *****************************************************************************
68  * This function looks for the main interface and tries to request
69  * a new video window. If it fails then the vout will still need to create the
70  * window by itself.
71  *****************************************************************************/
72 void *vout_RequestWindow( vout_thread_t *p_vout,
73                           int *pi_x_hint, int *pi_y_hint,
74                           unsigned int *pi_width_hint,
75                           unsigned int *pi_height_hint )
76 {
77     intf_thread_t *p_intf = NULL;
78     vlc_list_t *p_list;
79     void *p_window;
80     vlc_value_t val;
81     int i;
82
83     /* Small kludge */
84     if( !var_Type( p_vout, "aspect-ratio" ) ) vout_IntfInit( p_vout );
85
86     /* Get requested coordinates */
87     var_Get( p_vout, "video-x", &val );
88     *pi_x_hint = val.i_int ;
89     var_Get( p_vout, "video-y", &val );
90     *pi_y_hint = val.i_int;
91
92     *pi_width_hint = p_vout->i_window_width;
93     *pi_height_hint = p_vout->i_window_height;
94
95     /* Check whether someone provided us with a window ID */
96     var_Get( p_vout->p_libvlc, "drawable", &val );
97     if( val.i_int ) return (void *)val.i_int;
98
99     /* Find if the main interface supports embedding */
100     p_list = vlc_list_find( p_vout, VLC_OBJECT_INTF, FIND_ANYWHERE );
101     if( !p_list ) return NULL;
102
103     for( i = 0; i < p_list->i_count; i++ )
104     {
105         p_intf = (intf_thread_t *)p_list->p_values[i].p_object;
106         if( p_intf->b_block && p_intf->pf_request_window ) break;
107         p_intf = NULL;
108     }
109
110     if( !p_intf )
111     {
112         vlc_list_release( p_list );
113         return NULL;
114     }
115
116     vlc_object_yield( p_intf );
117     vlc_list_release( p_list );
118
119     p_window = p_intf->pf_request_window( p_intf, p_vout, pi_x_hint, pi_y_hint,
120                                           pi_width_hint, pi_height_hint );
121
122     if( !p_window ) vlc_object_release( p_intf );
123     else p_vout->p_parent_intf = p_intf;
124
125     return p_window;
126 }
127
128 void vout_ReleaseWindow( vout_thread_t *p_vout, void *p_window )
129 {
130     intf_thread_t *p_intf = p_vout->p_parent_intf;
131
132     if( !p_intf ) return;
133
134     vlc_mutex_lock( &p_intf->object_lock );
135     if( p_intf->b_dead )
136     {
137         vlc_mutex_unlock( &p_intf->object_lock );
138         return;
139     }
140
141     if( !p_intf->pf_release_window )
142     {
143         msg_Err( p_vout, "no pf_release_window");
144         vlc_mutex_unlock( &p_intf->object_lock );
145         vlc_object_release( p_intf );
146         return;
147     }
148
149     p_intf->pf_release_window( p_intf, p_window );
150
151     p_vout->p_parent_intf = NULL;
152     vlc_mutex_unlock( &p_intf->object_lock );
153     vlc_object_release( p_intf );
154 }
155
156 int vout_ControlWindow( vout_thread_t *p_vout, void *p_window,
157                         int i_query, va_list args )
158 {
159     intf_thread_t *p_intf = p_vout->p_parent_intf;
160     int i_ret;
161
162     if( !p_intf ) return VLC_EGENERIC;
163
164     vlc_mutex_lock( &p_intf->object_lock );
165     if( p_intf->b_dead )
166     {
167         vlc_mutex_unlock( &p_intf->object_lock );
168         return VLC_EGENERIC;
169     }
170
171     if( !p_intf->pf_control_window )
172     {
173         msg_Err( p_vout, "no pf_control_window");
174         vlc_mutex_unlock( &p_intf->object_lock );
175         return VLC_EGENERIC;
176     }
177
178     i_ret = p_intf->pf_control_window( p_intf, p_window, i_query, args );
179     vlc_mutex_unlock( &p_intf->object_lock );
180     return i_ret;
181 }
182
183 /*****************************************************************************
184  * vout_IntfInit: called during the vout creation to initialise misc things.
185  *****************************************************************************/
186 void vout_IntfInit( vout_thread_t *p_vout )
187 {
188     vlc_value_t val, text, old_val;
189     vlc_bool_t b_force_par = VLC_FALSE;
190     char *psz_buf;
191
192     /* Create a few object variables we'll need later on */
193     var_Create( p_vout, "snapshot-path", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
194     var_Create( p_vout, "snapshot-prefix", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
195     var_Create( p_vout, "snapshot-format", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
196     var_Create( p_vout, "snapshot-preview", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
197     var_Create( p_vout, "snapshot-sequential",
198                 VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
199     var_Create( p_vout, "snapshot-num", VLC_VAR_INTEGER );
200     var_SetInteger( p_vout, "snapshot-num", 1 );
201
202     var_Create( p_vout, "width", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
203     var_Create( p_vout, "height", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
204     var_Create( p_vout, "align", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
205     var_Get( p_vout, "align", &val );
206     p_vout->i_alignment = val.i_int;
207
208     var_Create( p_vout, "video-x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
209     var_Create( p_vout, "video-y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
210
211     /* Zoom object var */
212     var_Create( p_vout, "zoom", VLC_VAR_FLOAT | VLC_VAR_ISCOMMAND |
213                 VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT );
214
215     text.psz_string = _("Zoom");
216     var_Change( p_vout, "zoom", VLC_VAR_SETTEXT, &text, NULL );
217
218     var_Get( p_vout, "zoom", &old_val );
219     if( old_val.f_float == 0.25 ||
220         old_val.f_float == 0.5 ||
221         old_val.f_float == 1 ||
222         old_val.f_float == 2 )
223     {
224         var_Change( p_vout, "zoom", VLC_VAR_DELCHOICE, &old_val, NULL );
225     }
226
227     val.f_float = 0.25; text.psz_string = _("1:4 Quarter");
228     var_Change( p_vout, "zoom", VLC_VAR_ADDCHOICE, &val, &text );
229     val.f_float = 0.5; text.psz_string = _("1:2 Half");
230     var_Change( p_vout, "zoom", VLC_VAR_ADDCHOICE, &val, &text );
231     val.f_float = 1; text.psz_string = _("1:1 Original");
232     var_Change( p_vout, "zoom", VLC_VAR_ADDCHOICE, &val, &text );
233     val.f_float = 2; text.psz_string = _("2:1 Double");
234     var_Change( p_vout, "zoom", VLC_VAR_ADDCHOICE, &val, &text );
235
236     var_Set( p_vout, "zoom", old_val );
237
238     var_AddCallback( p_vout, "zoom", ZoomCallback, NULL );
239
240     /* Crop offset vars */
241     var_Create( p_vout, "crop-left", VLC_VAR_INTEGER );
242     var_Create( p_vout, "crop-top", VLC_VAR_INTEGER );
243     var_Create( p_vout, "crop-right", VLC_VAR_INTEGER );
244     var_Create( p_vout, "crop-bottom", VLC_VAR_INTEGER );
245
246     var_SetInteger( p_vout, "crop-left", 0 );
247     var_SetInteger( p_vout, "crop-top", 0 );
248     var_SetInteger( p_vout, "crop-right", 0 );
249     var_SetInteger( p_vout, "crop-bottom", 0 );
250
251     var_AddCallback( p_vout, "crop-left", CropCallback, NULL );
252     var_AddCallback( p_vout, "crop-top", CropCallback, NULL );
253     var_AddCallback( p_vout, "crop-right", CropCallback, NULL );
254     var_AddCallback( p_vout, "crop-bottom", CropCallback, NULL );
255
256     /* Crop object var */
257     var_Create( p_vout, "crop", VLC_VAR_STRING |
258                 VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT );
259
260     text.psz_string = _("Crop");
261     var_Change( p_vout, "crop", VLC_VAR_SETTEXT, &text, NULL );
262
263     val.psz_string = "";
264     var_Change( p_vout, "crop", VLC_VAR_DELCHOICE, &val, 0 );
265     val.psz_string = ""; text.psz_string = _("Default");
266     var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
267     val.psz_string = "1:1"; text.psz_string = "1:1";
268     var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
269     val.psz_string = "4:3"; text.psz_string = "4:3";
270     var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
271     val.psz_string = "16:9"; text.psz_string = "16:9";
272     var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
273     val.psz_string = "16:10"; text.psz_string = "16:10";
274     var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
275     val.psz_string = "221:100"; text.psz_string = "221:100";
276     var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
277     val.psz_string = "5:4"; text.psz_string = "5:4";
278     var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
279
280     /* Add custom crop ratios */
281     psz_buf = config_GetPsz( p_vout, "custom-crop-ratios" );
282     if( psz_buf && *psz_buf )
283     {
284         char *psz_cur = psz_buf;
285         char *psz_next;
286         while( psz_cur && *psz_cur )
287         {
288             psz_next = strchr( psz_cur, ',' );
289             if( psz_next )
290             {
291                 *psz_next = '\0';
292                 psz_next++;
293             }
294             val.psz_string = strdup( psz_cur );
295             text.psz_string = strdup( psz_cur );
296             var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text);
297             free( val.psz_string );
298             free( text.psz_string );
299             psz_cur = psz_next;
300         }
301     }
302     if( psz_buf ) free( psz_buf );
303
304     var_AddCallback( p_vout, "crop", CropCallback, NULL );
305     var_Get( p_vout, "crop", &old_val );
306     if( old_val.psz_string && *old_val.psz_string )
307         var_Change( p_vout, "crop", VLC_VAR_TRIGGER_CALLBACKS, 0, 0 );
308     if( old_val.psz_string ) free( old_val.psz_string );
309
310     /* Monitor pixel aspect-ratio */
311     var_Create( p_vout, "monitor-par", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
312     var_Get( p_vout, "monitor-par", &val );
313     if( val.psz_string && *val.psz_string )
314     {
315         char *psz_parser = strchr( val.psz_string, ':' );
316         unsigned int i_aspect_num = 0, i_aspect_den = 0;
317         float i_aspect = 0;
318         if( psz_parser )
319         {
320             i_aspect_num = strtol( val.psz_string, 0, 10 );
321             i_aspect_den = strtol( ++psz_parser, 0, 10 );
322         }
323         else
324         {
325             i_aspect = atof( val.psz_string );
326             vlc_ureduce( &i_aspect_num, &i_aspect_den,
327                          i_aspect *VOUT_ASPECT_FACTOR, VOUT_ASPECT_FACTOR, 0 );
328         }
329         if( !i_aspect_num || !i_aspect_den ) i_aspect_num = i_aspect_den = 1;
330
331         p_vout->i_par_num = i_aspect_num;
332         p_vout->i_par_den = i_aspect_den;
333
334         vlc_ureduce( &p_vout->i_par_num, &p_vout->i_par_den,
335                      p_vout->i_par_num, p_vout->i_par_den, 0 );
336
337         msg_Dbg( p_vout, "overriding monitor pixel aspect-ratio: %i:%i",
338                  p_vout->i_par_num, p_vout->i_par_den );
339         b_force_par = VLC_TRUE;
340     }
341     if( val.psz_string ) free( val.psz_string );
342
343     /* Aspect-ratio object var */
344     var_Create( p_vout, "aspect-ratio", VLC_VAR_STRING |
345                 VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT );
346
347     text.psz_string = _("Aspect-ratio");
348     var_Change( p_vout, "aspect-ratio", VLC_VAR_SETTEXT, &text, NULL );
349
350     val.psz_string = "";
351     var_Change( p_vout, "aspect-ratio", VLC_VAR_DELCHOICE, &val, 0 );
352     val.psz_string = ""; text.psz_string = _("Default");
353     var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
354     val.psz_string = "1:1"; text.psz_string = "1:1";
355     var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
356     val.psz_string = "4:3"; text.psz_string = "4:3";
357     var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
358     val.psz_string = "16:9"; text.psz_string = "16:9";
359     var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
360     val.psz_string = "16:10"; text.psz_string = "16:10";
361     var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
362     val.psz_string = "221:100"; text.psz_string = "221:100";
363     var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
364     val.psz_string = "5:4"; text.psz_string = "5:4";
365     var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
366
367     /* Add custom aspect ratios */
368     psz_buf = config_GetPsz( p_vout, "custom-aspect-ratios" );
369     if( psz_buf && *psz_buf )
370     {
371         char *psz_cur = psz_buf;
372         char *psz_next;
373         while( psz_cur && *psz_cur )
374         {
375             psz_next = strchr( psz_cur, ',' );
376             if( psz_next )
377             {
378                 *psz_next = '\0';
379                 psz_next++;
380             }
381             val.psz_string = strdup( psz_cur );
382             text.psz_string = strdup( psz_cur );
383             var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text);
384             free( val.psz_string );
385             free( text.psz_string );
386             psz_cur = psz_next;
387         }
388     }
389     if( psz_buf ) free( psz_buf );
390
391     var_AddCallback( p_vout, "aspect-ratio", AspectCallback, NULL );
392     var_Get( p_vout, "aspect-ratio", &old_val );
393     if( (old_val.psz_string && *old_val.psz_string) || b_force_par )
394         var_Change( p_vout, "aspect-ratio", VLC_VAR_TRIGGER_CALLBACKS, 0, 0 );
395     if( old_val.psz_string ) free( old_val.psz_string );
396
397     /* Initialize the dimensions of the video window */
398     InitWindowSize( p_vout, &p_vout->i_window_width,
399                     &p_vout->i_window_height );
400
401     /* Add a variable to indicate if the window should be on top of others */
402     var_Create( p_vout, "video-on-top", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
403     text.psz_string = _("Always on top");
404     var_Change( p_vout, "video-on-top", VLC_VAR_SETTEXT, &text, NULL );
405     var_AddCallback( p_vout, "video-on-top", OnTopCallback, NULL );
406
407     /* Add a variable to indicate whether we want window decoration or not */
408     var_Create( p_vout, "video-deco", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
409
410     /* Add a fullscreen variable */
411     var_Create( p_vout, "fullscreen", VLC_VAR_BOOL );
412     text.psz_string = _("Fullscreen");
413     var_Change( p_vout, "fullscreen", VLC_VAR_SETTEXT, &text, NULL );
414     var_Change( p_vout, "fullscreen", VLC_VAR_INHERITVALUE, &val, NULL );
415     if( val.b_bool )
416     {
417         /* user requested fullscreen */
418         p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
419     }
420     var_AddCallback( p_vout, "fullscreen", FullscreenCallback, NULL );
421
422     /* Add a snapshot variable */
423     var_Create( p_vout, "video-snapshot", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
424     text.psz_string = _("Snapshot");
425     var_Change( p_vout, "video-snapshot", VLC_VAR_SETTEXT, &text, NULL );
426     var_AddCallback( p_vout, "video-snapshot", SnapshotCallback, NULL );
427
428     /* Mouse coordinates */
429     var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER );
430     var_Create( p_vout, "mouse-y", VLC_VAR_INTEGER );
431     var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
432     var_Create( p_vout, "mouse-moved", VLC_VAR_BOOL );
433     var_Create( p_vout, "mouse-clicked", VLC_VAR_INTEGER );
434
435     var_Create( p_vout, "intf-change", VLC_VAR_BOOL );
436     val.b_bool = VLC_TRUE;
437     var_Set( p_vout, "intf-change", val );
438 }
439
440 /*****************************************************************************
441  * vout_Snapshot: generates a snapshot.
442  *****************************************************************************/
443 int vout_Snapshot( vout_thread_t *p_vout, picture_t *p_pic )
444 {
445     image_handler_t *p_image = image_HandlerCreate( p_vout );
446     video_format_t fmt_in = {0}, fmt_out = {0};
447     char *psz_filename = NULL;
448     subpicture_t *p_subpic;
449     picture_t *p_pif;
450     vlc_value_t val, format;
451     DIR *path;
452
453     int i_ret;
454
455     var_Get( p_vout, "snapshot-path", &val );
456     if( val.psz_string && !*val.psz_string )
457     {
458         free( val.psz_string );
459         val.psz_string = 0;
460     }
461
462     /* Embedded snapshot : if snapshot-path == object:object-id, then
463        create a snapshot_t* and store it in
464        object(object-id)->p_private, then unlock and signal the
465        waiting object.
466      */
467     if( val.psz_string && !strncmp( val.psz_string, "object:", 7 ) )
468     {
469         int i_id;
470         vlc_object_t* p_dest;
471         block_t *p_block;
472         snapshot_t *p_snapshot;
473         int i_size;
474
475         /* Destination object-id is following object: */
476         i_id = atoi( &val.psz_string[7] );
477         p_dest = ( vlc_object_t* )vlc_current_object( i_id );
478         if( !p_dest )
479         {
480             msg_Err( p_vout, "Cannot find calling object" );
481             image_HandlerDelete( p_image );
482             return VLC_EGENERIC;
483         }
484         /* Object must be locked. We will unlock it once we get the
485            snapshot and written it to p_private */
486         p_dest->p_private = NULL;
487
488         /* Save the snapshot to a memory zone */
489         fmt_in = p_vout->fmt_in;
490         fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
491         /* FIXME: should not be hardcoded. We should be able to
492         specify the snapshot size (snapshot-width and snapshot-height). */
493         fmt_out.i_width = 320;
494         fmt_out.i_height = 200;
495         fmt_out.i_chroma = VLC_FOURCC( 'p','n','g',' ' );
496         p_block = ( block_t* ) image_Write( p_image, p_pic, &fmt_in, &fmt_out );
497         if( !p_block )
498         {
499             msg_Err( p_vout, "Could not get snapshot" );
500             image_HandlerDelete( p_image );
501             vlc_cond_signal( &p_dest->object_wait );
502             vlc_mutex_unlock( &p_dest->object_lock );
503             vlc_object_release( p_dest );
504             return VLC_EGENERIC;
505         }
506
507         /* Copy the p_block data to a snapshot structure */
508         /* FIXME: get the timestamp */
509         p_snapshot = ( snapshot_t* ) malloc( sizeof( snapshot_t ) );
510         if( !p_snapshot )
511         {
512             block_Release( p_block );
513             image_HandlerDelete( p_image );
514             vlc_cond_signal( &p_dest->object_wait );
515             vlc_mutex_unlock( &p_dest->object_lock );
516             vlc_object_release( p_dest );
517             return VLC_ENOMEM;
518         }
519
520         i_size = p_block->i_buffer;
521
522         p_snapshot->i_width = fmt_out.i_width;
523         p_snapshot->i_height = fmt_out.i_height;
524         p_snapshot->i_datasize = i_size;
525         p_snapshot->date = p_block->i_pts; /* FIXME ?? */
526         p_snapshot->p_data = ( char* ) malloc( i_size );
527         if( !p_snapshot->p_data )
528         {
529             block_Release( p_block );
530             free( p_snapshot );
531             image_HandlerDelete( p_image );
532             vlc_cond_signal( &p_dest->object_wait );
533             vlc_mutex_unlock( &p_dest->object_lock );
534             vlc_object_release( p_dest );
535             return VLC_ENOMEM;
536         }
537         memcpy( p_snapshot->p_data, p_block->p_buffer, p_block->i_buffer );
538
539         p_dest->p_private = p_snapshot;
540
541         block_Release( p_block );
542
543         /* Unlock the object */
544         vlc_cond_signal( &p_dest->object_wait );
545         vlc_mutex_unlock( &p_dest->object_lock );
546         vlc_object_release( p_dest );
547
548         image_HandlerDelete( p_image );
549         return VLC_SUCCESS;
550     }
551
552
553 #if defined(__APPLE__) || defined(SYS_BEOS)
554     if( !val.psz_string && p_vout->p_libvlc->psz_homedir )
555     {
556         asprintf( &val.psz_string, "%s/Desktop",
557                   p_vout->p_libvlc->psz_homedir );
558     }
559
560 #elif defined(WIN32) && !defined(UNDER_CE)
561     if( !val.psz_string && p_vout->p_libvlc->psz_homedir )
562     {
563         /* Get the My Pictures folder path */
564
565         char *p_mypicturesdir = NULL;
566         typedef HRESULT (WINAPI *SHGETFOLDERPATH)( HWND, int, HANDLE, DWORD,
567                                                    LPWSTR );
568         #ifndef CSIDL_FLAG_CREATE
569         #   define CSIDL_FLAG_CREATE 0x8000
570         #endif
571         #ifndef CSIDL_MYPICTURES
572         #   define CSIDL_MYPICTURES 0x27
573         #endif
574         #ifndef SHGFP_TYPE_CURRENT
575         #   define SHGFP_TYPE_CURRENT 0
576         #endif
577
578         HINSTANCE shfolder_dll;
579         SHGETFOLDERPATH SHGetFolderPath ;
580
581         /* load the shfolder dll to retrieve SHGetFolderPath */
582         if( ( shfolder_dll = LoadLibrary( _T("SHFolder.dll") ) ) != NULL )
583         {
584            wchar_t wdir[PATH_MAX];
585            SHGetFolderPath = (void *)GetProcAddress( shfolder_dll,
586                                                       _T("SHGetFolderPathW") );
587             if ((SHGetFolderPath != NULL )
588              && SUCCEEDED (SHGetFolderPath (NULL,
589                                            CSIDL_MYPICTURES | CSIDL_FLAG_CREATE,
590                                            NULL, SHGFP_TYPE_CURRENT,
591                                            wdir)))
592                 p_mypicturesdir = FromWide (wdir);
593
594             FreeLibrary( shfolder_dll );
595         }
596
597         if( p_mypicturesdir == NULL )
598         {
599             asprintf( &val.psz_string, "%s/" CONFIG_DIR,
600                       p_vout->p_libvlc->psz_homedir );
601         }
602         else
603         {
604             asprintf( &val.psz_string, p_mypicturesdir );
605             free( p_mypicturesdir );
606         }
607     }
608
609 #else
610     if( !val.psz_string && p_vout->p_libvlc->psz_homedir )
611     {
612         asprintf( &val.psz_string, "%s/" CONFIG_DIR,
613                   p_vout->p_libvlc->psz_homedir );
614     }
615 #endif
616
617     if( !val.psz_string )
618     {
619         msg_Err( p_vout, "no path specified for snapshots" );
620         return VLC_EGENERIC;
621     }
622     var_Get( p_vout, "snapshot-format", &format );
623     if( !format.psz_string || !*format.psz_string )
624     {
625         if( format.psz_string ) free( format.psz_string );
626         format.psz_string = strdup( "png" );
627     }
628
629     /*
630      * Did the user specify a directory? If not, path = NULL.
631      */
632     path = utf8_opendir ( (const char *)val.psz_string  );
633
634     if ( path != NULL )
635     {
636         char *psz_prefix = var_GetString( p_vout, "snapshot-prefix" );
637         if( !psz_prefix ) psz_prefix = strdup( "vlcsnap-" );
638
639         closedir( path );
640         if( var_GetBool( p_vout, "snapshot-sequential" ) == VLC_TRUE )
641         {
642             int i_num = var_GetInteger( p_vout, "snapshot-num" );
643             FILE *p_file;
644             do
645             {
646                 asprintf( &psz_filename, "%s/%s%05d.%s", val.psz_string,
647                           psz_prefix, i_num++, format.psz_string );
648             }
649             while( ( p_file = utf8_fopen( psz_filename, "r" ) ) && !fclose( p_file ) );
650             var_SetInteger( p_vout, "snapshot-num", i_num );
651         }
652         else
653         {
654             asprintf( &psz_filename, "%s/%s%u.%s", val.psz_string,
655                       psz_prefix,
656                       (unsigned int)(p_pic->date / 100000) & 0xFFFFFF,
657                       format.psz_string );
658         }
659
660         free( psz_prefix );
661     }
662     else // The user specified a full path name (including file name)
663     {
664         psz_filename = str_format_meta( p_vout, val.psz_string );
665     }
666
667     free( val.psz_string );
668     free( format.psz_string );
669
670     /* Save the snapshot */
671     fmt_in = p_vout->fmt_in;
672     fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
673     i_ret = image_WriteUrl( p_image, p_pic, &fmt_in, &fmt_out, psz_filename );
674     if( i_ret != VLC_SUCCESS )
675     {
676         msg_Err( p_vout, "could not create snapshot %s", psz_filename );
677         free( psz_filename );
678         image_HandlerDelete( p_image );
679         return VLC_EGENERIC;
680     }
681
682     msg_Dbg( p_vout, "snapshot taken (%s)", psz_filename );
683     vout_OSDMessage( VLC_OBJECT( p_vout ), DEFAULT_CHAN,
684                      "%s", psz_filename );
685     free( psz_filename );
686
687     if( var_GetBool( p_vout, "snapshot-preview" ) )
688     {
689         /* Inject a subpicture with the snapshot */
690         memset( &fmt_out, 0, sizeof(fmt_out) );
691         fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
692         p_pif = image_Convert( p_image, p_pic, &fmt_in, &fmt_out );
693         image_HandlerDelete( p_image );
694         if( !p_pif ) return VLC_EGENERIC;
695
696         p_subpic = spu_CreateSubpicture( p_vout->p_spu );
697         if( p_subpic == NULL )
698         {
699              p_pif->pf_release( p_pif );
700              return VLC_EGENERIC;
701         }
702
703         p_subpic->i_channel = 0;
704         p_subpic->i_start = mdate();
705         p_subpic->i_stop = mdate() + 4000000;
706         p_subpic->b_ephemer = VLC_TRUE;
707         p_subpic->b_fade = VLC_TRUE;
708         p_subpic->i_original_picture_width = p_vout->render.i_width * 4;
709         p_subpic->i_original_picture_height = p_vout->render.i_height * 4;
710
711         p_subpic->p_region = spu_CreateRegion( p_vout->p_spu, &fmt_out );
712         vout_CopyPicture( p_image->p_parent, &p_subpic->p_region->picture,
713                           p_pif );
714         p_pif->pf_release( p_pif );
715
716         spu_DisplaySubpicture( p_vout->p_spu, p_subpic );
717     }
718     else
719     {
720         image_HandlerDelete( p_image );
721     }
722
723     return VLC_SUCCESS;
724 }
725
726 /*****************************************************************************
727  * Handle filters
728  *****************************************************************************/
729
730 void vout_EnableFilter( vout_thread_t *p_vout, char *psz_name,
731                         vlc_bool_t b_add, vlc_bool_t b_setconfig )
732 {
733     char *psz_parser;
734     char *psz_string = config_GetPsz( p_vout, "vout-filter" );
735
736     /* Todo : Use some generic chain manipulation functions */
737     if( !psz_string ) psz_string = strdup("");
738
739     psz_parser = strstr( psz_string, psz_name );
740     if( b_add )
741     {
742         if( !psz_parser )
743         {
744             psz_parser = psz_string;
745             asprintf( &psz_string, (*psz_string) ? "%s:%s" : "%s%s",
746                             psz_string, psz_name );
747             free( psz_parser );
748         }
749         else
750             return;
751     }
752     else
753     {
754         if( psz_parser )
755         {
756             memmove( psz_parser, psz_parser + strlen(psz_name) +
757                             (*(psz_parser + strlen(psz_name)) == ':' ? 1 : 0 ),
758                             strlen(psz_parser + strlen(psz_name)) + 1 );
759
760             /* Remove trailing : : */
761             if( *(psz_string+strlen(psz_string ) -1 ) == ':' )
762             {
763                 *(psz_string+strlen(psz_string ) -1 ) = '\0';
764             }
765          }
766          else
767          {
768              free( psz_string );
769              return;
770          }
771     }
772     if( b_setconfig )
773         config_PutPsz( p_vout, "vout-filter", psz_string );
774
775     var_SetString( p_vout, "vout-filter", psz_string );
776     free( psz_string );
777 }
778
779 /*****************************************************************************
780  * vout_ControlDefault: default methods for video output control.
781  *****************************************************************************/
782 int vout_vaControlDefault( vout_thread_t *p_vout, int i_query, va_list args )
783 {
784     switch( i_query )
785     {
786     case VOUT_REPARENT:
787     case VOUT_CLOSE:
788         if( p_vout->p_parent_intf )
789         {
790             vlc_object_release( p_vout->p_parent_intf );
791             p_vout->p_parent_intf = NULL;
792         }
793         return VLC_SUCCESS;
794         break;
795
796     case VOUT_SNAPSHOT:
797         p_vout->b_snapshot = VLC_TRUE;
798         return VLC_SUCCESS;
799         break;
800
801     default:
802         msg_Dbg( p_vout, "control query not supported" );
803         return VLC_EGENERIC;
804     }
805 }
806
807 /*****************************************************************************
808  * InitWindowSize: find the initial dimensions the video window should have.
809  *****************************************************************************
810  * This function will check the "width", "height" and "zoom" config options and
811  * will calculate the size that the video window should have.
812  *****************************************************************************/
813 static void InitWindowSize( vout_thread_t *p_vout, unsigned *pi_width,
814                             unsigned *pi_height )
815 {
816     vlc_value_t val;
817     int i_width, i_height;
818     uint64_t ll_zoom;
819
820 #define FP_FACTOR 1000                             /* our fixed point factor */
821
822     var_Get( p_vout, "width", &val );
823     i_width = val.i_int;
824     var_Get( p_vout, "height", &val );
825     i_height = val.i_int;
826     var_Get( p_vout, "zoom", &val );
827     ll_zoom = (uint64_t)( FP_FACTOR * val.f_float );
828
829     if( i_width > 0 && i_height > 0)
830     {
831         *pi_width = (int)( i_width * ll_zoom / FP_FACTOR );
832         *pi_height = (int)( i_height * ll_zoom / FP_FACTOR );
833         goto initwsize_end;
834     }
835     else if( i_width > 0 )
836     {
837         *pi_width = (int)( i_width * ll_zoom / FP_FACTOR );
838         *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom *
839             p_vout->fmt_in.i_sar_den * i_width / p_vout->fmt_in.i_sar_num /
840             FP_FACTOR / p_vout->fmt_in.i_visible_width );
841         goto initwsize_end;
842     }
843     else if( i_height > 0 )
844     {
845         *pi_height = (int)( i_height * ll_zoom / FP_FACTOR );
846         *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom *
847             p_vout->fmt_in.i_sar_num * i_height / p_vout->fmt_in.i_sar_den /
848             FP_FACTOR / p_vout->fmt_in.i_visible_height );
849         goto initwsize_end;
850     }
851
852     if( p_vout->fmt_in.i_sar_num >= p_vout->fmt_in.i_sar_den )
853     {
854         *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom *
855             p_vout->fmt_in.i_sar_num / p_vout->fmt_in.i_sar_den / FP_FACTOR );
856         *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom 
857             / FP_FACTOR );
858     }
859     else
860     {
861         *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom 
862             / FP_FACTOR );
863         *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom *
864             p_vout->fmt_in.i_sar_den / p_vout->fmt_in.i_sar_num / FP_FACTOR );
865     }
866
867 initwsize_end:
868     msg_Dbg( p_vout, "window size: %dx%d", p_vout->i_window_width, 
869              p_vout->i_window_height );
870
871 #undef FP_FACTOR
872 }
873
874 /*****************************************************************************
875  * Object variables callbacks
876  *****************************************************************************/
877 static int ZoomCallback( vlc_object_t *p_this, char const *psz_cmd,
878                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
879 {
880     vout_thread_t *p_vout = (vout_thread_t *)p_this;
881     InitWindowSize( p_vout, &p_vout->i_window_width,
882                     &p_vout->i_window_height );
883     vout_Control( p_vout, VOUT_SET_SIZE, p_vout->i_window_width,
884                   p_vout->i_window_height );
885     return VLC_SUCCESS;
886 }
887
888 static int CropCallback( vlc_object_t *p_this, char const *psz_cmd,
889                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
890 {
891     vout_thread_t *p_vout = (vout_thread_t *)p_this;
892     int64_t i_aspect_num, i_aspect_den;
893     unsigned int i_width, i_height;
894
895     /* Restore defaults */
896     p_vout->fmt_in.i_x_offset = p_vout->fmt_render.i_x_offset;
897     p_vout->fmt_in.i_visible_width = p_vout->fmt_render.i_visible_width;
898     p_vout->fmt_in.i_y_offset = p_vout->fmt_render.i_y_offset;
899     p_vout->fmt_in.i_visible_height = p_vout->fmt_render.i_visible_height;
900
901     if( !strcmp( psz_cmd, "crop" ) )
902     {
903         char *psz_end = NULL, *psz_parser = strchr( newval.psz_string, ':' );
904         if( psz_parser )
905         {
906             /* We're using the 3:4 syntax */
907             i_aspect_num = strtol( newval.psz_string, &psz_end, 10 );
908             if( psz_end == newval.psz_string || !i_aspect_num ) goto crop_end;
909
910             i_aspect_den = strtol( ++psz_parser, &psz_end, 10 );
911             if( psz_end == psz_parser || !i_aspect_den ) goto crop_end;
912
913             i_width = p_vout->fmt_in.i_sar_den*p_vout->fmt_render.i_visible_height *
914                 i_aspect_num / i_aspect_den / p_vout->fmt_in.i_sar_num;
915             i_height = p_vout->fmt_render.i_visible_width*p_vout->fmt_in.i_sar_num *
916                 i_aspect_den / i_aspect_num / p_vout->fmt_in.i_sar_den;
917
918             if( i_width < p_vout->fmt_render.i_visible_width )
919             {
920                 p_vout->fmt_in.i_x_offset = p_vout->fmt_render.i_x_offset +
921                     (p_vout->fmt_render.i_visible_width - i_width) / 2;
922                 p_vout->fmt_in.i_visible_width = i_width;
923             }
924             else
925             {
926                 p_vout->fmt_in.i_y_offset = p_vout->fmt_render.i_y_offset +
927                     (p_vout->fmt_render.i_visible_height - i_height) / 2;
928                 p_vout->fmt_in.i_visible_height = i_height;
929             }
930         }
931         else
932         {
933             psz_parser = strchr( newval.psz_string, 'x' );
934             if( psz_parser )
935             {
936                 /* Maybe we're using the <width>x<height>+<left>+<top> syntax */
937                 unsigned int i_crop_width, i_crop_height, i_crop_top, i_crop_left;
938
939                 i_crop_width = strtol( newval.psz_string, &psz_end, 10 );
940                 if( psz_end != psz_parser ) goto crop_end;
941
942                 psz_parser = strchr( ++psz_end, '+' );
943                 i_crop_height = strtol( psz_end, &psz_end, 10 );
944                 if( psz_end != psz_parser ) goto crop_end;
945
946                 psz_parser = strchr( ++psz_end, '+' );
947                 i_crop_left = strtol( psz_end, &psz_end, 10 );
948                 if( psz_end != psz_parser ) goto crop_end;
949
950                 psz_end++;
951                 i_crop_top = strtol( psz_end, &psz_end, 10 );
952                 if( *psz_end != '\0' ) goto crop_end;
953
954                 i_width = i_crop_width;
955                 p_vout->fmt_in.i_visible_width = i_width;
956
957                 i_height = i_crop_height;
958                 p_vout->fmt_in.i_visible_height = i_height;
959
960                 p_vout->fmt_in.i_x_offset = i_crop_left;
961                 p_vout->fmt_in.i_y_offset = i_crop_top;
962             }
963             else
964             {
965                 /* Maybe we're using the <left>+<top>+<right>+<bottom> syntax */
966                 unsigned int i_crop_top, i_crop_left, i_crop_bottom, i_crop_right;
967
968                 psz_parser = strchr( newval.psz_string, '+' );
969                 i_crop_left = strtol( newval.psz_string, &psz_end, 10 );
970                 if( psz_end != psz_parser ) goto crop_end;
971
972                 psz_parser = strchr( ++psz_end, '+' );
973                 i_crop_top = strtol( psz_end, &psz_end, 10 );
974                 if( psz_end != psz_parser ) goto crop_end;
975
976                 psz_parser = strchr( ++psz_end, '+' );
977                 i_crop_right = strtol( psz_end, &psz_end, 10 );
978                 if( psz_end != psz_parser ) goto crop_end;
979
980                 psz_end++;
981                 i_crop_bottom = strtol( psz_end, &psz_end, 10 );
982                 if( *psz_end != '\0' ) goto crop_end;
983
984                 i_width = p_vout->fmt_render.i_visible_width
985                           - i_crop_left - i_crop_right;
986                 p_vout->fmt_in.i_visible_width = i_width;
987
988                 i_height = p_vout->fmt_render.i_visible_height
989                            - i_crop_top - i_crop_bottom;
990                 p_vout->fmt_in.i_visible_height = i_height;
991
992                 p_vout->fmt_in.i_x_offset = i_crop_left;
993                 p_vout->fmt_in.i_y_offset = i_crop_top;
994             }
995         }
996     }
997     else if( !strcmp( psz_cmd, "crop-top" )
998           || !strcmp( psz_cmd, "crop-left" )
999           || !strcmp( psz_cmd, "crop-bottom" )
1000           || !strcmp( psz_cmd, "crop-right" ) )
1001     {
1002         unsigned int i_crop_top, i_crop_left, i_crop_bottom, i_crop_right;
1003
1004         i_crop_top = var_GetInteger( p_vout, "crop-top" );
1005         i_crop_left = var_GetInteger( p_vout, "crop-left" );
1006         i_crop_right = var_GetInteger( p_vout, "crop-right" );
1007         i_crop_bottom = var_GetInteger( p_vout, "crop-bottom" );
1008
1009         i_width = p_vout->fmt_render.i_visible_width
1010                   - i_crop_left - i_crop_right;
1011         p_vout->fmt_in.i_visible_width = i_width;
1012
1013         i_height = p_vout->fmt_render.i_visible_height
1014                    - i_crop_top - i_crop_bottom;
1015         p_vout->fmt_in.i_visible_height = i_height;
1016
1017         p_vout->fmt_in.i_x_offset = i_crop_left;
1018         p_vout->fmt_in.i_y_offset = i_crop_top;
1019     }
1020
1021  crop_end:
1022     InitWindowSize( p_vout, &p_vout->i_window_width,
1023                     &p_vout->i_window_height );
1024
1025     p_vout->i_changes |= VOUT_CROP_CHANGE;
1026
1027     msg_Dbg( p_vout, "cropping picture %ix%i to %i,%i,%ix%i",
1028              p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
1029              p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset,
1030              p_vout->fmt_in.i_visible_width,
1031              p_vout->fmt_in.i_visible_height );
1032
1033     return VLC_SUCCESS;
1034 }
1035
1036 static int AspectCallback( vlc_object_t *p_this, char const *psz_cmd,
1037                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1038 {
1039     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1040     unsigned int i_aspect_num, i_aspect_den, i_sar_num, i_sar_den;
1041     vlc_value_t val;
1042
1043     char *psz_end, *psz_parser = strchr( newval.psz_string, ':' );
1044
1045     /* Restore defaults */
1046     p_vout->fmt_in.i_sar_num = p_vout->fmt_render.i_sar_num;
1047     p_vout->fmt_in.i_sar_den = p_vout->fmt_render.i_sar_den;
1048     p_vout->fmt_in.i_aspect = p_vout->fmt_render.i_aspect;
1049     p_vout->render.i_aspect = p_vout->fmt_render.i_aspect;
1050
1051     if( !psz_parser ) goto aspect_end;
1052
1053     i_aspect_num = strtol( newval.psz_string, &psz_end, 10 );
1054     if( psz_end == newval.psz_string || !i_aspect_num ) goto aspect_end;
1055
1056     i_aspect_den = strtol( ++psz_parser, &psz_end, 10 );
1057     if( psz_end == psz_parser || !i_aspect_den ) goto aspect_end;
1058
1059     i_sar_num = i_aspect_num * p_vout->fmt_render.i_visible_height;
1060     i_sar_den = i_aspect_den * p_vout->fmt_render.i_visible_width;
1061     vlc_ureduce( &i_sar_num, &i_sar_den, i_sar_num, i_sar_den, 0 );
1062     p_vout->fmt_in.i_sar_num = i_sar_num;
1063     p_vout->fmt_in.i_sar_den = i_sar_den;
1064     p_vout->fmt_in.i_aspect = i_aspect_num * VOUT_ASPECT_FACTOR / i_aspect_den;
1065     p_vout->render.i_aspect = p_vout->fmt_in.i_aspect;
1066
1067  aspect_end:
1068     if( p_vout->i_par_num && p_vout->i_par_den )
1069     {
1070         p_vout->fmt_in.i_sar_num *= p_vout->i_par_den;
1071         p_vout->fmt_in.i_sar_den *= p_vout->i_par_num;
1072         p_vout->fmt_in.i_aspect = p_vout->fmt_in.i_aspect *
1073             p_vout->i_par_den / p_vout->i_par_num;
1074         p_vout->render.i_aspect = p_vout->fmt_in.i_aspect;
1075     }
1076
1077     p_vout->i_changes |= VOUT_ASPECT_CHANGE;
1078
1079     vlc_ureduce( &i_aspect_num, &i_aspect_den,
1080                  p_vout->fmt_in.i_aspect, VOUT_ASPECT_FACTOR, 0 );
1081     msg_Dbg( p_vout, "new aspect-ratio %i:%i, sample aspect-ratio %i:%i",
1082              i_aspect_num, i_aspect_den,
1083              p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den );
1084
1085     var_Get( p_vout, "crop", &val );
1086     return CropCallback( p_this, "crop", val, val, 0 );
1087
1088     return VLC_SUCCESS;
1089 }
1090
1091 static int OnTopCallback( vlc_object_t *p_this, char const *psz_cmd,
1092                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1093 {
1094     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1095     playlist_t *p_playlist = pl_Yield( p_this );
1096     vout_Control( p_vout, VOUT_SET_STAY_ON_TOP, newval.b_bool );
1097
1098     /* Modify playlist as well because the vout might have to be restarted */
1099     var_Create( p_playlist, "video-on-top", VLC_VAR_BOOL );
1100     var_Set( p_playlist, "video-on-top", newval );
1101
1102     pl_Release( p_this );
1103     return VLC_SUCCESS;
1104 }
1105
1106 static int FullscreenCallback( vlc_object_t *p_this, char const *psz_cmd,
1107                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1108 {
1109     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1110     vlc_value_t val;
1111     playlist_t *p_playlist = pl_Yield( p_this );
1112
1113     p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
1114
1115     /* Modify playlist as well because the vout might have to be restarted */
1116     var_Create( p_playlist, "fullscreen", VLC_VAR_BOOL );
1117     var_Set( p_playlist, "fullscreen", newval );
1118     pl_Release( p_playlist );
1119
1120     /* Disable "always on top" in fullscreen mode */
1121     var_Get( p_vout, "video-on-top", &val );
1122     if( val.b_bool )
1123         vout_Control( p_vout, VOUT_SET_STAY_ON_TOP,
1124                       (vlc_bool_t)!newval.b_bool );
1125
1126     val.b_bool = VLC_TRUE;
1127     var_Set( p_vout, "intf-change", val );
1128     return VLC_SUCCESS;
1129 }
1130
1131 static int SnapshotCallback( vlc_object_t *p_this, char const *psz_cmd,
1132                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1133 {
1134     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1135     vout_Control( p_vout, VOUT_SNAPSHOT );
1136     return VLC_SUCCESS;
1137 }