Avoid using msg_* when running out of memory.
[vlc.git] / modules / gui / maemo / 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 *
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 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <assert.h>
29
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32 #include <vlc_interface.h>
33 #include <vlc_window.h>
34
35 #include <hildon/hildon-program.h>
36 #include <hildon/hildon-banner.h>
37 #include <gtk/gtk.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     Run                ( intf_thread_t * );
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 void*    request_video      ( intf_thread_t *, vout_thread_t * );
57 static void     release_video      ( intf_thread_t * );
58 static gboolean video_widget_ready ( gpointer data );
59
60 /*****************************************************************************
61 * Module descriptor
62 *****************************************************************************/
63 vlc_module_begin();
64     set_shortname( "Maemo" );
65     set_description( N_("Maemo hildon interface") );
66     set_category( CAT_INTERFACE );
67     set_subcategory( SUBCAT_INTERFACE_MAIN );
68     set_capability( "interface", 70 );
69     set_callbacks( Open, Close );
70     add_shortcut( "maemo" );
71
72     add_submodule();
73         set_capability( "xwindow", 50 );
74         set_callbacks( OpenWindow, CloseWindow );
75 vlc_module_end();
76
77 /*****************************************************************************
78  * Module callbacks
79  *****************************************************************************/
80 static int Open( vlc_object_t *p_this )
81 {
82     intf_thread_t *p_intf = (intf_thread_t *)p_this;
83
84     /* Allocate instance and initialize some members */
85     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
86     if( p_intf->p_sys == NULL )
87         return VLC_ENOMEM;
88
89     p_intf->pf_run = Run;
90
91     p_intf->p_sys->p_playlist = pl_Hold( p_intf );
92     p_intf->p_sys->p_input = NULL;
93     p_intf->p_sys->p_vout = NULL;
94
95     p_intf->p_sys->p_main_window = NULL;
96     p_intf->p_sys->p_video_window = NULL;
97
98     vlc_spin_init( &p_intf->p_sys->event_lock );
99     vlc_mutex_init( &p_intf->p_sys->p_video_mutex );
100     vlc_cond_init( &p_intf->p_sys->p_video_cond );
101
102     return VLC_SUCCESS;
103 }
104
105 static void Close( vlc_object_t *p_this )
106 {
107     intf_thread_t *p_intf = (intf_thread_t *)p_this;
108
109     vlc_object_release( p_intf->p_sys->p_playlist );
110
111     vlc_spin_destroy( &p_intf->p_sys->event_lock );
112
113     /* Destroy structure */
114     free( p_intf->p_sys );
115 }
116
117 /*****************************************************************************
118 * Initialize and launch the interface
119 *****************************************************************************/
120 static void Run( intf_thread_t *p_intf )
121 {
122     char  *p_args[] = { "", NULL };
123     char **pp_args  = p_args;
124     int    i_args   = 1;
125
126     HildonProgram *program;
127     HildonWindow *window;
128     GtkWidget *main_vbox;
129
130     GtkWidget *tabs;
131     GtkWidget *video;
132     GtkWidget *bottom_hbox;
133     GtkWidget *play_button;
134     GtkWidget *prev_button;
135     GtkWidget *next_button;
136     GtkWidget *stop_button;
137     GtkWidget *seekbar;
138
139     gtk_init( &i_args, &pp_args );
140
141     program = HILDON_PROGRAM( hildon_program_get_instance() );
142     g_set_application_name( "VLC Media Player" );
143
144     window = HILDON_WINDOW( hildon_window_new() );
145     hildon_program_add_window( program, window );
146     gtk_object_set_data( GTK_OBJECT( window ),
147                          "p_intf", p_intf );
148     p_intf->p_sys->p_main_window = window;
149
150     // A little theming
151     char *psz_rc_file = NULL;
152     if( asprintf( &psz_rc_file, "%s/maemo/vlc_intf.rc",
153                   config_GetDataDir() ) != -1 )
154     {
155         gtk_rc_parse( psz_rc_file );
156         free( psz_rc_file );
157     }
158
159     // We create the main vertical box
160     main_vbox = gtk_vbox_new( FALSE, 0 );
161     gtk_container_add( GTK_CONTAINER( window ), main_vbox );
162
163     tabs = gtk_notebook_new();
164     p_intf->p_sys->p_tabs = tabs;
165     gtk_notebook_set_tab_pos( GTK_NOTEBOOK( tabs ), GTK_POS_LEFT );
166     gtk_notebook_set_show_border( GTK_NOTEBOOK( tabs ), FALSE );
167     gtk_box_pack_start( GTK_BOX( main_vbox ), tabs, TRUE, TRUE, 0 );
168
169     // We put first the embedded video
170     video = gtk_event_box_new();
171     gtk_notebook_append_page( GTK_NOTEBOOK( tabs ),
172                 video,
173                 gtk_image_new_from_stock( "vlc",
174                                           GTK_ICON_SIZE_DIALOG ) );
175     gtk_notebook_set_tab_label_packing( GTK_NOTEBOOK( tabs ),
176                                         video,
177                                         FALSE, FALSE, 0 );
178     create_playlist( p_intf );
179
180     // We put the horizontal box which contains all the buttons
181     bottom_hbox = gtk_hbox_new( FALSE, 0 );
182
183     // We create the buttons
184     play_button = gtk_button_new();
185     gtk_button_set_image( GTK_BUTTON( play_button ),
186                    gtk_image_new_from_stock( "vlc-play", GTK_ICON_SIZE_BUTTON ) );
187     gtk_widget_set_size_request( play_button, 60, 60);
188     p_intf->p_sys->p_play_button = play_button;
189     stop_button = gtk_button_new();
190     gtk_button_set_image( GTK_BUTTON( stop_button ),
191                           gtk_image_new_from_stock( "vlc-stop", GTK_ICON_SIZE_BUTTON ) );
192     prev_button = gtk_button_new();
193     gtk_button_set_image( GTK_BUTTON( prev_button ),
194                       gtk_image_new_from_stock( "vlc-previous", GTK_ICON_SIZE_BUTTON ) );
195     next_button = gtk_button_new();
196     gtk_button_set_image( GTK_BUTTON( next_button ),
197                       gtk_image_new_from_stock( "vlc-next", GTK_ICON_SIZE_BUTTON ) );
198     seekbar = hildon_seekbar_new();
199     p_intf->p_sys->p_seekbar = HILDON_SEEKBAR( seekbar );
200
201     // We add them to the hbox
202     gtk_box_pack_start( GTK_BOX( bottom_hbox ), play_button, FALSE, FALSE, 5 );
203     gtk_box_pack_start( GTK_BOX( bottom_hbox ), stop_button, FALSE, FALSE, 0 );
204     gtk_box_pack_start( GTK_BOX( bottom_hbox ), prev_button, FALSE, FALSE, 0 );
205     gtk_box_pack_start( GTK_BOX( bottom_hbox ), next_button, FALSE, FALSE, 0 );
206     gtk_box_pack_start( GTK_BOX( bottom_hbox ), seekbar    , TRUE , TRUE , 5 );
207     // We add the hbox to the main vbox
208     gtk_box_pack_start( GTK_BOX( main_vbox ), bottom_hbox, FALSE, FALSE, 0 );
209
210     g_signal_connect( window, "delete_event",
211                       G_CALLBACK( delete_event_cb ), NULL );
212     g_signal_connect( play_button, "clicked", G_CALLBACK( play_cb ), NULL );
213     g_signal_connect( stop_button, "clicked", G_CALLBACK( stop_cb ), NULL );
214     g_signal_connect( prev_button, "clicked", G_CALLBACK( prev_cb ), NULL );
215     g_signal_connect( next_button, "clicked", G_CALLBACK( next_cb ), NULL );
216     g_signal_connect( seekbar, "change-value",
217                       G_CALLBACK( seekbar_changed_cb ), NULL );
218
219     gtk_widget_show_all( GTK_WIDGET( window ) );
220
221     create_menu( p_intf );
222
223     // Set callback with the vlc core
224     g_timeout_add( INTF_IDLE_SLEEP / 1000, process_events, p_intf );
225     g_timeout_add( 150 /* miliseconds */, should_die, p_intf );
226     var_AddCallback( p_intf->p_sys->p_playlist, "item-change",
227                      item_changed_cb, p_intf );
228     var_AddCallback( p_intf->p_sys->p_playlist, "item-current",
229                      playlist_current_cb, p_intf );
230     var_AddCallback( p_intf->p_sys->p_playlist, "activity",
231                      activity_cb, p_intf );
232
233     // Look if the playlist is already started
234     item_changed_pl( p_intf );
235
236     // The embedded video is only ready after gtk_main and windows are shown
237     g_idle_add( video_widget_ready, video );
238
239     gtk_main();
240
241     delete_input( p_intf );
242     var_DelCallback( p_intf->p_sys->p_playlist, "item-change",
243                      item_changed_cb, p_intf );
244     var_DelCallback( p_intf->p_sys->p_playlist, "item-current",
245                      playlist_current_cb, p_intf );
246     var_DelCallback( p_intf->p_sys->p_playlist, "activity",
247                      activity_cb, p_intf );
248
249     assert( !p_intf->p_sys->p_vout ); /* too late */
250     gtk_object_destroy( GTK_OBJECT( window ) );
251 }
252
253 static gboolean should_die( gpointer data )
254 {
255     intf_thread_t *p_intf = (intf_thread_t *)data;
256     if( !vlc_object_alive( p_intf ) )
257         gtk_main_quit();
258     return TRUE;
259 }
260
261 /**
262 * Video output window provider
263 */
264 static int OpenWindow (vlc_object_t *obj)
265 {
266     vout_window_t *wnd = (vout_window_t *)obj;
267
268     /* TODO: should probably be in the libvlc core instead: */
269     if (!config_GetInt (obj, "embedded-video"))
270         return VLC_EGENERIC;
271
272     intf_thread_t *intf = (intf_thread_t *)
273     vlc_object_find_name (obj, "maemo", FIND_ANYWHERE);
274     if (intf == NULL)
275     {
276         msg_Err( obj, "Maemo interface not found" );
277         return VLC_EGENERIC; /* Maemo not in use */
278     }
279
280     wnd->handle.xid = request_video( intf, wnd->vout );
281     msg_Dbg( intf, "Using handle %"PRIu32, wnd->handle.xid );
282
283     wnd->control = ControlWindow;
284     wnd->p_private = intf;
285
286     // Signaling that the window is not at the requested sizeof
287     int i_width, i_height, i_x_top, i_y_top, i_x, i_y;
288     gdk_drawable_get_size( GDK_DRAWABLE( intf->p_sys->p_video_window->window ),
289                            &i_width, &i_height );
290     gdk_window_get_position( GTK_WIDGET(intf->p_sys->p_main_window)->window,
291                              &i_x_top, &i_y_top );
292     gdk_window_get_position( intf->p_sys->p_video_window->window, &i_x, &i_y );
293
294     wnd->width = i_width;
295     wnd->height = i_height;
296     wnd->pos_x = i_x_top + i_x;
297     wnd->pos_y = i_y_top + i_y;
298
299     return VLC_SUCCESS;
300 }
301
302 static int ControlWindow (vout_window_t *wnd, int query, va_list args)
303 {
304     (void)wnd; (void)query; (void)args;
305     return VLC_EGENERIC;
306 }
307
308 static void CloseWindow (vlc_object_t *obj)
309 {
310     intf_thread_t *intf = (intf_thread_t *)obj->p_private;
311
312     release_video( intf );
313     vlc_object_release (intf);
314 }
315
316 static void *request_video( intf_thread_t *p_intf, vout_thread_t *p_nvout )
317 {
318     if( p_intf->p_sys->p_vout )
319     {
320         msg_Dbg( p_intf, "Embedded video already in use" );
321         return NULL;
322     }
323
324     vlc_mutex_lock( &p_intf->p_sys->p_video_mutex );
325     mutex_cleanup_push( &p_intf->p_sys->p_video_mutex );
326
327     // We wait until the p_video_window is set
328     while( p_intf->p_sys->p_video_window == NULL )
329         vlc_cond_wait( &p_intf->p_sys->p_video_cond,
330                        &p_intf->p_sys->p_video_mutex );
331
332     vlc_cleanup_run();
333
334     p_intf->p_sys->p_vout = p_nvout;
335     return ( void * )GDK_WINDOW_XID( p_intf->p_sys->p_video_window->window );
336 }
337
338 static void release_video( intf_thread_t *p_intf )
339 {
340     msg_Dbg( p_intf, "Releasing embedded video" );
341     p_intf->p_sys->p_vout = NULL;
342 }
343
344 static gboolean video_widget_ready( gpointer data )
345 {
346     intf_thread_t *p_intf = NULL;
347     GtkWidget *top_window = NULL;
348     GtkWidget *video = (GtkWidget *)data;
349
350     top_window = gtk_widget_get_toplevel( GTK_WIDGET( video ) );
351     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT( top_window ),
352                                                    "p_intf" );
353     p_intf->p_sys->p_video_window = video;
354     gtk_widget_grab_focus( video );
355
356     vlc_cond_signal( &p_intf->p_sys->p_video_cond );
357
358     // We rewind the input
359     if( p_intf->p_sys->p_input )
360     {
361         input_Control( p_intf->p_sys->p_input, INPUT_SET_POSITION, 0.0 );
362     }
363
364     // We want it to be executed only one time
365     return FALSE;
366 }