Skip to content

Commit

Permalink
Support Linux framebuffer as new backend
Browse files Browse the repository at this point in the history
  • Loading branch information
shengwen-tw committed Jul 24, 2024
1 parent 2d85c45 commit fc00614
Show file tree
Hide file tree
Showing 4 changed files with 345 additions and 1 deletion.
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# FIXME: make these entries configurable
CONFIG_BACKEND_SDL := y
CONFIG_BACKEND_FB := y
CONFIG_LOADER_JPEG := y
CONFIG_LOADER_PNG := y

Expand Down Expand Up @@ -88,14 +89,18 @@ libbackend.a_cflags-y += $(shell sdl2-config --cflags)
TARGET_LIBS += $(shell sdl2-config --libs)
endif

ifeq ($(CONFIG_BACKEND_SDL), y)
libbackend.a_files-y += backend/fb.c
endif

# Platform-specific executables

ifeq ($(CONFIG_BACKEND_SDL), y)
target-y += demo-sdl

demo-sdl_depends-y += $(target.a-y)
demo-sdl_files-y = \
apps/twin-sdl.c
apps/twin-apps-main.c

demo-sdl_includes-y := include
demo-sdl_includes-y += backend
Expand Down
164 changes: 164 additions & 0 deletions apps/twin-apps-main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
* Twin - A Tiny Window System
* Copyright (c) 2004 Keith Packard <[email protected]>
* All rights reserved.
*/

#include <assert.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <twin.h>
#include <unistd.h>

#include "twin_fb.h"
#include "twin_sdl.h"

#include "apps_calc.h"
#include "apps_clock.h"
#include "apps_demo.h"
#include "apps_hello.h"
#include "apps_line.h"
#include "apps_spline.h"

#define WIDTH 640
#define HEIGHT 480

#define ASSET_PATH "assets/"

/**
* Load the background pixmap from storage.
* @filename: File name of a PNG background.
*
* Return a default pattern if the load of @filename fails.
*/
static twin_pixmap_t *load_background(twin_screen_t *screen,
const char *filename)
{
twin_pixmap_t *raw_background = twin_png_to_pixmap(filename, TWIN_ARGB32);
if (!raw_background) /* Fallback to a default pattern */
return twin_make_pattern();

if (screen->height == raw_background->height &&
screen->width == raw_background->width)
return raw_background;

/* Scale as needed. */
twin_pixmap_t *scaled_background =
twin_pixmap_create(TWIN_ARGB32, screen->width, screen->height);
if (!scaled_background) {
twin_pixmap_destroy(raw_background);
return twin_make_pattern();
}
twin_fixed_t sx, sy;
sx = twin_fixed_div(twin_int_to_fixed(raw_background->width),
twin_int_to_fixed(screen->width));
sy = twin_fixed_div(twin_int_to_fixed(raw_background->height),
twin_int_to_fixed(screen->height));

twin_matrix_scale(&raw_background->transform, sx, sy);
twin_operand_t srcop = {
.source_kind = TWIN_PIXMAP,
.u.pixmap = raw_background,
};
twin_composite(scaled_background, 0, 0, &srcop, 0, 0, NULL, 0, 0,
TWIN_SOURCE, screen->width, screen->height);

twin_pixmap_destroy(raw_background);

return scaled_background;
}

static void usage(const char *execpath)
{
fprintf(stderr, "Usage: %s -b backend [-p fb-path]\n", execpath);
}

static void handle_options(int argc,
char **argv,
char **backend,
char **fb_path)
{
*backend = *fb_path = NULL;

int optidx = 0;
struct option opts[] = {
{"backend", 1, NULL, 'b'},
{"fb-path", 1, NULL, 'p'},
{"help", 0, NULL, 'h'},
};

int c;
while ((c = getopt_long(argc, argv, "b:p:h", opts, &optidx)) != -1) {
switch (c) {
case 'b':
*backend = optarg;
break;
case 'p':
*fb_path = optarg;
break;
case 'h':
usage(argv[0]);
exit(0);
default:
break;
}
}

if (!*backend) {
fprintf(stderr, "Must assign a backend via -b option.\n");
usage(argv[0]);
exit(2);
}
}

int main(int argc, char **argv)
{
char *backend, *fb_path;
handle_options(argc, argv, &backend, &fb_path);

twin_sdl_t *sdl;
twin_fb_t *fb;
twin_screen_t *screen;

if (!strcmp(backend, "sdl")) {
/* Select SDL as backend */
sdl = twin_sdl_create(WIDTH, HEIGHT);
screen = sdl->screen;
} else if (!strcmp(backend, "fb")) {
if (!fb_path) {
fprintf(
stderr,
"Must assign the path to framebuffer device via -p option.\n");
usage(argv[0]);
exit(2);
}

/* Select Linux framebuffer as backend */
fb = twin_fb_create(fb_path);
screen = fb->screen;
} else {
/* Unknown backend */
fprintf(stderr,
"Unknown backend: %s\n"
"Must be 'sdl' or 'fb'.\n",
backend);
exit(2);
}

twin_screen_set_background(screen,
load_background(screen, ASSET_PATH "/tux.png"));

apps_demo_start(screen, "Demo", 100, 100, 400, 400);
apps_hello_start(screen, "Hello, World", 0, 0, 200, 200);
apps_clock_start(screen, "Clock", 10, 10, 200, 200);
apps_calc_start(screen, "Calculator", 100, 100, 200, 200);
apps_line_start(screen, "Line", 0, 0, 200, 200);
apps_spline_start(screen, "Spline", 20, 20, 400, 400);

twin_dispatch();
return 0;
}
145 changes: 145 additions & 0 deletions backend/fb.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* Twin - A Tiny Window System
* Copyright (c) 2024 National Cheng Kung University, Taiwan
* All rights reserved.
*/

