1 /*****************************************************************************
2 * maemo.c : Maemo plugin for VLC
3 *****************************************************************************
4 * Copyright (C) 2008 the VideoLAN team
7 * Authors: Antoine Lejeune <phytos@videolan.org>
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.
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.
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 *****************************************************************************/
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32 #include <vlc_interface.h>
33 #include <vlc_vout_window.h>
35 #include <hildon/hildon-program.h>
36 #include <hildon/hildon-banner.h>
43 #include "maemo_callbacks.h"
44 #include "maemo_input.h"
45 #include "maemo_interface.h"
47 /*****************************************************************************
49 *****************************************************************************/
50 static int Open ( vlc_object_t * );
51 static void Close ( vlc_object_t * );
52 static void Run ( intf_thread_t * );
53 static gboolean should_die ( gpointer );
54 static int OpenWindow ( vlc_object_t * );
55 static void CloseWindow ( vlc_object_t * );
56 static int ControlWindow ( vout_window_t *, int, va_list );
57 static uint32_t request_video ( intf_thread_t *, vout_thread_t * );
58 static void release_video ( intf_thread_t * );
59 static gboolean video_widget_ready ( gpointer data );
61 /*****************************************************************************
63 *****************************************************************************/
65 set_shortname( "Maemo" );
66 set_description( N_("Maemo hildon interface") );
67 set_category( CAT_INTERFACE );
68 set_subcategory( SUBCAT_INTERFACE_MAIN );
69 set_capability( "interface", 70 );
70 set_callbacks( Open, Close );
71 add_shortcut( "maemo" );
74 set_capability( "vout window xid", 50 );
75 set_callbacks( OpenWindow, CloseWindow );
84 } wnd_req = { VLC_STATIC_MUTEX, PTHREAD_COND_INITIALIZER, NULL, false };
86 /*****************************************************************************
88 *****************************************************************************/
89 static int Open( vlc_object_t *p_this )
91 intf_thread_t *p_intf = (intf_thread_t *)p_this;
93 /* Allocate instance and initialize some members */
94 p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
95 if( p_intf->p_sys == NULL )
100 p_intf->p_sys->p_playlist = pl_Hold( p_intf );
101 p_intf->p_sys->p_input = NULL;
102 p_intf->p_sys->p_vout = NULL;
104 p_intf->p_sys->p_main_window = NULL;
105 p_intf->p_sys->p_video_window = NULL;
107 wnd_req.enabled = true;
108 /* ^no need to lock, interfacesare started before video outputs */
109 vlc_spin_init( &p_intf->p_sys->event_lock );
114 static void Close( vlc_object_t *p_this )
116 intf_thread_t *p_intf = (intf_thread_t *)p_this;
118 vlc_object_release( p_intf->p_sys->p_playlist );
120 vlc_spin_destroy( &p_intf->p_sys->event_lock );
122 /* Destroy structure */
123 free( p_intf->p_sys );
126 /*****************************************************************************
127 * Initialize and launch the interface
128 *****************************************************************************/
129 static void Run( intf_thread_t *p_intf )
131 char *p_args[] = { (char *)"vlc", NULL };
132 char **pp_args = p_args;
135 HildonProgram *program;
136 HildonWindow *window;
137 GtkWidget *main_vbox;
141 GtkWidget *bottom_hbox;
142 GtkWidget *play_button;
143 GtkWidget *prev_button;
144 GtkWidget *next_button;
145 GtkWidget *stop_button;
148 gtk_init( &i_args, &pp_args );
150 program = HILDON_PROGRAM( hildon_program_get_instance() );
151 g_set_application_name( "VLC Media Player" );
153 window = HILDON_WINDOW( hildon_window_new() );
154 hildon_program_add_window( program, window );
155 gtk_object_set_data( GTK_OBJECT( window ),
157 p_intf->p_sys->p_main_window = window;
160 char *psz_rc_file = NULL;
161 char *psz_data = config_GetDataDir( p_intf );
162 if( asprintf( &psz_rc_file, "%s/maemo/vlc_intf.rc", psz_data ) != -1 )
164 gtk_rc_parse( psz_rc_file );
169 // We create the main vertical box
170 main_vbox = gtk_vbox_new( FALSE, 0 );
171 gtk_container_add( GTK_CONTAINER( window ), main_vbox );
173 tabs = gtk_notebook_new();
174 p_intf->p_sys->p_tabs = tabs;
175 gtk_notebook_set_tab_pos( GTK_NOTEBOOK( tabs ), GTK_POS_LEFT );
176 gtk_notebook_set_show_border( GTK_NOTEBOOK( tabs ), FALSE );
177 gtk_box_pack_start( GTK_BOX( main_vbox ), tabs, TRUE, TRUE, 0 );
179 // We put first the embedded video
180 video = gtk_event_box_new();
181 gtk_notebook_append_page( GTK_NOTEBOOK( tabs ),
183 gtk_image_new_from_stock( "vlc",
184 GTK_ICON_SIZE_DIALOG ) );
185 gtk_notebook_set_tab_label_packing( GTK_NOTEBOOK( tabs ),
188 create_playlist( p_intf );
190 // We put the horizontal box which contains all the buttons
191 bottom_hbox = gtk_hbox_new( FALSE, 0 );
193 // We create the buttons
194 play_button = gtk_button_new();
195 gtk_button_set_image( GTK_BUTTON( play_button ),
196 gtk_image_new_from_stock( "vlc-play", GTK_ICON_SIZE_BUTTON ) );
197 gtk_widget_set_size_request( play_button, 60, 60);
198 p_intf->p_sys->p_play_button = play_button;
199 stop_button = gtk_button_new();
200 gtk_button_set_image( GTK_BUTTON( stop_button ),
201 gtk_image_new_from_stock( "vlc-stop", GTK_ICON_SIZE_BUTTON ) );
202 prev_button = gtk_button_new();
203 gtk_button_set_image( GTK_BUTTON( prev_button ),
204 gtk_image_new_from_stock( "vlc-previous", GTK_ICON_SIZE_BUTTON ) );
205 next_button = gtk_button_new();
206 gtk_button_set_image( GTK_BUTTON( next_button ),
207 gtk_image_new_from_stock( "vlc-next", GTK_ICON_SIZE_BUTTON ) );
208 seekbar = hildon_seekbar_new();
209 p_intf->p_sys->p_seekbar = HILDON_SEEKBAR( seekbar );
211 // We add them to the hbox
212 gtk_box_pack_start( GTK_BOX( bottom_hbox ), play_button, FALSE, FALSE, 5 );
213 gtk_box_pack_start( GTK_BOX( bottom_hbox ), stop_button, FALSE, FALSE, 0 );
214 gtk_box_pack_start( GTK_BOX( bottom_hbox ), prev_button, FALSE, FALSE, 0 );
215 gtk_box_pack_start( GTK_BOX( bottom_hbox ), next_button, FALSE, FALSE, 0 );
216 gtk_box_pack_start( GTK_BOX( bottom_hbox ), seekbar , TRUE , TRUE , 5 );
217 // We add the hbox to the main vbox
218 gtk_box_pack_start( GTK_BOX( main_vbox ), bottom_hbox, FALSE, FALSE, 0 );
220 g_signal_connect( window, "delete_event",
221 G_CALLBACK( delete_event_cb ), NULL );
222 g_signal_connect( play_button, "clicked", G_CALLBACK( play_cb ), NULL );
223 g_signal_connect( stop_button, "clicked", G_CALLBACK( stop_cb ), NULL );
224 g_signal_connect( prev_button, "clicked", G_CALLBACK( prev_cb ), NULL );
225 g_signal_connect( next_button, "clicked", G_CALLBACK( next_cb ), NULL );
226 g_signal_connect( seekbar, "change-value",
227 G_CALLBACK( seekbar_changed_cb ), NULL );
229 gtk_widget_show_all( GTK_WIDGET( window ) );
231 create_menu( p_intf );
233 // Set callback with the vlc core
234 g_timeout_add( INTF_IDLE_SLEEP / 1000, process_events, p_intf );
235 g_timeout_add( 150 /* miliseconds */, should_die, p_intf );
236 var_AddCallback( p_intf->p_sys->p_playlist, "item-change",
237 item_changed_cb, p_intf );
238 var_AddCallback( p_intf->p_sys->p_playlist, "item-current",
239 playlist_current_cb, p_intf );
240 var_AddCallback( p_intf->p_sys->p_playlist, "activity",
241 activity_cb, p_intf );
243 // Look if the playlist is already started
244 item_changed_pl( p_intf );
246 // The embedded video is only ready after gtk_main and windows are shown
247 g_idle_add( video_widget_ready, video );
251 delete_input( p_intf );
252 var_DelCallback( p_intf->p_sys->p_playlist, "item-change",
253 item_changed_cb, p_intf );
254 var_DelCallback( p_intf->p_sys->p_playlist, "item-current",
255 playlist_current_cb, p_intf );
256 var_DelCallback( p_intf->p_sys->p_playlist, "activity",
257 activity_cb, p_intf );
259 /* FIXME: we need to wait for vout to clean up... */
260 assert( !p_intf->p_sys->p_vout ); /* too late */
261 gtk_object_destroy( GTK_OBJECT( window ) );
264 static gboolean should_die( gpointer data )
266 intf_thread_t *p_intf = (intf_thread_t *)data;
267 if( !vlc_object_alive( p_intf ) )
273 * Video output window provider
275 static int OpenWindow (vlc_object_t *obj)
277 vout_window_t *wnd = (vout_window_t *)obj;
280 if (wnd->cfg->is_standalone || !wnd_req.enabled)
283 /* FIXME it should NOT be needed */
284 vout_thread_t *vout = vlc_object_find (obj, VLC_OBJECT_VOUT, FIND_PARENT);
288 vlc_mutex_lock (&wnd_req.lock);
289 while ((intf = wnd_req.intf) == NULL)
290 vlc_cond_wait (&wnd_req.wait, &wnd_req.lock);
292 wnd->xid = request_video( intf, vout );
293 vlc_mutex_unlock (&wnd_req.lock);
295 vlc_object_release( vout );
300 msg_Dbg( intf, "Using handle %"PRIu32, wnd->xid );
302 wnd->control = ControlWindow;
303 wnd->sys = (vout_window_sys_t*)intf;
308 static int ControlWindow (vout_window_t *wnd, int query, va_list args)
310 intf_thread_t *intf = (intf_thread_t *)wnd->sys;
314 case VOUT_WINDOW_SET_SIZE:
316 int i_width = (int)va_arg( args, int );
317 int i_height = (int)va_arg( args, int );
319 int i_current_w, i_current_h;
320 gdk_drawable_get_size( GDK_DRAWABLE( intf->p_sys->p_video_window->window ),
321 &i_current_w, &i_current_h );
322 if( i_width != i_current_w || i_height != i_current_h )
331 static void CloseWindow (vlc_object_t *obj)
333 vout_window_t *wnd = (vout_window_t *)obj;
334 intf_thread_t *intf = (intf_thread_t *)wnd->sys;
336 vlc_mutex_lock( &wnd_req.lock );
337 release_video( intf );
338 vlc_mutex_unlock( &wnd_req.lock );
341 static uint32_t request_video( intf_thread_t *p_intf, vout_thread_t *p_nvout )
343 if( p_intf->p_sys->p_vout )
345 msg_Dbg( p_intf, "Embedded video already in use" );
349 p_intf->p_sys->p_vout = vlc_object_hold( p_nvout );
350 return GDK_WINDOW_XID( p_intf->p_sys->p_video_window->window );
353 static void release_video( intf_thread_t *p_intf )
355 msg_Dbg( p_intf, "Releasing embedded video" );
357 vlc_object_release( p_intf->p_sys->p_vout );
358 p_intf->p_sys->p_vout = NULL;
361 static gboolean video_widget_ready( gpointer data )
363 intf_thread_t *p_intf = NULL;
364 GtkWidget *top_window = NULL;
365 GtkWidget *video = (GtkWidget *)data;
367 top_window = gtk_widget_get_toplevel( GTK_WIDGET( video ) );
368 p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT( top_window ),
370 p_intf->p_sys->p_video_window = video;
371 gtk_widget_grab_focus( video );
373 vlc_mutex_lock( &wnd_req.lock );
374 wnd_req.intf = p_intf;
375 vlc_cond_signal( &wnd_req.wait );
376 vlc_mutex_unlock( &wnd_req.lock );
378 // We rewind the input
379 if( p_intf->p_sys->p_input )
381 input_Control( p_intf->p_sys->p_input, INPUT_SET_POSITION, 0.0 );
384 // We want it to be executed only one time