]> git.sesse.net Git - vlc/blob - modules/access/screen/mac.c
Screen capture module for Mac OS X using OpenGL.
[vlc] / modules / access / screen / mac.c
1 /*****************************************************************************
2  * mac.c: Screen capture module for the Mac.
3  *****************************************************************************
4  * Copyright (C) 2004 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Derk-Jan Hartman <hartman at videolan dot 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 #include <stdlib.h>
28
29 #include <vlc/vlc.h>
30 #include <vlc/input.h>
31
32 #import <OpenGL/OpenGL.h>
33 #include <GL/gl.h>
34 #include <ApplicationServices/ApplicationServices.h>
35
36 typedef int CGSConnectionRef;
37 extern CGError CGSNewConnection( void *, CGSConnectionRef * );
38 extern CGError CGSReleaseConnection( CGSConnectionRef );
39 extern CGError CGSGetGlobalCursorDataSize( CGSConnectionRef, int * );
40 extern CGError CGSGetGlobalCursorData( CGSConnectionRef, unsigned char *,
41                                        int *, int *, CGRect *, CGPoint *,
42                                        int *, int *, int * );
43 extern CGError CGSGetCurrentCursorLocation( CGSConnectionRef, CGPoint * );
44
45 #include "screen.h"
46
47 struct screen_data_t
48 {
49   CGLContextObj screen;
50   
51   CGLContextObj scaled;
52   char *scaled_image;
53   
54   GLuint texture;
55   char *texture_image;
56   
57   GLuint cursor_texture;
58   
59   int left;
60   int top;
61   int src_width;
62   int src_height;
63   
64   int dest_width;
65   int dest_height;
66   
67   int screen_width;
68   int screen_height;
69   
70   CGSConnectionRef connection;
71 };
72
73 int screen_InitCapture( demux_t *p_demux )
74 {
75     demux_sys_t   *p_sys = p_demux->p_sys;
76     screen_data_t *p_data;
77     CGLPixelFormatAttribute attribs[4];
78     CGLPixelFormatObj pix;
79     GLint npix;
80     GLint viewport[4];
81     
82     p_sys->p_data = p_data =
83         ( screen_data_t * )malloc( sizeof( screen_data_t ) );
84     
85     attribs[0] = kCGLPFAFullScreen;
86     attribs[1] = kCGLPFADisplayMask;
87     attribs[2] = CGDisplayIDToOpenGLDisplayMask( CGMainDisplayID() );
88     attribs[3] = 0;
89     
90     CGLChoosePixelFormat( attribs, &pix, &npix );
91     CGLCreateContext( pix, NULL, &( p_data->screen ) );
92     CGLDestroyPixelFormat( pix );
93
94     CGLSetCurrentContext( p_data->screen );
95     CGLSetFullScreen( p_data->screen );
96     
97     glGetIntegerv( GL_VIEWPORT, viewport );
98     
99     p_data->screen_width = viewport[2];
100     p_data->screen_height = viewport[3];
101     
102     p_data->left = 0;
103     p_data->top = 0;
104     p_data->src_width = p_data->screen_width;
105     p_data->src_height = p_data->screen_height;
106     p_data->dest_width = p_data->src_width;
107     p_data->dest_height = p_data->src_height;
108     
109     attribs [0] = kCGLPFAOffScreen;
110     attribs [1] = kCGLPFAColorSize;
111     attribs [2] = 32;
112     attribs [3] = 0;
113     
114     CGLChoosePixelFormat( attribs, &pix, &npix );
115     CGLCreateContext( pix, NULL, &( p_data->scaled ) );
116     CGLDestroyPixelFormat( pix );
117
118     CGLSetCurrentContext( p_data->scaled );
119     p_data->scaled_image = ( char * )malloc( p_data->dest_width
120                                           * p_data->dest_height * 4 );
121     CGLSetOffScreen( p_data->scaled, p_data->dest_width, p_data->dest_height,
122                      p_data->dest_width * 4, p_data->scaled_image );
123     
124     es_format_Init( &p_sys->fmt, VIDEO_ES, VLC_FOURCC( 'R','V','3','2' ) );
125     
126     p_sys->fmt.video.i_width = p_data->dest_width;
127     p_sys->fmt.video.i_visible_width = p_data->dest_width;
128     p_sys->fmt.video.i_height = p_data->dest_height;
129     p_sys->fmt.video.i_bits_per_pixel = 32;
130     
131     glGenTextures( 1, &( p_data->texture ) );
132     glBindTexture( GL_TEXTURE_2D, p_data->texture );
133     
134     p_data->texture_image
135       = ( char * )malloc( p_data->src_width * p_data->src_height * 4 );
136     
137     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
138     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
139     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
140     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
141
142     glGenTextures( 1, &( p_data->cursor_texture ) );
143     glBindTexture( GL_TEXTURE_2D, p_data->cursor_texture );
144     
145     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
146     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
147     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
148     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
149     
150     CGSNewConnection( NULL, &( p_data->connection ) );
151     
152     return VLC_SUCCESS;
153 }
154
155 int screen_CloseCapture( demux_t *p_demux )
156 {
157     screen_data_t *p_data = ( screen_data_t * )p_demux->p_sys->p_data;
158     
159     CGSReleaseConnection( p_data->connection );
160     
161     CGLSetCurrentContext( NULL );
162     CGLClearDrawable( p_data->screen );
163     CGLDestroyContext( p_data->screen );
164     
165     return VLC_SUCCESS;
166 }
167
168 block_t *screen_Capture( demux_t *p_demux )
169 {
170     demux_sys_t *p_sys = p_demux->p_sys;
171     screen_data_t *p_data = ( screen_data_t * )p_sys->p_data;
172     block_t *p_block;
173     int i_size;
174     
175     i_size = p_sys->fmt.video.i_height * p_sys->fmt.video.i_width * 4; 
176     
177     if( !( p_block = block_New( p_demux, i_size ) ) )
178     {
179         msg_Warn( p_demux, "cannot get block" );
180         return 0;
181     }
182     
183     CGLSetCurrentContext( p_data->screen );
184     glReadPixels( p_data->left,
185                   p_data->screen_height - p_data->top - p_data->src_height,
186                   p_data->src_width,
187                   p_data->src_height,
188                   GL_RGBA, GL_UNSIGNED_BYTE,
189                   p_data->texture_image );
190     
191     CGLSetCurrentContext( p_data->scaled );
192     glEnable( GL_TEXTURE_2D );
193     glBindTexture( GL_TEXTURE_2D, p_data->texture );
194     glTexImage2D( GL_TEXTURE_2D, 0,
195                   GL_RGBA8, p_data->src_width, p_data->src_height, 0,
196                   GL_RGBA, GL_UNSIGNED_BYTE, p_data->texture_image );
197     
198     glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
199     glClear( GL_COLOR_BUFFER_BIT );
200     glColor3f( 1.0f, 1.0f, 1.0f );
201     glEnable( GL_TEXTURE_2D );
202     glBindTexture( GL_TEXTURE_2D, p_data->texture );
203     glBegin( GL_POLYGON );
204     glTexCoord2f( 0.0, 1.0 ); glVertex2f( -1.0, -1.0 );
205     glTexCoord2f( 1.0, 1.0 ); glVertex2f( 1.0, -1.0 );
206     glTexCoord2f( 1.0, 0.0 ); glVertex2f( 1.0, 1.0 );
207     glTexCoord2f( 0.0, 0.0 ); glVertex2f( -1.0, 1.0 );
208     glEnd();
209     glDisable( GL_TEXTURE_2D );
210     
211     CGPoint cursor_pos;
212     int size;
213     int tmp1, tmp2, tmp3, tmp4;
214     unsigned char *cursor_image;
215     CGRect cursor_rect;
216     CGPoint cursor_hot;
217     
218     cursor_pos.x = 0;
219     cursor_pos.y = 0;
220     
221     if( CGSGetCurrentCursorLocation( p_data->connection, &cursor_pos )
222         == kCGErrorSuccess
223         && CGSGetGlobalCursorDataSize( p_data->connection, &size )
224         == kCGErrorSuccess )
225     {
226         cursor_image = ( unsigned char* )malloc( size );
227         if( CGSGetGlobalCursorData( p_data->connection,
228                                     cursor_image, &size,
229                                     &tmp1,
230                                     &cursor_rect, &cursor_hot,
231                                     &tmp2, &tmp3, &tmp4 )
232             == kCGErrorSuccess )
233         {
234             glEnable( GL_TEXTURE_2D );
235             glBindTexture( GL_TEXTURE_2D, p_data->cursor_texture );
236             glTexImage2D( GL_TEXTURE_2D, 0,
237                           GL_RGBA8,
238                           ( int )( cursor_rect.size.width ),
239                           ( int )( cursor_rect.size.height ), 0,
240                           GL_RGBA, GL_UNSIGNED_BYTE,
241                           ( char * )cursor_image );
242             
243             cursor_rect.origin.x = cursor_pos.x - p_data->left - cursor_hot.x;
244             cursor_rect.origin.y = cursor_pos.y - p_data->top - cursor_hot.y;
245             
246             cursor_rect.origin.x
247               = 2.0 * cursor_rect.origin.x / p_data->src_width - 1.0;
248             cursor_rect.origin.y
249               = 2.0 * cursor_rect.origin.y / p_data->src_height - 1.0;
250             cursor_rect.size.width
251               = 2.0 * cursor_rect.size.width / p_data->src_width;
252             cursor_rect.size.height
253               = 2.0 * cursor_rect.size.height / p_data->src_height;
254             
255             glColor3f( 1.0f, 1.0f, 1.0f );
256             glEnable( GL_TEXTURE_2D );
257             glEnable( GL_BLEND );
258             glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
259             glBindTexture( GL_TEXTURE_2D, p_data->cursor_texture );
260             glBegin( GL_POLYGON );
261             glTexCoord2f( 0.0, 0.0 ); glVertex2f( cursor_rect.origin.x,
262                                                   cursor_rect.origin.y );
263             glTexCoord2f( 1.0, 0.0 ); glVertex2f( cursor_rect.origin.x
264                                                   + cursor_rect.size.width,
265                                                   cursor_rect.origin.y );
266             glTexCoord2f( 1.0, 1.0 ); glVertex2f( cursor_rect.origin.x
267                                                   + cursor_rect.size.width,
268                                                   cursor_rect.origin.y
269                                                   + cursor_rect.size.height );
270             glTexCoord2f( 0.0, 1.0 ); glVertex2f( cursor_rect.origin.x,
271                                                   cursor_rect.origin.y
272                                                   + cursor_rect.size.height );
273             glEnd();
274             glDisable( GL_BLEND );
275             glDisable( GL_TEXTURE_2D );
276         }
277         free( cursor_image );
278     }
279     
280     glReadPixels( 0, 0, 
281                   p_data->dest_width,
282                   p_data->dest_height,
283                   GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
284                   p_block->p_buffer );
285     
286     return p_block;
287 }