]> git.sesse.net Git - vlc/blob - modules/gui/skins2/win32/win32_factory.cpp
* skins2: The skins2-systray option can now be changed on the fly
[vlc] / modules / gui / skins2 / win32 / win32_factory.cpp
1 /*****************************************************************************
2  * win32_factory.cpp
3  *****************************************************************************
4  * Copyright (C) 2003 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Cyril Deguet     <asmax@via.ecp.fr>
8  *          Olivier Teulière <ipkiss@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #ifdef WIN32_SKINS
26
27 #include "win32_factory.hpp"
28 #include "win32_graphics.hpp"
29 #include "win32_timer.hpp"
30 #include "win32_window.hpp"
31 #include "win32_tooltip.hpp"
32 #include "win32_popup.hpp"
33 #include "win32_loop.hpp"
34 #include "../src/theme.hpp"
35 #include "../src/window_manager.hpp"
36 #include "../commands/cmd_dialogs.hpp"
37 #include "../commands/cmd_minimize.hpp"
38
39 // Custom message for the notifications of the system tray
40 #define MY_WSTRAYACTION (WM_APP + 1)
41
42
43 LRESULT CALLBACK Win32Proc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
44 {
45     // Get pointer to thread info: should only work with the parent window
46     intf_thread_t *p_intf = (intf_thread_t *)GetWindowLongPtr( hwnd,
47         GWLP_USERDATA );
48
49     // If doesn't exist, treat windows message normally
50     if( p_intf == NULL || p_intf->p_sys->p_osFactory == NULL )
51     {
52         return DefWindowProc( hwnd, uMsg, wParam, lParam );
53     }
54
55     // Here we know we are getting a message for the parent window, since it is
56     // the only one to store p_intf...
57     // Yes, it is a kludge :)
58
59 //Win32Factory *pFactory = (Win32Factory*)Win32Factory::instance( p_intf );
60 //msg_Err( p_intf, "Parent window %p %p %u %i\n", pFactory->m_hParentWindow, hwnd, uMsg, wParam );
61     // If Window is parent window
62     // XXX: this test isn't needed, see the kludge above...
63 //    if( hwnd == pFactory->m_hParentWindow )
64     {
65         if( uMsg == WM_SYSCOMMAND )
66         {
67             // If closing parent window
68             if( wParam == SC_CLOSE )
69             {
70                 Win32Loop *pLoop = (Win32Loop*)Win32Loop::instance( p_intf );
71                 pLoop->exit();
72                 return 0;
73             }
74             else
75             {
76                 msg_Err( p_intf, "WM_SYSCOMMAND %i", wParam );
77             }
78 //            if( (Event *)wParam != NULL )
79 //                ( (Event *)wParam )->SendEvent();
80 //            return 0;
81         }
82         // Handle systray notifications
83         else if( uMsg == MY_WSTRAYACTION )
84         {
85             if( (UINT)lParam == WM_LBUTTONDOWN )
86             {
87                 p_intf->p_sys->p_theme->getWindowManager().raiseAll();
88             }
89             else if( (UINT)lParam == WM_RBUTTONDOWN )
90             {
91                 CmdDlgShowPopupMenu aCmdPopup( p_intf );
92                 aCmdPopup.execute();
93             }
94             else if( (UINT)lParam == WM_LBUTTONDBLCLK )
95             {
96                 CmdRestore aCmdRestore( p_intf );
97                 aCmdRestore.execute();
98             }
99         }
100     }
101
102     // If hwnd does not match any window or message not processed
103     return DefWindowProc( hwnd, uMsg, wParam, lParam );
104 }
105
106
107 Win32Factory::Win32Factory( intf_thread_t *pIntf ):
108     OSFactory( pIntf ), TransparentBlt( NULL ), AlphaBlend( NULL ),
109     SetLayeredWindowAttributes( NULL ), m_hParentWindow( NULL ),
110     m_dirSep( "\\" )
111 {
112     // see init()
113 }
114
115
116 bool Win32Factory::init()
117 {
118     // Get instance handle
119     m_hInst = GetModuleHandle( NULL );
120     if( m_hInst == NULL )
121     {
122         msg_Err( getIntf(), "Cannot get module handle" );
123     }
124
125     // Create window class
126     WNDCLASS skinWindowClass;
127     skinWindowClass.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
128     skinWindowClass.lpfnWndProc = (WNDPROC) Win32Proc;
129     skinWindowClass.lpszClassName = _T("SkinWindowClass");
130     skinWindowClass.lpszMenuName = NULL;
131     skinWindowClass.cbClsExtra = 0;
132     skinWindowClass.cbWndExtra = 0;
133     skinWindowClass.hbrBackground = NULL;
134     skinWindowClass.hCursor = LoadCursor( NULL , IDC_ARROW );
135     skinWindowClass.hIcon = LoadIcon( m_hInst, _T("VLC_ICON") );
136     skinWindowClass.hInstance = m_hInst;
137
138     // Register class and check it
139     if( !RegisterClass( &skinWindowClass ) )
140     {
141         WNDCLASS wndclass;
142
143         // Check why it failed. If it's because the class already exists
144         // then fine, otherwise return with an error.
145         if( !GetClassInfo( m_hInst, _T("SkinWindowClass"), &wndclass ) )
146         {
147             msg_Err( getIntf(), "cannot register window class" );
148             return false;
149         }
150     }
151
152     // Create Window
153     m_hParentWindow = CreateWindowEx( WS_EX_APPWINDOW, _T("SkinWindowClass"),
154         _T("VLC media player"), WS_SYSMENU|WS_POPUP,
155         -200, -200, 0, 0, 0, 0, m_hInst, 0 );
156     if( m_hParentWindow == NULL )
157     {
158         msg_Err( getIntf(), "cannot create parent window" );
159         return false;
160     }
161
162     // Store with it a pointer to the interface thread
163     SetWindowLongPtr( m_hParentWindow, GWLP_USERDATA, (LONG_PTR)getIntf() );
164
165     // Initialize the systray icon
166     m_trayIcon.cbSize = sizeof( NOTIFYICONDATA );
167     m_trayIcon.hWnd = m_hParentWindow;
168     m_trayIcon.uID = 42;
169     m_trayIcon.uFlags = NIF_ICON|NIF_TIP|NIF_MESSAGE;
170     m_trayIcon.uCallbackMessage = MY_WSTRAYACTION;
171     m_trayIcon.hIcon = LoadIcon( m_hInst, _T("VLC_ICON") );
172     strcpy( m_trayIcon.szTip, "VLC media player" );
173
174     // Show the systray icon if needed
175     if( config_GetInt( getIntf(), "skins2-systray" ) )
176     {
177         addInTray();
178     }
179
180     // We do it this way otherwise CreateWindowEx will fail
181     // if WS_EX_LAYERED is not supported
182     SetWindowLongPtr( m_hParentWindow, GWL_EXSTYLE,
183                       GetWindowLong( m_hParentWindow, GWL_EXSTYLE ) |
184                       WS_EX_LAYERED );
185
186     ShowWindow( m_hParentWindow, SW_SHOW );
187
188     // Initialize the OLE library (for drag & drop)
189     OleInitialize( NULL );
190
191     // We dynamically load msimg32.dll to get a pointer to TransparentBlt()
192     m_hMsimg32 = LoadLibrary( _T("msimg32.dll") );
193     if( !m_hMsimg32 ||
194         !( TransparentBlt =
195             (BOOL (WINAPI*)(HDC, int, int, int, int,
196                             HDC, int, int, int, int, unsigned int))
197             GetProcAddress( m_hMsimg32, _T("TransparentBlt") ) ) )
198     {
199         TransparentBlt = NULL;
200         msg_Dbg( getIntf(), "couldn't find TransparentBlt(), "
201                  "falling back to BitBlt()" );
202     }
203     if( !m_hMsimg32 ||
204         !( AlphaBlend =
205             (BOOL (WINAPI*)( HDC, int, int, int, int, HDC, int, int,
206                               int, int, BLENDFUNCTION ))
207             GetProcAddress( m_hMsimg32, _T("AlphaBlend") ) ) )
208     {
209         AlphaBlend = NULL;
210         msg_Dbg( getIntf(), "couldn't find AlphaBlend()" );
211     }
212
213     // Idem for user32.dll and SetLayeredWindowAttributes()
214     m_hUser32 = LoadLibrary( _T("user32.dll") );
215     if( !m_hUser32 ||
216         !( SetLayeredWindowAttributes =
217             (BOOL (WINAPI *)(HWND, COLORREF, BYTE, DWORD))
218             GetProcAddress( m_hUser32, _T("SetLayeredWindowAttributes") ) ) )
219     {
220         SetLayeredWindowAttributes = NULL;
221         msg_Dbg( getIntf(), "couldn't find SetLayeredWindowAttributes()" );
222     }
223
224     // Initialize the resource path
225     m_resourcePath.push_back( (string)getIntf()->p_vlc->psz_homedir +
226                                "\\" + CONFIG_DIR + "\\skins" );
227     m_resourcePath.push_back( (string)getIntf()->p_libvlc->psz_vlcpath +
228                               "\\skins" );
229     m_resourcePath.push_back( (string)getIntf()->p_libvlc->psz_vlcpath +
230                               "\\skins2" );
231     m_resourcePath.push_back( (string)getIntf()->p_libvlc->psz_vlcpath +
232                               "\\share\\skins" );
233     m_resourcePath.push_back( (string)getIntf()->p_libvlc->psz_vlcpath +
234                               "\\share\\skins2" );
235
236     // All went well
237     return true;
238 }
239
240
241 Win32Factory::~Win32Factory()
242 {
243     // Uninitialize the OLE library
244     OleUninitialize();
245
246     // Remove the systray icon
247     removeFromTray();
248
249     if( m_hParentWindow ) DestroyWindow( m_hParentWindow );
250
251     // Unload msimg32.dll and user32.dll
252     if( m_hMsimg32 )
253         FreeLibrary( m_hMsimg32 );
254     if( m_hUser32 )
255         FreeLibrary( m_hUser32 );
256 }
257
258
259 OSGraphics *Win32Factory::createOSGraphics( int width, int height )
260 {
261     return new Win32Graphics( getIntf(), width, height );
262 }
263
264
265 OSLoop *Win32Factory::getOSLoop()
266 {
267     return Win32Loop::instance( getIntf() );
268 }
269
270
271 void Win32Factory::destroyOSLoop()
272 {
273     Win32Loop::destroy( getIntf() );
274 }
275
276 void Win32Factory::minimize()
277 {
278     /* Make sure no tooltip is visible first */
279     getIntf()->p_sys->p_theme->getWindowManager().hideTooltip();
280
281     ShowWindow( m_hParentWindow, SW_MINIMIZE );
282 }
283
284 void Win32Factory::restore()
285 {
286     ShowWindow( m_hParentWindow, SW_RESTORE );
287 }
288
289 void Win32Factory::addInTray()
290 {
291     Shell_NotifyIcon( NIM_ADD, &m_trayIcon );
292 }
293
294 void Win32Factory::removeFromTray()
295 {
296     Shell_NotifyIcon( NIM_DELETE, &m_trayIcon );
297 }
298
299 OSTimer *Win32Factory::createOSTimer( CmdGeneric &rCmd )
300 {
301     return new Win32Timer( getIntf(), rCmd, m_hParentWindow );
302 }
303
304
305 OSWindow *Win32Factory::createOSWindow( GenericWindow &rWindow, bool dragDrop,
306                                         bool playOnDrop, OSWindow *pParent )
307 {
308     return new Win32Window( getIntf(), rWindow, m_hInst, m_hParentWindow,
309                             dragDrop, playOnDrop, (Win32Window*)pParent );
310 }
311
312
313 OSTooltip *Win32Factory::createOSTooltip()
314 {
315     return new Win32Tooltip( getIntf(), m_hInst, m_hParentWindow );
316 }
317
318
319 OSPopup *Win32Factory::createOSPopup()
320 {
321     // XXX FIXME: this way of getting the handle really sucks!
322     // In fact, the clean way would be to have in Builder::addPopup() a call
323     // to pPopup->associateToWindow() (to be written)... but the problem is
324     // that there is no way to access the OS-dependent window handle from a
325     // GenericWindow (we cannot even access the OSWindow).
326     if( m_windowMap.begin() == m_windowMap.end() )
327     {
328         msg_Err( getIntf(), "no window has been created before the popup!" );
329         return NULL;
330     }
331
332     return new Win32Popup( getIntf(), m_windowMap.begin()->first );
333 }
334
335
336 int Win32Factory::getScreenWidth() const
337 {
338     return GetSystemMetrics(SM_CXSCREEN);
339
340 }
341
342
343 int Win32Factory::getScreenHeight() const
344 {
345     return GetSystemMetrics(SM_CYSCREEN);
346 }
347
348
349 Rect Win32Factory::getWorkArea() const
350 {
351     RECT r;
352     SystemParametersInfo( SPI_GETWORKAREA, 0, &r, 0 );
353     // Fill a Rect object
354     Rect rect( r.left, r.top, r.right, r.bottom );
355     return rect;
356 }
357
358
359 void Win32Factory::getMousePos( int &rXPos, int &rYPos ) const
360 {
361     POINT mousePos;
362     GetCursorPos( &mousePos );
363     rXPos = mousePos.x;
364     rYPos = mousePos.y;
365 }
366
367
368 void Win32Factory::changeCursor( CursorType_t type ) const
369 {
370     LPCTSTR id;
371     switch( type )
372     {
373         case kDefaultArrow:
374             id = IDC_ARROW;
375             break;
376         case kResizeNWSE:
377             id = IDC_SIZENWSE;
378             break;
379         case kResizeNS:
380             id = IDC_SIZENS;
381             break;
382         case kResizeWE:
383             id = IDC_SIZEWE;
384             break;
385         case kResizeNESW:
386             id = IDC_SIZENESW;
387             break;
388         default:
389             id = IDC_ARROW;
390             break;
391     }
392
393     HCURSOR hCurs = LoadCursor( NULL, id );
394     SetCursor( hCurs );
395 }
396
397
398 void Win32Factory::rmDir( const string &rPath )
399 {
400     WIN32_FIND_DATA find;
401     string file;
402     string findFiles = rPath + "\\*";
403     HANDLE handle    = FindFirstFile( findFiles.c_str(), &find );
404
405     while( handle != INVALID_HANDLE_VALUE )
406     {
407         // If file is neither "." nor ".."
408         if( strcmp( find.cFileName, "." ) && strcmp( find.cFileName, ".." ) )
409         {
410             // Set file name
411             file = rPath + "\\" + (string)find.cFileName;
412
413             // If file is a directory, delete it recursively
414             if( find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
415             {
416                 rmDir( file );
417             }
418             // Else, it is a file so simply delete it
419             else
420             {
421                 DeleteFile( file.c_str() );
422             }
423         }
424
425         // If no more file in directory, exit while
426         if( !FindNextFile( handle, &find ) )
427             break;
428     }
429
430     // Now directory is empty so can be removed
431     FindClose( handle );
432     RemoveDirectory( rPath.c_str() );
433 }
434
435 #endif