#include #include #include #include #include #include #include #include #include #include "xcbXorg.h" namespace xcb_xorg { // Static member initialization std::map> ConnectionManager::connections; std::string ConnectionIdentifier::stringify() const { std::ostringstream os; os << "display=" << display << ", screen=" << screen; return os.str(); } XcbConnection::XcbConnection(const ConnectionIdentifier& 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)); } } std::shared_ptr ConnectionManager::getOrCreateConnection( const ConnectionIdentifier& id) { auto it = connections.find(id); if (it != connections.end()) { return it->second; } auto conn = std::make_shared(id); connections.emplace(id, conn); return conn; } void ConnectionManager::cleanupAllConnections() { connections.clear(); } size_t ConnectionManager::getConnectionCount() { return connections.size(); } /** * @brief Remove a specific connection from the manager * @param id Connection identifier to remove */ void ConnectionManager::removeConnection(const ConnectionIdentifier& id) { auto it = connections.find(id); if (it != connections.end()) { connections.erase(it); } } 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); } }; xcb_window_t findById( xcb_connection_t* conn, xcb_window_t root, uint32_t targetId) { xcb_query_tree_cookie_t cookie = xcb_query_tree(conn, root); std::unique_ptr reply( xcb_query_tree_reply(conn, cookie, nullptr)); if (!reply) return 0; if (root == targetId) return root; xcb_window_t* children = xcb_query_tree_children(reply.get()); int num_children = xcb_query_tree_children_length(reply.get()); for (int i = 0; i < num_children; ++i) { if (children[i] == targetId) { return children[i]; } // Recursively search this child's subtree if (xcb_window_t result = findById(conn, children[i], targetId)) { return result; } } return 0; } xcb_window_t findByName( xcb_connection_t* conn, xcb_window_t root, const std::string& targetName, std::string& outWindowName, MatchType matchType) { xcb_query_tree_cookie_t cookie = xcb_query_tree(conn, root); std::unique_ptr reply( xcb_query_tree_reply(conn, cookie, nullptr)); if (!reply) return 0; // First check current window xcb_get_property_cookie_t prop_cookie = xcb_get_property( conn, 0, root, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0, 1024); std::unique_ptr prop_reply( xcb_get_property_reply(conn, prop_cookie, nullptr)); if (prop_reply) { int len = xcb_get_property_value_length(prop_reply.get()); char* name = static_cast( xcb_get_property_value(prop_reply.get())); if (len > 0) { std::string windowName(name, len); if ((matchType == MatchType::EXACT && windowName == targetName) || (matchType == MatchType::SUBSTRING && windowName.find(targetName) != std::string::npos)) { outWindowName = windowName; 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()); for (int i = 0; i < num_children; ++i) { prop_cookie = xcb_get_property( conn, 0, children[i], XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0, 1024); prop_reply.reset(xcb_get_property_reply(conn, prop_cookie, nullptr)); if (prop_reply) { int len = xcb_get_property_value_length(prop_reply.get()); char* name = static_cast(xcb_get_property_value( prop_reply.get())); if (len > 0) { std::string windowName(name, len); if ((matchType == MatchType::EXACT && windowName == targetName) || (matchType == MatchType::SUBSTRING && windowName.find(targetName) != std::string::npos)) { outWindowName = windowName; return children[i]; } } } // Recursively search this child's subtree if (xcb_window_t result = findByName( conn, children[i], targetName, outWindowName, matchType)) { return result; } } return 0; } } // namespace window_search } // namespace xcb_xorg // 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_get_or_create_connection( int display, int screen) { xcb_xorg::ConnectionIdentifier id{display, screen}; auto conn = xcb_xorg::ConnectionManager::getOrCreateConnection(id); conn->incrementRefCount(); return conn; } /** * @brief Clean up all connections */ extern "C" cleanup_connections_fn xcb_xorg_cleanup_connections; void xcb_xorg_cleanup_connections() { xcb_xorg::ConnectionManager::cleanupAllConnections(); } /** * @brief Get the number of active connections * @return Number of active connections */ size_t xcb_xorg_get_connection_count() { return xcb_xorg::ConnectionManager::getConnectionCount(); } /** * @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) { if (!conn) return 0; auto connection = static_cast(conn); return xcb_xorg::window_search::findById( connection->getConnection(), root, targetId); } /** * @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) { if (!conn) return 0; auto connection = static_cast(conn); return xcb_xorg::window_search::findByName( connection->getConnection(), root, targetName, outWindowName, matchType); } /** * @brief Dereference a connection (decrements ref count and closes if zero) * @param conn Shared pointer to the connection to dereference */ extern "C" dereference_connection_fn xcb_xorg_dereference_connection; void xcb_xorg_dereference_connection(std::shared_ptr conn) { if (!conn) return; int newRefCount = conn->decrementRefCount(); // Remove from connection manager if ref count reaches zero if (newRefCount <= 0) { xcb_xorg::ConnectionManager::removeConnection(conn->getIdentifier()); } }