fe1280eb497a33d7eef29605fdc662a5fdec0f01
1 #include <algorithm>
2 #include <cinttypes>
3 #include <cstdio>
4 #include <iostream>
5 #include <string>
6 #include <unistd.h>
8 #include <kms++/kms++.h>
9 #include <kms++util/kms++util.h>
11 using namespace std;
12 using namespace kms;
14 static struct {
15 bool print_props;
16 bool print_modes;
17 bool print_list;
18 bool x_modeline;
19 } s_opts;
21 static string format_mode(const Videomode& m, unsigned idx)
22 {
23 string str;
25 str = sformat(" %2u ", idx);
27 if (s_opts.x_modeline) {
28 str += sformat("%12s %6d %4u %4u %4u %4u %4u %4u %4u %4u %2u %#x %#x",
29 m.name.c_str(),
30 m.clock,
31 m.hdisplay, m.hsync_start, m.hsync_end, m.htotal,
32 m.vdisplay, m.vsync_start, m.vsync_end, m.vtotal,
33 m.vrefresh,
34 m.flags,
35 m.type);
36 } else {
37 string h = sformat("%u/%u/%u/%u", m.hdisplay, m.hfp(), m.hsw(), m.hbp());
38 string v = sformat("%u/%u/%u/%u", m.vdisplay, m.vfp(), m.vsw(), m.vbp());
40 str += sformat("%-12s %6d %-16s %-16s %2u %#10x %#6x",
41 m.name.c_str(),
42 m.clock,
43 h.c_str(), v.c_str(),
44 m.vrefresh,
45 m.flags,
46 m.type);
47 }
49 return str;
50 }
52 static string format_mode_short(const Videomode& m)
53 {
54 string h = sformat("%u/%u/%u/%u", m.hdisplay, m.hfp(), m.hsw(), m.hbp());
55 string v = sformat("%u/%u/%u/%u", m.vdisplay, m.vfp(), m.vsw(), m.vbp());
57 return sformat("%s %d %s %s %u",
58 m.name.c_str(),
59 m.clock,
60 h.c_str(), v.c_str(),
61 m.vrefresh);
62 }
64 static string format_connector(Connector& c)
65 {
66 string str;
68 str = sformat("Connector %u (%u) %s",
69 c.idx(), c.id(), c.fullname().c_str());
71 if (c.connected())
72 str += " (connected)";
74 return str;
75 }
77 static string format_encoder(Encoder& e)
78 {
79 return sformat("Encoder %u (%u) %s",
80 e.idx(), e.id(), e.get_encoder_type().c_str());
81 }
83 static string format_crtc(Crtc& c)
84 {
85 string str;
87 str = sformat("Crtc %u (%u)", c.idx(), c.id());
89 if (c.mode_valid())
90 str += " " + format_mode_short(c.mode());
92 return str;
93 }
95 static string format_plane(Plane& p)
96 {
97 string str;
99 str = sformat("Plane %u (%u)", p.idx(), p.id());
101 if (p.fb_id())
102 str += sformat(" fb-id: %u", p.fb_id());
104 if (p.card().has_atomic()) {
105 str += sformat(" %u,%u %ux%u -> %u,%u %ux%u",
106 (uint32_t)p.get_prop_value("SRC_X") >> 16,
107 (uint32_t)p.get_prop_value("SRC_Y") >> 16,
108 (uint32_t)p.get_prop_value("SRC_W") >> 16,
109 (uint32_t)p.get_prop_value("SRC_H") >> 16,
110 (uint32_t)p.get_prop_value("CRTC_X"),
111 (uint32_t)p.get_prop_value("CRTC_Y"),
112 (uint32_t)p.get_prop_value("CRTC_W"),
113 (uint32_t)p.get_prop_value("CRTC_H"));
114 }
116 string fmts = join<PixelFormat>(p.get_formats(), " ", [](PixelFormat fmt) { return PixelFormatToFourCC(fmt); });
118 str += sformat(" (%s)", fmts.c_str());
120 return str;
121 }
123 static string format_fb(Framebuffer& fb)
124 {
125 return sformat("FB %u %ux%u",
126 fb.id(), fb.width(), fb.height());
127 }
129 static string format_property(const Property* prop, uint64_t val)
130 {
131 string ret = sformat("%s = ", prop->name().c_str());
133 switch (prop->type()) {
134 case PropertyType::Bitmask:
135 {
136 vector<string> v, vall;
138 for (auto kvp : prop->get_enums()) {
139 if (val & (1 << kvp.first))
140 v.push_back(kvp.second);
141 vall.push_back(kvp.second);
142 }
144 ret += sformat("%s (%s)", join(v, "|").c_str(), join(vall, "|").c_str());
146 break;
147 }
149 case PropertyType::Blob:
150 {
151 uint32_t blob_id = (uint32_t)val;
153 if (blob_id) {
154 Blob blob(prop->card(), blob_id);
155 auto data = blob.data();
157 ret += sformat("blob-id %u len %zu", blob_id, data.size());
158 } else {
159 ret += sformat("blob-id %u", blob_id);
160 }
162 break;
163 }
165 case PropertyType::Enum:
166 {
167 string cur;
168 vector<string> vall;
170 for (auto kvp : prop->get_enums()) {
171 if (val == kvp.first)
172 cur = kvp.second;
173 vall.push_back(kvp.second);
174 }
176 ret += sformat("%s (%s)", cur.c_str(), join(vall, "|").c_str());
178 break;
179 }
181 case PropertyType::Object:
182 {
183 ret += sformat("object id %u", (uint32_t)val);
184 break;
185 }
187 case PropertyType::Range:
188 {
189 auto values = prop->get_values();
191 ret += sformat("%" PRIu64 " [%" PRIu64 " - %" PRIu64 "]",
192 val, values[0], values[1]);
194 break;
195 }
197 case PropertyType::SignedRange:
198 {
199 auto values = prop->get_values();
201 ret += sformat("%" PRIi64 " [%" PRIi64 " - %" PRIi64 "]",
202 (int64_t)val, (int64_t)values[0], (int64_t)values[1]);
204 break;
205 }
207 }
209 if (prop->is_pending())
210 ret += " (pending)";
211 if (prop->is_immutable())
212 ret += " (immutable)";
214 return ret;
215 }
217 static vector<string> format_props(DrmPropObject* o)
218 {
219 vector<string> lines;
221 auto pmap = o->get_prop_map();
222 for (auto pp : pmap) {
223 const Property* p = o->card().get_prop(pp.first);
224 lines.push_back(format_property(p, pp.second));
225 }
227 return lines;
228 }
230 static string format_ob(DrmObject* ob)
231 {
232 if (auto o = dynamic_cast<Connector*>(ob))
233 return format_connector(*o);
234 else if (auto o = dynamic_cast<Encoder*>(ob))
235 return format_encoder(*o);
236 else if (auto o = dynamic_cast<Crtc*>(ob))
237 return format_crtc(*o);
238 else if (auto o = dynamic_cast<Plane*>(ob))
239 return format_plane(*o);
240 else if (auto o = dynamic_cast<Framebuffer*>(ob))
241 return format_fb(*o);
242 else
243 EXIT("Unkown DRM Object type\n");
244 }
246 template<class T>
247 vector<T> filter(const vector<T>& sequence, function<bool(T)> predicate)
248 {
249 vector<T> result;
251 for(auto it = sequence.begin(); it != sequence.end(); ++it)
252 if(predicate(*it))
253 result.push_back(*it);
255 return result;
256 }
258 struct Entry
259 {
260 string title;
261 vector<string> lines;
262 vector<Entry> children;
263 };
265 static Entry& add_entry(vector<Entry>& entries)
266 {
267 entries.emplace_back();
268 return entries.back();
269 }
270 /*
271 static bool on_tty()
272 {
273 return isatty(STDOUT_FILENO) > 0;
274 }
275 */
276 enum class TreeGlyphMode {
277 None,
278 ASCII,
279 UTF8,
280 };
282 static TreeGlyphMode s_glyph_mode = TreeGlyphMode::None;
284 enum class TreeGlyph {
285 Vertical,
286 Branch,
287 Right,
288 Space,
289 };
291 static const map<TreeGlyph, string> glyphs_utf8 = {
292 { TreeGlyph::Vertical, "│ " },
293 { TreeGlyph::Branch, "├─" },
294 { TreeGlyph::Right, "└─" },
295 { TreeGlyph::Space, " " },
297 };
299 static const map<TreeGlyph, string> glyphs_ascii = {
300 { TreeGlyph::Vertical, "| " },
301 { TreeGlyph::Branch, "|-" },
302 { TreeGlyph::Right, "`-" },
303 { TreeGlyph::Space, " " },
305 };
307 const char* get_glyph(TreeGlyph glyph)
308 {
309 if (s_glyph_mode == TreeGlyphMode::None)
310 return " ";
312 const map<TreeGlyph, string>& glyphs = s_glyph_mode == TreeGlyphMode::UTF8 ? glyphs_utf8 : glyphs_ascii;
314 return glyphs.at(glyph).c_str();
315 }
317 static void print_entry(const Entry& e, const string& prefix, bool is_child, bool is_last)
318 {
319 string prefix1;
320 string prefix2;
322 if (is_child) {
323 prefix1 = prefix + (is_last ? get_glyph(TreeGlyph::Right) : get_glyph(TreeGlyph::Branch));
324 prefix2 = prefix + (is_last ? get_glyph(TreeGlyph::Space) : get_glyph(TreeGlyph::Vertical));
325 }
327 printf("%s%s\n", prefix1.c_str(), e.title.c_str());
329 bool has_children = e.children.size() > 0;
331 string data_prefix = prefix2 + (has_children ? get_glyph(TreeGlyph::Vertical) : get_glyph(TreeGlyph::Space));
333 for (const string& str : e.lines) {
334 string p = data_prefix + get_glyph(TreeGlyph::Space);
335 printf("%s%s\n", p.c_str(), str.c_str());
336 }
338 for (const Entry& child : e.children) {
339 bool is_last = &child == &e.children.back();
341 print_entry(child, prefix2, true, is_last);
342 }
343 }
345 static void print_entries(const vector<Entry>& entries, const string& prefix)
346 {
347 for (const Entry& e: entries) {
348 print_entry(e, "", false, false);
349 }
350 }
352 template<class T>
353 static void append(vector<DrmObject*>& dst, const vector<T*>& src)
354 {
355 dst.insert(dst.end(), src.begin(), src.end());
356 }
359 static void print_as_list(Card& card)
360 {
361 vector<DrmPropObject*> obs;
362 vector<Framebuffer*> fbs;
364 for (Connector* conn : card.get_connectors()) {
365 obs.push_back(conn);
366 }
368 for (Encoder* enc : card.get_encoders()) {
369 obs.push_back(enc);
370 }
372 for (Crtc* crtc : card.get_crtcs()) {
373 obs.push_back(crtc);
374 if (crtc->buffer_id() && !card.has_has_universal_planes()) {
375 Framebuffer* fb = new Framebuffer(card, crtc->buffer_id());
376 fbs.push_back(fb);
377 }
378 }
380 for (Plane* plane : card.get_planes()) {
381 obs.push_back(plane);
382 if (plane->fb_id()) {
383 Framebuffer* fb = new Framebuffer(card, plane->fb_id());
384 fbs.push_back(fb);
385 }
386 }
388 for (DrmPropObject* ob: obs) {
389 printf("%s\n", format_ob(ob).c_str());
391 if (s_opts.print_props) {
392 for (string str : format_props(ob))
393 printf(" %s\n", str.c_str());
394 }
395 }
397 for (Framebuffer* fb: fbs) {
398 printf("%s\n", format_ob(fb).c_str());
399 }
400 }
402 static void print_as_tree(Card& card)
403 {
404 vector<Entry> entries;
406 for (Connector* conn : card.get_connectors()) {
407 if (!conn->connected())
408 continue;
410 Entry& e1 = add_entry(entries);
411 e1.title = format_ob(conn);
412 if (s_opts.print_props)
413 e1.lines = format_props(conn);
415 for (Encoder* enc : conn->get_encoders()) {
417 Entry& e2 = add_entry(e1.children);
418 e2.title = format_ob(enc);
419 if (s_opts.print_props)
420 e2.lines = format_props(enc);
422 if (Crtc* crtc = enc->get_crtc()) {
423 Entry& e3 = add_entry(e2.children);
424 e3.title = format_ob(crtc);
425 if (s_opts.print_props)
426 e3.lines = format_props(crtc);
428 if (crtc->buffer_id() && !card.has_has_universal_planes()) {
429 Framebuffer fb(card, crtc->buffer_id());
430 Entry& e5 = add_entry(e3.children);
432 e5.title = format_ob(&fb);
433 }
435 for (Plane* plane : card.get_planes()) {
436 if (plane->crtc_id() != crtc->id())
437 continue;
439 Entry& e4 = add_entry(e3.children);
440 e4.title = format_ob(plane);
441 if (s_opts.print_props)
442 e4.lines = format_props(plane);
444 uint32_t fb_id = plane->fb_id();
445 if (fb_id) {
446 Framebuffer fb(card, fb_id);
448 Entry& e5 = add_entry(e4.children);
450 e5.title = format_ob(&fb);
451 }
452 }
453 }
454 }
455 }
457 print_entries(entries, "");
458 }
460 static void print_modes(Card& card)
461 {
462 for (Connector* conn : card.get_connectors()) {
463 if (!conn->connected())
464 continue;
466 printf("%s\n", format_ob(conn).c_str());
468 auto modes = conn->get_modes();
469 for (unsigned i = 0; i < modes.size(); ++i)
470 printf("%s\n", format_mode(modes[i], i).c_str());
471 }
472 }
474 static const char* usage_str =
475 "Usage: kmsprint [OPTIONS]\n\n"
476 "Options:\n"
477 " -l, --list Print list instead of tree\n"
478 " -m, --modes Print modes\n"
479 " --xmode Print modes using X modeline\n"
480 " -p, --props Print properties\n"
481 ;
483 static void usage()
484 {
485 puts(usage_str);
486 }
488 int main(int argc, char **argv)
489 {
490 string dev_path = "/dev/dri/card0";
492 OptionSet optionset = {
493 Option("|device=", [&dev_path](string s)
494 {
495 dev_path = s;
496 }),
497 Option("l|list", []()
498 {
499 s_opts.print_list = true;
500 }),
501 Option("m|modes", []()
502 {
503 s_opts.print_modes = true;
504 }),
505 Option("p|props", []()
506 {
507 s_opts.print_props = true;
508 }),
509 Option("|xmode", []() {
510 s_opts.x_modeline = true;
511 }),
512 Option("h|help", []()
513 {
514 usage();
515 exit(-1);
516 }),
517 };
519 optionset.parse(argc, argv);
521 if (optionset.params().size() > 0) {
522 usage();
523 exit(-1);
524 }
526 Card card(dev_path);
528 if (s_opts.print_modes) {
529 print_modes(card);
530 return 0;
531 }
533 if (s_opts.print_list)
534 print_as_list(card);
535 else
536 print_as_tree(card);
537 }