diff options
| author | PancakeTAS <pancake@mgnet.work> | 2026-07-06 22:34:07 +0200 |
|---|---|---|
| committer | PancakeTAS <pancake@mgnet.work> | 2026-07-06 22:34:07 +0200 |
| commit | 818db23b28f0e390aff0f225d1dad69779364a40 (patch) | |
| tree | b82bab590b8cbc4325eb70ba3119ef8fc09ff2fa | |
| parent | Implement framebuffer object (diff) | |
| -rw-r--r-- | CMakeLists.txt | 1 | ||||
| -rw-r--r-- | include/drm++/mode/mode.hpp | 138 | ||||
| -rw-r--r-- | src/mode/mode.cpp | 142 |
3 files changed, 281 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index e12ed45..492bd1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,7 @@ include(cmake/Diagnostics.cmake) add_library(drm++ SHARED "src/helper/ioctl.cpp" "src/mode/framebuffer.cpp" + "src/mode/mode.cpp" "src/drm.cpp" "src/gem.cpp" "src/syncobject.cpp") diff --git a/include/drm++/mode/mode.hpp b/include/drm++/mode/mode.hpp new file mode 100644 index 0000000..8df8845 --- /dev/null +++ b/include/drm++/mode/mode.hpp @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: MIT */ + +#pragma once + +/// +/// * DRM Resource +/// +/// ... +/// +/// * Notes to self +/// +/// - No encoder abstraction in atomic +/// - No subpixel due to inconsistent +/// - Might need to delete copy/move +/// - Raw mode as drm_mode_modeinfo +/// - Examine mode change behavior +/// + +#include "../helper/macros.hpp" +#include "../helper/types.hpp" + +#include <string> +#include <utility> +#include <vector> + +namespace drm::mode { + +enum class ConnectorType : u32; + +/// +/// Mode information +/// +struct ModeInfo { + /// Width and height in pixels + std::pair<u16, u16> resolution; + /// Refresh rate in Hz + double refreshRate; + /// Whether the mode is preferred (cannot be relied on) + bool preferred; + + /// Raw drm_mode_modeinfo struct + u8 raw[68]; +}; + +/// +/// CRTC object +/// +struct Crtc { + u32 id; +}; + +/// +/// Connector object +/// +struct Connector { + u32 id; + + /// Type of the connector + ConnectorType type; + /// Index of the connector per type (e.g. DP-3 or HDMI-A-1) + u32 typeIndex; + /// Supported modes of the connector (may change after failed commit) + std::vector<ModeInfo> modes; + /// Physical size of the display in millimeters if available + std::pair<u32, u32> physicalSize; +}; + +struct Plane { + u32 id; + + /// CRTC IDs the plane can be used with + std::vector<u32> crtcs; +}; + +/// +/// Modesetting resources +/// +/// @throws drm::ioctl::Exception on failure +/// +class Resources { +public: + /// + /// Query all modesetting resources + /// + /// @param fetchModes Whether to fetch connector modes (blocking). + /// + Resources(int fd, bool fetchModes = false); + + // Private access + GETTER(crtcs) + GETTER(connectors) + GETTER(planes) + + // Default operators and destructor + DEFAULT_OPERATORS(Resources) + ~Resources() noexcept = default; +private: + int m_fd; + + std::vector<Crtc> m_crtcs; + std::vector<Connector> m_connectors; + std::vector<Plane> m_planes; +}; + + +/// +/// Connector type +/// +enum class ConnectorType : u32 { + Unknown = 0, + VGA = 1, + DVII = 2, + DVID = 3, + DVIA = 4, + Composite = 5, + SVIDEO = 6, + LVDS = 7, + Component = 8, + NPinDIN = 9, + DisplayPort = 10, + HDMI_A = 11, + HDMI_B = 12, + TV = 13, + eDP = 14, + Virtual = 15, + DSI = 16, + DPI = 17, + Writeback = 18, + SPI = 19, + USB = 20 +}; + +/// +/// Convert a ConnectorType enum to a string representation +/// +std::string connectorTypeToString(ConnectorType type); + +} diff --git a/src/mode/mode.cpp b/src/mode/mode.cpp new file mode 100644 index 0000000..1441b56 --- /dev/null +++ b/src/mode/mode.cpp @@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: MIT */ + +#include "drm++/mode/mode.hpp" +#include "drm++/helper/ioctl.hpp" +#include "drm++/helper/types.hpp" + +#include <cstring> +#include <string> +#include <utility> +#include <vector> + +#include <drm.h> +#include <drm_mode.h> + +using namespace drm; +using namespace drm::mode; + +Resources::Resources(int fd, bool fetchModes) : m_fd(fd) { + drm_mode_card_res card_args{ + .count_crtcs = 0, + }; + ioctl::perform(this->m_fd, DRM_IOCTL_MODE_GETRESOURCES, card_args); + + std::vector<u32> crtc_ids(card_args.count_crtcs); + std::vector<u32> connector_ids(card_args.count_connectors); + + card_args = { + .crtc_id_ptr = reinterpret_cast<u64>(crtc_ids.data()), + .connector_id_ptr = reinterpret_cast<u64>(connector_ids.data()), + .count_crtcs = static_cast<u32>(crtc_ids.size()), + .count_connectors = static_cast<u32>(connector_ids.size()) + }; + ioctl::perform(this->m_fd, DRM_IOCTL_MODE_GETRESOURCES, card_args); + + this->m_crtcs.reserve(card_args.count_crtcs); + for (const u32 crtc_id : crtc_ids) { + drm_mode_crtc args{ + .crtc_id = static_cast<u32>(crtc_id), + }; + ioctl::perform(this->m_fd, DRM_IOCTL_MODE_GETCRTC, args); + + this->m_crtcs.push_back({ + .id = static_cast<u32>(args.crtc_id), + }); + } + + this->m_connectors.reserve(card_args.count_connectors); + for (const u32 connector_id : connector_ids) { + drm_mode_get_connector args{ + .count_modes = fetchModes ? 0U : 1U, + .connector_id = static_cast<u32>(connector_id), + }; + ioctl::perform(this->m_fd, DRM_IOCTL_MODE_GETCONNECTOR, args); + + std::vector<drm_mode_modeinfo> modes_raw(args.count_modes); + args = { + .modes_ptr = reinterpret_cast<u64>(modes_raw.data()), + .count_modes = static_cast<u32>(modes_raw.size()), + .connector_id = static_cast<u32>(connector_id), + }; + ioctl::perform(this->m_fd, DRM_IOCTL_MODE_GETCONNECTOR, args); + + std::vector<ModeInfo> modes; + modes.reserve(modes_raw.size()); + for (const auto& mode_raw : modes_raw) { + modes.push_back({ + .resolution = { mode_raw.hdisplay, mode_raw.vdisplay }, + .refreshRate = (mode_raw.clock * 1000.0) / (mode_raw.htotal * mode_raw.vtotal), + .preferred = (mode_raw.type & DRM_MODE_TYPE_PREFERRED) != 0, + .raw = {0}, + }); + std::memcpy(modes.back().raw, &mode_raw, sizeof(mode_raw)); // NOLINT (raw memory) + } + + this->m_connectors.push_back({ + .id = static_cast<u32>(args.connector_id), + .type = static_cast<ConnectorType>(args.connector_type), + .typeIndex = static_cast<u32>(args.connector_type_id), + .modes = std::move(modes), + .physicalSize = { args.mm_width, args.mm_height }, + }); + } + + // Fetch planes + drm_mode_get_plane_res plane_args{ + .count_planes = 0, + }; + ioctl::perform(this->m_fd, DRM_IOCTL_MODE_GETPLANERESOURCES, plane_args); + + std::vector<u32> plane_ids(plane_args.count_planes); + + plane_args.plane_id_ptr = reinterpret_cast<u64>(plane_ids.data()); + ioctl::perform(this->m_fd, DRM_IOCTL_MODE_GETPLANERESOURCES, plane_args); + + this->m_planes.reserve(plane_args.count_planes); + for (const u32 plane_id : plane_ids) { + drm_mode_get_plane args{ + .plane_id = static_cast<u32>(plane_id), + }; + ioctl::perform(this->m_fd, DRM_IOCTL_MODE_GETPLANE, args); + + std::vector<u32> possible_crtcs; + for (u32 i = 0; i < this->m_crtcs.size(); ++i) { + if (!(args.possible_crtcs & (1 << i))) + continue; + possible_crtcs.push_back(this->m_crtcs.at(i).id); + } + + this->m_planes.push_back({ + .id = static_cast<u32>(args.plane_id), + .crtcs = std::vector<u32>(possible_crtcs), + }); + } +} + +/* Enums */ + +std::string mode::connectorTypeToString(ConnectorType type) { + switch (type) { + case ConnectorType::Unknown: return "Unknown"; + case ConnectorType::VGA: return "VGA"; + case ConnectorType::DVII: return "DVI-I"; + case ConnectorType::DVID: return "DVI-D"; + case ConnectorType::DVIA: return "DVI-A"; + case ConnectorType::Composite: return "Composite"; + case ConnectorType::SVIDEO: return "SVIDEO"; + case ConnectorType::LVDS: return "LVDS"; + case ConnectorType::Component: return "Component"; + case ConnectorType::NPinDIN: return "DIN"; + case ConnectorType::DisplayPort: return "DP"; + case ConnectorType::HDMI_A: return "HDMI-A"; + case ConnectorType::HDMI_B: return "HDMI-B"; + case ConnectorType::TV: return "TV"; + case ConnectorType::eDP: return "eDP"; + case ConnectorType::Virtual: return "Virtual"; + case ConnectorType::DSI: return "DSI"; + case ConnectorType::DPI: return "DPI"; + case ConnectorType::Writeback: return "Writeback"; + case ConnectorType::SPI: return "SPI"; + case ConnectorType::USB: return "USB"; + } +} |
