-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support Linux framebuffer as new backend
- Loading branch information
1 parent
2d85c45
commit fc00614
Showing
4 changed files
with
345 additions
and
1 deletion.
There are no files selected for viewing
This file contains 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
This file contains 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
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; | ||
} |
This file contains 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
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); | ||
} |
This file contains 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
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_ */ |