Mercurial > minori
annotate src/sys/x11/settings.cc @ 364:99c961c91809
core: refactor out byte stream into its own file
easy dubs
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Tue, 16 Jul 2024 21:15:59 -0400 |
parents | 9aaf1e788896 |
children |
rev | line source |
---|---|
351 | 1 #include "sys/x11/settings.h" |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
2 #include "core/byte_stream.h" |
351 | 3 |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
4 #include <cassert> |
351 | 5 #include <cstring> |
6 #include <cstdint> | |
7 #include <climits> | |
8 #include <string_view> | |
9 #include <memory> | |
10 #include <array> | |
11 #include <optional> | |
12 #include <map> | |
13 | |
14 #include <xcb/xcb.h> | |
15 | |
16 #include "fmt/core.h" | |
17 | |
18 namespace x11 { | |
19 | |
20 bool SettingsItem::VerifyType() { | |
21 switch (type) { | |
22 case SettingsItem::TypeInt: | |
23 case SettingsItem::TypeStr: | |
24 case SettingsItem::TypeRgba: | |
25 return true; | |
26 default: | |
27 return false; | |
28 } | |
29 } | |
30 | |
31 /* -------------------------------------------------------------------------- */ | |
32 /* xsettings parser */ | |
33 | |
34 static constexpr std::size_t GetPadding(std::size_t length, std::size_t increment) { | |
35 /* ripped from xsettingsd */ | |
36 return (increment - (length % increment)) % increment; | |
37 } | |
38 | |
39 class Parser { | |
40 public: | |
41 Parser(std::uint8_t *bytes, std::size_t size); | |
42 | |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
43 bool ParseHeader(void); |
351 | 44 std::optional<SettingsItem> ParseNextItem(void); |
45 | |
46 std::uint32_t GetTotalItems(void); | |
47 | |
48 private: | |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
49 enum ByteOrder { |
351 | 50 LSBFirst = 0, |
51 MSBFirst = 1, | |
52 }; | |
53 | |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
54 ByteStream stream; |
351 | 55 |
56 /* parsed in the constructor */ | |
57 std::uint32_t serial_ = 0; | |
58 std::uint32_t total_items_ = 0; | |
59 }; | |
60 | |
61 std::uint32_t Parser::GetTotalItems(void) { | |
62 return total_items_; | |
63 } | |
64 | |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
65 Parser::Parser(std::uint8_t *bytes, std::size_t size) : stream(bytes, size) { |
351 | 66 } |
67 | |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
68 bool Parser::ParseHeader(void) { |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
69 std::uint8_t byte_order; |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
70 if (!stream.ReadBinary<std::uint8_t>(byte_order)) |
351 | 71 return false; |
72 | |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
73 switch (byte_order) { |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
74 case MSBFirst: |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
75 stream.SetEndianness(ByteStream::ByteOrder::Big); |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
76 break; |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
77 case LSBFirst: |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
78 stream.SetEndianness(ByteStream::ByteOrder::Little); |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
79 break; |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
80 default: |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
81 return false; /* errr */ |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
82 } |
351 | 83 |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
84 stream.Advance(3); |
351 | 85 |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
86 if (!stream.ReadInt<std::uint32_t>(serial_)) |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
87 return false; |
351 | 88 |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
89 if (!stream.ReadInt<std::uint32_t>(total_items_)) |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
90 return false; |
351 | 91 |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
92 return true; |
351 | 93 } |
94 | |
95 std::optional<SettingsItem> Parser::ParseNextItem(void) { | |
96 SettingsItem item; | |
97 | |
98 /* read one byte */ | |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
99 if (!stream.ReadInt<std::uint8_t>(item.type)) |
351 | 100 return std::nullopt; |
101 | |
102 if (!item.VerifyType()) | |
103 return std::nullopt; | |
104 | |
105 /* skip padding */ | |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
106 if (!stream.Advance(1)) |
351 | 107 return std::nullopt; |
108 | |
109 /* parse the name */ | |
110 std::uint16_t name_size; | |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
111 if (!stream.ReadInt<std::uint16_t>(name_size)) |
351 | 112 return std::nullopt; |
113 | |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
114 if (!stream.ReadString(item.name, name_size)) |
351 | 115 return std::nullopt; |
116 | |
117 /* padding */ | |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
118 if (!stream.Advance(GetPadding(name_size, 4))) |
351 | 119 return std::nullopt; |
120 | |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
121 if (!stream.ReadInt<std::uint32_t>(item.serial)) |
351 | 122 return std::nullopt; |
123 | |
124 switch (item.type) { | |
125 case SettingsItem::TypeInt: { | |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
126 if (!stream.ReadInt<std::int32_t>(item.data.integer)) |
351 | 127 return std::nullopt; |
128 | |
129 break; | |
130 } | |
131 case SettingsItem::TypeStr: { | |
132 std::uint32_t size; | |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
133 if (!stream.ReadInt<std::uint32_t>(size)) |
351 | 134 return std::nullopt; |
135 | |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
136 if (!stream.ReadString(item.data.string, size)) |
351 | 137 return std::nullopt; |
138 | |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
139 /* don't fail if advancing fails on this padding, |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
140 * because this causes parsing to fail for strings |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
141 * at the end of the data */ |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
142 stream.Advance(GetPadding(size, 4)); |
351 | 143 |
144 break; | |
145 } | |
146 case SettingsItem::TypeRgba: { | |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
147 /* it's actually RBGA, but whatever. */ |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
148 if (!stream.ReadInt<std::uint16_t>(item.data.rgba.red) |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
149 || !stream.ReadInt<std::uint16_t>(item.data.rgba.blue) |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
150 || !stream.ReadInt<std::uint16_t>(item.data.rgba.green) |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
151 || !stream.ReadInt<std::uint16_t>(item.data.rgba.alpha)) |
351 | 152 return std::nullopt; |
153 | |
154 break; | |
155 } | |
156 default: | |
157 /* can't do anything now, can we? */ | |
158 return std::nullopt; | |
159 } | |
160 | |
161 return item; | |
162 } | |
163 | |
164 /* ------------------------------------------------------------------------- */ | |
165 /* real X11 code */ | |
166 | |
167 template<typename T> | |
168 struct MallocDestructor { | |
169 void operator()(T *t) const { std::free(t); }; | |
170 }; | |
171 | |
172 struct XcbConnectionDestructor { | |
173 void operator()(xcb_connection_t *conn) const { ::xcb_disconnect(conn); }; | |
174 }; | |
175 | |
176 template<typename T> | |
177 using MallocPtr = std::unique_ptr<T, MallocDestructor<T>>; | |
178 | |
179 using XcbConnectionPtr = std::unique_ptr<xcb_connection_t, XcbConnectionDestructor>; | |
180 | |
181 /* RAII is nice */ | |
182 struct XcbGrabber { | |
183 XcbGrabber(::xcb_connection_t *conn) { ::xcb_grab_server(conn); conn_ = conn; } | |
184 ~XcbGrabber() { ::xcb_ungrab_server(conn_); } | |
185 | |
186 private: | |
187 ::xcb_connection_t *conn_; | |
188 }; | |
189 | |
190 static ::xcb_window_t GetSelectionOwner(::xcb_connection_t *conn, ::xcb_atom_t selection) { | |
191 ::xcb_window_t owner = XCB_NONE; | |
192 MallocPtr<::xcb_get_selection_owner_reply_t> reply(::xcb_get_selection_owner_reply(conn, ::xcb_get_selection_owner(conn, selection), nullptr)); | |
193 | |
194 if (reply) | |
195 owner = reply->owner; | |
196 | |
197 return owner; | |
198 } | |
199 | |
200 static bool GetRawSettingsData(std::vector<uint8_t>& bytes) { | |
201 int screen; | |
202 | |
203 XcbConnectionPtr conn(::xcb_connect(nullptr, &screen)); | |
204 if (::xcb_connection_has_error(conn.get())) | |
205 return false; | |
206 | |
207 /* get our needed atoms, available as atoms[Atom] */ | |
208 enum Atom { | |
209 XSETTINGS_SCREEN, /* _XSETTINGS_S[N] */ | |
210 XSETTINGS_SETTINGS, /* _XSETTINGS_SETTINGS */ | |
211 }; | |
212 | |
213 std::map<Atom, ::xcb_atom_t> atoms; | |
214 { | |
215 std::map<Atom, std::string> names = { | |
216 {XSETTINGS_SCREEN, fmt::format("_XSETTINGS_S{}", screen)}, | |
217 {XSETTINGS_SETTINGS, "_XSETTINGS_SETTINGS"}, | |
218 }; | |
219 | |
220 std::map<Atom, ::xcb_intern_atom_cookie_t> atom_cookies; | |
221 for (const auto& name : names) | |
222 atom_cookies[name.first] = ::xcb_intern_atom(conn.get(), false, name.second.size(), name.second.data()); | |
223 | |
224 for (const auto& cookie : atom_cookies) { | |
225 MallocPtr<::xcb_intern_atom_reply_t> reply(::xcb_intern_atom_reply(conn.get(), cookie.second, nullptr)); | |
226 if (!reply || reply->atom == XCB_NONE) | |
227 return false; | |
228 | |
229 atoms[cookie.first] = reply->atom; | |
230 } | |
231 } | |
232 | |
233 MallocPtr<xcb_get_property_reply_t> reply; | |
234 { | |
235 /* grab the X server as *required* by xsettings docs */ | |
236 const XcbGrabber grabber(conn.get()); | |
237 | |
238 ::xcb_window_t win = GetSelectionOwner(conn.get(), atoms[XSETTINGS_SCREEN]); | |
239 if (win == XCB_NONE) | |
240 return false; | |
241 | |
242 reply.reset(::xcb_get_property_reply(conn.get(), ::xcb_get_property(conn.get(), 0, win, atoms[XSETTINGS_SETTINGS], XCB_ATOM_ANY, 0L, UINT_MAX), nullptr)); | |
243 }; | |
244 if (!reply) | |
245 return false; | |
246 | |
247 uint8_t *data = reinterpret_cast<uint8_t *>(xcb_get_property_value(reply.get())); | |
248 int size = xcb_get_property_value_length(reply.get()); | |
249 if (size < 0) | |
250 return false; | |
251 | |
252 bytes.assign(data, data + size); | |
253 | |
254 return true; | |
255 } | |
256 | |
257 /* ------------------------------------------------------------------------- */ | |
258 /* now for the actual all-important public API stringing all this together */ | |
259 | |
260 bool GetSettings(std::vector<SettingsItem>& settings) { | |
261 std::vector<std::uint8_t> xsettings_raw; | |
262 if (!GetRawSettingsData(xsettings_raw)) | |
263 return false; | |
264 | |
265 Parser parser(xsettings_raw.data(), xsettings_raw.size()); | |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
266 if (!parser.ParseHeader()) |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
267 return false; |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
268 |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
269 std::uint32_t total = parser.GetTotalItems(); |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
270 |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
271 while (total--) { |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
272 std::optional<SettingsItem> opt_item = parser.ParseNextItem(); |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
273 if (!opt_item) |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
274 break; |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
275 |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
276 settings.push_back(opt_item.value()); |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
277 } |
351 | 278 |
279 return true; | |
280 } | |
281 | |
282 bool FindSetting(const std::string& name, SettingsItem& setting) { | |
283 std::vector<std::uint8_t> xsettings_raw; | |
284 if (!GetRawSettingsData(xsettings_raw)) | |
285 return false; | |
286 | |
287 Parser parser(xsettings_raw.data(), xsettings_raw.size()); | |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
288 if (!parser.ParseHeader()) |
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
289 return false; |
351 | 290 |
291 std::uint32_t total = parser.GetTotalItems(); | |
292 | |
364
99c961c91809
core: refactor out byte stream into its own file
Paper <paper@paper.us.eu.org>
parents:
354
diff
changeset
|
293 while (total--) { |
351 | 294 std::optional<SettingsItem> opt_item = parser.ParseNextItem(); |
295 if (!opt_item) | |
296 return false; | |
297 | |
298 SettingsItem& item = opt_item.value(); | |
299 if (item.name == name) { | |
300 setting = item; | |
301 return true; | |
302 } | |
303 } | |
304 | |
305 return false; | |
306 } | |
307 | |
308 } // namespace x11 |