Added drm support

This commit is contained in:
cldemote 2025-08-25 15:14:18 +10:00
parent 3e75f74d6f
commit cdd5c9bcf5
10 changed files with 486 additions and 122 deletions

View File

@ -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 -

View File

@ -1,7 +1,23 @@
#include "twm.h"
#include "../twm.h"
#include <stdio.h>
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;
}

View File

@ -1,34 +1,88 @@
#include "twm.h"
#include "../twm.h"
#include <stdio.h>
#include <dlfcn.h>
#include <dirent.h>
#include <string.h>
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;
}

View File

@ -1,38 +0,0 @@
#include <stdint.h>
#include <stdbool.h>
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;

View File

@ -1,8 +0,0 @@
#ifndef _TWM_H
#define _TWM_H
#include "module_display.h"
#include <stdlib.h>
ModuleDisplay load_display_module(char* name);
#endif

25
src/module.h Normal file
View File

@ -0,0 +1,25 @@
#include <stdbool.h>
#include <stdint.h>
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;

View File

@ -1,38 +0,0 @@
#include <stdint.h>
#include <stdbool.h>
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;

228
src/modules/drm.c Normal file
View File

@ -0,0 +1,228 @@
#define MODULE_NAME "drm"
#include "../twm.h"
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <drm_fourcc.h>
#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);
}

View File

@ -1 +1,83 @@
void t(){}
#define MODULE_NAME "drm"
#include "../twm.h"
#include <linux/fb.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
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<<data->vscrinfo.red.offset) | (col.green<<data->vscrinfo.green.offset) | (col.blue<<data->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);
}

View File

@ -1,8 +1,47 @@
#ifndef _TWM_H
#define _TWM_H
#include "module_display.h"
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
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