Skip to content

Instantly share code, notes, and snippets.

@henkwiedig
Created November 9, 2024 14:32
Show Gist options
  • Select an option

  • Save henkwiedig/998aee2c8d5d00290b1ccd23f333501f to your computer and use it in GitHub Desktop.

Select an option

Save henkwiedig/998aee2c8d5d00290b1ccd23f333501f to your computer and use it in GitHub Desktop.
#include <drm/drm_mode.h>
#include <cairo/cairo.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <string.h>
// Constants for framebuffer
const int WIDTH = 1280;
const int HEIGHT = 720;
// Utility function to find a free overlay plane
uint32_t find_free_overlay_plane(int fd) {
drmModePlaneRes *plane_res = drmModeGetPlaneResources(fd);
if (!plane_res) {
perror("Cannot get plane resources");
exit(1);
}
uint32_t plane_id = 0;
for (uint32_t i = 0; i < plane_res->count_planes; i++) {
drmModePlane *plane = drmModeGetPlane(fd, plane_res->planes[i]);
// Check if the plane supports overlay (some planes might only support primary display)
if (plane && (plane->possible_crtcs)) {
plane_id = plane->plane_id;
drmModeFreePlane(plane);
break; // Found a suitable plane
}
drmModeFreePlane(plane);
}
drmModeFreePlaneResources(plane_res);
if (!plane_id) {
fprintf(stderr, "No free overlay planes available\n");
exit(1);
}
return plane_id;
}
// Utility function to find the primary CRTC (we assume there's only one CRTC here for simplicity)
uint32_t find_crtc(int fd) {
drmModeRes *res = drmModeGetResources(fd);
if (!res) {
perror("Cannot get resources");
exit(1);
}
uint32_t crtc_id = res->crtcs[0]; // Assume the first CRTC is the primary one for simplicity
drmModeFreeResources(res);
return crtc_id;
}
// Create a framebuffer for OSD content
uint32_t create_framebuffer(int fd, uint32_t *fb_handle, uint8_t **map) {
// Request dumb buffer for framebuffer
struct drm_mode_create_dumb create = {
.width = WIDTH,
.height = HEIGHT,
.bpp = 32,
};
if (ioctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create) < 0) {
perror("Cannot create dumb buffer");
exit(1);
}
*fb_handle = create.handle;
// Create DRM framebuffer
uint32_t fb_id;
if (drmModeAddFB(fd, WIDTH, HEIGHT, 32, 32, create.pitch, *fb_handle, &fb_id) != 0) {
perror("Cannot add framebuffer");
exit(1);
}
// Map framebuffer to userspace
struct drm_mode_map_dumb map_dumb = { .handle = create.handle };
if (ioctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb) < 0) {
perror("Cannot map dumb buffer");
exit(1);
}
*map = mmap(0, create.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, map_dumb.offset);
if (*map == MAP_FAILED) {
perror("Cannot mmap dumb buffer");
exit(1);
}
return fb_id;
}
// Render OSD content with Cairo
void render_osd(uint8_t *map) {
cairo_surface_t *surface = cairo_image_surface_create_for_data(
map, CAIRO_FORMAT_ARGB32, WIDTH, HEIGHT, cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, WIDTH)
);
cairo_t *cr = cairo_create(surface);
// Clear with a fully transparent background
cairo_set_source_rgba(cr, 0, 0, 0, 0);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_paint(cr);
// Example overlay content
//cairo_set_source_rgba(cr, 1, 0, 0, 0.5); // Semi-transparent red
//cairo_rectangle(cr, 100, 100, 400, 200);
//cairo_fill(cr);
// Example text overlay
cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size(cr, 40);
cairo_set_source_rgba(cr, 1, 1, 1, 1); // White text
cairo_move_to(cr, 120, 180);
cairo_show_text(cr, "Overlay OSD Text");
cairo_destroy(cr);
cairo_surface_destroy(surface);
}
// Utility to set a DRM property by name
void set_plane_property(int fd, uint32_t plane_id, const char *prop_name, uint64_t value) {
drmModeObjectProperties *props = drmModeObjectGetProperties(fd, plane_id, DRM_MODE_OBJECT_PLANE);
if (!props) {
perror("Cannot get plane properties");
return;
}
for (uint32_t i = 0; i < props->count_props; i++) {
drmModePropertyRes *prop = drmModeGetProperty(fd, props->props[i]);
if (prop && strcmp(prop->name, prop_name) == 0) {
drmModeObjectSetProperty(fd, plane_id, DRM_MODE_OBJECT_PLANE, prop->prop_id, value);
drmModeFreeProperty(prop);
break;
}
drmModeFreeProperty(prop);
}
drmModeFreeObjectProperties(props);
}
int main() {
// Step 1: Open DRM device
int fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
if (fd < 0) {
perror("Cannot open DRM device");
exit(1);
}
// Step 2: Find CRTC and free overlay plane
uint32_t crtc_id = find_crtc(fd);
uint32_t plane_id = find_free_overlay_plane(fd);
// Example: Set plane transparency or blend mode (if available)
set_plane_property(fd, plane_id, "alpha", 0xFF); // Set max alpha (opaque)
set_plane_property(fd, plane_id, "zpos", 1); // Set z-order, if supported
// Step 3: Create framebuffer for overlay content
uint32_t fb_handle, fb_id;
uint8_t *map;
fb_id = create_framebuffer(fd, &fb_handle, &map);
// Step 4: Render initial OSD content to framebuffer
render_osd(map);
// Step 5: Set up the overlay plane with the framebuffer
if (drmModeSetPlane(fd, plane_id, crtc_id, fb_id, 0,
0, 0, WIDTH, HEIGHT, 0 << 16, 0 << 16, WIDTH << 16, HEIGHT << 16)) {
perror("Cannot set plane");
exit(1);
}
// Step 6: Keep running, simulate an event loop
while (1) {
render_osd(map); // Re-render content if needed
drmModeSetPlane(fd, plane_id, crtc_id, fb_id, 0,
0, 0, WIDTH, HEIGHT, 0 << 16, 0 << 16, WIDTH << 16, HEIGHT << 16);
usleep(500000); // Sleep for 500 ms
}
// Clean up resources
drmModeRmFB(fd, fb_id);
munmap(map, WIDTH * HEIGHT * 4); // Unmap the framebuffer
close(fd);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment