]> git.sesse.net Git - vlc/blob - activex/plugin.cpp
- activex: fix plugin path search, now relative to DLL location & registry settings
[vlc] / activex / plugin.cpp
1 /*****************************************************************************
2  * plugin.cpp: ActiveX control for VLC
3  *****************************************************************************
4  * Copyright (C) 2006 the VideoLAN team
5  *
6  * Authors: Damien Fouilleul <Damien.Fouilleul@laposte.net>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 #include "plugin.h"
24
25 #include "oleobject.h"
26 #include "olecontrol.h"
27 #include "oleinplaceobject.h"
28 #include "oleinplaceactiveobject.h"
29 #include "persistpropbag.h"
30 #include "persiststreaminit.h"
31 #include "persiststorage.h"
32 #include "provideclassinfo.h"
33 #include "connectioncontainer.h"
34 #include "objectsafety.h"
35 #include "vlccontrol.h"
36 #include "vlccontrol2.h"
37 #include "viewobject.h"
38 #include "dataobject.h"
39
40 #include "utils.h"
41
42 #include <string.h>
43 #include <winreg.h>
44 #include <winuser.h>
45 #include <servprov.h>
46 #include <shlwapi.h>
47 #include <wininet.h>
48
49 using namespace std;
50
51 ////////////////////////////////////////////////////////////////////////
52 //class factory
53
54 static LRESULT CALLBACK VLCInPlaceClassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
55     switch( uMsg )
56     {
57         case WM_ERASEBKGND:
58             return 1L;
59
60         case WM_PAINT:
61             PAINTSTRUCT ps;
62             if( GetUpdateRect(hWnd, NULL, FALSE) )
63             {
64                 BeginPaint(hWnd, &ps);
65                 EndPaint(hWnd, &ps);
66             }
67             return 0L;
68
69         default:
70             return DefWindowProc(hWnd, uMsg, wParam, lParam);
71     }
72 };
73
74 static LRESULT CALLBACK VLCVideoClassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
75     VLCPlugin *p_instance = reinterpret_cast<VLCPlugin *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
76
77     switch( uMsg )
78     {
79         case WM_ERASEBKGND:
80             return 1L;
81
82         case WM_PAINT:
83             PAINTSTRUCT ps;
84             RECT pr;
85             if( GetUpdateRect(hWnd, &pr, FALSE) )
86             {
87                 RECT bounds;
88                 GetClientRect(hWnd, &bounds);
89                 BeginPaint(hWnd, &ps);
90                 p_instance->onPaint(ps.hdc, bounds, pr);
91                 EndPaint(hWnd, &ps);
92             }
93             return 0L;
94
95         default:
96             return DefWindowProc(hWnd, uMsg, wParam, lParam);
97     }
98 };
99
100 VLCPluginClass::VLCPluginClass(LONG *p_class_ref, HINSTANCE hInstance, REFCLSID rclsid) :
101     _p_class_ref(p_class_ref),
102     _hinstance(hInstance),
103     _classid(rclsid),
104     _inplace_picture(NULL)
105 {
106     WNDCLASS wClass;
107
108     if( ! GetClassInfo(hInstance, getInPlaceWndClassName(), &wClass) )
109     {
110         wClass.style          = CS_NOCLOSE|CS_DBLCLKS;
111         wClass.lpfnWndProc    = VLCInPlaceClassWndProc;
112         wClass.cbClsExtra     = 0;
113         wClass.cbWndExtra     = 0;
114         wClass.hInstance      = hInstance;
115         wClass.hIcon          = NULL;
116         wClass.hCursor        = LoadCursor(NULL, IDC_ARROW);
117         wClass.hbrBackground  = NULL;
118         wClass.lpszMenuName   = NULL;
119         wClass.lpszClassName  = getInPlaceWndClassName();
120        
121         _inplace_wndclass_atom = RegisterClass(&wClass);
122     }
123     else
124     {
125         _inplace_wndclass_atom = 0;
126     }
127
128     if( ! GetClassInfo(hInstance, getVideoWndClassName(), &wClass) )
129     {
130         wClass.style          = CS_NOCLOSE;
131         wClass.lpfnWndProc    = VLCVideoClassWndProc;
132         wClass.cbClsExtra     = 0;
133         wClass.cbWndExtra     = 0;
134         wClass.hInstance      = hInstance;
135         wClass.hIcon          = NULL;
136         wClass.hCursor        = LoadCursor(NULL, IDC_ARROW);
137         wClass.hbrBackground  = NULL;
138         wClass.lpszMenuName   = NULL;
139         wClass.lpszClassName  = getVideoWndClassName();
140        
141         _video_wndclass_atom = RegisterClass(&wClass);
142     }
143     else
144     {
145         _video_wndclass_atom = 0;
146     }
147
148     HBITMAP hbitmap = (HBITMAP)LoadImage(getHInstance(), TEXT("INPLACE-PICT"), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
149     if( NULL != hbitmap )
150     {
151         PICTDESC pictDesc;
152
153         pictDesc.cbSizeofstruct = sizeof(PICTDESC);
154         pictDesc.picType        = PICTYPE_BITMAP;
155         pictDesc.bmp.hbitmap    = hbitmap;
156         pictDesc.bmp.hpal       = NULL;
157
158         if( FAILED(OleCreatePictureIndirect(&pictDesc, IID_IPicture, TRUE, reinterpret_cast<LPVOID*>(&_inplace_picture))) )
159             _inplace_picture = NULL;
160     }
161     AddRef();
162 };
163
164 VLCPluginClass::~VLCPluginClass()
165 {
166     if( 0 != _inplace_wndclass_atom )
167         UnregisterClass(MAKEINTATOM(_inplace_wndclass_atom), _hinstance);
168
169     if( 0 != _video_wndclass_atom )
170         UnregisterClass(MAKEINTATOM(_video_wndclass_atom), _hinstance);
171
172     if( NULL != _inplace_picture )
173         _inplace_picture->Release();
174 };
175
176 STDMETHODIMP VLCPluginClass::QueryInterface(REFIID riid, void **ppv)
177 {
178     if( NULL == ppv )
179         return E_INVALIDARG;
180
181     if( (IID_IUnknown == riid)
182      || (IID_IClassFactory == riid) )
183     {
184         AddRef();
185         *ppv = reinterpret_cast<LPVOID>(this);
186
187         return NOERROR;
188     }
189
190     *ppv = NULL;
191
192     return E_NOINTERFACE;
193 };
194
195 STDMETHODIMP_(ULONG) VLCPluginClass::AddRef(void)
196 {
197     return InterlockedIncrement(_p_class_ref);
198 };
199
200 STDMETHODIMP_(ULONG) VLCPluginClass::Release(void)
201 {
202     ULONG refcount = InterlockedDecrement(_p_class_ref);
203     if( 0 == refcount )
204     {
205         delete this;
206         return 0;
207     }
208     return refcount;
209 };
210
211 STDMETHODIMP VLCPluginClass::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void **ppv)
212 {
213     if( NULL == ppv )
214         return E_POINTER;
215
216     *ppv = NULL;
217
218     if( (NULL != pUnkOuter) && (IID_IUnknown != riid) ) {
219         return CLASS_E_NOAGGREGATION;
220     }
221
222     VLCPlugin *plugin = new VLCPlugin(this, pUnkOuter);
223     if( NULL != plugin )
224     {
225         HRESULT hr = plugin->QueryInterface(riid, ppv);
226         // the following will destroy the object if QueryInterface() failed
227         plugin->Release();
228         return hr;
229     }
230     return E_OUTOFMEMORY;
231 };
232
233 STDMETHODIMP VLCPluginClass::LockServer(BOOL fLock)
234 {
235     if( fLock )
236         AddRef();
237     else 
238         Release();
239
240     return S_OK;
241 };
242
243 ////////////////////////////////////////////////////////////////////////
244
245 VLCPlugin::VLCPlugin(VLCPluginClass *p_class, LPUNKNOWN pUnkOuter) :
246     _inplacewnd(NULL),
247     _videownd(NULL),
248     _p_class(p_class),
249     _i_ref(1UL),
250     _p_libvlc(NULL),
251     _i_codepage(CP_ACP),
252     _b_usermode(TRUE)
253 {
254     p_class->AddRef();
255
256     vlcOleControl = new VLCOleControl(this);
257     vlcOleInPlaceObject = new VLCOleInPlaceObject(this);
258     vlcOleInPlaceActiveObject = new VLCOleInPlaceActiveObject(this);
259     vlcPersistStorage = new VLCPersistStorage(this);
260     vlcPersistStreamInit = new VLCPersistStreamInit(this);
261     vlcPersistPropertyBag = new VLCPersistPropertyBag(this);
262     vlcProvideClassInfo = new VLCProvideClassInfo(this);
263     vlcConnectionPointContainer = new VLCConnectionPointContainer(this);
264     vlcObjectSafety = new VLCObjectSafety(this);
265     vlcControl = new VLCControl(this);
266     vlcControl2 = new VLCControl2(this);
267     vlcViewObject = new VLCViewObject(this);
268     vlcDataObject = new VLCDataObject(this);
269     vlcOleObject = new VLCOleObject(this);
270
271     // configure controlling IUnknown interface for implemented interfaces
272     this->pUnkOuter = (NULL != pUnkOuter) ? pUnkOuter : dynamic_cast<LPUNKNOWN>(this);
273
274     // default picure
275     _p_pict = p_class->getInPlacePict();
276
277     // make sure that persistable properties are initialized
278     onInit();
279 };
280
281 VLCPlugin::~VLCPlugin()
282 {
283     delete vlcOleObject;
284     delete vlcDataObject;
285     delete vlcViewObject;
286     delete vlcControl2;
287     delete vlcControl;
288     delete vlcConnectionPointContainer;
289     delete vlcProvideClassInfo;
290     delete vlcPersistPropertyBag;
291     delete vlcPersistStreamInit;
292     delete vlcPersistStorage;
293     delete vlcOleInPlaceActiveObject;
294     delete vlcOleInPlaceObject;
295     delete vlcObjectSafety;
296
297     delete vlcOleControl;
298     if( _p_pict )
299         _p_pict->Release();
300
301     SysFreeString(_bstr_mrl);
302
303     _p_class->Release();
304 };
305
306 STDMETHODIMP VLCPlugin::QueryInterface(REFIID riid, void **ppv)
307 {
308     if( NULL == ppv )
309         return E_INVALIDARG;
310
311     if( IID_IUnknown == riid )
312         *ppv = reinterpret_cast<LPVOID>(this);
313     else if( IID_IOleObject == riid )
314         *ppv = reinterpret_cast<LPVOID>(vlcOleObject);
315     else if( IID_IOleControl == riid )
316         *ppv = reinterpret_cast<LPVOID>(vlcOleControl);
317     else if( IID_IOleWindow == riid )
318         *ppv = reinterpret_cast<LPVOID>(vlcOleInPlaceObject);
319     else if( IID_IOleInPlaceObject == riid )
320         *ppv = reinterpret_cast<LPVOID>(vlcOleInPlaceObject);
321     else if( IID_IOleInPlaceActiveObject == riid )
322         *ppv = reinterpret_cast<LPVOID>(vlcOleInPlaceActiveObject);
323     else if( IID_IPersist == riid )
324         *ppv = reinterpret_cast<LPVOID>(vlcPersistStreamInit);
325     else if( IID_IPersistStreamInit == riid )
326         *ppv = reinterpret_cast<LPVOID>(vlcPersistStreamInit);
327     else if( IID_IPersistStorage == riid )
328         *ppv = reinterpret_cast<LPVOID>(vlcPersistStorage);
329     else if( IID_IPersistPropertyBag == riid )
330         *ppv = reinterpret_cast<LPVOID>(vlcPersistPropertyBag);
331     else if( IID_IProvideClassInfo == riid )
332         *ppv = reinterpret_cast<LPVOID>(vlcProvideClassInfo);
333     else if( IID_IProvideClassInfo2 == riid )
334         *ppv = reinterpret_cast<LPVOID>(vlcProvideClassInfo);
335     else if( IID_IConnectionPointContainer == riid )
336         *ppv = reinterpret_cast<LPVOID>(vlcConnectionPointContainer);
337     else if( IID_IObjectSafety == riid )
338         *ppv = reinterpret_cast<LPVOID>(vlcObjectSafety);
339     else if( IID_IDispatch == riid )
340         *ppv = (CLSID_VLCPlugin2 == getClassID()) ?
341                 reinterpret_cast<LPVOID>(vlcControl2) :
342                 reinterpret_cast<LPVOID>(vlcControl);
343     else if( IID_IVLCControl == riid )
344         *ppv = reinterpret_cast<LPVOID>(vlcControl);
345     else if( IID_IVLCControl2 == riid )
346         *ppv = reinterpret_cast<LPVOID>(vlcControl2);
347     else if( IID_IViewObject == riid )
348         *ppv = reinterpret_cast<LPVOID>(vlcViewObject);
349     else if( IID_IViewObject2 == riid )
350         *ppv = reinterpret_cast<LPVOID>(vlcViewObject);
351     else if( IID_IDataObject == riid )
352         *ppv = reinterpret_cast<LPVOID>(vlcDataObject);
353     else
354     {
355         *ppv = NULL;
356         return E_NOINTERFACE;
357     }
358     ((LPUNKNOWN)*ppv)->AddRef();
359     return NOERROR;
360 };
361
362 STDMETHODIMP_(ULONG) VLCPlugin::AddRef(void)
363 {
364     return InterlockedIncrement((LONG *)&_i_ref);
365 };
366
367 STDMETHODIMP_(ULONG) VLCPlugin::Release(void)
368 {
369     if( ! InterlockedDecrement((LONG *)&_i_ref) )
370     {
371         delete this;
372         return 0;
373     }
374     return _i_ref;
375 };
376
377 //////////////////////////////////////
378
379 /*
380 ** we use a window to represent plugin viewport,
381 ** whose geometry is limited by the clipping rectangle
382 ** all drawing within this window must follow must
383 ** follow coordinates system described in lprPosRect
384 */
385
386 static void getViewportCoords(LPRECT lprPosRect, LPRECT lprClipRect)
387 {
388     RECT bounds;
389     bounds.right  = lprPosRect->right-lprPosRect->left;
390
391     if( lprClipRect->left <= lprPosRect->left )
392     {
393         // left side is not clipped out
394         bounds.left = 0;
395
396         if( lprClipRect->right >= lprPosRect->right )
397         {
398             // right side is not clipped out, no change
399         }
400         else if( lprClipRect->right >= lprPosRect->left )
401         {
402             // right side is clipped out
403             lprPosRect->right = lprClipRect->right;
404         }
405         else
406         {
407             // outside of clipping rectange, not visible
408             lprPosRect->right = lprPosRect->left;
409         }
410     }
411     else
412     {
413         // left side is clipped out
414         bounds.left = lprPosRect->left-lprClipRect->left;
415         bounds.right += bounds.left;
416
417         lprPosRect->left = lprClipRect->left;
418         if( lprClipRect->right >= lprPosRect->right )
419         {
420             // right side is not clipped out
421         }
422         else
423         {
424             // right side is clipped out
425             lprPosRect->right = lprClipRect->right;
426         }
427     }
428
429     bounds.bottom = lprPosRect->bottom-lprPosRect->top;
430
431     if( lprClipRect->top <= lprPosRect->top )
432     {
433         // top side is not clipped out
434         bounds.top = 0;
435
436         if( lprClipRect->bottom >= lprPosRect->bottom )
437         {
438             // bottom side is not clipped out, no change
439         }
440         else if( lprClipRect->bottom >= lprPosRect->top )
441         {
442             // bottom side is clipped out
443             lprPosRect->bottom = lprClipRect->bottom;
444         }
445         else
446         {
447             // outside of clipping rectange, not visible
448             lprPosRect->right = lprPosRect->left;
449         }
450     }
451     else
452     {
453         bounds.top = lprPosRect->top-lprClipRect->top;
454         bounds.bottom += bounds.top;
455
456         lprPosRect->top = lprClipRect->top;
457         if( lprClipRect->bottom >= lprPosRect->bottom )
458         {
459             // bottom side is not clipped out
460         }
461         else
462         {
463             // bottom side is clipped out
464             lprPosRect->bottom = lprClipRect->bottom;
465         }
466     }
467     *lprClipRect = *lprPosRect;
468     *lprPosRect  = bounds;
469 };
470
471 HRESULT VLCPlugin::onInit(void)
472 {
473     if( NULL == _p_libvlc )
474     {
475         // initialize persistable properties
476         _b_autoplay = TRUE;
477         _b_autoloop = FALSE;
478         _bstr_baseurl = NULL;
479         _bstr_mrl = NULL;
480         _b_visible = TRUE;
481         _b_mute = FALSE;
482         _i_volume = 50;
483         _i_time   = 0;
484         // set default/preferred size (320x240) pixels in HIMETRIC
485         HDC hDC = CreateDevDC(NULL);
486         _extent.cx = 320;
487         _extent.cy = 240;
488         HimetricFromDP(hDC, (LPPOINT)&_extent, 1);
489         DeleteDC(hDC);
490
491         return S_OK;
492     }
493     return CO_E_ALREADYINITIALIZED;
494 };
495
496 HRESULT VLCPlugin::onLoad(void)
497 {
498     if( SysStringLen(_bstr_baseurl) == 0 )
499     {
500         /*
501         ** try to retreive the base URL using the client site moniker, which for Internet Explorer
502         ** is the URL of the page the plugin is embedded into. 
503         */
504         LPOLECLIENTSITE pClientSite;
505         if( SUCCEEDED(vlcOleObject->GetClientSite(&pClientSite)) && (NULL != pClientSite) )
506         {
507             IBindCtx *pBC = 0;
508             if( SUCCEEDED(CreateBindCtx(0, &pBC)) )
509             {
510                 LPMONIKER pContMoniker = NULL;
511                 if( SUCCEEDED(pClientSite->GetMoniker(OLEGETMONIKER_ONLYIFTHERE,
512                                 OLEWHICHMK_CONTAINER, &pContMoniker)) )
513                 {
514                     LPOLESTR base_url;
515                     if( SUCCEEDED(pContMoniker->GetDisplayName(pBC, NULL, &base_url)) )
516                     {
517                         /*
518                         ** check that the moniker name is a URL
519                         */
520                         if( UrlIsW(base_url, URLIS_URL) )
521                         {
522                             /* copy base URL */
523                             _bstr_baseurl = SysAllocString(base_url);
524                         }
525                         CoTaskMemFree(base_url);
526                     }
527                 }
528             }
529         }
530     }
531     setDirty(FALSE);
532     return S_OK;
533 };
534
535 HRESULT VLCPlugin::getVLCObject(int* i_vlc)
536 {
537     libvlc_instance_t *p_libvlc;
538     HRESULT result = getVLC(&p_libvlc);
539     if( SUCCEEDED(result) )
540     {
541         *i_vlc = libvlc_get_vlc_id(p_libvlc);
542     }
543     else
544     {
545         *i_vlc = 0;
546     }
547     return result;
548 }
549
550 HRESULT VLCPlugin::getVLC(libvlc_instance_t** pp_libvlc)
551 {
552     extern HMODULE DllGetModule();
553
554     if( ! isRunning() )
555     {
556         /*
557         ** default initialization options
558         */
559         char *ppsz_argv[32] = { "vlc" };
560         int   ppsz_argc = 1;
561
562         HKEY h_key;
563         DWORD i_type, i_data = MAX_PATH + 1;
564         char p_data[MAX_PATH + 1];
565         if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\VideoLAN\\VLC",
566                           0, KEY_READ, &h_key ) == ERROR_SUCCESS )
567         {
568              if( RegQueryValueEx( h_key, "InstallDir", 0, &i_type,
569                                   (LPBYTE)p_data, &i_data ) == ERROR_SUCCESS )
570              {
571                  if( i_type == REG_SZ )
572                  {
573                      strcat( p_data, "\\plugins" );
574                      ppsz_argv[ppsz_argc++] = "--plugin-path";
575                      ppsz_argv[ppsz_argc++] = p_data;
576                  }
577              }
578              RegCloseKey( h_key );
579         }
580
581         char p_path[MAX_PATH+1];
582         DWORD len = GetModuleFileNameA(DllGetModule(), p_path, sizeof(p_path));
583         if( len > 0 )
584         {
585             p_path[len] = '\0';
586             ppsz_argv[0] = p_path;
587         }
588
589         // make sure plugin isn't affected with VLC single instance mode
590         ppsz_argv[ppsz_argc++] = "--no-one-instance";
591
592         /* common settings */
593         ppsz_argv[ppsz_argc++] = "-vv";
594         ppsz_argv[ppsz_argc++] = "--no-stats";
595         ppsz_argv[ppsz_argc++] = "--no-media-library";
596         ppsz_argv[ppsz_argc++] = "--intf";
597         ppsz_argv[ppsz_argc++] = "dummy";
598
599         // loop mode is a configuration option only
600         if( _b_autoloop )
601             ppsz_argv[ppsz_argc++] = "--loop";
602
603         // initial volume setting
604         char volBuffer[16];
605         ppsz_argv[ppsz_argc++] = "--volume";
606         if( _b_mute )
607         {
608            ppsz_argv[ppsz_argc++] = "0";
609         }
610         else
611         {
612             snprintf(volBuffer, sizeof(volBuffer), "%d", _i_volume);
613             ppsz_argv[ppsz_argc++] = volBuffer;
614         }
615             
616         if( IsDebuggerPresent() )
617         {
618             /*
619             ** VLC default threading mechanism is designed to be as compatible
620             ** with POSIX as possible. However when debugged on win32, threads
621             ** lose signals and eventually VLC get stuck during initialization.
622             ** threading support can be configured to be more debugging friendly
623             ** but it will be less compatible with POSIX.
624             ** This is done by initializing with the following options:
625             */
626             ppsz_argv[ppsz_argc++] = "--fast-mutex";
627             ppsz_argv[ppsz_argc++] = "--win9x-cv-method=1";
628         }
629
630         _p_libvlc = libvlc_new(ppsz_argc, ppsz_argv, NULL);
631         if( NULL == _p_libvlc )
632         {
633             *pp_libvlc = NULL;
634             return E_FAIL;
635         }
636
637         if( SysStringLen(_bstr_mrl) > 0 )
638         {
639             char *psz_mrl = NULL;
640
641             if( SysStringLen(_bstr_baseurl) > 0 )
642             {
643                 DWORD len = INTERNET_MAX_URL_LENGTH;
644                 LPOLESTR abs_url = (LPOLESTR)CoTaskMemAlloc(sizeof(OLECHAR)*len);
645                 if( NULL != abs_url )
646                 {
647                     /*
648                     ** if the MRL a relative URL, we should end up with an absolute URL
649                     */
650                     if( SUCCEEDED(UrlCombineW(_bstr_baseurl, _bstr_mrl, abs_url, &len,
651                                     URL_ESCAPE_UNSAFE|URL_PLUGGABLE_PROTOCOL)) )
652                     {
653                         psz_mrl = CStrFromBSTR(CP_UTF8, abs_url);
654                     }
655                     else
656                     {
657                         psz_mrl = CStrFromBSTR(CP_UTF8, _bstr_mrl);
658                     }
659                     CoTaskMemFree(abs_url);
660                 }
661             }
662             else
663             {
664                 /*
665                 ** baseURL is empty, assume MRL is absolute
666                 */
667                 psz_mrl = CStrFromBSTR(CP_UTF8, _bstr_mrl);
668             }
669             if( NULL != psz_mrl )
670             {
671                 const char *options[1];
672                 int i_options = 0;
673
674                 char timeBuffer[32];
675                 if( _i_time )
676                 {
677                     snprintf(timeBuffer, sizeof(timeBuffer), ":start-time=%d", _i_time);
678                     options[i_options++] = timeBuffer;
679                 }
680                 // add default target to playlist
681                 libvlc_playlist_add_extended(_p_libvlc, psz_mrl, NULL, i_options, options, NULL);
682                 CoTaskMemFree(psz_mrl);
683             }
684         }
685     }
686     *pp_libvlc = _p_libvlc;
687     return S_OK;
688 };
689
690 HRESULT VLCPlugin::onAmbientChanged(LPUNKNOWN pContainer, DISPID dispID)
691 {
692     VARIANT v;
693     switch( dispID )
694     {
695         case DISPID_AMBIENT_BACKCOLOR:
696             break;
697         case DISPID_AMBIENT_DISPLAYNAME:
698             break;
699         case DISPID_AMBIENT_FONT:
700             break;
701         case DISPID_AMBIENT_FORECOLOR:
702             break;
703         case DISPID_AMBIENT_LOCALEID:
704             break;
705         case DISPID_AMBIENT_MESSAGEREFLECT:
706             break;
707         case DISPID_AMBIENT_SCALEUNITS:
708             break;
709         case DISPID_AMBIENT_TEXTALIGN:
710             break;
711         case DISPID_AMBIENT_USERMODE:
712             VariantInit(&v);
713             V_VT(&v) = VT_BOOL;
714             if( SUCCEEDED(GetObjectProperty(pContainer, dispID, v)) )
715             {
716                 setUserMode(V_BOOL(&v) != VARIANT_FALSE);
717             }
718             break;
719         case DISPID_AMBIENT_UIDEAD:
720             break;
721         case DISPID_AMBIENT_SHOWGRABHANDLES:
722             break;
723         case DISPID_AMBIENT_SHOWHATCHING:
724             break;
725         case DISPID_AMBIENT_DISPLAYASDEFAULT:
726             break;
727         case DISPID_AMBIENT_SUPPORTSMNEMONICS:
728             break;
729         case DISPID_AMBIENT_AUTOCLIP:
730             break;
731         case DISPID_AMBIENT_APPEARANCE:
732             break;
733         case DISPID_AMBIENT_CODEPAGE:
734             VariantInit(&v);
735             V_VT(&v) = VT_I4;
736             if( SUCCEEDED(GetObjectProperty(pContainer, dispID, v)) )
737             {
738                 setCodePage(V_I4(&v));
739             }
740             break;
741         case DISPID_AMBIENT_PALETTE:
742             break;
743         case DISPID_AMBIENT_CHARSET:
744             break;
745         case DISPID_AMBIENT_RIGHTTOLEFT:
746             break;
747         case DISPID_AMBIENT_TOPTOBOTTOM:
748             break;
749         case DISPID_UNKNOWN:
750             /*
751             ** multiple property change, look up the ones we are interested in
752             */
753             VariantInit(&v);
754             V_VT(&v) = VT_BOOL;
755             if( SUCCEEDED(GetObjectProperty(pContainer, DISPID_AMBIENT_USERMODE, v)) )
756             {
757                 setUserMode(V_BOOL(&v) != VARIANT_FALSE);
758             }
759             VariantInit(&v);
760             V_VT(&v) = VT_I4;
761             if( SUCCEEDED(GetObjectProperty(pContainer, DISPID_AMBIENT_CODEPAGE, v)) )
762             {
763                 setCodePage(V_I4(&v));
764             }
765             break;
766     }
767     return S_OK;
768 };
769
770 HRESULT VLCPlugin::onClose(DWORD dwSaveOption)
771 {
772     if( isInPlaceActive() )
773     {
774         onInPlaceDeactivate();
775     }
776     if( isRunning() )
777     {
778         libvlc_instance_t* p_libvlc = _p_libvlc;
779
780         _p_libvlc = NULL;
781         vlcDataObject->onClose();
782
783         libvlc_destroy(p_libvlc, NULL );
784     }
785     return S_OK;
786 };
787
788 BOOL VLCPlugin::isInPlaceActive(void)
789 {
790     return (NULL != _inplacewnd);
791 };
792
793 HRESULT VLCPlugin::onActivateInPlace(LPMSG lpMesg, HWND hwndParent, LPCRECT lprcPosRect, LPCRECT lprcClipRect)
794 {
795     RECT posRect = *lprcPosRect;
796     RECT clipRect = *lprcClipRect;
797
798     /*
799     ** record keeping of control geometry within container
800     */ 
801     _posRect = posRect;
802
803     /*
804     ** convert posRect & clipRect to match control viewport coordinates
805     */
806     getViewportCoords(&posRect, &clipRect);
807
808     /*
809     ** Create a window for in place activated control.
810     ** the window geometry matches the control viewport
811     ** within container so that embedded video is always
812     ** properly clipped.
813     */
814     _inplacewnd = CreateWindow(_p_class->getInPlaceWndClassName(),
815             "VLC Plugin In-Place Window",
816             WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,
817             clipRect.left,
818             clipRect.top,
819             clipRect.right-clipRect.left,
820             clipRect.bottom-clipRect.top,
821             hwndParent,
822             0,
823             _p_class->getHInstance(),
824             NULL
825            );
826
827     if( NULL == _inplacewnd )
828         return E_FAIL;
829
830     SetWindowLongPtr(_inplacewnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
831
832     /*
833     ** VLC embedded video automatically grows to cover client
834     ** area of parent window.
835     ** hence create a such a 'parent' window whose geometry
836     ** is always correct relative to the viewport bounds
837     */
838     _videownd = CreateWindow(_p_class->getVideoWndClassName(),
839             "VLC Plugin Video Window",
840             WS_CHILD|WS_CLIPCHILDREN|WS_VISIBLE,
841             posRect.left,
842             posRect.top,
843             posRect.right-posRect.left,
844             posRect.bottom-posRect.top,
845             _inplacewnd,
846             0,
847             _p_class->getHInstance(),
848             NULL
849            );
850
851     if( NULL == _videownd )
852     {
853         DestroyWindow(_inplacewnd);
854         return E_FAIL;
855     }
856
857     SetWindowLongPtr(_videownd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
858
859     if( _b_usermode )
860     {
861         /* will run vlc if not done already */
862         libvlc_instance_t* p_libvlc;
863         HRESULT result = getVLC(&p_libvlc);
864         if( FAILED(result) )
865             return result;
866
867         /* set internal video width and height */
868         libvlc_video_set_size(p_libvlc,
869             posRect.right-posRect.left,
870             posRect.bottom-posRect.top,
871             NULL );
872
873         /* set internal video parent window */
874         libvlc_video_set_parent(p_libvlc,
875             reinterpret_cast<libvlc_drawable_t>(_videownd), NULL);
876
877         if( _b_autoplay & (libvlc_playlist_items_count(p_libvlc, NULL) > 0) )
878         {
879             libvlc_playlist_play(p_libvlc, 0, 0, NULL, NULL);
880             fireOnPlayEvent();
881         }
882     }
883
884     if( isVisible() )
885         ShowWindow(_inplacewnd, SW_SHOW);
886
887     return S_OK;
888 };
889
890 HRESULT VLCPlugin::onInPlaceDeactivate(void)
891 {
892     if( isRunning() )
893     {
894         libvlc_playlist_stop(_p_libvlc, NULL);
895         fireOnStopEvent();
896     }
897
898     DestroyWindow(_videownd);
899     _videownd = NULL;
900     DestroyWindow(_inplacewnd);
901     _inplacewnd = NULL;
902  
903     return S_OK;
904 };
905
906 void VLCPlugin::setVisible(BOOL fVisible)
907 {
908     if( fVisible != _b_visible )
909     {
910         _b_visible = fVisible;
911         if( isInPlaceActive() )
912         {
913             ShowWindow(_inplacewnd, fVisible ? SW_SHOW : SW_HIDE);
914             if( fVisible )
915                 InvalidateRect(_videownd, NULL, TRUE);
916         }
917         setDirty(TRUE);
918         firePropChangedEvent(DISPID_Visible);
919     }
920 };
921
922 void VLCPlugin::setVolume(int volume)
923 {
924     if( volume < 0 )
925         volume = 0;
926     else if( volume > 200 )
927         volume = 200;
928
929     if( volume != _i_volume )
930     {
931         _i_volume = volume;
932         if( isRunning() )
933         {
934             libvlc_audio_set_volume(_p_libvlc, _i_volume, NULL);
935         }
936         setDirty(TRUE);
937     }
938 };
939
940 void VLCPlugin::setTime(int seconds)
941 {
942     if( seconds < 0 )
943         seconds = 0;
944
945     if( seconds != _i_time )
946     {
947         setStartTime(_i_time);
948         if( isRunning() )
949         {
950             libvlc_input_t *p_input = libvlc_playlist_get_input(_p_libvlc, NULL);
951             if( NULL != p_input )
952             {
953                 libvlc_input_set_time(p_input, _i_time, NULL);
954                 libvlc_input_free(p_input);
955             }
956         }
957     }
958 };
959
960 void VLCPlugin::setFocus(BOOL fFocus)
961 {
962     if( fFocus )
963         SetActiveWindow(_inplacewnd);
964 };
965
966 BOOL VLCPlugin::hasFocus(void)
967 {
968     return GetActiveWindow() == _inplacewnd;
969 };
970
971 void VLCPlugin::onDraw(DVTARGETDEVICE * ptd, HDC hicTargetDev,
972         HDC hdcDraw, LPCRECTL lprcBounds, LPCRECTL lprcWBounds)
973 {
974     if( isVisible() )
975     {
976         long width = lprcBounds->right-lprcBounds->left;
977         long height = lprcBounds->bottom-lprcBounds->top;
978
979         RECT bounds = { lprcBounds->left, lprcBounds->top, lprcBounds->right, lprcBounds->bottom };
980         FillRect(hdcDraw, &bounds, (HBRUSH)GetStockObject(WHITE_BRUSH));
981
982         LPPICTURE pict = getPicture();
983         if( NULL != pict )
984         {
985             OLE_XSIZE_HIMETRIC picWidth;
986             OLE_YSIZE_HIMETRIC picHeight;
987
988             pict->get_Width(&picWidth);
989             pict->get_Height(&picHeight);
990
991             SIZEL picSize = { picWidth, picHeight };
992
993             if( NULL != hicTargetDev )
994             {
995                 DPFromHimetric(hicTargetDev, (LPPOINT)&picSize, 1);
996             }
997             else if( NULL != (hicTargetDev = CreateDevDC(ptd)) )
998             {
999                 DPFromHimetric(hicTargetDev, (LPPOINT)&picSize, 1);
1000                 DeleteDC(hicTargetDev);
1001             }
1002
1003             if( picSize.cx > width-4 )
1004                 picSize.cx = width-4;
1005             if( picSize.cy > height-4 )
1006                 picSize.cy = height-4;
1007
1008             LONG dstX = lprcBounds->left+(width-picSize.cx)/2;
1009             LONG dstY = lprcBounds->top+(height-picSize.cy)/2;
1010
1011             if( NULL != lprcWBounds )
1012             {
1013                 RECT wBounds = { lprcWBounds->left, lprcWBounds->top, lprcWBounds->right, lprcWBounds->bottom };
1014                 pict->Render(hdcDraw, dstX, dstY, picSize.cx, picSize.cy,
1015                         0L, picHeight, picWidth, -picHeight, &wBounds);
1016             }
1017             else 
1018                 pict->Render(hdcDraw, dstX, dstY, picSize.cx, picSize.cy,
1019                         0L, picHeight, picWidth, -picHeight, NULL);
1020
1021             pict->Release();
1022         }
1023
1024         SelectObject(hdcDraw, GetStockObject(BLACK_BRUSH));
1025
1026         MoveToEx(hdcDraw, bounds.left, bounds.top, NULL);
1027         LineTo(hdcDraw, bounds.left+width-1, bounds.top);
1028         LineTo(hdcDraw, bounds.left+width-1, bounds.top+height-1);
1029         LineTo(hdcDraw, bounds.left, bounds.top+height-1);
1030         LineTo(hdcDraw, bounds.left, bounds.top);
1031     }
1032 };
1033
1034 void VLCPlugin::onPaint(HDC hdc, const RECT &bounds, const RECT &clipRect)
1035 {
1036     if( isVisible() )
1037     {
1038         /** if VLC is playing, it may not display any VIDEO content 
1039         ** hence, draw control logo*/
1040         HDC hdcDraw = CreateCompatibleDC(hdc);
1041         if( NULL != hdcDraw )
1042         {
1043             SIZEL size = getExtent();
1044             DPFromHimetric(hdc, (LPPOINT)&size, 1);
1045             RECTL posRect = { 0, 0, size.cx, size.cy };
1046
1047             int width = bounds.right-bounds.left;
1048             int height = bounds.bottom-bounds.top;
1049
1050             HBITMAP hBitmap = CreateCompatibleBitmap(hdc, width, height);
1051             if( NULL != hBitmap )
1052             {
1053                 HBITMAP oldBmp = (HBITMAP)SelectObject(hdcDraw, hBitmap);
1054
1055                 if( (size.cx != width) || (size.cy != height) )
1056                 {
1057                     // needs to scale canvas
1058                     SetMapMode(hdcDraw, MM_ANISOTROPIC);
1059                     SetWindowExtEx(hdcDraw, size.cx, size.cy, NULL);
1060                     SetViewportExtEx(hdcDraw, width, height, NULL);
1061                 }
1062
1063                 onDraw(NULL, hdc, hdcDraw, &posRect, NULL);
1064
1065                 SetMapMode(hdcDraw, MM_TEXT);
1066                 BitBlt(hdc, bounds.left, bounds.top,
1067                         width, height,
1068                         hdcDraw, 0, 0,
1069                         SRCCOPY);
1070
1071                 SelectObject(hdcDraw, oldBmp);
1072                 DeleteObject(hBitmap);
1073             }
1074             DeleteDC(hdcDraw);
1075         }
1076     }
1077 };
1078
1079 void VLCPlugin::onPositionChange(LPCRECT lprcPosRect, LPCRECT lprcClipRect)
1080 {
1081     RECT clipRect = *lprcClipRect;
1082     RECT posRect  = *lprcPosRect;
1083
1084     //RedrawWindow(GetParent(_inplacewnd), &_posRect, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);
1085
1086     /*
1087     ** record keeping of control geometry within container
1088     */
1089     _posRect = posRect;
1090
1091     /*
1092     ** convert posRect & clipRect to match control viewport coordinates
1093     */
1094     getViewportCoords(&posRect, &clipRect);
1095
1096     /*
1097     ** change in-place window geometry to match clipping region
1098     */
1099     SetWindowPos(_inplacewnd, NULL,
1100             clipRect.left,
1101             clipRect.top,
1102             clipRect.right-clipRect.left,
1103             clipRect.bottom-clipRect.top,
1104             SWP_NOACTIVATE|
1105             SWP_NOCOPYBITS|
1106             SWP_NOZORDER|
1107             SWP_NOOWNERZORDER );
1108
1109     /*
1110     ** change video window geometry to match object bounds within clipping region
1111     */
1112     SetWindowPos(_videownd, NULL,
1113             posRect.left,
1114             posRect.top,
1115             posRect.right-posRect.left,
1116             posRect.bottom-posRect.top,
1117             SWP_NOACTIVATE|
1118             SWP_NOCOPYBITS|
1119             SWP_NOZORDER|
1120             SWP_NOOWNERZORDER );
1121
1122     //RedrawWindow(_videownd, &posRect, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);
1123     if( isRunning() )
1124     {
1125         libvlc_video_set_size(_p_libvlc,
1126             posRect.right-posRect.left,
1127             posRect.bottom-posRect.top,
1128             NULL );
1129     }
1130 };
1131
1132 void VLCPlugin::freezeEvents(BOOL freeze)
1133 {
1134     vlcConnectionPointContainer->freezeEvents(freeze);
1135 };
1136
1137 void VLCPlugin::firePropChangedEvent(DISPID dispid)
1138 {
1139     vlcConnectionPointContainer->firePropChangedEvent(dispid); 
1140 };
1141
1142 void VLCPlugin::fireOnPlayEvent(void)
1143 {
1144     DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
1145     vlcConnectionPointContainer->fireEvent(DISPID_PlayEvent, &dispparamsNoArgs); 
1146 };
1147
1148 void VLCPlugin::fireOnPauseEvent(void)
1149 {
1150     DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
1151     vlcConnectionPointContainer->fireEvent(DISPID_PauseEvent, &dispparamsNoArgs); 
1152 };
1153
1154 void VLCPlugin::fireOnStopEvent(void)
1155 {
1156     DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
1157     vlcConnectionPointContainer->fireEvent(DISPID_StopEvent, &dispparamsNoArgs); 
1158 };
1159