1 /*****************************************************************************
2 * win32.c: Screen capture module.
3 *****************************************************************************
4 * Copyright (C) 2004-2011 VLC authors and VideoLAN
5 * $Id: d64eada92b5131ebffe301cdb73cb14272971cc9 $
6 *
7 * Authors: Gildas Bazin <gbazin@videolan.org>
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
23
24 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33
34 #include "screen.h"
35
36 struct screen_data_t
37 {
38 HDC hdc_src;
39 HDC hdc_dst;
40 BITMAPINFO bmi;
41 HGDIOBJ hgdi_backup;
42 POINT ptl; /* Coordinates of the primary display's top left, when the origin
43 * is taken to be the top left of the entire virtual screen */
44
45 int i_fragment_size;
46 int i_fragment;
47 block_t *p_block;
48 };
49
50 /*
51 * In screen coordinates the origin is the upper-left corner of the primary
52 * display, and points can have negative x/y when other displays are located
53 * to the left/top of the primary.
54 *
55 * Windows may supply these coordinates in physical or logical units
56 * depending on the version of Windows and the DPI awareness of the application.
57 * I have noticed that even different interfaces of VLC (qt, rc...) can lead
58 * to differences in DPI awareness. The choice of physical vs logical seems
59 * to be universal though (it applies to everything we use, from GetCursorPos
60 * to GetSystemMetrics and BitBlt) so we don't have to worry about anything.
61 *
62 * The only issue here is that it can be confusing to users when setting e.g.
63 * subscreen position and dimensions. This however can be controlled by
64 * disabling display scaling in the compatibility settings of the VLC executable.
65 */
FromScreenCoordinates(demux_t * p_demux,POINT * p_point)66 static inline void FromScreenCoordinates( demux_t *p_demux, POINT *p_point )
67 {
68 screen_data_t *p_data = p_demux->p_sys->p_data;
69 p_point->x += p_data->ptl.x;
70 p_point->y += p_data->ptl.y;
71 }
72
ToScreenCoordinates(demux_t * p_demux,POINT * p_point)73 static inline void ToScreenCoordinates( demux_t *p_demux, POINT *p_point )
74 {
75 screen_data_t *p_data = p_demux->p_sys->p_data;
76 p_point->x -= p_data->ptl.x;
77 p_point->y -= p_data->ptl.y;
78 }
79
screen_InitCapture(demux_t * p_demux)80 int screen_InitCapture( demux_t *p_demux )
81 {
82 demux_sys_t *p_sys = p_demux->p_sys;
83 screen_data_t *p_data;
84 int i_chroma, i_bits_per_pixel;
85
86 p_sys->p_data = p_data = calloc( 1, sizeof( screen_data_t ) );
87 if( !p_data )
88 return VLC_ENOMEM;
89
90 /* Get the device context for the whole screen */
91 p_data->hdc_src = CreateDC( TEXT("DISPLAY"), NULL, NULL, NULL );
92 if( !p_data->hdc_src )
93 {
94 msg_Err( p_demux, "cannot get device context" );
95 free( p_data );
96 return VLC_EGENERIC;
97 }
98
99 p_data->hdc_dst = CreateCompatibleDC( p_data->hdc_src );
100 if( !p_data->hdc_dst )
101 {
102 msg_Err( p_demux, "cannot get compat device context" );
103 ReleaseDC( 0, p_data->hdc_src );
104 free( p_data );
105 return VLC_EGENERIC;
106 }
107
108 i_bits_per_pixel = GetDeviceCaps( p_data->hdc_src, BITSPIXEL );
109 switch( i_bits_per_pixel )
110 {
111 case 8: /* FIXME: set the palette */
112 i_chroma = VLC_CODEC_RGB8; break;
113 case 15:
114 case 16: /* Yes it is really 15 bits (when using BI_RGB) */
115 i_chroma = VLC_CODEC_RGB15; break;
116 case 24:
117 i_chroma = VLC_CODEC_RGB24; break;
118 case 32:
119 i_chroma = VLC_CODEC_RGB32; break;
120 default:
121 msg_Err( p_demux, "unknown screen depth %i", i_bits_per_pixel );
122 DeleteDC( p_data->hdc_dst );
123 ReleaseDC( 0, p_data->hdc_src );
124 free( p_data );
125 return VLC_EGENERIC;
126 }
127
128 es_format_Init( &p_sys->fmt, VIDEO_ES, i_chroma );
129 p_sys->fmt.video.i_visible_width =
130 p_sys->fmt.video.i_width = GetSystemMetrics( SM_CXVIRTUALSCREEN );
131 p_sys->fmt.video.i_visible_height =
132 p_sys->fmt.video.i_height = GetSystemMetrics( SM_CYVIRTUALSCREEN );
133 p_sys->fmt.video.i_bits_per_pixel = i_bits_per_pixel;
134 p_sys->fmt.video.i_sar_num = p_sys->fmt.video.i_sar_den = 1;
135 p_sys->fmt.video.i_chroma = i_chroma;
136 p_sys->fmt.video.transfer = TRANSFER_FUNC_SRGB;
137 p_sys->fmt.video.b_color_range_full = true;
138
139 switch( i_chroma )
140 {
141 case VLC_CODEC_RGB15:
142 p_sys->fmt.video.i_rmask = 0x7c00;
143 p_sys->fmt.video.i_gmask = 0x03e0;
144 p_sys->fmt.video.i_bmask = 0x001f;
145 break;
146 case VLC_CODEC_RGB24:
147 p_sys->fmt.video.i_rmask = 0x00ff0000;
148 p_sys->fmt.video.i_gmask = 0x0000ff00;
149 p_sys->fmt.video.i_bmask = 0x000000ff;
150 break;
151 case VLC_CODEC_RGB32:
152 p_sys->fmt.video.i_rmask = 0x00ff0000;
153 p_sys->fmt.video.i_gmask = 0x0000ff00;
154 p_sys->fmt.video.i_bmask = 0x000000ff;
155 break;
156 default:
157 msg_Warn( p_demux, "Unknown RGB masks" );
158 break;
159 }
160
161 p_data->ptl.x = - GetSystemMetrics( SM_XVIRTUALSCREEN );
162 p_data->ptl.y = - GetSystemMetrics( SM_YVIRTUALSCREEN );
163
164 return VLC_SUCCESS;
165 }
166
screen_CloseCapture(demux_t * p_demux)167 int screen_CloseCapture( demux_t *p_demux )
168 {
169 demux_sys_t *p_sys = p_demux->p_sys;
170 screen_data_t *p_data = p_sys->p_data;
171
172 if( p_data->p_block ) block_Release( p_data->p_block );
173
174 if( p_data->hgdi_backup)
175 SelectObject( p_data->hdc_dst, p_data->hgdi_backup );
176
177 DeleteDC( p_data->hdc_dst );
178 ReleaseDC( 0, p_data->hdc_src );
179 free( p_data );
180
181 return VLC_SUCCESS;
182 }
183
184 struct block_sys_t
185 {
186 block_t self;
187 HBITMAP hbmp;
188 };
189
CaptureBlockRelease(block_t * p_block)190 static void CaptureBlockRelease( block_t *p_block )
191 {
192 DeleteObject( ((struct block_sys_t *)p_block)->hbmp );
193 free( p_block );
194 }
195
CaptureBlockNew(demux_t * p_demux)196 static block_t *CaptureBlockNew( demux_t *p_demux )
197 {
198 demux_sys_t *p_sys = p_demux->p_sys;
199 screen_data_t *p_data = p_sys->p_data;
200 struct block_sys_t *p_block;
201 void *p_buffer;
202 int i_buffer;
203 HBITMAP hbmp;
204
205 if( p_data->bmi.bmiHeader.biSize == 0 )
206 {
207 int i_val;
208 /* Create the bitmap info header */
209 p_data->bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
210 p_data->bmi.bmiHeader.biWidth = p_sys->fmt.video.i_width;
211 p_data->bmi.bmiHeader.biHeight = - p_sys->fmt.video.i_height;
212 p_data->bmi.bmiHeader.biPlanes = 1;
213 p_data->bmi.bmiHeader.biBitCount = p_sys->fmt.video.i_bits_per_pixel;
214 p_data->bmi.bmiHeader.biCompression = BI_RGB;
215 p_data->bmi.bmiHeader.biSizeImage = 0;
216 p_data->bmi.bmiHeader.biXPelsPerMeter = 0;
217 p_data->bmi.bmiHeader.biYPelsPerMeter = 0;
218 p_data->bmi.bmiHeader.biClrUsed = 0;
219 p_data->bmi.bmiHeader.biClrImportant = 0;
220
221 i_val = var_CreateGetInteger( p_demux, "screen-fragment-size" );
222 p_data->i_fragment_size = i_val > 0 ? i_val : (int)p_sys->fmt.video.i_height;
223 p_data->i_fragment_size = i_val > (int)p_sys->fmt.video.i_height ?
224 (int)p_sys->fmt.video.i_height :
225 p_data->i_fragment_size;
226 p_sys->f_fps *= (p_sys->fmt.video.i_height/p_data->i_fragment_size);
227 p_sys->i_incr = 1000000 / p_sys->f_fps;
228 p_data->i_fragment = 0;
229 p_data->p_block = 0;
230 }
231
232
233 /* Create the bitmap storage space */
234 hbmp = CreateDIBSection( p_data->hdc_dst, &p_data->bmi, DIB_RGB_COLORS,
235 &p_buffer, NULL, 0 );
236 if( !hbmp || !p_buffer )
237 {
238 msg_Err( p_demux, "cannot create bitmap" );
239 goto error;
240 }
241
242 /* Select the bitmap into the compatible DC */
243 if( !p_data->hgdi_backup )
244 p_data->hgdi_backup = SelectObject( p_data->hdc_dst, hbmp );
245 else
246 SelectObject( p_data->hdc_dst, hbmp );
247
248 if( !p_data->hgdi_backup )
249 {
250 msg_Err( p_demux, "cannot select bitmap" );
251 goto error;
252 }
253
254 /* Build block */
255 if( !(p_block = malloc( sizeof( block_t ) + sizeof( struct block_sys_t ) )) )
256 goto error;
257
258 /* Fill all fields */
259 int i_stride =
260 ( ( ( ( p_sys->fmt.video.i_width * p_sys->fmt.video.i_bits_per_pixel ) + 31 ) & ~31 ) >> 3 );
261 i_buffer = i_stride * p_sys->fmt.video.i_height;
262 block_Init( &p_block->self, p_buffer, i_buffer );
263 p_block->self.pf_release = CaptureBlockRelease;
264 p_block->hbmp = hbmp;
265
266 return &p_block->self;
267
268 error:
269 if( hbmp ) DeleteObject( hbmp );
270 return NULL;
271 }
272
screen_Capture(demux_t * p_demux)273 block_t *screen_Capture( demux_t *p_demux )
274 {
275 demux_sys_t *p_sys = p_demux->p_sys;
276 screen_data_t *p_data = p_sys->p_data;
277
278 if( !p_data->i_fragment )
279 {
280 if( !( p_data->p_block = CaptureBlockNew( p_demux ) ) )
281 {
282 msg_Warn( p_demux, "cannot get block" );
283 return NULL;
284 }
285 }
286
287 if( p_sys->b_follow_mouse )
288 {
289 POINT pos;
290 GetCursorPos( &pos );
291 FromScreenCoordinates( p_demux, &pos );
292 FollowMouse( p_sys, pos.x, pos.y );
293 }
294
295 POINT top_left = { p_sys->i_left, p_sys->i_top };
296 ToScreenCoordinates( p_demux, &top_left );
297
298 if( !BitBlt( p_data->hdc_dst, 0,
299 p_data->i_fragment * p_data->i_fragment_size,
300 p_sys->fmt.video.i_width, p_data->i_fragment_size,
301 p_data->hdc_src, top_left.x, top_left.y +
302 p_data->i_fragment * p_data->i_fragment_size,
303 SRCCOPY | CAPTUREBLT ) )
304 {
305 msg_Err( p_demux, "error during BitBlt()" );
306 return NULL;
307 }
308
309 p_data->i_fragment++;
310
311 if( !( p_data->i_fragment %
312 (p_sys->fmt.video.i_height/p_data->i_fragment_size) ) )
313 {
314 block_t *p_block = p_data->p_block;
315 p_data->i_fragment = 0;
316 p_data->p_block = 0;
317
318 if( p_sys->p_mouse )
319 {
320 POINT pos;
321
322 GetCursorPos( &pos );
323 FromScreenCoordinates( p_demux, &pos );
324 RenderCursor( p_demux, pos.x, pos.y,
325 p_block->p_buffer );
326 }
327
328 return p_block;
329 }
330
331 return NULL;
332 }
333