Split xcbXorg into xcbXorg and xcbWindow
This commit is contained in:
+121
-415
@@ -6,329 +6,91 @@
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
#include <user/senseDeviceSpec.h>
|
||||
#include <user/senseApiDesc.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include "xcbXorg.h"
|
||||
|
||||
/**
|
||||
* @brief Manages X server connections using XCB
|
||||
*
|
||||
* This struct manages connections to the X server using the XCB library. It
|
||||
* ensures that each unique display and screen combination has a single
|
||||
* connection, and provides RAII management for these connections.
|
||||
*/
|
||||
struct XcbConnection
|
||||
namespace xcb_xorg {
|
||||
|
||||
// Static member initialization
|
||||
std::map<ConnectionIdentifier, std::shared_ptr<XcbConnection>>
|
||||
ConnectionManager::connections;
|
||||
|
||||
std::string ConnectionIdentifier::stringify() const
|
||||
{
|
||||
struct XConnectionIdentifier
|
||||
{
|
||||
int display;
|
||||
int screen;
|
||||
|
||||
bool operator<(const XConnectionIdentifier& other) const
|
||||
{
|
||||
if (display != other.display) return display < other.display;
|
||||
return screen < other.screen;
|
||||
}
|
||||
|
||||
std::string stringify() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "display=" << display << ", screen=" << screen;
|
||||
return os.str();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief RAII guard for managing X server connection lifetime
|
||||
*
|
||||
* This guard ensures proper cleanup of X server connections in case of
|
||||
* errors during attachDeviceReq. If a connection is created but the device
|
||||
* attachment fails, and the connection's refcount is 0, this guard will
|
||||
* automatically remove the connection from the connections map.
|
||||
*
|
||||
* The guard can be "committed" using commit() to indicate successful
|
||||
* device attachment, in which case it will not perform cleanup on destruction.
|
||||
*/
|
||||
class ConnectionGuard
|
||||
{
|
||||
std::shared_ptr<XcbConnection> conn;
|
||||
bool committed = false;
|
||||
|
||||
public:
|
||||
explicit ConnectionGuard(std::shared_ptr<XcbConnection> c)
|
||||
: conn(std::move(c))
|
||||
{}
|
||||
|
||||
void commit(void) { committed = true; }
|
||||
|
||||
~ConnectionGuard()
|
||||
{
|
||||
if (!committed && conn && conn->refCount == 0) {
|
||||
XcbConnection::connections.erase(conn->connectionIdentifier);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
XcbConnection(const XConnectionIdentifier& id)
|
||||
: connection(nullptr, &xcb_disconnect),
|
||||
connectionIdentifier(id), refCount(0)
|
||||
{
|
||||
// Convert to X display string format (e.g., ":0.1")
|
||||
std::string displayString = ":" + std::to_string(id.display)
|
||||
+ "." + std::to_string(id.screen);
|
||||
|
||||
int screenNum;
|
||||
connection.reset(xcb_connect(displayString.c_str(), &screenNum));
|
||||
if (xcb_connection_has_error(connection.get()))
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string(__func__) + ": Failed to connect to X server "
|
||||
+ connectionIdentifier.stringify());
|
||||
}
|
||||
|
||||
// Verify we got the screen we asked for
|
||||
if (screenNum != id.screen)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string(__func__) + ": Connected to wrong screen. "
|
||||
"Requested " + connectionIdentifier.stringify()
|
||||
+ " but got screen " + std::to_string(screenNum));
|
||||
}
|
||||
}
|
||||
|
||||
// Delete copy/move operations - we'll manage instances through pointers
|
||||
XcbConnection(const XcbConnection&) = delete;
|
||||
XcbConnection& operator=(const XcbConnection&) = delete;
|
||||
XcbConnection(XcbConnection&&) = delete;
|
||||
XcbConnection& operator=(XcbConnection&&) = delete;
|
||||
|
||||
std::unique_ptr<xcb_connection_t, decltype(&xcb_disconnect)> connection;
|
||||
XConnectionIdentifier connectionIdentifier;
|
||||
std::atomic<int> refCount;
|
||||
|
||||
public:
|
||||
static std::map<XConnectionIdentifier, std::shared_ptr<XcbConnection>>
|
||||
connections;
|
||||
|
||||
static std::shared_ptr<XcbConnection> getOrCreateConnection(
|
||||
const XConnectionIdentifier& id)
|
||||
{
|
||||
auto it = connections.find(id);
|
||||
if (it != connections.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
auto conn = std::make_shared<XcbConnection>(id);
|
||||
connections.emplace(id, conn);
|
||||
return conn;
|
||||
}
|
||||
};
|
||||
|
||||
namespace xcb_window_search {
|
||||
|
||||
// Custom deleters for XCB reply types
|
||||
struct XcbReplyDeleter {
|
||||
void operator()(xcb_query_tree_reply_t* p) { free(p); }
|
||||
void operator()(xcb_get_property_reply_t* p) { free(p); }
|
||||
};
|
||||
|
||||
enum class MatchType { SUBSTRING, EXACT, ID };
|
||||
|
||||
static xcb_window_t findById(
|
||||
xcb_connection_t* conn, xcb_window_t root, uint32_t targetId);
|
||||
|
||||
static xcb_window_t findByName(
|
||||
xcb_connection_t* conn, xcb_window_t root,
|
||||
const std::string& targetName, std::string& outWindowName,
|
||||
MatchType matchType);
|
||||
std::ostringstream os;
|
||||
os << "display=" << display << ", screen=" << screen;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Represents an attached device and its associated window
|
||||
*
|
||||
* This class represents a device that has been attached to the X server. It
|
||||
* manages the connection to the X server and the window selection criteria.
|
||||
*/
|
||||
class AttachedDevice
|
||||
XcbConnection::XcbConnection(const ConnectionIdentifier& id)
|
||||
: connection(nullptr, &xcb_disconnect),
|
||||
connectionIdentifier(id), refCount(0)
|
||||
{
|
||||
public:
|
||||
struct WindowSelector
|
||||
// Convert to X display string format (e.g., ":0.1")
|
||||
std::string displayString = ":" + std::to_string(id.display)
|
||||
+ "." + std::to_string(id.screen);
|
||||
|
||||
int screenNum;
|
||||
|
||||
connection.reset(xcb_connect(displayString.c_str(), &screenNum));
|
||||
if (xcb_connection_has_error(connection.get()))
|
||||
{
|
||||
xcb_window_search::MatchType matchType;
|
||||
|
||||
XcbConnection::XConnectionIdentifier xconn;
|
||||
struct
|
||||
{
|
||||
uint32_t id;
|
||||
std::string name;
|
||||
} window;
|
||||
|
||||
std::string stringify() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Display: " << xconn.display
|
||||
<< ", Screen: " << xconn.screen << ", Window: ";
|
||||
if (matchType == xcb_window_search::MatchType::ID) {
|
||||
os << window.id;
|
||||
} else {
|
||||
os << "\"" << window.name << "\"";
|
||||
}
|
||||
os << " (matchType="
|
||||
<< (matchType == xcb_window_search::MatchType::EXACT
|
||||
? "exact" :
|
||||
(matchType == xcb_window_search::MatchType::SUBSTRING)
|
||||
? "substring" : "id")
|
||||
<< ")";
|
||||
return os.str();
|
||||
}
|
||||
};
|
||||
|
||||
AttachedDevice(const smo::device::SenseDeviceSpec &spec,
|
||||
std::shared_ptr<XcbConnection> conn)
|
||||
: deviceSpec(spec)
|
||||
// This std::move is moving ownership from a shared_ptr to a shared_ptr
|
||||
, connection(std::move(conn))
|
||||
{
|
||||
windowSelector.xconn.display = getRequiredParamAsInt(spec, "display");
|
||||
windowSelector.xconn.screen = getRequiredParamAsInt(spec, "screen");
|
||||
parseWindowSelector(spec, windowSelector);
|
||||
|
||||
// Get the root window
|
||||
const xcb_setup_t* setup = xcb_get_setup(connection->connection.get());
|
||||
xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);
|
||||
for (int i = 0; i < windowSelector.xconn.screen; ++i) {
|
||||
xcb_screen_next(&iter);
|
||||
}
|
||||
xcb_window_t root = iter.data->root;
|
||||
|
||||
// Search for window
|
||||
xcb_window_t targetWindow = 0;
|
||||
if (windowSelector.matchType == xcb_window_search::MatchType::ID)
|
||||
{
|
||||
targetWindow = xcb_window_search::findById(
|
||||
connection->connection.get(), root, windowSelector.window.id);
|
||||
}
|
||||
else
|
||||
{
|
||||
targetWindow = xcb_window_search::findByName(
|
||||
connection->connection.get(), root,
|
||||
windowSelector.window.name, actualWindowName,
|
||||
windowSelector.matchType);
|
||||
}
|
||||
|
||||
if (!targetWindow)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Failed to find window "
|
||||
+ (windowSelector.matchType == xcb_window_search::MatchType::ID
|
||||
? std::to_string(windowSelector.window.id)
|
||||
: "\"" + windowSelector.window.name + "\"")
|
||||
+ " on display " + std::to_string(windowSelector.xconn.display)
|
||||
+ ", screen " + std::to_string(windowSelector.xconn.screen));
|
||||
}
|
||||
throw std::runtime_error(
|
||||
std::string(__func__) + ": Failed to connect to X server "
|
||||
+ connectionIdentifier.stringify());
|
||||
}
|
||||
|
||||
smo::device::SenseDeviceSpec deviceSpec;
|
||||
WindowSelector windowSelector;
|
||||
std::shared_ptr<XcbConnection> connection;
|
||||
std::string actualWindowName;
|
||||
|
||||
public:
|
||||
static int getRequiredParamAsInt(
|
||||
const smo::device::SenseDeviceSpec& spec,
|
||||
const std::string& paramName)
|
||||
// Verify we got the screen we asked for
|
||||
if (screenNum != id.screen)
|
||||
{
|
||||
auto it = std::find_if(
|
||||
spec.providerParams.begin(),
|
||||
spec.providerParams.end(),
|
||||
[¶mName](const auto& param) {
|
||||
return param.first == paramName;
|
||||
}
|
||||
);
|
||||
throw std::runtime_error(
|
||||
std::string(__func__) + ": Connected to wrong screen. "
|
||||
"Requested " + connectionIdentifier.stringify()
|
||||
+ " but got screen " + std::to_string(screenNum));
|
||||
}
|
||||
}
|
||||
|
||||
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::shared_ptr<XcbConnection> ConnectionManager::getOrCreateConnection(
|
||||
const ConnectionIdentifier& id)
|
||||
{
|
||||
auto it = connections.find(id);
|
||||
if (it != connections.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
static void parseWindowSelector(
|
||||
const smo::device::SenseDeviceSpec& spec,
|
||||
WindowSelector& windowSelector)
|
||||
{
|
||||
// Default match type
|
||||
windowSelector.matchType = xcb_window_search::MatchType::SUBSTRING;
|
||||
auto conn = std::make_shared<XcbConnection>(id);
|
||||
connections.emplace(id, conn);
|
||||
return conn;
|
||||
}
|
||||
|
||||
// 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_window_search::MatchType::ID;
|
||||
break;
|
||||
}
|
||||
if (param.first == "dev-string" || param.first == "dev-str"
|
||||
|| param.first == "devstr" || param.first == "devstring")
|
||||
{
|
||||
windowSelector.matchType = xcb_window_search::MatchType::EXACT;
|
||||
}
|
||||
if (param.first == "dev-substring" || param.first == "dev-substr"
|
||||
|| param.first == "devsubstr" || param.first == "devsubstring")
|
||||
{
|
||||
windowSelector.matchType = xcb_window_search::
|
||||
MatchType::SUBSTRING;
|
||||
}
|
||||
}
|
||||
void ConnectionManager::cleanupAllConnections()
|
||||
{
|
||||
connections.clear();
|
||||
}
|
||||
|
||||
if (windowSelector.matchType == xcb_window_search::MatchType::ID)
|
||||
{
|
||||
try {
|
||||
windowSelector.window.id = std::stoul(
|
||||
spec.deviceSelector, nullptr, 0);
|
||||
} catch (const std::exception&) {
|
||||
throw std::runtime_error(
|
||||
"Window selector: 'dev-id' present, but selector is not "
|
||||
"numeric");
|
||||
}
|
||||
}
|
||||
else {
|
||||
windowSelector.window.name = spec.deviceSelector;
|
||||
}
|
||||
}
|
||||
size_t ConnectionManager::getConnectionCount()
|
||||
{
|
||||
return connections.size();
|
||||
}
|
||||
|
||||
namespace window_search {
|
||||
|
||||
struct XcbReplyDeleter {
|
||||
void operator()(xcb_query_tree_reply_t* p) { free(p); }
|
||||
void operator()(xcb_get_property_reply_t* p) { free(p); }
|
||||
};
|
||||
|
||||
// Define the static member
|
||||
std::map<XcbConnection::XConnectionIdentifier, std::shared_ptr<XcbConnection>>
|
||||
XcbConnection::connections;
|
||||
|
||||
static std::vector<AttachedDevice> attachedDevices;
|
||||
|
||||
namespace xcb_window_search {
|
||||
|
||||
static xcb_window_t findById(
|
||||
xcb_window_t findById(
|
||||
xcb_connection_t* conn, xcb_window_t root, uint32_t targetId)
|
||||
{
|
||||
if (root == targetId) return root;
|
||||
|
||||
xcb_query_tree_cookie_t cookie = xcb_query_tree(conn, root);
|
||||
std::unique_ptr<xcb_query_tree_reply_t, XcbReplyDeleter> reply(
|
||||
xcb_query_tree_reply(conn, cookie, nullptr));
|
||||
|
||||
if (!reply) return 0;
|
||||
|
||||
// First check if current window is the target
|
||||
if (root == targetId) {
|
||||
return root;
|
||||
}
|
||||
|
||||
// Then check all children
|
||||
xcb_window_t* children = xcb_query_tree_children(reply.get());
|
||||
int num_children = xcb_query_tree_children_length(reply.get());
|
||||
|
||||
@@ -339,9 +101,7 @@ static xcb_window_t findById(
|
||||
}
|
||||
|
||||
// Recursively search this child's subtree
|
||||
if (xcb_window_t result = findById(
|
||||
conn, children[i], targetId))
|
||||
{
|
||||
if (xcb_window_t result = findById(conn, children[i], targetId)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -349,7 +109,7 @@ static xcb_window_t findById(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static xcb_window_t findByName(
|
||||
xcb_window_t findByName(
|
||||
xcb_connection_t* conn, xcb_window_t root,
|
||||
const std::string& targetName, std::string& outWindowName,
|
||||
MatchType matchType)
|
||||
@@ -357,6 +117,7 @@ static xcb_window_t findByName(
|
||||
xcb_query_tree_cookie_t cookie = xcb_query_tree(conn, root);
|
||||
std::unique_ptr<xcb_query_tree_reply_t, XcbReplyDeleter> reply(
|
||||
xcb_query_tree_reply(conn, cookie, nullptr));
|
||||
|
||||
if (!reply) return 0;
|
||||
|
||||
// First check current window
|
||||
@@ -426,133 +187,78 @@ static xcb_window_t findByName(
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace xcb_window_search
|
||||
} // namespace window_search
|
||||
|
||||
static smo::sense_api::sal_mlo_initializeIndFn xcbXorg_initializeInd;
|
||||
static smo::sense_api::sal_mlo_finalizeIndFn xcbXorg_finalizeInd;
|
||||
static smo::sense_api::sal_mlo_attachDeviceReqFn xcbXorg_attachDeviceReq;
|
||||
static smo::sense_api::sal_mlo_detachDeviceReqFn xcbXorg_detachDeviceReq;
|
||||
} // namespace xcb_xorg
|
||||
|
||||
static smo::sense_api::SenseApiDesc xcbXorgApiDesc =
|
||||
// Export functions for external libraries
|
||||
/**
|
||||
* @brief Get or create a connection to the X server
|
||||
* @param display Display number
|
||||
* @param screen Screen number
|
||||
* @return Shared pointer to connection (as void* for C compatibility)
|
||||
*/
|
||||
extern "C" get_or_create_connection_fn xcb_xorg_get_or_create_connection;
|
||||
std::shared_ptr<xcb_xorg::XcbConnection> xcb_xorg_get_or_create_connection(
|
||||
int display, int screen)
|
||||
{
|
||||
.name = "xcb",
|
||||
.exportedImplexorApis = { { "video-implexor" } },
|
||||
.sal_mgmt_libOps = {
|
||||
.initializeInd = xcbXorg_initializeInd,
|
||||
.finalizeInd = xcbXorg_finalizeInd,
|
||||
.attachDeviceReq = xcbXorg_attachDeviceReq,
|
||||
.detachDeviceReq = xcbXorg_detachDeviceReq
|
||||
}
|
||||
};
|
||||
|
||||
/* Below here are the exported functions that Salmanoff will use both to load
|
||||
* and use this library.
|
||||
******************************************************************************/
|
||||
xcb_xorg::ConnectionIdentifier id{display, screen};
|
||||
auto conn = xcb_xorg::ConnectionManager::getOrCreateConnection(id);
|
||||
conn->incrementRefCount();
|
||||
return conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the Sense API Descriptor
|
||||
*
|
||||
* This function is used to retrieve the descriptor for the Sense API provided
|
||||
* by this library. The descriptor contains information about the API, such as
|
||||
* its name, the implexors it exports, and the management operations it
|
||||
* supports.
|
||||
*
|
||||
* @param callbacks Hooks provided by Salmanoff for library operations
|
||||
* @return A reference to the SenseApiDesc structure describing the API.
|
||||
* @brief Clean up all connections
|
||||
*/
|
||||
extern SMO_UNMANGLED smo::sense_api::SMO_GET_SENSE_API_DESC_FN_TYPEDEF
|
||||
SMO_GET_SENSE_API_DESC_FN_NAME;
|
||||
|
||||
static const smo::sense_api::SalmanoffCallbacks* xcbXorg_callbacks = nullptr;
|
||||
|
||||
const smo::sense_api::SenseApiDesc &SMO_GET_SENSE_API_DESC_FN_NAME(
|
||||
const smo::sense_api::SalmanoffCallbacks& callbacks)
|
||||
extern "C" cleanup_connections_fn xcb_xorg_cleanup_connections;
|
||||
void xcb_xorg_cleanup_connections()
|
||||
{
|
||||
xcbXorg_callbacks = &callbacks;
|
||||
return xcbXorgApiDesc;
|
||||
xcb_xorg::ConnectionManager::cleanupAllConnections();
|
||||
}
|
||||
|
||||
int xcbXorg_initializeInd(void)
|
||||
/**
|
||||
* @brief Get the number of active connections
|
||||
* @return Number of active connections
|
||||
*/
|
||||
size_t xcb_xorg_get_connection_count()
|
||||
{
|
||||
return 0;
|
||||
return xcb_xorg::ConnectionManager::getConnectionCount();
|
||||
}
|
||||
|
||||
int xcbXorg_finalizeInd(void)
|
||||
/**
|
||||
* @brief Find window by ID
|
||||
* @param conn Connection pointer (from xcb_xorg_get_connection)
|
||||
* @param root Root window
|
||||
* @param targetId Target window ID
|
||||
* @return Window ID if found, 0 if not found
|
||||
*/
|
||||
extern "C" find_window_by_id_fn xcb_xorg_find_window_by_id;
|
||||
xcb_window_t xcb_xorg_find_window_by_id(void* conn, xcb_window_t root, uint32_t targetId)
|
||||
{
|
||||
XcbConnection::connections.clear();
|
||||
return 0;
|
||||
if (!conn) return 0;
|
||||
auto connection = static_cast<xcb_xorg::XcbConnection*>(conn);
|
||||
return xcb_xorg::window_search::findById(
|
||||
connection->getConnection(), root, targetId);
|
||||
}
|
||||
|
||||
int xcbXorg_attachDeviceReq(const smo::device::SenseDeviceSpec &desc)
|
||||
/**
|
||||
* @brief Find window by name
|
||||
* @param conn Connection pointer (from xcb_xorg_get_connection)
|
||||
* @param root Root window
|
||||
* @param targetName Target window name
|
||||
* @param outWindowName Output parameter for actual window name
|
||||
* @param matchType Type of name matching
|
||||
* @return Window ID if found, 0 if not found
|
||||
*/
|
||||
extern "C" find_window_by_name_fn xcb_xorg_find_window_by_name;
|
||||
xcb_window_t xcb_xorg_find_window_by_name(void* conn, xcb_window_t root,
|
||||
const std::string& targetName, std::string& outWindowName,
|
||||
xcb_xorg::window_search::MatchType matchType)
|
||||
{
|
||||
// Ensure connection exists before creating device. Create conn'tion if not.
|
||||
XcbConnection::XConnectionIdentifier id{
|
||||
AttachedDevice::getRequiredParamAsInt(desc, "display"),
|
||||
AttachedDevice::getRequiredParamAsInt(desc, "screen")
|
||||
};
|
||||
auto conn = XcbConnection::getOrCreateConnection(id);
|
||||
// RAII protection in case AttachDevice construction below fails
|
||||
XcbConnection::ConnectionGuard guard(conn);
|
||||
|
||||
// Create device and increment connection refcount
|
||||
attachedDevices.emplace_back(desc, conn);
|
||||
// Successfully attached device, so decouple guard from RAII cleanup
|
||||
conn->refCount++;
|
||||
guard.commit();
|
||||
|
||||
std::cout << "Attaching X11 window:\n "
|
||||
<< attachedDevices.back().windowSelector.stringify() << "\n"
|
||||
<< " Actual window name: \""
|
||||
<< attachedDevices.back().actualWindowName << "\"\n"
|
||||
<< " Using " << (conn->refCount > 1 ? "existing" : "new")
|
||||
<< " connection to X server\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xcbXorg_detachDeviceReq(const smo::device::SenseDeviceSpec &spec)
|
||||
{
|
||||
auto it = std::find_if(attachedDevices.begin(), attachedDevices.end(),
|
||||
[&spec](const AttachedDevice &device) {
|
||||
return device.deviceSpec == spec;
|
||||
}
|
||||
);
|
||||
|
||||
if (it == attachedDevices.end())
|
||||
{
|
||||
auto displayIt = std::find_if(
|
||||
spec.providerParams.begin(), spec.providerParams.end(),
|
||||
[](const auto& param) { return param.first == "display"; });
|
||||
auto screenIt = std::find_if(
|
||||
spec.providerParams.begin(), spec.providerParams.end(),
|
||||
[](const auto& param) { return param.first == "screen"; });
|
||||
|
||||
std::cerr << __func__ << ": Device not attached: "
|
||||
<< "display=" << (displayIt != spec.providerParams.end()
|
||||
? displayIt->second : "not specified")
|
||||
<< ", screen=" << (screenIt != spec.providerParams.end()
|
||||
? screenIt->second : "not specified")
|
||||
<< ", selector=" << spec.deviceSelector << "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
XcbConnection::XConnectionIdentifier id{
|
||||
it->windowSelector.xconn.display,
|
||||
it->windowSelector.xconn.screen
|
||||
};
|
||||
|
||||
// Atomic decrement refcount
|
||||
int newCount = --it->connection->refCount;
|
||||
|
||||
// If no more references, close the connection
|
||||
if (newCount == 0)
|
||||
{
|
||||
XcbConnection::connections.erase(id);
|
||||
std::cout << "Closed X server connection (display="
|
||||
<< id.display << ", screen=" << id.screen << ")\n";
|
||||
}
|
||||
|
||||
attachedDevices.erase(it);
|
||||
std::cout << __func__ << ": Detached device\n";
|
||||
return 0;
|
||||
if (!conn) return 0;
|
||||
auto connection = static_cast<xcb_xorg::XcbConnection*>(conn);
|
||||
return xcb_xorg::window_search::findByName(
|
||||
connection->getConnection(), root, targetName, outWindowName,
|
||||
matchType);
|
||||
}
|
||||
|
||||
+166
-12
@@ -1,22 +1,176 @@
|
||||
#ifndef X11_XCB_API_H
|
||||
#define X11_XCB_API_H
|
||||
#ifndef XCB_XORG_API_H
|
||||
#define XCB_XORG_API_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <user/senseApiDesc.h>
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
class Xcb_XorgApi
|
||||
namespace xcb_xorg {
|
||||
|
||||
/**
|
||||
* @brief Connection identifier for X server connections
|
||||
*/
|
||||
struct ConnectionIdentifier
|
||||
{
|
||||
public:
|
||||
Xcb_XorgApi(const std::string& _displayName)
|
||||
: displayName(_displayName)
|
||||
{}
|
||||
int display;
|
||||
int screen;
|
||||
|
||||
~Xcb_XorgApi() = default;
|
||||
bool operator<(const ConnectionIdentifier& other) const
|
||||
{
|
||||
if (display != other.display) return display < other.display;
|
||||
return screen < other.screen;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string displayName;
|
||||
std::string stringify() const;
|
||||
};
|
||||
|
||||
#endif // X11_XCB_API_H
|
||||
/**
|
||||
* @brief Represents a single X server connection using XCB
|
||||
*
|
||||
* This class manages a single connection to the X server. It provides
|
||||
* RAII management for the connection and maintains a reference count
|
||||
* for shared usage.
|
||||
*/
|
||||
class XcbConnection
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor for creating a new connection
|
||||
* @param id Connection identifier specifying display and screen
|
||||
* @throws std::runtime_error if connection fails
|
||||
*/
|
||||
explicit XcbConnection(const ConnectionIdentifier& id);
|
||||
|
||||
// Delete copy/move operations - we'll manage instances through pointers
|
||||
XcbConnection(const XcbConnection&) = delete;
|
||||
XcbConnection& operator=(const XcbConnection&) = delete;
|
||||
XcbConnection(XcbConnection&&) = delete;
|
||||
XcbConnection& operator=(XcbConnection&&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Destructor - automatically closes the connection
|
||||
*/
|
||||
~XcbConnection() = default;
|
||||
|
||||
/**
|
||||
* @brief Get the underlying XCB connection
|
||||
* @return Raw XCB connection pointer
|
||||
*/
|
||||
xcb_connection_t* getConnection() const { return connection.get(); }
|
||||
|
||||
/**
|
||||
* @brief Get the connection identifier
|
||||
* @return Connection identifier
|
||||
*/
|
||||
const ConnectionIdentifier& getIdentifier() const { return connectionIdentifier; }
|
||||
|
||||
/**
|
||||
* @brief Increment reference count
|
||||
*/
|
||||
void incrementRefCount() { refCount++; }
|
||||
|
||||
/**
|
||||
* @brief Decrement reference count
|
||||
* @return New reference count
|
||||
*/
|
||||
int decrementRefCount() { return --refCount; }
|
||||
|
||||
/**
|
||||
* @brief Get current reference count
|
||||
* @return Current reference count
|
||||
*/
|
||||
int getRefCount() const { return refCount; }
|
||||
|
||||
/**
|
||||
* @brief Check if connection is valid
|
||||
* @return true if connection is valid, false otherwise
|
||||
*/
|
||||
bool isValid() const { return connection && !xcb_connection_has_error(connection.get()); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<xcb_connection_t, decltype(&xcb_disconnect)> connection;
|
||||
ConnectionIdentifier connectionIdentifier;
|
||||
std::atomic<int> refCount;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Manages multiple X server connections
|
||||
*
|
||||
* This class manages a collection of XcbConnection instances. It ensures
|
||||
* that each unique display and screen combination has a single connection,
|
||||
* and provides RAII management for these connections.
|
||||
*/
|
||||
class ConnectionManager
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Get or create a connection to the X server
|
||||
* @param id Connection identifier specifying display and screen
|
||||
* @return Shared pointer to the connection
|
||||
* @throws std::runtime_error if connection fails
|
||||
*/
|
||||
static std::shared_ptr<XcbConnection> getOrCreateConnection(
|
||||
const ConnectionIdentifier& id);
|
||||
|
||||
/**
|
||||
* @brief Clean up all connections
|
||||
*/
|
||||
static void cleanupAllConnections();
|
||||
|
||||
/**
|
||||
* @brief Get the number of active connections
|
||||
* @return Number of active connections
|
||||
*/
|
||||
static size_t getConnectionCount();
|
||||
|
||||
private:
|
||||
static std::map<ConnectionIdentifier, std::shared_ptr<XcbConnection>> connections;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Window search functionality
|
||||
*/
|
||||
namespace window_search {
|
||||
|
||||
enum class MatchType { SUBSTRING, EXACT, ID };
|
||||
|
||||
/**
|
||||
* @brief Find window by ID
|
||||
* @param conn XCB connection
|
||||
* @param root Root window
|
||||
* @param targetId Target window ID
|
||||
* @return Window ID if found, 0 if not found
|
||||
*/
|
||||
xcb_window_t findById(xcb_connection_t* conn, xcb_window_t root, uint32_t targetId);
|
||||
|
||||
/**
|
||||
* @brief Find window by name
|
||||
* @param conn XCB connection
|
||||
* @param root Root window
|
||||
* @param targetName Target window name
|
||||
* @param outWindowName Output parameter for actual window name
|
||||
* @param matchType Type of name matching to perform
|
||||
* @return Window ID if found, 0 if not found
|
||||
*/
|
||||
xcb_window_t findByName(xcb_connection_t* conn, xcb_window_t root,
|
||||
const std::string& targetName, std::string& outWindowName,
|
||||
MatchType matchType);
|
||||
|
||||
} // namespace window_search
|
||||
|
||||
} // namespace xcb_xorg
|
||||
|
||||
// Function signature types for dynamic loading of libxcbXorg
|
||||
typedef std::shared_ptr<xcb_xorg::XcbConnection> get_or_create_connection_fn(
|
||||
int display, int screen);
|
||||
typedef void cleanup_connections_fn();
|
||||
typedef xcb_window_t find_window_by_id_fn(
|
||||
void* conn, xcb_window_t root, uint32_t targetId);
|
||||
typedef xcb_window_t find_window_by_name_fn(void* conn, xcb_window_t root,
|
||||
const std::string& targetName, std::string& outWindowName,
|
||||
xcb_xorg::window_search::MatchType matchType);
|
||||
|
||||
#endif // XCB_XORG_API_H
|
||||
|
||||
Reference in New Issue
Block a user