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