#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include "drmtap_internal.h"
static int get_property_value(int fd, uint32_t object_id, uint32_t object_type,
const char *name, uint64_t *value) {
drmModeObjectProperties *props = drmModeObjectGetProperties(
fd, object_id, object_type);
if (!props) {
return -1;
}
int found = -1;
for (uint32_t i = 0; i < props->count_props; i++) {
drmModePropertyRes *prop = drmModeGetProperty(fd, props->props[i]);
if (prop) {
if (strcmp(prop->name, name) == 0) {
*value = props->prop_values[i];
found = 0;
}
drmModeFreeProperty(prop);
if (found == 0) {
break;
}
}
}
drmModeFreeObjectProperties(props);
return found;
}
static uint32_t find_cursor_plane(drmtap_ctx *ctx) {
drmModePlaneRes *planes = drmModeGetPlaneResources(ctx->drm_fd);
if (!planes) {
return 0;
}
uint32_t target_crtc = ctx->crtc_id;
uint32_t result = 0;
for (uint32_t i = 0; i < planes->count_planes; i++) {
drmModePlane *plane = drmModeGetPlane(ctx->drm_fd, planes->planes[i]);
if (!plane) {
continue;
}
int crtc_ok = (target_crtc != 0) ? (plane->crtc_id == target_crtc)
: (plane->crtc_id != 0);
if (!crtc_ok) {
drmModeFreePlane(plane);
continue;
}
uint64_t type_val = 0;
if (get_property_value(ctx->drm_fd, plane->plane_id,
DRM_MODE_OBJECT_PLANE, "type",
&type_val) == 0) {
if (type_val == DRM_PLANE_TYPE_CURSOR) {
result = plane->plane_id;
drmModeFreePlane(plane);
break;
}
}
drmModeFreePlane(plane);
}
drmModeFreePlaneResources(planes);
return result;
}
int drmtap_get_cursor(drmtap_ctx *ctx, drmtap_cursor_info *cursor) {
if (!ctx || !cursor) {
return -EINVAL;
}
memset(cursor, 0, sizeof(*cursor));
if (ctx->helper_fd >= 0) {
return drmtap_helper_get_cursor(ctx, cursor);
}
uint32_t cursor_plane_id = find_cursor_plane(ctx);
if (cursor_plane_id == 0) {
cursor->visible = 0;
return 0;
}
drmModePlane *plane = drmModeGetPlane(ctx->drm_fd, cursor_plane_id);
if (!plane) {
return -ENODEV;
}
if (plane->fb_id == 0) {
cursor->visible = 0;
cursor->pixels = NULL;
drmModeFreePlane(plane);
return 0;
}
cursor->visible = 1;
uint64_t crtc_x = 0, crtc_y = 0;
get_property_value(ctx->drm_fd, cursor_plane_id,
DRM_MODE_OBJECT_PLANE, "CRTC_X", &crtc_x);
get_property_value(ctx->drm_fd, cursor_plane_id,
DRM_MODE_OBJECT_PLANE, "CRTC_Y", &crtc_y);
cursor->x = (int32_t)crtc_x;
cursor->y = (int32_t)crtc_y;
uint64_t hot_x = 0, hot_y = 0;
get_property_value(ctx->drm_fd, cursor_plane_id,
DRM_MODE_OBJECT_PLANE, "HOTSPOT_X", &hot_x);
get_property_value(ctx->drm_fd, cursor_plane_id,
DRM_MODE_OBJECT_PLANE, "HOTSPOT_Y", &hot_y);
cursor->hot_x = (int32_t)hot_x;
cursor->hot_y = (int32_t)hot_y;
drmModeFB2 *fb2 = drmModeGetFB2(ctx->drm_fd, plane->fb_id);
if (!fb2) {
drmModeFreePlane(plane);
return drmtap_helper_get_cursor(ctx, cursor);
}
cursor->width = fb2->width;
cursor->height = fb2->height;
if (fb2->handles[0] != 0) {
int prime_fd = -1;
int ret = drmPrimeHandleToFD(ctx->drm_fd, fb2->handles[0],
O_RDONLY | O_CLOEXEC, &prime_fd);
if (ret == 0 && prime_fd >= 0) {
uint32_t stride = fb2->pitches[0];
if (fb2->width <= 256 && fb2->height <= 256 &&
stride != 0 && stride >= fb2->width * 4) {
size_t map_size = (size_t)stride * fb2->height;
void *mapped = mmap(NULL, map_size, PROT_READ, MAP_SHARED,
prime_fd, 0);
if (mapped != MAP_FAILED) {
size_t tight = (size_t)fb2->width * fb2->height * 4;
cursor->pixels = malloc(tight);
if (cursor->pixels) {
for (uint32_t y = 0; y < fb2->height; y++) {
memcpy((uint8_t *)cursor->pixels +
(size_t)y * fb2->width * 4,
(const uint8_t *)mapped + (size_t)y * stride,
(size_t)fb2->width * 4);
}
}
munmap(mapped, map_size);
}
}
close(prime_fd);
}
}
drmModeFreeFB2(fb2);
drmModeFreePlane(plane);
if (cursor->visible && cursor->pixels == NULL) {
return drmtap_helper_get_cursor(ctx, cursor);
}
drmtap_debug_log(ctx, "cursor: %ux%u at (%d,%d) hotspot=(%d,%d) %s",
cursor->width, cursor->height,
cursor->x, cursor->y,
cursor->hot_x, cursor->hot_y,
cursor->visible ? "visible" : "hidden");
return 0;
}
void drmtap_cursor_release(drmtap_ctx *ctx, drmtap_cursor_info *cursor) {
(void)ctx;
if (!cursor) {
return;
}
free(cursor->pixels);
cursor->pixels = NULL;
}