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