# HG changeset patch # User František Kučera # Date 1616794186 -3600 # Node ID 3bbf848b3565300ae37768f8882c60ae959390e1 # Parent 3493d6e7016ed8d257dd2a91be119fc314123d9b Configuration + CLIParser + command for listing X11 devices diff -r 3493d6e7016e -r 3bbf848b3565 src/CLIParser.h --- a/src/CLIParser.h Sat Mar 20 19:18:10 2021 +0100 +++ b/src/CLIParser.h Fri Mar 26 22:29:46 2021 +0100 @@ -37,17 +37,6 @@ else throw relpipe::cli::RelpipeCLIException(L"Missing CLI argument" + (i > 0 ? (L" after " + arguments[i - 1]) : L""), relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS); } - void readNextRecord(const std::vector& arguments, int& i, RelationConfiguration& currentRelation) { - for (const auto& a : currentRelation.attributes) currentRelation.values.emplace_back(readNext(arguments, i)); - } - - void addRelation(Configuration& c, RelationConfiguration& currentRelation) { - if (currentRelation.relation.size()) { - c.relationConfigurations.push_back(currentRelation); - currentRelation = RelationConfiguration(); - } - } - /** * TODO: use a common method */ @@ -57,58 +46,23 @@ else throw relpipe::cli::RelpipeCLIException(L"Unable to parse boolean value: " + value + L" (expecting true or false)", relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS); } - /** - * TODO: use a common method - */ - relpipe::writer::TypeId parseTypeId(const relpipe::common::type::StringX& value) { - using t = relpipe::writer::TypeId; - if (value == L"string") return t::STRING; - else if (value == L"integer") return t::INTEGER; - else if (value == L"boolean") return t::BOOLEAN; - else throw relpipe::cli::RelpipeCLIException(L"Unable to parse TypeId: " + value, relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS); - } - public: - static const relpipe::common::type::StringX OPTION_RELATION; - static const relpipe::common::type::StringX OPTION_WRITE_HEADER; - static const relpipe::common::type::StringX OPTION_ATTRIBUTE; - static const relpipe::common::type::StringX OPTION_RECORD; - static const relpipe::common::type::StringX OPTION_RECORDS; - static const relpipe::common::type::StringX OPTION_RECORDS_STDIN; + static const relpipe::common::type::StringX OPTION_LIST_INPUT_DEVICES; + static const relpipe::common::type::StringX OPTION_LIST_INPUT_EVENTS; Configuration parse(const std::vector& arguments) { Configuration c; - RelationConfiguration currentRelation; for (int i = 0; i < arguments.size();) { relpipe::common::type::StringX option = readNext(arguments, i); - if (option == OPTION_RELATION) { - addRelation(c, currentRelation); // previous relation - currentRelation.relation = readNext(arguments, i); - } else if (option == OPTION_WRITE_HEADER) { - currentRelation.writeHeader = parseBoolean(readNext(arguments, i)); - } else if (option == OPTION_ATTRIBUTE) { - AttributeRecipe attribute; - attribute.name = readNext(arguments, i); - attribute.type = parseTypeId(readNext(arguments, i)); - currentRelation.attributes.push_back(attribute); - } else if (option == OPTION_RECORD) { - readNextRecord(arguments, i, currentRelation); - } else if (option == OPTION_RECORDS) { - while (i < arguments.size()) readNextRecord(arguments, i, currentRelation); - } else if (option == OPTION_RECORDS_STDIN) { - if (parseBoolean(readNext(arguments, i))) { - for (RelationConfiguration r : c.relationConfigurations) if (r.valueStream) throw relpipe::cli::RelpipeCLIException(L"Only one relation can read data from STDIN.", relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS); - currentRelation.valueStream = &std::cin; - } + if (option == OPTION_LIST_INPUT_DEVICES) { + c.listInputDevices = parseBoolean(readNext(arguments, i)); + } else if (option == OPTION_LIST_INPUT_EVENTS) { + c.listInputEvents = parseBoolean(readNext(arguments, i)); } else throw relpipe::cli::RelpipeCLIException(L"Unsupported CLI option: " + option, relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS); } - addRelation(c, currentRelation); // last relation - - if (c.relationConfigurations.size() == 0) throw relpipe::cli::RelpipeCLIException(L"There must be at least one relation. Use the " + OPTION_RELATION + L" option.", relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS); - for (auto& rc : c.relationConfigurations) if (rc.attributes.size() == 0) throw relpipe::cli::RelpipeCLIException(L"There must be at least one attribute. Use the " + OPTION_ATTRIBUTE + L" option.", relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS); return c; } @@ -117,12 +71,8 @@ } }; -const relpipe::common::type::StringX CLIParser::OPTION_RELATION = L"--relation"; -const relpipe::common::type::StringX CLIParser::OPTION_WRITE_HEADER = L"--write-header"; -const relpipe::common::type::StringX CLIParser::OPTION_ATTRIBUTE = L"--attribute"; -const relpipe::common::type::StringX CLIParser::OPTION_RECORD = L"--record"; -const relpipe::common::type::StringX CLIParser::OPTION_RECORDS = L"--records"; -const relpipe::common::type::StringX CLIParser::OPTION_RECORDS_STDIN = L"--records-on-stdin"; +const relpipe::common::type::StringX CLIParser::OPTION_LIST_INPUT_DEVICES = L"--list-input-devices"; +const relpipe::common::type::StringX CLIParser::OPTION_LIST_INPUT_EVENTS = L"--list-input-events"; } } diff -r 3493d6e7016e -r 3bbf848b3565 src/CMakeLists.txt --- a/src/CMakeLists.txt Sat Mar 20 19:18:10 2021 +0100 +++ b/src/CMakeLists.txt Fri Mar 26 22:29:46 2021 +0100 @@ -17,7 +17,7 @@ # Relpipe libraries: INCLUDE(FindPkgConfig) -pkg_check_modules (RELPIPE_LIBS relpipe-lib-writer.cpp relpipe-lib-cli.cpp) +pkg_check_modules (RELPIPE_LIBS relpipe-lib-writer.cpp relpipe-lib-cli.cpp x11 xi xtst) include_directories(${RELPIPE_LIBS_INCLUDE_DIRS}) link_directories(${RELPIPE_LIBS_LIBRARY_DIRS}) diff -r 3493d6e7016e -r 3bbf848b3565 src/Configuration.h --- a/src/Configuration.h Sat Mar 20 19:18:10 2021 +0100 +++ b/src/Configuration.h Fri Mar 26 22:29:46 2021 +0100 @@ -26,33 +26,10 @@ namespace in { namespace x11 { -class AttributeRecipe { -public: - - virtual ~AttributeRecipe() { - } - - relpipe::common::type::StringX name; - relpipe::writer::TypeId type; - -}; - -class RelationConfiguration { -public: - - virtual ~RelationConfiguration() { - } - - relpipe::common::type::StringX relation; - relpipe::common::type::Boolean writeHeader = true; - std::vector attributes; - std::vector values; - std::istream* valueStream = nullptr; -}; - class Configuration { public: - std::vector relationConfigurations; + bool listInputDevices = true; + bool listInputEvents = false; virtual ~Configuration() { } diff -r 3493d6e7016e -r 3bbf848b3565 src/X11Command.h --- a/src/X11Command.h Sat Mar 20 19:18:10 2021 +0100 +++ b/src/X11Command.h Fri Mar 26 22:29:46 2021 +0100 @@ -19,6 +19,9 @@ #include #include #include +#include + +#include #include "Configuration.h" @@ -29,43 +32,72 @@ class X11Command { private: + class Display { + public: + ::Display* display = nullptr; + // TODO: more OOP + + virtual ~Display() { + if (display) XCloseDisplay(display); + } + + }; + + class DeviceList { + public: + XDeviceInfo* items = nullptr; + int count = 0; + // TODO: more OOP + + virtual ~DeviceList() { + if (items) XFreeDeviceList(items); + } + + }; + + std::wstring_convert> convertor; // TODO: support also other encodings and use platform encoding as default - void processRelation(RelationConfiguration& configuration, std::shared_ptr writer) { - // Write header / metadata: - std::vector attributesMetadata; - for (AttributeRecipe ar : configuration.attributes) attributesMetadata.push_back({ar.name, ar.type}); - writer->startRelation(configuration.relation, attributesMetadata, configuration.writeHeader); + void listInputDevices(Display& display, Configuration& configuration, std::shared_ptr writer) { + writer->startRelation(L"x11_input_device",{ + {L"id", relpipe::writer::TypeId::INTEGER}, + {L"name", relpipe::writer::TypeId::STRING}, + // TODO: device type name and ID + }, true); - // Write records from CLI: - for (auto value : configuration.values) writer->writeAttribute(value); + DeviceList devices; + devices.items = XListInputDevices(display.display, &devices.count); - // Write records from STDIN: - if (configuration.valueStream) { - std::stringstream rawValue; - while (true) { - char ch = 0; - configuration.valueStream->get(ch); - if (ch == 0 && configuration.valueStream->good()) { - writer->writeAttribute(convertor.from_bytes(rawValue.str())); - rawValue.str(""); - rawValue.clear(); - } else if (configuration.valueStream->good()) { - rawValue << ch; - } else if (configuration.valueStream->eof()) { - return; - } else { - throw relpipe::cli::RelpipeCLIException(L"Error while reading values from the input stream.", relpipe::cli::CLI::EXIT_CODE_UNEXPECTED_ERROR); // TODO: better exception - } - } + for (int i = 0; i < devices.count; i++) { + relpipe::common::type::Integer id = (devices.items + i)->id; + relpipe::common::type::StringX name = convertor.from_bytes((devices.items + i)->name); + + writer->writeAttribute(&id, typeid (id)); + writer->writeAttribute(&name, typeid (name)); + } + } + void listInputEvents(Display& display, Configuration& configuration, std::shared_ptr writer) { + writer->startRelation(L"x11_input_event",{ + {L"device_id", relpipe::writer::TypeId::INTEGER}, + }, true); + + // TODO: list events } public: void process(Configuration& configuration, std::shared_ptr writer) { - for (RelationConfiguration& relationConfiguration : configuration.relationConfigurations) processRelation(relationConfiguration, writer); + Display display; + display.display = XOpenDisplay(nullptr); + + if (display.display) { + if (configuration.listInputDevices) listInputDevices(display, configuration, writer); + if (configuration.listInputEvents) listInputEvents(display, configuration, writer); + } else { + throw std::invalid_argument("Unable to open display. Please check the $DISPLAY variable."); + } } };