Missing include (GDK_WINDOW_XID)
[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_vout_window.h>
34
35 #include <hildon/hildon-program.h>
36 #include <hildon/hildon-banner.h>
37 #include <gtk/gtk.h>
38 #include <gdk/gdkx.h>
39 #include <stdio.h>
40 #include <inttypes.h>
41
42 #include "maemo.h"
43 #include "maemo_callbacks.h"
44 #include "maemo_input.h"
45 #include "maemo_interface.h"
46
47 /*****************************************************************************
48  * Local prototypes.
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 );
60
61 /*****************************************************************************
62 * Module descriptor
63 *****************************************************************************/
64 vlc_module_begin();
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" );
72
73     add_submodule();
74         set_capability( "vout window xid", 50 );
75         set_callbacks( OpenWindow, CloseWindow );
76 vlc_module_end();
77
78 static struct
79 {
80     vlc_mutex_t    lock;
81     vlc_cond_t     wait;
82     intf_thread_t *intf;
83     bool           enabled;
84 } wnd_req = { VLC_STATIC_MUTEX, PTHREAD_COND_INITIALIZER, NULL, false };
85
86 /*****************************************************************************
87  * Module callbacks
88  *****************************************************************************/
89 static int Open( vlc_object_t *p_this )
90 {
91     intf_thread_t *p_intf = (intf_thread_t *)p_this;
92
93     /* Allocate instance and initialize some members */
94     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
95     if( p_intf->p_sys == NULL )
96         return VLC_ENOMEM;
97
98     p_intf->pf_run = Run;
99
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;
103
104     p_intf->p_sys->p_main_window = NULL;
105     p_intf->p_sys->p_video_window = NULL;
106
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 );
110
111     return VLC_SUCCESS;
112 }
113
114 static void Close( vlc_object_t *p_this )
115 {
116     intf_thread_t *p_intf = (intf_thread_t *)p_this;
117
118     vlc_object_release( p_intf->p_sys->p_playlist );
119
120     vlc_spin_destroy( &p_intf->p_sys->event_lock );
121
122     /* Destroy structure */
123     free( p_intf->p_sys );
124 }
125
126 /*****************************************************************************
127 * Initialize and launch the interface
128 *****************************************************************************/
129 static void Run( intf_thread_t *p_intf )
130 {
131     char  *p_args[] = { (char *)"vlc", NULL };
132     char **pp_args  = p_args;
133     int    i_args   = 1;
134
135     HildonProgram *program;
136     HildonWindow *window;
137     GtkWidget *main_vbox;
138
139     GtkWidget *tabs;
140     GtkWidget *video;
141     GtkWidget *bottom_hbox;
142     GtkWidget *play_button;
143     GtkWidget *prev_button;
144     GtkWidget *next_button;
145     GtkWidget *stop_button;
146     GtkWidget *seekbar;
147
148     gtk_init( &i_args, &pp_args );
149
150     program = HILDON_PROGRAM( hildon_program_get_instance() );
151     g_set_application_name( "VLC Media Player" );
152
153     window = HILDON_WINDOW( hildon_window_new() );
154     hildon_program_add_window( program, window );
155     gtk_object_set_data( GTK_OBJECT( window ),
156                          "p_intf", p_intf );
157     p_intf->p_sys->p_main_window = window;
158
159     // A little theming
160     char *psz_rc_file = NULL;
161     if( asprintf( &psz_rc_file, "%s/maemo/vlc_intf.rc",
162                   config_GetDataDir() ) != -1 )
163     {
164         gtk_rc_parse( psz_rc_file );
165         free( psz_rc_file );
166     }
167
168     // We create the main vertical box
169     main_vbox = gtk_vbox_new( FALSE, 0 );
170     gtk_container_add( GTK_CONTAINER( window ), main_vbox );
171
172     tabs = gtk_notebook_new();
173     p_intf->p_sys->p_tabs = tabs;
174     gtk_notebook_set_tab_pos( GTK_NOTEBOOK( tabs ), GTK_POS_LEFT );
175     gtk_notebook_set_show_border( GTK_NOTEBOOK( tabs ), FALSE );
176     gtk_box_pack_start( GTK_BOX( main_vbox ), tabs, TRUE, TRUE, 0 );
177
178     // We put first the embedded video
179     video = gtk_event_box_new();
180     gtk_notebook_append_page( GTK_NOTEBOOK( tabs ),
181                 video,
182                 gtk_image_new_from_stock( "vlc",
183                                           GTK_ICON_SIZE_DIALOG ) );
184     gtk_notebook_set_tab_label_packing( GTK_NOTEBOOK( tabs ),
185                                         video,
186                                         FALSE, FALSE, 0 );
187     create_playlist( p_intf );
188
189     // We put the horizontal box which contains all the buttons
190     bottom_hbox = gtk_hbox_new( FALSE, 0 );
191
192     // We create the buttons
193     play_button = gtk_button_new();
194     gtk_button_set_image( GTK_BUTTON( play_button ),
195                    gtk_image_new_from_stock( "vlc-play", GTK_ICON_SIZE_BUTTON ) );
196     gtk_widget_set_size_request( play_button, 60, 60);
197     p_intf->p_sys->p_play_button = play_button;
198     stop_button = gtk_button_new();
199     gtk_button_set_image( GTK_BUTTON( stop_button ),
200                           gtk_image_new_from_stock( "vlc-stop", GTK_ICON_SIZE_BUTTON ) );
201     prev_button = gtk_button_new();
202     gtk_button_set_image( GTK_BUTTON( prev_button ),
203                       gtk_image_new_from_stock( "vlc-previous", GTK_ICON_SIZE_BUTTON ) );
204     next_button = gtk_button_new();
205     gtk_button_set_image( GTK_BUTTON( next_button ),
206                       gtk_image_new_from_stock( "vlc-next", GTK_ICON_SIZE_BUTTON ) );
207     seekbar = hildon_seekbar_new();
208     p_intf->p_sys->p_seekbar = HILDON_SEEKBAR( seekbar );
209
210     // We add them to the hbox
211     gtk_box_pack_start( GTK_BOX( bottom_hbox ), play_button, FALSE, FALSE, 5 );
212     gtk_box_pack_start( GTK_BOX( bottom_hbox ), stop_button, FALSE, FALSE, 0 );
213     gtk_box_pack_start( GTK_BOX( bottom_hbox ), prev_button, FALSE, FALSE, 0 );
214     gtk_box_pack_start( GTK_BOX( bottom_hbox ), next_button, FALSE, FALSE, 0 );
215     gtk_box_pack_start( GTK_BOX( bottom_hbox ), seekbar    , TRUE , TRUE , 5 );
216     // We add the hbox to the main vbox
217     gtk_box_pack_start( GTK_BOX( main_vbox ), bottom_hbox, FALSE, FALSE, 0 );
218
219     g_signal_connect( window, "delete_event",
220                       G_CALLBACK( delete_event_cb ), NULL );
221     g_signal_connect( play_button, "clicked", G_CALLBACK( play_cb ), NULL );
222     g_signal_connect( stop_button, "clicked", G_CALLBACK( stop_cb ), NULL );
223     g_signal_connect( prev_button, "clicked", G_CALLBACK( prev_cb ), NULL );
224     g_signal_connect( next_button, "clicked", G_CALLBACK( next_cb ), NULL );
225     g_signal_connect( seekbar, "change-value",
226                       G_CALLBACK( seekbar_changed_cb ), NULL );
227
228     gtk_widget_show_all( GTK_WIDGET( window ) );
229
230     create_menu( p_intf );
231
232     // Set callback with the vlc core
233     g_timeout_add( INTF_IDLE_SLEEP / 1000, process_events, p_intf );
234     g_timeout_add( 150 /* miliseconds */, should_die, p_intf );
235     var_AddCallback( p_intf->p_sys->p_playlist, "item-change",
236                      item_changed_cb, p_intf );
237     var_AddCallback( p_intf->p_sys->p_playlist, "item-current",
238                      playlist_current_cb, p_intf );
239     var_AddCallback( p_intf->p_sys->p_playlist, "activity",
240                      activity_cb, p_intf );
241
242     // Look if the playlist is already started
243     item_changed_pl( p_intf );
244
245     // The embedded video is only ready after gtk_main and windows are shown
246     g_idle_add( video_widget_ready, video );
247
248     gtk_main();
249
250     delete_input( p_intf );
251     var_DelCallback( p_intf->p_sys->p_playlist, "item-change",
252                      item_changed_cb, p_intf );
253     var_DelCallback( p_intf->p_sys->p_playlist, "item-current",
254                      playlist_current_cb, p_intf );
255     var_DelCallback( p_intf->p_sys->p_playlist, "activity",
256                      activity_cb, p_intf );
257
258     /* FIXME: we need to wait for vout to clean up... */
259     assert( !p_intf->p_sys->p_vout ); /* too late */
260     gtk_object_destroy( GTK_OBJECT( window ) );
261 }
262
263 static gboolean should_die( gpointer data )
264 {
265     intf_thread_t *p_intf = (intf_thread_t *)data;
266     if( !vlc_object_alive( p_intf ) )
267         gtk_main_quit();
268     return TRUE;
269 }
270
271 /**
272 * Video output window provider
273 */
274 static int OpenWindow (vlc_object_t *obj)
275 {
276     vout_window_t *wnd = (vout_window_t *)obj;
277     intf_thread_t *intf;
278
279     if (wnd->cfg->is_standalone || !wnd_req.enabled)
280         return VLC_EGENERIC;
281
282     /* FIXME it should NOT be needed */
283     vout_thread_t *vout = vlc_object_find (obj, VLC_OBJECT_VOUT, FIND_PARENT);
284     if (!vout)
285         return VLC_EGENERIC;
286
287     vlc_mutex_lock (&wnd_req.lock);
288     while ((intf = wnd_req.intf) == NULL)
289         vlc_cond_wait (&wnd_req.wait, &wnd_req.lock);
290
291     wnd->handle.xid = request_video( intf, vout );
292     vlc_mutex_unlock (&wnd_req.lock);
293
294     vlc_object_release( vout );
295
296     if (!wnd->handle.xid)
297         return VLC_EGENERIC;
298
299     msg_Dbg( intf, "Using handle %"PRIu32, wnd->handle.xid );
300
301     wnd->control = ControlWindow;
302     wnd->sys = (vout_window_sys_t*)intf;
303
304     return VLC_SUCCESS;
305 }
306
307 static int ControlWindow (vout_window_t *wnd, int query, va_list args)
308 {
309     intf_thread_t *intf = (intf_thread_t *)wnd->sys;
310
311     switch( query )
312     {
313     case VOUT_WINDOW_SET_SIZE:
314     {
315         int i_width  = (int)va_arg( args, int );
316         int i_height = (int)va_arg( args, int );
317
318         int i_current_w, i_current_h;
319         gdk_drawable_get_size( GDK_DRAWABLE( intf->p_sys->p_video_window->window ),
320                                &i_current_w, &i_current_h );
321         if( i_width != i_current_w || i_height != i_current_h )
322             return VLC_EGENERIC;
323         return VLC_SUCCESS;
324     }
325     default:
326         return VLC_EGENERIC;
327     }
328 }
329
330 static void CloseWindow (vlc_object_t *obj)
331 {
332     vout_window_t *wnd = (vout_window_t *)obj;
333     intf_thread_t *intf = (intf_thread_t *)wnd->sys;
334
335     vlc_mutex_lock( &wnd_req.lock );
336     release_video( intf );
337     vlc_mutex_unlock( &wnd_req.lock );
338 }
339
340 static uint32_t request_video( intf_thread_t *p_intf, vout_thread_t *p_nvout )
341 {
342     if( p_intf->p_sys->p_vout )
343     {
344         msg_Dbg( p_intf, "Embedded video already in use" );
345         return 0;
346     }
347
348     p_intf->p_sys->p_vout = vlc_object_hold( p_nvout );
349     return GDK_WINDOW_XID( p_intf->p_sys->p_video_window->window );
350 }
351
352 static void release_video( intf_thread_t *p_intf )
353 {
354     msg_Dbg( p_intf, "Releasing embedded video" );
355
356     vlc_object_release( p_intf->p_sys->p_vout );
357     p_intf->p_sys->p_vout = NULL;
358 }
359
360 static gboolean video_widget_ready( gpointer data )
361 {
362     intf_thread_t *p_intf = NULL;
363     GtkWidget *top_window = NULL;
364     GtkWidget *video = (GtkWidget *)data;
365
366     top_window = gtk_widget_get_toplevel( GTK_WIDGET( video ) );
367     p_intf = (intf_thread_t *)gtk_object_get_data( GTK_OBJECT( top_window ),
368                                                    "p_intf" );
369     p_intf->p_sys->p_video_window = video;
370     gtk_widget_grab_focus( video );
371
372     vlc_mutex_lock( &wnd_req.lock );
373     wnd_req.intf = p_intf;
374     vlc_cond_signal( &wnd_req.wait );
375     vlc_mutex_unlock( &wnd_req.lock );
376
377     // We rewind the input
378     if( p_intf->p_sys->p_input )
379     {
380         input_Control( p_intf->p_sys->p_input, INPUT_SET_POSITION, 0.0 );
381     }
382
383     // We want it to be executed only one time
384     return FALSE;
385 }