xcbXorg: Use apiParams to choose match method for window attachment

We now use param keys in the API params to choose what type of ID
the deviceSelector holds, and how to match it.

* dev-id/devid: matches by ID.
* dev-substr/dev-substring/devsubstr: Matches window name by substring.
* dev-string/devstr/dev-str: matches window name by exact whole string.
This commit is contained in:
2025-01-14 20:59:28 -04:00
parent 64baa7906b
commit 20154d1e95
+65 -29
View File
@@ -127,12 +127,15 @@ namespace xcb_window_search {
void operator()(xcb_get_property_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( static xcb_window_t findById(
xcb_connection_t* conn, xcb_window_t root, uint32_t targetId); xcb_connection_t* conn, xcb_window_t root, uint32_t targetId);
static xcb_window_t findByName( static xcb_window_t findByName(
xcb_connection_t* conn, xcb_window_t root, xcb_connection_t* conn, xcb_window_t root,
const std::string& targetName, std::string& outWindowName); const std::string& targetName, std::string& outWindowName,
MatchType matchType);
} }
class AttachedDevice class AttachedDevice
@@ -140,23 +143,31 @@ class AttachedDevice
public: public:
struct WindowSelector struct WindowSelector
{ {
xcb_window_search::MatchType matchType;
XcbConnection::XConnectionIdentifier xconn; XcbConnection::XConnectionIdentifier xconn;
struct struct
{ {
uint32_t id; uint32_t id;
std::string name; std::string name;
} window; } window;
bool useWindowId;
std::string stringify() const std::string stringify() const
{ {
std::ostringstream os; std::ostringstream os;
os << "Display: " << xconn.display os << "Display: " << xconn.display
<< ", Screen: " << xconn.screen << ", Screen: " << xconn.screen << ", Window: ";
<< ", Window: " << (useWindowId if (matchType == xcb_window_search::MatchType::ID) {
? std::to_string(window.id) os << window.id;
: "\"" + window.name + "\""); } else {
os << "\"" << window.name << "\"";
}
os << " (matchType="
<< (matchType == xcb_window_search::MatchType::EXACT
? "exact" :
(matchType == xcb_window_search::MatchType::SUBSTRING)
? "substring" : "id")
<< ")";
return os.str(); return os.str();
} }
}; };
@@ -169,7 +180,7 @@ public:
{ {
windowSelector.xconn.display = getRequiredParamAsInt(spec, "display"); windowSelector.xconn.display = getRequiredParamAsInt(spec, "display");
windowSelector.xconn.screen = getRequiredParamAsInt(spec, "screen"); windowSelector.xconn.screen = getRequiredParamAsInt(spec, "screen");
parseWindowSelector(spec.deviceSelector, windowSelector); parseWindowSelector(spec, windowSelector);
// Get the root window // Get the root window
const xcb_setup_t* setup = xcb_get_setup(connection->connection.get()); const xcb_setup_t* setup = xcb_get_setup(connection->connection.get());
@@ -181,7 +192,7 @@ public:
// Search for window // Search for window
xcb_window_t targetWindow = 0; xcb_window_t targetWindow = 0;
if (windowSelector.useWindowId) if (windowSelector.matchType == xcb_window_search::MatchType::ID)
{ {
targetWindow = xcb_window_search::findById( targetWindow = xcb_window_search::findById(
connection->connection.get(), root, windowSelector.window.id); connection->connection.get(), root, windowSelector.window.id);
@@ -190,14 +201,15 @@ public:
{ {
targetWindow = xcb_window_search::findByName( targetWindow = xcb_window_search::findByName(
connection->connection.get(), root, connection->connection.get(), root,
windowSelector.window.name, actualWindowName); windowSelector.window.name, actualWindowName,
windowSelector.matchType);
} }
if (!targetWindow) if (!targetWindow)
{ {
throw std::runtime_error( throw std::runtime_error(
"Failed to find window " "Failed to find window "
+ (windowSelector.useWindowId + (windowSelector.matchType == xcb_window_search::MatchType::ID
? std::to_string(windowSelector.window.id) ? std::to_string(windowSelector.window.id)
: "\"" + windowSelector.window.name + "\"") : "\"" + windowSelector.window.name + "\"")
+ " on display " + std::to_string(windowSelector.xconn.display) + " on display " + std::to_string(windowSelector.xconn.display)
@@ -239,30 +251,47 @@ public:
} }
static void parseWindowSelector( static void parseWindowSelector(
const std::string& selector, const hk::device::SenseDeviceSpec& spec,
WindowSelector& windowSelector) WindowSelector& windowSelector)
{ {
if (selector.length() >= 2 && // Default match type
((selector[0] == '"' && selector.back() == '"') || windowSelector.matchType = xcb_window_search::MatchType::SUBSTRING;
(selector[0] == '\'' && selector.back() == '\'') ||
(selector[0] == '`' && selector.back() == '`'))) // Check if 'dev-id', 'dev-string', or 'dev-substring' is specified
for (const auto& param : spec.apiParams)
{ {
windowSelector.window.name = selector.substr( if (param.first == "dev-id" || param.first == "devid")
1, selector.length() - 2); {
windowSelector.useWindowId = false; windowSelector.matchType = xcb_window_search::MatchType::ID;
break;
}
if (param.first == "dev-string"
|| param.first == "dev-str" || param.first == "devstr")
{
windowSelector.matchType = xcb_window_search::MatchType::EXACT;
}
if (param.first == "dev-substring"
|| param.first == "dev-substr" || param.first == "devsubstr")
{
windowSelector.matchType = xcb_window_search::
MatchType::SUBSTRING;
}
} }
else
if (windowSelector.matchType == xcb_window_search::MatchType::ID)
{ {
try { try {
// Allow hex numbers windowSelector.window.id = std::stoul(
windowSelector.window.id = std::stoul(selector, nullptr, 0); spec.deviceSelector, nullptr, 0);
windowSelector.useWindowId = true;
} catch (const std::exception&) { } catch (const std::exception&) {
throw std::runtime_error( throw std::runtime_error(
"Window selector must be either a quoted string or a " "Window selector: 'dev-id' present, but selector is not "
"numeric ID"); "numeric");
} }
} }
else {
windowSelector.window.name = spec.deviceSelector;
}
} }
}; };
@@ -310,7 +339,8 @@ static xcb_window_t findById(
static xcb_window_t findByName( static xcb_window_t findByName(
xcb_connection_t* conn, xcb_window_t root, xcb_connection_t* conn, xcb_window_t root,
const std::string& targetName, std::string& outWindowName) const std::string& targetName, std::string& outWindowName,
MatchType matchType)
{ {
xcb_query_tree_cookie_t cookie = xcb_query_tree(conn, root); xcb_query_tree_cookie_t cookie = xcb_query_tree(conn, root);
std::unique_ptr<xcb_query_tree_reply_t, XcbReplyDeleter> reply( std::unique_ptr<xcb_query_tree_reply_t, XcbReplyDeleter> reply(
@@ -332,7 +362,10 @@ static xcb_window_t findByName(
if (len > 0) if (len > 0)
{ {
std::string windowName(name, len); std::string windowName(name, len);
if (windowName == targetName) if ((matchType == MatchType::EXACT
&& windowName == targetName)
|| (matchType == MatchType::SUBSTRING
&& windowName.find(targetName) != std::string::npos))
{ {
outWindowName = windowName; outWindowName = windowName;
return root; return root;
@@ -359,7 +392,10 @@ static xcb_window_t findByName(
if (len > 0) if (len > 0)
{ {
std::string windowName(name, len); std::string windowName(name, len);
if (windowName == targetName) if ((matchType == MatchType::EXACT
&& windowName == targetName)
|| (matchType == MatchType::SUBSTRING
&& windowName.find(targetName) != std::string::npos))
{ {
outWindowName = windowName; outWindowName = windowName;
return children[i]; return children[i];
@@ -369,7 +405,7 @@ static xcb_window_t findByName(
// Recursively search this child's subtree // Recursively search this child's subtree
if (xcb_window_t result = findByName( if (xcb_window_t result = findByName(
conn, children[i], targetName, outWindowName)) conn, children[i], targetName, outWindowName, matchType))
{ {
return result; return result;
} }