4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "bccapture.h"
23 #include "bcresources.h"
24 #include "bcwindowbase.h"
25 #include "bccmodels.h"
32 #include <X11/Xutil.h>
33 #include <X11/extensions/Xfixes.h>
37 // 24 bpp unpacked: 0bgr
40 BC_Capture::BC_Capture(int w, int h, const char *display_path)
47 for( int i=0; i<4; ++i ) border[i] = 0;
48 bar_w = 0; bar_color = 0;
49 init_window(display_path);
53 BC_Capture::~BC_Capture()
57 XCloseDisplay(display);
61 int BC_Capture::init_window(const char *display_path)
64 if( display_path && display_path[0] == 0 ) display_path = NULL;
65 if( (display = XOpenDisplay(display_path)) == NULL ) {
66 printf(_("cannot connect to X server.\n"));
67 if( getenv("DISPLAY") == NULL )
68 printf(_("'DISPLAY' environment variable not set.\n"));
73 screen = DefaultScreen(display);
74 rootwin = RootWindow(display, screen);
75 vis = DefaultVisual(display, screen);
76 default_depth = DefaultDepth(display, screen);
77 client_byte_order = (*(const u_int32_t*)"a ") & 0x00000001;
78 server_byte_order = (XImageByteOrder(display) == MSBFirst) ? 0 : 1;
81 ximage = XCreateImage(display, vis, default_depth,
82 ZPixmap, 0, data, 16, 16, 8, 0);
83 bits_per_pixel = ximage->bits_per_pixel;
84 XDestroyImage(ximage);
85 bitmap_color_model = BC_WindowBase::evaluate_color_model(client_byte_order, server_byte_order, bits_per_pixel);
88 // This doesn't ensure the X Server is on the local host
89 if( use_shm && !XShmQueryExtension(display) )
94 Window BC_Capture::bar(int x, int y, int w, int h, int color)
96 unsigned long mask = CWEventMask | CWBackPixel |
97 CWOverrideRedirect | CWSaveUnder;
98 XSetWindowAttributes attr;
99 memset(&attr, 0, sizeof(attr));
100 Screen *scr = XDefaultScreenOfDisplay(display);
101 Window root = RootWindowOfScreen(scr);
102 Visual *vis = DefaultVisualOfScreen(scr);
103 int depth = DefaultDepthOfScreen(scr);
104 attr.background_pixel = color;
105 attr.override_redirect = True;
106 attr.save_under = True;
107 Window win = XCreateWindow(display, root, x,y,w,h, 0,depth,
108 InputOutput, vis, mask, &attr);
109 XMapWindow(display, win);
113 void BC_Capture::bars_on(int bw, int color, int x, int y, int w, int h)
116 this->bar_color = color;
117 border[0] = bar(x-bw, y-bw, w+2*bw, bw, color);
118 border[1] = bar(x-bw, y, bw, h, color);
119 border[2] = bar(x-bw, y+h, w+2*bw, bw, color);
120 border[3] = bar(x+w, y, bw, h, color);
124 void BC_Capture::bars_off()
126 for( int i=0; i<4; ++i ) {
127 if( !border[i] ) continue;
128 XUnmapWindow(display, border[i]);
130 for( int i=0; i<4; ++i ) {
131 if( !border[i] ) continue;
132 XDestroyWindow(display, border[i]);
138 void BC_Capture::bars_reposition(int x, int y, int w, int h)
140 int bw = this->bar_w;
142 XMoveResizeWindow(display, border[0], x-bw, y-bw, w+2*bw, bw);
144 XMoveResizeWindow(display, border[1], x-bw, y, bw, h );
146 XMoveResizeWindow(display, border[2], x-bw, y+h, w+2*bw, bw);
148 XMoveResizeWindow(display, border[3], x+w, y, bw, h );
151 int BC_Capture::allocate_data()
154 if( !display ) return 1;
156 ximage = XShmCreateImage(display, vis, default_depth, ZPixmap, (char*)NULL, &shm_info, w, h);
158 shm_info.shmid = shmget(IPC_PRIVATE, h * ximage->bytes_per_line, IPC_CREAT | 0600);
159 if( shm_info.shmid == -1 ) {
160 perror("BC_Capture::allocate_data shmget");
163 data = (unsigned char *)shmat(shm_info.shmid, NULL, 0);
164 shmctl(shm_info.shmid, IPC_RMID, 0);
165 ximage->data = shm_info.shmaddr = (char*)data; // setting ximage->data stops BadValue
166 shm_info.readOnly = 0;
168 // Crashes here if remote server.
169 BC_Resources::error = 0;
170 XShmAttach(display, &shm_info);
171 XSync(display, False);
172 if( BC_Resources::error ) {
173 XDestroyImage(ximage);
174 shmdt(shm_info.shmaddr);
180 // need to use bytes_per_line for some X servers
182 ximage = XCreateImage(display, vis, default_depth, ZPixmap, 0, (char*)data, w, h, 8, 0);
183 data = (unsigned char*)malloc(h * ximage->bytes_per_line);
184 XDestroyImage(ximage);
186 ximage = XCreateImage(display, vis, default_depth, ZPixmap, 0, (char*)data, w, h, 8, 0);
189 row_data = new unsigned char*[h];
190 for( int i = 0; i < h; i++ ) {
191 row_data[i] = &data[i * ximage->bytes_per_line];
193 // This differs from the depth parameter of the top_level.
194 bits_per_pixel = ximage->bits_per_pixel;
198 int BC_Capture::delete_data()
200 if( !display ) return 1;
203 XShmDetach(display, &shm_info);
204 XDestroyImage(ximage);
205 shmdt(shm_info.shmaddr);
208 XDestroyImage(ximage);
211 // data is automatically freed by XDestroyImage
219 int BC_Capture::get_w() { return w; }
220 int BC_Capture::get_h() { return h; }
222 // Capture a frame from the screen
223 #define RGB_TO_YUV(y, u, v, r, g, b) { \
224 YUV::yuv.rgb_to_yuv_8(r, g, b, y, u, v); \
225 bclamp(y, 0,0xff); bclamp(u, 0,0xff); bclamp(v, 0,0xff); }
227 int BC_Capture::capture_frame(VFrame *frame, int &x1, int &y1,
228 int do_cursor) // the scale of the cursor if nonzero
230 if( !display ) return 1;
233 if( x1 > get_top_w() - w ) x1 = get_top_w() - w;
234 if( y1 > get_top_h() - h ) y1 = get_top_h() - h;
239 XShmGetImage(display, rootwin, ximage, x1, y1, 0xffffffff);
241 XGetSubImage(display, rootwin, x1, y1, w, h, 0xffffffff, ZPixmap, ximage, 0, 0);
243 BC_CModels::transfer(frame->get_rows(), row_data,
244 frame->get_y(), frame->get_u(), frame->get_v(), 0,
245 0, 0, 0, 0, w, h, 0, 0,
246 frame->get_w(), frame->get_h(),
247 bitmap_color_model, frame->get_color_model(),
248 0, frame->get_w(), w);
251 XFixesCursorImage *cursor;
252 cursor = XFixesGetCursorImage(display);
254 //printf("BC_Capture::capture_frame %d cursor=%p colormodel=%d\n",
255 // __LINE__, cursor, frame->get_color_model());
256 int scale = do_cursor;
257 int cursor_x = cursor->x - x1 - cursor->xhot * scale;
258 int cursor_y = cursor->y - y1 - cursor->yhot * scale;
259 int w = frame->get_w();
260 int h = frame->get_h();
261 for( int i = 0; i < cursor->height; i++ ) {
262 for( int yscale = 0; yscale < scale; yscale++ ) {
263 if( cursor_y + i * scale + yscale >= 0 &&
264 cursor_y + i * scale + yscale < h ) {
265 unsigned char *src = (unsigned char*)(cursor->pixels +
267 int dst_y = cursor_y + i * scale + yscale;
268 int dst_x = cursor_x;
269 for( int j = 0; j < cursor->width; j++ ) {
270 for( int xscale = 0; xscale < scale ; xscale++ ) {
271 if( cursor_x + j * scale + xscale >= 0 &&
272 cursor_x + j * scale + xscale < w ) {
274 int invert_a = 0xff - a;
278 switch( frame->get_color_model() ) {
280 unsigned char *dst = frame->get_rows()[dst_y] +
282 dst[0] = (r * a + dst[0] * invert_a) / 0xff;
283 dst[1] = (g * a + dst[1] * invert_a) / 0xff;
284 dst[2] = (b * a + dst[2] * invert_a) / 0xff;
288 unsigned char *dst_y_ = frame->get_y() +
290 unsigned char *dst_u = frame->get_u() +
291 (dst_y / 2) * (w / 2) + (dst_x / 2);
292 unsigned char *dst_v = frame->get_v() +
293 (dst_y / 2) * (w / 2) + (dst_x / 2);
295 RGB_TO_YUV(y, u, v, r, g, b);
297 *dst_y_ = (y * a + *dst_y_ * invert_a) / 0xff;
298 *dst_u = (u * a + *dst_u * invert_a) / 0xff;
299 *dst_v = (v * a + *dst_v * invert_a) / 0xff;
311 // This frees cursor->pixels
319 int BC_Capture::get_top_w()
321 Screen *screen_ptr = XDefaultScreenOfDisplay(display);
322 return WidthOfScreen(screen_ptr);
325 int BC_Capture::get_top_h()
327 Screen *screen_ptr = XDefaultScreenOfDisplay(display);
328 return HeightOfScreen(screen_ptr);