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