]> git.sesse.net Git - vlc/blob - projects/mozilla/vlcplugin.cpp
Mozilla-plugin linux toolbar can be hidden by adding show_toolbar="no" to the
[vlc] / projects / mozilla / vlcplugin.cpp
1 /*****************************************************************************
2  * vlcplugin.cpp: a VLC plugin for Mozilla
3  *****************************************************************************
4  * Copyright (C) 2002-2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          Damien Fouilleul <damienf.fouilleul@laposte.net>
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 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include "config.h"
29
30 #ifdef HAVE_MOZILLA_CONFIG_H
31 #   include <mozilla-config.h>
32 #endif
33
34 #include "vlcplugin.h"
35 #include "control/npovlc.h"
36 #include "control/npolibvlc.h"
37
38 #include <ctype.h>
39
40 /*****************************************************************************
41  * VlcPlugin constructor and destructor
42  *****************************************************************************/
43 VlcPlugin::VlcPlugin( NPP instance, uint16 mode ) :
44     i_npmode(mode),
45     b_stream(0),
46     b_autoplay(1),
47     b_show_toolbar(1),
48 #if XP_UNIX
49     i_control_height(45),
50 #endif
51     psz_target(NULL),
52     libvlc_instance(NULL),
53     libvlc_log(NULL),
54     p_scriptClass(NULL),
55     p_browser(instance),
56     psz_baseURL(NULL)
57 #if XP_WIN
58     ,pf_wndproc(NULL)
59 #endif
60 #if XP_UNIX
61     ,i_width((unsigned)-1)
62     ,i_height((unsigned)-1)
63     ,i_last_position(0)
64 #endif
65 {
66     memset(&npwindow, 0, sizeof(NPWindow));
67 }
68
69 static bool boolValue(const char *value) {
70     return ( !strcmp(value, "1") ||
71              !strcasecmp(value, "true") ||
72              !strcasecmp(value, "yes") );
73 }
74
75 NPError VlcPlugin::init(int argc, char* const argn[], char* const argv[])
76 {
77     /* prepare VLC command line */
78     char *ppsz_argv[32];
79     int ppsz_argc = 0;
80
81     /* locate VLC module path */
82 #ifdef XP_MACOSX
83     ppsz_argv[ppsz_argc++] = "--plugin-path";
84     ppsz_argv[ppsz_argc++] = "/Library/Internet Plug-Ins/VLC Plugin.plugin/"
85                              "Contents/MacOS/modules";
86 #elif defined(XP_WIN)
87     HKEY h_key;
88     DWORD i_type, i_data = MAX_PATH + 1;
89     char p_data[MAX_PATH + 1];
90     if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\VideoLAN\\VLC",
91                       0, KEY_READ, &h_key ) == ERROR_SUCCESS )
92     {
93          if( RegQueryValueEx( h_key, "InstallDir", 0, &i_type,
94                               (LPBYTE)p_data, &i_data ) == ERROR_SUCCESS )
95          {
96              if( i_type == REG_SZ )
97              {
98                  strcat( p_data, "\\plugins" );
99                  ppsz_argv[ppsz_argc++] = "--plugin-path";
100                  ppsz_argv[ppsz_argc++] = p_data;
101              }
102          }
103          RegCloseKey( h_key );
104     }
105     ppsz_argv[ppsz_argc++] = "--no-one-instance";
106
107 #endif /* XP_MACOSX */
108
109     /* common settings */
110     ppsz_argv[ppsz_argc++] = "-vv";
111     ppsz_argv[ppsz_argc++] = "--no-stats";
112     ppsz_argv[ppsz_argc++] = "--no-media-library";
113     ppsz_argv[ppsz_argc++] = "--intf";
114     ppsz_argv[ppsz_argc++] = "dummy";
115
116     const char *progid = NULL;
117
118     /* parse plugin arguments */
119     for( int i = 0; i < argc ; i++ )
120     {
121         fprintf(stderr, "argn=%s, argv=%s\n", argn[i], argv[i]);
122
123         if( !strcmp( argn[i], "target" )
124          || !strcmp( argn[i], "mrl")
125          || !strcmp( argn[i], "filename")
126          || !strcmp( argn[i], "src") )
127         {
128             psz_target = argv[i];
129         }
130         else if( !strcmp( argn[i], "autoplay")
131               || !strcmp( argn[i], "autostart") )
132         {
133             b_autoplay = boolValue(argv[i]);
134         }
135         else if( !strcmp( argn[i], "fullscreen" ) )
136         {
137             if( boolValue(argv[i]) )
138             {
139                 ppsz_argv[ppsz_argc++] = "--fullscreen";
140             }
141             else
142             {
143                 ppsz_argv[ppsz_argc++] = "--no-fullscreen";
144             }
145         }
146         else if( !strcmp( argn[i], "mute" ) )
147         {
148             if( boolValue(argv[i]) )
149             {
150                 ppsz_argv[ppsz_argc++] = "--volume";
151                 ppsz_argv[ppsz_argc++] = "0";
152             }
153         }
154         else if( !strcmp( argn[i], "loop")
155               || !strcmp( argn[i], "autoloop") )
156         {
157             if( boolValue(argv[i]) )
158             {
159                 ppsz_argv[ppsz_argc++] = "--loop";
160             }
161             else {
162                 ppsz_argv[ppsz_argc++] = "--no-loop";
163             }
164         }
165         else if( !strcmp( argn[i], "version")
166               || !strcmp( argn[i], "progid") )
167         {
168             progid = argv[i];
169         }
170         else if( !strcmp( argn[i], "show_toolbar" ) )
171         {
172             b_show_toolbar = boolValue(argv[i]);
173         }
174     }
175
176
177
178     libvlc_instance = libvlc_new(ppsz_argc, ppsz_argv, NULL);
179     if( ! libvlc_instance )
180     {
181         return NPERR_GENERIC_ERROR;
182     }
183
184     /*
185     ** fetch plugin base URL, which is the URL of the page containing the plugin
186     ** this URL is used for making absolute URL from relative URL that may be
187     ** passed as an MRL argument
188     */
189     NPObject *plugin;
190
191     if( NPERR_NO_ERROR == NPN_GetValue(p_browser, NPNVWindowNPObject, &plugin) )
192     {
193         /*
194         ** is there a better way to get that info ?
195         */
196         static const char docLocHref[] = "document.location.href";
197         NPString script;
198         NPVariant result;
199
200         script.utf8characters = docLocHref;
201         script.utf8length = sizeof(docLocHref)-1;
202
203         if( NPN_Evaluate(p_browser, plugin, &script, &result) )
204         {
205             if( NPVARIANT_IS_STRING(result) )
206             {
207                 NPString &location = NPVARIANT_TO_STRING(result);
208
209                 psz_baseURL = new char[location.utf8length+1];
210                 if( psz_baseURL )
211                 {
212                     strncpy(psz_baseURL, location.utf8characters, location.utf8length);
213                     psz_baseURL[location.utf8length] = '\0';
214                 }
215             }
216             NPN_ReleaseVariantValue(&result);
217         }
218         NPN_ReleaseObject(plugin);
219     }
220
221     if( psz_target )
222     {
223         // get absolute URL from src
224         char *psz_absurl = getAbsoluteURL(psz_target);
225         psz_target = psz_absurl ? psz_absurl : strdup(psz_target);
226     }
227
228     /* assign plugin script root class */
229     if( (NULL != progid) && (!strcmp(progid, "VideoLAN.VLCPlugin.2")) )
230     {
231         /* new APIs */
232         p_scriptClass = RuntimeNPClass<LibvlcRootNPObject>::getClass();
233     }
234     else
235     {
236         /* legacy APIs */
237         p_scriptClass = RuntimeNPClass<VlcNPObject>::getClass();
238     }
239
240     return NPERR_NO_ERROR;
241 }
242
243 #if 0
244 #ifdef XP_WIN
245 /* This is really ugly but there is a deadlock when stopping a stream
246  * (in VLC_CleanUp()) because the video output is a child of the drawable but
247  * is in a different thread. */
248 static void HackStopVout( VlcPlugin* p_plugin )
249 {
250     MSG msg;
251     HWND hwnd;
252     vlc_value_t value;
253
254     int i_vlc = libvlc_get_vlc_id(p_plugin->libvlc_instance);
255     VLC_VariableGet( i_vlc, "drawable", &value );
256
257     hwnd = FindWindowEx( (HWND)value.i_int, 0, 0, 0 );
258     if( !hwnd ) return;
259
260     PostMessage( hwnd, WM_CLOSE, 0, 0 );
261
262     do
263     {
264         while( PeekMessage( &msg, (HWND)value.i_int, 0, 0, PM_REMOVE ) )
265         {
266             TranslateMessage(&msg);
267             DispatchMessage(&msg);
268         }
269         if( FindWindowEx( (HWND)value.i_int, 0, 0, 0 ) ) Sleep( 10 );
270     }
271     while( (hwnd = FindWindowEx( (HWND)value.i_int, 0, 0, 0 )) );
272 }
273 #endif /* XP_WIN */
274 #endif
275
276 VlcPlugin::~VlcPlugin()
277 {
278     delete psz_baseURL;
279     delete psz_target;
280     if( libvlc_log )
281         libvlc_log_close(libvlc_log, NULL);
282     if( libvlc_instance )
283         libvlc_release(libvlc_instance);
284 }
285
286 /*****************************************************************************
287  * VlcPlugin methods
288  *****************************************************************************/
289
290 char *VlcPlugin::getAbsoluteURL(const char *url)
291 {
292     if( NULL != url )
293     {
294         // check whether URL is already absolute
295         const char *end=strchr(url, ':');
296         if( (NULL != end) && (end != url) )
297         {
298             // validate protocol header
299             const char *start = url;
300             char c = *start;
301             if( isalpha(c) )
302             {
303                 ++start;
304                 while( start != end )
305                 {
306                     c  = *start;
307                     if( ! (isalnum(c)
308                        || ('-' == c)
309                        || ('+' == c)
310                        || ('.' == c)
311                        || ('/' == c)) ) /* VLC uses / to allow user to specify a demuxer */
312                         // not valid protocol header, assume relative URL
313                         goto relativeurl;
314                     ++start;
315                 }
316                 /* we have a protocol header, therefore URL is absolute */
317                 return strdup(url);
318             }
319             // not a valid protocol header, assume relative URL
320         }
321
322 relativeurl:
323
324         if( psz_baseURL )
325         {
326             size_t baseLen = strlen(psz_baseURL);
327             char *href = new char[baseLen+strlen(url)+1];
328             if( href )
329             {
330                 /* prepend base URL */
331                 strcpy(href, psz_baseURL);
332
333                 /*
334                 ** relative url could be empty,
335                 ** in which case return base URL
336                 */
337                 if( '\0' == *url )
338                     return href;
339
340                 /*
341                 ** locate pathname part of base URL
342                 */
343
344                 /* skip over protocol part  */
345                 char *pathstart = strchr(href, ':');
346                 char *pathend;
347                 if( pathstart )
348                 {
349                     if( '/' == *(++pathstart) )
350                     {
351                         if( '/' == *(++pathstart) )
352                         {
353                             ++pathstart;
354                         }
355                     }
356                     /* skip over host part */
357                     pathstart = strchr(pathstart, '/');
358                     pathend = href+baseLen;
359                     if( ! pathstart )
360                     {
361                         // no path, add a / past end of url (over '\0')
362                         pathstart = pathend;
363                         *pathstart = '/';
364                     }
365                 }
366                 else
367                 {
368                     /* baseURL is just a UNIX path */
369                     if( '/' != *href )
370                     {
371                         /* baseURL is not an absolute path */
372                         return NULL;
373                     }
374                     pathstart = href;
375                     pathend = href+baseLen;
376                 }
377
378                 /* relative URL made of an absolute path ? */
379                 if( '/' == *url )
380                 {
381                     /* replace path completely */
382                     strcpy(pathstart, url);
383                     return href;
384                 }
385
386                 /* find last path component and replace it */
387                 while( '/' != *pathend)
388                     --pathend;
389
390                 /*
391                 ** if relative url path starts with one or more '../',
392                 ** factor them out of href so that we return a
393                 ** normalized URL
394                 */
395                 while( pathend != pathstart )
396                 {
397                     const char *p = url;
398                     if( '.' != *p )
399                         break;
400                     ++p;
401                     if( '\0' == *p  )
402                     {
403                         /* relative url is just '.' */
404                         url = p;
405                         break;
406                     }
407                     if( '/' == *p  )
408                     {
409                         /* relative url starts with './' */
410                         url = ++p;
411                         continue;
412                     }
413                     if( '.' != *p )
414                         break;
415                     ++p;
416                     if( '\0' == *p )
417                     {
418                         /* relative url is '..' */
419                     }
420                     else
421                     {
422                         if( '/' != *p )
423                             break;
424                         /* relative url starts with '../' */
425                         ++p;
426                     }
427                     url = p;
428                     do
429                     {
430                         --pathend;
431                     }
432                     while( '/' != *pathend );
433                 }
434                 /* skip over '/' separator */
435                 ++pathend;
436                 /* concatenate remaining base URL and relative URL */
437                 strcpy(pathend, url);
438             }
439             return href;
440         }
441     }
442     return NULL;
443 }
444
445 #if XP_UNIX
446 int  VlcPlugin::setSize(unsigned width, unsigned height)
447 {
448     int diff = (width != i_width) || (height != i_height);
449
450     i_width = width;
451     i_height = height;
452
453     /* return size */
454     return diff;
455 }
456
457 void VlcPlugin::showToolbar()
458 {
459     const NPWindow& window = getWindow();
460     Window control = getControlWindow();
461     Display *p_display = ((NPSetWindowCallbackStruct *)window.ws_info)->display;
462
463     /* load icons */
464     XpmReadFileToImage( p_display, DATA_PATH "/mozilla/play.xpm",
465                         &p_btnPlay, NULL, NULL);
466     if( p_btnPlay )
467         i_control_height = __MAX( i_control_height, p_btnPlay->height );
468
469     XpmReadFileToImage( p_display, DATA_PATH "/mozilla/pause.xpm",
470                         &p_btnPause, NULL, NULL);
471     if( p_btnPause )
472         i_control_height = __MAX( i_control_height, p_btnPause->height );
473
474     XpmReadFileToImage( p_display, DATA_PATH "/mozilla/stop.xpm",
475                         &p_btnStop, NULL, NULL );
476     if( p_btnStop )
477         i_control_height = __MAX( i_control_height, p_btnStop->height );
478
479     XpmReadFileToImage( p_display, DATA_PATH "/mozilla/time_line.xpm",
480                         &p_timeline, NULL, NULL);
481     if( p_timeline )
482         i_control_height = __MAX( i_control_height, p_timeline->height );
483
484     XpmReadFileToImage( p_display, DATA_PATH "/mozilla/time_icon.xpm",
485                         &p_btnTime, NULL, NULL);
486     if( p_btnTime )
487         i_control_height = __MAX( i_control_height, p_btnTime->height );
488
489     XpmReadFileToImage( p_display, DATA_PATH "/mozilla/fullscreen.xpm",
490                         &p_btnFullscreen, NULL, NULL);
491     if( p_btnFullscreen )
492         i_control_height = __MAX( i_control_height, p_btnFullscreen->height);
493
494     XpmReadFileToImage( p_display, DATA_PATH "/mozilla/volume_max.xpm",
495                         &p_btnMute, NULL, NULL);
496     if( p_btnMute )
497         i_control_height = __MAX( i_control_height, p_btnMute->height);
498
499     XpmReadFileToImage( p_display, DATA_PATH "/mozilla/volume_mute.xpm",
500                         &p_btnUnmute, NULL, NULL);
501     if( p_btnUnmute )
502         i_control_height = __MAX( i_control_height, p_btnUnmute->height);
503
504     if( !p_btnPlay || !p_btnPause || !p_btnStop || !p_timeline ||
505         !p_btnTime || !p_btnFullscreen || !p_btnMute || !p_btnUnmute )
506         fprintf(stderr, "Error: some button images not found in %s\n", DATA_PATH );
507 }
508
509 void VlcPlugin::hideToolbar()
510 {
511     if( p_btnPlay )  XDestroyImage( p_btnPlay );
512     if( p_btnPause ) XDestroyImage( p_btnPause );
513     if( p_btnStop )  XDestroyImage( p_btnStop );
514     if( p_timeline ) XDestroyImage( p_timeline );
515     if( p_btnTime )  XDestroyImage( p_btnTime );
516     if( p_btnFullscreen ) XDestroyImage( p_btnFullscreen );
517     if( p_btnMute )  XDestroyImage( p_btnMute );
518     if( p_btnUnmute ) XDestroyImage( p_btnUnmute );
519
520     p_btnPlay = NULL;
521     p_btnPause = NULL;
522     p_btnStop = NULL;
523     p_timeline = NULL;
524     p_btnTime = NULL;
525     p_btnFullscreen = NULL;
526     p_btnMute = NULL;
527     p_btnUnmute = NULL;
528 }
529
530 void VlcPlugin::redrawToolbar()
531 {
532     libvlc_media_instance_t *p_md = NULL;
533     libvlc_exception_t ex;
534     float f_position = 0.0;
535     int i_playing = 0;
536     bool b_mute = false;
537
538     GC gc;
539     XGCValues gcv;
540
541     const NPWindow& window = getWindow();
542     Window control = getControlWindow();
543     Display *p_display = ((NPSetWindowCallbackStruct *)window.ws_info)->display;
544
545     /* get media instance */
546     libvlc_exception_init( &ex );
547     p_md = libvlc_playlist_get_media_instance( getVLC(), &ex );
548     libvlc_exception_clear( &ex );
549
550     /* get isplaying */
551     libvlc_exception_init( &ex );
552     i_playing = libvlc_playlist_isplaying( getVLC(), &ex );
553     libvlc_exception_clear( &ex );
554
555     /* get mute info */
556     libvlc_exception_init(&ex);
557     b_mute = libvlc_audio_get_mute( getVLC(), &ex );
558     libvlc_exception_clear( &ex );
559
560     /* get movie position in % */
561     if( i_playing == 1 )
562     {
563         libvlc_exception_init( &ex );
564         f_position = libvlc_media_instance_get_position( p_md, &ex ) * 100;
565         libvlc_exception_clear( &ex );
566     }
567     libvlc_media_instance_release( p_md );
568
569     gcv.foreground = BlackPixel( p_display, 0 );
570     gc = XCreateGC( p_display, control, GCForeground, &gcv );
571
572     XFillRectangle( p_display, control, gc,
573                     0, 0, window.width, i_control_height );
574     gcv.foreground = WhitePixel( p_display, 0 );
575     XChangeGC( p_display, gc, GCForeground, &gcv );
576
577     /* position icons */
578     fprintf( stderr, ">>>>>> is playing = %d\n", i_playing );
579     if( p_btnPause && (i_playing == 1) )
580     {
581         XPutImage( p_display, control, gc, p_btnPause, 0, 0, 4, 14,
582                    p_btnPause->width, p_btnPause->height );
583     }
584     else if( p_btnPlay )
585     {
586         XPutImage( p_display, control, gc, p_btnPlay, 0, 0, 4, 14,
587                    p_btnPlay->width, p_btnPlay->height );
588     }
589
590     if( p_btnStop )
591         XPutImage( p_display, control, gc, p_btnStop, 0, 0, 39, 14,
592                    p_btnStop->width, p_btnStop->height );
593     if( p_btnFullscreen )
594         XPutImage( p_display, control, gc, p_btnFullscreen, 0, 0, 67, 21,
595                    p_btnFullscreen->width, p_btnFullscreen->height );
596
597     if( p_btnUnmute && b_mute )
598     {
599         XPutImage( p_display, control, gc, p_btnUnmute, 0, 0, 94, 30,
600                    p_btnUnmute->width, p_btnUnmute->height );
601     }
602     else if( p_btnMute )
603     {
604         XPutImage( p_display, control, gc, p_btnMute, 0, 0, 94, 30,
605                    p_btnMute->width, p_btnMute->height );
606     }
607
608     if( p_timeline )
609         XPutImage( p_display, control, gc, p_timeline, 0, 0, 4, 4,
610                    (window.width-8), p_timeline->height );
611
612     if( f_position > 0 )
613         i_last_position = (((float)window.width-8.0)/100.0)*f_position;
614     if( p_btnTime )
615         XPutImage( p_display, control, gc, p_btnTime,
616                    0, 0, (4+i_last_position), 2,
617                    p_btnTime->width, p_btnTime->height );
618
619     XFreeGC( p_display, gc );
620 }
621 #endif