📄 kinect2-sys/csrc/shim.cpp
// A minimal C ABI over the parts of libfreenect2 the kinect-view
// capture pipeline needs. C++ exceptions are caught and reported
// through a thread-local buffer, fetched with lf2_take_error.
//
// Every lf2_frame borrows or owns a libfreenect2 Frame via the opaque
// `frame` pointer. Listener views are valid until lf2_release; frames
// from lf2_frame_new are valid until lf2_frame_free.

#include <libfreenect2/libfreenect2.hpp>
#include <libfreenect2/frame_listener.hpp>
#include <libfreenect2/frame_listener_impl.h>
#include <libfreenect2/packet_pipeline.h>
#include <libfreenect2/registration.h>

#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <exception>
#include <new>
#include <string>

typedef struct lf2_frame
{
  void *frame;             // opaque libfreenect2::Frame*
  size_t width;
  size_t height;
  size_t bytes_per_pixel;
  unsigned char *data;
  int32_t format;          // libfreenect2::Frame::Format
  uint32_t timestamp;
} lf2_frame;

typedef struct lf2_ir_params
{
  float fx, fy, cx, cy;
  float k1, k2, k3, p1, p2;
} lf2_ir_params;

// Keep the Rust side honest about the ABI.
static_assert(offsetof(lf2_frame, frame) == 0, "lf2_frame layout");
static_assert(offsetof(lf2_frame, width) == sizeof(void *), "lf2_frame layout");
static_assert(offsetof(lf2_ir_params, fx) == 0, "lf2_ir_params layout");

static thread_local std::string lf2_error;
static thread_local std::string lf2_error_out;

static void lf2_set_error(const char *message)
{
  lf2_error = message;
}

// Keep the C++ helper and the extern "C" export distinct.

// The library-wide device enumerator (its USB context is a global
// singleton internally, so one static instance is enough).
static libfreenect2::Freenect2 &lf2_context()
{
  static libfreenect2::Freenect2 context;
  return context;
}

static void lf2_fill_view(lf2_frame *view, const libfreenect2::Frame *frame)
{
  view->frame = const_cast<void *>(static_cast<const void *>(frame));
  view->width = frame->width;
  view->height = frame->height;
  view->bytes_per_pixel = frame->bytes_per_pixel;
  view->data = frame->data;
  view->format = static_cast<int32_t>(frame->format);
  view->timestamp = frame->timestamp;
}

struct lf2_listener
{
  libfreenect2::SyncMultiFrameListener listener;
  libfreenect2::FrameMap frames;

  lf2_listener() : listener(libfreenect2::Frame::Color | libfreenect2::Frame::Depth) {}
};

struct lf2_registration
{
  libfreenect2::Registration registration;
  lf2_registration(const libfreenect2::Freenect2Device::IrCameraParams &depth,
                   const libfreenect2::Freenect2Device::ColorCameraParams &color)
      : registration(depth, color) {}
};

