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