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