Implement mouse pointer support in win32 screen.
[vlc.git] / modules / access / screen / win32.c
1 /*****************************************************************************
2  * win32.c: Screen capture module.
3  *****************************************************************************
4  * Copyright (C) 2004-2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 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 General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, 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 #ifndef CAPTUREBLT
37 #   define CAPTUREBLT (DWORD)0x40000000 /* Include layered windows */
38 #endif
39
40 struct screen_data_t
41 {
42     HDC hdc_src;
43     HDC hdc_dst;
44     BITMAPINFO bmi;
45     HGDIOBJ hgdi_backup;
46
47     int i_fragment_size;
48     int i_fragment;
49     block_t *p_block;
50 };
51
52 int screen_InitCapture( demux_t *p_demux )
53 {
54     demux_sys_t *p_sys = p_demux->p_sys;
55     screen_data_t *p_data;
56     int i_chroma, i_bits_per_pixel;
57
58     p_sys->p_data = p_data = malloc( sizeof( screen_data_t ) );
59     if( !p_data )
60         return VLC_ENOMEM;
61     memset( p_data, 0, sizeof( screen_data_t ) );
62
63     /* Get the device context for the whole screen */
64     p_data->hdc_src = CreateDC( "DISPLAY", NULL, NULL, NULL );
65     if( !p_data->hdc_src )
66     {
67         msg_Err( p_demux, "cannot get device context" );
68         return VLC_EGENERIC;
69     }
70
71     p_data->hdc_dst = CreateCompatibleDC( p_data->hdc_src );
72     if( !p_data->hdc_dst )
73     {
74         msg_Err( p_demux, "cannot get compat device context" );
75         ReleaseDC( 0, p_data->hdc_src );
76         return VLC_EGENERIC;
77     }
78
79     i_bits_per_pixel = GetDeviceCaps( p_data->hdc_src, BITSPIXEL );
80     switch( i_bits_per_pixel )
81     {
82     case 8: /* FIXME: set the palette */
83         i_chroma = VLC_FOURCC('R','G','B','2'); break;
84     case 15:
85     case 16:    /* Yes it is really 15 bits (when using BI_RGB) */
86         i_chroma = VLC_FOURCC('R','V','1','5'); break;
87     case 24:
88         i_chroma = VLC_FOURCC('R','V','2','4'); break;
89     case 32:
90         i_chroma = VLC_FOURCC('R','V','3','2'); break;
91     default:
92         msg_Err( p_demux, "unknown screen depth %i",
93                  p_sys->fmt.video.i_bits_per_pixel );
94         ReleaseDC( 0, p_data->hdc_src );
95         ReleaseDC( 0, p_data->hdc_dst );
96         return VLC_EGENERIC;
97     }
98
99     es_format_Init( &p_sys->fmt, VIDEO_ES, i_chroma );
100     p_sys->fmt.video.i_visible_width =
101     p_sys->fmt.video.i_width  = GetDeviceCaps( p_data->hdc_src, HORZRES );
102     p_sys->fmt.video.i_visible_height =
103     p_sys->fmt.video.i_height = GetDeviceCaps( p_data->hdc_src, VERTRES );
104     p_sys->fmt.video.i_bits_per_pixel = i_bits_per_pixel;
105     p_sys->fmt.video.i_chroma = i_chroma;
106
107     switch( i_chroma )
108     {
109     case VLC_FOURCC('R','V','1','5'):
110         p_sys->fmt.video.i_rmask = 0x7c00;
111         p_sys->fmt.video.i_gmask = 0x03e0;
112         p_sys->fmt.video.i_bmask = 0x001f;
113         break;
114     case VLC_FOURCC('R','V','2','4'):
115         p_sys->fmt.video.i_rmask = 0x00ff0000;
116         p_sys->fmt.video.i_gmask = 0x0000ff00;
117         p_sys->fmt.video.i_bmask = 0x000000ff;
118         break;
119     case VLC_FOURCC('R','V','3','2'):
120         p_sys->fmt.video.i_rmask = 0x00ff0000;
121         p_sys->fmt.video.i_gmask = 0x0000ff00;
122         p_sys->fmt.video.i_bmask = 0x000000ff;
123         break;
124     default:
125         msg_Warn( p_demux, "Unknown RGB masks" );
126         break;
127     }
128
129
130     return VLC_SUCCESS;
131 }
132
133 int screen_CloseCapture( demux_t *p_demux )
134 {
135     demux_sys_t *p_sys = p_demux->p_sys;
136     screen_data_t *p_data = p_sys->p_data;
137
138     if( p_data->p_block ) block_Release( p_data->p_block );
139
140     if( p_data->hgdi_backup)
141         SelectObject( p_data->hdc_dst, p_data->hgdi_backup );
142
143     DeleteDC( p_data->hdc_dst );
144     ReleaseDC( 0, p_data->hdc_src );
145     free( p_data );
146
147     return VLC_SUCCESS;
148 }
149
150 struct block_sys_t
151 {
152     block_t self;
153     HBITMAP hbmp;
154 };
155
156 static void CaptureBlockRelease( block_t *p_block )
157 {
158     DeleteObject( ((block_sys_t *)p_block)->hbmp );
159     free( p_block );
160 }
161
162 static block_t *CaptureBlockNew( demux_t *p_demux )
163 {
164     demux_sys_t *p_sys = p_demux->p_sys;
165     screen_data_t *p_data = p_sys->p_data;
166     block_sys_t *p_block;
167     void *p_buffer;
168     int i_buffer;
169     HBITMAP hbmp;
170
171     if( p_data->bmi.bmiHeader.biSize == 0 )
172     {
173         vlc_value_t val;
174         /* Create the bitmap info header */
175         p_data->bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
176         p_data->bmi.bmiHeader.biWidth = p_sys->fmt.video.i_width;
177         p_data->bmi.bmiHeader.biHeight = - p_sys->fmt.video.i_height;
178         p_data->bmi.bmiHeader.biPlanes = 1;
179         p_data->bmi.bmiHeader.biBitCount = p_sys->fmt.video.i_bits_per_pixel;
180         p_data->bmi.bmiHeader.biCompression = BI_RGB;
181         p_data->bmi.bmiHeader.biSizeImage = 0;
182         p_data->bmi.bmiHeader.biXPelsPerMeter =
183             p_data->bmi.bmiHeader.biYPelsPerMeter = 0;
184         p_data->bmi.bmiHeader.biClrUsed = 0;
185         p_data->bmi.bmiHeader.biClrImportant = 0;
186
187         var_Create( p_demux, "screen-fragment-size",
188                     VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
189         var_Get( p_demux, "screen-fragment-size", &val );
190         p_data->i_fragment_size =
191             val.i_int > 0 ? val.i_int : p_sys->fmt.video.i_height;
192         p_data->i_fragment_size =
193             val.i_int > p_sys->fmt.video.i_height ? p_sys->fmt.video.i_height :
194             p_data->i_fragment_size;
195         p_sys->f_fps *= (p_sys->fmt.video.i_height/p_data->i_fragment_size);
196         p_sys->i_incr = 1000000 / p_sys->f_fps;
197         p_data->i_fragment = 0;
198         p_data->p_block = 0;
199     }
200
201
202     /* Create the bitmap storage space */
203     hbmp = CreateDIBSection( p_data->hdc_dst, &p_data->bmi, DIB_RGB_COLORS,
204                              &p_buffer, NULL, 0 );
205     if( !hbmp || !p_buffer )
206     {
207         msg_Err( p_demux, "cannot create bitmap" );
208         if( hbmp ) DeleteObject( hbmp );
209         return NULL;
210     }
211
212     /* Select the bitmap into the compatible DC */
213     if( !p_data->hgdi_backup )
214         p_data->hgdi_backup = SelectObject( p_data->hdc_dst, hbmp );
215     else
216         SelectObject( p_data->hdc_dst, hbmp );
217
218     if( !p_data->hgdi_backup )
219     {
220         msg_Err( p_demux, "cannot select bitmap" );
221         DeleteObject( hbmp );
222         return NULL;
223     }
224
225     /* Build block */
226     if( !(p_block = malloc( sizeof( block_t ) + sizeof( block_sys_t ) )) )
227     {
228         DeleteObject( hbmp );
229         return NULL;
230     }
231     /* Fill all fields */
232     i_buffer = (p_sys->fmt.video.i_bits_per_pixel + 7) / 8 *
233         p_sys->fmt.video.i_width * p_sys->fmt.video.i_height;
234     block_Init( &p_block->self, p_buffer, i_buffer );
235     p_block->self.pf_release = CaptureBlockRelease;
236     p_block->hbmp            = hbmp;
237
238     return &p_block->self;
239 }
240
241 block_t *screen_Capture( demux_t *p_demux )
242 {
243     demux_sys_t *p_sys = p_demux->p_sys;
244     screen_data_t *p_data = p_sys->p_data;
245
246     if( !p_data->i_fragment )
247     {
248         if( !( p_data->p_block = CaptureBlockNew( p_demux ) ) )
249         {
250             msg_Warn( p_demux, "cannot get block" );
251             return 0;
252         }
253     }
254
255     if( p_sys->b_follow_mouse )
256     {
257         POINT pos;
258         GetCursorPos( &pos );
259         FollowMouse( p_sys, pos.x, pos.y );
260     }
261
262     if( !BitBlt( p_data->hdc_dst, 0,
263                  p_data->i_fragment * p_data->i_fragment_size,
264                  p_sys->fmt.video.i_width, p_data->i_fragment_size,
265                  p_data->hdc_src, p_sys->i_left, p_sys->i_top +
266                  p_data->i_fragment * p_data->i_fragment_size,
267                  IS_WINNT ? SRCCOPY | CAPTUREBLT : SRCCOPY ) )
268     {
269         msg_Err( p_demux, "error during BitBlt()" );
270         return NULL;
271     }
272
273     p_data->i_fragment++;
274
275     if( !( p_data->i_fragment %
276            (p_sys->fmt.video.i_height/p_data->i_fragment_size) ) )
277     {
278         block_t *p_block = p_data->p_block;
279         p_data->i_fragment = 0;
280         p_data->p_block = 0;
281
282         if( p_sys->p_mouse )
283         {
284             POINT pos;
285             GetCursorPos( &pos );
286             RenderCursor( p_demux, pos.x, pos.y,
287                           p_block->p_buffer );
288         }
289
290         return p_block;
291     }
292
293     return NULL;
294 }