kmsprint: print plane formats
[android/external-libkmsxx.git] / utils / kmsprint.cpp
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         string fmts = join<PixelFormat>(p.get_formats(), " ", [](PixelFormat fmt) { return PixelFormatToFourCC(fmt); });
120         str += sformat(" (%s)", fmts.c_str());
122         return str;
125 static string format_fb(Framebuffer& fb)
127         return sformat("FB %u %ux%u",
128                        fb.id(), fb.width(), fb.height());
131 static string format_property(const Property* prop, uint64_t val)
133         string ret = sformat("%s = ", prop->name().c_str());
135         switch (prop->type()) {
136         case PropertyType::Bitmask:
137         {
138                 vector<string> v, vall;
140                 for (auto kvp : prop->get_enums()) {
141                         if (val & (1 << kvp.first))
142                                 v.push_back(kvp.second);
143                         vall.push_back(kvp.second);
144                 }
146                 ret += sformat("%s (%s)", join(v, "|").c_str(), join(vall, "|").c_str());
148                 break;
149         }
151         case PropertyType::Blob:
152         {
153                 uint32_t blob_id = (uint32_t)val;
155                 if (blob_id) {
156                         Blob blob(prop->card(), blob_id);
157                         auto data = blob.data();
159                         ret += sformat("blob-id %u len %zu", blob_id, data.size());
160                 } else {
161                         ret += sformat("blob-id %u", blob_id);
162                 }
164                 break;
165         }
167         case PropertyType::Enum:
168         {
169                 string cur;
170                 vector<string> vall;
172                 for (auto kvp : prop->get_enums()) {
173                         if (val == kvp.first)
174                                 cur = kvp.second;
175                         vall.push_back(kvp.second);
176                 }
178                 ret += sformat("%s (%s)", cur.c_str(), join(vall, "|").c_str());
180                 break;
181         }
183         case PropertyType::Object:
184         {
185                 ret += sformat("object id %u", (uint32_t)val);
186                 break;
187         }
189         case PropertyType::Range:
190         {
191                 auto values = prop->get_values();
193                 ret += sformat("%" PRIu64 " [%" PRIu64 " - %" PRIu64 "]",
194                                val, values[0], values[1]);
196                 break;
197         }
199         case PropertyType::SignedRange:
200         {
201                 auto values = prop->get_values();
203                 ret += sformat("%" PRIi64 " [%" PRIi64 " - %" PRIi64 "]",
204                                (int64_t)val, (int64_t)values[0], (int64_t)values[1]);
206                 break;
207         }
209         }
211         if (prop->is_pending())
212                 ret += " (pending)";
213         if (prop->is_immutable())
214                 ret += " (immutable)";
216         return ret;
219 static vector<string> format_props(DrmPropObject* o)
221         vector<string> lines;
223         auto pmap = o->get_prop_map();
224         for (auto pp : pmap) {
225                 const Property* p = o->card().get_prop(pp.first);
226                 lines.push_back(format_property(p, pp.second));
227         }
229         return lines;
232 static string format_ob(DrmObject* ob)
234         if (auto o = dynamic_cast<Connector*>(ob))
235                 return format_connector(*o);
236         else if (auto o = dynamic_cast<Encoder*>(ob))
237                 return format_encoder(*o);
238         else if (auto o = dynamic_cast<Crtc*>(ob))
239                 return format_crtc(*o);
240         else if (auto o = dynamic_cast<Plane*>(ob))
241                 return format_plane(*o);
242         else if (auto o = dynamic_cast<Framebuffer*>(ob))
243                 return format_fb(*o);
244         else
245                 EXIT("Unkown DRM Object type\n");
248 template<class T>
249 vector<T> filter(const vector<T>& sequence, function<bool(T)> predicate)
251         vector<T> result;
253         for(auto it = sequence.begin(); it != sequence.end(); ++it)
254                 if(predicate(*it))
255                         result.push_back(*it);
257         return result;
260 struct Entry
262         string title;
263         vector<string> lines;
264         vector<Entry> children;
265 };
267 static Entry& add_entry(vector<Entry>& entries)
269         entries.emplace_back();
270         return entries.back();
272 /*
273 static bool on_tty()
275         return isatty(STDOUT_FILENO) > 0;
277 */
278 enum class TreeGlyphMode {
279         None,
280         ASCII,
281         UTF8,
282 };
284 static TreeGlyphMode s_glyph_mode = TreeGlyphMode::None;
286 enum class TreeGlyph {
287         Vertical,
288         Branch,
289         Right,
290         Space,
291 };
293 static const map<TreeGlyph, string> glyphs_utf8 = {
294         { TreeGlyph::Vertical, "│ " },
295         { TreeGlyph::Branch, "├─" },
296         { TreeGlyph::Right, "└─" },
297         { TreeGlyph::Space, "  " },
299 };
301 static const map<TreeGlyph, string> glyphs_ascii = {
302         { TreeGlyph::Vertical, "| " },
303         { TreeGlyph::Branch, "|-" },
304         { TreeGlyph::Right, "`-" },
305         { TreeGlyph::Space, "  " },
307 };
309 const char* get_glyph(TreeGlyph glyph)
311         if (s_glyph_mode == TreeGlyphMode::None)
312                 return "  ";
314         const map<TreeGlyph, string>& glyphs = s_glyph_mode == TreeGlyphMode::UTF8 ? glyphs_utf8 : glyphs_ascii;
316         return glyphs.at(glyph).c_str();
319 static void print_entry(const Entry& e, const string& prefix, bool is_child, bool is_last)
321         string prefix1;
322         string prefix2;
324         if (is_child) {
325                 prefix1 = prefix + (is_last ? get_glyph(TreeGlyph::Right) : get_glyph(TreeGlyph::Branch));
326                 prefix2 = prefix + (is_last ? get_glyph(TreeGlyph::Space) : get_glyph(TreeGlyph::Vertical));
327         }
329         printf("%s%s\n", prefix1.c_str(), e.title.c_str());
331         bool has_children = e.children.size() > 0;
333         string data_prefix = prefix2 + (has_children ? get_glyph(TreeGlyph::Vertical) : get_glyph(TreeGlyph::Space));
335         for (const string& str : e.lines) {
336                 string p = data_prefix + get_glyph(TreeGlyph::Space);
337                 printf("%s%s\n", p.c_str(), str.c_str());
338         }
340         for (const Entry& child : e.children) {
341                 bool is_last = &child == &e.children.back();
343                 print_entry(child, prefix2, true, is_last);
344         }
347 static void print_entries(const vector<Entry>& entries, const string& prefix)
349         for (const Entry& e: entries) {
350                 print_entry(e, "", false, false);
351         }
354 template<class T>
355 static void append(vector<DrmObject*>& dst, const vector<T*>& src)
357         dst.insert(dst.end(), src.begin(), src.end());
361 static void print_as_list(Card& card)
363         vector<DrmPropObject*> obs;
364         vector<Framebuffer*> fbs;
366         for (Connector* conn : card.get_connectors()) {
367                 obs.push_back(conn);
368         }
370         for (Encoder* enc : card.get_encoders()) {
371                 obs.push_back(enc);
372         }
374         for (Crtc* crtc : card.get_crtcs()) {
375                 obs.push_back(crtc);
376                 if (crtc->buffer_id() && !card.has_has_universal_planes()) {
377                         Framebuffer* fb = new Framebuffer(card, crtc->buffer_id());
378                         fbs.push_back(fb);
379                 }
380         }
382         for (Plane* plane : card.get_planes()) {
383                 obs.push_back(plane);
384                 if (plane->fb_id()) {
385                         Framebuffer* fb = new Framebuffer(card, plane->fb_id());
386                         fbs.push_back(fb);
387                 }
388         }
390         for (DrmPropObject* ob: obs) {
391                 printf("%s\n", format_ob(ob).c_str());
393                 if (s_opts.print_props) {
394                         for (string str : format_props(ob))
395                                 printf("    %s\n", str.c_str());
396                 }
397         }
399         for (Framebuffer* fb: fbs) {
400                 printf("%s\n", format_ob(fb).c_str());
401         }
404 static void print_as_tree(Card& card)
406         vector<Entry> entries;
408         for (Connector* conn : card.get_connectors()) {
409                 if (!conn->connected())
410                         continue;
412                 Entry& e1 = add_entry(entries);
413                 e1.title = format_ob(conn);
414                 if (s_opts.print_props)
415                         e1.lines = format_props(conn);
417                 for (Encoder* enc : conn->get_encoders()) {
419                         Entry& e2 = add_entry(e1.children);
420                         e2.title = format_ob(enc);
421                         if (s_opts.print_props)
422                                 e2.lines = format_props(enc);
424                         if (Crtc* crtc = enc->get_crtc()) {
425                                 Entry& e3 = add_entry(e2.children);
426                                 e3.title = format_ob(crtc);
427                                 if (s_opts.print_props)
428                                         e3.lines = format_props(crtc);
430                                 if (crtc->buffer_id() && !card.has_has_universal_planes()) {
431                                         Framebuffer fb(card, crtc->buffer_id());
432                                         Entry& e5 = add_entry(e3.children);
434                                         e5.title = format_ob(&fb);
435                                 }
437                                 for (Plane* plane : card.get_planes()) {
438                                         if (plane->crtc_id() != crtc->id())
439                                                 continue;
441                                         Entry& e4 = add_entry(e3.children);
442                                         e4.title = format_ob(plane);
443                                         if (s_opts.print_props)
444                                                 e4.lines = format_props(plane);
446                                         uint32_t fb_id = plane->fb_id();
447                                         if (fb_id) {
448                                                 Framebuffer fb(card, fb_id);
450                                                 Entry& e5 = add_entry(e4.children);
452                                                 e5.title = format_ob(&fb);
453                                         }
454                                 }
455                         }
456                 }
457         }
459         print_entries(entries, "");
462 static void print_modes(Card& card)
464         for (Connector* conn : card.get_connectors()) {
465                 if (!conn->connected())
466                         continue;
468                 printf("%s\n", format_ob(conn).c_str());
470                 auto modes = conn->get_modes();
471                 for (unsigned i = 0; i < modes.size(); ++i)
472                         printf("%s\n", format_mode(modes[i], i).c_str());
473         }
476 static const char* usage_str =
477                 "Usage: kmsprint [OPTIONS]\n\n"
478                 "Options:\n"
479                 "  -l, --list        Print list instead of tree\n"
480                 "  -m, --modes       Print modes\n"
481                 "      --xmode       Print modes using X modeline\n"
482                 "  -p, --props       Print properties\n"
483                 ;
485 static void usage()
487         puts(usage_str);
490 int main(int argc, char **argv)
492         string dev_path = "/dev/dri/card0";
494         OptionSet optionset = {
495                 Option("|device=", [&dev_path](string s)
496                 {
497                         dev_path = s;
498                 }),
499                 Option("l|list", []()
500                 {
501                         s_opts.print_list = true;
502                 }),
503                 Option("m|modes", []()
504                 {
505                         s_opts.print_modes = true;
506                 }),
507                 Option("p|props", []()
508                 {
509                         s_opts.print_props = true;
510                 }),
511                 Option("|xmode", []() {
512                         s_opts.x_modeline = true;
513                 }),
514                 Option("h|help", []()
515                 {
516                         usage();
517                         exit(-1);
518                 }),
519         };
521         optionset.parse(argc, argv);
523         if (optionset.params().size() > 0) {
524                 usage();
525                 exit(-1);
526         }
528         Card card(dev_path);
530         if (s_opts.print_modes) {
531                 print_modes(card);
532                 return 0;
533         }
535         if (s_opts.print_list)
536                 print_as_list(card);
537         else
538                 print_as_tree(card);