43
39
# define MONITOR_DEFAULTTONEAREST 2
42
#include "../opengl.h"
45
/*****************************************************************************
47
*****************************************************************************/
48
static int Open (vlc_object_t *);
49
static void Close(vlc_object_t *);
52
set_category(CAT_VIDEO)
53
set_subcategory(SUBCAT_VIDEO_VOUT)
54
set_shortname("OpenGL")
55
set_description(N_("OpenGL video output"))
56
set_capability("vout display", 20)
57
add_shortcut("glwin32")
58
add_shortcut("opengl")
59
set_callbacks(Open, Close)
50
62
/*****************************************************************************
51
63
* Local prototypes.
52
64
*****************************************************************************/
53
static int OpenVideo ( vlc_object_t * );
54
static void CloseVideo ( vlc_object_t * );
56
static int Init ( vout_thread_t * );
57
static void End ( vout_thread_t * );
58
static int Manage ( vout_thread_t * );
59
static void GLSwapBuffers( vout_thread_t * );
60
static void FirstSwap( vout_thread_t * );
62
/*****************************************************************************
64
*****************************************************************************/
66
set_category( CAT_VIDEO )
67
set_subcategory( SUBCAT_VIDEO_VOUT )
68
set_shortname( "OpenGL" )
69
set_description( N_("OpenGL video output") )
70
set_capability( "opengl provider", 100 )
71
add_shortcut( "glwin32" )
72
set_callbacks( OpenVideo, CloseVideo )
74
/* FIXME: Hack to avoid unregistering our window class */
75
linked_with_a_crap_library_which_uses_atexit ()
79
/* check if we registered a window class because we need to
82
if( GetClassInfo( GetModuleHandle(NULL), "VLC DirectX", &wndclass ) )
83
UnregisterClass( "VLC DirectX", GetModuleHandle(NULL) );
86
/*****************************************************************************
87
* OpenVideo: allocate OpenGL provider
88
*****************************************************************************
89
* This function creates and initializes a video window.
90
*****************************************************************************/
91
static int OpenVideo( vlc_object_t *p_this )
65
static picture_pool_t *Pool (vout_display_t *, unsigned);
66
static void Prepare(vout_display_t *, picture_t *);
67
static void Display(vout_display_t *, picture_t *);
68
static int Control(vout_display_t *, int, va_list);
69
static void Manage (vout_display_t *);
71
static void Swap (vout_opengl_t *);
74
* It creates an OpenGL vout display.
76
static int Open(vlc_object_t *object)
93
vout_thread_t * p_vout = (vout_thread_t *)p_this;
78
vout_display_t *vd = (vout_display_t *)object;
79
vout_display_sys_t *sys;
95
81
/* Allocate structure */
96
p_vout->p_sys = calloc( 1, sizeof( vout_sys_t ) );
97
if( p_vout->p_sys == NULL )
82
vd->sys = sys = calloc(1, sizeof(*sys));
100
/* Initialisations */
101
p_vout->pf_init = Init;
102
p_vout->pf_end = End;
103
p_vout->pf_manage = Manage;
104
p_vout->pf_swap = FirstSwap;
106
p_vout->p_sys->hwnd = p_vout->p_sys->hvideownd = NULL;
107
p_vout->p_sys->hparent = p_vout->p_sys->hfswnd = NULL;
108
p_vout->p_sys->i_changes = 0;
109
vlc_mutex_init( &p_vout->p_sys->lock );
110
SetRectEmpty( &p_vout->p_sys->rect_display );
111
SetRectEmpty( &p_vout->p_sys->rect_parent );
113
var_Create( p_vout, "video-title", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
115
p_vout->p_sys->b_cursor_hidden = 0;
116
p_vout->p_sys->i_lastmoved = mdate();
117
p_vout->p_sys->i_mouse_hide_timeout =
118
var_GetInteger(p_vout, "mouse-hide-timeout") * 1000;
120
/* Set main window's size */
121
p_vout->p_sys->i_window_width = p_vout->i_window_width;
122
p_vout->p_sys->i_window_height = p_vout->i_window_height;
124
/* Create the Vout EventThread, this thread is created by us to isolate
125
* the Win32 PeekMessage function calls. We want to do this because
126
* Windows can stay blocked inside this call for a long time, and when
127
* this happens it thus blocks vlc's video_output thread.
128
* Vout EventThread will take care of the creation of the video
129
* window (because PeekMessage has to be called from the same thread which
130
* created the window). */
131
msg_Dbg( p_vout, "creating Vout EventThread" );
132
p_vout->p_sys->p_event =
133
vlc_object_create( p_vout, sizeof(event_thread_t) );
134
p_vout->p_sys->p_event->p_vout = p_vout;
135
p_vout->p_sys->p_event->window_ready = CreateEvent( NULL, TRUE, FALSE, NULL );
136
if( vlc_thread_create( p_vout->p_sys->p_event, "Vout Events Thread",
139
msg_Err( p_vout, "cannot create Vout EventThread" );
140
CloseHandle( p_vout->p_sys->p_event->window_ready );
141
vlc_object_release( p_vout->p_sys->p_event );
142
p_vout->p_sys->p_event = NULL;
145
WaitForSingleObject( p_vout->p_sys->p_event->window_ready, INFINITE );
146
CloseHandle( p_vout->p_sys->p_event->window_ready );
148
if( p_vout->p_sys->p_event->b_error )
150
msg_Err( p_vout, "Vout EventThread failed" );
154
vlc_object_attach( p_vout->p_sys->p_event, p_vout );
156
msg_Dbg( p_vout, "Vout EventThread running" );
158
/* Variable to indicate if the window should be on top of others */
159
/* Trigger a callback right now */
160
var_TriggerCallback( p_vout, "video-on-top" );
165
CloseVideo( VLC_OBJECT(p_vout) );
169
/*****************************************************************************
170
* Init: initialize video thread output method
171
*****************************************************************************/
172
static int Init( vout_thread_t *p_vout )
90
EventThreadUpdateTitle(sys->event, VOUT_TITLE " (OpenGL output)");
93
sys->hGLDC = GetDC(sys->hvideownd);
95
/* Set the pixel format for the DC */
174
96
PIXELFORMATDESCRIPTOR pfd;
177
/* Change the window title bar text */
178
PostMessage( p_vout->p_sys->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 );
180
p_vout->p_sys->hGLDC = GetDC( p_vout->p_sys->hvideownd );
182
/* Set the pixel format for the DC */
183
memset( &pfd, 0, sizeof( pfd ) );
184
pfd.nSize = sizeof( pfd );
97
memset(&pfd, 0, sizeof(pfd));
98
pfd.nSize = sizeof(pfd);
186
100
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
187
101
pfd.iPixelType = PFD_TYPE_RGBA;
188
102
pfd.cColorBits = 24;
189
103
pfd.cDepthBits = 16;
190
104
pfd.iLayerType = PFD_MAIN_PLANE;
191
iFormat = ChoosePixelFormat( p_vout->p_sys->hGLDC, &pfd );
192
SetPixelFormat( p_vout->p_sys->hGLDC, iFormat, &pfd );
105
SetPixelFormat(sys->hGLDC,
106
ChoosePixelFormat(sys->hGLDC, &pfd), &pfd);
194
108
/* Create and enable the render context */
195
p_vout->p_sys->hGLRC = wglCreateContext( p_vout->p_sys->hGLDC );
196
wglMakeCurrent( p_vout->p_sys->hGLDC, p_vout->p_sys->hGLRC );
201
/*****************************************************************************
202
* End: terminate Sys video thread output method
203
*****************************************************************************
204
* Terminate an output method created by Create.
205
* It is called at the end of the thread.
206
*****************************************************************************/
207
static void End( vout_thread_t *p_vout )
209
wglMakeCurrent( NULL, NULL );
210
wglDeleteContext( p_vout->p_sys->hGLRC );
211
ReleaseDC( p_vout->p_sys->hvideownd, p_vout->p_sys->hGLDC );
215
/*****************************************************************************
216
* CloseVideo: destroy Sys video thread output method
217
*****************************************************************************
218
* Terminate an output method created by Create
219
*****************************************************************************/
220
static void CloseVideo( vlc_object_t *p_this )
222
vout_thread_t * p_vout = (vout_thread_t *)p_this;
224
if( p_vout->b_fullscreen )
226
msg_Dbg( p_vout, "Quitting fullscreen" );
227
Win32ToggleFullscreen( p_vout );
228
/* Force fullscreen in the core for the next video */
229
var_SetBool( p_vout, "fullscreen", true );
232
if( p_vout->p_sys->p_event )
234
vlc_object_detach( p_vout->p_sys->p_event );
236
/* Kill Vout EventThread */
237
vlc_object_kill( p_vout->p_sys->p_event );
239
/* we need to be sure Vout EventThread won't stay stuck in
240
* GetMessage, so we send a fake message */
241
if( p_vout->p_sys->hwnd )
243
PostMessage( p_vout->p_sys->hwnd, WM_NULL, 0, 0);
246
vlc_thread_join( p_vout->p_sys->p_event );
247
vlc_object_release( p_vout->p_sys->p_event );
250
vlc_mutex_destroy( &p_vout->p_sys->lock );
252
free( p_vout->p_sys );
253
p_vout->p_sys = NULL;
256
/*****************************************************************************
257
* Manage: handle Sys events
258
*****************************************************************************
259
* This function should be called regularly by the video output thread.
260
* It returns a non null value if an error occurred.
261
*****************************************************************************/
262
static int Manage( vout_thread_t *p_vout )
264
int i_width = p_vout->p_sys->rect_dest.right -
265
p_vout->p_sys->rect_dest.left;
266
int i_height = p_vout->p_sys->rect_dest.bottom -
267
p_vout->p_sys->rect_dest.top;
268
glViewport( 0, 0, i_width, i_height );
270
/* If we do not control our window, we check for geometry changes
271
* ourselves because the parent might not send us its events. */
272
vlc_mutex_lock( &p_vout->p_sys->lock );
273
if( p_vout->p_sys->hparent && !p_vout->b_fullscreen )
278
vlc_mutex_unlock( &p_vout->p_sys->lock );
280
GetClientRect( p_vout->p_sys->hparent, &rect_parent );
281
point.x = point.y = 0;
282
ClientToScreen( p_vout->p_sys->hparent, &point );
283
OffsetRect( &rect_parent, point.x, point.y );
285
if( !EqualRect( &rect_parent, &p_vout->p_sys->rect_parent ) )
287
p_vout->p_sys->rect_parent = rect_parent;
289
/* This one is to force the update even if only
290
* the position has changed */
291
SetWindowPos( p_vout->p_sys->hwnd, 0, 1, 1,
292
rect_parent.right - rect_parent.left,
293
rect_parent.bottom - rect_parent.top, 0 );
295
SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
296
rect_parent.right - rect_parent.left,
297
rect_parent.bottom - rect_parent.top, 0 );
302
vlc_mutex_unlock( &p_vout->p_sys->lock );
305
/* autoscale toggle */
306
if( p_vout->i_changes & VOUT_SCALE_CHANGE )
308
p_vout->i_changes &= ~VOUT_SCALE_CHANGE;
310
p_vout->b_autoscale = var_GetBool( p_vout, "autoscale" );
311
p_vout->i_zoom = (int) ZOOM_FP_FACTOR;
313
UpdateRects( p_vout, true );
317
if( p_vout->i_changes & VOUT_ZOOM_CHANGE )
319
p_vout->i_changes &= ~VOUT_ZOOM_CHANGE;
321
p_vout->b_autoscale = false;
323
(int)( ZOOM_FP_FACTOR * var_GetFloat( p_vout, "scale" ) );
324
UpdateRects( p_vout, true );
327
/* Check for cropping / aspect changes */
328
if( p_vout->i_changes & VOUT_CROP_CHANGE ||
329
p_vout->i_changes & VOUT_ASPECT_CHANGE )
331
p_vout->i_changes &= ~VOUT_CROP_CHANGE;
332
p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;
334
p_vout->fmt_out.i_x_offset = p_vout->fmt_in.i_x_offset;
335
p_vout->fmt_out.i_y_offset = p_vout->fmt_in.i_y_offset;
336
p_vout->fmt_out.i_visible_width = p_vout->fmt_in.i_visible_width;
337
p_vout->fmt_out.i_visible_height = p_vout->fmt_in.i_visible_height;
338
p_vout->fmt_out.i_aspect = p_vout->fmt_in.i_aspect;
339
p_vout->fmt_out.i_sar_num = p_vout->fmt_in.i_sar_num;
340
p_vout->fmt_out.i_sar_den = p_vout->fmt_in.i_sar_den;
341
p_vout->output.i_aspect = p_vout->fmt_in.i_aspect;
342
UpdateRects( p_vout, true );
345
/* We used to call the Win32 PeekMessage function here to read the window
346
* messages. But since window can stay blocked into this function for a
347
* long time (for example when you move your window on the screen), I
348
* decided to isolate PeekMessage in another thread. */
353
if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE
354
|| p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE )
356
Win32ToggleFullscreen( p_vout );
358
p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
359
p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
365
if( p_vout->b_fullscreen && !p_vout->p_sys->b_cursor_hidden &&
366
(mdate() - p_vout->p_sys->i_lastmoved) >
367
p_vout->p_sys->i_mouse_hide_timeout )
372
/* Hide the cursor only if it is inside our window */
373
GetCursorPos( &point );
374
hwnd = WindowFromPoint(point);
375
if( hwnd == p_vout->p_sys->hwnd || hwnd == p_vout->p_sys->hvideownd )
377
PostMessage( p_vout->p_sys->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 );
381
p_vout->p_sys->i_lastmoved = mdate();
386
* "Always on top" status change
388
if( p_vout->p_sys->b_on_top_change )
391
HMENU hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );
393
var_Get( p_vout, "video-on-top", &val );
395
/* Set the window on top if necessary */
396
if( val.b_bool && !( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
399
CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
400
MF_BYCOMMAND | MFS_CHECKED );
401
SetWindowPos( p_vout->p_sys->hwnd, HWND_TOPMOST, 0, 0, 0, 0,
402
SWP_NOSIZE | SWP_NOMOVE );
405
/* The window shouldn't be on top */
406
if( !val.b_bool && ( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
409
CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
410
MF_BYCOMMAND | MFS_UNCHECKED );
411
SetWindowPos( p_vout->p_sys->hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
412
SWP_NOSIZE | SWP_NOMOVE );
415
p_vout->p_sys->b_on_top_change = false;
418
/* Check if the event thread is still running */
419
if( !vlc_object_alive (p_vout->p_sys->p_event) )
421
return VLC_EGENERIC; /* exit */
427
/*****************************************************************************
428
* GLSwapBuffers: swap front/back buffers
429
*****************************************************************************/
430
static void GLSwapBuffers( vout_thread_t *p_vout )
432
SwapBuffers( p_vout->p_sys->hGLDC );
436
** this function is only used once when the first picture is received
437
** this function will show the video window once a picture is ready
440
static void FirstSwap( vout_thread_t *p_vout )
442
/* get initial picture buffer swapped to front buffer */
443
GLSwapBuffers( p_vout );
446
** Video window is initially hidden, show it now since we got a
449
SetWindowPos( p_vout->p_sys->hvideownd, NULL, 0, 0, 0, 0,
457
/* use and restores proper swap function for further pictures */
458
p_vout->pf_swap = GLSwapBuffers;
109
sys->hGLRC = wglCreateContext(sys->hGLDC);
110
wglMakeCurrent(sys->hGLDC, sys->hGLRC);
114
sys->gl.unlock = NULL;
118
video_format_t fmt = vd->fmt;
119
if (vout_display_opengl_Init(&sys->vgl, &fmt, &sys->gl))
122
vout_display_info_t info = vd->info;
123
info.has_double_click = true;
124
info.has_hide_mouse = false;
125
info.has_pictures_invalid = true;
127
/* Setup vout_display now that everything is fine */
132
vd->prepare = Prepare;
133
vd->display = Display;
134
vd->control = Control;
145
* It destroys an OpenGL vout display.
147
static void Close(vlc_object_t *object)
149
vout_display_t *vd = (vout_display_t *)object;
150
vout_display_sys_t *sys = vd->sys;
153
vout_display_opengl_Clean(&sys->vgl);
155
if (sys->hGLDC && sys->hGLRC)
156
wglMakeCurrent(NULL, NULL);
158
wglDeleteContext(sys->hGLRC);
160
ReleaseDC(sys->hvideownd, sys->hGLDC);
168
static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
170
vout_display_sys_t *sys = vd->sys;
174
sys->pool = vout_display_opengl_GetPool(&sys->vgl);
178
static void Prepare(vout_display_t *vd, picture_t *picture)
180
vout_display_sys_t *sys = vd->sys;
182
vout_display_opengl_Prepare(&sys->vgl, picture);
185
static void Display(vout_display_t *vd, picture_t *picture)
187
vout_display_sys_t *sys = vd->sys;
189
vout_display_opengl_Display(&sys->vgl, &vd->source);
191
picture_Release(picture);
196
static int Control(vout_display_t *vd, int query, va_list args)
199
case VOUT_DISPLAY_GET_OPENGL: {
200
vout_opengl_t **gl = va_arg(args, vout_opengl_t **);
205
return CommonControl(vd, query, args);
209
static void Manage (vout_display_t *vd)
211
vout_display_sys_t *sys = vd->sys;
215
const int width = sys->rect_dest.right - sys->rect_dest.left;
216
const int height = sys->rect_dest.bottom - sys->rect_dest.top;
217
glViewport(0, 0, width, height);
220
static void Swap(vout_opengl_t *gl)
222
vout_display_t *vd = gl->sys;
224
SwapBuffers(vd->sys->hGLDC);