Created
November 9, 2024 14:32
-
-
Save henkwiedig/998aee2c8d5d00290b1ccd23f333501f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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