kmsprint: print connectors even if disconnected
[android/external-libkmsxx.git] / utils / kmsprint.cpp
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 %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());
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         }
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 %.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 }
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)";
73         else
74                 str += " (disconnected)";
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         string crtcs = join<Crtc*>(p.get_possible_crtcs(), " ", [](Crtc* crtc) { return to_string(crtc->idx()); });
108         str += sformat(" (crtcs: %s)", crtcs.c_str());
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         }
122         string fmts = join<PixelFormat>(p.get_formats(), " ", [](PixelFormat fmt) { return PixelFormatToFourCC(fmt); });
124         str += sformat(" (%s)", fmts.c_str());
126         return str;
129 static string format_fb(Framebuffer& fb)
131         return sformat("FB %u %ux%u",
132                        fb.id(), fb.width(), fb.height());
135 static string format_property(const Property* prop, uint64_t val)
137         string ret = sformat("%s = ", prop->name().c_str());
139         switch (prop->type()) {
140         case PropertyType::Bitmask:
141         {
142                 vector<string> v, vall;
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                 }
150                 ret += sformat("0x%" PRIx64 " (%s) [%s]", val, join(v, "|").c_str(), join(vall, "|").c_str());
152                 break;
153         }
155         case PropertyType::Blob:
156         {
157                 uint32_t blob_id = (uint32_t)val;
159                 if (blob_id) {
160                         Blob blob(prop->card(), blob_id);
161                         auto data = blob.data();
163                         ret += sformat("blob-id %u len %zu", blob_id, data.size());
164                 } else {
165                         ret += sformat("blob-id %u", blob_id);
166                 }
168                 break;
169         }
171         case PropertyType::Enum:
172         {
173                 string cur;
174                 vector<string> vall;
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                 }
182                 ret += sformat("%" PRIu64 " (%s) [%s]", val, cur.c_str(), join(vall, "|").c_str());
184                 break;
185         }
187         case PropertyType::Object:
188         {
189                 ret += sformat("object id %u", (uint32_t)val);
190                 break;
191         }
193         case PropertyType::Range:
194         {
195                 auto values = prop->get_values();
197                 ret += sformat("%" PRIu64 " [%" PRIu64 " - %" PRIu64 "]",
198                                val, values[0], values[1]);
200                 break;
201         }
203         case PropertyType::SignedRange:
204         {
205                 auto values = prop->get_values();
207                 ret += sformat("%" PRIi64 " [%" PRIi64 " - %" PRIi64 "]",
208                                (int64_t)val, (int64_t)values[0], (int64_t)values[1]);
210                 break;
211         }
213         }
215         if (prop->is_pending())
216                 ret += " (pending)";
217         if (prop->is_immutable())
218                 ret += " (immutable)";
220         return ret;
223 static vector<string> format_props(DrmPropObject* o)
225         vector<string> lines;
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         }
233         return lines;
236 static string format_ob(DrmObject* ob)
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");
252 template<class T>
253 vector<T> filter(const vector<T>& sequence, function<bool(T)> predicate)
255         vector<T> result;
257         for(auto it = sequence.begin(); it != sequence.end(); ++it)
258                 if(predicate(*it))
259                         result.push_back(*it);
261         return result;
264 struct Entry
266         string title;
267         vector<string> lines;
268         vector<Entry> children;
269 };
271 static Entry& add_entry(vector<Entry>& entries)
273         entries.emplace_back();
274         return entries.back();
276 /*
277 static bool on_tty()
279         return isatty(STDOUT_FILENO) > 0;
281 */
282 enum class TreeGlyphMode {
283         None,
284         ASCII,
285         UTF8,
286 };
288 static TreeGlyphMode s_glyph_mode = TreeGlyphMode::None;
290 enum class TreeGlyph {
291         Vertical,
292         Branch,
293         Right,
294         Space,
295 };
297 static const map<TreeGlyph, string> glyphs_utf8 = {
298         { TreeGlyph::Vertical, "│ " },
299         { TreeGlyph::Branch, "├─" },
300         { TreeGlyph::Right, "└─" },
301         { TreeGlyph::Space, "  " },
303 };
305 static const map<TreeGlyph, string> glyphs_ascii = {
306         { TreeGlyph::Vertical, "| " },
307         { TreeGlyph::Branch, "|-" },
308         { TreeGlyph::Right, "`-" },
309         { TreeGlyph::Space, "  " },
311 };
313 const char* get_glyph(TreeGlyph glyph)
315         if (s_glyph_mode == TreeGlyphMode::None)
316                 return "  ";
318         const map<TreeGlyph, string>& glyphs = s_glyph_mode == TreeGlyphMode::UTF8 ? glyphs_utf8 : glyphs_ascii;
320         return glyphs.at(glyph).c_str();
323 static void print_entry(const Entry& e, const string& prefix, bool is_child, bool is_last)
325         string prefix1;
326         string prefix2;
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         }
333         printf("%s%s\n", prefix1.c_str(), e.title.c_str());
335         bool has_children = e.children.size() > 0;
337         string data_prefix = prefix2 + (has_children ? get_glyph(TreeGlyph::Vertical) : get_glyph(TreeGlyph::Space));
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         }
344         for (const Entry& child : e.children) {
345                 bool is_last = &child == &e.children.back();
347                 print_entry(child, prefix2, true, is_last);
348         }
351 static void print_entries(const vector<Entry>& entries, const string& prefix)
353         for (const Entry& e: entries) {
354                 print_entry(e, "", false, false);
355         }
358 template<class T>
359 static void append(vector<DrmObject*>& dst, const vector<T*>& src)
361         dst.insert(dst.end(), src.begin(), src.end());
365 static void print_as_list(Card& card)
367         vector<DrmPropObject*> obs;
368         vector<Framebuffer*> fbs;
370         for (Connector* conn : card.get_connectors()) {
371                 obs.push_back(conn);
372         }
374         for (Encoder* enc : card.get_encoders()) {
375                 obs.push_back(enc);
376         }
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         }
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         }
394         for (DrmPropObject* ob: obs) {
395                 printf("%s\n", format_ob(ob).c_str());
397                 if (s_opts.print_props) {
398                         for (string str : format_props(ob))
399                                 printf("    %s\n", str.c_str());
400                 }
401         }
403         for (Framebuffer* fb: fbs) {
404                 printf("%s\n", format_ob(fb).c_str());
405         }
408 static void print_as_tree(Card& card)
410         vector<Entry> entries;
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);
418                 for (Encoder* enc : conn->get_encoders()) {
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);
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);
431                                 if (crtc->buffer_id() && !card.has_has_universal_planes()) {
432                                         Framebuffer fb(card, crtc->buffer_id());
433                                         Entry& e5 = add_entry(e3.children);
435                                         e5.title = format_ob(&fb);
436                                 }
438                                 for (Plane* plane : card.get_planes()) {
439                                         if (plane->crtc_id() != crtc->id())
440                                                 continue;
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);
447                                         uint32_t fb_id = plane->fb_id();
448                                         if (fb_id) {
449                                                 Framebuffer fb(card, fb_id);
451                                                 Entry& e5 = add_entry(e4.children);
453                                                 e5.title = format_ob(&fb);
454                                         }
455                                 }
456                         }
457                 }
458         }
460         print_entries(entries, "");
463 static void print_modes(Card& card)
465         for (Connector* conn : card.get_connectors()) {
466                 if (!conn->connected())
467                         continue;
469                 printf("%s\n", format_ob(conn).c_str());
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         }
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                 ;
486 static void usage()
488         puts(usage_str);
491 int main(int argc, char **argv)
493         string dev_path = "/dev/dri/card0";
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         };
522         optionset.parse(argc, argv);
524         if (optionset.params().size() > 0) {
525                 usage();
526                 exit(-1);
527         }
529         Card card(dev_path);
531         if (s_opts.print_modes) {
532                 print_modes(card);
533                 return 0;
534         }
536         if (s_opts.print_list)
537                 print_as_list(card);
538         else
539                 print_as_tree(card);