#include <fcntl.h>
#include <linux/fb.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>

#include "twin_fb.h"
#include "twin_private.h"

static void _twin_fb_put_begin(twin_coord_t left,
twin_coord_t top,
twin_coord_t right,
twin_coord_t bottom,
void *closure)
{
twin_fb_t *tx = closure;
tx->width = right - left;
tx->height = bottom - top;
tx->image_y = top;
}

static void _twin_fb_put_span(twin_coord_t left,
twin_coord_t top,
twin_coord_t right,
twin_argb32_t *pixels,
void *closure)
{
twin_fb_t *tx = closure;
twin_coord_t ix;
twin_coord_t iy = top;

for (ix = left; ix < right; ix++) {
twin_argb32_t pixel = *pixels++;

if (tx->depth == 16)
pixel = twin_argb32_to_rgb16(pixel);

tx->pixels[iy * tx->screen->width + ix] = pixel;
}
if ((top + 1 - tx->image_y) == tx->height) {
/* Update the pixels to the framebuffer */
memcpy(tx->fb_raw, tx->pixels,
sizeof(uint32_t) * tx->width * tx->height);
}
}

static bool twin_fb_read_events(int maybe_unused file,
twin_file_op_t maybe_unused ops,
void *closure)
{
/* TODO: Implement events handling for mouse, keyboard, etc. */
return true;
}

static bool twin_fb_work(void *closure)
{
twin_fb_t *tx = closure;

if (twin_screen_damaged(tx->screen))
twin_fb_update(tx);
return true;
}

twin_fb_t *twin_fb_create(char *fb_path)
{
/* Allocate TWIN framebuffer object */
twin_fb_t *tx = malloc(sizeof(twin_fb_t));
if (!tx) {
fprintf(stderr, "Error in malloc.\n");
exit(2);
}

/* Open the framebuffer device */
tx->fd = open(fb_path, O_RDWR);
if (tx->fd == -1) {
fprintf(stderr, "Error opening %s\n", fb_path);
exit(2);
}

/* Read framebuffer information */
struct fb_var_screeninfo info;
if (ioctl(tx->fd, FBIOGET_VSCREENINFO, &info) == -1) {
fprintf(stderr, "Error reading framebuffer information from %s\n",
fb_path);
close(tx->fd);
exit(2);
}
tx->width = info.xres;
tx->height = info.yres;

/* Create memory mapping for accessing the framebuffer */
tx->fb_raw = mmap(NULL, tx->width * tx->height * info.bits_per_pixel / 8,
PROT_READ | PROT_WRITE, MAP_SHARED, tx->fd, 0);
if (tx->fb_raw == MAP_FAILED) {
fprintf(stderr, "Error in mmap\n");
close(tx->fd);
exit(2);
}

/* Create buffer space for TWIN */
tx->pixels = malloc(tx->width * tx->height * sizeof(uint32_t));
memset(tx->pixels, 255, tx->width * tx->height * sizeof(uint32_t));

/* Create TWIN screen */
tx->screen = twin_screen_create(tx->width, tx->height, _twin_fb_put_begin,
_twin_fb_put_span, tx);

/* Set up framebuffer work functions */
twin_set_file(twin_fb_read_events, 0, TWIN_READ, tx);
twin_set_work(twin_fb_work, TWIN_WORK_REDISPLAY, tx);

return tx;
}

void twin_fb_destroy(twin_fb_t *tx)
{
twin_screen_destroy(tx->screen);
close(tx->fd);
}

void twin_fb_damage(twin_fb_t *tx)
{
int width, height;
struct fb_var_screeninfo info;

/* Read screan width and height */
ioctl(tx->fd, FBIOGET_VSCREENINFO, &info);
width = info.xres;
height = info.yres;

twin_screen_damage(tx->screen, 0, 0, width, height);
}

void twin_fb_update(twin_fb_t *tx)
{
twin_screen_update(tx->screen);
}
30 changes: 30 additions & 0 deletions backend/twin_fb.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Twin - A Tiny Window System
* Copyright (c) 2024 National Cheng Kung University, Taiwan
* All rights reserved.
*/

#ifndef _TWIN_FB_H_
#define _TWIN_FB_H_

#include <twin.h>

typedef struct _twin_fb {
int fd;
twin_screen_t *screen;
int depth;
int *pixels;
int *fb_raw;
twin_coord_t width, height;
int image_y;
} twin_fb_t;

twin_fb_t *twin_fb_create(char *fb_path);

void twin_fb_destroy(twin_fb_t *tx);

void twin_fb_damage(twin_fb_t *tx);

void twin_fb_update(twin_fb_t *tx);

#endif /* _TWIN_FB_H_ */

0 comments on commit fc00614

Please sign in to comment.