Split xcbXorg into xcbXorg and xcbWindow

This commit is contained in:
2025-07-24 06:00:35 -04:00
parent 1bf5f46404
commit 1e17b83061
6 changed files with 695 additions and 446 deletions
+15 -10
View File
@@ -1,22 +1,27 @@
# XCB/Xorg Window Attaching SenseAPI backend
cmake_dependent_option(ENABLE_SENSEAPI_xcbWindow
"Enable XCB/Xorg Window Attaching SenseAPI backend" OFF
"ENABLE_LIB_xcbXorg" OFF)
"Enable XCB/Xorg Window Attaching SenseAPI backend" ON
"ENABLE_LIB_xcbXorg" ON)
if(ENABLE_SENSEAPI_xcbWindow)
# For now, just create a placeholder library
add_library(senseApiXcbWindow STATIC
# xcbWindow.cpp would go here
)
target_link_libraries(senseApiXcbWindow
xcbXorg
add_library(senseApiXcbWindow SHARED
xcbWindow.cpp
)
target_include_directories(senseApiXcbWindow PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/../../include
${CMAKE_CURRENT_SOURCE_DIR}/../../smocore/include
${CMAKE_CURRENT_SOURCE_DIR}/../../commonLibs
)
# Link against XCB library directly (libxcbXorg will be loaded dynamically)
pkg_check_modules(XCB REQUIRED xcb)
target_link_libraries(senseApiXcbWindow ${XCB_LIBRARIES})
# Set config define for header generation
add_compile_definitions(CONFIG_SENSEAPI_XCBWINDOW_ENABLED)
# Install rules
install(TARGETS senseApiXcbWindow DESTINATION lib)
endif()
+330
View File
@@ -0,0 +1,330 @@
#include <iostream>
#include <algorithm>
#include <stdexcept>
#include <memory>
#include <vector>
#include <sstream>
#include <dlfcn.h>
#include <xcb/xcb.h>
#include <user/senseApiDesc.h>
#include <user/senseDeviceSpec.h>
#include "xcbWindow.h"
#include "../../commonLibs/xcbXorg/xcbXorg.h"
// Function pointers to API entry points exported by libxcbXorg.
struct XcbXorgFunctions {
get_or_create_connection_fn* getOrCreateConnection = nullptr;
cleanup_connections_fn* cleanupConnections = nullptr;
find_window_by_id_fn* findWindowById = nullptr;
find_window_by_name_fn* findWindowByName = nullptr;
};
static void* g_xcbXorgHandle = nullptr;
static XcbXorgFunctions xcbXorgFns;
// Salmanoff hooks, obtained from SMO_GET_SENSE_API_DESC_FN_NAME().
static const smo::sense_api::SalmanoffCallbacks* smoHooksPtr = nullptr;
// Attached windows.
static std::vector<std::unique_ptr<xcb_window::AttachedWindow>>
g_attachedWindows;
namespace xcb_window {
std::string WindowSelector::stringify() const
{
std::ostringstream os;
os << "Display: " << display
<< ", Screen: " << screen << ", Window: ";
if (matchType == xcb_xorg::window_search::MatchType::ID) {
os << windowId;
} else {
os << "\"" << windowName << "\"";
}
os << " (matchType="
<< (matchType == xcb_xorg::window_search::MatchType::EXACT ? "exact" :
(matchType == xcb_xorg::window_search::MatchType::SUBSTRING) ? "substring" : "id")
<< ")";
return os.str();
}
AttachedWindow::AttachedWindow(const smo::device::SenseDeviceSpec& spec)
: deviceSpec(spec), xcbConnection(nullptr)
{
// Validate required function pointers are available
if (!xcbXorgFns.getOrCreateConnection ||
!xcbXorgFns.findWindowById || !xcbXorgFns.findWindowByName)
{
throw std::runtime_error("xcbWindow:" + std::string(__func__) +
": Required xcbXorg function pointers not available");
}
windowSelector.display = getRequiredParamAsInt(spec, "display");
windowSelector.screen = getRequiredParamAsInt(spec, "screen");
parseWindowSelector(spec);
// Get connection from libxcbXorg
std::shared_ptr<xcb_xorg::XcbConnection> conn =
(*xcbXorgFns.getOrCreateConnection)(
windowSelector.display, windowSelector.screen);
xcbConnection = conn.get();
// Find the target window
xcb_window_t foundWindow = 0;
const xcb_setup_t* setup = xcb_get_setup(conn->getConnection());
const xcb_screen_t* screen = xcb_setup_roots_iterator(setup).data;
if (windowSelector.matchType == xcb_xorg::window_search::MatchType::ID)
{
foundWindow = (*xcbXorgFns.findWindowById)(
xcbConnection, screen->root,
windowSelector.windowId);
}
else
{
foundWindow = (*xcbXorgFns.findWindowByName)(
xcbConnection, screen->root,
windowSelector.windowName, actualWindowName,
windowSelector.matchType);
}
if (!foundWindow)
{
throw std::runtime_error("xcbWindow:" + std::string(__func__) +
": Window not found: " + windowSelector.stringify());
}
}
void AttachedWindow::parseWindowSelector(
const smo::device::SenseDeviceSpec& spec
)
{
// Default match type
windowSelector.matchType = xcb_xorg::window_search::MatchType::SUBSTRING;
// Check if 'dev-id', 'dev-string', or 'dev-substring' is specified
for (const auto& param : spec.apiParams)
{
if (param.first == "dev-id" || param.first == "devid")
{
windowSelector.matchType = xcb_xorg::window_search::MatchType::ID;
break;
}
if (param.first == "dev-string" || param.first == "dev-str"
|| param.first == "devstr" || param.first == "devstring")
{
windowSelector.matchType = xcb_xorg::window_search::MatchType::EXACT;
}
if (param.first == "dev-substring" || param.first == "dev-substr"
|| param.first == "devsubstr" || param.first == "devsubstring")
{
windowSelector.matchType = xcb_xorg::window_search::MatchType::SUBSTRING;
}
}
if (windowSelector.matchType == xcb_xorg::window_search::MatchType::ID)
{
try {
windowSelector.windowId = std::stoul(
spec.deviceSelector, nullptr, 0);
} catch (const std::exception&) {
throw std::runtime_error(
"xcbWindow:" + std::string(__func__) + ": Window selector: "
"'dev-id' present, but selector is not numeric");
}
}
else {
windowSelector.windowName = spec.deviceSelector;
}
}
int AttachedWindow::getRequiredParamAsInt(const smo::device::SenseDeviceSpec& spec,
const std::string& paramName)
{
auto it = std::find_if(
spec.providerParams.begin(),
spec.providerParams.end(),
[&paramName](const auto& param) {
return param.first == paramName;
}
);
if (it == spec.providerParams.end())
{
throw std::runtime_error(
"No " + paramName + " specified in provider params");
}
try {
return std::stoi(it->second);
} catch (const std::exception& e) {
throw std::runtime_error(
"Failed to parse '" + paramName + "' param value '"
+ it->second + "' as integer: " + e.what());
}
}
std::string AttachedWindow::stringify() const {
std::ostringstream os;
auto matchTypeStr = [](xcb_xorg::window_search::MatchType mt) -> const char* {
switch (mt) {
case xcb_xorg::window_search::MatchType::SUBSTRING:
return "substring";
case xcb_xorg::window_search::MatchType::EXACT:
return "exact";
case xcb_xorg::window_search::MatchType::ID:
return "id";
default:
return "unknown";
}
};
auto formatWindowId = [](uint32_t id) -> std::string {
std::ostringstream oss;
oss << "Window ID 0x" << std::hex << id << std::dec;
return oss.str();
};
os << "Display: " << windowSelector.display
<< ", Screen: " << windowSelector.screen
<< ", MatchType: " << matchTypeStr(windowSelector.matchType)
<< ", Target: ";
if (windowSelector.matchType == xcb_xorg::window_search::MatchType::ID) {
os << formatWindowId(windowSelector.windowId);
} else {
os << '"' << windowSelector.windowName << '"';
}
os << ", Found: ";
if (windowSelector.matchType == xcb_xorg::window_search::MatchType::ID) {
os << formatWindowId(windowSelector.windowId);
} else {
os << '"' << actualWindowName << '"';
if (windowSelector.matchType ==
xcb_xorg::window_search::MatchType::SUBSTRING &&
actualWindowName != windowSelector.windowName)
{
os << " (matched substring '"
<< windowSelector.windowName << "')";
}
}
return os.str();
}
} // namespace xcb_window
// SenseApi functions
static int xcbWindow_initializeInd(void)
{
if (!smoHooksPtr)
{
throw std::runtime_error(std::string(__func__) + ": SMO hooks "
"pointers not filled in.");
}
// Try to load libxcbXorg using the search path hook
auto libPath = smoHooksPtr->searchForLibInSmoSearchPaths("libxcbXorg.so");
g_xcbXorgHandle = dlopen(
libPath.value_or("libxcbXorg.so").c_str(),
RTLD_LAZY);
if (!g_xcbXorgHandle)
{
throw std::runtime_error("xcbWindow:" + std::string(__func__) +
": Failed to load libxcbXorg: " + std::string(dlerror()));
}
// Fill in function pointers from libxcbXorg.
xcbXorgFns.getOrCreateConnection =
reinterpret_cast<get_or_create_connection_fn*>(
dlsym(g_xcbXorgHandle, "xcb_xorg_get_or_create_connection"));
xcbXorgFns.cleanupConnections = reinterpret_cast<cleanup_connections_fn*>(
dlsym(g_xcbXorgHandle, "xcb_xorg_cleanup_connections"));
xcbXorgFns.findWindowById = reinterpret_cast<find_window_by_id_fn*>(
dlsym(g_xcbXorgHandle, "xcb_xorg_find_window_by_id"));
xcbXorgFns.findWindowByName = reinterpret_cast<find_window_by_name_fn*>(
dlsym(g_xcbXorgHandle, "xcb_xorg_find_window_by_name"));
if (!xcbXorgFns.getOrCreateConnection || !xcbXorgFns.cleanupConnections
|| !xcbXorgFns.findWindowById || !xcbXorgFns.findWindowByName)
{
throw std::runtime_error(
std::string("xcbWindow:") + __func__ +
": Failed to get required function pointers from libxcbXorg");
}
return 0;
}
static int xcbWindow_finalizeInd(void)
{
g_attachedWindows.clear();
if (g_xcbXorgHandle)
{
dlclose(g_xcbXorgHandle);
g_xcbXorgHandle = nullptr;
xcbXorgFns = { nullptr, nullptr, nullptr, nullptr };
}
return 0;
}
static int xcbWindow_attachDeviceReq(const smo::device::SenseDeviceSpec& desc)
{
g_attachedWindows.emplace_back(
std::make_unique<xcb_window::AttachedWindow>(desc));
std::cout << __func__ << ": Attached X11 window:\n "
<< g_attachedWindows.back()->stringify()
<< "\n";
return 0;
}
static int xcbWindow_detachDeviceReq(const smo::device::SenseDeviceSpec& spec)
{
auto it = std::find_if(g_attachedWindows.begin(), g_attachedWindows.end(),
[&spec](const std::unique_ptr<xcb_window::AttachedWindow>& window) {
return window->getDeviceSpec() == spec;
}
);
if (it != g_attachedWindows.end())
{
g_attachedWindows.erase(it);
std::cout << __func__ << ": Detached X11 window device\n";
return 0;
}
std::cerr << __func__ << ": Device not found for detachment\n";
return -1;
}
// SenseApi descriptor
static smo::sense_api::SenseApiDesc xcbWindowApiDesc = {
.name = "xcb",
.exportedImplexorApis = { { "video-implexor" } },
.sal_mgmt_libOps = {
.initializeInd = xcbWindow_initializeInd,
.finalizeInd = xcbWindow_finalizeInd,
.attachDeviceReq = xcbWindow_attachDeviceReq,
.detachDeviceReq = xcbWindow_detachDeviceReq
}
};
// Exported function
extern "C" smo::sense_api::SMO_GET_SENSE_API_DESC_FN_TYPEDEF
SMO_GET_SENSE_API_DESC_FN_NAME;
const smo::sense_api::SenseApiDesc& SMO_GET_SENSE_API_DESC_FN_NAME(
const smo::sense_api::SalmanoffCallbacks& callbacks)
{
smoHooksPtr = &callbacks;
return xcbWindowApiDesc;
}
+55
View File
@@ -0,0 +1,55 @@
#ifndef XCB_WINDOW_SENSE_API_H
#define XCB_WINDOW_SENSE_API_H
#include <string>
#include <vector>
#include <memory>
#include <user/senseApiDesc.h>
#include <user/senseDeviceSpec.h>
#include <xcbXorg/xcbXorg.h>
namespace xcb_window {
/**
* @brief Window selector for X11 windows
*/
struct WindowSelector
{
xcb_xorg::window_search::MatchType matchType;
int display;
int screen;
uint32_t windowId;
std::string windowName;
std::string stringify() const;
};
/**
* @brief Represents an attached X11 window device
*/
class AttachedWindow
{
public:
AttachedWindow(const smo::device::SenseDeviceSpec& spec);
~AttachedWindow() = default;
const smo::device::SenseDeviceSpec& getDeviceSpec() const { return deviceSpec; }
const WindowSelector& getWindowSelector() const { return windowSelector; }
const std::string& getActualWindowName() const { return actualWindowName; }
void* getXcbConnection() const { return xcbConnection; }
std::string stringify() const;
private:
void parseWindowSelector(const smo::device::SenseDeviceSpec& spec);
int getRequiredParamAsInt(const smo::device::SenseDeviceSpec& spec,
const std::string& paramName);
smo::device::SenseDeviceSpec deviceSpec;
WindowSelector windowSelector;
std::string actualWindowName;
void* xcbConnection; // Raw pointer to XCB connection from libxcbXorg
};
} // namespace xcb_window
#endif // XCB_WINDOW_SENSE_API_H