]> git.sesse.net Git - vlc/blob - modules/video_output/directx/glwin32.c
eee84c718f0aba14082d394bed54b37591080ae8
[vlc] / modules / video_output / directx / glwin32.c
1 /*****************************************************************************\r
2  * glwin32.c: Windows OpenGL provider\r
3  *****************************************************************************\r
4  * Copyright (C) 2001-2004 VideoLAN\r
5  * $Id$\r
6  *\r
7  * Authors: Gildas Bazin <gbazin@videolan.org>\r
8  *\r
9  * This program is free software; you can redistribute it and/or modify\r
10  * it under the terms of the GNU General Public License as published by\r
11  * the Free Software Foundation; either version 2 of the License, or\r
12  * (at your option) any later version.\r
13  *\r
14  * This program is distributed in the hope that it will be useful,\r
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
17  * GNU General Public License for more details.\r
18  *\r
19  * You should have received a copy of the GNU General Public License\r
20  * along with this program; if not, write to the Free Software\r
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.\r
22  *****************************************************************************/\r
23 \r
24 #include <errno.h>                                                 /* ENOMEM */\r
25 #include <stdlib.h>                                                /* free() */\r
26 #include <string.h>                                            /* strerror() */\r
27 \r
28 #include <vlc/vlc.h>\r
29 #include <vlc/intf.h>\r
30 #include <vlc/vout.h>\r
31 \r
32 #include <windows.h>\r
33 #include <ddraw.h>\r
34 #include <commctrl.h>\r
35 \r
36 #include <multimon.h>\r
37 #undef GetSystemMetrics\r
38 \r
39 #ifndef MONITOR_DEFAULTTONEAREST\r
40 #   define MONITOR_DEFAULTTONEAREST 2\r
41 #endif\r
42 \r
43 #include <GL/gl.h>\r
44 \r
45 #include "vout.h"\r
46 \r
47 /*****************************************************************************\r
48  * Local prototypes.\r
49  *****************************************************************************/\r
50 static int  OpenVideo  ( vlc_object_t * );\r
51 static void CloseVideo ( vlc_object_t * );\r
52 \r
53 static int  Init      ( vout_thread_t * );\r
54 static void End       ( vout_thread_t * );\r
55 static int  Manage    ( vout_thread_t * );\r
56 static void GLSwapBuffers( vout_thread_t * );\r
57 \r
58 /*****************************************************************************\r
59  * Module descriptor\r
60  *****************************************************************************/\r
61 vlc_module_begin();\r
62     set_description( _("Win32 OpenGL provider") );\r
63     set_capability( "opengl provider", 100 );\r
64     add_shortcut( "glwin32" );\r
65     set_callbacks( OpenVideo, CloseVideo );\r
66 vlc_module_end();\r
67 \r
68 #if 0 /* FIXME */\r
69     /* check if we registered a window class because we need to\r
70      * unregister it */\r
71     WNDCLASS wndclass;\r
72     if( GetClassInfo( GetModuleHandle(NULL), "VLC DirectX", &wndclass ) )\r
73         UnregisterClass( "VLC DirectX", GetModuleHandle(NULL) );\r
74 #endif\r
75 \r
76 /*****************************************************************************\r
77  * OpenVideo: allocate OpenGL provider\r
78  *****************************************************************************\r
79  * This function creates and initializes a video window.\r
80  *****************************************************************************/\r
81 static int OpenVideo( vlc_object_t *p_this )\r
82 {\r
83     vout_thread_t * p_vout = (vout_thread_t *)p_this;\r
84     vlc_value_t val;\r
85 \r
86     /* Allocate structure */\r
87     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );\r
88     if( p_vout->p_sys == NULL )\r
89     {\r
90         msg_Err( p_vout, "out of memory" );\r
91         return VLC_ENOMEM;\r
92     }\r
93     memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );\r
94 \r
95     /* Initialisations */\r
96     p_vout->pf_init = Init;\r
97     p_vout->pf_end = End;\r
98     p_vout->pf_manage = Manage;\r
99     p_vout->pf_swap = GLSwapBuffers;\r
100 \r
101     p_vout->p_sys->p_ddobject = NULL;\r
102     p_vout->p_sys->p_display = NULL;\r
103     p_vout->p_sys->p_current_surface = NULL;\r
104     p_vout->p_sys->p_clipper = NULL;\r
105     p_vout->p_sys->hwnd = p_vout->p_sys->hvideownd = NULL;\r
106     p_vout->p_sys->hparent = NULL;\r
107     p_vout->p_sys->i_changes = 0;\r
108     p_vout->p_sys->b_wallpaper = 0;\r
109     vlc_mutex_init( p_vout, &p_vout->p_sys->lock );\r
110     SetRectEmpty( &p_vout->p_sys->rect_display );\r
111     SetRectEmpty( &p_vout->p_sys->rect_parent );\r
112 \r
113     var_Create( p_vout, "video-title", VLC_VAR_STRING | VLC_VAR_DOINHERIT );\r
114 \r
115     p_vout->p_sys->b_cursor_hidden = 0;\r
116     p_vout->p_sys->i_lastmoved = mdate();\r
117 \r
118     /* Set main window's size */\r
119     p_vout->p_sys->i_window_width = p_vout->i_window_width;\r
120     p_vout->p_sys->i_window_height = p_vout->i_window_height;\r
121 \r
122     /* Create the DirectXEventThread, this thread is created by us to isolate\r
123      * the Win32 PeekMessage function calls. We want to do this because\r
124      * Windows can stay blocked inside this call for a long time, and when\r
125      * this happens it thus blocks vlc's video_output thread.\r
126      * DirectXEventThread will take care of the creation of the video\r
127      * window (because PeekMessage has to be called from the same thread which\r
128      * created the window). */\r
129     msg_Dbg( p_vout, "creating DirectXEventThread" );\r
130     p_vout->p_sys->p_event =\r
131         vlc_object_create( p_vout, sizeof(event_thread_t) );\r
132     p_vout->p_sys->p_event->p_vout = p_vout;\r
133     if( vlc_thread_create( p_vout->p_sys->p_event, "DirectX Events Thread",\r
134                            DirectXEventThread, 0, 1 ) )\r
135     {\r
136         msg_Err( p_vout, "cannot create DirectXEventThread" );\r
137         vlc_object_destroy( p_vout->p_sys->p_event );\r
138         p_vout->p_sys->p_event = NULL;\r
139         goto error;\r
140     }\r
141 \r
142     if( p_vout->p_sys->p_event->b_error )\r
143     {\r
144         msg_Err( p_vout, "DirectXEventThread failed" );\r
145         goto error;\r
146     }\r
147 \r
148     vlc_object_attach( p_vout->p_sys->p_event, p_vout );\r
149 \r
150     msg_Dbg( p_vout, "DirectXEventThread running" );\r
151 \r
152     /* Variable to indicate if the window should be on top of others */\r
153     /* Trigger a callback right now */\r
154     var_Get( p_vout, "video-on-top", &val );\r
155     var_Set( p_vout, "video-on-top", val );\r
156 \r
157     return VLC_SUCCESS;\r
158 \r
159  error:\r
160     CloseVideo( VLC_OBJECT(p_vout) );\r
161     return VLC_EGENERIC;\r
162 }\r
163 \r
164 /*****************************************************************************\r
165  * Init: initialize video thread output method\r
166  *****************************************************************************/\r
167 static int Init( vout_thread_t *p_vout )\r
168 {\r
169     PIXELFORMATDESCRIPTOR pfd;\r
170     int iFormat;\r
171 \r
172     /* Change the window title bar text */\r
173     PostMessage( p_vout->p_sys->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 );\r
174 \r
175     p_vout->p_sys->hGLDC = GetDC( p_vout->p_sys->hvideownd );\r
176 \r
177     /* Set the pixel format for the DC */\r
178     memset( &pfd, 0, sizeof( pfd ) );\r
179     pfd.nSize = sizeof( pfd );\r
180     pfd.nVersion = 1;\r
181     pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;\r
182     pfd.iPixelType = PFD_TYPE_RGBA;\r
183     pfd.cColorBits = 24;\r
184     pfd.cDepthBits = 16;\r
185     pfd.iLayerType = PFD_MAIN_PLANE;\r
186     iFormat = ChoosePixelFormat( p_vout->p_sys->hGLDC, &pfd );\r
187     SetPixelFormat( p_vout->p_sys->hGLDC, iFormat, &pfd );\r
188 \r
189     /* Create and enable the render context */\r
190     p_vout->p_sys->hGLRC = wglCreateContext( p_vout->p_sys->hGLDC );\r
191     wglMakeCurrent( p_vout->p_sys->hGLDC, p_vout->p_sys->hGLRC );\r
192 \r
193     return VLC_SUCCESS;\r
194 }\r
195 \r
196 /*****************************************************************************\r
197  * End: terminate Sys video thread output method\r
198  *****************************************************************************\r
199  * Terminate an output method created by Create.\r
200  * It is called at the end of the thread.\r
201  *****************************************************************************/\r
202 static void End( vout_thread_t *p_vout )\r
203 {\r
204     wglMakeCurrent( NULL, NULL );\r
205     wglDeleteContext( p_vout->p_sys->hGLRC );\r
206     ReleaseDC( p_vout->p_sys->hvideownd, p_vout->p_sys->hGLDC );\r
207     return;\r
208 }\r
209 \r
210 /*****************************************************************************\r
211  * CloseVideo: destroy Sys video thread output method\r
212  *****************************************************************************\r
213  * Terminate an output method created by Create\r
214  *****************************************************************************/\r
215 static void CloseVideo( vlc_object_t *p_this )\r
216 {\r
217     vout_thread_t * p_vout = (vout_thread_t *)p_this;\r
218 \r
219     msg_Dbg( p_vout, "CloseVideo" );\r
220 \r
221     if( p_vout->p_sys->p_event )\r
222     {\r
223         vlc_object_detach( p_vout->p_sys->p_event );\r
224 \r
225         /* Kill DirectXEventThread */\r
226         p_vout->p_sys->p_event->b_die = VLC_TRUE;\r
227 \r
228         /* we need to be sure DirectXEventThread won't stay stuck in\r
229          * GetMessage, so we send a fake message */\r
230         if( p_vout->p_sys->hwnd )\r
231         {\r
232             PostMessage( p_vout->p_sys->hwnd, WM_NULL, 0, 0);\r
233         }\r
234 \r
235         vlc_thread_join( p_vout->p_sys->p_event );\r
236         vlc_object_destroy( p_vout->p_sys->p_event );\r
237     }\r
238 \r
239     vlc_mutex_destroy( &p_vout->p_sys->lock );\r
240 \r
241     if( p_vout->p_sys )\r
242     {\r
243         free( p_vout->p_sys );\r
244         p_vout->p_sys = NULL;\r
245     }\r
246 }\r
247 \r
248 /*****************************************************************************\r
249  * Manage: handle Sys events\r
250  *****************************************************************************\r
251  * This function should be called regularly by the video output thread.\r
252  * It returns a non null value if an error occured.\r
253  *****************************************************************************/\r
254 static int Manage( vout_thread_t *p_vout )\r
255 {\r
256     WINDOWPLACEMENT window_placement;\r
257 \r
258     int i_width = p_vout->p_sys->rect_dest.right -\r
259         p_vout->p_sys->rect_dest.left;\r
260     int i_height = p_vout->p_sys->rect_dest.bottom -\r
261         p_vout->p_sys->rect_dest.top;\r
262     glViewport( 0, 0, i_width, i_height );\r
263 \r
264     /* If we do not control our window, we check for geometry changes\r
265      * ourselves because the parent might not send us its events. */\r
266     vlc_mutex_lock( &p_vout->p_sys->lock );\r
267     if( p_vout->p_sys->hparent && !p_vout->b_fullscreen )\r
268     {\r
269         RECT rect_parent;\r
270         POINT point;\r
271 \r
272         vlc_mutex_unlock( &p_vout->p_sys->lock );\r
273 \r
274         GetClientRect( p_vout->p_sys->hparent, &rect_parent );\r
275         point.x = point.y = 0;\r
276         ClientToScreen( p_vout->p_sys->hparent, &point );\r
277         OffsetRect( &rect_parent, point.x, point.y );\r
278 \r
279         if( !EqualRect( &rect_parent, &p_vout->p_sys->rect_parent ) )\r
280         {\r
281             p_vout->p_sys->rect_parent = rect_parent;\r
282 \r
283             /* This one is to force the update even if only\r
284              * the position has changed */\r
285             SetWindowPos( p_vout->p_sys->hwnd, 0, 1, 1,\r
286                           rect_parent.right - rect_parent.left,\r
287                           rect_parent.bottom - rect_parent.top, 0 );\r
288 \r
289             SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,\r
290                           rect_parent.right - rect_parent.left,\r
291                           rect_parent.bottom - rect_parent.top, 0 );\r
292         }\r
293     }\r
294     else\r
295     {\r
296         vlc_mutex_unlock( &p_vout->p_sys->lock );\r
297     }\r
298 \r
299     /* We used to call the Win32 PeekMessage function here to read the window\r
300      * messages. But since window can stay blocked into this function for a\r
301      * long time (for example when you move your window on the screen), I\r
302      * decided to isolate PeekMessage in another thread. */\r
303 \r
304     /*\r
305      * Fullscreen change\r
306      */\r
307     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE\r
308         || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE )\r
309     {\r
310         int i_style = 0;\r
311         vlc_value_t val;\r
312 \r
313         p_vout->b_fullscreen = ! p_vout->b_fullscreen;\r
314 \r
315         /* We need to switch between Maximized and Normal sized window */\r
316         window_placement.length = sizeof(WINDOWPLACEMENT);\r
317         if( p_vout->b_fullscreen )\r
318         {\r
319             if( p_vout->p_sys->hparent )\r
320             {\r
321                 POINT point;\r
322 \r
323                 /* Retrieve the window position */\r
324                 point.x = point.y = 0;\r
325                 ClientToScreen( p_vout->p_sys->hwnd, &point );\r
326                 SetParent( p_vout->p_sys->hwnd, GetDesktopWindow() );\r
327                 SetWindowPos( p_vout->p_sys->hwnd, 0, point.x, point.y, 0, 0,\r
328                               SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED );\r
329                 SetForegroundWindow( p_vout->p_sys->hwnd );\r
330             }\r
331 \r
332             /* Maximized window */\r
333             GetWindowPlacement( p_vout->p_sys->hwnd, &window_placement );\r
334             window_placement.showCmd = SW_SHOWMAXIMIZED;\r
335             /* Change window style, no borders and no title bar */\r
336             i_style = WS_CLIPCHILDREN | WS_VISIBLE | WS_POPUP;\r
337         }\r
338         else\r
339         {\r
340             if( p_vout->p_sys->hparent )\r
341             {\r
342                 SetParent( p_vout->p_sys->hwnd, p_vout->p_sys->hparent );\r
343                 SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0, 0, 0,\r
344                               SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED );\r
345                 i_style = WS_CLIPCHILDREN | WS_VISIBLE | WS_CHILD;\r
346                 SetForegroundWindow( p_vout->p_sys->hparent );\r
347             }\r
348             else\r
349             {\r
350                 i_style = WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW |\r
351                           WS_SIZEBOX | WS_VISIBLE;\r
352             }\r
353 \r
354             /* Normal window */\r
355             GetWindowPlacement( p_vout->p_sys->hwnd, &window_placement );\r
356             window_placement.showCmd = SW_SHOWNORMAL;\r
357 \r
358             /* Make sure the mouse cursor is displayed */\r
359             PostMessage( p_vout->p_sys->hwnd, WM_VLC_SHOW_MOUSE, 0, 0 );\r
360         }\r
361 \r
362         /* Change window style, borders and title bar */\r
363         SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE, i_style );\r
364         SetWindowPlacement( p_vout->p_sys->hwnd, &window_placement );\r
365         SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0, 0, 0,\r
366                       SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED );\r
367 \r
368         /* Update the object variable and trigger callback */\r
369         val.b_bool = p_vout->b_fullscreen;\r
370         var_Set( p_vout, "fullscreen", val );\r
371 \r
372         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;\r
373         p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE;\r
374     }\r
375 \r
376     /*\r
377      * Pointer change\r
378      */\r
379     if( p_vout->b_fullscreen && !p_vout->p_sys->b_cursor_hidden &&\r
380         (mdate() - p_vout->p_sys->i_lastmoved) > 5000000 )\r
381     {\r
382         POINT point;\r
383         HWND hwnd;\r
384 \r
385         /* Hide the cursor only if it is inside our window */\r
386         GetCursorPos( &point );\r
387         hwnd = WindowFromPoint(point);\r
388         if( hwnd == p_vout->p_sys->hwnd || hwnd == p_vout->p_sys->hvideownd )\r
389         {\r
390             PostMessage( p_vout->p_sys->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 );\r
391         }\r
392         else\r
393         {\r
394             p_vout->p_sys->i_lastmoved = mdate();\r
395         }\r
396     }\r
397 \r
398     /*\r
399      * "Always on top" status change\r
400      */\r
401     if( p_vout->p_sys->b_on_top_change )\r
402     {\r
403         vlc_value_t val;\r
404         HMENU hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );\r
405 \r
406         var_Get( p_vout, "video-on-top", &val );\r
407 \r
408         /* Set the window on top if necessary */\r
409         if( val.b_bool && !( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )\r
410                            & WS_EX_TOPMOST ) )\r
411         {\r
412             CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,\r
413                            MF_BYCOMMAND | MFS_CHECKED );\r
414             SetWindowPos( p_vout->p_sys->hwnd, HWND_TOPMOST, 0, 0, 0, 0,\r
415                           SWP_NOSIZE | SWP_NOMOVE );\r
416         }\r
417         else\r
418         /* The window shouldn't be on top */\r
419         if( !val.b_bool && ( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )\r
420                            & WS_EX_TOPMOST ) )\r
421         {\r
422             CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,\r
423                            MF_BYCOMMAND | MFS_UNCHECKED );\r
424             SetWindowPos( p_vout->p_sys->hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,\r
425                           SWP_NOSIZE | SWP_NOMOVE );\r
426         }\r
427 \r
428         p_vout->p_sys->b_on_top_change = VLC_FALSE;\r
429     }\r
430 \r
431     /* Check if the event thread is still running */\r
432     if( p_vout->p_sys->p_event->b_die )\r
433     {\r
434         return VLC_EGENERIC; /* exit */\r
435     }\r
436 \r
437     return VLC_SUCCESS;\r
438 }\r
439 \r
440 /*****************************************************************************\r
441  * GLSwapBuffers: swap front/back buffers\r
442  *****************************************************************************/\r
443 static void GLSwapBuffers( vout_thread_t *p_vout )\r
444 {\r
445     SwapBuffers( p_vout->p_sys->hGLDC );\r
446 }\r
447 \r
448 int DirectXUpdateOverlay( vout_thread_t *p_vout )\r
449 {\r
450     return 1;\r
451 }\r