X11 Xlib截屏问题及深入分析二 —— 源码实现1

507 阅读5分钟

本文已参与 [新人创作礼] 活动,一起开启掘金创作之路。

接上一篇文章《X11 Xlib截屏问题及深入分析一 —— 源码位置》,链接为:

juejin.cn/post/716848…

上一篇文章讲到了源码包的内容,本文看一下几个接口函数对应的位置。再次列出应用源码:

#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
 
int main(int argc, char *argv[])
{
	Display *display = XOpenDisplay(NULL);
	if(!display)
	{
		printf("XOpenDisplay failed\n");
		return -1;
	}
 
	int screen_num = DefaultScreen(display);
	printf("default screen_num is: %d\n", screen_num);
 
	int screen_width = DisplayWidth(display, screen_num);
	int screen_height = DisplayHeight(display, screen_num);
	printf("screen_width: %d, screen_height: %d\n", screen_width, screen_height);
 
	Window root_win = RootWindow(display, screen_num);
	if(!root_win)
	{
		printf("can not get root window\n");
		return -1;
	}
 
	Window disp_win = XCreateSimpleWindow(display, root_win, 0, 0, screen_width, screen_height, 1, 0, 0) ;
	if(!disp_win)
	{
		printf("can not get snapshot display window\n");
		return -1;
	}
 
	XMapWindow(display, disp_win);
 
	XImage *img = XGetImage(display, root_win, 0, 0, screen_width, screen_height, ~0, ZPixmap);	
 
	XPutImage(display, disp_win, DefaultGC(display, screen_num), img, 0, 0, 0, 0, screen_width, screen_height);
 
	char c = getchar();
 
	//XDestroyImage(img);
	XCloseDisplay(display);
 
	printf("screen_snapshot finished\n");
 
	return 0;
}

由以上代码可以看到,总共调用了几个接口函数:

  • XOpenDisplay
  • XCloseDisplay
  • XCreateSimpleWindow
  • XMapWindow
  • XGetImage
  • XPutImage

下边分别列出各个接口函数对应源码。

  • XOpenDisplay源码

XOpenDisplay函数对应的源码在src/OpenDis.c中。如下所示:

/*
 * Connects to a server, creates a Display object and returns a pointer to
 * the newly created Display back to the caller.
 */
