You can take single- and multi-monitor screenshots on Linux using various tools. However, if you need to implement this feature in your product, the best solution is to write your own code from scratch.
In this article, we describe the process of capturing screenshots for single-monitor and multi-monitor setups. For these purposes, we use two implementations of X.Org clients: the Xlib and XCB libraries. We also explain how to save screenshots in the BMP file format.
Screenshots can be taken for various reasons. For instance, employee monitoring tools often take screenshots to record activity and detect violations like accessing forbidden websites and compromising sensitive data.
And since lots of employees use multiple monitors for convenience and efficiency, it’s necessary to know how to take multi-monitor screenshots.
In this article, we show two comprehensive approaches to capturing multi-monitor screenshots on Linux. Each step is described in detail and illustrated with example code. At the end of the article, you’ll get ready-to-go code for capturing screenshots.
Before we proceed to taking multi-monitor screenshots, let’s define the terms we’ll use.
- X.Org Server — An open-source display server implementation for the X Window System provided by the X.Org Foundation.
- Xlib — A library created for interacting with an X server that allows programmers to write software without needing to deal with hardware details. Xlib serves as an interface between a programming language and graphics card and monitor hardware.
- XCB — A library distributed under the MIT License that was designed as an advanced replacement for Xlib. XCB aims to reduce the library size and complexity and provide direct access to the X11 protocol.
Let’s start with capturing a multi-monitor screenshot using the Xlib library.
The Xlib library offers a number of features, including the possibility to take a screenshot of the entire desktop. Here’s the code for the XGetImage function:
To take a screenshot, we need to perform the following actions:
- Establish a connection to the X.Org Server
- Get the root window to take a screenshot of it
- Get the bounding box of the root window
- Call the XGetImage function
But first, we need to include the Xlib header in our code so we can use the Xlib library’s functionality:
After that, we can establish a connection to the X.Org Server and get the root window:
The next step is to determine the width and height of the root window. To do so, we use the XGetWindowAttributes function:
Now we have all the information needed to call the XGetImage function:
Note: To avoid a memory leak, the received instance of the XImage structure should be freed using the XDestroyImage function. Also, at the end of the program, you should disconnect the display server using the XCloseDisplay function.
To run our program and check its performance, we need to build an executable using the following command:
Now that we’ve learned how to take screenshots using the Xlib library, let’s explore the same process using the XCB library.
XCB, like its older brother Xlib, has a function for getting the screen buffer. It’s called xcb_get_image and looks like this:
In contrast to the Xlib implementation, this function simply generates a request to the server, which should be sent using the xcb_get_image_reply function:
This request–reply mechanism is a key feature of XCB that makes it a bit more powerful and faster than Xlib. The point is that such requests can be reused, which decreases memory consumption and allows for faster performance, since we don’t waste time allocating memory when reusing already generated requests.
Again, we need to take several preparatory steps before actually taking a screenshot. These steps, however, are pretty much the same as with Xlib.
First, we need to include the XCB header in our code to use the functionality of the XCB library:
After that, we need to set up a connection to the X.Org Server and get the root window of the screen:
Now we can determine the size of the root window. In XCB, we use a function called xcb_get_geometry to do that.
Just as XCB has the xcb_get_image function, it also has a function with the _reply suffix that generates and sends requests to the server. You can also use the smart pointer std::shared_ptr to automatically free the memory allocated for XCB replies:
Once we’ve performed all these steps, it’s time to take a screenshot:
To access the RGBA data of the image, use the xcb_get_image_data function:
Note: To prevent memory leaks, you should close the display connection using the xcb_disconnect function at the end of your program.
To build an executable from the source code shown above, you can use the following command:
Now let’s explore how to save images in the BMP file format.
Now that you have the RGBA buffer, you can save it to the file system and open it with any image viewer. To do that, you can use a simple file format like BitMap Picture, or BMP. The structure of the BMP file format is shown in the image below:
The image below shows the Bitmap file header structure:
The image below shows the DIB header structure:
To save the raw pixel buffers, we need to implement the following structures first:
Then we need to implement the PixelsToBitmap function that will convert the raw pixels buffer to the bitmap buffer:
Our last step is to call the final function, PixelsToBitmap, which will generate the bitmap buffer that we’ll write to the file.
First, let’s take a look at an example with Xlib:
Now it’s time to explore the same operation with the XCB library:
The final code is ready and can be used to save screenshots in the BMP file format.
Here’s an example of a screenshot taken with XCB:
In this article, we showed how to capture screenshots using the Xlib and XCB libraries. We also showed how to save these screenshots in the BMP image format.
You can use the example code shown in this article for capturing screenshots in your own product.
At Apriorit, we have experienced teams of dedicated software developers that are ready to help you build a product of any complexity. Contact us if you’re ready to bring your ideas to life.