]> git.sesse.net Git - vlc/blob - projects/mozilla/vlcplugin.cpp
Merge branch 'master' into lpcm_encoder
[vlc] / projects / mozilla / vlcplugin.cpp
1 /*****************************************************************************
2  * vlcplugin.cpp: a VLC plugin for Mozilla
3  *****************************************************************************
4  * Copyright (C) 2002-2010 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          Damien Fouilleul <damienf.fouilleul@laposte.net>
9  *          Jean-Paul Saman <jpsaman@videolan.org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include "config.h"
30
31 #include "vlcplugin.h"
32
33 #include "control/npolibvlc.h"
34
35 #include <ctype.h>
36
37 #if defined(XP_UNIX)
38 #   include <pthread.h>
39 #elif defined(XP_WIN)
40     /* windows headers */
41 #   include <winbase.h>
42 #else
43 #warning "locking not implemented for this platform"
44 #endif
45
46 #include <stdio.h>
47 #include <assert.h>
48 #include <stdlib.h>
49
50 /*****************************************************************************
51  * utilitiy functions
52  *****************************************************************************/
53 static void plugin_lock_init(plugin_lock_t *lock)
54 {
55     assert(lock);
56
57 #if defined(XP_UNIX)
58     pthread_mutex_init(&lock->mutex, NULL);
59 #elif defined(XP_WIN)
60     InitializeCriticalSection(&lock->cs);
61 #else
62 #warning "locking not implemented in this platform"
63 #endif
64 }
65
66 static void plugin_lock_destroy(plugin_lock_t *lock)
67 {
68     assert(lock);
69
70 #if defined(XP_UNIX)
71     pthread_mutex_destroy(&lock->mutex);
72 #elif defined(XP_WIN)
73     DeleteCriticalSection(&lock->cs);
74 #else
75 #warning "locking not implemented in this platform"
76 #endif
77 }
78
79 static void plugin_lock(plugin_lock_t *lock)
80 {
81     assert(lock);
82
83 #if defined(XP_UNIX)
84     pthread_mutex_lock(&lock->mutex);
85 #elif defined(XP_WIN)
86     EnterCriticalSection(&lock->cs);
87 #else
88 #warning "locking not implemented in this platform"
89 #endif
90 }
91
92 static void plugin_unlock(plugin_lock_t *lock)
93 {
94     assert(lock);
95
96 #if defined(XP_UNIX)
97     pthread_mutex_unlock(&lock->mutex);
98 #elif defined(XP_WIN)
99     LeaveCriticalSection(&lock->cs);
100 #else
101 #warning "locking not implemented in this platform"
102 #endif
103 }
104
105 /*****************************************************************************
106  * Event Object
107  *****************************************************************************/
108 static void handle_input_event(const libvlc_event_t* event, void *param);
109 static void handle_changed_event(const libvlc_event_t* event, void *param);
110
111 static vlcplugin_event_t vlcevents[] = {
112     { "MediaPlayerMediaChanged", libvlc_MediaPlayerMediaChanged, handle_input_event },
113     { "MediaPlayerNothingSpecial", libvlc_MediaPlayerNothingSpecial, handle_input_event },
114     { "MediaPlayerOpening", libvlc_MediaPlayerOpening, handle_input_event },
115     { "MediaPlayerBuffering", libvlc_MediaPlayerBuffering, handle_changed_event },
116     { "MediaPlayerPlaying", libvlc_MediaPlayerPlaying, handle_input_event },
117     { "MediaPlayerPaused", libvlc_MediaPlayerPaused, handle_input_event },
118     { "MediaPlayerStopped", libvlc_MediaPlayerStopped, handle_input_event },
119     { "MediaPlayerForward", libvlc_MediaPlayerForward, handle_input_event },
120     { "MediaPlayerBackward", libvlc_MediaPlayerBackward, handle_input_event },
121     { "MediaPlayerEndReached", libvlc_MediaPlayerEndReached, handle_input_event },
122     { "MediaPlayerEncounteredError", libvlc_MediaPlayerEncounteredError, handle_input_event },
123     { "MediaPlayerTimeChanged", libvlc_MediaPlayerTimeChanged, handle_changed_event },
124     { "MediaPlayerPositionChanged", libvlc_MediaPlayerPositionChanged, handle_changed_event },
125     { "MediaPlayerSeekableChanged", libvlc_MediaPlayerSeekableChanged, handle_changed_event },
126     { "MediaPlayerPausableChanged", libvlc_MediaPlayerPausableChanged, handle_changed_event },
127     { "MediaPlayerTitleChanged", libvlc_MediaPlayerTitleChanged, handle_changed_event },
128     { "MediaPlayerLengthChanged", libvlc_MediaPlayerLengthChanged, handle_changed_event },
129 };
130
131 static void handle_input_event(const libvlc_event_t* event, void *param)
132 {
133     VlcPlugin *plugin = (VlcPlugin*)param;
134     switch( event->type )
135     {
136         case libvlc_MediaPlayerNothingSpecial:
137         case libvlc_MediaPlayerOpening:
138         case libvlc_MediaPlayerPlaying:
139         case libvlc_MediaPlayerPaused:
140         case libvlc_MediaPlayerStopped:
141         case libvlc_MediaPlayerForward:
142         case libvlc_MediaPlayerBackward:
143         case libvlc_MediaPlayerEndReached:
144         case libvlc_MediaPlayerEncounteredError:
145             plugin->event_callback(event, NULL, 0, param);
146             break;
147         default: /* ignore all other libvlc_event_type_t */
148             break;
149     }
150 }
151
152 static void handle_changed_event(const libvlc_event_t* event, void *param)
153 {
154     uint32_t   npcount = 1;
155     NPVariant *npparam = (NPVariant *) NPN_MemAlloc( sizeof(NPVariant) * npcount );
156
157     VlcPlugin *plugin = (VlcPlugin*)param;
158     switch( event->type )
159     {
160         case libvlc_MediaPlayerBuffering:
161             DOUBLE_TO_NPVARIANT(event->u.media_player_buffering.new_cache, npparam[0]);
162             break;
163         case libvlc_MediaPlayerTimeChanged:
164             DOUBLE_TO_NPVARIANT(event->u.media_player_time_changed.new_time, npparam[0]);
165             break;
166         case libvlc_MediaPlayerPositionChanged:
167             DOUBLE_TO_NPVARIANT(event->u.media_player_position_changed.new_position, npparam[0]);
168             break;
169         case libvlc_MediaPlayerSeekableChanged:
170             BOOLEAN_TO_NPVARIANT(event->u.media_player_seekable_changed.new_seekable, npparam[0]);
171             break;
172         case libvlc_MediaPlayerPausableChanged:
173             BOOLEAN_TO_NPVARIANT(event->u.media_player_pausable_changed.new_pausable, npparam[0]);
174             break;
175         case libvlc_MediaPlayerTitleChanged:
176             BOOLEAN_TO_NPVARIANT(event->u.media_player_title_changed.new_title, npparam[0]);
177             break;
178         case libvlc_MediaPlayerLengthChanged:
179             DOUBLE_TO_NPVARIANT(event->u.media_player_length_changed.new_length, npparam[0]);
180             break;
181         default: /* ignore all other libvlc_event_type_t */
182             NPN_MemFree( npparam );
183             return;
184     }
185     plugin->event_callback(event, npparam, npcount, param);
186 }
187
188 bool EventObj::init()
189 {
190     plugin_lock_init(&lock);
191     return true;
192 }
193
194 EventObj::~EventObj()
195 {
196     plugin_lock_destroy(&lock);
197 }
198
199 void EventObj::deliver(NPP browser)
200 {
201     plugin_lock(&lock);
202
203     for( ev_l::iterator iter = _elist.begin(); iter != _elist.end(); ++iter )
204     {
205         for( lr_l::iterator j = _llist.begin(); j != _llist.end(); ++j )
206         {
207             if( j->event_type() == iter->event_type() )
208             {
209                 NPVariant result;
210                 NPVariant *params = iter->params();
211                 uint32_t   count  = iter->count();
212
213                 NPObject *listener = j->listener();
214                 assert( listener );
215
216                 NPN_InvokeDefault( browser, listener, params, count, &result );
217                 NPN_ReleaseVariantValue( &result );
218
219                 for( uint32_t n = 0; n < count; n++ )
220                 {
221                     if( NPVARIANT_IS_STRING(params[n]) )
222                         NPN_MemFree( (void*) NPVARIANT_TO_STRING(params[n]).UTF8Characters );
223                     else if( NPVARIANT_IS_OBJECT(params[n]) )
224                     {
225                         NPN_ReleaseObject( NPVARIANT_TO_OBJECT(params[n]) );
226                         NPN_MemFree( (void*)NPVARIANT_TO_OBJECT(params[n]) );
227                     }
228                 }
229                 if (params) NPN_MemFree( params );
230             }
231         }
232     }
233     _elist.clear();
234
235     plugin_unlock(&lock);
236 }
237
238 void EventObj::callback(const libvlc_event_t* event,
239                         NPVariant *npparams, uint32_t count)
240 {
241     plugin_lock(&lock);
242     _elist.push_back(VLCEvent(event->type, npparams, count));
243     plugin_unlock(&lock);
244 }
245
246 vlcplugin_event_t *EventObj::find_event(const char *s) const
247 {
248     for( int i = 0; i < ARRAY_SIZE(vlcevents); i++ )
249     {
250         if( strncmp(vlcevents[i].name, s, strlen(vlcevents[i].name)) == 0 )
251             return &vlcevents[i];
252     }
253     return NULL;
254 }
255
256 const char *EventObj::find_name(const libvlc_event_t *event)
257 {
258     for( int i = 0; i < ARRAY_SIZE(vlcevents); i++ )
259     {
260         if( vlcevents[i].libvlc_type == event->type )
261             return vlcevents[i].name;
262     }
263     return NULL;
264 }
265
266 bool EventObj::insert(const NPString &name, NPObject *listener, bool bubble)
267 {
268     vlcplugin_event_t *event = find_event(name.UTF8Characters);
269     if( !event )
270         return false;
271
272     lr_l::iterator iter;
273     for( iter = _llist.begin(); iter != _llist.end(); ++iter )
274         if( iter->listener() == listener && iter->bubble() == bubble )
275             break;
276
277     if( iter == _llist.end() )
278     {
279         _llist.push_back(Listener(event, listener, bubble));
280         return true;
281     }
282
283     return false;
284 }
285
286 bool EventObj::remove(const NPString &name, NPObject *listener, bool bubble)
287 {
288     vlcplugin_event_t *event = find_event(name.UTF8Characters);
289     if( !event )
290         return false;
291
292     for( lr_l::iterator iter = _llist.begin(); iter !=_llist.end(); iter++ )
293     {
294         if( iter->event_type() == event->libvlc_type &&
295             iter->listener() == listener &&
296             iter->bubble() == bubble )
297         {
298             iter = _llist.erase(iter);
299             return true;
300         }
301     }
302
303     return false;
304 }
305
306 void EventObj::hook_manager( libvlc_event_manager_t *em, void *userdata )
307 {
308     _em = em;
309
310     if( _em )
311     {
312         /* attach all libvlc events we care about */
313         for( int i = 0; i < ARRAY_SIZE(vlcevents); i++ )
314         {
315             libvlc_event_attach( _em, vlcevents[i].libvlc_type,
316                                       vlcevents[i].libvlc_callback,
317                                       userdata );
318         }
319     }
320 }
321
322 void EventObj::unhook_manager( void *userdata )
323 {
324     if( _em )
325     {
326                 /* detach all libvlc events we cared about */
327         for( int i = 0; i < ARRAY_SIZE(vlcevents); i++ )
328         {
329             libvlc_event_detach( _em, vlcevents[i].libvlc_type,
330                                       vlcevents[i].libvlc_callback,
331                                       userdata );
332         }
333     }
334 }
335
336 /*****************************************************************************
337  * VlcPlugin constructor and destructor
338  *****************************************************************************/
339 #if (((NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR) < 20)
340 VlcPlugin::VlcPlugin( NPP instance, uint16 mode ) :
341 #else
342 VlcPlugin::VlcPlugin( NPP instance, uint16_t mode ) :
343 #endif
344     i_npmode(mode),
345     b_stream(0),
346     b_autoplay(1),
347     b_toolbar(0),
348     psz_text(NULL),
349     psz_target(NULL),
350     playlist_index(-1),
351     libvlc_instance(NULL),
352     libvlc_media_list(NULL),
353     libvlc_media_player(NULL),
354     p_scriptClass(NULL),
355     p_browser(instance),
356     psz_baseURL(NULL)
357 #if defined(XP_WIN)
358     ,pf_wndproc(NULL)
359 #endif
360 #if defined(XP_UNIX)
361     ,i_width((unsigned)-1)
362     ,i_height((unsigned)-1)
363     ,i_tb_width(0)
364     ,i_tb_height(0)
365     ,i_last_position(0)
366     ,p_btnPlay(NULL)
367     ,p_btnPause(NULL)
368     ,p_btnStop(NULL)
369     ,p_btnMute(NULL)
370     ,p_btnUnmute(NULL)
371     ,p_btnFullscreen(NULL)
372     ,p_btnTime(NULL)
373     ,p_timeline(NULL)
374 #endif
375 {
376     memset(&npwindow, 0, sizeof(NPWindow));
377 #if defined(XP_UNIX)
378     memset(&npvideo, 0, sizeof(Window));
379     memset(&npcontrol, 0, sizeof(Window));
380 #endif
381 }
382
383 static bool boolValue(const char *value) {
384     return ( !strcmp(value, "1") ||
385              !strcasecmp(value, "true") ||
386              !strcasecmp(value, "yes") );
387 }
388
389 void VlcPlugin::eventAsync(void *param)
390 {
391     VlcPlugin *plugin = (VlcPlugin*)param;
392     plugin->events.deliver(plugin->getBrowser());
393 }
394
395 void VlcPlugin::event_callback(const libvlc_event_t* event,
396                 NPVariant *npparams, uint32_t npcount, void *userdata)
397 {
398     VlcPlugin *plugin = (VlcPlugin*)userdata;
399 #if defined(XP_UNIX)
400     plugin->events.callback(event, npparams, npcount);
401     NPN_PluginThreadAsyncCall(plugin->getBrowser(), eventAsync, plugin);
402 #else
403 #warning NPN_PluginThreadAsyncCall not implemented yet.
404     printf("No NPN_PluginThreadAsyncCall(), doing nothing.\n");
405 #endif
406 }
407
408 NPError VlcPlugin::init(int argc, char* const argn[], char* const argv[])
409 {
410     /* prepare VLC command line */
411     const char *ppsz_argv[32];
412     int ppsz_argc = 0;
413
414 #ifndef NDEBUG
415     ppsz_argv[ppsz_argc++] = "--no-plugins-cache";
416 #endif
417
418     /* locate VLC module path */
419 #ifdef XP_MACOSX
420     ppsz_argv[ppsz_argc++] = "--plugin-path=/Library/Internet\\ Plug-Ins/VLC\\ Plugin.plugin/Contents/MacOS/plugins";
421     ppsz_argv[ppsz_argc++] = "--vout=minimal_macosx";
422 #elif defined(XP_WIN)
423     HKEY h_key;
424     DWORD i_type, i_data = MAX_PATH + 1;
425     char p_data[MAX_PATH + 1];
426     if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\VideoLAN\\VLC",
427                       0, KEY_READ, &h_key ) == ERROR_SUCCESS )
428     {
429          if( RegQueryValueEx( h_key, "InstallDir", 0, &i_type,
430                               (LPBYTE)p_data, &i_data ) == ERROR_SUCCESS )
431          {
432              if( i_type == REG_SZ )
433              {
434                  strcat( p_data, "\\plugins" );
435                  ppsz_argv[ppsz_argc++] = "--plugin-path";
436                  ppsz_argv[ppsz_argc++] = p_data;
437              }
438          }
439          RegCloseKey( h_key );
440     }
441     ppsz_argv[ppsz_argc++] = "--no-one-instance";
442
443 #endif /* XP_MACOSX */
444
445     /* common settings */
446     ppsz_argv[ppsz_argc++] = "-vv";
447     ppsz_argv[ppsz_argc++] = "--no-stats";
448     ppsz_argv[ppsz_argc++] = "--no-media-library";
449     ppsz_argv[ppsz_argc++] = "--intf=dummy";
450     ppsz_argv[ppsz_argc++] = "--no-video-title-show";
451     ppsz_argv[ppsz_argc++] = "--no-xlib";
452
453     const char *progid = NULL;
454
455     /* parse plugin arguments */
456     for( int i = 0; (i < argc) && (ppsz_argc < 32); i++ )
457     {
458        /* fprintf(stderr, "argn=%s, argv=%s\n", argn[i], argv[i]); */
459
460         if( !strcmp( argn[i], "target" )
461          || !strcmp( argn[i], "mrl")
462          || !strcmp( argn[i], "filename")
463          || !strcmp( argn[i], "src") )
464         {
465             psz_target = argv[i];
466         }
467         else if( !strcmp( argn[i], "text" ) )
468         {
469             free( psz_text );
470             psz_text = strdup( argv[i] );
471         }
472         else if( !strcmp( argn[i], "autoplay")
473               || !strcmp( argn[i], "autostart") )
474         {
475             b_autoplay = boolValue(argv[i]);
476         }
477         else if( !strcmp( argn[i], "fullscreen" ) )
478         {
479             if( boolValue(argv[i]) )
480             {
481                 ppsz_argv[ppsz_argc++] = "--fullscreen";
482             }
483             else
484             {
485                 ppsz_argv[ppsz_argc++] = "--no-fullscreen";
486             }
487         }
488         else if( !strcmp( argn[i], "mute" ) )
489         {
490             if( boolValue(argv[i]) )
491             {
492                 ppsz_argv[ppsz_argc++] = "--volume=0";
493             }
494         }
495         else if( !strcmp( argn[i], "loop")
496               || !strcmp( argn[i], "autoloop") )
497         {
498             if( boolValue(argv[i]) )
499             {
500                 ppsz_argv[ppsz_argc++] = "--loop";
501             }
502             else
503             {
504                 ppsz_argv[ppsz_argc++] = "--no-loop";
505             }
506         }
507         else if( !strcmp( argn[i], "version")
508               || !strcmp( argn[i], "progid") )
509         {
510             progid = argv[i];
511         }
512         else if( !strcmp( argn[i], "toolbar" ) )
513         {
514 /* FIXME: Remove this when toolbar functionality has been implemented on
515  * MacOS X and Win32 for Firefox/Mozilla/Safari. */
516 #ifdef XP_UNIX
517             b_toolbar = boolValue(argv[i]);
518 #endif
519         }
520     }
521
522     libvlc_instance = libvlc_new(ppsz_argc, ppsz_argv);
523     if( !libvlc_instance )
524         return NPERR_GENERIC_ERROR;
525     libvlc_media_list = libvlc_media_list_new(libvlc_instance);
526
527     /*
528     ** fetch plugin base URL, which is the URL of the page containing the plugin
529     ** this URL is used for making absolute URL from relative URL that may be
530     ** passed as an MRL argument
531     */
532     NPObject *plugin = NULL;
533
534     if( NPERR_NO_ERROR == NPN_GetValue(p_browser, NPNVWindowNPObject, &plugin) )
535     {
536         /*
537         ** is there a better way to get that info ?
538         */
539         static const char docLocHref[] = "document.location.href";
540         NPString script;
541         NPVariant result;
542
543         script.UTF8Characters = docLocHref;
544         script.UTF8Length = sizeof(docLocHref)-1;
545
546         if( NPN_Evaluate(p_browser, plugin, &script, &result) )
547         {
548             if( NPVARIANT_IS_STRING(result) )
549             {
550                 NPString &location = NPVARIANT_TO_STRING(result);
551
552                 psz_baseURL = (char *) malloc(location.UTF8Length+1);
553                 if( psz_baseURL )
554                 {
555                     strncpy(psz_baseURL, location.UTF8Characters, location.UTF8Length);
556                     psz_baseURL[location.UTF8Length] = '\0';
557                 }
558             }
559             NPN_ReleaseVariantValue(&result);
560         }
561         NPN_ReleaseObject(plugin);
562     }
563
564     if( psz_target )
565     {
566         // get absolute URL from src
567         char *psz_absurl = getAbsoluteURL(psz_target);
568         psz_target = psz_absurl ? psz_absurl : strdup(psz_target);
569     }
570
571     /* assign plugin script root class */
572     /* new APIs */
573     p_scriptClass = RuntimeNPClass<LibvlcRootNPObject>::getClass();
574
575     if( !events.init() )
576         return NPERR_GENERIC_ERROR;
577
578     return NPERR_NO_ERROR;
579 }
580
581 VlcPlugin::~VlcPlugin()
582 {
583     free(psz_baseURL);
584     free(psz_target);
585     free(psz_text);
586
587     if( libvlc_media_player )
588     {
589         if( playlist_isplaying() )
590             playlist_stop();
591         events.unhook_manager( this );
592         libvlc_media_player_release( libvlc_media_player );
593     }
594     if( libvlc_media_list )
595         libvlc_media_list_release( libvlc_media_list );
596     if( libvlc_instance )
597         libvlc_release( libvlc_instance );
598 }
599
600 /*****************************************************************************
601  * VlcPlugin playlist replacement methods
602  *****************************************************************************/
603 void VlcPlugin::set_player_window()
604 {
605 #ifdef XP_UNIX
606     libvlc_media_player_set_xwindow(libvlc_media_player,
607                                     (uint32_t)getVideoWindow());
608 #endif
609 #ifdef XP_MACOSX
610     // XXX FIXME insert appropriate call here
611 #endif
612 #ifdef XP_WIN
613     libvlc_media_player_set_hwnd(libvlc_media_player,
614                                  getWindow().window);
615 #endif
616 }
617
618 int VlcPlugin::playlist_add( const char *mrl )
619 {
620     int item = -1;
621     libvlc_media_t *p_m = libvlc_media_new_location(libvlc_instance,mrl);
622     if( !p_m )
623         return -1;
624     assert( libvlc_media_list );
625     libvlc_media_list_lock(libvlc_media_list);
626     if( !libvlc_media_list_add_media(libvlc_media_list,p_m) )
627         item = libvlc_media_list_count(libvlc_media_list)-1;
628     libvlc_media_list_unlock(libvlc_media_list);
629
630     libvlc_media_release(p_m);
631
632     return item;
633 }
634
635 int VlcPlugin::playlist_add_extended_untrusted( const char *mrl, const char *name,
636                     int optc, const char **optv )
637 {
638     libvlc_media_t *p_m;
639     int item = -1;
640
641     assert( libvlc_media_list );
642
643     p_m = libvlc_media_new_location(libvlc_instance, mrl);
644     if( !p_m )
645         return -1;
646
647     for( int i = 0; i < optc; ++i )
648         libvlc_media_add_option_flag(p_m, optv[i], libvlc_media_option_unique);
649
650     libvlc_media_list_lock(libvlc_media_list);
651     if( !libvlc_media_list_add_media(libvlc_media_list,p_m) )
652         item = libvlc_media_list_count(libvlc_media_list)-1;
653     libvlc_media_list_unlock(libvlc_media_list);
654     libvlc_media_release(p_m);
655
656     return item;
657 }
658
659 bool VlcPlugin::playlist_select( int idx )
660 {
661     libvlc_media_t *p_m = NULL;
662
663     assert( libvlc_media_list );
664
665     libvlc_media_list_lock(libvlc_media_list);
666
667     int count = libvlc_media_list_count(libvlc_media_list);
668     if( idx<0||idx>=count )
669         goto bad_unlock;
670
671     playlist_index = idx;
672
673     p_m = libvlc_media_list_item_at_index(libvlc_media_list,playlist_index);
674     libvlc_media_list_unlock(libvlc_media_list);
675
676     if( !p_m )
677         return false;
678
679     if( libvlc_media_player )
680     {
681         if( playlist_isplaying() )
682             playlist_stop();
683         events.unhook_manager( this );
684         libvlc_media_player_release( libvlc_media_player );
685         libvlc_media_player = NULL;
686     }
687
688     libvlc_media_player = libvlc_media_player_new_from_media( p_m );
689     if( libvlc_media_player )
690     {
691         set_player_window();
692
693         libvlc_event_manager_t *p_em;
694         p_em = libvlc_media_player_event_manager( libvlc_media_player );
695         events.hook_manager( p_em, this );
696     }
697
698     libvlc_media_release( p_m );
699     return true;
700
701 bad_unlock:
702     libvlc_media_list_unlock( libvlc_media_list );
703     return false;
704 }
705
706 int VlcPlugin::playlist_delete_item( int idx )
707 {
708     if( !libvlc_media_list )
709         return -1;
710     libvlc_media_list_lock(libvlc_media_list);
711     int ret = libvlc_media_list_remove_index(libvlc_media_list,idx);
712     libvlc_media_list_unlock(libvlc_media_list);
713     return ret;
714 }
715
716 void VlcPlugin::playlist_clear()
717 {
718     if( libvlc_media_list )
719         libvlc_media_list_release(libvlc_media_list);
720     libvlc_media_list = libvlc_media_list_new(getVLC());
721 }
722
723 int VlcPlugin::playlist_count()
724 {
725     int items_count = 0;
726     if( !libvlc_media_list )
727         return items_count;
728     libvlc_media_list_lock(libvlc_media_list);
729     items_count = libvlc_media_list_count(libvlc_media_list);
730     libvlc_media_list_unlock(libvlc_media_list);
731     return items_count;
732 }
733
734 void VlcPlugin::toggle_fullscreen()
735 {
736     if( playlist_isplaying() )
737         libvlc_toggle_fullscreen(libvlc_media_player);
738 }
739
740 void VlcPlugin::set_fullscreen( int yes )
741 {
742     if( playlist_isplaying() )
743         libvlc_set_fullscreen(libvlc_media_player,yes);
744 }
745
746 int  VlcPlugin::get_fullscreen()
747 {
748     int r = 0;
749     if( playlist_isplaying() )
750         r = libvlc_get_fullscreen(libvlc_media_player);
751     return r;
752 }
753
754 bool  VlcPlugin::player_has_vout()
755 {
756     bool r = false;
757     if( playlist_isplaying() )
758         r = libvlc_media_player_has_vout(libvlc_media_player);
759     return r;
760 }
761
762 /*****************************************************************************
763  * VlcPlugin methods
764  *****************************************************************************/
765
766 char *VlcPlugin::getAbsoluteURL(const char *url)
767 {
768     if( NULL != url )
769     {
770         // check whether URL is already absolute
771         const char *end=strchr(url, ':');
772         if( (NULL != end) && (end != url) )
773         {
774             // validate protocol header
775             const char *start = url;
776             char c = *start;
777             if( isalpha(c) )
778             {
779                 ++start;
780                 while( start != end )
781                 {
782                     c  = *start;
783                     if( ! (isalnum(c)
784                        || ('-' == c)
785                        || ('+' == c)
786                        || ('.' == c)
787                        || ('/' == c)) ) /* VLC uses / to allow user to specify a demuxer */
788                         // not valid protocol header, assume relative URL
789                         goto relativeurl;
790                     ++start;
791                 }
792                 /* we have a protocol header, therefore URL is absolute */
793                 return strdup(url);
794             }
795             // not a valid protocol header, assume relative URL
796         }
797
798 relativeurl:
799
800         if( psz_baseURL )
801         {
802             size_t baseLen = strlen(psz_baseURL);
803             char *href = (char *) malloc(baseLen+strlen(url)+1);
804             if( href )
805             {
806                 /* prepend base URL */
807                 memcpy(href, psz_baseURL, baseLen+1);
808
809                 /*
810                 ** relative url could be empty,
811                 ** in which case return base URL
812                 */
813                 if( '\0' == *url )
814                     return href;
815
816                 /*
817                 ** locate pathname part of base URL
818                 */
819
820                 /* skip over protocol part  */
821                 char *pathstart = strchr(href, ':');
822                 char *pathend = href+baseLen;
823                 if( pathstart )
824                 {
825                     if( '/' == *(++pathstart) )
826                     {
827                         if( '/' == *(++pathstart) )
828                         {
829                             ++pathstart;
830                         }
831                     }
832                     /* skip over host part */
833                     pathstart = strchr(pathstart, '/');
834                     if( ! pathstart )
835                     {
836                         // no path, add a / past end of url (over '\0')
837                         pathstart = pathend;
838                         *pathstart = '/';
839                     }
840                 }
841                 else
842                 {
843                     /* baseURL is just a UNIX path */
844                     if( '/' != *href )
845                     {
846                         /* baseURL is not an absolute path */
847                         free(href);
848                         return NULL;
849                     }
850                     pathstart = href;
851                 }
852
853                 /* relative URL made of an absolute path ? */
854                 if( '/' == *url )
855                 {
856                     /* replace path completely */
857                     strcpy(pathstart, url);
858                     return href;
859                 }
860
861                 /* find last path component and replace it */
862                 while( '/' != *pathend)
863                     --pathend;
864
865                 /*
866                 ** if relative url path starts with one or more '../',
867                 ** factor them out of href so that we return a
868                 ** normalized URL
869                 */
870                 while( pathend != pathstart )
871                 {
872                     const char *p = url;
873                     if( '.' != *p )
874                         break;
875                     ++p;
876                     if( '\0' == *p  )
877                     {
878                         /* relative url is just '.' */
879                         url = p;
880                         break;
881                     }
882                     if( '/' == *p  )
883                     {
884                         /* relative url starts with './' */
885                         url = ++p;
886                         continue;
887                     }
888                     if( '.' != *p )
889                         break;
890                     ++p;
891                     if( '\0' == *p )
892                     {
893                         /* relative url is '..' */
894                     }
895                     else
896                     {
897                         if( '/' != *p )
898                             break;
899                         /* relative url starts with '../' */
900                         ++p;
901                     }
902                     url = p;
903                     do
904                     {
905                         --pathend;
906                     }
907                     while( '/' != *pathend );
908                 }
909                 /* skip over '/' separator */
910                 ++pathend;
911                 /* concatenate remaining base URL and relative URL */
912                 strcpy(pathend, url);
913             }
914             return href;
915         }
916     }
917     return NULL;
918 }
919
920 #if defined(XP_UNIX)
921 int  VlcPlugin::setSize(unsigned width, unsigned height)
922 {
923     int diff = (width != i_width) || (height != i_height);
924
925     i_width = width;
926     i_height = height;
927
928     /* return size */
929     return diff;
930 }
931
932 #define BTN_SPACE ((unsigned int)4)
933 void VlcPlugin::showToolbar()
934 {
935     const NPWindow& window = getWindow();
936     Window control = getControlWindow();
937     Window video = getVideoWindow();
938     Display *p_display = ((NPSetWindowCallbackStruct *)window.ws_info)->display;
939     unsigned int i_height = 0, i_width = BTN_SPACE;
940
941     /* load icons */
942     if( !p_btnPlay )
943         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/play.xpm",
944                             &p_btnPlay, NULL, NULL);
945     if( p_btnPlay )
946     {
947         i_height = __MAX( i_height, p_btnPlay->height );
948     }
949     if( !p_btnPause )
950         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/pause.xpm",
951                             &p_btnPause, NULL, NULL);
952     if( p_btnPause )
953     {
954         i_height = __MAX( i_height, p_btnPause->height );
955     }
956     i_width += __MAX( p_btnPause->width, p_btnPlay->width );
957
958     if( !p_btnStop )
959         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/stop.xpm",
960                             &p_btnStop, NULL, NULL );
961     if( p_btnStop )
962     {
963         i_height = __MAX( i_height, p_btnStop->height );
964         i_width += BTN_SPACE + p_btnStop->width;
965     }
966     if( !p_timeline )
967         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/time_line.xpm",
968                             &p_timeline, NULL, NULL);
969     if( p_timeline )
970     {
971         i_height = __MAX( i_height, p_timeline->height );
972         i_width += BTN_SPACE + p_timeline->width;
973     }
974     if( !p_btnTime )
975         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/time_icon.xpm",
976                             &p_btnTime, NULL, NULL);
977     if( p_btnTime )
978     {
979         i_height = __MAX( i_height, p_btnTime->height );
980         i_width += BTN_SPACE + p_btnTime->width;
981     }
982     if( !p_btnFullscreen )
983         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/fullscreen.xpm",
984                             &p_btnFullscreen, NULL, NULL);
985     if( p_btnFullscreen )
986     {
987         i_height = __MAX( i_height, p_btnFullscreen->height );
988         i_width += BTN_SPACE + p_btnFullscreen->width;
989     }
990     if( !p_btnMute )
991         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/volume_max.xpm",
992                             &p_btnMute, NULL, NULL);
993     if( p_btnMute )
994     {
995         i_height = __MAX( i_height, p_btnMute->height );
996     }
997     if( !p_btnUnmute )
998         XpmReadFileToImage( p_display, DATA_PATH "/mozilla/volume_mute.xpm",
999                             &p_btnUnmute, NULL, NULL);
1000     if( p_btnUnmute )
1001     {
1002         i_height = __MAX( i_height, p_btnUnmute->height );
1003     }
1004     i_width += BTN_SPACE + __MAX( p_btnUnmute->width, p_btnMute->width );
1005
1006     setToolbarSize( i_width, i_height );
1007
1008     if( !p_btnPlay || !p_btnPause || !p_btnStop || !p_timeline ||
1009         !p_btnTime || !p_btnFullscreen || !p_btnMute || !p_btnUnmute )
1010         fprintf(stderr, "Error: some button images not found in %s\n", DATA_PATH );
1011
1012     /* reset panels position and size */
1013     /* XXX  use i_width */
1014     XResizeWindow( p_display, video, window.width, window.height - i_height);
1015     XMoveWindow( p_display, control, 0, window.height - i_height );
1016     XResizeWindow( p_display, control, window.width, i_height -1);
1017
1018     b_toolbar = 1; /* says toolbar is now shown */
1019     redrawToolbar();
1020 }
1021
1022 void VlcPlugin::hideToolbar()
1023 {
1024     const NPWindow& window = getWindow();
1025     Display *p_display = ((NPSetWindowCallbackStruct *)window.ws_info)->display;
1026     Window control = getControlWindow();
1027     Window video = getVideoWindow();
1028
1029     i_tb_width = i_tb_height = 0;
1030
1031     if( p_btnPlay )  XDestroyImage( p_btnPlay );
1032     if( p_btnPause ) XDestroyImage( p_btnPause );
1033     if( p_btnStop )  XDestroyImage( p_btnStop );
1034     if( p_timeline ) XDestroyImage( p_timeline );
1035     if( p_btnTime )  XDestroyImage( p_btnTime );
1036     if( p_btnFullscreen ) XDestroyImage( p_btnFullscreen );
1037     if( p_btnMute )  XDestroyImage( p_btnMute );
1038     if( p_btnUnmute ) XDestroyImage( p_btnUnmute );
1039
1040     p_btnPlay = NULL;
1041     p_btnPause = NULL;
1042     p_btnStop = NULL;
1043     p_timeline = NULL;
1044     p_btnTime = NULL;
1045     p_btnFullscreen = NULL;
1046     p_btnMute = NULL;
1047     p_btnUnmute = NULL;
1048
1049     /* reset panels position and size */
1050     /* XXX  use i_width */
1051     XResizeWindow( p_display, video, window.width, window.height );
1052     XMoveWindow( p_display, control, 0, window.height-1 );
1053     XResizeWindow( p_display, control, window.width, 1 );
1054
1055     b_toolbar = 0; /* says toolbar is now hidden */
1056     redrawToolbar();
1057 }
1058
1059 void VlcPlugin::redrawToolbar()
1060 {
1061     int is_playing = 0;
1062     bool b_mute = false;
1063     unsigned int dst_x, dst_y;
1064     GC gc;
1065     XGCValues gcv;
1066     unsigned int i_tb_width, i_tb_height;
1067
1068     /* This method does nothing if toolbar is hidden. */
1069     if( !b_toolbar || !libvlc_media_player )
1070         return;
1071
1072     const NPWindow& window = getWindow();
1073     Window control = getControlWindow();
1074     Display *p_display = ((NPSetWindowCallbackStruct *)window.ws_info)->display;
1075
1076     getToolbarSize( &i_tb_width, &i_tb_height );
1077
1078     /* get mute info */
1079     b_mute = libvlc_audio_get_mute( libvlc_media_player );
1080
1081     gcv.foreground = BlackPixel( p_display, 0 );
1082     gc = XCreateGC( p_display, control, GCForeground, &gcv );
1083
1084     XFillRectangle( p_display, control, gc,
1085                     0, 0, window.width, i_tb_height );
1086     gcv.foreground = WhitePixel( p_display, 0 );
1087     XChangeGC( p_display, gc, GCForeground, &gcv );
1088
1089     /* position icons */
1090     dst_x = BTN_SPACE;
1091     dst_y = i_tb_height >> 1; /* baseline = vertical middle */
1092
1093     if( p_btnPause && (is_playing == 1) )
1094     {
1095         XPutImage( p_display, control, gc, p_btnPause, 0, 0, dst_x,
1096                    dst_y - (p_btnPause->height >> 1),
1097                    p_btnPause->width, p_btnPause->height );
1098         dst_x += BTN_SPACE + p_btnPause->width;
1099     }
1100     else if( p_btnPlay )
1101     {
1102         XPutImage( p_display, control, gc, p_btnPlay, 0, 0, dst_x,
1103                    dst_y - (p_btnPlay->height >> 1),
1104                    p_btnPlay->width, p_btnPlay->height );
1105         dst_x += BTN_SPACE + p_btnPlay->width;
1106     }
1107
1108     if( p_btnStop )
1109         XPutImage( p_display, control, gc, p_btnStop, 0, 0, dst_x,
1110                    dst_y - (p_btnStop->height >> 1),
1111                    p_btnStop->width, p_btnStop->height );
1112
1113     dst_x += BTN_SPACE + ( p_btnStop ? p_btnStop->width : 0 );
1114
1115     if( p_btnFullscreen )
1116         XPutImage( p_display, control, gc, p_btnFullscreen, 0, 0, dst_x,
1117                    dst_y - (p_btnFullscreen->height >> 1),
1118                    p_btnFullscreen->width, p_btnFullscreen->height );
1119
1120     dst_x += BTN_SPACE + ( p_btnFullscreen ? p_btnFullscreen->width : 0 );
1121
1122     if( p_btnUnmute && b_mute )
1123     {
1124         XPutImage( p_display, control, gc, p_btnUnmute, 0, 0, dst_x,
1125                    dst_y - (p_btnUnmute->height >> 1),
1126                    p_btnUnmute->width, p_btnUnmute->height );
1127
1128         dst_x += BTN_SPACE + ( p_btnUnmute ? p_btnUnmute->width : 0 );
1129     }
1130     else if( p_btnMute )
1131     {
1132         XPutImage( p_display, control, gc, p_btnMute, 0, 0, dst_x,
1133                    dst_y - (p_btnMute->height >> 1),
1134                    p_btnMute->width, p_btnMute->height );
1135
1136         dst_x += BTN_SPACE + ( p_btnMute ? p_btnMute->width : 0 );
1137     }
1138
1139     if( p_timeline )
1140         XPutImage( p_display, control, gc, p_timeline, 0, 0, dst_x,
1141                    dst_y - (p_timeline->height >> 1),
1142                    (window.width-(dst_x+BTN_SPACE)), p_timeline->height );
1143
1144     /* get movie position in % */
1145     if( playlist_isplaying() )
1146     {
1147         i_last_position = (int)((window.width-(dst_x+BTN_SPACE))*
1148                    libvlc_media_player_get_position(libvlc_media_player));
1149     }
1150
1151     if( p_btnTime )
1152         XPutImage( p_display, control, gc, p_btnTime,
1153                    0, 0, (dst_x+i_last_position),
1154                    dst_y - (p_btnTime->height >> 1),
1155                    p_btnTime->width, p_btnTime->height );
1156
1157     XFreeGC( p_display, gc );
1158 }
1159
1160 vlc_toolbar_clicked_t VlcPlugin::getToolbarButtonClicked( int i_xpos, int i_ypos )
1161 {
1162     unsigned int i_dest = BTN_SPACE;
1163     int is_playing = 0;
1164     bool b_mute = false;
1165
1166 #ifndef NDEBUG
1167     fprintf( stderr, "ToolbarButtonClicked:: "
1168                      "trying to match (%d,%d) (%d,%d)\n",
1169              i_xpos, i_ypos, i_tb_height, i_tb_width );
1170 #endif
1171     if( i_ypos >= i_tb_width )
1172         return clicked_Unknown;
1173
1174     /* Note: the order of testing is dependend on the original
1175      * drawing positions of the icon buttons. Buttons are tested
1176      * left to right.
1177      */
1178
1179     /* get isplaying */
1180     is_playing = playlist_isplaying();
1181
1182     /* get mute info */
1183     if( libvlc_media_player )
1184         b_mute = libvlc_audio_get_mute( libvlc_media_player );
1185
1186     /* is Pause of Play button clicked */
1187     if( (is_playing != 1) &&
1188         (i_xpos >= (BTN_SPACE>>1)) &&
1189         (i_xpos <= i_dest + p_btnPlay->width + (BTN_SPACE>>1)) )
1190         return clicked_Play;
1191     else if( (i_xpos >= (BTN_SPACE>>1))  &&
1192              (i_xpos <= i_dest + p_btnPause->width) )
1193         return clicked_Pause;
1194
1195     /* is Stop button clicked */
1196     if( is_playing != 1 )
1197         i_dest += (p_btnPlay->width + (BTN_SPACE>>1));
1198     else
1199         i_dest += (p_btnPause->width + (BTN_SPACE>>1));
1200
1201     if( (i_xpos >= i_dest) &&
1202         (i_xpos <= i_dest + p_btnStop->width + (BTN_SPACE>>1)) )
1203         return clicked_Stop;
1204
1205     /* is Fullscreen button clicked */
1206     i_dest += (p_btnStop->width + (BTN_SPACE>>1));
1207     if( (i_xpos >= i_dest) &&
1208         (i_xpos <= i_dest + p_btnFullscreen->width + (BTN_SPACE>>1)) )
1209         return clicked_Fullscreen;
1210
1211     /* is Mute or Unmute button clicked */
1212     i_dest += (p_btnFullscreen->width + (BTN_SPACE>>1));
1213     if( !b_mute && (i_xpos >= i_dest) &&
1214         (i_xpos <= i_dest + p_btnMute->width + (BTN_SPACE>>1)) )
1215         return clicked_Mute;
1216     else if( (i_xpos >= i_dest) &&
1217              (i_xpos <= i_dest + p_btnUnmute->width + (BTN_SPACE>>1)) )
1218         return clicked_Unmute;
1219
1220     /* is timeline clicked */
1221     if( !b_mute )
1222         i_dest += (p_btnMute->width + (BTN_SPACE>>1));
1223     else
1224         i_dest += (p_btnUnmute->width + (BTN_SPACE>>1));
1225     if( (i_xpos >= i_dest) &&
1226         (i_xpos <= i_dest + p_timeline->width + (BTN_SPACE>>1)) )
1227         return clicked_timeline;
1228
1229     /* is time button clicked */
1230     i_dest += (p_timeline->width + (BTN_SPACE>>1));
1231     if( (i_xpos >= i_dest) &&
1232         (i_xpos <= i_dest + p_btnTime->width + (BTN_SPACE>>1)) )
1233         return clicked_Time;
1234
1235     return clicked_Unknown;
1236 }
1237 #undef BTN_SPACE
1238 #endif
1239
1240 // Verifies the version of the NPAPI.
1241 // The eventListeners use a NPAPI function available
1242 // since Gecko 1.9.
1243 bool VlcPlugin::canUseEventListener()
1244 {
1245     int plugin_major, plugin_minor;
1246     int browser_major, browser_minor;
1247
1248     NPN_Version(&plugin_major, &plugin_minor,
1249                 &browser_major, &browser_minor);
1250
1251     if (browser_minor >= 19 || browser_major > 0)
1252         return true;
1253     return false;
1254 }