]> git.sesse.net Git - vlc/blob - src/video_output/vout_intf.c
4462ffe9eb4e409248b366014e8eb14883834d9d
[vlc] / src / video_output / vout_intf.c
1 /*****************************************************************************
2  * vout_intf.c : video output interface
3  *****************************************************************************
4  * Copyright (C) 2000-2004 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         /* Maybe we're using the <width> x <height> + <left> + <top> syntax */
823         unsigned int i_crop_width, i_crop_height, i_crop_top, i_crop_left;
824
825         psz_parser = strchr( newval.psz_string, 'x' );
826         i_crop_width = strtol( newval.psz_string, &psz_end, 10 );
827         if( psz_end != psz_parser ) goto crop_end;
828
829         psz_parser = strchr( ++psz_end, '+' );
830         i_crop_height = strtol( psz_end, &psz_end, 10 );
831         if( psz_end != psz_parser ) goto crop_end;
832
833         psz_parser = strchr( ++psz_end, '+' );
834         i_crop_left = strtol( psz_end, &psz_end, 10 );
835         if( psz_end != psz_parser ) goto crop_end;
836
837         i_crop_top = strtol( ++psz_end, &psz_end, 10 );
838         if( *psz_end != '\0' ) goto crop_end;
839
840         i_width = i_crop_width;
841         p_vout->fmt_in.i_visible_width = i_width;
842
843         i_height = i_crop_height;
844         p_vout->fmt_in.i_visible_height = i_height;
845
846         p_vout->fmt_in.i_x_offset = i_crop_left;
847         p_vout->fmt_in.i_y_offset = i_crop_top;
848     }
849
850  crop_end:
851     InitWindowSize( p_vout, &p_vout->i_window_width,
852                     &p_vout->i_window_height );
853
854     p_vout->i_changes |= VOUT_CROP_CHANGE;
855
856     msg_Dbg( p_vout, "cropping picture %ix%i to %i,%i,%ix%i",
857              p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
858              p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset,
859              p_vout->fmt_in.i_visible_width,
860              p_vout->fmt_in.i_visible_height );
861
862     return VLC_SUCCESS;
863 }
864
865 static int AspectCallback( vlc_object_t *p_this, char const *psz_cmd,
866                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
867 {
868     vout_thread_t *p_vout = (vout_thread_t *)p_this;
869     unsigned int i_aspect_num, i_aspect_den, i_sar_num, i_sar_den;
870     vlc_value_t val;
871
872     char *psz_end, *psz_parser = strchr( newval.psz_string, ':' );
873
874     /* Restore defaults */
875     p_vout->fmt_in.i_sar_num = p_vout->fmt_render.i_sar_num;
876     p_vout->fmt_in.i_sar_den = p_vout->fmt_render.i_sar_den;
877     p_vout->fmt_in.i_aspect = p_vout->fmt_render.i_aspect;
878     p_vout->render.i_aspect = p_vout->fmt_render.i_aspect;
879
880     if( !psz_parser ) goto aspect_end;
881
882     i_aspect_num = strtol( newval.psz_string, &psz_end, 10 );
883     if( psz_end == newval.psz_string || !i_aspect_num ) goto aspect_end;
884
885     i_aspect_den = strtol( ++psz_parser, &psz_end, 10 );
886     if( psz_end == psz_parser || !i_aspect_den ) goto aspect_end;
887
888     i_sar_num = i_aspect_num * p_vout->fmt_render.i_visible_height;
889     i_sar_den = i_aspect_den * p_vout->fmt_render.i_visible_width;
890     vlc_ureduce( &i_sar_num, &i_sar_den, i_sar_num, i_sar_den, 0 );
891     p_vout->fmt_in.i_sar_num = i_sar_num;
892     p_vout->fmt_in.i_sar_den = i_sar_den;
893     p_vout->fmt_in.i_aspect = i_aspect_num * VOUT_ASPECT_FACTOR / i_aspect_den;
894     p_vout->render.i_aspect = p_vout->fmt_in.i_aspect;
895
896  aspect_end:
897     if( p_vout->i_par_num && p_vout->i_par_den )
898     {
899         p_vout->fmt_in.i_sar_num *= p_vout->i_par_den;
900         p_vout->fmt_in.i_sar_den *= p_vout->i_par_num;
901         p_vout->fmt_in.i_aspect = p_vout->fmt_in.i_aspect *
902             p_vout->i_par_den / p_vout->i_par_num;
903         p_vout->render.i_aspect = p_vout->fmt_in.i_aspect;
904     }
905
906     p_vout->i_changes |= VOUT_ASPECT_CHANGE;
907
908     vlc_ureduce( &i_aspect_num, &i_aspect_den,
909                  p_vout->fmt_in.i_aspect, VOUT_ASPECT_FACTOR, 0 );
910     msg_Dbg( p_vout, "new aspect-ratio %i:%i, sample aspect-ratio %i:%i",
911              i_aspect_num, i_aspect_den,
912              p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den );
913
914     var_Get( p_vout, "crop", &val );
915     return CropCallback( p_this, 0, val, val, 0 );
916
917     return VLC_SUCCESS;
918 }
919
920 static int OnTopCallback( vlc_object_t *p_this, char const *psz_cmd,
921                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
922 {
923     vout_thread_t *p_vout = (vout_thread_t *)p_this;
924     playlist_t *p_playlist;
925     vout_Control( p_vout, VOUT_SET_STAY_ON_TOP, newval.b_bool );
926
927     p_playlist = (playlist_t *)vlc_object_find( p_this, VLC_OBJECT_PLAYLIST,
928                                                  FIND_PARENT );
929     if( p_playlist )
930     {
931         /* Modify playlist as well because the vout might have to be restarted */
932         var_Create( p_playlist, "video-on-top", VLC_VAR_BOOL );
933         var_Set( p_playlist, "video-on-top", newval );
934
935         vlc_object_release( p_playlist );
936     }
937     return VLC_SUCCESS;
938 }
939
940 static int FullscreenCallback( vlc_object_t *p_this, char const *psz_cmd,
941                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
942 {
943     vout_thread_t *p_vout = (vout_thread_t *)p_this;
944     playlist_t *p_playlist;
945     vlc_value_t val;
946
947     p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
948
949     p_playlist = (playlist_t *)vlc_object_find( p_this, VLC_OBJECT_PLAYLIST,
950                                                  FIND_PARENT );
951     if( p_playlist )
952     {
953         /* Modify playlist as well because the vout might have to be restarted */
954         var_Create( p_playlist, "fullscreen", VLC_VAR_BOOL );
955         var_Set( p_playlist, "fullscreen", newval );
956
957         vlc_object_release( p_playlist );
958     }
959
960     /* Disable "always on top" in fullscreen mode */
961     var_Get( p_vout, "video-on-top", &val );
962     if( newval.b_bool && val.b_bool )
963     {
964         val.b_bool = VLC_FALSE;
965         vout_Control( p_vout, VOUT_SET_STAY_ON_TOP, val.b_bool );
966     }
967     else if( !newval.b_bool && val.b_bool )
968     {
969         vout_Control( p_vout, VOUT_SET_STAY_ON_TOP, val.b_bool );
970     }
971
972     val.b_bool = VLC_TRUE;
973     var_Set( p_vout, "intf-change", val );
974     return VLC_SUCCESS;
975 }
976
977 static int SnapshotCallback( vlc_object_t *p_this, char const *psz_cmd,
978                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
979 {
980     vout_thread_t *p_vout = (vout_thread_t *)p_this;
981     vout_Control( p_vout, VOUT_SNAPSHOT );
982     return VLC_SUCCESS;
983 }