/* * CINELERRA * Copyright (C) 2008 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "bccapture.h" #include "bcresources.h" #include "bcwindowbase.h" #include "bccmodels.h" #include "bccolors.h" #include "clip.h" #include "language.h" #include "vframe.h" #include #include #include #include // Byte orders: // 24 bpp packed: bgr // 24 bpp unpacked: 0bgr BC_Capture::BC_Capture(int w, int h, const char *display_path) { this->w = w; this->h = h; data = 0; use_shm = 1; for( int i=0; i<4; ++i ) border[i] = 0; bar_w = 0; bar_color = 0; init_window(display_path); allocate_data(); } BC_Capture::~BC_Capture() { bars_off(); delete_data(); XCloseDisplay(display); } int BC_Capture::init_window(const char *display_path) { int bits_per_pixel; if( display_path && display_path[0] == 0 ) display_path = NULL; if( (display = XOpenDisplay(display_path)) == NULL ) { printf(_("cannot connect to X server.\n")); if( getenv("DISPLAY") == NULL ) printf(_("'DISPLAY' environment variable not set.\n")); exit(-1); return 1; } screen = DefaultScreen(display); rootwin = RootWindow(display, screen); vis = DefaultVisual(display, screen); default_depth = DefaultDepth(display, screen); client_byte_order = (*(const u_int32_t*)"a ") & 0x00000001; server_byte_order = (XImageByteOrder(display) == MSBFirst) ? 0 : 1; char *data = 0; XImage *ximage; ximage = XCreateImage(display, vis, default_depth, ZPixmap, 0, data, 16, 16, 8, 0); bits_per_pixel = ximage->bits_per_pixel; XDestroyImage(ximage); bitmap_color_model = BC_WindowBase::evaluate_color_model(client_byte_order, server_byte_order, bits_per_pixel); // test shared memory // This doesn't ensure the X Server is on the local host if( use_shm && !XShmQueryExtension(display) ) use_shm = 0; return 0; } Window BC_Capture::bar(int x, int y, int w, int h, int color) { unsigned long mask = CWEventMask | CWBackPixel | CWOverrideRedirect | CWSaveUnder; XSetWindowAttributes attr; memset(&attr, 0, sizeof(attr)); Screen *scr = XDefaultScreenOfDisplay(display); Window root = RootWindowOfScreen(scr); Visual *vis = DefaultVisualOfScreen(scr); int depth = DefaultDepthOfScreen(scr); attr.background_pixel = color; attr.override_redirect = True; attr.save_under = True; Window win = XCreateWindow(display, root, x,y,w,h, 0,depth, InputOutput, vis, mask, &attr); XMapWindow(display, win); return win; } void BC_Capture::bars_on(int bw, int color, int x, int y, int w, int h) { this->bar_w = bw; this->bar_color = color; border[0] = bar(x-bw, y-bw, w+2*bw, bw, color); border[1] = bar(x-bw, y, bw, h, color); border[2] = bar(x-bw, y+h, w+2*bw, bw, color); border[3] = bar(x+w, y, bw, h, color); XFlush(display); } void BC_Capture::bars_off() { for( int i=0; i<4; ++i ) { if( !border[i] ) continue; XUnmapWindow(display, border[i]); } for( int i=0; i<4; ++i ) { if( !border[i] ) continue; XDestroyWindow(display, border[i]); border[i] = 0; } XFlush(display); } void BC_Capture::bars_reposition(int x, int y, int w, int h) { int bw = this->bar_w; if( border[0] ) XMoveResizeWindow(display, border[0], x-bw, y-bw, w+2*bw, bw); if( border[1] ) XMoveResizeWindow(display, border[1], x-bw, y, bw, h ); if( border[2] ) XMoveResizeWindow(display, border[2], x-bw, y+h, w+2*bw, bw); if( border[3] ) XMoveResizeWindow(display, border[3], x+w, y, bw, h ); } int BC_Capture::allocate_data() { // try shared memory if( !display ) return 1; if( use_shm ) { ximage = XShmCreateImage(display, vis, default_depth, ZPixmap, (char*)NULL, &shm_info, w, h); shm_info.shmid = shmget(IPC_PRIVATE, h * ximage->bytes_per_line, IPC_CREAT | 0600); if( shm_info.shmid == -1 ) { perror("BC_Capture::allocate_data shmget"); abort(); } data = (unsigned char *)shmat(shm_info.shmid, NULL, 0); shmctl(shm_info.shmid, IPC_RMID, 0); ximage->data = shm_info.shmaddr = (char*)data; // setting ximage->data stops BadValue shm_info.readOnly = 0; // Crashes here if remote server. BC_Resources::error = 0; XShmAttach(display, &shm_info); XSync(display, False); if( BC_Resources::error ) { XDestroyImage(ximage); shmdt(shm_info.shmaddr); use_shm = 0; } } if( !use_shm ) { // need to use bytes_per_line for some X servers data = 0; ximage = XCreateImage(display, vis, default_depth, ZPixmap, 0, (char*)data, w, h, 8, 0); data = (unsigned char*)malloc(h * ximage->bytes_per_line); XDestroyImage(ximage); ximage = XCreateImage(display, vis, default_depth, ZPixmap, 0, (char*)data, w, h, 8, 0); } row_data = new unsigned char*[h]; for( int i = 0; i < h; i++ ) { row_data[i] = &data[i * ximage->bytes_per_line]; } // This differs from the depth parameter of the top_level. bits_per_pixel = ximage->bits_per_pixel; return 0; } int BC_Capture::delete_data() { if( !display ) return 1; if( data ) { if( use_shm ) { XShmDetach(display, &shm_info); XDestroyImage(ximage); shmdt(shm_info.shmaddr); } else { XDestroyImage(ximage); } // data is automatically freed by XDestroyImage data = 0; delete [] row_data; } return 0; } int BC_Capture::get_w() { return w; } int BC_Capture::get_h() { return h; } // Capture a frame from the screen #define RGB_TO_YUV(y, u, v, r, g, b) { \ YUV::yuv.rgb_to_yuv_8(r, g, b, y, u, v); \ bclamp(y, 0,0xff); bclamp(u, 0,0xff); bclamp(v, 0,0xff); } int BC_Capture::capture_frame(VFrame *frame, int &x1, int &y1, int do_cursor) // the scale of the cursor if nonzero { if( !display ) return 1; if( x1 < 0 ) x1 = 0; if( y1 < 0 ) y1 = 0; if( x1 > get_top_w() - w ) x1 = get_top_w() - w; if( y1 > get_top_h() - h ) y1 = get_top_h() - h; // Read the raw data if( use_shm ) XShmGetImage(display, rootwin, ximage, x1, y1, 0xffffffff); else XGetSubImage(display, rootwin, x1, y1, w, h, 0xffffffff, ZPixmap, ximage, 0, 0); BC_CModels::transfer(frame->get_rows(), row_data, frame->get_y(), frame->get_u(), frame->get_v(), 0, 0, 0, 0, 0, w, h, 0, 0, frame->get_w(), frame->get_h(), bitmap_color_model, frame->get_color_model(), 0, frame->get_w(), w); if( do_cursor ) { XFixesCursorImage *cursor; cursor = XFixesGetCursorImage(display); if( cursor ) { //printf("BC_Capture::capture_frame %d cursor=%p colormodel=%d\n", // __LINE__, cursor, frame->get_color_model()); int scale = do_cursor; int cursor_x = cursor->x - x1 - cursor->xhot * scale; int cursor_y = cursor->y - y1 - cursor->yhot * scale; int w = frame->get_w(); int h = frame->get_h(); for( int i = 0; i < cursor->height; i++ ) { for( int yscale = 0; yscale < scale; yscale++ ) { if( cursor_y + i * scale + yscale >= 0 && cursor_y + i * scale + yscale < h ) { unsigned char *src = (unsigned char*)(cursor->pixels + i * cursor->width); int dst_y = cursor_y + i * scale + yscale; int dst_x = cursor_x; for( int j = 0; j < cursor->width; j++ ) { for( int xscale = 0; xscale < scale ; xscale++ ) { if( cursor_x + j * scale + xscale >= 0 && cursor_x + j * scale + xscale < w ) { int a = src[3]; int invert_a = 0xff - a; int r = src[2]; int g = src[1]; int b = src[0]; switch( frame->get_color_model() ) { case BC_RGB888: { unsigned char *dst = frame->get_rows()[dst_y] + dst_x * 3; dst[0] = (r * a + dst[0] * invert_a) / 0xff; dst[1] = (g * a + dst[1] * invert_a) / 0xff; dst[2] = (b * a + dst[2] * invert_a) / 0xff; break; } case BC_YUV420P: { unsigned char *dst_y_ = frame->get_y() + dst_y * w + dst_x; unsigned char *dst_u = frame->get_u() + (dst_y / 2) * (w / 2) + (dst_x / 2); unsigned char *dst_v = frame->get_v() + (dst_y / 2) * (w / 2) + (dst_x / 2); int y, u, v; RGB_TO_YUV(y, u, v, r, g, b); *dst_y_ = (y * a + *dst_y_ * invert_a) / 0xff; *dst_u = (u * a + *dst_u * invert_a) / 0xff; *dst_v = (v * a + *dst_v * invert_a) / 0xff; break; } } } dst_x++; } src += sizeof(long); } } } } // This frees cursor->pixels XFree(cursor); } } return 0; } int BC_Capture::get_top_w() { Screen *screen_ptr = XDefaultScreenOfDisplay(display); return WidthOfScreen(screen_ptr); } int BC_Capture::get_top_h() { Screen *screen_ptr = XDefaultScreenOfDisplay(display); return HeightOfScreen(screen_ptr); }