From cdd5c9bcf53e6bfda8d06ff341f169cf9b2dd575 Mon Sep 17 00:00:00 2001 From: pcmpstri Date: Mon, 25 Aug 2025 15:14:18 +1000 Subject: [PATCH] Added drm support --- Makefile | 10 +- src/core/main.c | 22 +++- src/core/module.c | 112 ++++++++++++++----- src/core/module_display.h | 38 ------- src/core/twm.h | 8 -- src/module.h | 25 +++++ src/module_display.h | 38 ------- src/modules/drm.c | 228 ++++++++++++++++++++++++++++++++++++++ src/modules/fb.c | 84 +++++++++++++- src/twm.h | 43 ++++++- 10 files changed, 486 insertions(+), 122 deletions(-) delete mode 100644 src/core/module_display.h delete mode 100644 src/core/twm.h create mode 100644 src/module.h delete mode 100644 src/module_display.h create mode 100644 src/modules/drm.c diff --git a/Makefile b/Makefile index f0fcbad..b684b49 100755 --- a/Makefile +++ b/Makefile @@ -1,16 +1,20 @@ CC = gcc -CFLAGS = -Wall -Wshadow -Werror -LDFLAGS = -lc +CFLAGS = -Wall -Wshadow -Werror -I/usr/include/libdrm +LDFLAGS = -lc -ldrm SRCS := $(wildcard src/modules/*.c) OBJS := $(patsubst src/modules/%.c,%,$(SRCS)) +MODS := $(patsubst src/modules/%.c,%.so,$(SRCS)) %: mkdir -p build/modules - $(CC) $(CFLAGS) src/modules/$@.c -o build/modules/$@.so -shared -fPIC -rdynamic + $(CC) $(CFLAGS) src/modules/$@.c -o build/modules/$@.so -shared -fPIC -rdynamic -g all: $(OBJS) gcc -std=c23 -g -o build/twm src/core/*.c $(CFLAGS) $(LDFLAGS) -ldl clean: rm -rf build/* + +run: all + cd build; ./twm drm fb uevent ; cd - diff --git a/src/core/main.c b/src/core/main.c index 1d27d5f..f174240 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1,7 +1,23 @@ -#include "twm.h" +#include "../twm.h" +#include int main(int argc, char* argv[]) { - ModuleDisplay m = load_display_module("modules/fb.so"); - if (m.init == 0) abort(); + Modules ms = load_modules("modules", argv + 1, argc - 1); + Vec2 (*get_size)() = module_find_func(ms, DISPLAY_GET_SIZE); + void (*flip)() = module_find_func(ms, DISPLAY_FLIP); + void (*put_pixel)(Vec2, Color) = module_find_func(ms, DISPLAY_PUT_PIXEL); + + Vec2 display_size = get_size(); + for (int x = 0; x < display_size.x; x++) { + for (int y = 0; y < display_size.y; y++) { + put_pixel((Vec2){x,y}, (Color){x%256,y%256,0}); + } + } + + flip(); + + getchar(); + + unload_modules(ms); return 0; } diff --git a/src/core/module.c b/src/core/module.c index 00e38f3..787335e 100644 --- a/src/core/module.c +++ b/src/core/module.c @@ -1,34 +1,88 @@ -#include "twm.h" +#include "../twm.h" #include #include +#include +#include -ModuleDisplay load_display_module(char* name) { - void* dl = dlopen(name, RTLD_LAZY); - if (dl == NULL) { - fflush(stdout); - fprintf(stderr, "Failed to load module %s (%s)\n", name, dlerror()); - fflush(stderr); - return (ModuleDisplay){0}; +int bitcount(uint64_t v) { + int count = 0; + while (v > 0) { + v &= v - 1; + count ++; } - - ModuleDisplay m = {0}; - m.dll = dl; - m.init = (DisplayInit)dlsym(dl, "display_init"); - if (m.init == NULL) goto error; - m.blit = (DisplayBlit)dlsym(dl, "display_blit"); - if (m.blit == NULL) goto error; - m.flip = (DisplayFlip)dlsym(dl, "display_flip"); - if (m.flip == NULL) goto error; - m.close = (DisplayClose)dlsym(dl, "display_close"); - if (m.close == NULL) goto error; - m.put_pixel = (DisplayPutPixel)dlsym(dl, "display_put_pixel"); - if (m.put_pixel == NULL) goto error; - return m; - -error: - fflush(stdout); - fprintf(stderr, "Failed to load module %s (%s)\n", name, dlerror()); - fflush(stderr); - dlclose(dl); - return (ModuleDisplay){0}; + return count; +} + +void unload_modules(Modules ms) { + for (size_t i = 0; i < ms.count; i++) { + ms.modules[i].close(); + dlclose(ms.modules[i].dll); + } +} + +Modules load_modules(char *module_dir, char *modules[], size_t num_modules) { + Modules ms = { 0 }; + uint64_t needed = BIT(FEATURE_MAX) - 1; + + for (size_t i = 0; i < num_modules && needed != 0; i++) { + Module m = { 0 }; + char dll_name[1024]; + strcpy(dll_name, module_dir); + strcat(dll_name, "/"); + strcat(dll_name, modules[i]); + strcat(dll_name, ".so"); + m.dll = dlopen(dll_name, RTLD_LAZY); + if (!m.dll) { + LOG_WARNING("Failed to load library %s (%s)", dll_name, dlerror()); + continue; + } + uint64_t *feature_lists = (uint64_t*)dlsym(m.dll, "FEATURE_LISTS"); + + size_t best_feature = 0; + int best_feature_value = 0; + + for (size_t j = 0; feature_lists[j] != 0; j++) { + if ((needed & feature_lists[j]) != 0 && (~needed & feature_lists[j]) == 0) { + int value = bitcount(feature_lists[j]); + if (value > best_feature_value) { + best_feature_value = value; + best_feature = j; + } + } + } + if (best_feature_value == 0) { + dlclose(m.dll); + continue; + } + + m.init = (ModuleInit)dlsym(m.dll, "module_init"); + twm_assert(m.init, "Module doesn't have module_init"); + m.close = (ModuleClose)dlsym(m.dll, "module_close"); + twm_assert(m.close, "Module doesn't have module_close"); + + if ((m.funcs = (void*)m.init(best_feature)) == NULL) { + dlclose(m.dll); + continue; + } + + ms.modules = realloc(ms.modules, sizeof(Module) * ++ms.count); + twm_assert(ms.modules, "Get more ram"); + memcpy(&ms.modules[ms.count - 1], &m, sizeof(Module)); + needed &= ~feature_lists[best_feature]; + } + if (needed != 0) { + LOG_WARNING("Failed to find all needed modules: %lu", needed); + } + return ms; +} + +void* module_find_func(Modules ms, ModuleFunc type) { + for (size_t i = 0; i < ms.count; i++) { + for (size_t j = 0; ms.modules[i].funcs[j].function_type != FUNC_END; j++) { + if (ms.modules[i].funcs[j].function_type == type) { + return ms.modules[i].funcs[j].func; + } + } + } + return NULL; } diff --git a/src/core/module_display.h b/src/core/module_display.h deleted file mode 100644 index 4ef1019..0000000 --- a/src/core/module_display.h +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include - -typedef struct { - uint8_t red; - uint8_t green; - uint8_t blue; -} Color; - -typedef struct { - int x; - int y; -} Vec2; - -typedef struct { - Vec2 pos; - Vec2 size; -} Rect; - -typedef struct { - Rect size; - Color *data; -} Image; - -typedef bool (*DisplayInit)(void**); -typedef void (*DisplayPutPixel)(void*, Vec2, Color); -typedef void (*DisplayBlit)(void*, Vec2, Image); -typedef void (*DisplayFlip)(void*); -typedef void (*DisplayClose)(void*); - -typedef struct { - void* dll; - DisplayInit init; - DisplayPutPixel put_pixel; - DisplayBlit blit; - DisplayFlip flip; - DisplayClose close; -} ModuleDisplay; diff --git a/src/core/twm.h b/src/core/twm.h deleted file mode 100644 index 0cc9567..0000000 --- a/src/core/twm.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _TWM_H -#define _TWM_H -#include "module_display.h" -#include - -ModuleDisplay load_display_module(char* name); - -#endif diff --git a/src/module.h b/src/module.h new file mode 100644 index 0000000..eae1c48 --- /dev/null +++ b/src/module.h @@ -0,0 +1,25 @@ +#include +#include + +typedef void* (*ModuleInit)(int); +typedef void (*ModuleClose)(); + +typedef struct { + ModuleFunc function_type; + void *func; +} FeatureFunc; + +typedef struct { + void *dll; + void *state; + + ModuleInit init; + ModuleClose close; + + FeatureFunc *funcs; +} Module; + +typedef struct { + uint32_t count; + Module *modules; +} Modules; diff --git a/src/module_display.h b/src/module_display.h deleted file mode 100644 index 4ef1019..0000000 --- a/src/module_display.h +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include - -typedef struct { - uint8_t red; - uint8_t green; - uint8_t blue; -} Color; - -typedef struct { - int x; - int y; -} Vec2; - -typedef struct { - Vec2 pos; - Vec2 size; -} Rect; - -typedef struct { - Rect size; - Color *data; -} Image; - -typedef bool (*DisplayInit)(void**); -typedef void (*DisplayPutPixel)(void*, Vec2, Color); -typedef void (*DisplayBlit)(void*, Vec2, Image); -typedef void (*DisplayFlip)(void*); -typedef void (*DisplayClose)(void*); - -typedef struct { - void* dll; - DisplayInit init; - DisplayPutPixel put_pixel; - DisplayBlit blit; - DisplayFlip flip; - DisplayClose close; -} ModuleDisplay; diff --git a/src/modules/drm.c b/src/modules/drm.c new file mode 100644 index 0000000..b758652 --- /dev/null +++ b/src/modules/drm.c @@ -0,0 +1,228 @@ +#define MODULE_NAME "drm" +#include "../twm.h" +#include +#include +#include +#include +#include +#include +#include + +#define max(a,b) (((a)>(b))?(a):(b)) + +typedef struct { + uint32_t id; // Unknown use + uint32_t width; + uint32_t height; + uint32_t pitch; + uint32_t handle; + uint64_t size; + uint8_t *data; +} DRMFb; + +typedef struct _DRMConnector { + uint32_t id; + bool enabled; + + drmModeCrtcPtr old_mode; + + uint32_t crtc_id; + drmModeModeInfo mode; + + Vec2 size; + DRMFb frame; + + struct _DRMConnector *next; +} DRMConnector; + +typedef struct { + int drm_fd; + DRMFb frameBuffer; + DRMConnector *connectors; +} DRMState; + +static DRMState *data; + +uint32_t find_crtc(int fd, drmModeResPtr resources, drmModeConnectorPtr connector, uint32_t *taken) { + for (int i = 0; i < connector->count_encoders; i++) { + drmModeEncoderPtr encoder = drmModeGetEncoder(fd, connector->encoders[i]); + if (!encoder) continue; + for (int j = 0; j < resources->count_crtcs; j++) { + if ((encoder->possible_crtcs & BIT(j)) == 0) continue; // Not compatible + if ((*taken & BIT(j)) != 0) continue; // Already taken + drmModeFreeEncoder(encoder); + *taken |= BIT(j); + return resources->crtcs[j]; + } + drmModeFreeEncoder(encoder); + } + LOG_WARNING("Failed to find CRTC for connector %d", connector->connector_id); + return 0; +} + +bool create_fb(int fd, DRMFb *out, Vec2 size) { + if(drmModeCreateDumbBuffer(fd, size.x, size.y, 32, 0, &out->handle, &out->pitch, &out->size) < 0) return false; + + uint32_t handles[4] = { out->handle }; + uint32_t pitches[4] = { out->pitch }; + uint32_t offsets[4] = { 0 }; + + if (drmModeAddFB2(fd, size.x, size.y, DRM_FORMAT_XRGB8888, handles, pitches, offsets, &out->id, 0) < 0) goto err_dumb; + + uint64_t offset; + if(drmModeMapDumbBuffer(fd,out->handle, &offset) < 0) goto err_dumb; + + out->data = mmap(0, out->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset); + if (out->data == MAP_FAILED) goto err_fb; + return true; + +err_fb: + drmModeRmFB(fd, out->id); +err_dumb: + drmModeDestroyDumbBuffer(fd, out->handle); + return false; +} + +void destroy_fb(int fd, DRMFb *fb) { + munmap(fb->data, fb->size); + drmModeRmFB(fd, fb->id); + drmModeDestroyDumbBuffer(fd, fb->handle); +} + +void display_put_pixel(Vec2 position, Color col) { + DRMConnector *conn = data->connectors; + while (conn) { + if (conn->enabled) { + if (position.x < 0 || position.x >= conn->size.x || position.y < 0 || position.y >= conn->size.y) continue; + uint8_t *row = conn->frame.data + position.y*conn->frame.pitch; + row[position.x * 4 + 0] = col.blue; + row[position.x * 4 + 1] = col.green; + row[position.x * 4 + 2] = col.red; + row[position.x * 4 + 3] = 0x00; + } + conn = conn->next; + } +} + +void display_blit(Vec2 pos, Image image) { + for (int x = 0; x < image.size.x; x++) { + for (int y = 0; y < image.size.y; y++) { + display_put_pixel((Vec2){x+pos.x,y+pos.y}, image.data[y][x]); + } + } +} + +void display_flip() { + DRMConnector *conn = data->connectors; + while (conn) { + if (conn->enabled) { + drmModeClip clip = (drmModeClip){.x1 = 0, .y1 = 0, .x2 = conn->frame.width, .y2 = conn->frame.height}; + drmModeDirtyFB(data->drm_fd, conn->frame.id, &clip, 1); + } + conn = conn->next; + } +} + +Vec2 display_get_size() { + int maxX = 0; + int maxY = 0; + DRMConnector *conn = data->connectors; + while (conn) { + if (conn->enabled) { + maxX = max(maxX, conn->size.x); + maxY = max(maxY, conn->size.y); + } + conn = conn->next; + } + return (Vec2){maxX, maxY}; +} + +//-------------------------------------------------------- + +const uint64_t FEATURE_LISTS[] = { + BIT(BASIC_RENDERING) | BIT(ADVANCED_RENDERING), + 0 +}; + +const FeatureFunc FUNCTIONS[] = { + (FeatureFunc){ DISPLAY_PUT_PIXEL, display_put_pixel }, + (FeatureFunc){ DISPLAY_BLIT, display_blit }, + (FeatureFunc){ DISPLAY_GET_SIZE, display_get_size }, + (FeatureFunc){ DISPLAY_FLIP, display_flip }, + (FeatureFunc){ FUNC_END, NULL } +}; + +FeatureFunc* module_init(size_t features_selected) { + data = malloc(sizeof(DRMState)); + if (data == NULL) return NULL; + + data->drm_fd = open("/dev/dri/card0", O_RDWR); + + drmModeResPtr resources = drmModeGetResources(data->drm_fd); + twm_assert_custom(resources, "Failed to find drm resources", return NULL); + + data->connectors = NULL; + uint32_t taken_crtcs = 0; + + for (int i = 0; i < resources->count_connectors; i++) { + drmModeConnectorPtr drm_connector = drmModeGetConnector(data->drm_fd, resources->connectors[i]); + twm_assert_custom(drm_connector != NULL, "Failed to open connector", continue); + + DRMConnector *connector = malloc(sizeof(DRMConnector)); + twm_assert_custom(connector != NULL, "Failed to allocate connector data", goto cleanup); + connector->next = data->connectors; + data->connectors = connector; + + connector->id = drm_connector->connector_id; + connector->enabled = drm_connector->connection == DRM_MODE_CONNECTED; + + LOG_INFO("Found connector %d (%s)", connector->id, connector->enabled?"Enabled":"Disabled"); + + if (!connector->enabled) goto cleanup; + twm_assert_custom(drm_connector->modes != NULL, "Connector has no modes", connector->enabled = false; goto cleanup); + + connector->crtc_id = find_crtc(data->drm_fd, resources, drm_connector, &taken_crtcs); + twm_assert_custom(connector->crtc_id != 0, "Failed to find crtc", connector->enabled = false; goto cleanup); + + LOG_INFO("\tFound CRTC %d", connector->crtc_id); + + connector->mode = drm_connector->modes[0]; + connector->size = (Vec2){connector->mode.hdisplay, connector->mode.vdisplay}; + + LOG_INFO("\tUsing Mode %dx%d", connector->size.x, connector->size.y); + + twm_assert_custom(create_fb(data->drm_fd, &connector->frame, connector->size), "Failed to make framebuffer", connector->enabled = false; goto cleanup); + LOG_INFO("\tMade framebuffer with id %d", connector->frame.id); + + connector->old_mode = drmModeGetCrtc(data->drm_fd, connector->crtc_id); + + twm_assert_custom(drmModeSetCrtc(data->drm_fd, connector->crtc_id, connector->frame.id, 0, 0, &connector->id, 1, &connector->mode) >= 0, "Failed to set CRTC mode", connector->enabled = false; goto cleanup); + + cleanup: + drmModeFreeConnector(drm_connector); + } + + drmModeFreeResources(resources); + + return (FeatureFunc*)FUNCTIONS; +} + + +void module_close() { + while (data->connectors) { + if (data->connectors->enabled) { + destroy_fb(data->drm_fd, &data->connectors->frame); + drmModeCrtcPtr crtc = data->connectors->old_mode; + if (crtc) { + drmModeSetCrtc(data->drm_fd, crtc->crtc_id, crtc->buffer_id, + crtc->x, crtc->y, &data->connectors->id, 1, &crtc->mode); + drmModeFreeCrtc(crtc); + } + } + DRMConnector *tmp = data->connectors->next; + free(data->connectors); + data->connectors = tmp; + } + close(data->drm_fd); + free(data); +} diff --git a/src/modules/fb.c b/src/modules/fb.c index 466f72f..3285a9f 100644 --- a/src/modules/fb.c +++ b/src/modules/fb.c @@ -1 +1,83 @@ -void t(){} +#define MODULE_NAME "drm" +#include "../twm.h" +#include +#include +#include +#include +#include + +typedef struct { + int fb_fd; + long screen_size; + struct fb_fix_screeninfo fscrinfo; + struct fb_var_screeninfo vscrinfo; + uint8_t *frame_buffer; +} FBData; + +static FBData *data; + +Vec2 display_get_size() { + return (Vec2){data->vscrinfo.xres, data->vscrinfo.yres}; +} + +uint32_t get_pixel_color(Color col) { + return (col.red<vscrinfo.red.offset) | (col.green<vscrinfo.green.offset) | (col.blue<vscrinfo.blue.offset); +} + +void display_put_pixel(Vec2 pos, Color col) { + long pixel_offset = (pos.x + data->vscrinfo.xoffset) * (data->vscrinfo.bits_per_pixel / 8) + (pos.y + data->vscrinfo.yoffset) * data->fscrinfo.line_length; + *((uint32_t*)(data->frame_buffer + pixel_offset)) = get_pixel_color(col); +} + + +void display_blit(Vec2 pos, Image image) { + for (int x = 0; x < image.size.x; x++) { + for (int y = 0; y < image.size.y; y++) { + display_put_pixel((Vec2){x+pos.x,y+pos.y}, image.data[y][x]); + } + } +} + +void display_flip() { +} + +//--------------------------------------------- + +const uint64_t FEATURE_LISTS[] = { + BIT(BASIC_RENDERING), + 0 +}; + +const FeatureFunc FUNCTIONS[] = { + (FeatureFunc){ DISPLAY_PUT_PIXEL, display_put_pixel }, + (FeatureFunc){ DISPLAY_BLIT, display_blit }, + (FeatureFunc){ DISPLAY_GET_SIZE, display_get_size }, + (FeatureFunc){ DISPLAY_FLIP, display_flip }, + (FeatureFunc){ FUNC_END, NULL }, +}; + +FeatureFunc *module_init(size_t feature_selected) { + data = malloc(sizeof(FBData)); + if (data == NULL) return NULL; + data->fb_fd = open("/dev/fb0", O_RDWR); + + ioctl(data->fb_fd, FBIOGET_FSCREENINFO, &data->fscrinfo); + ioctl(data->fb_fd, FBIOGET_VSCREENINFO, &data->vscrinfo); + + data->vscrinfo.bits_per_pixel = 32; + data->vscrinfo.grayscale = 0; + + ioctl(data->fb_fd, FBIOPUT_VSCREENINFO, &data->vscrinfo); + ioctl(data->fb_fd, FBIOGET_VSCREENINFO, &data->vscrinfo); + + data->screen_size = data->vscrinfo.yres_virtual * data->fscrinfo.line_length; + data->frame_buffer = mmap(0, data->screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, data->fb_fd, (off_t)0); + + return (FeatureFunc*)FUNCTIONS; +} + +void module_close() { + munmap(data->frame_buffer, data->screen_size); + close(data->fb_fd); + free(data); +} diff --git a/src/twm.h b/src/twm.h index 0cc9567..daeb078 100644 --- a/src/twm.h +++ b/src/twm.h @@ -1,8 +1,47 @@ #ifndef _TWM_H #define _TWM_H -#include "module_display.h" #include +#include +#include -ModuleDisplay load_display_module(char* name); +typedef enum { + BASIC_RENDERING = 0, + ADVANCED_RENDERING = 1, + FEATURE_MAX +} ModuleFeatures; + +typedef enum { + FUNC_END = 0, + DISPLAY_PUT_PIXEL, + DISPLAY_BLIT, + DISPLAY_FLIP, + DISPLAY_GET_SIZE, + MODULE_FUNC_MAX +} ModuleFunc; + +#include "module.h" + +#define BIT(n) ((uint64_t)1<<(n)) + +#ifdef MODULE_NAME +#define LOG_WARNING(f, ...) do {printf("\033[33;1m[ WARNING ]\033[0m Module " MODULE_NAME ": " f "\n", __VA_ARGS__); fflush(stdout); } while (0) +#define LOG_INFO(f, ...) do {printf("\033[34;1m[ INFO ]\033[0m Module " MODULE_NAME ": " f "\n", __VA_ARGS__); fflush(stdout); } while (0) +#define LOG_ERROR(f, ...) do {printf("\033[31;1m[ ERROR ]\033[0m Module " MODULE_NAME ": " f "\n", __VA_ARGS__); fflush(stdout); } while (0) +#else +#define LOG_WARNING(f, ...) do {printf("\033[33;1m[ WARNING ]\033[0m " f "\n", __VA_ARGS__); fflush(stdout); } while (0) +#define LOG_INFO(f, ...) do {printf("\033[34;1m[ INFO ]\033[0m " f "\n", __VA_ARGS__); fflush(stdout); } while (0) +#define LOG_ERROR(f, ...) do {printf("\033[31;1m[ ERROR ]\033[0m " f "\n", __VA_ARGS__); fflush(stdout); } while (0) +#endif + +#define twm_assert(e, m) do { if (!e) { LOG_ERROR("Assertion Failed: %s (%s:%d %s)", m, __FILE__, __LINE__, #e); abort(); } } while (0) +#define twm_assert_custom(e, m, c) do { if (!(e)) { LOG_ERROR("Assertion Failed: %s (%s:%d %s)", m, __FILE__, __LINE__, #e); c; } } while (0) + +Modules load_modules(char*, char*[], size_t); +void unload_modules(Modules); +void* module_find_func(Modules ms, ModuleFunc type); + +typedef struct { int x; int y; } Vec2; +typedef struct { uint8_t red; uint8_t green; uint8_t blue; } Color; +typedef struct { Vec2 size; Color** data; } Image; #endif