comparison 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
comparison
equal deleted inserted replaced
336:d260549151d6 337:a7d4e5107531
3 3
4 #include <map> 4 #include <map>
5 #include <sstream> 5 #include <sstream>
6 #include <string> 6 #include <string>
7 #include <vector> 7 #include <vector>
8 #include <optional>
9
10 #include <iostream>
8 11
9 namespace animone { 12 namespace animone {
10 13
11 namespace internal::parser { 14 namespace internal::parser {
12 15
13 enum class State { 16 struct State {
14 ExpectPlayerName, 17 enum class Name {
15 ExpectSection, 18 ExpectPlayerName,
16 ExpectWindow, 19 ExpectSection,
17 ExpectExecutable, 20 ExpectWindowPlatform,
18 ExpectStrategy, 21 ExpectExecutablePlatform,
19 ExpectType, 22 ExpectWindow,
20 ExpectWindowTitle, 23 ExpectExecutable,
24 ExpectStrategy,
25 ExpectType,
26 ExpectWindowTitle,
27 };
28
29 Name state = Name::ExpectPlayerName;
30 WindowPlatform window_platform = WindowPlatform::Unknown;
31 ExecutablePlatform executable_platform = ExecutablePlatform::Unknown;
21 }; 32 };
22 33
23 size_t GetIndentation(const std::string& line) { 34 size_t GetIndentation(const std::string& line) {
24 return line.find_first_not_of('\t'); 35 return line.find_first_not_of('\t');
25 } 36 }
26 37
27 bool HandleIndentation(const size_t current, const std::vector<Player>& players, State& state) { 38 bool HandleIndentation(const size_t current, const std::vector<Player>& players, State& state) {
28 // Each state has a definitive expected indentation 39 // Each state has a definitive expected indentation
29 const auto expected = [&state]() -> size_t { 40 const auto expected = [](const State::Name& state) -> size_t {
30 switch (state) { 41 switch (state) {
31 default: 42 default:
32 case State::ExpectPlayerName: return 0; 43 case State::Name::ExpectPlayerName: return 0;
33 case State::ExpectSection: return 1; 44 case State::Name::ExpectSection: return 1;
34 case State::ExpectWindow: 45 case State::Name::ExpectWindowPlatform:
35 case State::ExpectExecutable: 46 case State::Name::ExpectExecutablePlatform:
36 case State::ExpectStrategy: 47 case State::Name::ExpectStrategy:
37 case State::ExpectType: return 2; 48 case State::Name::ExpectType: return 2;
38 case State::ExpectWindowTitle: return 3; 49 case State::Name::ExpectWindow:
39 } 50 case State::Name::ExpectExecutable:
40 }(); 51 case State::Name::ExpectWindowTitle: return 3;
41 52 }
42 if (current > expected) 53 }(state.state);
54
55 if (current > expected) {
56 std::cerr << "animone: excessive indentation found" << std::endl;
43 return false; // Disallow excessive indentation 57 return false; // Disallow excessive indentation
58 }
44 59
45 if (current < expected) { 60 if (current < expected) {
46 auto fix_state = [&]() { state = !current ? State::ExpectPlayerName : State::ExpectSection; }; 61 const std::optional<State::Name> st = [current, state]() -> std::optional<State::Name> {
47 switch (state) { 62 switch (current) {
48 case State::ExpectWindow: 63 case 0: return State::Name::ExpectPlayerName;
64 default:
65 case 1: return State::Name::ExpectSection;
66 case 2:
67 switch (state.state) {
68 case State::Name::ExpectWindow: return State::Name::ExpectWindowPlatform;
69 case State::Name::ExpectExecutable: return State::Name::ExpectExecutablePlatform;
70 default: return std::nullopt;
71 }
72 }
73 }();
74 if (!st.has_value())
75 return false;
76
77 switch (state.state) {
78 case State::Name::ExpectWindow:
49 if (players.back().windows.empty()) 79 if (players.back().windows.empty())
50 return false; 80 return false;
51 fix_state(); 81 state.state = st.value();
52 break; 82 break;
53 case State::ExpectExecutable: 83 case State::Name::ExpectExecutable:
54 if (players.back().executables.empty()) 84 if (players.back().executables.empty())
55 return false; 85 return false;
56 fix_state(); 86 state.state = st.value();
57 break; 87 break;
58 case State::ExpectStrategy: 88 case State::Name::ExpectStrategy:
59 if (players.back().strategies.empty()) 89 if (players.back().strategies.empty())
60 return false; 90 return false;
61 fix_state(); 91 state.state = st.value();
62 break; 92 break;
63 case State::ExpectType: fix_state(); break; 93 case State::Name::ExpectType:
64 case State::ExpectWindowTitle: return false; 94 state.state = st.value();
95 break;
96 case State::Name::ExpectWindowTitle:
97 return false;
65 } 98 }
66 } 99 }
67 100
68 return true; 101 return true;
69 } 102 }
70 103
71 bool HandleState(std::string& line, std::vector<Player>& players, State& state) { 104 bool HandleState(std::string& line, std::vector<Player>& players, State& state) {
72 switch (state) { 105 switch (state.state) {
73 case State::ExpectPlayerName: 106 case State::Name::ExpectPlayerName:
74 players.push_back(Player()); 107 players.push_back(Player());
75 players.back().name = line; 108 players.back().name = line;
76 state = State::ExpectSection; 109 state.state = State::Name::ExpectSection;
77 break; 110 break;
78 111
79 case State::ExpectSection: { 112 case State::Name::ExpectSection: {
80 static const std::map<std::string, State> sections = { 113 static const std::map<std::string, State::Name> sections = {
81 {"windows", State::ExpectWindow }, 114 {"windows", State::Name::ExpectWindowPlatform},
82 {"executables", State::ExpectExecutable}, 115 {"executables", State::Name::ExpectExecutablePlatform},
83 {"strategies", State::ExpectStrategy }, 116 {"strategies", State::Name::ExpectStrategy },
84 {"type", State::ExpectType }, 117 {"type", State::Name::ExpectType },
85 }; 118 };
86 util::TrimRight(line, ":"); 119 util::TrimRight(line, ":");
87 const auto it = sections.find(line); 120 const auto it = sections.find(line);
88 if (it == sections.end()) 121 if (it == sections.end())
89 return false; 122 return false;
90 state = it->second; 123 state.state = it->second;
91 break; 124 break;
92 } 125 }
93 126
94 case State::ExpectWindow: players.back().windows.push_back(line); break; 127 case State::Name::ExpectWindowPlatform: {
95 128 static const std::map<std::string, WindowPlatform> platforms = {
96 case State::ExpectExecutable: players.back().executables.push_back(line); break; 129 {"quartz", WindowPlatform::Quartz},
97 130 {"win32", WindowPlatform::Win32},
98 case State::ExpectStrategy: { 131 {"x11", WindowPlatform::X11},
132 };
133 util::TrimRight(line, ":");
134 const auto it = platforms.find(line);
135 if (it == platforms.end())
136 return false;
137 state.state = State::Name::ExpectWindow;
138 state.window_platform = it->second;
139 break;
140 }
141
142 case State::Name::ExpectExecutablePlatform: {
143 static const std::map<std::string, ExecutablePlatform> platforms = {
144 {"posix", ExecutablePlatform::Posix},
145 {"win32", ExecutablePlatform::Win32},
146 {"macosx", ExecutablePlatform::Xnu},
147 };
148 util::TrimRight(line, ":");
149 const auto it = platforms.find(line);
150 if (it == platforms.end())
151 return false;
152 state.state = State::Name::ExpectExecutable;
153 state.executable_platform = it->second;
154 break;
155 }
156
157 case State::Name::ExpectWindow: players.back().windows[state.window_platform].push_back(line); break;
158
159 case State::Name::ExpectExecutable: players.back().executables[state.executable_platform].push_back(line); break;
160
161 case State::Name::ExpectStrategy: {
99 static const std::map<std::string, Strategy> strategies = { 162 static const std::map<std::string, Strategy> strategies = {
100 {"window_title", Strategy::WindowTitle }, 163 {"window_title", Strategy::WindowTitle },
101 {"open_files", Strategy::OpenFiles }, 164 {"open_files", Strategy::OpenFiles },
102 {"ui_automation", Strategy::UiAutomation}, 165 {"ui_automation", Strategy::UiAutomation},
103 }; 166 };
106 if (it == strategies.end()) 169 if (it == strategies.end())
107 return false; 170 return false;
108 const auto strategy = it->second; 171 const auto strategy = it->second;
109 players.back().strategies.push_back(strategy); 172 players.back().strategies.push_back(strategy);
110 switch (strategy) { 173 switch (strategy) {
111 case Strategy::WindowTitle: state = State::ExpectWindowTitle; break; 174 case Strategy::WindowTitle: state.state = State::Name::ExpectWindowTitle; break;
112 } 175 }
113 break; 176 break;
114 } 177 }
115 178
116 case State::ExpectType: { 179 case State::Name::ExpectType: {
117 static const std::map<std::string, PlayerType> types = { 180 static const std::map<std::string, PlayerType> types = {
118 {"default", PlayerType::Default }, 181 {"default", PlayerType::Default },
119 {"web_browser", PlayerType::WebBrowser}, 182 {"web_browser", PlayerType::WebBrowser},
120 }; 183 };
121 const auto it = types.find(line); 184 const auto it = types.find(line);
123 return false; 186 return false;
124 players.back().type = it->second; 187 players.back().type = it->second;
125 break; 188 break;
126 } 189 }
127 190
128 case State::ExpectWindowTitle: 191 case State::Name::ExpectWindowTitle:
129 players.back().window_title_format = line; 192 players.back().window_title_format = line;
130 state = State::ExpectStrategy; 193 state.state = State::Name::ExpectStrategy;
131 break; 194 break;
132 } 195 }
133 196
134 return true; 197 return true;
135 } 198 }
143 return false; 206 return false;
144 207
145 std::istringstream stream(data); 208 std::istringstream stream(data);
146 std::string line; 209 std::string line;
147 size_t indentation = 0; 210 size_t indentation = 0;
148 auto state = internal::parser::State::ExpectPlayerName; 211 internal::parser::State state;
149 212
150 while (std::getline(stream, line, '\n')) { 213 int ln = 1;
214 for (; std::getline(stream, line, '\n'); ln++) {
151 if (line.empty()) 215 if (line.empty())
152 continue; // Ignore empty lines 216 continue; // Ignore empty lines
153 217
154 indentation = internal::parser::GetIndentation(line); 218 indentation = internal::parser::GetIndentation(line);
155 219
157 internal::util::TrimRight(line, "\n\r"); 221 internal::util::TrimRight(line, "\n\r");
158 222
159 if (line.empty() || line.front() == '#') 223 if (line.empty() || line.front() == '#')
160 continue; // Ignore empty lines and comments 224 continue; // Ignore empty lines and comments
161 225
162 if (!internal::parser::HandleIndentation(indentation, players, state)) 226 if (!internal::parser::HandleIndentation(indentation, players, state)) {
227 std::cerr << "animone: indentation: failed on line " << ln << std::endl;
163 return false; 228 return false;
164 229 }
165 if (!internal::parser::HandleState(line, players, state)) 230
231 if (!internal::parser::HandleState(line, players, state)) {
232 std::cerr << "animone: state: failed on line " << ln << std::endl;
166 return false; 233 return false;
234 }
167 } 235 }
168 236
169 return !players.empty(); 237 return !players.empty();
170 } 238 }
171 239