Gdk / X11 屏幕截图
Gdk / X11 Screen Capture
我想写一个连续捕获屏幕并对图像进行一些修改的程序。可以在以下位置找到完整的测试程序:
https://gist.github.com/blogsh/eb4dd4b96aca468c8bfa
但是,我运行陷入了一些问题。我做的第一个实验是使用 Gdk root window,从中创建一个 Cairo 上下文,然后使用它的目标作为另一个 window 的源,其中内容被绘制到:
mScreenContext = Gdk::Screen::get_default()->get_root_window()->create_cairo_context()
...
context->set_source(mScreenContext->get_target(), 0, 0);
context->paint();
这工作得很好(上面源代码中的变体 1)。它只是将整个屏幕绘制到另一个 window。所以我的下一步是尝试将内容保存到 Cairo ImageSurface 中以便对其进行修改:
mImageContext->set_source(mScreenContext->get_target(), 0, 0);
mImageContext->paint();
context->set_source(mImageSurface, 0, 0);
context->paint();
令人惊讶的是,对于 Gtk window 的第一次绘制,屏幕被捕获并绘制。不幸的是,之后什么也没有发生,仍然显示初始屏幕。如何解释这种行为?我必须承认我对这里的底层流程了解不多,所以也许有人可以提供一些提示?
使用 Gdk::Pixbuf
的第三个变体产生完全相同的行为:
mScreenBuffer = Gdk::Pixbuf::create(mGdkRootWindow, 0, 0, mScreenWidth, mScreenHeight);
Gdk::Cairo::set_source_pixbuf(context, mScreenBuffer, 0, 0);
context->paint();
最后(变体 4)我尝试直接使用 X11
:
Display *display = XOpenDisplay((char*)0);
XImage *image = XGetImage(display, RootWindow(display, DefaultScreen(display)), 0, 0, mScreenWidth, mScreenHeight, AllPlanes, XYPixmap);
mScreenBuffer = Gdk::Pixbuf::create_from_data((const guint8*)image->data, Gdk::COLORSPACE_RGB, 0, 8, mScreenWidth, mScreenHeight, mScreenWidth);
Gdk::Cairo::set_source_pixbuf(context, mScreenBuffer, 0, 0);
context->paint();
XFree(image);
实际上,这可行(虽然我还没有努力正确匹配像素格式),但它非常慢!
因此,如果能提供有关两个 Gdk 变体问题的提示,我将不胜感激 and/or 如何加速 X11 方法。或者也许有人知道一种完全不同的快速捕获屏幕的方法。
不幸的是,我对整个主题不是很熟悉,但另一个想法是使用基于 OpenGL 的 window 管理器,我可以在其中直接读取帧缓冲区?这有意义吗?
该程序的主要思想是我有一台无法直接放置在墙前的投影仪。所以我的想法是捕获屏幕,做一些双线性 t运行sformation 来解释投影的倾斜度,然后在另一个 window 中显示修改后的屏幕,这将显示在投影仪上。 .
XShmGetImage 和 XShmPutImage 比 XGetImage 和 XPutImage 更快。
在下一个示例中,我创建了两个图像:src 和 dst。在每次迭代中,我在 src 中保存一个屏幕截图,然后在 dst 中渲染它的缩放版本。
下图显示了标题为 "screencap" 的 window 中的示例 运行。
在低需求时,它以 60 fps 的速度运行(如 top-right 角的终端所示)。在高需求下,性能可能会下降到 25fps。
测试电脑:
Display resolution: 1920x1080
Graphic card: ATI Radeon HD 4200 (integrated)
CPU: AMD Phenom(tm) II X4 945, 3013.85 MHz
Window manager: XFCE 4.12 (compositing off)
Operating system: OpenBSD 5.9
Tested also in Linux (openSUSE Leap 42.1)
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <math.h>
#include <stdbool.h>
#include <sys/shm.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/XShm.h>
#ifdef __linux__
#include <sys/time.h>
#endif
// comment the next line to busy-wait at each frame
//#define __SLEEP__
#define FRAME 16667
#define PERIOD 1000000
#define NAME "screencap"
#define NAMESP " "
#define BPP 4
struct shmimage
{
XShmSegmentInfo shminfo ;
XImage * ximage ;
unsigned int * data ; // will point to the image's BGRA packed pixels
} ;
void initimage( struct shmimage * image )
{
image->ximage = NULL ;
image->shminfo.shmaddr = (char *) -1 ;
}
void destroyimage( Display * dsp, struct shmimage * image )
{
if( image->ximage )
{
XShmDetach( dsp, &image->shminfo ) ;
XDestroyImage( image->ximage ) ;
image->ximage = NULL ;
}
if( image->shminfo.shmaddr != ( char * ) -1 )
{
shmdt( image->shminfo.shmaddr ) ;
image->shminfo.shmaddr = ( char * ) -1 ;
}
}
int createimage( Display * dsp, struct shmimage * image, int width, int height )
{
// Create a shared memory area
image->shminfo.shmid = shmget( IPC_PRIVATE, width * height * BPP, IPC_CREAT | 0600 ) ;
if( image->shminfo.shmid == -1 )
{
perror( NAME ) ;
return false ;
}
// Map the shared memory segment into the address space of this process
image->shminfo.shmaddr = (char *) shmat( image->shminfo.shmid, 0, 0 ) ;
if( image->shminfo.shmaddr == (char *) -1 )
{
perror( NAME ) ;
return false ;
}
image->data = (unsigned int*) image->shminfo.shmaddr ;
image->shminfo.readOnly = false ;
// Mark the shared memory segment for removal
// It will be removed even if this program crashes
shmctl( image->shminfo.shmid, IPC_RMID, 0 ) ;
// Allocate the memory needed for the XImage structure
image->ximage = XShmCreateImage( dsp, XDefaultVisual( dsp, XDefaultScreen( dsp ) ),
DefaultDepth( dsp, XDefaultScreen( dsp ) ), ZPixmap, 0,
&image->shminfo, 0, 0 ) ;
if( !image->ximage )
{
destroyimage( dsp, image ) ;
printf( NAME ": could not allocate the XImage structure\n" ) ;
return false ;
}
image->ximage->data = (char *)image->data ;
image->ximage->width = width ;
image->ximage->height = height ;
// Ask the X server to attach the shared memory segment and sync
XShmAttach( dsp, &image->shminfo ) ;
XSync( dsp, false ) ;
return true ;
}
void getrootwindow( Display * dsp, struct shmimage * image )
{
XShmGetImage( dsp, XDefaultRootWindow( dsp ), image->ximage, 0, 0, AllPlanes ) ;
}
long timestamp( )
{
struct timeval tv ;
struct timezone tz ;
gettimeofday( &tv, &tz ) ;
return tv.tv_sec*1000000L + tv.tv_usec ;
}
Window createwindow( Display * dsp, int width, int height )
{
unsigned long mask = CWBackingStore ;
XSetWindowAttributes attributes ;
attributes.backing_store = NotUseful ;
mask |= CWBackingStore ;
Window window = XCreateWindow( dsp, DefaultRootWindow( dsp ),
0, 0, width, height, 0,
DefaultDepth( dsp, XDefaultScreen( dsp ) ),
InputOutput, CopyFromParent, mask, &attributes ) ;
XStoreName( dsp, window, NAME );
XSelectInput( dsp, window, StructureNotifyMask ) ;
XMapWindow( dsp, window );
return window ;
}
void destroywindow( Display * dsp, Window window )
{
XDestroyWindow( dsp, window );
}
unsigned int getpixel( struct shmimage * src, struct shmimage * dst,
int j, int i, int w, int h )
{
int x = (float)(i * src->ximage->width) / (float)w ;
int y = (float)(j * src->ximage->height) / (float)h ;
return src->data[ y * src->ximage->width + x ] ;
}
int processimage( struct shmimage * src, struct shmimage * dst )
{
int sw = src->ximage->width ;
int sh = src->ximage->height ;
int dw = dst->ximage->width ;
int dh = dst->ximage->height ;
// Here you can set the resulting position and size of the captured screen
// Because of the limitations of this example, it must fit in dst->ximage
int w = dw / 2 ;
int h = dh / 2 ;
int x = ( dw - w ) ;
int y = ( dh - h ) / 2 ;
// Just in case...
if( x < 0 || y < 0 || x + w > dw || y + h > dh || sw < dw || sh < dh )
{
printf( NAME ": This is only a limited example\n" ) ;
printf( NAMESP " Please implement a complete scaling algorithm\n" ) ;
return false ;
}
unsigned int * d = dst->data + y * dw + x ;
int r = dw - w ;
int j, i ;
for( j = 0 ; j < h ; ++j )
{
for( i = 0 ; i < w ; ++i )
{
*d++ = getpixel( src, dst, j, i, w, h ) ;
}
d += r ;
}
return true ;
}
int run( Display * dsp, Window window, struct shmimage * src, struct shmimage * dst )
{
XGCValues xgcvalues ;
xgcvalues.graphics_exposures = False ;
GC gc = XCreateGC( dsp, window, GCGraphicsExposures, &xgcvalues ) ;
Atom delete_atom = XInternAtom( dsp, "WM_DELETE_WINDOW", False ) ;
XSetWMProtocols( dsp, window, &delete_atom, True ) ;
XEvent xevent ;
int running = true ;
int initialized = false ;
int dstwidth = dst->ximage->width ;
int dstheight = dst->ximage->height ;
long framets = timestamp( ) ;
long periodts = timestamp( ) ;
long frames = 0 ;
int fd = ConnectionNumber( dsp ) ;
while( running )
{
while( XPending( dsp ) )
{
XNextEvent( dsp, &xevent ) ;
if( ( xevent.type == ClientMessage && xevent.xclient.data.l[0] == delete_atom )
|| xevent.type == DestroyNotify )
{
running = false ;
break ;
}
else if( xevent.type == ConfigureNotify )
{
if( xevent.xconfigure.width == dstwidth
&& xevent.xconfigure.height == dstheight )
{
initialized = true ;
}
}
}
if( initialized )
{
getrootwindow( dsp, src ) ;
if( !processimage( src, dst ) )
{
return false ;
}
XShmPutImage( dsp, window, gc, dst->ximage,
0, 0, 0, 0, dstwidth, dstheight, False ) ;
XSync( dsp, False ) ;
int frameus = timestamp( ) - framets ;
++frames ;
while( frameus < FRAME )
{
#if defined( __SLEEP__ )
usleep( FRAME - frameus ) ;
#endif
frameus = timestamp( ) - framets ;
}
framets = timestamp( ) ;
int periodus = timestamp( ) - periodts ;
if( periodus >= PERIOD )
{
printf( "fps: %d\n", (int)round( 1000000.0L * frames / periodus ) ) ;
frames = 0 ;
periodts = framets ;
}
}
}
return true ;
}
int main( int argc, char * argv[] )
{
Display * dsp = XOpenDisplay( NULL ) ;
if( !dsp )
{
printf( NAME ": could not open a connection to the X server\n" ) ;
return 1 ;
}
if( !XShmQueryExtension( dsp ) )
{
XCloseDisplay( dsp ) ;
printf( NAME ": the X server does not support the XSHM extension\n" ) ;
return 1 ;
}
int screen = XDefaultScreen( dsp ) ;
struct shmimage src, dst ;
initimage( &src ) ;
int width = XDisplayWidth( dsp, screen ) ;
int height = XDisplayHeight( dsp, screen ) ;
if( !createimage( dsp, &src, width, height ) )
{
XCloseDisplay( dsp ) ;
return 1 ;
}
initimage( &dst ) ;
int dstwidth = width / 2 ;
int dstheight = height / 2 ;
if( !createimage( dsp, &dst, dstwidth, dstheight ) )
{
destroyimage( dsp, &src ) ;
XCloseDisplay( dsp ) ;
return 1 ;
}
if( dst.ximage->bits_per_pixel != 32 )
{
destroyimage( dsp, &src ) ;
destroyimage( dsp, &dst ) ;
XCloseDisplay( dsp ) ;
printf( NAME ": This is only a limited example\n" ) ;
printf( NAMESP " Please add support for all pixel formats using: \n" ) ;
printf( NAMESP " dst.ximage->bits_per_pixel\n" ) ;
printf( NAMESP " dst.ximage->red_mask\n" ) ;
printf( NAMESP " dst.ximage->green_mask\n" ) ;
printf( NAMESP " dst.ximage->blue_mask\n" ) ;
return 1 ;
}
Window window = createwindow( dsp, dstwidth, dstheight ) ;
run( dsp, window, &src, &dst ) ;
destroywindow( dsp, window ) ;
destroyimage( dsp, &src ) ;
destroyimage( dsp, &dst ) ;
XCloseDisplay( dsp ) ;
return 0 ;
}
这只是一个例子。如果您喜欢它的表现,您应该考虑添加更合适的缩放算法并支持所有像素格式。
您可以像这样编译示例:
gcc screencap.c -o screencap -std=c99 -I/usr/X11R6/include -L/usr/X11R6/lib -lX11 -lXext -lm
在引用根window的cairo表面上调用cairo_surface_mark_dirty()
。由于您似乎只有开罗上下文而不是直接的表面:您可以使用 cairo_surface_get_target()
从开罗上下文中获取表面。
Cairo 假定您仅通过 cairo API 绘制到表面,除非您告诉它您在 cairo 之外更改了某些内容。下载屏幕截图的费用很高,因此 cairo 会缓存结果以便以后可以重新使用。
可能,您还必须在调用 cairo_surface_mark_dirty()
之前调用 cairo_surface_flush()
...
我想写一个连续捕获屏幕并对图像进行一些修改的程序。可以在以下位置找到完整的测试程序:
https://gist.github.com/blogsh/eb4dd4b96aca468c8bfa
但是,我运行陷入了一些问题。我做的第一个实验是使用 Gdk root window,从中创建一个 Cairo 上下文,然后使用它的目标作为另一个 window 的源,其中内容被绘制到:
mScreenContext = Gdk::Screen::get_default()->get_root_window()->create_cairo_context()
...
context->set_source(mScreenContext->get_target(), 0, 0);
context->paint();
这工作得很好(上面源代码中的变体 1)。它只是将整个屏幕绘制到另一个 window。所以我的下一步是尝试将内容保存到 Cairo ImageSurface 中以便对其进行修改:
mImageContext->set_source(mScreenContext->get_target(), 0, 0);
mImageContext->paint();
context->set_source(mImageSurface, 0, 0);
context->paint();
令人惊讶的是,对于 Gtk window 的第一次绘制,屏幕被捕获并绘制。不幸的是,之后什么也没有发生,仍然显示初始屏幕。如何解释这种行为?我必须承认我对这里的底层流程了解不多,所以也许有人可以提供一些提示?
使用 Gdk::Pixbuf
的第三个变体产生完全相同的行为:
mScreenBuffer = Gdk::Pixbuf::create(mGdkRootWindow, 0, 0, mScreenWidth, mScreenHeight);
Gdk::Cairo::set_source_pixbuf(context, mScreenBuffer, 0, 0);
context->paint();
最后(变体 4)我尝试直接使用 X11
:
Display *display = XOpenDisplay((char*)0);
XImage *image = XGetImage(display, RootWindow(display, DefaultScreen(display)), 0, 0, mScreenWidth, mScreenHeight, AllPlanes, XYPixmap);
mScreenBuffer = Gdk::Pixbuf::create_from_data((const guint8*)image->data, Gdk::COLORSPACE_RGB, 0, 8, mScreenWidth, mScreenHeight, mScreenWidth);
Gdk::Cairo::set_source_pixbuf(context, mScreenBuffer, 0, 0);
context->paint();
XFree(image);
实际上,这可行(虽然我还没有努力正确匹配像素格式),但它非常慢!
因此,如果能提供有关两个 Gdk 变体问题的提示,我将不胜感激 and/or 如何加速 X11 方法。或者也许有人知道一种完全不同的快速捕获屏幕的方法。
不幸的是,我对整个主题不是很熟悉,但另一个想法是使用基于 OpenGL 的 window 管理器,我可以在其中直接读取帧缓冲区?这有意义吗?
该程序的主要思想是我有一台无法直接放置在墙前的投影仪。所以我的想法是捕获屏幕,做一些双线性 t运行sformation 来解释投影的倾斜度,然后在另一个 window 中显示修改后的屏幕,这将显示在投影仪上。 .
XShmGetImage 和 XShmPutImage 比 XGetImage 和 XPutImage 更快。 在下一个示例中,我创建了两个图像:src 和 dst。在每次迭代中,我在 src 中保存一个屏幕截图,然后在 dst 中渲染它的缩放版本。
下图显示了标题为 "screencap" 的 window 中的示例 运行。 在低需求时,它以 60 fps 的速度运行(如 top-right 角的终端所示)。在高需求下,性能可能会下降到 25fps。
测试电脑:
Display resolution: 1920x1080
Graphic card: ATI Radeon HD 4200 (integrated)
CPU: AMD Phenom(tm) II X4 945, 3013.85 MHz
Window manager: XFCE 4.12 (compositing off)
Operating system: OpenBSD 5.9
Tested also in Linux (openSUSE Leap 42.1)
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <math.h>
#include <stdbool.h>
#include <sys/shm.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/XShm.h>
#ifdef __linux__
#include <sys/time.h>
#endif
// comment the next line to busy-wait at each frame
//#define __SLEEP__
#define FRAME 16667
#define PERIOD 1000000
#define NAME "screencap"
#define NAMESP " "
#define BPP 4
struct shmimage
{
XShmSegmentInfo shminfo ;
XImage * ximage ;
unsigned int * data ; // will point to the image's BGRA packed pixels
} ;
void initimage( struct shmimage * image )
{
image->ximage = NULL ;
image->shminfo.shmaddr = (char *) -1 ;
}
void destroyimage( Display * dsp, struct shmimage * image )
{
if( image->ximage )
{
XShmDetach( dsp, &image->shminfo ) ;
XDestroyImage( image->ximage ) ;
image->ximage = NULL ;
}
if( image->shminfo.shmaddr != ( char * ) -1 )
{
shmdt( image->shminfo.shmaddr ) ;
image->shminfo.shmaddr = ( char * ) -1 ;
}
}
int createimage( Display * dsp, struct shmimage * image, int width, int height )
{
// Create a shared memory area
image->shminfo.shmid = shmget( IPC_PRIVATE, width * height * BPP, IPC_CREAT | 0600 ) ;
if( image->shminfo.shmid == -1 )
{
perror( NAME ) ;
return false ;
}
// Map the shared memory segment into the address space of this process
image->shminfo.shmaddr = (char *) shmat( image->shminfo.shmid, 0, 0 ) ;
if( image->shminfo.shmaddr == (char *) -1 )
{
perror( NAME ) ;
return false ;
}
image->data = (unsigned int*) image->shminfo.shmaddr ;
image->shminfo.readOnly = false ;
// Mark the shared memory segment for removal
// It will be removed even if this program crashes
shmctl( image->shminfo.shmid, IPC_RMID, 0 ) ;
// Allocate the memory needed for the XImage structure
image->ximage = XShmCreateImage( dsp, XDefaultVisual( dsp, XDefaultScreen( dsp ) ),
DefaultDepth( dsp, XDefaultScreen( dsp ) ), ZPixmap, 0,
&image->shminfo, 0, 0 ) ;
if( !image->ximage )
{
destroyimage( dsp, image ) ;
printf( NAME ": could not allocate the XImage structure\n" ) ;
return false ;
}
image->ximage->data = (char *)image->data ;
image->ximage->width = width ;
image->ximage->height = height ;
// Ask the X server to attach the shared memory segment and sync
XShmAttach( dsp, &image->shminfo ) ;
XSync( dsp, false ) ;
return true ;
}
void getrootwindow( Display * dsp, struct shmimage * image )
{
XShmGetImage( dsp, XDefaultRootWindow( dsp ), image->ximage, 0, 0, AllPlanes ) ;
}
long timestamp( )
{
struct timeval tv ;
struct timezone tz ;
gettimeofday( &tv, &tz ) ;
return tv.tv_sec*1000000L + tv.tv_usec ;
}
Window createwindow( Display * dsp, int width, int height )
{
unsigned long mask = CWBackingStore ;
XSetWindowAttributes attributes ;
attributes.backing_store = NotUseful ;
mask |= CWBackingStore ;
Window window = XCreateWindow( dsp, DefaultRootWindow( dsp ),
0, 0, width, height, 0,
DefaultDepth( dsp, XDefaultScreen( dsp ) ),
InputOutput, CopyFromParent, mask, &attributes ) ;
XStoreName( dsp, window, NAME );
XSelectInput( dsp, window, StructureNotifyMask ) ;
XMapWindow( dsp, window );
return window ;
}
void destroywindow( Display * dsp, Window window )
{
XDestroyWindow( dsp, window );
}
unsigned int getpixel( struct shmimage * src, struct shmimage * dst,
int j, int i, int w, int h )
{
int x = (float)(i * src->ximage->width) / (float)w ;
int y = (float)(j * src->ximage->height) / (float)h ;
return src->data[ y * src->ximage->width + x ] ;
}
int processimage( struct shmimage * src, struct shmimage * dst )
{
int sw = src->ximage->width ;
int sh = src->ximage->height ;
int dw = dst->ximage->width ;
int dh = dst->ximage->height ;
// Here you can set the resulting position and size of the captured screen
// Because of the limitations of this example, it must fit in dst->ximage
int w = dw / 2 ;
int h = dh / 2 ;
int x = ( dw - w ) ;
int y = ( dh - h ) / 2 ;
// Just in case...
if( x < 0 || y < 0 || x + w > dw || y + h > dh || sw < dw || sh < dh )
{
printf( NAME ": This is only a limited example\n" ) ;
printf( NAMESP " Please implement a complete scaling algorithm\n" ) ;
return false ;
}
unsigned int * d = dst->data + y * dw + x ;
int r = dw - w ;
int j, i ;
for( j = 0 ; j < h ; ++j )
{
for( i = 0 ; i < w ; ++i )
{
*d++ = getpixel( src, dst, j, i, w, h ) ;
}
d += r ;
}
return true ;
}
int run( Display * dsp, Window window, struct shmimage * src, struct shmimage * dst )
{
XGCValues xgcvalues ;
xgcvalues.graphics_exposures = False ;
GC gc = XCreateGC( dsp, window, GCGraphicsExposures, &xgcvalues ) ;
Atom delete_atom = XInternAtom( dsp, "WM_DELETE_WINDOW", False ) ;
XSetWMProtocols( dsp, window, &delete_atom, True ) ;
XEvent xevent ;
int running = true ;
int initialized = false ;
int dstwidth = dst->ximage->width ;
int dstheight = dst->ximage->height ;
long framets = timestamp( ) ;
long periodts = timestamp( ) ;
long frames = 0 ;
int fd = ConnectionNumber( dsp ) ;
while( running )
{
while( XPending( dsp ) )
{
XNextEvent( dsp, &xevent ) ;
if( ( xevent.type == ClientMessage && xevent.xclient.data.l[0] == delete_atom )
|| xevent.type == DestroyNotify )
{
running = false ;
break ;
}
else if( xevent.type == ConfigureNotify )
{
if( xevent.xconfigure.width == dstwidth
&& xevent.xconfigure.height == dstheight )
{
initialized = true ;
}
}
}
if( initialized )
{
getrootwindow( dsp, src ) ;
if( !processimage( src, dst ) )
{
return false ;
}
XShmPutImage( dsp, window, gc, dst->ximage,
0, 0, 0, 0, dstwidth, dstheight, False ) ;
XSync( dsp, False ) ;
int frameus = timestamp( ) - framets ;
++frames ;
while( frameus < FRAME )
{
#if defined( __SLEEP__ )
usleep( FRAME - frameus ) ;
#endif
frameus = timestamp( ) - framets ;
}
framets = timestamp( ) ;
int periodus = timestamp( ) - periodts ;
if( periodus >= PERIOD )
{
printf( "fps: %d\n", (int)round( 1000000.0L * frames / periodus ) ) ;
frames = 0 ;
periodts = framets ;
}
}
}
return true ;
}
int main( int argc, char * argv[] )
{
Display * dsp = XOpenDisplay( NULL ) ;
if( !dsp )
{
printf( NAME ": could not open a connection to the X server\n" ) ;
return 1 ;
}
if( !XShmQueryExtension( dsp ) )
{
XCloseDisplay( dsp ) ;
printf( NAME ": the X server does not support the XSHM extension\n" ) ;
return 1 ;
}
int screen = XDefaultScreen( dsp ) ;
struct shmimage src, dst ;
initimage( &src ) ;
int width = XDisplayWidth( dsp, screen ) ;
int height = XDisplayHeight( dsp, screen ) ;
if( !createimage( dsp, &src, width, height ) )
{
XCloseDisplay( dsp ) ;
return 1 ;
}
initimage( &dst ) ;
int dstwidth = width / 2 ;
int dstheight = height / 2 ;
if( !createimage( dsp, &dst, dstwidth, dstheight ) )
{
destroyimage( dsp, &src ) ;
XCloseDisplay( dsp ) ;
return 1 ;
}
if( dst.ximage->bits_per_pixel != 32 )
{
destroyimage( dsp, &src ) ;
destroyimage( dsp, &dst ) ;
XCloseDisplay( dsp ) ;
printf( NAME ": This is only a limited example\n" ) ;
printf( NAMESP " Please add support for all pixel formats using: \n" ) ;
printf( NAMESP " dst.ximage->bits_per_pixel\n" ) ;
printf( NAMESP " dst.ximage->red_mask\n" ) ;
printf( NAMESP " dst.ximage->green_mask\n" ) ;
printf( NAMESP " dst.ximage->blue_mask\n" ) ;
return 1 ;
}
Window window = createwindow( dsp, dstwidth, dstheight ) ;
run( dsp, window, &src, &dst ) ;
destroywindow( dsp, window ) ;
destroyimage( dsp, &src ) ;
destroyimage( dsp, &dst ) ;
XCloseDisplay( dsp ) ;
return 0 ;
}
这只是一个例子。如果您喜欢它的表现,您应该考虑添加更合适的缩放算法并支持所有像素格式。
您可以像这样编译示例:
gcc screencap.c -o screencap -std=c99 -I/usr/X11R6/include -L/usr/X11R6/lib -lX11 -lXext -lm
在引用根window的cairo表面上调用cairo_surface_mark_dirty()
。由于您似乎只有开罗上下文而不是直接的表面:您可以使用 cairo_surface_get_target()
从开罗上下文中获取表面。
Cairo 假定您仅通过 cairo API 绘制到表面,除非您告诉它您在 cairo 之外更改了某些内容。下载屏幕截图的费用很高,因此 cairo 会缓存结果以便以后可以重新使用。
可能,您还必须在调用 cairo_surface_mark_dirty()
之前调用 cairo_surface_flush()
...