cecba5ca728edbc0ef55ba9c8f833769c15b7344
1 #include <cstdio>
2 #include <algorithm>
3 #include <iostream>
4 #include <string>
5 #include <unistd.h>
6 #include <inttypes.h>
8 #include "kms++.h"
9 #include "opts.h"
10 #include "kms++util.h"
11 #include "strhelpers.h"
13 using namespace std;
14 using namespace kms;
16 static struct {
17 bool print_props;
18 bool print_modes;
19 bool print_list;
20 bool x_modeline;
21 } s_opts;
23 static string format_mode(const Videomode& m, unsigned idx)
24 {
25 string str;
27 str = sformat(" %2u ", idx);
29 if (s_opts.x_modeline) {
30 str += sformat("%12s %6d %4u %4u %4u %4u %4u %4u %4u %4u %2u %#x %#x",
31 m.name.c_str(),
32 m.clock,
33 m.hdisplay, m.hsync_start, m.hsync_end, m.htotal,
34 m.vdisplay, m.vsync_start, m.vsync_end, m.vtotal,
35 m.vrefresh,
36 m.flags,
37 m.type);
38 } else {
39 string h = sformat("%u/%u/%u/%u", m.hdisplay, m.hfp(), m.hsw(), m.hbp());
40 string v = sformat("%u/%u/%u/%u", m.vdisplay, m.vfp(), m.vsw(), m.vbp());
42 str += sformat("%-12s %6d %-16s %-16s %2u %#10x %#6x",
43 m.name.c_str(),
44 m.clock,
45 h.c_str(), v.c_str(),
46 m.vrefresh,
47 m.flags,
48 m.type);
49 }
51 return str;
52 }
54 static string format_mode_short(const Videomode& m)
55 {
56 string h = sformat("%u/%u/%u/%u", m.hdisplay, m.hfp(), m.hsw(), m.hbp());
57 string v = sformat("%u/%u/%u/%u", m.vdisplay, m.vfp(), m.vsw(), m.vbp());
59 return sformat("%s %d %s %s %u",
60 m.name.c_str(),
61 m.clock,
62 h.c_str(), v.c_str(),
63 m.vrefresh);
64 }
66 static string format_connector(Connector& c)
67 {
68 string str;
70 str = sformat("Connector %u (%u) %s",
71 c.idx(), c.id(), c.fullname().c_str());
73 if (c.connected())
74 str += " (connected)";
76 return str;
77 }
79 static string format_encoder(Encoder& e)
80 {
81 return sformat("Encoder %u (%u) %s",
82 e.idx(), e.id(), e.get_encoder_type().c_str());
83 }
85 static string format_crtc(Crtc& c)
86 {
87 string str;
89 str = sformat("Crtc %u (%u)", c.idx(), c.id());
91 if (c.mode_valid())
92 str += " " + format_mode_short(c.mode());
94 return str;
95 }
97 static string format_plane(Plane& p)
98 {
99 string str;
101 str = sformat("Plane %u (%u)", p.idx(), p.id());
103 if (p.fb_id())
104 str += sformat(" fb-id: %u", p.fb_id());
106 if (p.card().has_atomic()) {
107 str += sformat(" %u,%u %ux%u -> %u,%u %ux%u",
108 (uint32_t)p.get_prop_value("SRC_X") >> 16,
109 (uint32_t)p.get_prop_value("SRC_Y") >> 16,
110 (uint32_t)p.get_prop_value("SRC_W") >> 16,
111 (uint32_t)p.get_prop_value("SRC_H") >> 16,
112 (uint32_t)p.get_prop_value("CRTC_X"),
113 (uint32_t)p.get_prop_value("CRTC_Y"),
114 (uint32_t)p.get_prop_value("CRTC_W"),
115 (uint32_t)p.get_prop_value("CRTC_H"));
116 }
118 return str;
119 }
121 static string format_fb(Framebuffer& fb)
122 {
123 return sformat("FB %u %ux%u",
124 fb.id(), fb.width(), fb.height());
125 }
127 static string format_property(const Property* prop, uint64_t val)
128 {
129 string ret = sformat("%s = ", prop->name().c_str());
131 switch (prop->type()) {
132 case PropertyType::Bitmask:
133 {
134 vector<string> v, vall;
136 for (auto kvp : prop->get_enums()) {
137 if (val & (1 << kvp.first))
138 v.push_back(kvp.second);
139 vall.push_back(kvp.second);
140 }
142 ret += sformat("%s (%s)", join(v, "|").c_str(), join(vall, "|").c_str());
144 break;
145 }
147 case PropertyType::Blob:
148 {
149 uint32_t blob_id = (uint32_t)val;
151 if (blob_id) {
152 Blob blob(prop->card(), blob_id);
153 auto data = blob.data();
155 ret += sformat("blob-id %u len %zu", blob_id, data.size());
156 } else {
157 ret += sformat("blob-id %u", blob_id);
158 }
160 break;
161 }
163 case PropertyType::Enum:
164 {
165 string cur;
166 vector<string> vall;
168 for (auto kvp : prop->get_enums()) {
169 if (val == kvp.first)
170 cur = kvp.second;
171 vall.push_back(kvp.second);
172 }
174 ret += sformat("%s (%s)", cur.c_str(), join(vall, "|").c_str());
176 break;
177 }
179 case PropertyType::Object:
180 {
181 ret += sformat("object id %u", (uint32_t)val);
182 break;
183 }
185 case PropertyType::Range:
186 {
187 auto values = prop->get_values();
189 ret += sformat("%" PRIu64 " [%" PRIu64 " - %" PRIu64 "]",
190 val, values[0], values[1]);
192 break;
193 }
195 case PropertyType::SignedRange:
196 {
197 auto values = prop->get_values();
199 ret += sformat("%" PRIi64 " [%" PRIi64 " - %" PRIi64 "]",
200 (int64_t)val, (int64_t)values[0], (int64_t)values[1]);
202 break;
203 }
205 }
207 if (prop->is_pending())
208 ret += " (pending)";
209 if (prop->is_immutable())
210 ret += " (immutable)";
212 return ret;
213 }
215 static vector<string> format_props(DrmPropObject* o)
216 {
217 vector<string> lines;
219 auto pmap = o->get_prop_map();
220 for (auto pp : pmap) {
221 const Property* p = o->card().get_prop(pp.first);
222 lines.push_back(format_property(p, pp.second));
223 }
225 return lines;
226 }
228 static string format_ob(DrmObject* ob)
229 {
230 if (auto o = dynamic_cast<Connector*>(ob))
231 return format_connector(*o);
232 else if (auto o = dynamic_cast<Encoder*>(ob))
233 return format_encoder(*o);
234 else if (auto o = dynamic_cast<Crtc*>(ob))
235 return format_crtc(*o);
236 else if (auto o = dynamic_cast<Plane*>(ob))
237 return format_plane(*o);
238 else if (auto o = dynamic_cast<Framebuffer*>(ob))
239 return format_fb(*o);
240 else
241 EXIT("Unkown DRM Object type\n");
242 }
244 template<class T>
245 vector<T> filter(const vector<T>& sequence, function<bool(T)> predicate)
246 {
247 vector<T> result;
249 for(auto it = sequence.begin(); it != sequence.end(); ++it)
250 if(predicate(*it))
251 result.push_back(*it);
253 return result;
254 }
256 struct Entry
257 {
258 string title;
259 vector<string> lines;
260 vector<Entry> children;
261 };
263 static Entry& add_entry(vector<Entry>& entries)
264 {
265 entries.emplace_back();
266 return entries.back();
267 }
268 /*
269 static bool on_tty()
270 {
271 return isatty(STDOUT_FILENO) > 0;
272 }
273 */
274 enum class TreeGlyphMode {
275 None,
276 ASCII,
277 UTF8,
278 };
280 static TreeGlyphMode s_glyph_mode = TreeGlyphMode::None;
282 enum class TreeGlyph {
283 Vertical,
284 Branch,
285 Right,
286 Space,
287 };
289 static const map<TreeGlyph, string> glyphs_utf8 = {
290 { TreeGlyph::Vertical, "│ " },
291 { TreeGlyph::Branch, "├─" },
292 { TreeGlyph::Right, "└─" },
293 { TreeGlyph::Space, " " },
295 };
297 static const map<TreeGlyph, string> glyphs_ascii = {
298 { TreeGlyph::Vertical, "| " },
299 { TreeGlyph::Branch, "|-" },
300 { TreeGlyph::Right, "`-" },
301 { TreeGlyph::Space, " " },
303 };
305 const char* get_glyph(TreeGlyph glyph)
306 {
307 if (s_glyph_mode == TreeGlyphMode::None)
308 return " ";
310 const map<TreeGlyph, string>& glyphs = s_glyph_mode == TreeGlyphMode::UTF8 ? glyphs_utf8 : glyphs_ascii;
312 return glyphs.at(glyph).c_str();
313 }
315 static void print_entry(const Entry& e, const string& prefix, bool is_child, bool is_last)
316 {
317 string prefix1;
318 string prefix2;
320 if (is_child) {
321 prefix1 = prefix + (is_last ? get_glyph(TreeGlyph::Right) : get_glyph(TreeGlyph::Branch));
322 prefix2 = prefix + (is_last ? get_glyph(TreeGlyph::Space) : get_glyph(TreeGlyph::Vertical));
323 }
325 printf("%s%s\n", prefix1.c_str(), e.title.c_str());
327 bool has_children = e.children.size() > 0;
329 string data_prefix = prefix2 + (has_children ? get_glyph(TreeGlyph::Vertical) : get_glyph(TreeGlyph::Space));
331 for (const string& str : e.lines) {
332 string p = data_prefix + get_glyph(TreeGlyph::Space);
333 printf("%s%s\n", p.c_str(), str.c_str());
334 }
336 for (const Entry& child : e.children) {
337 bool is_last = &child == &e.children.back();
339 print_entry(child, prefix2, true, is_last);
340 }
341 }
343 static void print_entries(const vector<Entry>& entries, const string& prefix)
344 {
345 for (const Entry& e: entries) {
346 print_entry(e, "", false, false);
347 }
348 }
350 template<class T>
351 static void append(vector<DrmObject*>& dst, const vector<T*>& src)
352 {
353 dst.insert(dst.end(), src.begin(), src.end());
354 }
357 static void print_as_list(Card& card)
358 {
359 vector<DrmPropObject*> obs;
360 vector<Framebuffer*> fbs;
362 for (Connector* conn : card.get_connectors()) {
363 obs.push_back(conn);
364 }
366 for (Encoder* enc : card.get_encoders()) {
367 obs.push_back(enc);
368 }
370 for (Crtc* crtc : card.get_crtcs()) {
371 obs.push_back(crtc);
372 if (crtc->buffer_id() && !card.has_has_universal_planes()) {
373 Framebuffer* fb = new Framebuffer(card, crtc->buffer_id());
374 fbs.push_back(fb);
375 }
376 }
378 for (Plane* plane : card.get_planes()) {
379 obs.push_back(plane);
380 if (plane->fb_id()) {
381 Framebuffer* fb = new Framebuffer(card, plane->fb_id());
382 fbs.push_back(fb);
383 }
384 }
386 for (DrmPropObject* ob: obs) {
387 printf("%s\n", format_ob(ob).c_str());
389 if (s_opts.print_props) {
390 for (string str : format_props(ob))
391 printf(" %s\n", str.c_str());
392 }
393 }
395 for (Framebuffer* fb: fbs) {
396 printf("%s\n", format_ob(fb).c_str());
397 }
398 }
400 static void print_as_tree(Card& card)
401 {
402 vector<Entry> entries;
404 for (Connector* conn : card.get_connectors()) {
405 if (!conn->connected())
406 continue;
408 Entry& e1 = add_entry(entries);
409 e1.title = format_ob(conn);
410 if (s_opts.print_props)
411 e1.lines = format_props(conn);
413 for (Encoder* enc : conn->get_encoders()) {
415 Entry& e2 = add_entry(e1.children);
416 e2.title = format_ob(enc);
417 if (s_opts.print_props)
418 e2.lines = format_props(enc);
420 if (Crtc* crtc = enc->get_crtc()) {
421 Entry& e3 = add_entry(e2.children);
422 e3.title = format_ob(crtc);
423 if (s_opts.print_props)
424 e3.lines = format_props(crtc);
426 if (crtc->buffer_id() && !card.has_has_universal_planes()) {
427 Framebuffer fb(card, crtc->buffer_id());
428 Entry& e5 = add_entry(e3.children);
430 e5.title = format_ob(&fb);
431 }
433 for (Plane* plane : card.get_planes()) {
434 if (plane->crtc_id() != crtc->id())
435 continue;
437 Entry& e4 = add_entry(e3.children);
438 e4.title = format_ob(plane);
439 if (s_opts.print_props)
440 e4.lines = format_props(plane);
442 uint32_t fb_id = plane->fb_id();
443 if (fb_id) {
444 Framebuffer fb(card, fb_id);
446 Entry& e5 = add_entry(e4.children);
448 e5.title = format_ob(&fb);
449 }
450 }
451 }
452 }
453 }
455 print_entries(entries, "");
456 }
458 static void print_modes(Card& card)
459 {
460 for (Connector* conn : card.get_connectors()) {
461 if (!conn->connected())
462 continue;
464 printf("%s\n", format_ob(conn).c_str());
466 auto modes = conn->get_modes();
467 for (unsigned i = 0; i < modes.size(); ++i)
468 printf("%s\n", format_mode(modes[i], i).c_str());
469 }
470 }
472 static const char* usage_str =
473 "Usage: kmsprint [OPTIONS]\n\n"
474 "Options:\n"
475 " -l, --list Print list instead of tree\n"
476 " -m, --modes Print modes\n"
477 " --xmode Print modes using X modeline\n"
478 " -p, --props Print properties\n"
479 ;
481 static void usage()
482 {
483 puts(usage_str);
484 }
486 int main(int argc, char **argv)
487 {
488 string dev_path = "/dev/dri/card0";
490 OptionSet optionset = {
491 Option("|device=", [&dev_path](string s)
492 {
493 dev_path = s;
494 }),
495 Option("l|list", []()
496 {
497 s_opts.print_list = true;
498 }),
499 Option("m|modes", []()
500 {
501 s_opts.print_modes = true;
502 }),
503 Option("p|props", []()
504 {
505 s_opts.print_props = true;
506 }),
507 Option("|xmode", []() {
508 s_opts.x_modeline = true;
509 }),
510 Option("h|help", []()
511 {
512 usage();
513 exit(-1);
514 }),
515 };
517 optionset.parse(argc, argv);
519 if (optionset.params().size() > 0) {
520 usage();
521 exit(-1);
522 }
524 Card card(dev_path);
526 if (s_opts.print_modes) {
527 print_modes(card);
528 return 0;
529 }
531 if (s_opts.print_list)
532 print_as_list(card);
533 else
534 print_as_tree(card);
535 }