extern "C"
{

const char *lf2_take_error(void)
{
  lf2_error_out = lf2_error;
  lf2_error.clear();
  return lf2_error_out.c_str();
}

int lf2_count_devices(void)
{
  try
  {
    return static_cast<int>(lf2_context().enumerateDevices());
  }
  catch (const std::exception &e)
  {
    lf2_set_error(e.what());
    return -1;
  }
  catch (...)
  {
    lf2_set_error("unknown failure enumerating devices");
    return -1;
  }
}

void *lf2_open_default(void)
{
  try
  {
    // Explicit CPU depth processing: deterministic, no OpenCL/CUDA.
    libfreenect2::Freenect2Device *device =
        lf2_context().openDevice(0, new libfreenect2::CpuPacketPipeline());
    if (device == nullptr)
      lf2_set_error("no Kinect v2 device found — is it plugged into a USB 3.0 port?");
    return device;
  }
  catch (const std::exception &e)
  {
    lf2_set_error(e.what());
    return nullptr;
  }
  catch (...)
  {
    lf2_set_error("unknown failure opening the device");
    return nullptr;
  }
}

void lf2_device_close(void *device)
{
  try
  {
    auto *dev = static_cast<libfreenect2::Freenect2Device *>(device);
    if (dev == nullptr)
      return;
    dev->stop();
    dev->close();
    // Upstream usage (Protonect) does not delete the device handle
    // after close(); match it.
  }
  catch (...)
  {
    // Closing must not throw across the FFI boundary.
  }
}

int lf2_start(void *device)
{
  try
  {
    static_cast<libfreenect2::Freenect2Device *>(device)->start();
    return 0;
  }
  catch (const std::exception &e)
  {
    lf2_set_error(e.what());
    return -1;
  }
  catch (...)
  {
    lf2_set_error("unknown failure starting the device");
    return -1;
  }
}

int lf2_stop(void *device)
{
  try
  {
    static_cast<libfreenect2::Freenect2Device *>(device)->stop();
    return 0;
  }
  catch (const std::exception &e)
  {
    lf2_set_error(e.what());
    return -1;
  }
  catch (...)
  {
    lf2_set_error("unknown failure stopping the device");
    return -1;
  }
}

void lf2_get_ir_params(void *device, lf2_ir_params *out)
{
  // Trivially-copyable struct; cannot throw.
  libfreenect2::Freenect2Device::IrCameraParams params =
      static_cast<libfreenect2::Freenect2Device *>(device)->getIrCameraParams();
  out->fx = params.fx;
  out->fy = params.fy;
  out->cx = params.cx;
  out->cy = params.cy;
  out->k1 = params.k1;
  out->k2 = params.k2;
  out->k3 = params.k3;
  out->p1 = params.p1;
  out->p2 = params.p2;
}

void lf2_get_serial(void *device, char *buf, size_t len)
{
  try
  {
    std::snprintf(buf, len, "%s",
                  static_cast<libfreenect2::Freenect2Device *>(device)->getSerialNumber().c_str());
  }
  catch (...)
  {
    if (len > 0)
      buf[0] = '\0';
  }
}

lf2_frame *lf2_frame_new(int32_t width, int32_t height, int32_t bytes_per_pixel)
{
  try
  {
    auto *frame = new libfreenect2::Frame(width, height, bytes_per_pixel);
    auto *view = new lf2_frame;
    lf2_fill_view(view, frame);
    return view;
  }
  catch (const std::exception &e)
  {
    lf2_set_error(e.what());
    return nullptr;
  }
  catch (...)
  {
    lf2_set_error("unknown failure allocating a frame");
    return nullptr;
  }
}

void lf2_frame_free(lf2_frame *view)
{
  if (view == nullptr)
    return;
  delete static_cast<libfreenect2::Frame *>(view->frame);
  delete view;
}

void *lf2_listener_new(void)
{
  try
  {
    return new lf2_listener;
  }
  catch (const std::exception &e)
  {
    lf2_set_error(e.what());
    return nullptr;
  }
  catch (...)
  {
    lf2_set_error("unknown failure creating the listener");
    return nullptr;
  }
}

void lf2_listener_free(void *listener)
{
  delete static_cast<lf2_listener *>(listener);
}

int lf2_listener_attach(void *device, void *listener)
{
  try
  {
    auto *dev = static_cast<libfreenect2::Freenect2Device *>(device);
    auto *l = static_cast<lf2_listener *>(listener);
    dev->setColorFrameListener(&l->listener);
    dev->setIrAndDepthFrameListener(&l->listener);
    return 0;
  }
  catch (const std::exception &e)
  {
    lf2_set_error(e.what());
    return -1;
  }
  catch (...)
  {
    lf2_set_error("unknown failure attaching the listener");
    return -1;
  }
}

int lf2_wait_for_frame(void *listener, int32_t timeout_ms, lf2_frame *out_color, lf2_frame *out_depth)
{
  try
  {
    auto *l = static_cast<lf2_listener *>(listener);
    std::memset(&out_color->frame, 0, sizeof(lf2_frame) - 0);
    if (!l->listener.waitForNewFrame(l->frames, timeout_ms))
      return 0;
    auto *color = l->frames[libfreenect2::Frame::Color];
    auto *depth = l->frames[libfreenect2::Frame::Depth];
    lf2_fill_view(out_color, color);
    lf2_fill_view(out_depth, depth);
    return 1;
  }
  catch (const std::exception &e)
  {
    lf2_set_error(e.what());
    return -1;
  }
  catch (...)
  {
    lf2_set_error("unknown failure waiting for frames");
    return -1;
  }
}

void lf2_release(void *listener)
{
  static_cast<lf2_listener *>(listener)->listener.release(
      static_cast<lf2_listener *>(listener)->frames);
}

void *lf2_registration_new(void *device)
{
  try
  {
    auto *dev = static_cast<libfreenect2::Freenect2Device *>(device);
    return new lf2_registration(dev->getIrCameraParams(), dev->getColorCameraParams());
  }
  catch (const std::exception &e)
  {
    lf2_set_error(e.what());
    return nullptr;
  }
  catch (...)
  {
    lf2_set_error("unknown failure creating the registration");
    return nullptr;
  }
}

void lf2_registration_free(void *registration)
{
  delete static_cast<lf2_registration *>(registration);
}

int lf2_register(void *registration, lf2_frame *color, lf2_frame *depth,
                  lf2_frame *out_undistorted, lf2_frame *out_registered)
{
  try
  {
    auto *reg = static_cast<lf2_registration *>(registration);
    auto *rgb_frame = static_cast<libfreenect2::Frame *>(color->frame);
    auto *depth_frame = static_cast<libfreenect2::Frame *>(depth->frame);
    auto *undistorted = static_cast<libfreenect2::Frame *>(out_undistorted->frame);
    auto *registered = static_cast<libfreenect2::Frame *>(out_registered->frame);
    reg->registration.apply(rgb_frame, depth_frame, undistorted, registered, true);
    // Refresh the output views (data pointers stay put; formats may be
    // informative).
    lf2_fill_view(out_undistorted, undistorted);
    lf2_fill_view(out_registered, registered);
    return 0;
  }
  catch (const std::exception &e)
  {
    lf2_set_error(e.what());
    return -1;
  }
  catch (...)
  {
    lf2_set_error("unknown failure registering frames");
    return -1;
  }
}

} // extern "C"