PPM image from xcb_shm_get_image looks strange
I'm writing code to grab a screenshot of a portion of the root window. Here is my code so far:
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <memory>
#include <sys/mman.h>
#include <xcb/shm.h>
#include <xcb/xcb.h>
#include <xcb/xproto.h>
#include <spdlog/spdlog.h>
const auto IMAGE_WIDTH = 640;
const auto IMAGE_HEIGHT = 480;
const auto SHM_SIZE = 4 * 1024 * 1024;
auto main() -> int
{
/* XCB CONNECTION STUFF */
std::unique_ptr<xcb_connection_t, decltype(&xcb_disconnect)> c(
xcb_connect(nullptr, nullptr), &xcb_disconnect);
if (!c) {
spdlog::error("failed to connect to X server");
return EXIT_FAILURE;
}
auto roots_iter = xcb_setup_roots_iterator(xcb_get_setup(c.get()));
if (roots_iter.rem == 0) {
spdlog::error("no screen found");
return EXIT_FAILURE;
}
auto root = roots_iter.data->root;
xcb_shm_seg_t shmseg = xcb_generate_id(c.get());
/* SETUP SHARED MEMORY */
auto shm_reply = std::unique_ptr<xcb_shm_create_segment_reply_t>(
xcb_shm_create_segment_reply(
c.get(),
xcb_shm_create_segment_unchecked(c.get(), shmseg, SHM_SIZE, false),
nullptr));
if (!shm_reply) {
spdlog::error("failed to create shared memory segment");
return EXIT_FAILURE;
}
auto fds = xcb_shm_create_segment_reply_fds(c.get(), shm_reply.get());
if (shm_reply->nfd != 1) {
for (int i = 0; i < shm_reply->nfd; i++) {
close(fds[i]);
}
spdlog::error("failed to create shared memory segment");
std::ignore = shm_reply.release();
return EXIT_FAILURE;
}
/* MAP SHARED MEMORY */
std::shared_ptr<uint8_t> data(
static_cast<uint8_t *>(
mmap(nullptr, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fds[0], 0)),
[](uint8_t *p) {
if (p != MAP_FAILED) {
munmap(p, SHM_SIZE);
}
});
if (data.get() == MAP_FAILED) {
spdlog::error("failed to map shared memory segment");
close(fds[0]);
xcb_shm_detach(c.get(), shmseg);
std::ignore = shm_reply.release();
return EXIT_FAILURE;
}
std::memset(data.get(), 0, SHM_SIZE);
close(fds[0]);
std::ignore = shm_reply.release();
/* GET IMAGE OF ROOT WINDOW AND STORE IT IN SHARED MEMORY */
std::shared_ptr<xcb_shm_get_image_reply_t> image(xcb_shm_get_image_reply(
c.get(),
xcb_shm_get_image_unchecked(c.get(),
root,
0,
0,
IMAGE_WIDTH,
IMAGE_HEIGHT,
~0,
XCB_IMAGE_FORMAT_Z_PIXMAP,
shmseg,
0),
nullptr));
if (image) {
auto f = std::fopen("/tmp/screenshot.ppm", "wb");
if (f) {
/* WRITE IMAGE TO FILE IN PPM FORMAT */
std::fprintf(f, "P6\n%d %d\n255\n", IMAGE_WIDTH, IMAGE_HEIGHT);
std::fwrite(data.get(), 1, image->size, f);
std::fclose(f);
} else {
spdlog::error("failed to open file");
}
} else {
spdlog::error("failed to get image");
}
xcb_shm_detach(c.get(), shmseg);
return 0;
}
So far, I'm able to grab the image but it looks strange to me. Here is a sample (converted to png for easier viewing):
It looks like it's repeating some parts, and there doesn't seem to be any color information associated with the image. Am I supposed to read it twice? Once with XCB_IMAGE_FORMAT_Z_PIXMAP
and another with XCB_IMAGE_FORMAT_XY_PIXMAP
format?
Does anyone have an idea how I can accomplish?
Thanks
Original ppm: screenshot.ppm
Edited by Chigozirim Chukwu