]> git.sesse.net Git - vlc/blob - activex/plugin.cpp
dataobject isn't ready for prime time yet :)
[vlc] / activex / plugin.cpp
1 /*****************************************************************************
2  * plugin.cpp: ActiveX control for VLC
3  *****************************************************************************
4  * Copyright (C) 2005 VideoLAN
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., 59 Temple Place - Suite 330, Boston, MA  02111, 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
38 #include "utils.h"
39
40 #include <string.h>
41 #include <winreg.h>
42
43 using namespace std;
44
45 ////////////////////////////////////////////////////////////////////////
46 //class factory
47
48 // {E23FE9C6-778E-49d4-B537-38FCDE4887D8}
49 //const GUID CLSID_VLCPlugin = 
50 //    { 0xe23fe9c6, 0x778e, 0x49d4, { 0xb5, 0x37, 0x38, 0xfc, 0xde, 0x48, 0x87, 0xd8 } };
51
52 static LRESULT CALLBACK VLCInPlaceClassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
53     switch( uMsg )
54     {
55         case WM_ERASEBKGND:
56             return 1L;
57
58         case WM_PAINT:
59             PAINTSTRUCT ps;
60             if( GetUpdateRect(hWnd, NULL, FALSE) )
61             {
62                 BeginPaint(hWnd, &ps);
63                 EndPaint(hWnd, &ps);
64             }
65             return 0L;
66
67         default:
68             return DefWindowProc(hWnd, uMsg, wParam, lParam);
69     }
70 };
71
72 static LRESULT CALLBACK VLCVideoClassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
73     VLCPlugin *p_instance = reinterpret_cast<VLCPlugin *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
74
75     switch( uMsg )
76     {
77         case WM_ERASEBKGND:
78             return 1L;
79
80         case WM_PAINT:
81             PAINTSTRUCT ps;
82             RECT pr;
83             if( GetUpdateRect(hWnd, &pr, FALSE) )
84             {
85                 RECT bounds;
86                 GetClientRect(hWnd, &bounds);
87                 BeginPaint(hWnd, &ps);
88                 p_instance->onPaint(ps.hdc, bounds, pr);
89                 EndPaint(hWnd, &ps);
90             }
91             return 0L;
92
93         default:
94             return DefWindowProc(hWnd, uMsg, wParam, lParam);
95     }
96 };
97
98 VLCPluginClass::VLCPluginClass(LONG *p_class_ref, HINSTANCE hInstance) :
99     _p_class_ref(p_class_ref),
100     _hinstance(hInstance)
101 {
102     WNDCLASS wClass;
103
104     if( ! GetClassInfo(hInstance, getInPlaceWndClassName(), &wClass) )
105     {
106         wClass.style          = CS_NOCLOSE|CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
107         wClass.lpfnWndProc    = VLCInPlaceClassWndProc;
108         wClass.cbClsExtra     = 0;
109         wClass.cbWndExtra     = 0;
110         wClass.hInstance      = hInstance;
111         wClass.hIcon          = NULL;
112         wClass.hCursor        = LoadCursor(NULL, IDC_ARROW);
113         wClass.hbrBackground  = NULL;
114         wClass.lpszMenuName   = NULL;
115         wClass.lpszClassName  = getInPlaceWndClassName();
116        
117         _inplace_wndclass_atom = RegisterClass(&wClass);
118     }
119     else
120     {
121         _inplace_wndclass_atom = 0;
122     }
123
124     if( ! GetClassInfo(hInstance, getVideoWndClassName(), &wClass) )
125     {
126         wClass.style          = CS_NOCLOSE|CS_HREDRAW|CS_VREDRAW;
127         wClass.lpfnWndProc    = VLCVideoClassWndProc;
128         wClass.cbClsExtra     = 0;
129         wClass.cbWndExtra     = 0;
130         wClass.hInstance      = hInstance;
131         wClass.hIcon          = NULL;
132         wClass.hCursor        = LoadCursor(NULL, IDC_ARROW);
133         wClass.hbrBackground  = NULL;
134         wClass.lpszMenuName   = NULL;
135         wClass.lpszClassName  = getVideoWndClassName();
136        
137         _video_wndclass_atom = RegisterClass(&wClass);
138     }
139     else
140     {
141         _video_wndclass_atom = 0;
142     }
143
144     _inplace_hbitmap = (HBITMAP)LoadImage(getHInstance(), TEXT("INPLACE-PICT"), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
145
146     AddRef();
147 };
148
149 VLCPluginClass::~VLCPluginClass()
150 {
151     if( 0 != _inplace_wndclass_atom )
152         UnregisterClass(MAKEINTATOM(_inplace_wndclass_atom), _hinstance);
153
154     if( 0 != _video_wndclass_atom )
155         UnregisterClass(MAKEINTATOM(_video_wndclass_atom), _hinstance);
156
157     if( NULL != _inplace_hbitmap )
158         DeleteObject(_inplace_hbitmap);
159 };
160
161 STDMETHODIMP VLCPluginClass::QueryInterface(REFIID riid, void **ppv)
162 {
163     if( NULL == ppv )
164         return E_INVALIDARG;
165
166     if( (IID_IUnknown == riid) || (IID_IClassFactory == riid) )
167     {
168         AddRef();
169         *ppv = reinterpret_cast<LPVOID>(this);
170
171         return NOERROR;
172     }
173
174     *ppv = NULL;
175
176     return E_NOINTERFACE;
177 };
178
179 STDMETHODIMP_(ULONG) VLCPluginClass::AddRef(void)
180 {
181     return InterlockedIncrement(_p_class_ref);
182 };
183
184 STDMETHODIMP_(ULONG) VLCPluginClass::Release(void)
185 {
186     ULONG refcount = InterlockedDecrement(_p_class_ref);
187     if( 0 == refcount )
188     {
189         delete this;
190         return 0;
191     }
192     return refcount;
193 };
194
195 STDMETHODIMP VLCPluginClass::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv)
196 {
197     if( NULL == ppv )
198         return E_POINTER;
199
200     *ppv = NULL;
201
202     if( NULL != pUnkOuter )
203         return CLASS_E_NOAGGREGATION;
204
205     VLCPlugin *plugin = new VLCPlugin(this);
206     if( NULL != plugin )
207     {
208         HRESULT hr = plugin->QueryInterface(riid, ppv);
209         plugin->Release();
210         return hr;
211     }
212     return E_OUTOFMEMORY;
213 };
214
215 STDMETHODIMP VLCPluginClass::LockServer(BOOL fLock)
216 {
217     if( fLock )
218         AddRef();
219     else 
220         Release();
221
222     return S_OK;
223 };
224
225 ////////////////////////////////////////////////////////////////////////
226
227 VLCPlugin::VLCPlugin(VLCPluginClass *p_class) :
228     _inplacewnd(NULL),
229     _p_class(p_class),
230     _i_ref(1UL),
231     _codepage(CP_ACP),
232     _psz_src(NULL),
233     _b_autostart(TRUE),
234     _b_loopmode(FALSE),
235     _b_visible(TRUE),
236     _b_mute(FALSE),
237     _b_sendevents(TRUE),
238     _i_vlc(0)
239 {
240     p_class->AddRef();
241
242     vlcOleObject = new VLCOleObject(this);
243     vlcOleControl = new VLCOleControl(this);
244     vlcOleInPlaceObject = new VLCOleInPlaceObject(this);
245     vlcOleInPlaceActiveObject = new VLCOleInPlaceActiveObject(this);
246     vlcPersistStorage = new VLCPersistStorage(this);
247     vlcPersistStreamInit = new VLCPersistStreamInit(this);
248     vlcPersistPropertyBag = new VLCPersistPropertyBag(this);
249     vlcProvideClassInfo = new VLCProvideClassInfo(this);
250     vlcConnectionPointContainer = new VLCConnectionPointContainer(this);
251     vlcObjectSafety = new VLCObjectSafety(this);
252     vlcControl = new VLCControl(this);
253     vlcViewObject = new VLCViewObject(this);
254
255     // set default/preferred size (320x240) pixels in HIMETRIC
256     HDC hDC = CreateDevDC(NULL);
257     _extent.cx = (320*2540L)/GetDeviceCaps(hDC, LOGPIXELSX);
258     _extent.cy = (240*2540L)/GetDeviceCaps(hDC, LOGPIXELSY);
259     DeleteDC(hDC);
260 };
261
262 VLCPlugin::~VLCPlugin()
263 {
264     vlcOleInPlaceObject->UIDeactivate();
265     vlcOleInPlaceObject->InPlaceDeactivate();
266
267     delete vlcViewObject;
268     delete vlcControl;
269     delete vlcObjectSafety;
270     delete vlcConnectionPointContainer;
271     delete vlcProvideClassInfo;
272     delete vlcPersistPropertyBag;
273     delete vlcPersistStreamInit;
274     delete vlcPersistStorage;
275     delete vlcOleInPlaceActiveObject;
276     delete vlcOleInPlaceObject;
277     delete vlcOleControl;
278     delete vlcOleObject;
279
280     if( _psz_src )
281         free(_psz_src);
282
283     _p_class->Release();
284 };
285
286 STDMETHODIMP VLCPlugin::QueryInterface(REFIID riid, void **ppv)
287 {
288     if( NULL == ppv )
289         return E_INVALIDARG;
290
291     if( IID_IUnknown == riid )
292     {
293         AddRef();
294         *ppv = reinterpret_cast<LPVOID>(this);
295         return NOERROR;
296     }
297     else if( IID_IOleObject == riid )
298     {
299         AddRef();
300         *ppv = reinterpret_cast<LPVOID>(vlcOleObject);
301         return NOERROR;
302     }
303     else if( IID_IOleControl == riid )
304     {
305         AddRef();
306         *ppv = reinterpret_cast<LPVOID>(vlcOleControl);
307         return NOERROR;
308     }
309     else if( IID_IOleWindow == riid )
310     {
311         AddRef();
312         *ppv = reinterpret_cast<LPVOID>(vlcOleInPlaceObject);
313         return NOERROR;
314     }
315     else if( IID_IOleInPlaceObject == riid )
316     {
317         AddRef();
318         *ppv = reinterpret_cast<LPVOID>(vlcOleInPlaceObject);
319         return NOERROR;
320     }
321     else if( IID_IOleInPlaceActiveObject == riid )
322     {
323         AddRef();
324         *ppv = reinterpret_cast<LPVOID>(vlcOleInPlaceActiveObject);
325         return NOERROR;
326     }
327     else if( IID_IPersist == riid )
328     {
329         AddRef();
330         *ppv = reinterpret_cast<LPVOID>(vlcPersistPropertyBag);
331         return NOERROR;
332     }
333     else if( IID_IPersistStreamInit == riid )
334     {
335         AddRef();
336         *ppv = reinterpret_cast<LPVOID>(vlcPersistStreamInit);
337         return NOERROR;
338     }
339     else if( IID_IPersistStorage == riid )
340     {
341         AddRef();
342         *ppv = reinterpret_cast<LPVOID>(vlcPersistStorage);
343         return NOERROR;
344     }
345     else if( IID_IPersistPropertyBag == riid )
346     {
347         AddRef();
348         *ppv = reinterpret_cast<LPVOID>(vlcPersistPropertyBag);
349         return NOERROR;
350     }
351     else if( IID_IProvideClassInfo == riid )
352     {
353         AddRef();
354         *ppv = reinterpret_cast<LPVOID>(vlcProvideClassInfo);
355         return NOERROR;
356     }
357     else if( IID_IProvideClassInfo2 == riid )
358     {
359         AddRef();
360         *ppv = reinterpret_cast<LPVOID>(vlcProvideClassInfo);
361         return NOERROR;
362     }
363     else if( IID_IConnectionPointContainer == riid )
364     {
365         AddRef();
366         *ppv = reinterpret_cast<LPVOID>(vlcConnectionPointContainer);
367         return NOERROR;
368     }
369     else if( IID_IObjectSafety == riid )
370     {
371         AddRef();
372         *ppv = reinterpret_cast<LPVOID>(vlcObjectSafety);
373         return NOERROR;
374     }
375     else if( IID_IDispatch == riid )
376     {
377         AddRef();
378         *ppv = reinterpret_cast<LPVOID>(vlcControl);
379         return NOERROR;
380     }
381     else if( IID_IVLCControl == riid )
382     {
383         AddRef();
384         *ppv = reinterpret_cast<LPVOID>(vlcControl);
385         return NOERROR;
386     }
387     else if( IID_IViewObject == riid )
388     {
389         AddRef();
390         *ppv = reinterpret_cast<LPVOID>(vlcViewObject);
391         return NOERROR;
392     }
393     else if( IID_IViewObject2 == riid )
394     {
395         AddRef();
396         *ppv = reinterpret_cast<LPVOID>(vlcViewObject);
397         return NOERROR;
398     }
399
400     *ppv = NULL;
401
402     return E_NOINTERFACE;
403 };
404
405 STDMETHODIMP_(ULONG) VLCPlugin::AddRef(void)
406 {
407     return InterlockedIncrement((LONG *)&_i_ref);
408 };
409
410 STDMETHODIMP_(ULONG) VLCPlugin::Release(void)
411 {
412     if( ! InterlockedDecrement((LONG *)&_i_ref) )
413     {
414         delete this;
415         return 0;
416     }
417     return _i_ref;
418 };
419
420 //////////////////////////////////////
421
422 /*
423 ** we use a window to represent plugin viewport,
424 ** whose geometry is limited by the clipping rectangle
425 ** all drawing within this window must follow must
426 ** follow coordinates system described in lprPosRect
427 */
428
429 static void getViewportCoords(LPRECT lprPosRect, LPRECT lprClipRect)
430 {
431     RECT bounds;
432     bounds.right  = lprPosRect->right-lprPosRect->left;
433
434     if( lprClipRect->left <= lprPosRect->left )
435     {
436         // left side is not clipped out
437         bounds.left = 0;
438
439         if( lprClipRect->right >= lprPosRect->right )
440         {
441             // right side is not clipped out, no change
442         }
443         else if( lprClipRect->right >= lprPosRect->left )
444         {
445             // right side is clipped out
446             lprPosRect->right = lprClipRect->right;
447         }
448         else
449         {
450             // outside of clipping rectange, not visible
451             lprPosRect->right = lprPosRect->left;
452         }
453     }
454     else
455     {
456         // left side is clipped out
457         bounds.left = lprPosRect->left-lprClipRect->left;
458         bounds.right += bounds.left;
459
460         lprPosRect->left = lprClipRect->left;
461         if( lprClipRect->right >= lprPosRect->right )
462         {
463             // right side is not clipped out
464         }
465         else
466         {
467             // right side is clipped out
468             lprPosRect->right = lprClipRect->right;
469         }
470     }
471
472     bounds.bottom = lprPosRect->bottom-lprPosRect->top;
473
474     if( lprClipRect->top <= lprPosRect->top )
475     {
476         // top side is not clipped out
477         bounds.top = 0;
478
479         if( lprClipRect->bottom >= lprPosRect->bottom )
480         {
481             // bottom side is not clipped out, no change
482         }
483         else if( lprClipRect->bottom >= lprPosRect->top )
484         {
485             // bottom side is clipped out
486             lprPosRect->bottom = lprClipRect->bottom;
487         }
488         else
489         {
490             // outside of clipping rectange, not visible
491             lprPosRect->right = lprPosRect->left;
492         }
493     }
494     else
495     {
496         bounds.top = lprPosRect->top-lprClipRect->top;
497         bounds.bottom += bounds.top;
498
499         lprPosRect->top = lprClipRect->top;
500         if( lprClipRect->bottom >= lprPosRect->bottom )
501         {
502             // bottom side is not clipped out
503         }
504         else
505         {
506             // bottom side is clipped out
507             lprPosRect->bottom = lprClipRect->bottom;
508         }
509     }
510     *lprClipRect = *lprPosRect;
511     *lprPosRect  = bounds;
512 };
513
514 HRESULT VLCPlugin::onInit(void)
515 {
516     if( 0 == _i_vlc )
517     {
518 #ifdef ACTIVEX_DEBUG
519         char *ppsz_argv[] = { "vlc", "-vvv", "--fast-mutex", "--win9x-cv-method=1" };
520 #else
521         char *ppsz_argv[] = { "vlc", "-vv" };
522 #endif
523         HKEY h_key;
524         DWORD i_type, i_data = MAX_PATH + 1;
525         char p_data[MAX_PATH + 1];
526         if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\VideoLAN\\VLC",
527                           0, KEY_READ, &h_key ) == ERROR_SUCCESS )
528         {
529              if( RegQueryValueEx( h_key, "InstallDir", 0, &i_type,
530                                   (LPBYTE)p_data, &i_data ) == ERROR_SUCCESS )
531              {
532                  if( i_type == REG_SZ )
533                  {
534                      strcat( p_data, "\\vlc" );
535                      ppsz_argv[0] = p_data;
536                  }
537              }
538              RegCloseKey( h_key );
539         }
540
541 #if 0
542         ppsz_argv[0] = "C:\\cygwin\\home\\Damien_Fouilleul\\dev\\videolan\\vlc-trunk\\vlc";
543 #endif
544
545         _i_vlc = VLC_Create();
546
547         if( VLC_Init(_i_vlc, sizeof(ppsz_argv)/sizeof(char*), ppsz_argv) )
548         {
549             VLC_Destroy(_i_vlc);
550             _i_vlc = 0;
551             return E_FAIL;
552         }
553         return S_OK;
554     }
555     return E_UNEXPECTED;
556 };
557
558 HRESULT VLCPlugin::onLoad(void)
559 {
560     if( _b_mute )
561         VLC_VolumeMute(_i_vlc);
562
563     if( NULL != _psz_src )
564     {
565         // add default target to playlist
566         char *cOptions[1];
567         int  cOptionsCount = 0;
568
569         if( _b_loopmode )
570         {
571             cOptions[cOptionsCount++] = "loop";
572         }
573         VLC_AddTarget(_i_vlc, _psz_src, (const char **)&cOptions, cOptionsCount, PLAYLIST_APPEND, PLAYLIST_END);
574     }
575     return S_OK;
576 };
577
578 HRESULT VLCPlugin::onClientSiteChanged(LPOLECLIENTSITE pActiveSite)
579 {
580     if( NULL != pActiveSite )
581     {
582         /*
583         ** object is embedded in container 
584         ** try to activate in place if it has initialized
585         */
586         if( _i_vlc )
587         {
588             vlcOleObject->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, pActiveSite, 0, NULL, NULL);
589         }
590     }
591     return S_OK;
592 };
593
594 HRESULT VLCPlugin::onClose(DWORD dwSaveOption)
595 {
596     if( _i_vlc )
597     {
598         if( isInPlaceActive() )
599         {
600             onInPlaceDeactivate();
601         }
602
603         VLC_CleanUp(_i_vlc);
604         VLC_Destroy(_i_vlc);
605         _i_vlc = 0;
606     }
607     return S_OK;
608 };
609
610 BOOL VLCPlugin::isInPlaceActive(void)
611 {
612     return (NULL != _inplacewnd);
613 };
614
615 HRESULT VLCPlugin::onActivateInPlace(LPMSG lpMesg, HWND hwndParent, LPCRECT lprcPosRect, LPCRECT lprcClipRect)
616 {
617     RECT posRect = *lprcPosRect;
618     RECT clipRect = *lprcClipRect;
619
620     /*
621     ** record keeping of control geometry within container
622     */ 
623     _posRect = posRect;
624
625     /*
626     ** convert posRect & clipRect to match control viewport coordinates
627     */
628     getViewportCoords(&posRect, &clipRect);
629
630     /*
631     ** Create a window for in place activated control.
632     ** the window geometry represents the control viewport
633     ** so that embedded video is always properly clipped.
634     */
635     _inplacewnd = CreateWindow(_p_class->getInPlaceWndClassName(),
636             "VLC Plugin In-Place Window",
637             WS_CHILD|WS_CLIPCHILDREN|WS_TABSTOP,
638             clipRect.left,
639             clipRect.top,
640             clipRect.right-clipRect.left,
641             clipRect.bottom-clipRect.top,
642             hwndParent,
643             0,
644             _p_class->getHInstance(),
645             NULL
646            );
647
648     if( NULL == _inplacewnd )
649         return E_FAIL;
650
651     SetWindowLongPtr(_inplacewnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
652
653     /*
654     ** VLC embedded video geometry automatically matches parent window.
655     ** hence create a child window so that video position and size
656     ** is always correct relative to the viewport bounds
657     */
658     _videownd = CreateWindow(_p_class->getVideoWndClassName(),
659             "VLC Plugin Video Window",
660             WS_CHILD|WS_CLIPCHILDREN|WS_VISIBLE,
661             posRect.left,
662             posRect.top,
663             posRect.right-posRect.left,
664             posRect.bottom-posRect.top,
665             _inplacewnd,
666             0,
667             _p_class->getHInstance(),
668             NULL
669            );
670
671     if( NULL == _videownd )
672     {
673         DestroyWindow(_inplacewnd);
674         return E_FAIL;
675     }
676
677     SetWindowLongPtr(_videownd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
678
679     if( getVisible() )
680         ShowWindow(_inplacewnd, SW_SHOWNORMAL);
681
682     /* horrible cast there */
683     vlc_value_t val;
684     val.i_int = reinterpret_cast<int>(_videownd);
685     VLC_VariableSet(_i_vlc, "drawable", val);
686
687     if( _b_autostart & (VLC_PlaylistNumberOfItems(_i_vlc) > 0) )
688     {
689         VLC_Play(_i_vlc);
690         fireOnPlayEvent();
691     }
692     return S_OK;
693 };
694
695 HRESULT VLCPlugin::onInPlaceDeactivate(void)
696 {
697     VLC_Stop(_i_vlc);
698     fireOnStopEvent();
699
700     DestroyWindow(_videownd);
701     _videownd = NULL;
702     DestroyWindow(_inplacewnd);
703     _inplacewnd = NULL;
704  
705     return S_OK;
706 };
707
708 void VLCPlugin::setVisible(BOOL fVisible)
709 {
710     _b_visible = fVisible;
711     if( isInPlaceActive() )
712         ShowWindow(_inplacewnd, fVisible ? SW_SHOWNORMAL : SW_HIDE);
713     firePropChangedEvent(DISPID_Visible);
714 };
715
716 void VLCPlugin::setFocus(BOOL fFocus)
717 {
718     if( fFocus )
719         SetActiveWindow(_inplacewnd);
720 };
721
722 BOOL VLCPlugin::hasFocus(void)
723 {
724     return GetActiveWindow() == _inplacewnd;
725 };
726
727 void VLCPlugin::onPaint(HDC hdc, const RECT &bounds, const RECT &pr)
728 {
729     if( getVisible() )
730     {
731         /*
732         ** if VLC is playing, it may not display any VIDEO content 
733         ** hence, draw control logo
734         */ 
735         int width = bounds.right-bounds.left;
736         int height = bounds.bottom-bounds.top;
737
738         HBITMAP pict = _p_class->getInPlacePict();
739         if( NULL != pict )
740         {
741             HDC hdcPict = CreateCompatibleDC(hdc);
742             if( NULL != hdcPict )
743             {
744                 BITMAP bm;
745                 if( GetObject(pict, sizeof(BITMAPINFO), &bm) )
746                 {
747                     int dstWidth = bm.bmWidth;
748                     if( dstWidth > width-4 )
749                         dstWidth = width-4;
750
751                     int dstHeight = bm.bmHeight;
752                     if( dstHeight > height-4 )
753                         dstHeight = height-4;
754
755                     int dstX = bounds.left+(width-dstWidth)/2;
756                     int dstY = bounds.top+(height-dstHeight)/2;
757
758                     SelectObject(hdcPict, pict);
759                     StretchBlt(hdc, dstX, dstY, dstWidth, dstHeight,
760                             hdcPict, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
761                     DeleteDC(hdcPict);
762                     ExcludeClipRect(hdc, dstX, dstY, dstWidth+dstX, dstHeight+dstY);
763                 }
764             }
765         }
766
767         FillRect(hdc, &pr, (HBRUSH)GetStockObject(WHITE_BRUSH));
768         SelectObject(hdc, GetStockObject(BLACK_BRUSH));
769
770         MoveToEx(hdc, bounds.left, bounds.top, NULL);
771         LineTo(hdc, bounds.left+width-1, bounds.top);
772         LineTo(hdc, bounds.left+width-1, bounds.top+height-1);
773         LineTo(hdc, bounds.left, bounds.top+height-1);
774         LineTo(hdc, bounds.left, bounds.top);
775     }
776 };
777
778 void VLCPlugin::onPositionChange(LPCRECT lprcPosRect, LPCRECT lprcClipRect)
779 {
780     RECT clipRect = *lprcClipRect;
781     RECT posRect  = *lprcPosRect;
782
783     /*
784     ** tell container that previous area needs redrawing
785     */
786
787     InvalidateRect(GetParent(_inplacewnd), &_posRect, TRUE);
788
789     /*
790     ** record keeping of control geometry within container
791     */
792
793     _posRect = posRect;
794
795     /*
796     ** convert posRect & clipRect to match control viewport coordinates
797     */
798     getViewportCoords(&posRect, &clipRect);
799
800     /*
801     ** change in-place window geometry to match clipping region
802     */
803     MoveWindow(_inplacewnd,
804             clipRect.left,
805             clipRect.top,
806             clipRect.right-clipRect.left,
807             clipRect.bottom-clipRect.top,
808             FALSE);
809
810     /*
811     ** change video window geometry to match object bounds within clipping region
812     */
813     MoveWindow(_videownd,
814             posRect.left,
815             posRect.top,
816             posRect.right-posRect.left,
817             posRect.bottom-posRect.top,
818             FALSE);
819
820     /*
821     ** force a full refresh of control content
822     */
823     RECT updateRect;
824     updateRect.left = -posRect.left;
825     updateRect.top = -posRect.top;
826     updateRect.right = posRect.right-posRect.left;
827     updateRect.bottom = posRect.bottom-posRect.top;
828
829     ValidateRect(_videownd, NULL);
830     InvalidateRect(_videownd, &updateRect, FALSE);
831     UpdateWindow(_videownd);
832 };
833
834 void VLCPlugin::firePropChangedEvent(DISPID dispid)
835 {
836     if( _b_sendevents )
837     {
838         vlcConnectionPointContainer->firePropChangedEvent(dispid); 
839     }
840 };
841
842 void VLCPlugin::fireOnPlayEvent(void)
843 {
844     if( _b_sendevents )
845     {
846         DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
847         vlcConnectionPointContainer->fireEvent(DISPID_PlayEvent, &dispparamsNoArgs); 
848     }
849 };
850
851 void VLCPlugin::fireOnPauseEvent(void)
852 {
853     if( _b_sendevents )
854     {
855         DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
856         vlcConnectionPointContainer->fireEvent(DISPID_PauseEvent, &dispparamsNoArgs); 
857     }
858 };
859
860 void VLCPlugin::fireOnStopEvent(void)
861 {
862     if( _b_sendevents )
863     {
864         DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
865         vlcConnectionPointContainer->fireEvent(DISPID_StopEvent, &dispparamsNoArgs); 
866     }
867 };
868