mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-13 02:42:09 +02:00
DisplayServer (Linux): rewrites libdrm calls with plain ioctls
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
#include "common/io.h"
|
||||
#include "common/edidHelper.h"
|
||||
#include "common/strutil.h"
|
||||
#include "common/mallocHelper.h"
|
||||
|
||||
#ifdef __linux__
|
||||
#include <dirent.h>
|
||||
@@ -107,11 +108,10 @@ static const char* drmParseSysfs(FFDisplayServerResult* result) {
|
||||
|
||||
#ifdef FF_HAVE_DRM
|
||||
|
||||
#include "common/library.h"
|
||||
|
||||
#include <xf86drm.h>
|
||||
#include <xf86drmMode.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <drm.h>
|
||||
#include <drm_mode.h>
|
||||
|
||||
// https://gitlab.freedesktop.org/mesa/drm/-/blob/main/xf86drmMode.c#L1785
|
||||
// It's not supported on Ubuntu 20.04
|
||||
@@ -207,218 +207,226 @@ FF_A_UNUSED static const char* drmGetEdidByConnId(uint32_t connId, uint8_t* edid
|
||||
}
|
||||
|
||||
static const char* drmConnectLibdrm(FFDisplayServerResult* result) {
|
||||
FF_LIBRARY_LOAD_MESSAGE(libdrm, "libdrm" FF_LIBRARY_EXTENSION, 2)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmGetDevices)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeGetResources)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeGetConnectorCurrent)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeGetCrtc)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeGetEncoder)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeGetFB)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeGetProperty)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeGetPropertyBlob)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeFreeResources)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeFreeCrtc)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeFreeConnector)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeFreeEncoder)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeFreeFB)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeFreeProperty)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeFreePropertyBlob)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmFreeDevices)
|
||||
|
||||
drmDevice* devices[64];
|
||||
int nDevices = ffdrmGetDevices(devices, ARRAY_SIZE(devices));
|
||||
if (nDevices <= 0) {
|
||||
return "drmGetDevices() failed";
|
||||
const char* drmDirPath = "/dev/dri/";
|
||||
FF_AUTO_CLOSE_DIR DIR* dirp = opendir(drmDirPath);
|
||||
if (dirp == NULL) {
|
||||
return "opendir(/dev/dri/) failed";
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY pathBuf = ffStrbufCreateA(64);
|
||||
ffStrbufAppendS(&pathBuf, drmDirPath);
|
||||
uint32_t pathLen = pathBuf.length;
|
||||
|
||||
int nSuccess = 0;
|
||||
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate();
|
||||
|
||||
for (int iDev = 0; iDev < nDevices; ++iDev) {
|
||||
drmDevice* dev = devices[iDev];
|
||||
|
||||
if (!(dev->available_nodes & (1 << DRM_NODE_PRIMARY))) {
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(dirp)) != NULL) {
|
||||
if (entry->d_name[0] == '.' || !ffStrStartsWith(entry->d_name, "card")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char* path = dev->nodes[DRM_NODE_PRIMARY];
|
||||
ffStrbufAppendS(&pathBuf, entry->d_name);
|
||||
|
||||
#if __linux__
|
||||
ffStrbufSetF(&name, "/sys/class/drm/%s/device/power/runtime_status", strrchr(path, '/') + 1);
|
||||
char powerStatusBuf[512];
|
||||
snprintf(powerStatusBuf, ARRAY_SIZE(powerStatusBuf), "/sys/class/drm/%s/device/power/runtime_status", entry->d_name);
|
||||
|
||||
char buffer[8] = "";
|
||||
if (ffReadFileData(name.chars, strlen("suspend"), buffer) > 0 && ffStrStartsWith(buffer, "suspend")) {
|
||||
if (ffReadFileData(powerStatusBuf, strlen("suspend"), buffer) > 0 && ffStrStartsWith(buffer, "suspend")) {
|
||||
ffStrbufSubstrBefore(&pathBuf, pathLen);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
FF_AUTO_CLOSE_FD int primaryFd = open(path, O_RDWR | O_CLOEXEC);
|
||||
FF_AUTO_CLOSE_FD int primaryFd = open(pathBuf.chars, O_RDWR | O_CLOEXEC);
|
||||
ffStrbufSubstrBefore(&pathBuf, pathLen);
|
||||
if (primaryFd < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
drmModeRes* res = ffdrmModeGetResources(primaryFd);
|
||||
if (!res) {
|
||||
struct drm_mode_card_res res_ = {};
|
||||
if (ioctl(primaryFd, DRM_IOCTL_MODE_GETRESOURCES, &res_) < 0 || res_.count_connectors <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FF_AUTO_FREE uint32_t* connectors = malloc(res_.count_connectors * sizeof(*connectors));
|
||||
struct drm_mode_card_res res = {
|
||||
.count_connectors = res_.count_connectors,
|
||||
.connector_id_ptr = (uintptr_t) connectors,
|
||||
};
|
||||
|
||||
if (ioctl(primaryFd, DRM_IOCTL_MODE_GETRESOURCES, &res) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
++nSuccess;
|
||||
|
||||
for (int iConn = 0; iConn < res->count_connectors; ++iConn) {
|
||||
drmModeConnector* conn = ffdrmModeGetConnectorCurrent(primaryFd, res->connectors[iConn]);
|
||||
if (!conn) {
|
||||
for (uint32_t iConn = 0; iConn < res.count_connectors; ++iConn) {
|
||||
struct drm_mode_get_connector conn = {
|
||||
.connector_id = connectors[iConn]
|
||||
};
|
||||
if (ioctl(primaryFd, DRM_IOCTL_MODE_GETCONNECTOR, &conn) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (conn->connection != DRM_MODE_DISCONNECTED) {
|
||||
drmModeEncoder* encoder = ffdrmModeGetEncoder(primaryFd, conn->encoder_id);
|
||||
uint32_t width = 0, height = 0, refreshRate = 0;
|
||||
uint8_t bitDepth = 0;
|
||||
if (conn.connection == 2 /* connector_status_disconnected */) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (encoder) {
|
||||
drmModeCrtc* crtc = ffdrmModeGetCrtc(primaryFd, encoder->crtc_id);
|
||||
if (crtc) {
|
||||
width = crtc->mode.hdisplay;
|
||||
height = crtc->mode.vdisplay;
|
||||
refreshRate = crtc->mode.vrefresh;
|
||||
if (refreshRate == 0) {
|
||||
// There are weird cases that we can't get the refresh rate from the CRTC but from the modes
|
||||
for (int iMode = 0; iMode < conn->count_modes; ++iMode) {
|
||||
drmModeModeInfo* mode = &conn->modes[iMode];
|
||||
if (mode->clock == crtc->mode.clock && mode->htotal == crtc->mode.htotal) {
|
||||
refreshRate = mode->vrefresh;
|
||||
break;
|
||||
}
|
||||
uint32_t width = 0, height = 0, refreshRate = 0;
|
||||
uint8_t bitDepth = 0;
|
||||
|
||||
if (conn.encoder_id > 0) {
|
||||
struct drm_mode_get_encoder enc = {
|
||||
.encoder_id = conn.encoder_id
|
||||
};
|
||||
if (ioctl(primaryFd, DRM_IOCTL_MODE_GETENCODER, &enc) >= 0 && enc.crtc_id > 0) {
|
||||
struct drm_mode_crtc crtc = {
|
||||
.crtc_id = enc.crtc_id,
|
||||
};
|
||||
if (ioctl(primaryFd, DRM_IOCTL_MODE_GETCRTC, &crtc) >= 0) {
|
||||
width = crtc.mode.hdisplay;
|
||||
height = crtc.mode.vdisplay;
|
||||
refreshRate = crtc.mode.vrefresh;
|
||||
|
||||
if (crtc.fb_id > 0) {
|
||||
struct drm_mode_fb_cmd fb = {
|
||||
.fb_id = crtc.fb_id,
|
||||
};
|
||||
if (ioctl(primaryFd, DRM_IOCTL_MODE_GETFB, &fb) >= 0) {
|
||||
bitDepth = (uint8_t) (fb.depth / 3);
|
||||
}
|
||||
}
|
||||
|
||||
drmModeFBPtr fb = ffdrmModeGetFB(primaryFd, crtc->buffer_id);
|
||||
if (fb) {
|
||||
bitDepth = (uint8_t) (fb->depth / 3);
|
||||
ffdrmModeFreeFB(fb);
|
||||
}
|
||||
|
||||
ffdrmModeFreeCrtc(crtc);
|
||||
}
|
||||
|
||||
ffdrmModeFreeEncoder(encoder);
|
||||
}
|
||||
|
||||
uint32_t preferredWidth = 0, preferredHeight = 0, preferredRefreshRate = 0;
|
||||
|
||||
for (int iMode = 0; iMode < conn->count_modes; ++iMode) {
|
||||
drmModeModeInfo* mode = &conn->modes[iMode];
|
||||
|
||||
if (mode->type & DRM_MODE_TYPE_PREFERRED) {
|
||||
preferredWidth = mode->hdisplay;
|
||||
preferredHeight = mode->vdisplay;
|
||||
preferredRefreshRate = mode->vrefresh;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// NVIDIA DRM driver seems incomplete and conn->encoder_id == 0
|
||||
// Assume preferred resolution is used as what we do in drmParseSys
|
||||
if (width == 0 || height == 0) {
|
||||
width = preferredWidth;
|
||||
height = preferredHeight;
|
||||
refreshRate = preferredRefreshRate;
|
||||
}
|
||||
|
||||
ffStrbufClear(&name);
|
||||
uint16_t myear = 0, mweak = 0;
|
||||
uint32_t serial = 0;
|
||||
FFDisplayHdrStatus hdrStatus = FF_DISPLAY_HDR_STATUS_UNKNOWN;
|
||||
|
||||
for (int iProp = 0; iProp < conn->count_props; ++iProp) {
|
||||
drmModePropertyRes* prop = ffdrmModeGetProperty(primaryFd, conn->props[iProp]);
|
||||
if (!prop) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t type = prop->flags & (DRM_MODE_PROP_LEGACY_TYPE | DRM_MODE_PROP_EXTENDED_TYPE);
|
||||
if (type == DRM_MODE_PROP_BLOB && ffStrEquals(prop->name, "EDID")) {
|
||||
drmModePropertyBlobPtr blob = NULL;
|
||||
|
||||
if (prop->count_blobs > 0 && prop->blob_ids != NULL) {
|
||||
blob = ffdrmModeGetPropertyBlob(primaryFd, prop->blob_ids[0]);
|
||||
} else {
|
||||
blob = ffdrmModeGetPropertyBlob(primaryFd, (uint32_t) conn->prop_values[iProp]);
|
||||
}
|
||||
|
||||
if (blob) {
|
||||
if (blob->length >= 128) {
|
||||
ffEdidGetName(blob->data, &name);
|
||||
hdrStatus = ffEdidGetHdrCompatible(blob->data, blob->length) ? FF_DISPLAY_HDR_STATUS_SUPPORTED : FF_DISPLAY_HDR_STATUS_UNSUPPORTED;
|
||||
ffEdidGetSerialAndManufactureDate(blob->data, &serial, &myear, &mweak);
|
||||
}
|
||||
ffdrmModeFreePropertyBlob(blob);
|
||||
}
|
||||
break;
|
||||
}
|
||||
ffdrmModeFreeProperty(prop);
|
||||
}
|
||||
|
||||
#if __linux__
|
||||
if (name.length == 0) {
|
||||
uint8_t edidData[512];
|
||||
ssize_t edidLength = 0;
|
||||
drmGetEdidByConnId(conn->connector_id, edidData, &edidLength);
|
||||
if (edidLength > 0 && edidLength % 128 == 0) {
|
||||
ffEdidGetName(edidData, &name);
|
||||
hdrStatus = ffEdidGetHdrCompatible(edidData, (uint32_t) edidLength) ? FF_DISPLAY_HDR_STATUS_SUPPORTED : FF_DISPLAY_HDR_STATUS_UNSUPPORTED;
|
||||
ffEdidGetSerialAndManufactureDate(edidData, &serial, &myear, &mweak);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (name.length == 0) {
|
||||
const char* connectorTypeName = drmType2Name(conn->connector_type);
|
||||
if (connectorTypeName == NULL) {
|
||||
connectorTypeName = "Unknown";
|
||||
}
|
||||
ffStrbufSetF(&name, "%s-%d", connectorTypeName, iConn + 1);
|
||||
}
|
||||
|
||||
FFDisplayResult* item = ffdsAppendDisplay(result,
|
||||
width,
|
||||
height,
|
||||
refreshRate,
|
||||
0,
|
||||
preferredWidth,
|
||||
preferredHeight,
|
||||
preferredRefreshRate,
|
||||
0,
|
||||
&name,
|
||||
conn->connector_type == DRM_MODE_CONNECTOR_eDP || conn->connector_type == DRM_MODE_CONNECTOR_LVDS
|
||||
? FF_DISPLAY_TYPE_BUILTIN
|
||||
: conn->connector_type == DRM_MODE_CONNECTOR_HDMIA || conn->connector_type == DRM_MODE_CONNECTOR_HDMIB || conn->connector_type == DRM_MODE_CONNECTOR_DisplayPort
|
||||
? FF_DISPLAY_TYPE_EXTERNAL
|
||||
: FF_DISPLAY_TYPE_UNKNOWN,
|
||||
false,
|
||||
conn->connector_id,
|
||||
conn->mmWidth,
|
||||
conn->mmHeight,
|
||||
"libdrm");
|
||||
|
||||
if (item) {
|
||||
item->hdrStatus = hdrStatus;
|
||||
item->serial = serial;
|
||||
item->manufactureYear = myear;
|
||||
item->manufactureWeek = mweak;
|
||||
item->bitDepth = bitDepth;
|
||||
}
|
||||
}
|
||||
|
||||
ffdrmModeFreeConnector(conn);
|
||||
}
|
||||
uint32_t preferredWidth = 0, preferredHeight = 0, preferredRefreshRate = 0;
|
||||
if (conn.count_modes > 0) {
|
||||
FF_AUTO_FREE struct drm_mode_modeinfo* modes = malloc(conn.count_modes * sizeof(*modes));
|
||||
struct drm_mode_get_connector connModes = {
|
||||
.connector_id = connectors[iConn],
|
||||
.modes_ptr = (uintptr_t) modes,
|
||||
.count_modes = conn.count_modes
|
||||
};
|
||||
if (ioctl(primaryFd, DRM_IOCTL_MODE_GETCONNECTOR, &connModes) >= 0) {
|
||||
for (uint32_t iMode = 0; iMode < connModes.count_modes; ++iMode) {
|
||||
if (modes[iMode].type & DRM_MODE_TYPE_PREFERRED) {
|
||||
preferredWidth = modes[iMode].hdisplay;
|
||||
preferredHeight = modes[iMode].vdisplay;
|
||||
preferredRefreshRate = modes[iMode].vrefresh;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ffdrmModeFreeResources(res);
|
||||
if (width == 0 || height == 0) {
|
||||
width = preferredWidth;
|
||||
height = preferredHeight;
|
||||
refreshRate = preferredRefreshRate;
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate();
|
||||
uint16_t myear = 0, mweak = 0;
|
||||
uint32_t serial = 0;
|
||||
FFDisplayHdrStatus hdrStatus = FF_DISPLAY_HDR_STATUS_UNKNOWN;
|
||||
|
||||
uint32_t* props = malloc(conn.count_props * sizeof(uint32_t));
|
||||
uint64_t* propValues = malloc(conn.count_props * sizeof(uint64_t));
|
||||
if (props && propValues && conn.count_props > 0) {
|
||||
struct drm_mode_get_connector connProps = {
|
||||
.connector_id = connectors[iConn],
|
||||
.props_ptr = (uintptr_t) props,
|
||||
.prop_values_ptr = (uintptr_t) propValues,
|
||||
.count_props = conn.count_props,
|
||||
};
|
||||
if (ioctl(primaryFd, DRM_IOCTL_MODE_GETCONNECTOR, &connProps) >= 0) {
|
||||
for (uint32_t iProp = 0; iProp < connProps.count_props; ++iProp) {
|
||||
struct drm_mode_get_property prop = {
|
||||
.prop_id = props[iProp],
|
||||
};
|
||||
if (ioctl(primaryFd, DRM_IOCTL_MODE_GETPROPERTY, &prop) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t type = prop.flags & (DRM_MODE_PROP_LEGACY_TYPE | DRM_MODE_PROP_EXTENDED_TYPE);
|
||||
if (type == DRM_MODE_PROP_BLOB && ffStrEquals(prop.name, "EDID")) {
|
||||
struct drm_mode_get_blob blob_ = {
|
||||
.blob_id = (uint32_t) propValues[iProp]
|
||||
};
|
||||
if (ioctl(primaryFd, DRM_IOCTL_MODE_GETPROPBLOB, &blob_) >= 0 && blob_.length > 0) {
|
||||
FF_AUTO_FREE void* blobData = malloc(blob_.length);
|
||||
struct drm_mode_get_blob blob = {
|
||||
.blob_id = blob_.blob_id,
|
||||
.length = blob_.length,
|
||||
.data = (uintptr_t) blobData
|
||||
};
|
||||
if (ioctl(primaryFd, DRM_IOCTL_MODE_GETPROPBLOB, &blob) >= 0 && ffEdidIsValid(blobData, blob.length)) {
|
||||
ffEdidGetName(blobData, &name);
|
||||
hdrStatus = ffEdidGetHdrCompatible(blobData, blob.length) ? FF_DISPLAY_HDR_STATUS_SUPPORTED : FF_DISPLAY_HDR_STATUS_UNSUPPORTED;
|
||||
ffEdidGetSerialAndManufactureDate(blobData, &serial, &myear, &mweak);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if __linux__
|
||||
if (name.length == 0) {
|
||||
uint8_t edidData[512];
|
||||
ssize_t edidLength = 0;
|
||||
drmGetEdidByConnId(conn.connector_id, edidData, &edidLength);
|
||||
if (edidLength > 0 && edidLength % 128 == 0) {
|
||||
ffEdidGetName(edidData, &name);
|
||||
hdrStatus = ffEdidGetHdrCompatible(edidData, (uint32_t) edidLength) ? FF_DISPLAY_HDR_STATUS_SUPPORTED : FF_DISPLAY_HDR_STATUS_UNSUPPORTED;
|
||||
ffEdidGetSerialAndManufactureDate(edidData, &serial, &myear, &mweak);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (name.length == 0) {
|
||||
const char* connectorTypeName = drmType2Name(conn.connector_type);
|
||||
if (connectorTypeName == NULL) {
|
||||
connectorTypeName = "Unknown";
|
||||
}
|
||||
ffStrbufSetF(&name, "%s-%d", connectorTypeName, iConn + 1);
|
||||
}
|
||||
|
||||
FFDisplayResult* item = ffdsAppendDisplay(result,
|
||||
width,
|
||||
height,
|
||||
refreshRate,
|
||||
0,
|
||||
preferredWidth,
|
||||
preferredHeight,
|
||||
preferredRefreshRate,
|
||||
0,
|
||||
&name,
|
||||
conn.connector_type == DRM_MODE_CONNECTOR_eDP || conn.connector_type == DRM_MODE_CONNECTOR_LVDS
|
||||
? FF_DISPLAY_TYPE_BUILTIN
|
||||
: conn.connector_type == DRM_MODE_CONNECTOR_HDMIA || conn.connector_type == DRM_MODE_CONNECTOR_HDMIB || conn.connector_type == DRM_MODE_CONNECTOR_DisplayPort
|
||||
? FF_DISPLAY_TYPE_EXTERNAL
|
||||
: FF_DISPLAY_TYPE_UNKNOWN,
|
||||
false,
|
||||
conn.connector_id,
|
||||
conn.mm_width,
|
||||
conn.mm_height,
|
||||
"libdrm");
|
||||
|
||||
if (item) {
|
||||
item->hdrStatus = hdrStatus;
|
||||
item->serial = serial;
|
||||
item->manufactureYear = myear;
|
||||
item->manufactureWeek = mweak;
|
||||
item->bitDepth = bitDepth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ffdrmFreeDevices(devices, nDevices);
|
||||
|
||||
return nSuccess > 0 ? NULL : "No DRM devices succeeded to connect";
|
||||
return nSuccess > 0 ? NULL : "No connectors found using libdrm";
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user