]> git.sesse.net Git - vlc/blob - modules/gui/hildon/maemo.c
Make the maemo interface a bit more useable
[vlc] / modules / gui / hildon / maemo.c
1 /****************************************************************************
2  * maemo.c : Maemo plugin for VLC
3  *****************************************************************************
4  * Copyright (C) 2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Lejeune <phytos@videolan.org>
8  *          Gildas Bazin <gbazin@videolan.org>
9  *
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.
14  *
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.
19  *
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  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
31 #include <vlc_interface.h>
32 #include <vlc_vout_window.h>
33
34 #include <hildon/hildon-program.h>
35 #include <hildon/hildon-banner.h>
36 #include <gtk/gtk.h>
37 #include <gdk/gdkx.h>
38 #include <stdio.h>
39 #include <inttypes.h>
40
41 #include "maemo.h"
42 #include "maemo_callbacks.h"
43 #include "maemo_input.h"
44 #include "maemo_interface.h"
45
46 /*****************************************************************************
47  * Local prototypes.
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 );
57
58 /*****************************************************************************
59 * Module descriptor
60 *****************************************************************************/
61 vlc_module_begin();
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" );
69
70     add_submodule();
71         set_capability( "vout window xid", 50 );
72         set_callbacks( OpenWindow, CloseWindow );
73 vlc_module_end();
74
75 /*****************************************************************************
76  * Module callbacks
77  *****************************************************************************/
78 static int Open( vlc_object_t *p_this )
79 {
80     intf_thread_t *p_intf = (intf_thread_t *)p_this;
81     intf_sys_t *p_sys;;
82     vlc_value_t val;
83
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 )
87         return VLC_ENOMEM;
88
89     p_sys->p_playlist = pl_Hold( p_intf );
90     p_sys->p_input = NULL;
91
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;
96
97     vlc_spin_init( &p_sys->event_lock );
98
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 ) )
102     {
103         pl_Release (p_sys->p_playlist);
104         free (p_sys);
105         return VLC_ENOMEM;
106     }
107
108     /* Wait for interface thread to be fully initialised */
109     vlc_sem_wait (&p_sys->ready);
110     vlc_sem_destroy (&p_sys->ready);
111
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);
115
116     return VLC_SUCCESS;
117 }
118
119 static void Close( vlc_object_t *p_this )
120 {
121     intf_thread_t *p_intf = (intf_thread_t *)p_this;
122
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 );
128 }
129
130 static gint quit_event( GtkWidget *widget, GdkEvent *event, gpointer data )
131 {
132     intf_thread_t *p_intf = (intf_thread_t *)data;
133     (void)widget; (void)event;
134     libvlc_Quit( p_intf->p_libvlc );
135     return TRUE;
136 }
137
138 /*****************************************************************************
139 * Initialize and launch the interface
140 *****************************************************************************/
141 static void *Thread( void *obj )
142 {
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;
147
148     HildonProgram *program;
149     HildonWindow *window;
150     GtkWidget *main_vbox;
151
152     GtkWidget *video;
153     GtkWidget *bottom_hbox;
154     GtkWidget *play_button;
155     GtkWidget *prev_button;
156     GtkWidget *next_button;
157     GtkWidget *stop_button;
158     GtkWidget *playlist_button;
159     GtkWidget *seekbar;
160
161     gtk_init( &i_args, &pp_args );
162
163     program = HILDON_PROGRAM( hildon_program_get_instance() );
164     g_set_application_name( "VLC Media Player" );
165
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;
170
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 );
175
176     // A little theming
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 )
180     {
181         gtk_rc_parse( psz_rc_file );
182         free( psz_rc_file );
183     }
184     free( psz_data );
185
186     // We create the main vertical box
187     main_vbox = gtk_vbox_new( FALSE, 0 );
188     gtk_container_add( GTK_CONTAINER( window ), main_vbox );
189
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 );
196
197     create_playlist( p_intf );
198     gtk_box_pack_start( GTK_BOX( main_vbox ), p_intf->p_sys->p_playlist_window, TRUE, TRUE, 0 );
199
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 );
202
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 );
223
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 );
233
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 );
243
244     gtk_widget_show_all( GTK_WIDGET( window ) );
245     gtk_widget_hide_all( p_intf->p_sys->p_playlist_window );
246
247     create_menu( p_intf );
248
249 #if 1
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;
256
257     XGetWindowAttributes( dpy, w, &attr );
258     attr.your_event_mask &= ~(ButtonPressMask|ButtonReleaseMask);
259     XSelectInput( dpy, w, attr.your_event_mask );
260 #endif
261
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 );
271
272     // The embedded video is only ready after gtk_main and windows are shown
273     g_idle_add( interface_ready, p_intf );
274
275     gtk_main();
276
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 );
284
285     gtk_object_destroy( GTK_OBJECT( window ) );
286
287     return NULL;
288 }
289
290 static gboolean should_die( gpointer data )
291 {
292     intf_thread_t *p_intf = (intf_thread_t *)data;
293     if( !vlc_object_alive( p_intf ) )
294         gtk_main_quit();
295     return TRUE;
296 }
297
298 /**
299 * Video output window provider
300 */
301 static int OpenWindow (vlc_object_t *p_obj)
302 {
303     vout_window_t *p_wnd = (vout_window_t *)p_obj;
304     intf_thread_t *p_intf;
305     vlc_value_t val;
306
307     if (p_wnd->cfg->is_standalone)
308         return VLC_EGENERIC;
309
310     if( var_Get( p_obj->p_libvlc, "hildon-iface", &val ) )
311         val.p_address = NULL;
312
313     p_intf = (intf_thread_t *)val.p_address;
314     if( !p_intf )
315     {   /* If another interface is used, this plugin cannot work */
316         msg_Dbg( p_obj, "Hildon interface not found" );
317         return VLC_EGENERIC;
318     }
319
320     p_wnd->handle.xid = p_intf->p_sys->xid;
321
322     if (!p_wnd->handle.xid)
323         return VLC_EGENERIC;
324
325     p_wnd->control = ControlWindow;
326     p_wnd->sys = (vout_window_sys_t*)p_intf;
327
328     return VLC_SUCCESS;
329 }
330
331 static int ControlWindow (vout_window_t *p_wnd, int query, va_list args)
332 {
333     intf_thread_t *p_intf = (intf_thread_t *)p_wnd->sys;
334
335     switch( query )
336     {
337     case VOUT_WINDOW_SET_SIZE:
338     {
339         int i_width  = (int)va_arg( args, int );
340         int i_height = (int)va_arg( args, int );
341
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 )
346             return VLC_EGENERIC;
347         return VLC_SUCCESS;
348     }
349     case VOUT_WINDOW_SET_FULLSCREEN:
350     {
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 );
354         return VLC_SUCCESS;
355     }
356     default:
357         return VLC_EGENERIC;
358     }
359 }
360
361 static void CloseWindow (vlc_object_t *p_obj)
362 {
363     vout_window_t *p_wnd = (vout_window_t *)p_obj;
364     intf_thread_t *p_intf = (intf_thread_t *)p_wnd->sys;
365
366     if( p_intf->p_sys->b_fullscreen )
367     {
368         p_intf->p_sys->b_fullscreen = false;
369         g_idle_add( fullscreen_cb, p_intf );
370     }
371 }
372
373 static gboolean interface_ready( gpointer data )
374 {
375     intf_thread_t *p_intf = (intf_thread_t *)data;
376
377     p_intf->p_sys->xid =
378         GDK_WINDOW_XID( gtk_widget_get_window(p_intf->p_sys->p_video_window) );
379
380     // Look if the playlist is already started
381     item_changed_pl( p_intf );
382
383     // Everything is initialised
384     vlc_sem_post (&p_intf->p_sys->ready);
385
386     // We want it to be executed only one time
387     return FALSE;
388 }