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