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