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