screen: fix mouse file location
[vlc.git] / modules / access / screen / mac.c
1 /*****************************************************************************
2  * mac.c: Screen capture module for the Mac.
3  *****************************************************************************
4  * Copyright (C) 2004 - 2013 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: FUJISAWA Tooru <arai_a@mac.com>
8  *          Derk-Jan Hartman <hartman at videolan dot org>
9  *          Pierre d'Herbemont <pdherbemont # videolan org>
10  *          Felix Paul K├╝hne <fkuehne # videolan org>
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU Lesser General Public License as published by
14  * the Free Software Foundation; either version 2.1 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public License
23  * along with this program; if not, write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #import <vlc_common.h>
36 #import <vlc_block.h>
37
38 #import "screen.h"
39
40 #import <ApplicationServices/ApplicationServices.h>
41 #import <QuartzCore/QuartzCore.h>
42
43 extern int CGSMainConnectionID();
44 extern CGImageRef CGSCreateRegisteredCursorImage(int, char*, CGPoint*);
45
46 struct screen_data_t
47 {
48     block_t *p_block;
49
50     int width;
51     int height;
52
53     int screen_top;
54     int screen_left;
55     int screen_width;
56     int screen_height;
57
58     CGDirectDisplayID display_id;
59
60     CGContextRef offscreen_context;
61     CGRect offscreen_rect;
62     void *offscreen_bitmap;
63     size_t offscreen_bitmap_size;
64 };
65
66 int screen_InitCapture(demux_t *p_demux)
67 {
68     demux_sys_t *p_sys = p_demux->p_sys;
69     screen_data_t *p_data;
70     CGLError returnedError;
71
72     p_sys->p_data = p_data = calloc(1, sizeof(screen_data_t));
73     if (!p_data)
74         return VLC_ENOMEM;
75
76     /* fetch the screen we should capture */
77     p_data->display_id = kCGDirectMainDisplay;
78
79     unsigned int displayCount = 0;
80     returnedError = CGGetOnlineDisplayList(0, NULL, &displayCount);
81     if (!returnedError) {
82         CGDirectDisplayID *ids;
83         ids = (CGDirectDisplayID *)malloc(displayCount * sizeof(CGDirectDisplayID));
84         returnedError = CGGetOnlineDisplayList(displayCount, ids, &displayCount);
85         if (!returnedError) {
86             if (p_sys->i_display_id > 0) {
87                 for (unsigned int i = 0; i < displayCount; i++) {
88                     if (p_sys->i_display_id == ids[i]) {
89                         p_data->display_id = ids[i];
90                         break;
91                     }
92                 }
93             } else if (p_sys->i_screen_index > 0 && p_sys->i_screen_index <= displayCount)
94                 p_data->display_id = ids[p_sys->i_screen_index - 1];
95         }
96         free(ids);
97     }
98
99     /* Get the device context for the whole screen */
100     CGRect rect = CGDisplayBounds(p_data->display_id);
101     p_data->screen_left = rect.origin.x;
102     p_data->screen_top = rect.origin.y;
103     p_data->screen_width = rect.size.width;
104     p_data->screen_height = rect.size.height;
105
106     p_data->width = p_sys->i_width;
107     p_data->height = p_sys->i_height;
108     if (p_data->width <= 0 || p_data->height <= 0) {
109         p_data->width = p_data->screen_width;
110         p_data->height = p_data->screen_height;
111     }
112
113     /* setup format */
114     es_format_Init(&p_sys->fmt, VIDEO_ES, VLC_CODEC_RGB32);
115     p_sys->fmt.video.i_visible_width  =
116     p_sys->fmt.video.i_width          = rect.size.width;
117     p_sys->fmt.video.i_visible_height =
118     p_sys->fmt.video.i_height         = rect.size.height;
119     p_sys->fmt.video.i_bits_per_pixel = 32;
120     p_sys->fmt.video.i_chroma         = VLC_CODEC_RGB32;
121     p_sys->fmt.video.i_rmask          = 0x00ff0000;
122     p_sys->fmt.video.i_gmask          = 0x0000ff00;
123     p_sys->fmt.video.i_bmask          = 0x000000ff;
124
125     return VLC_SUCCESS;
126 }
127
128 int screen_CloseCapture(demux_t *p_demux)
129 {
130     demux_sys_t *p_sys = p_demux->p_sys;
131     screen_data_t *p_data = p_sys->p_data;
132
133     if (p_data->offscreen_context)
134         CFRelease(p_data->offscreen_context);
135
136     if (p_data->offscreen_bitmap)
137         free(p_data->offscreen_bitmap);
138
139     if (p_data->p_block)
140         block_Release(p_data->p_block);
141
142     free(p_data);
143
144     return VLC_SUCCESS;
145 }
146
147 block_t *screen_Capture(demux_t *p_demux)
148 {
149     demux_sys_t *p_sys = p_demux->p_sys;
150     screen_data_t *p_data = (screen_data_t *)p_sys->p_data;
151     block_t *p_block;
152     CGRect capture_rect;
153     CGImageRef image;
154
155     /* forward cursor location */
156     CGPoint cursor_pos;
157
158     CGEventRef event = CGEventCreate(NULL);
159     cursor_pos = CGEventGetLocation(event);
160     CFRelease(event);
161
162     cursor_pos.x -= p_data->screen_left;
163     cursor_pos.y -= p_data->screen_top;
164
165     if (p_sys->b_follow_mouse)
166         FollowMouse(p_sys, cursor_pos.x, cursor_pos.y);
167
168     capture_rect.origin.x = p_sys->i_left;
169     capture_rect.origin.y = p_sys->i_top;
170     capture_rect.size.width = p_data->width;
171     capture_rect.size.height = p_data->height;
172
173     /* fetch image data */
174     image = CGDisplayCreateImageForRect(p_data->display_id, capture_rect);
175     if (!image) {
176         msg_Warn(p_demux, "no image!");
177         return NULL;
178     }
179
180     /* create offscreen context */
181     if (!p_data->offscreen_context) {
182         CGColorSpaceRef colorspace;
183
184         colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
185
186         p_data->offscreen_bitmap_size = p_sys->fmt.video.i_width * p_sys->fmt.video.i_height * 4;
187         p_data->offscreen_bitmap = calloc(1, p_data->offscreen_bitmap_size);
188         if (p_data->offscreen_bitmap == NULL) {
189             msg_Warn(p_demux, "can't allocate offscreen bitmap");
190             CFRelease(image);
191             return NULL;
192         }
193
194         p_data->offscreen_context = CGBitmapContextCreate(p_data->offscreen_bitmap, p_sys->fmt.video.i_width, p_sys->fmt.video.i_height, 8, p_sys->fmt.video.i_width * 4, colorspace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
195         if (!p_data->offscreen_context) {
196             msg_Warn(p_demux, "can't create offscreen bitmap context");
197             CFRelease(image);
198             return NULL;
199         }
200
201         CGColorSpaceRelease(colorspace);
202
203         p_data->offscreen_rect = CGRectMake(0, 0, p_sys->fmt.video.i_width, p_sys->fmt.video.i_height);
204     }
205
206     /* fetch cursor image */
207     CGImageRef cursor_image;
208     int cid = CGSMainConnectionID();
209     CGPoint outHotSpot;
210     cursor_image = CGSCreateRegisteredCursorImage(cid, (char *)"com.apple.coregraphics.GlobalCurrent", &outHotSpot);
211
212     /* draw screen image and cursor image */
213     CGRect cursor_rect;
214     cursor_rect.size.width = CGImageGetWidth(cursor_image);
215     cursor_rect.size.height = CGImageGetHeight(cursor_image);
216     cursor_rect.origin.x = cursor_pos.x - p_sys->i_left - outHotSpot.x;
217     cursor_rect.origin.y = p_data->offscreen_rect.size.height
218         - (cursor_pos.y + cursor_rect.size.height - p_sys->i_top - outHotSpot.y);
219
220     CGContextDrawImage(p_data->offscreen_context, p_data->offscreen_rect, image);
221     CGContextDrawImage(p_data->offscreen_context, cursor_rect, cursor_image);
222
223     /* build block */
224     p_block = block_Alloc(p_data->offscreen_bitmap_size);
225     if (!p_block) {
226         msg_Warn(p_demux, "can't get block");
227         CFRelease(image);
228         return NULL;
229     }
230
231     memmove(p_block->p_buffer, p_data->offscreen_bitmap, p_data->offscreen_bitmap_size);
232
233     CFRelease(image);
234
235     return p_block;
236 }