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