Mirai's Miscellaneous Misadventures
M46 / engines / fbdev.c
#include <unistd.h>
#include <stdlib.h>
#include <termios.h>
#include <time.h>
#include <fcntl.h>
#include <stdint.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
#include <linux/kd.h>
#include <linux/input.h>
#include <mimimi.h>
struct mimimi_fbdev
{
unsigned char *buffer;
int width, height;
int x_offset, y_offset;
int line_length;
int bytes_per_pixel;
};
static struct termios mimimi_fbdev_tio;
static void mimimi_fbdev_exit(void)
{
ioctl(0, KDSETMODE, KD_TEXT);
tcsetattr(0, TCSANOW, &mimimi_fbdev_tio);
}
static void mimimi_fbdev_stamp(void *data, int x0, int y0, void *texture)
{
static unsigned char rch[] = {0x11, 0x44, 0x77, 0x99, 0xCC, 0xFF};
static unsigned char gch[] = {0x11, 0x33, 0x55, 0x77, 0x99, 0xBB, 0xDD, 0xFF};
static unsigned char bch[] = {0x22, 0x55, 0x88, 0xBB, 0xEE};
struct mimimi_image *image;
struct mimimi_fbdev *fbdev_data;
int x, y;
int offset_x, offset_y, offset;
unsigned char color;
unsigned char r, g, b;
image = texture;
fbdev_data = data;
for (y = 0 ; y < image->height ; y++)
for (x = 0 ; x < image->width ; x++)
{
if (y + y0 < 0) continue;
if (x + x0 < 0) continue;
if (y + y0 >= fbdev_data->height) continue;
if (x + x0 >= fbdev_data->width) continue;
offset_x = (x + x0 + fbdev_data->x_offset) * fbdev_data->bytes_per_pixel;
offset_y = (y + y0 + fbdev_data->y_offset) * fbdev_data->line_length;
offset = offset_x + offset_y;
color = image->colors[x + y * image->width];
if (color == 0) continue;
if (fbdev_data->bytes_per_pixel == 4)
fbdev_data->buffer[offset + 3] = 0xFF;
if (color < 0x10)
{
fbdev_data->buffer[offset + 2] = color * 0x11;
fbdev_data->buffer[offset + 1] = color * 0x11;
fbdev_data->buffer[offset + 0] = color * 0x11;
continue;
}
color -= 0x10;
r = rch[(color / 40) % 6];
g = gch[(color / 5) % 8];
b = bch[(color / 1) % 5];
fbdev_data->buffer[offset + 2] = r;
fbdev_data->buffer[offset + 1] = g;
fbdev_data->buffer[offset + 0] = b;
}
}
static void *mimimi_fbdev_texture(void *data, struct mimimi_image *image)
{
(void) data;
return image;
}
static void mimimi_fbdev_invalidate(void *data, void *texture)
{
(void) data;
(void) texture;
}
int mimimi_fbdev(void (*start)(void *chapter, struct mimimi_engine *engine), void (*tick)(void *chapter, unsigned char left, unsigned char right), unsigned long int chapter_size)
{
static char *file_names[] =
{
"/dev/input/event0", "/dev/input/event1", "/dev/input/event2", "/dev/input/event3",
"/dev/input/event4", "/dev/input/event5", "/dev/input/event6", "/dev/input/event7",
"/dev/input/event8", "/dev/input/event9", "/dev/input/event10", "/dev/input/event11",
"/dev/input/event12", "/dev/input/event13", "/dev/input/event14", "/dev/input/event15",
"/dev/input/event16", "/dev/input/event17", "/dev/input/event18", "/dev/input/event19",
"/dev/input/event20", "/dev/input/event21", "/dev/input/event22", "/dev/input/event23",
"/dev/input/event24", "/dev/input/event25", "/dev/input/event26", "/dev/input/event27",
"/dev/input/event28", "/dev/input/event29", "/dev/input/event30", "/dev/input/event31",
};
void *chapter;
int fd, in_fd;
struct fb_fix_screeninfo finfo;
struct fb_var_screeninfo vinfo;
void *aux;
unsigned char *buffer;
int bytes_per_pixel;
unsigned int width, height;
struct mimimi_engine engine;
struct mimimi_fbdev data;
int in_count;
int in[32];
struct timeval past, tv;
struct timespec ts;
long int delay;
unsigned long int usec;
int i;
int nfds;
int available;
struct input_event event;
ssize_t size, r;
unsigned char left, right;
fd_set fds;
tcflag_t flag;
chapter = malloc(chapter_size);
if (chapter == NULL) return 1;
fd = open("/dev/fb0", O_RDWR);
if (fd < 0) return 1;
if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) < 0) return 1;
if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) < 0) return 1;
aux = malloc(finfo.smem_len);
if (aux == NULL) return 1;
buffer = mmap(NULL, finfo.smem_len, PROT_WRITE, MAP_SHARED, fd, 0);
if ((intptr_t) buffer == -1) return 1;
bytes_per_pixel = vinfo.bits_per_pixel / 8;
if (bytes_per_pixel != 4 && bytes_per_pixel != 3) return 1;
width = 512;
height = 256;
if (vinfo.xres < width) width = vinfo.xres;
if (vinfo.yres < height) height = vinfo.yres;
tcgetattr(0, &mimimi_fbdev_tio);
flag = mimimi_fbdev_tio.c_lflag;
mimimi_fbdev_tio.c_lflag &= ~ICANON & ~ECHO;
tcsetattr(0, TCSANOW, &mimimi_fbdev_tio);
mimimi_fbdev_tio.c_lflag = flag;
atexit(&mimimi_fbdev_exit);
data.buffer = aux;
data.width = width;
data.height = height;
data.x_offset = vinfo.xoffset;
data.y_offset = vinfo.yoffset;
data.line_length = finfo.line_length;
data.bytes_per_pixel = bytes_per_pixel;
engine.data = &data;
engine.stamp = &mimimi_fbdev_stamp;
engine.texture = &mimimi_fbdev_texture;
engine.invalidate = &mimimi_fbdev_invalidate;
engine.size.width = width;
engine.size.height = height;
for (in_count = 0 ; in_count < 32 ; in_count++)
{
in_fd = open(file_names[in_count], O_RDONLY);
if (in_fd < 0) break;
in[in_count] = in_fd;
}
left = 0;
right = 0;
past.tv_sec = 0;
past.tv_usec = 0;
(*start)(chapter, &engine);
for (;;)
{
clock_gettime(CLOCK_MONOTONIC, &ts);
tv.tv_sec = ts.tv_sec;
tv.tv_usec = ts.tv_nsec / 1000;
delay =
(tv.tv_sec - past.tv_sec) * 1000000 +
(tv.tv_usec - past.tv_usec);
if (delay < 0) delay = 0;
past.tv_sec = tv.tv_sec;
past.tv_usec = tv.tv_usec;
usec = 0;
if (delay < 33333) usec = 33333 - delay;
tv.tv_sec = usec / 1000000;
tv.tv_usec = usec % 1000000;
nfds = 0;
FD_ZERO(&fds);
for (i = 0 ; i < in_count ; i++)
{
if (nfds < in[i]) nfds = in[i];
FD_SET(in[i], &fds);
}
for (;;)
{
available = select(nfds, &fds, NULL, NULL, &tv);
if (available == -1) return 1;
if (available == 0) break;
tv.tv_sec = 0;
tv.tv_usec = 0;
for (i = 0 ; i < in_count ; i++)
{
in_fd = in[i];
if (FD_ISSET(in_fd, &fds) == 0) continue;
size = sizeof event;
r = read(in_fd, &event, size);
if (r < size) return 1;
if (event.type != EV_KEY) continue;
if (event.value == 2) continue;
switch (event.code)
{
case KEY_LEFT:
case KEY_A:
left = event.value;
break;
case KEY_RIGHT:
case KEY_D:
right = event.value;
break;
case KEY_Q:
if (event.value == 0) return 0;
break;
}
}
}
(*tick)(chapter, left, right);
memcpy(buffer, aux, finfo.smem_len);
}
}