diff options
Diffstat (limited to 'utils/kmsprint.cpp')
| -rw-r--r-- | utils/kmsprint.cpp | 525 |
1 files changed, 525 insertions, 0 deletions
diff --git a/utils/kmsprint.cpp b/utils/kmsprint.cpp new file mode 100644 index 0000000..849c05c --- /dev/null +++ b/utils/kmsprint.cpp @@ -0,0 +1,525 @@ +#include <algorithm> +#include <cinttypes> +#include <cstdio> +#include <iostream> +#include <string> +#include <unistd.h> +#include <kms++/format.h> + +#include <kms++/kms++.h> +#include <kms++util/kms++util.h> + +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 = fmt::format(" {:2} ", idx); + + if (s_opts.x_modeline) { + str += fmt::format("{:12} {:6} {:4} {:4} {:4} {:4} {:4} {:4} {:4} {:4} {:3} {:#x} {:#x}", + m.name, + 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 { + str += m.to_string_long_padded(); + } + + return str; +} + +static string format_mode_short(const Videomode& m) +{ + return m.to_string_long(); +} + +static string format_connector(Connector& c) +{ + string str; + + str = fmt::format("Connector {} ({}) {}", + c.idx(), c.id(), c.fullname()); + + switch (c.connector_status()) { + case ConnectorStatus::Connected: + str += " (connected)"; + break; + case ConnectorStatus::Disconnected: + str += " (disconnected)"; + break; + default: + str += " (unknown)"; + break; + } + + return str; +} + +static string format_encoder(Encoder& e) +{ + return fmt::format("Encoder {} ({}) {}", + e.idx(), e.id(), e.get_encoder_type()); +} + +static string format_crtc(Crtc& c) +{ + string str; + + str = fmt::format("Crtc {} ({})", 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 = fmt::format("Plane {} ({})", p.idx(), p.id()); + + if (p.fb_id()) + str += fmt::format(" fb-id: {}", p.fb_id()); + + string crtcs = join<Crtc*>(p.get_possible_crtcs(), " ", [](Crtc* crtc) { return to_string(crtc->idx()); }); + + str += fmt::format(" (crtcs: {})", crtcs); + + if (p.card().has_atomic()) { + str += fmt::format(" {},{} {}x{} -> {},{} {}x{}", + (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, + (int32_t)p.get_prop_value("CRTC_X"), + (int32_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<uint32_t>(p.get_fourccs(), " ", [](uint32_t fourcc) { return fourcc_to_str(fourcc); }); + + str += fmt::format(" ({})", fmts); + + return str; +} + +static string format_fb(Framebuffer& fb) +{ + return fmt::format("FB {} {}x{} {}", + fb.id(), fb.width(), fb.height(), + fourcc_to_str(fb.fourcc())); +} + +static string format_property(const Property* prop, uint64_t val) +{ + string ret = fmt::format("{} ({}) = ", prop->name(), prop->id()); + + switch (prop->type()) { + case PropertyType::Bitmask: { + vector<string> v, vall; + + for (auto kvp : prop->get_enums()) { + if (val & (1 << kvp.first)) + v.push_back(kvp.second); + vall.push_back(fmt::format("{}={:#x}", kvp.second, 1 << kvp.first)); + } + + // XXX + ret += fmt::format("{:#x} ({}) [{}]", val, join(v, "|"), join(vall, "|")); + + 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 += fmt::format("blob-id {} len {}", blob_id, data.size()); + } else { + ret += fmt::format("blob-id {}", blob_id); + } + + break; + } + + case PropertyType::Enum: { + string cur; + vector<string> vall; + + for (auto kvp : prop->get_enums()) { + if (val == kvp.first) + cur = kvp.second; + vall.push_back(fmt::format("{}={}", kvp.second, kvp.first)); + } + + ret += fmt::format("{} ({}) [{}]", val, cur, join(vall, "|")); + + break; + } + + case PropertyType::Object: { + ret += fmt::format("object id {}", val); + break; + } + + case PropertyType::Range: { + auto values = prop->get_values(); + + ret += fmt::format("{} [{} - {}]", + val, values[0], values[1]); + + break; + } + + case PropertyType::SignedRange: { + auto values = prop->get_values(); + + ret += fmt::format("{} [{} - {}]", + (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<string> format_props(DrmPropObject* o) +{ + vector<string> 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<Connector*>(ob)) + return format_connector(*o); + else if (auto o = dynamic_cast<Encoder*>(ob)) + return format_encoder(*o); + else if (auto o = dynamic_cast<Crtc*>(ob)) + return format_crtc(*o); + else if (auto o = dynamic_cast<Plane*>(ob)) + return format_plane(*o); + else if (auto o = dynamic_cast<Framebuffer*>(ob)) + return format_fb(*o); + else + EXIT("Unkown DRM Object type\n"); +} + +template<class T> +vector<T> filter(const vector<T>& sequence, function<bool(T)> predicate) +{ + vector<T> result; + + for (auto it = sequence.begin(); it != sequence.end(); ++it) + if (predicate(*it)) + result.push_back(*it); + + return result; +} + +struct Entry { + string title; + vector<string> lines; + vector<Entry> children; +}; + +static Entry& add_entry(vector<Entry>& 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<TreeGlyph, string> glyphs_utf8 = { + { TreeGlyph::Vertical, "│ " }, + { TreeGlyph::Branch, "├─" }, + { TreeGlyph::Right, "└─" }, + { TreeGlyph::Space, " " }, + +}; + +static const map<TreeGlyph, string> glyphs_ascii = { + { TreeGlyph::Vertical, "| " }, + { TreeGlyph::Branch, "|-" }, + { TreeGlyph::Right, "`-" }, + { TreeGlyph::Space, " " }, + +}; + +const string& get_glyph(TreeGlyph glyph) +{ + static const string s_empty = " "; + + if (s_glyph_mode == TreeGlyphMode::None) + return s_empty; + + const map<TreeGlyph, string>& glyphs = s_glyph_mode == TreeGlyphMode::UTF8 ? glyphs_utf8 : glyphs_ascii; + + return glyphs.at(glyph); +} + +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)); + } + + fmt::print("{}{}\n", prefix1, e.title); + + 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); + fmt::print("{}{}\n", p, str); + } + + for (const Entry& child : e.children) { + bool child_is_last = &child == &e.children.back(); + + print_entry(child, prefix2, true, child_is_last); + } +} + +static void print_entries(const vector<Entry>& entries, const string& prefix) +{ + for (const Entry& e : entries) { + print_entry(e, "", false, false); + } +} + +template<class T> +static void append(vector<DrmObject*>& dst, const vector<T*>& src) +{ + dst.insert(dst.end(), src.begin(), src.end()); +} + +static void print_as_list(Card& card) +{ + vector<DrmPropObject*> obs; + vector<Framebuffer*> 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_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) { + fmt::print("{}\n", format_ob(ob)); + + if (s_opts.print_props) { + for (string str : format_props(ob)) + fmt::print(" {}\n", str); + } + } + + for (Framebuffer* fb : fbs) { + fmt::print("{}\n", format_ob(fb)); + } +} + +static void print_as_tree(Card& card) +{ + vector<Entry> entries; + + for (Connector* conn : card.get_connectors()) { + 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_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; + + fmt::print("{}\n", format_ob(conn)); + + auto modes = conn->get_modes(); + for (unsigned i = 0; i < modes.size(); ++i) + fmt::print("{}\n", format_mode(modes[i], i)); + } +} + +static const char* usage_str = + "Usage: kmsprint [OPTIONS]\n\n" + "Options:\n" + " --device=DEVICE DEVICE is the path to DRM card to open\n" + " -C, --card=NUM open /dev/dri/card<NUM>\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; + + OptionSet optionset = { + Option("|device=", [&dev_path](string s) { + dev_path = s; + }), + Option("C|card=", [&dev_path](string s) { + dev_path = "/dev/dri/card" + 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); +} |
