Mercurial > minori
view dep/animone/src/player.cc @ 337:a7d4e5107531
dep/animone: REFACTOR ALL THE THINGS
1: animone now has its own syntax divergent from anisthesia,
making different platforms actually have their own sections
2: process names in animone are now called `comm' (this will
probably break things). this is what its called in bsd/linux
so I'm just going to use it everywhere
3: the X11 code now checks for the existence of a UTF-8 window title
and passes it if available
4: ANYTHING THATS NOT LINUX IS 100% UNTESTED AND CAN AND WILL BREAK!
I still actually need to test the bsd code. to be honest I'm probably
going to move all of the bsds into separate files because they're
all essentially different operating systems at this point
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Wed, 19 Jun 2024 12:51:15 -0400 |
parents | b1f625b0227c |
children |
line wrap: on
line source
#include "animone/player.h" #include "animone/util.h" #include <map> #include <sstream> #include <string> #include <vector> #include <optional> #include <iostream> namespace animone { namespace internal::parser { struct State { enum class Name { ExpectPlayerName, ExpectSection, ExpectWindowPlatform, ExpectExecutablePlatform, ExpectWindow, ExpectExecutable, ExpectStrategy, ExpectType, ExpectWindowTitle, }; Name state = Name::ExpectPlayerName; WindowPlatform window_platform = WindowPlatform::Unknown; ExecutablePlatform executable_platform = ExecutablePlatform::Unknown; }; size_t GetIndentation(const std::string& line) { return line.find_first_not_of('\t'); } bool HandleIndentation(const size_t current, const std::vector<Player>& players, State& state) { // Each state has a definitive expected indentation const auto expected = [](const State::Name& state) -> size_t { switch (state) { default: case State::Name::ExpectPlayerName: return 0; case State::Name::ExpectSection: return 1; case State::Name::ExpectWindowPlatform: case State::Name::ExpectExecutablePlatform: case State::Name::ExpectStrategy: case State::Name::ExpectType: return 2; case State::Name::ExpectWindow: case State::Name::ExpectExecutable: case State::Name::ExpectWindowTitle: return 3; } }(state.state); if (current > expected) { std::cerr << "animone: excessive indentation found" << std::endl; return false; // Disallow excessive indentation } if (current < expected) { const std::optional<State::Name> st = [current, state]() -> std::optional<State::Name> { switch (current) { case 0: return State::Name::ExpectPlayerName; default: case 1: return State::Name::ExpectSection; case 2: switch (state.state) { case State::Name::ExpectWindow: return State::Name::ExpectWindowPlatform; case State::Name::ExpectExecutable: return State::Name::ExpectExecutablePlatform; default: return std::nullopt; } } }(); if (!st.has_value()) return false; switch (state.state) { case State::Name::ExpectWindow: if (players.back().windows.empty()) return false; state.state = st.value(); break; case State::Name::ExpectExecutable: if (players.back().executables.empty()) return false; state.state = st.value(); break; case State::Name::ExpectStrategy: if (players.back().strategies.empty()) return false; state.state = st.value(); break; case State::Name::ExpectType: state.state = st.value(); break; case State::Name::ExpectWindowTitle: return false; } } return true; } bool HandleState(std::string& line, std::vector<Player>& players, State& state) { switch (state.state) { case State::Name::ExpectPlayerName: players.push_back(Player()); players.back().name = line; state.state = State::Name::ExpectSection; break; case State::Name::ExpectSection: { static const std::map<std::string, State::Name> sections = { {"windows", State::Name::ExpectWindowPlatform}, {"executables", State::Name::ExpectExecutablePlatform}, {"strategies", State::Name::ExpectStrategy }, {"type", State::Name::ExpectType }, }; util::TrimRight(line, ":"); const auto it = sections.find(line); if (it == sections.end()) return false; state.state = it->second; break; } case State::Name::ExpectWindowPlatform: { static const std::map<std::string, WindowPlatform> platforms = { {"quartz", WindowPlatform::Quartz}, {"win32", WindowPlatform::Win32}, {"x11", WindowPlatform::X11}, }; util::TrimRight(line, ":"); const auto it = platforms.find(line); if (it == platforms.end()) return false; state.state = State::Name::ExpectWindow; state.window_platform = it->second; break; } case State::Name::ExpectExecutablePlatform: { static const std::map<std::string, ExecutablePlatform> platforms = { {"posix", ExecutablePlatform::Posix}, {"win32", ExecutablePlatform::Win32}, {"macosx", ExecutablePlatform::Xnu}, }; util::TrimRight(line, ":"); const auto it = platforms.find(line); if (it == platforms.end()) return false; state.state = State::Name::ExpectExecutable; state.executable_platform = it->second; break; } case State::Name::ExpectWindow: players.back().windows[state.window_platform].push_back(line); break; case State::Name::ExpectExecutable: players.back().executables[state.executable_platform].push_back(line); break; case State::Name::ExpectStrategy: { static const std::map<std::string, Strategy> strategies = { {"window_title", Strategy::WindowTitle }, {"open_files", Strategy::OpenFiles }, {"ui_automation", Strategy::UiAutomation}, }; util::TrimRight(line, ":"); const auto it = strategies.find(line); if (it == strategies.end()) return false; const auto strategy = it->second; players.back().strategies.push_back(strategy); switch (strategy) { case Strategy::WindowTitle: state.state = State::Name::ExpectWindowTitle; break; } break; } case State::Name::ExpectType: { static const std::map<std::string, PlayerType> types = { {"default", PlayerType::Default }, {"web_browser", PlayerType::WebBrowser}, }; const auto it = types.find(line); if (it == types.end()) return false; players.back().type = it->second; break; } case State::Name::ExpectWindowTitle: players.back().window_title_format = line; state.state = State::Name::ExpectStrategy; break; } return true; } } // namespace internal::parser //////////////////////////////////////////////////////////////////////////////// bool ParsePlayersData(const std::string& data, std::vector<Player>& players) { if (data.empty()) return false; std::istringstream stream(data); std::string line; size_t indentation = 0; internal::parser::State state; int ln = 1; for (; std::getline(stream, line, '\n'); ln++) { if (line.empty()) continue; // Ignore empty lines indentation = internal::parser::GetIndentation(line); internal::util::TrimLeft(line, "\t"); internal::util::TrimRight(line, "\n\r"); if (line.empty() || line.front() == '#') continue; // Ignore empty lines and comments if (!internal::parser::HandleIndentation(indentation, players, state)) { std::cerr << "animone: indentation: failed on line " << ln << std::endl; return false; } if (!internal::parser::HandleState(line, players, state)) { std::cerr << "animone: state: failed on line " << ln << std::endl; return false; } } return !players.empty(); } bool ParsePlayersFile(const std::string& path, std::vector<Player>& players) { std::string data; if (!internal::util::ReadFile(path, data)) return false; return ParsePlayersData(data, players); } } // namespace animone