Cosmetics
[vlc.git] / modules / access / screen / mac.c
1 /*****************************************************************************
2  * mac.c: Screen capture module for the Mac.
3  *****************************************************************************
4  * Copyright (C) 2004, 2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Derk-Jan Hartman <hartman at videolan dot org>
8  *          arai <arai_a@mac.com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # import "config.h"
31 #endif
32
33 #import <vlc_common.h>
34
35 #import <ApplicationServices/ApplicationServices.h>
36 #import <OpenGL/OpenGL.h>
37 #import <OpenGL/gl.h>
38 #import <stdlib.h>
39
40 typedef int CGSConnectionRef;
41 extern CGError CGSNewConnection( void *, CGSConnectionRef * );
42 extern CGError CGSReleaseConnection( CGSConnectionRef );
43 extern CGError CGSGetGlobalCursorDataSize( CGSConnectionRef, int * );
44 extern CGError CGSGetGlobalCursorData( CGSConnectionRef, unsigned char *,
45                                        int *, int *, CGRect *, CGPoint *,
46                                        int *, int *, int * );
47 extern CGError CGSGetCurrentCursorLocation( CGSConnectionRef, CGPoint * );
48
49 #import "screen.h"
50
51 struct screen_data_t
52 {
53   CGLContextObj screen;
54   
55   CGLContextObj scaled;
56   char *scaled_image;
57   
58   GLuint texture;
59   char *texture_image;
60   
61   GLuint cursor_texture;
62   
63   int left;
64   int top;
65   int src_width;
66   int src_height;
67   
68   int dest_width;
69   int dest_height;
70   
71   int screen_width;
72   int screen_height;
73   
74   CGSConnectionRef connection;
75 };
76
77 int screen_InitCapture( demux_t *p_demux )
78 {
79     demux_sys_t   *p_sys = p_demux->p_sys;
80     screen_data_t *p_data;
81     CGLPixelFormatAttribute attribs[4];
82     CGLPixelFormatObj pix;
83     GLint npix;
84     GLint viewport[4];
85     
86     p_sys->p_data = p_data =
87         ( screen_data_t * )malloc( sizeof( screen_data_t ) );
88     
89     attribs[0] = kCGLPFAFullScreen;
90     attribs[1] = kCGLPFADisplayMask;
91     attribs[2] = CGDisplayIDToOpenGLDisplayMask( CGMainDisplayID() );
92     attribs[3] = 0;
93     
94     CGLChoosePixelFormat( attribs, &pix, &npix );
95     CGLCreateContext( pix, NULL, &( p_data->screen ) );
96     CGLDestroyPixelFormat( pix );
97
98     CGLSetCurrentContext( p_data->screen );
99     CGLSetFullScreen( p_data->screen );
100     
101     glGetIntegerv( GL_VIEWPORT, viewport );
102     
103     p_data->screen_width = viewport[2];
104     p_data->screen_height = viewport[3];
105     
106     p_data->left = p_sys->i_left;
107     p_data->top = p_sys->i_top;
108     p_data->src_width = var_CreateGetInteger( p_demux, "screen-width" );
109     p_data->src_height = var_CreateGetInteger( p_demux, "screen-height" );
110     if (p_data->src_width <= 0 || p_data->src_height <= 0) {
111       p_data->src_width = p_data->screen_width;
112       p_data->src_height = p_data->screen_height;
113     }
114     p_data->dest_width = p_data->src_width;
115     p_data->dest_height = p_data->src_height;
116     
117     attribs [0] = kCGLPFAOffScreen;
118     attribs [1] = kCGLPFAColorSize;
119     attribs [2] = 32;
120     attribs [3] = 0;
121     
122     CGLChoosePixelFormat( attribs, &pix, &npix );
123     CGLCreateContext( pix, NULL, &( p_data->scaled ) );
124     CGLDestroyPixelFormat( pix );
125
126     CGLSetCurrentContext( p_data->scaled );
127     p_data->scaled_image = ( char * )malloc( p_data->dest_width
128                                           * p_data->dest_height * 4 );
129     CGLSetOffScreen( p_data->scaled, p_data->dest_width, p_data->dest_height,
130                      p_data->dest_width * 4, p_data->scaled_image );
131     
132     es_format_Init( &p_sys->fmt, VIDEO_ES, VLC_CODEC_RGB32 );
133     
134     /* p_sys->fmt.video.i_* must set to screen size, not subscreen size */
135     p_sys->fmt.video.i_width = p_data->screen_width;
136     p_sys->fmt.video.i_visible_width = p_data->screen_width;
137     p_sys->fmt.video.i_height = p_data->screen_height;
138     p_sys->fmt.video.i_bits_per_pixel = 32;
139     
140     glGenTextures( 1, &( p_data->texture ) );
141     glBindTexture( GL_TEXTURE_2D, p_data->texture );
142     
143     p_data->texture_image
144       = ( char * )malloc( p_data->src_width * p_data->src_height * 4 );
145     
146     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
147     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
148     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
149     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
150
151     glGenTextures( 1, &( p_data->cursor_texture ) );
152     glBindTexture( GL_TEXTURE_2D, p_data->cursor_texture );
153     
154     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
155     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
156     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
157     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
158     
159     CGSNewConnection( NULL, &( p_data->connection ) );
160     
161     return VLC_SUCCESS;
162 }
163
164 int screen_CloseCapture( demux_t *p_demux )
165 {
166     screen_data_t *p_data = ( screen_data_t * )p_demux->p_sys->p_data;
167     
168     CGSReleaseConnection( p_data->connection );
169     
170     CGLSetCurrentContext( NULL );
171     CGLClearDrawable( p_data->screen );
172     CGLDestroyContext( p_data->screen );
173     
174     return VLC_SUCCESS;
175 }
176
177 block_t *screen_Capture( demux_t *p_demux )
178 {
179     demux_sys_t *p_sys = p_demux->p_sys;
180     screen_data_t *p_data = ( screen_data_t * )p_sys->p_data;
181     block_t *p_block;
182     int i_size;
183     
184     i_size = p_sys->fmt.video.i_height * p_sys->fmt.video.i_width * 4; 
185     
186     if( !( p_block = block_New( p_demux, i_size ) ) )
187     {
188         msg_Warn( p_demux, "cannot get block" );
189         return NULL;
190     }
191     
192     CGPoint cursor_pos;
193     CGError cursor_result;
194     
195     cursor_pos.x = 0;
196     cursor_pos.y = 0;
197     
198     cursor_result
199       = CGSGetCurrentCursorLocation( p_data->connection, &cursor_pos );
200     
201     if( p_sys->b_follow_mouse
202         && cursor_result == kCGErrorSuccess )
203     {
204         FollowMouse( p_sys, cursor_pos.x, cursor_pos.y );
205         p_data->left = p_sys->i_left;
206         p_data->top = p_sys->i_top;
207     }
208     
209     CGLSetCurrentContext( p_data->screen );
210     glReadPixels( p_data->left,
211                   p_data->screen_height - p_data->top - p_data->src_height,
212                   p_data->src_width,
213                   p_data->src_height,
214                   GL_RGBA, GL_UNSIGNED_BYTE,
215                   p_data->texture_image );
216     
217     CGLSetCurrentContext( p_data->scaled );
218     glEnable( GL_TEXTURE_2D );
219     glBindTexture( GL_TEXTURE_2D, p_data->texture );
220     glTexImage2D( GL_TEXTURE_2D, 0,
221                   GL_RGBA8, p_data->src_width, p_data->src_height, 0,
222                   GL_RGBA, GL_UNSIGNED_BYTE, p_data->texture_image );
223     
224     glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
225     glClear( GL_COLOR_BUFFER_BIT );
226     glColor3f( 1.0f, 1.0f, 1.0f );
227     glEnable( GL_TEXTURE_2D );
228     glBindTexture( GL_TEXTURE_2D, p_data->texture );
229     glBegin( GL_POLYGON );
230     glTexCoord2f( 0.0, 1.0 ); glVertex2f( -1.0, -1.0 );
231     glTexCoord2f( 1.0, 1.0 ); glVertex2f( 1.0, -1.0 );
232     glTexCoord2f( 1.0, 0.0 ); glVertex2f( 1.0, 1.0 );
233     glTexCoord2f( 0.0, 0.0 ); glVertex2f( -1.0, 1.0 );
234     glEnd();
235     glDisable( GL_TEXTURE_2D );
236     
237     int size;
238     int tmp1, tmp2, tmp3, tmp4;
239     unsigned char *cursor_image;
240     CGRect cursor_rect;
241     CGPoint cursor_hot;
242     
243     if( cursor_result == kCGErrorSuccess
244         && CGSGetGlobalCursorDataSize( p_data->connection, &size )
245         == kCGErrorSuccess )
246     {
247         cursor_image = ( unsigned char* )malloc( size );
248         if( CGSGetGlobalCursorData( p_data->connection,
249                                     cursor_image, &size,
250                                     &tmp1,
251                                     &cursor_rect, &cursor_hot,
252                                     &tmp2, &tmp3, &tmp4 )
253             == kCGErrorSuccess )
254         {
255             glEnable( GL_TEXTURE_2D );
256             glBindTexture( GL_TEXTURE_2D, p_data->cursor_texture );
257             glTexImage2D( GL_TEXTURE_2D, 0,
258                           GL_RGBA8,
259                           ( int )( cursor_rect.size.width ),
260                           ( int )( cursor_rect.size.height ), 0,
261                           GL_RGBA, GL_UNSIGNED_BYTE,
262                           ( char * )cursor_image );
263             
264             cursor_rect.origin.x = cursor_pos.x - p_data->left - cursor_hot.x;
265             cursor_rect.origin.y = cursor_pos.y - p_data->top - cursor_hot.y;
266             
267             cursor_rect.origin.x
268               = 2.0 * cursor_rect.origin.x / p_data->src_width - 1.0;
269             cursor_rect.origin.y
270               = 2.0 * cursor_rect.origin.y / p_data->src_height - 1.0;
271             cursor_rect.size.width
272               = 2.0 * cursor_rect.size.width / p_data->src_width;
273             cursor_rect.size.height
274               = 2.0 * cursor_rect.size.height / p_data->src_height;
275             
276             glColor3f( 1.0f, 1.0f, 1.0f );
277             glEnable( GL_TEXTURE_2D );
278             glEnable( GL_BLEND );
279             glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
280             glBindTexture( GL_TEXTURE_2D, p_data->cursor_texture );
281             glBegin( GL_POLYGON );
282             glTexCoord2f( 0.0, 0.0 ); glVertex2f( cursor_rect.origin.x,
283                                                   cursor_rect.origin.y );
284             glTexCoord2f( 1.0, 0.0 ); glVertex2f( cursor_rect.origin.x
285                                                   + cursor_rect.size.width,
286                                                   cursor_rect.origin.y );
287             glTexCoord2f( 1.0, 1.0 ); glVertex2f( cursor_rect.origin.x
288                                                   + cursor_rect.size.width,
289                                                   cursor_rect.origin.y
290                                                   + cursor_rect.size.height );
291             glTexCoord2f( 0.0, 1.0 ); glVertex2f( cursor_rect.origin.x,
292                                                   cursor_rect.origin.y
293                                                   + cursor_rect.size.height );
294             glEnd();
295             glDisable( GL_BLEND );
296             glDisable( GL_TEXTURE_2D );
297         }
298         free( cursor_image );
299     }
300     
301     glReadPixels( 0, 0, 
302                   p_data->dest_width,
303                   p_data->dest_height,
304                   GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
305                   p_block->p_buffer );
306     
307     return p_block;
308 }