Как мне получить доступ к общей памяти с помощью xcb

Я пытаюсь сделать снимок экрана части окна, используя xcb_shm_get_image_unchecked. Я создал общую память, используя следующий код:

#include <cstdlib>
#include <memory>

#include <sys/shm.h>
#include <xcb/shm.h>
#include <xcb/xcb.h>
#include <xcb/xcb_image.h>
#include <xcb/xcb_pixel.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
{
  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());
  // xcb_shm_get_im
  auto shm_reply = xcb_shm_create_segment_reply(
    c.get(), xcb_shm_create_segment(c.get(), shmseg, SHM_SIZE, 0), 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);
  spdlog::info("found {} fds", shm_reply->nfd);
  for (int i = 0; i < shm_reply->nfd; i++) {
    auto err = xcb_request_check(
      c.get(), xcb_shm_attach_fd(c.get(), shmseg, fds[i], true));
    if (err) {
      spdlog::error("failed to attach fd: {}", fds[i]);
      delete err;
    }
  }

  for (int i = 0; i < shm_reply->nfd; i++) {
      close(fds[i]);
  }
  xcb_shm_detach(c.get(), shmseg);
  return 0;
}

Первый вопрос: правильно ли я сделал с точки зрения освобождения ресурсов?

2-й: Как мне получить доступ к сегменту общей памяти?

Наконец, как мне сделать снимок экрана корневого окна размером 640x480? Я на правильном пути с этим:

auto image = xcb_shm_get_image_reply(
  c.get(),
  xcb_shm_get_image_unchecked(c.get(),
                              root,
                              0,
                              0,
                              IMAGE_WIDTH,
                              IMAGE_HEIGHT,
                              XCB_GC_PLANE_MASK,
                              XCB_IMAGE_FORMAT_Z_PIXMAP,
                              shmseg,
                              0),
  nullptr);

if (!image) {
  spdlog::error("failed to get image");
  xcb_shm_detach(c.get(), shmseg);
  return EXIT_FAILURE;
}

spdlog::info("image: size {}", image->size);
delete image;

Перекрестная ссылка, которая может быть полезной, а может и нет (похоже, речь идет о тесно связанной проблеме с похожим кодом и даже может быть написана тем же автором): gitlab.freedesktop.org/xorg/lib/libxcb/-/issues/ 80

Uli Schlachter 26.04.2024 13:12
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
60
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам нужно вызвать на mmap, чтобы получить доступ к общей памяти. Из документации для xcb_shm_create_segment_unchecked:

Запрашивает сервер выделить сегмент общей памяти. Ответ сервера будет включать дескриптор файла, который клиент должен передать в mmap().

И как получить доступ?

Вам нужно вызвать mmap, включая дескриптор файла, чтобы сообщить mmap, где найти область памяти, выделенную X сервером.

Вы можете сделать это, используя что-то вроде этого:

auto *shmem = mmap(nullptr, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fds[0], 0);

if (shmem == MAP_FAILED) {
  ... // cleanup
}

close(fds[0]);

Аргументы должны быть понятны, как только вы прочтете документацию по mmap. Последний 0 просто означает, что мы хотим, чтобы указатель ссылался на начало области памяти.

Конечно, не забудьте поставить munmap, когда закончите:

munmap(p, SHM_SIZE);

Наконец, xcb_shm_attach_fd(c.get(), shmseg, fds[i], true)); не нужен. Это необходимо только в том случае, если вы сами создали файл mmap и передали его X-серверу, чтобы он мог получить доступ.

Первый вопрос: правильно ли я сделал с точки зрения освобождения ресурсов?

да у тебя есть

как мне сделать снимок экрана корневого окна размером 640x480?

Код, который у вас есть для получения изображения, почти правильный. Просто измените аргумент plane_mask на ~0. Вы можете видеть, что это похоже на то, как cairoэто делает , а также на то, как obs это делает.

Чтобы фактически преобразовать байты в изображение, вы можете преобразовать его в формат изображения с наименьшим общим знаменателем, например PPM. Вот как может выглядеть этот код:

if (auto ppmFile = std::ofstream("/path/to/screenshot.ppm", std::ios::binary); ppmFile) {
  ppmFile << "P6\n" << IMAGE_WIDTH << " " << IMAGE_HEIGHT << "\n255\n";
  const auto *const imageData = shmem;
  for (uint32_t rgb = 0; rgb + 4 <= image->size; rgb += 4) {
    auto b = imageData[rgb];
    auto g = imageData[rgb + 1];
    auto r = imageData[rgb + 2];
    if (setup->image_byte_order == XCB_IMAGE_ORDER_MSB_FIRST) {
      std::swap(b, r);
    }
    ppmFile << r << g << b;
  }
}

Конечным результатом должно быть изображение в формате ppm, расположенное по адресу /path/to/screenshot.ppm. Если у вас нет возможности просмотреть его, imagemagick может конвертировать ppm в png, и вы можете просмотреть его таким образом.

Другие вопросы по теме