Display *
XOpenDisplay (
	register _Xconst char *display)
{
	register Display *dpy;		/* New Display object being created. */
	register int i;
	int j, k;			/* random iterator indexes */
	char *display_name;		/* pointer to display name */
	char *setup = NULL;		/* memory allocated at startup */
	int iscreen;			/* screen number */
	xConnSetupPrefix prefix;	/* prefix information */
	int vendorlen;			/* length of vendor string */
	union {
		xConnSetup *setup;
		char *failure;
		char *vendor;
		xPixmapFormat *sf;
		xWindowRoot *rp;
		xDepth *dp;
		xVisualType *vp;
	} u;				/* proto data returned from server */
	long setuplength;	/* number of bytes in setup message */
	long usedbytes = 0;     /* number of bytes we have processed */
	unsigned long mask;
	long int conn_buf_size;
	char *xlib_buffer_size;

	/*
	 * If the display specifier string supplied as an argument to this
	 * routine is NULL or a pointer to NULL, read the DISPLAY variable.
	 */
	if (display == NULL || *display == '\0') {
		if ((display_name = getenv("DISPLAY")) == NULL) {
			/* Oops! No DISPLAY environment variable - error. */
			return(NULL);
		}
	}
	else {
		/* Display is non-NULL, copy the pointer */
		display_name = (char *)display;
	}

/*
 * Set the default error handlers.  This allows the global variables to
 * default to NULL for use with shared libraries.
 */
	if (_XErrorFunction == NULL) (void) XSetErrorHandler (NULL);
	if (_XIOErrorFunction == NULL) (void) XSetIOErrorHandler (NULL);

/*
 * Attempt to allocate a display structure. Return NULL if allocation fails.
 */
	if ((dpy = Xcalloc(1, sizeof(Display))) == NULL) {
		return(NULL);
	}

	if ((dpy->display_name = strdup(display_name)) == NULL) {
		OutOfMemory(dpy);
		return(NULL);
	}

/*
 * Call the Connect routine to get the transport connection object.
 * If NULL is returned, the connection failed.
 */

	if(!_XConnectXCB(dpy, display, &iscreen)) {
		OutOfMemory(dpy);
		return NULL;
	}

	/* Initialize as much of the display structure as we can.
	 * Initialize pointers to NULL so that XFreeDisplayStructure will
	 * work if we run out of memory before we finish initializing.
	 */
	dpy->keysyms		= (KeySym *) NULL;
	dpy->modifiermap	= NULL;
	dpy->lock_meaning	= NoSymbol;
	dpy->keysyms_per_keycode = 0;
	dpy->xdefaults		= (char *)NULL;
	dpy->scratch_length	= 0L;
	dpy->scratch_buffer	= NULL;
	dpy->key_bindings	= NULL;
	dpy->ext_procs		= (_XExtension *)NULL;
	dpy->ext_data		= (XExtData *)NULL;
	dpy->ext_number 	= 0;
	dpy->event_vec[X_Error] = _XUnknownWireEvent;
	dpy->event_vec[X_Reply] = _XUnknownWireEvent;
	dpy->wire_vec[X_Error]  = _XUnknownNativeEvent;
	dpy->wire_vec[X_Reply]  = _XUnknownNativeEvent;
	for (i = KeyPress; i < LASTEvent; i++) {
	    dpy->event_vec[i] 	= _XWireToEvent;
	    dpy->wire_vec[i] 	= NULL;
	}
	for (i = LASTEvent; i < 128; i++) {
	    dpy->event_vec[i] 	= _XUnknownWireEvent;
	    dpy->wire_vec[i] 	= _XUnknownNativeEvent;
	}
	dpy->resource_id	= 0;
	dpy->db 		= (struct _XrmHashBucketRec *)NULL;
	dpy->cursor_font	= None;
	dpy->flags		= 0;
	dpy->async_handlers	= NULL;
	dpy->screens		= NULL;
	dpy->vendor		= NULL;
	dpy->buffer		= NULL;
	dpy->atoms		= NULL;
	dpy->error_vec		= NULL;
	dpy->context_db		= NULL;
	dpy->free_funcs		= NULL;
	dpy->pixmap_format	= NULL;
	dpy->cms.clientCmaps	= NULL;
	dpy->cms.defaultCCCs	= NULL;
	dpy->cms.perVisualIntensityMaps = NULL;
	dpy->im_filters		= NULL;
 	dpy->bigreq_size	= 0;
	dpy->lock		= NULL;
	dpy->lock_fns		= NULL;
	dpy->qfree		= NULL;
	dpy->next_event_serial_num = 1;
	dpy->im_fd_info		= NULL;
	dpy->im_fd_length	= 0;
	dpy->conn_watchers	= NULL;
	dpy->watcher_count	= 0;
	dpy->filedes		= NULL;
	dpy->flushes		= NULL;
	dpy->xcmisc_opcode	= 0;
	dpy->xkb_info		= NULL;
	dpy->exit_handler_data	= NULL;

/*
 * Setup other information in this display structure.
 */
	dpy->vnumber = X_PROTOCOL;
	dpy->resource_alloc = _XAllocID;
	dpy->idlist_alloc = _XAllocIDs;
	dpy->synchandler = NULL;
	dpy->savedsynchandler = NULL;
	X_DPY_SET_REQUEST(dpy, 0);
	X_DPY_SET_LAST_REQUEST_READ(dpy, 0);
	dpy->default_screen = iscreen;  /* Value returned by ConnectDisplay */
	dpy->last_req = (char *)&_dummy_request;
	dpy->error_threads = NULL;
	dpy->exit_handler = _XDefaultIOErrorExit;

	/* Initialize the display lock */
	if (InitDisplayLock(dpy) != 0) {
	        OutOfMemory (dpy);
		return(NULL);
	}

	if (!_XPollfdCacheInit(dpy)) {
	        OutOfMemory (dpy);
		return(NULL);
	}

	/* Set up the output buffers. */
#ifndef XLIBDEFAULTBUFSIZE
#define XLIBDEFAULTBUFSIZE 16384 /* 16k */
#endif
#ifndef XLIBMINBUFSIZE
#define XLIBMINBUFSIZE BUFSIZE /* old default buffer size */
#endif
	xlib_buffer_size = getenv("XLIBBUFFERSIZE");

#ifdef __sun /* Backwards compatibility for old Solaris libX11 name */
	if (xlib_buffer_size == NULL)
	    xlib_buffer_size = getenv("XSUNBUFFERSIZE");
#endif

	if (xlib_buffer_size == NULL)
	    conn_buf_size = XLIBDEFAULTBUFSIZE;
	else
	    conn_buf_size = 1024 * strtol(xlib_buffer_size, NULL, 10);
	if (conn_buf_size < XLIBMINBUFSIZE)
	    conn_buf_size = XLIBMINBUFSIZE;

	if ((dpy->bufptr = dpy->buffer = Xcalloc(1, conn_buf_size)) == NULL) {
	    OutOfMemory (dpy);
	    return(NULL);
	}
	dpy->xcb->real_bufmax = dpy->buffer + conn_buf_size;
	dpy->bufmax = dpy->buffer;

	/* Set up the input event queue and input event queue parameters. */
	dpy->head = dpy->tail = NULL;
	dpy->qlen = 0;

	/* Set up free-function record */
	if ((dpy->free_funcs = Xcalloc(1, sizeof(_XFreeFuncRec))) == NULL) {
	    OutOfMemory (dpy);
	    return(NULL);
	}

	{
		const struct xcb_setup_t *xcbsetup = xcb_get_setup(dpy->xcb->connection);
		memcpy(&prefix, xcbsetup, sizeof(prefix));
		setuplength = prefix.length << 2;
		setup = (char *) xcbsetup;
		setup += SIZEOF(xConnSetupPrefix);
		u.setup = (xConnSetup *) setup;
	}

/*
 * Check if the reply was long enough to get any information out of it.
 */
	usedbytes = sz_xConnSetup;
	if (setuplength < usedbytes ) {
	    fprintf (stderr, "Xlib: Broken initial reply: Too short (%ld)\n", setuplength);
	    OutOfMemory(dpy);
	    return (NULL);
	}

/*
 * We succeeded at authorization, so let us move the data into
 * the display structure.
 */
	dpy->proto_major_version= prefix.majorVersion;
	dpy->proto_minor_version= prefix.minorVersion;
	dpy->release 		= u.setup->release;
	dpy->resource_base	= u.setup->ridBase;
	dpy->resource_mask	= u.setup->ridMask;
	dpy->min_keycode	= u.setup->minKeyCode;
	dpy->max_keycode	= u.setup->maxKeyCode;
	dpy->motion_buffer	= u.setup->motionBufferSize;
	dpy->nformats		= u.setup->numFormats;
	dpy->nscreens		= u.setup->numRoots;
	dpy->byte_order		= u.setup->imageByteOrder;
	dpy->bitmap_unit	= u.setup->bitmapScanlineUnit;
	dpy->bitmap_pad		= u.setup->bitmapScanlinePad;
	dpy->bitmap_bit_order   = u.setup->bitmapBitOrder;
	dpy->max_request_size	= u.setup->maxRequestSize;
	mask = dpy->resource_mask;
	dpy->resource_shift	= 0;
	if (!mask)
	{
	    fprintf (stderr, "Xlib: connection to \"%s\" invalid setup\n",
		     dpy->display_name);
	    OutOfMemory(dpy);
	    return (NULL);
	}

	while (!(mask & 1)) {
	    dpy->resource_shift++;
	    mask = mask >> 1;
	}
	dpy->resource_max = (dpy->resource_mask >> dpy->resource_shift) - 5;
/*
 * now extract the vendor string...  String must be null terminated,
 * padded to multiple of 4 bytes.
 */
	/* Check for a sane vendor string length */
	if (u.setup->nbytesVendor > 256) {
	    OutOfMemory(dpy);
	    return (NULL);
	}

	dpy->vendor = Xmalloc(u.setup->nbytesVendor + 1);
	if (dpy->vendor == NULL) {
	    OutOfMemory(dpy);
	    return (NULL);
	}
	vendorlen = u.setup->nbytesVendor;

/*
 * validate setup length
 */
	usedbytes += (vendorlen + 3) & ~3;
	if (setuplength < usedbytes) {
	    fprintf (stderr, "Xlib: Broken initial reply: Too short (%ld)\n", setuplength);
	    OutOfMemory(dpy);
	    return (NULL);
	}

 	u.setup = (xConnSetup *) (((char *) u.setup) + sz_xConnSetup);
  	(void) strncpy(dpy->vendor, u.vendor, (size_t) vendorlen);
	dpy->vendor[vendorlen] = '\0';
 	vendorlen = (vendorlen + 3) & ~3;	/* round up */
	u.vendor += vendorlen;

/*
 * Now iterate down setup information.....
 */
	dpy->pixmap_format = Xcalloc(dpy->nformats, sizeof(ScreenFormat));
	if (dpy->pixmap_format == NULL) {
	        OutOfMemory (dpy);
		return(NULL);
	}
/*
 * First decode the Z axis Screen format information.
 */
	usedbytes += dpy->nformats * sz_xPixmapFormat;

	if (setuplength < usedbytes) {
	    fprintf (stderr, "Xlib: Broken initial reply: Too short (%ld)\n", setuplength);
	    OutOfMemory (dpy);
	    return(NULL);
	}

	for (i = 0; i < dpy->nformats; i++) {
	    register ScreenFormat *fmt = &dpy->pixmap_format[i];
	    fmt->depth = u.sf->depth;
	    fmt->bits_per_pixel = u.sf->bitsPerPixel;
	    fmt->scanline_pad = u.sf->scanLinePad;
	    fmt->ext_data = NULL;
	    u.sf = (xPixmapFormat *) (((char *) u.sf) + sz_xPixmapFormat);
	}

/*
 * next the Screen structures.
 */
	dpy->screens = Xcalloc(dpy->nscreens, sizeof(Screen));
	if (dpy->screens == NULL) {
	        OutOfMemory (dpy);
		return(NULL);
	}

/*
 * Now go deal with each screen structure.
 */
	for (i = 0; i < dpy->nscreens; i++) {
	    register Screen *sp = &dpy->screens[i];
	    VisualID root_visualID;

	    usedbytes += sz_xWindowRoot;
	    if (setuplength < usedbytes) {
		fprintf (stderr, "Xlib: Broken initial reply: Too short (%ld)\n", setuplength);
		OutOfMemory (dpy);
		return(NULL);
	    }

	    root_visualID = u.rp->rootVisualID;
	    sp->display	    = dpy;
	    sp->root 	    = u.rp->windowId;
	    sp->cmap 	    = u.rp->defaultColormap;
	    sp->white_pixel = u.rp->whitePixel;
	    sp->black_pixel = u.rp->blackPixel;
	    sp->root_input_mask = u.rp->currentInputMask;
	    sp->width	    = u.rp->pixWidth;
	    sp->height	    = u.rp->pixHeight;
	    sp->mwidth	    = u.rp->mmWidth;
	    sp->mheight	    = u.rp->mmHeight;
	    sp->min_maps    = u.rp->minInstalledMaps;
	    sp->max_maps    = u.rp->maxInstalledMaps;
	    sp->backing_store= u.rp->backingStore;
	    sp->save_unders = u.rp->saveUnders;
	    sp->root_depth  = u.rp->rootDepth;
	    sp->ndepths	    = u.rp->nDepths;
	    sp->ext_data   = NULL;
	    u.rp = (xWindowRoot *) (((char *) u.rp) + sz_xWindowRoot);
/*
 * lets set up the depth structures.
 */
	    sp->depths = Xcalloc(sp->ndepths, sizeof(Depth));
	    if (sp->depths == NULL) {
		OutOfMemory (dpy);
		return(NULL);
	    }
	    /*
	     * for all depths on this screen.
	     */
	    for (j = 0; j < sp->ndepths; j++) {
		Depth *dp = &sp->depths[j];

		usedbytes += sz_xDepth;
		if (setuplength < usedbytes) {
		    fprintf (stderr, "Xlib: Broken initial reply: Too short (%ld)\n", setuplength);
		    OutOfMemory (dpy);
		    return(NULL);
		}

		dp->depth = u.dp->depth;
		dp->nvisuals = u.dp->nVisuals;
		u.dp = (xDepth *) (((char *) u.dp) + sz_xDepth);
		if (dp->nvisuals > 0) {
		    dp->visuals = Xcalloc(dp->nvisuals, sizeof(Visual));
		    if (dp->visuals == NULL) {
			OutOfMemory (dpy);
			return(NULL);
		    }
		    for (k = 0; k < dp->nvisuals; k++) {
			register Visual *vp = &dp->visuals[k];

			usedbytes += sz_xVisualType;
			if (setuplength < usedbytes) {
			    fprintf (stderr, "Xlib: Broken initial reply: Too short (%ld)\n", setuplength);
			    OutOfMemory (dpy);
			    return(NULL);
			}

			vp->visualid	= u.vp->visualID;
			vp->class	= u.vp->class;
			vp->bits_per_rgb= u.vp->bitsPerRGB;
			vp->map_entries	= u.vp->colormapEntries;
			vp->red_mask	= u.vp->redMask;
			vp->green_mask	= u.vp->greenMask;
			vp->blue_mask	= u.vp->blueMask;
			vp->ext_data	= NULL;
			u.vp = (xVisualType *) (((char *) u.vp) +
						sz_xVisualType);
		    }
		    if (dp->depth == 32 && getenv ("XLIB_SKIP_ARGB_VISUALS"))
		    {
			Xfree (dp->visuals);
			dp->visuals = NULL;
			dp->nvisuals = 0;
		    }
		} else {
		    dp->visuals = (Visual *) NULL;
		}
	    }
	    sp->root_visual = _XVIDtoVisual(dpy, root_visualID);
	}

	if(usedbytes != setuplength){
	    /* Sanity check, shouldn't happen. */
	    fprintf(stderr, "Xlib: Did not parse entire setup message: "
	                    "parsed: %ld, message: %ld\n",
		    usedbytes, setuplength);
	    OutOfMemory(dpy);
	    return(NULL);
	}

/*
 * Now start talking to the server to setup all other information...
 */

/*
 * Make sure default screen is legal.
 */
	if (iscreen >= dpy->nscreens) {
	    OutOfMemory(dpy);
	    return(NULL);
	}

/*
 * get availability of large requests
 */
	dpy->bigreq_size = xcb_get_maximum_request_length(dpy->xcb->connection);
	if(dpy->bigreq_size <= dpy->max_request_size)
		dpy->bigreq_size = 0;

/*
 * Set up other stuff clients are always going to use.
 */
	for (i = 0; i < dpy->nscreens; i++) {
	    register Screen *sp = &dpy->screens[i];
	    XGCValues values;
	    values.foreground = sp->black_pixel;
	    values.background = sp->white_pixel;
	    if ((sp->default_gc = XCreateGC (dpy, sp->root,
					     GCForeground|GCBackground,
					     &values)) == NULL) {
		OutOfMemory(dpy);
		return (NULL);
	    }
	}
/*
 * call into synchronization routine so that all programs can be
 * forced synchronous
 */
	(void) XSynchronize(dpy, _Xdebug);

/*
 * get the resource manager database off the root window.
 */
	LockDisplay(dpy);
	{
	    xGetPropertyReply reply;
	    xGetPropertyReq *req;

	    GetReq (GetProperty, req);
	    req->window = RootWindow(dpy, 0);
	    req->property = XA_RESOURCE_MANAGER;
	    req->type = XA_STRING;
	    req->delete = False;
	    req->longOffset = 0;
	    req->longLength = 100000000L;

	    if (_XReply (dpy, (xReply *) &reply, 0, xFalse)) {
		if (reply.format == 8 && reply.propertyType == XA_STRING &&
		    (reply.nItems + 1 > 0) &&
		    (reply.nItems <= req->longLength * 4) &&
		    (dpy->xdefaults = Xmalloc (reply.nItems + 1))) {
		    _XReadPad (dpy, dpy->xdefaults, reply.nItems);
		    dpy->xdefaults[reply.nItems] = '\0';
		}
		else if (reply.propertyType != None)
		    _XEatDataWords(dpy, reply.length);
	    }
	}
	UnlockDisplay(dpy);

#ifdef MOTIFBC
	{
	    extern Display *_XHeadOfDisplayList;
	    _XHeadOfDisplayList = dpy;
	}
#endif
#ifdef XKB
	XkbUseExtension(dpy,NULL,NULL);
#endif
/*
 * and return successfully
 */
 	return(dpy);
}

  • XCloseDisplay源码

