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); }
};
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);
const std::string& targetName, std::string& outWindowName,
MatchType matchType);
}
class AttachedDevice
@@ -140,23 +143,31 @@ class AttachedDevice
public:
struct WindowSelector
{
xcb_window_search::MatchType matchType;
XcbConnection::XConnectionIdentifier xconn;
struct
{
uint32_t id;
std::string name;
} window;
bool useWindowId;
std::string stringify() const
{
std::ostringstream os;
os << "Display: " << xconn.display
<< ", Screen: " << xconn.screen
<< ", Window: " << (useWindowId
? std::to_string(window.id)
: "\"" + window.name + "\"");
<< ", 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();
}
};
@@ -169,7 +180,7 @@ public:
{
windowSelector.xconn.display = getRequiredParamAsInt(spec, "display");
windowSelector.xconn.screen = getRequiredParamAsInt(spec, "screen");
parseWindowSelector(spec.deviceSelector, windowSelector);
parseWindowSelector(spec, windowSelector);
// Get the root window
const xcb_setup_t* setup = xcb_get_setup(connection->connection.get());
@@ -181,7 +192,7 @@ public:
// Search for window
xcb_window_t targetWindow = 0;
if (windowSelector.useWindowId)
if (windowSelector.matchType == xcb_window_search::MatchType::ID)
{
targetWindow = xcb_window_search::findById(
connection->connection.get(), root, windowSelector.window.id);
@@ -190,14 +201,15 @@ public:
{
targetWindow = xcb_window_search::findByName(
connection->connection.get(), root,
windowSelector.window.name, actualWindowName);
windowSelector.window.name, actualWindowName,
windowSelector.matchType);
}
if (!targetWindow)
{
throw std::runtime_error(
"Failed to find window "
+ (windowSelector.useWindowId
+ (windowSelector.matchType == xcb_window_search::MatchType::ID
? std::to_string(windowSelector.window.id)
: "\"" + windowSelector.window.name + "\"")
+ " on display " + std::to_string(windowSelector.xconn.display)
@@ -239,30 +251,47 @@ public:
}
static void parseWindowSelector(
const std::string& selector,
const hk::device::SenseDeviceSpec& spec,
WindowSelector& windowSelector)
{
if (selector.length() >= 2 &&
((selector[0] == '"' && selector.back() == '"') ||
(selector[0] == '\'' && selector.back() == '\'') ||
(selector[0] == '`' && selector.back() == '`')))
// Default match type
windowSelector.matchType = xcb_window_search::MatchType::SUBSTRING;
// Check if 'dev-id', 'dev-string', or 'dev-substring' is specified
for (const auto& param : spec.apiParams)
{
windowSelector.window.name = selector.substr(
1, selector.length() - 2);
windowSelector.useWindowId = false;
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")
{
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 {
// Allow hex numbers
windowSelector.window.id = std::stoul(selector, nullptr, 0);
windowSelector.useWindowId = true;
windowSelector.window.id = std::stoul(
spec.deviceSelector, nullptr, 0);
} catch (const std::exception&) {
throw std::runtime_error(
"Window selector must be either a quoted string or a "
"numeric ID");
"Window selector: 'dev-id' present, but selector is not "
"numeric");
}
}
else {
windowSelector.window.name = spec.deviceSelector;
}
}
};
@@ -310,7 +339,8 @@ static xcb_window_t findById(
static xcb_window_t findByName(
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);
std::unique_ptr<xcb_query_tree_reply_t, XcbReplyDeleter> reply(
@@ -332,7 +362,10 @@ static xcb_window_t findByName(
if (len > 0)
{
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;
return root;
@@ -359,7 +392,10 @@ static xcb_window_t findByName(
if (len > 0)
{
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;
return children[i];
@@ -369,7 +405,7 @@ static xcb_window_t findByName(
// Recursively search this child's subtree
if (xcb_window_t result = findByName(
conn, children[i], targetName, outWindowName))
conn, children[i], targetName, outWindowName, matchType))
{
return result;
}