#include #include #include #include #include #include #include #include using namespace std; using namespace kms; static struct { bool print_props; bool print_modes; bool print_list; bool x_modeline; } s_opts; static string format_mode(const Videomode& m, unsigned idx) { string str; str = sformat(" %2u ", idx); if (s_opts.x_modeline) { str += sformat("%12s %6d %4u %4u %4u %4u %4u %4u %4u %4u %2u %#x %#x", m.name.c_str(), m.clock, m.hdisplay, m.hsync_start, m.hsync_end, m.htotal, m.vdisplay, m.vsync_start, m.vsync_end, m.vtotal, m.vrefresh, m.flags, m.type); } else { string h = sformat("%u/%u/%u/%u", m.hdisplay, m.hfp(), m.hsw(), m.hbp()); string v = sformat("%u/%u/%u/%u", m.vdisplay, m.vfp(), m.vsw(), m.vbp()); str += sformat("%-12s %6d %-16s %-16s %2u %#10x %#6x", m.name.c_str(), m.clock, h.c_str(), v.c_str(), m.vrefresh, m.flags, m.type); } return str; } static string format_mode_short(const Videomode& m) { string h = sformat("%u/%u/%u/%u", m.hdisplay, m.hfp(), m.hsw(), m.hbp()); string v = sformat("%u/%u/%u/%u", m.vdisplay, m.vfp(), m.vsw(), m.vbp()); return sformat("%s %d %s %s %u", m.name.c_str(), m.clock, h.c_str(), v.c_str(), m.vrefresh); } static string format_connector(Connector& c) { string str; str = sformat("Connector %u (%u) %s", c.idx(), c.id(), c.fullname().c_str()); if (c.connected()) str += " (connected)"; return str; } static string format_encoder(Encoder& e) { return sformat("Encoder %u (%u) %s", e.idx(), e.id(), e.get_encoder_type().c_str()); } static string format_crtc(Crtc& c) { string str; str = sformat("Crtc %u (%u)", c.idx(), c.id()); if (c.mode_valid()) str += " " + format_mode_short(c.mode()); return str; } static string format_plane(Plane& p) { string str; str = sformat("Plane %u (%u)", p.idx(), p.id()); if (p.fb_id()) str += sformat(" fb-id: %u", p.fb_id()); if (p.card().has_atomic()) { str += sformat(" %u,%u %ux%u -> %u,%u %ux%u", (uint32_t)p.get_prop_value("SRC_X") >> 16, (uint32_t)p.get_prop_value("SRC_Y") >> 16, (uint32_t)p.get_prop_value("SRC_W") >> 16, (uint32_t)p.get_prop_value("SRC_H") >> 16, (uint32_t)p.get_prop_value("CRTC_X"), (uint32_t)p.get_prop_value("CRTC_Y"), (uint32_t)p.get_prop_value("CRTC_W"), (uint32_t)p.get_prop_value("CRTC_H")); } string fmts = join(p.get_formats(), " ", [](PixelFormat fmt) { return PixelFormatToFourCC(fmt); }); str += sformat(" (%s)", fmts.c_str()); return str; } static string format_fb(Framebuffer& fb) { return sformat("FB %u %ux%u", fb.id(), fb.width(), fb.height()); } static string format_property(const Property* prop, uint64_t val) { string ret = sformat("%s = ", prop->name().c_str()); switch (prop->type()) { case PropertyType::Bitmask: { vector v, vall; for (auto kvp : prop->get_enums()) { if (val & (1 << kvp.first)) v.push_back(kvp.second); vall.push_back(kvp.second); } ret += sformat("%s (%s)", join(v, "|").c_str(), join(vall, "|").c_str()); break; } case PropertyType::Blob: { uint32_t blob_id = (uint32_t)val; if (blob_id) { Blob blob(prop->card(), blob_id); auto data = blob.data(); ret += sformat("blob-id %u len %zu", blob_id, data.size()); } else { ret += sformat("blob-id %u", blob_id); } break; } case PropertyType::Enum: { string cur; vector vall; for (auto kvp : prop->get_enums()) { if (val == kvp.first) cur = kvp.second; vall.push_back(kvp.second); } ret += sformat("%s (%s)", cur.c_str(), join(vall, "|").c_str()); break; } case PropertyType::Object: { ret += sformat("object id %u", (uint32_t)val); break; } case PropertyType::Range: { auto values = prop->get_values(); ret += sformat("%" PRIu64 " [%" PRIu64 " - %" PRIu64 "]", val, values[0], values[1]); break; } case PropertyType::SignedRange: { auto values = prop->get_values(); ret += sformat("%" PRIi64 " [%" PRIi64 " - %" PRIi64 "]", (int64_t)val, (int64_t)values[0], (int64_t)values[1]); break; } } if (prop->is_pending()) ret += " (pending)"; if (prop->is_immutable()) ret += " (immutable)"; return ret; } static vector format_props(DrmPropObject* o) { vector lines; auto pmap = o->get_prop_map(); for (auto pp : pmap) { const Property* p = o->card().get_prop(pp.first); lines.push_back(format_property(p, pp.second)); } return lines; } static string format_ob(DrmObject* ob) { if (auto o = dynamic_cast(ob)) return format_connector(*o); else if (auto o = dynamic_cast(ob)) return format_encoder(*o); else if (auto o = dynamic_cast(ob)) return format_crtc(*o); else if (auto o = dynamic_cast(ob)) return format_plane(*o); else if (auto o = dynamic_cast(ob)) return format_fb(*o); else EXIT("Unkown DRM Object type\n"); } template vector filter(const vector& sequence, function predicate) { vector result; for(auto it = sequence.begin(); it != sequence.end(); ++it) if(predicate(*it)) result.push_back(*it); return result; } struct Entry { string title; vector lines; vector children; }; static Entry& add_entry(vector& entries) { entries.emplace_back(); return entries.back(); } /* static bool on_tty() { return isatty(STDOUT_FILENO) > 0; } */ enum class TreeGlyphMode { None, ASCII, UTF8, }; static TreeGlyphMode s_glyph_mode = TreeGlyphMode::None; enum class TreeGlyph { Vertical, Branch, Right, Space, }; static const map glyphs_utf8 = { { TreeGlyph::Vertical, "│ " }, { TreeGlyph::Branch, "├─" }, { TreeGlyph::Right, "└─" }, { TreeGlyph::Space, " " }, }; static const map glyphs_ascii = { { TreeGlyph::Vertical, "| " }, { TreeGlyph::Branch, "|-" }, { TreeGlyph::Right, "`-" }, { TreeGlyph::Space, " " }, }; const char* get_glyph(TreeGlyph glyph) { if (s_glyph_mode == TreeGlyphMode::None) return " "; const map& glyphs = s_glyph_mode == TreeGlyphMode::UTF8 ? glyphs_utf8 : glyphs_ascii; return glyphs.at(glyph).c_str(); } static void print_entry(const Entry& e, const string& prefix, bool is_child, bool is_last) { string prefix1; string prefix2; if (is_child) { prefix1 = prefix + (is_last ? get_glyph(TreeGlyph::Right) : get_glyph(TreeGlyph::Branch)); prefix2 = prefix + (is_last ? get_glyph(TreeGlyph::Space) : get_glyph(TreeGlyph::Vertical)); } printf("%s%s\n", prefix1.c_str(), e.title.c_str()); bool has_children = e.children.size() > 0; string data_prefix = prefix2 + (has_children ? get_glyph(TreeGlyph::Vertical) : get_glyph(TreeGlyph::Space)); for (const string& str : e.lines) { string p = data_prefix + get_glyph(TreeGlyph::Space); printf("%s%s\n", p.c_str(), str.c_str()); } for (const Entry& child : e.children) { bool is_last = &child == &e.children.back(); print_entry(child, prefix2, true, is_last); } } static void print_entries(const vector& entries, const string& prefix) { for (const Entry& e: entries) { print_entry(e, "", false, false); } } template static void append(vector& dst, const vector& src) { dst.insert(dst.end(), src.begin(), src.end()); } static void print_as_list(Card& card) { vector obs; vector fbs; for (Connector* conn : card.get_connectors()) { obs.push_back(conn); } for (Encoder* enc : card.get_encoders()) { obs.push_back(enc); } for (Crtc* crtc : card.get_crtcs()) { obs.push_back(crtc); if (crtc->buffer_id() && !card.has_has_universal_planes()) { Framebuffer* fb = new Framebuffer(card, crtc->buffer_id()); fbs.push_back(fb); } } for (Plane* plane : card.get_planes()) { obs.push_back(plane); if (plane->fb_id()) { Framebuffer* fb = new Framebuffer(card, plane->fb_id()); fbs.push_back(fb); } } for (DrmPropObject* ob: obs) { printf("%s\n", format_ob(ob).c_str()); if (s_opts.print_props) { for (string str : format_props(ob)) printf(" %s\n", str.c_str()); } } for (Framebuffer* fb: fbs) { printf("%s\n", format_ob(fb).c_str()); } } static void print_as_tree(Card& card) { vector entries; for (Connector* conn : card.get_connectors()) { if (!conn->connected()) continue; Entry& e1 = add_entry(entries); e1.title = format_ob(conn); if (s_opts.print_props) e1.lines = format_props(conn); for (Encoder* enc : conn->get_encoders()) { Entry& e2 = add_entry(e1.children); e2.title = format_ob(enc); if (s_opts.print_props) e2.lines = format_props(enc); if (Crtc* crtc = enc->get_crtc()) { Entry& e3 = add_entry(e2.children); e3.title = format_ob(crtc); if (s_opts.print_props) e3.lines = format_props(crtc); if (crtc->buffer_id() && !card.has_has_universal_planes()) { Framebuffer fb(card, crtc->buffer_id()); Entry& e5 = add_entry(e3.children); e5.title = format_ob(&fb); } for (Plane* plane : card.get_planes()) { if (plane->crtc_id() != crtc->id()) continue; Entry& e4 = add_entry(e3.children); e4.title = format_ob(plane); if (s_opts.print_props) e4.lines = format_props(plane); uint32_t fb_id = plane->fb_id(); if (fb_id) { Framebuffer fb(card, fb_id); Entry& e5 = add_entry(e4.children); e5.title = format_ob(&fb); } } } } } print_entries(entries, ""); } static void print_modes(Card& card) { for (Connector* conn : card.get_connectors()) { if (!conn->connected()) continue; printf("%s\n", format_ob(conn).c_str()); auto modes = conn->get_modes(); for (unsigned i = 0; i < modes.size(); ++i) printf("%s\n", format_mode(modes[i], i).c_str()); } } static const char* usage_str = "Usage: kmsprint [OPTIONS]\n\n" "Options:\n" " -l, --list Print list instead of tree\n" " -m, --modes Print modes\n" " --xmode Print modes using X modeline\n" " -p, --props Print properties\n" ; static void usage() { puts(usage_str); } int main(int argc, char **argv) { string dev_path = "/dev/dri/card0"; OptionSet optionset = { Option("|device=", [&dev_path](string s) { dev_path = s; }), Option("l|list", []() { s_opts.print_list = true; }), Option("m|modes", []() { s_opts.print_modes = true; }), Option("p|props", []() { s_opts.print_props = true; }), Option("|xmode", []() { s_opts.x_modeline = true; }), Option("h|help", []() { usage(); exit(-1); }), }; optionset.parse(argc, argv); if (optionset.params().size() > 0) { usage(); exit(-1); } Card card(dev_path); if (s_opts.print_modes) { print_modes(card); return 0; } if (s_opts.print_list) print_as_list(card); else print_as_tree(card); }