diff --git a/senseApis/xcbXorg/xcbXorg.cpp b/senseApis/xcbXorg/xcbXorg.cpp index 267cc3a..4d9ea61 100644 --- a/senseApis/xcbXorg/xcbXorg.cpp +++ b/senseApis/xcbXorg/xcbXorg.cpp @@ -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 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; }