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