XCloseDisplay函数对应的源码在src/ClDisplay.c中。如下所示:

/*
 * XCloseDisplay - XSync the connection to the X Server, close the connection,
 * and free all associated storage.  Extension close procs should only free
 * memory and must be careful about the types of requests they generate.
 */

int
XCloseDisplay (
	register Display *dpy)
{
	register _XExtension *ext;
	register int i;

	if (!(dpy->flags & XlibDisplayClosing))
	{
	    dpy->flags |= XlibDisplayClosing;
	    for (i = 0; i < dpy->nscreens; i++) {
		    register Screen *sp = &dpy->screens[i];
		    XFreeGC (dpy, sp->default_gc);
	    }
	    if (dpy->cursor_font != None) {
		XUnloadFont (dpy, dpy->cursor_font);
	    }
	    XSync(dpy, 1);  /* throw away pending events, catch errors */
	    /* call out to any extensions interested */
	    for (ext = dpy->ext_procs; ext; ext = ext->next) {
		if (ext->close_display)
		    (*ext->close_display)(dpy, &ext->codes);
	    }
	    /* if the closes generated more protocol, sync them up */
	    if (X_DPY_GET_REQUEST(dpy) != X_DPY_GET_LAST_REQUEST_READ(dpy))
		XSync(dpy, 1);
	}
	xcb_disconnect(dpy->xcb->connection);
	_XFreeDisplayStructure (dpy);
	return 0;
}

由于源码已经比较长了,为了方便理解和阅读,把余下的函数放到下一篇中。