Split xcbXorg into xcbXorg and xcbWindow
This commit is contained in:
@@ -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(),
|
||||
[¶mName](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;
|
||||
}
|
||||
Reference in New Issue
Block a user