1 /*****************************************************************************
2  * win32_factory.cpp
3  *****************************************************************************
4  * Copyright (C) 2003 the VideoLAN team
5  * $Id: a357276f29a5ce4a16a14c7447f7d1bf0a3e0ec8 $
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 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30 
31 #include <windows.h>
32 #include <winuser.h>
33 #include <wingdi.h>
34 #include <tchar.h>
35 #include <shellapi.h>
36 
37 #include "win32_factory.hpp"
38 #include "win32_graphics.hpp"
39 #include "win32_timer.hpp"
40 #include "win32_window.hpp"
41 #include "win32_tooltip.hpp"
42 #include "win32_popup.hpp"
43 #include "win32_loop.hpp"
44 #include "../src/theme.hpp"
45 #include "../src/window_manager.hpp"
46 #include "../src/generic_window.hpp"
47 #include "../commands/cmd_dialogs.hpp"
48 #include "../commands/cmd_minimize.hpp"
49 
50 // Custom message for the notifications of the system tray
51 #define MY_WM_TRAYACTION (WM_APP + 1)
52 
53 
Win32Proc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)54 LRESULT CALLBACK Win32Factory::Win32Proc( HWND hwnd, UINT uMsg,
55                                           WPARAM wParam, LPARAM lParam )
56 {
57     // Get pointer to thread info: should only work with the parent window
58     intf_thread_t *p_intf = (intf_thread_t *)GetWindowLongPtr( hwnd,
59         GWLP_USERDATA );
60 
61     // If doesn't exist, treat windows message normally
62     if( p_intf == NULL || p_intf->p_sys->p_osFactory == NULL )
63     {
64         return DefWindowProc( hwnd, uMsg, wParam, lParam );
65     }
66 
67     Win32Factory *pFactory = (Win32Factory*)Win32Factory::instance( p_intf );
68     GenericWindow *pWin = pFactory->m_windowMap[hwnd];
69 
70     if( hwnd == pFactory->getParentWindow() )
71     {
72         if( uMsg == WM_SYSCOMMAND )
73         {
74             // If closing parent window
75             if( (wParam & 0xFFF0) == SC_CLOSE )
76             {
77                 libvlc_Quit( p_intf->obj.libvlc );
78                 return 0;
79             }
80             else if( (wParam & 0xFFF0) == SC_MINIMIZE )
81             {
82                 pFactory->minimize();
83                 return 0;
84             }
85             else if( (wParam & 0xFFF0) == SC_RESTORE )
86             {
87                 pFactory->restore();
88                 return 0;
89             }
90             else
91             {
92                 msg_Dbg( p_intf, "WM_SYSCOMMAND %i", (wParam  & 0xFFF0) );
93             }
94         }
95         // Handle systray notifications
96         else if( uMsg == MY_WM_TRAYACTION )
97         {
98             if( (UINT)lParam == WM_LBUTTONDOWN )
99             {
100                 p_intf->p_sys->p_theme->getWindowManager().raiseAll();
101                 CmdDlgHidePopupMenu aCmdPopup( p_intf );
102                 aCmdPopup.execute();
103                 return 0;
104             }
105             else if( (UINT)lParam == WM_RBUTTONDOWN )
106             {
107                 CmdDlgShowPopupMenu aCmdPopup( p_intf );
108                 aCmdPopup.execute();
109                 return 0;
110             }
111             else if( (UINT)lParam == WM_LBUTTONDBLCLK )
112             {
113                 CmdRestore aCmdRestore( p_intf );
114                 aCmdRestore.execute();
115                 return 0;
116             }
117         }
118     }
119     else if( pWin )
120     {
121         Win32Loop* pLoop =
122             (Win32Loop*) OSFactory::instance( p_intf )->getOSLoop();
123         if( pLoop )
124             return pLoop->processEvent( hwnd, uMsg, wParam, lParam );
125     }
126 
127     // If hwnd does not match any window or message not processed
128     return DefWindowProc( hwnd, uMsg, wParam, lParam );
129 }
130 
131 
MonitorEnumProc(HMONITOR hMonitor,HDC hdcMonitor,LPRECT lprcMonitor,LPARAM dwData)132 BOOL CALLBACK Win32Factory::MonitorEnumProc( HMONITOR hMonitor, HDC hdcMonitor,
133                                              LPRECT lprcMonitor, LPARAM dwData )
134 {
135     (void)hdcMonitor; (void)lprcMonitor;
136     std::list<HMONITOR>* pList = (std::list<HMONITOR>*)dwData;
137     pList->push_back( hMonitor );
138 
139     return TRUE;
140 }
141 
Win32Factory(intf_thread_t * pIntf)142 Win32Factory::Win32Factory( intf_thread_t *pIntf ):
143     OSFactory( pIntf ), m_hParentWindow( NULL ),
144     m_dirSep( "\\" )
145 {
146     // see init()
147 }
148 
149 
init()150 bool Win32Factory::init()
151 {
152     LPCTSTR vlc_name = TEXT("VLC Media Player");
153     LPCTSTR vlc_icon = TEXT("VLC_ICON");
154     LPCTSTR vlc_class = TEXT("SkinWindowClass");
155 
156     // Get instance handle
157     m_hInst = GetModuleHandle( NULL );
158     if( m_hInst == NULL )
159     {
160         msg_Err( getIntf(), "Cannot get module handle" );
161     }
162 
163     // Create window class
164     WNDCLASS skinWindowClass;
165     skinWindowClass.style = CS_DBLCLKS;
166     skinWindowClass.lpfnWndProc = (WNDPROC)Win32Factory::Win32Proc;
167     skinWindowClass.lpszClassName = vlc_class;
168     skinWindowClass.lpszMenuName = NULL;
169     skinWindowClass.cbClsExtra = 0;
170     skinWindowClass.cbWndExtra = 0;
171     skinWindowClass.hbrBackground = NULL;
172     skinWindowClass.hCursor = LoadCursor( NULL, IDC_ARROW );
173     skinWindowClass.hIcon = LoadIcon( m_hInst, vlc_icon );
174     skinWindowClass.hInstance = m_hInst;
175 
176     // Register class and check it
177     if( !RegisterClass( &skinWindowClass ) )
178     {
179         WNDCLASS wndclass;
180 
181         // Check why it failed. If it's because the class already exists
182         // then fine, otherwise return with an error.
183         if( !GetClassInfo( m_hInst, vlc_class, &wndclass ) )
184         {
185             msg_Err( getIntf(), "cannot register window class" );
186             return false;
187         }
188     }
189 
190     // Create Window
191     m_hParentWindow = CreateWindowEx( WS_EX_TOOLWINDOW, vlc_class,
192         vlc_name, WS_POPUP | WS_SYSMENU | WS_MINIMIZEBOX,
193         -200, -200, 0, 0, 0, 0, m_hInst, 0 );
194     if( m_hParentWindow == NULL )
195     {
196         msg_Err( getIntf(), "cannot create parent window" );
197         return false;
198     }
199 
200     // Store with it a pointer to the interface thread
201     SetWindowLongPtr( m_hParentWindow, GWLP_USERDATA, (LONG_PTR)getIntf() );
202 
203     // We do it this way otherwise CreateWindowEx will fail
204     // if WS_EX_LAYERED is not supported
205     SetWindowLongPtr( m_hParentWindow, GWL_EXSTYLE,
206                       GetWindowLongPtr( m_hParentWindow, GWL_EXSTYLE ) |
207                       WS_EX_LAYERED );
208 
209     ShowWindow( m_hParentWindow, SW_SHOW );
210 
211     // Initialize the systray icon
212     m_trayIcon.cbSize = sizeof( NOTIFYICONDATA );
213     m_trayIcon.hWnd = m_hParentWindow;
214     m_trayIcon.uID = 42;
215     m_trayIcon.uFlags = NIF_ICON|NIF_TIP|NIF_MESSAGE;
216     m_trayIcon.uCallbackMessage = MY_WM_TRAYACTION;
217     m_trayIcon.hIcon = LoadIcon( m_hInst, vlc_icon );
218     _tcscpy( m_trayIcon.szTip, vlc_name );
219 
220     // Show the systray icon if needed
221     if( var_InheritBool( getIntf(), "skins2-systray" ) )
222     {
223         addInTray();
224     }
225 
226     // Show the task in the task bar if needed
227     if( var_InheritBool( getIntf(), "skins2-taskbar" ) )
228     {
229         addInTaskBar();
230     }
231 
232     // Initialize the OLE library (for drag & drop)
233     OleInitialize( NULL );
234 
235     // Initialize the resource path
236     char *datadir = config_GetUserDir( VLC_DATA_DIR );
237     m_resourcePath.push_back( (std::string)datadir + "\\skins" );
238     free( datadir );
239     datadir = config_GetDataDir();
240     m_resourcePath.push_back( (std::string)datadir + "\\skins" );
241     m_resourcePath.push_back( (std::string)datadir + "\\skins2" );
242     m_resourcePath.push_back( (std::string)datadir + "\\share\\skins" );
243     m_resourcePath.push_back( (std::string)datadir + "\\share\\skins2" );
244     free( datadir );
245 
246     // Enumerate all monitors available
247     EnumDisplayMonitors( NULL, NULL, MonitorEnumProc, (LPARAM)&m_monitorList );
248     int num = 0;
249     for( std::list<HMONITOR>::iterator it = m_monitorList.begin();
250          it != m_monitorList.end(); ++it, num++ )
251     {
252         MONITORINFO mi;
253         mi.cbSize = sizeof( MONITORINFO );
254         if( GetMonitorInfo( *it, &mi ) )
255         {
256             msg_Dbg( getIntf(), "monitor #%i, %ldx%ld at +%ld+%ld", num,
257                         mi.rcMonitor.right - mi.rcMonitor.left,
258                         mi.rcMonitor.bottom - mi.rcMonitor.top,
259                         mi.rcMonitor.left,
260                         mi.rcMonitor.top );
261         }
262     }
263 
264     // All went well
265     return true;
266 }
267 
268 
~Win32Factory()269 Win32Factory::~Win32Factory()
270 {
271     // Uninitialize the OLE library
272     OleUninitialize();
273 
274     // Remove the systray icon
275     removeFromTray();
276 
277     if( m_hParentWindow ) DestroyWindow( m_hParentWindow );
278 }
279 
280 
createOSGraphics(int width,int height)281 OSGraphics *Win32Factory::createOSGraphics( int width, int height )
282 {
283     return new Win32Graphics( getIntf(), width, height );
284 }
285 
286 
getOSLoop()287 OSLoop *Win32Factory::getOSLoop()
288 {
289     return Win32Loop::instance( getIntf() );
290 }
291 
292 
destroyOSLoop()293 void Win32Factory::destroyOSLoop()
294 {
295     Win32Loop::destroy( getIntf() );
296 }
297 
minimize()298 void Win32Factory::minimize()
299 {
300     /* Make sure no tooltip is visible first */
301     getIntf()->p_sys->p_theme->getWindowManager().hideTooltip();
302 
303     ShowWindow( m_hParentWindow, SW_MINIMIZE );
304 }
305 
restore()306 void Win32Factory::restore()
307 {
308     ShowWindow( m_hParentWindow, SW_RESTORE );
309 }
310 
addInTray()311 void Win32Factory::addInTray()
312 {
313     Shell_NotifyIcon( NIM_ADD, &m_trayIcon );
314 }
315 
removeFromTray()316 void Win32Factory::removeFromTray()
317 {
318     Shell_NotifyIcon( NIM_DELETE, &m_trayIcon );
319 }
320 
addInTaskBar()321 void Win32Factory::addInTaskBar()
322 {
323     ShowWindow( m_hParentWindow, SW_HIDE );
324     SetWindowLongPtr( m_hParentWindow, GWL_EXSTYLE,
325                       WS_EX_LAYERED|WS_EX_APPWINDOW );
326     ShowWindow( m_hParentWindow, SW_SHOW );
327 }
328 
removeFromTaskBar()329 void Win32Factory::removeFromTaskBar()
330 {
331     ShowWindow( m_hParentWindow, SW_HIDE );
332     SetWindowLongPtr( m_hParentWindow, GWL_EXSTYLE,
333                       WS_EX_LAYERED|WS_EX_TOOLWINDOW );
334     ShowWindow( m_hParentWindow, SW_SHOW );
335 }
336 
createOSTimer(CmdGeneric & rCmd)337 OSTimer *Win32Factory::createOSTimer( CmdGeneric &rCmd )
338 {
339     return new Win32Timer( getIntf(), rCmd, m_hParentWindow );
340 }
341 
342 
createOSWindow(GenericWindow & rWindow,bool dragDrop,bool playOnDrop,OSWindow * pParent,GenericWindow::WindowType_t type)343 OSWindow *Win32Factory::createOSWindow( GenericWindow &rWindow, bool dragDrop,
344                                         bool playOnDrop, OSWindow *pParent,
345                                         GenericWindow::WindowType_t type )
346 {
347     return new Win32Window( getIntf(), rWindow, m_hInst, m_hParentWindow,
348                             dragDrop, playOnDrop, (Win32Window*)pParent, type );
349 }
350 
351 
createOSTooltip()352 OSTooltip *Win32Factory::createOSTooltip()
353 {
354     return new Win32Tooltip( getIntf(), m_hInst, m_hParentWindow );
355 }
356 
357 
createOSPopup()358 OSPopup *Win32Factory::createOSPopup()
359 {
360     // XXX FIXME: this way of getting the handle really sucks!
361     // In fact, the clean way would be to have in Builder::addPopup() a call
362     // to pPopup->associateToWindow() (to be written)... but the problem is
363     // that there is no way to access the OS-dependent window handle from a
364     // GenericWindow (we cannot even access the OSWindow).
365     if( m_windowMap.begin() == m_windowMap.end() )
366     {
367         msg_Err( getIntf(), "no window has been created before the popup!" );
368         return NULL;
369     }
370 
371     return new Win32Popup( getIntf(), m_windowMap.begin()->first );
372 }
373 
374 
getScreenWidth() const375 int Win32Factory::getScreenWidth() const
376 {
377     return GetSystemMetrics(SM_CXSCREEN);
378 }
379 
380 
getScreenHeight() const381 int Win32Factory::getScreenHeight() const
382 {
383     return GetSystemMetrics(SM_CYSCREEN);
384 }
385 
386 
getMonitorInfo(const GenericWindow & rWindow,int * p_x,int * p_y,int * p_width,int * p_height) const387 void Win32Factory::getMonitorInfo( const GenericWindow &rWindow,
388                                    int* p_x, int* p_y,
389                                    int* p_width, int* p_height ) const
390 {
391     HWND wnd = (HWND)rWindow.getOSHandle();
392     HMONITOR hmon = MonitorFromWindow( wnd, MONITOR_DEFAULTTONEAREST );
393     MONITORINFO mi;
394     mi.cbSize = sizeof( MONITORINFO );
395     if( hmon && GetMonitorInfo( hmon, &mi ) )
396     {
397         *p_x = mi.rcMonitor.left;
398         *p_y = mi.rcMonitor.top;
399         *p_width = mi.rcMonitor.right - mi.rcMonitor.left;
400         *p_height = mi.rcMonitor.bottom - mi.rcMonitor.top;
401     }
402     else
403     {
404         *p_x = 0;
405         *p_y = 0;
406         *p_width = getScreenWidth();
407         *p_height = getScreenHeight();
408     }
409 }
410 
411 
getMonitorInfo(int numScreen,int * p_x,int * p_y,int * p_width,int * p_height) const412 void Win32Factory::getMonitorInfo( int numScreen, int* p_x, int* p_y,
413                                    int* p_width, int* p_height ) const
414 {
415     HMONITOR hmon = NULL;
416     std::list<HMONITOR>::const_iterator it = m_monitorList.begin();
417     for( int i = 0; it != m_monitorList.end(); ++it, i++ )
418     {
419         if( i == numScreen )
420         {
421             hmon = *it;
422             break;
423         }
424     }
425     MONITORINFO mi;
426     mi.cbSize = sizeof( MONITORINFO );
427     if( hmon && GetMonitorInfo( hmon, &mi ) )
428     {
429         *p_x = mi.rcMonitor.left;
430         *p_y = mi.rcMonitor.top;
431         *p_width = mi.rcMonitor.right - mi.rcMonitor.left;
432         *p_height = mi.rcMonitor.bottom - mi.rcMonitor.top;
433     }
434     else
435     {
436         *p_x = 0;
437         *p_y = 0;
438         *p_width = getScreenWidth();
439         *p_height = getScreenHeight();
440     }
441 }
442 
443 
getWorkArea() const444 SkinsRect Win32Factory::getWorkArea() const
445 {
446     RECT r;
447     SystemParametersInfo( SPI_GETWORKAREA, 0, &r, 0 );
448     // Fill a Rect object
449     return  SkinsRect( r.left, r.top, r.right, r.bottom );
450 }
451 
452 
getMousePos(int & rXPos,int & rYPos) const453 void Win32Factory::getMousePos( int &rXPos, int &rYPos ) const
454 {
455     POINT mousePos;
456     GetCursorPos( &mousePos );
457     rXPos = mousePos.x;
458     rYPos = mousePos.y;
459 }
460 
461 
changeCursor(CursorType_t type) const462 void Win32Factory::changeCursor( CursorType_t type ) const
463 {
464     LPCTSTR id;
465     switch( type )
466     {
467     default:
468     case kDefaultArrow: id = IDC_ARROW;    break;
469     case kResizeNWSE:   id = IDC_SIZENWSE; break;
470     case kResizeNS:     id = IDC_SIZENS;   break;
471     case kResizeWE:     id = IDC_SIZEWE;   break;
472     case kResizeNESW:   id = IDC_SIZENESW; break;
473     }
474 
475     HCURSOR hCurs = LoadCursor( NULL, id );
476     SetCursor( hCurs );
477 }
478 
479 
rmDir(const std::string & rPath)480 void Win32Factory::rmDir( const std::string &rPath )
481 {
482     LPWSTR dir_temp = ToWide( rPath.c_str() );
483     size_t len = wcslen( dir_temp );
484 
485     LPWSTR dir = (wchar_t *)vlc_alloc( len + 2, sizeof (wchar_t) );
486     wcsncpy( dir, dir_temp, len + 2);
487 
488     SHFILEOPSTRUCTW file_op = {
489         NULL,
490         FO_DELETE,
491         dir,
492         NULL,
493         FOF_NOCONFIRMATION |
494         FOF_NOERRORUI |
495         FOF_SILENT,
496         false,
497         NULL,
498         L"" };
499 
500      SHFileOperationW(&file_op);
501 
502      free(dir_temp);
503      free(dir);
504 }
505 
506 #endif
507