1 /****************************************************************************
2 * maemo.c : Maemo plugin for VLC
3 *****************************************************************************
4 * Copyright (C) 2008 the VideoLAN team
7 * Authors: Antoine Lejeune <phytos@videolan.org>
8 * Gildas Bazin <gbazin@videolan.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
31 #include <vlc_interface.h>
32 #include <vlc_vout_window.h>
34 #include <hildon/hildon-program.h>
35 #include <hildon/hildon-banner.h>
42 #include "maemo_callbacks.h"
43 #include "maemo_input.h"
44 #include "maemo_interface.h"
46 /*****************************************************************************
48 *****************************************************************************/
49 static int Open ( vlc_object_t * );
50 static void Close ( vlc_object_t * );
51 static void *Thread ( void * );
52 static gboolean should_die ( gpointer );
53 static int OpenWindow ( vlc_object_t * );
54 static void CloseWindow ( vlc_object_t * );
55 static int ControlWindow ( vout_window_t *, int, va_list );
56 static gboolean interface_ready ( gpointer );
58 /*****************************************************************************
60 *****************************************************************************/
62 set_shortname( "Maemo" );
63 set_description( N_("Maemo hildon interface") );
64 set_category( CAT_INTERFACE );
65 set_subcategory( SUBCAT_INTERFACE_MAIN );
66 set_capability( "interface", 70 );
67 set_callbacks( Open, Close );
68 add_shortcut( "maemo" );
71 set_capability( "vout window xid", 50 );
72 set_callbacks( OpenWindow, CloseWindow );
75 /*****************************************************************************
77 *****************************************************************************/
78 static int Open( vlc_object_t *p_this )
80 intf_thread_t *p_intf = (intf_thread_t *)p_this;
84 /* Allocate instance and initialize some members */
85 p_intf->p_sys = p_sys = malloc( sizeof( intf_sys_t ) );
86 if( p_intf->p_sys == NULL )
89 p_sys->p_playlist = pl_Hold( p_intf );
90 p_sys->p_input = NULL;
92 p_sys->p_main_window = NULL;
93 p_sys->p_video_window = NULL;
94 p_sys->p_control_window = NULL;
95 p_sys->b_fullscreen = false;
97 vlc_spin_init( &p_sys->event_lock );
99 /* Create separate thread for main interface */
100 vlc_sem_init (&p_sys->ready, 0);
101 if( vlc_clone( &p_sys->thread, Thread, p_intf, VLC_THREAD_PRIORITY_LOW ) )
103 pl_Release (p_sys->p_playlist);
108 /* Wait for interface thread to be fully initialised */
109 vlc_sem_wait (&p_sys->ready);
110 vlc_sem_destroy (&p_sys->ready);
112 var_Create (p_this->p_libvlc, "hildon-iface", VLC_VAR_ADDRESS);
113 val.p_address = p_this;
114 var_Set (p_this->p_libvlc, "hildon-iface", val);
119 static void Close( vlc_object_t *p_this )
121 intf_thread_t *p_intf = (intf_thread_t *)p_this;
123 var_Destroy (p_this->p_libvlc, "hildon-iface");
124 vlc_join (p_intf->p_sys->thread, NULL);
125 pl_Release ( p_intf->p_sys->p_playlist );
126 vlc_spin_destroy( &p_intf->p_sys->event_lock );
127 free( p_intf->p_sys );
130 static gint quit_event( GtkWidget *widget, GdkEvent *event, gpointer data )
132 intf_thread_t *p_intf = (intf_thread_t *)data;
133 (void)widget; (void)event;
134 libvlc_Quit( p_intf->p_libvlc );
138 /*****************************************************************************
139 * Initialize and launch the interface
140 *****************************************************************************/
141 static void *Thread( void *obj )
143 intf_thread_t *p_intf = (intf_thread_t *)obj;
144 const char *p_args[] = { "vlc", "--sync" };
145 int i_args = sizeof(p_args)/sizeof(char *);
146 char **pp_args = (char **)p_args;
148 HildonProgram *program;
149 HildonWindow *window;
150 GtkWidget *main_vbox;
153 GtkWidget *bottom_hbox;
154 GtkWidget *play_button;
155 GtkWidget *prev_button;
156 GtkWidget *next_button;
157 GtkWidget *stop_button;
158 GtkWidget *playlist_button;
161 gtk_init( &i_args, &pp_args );
163 program = HILDON_PROGRAM( hildon_program_get_instance() );
164 g_set_application_name( "VLC Media Player" );
166 window = HILDON_WINDOW( hildon_window_new() );
167 hildon_program_add_window( program, window );
168 gtk_object_set_data( GTK_OBJECT( window ), "p_intf", p_intf );
169 p_intf->p_sys->p_main_window = window;
171 g_signal_connect( GTK_WIDGET(window), "key-press-event",
172 G_CALLBACK( key_cb ), p_intf );
173 g_signal_connect (GTK_WIDGET(window), "delete_event",
174 GTK_SIGNAL_FUNC( quit_event), p_intf );
177 char *psz_rc_file = NULL;
178 char *psz_data = config_GetDataDir( p_intf );
179 if( asprintf( &psz_rc_file, "%s/maemo/vlc_intf.rc", psz_data ) != -1 )
181 gtk_rc_parse( psz_rc_file );
186 // We create the main vertical box
187 main_vbox = gtk_vbox_new( FALSE, 0 );
188 gtk_container_add( GTK_CONTAINER( window ), main_vbox );
190 // We put first the embedded video
191 video = gtk_event_box_new();
192 GdkColor black = {0,0,0,0};
193 gtk_widget_modify_bg(video, GTK_STATE_NORMAL, &black);
194 p_intf->p_sys->p_video_window = video;
195 gtk_box_pack_start( GTK_BOX( main_vbox ), video, TRUE, TRUE, 0 );
197 create_playlist( p_intf );
198 gtk_box_pack_start( GTK_BOX( main_vbox ), p_intf->p_sys->p_playlist_window, TRUE, TRUE, 0 );
200 // We put the horizontal box which contains all the buttons
201 p_intf->p_sys->p_control_window = bottom_hbox = gtk_hbox_new( FALSE, 0 );
203 // We create the buttons
204 play_button = gtk_button_new();
205 gtk_button_set_image( GTK_BUTTON( play_button ),
206 gtk_image_new_from_stock( "vlc-play", GTK_ICON_SIZE_BUTTON ) );
207 gtk_widget_set_size_request( play_button, 60, 60);
208 p_intf->p_sys->p_play_button = play_button;
209 stop_button = gtk_button_new();
210 gtk_button_set_image( GTK_BUTTON( stop_button ),
211 gtk_image_new_from_stock( "vlc-stop", GTK_ICON_SIZE_BUTTON ) );
212 prev_button = gtk_button_new();
213 gtk_button_set_image( GTK_BUTTON( prev_button ),
214 gtk_image_new_from_stock( "vlc-previous", GTK_ICON_SIZE_BUTTON ) );
215 next_button = gtk_button_new();
216 gtk_button_set_image( GTK_BUTTON( next_button ),
217 gtk_image_new_from_stock( "vlc-next", GTK_ICON_SIZE_BUTTON ) );
218 playlist_button = gtk_button_new();
219 gtk_button_set_image( GTK_BUTTON( playlist_button ),
220 gtk_image_new_from_stock( "vlc-playlist", GTK_ICON_SIZE_BUTTON ) );
221 seekbar = hildon_seekbar_new();
222 p_intf->p_sys->p_seekbar = HILDON_SEEKBAR( seekbar );
224 // We add them to the hbox
225 gtk_box_pack_start( GTK_BOX( bottom_hbox ), play_button, FALSE, FALSE, 5 );
226 gtk_box_pack_start( GTK_BOX( bottom_hbox ), stop_button, FALSE, FALSE, 0 );
227 gtk_box_pack_start( GTK_BOX( bottom_hbox ), prev_button, FALSE, FALSE, 0 );
228 gtk_box_pack_start( GTK_BOX( bottom_hbox ), next_button, FALSE, FALSE, 0 );
229 gtk_box_pack_start( GTK_BOX( bottom_hbox ), playlist_button, FALSE, FALSE, 0 );
230 gtk_box_pack_start( GTK_BOX( bottom_hbox ), seekbar , TRUE , TRUE , 5 );
231 // We add the hbox to the main vbox
232 gtk_box_pack_start( GTK_BOX( main_vbox ), bottom_hbox, FALSE, FALSE, 0 );
234 g_signal_connect( window, "delete_event",
235 G_CALLBACK( delete_event_cb ), NULL );
236 g_signal_connect( play_button, "clicked", G_CALLBACK( play_cb ), NULL );
237 g_signal_connect( stop_button, "clicked", G_CALLBACK( stop_cb ), NULL );
238 g_signal_connect( prev_button, "clicked", G_CALLBACK( prev_cb ), NULL );
239 g_signal_connect( next_button, "clicked", G_CALLBACK( next_cb ), NULL );
240 g_signal_connect( playlist_button, "clicked", G_CALLBACK( playlist_cb ), NULL );
241 g_signal_connect( seekbar, "change-value",
242 G_CALLBACK( seekbar_changed_cb ), NULL );
244 gtk_widget_show_all( GTK_WIDGET( window ) );
245 gtk_widget_hide_all( p_intf->p_sys->p_playlist_window );
247 create_menu( p_intf );
250 /* HACK: Only one X11 client can subscribe to mouse button press events.
251 * VLC currently handles those in the video display.
252 * Force GTK to unsubscribe from mouse press and release events. */
253 Display *dpy = GDK_WINDOW_XDISPLAY( gtk_widget_get_window(p_intf->p_sys->p_video_window) );
254 Window w = GDK_WINDOW_XID( gtk_widget_get_window(p_intf->p_sys->p_video_window) );
255 XWindowAttributes attr;
257 XGetWindowAttributes( dpy, w, &attr );
258 attr.your_event_mask &= ~(ButtonPressMask|ButtonReleaseMask);
259 XSelectInput( dpy, w, attr.your_event_mask );
262 // Set callback with the vlc core
263 g_timeout_add( INTF_IDLE_SLEEP / 1000, process_events, p_intf );
264 g_timeout_add( 150 /* miliseconds */, should_die, p_intf );
265 var_AddCallback( p_intf->p_sys->p_playlist, "item-change",
266 item_changed_cb, p_intf );
267 var_AddCallback( p_intf->p_sys->p_playlist, "item-current",
268 playlist_current_cb, p_intf );
269 var_AddCallback( p_intf->p_sys->p_playlist, "activity",
270 activity_cb, p_intf );
272 // The embedded video is only ready after gtk_main and windows are shown
273 g_idle_add( interface_ready, p_intf );
277 delete_input( p_intf );
278 var_DelCallback( p_intf->p_sys->p_playlist, "item-change",
279 item_changed_cb, p_intf );
280 var_DelCallback( p_intf->p_sys->p_playlist, "item-current",
281 playlist_current_cb, p_intf );
282 var_DelCallback( p_intf->p_sys->p_playlist, "activity",
283 activity_cb, p_intf );
285 gtk_object_destroy( GTK_OBJECT( window ) );
290 static gboolean should_die( gpointer data )
292 intf_thread_t *p_intf = (intf_thread_t *)data;
293 if( !vlc_object_alive( p_intf ) )
299 * Video output window provider
301 static int OpenWindow (vlc_object_t *p_obj)
303 vout_window_t *p_wnd = (vout_window_t *)p_obj;
304 intf_thread_t *p_intf;
307 if (p_wnd->cfg->is_standalone)
310 if( var_Get( p_obj->p_libvlc, "hildon-iface", &val ) )
311 val.p_address = NULL;
313 p_intf = (intf_thread_t *)val.p_address;
315 { /* If another interface is used, this plugin cannot work */
316 msg_Dbg( p_obj, "Hildon interface not found" );
320 p_wnd->handle.xid = p_intf->p_sys->xid;
322 if (!p_wnd->handle.xid)
325 p_wnd->control = ControlWindow;
326 p_wnd->sys = (vout_window_sys_t*)p_intf;
331 static int ControlWindow (vout_window_t *p_wnd, int query, va_list args)
333 intf_thread_t *p_intf = (intf_thread_t *)p_wnd->sys;
337 case VOUT_WINDOW_SET_SIZE:
339 int i_width = (int)va_arg( args, int );
340 int i_height = (int)va_arg( args, int );
342 int i_current_w, i_current_h;
343 gdk_drawable_get_size( GDK_DRAWABLE( p_intf->p_sys->p_video_window ),
344 &i_current_w, &i_current_h );
345 if( i_width != i_current_w || i_height != i_current_h )
349 case VOUT_WINDOW_SET_FULLSCREEN:
351 bool b_fs = va_arg( args, int );
352 p_intf->p_sys->b_fullscreen = b_fs;
353 g_idle_add( fullscreen_cb, p_intf );
361 static void CloseWindow (vlc_object_t *p_obj)
363 vout_window_t *p_wnd = (vout_window_t *)p_obj;
364 intf_thread_t *p_intf = (intf_thread_t *)p_wnd->sys;
366 if( p_intf->p_sys->b_fullscreen )
368 p_intf->p_sys->b_fullscreen = false;
369 g_idle_add( fullscreen_cb, p_intf );
373 static gboolean interface_ready( gpointer data )
375 intf_thread_t *p_intf = (intf_thread_t *)data;
378 GDK_WINDOW_XID( gtk_widget_get_window(p_intf->p_sys->p_video_window) );
380 // Look if the playlist is already started
381 item_changed_pl( p_intf );
383 // Everything is initialised
384 vlc_sem_post (&p_intf->p_sys->ready);
386 // We want it to be executed only one time