uterm: split fbdev module into different files
This is the first step towards a uterm-fbdev module. It splits the huge video file into a video backend and a render backend. A monitor backend will follow later when a full uterm module infrastructure is available. Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
This commit is contained in:
parent
a272e506f7
commit
cad0d410d7
@ -266,7 +266,10 @@ libuterm_la_LIBADD += $(UDEV_LIBS)
|
||||
endif
|
||||
|
||||
if BUILD_ENABLE_VIDEO_FBDEV
|
||||
libuterm_la_SOURCES += src/uterm_video_fbdev.c
|
||||
libuterm_la_SOURCES += \
|
||||
src/uterm_fbdev_internal.h \
|
||||
src/uterm_fbdev_video.c \
|
||||
src/uterm_fbdev_render.c
|
||||
endif
|
||||
|
||||
if BUILD_ENABLE_VIDEO_DUMB
|
||||
|
88
src/uterm_fbdev_internal.h
Normal file
88
src/uterm_fbdev_internal.h
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* uterm - Linux User-Space Terminal fbdev module
|
||||
*
|
||||
* Copyright (c) 2011-2013 David Herrmann <dh.herrmann@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Internal definitions */
|
||||
|
||||
#ifndef UTERM_FBDEV_INTERNAL_H
|
||||
#define UTERM_FBDEV_INTERNAL_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <linux/fb.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include "uterm_video.h"
|
||||
|
||||
struct fbdev_mode {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
};
|
||||
|
||||
struct fbdev_display {
|
||||
char *node;
|
||||
int fd;
|
||||
bool pending_intro;
|
||||
|
||||
struct fb_fix_screeninfo finfo;
|
||||
struct fb_var_screeninfo vinfo;
|
||||
unsigned int rate;
|
||||
|
||||
unsigned int bufid;
|
||||
size_t xres;
|
||||
size_t yres;
|
||||
size_t len;
|
||||
uint8_t *map;
|
||||
unsigned int stride;
|
||||
|
||||
bool xrgb32;
|
||||
unsigned int Bpp;
|
||||
unsigned int off_r;
|
||||
unsigned int off_g;
|
||||
unsigned int off_b;
|
||||
unsigned int len_r;
|
||||
unsigned int len_g;
|
||||
unsigned int len_b;
|
||||
int_fast32_t dither_r;
|
||||
int_fast32_t dither_g;
|
||||
int_fast32_t dither_b;
|
||||
};
|
||||
|
||||
int uterm_fbdev_display_blit(struct uterm_display *disp,
|
||||
const struct uterm_video_buffer *buf,
|
||||
unsigned int x, unsigned int y);
|
||||
int uterm_fbdev_display_blend(struct uterm_display *disp,
|
||||
const struct uterm_video_buffer *buf,
|
||||
unsigned int x, unsigned int y,
|
||||
uint8_t fr, uint8_t fg, uint8_t fb,
|
||||
uint8_t br, uint8_t bg, uint8_t bb);
|
||||
int uterm_fbdev_display_fake_blendv(struct uterm_display *disp,
|
||||
const struct uterm_video_blend_req *req,
|
||||
size_t num);
|
||||
int uterm_fbdev_display_fill(struct uterm_display *disp,
|
||||
uint8_t r, uint8_t g, uint8_t b,
|
||||
unsigned int x, unsigned int y,
|
||||
unsigned int width, unsigned int height);
|
||||
|
||||
#endif /* UTERM_FBDEV_INTERNAL_H */
|
524
src/uterm_fbdev_render.c
Normal file
524
src/uterm_fbdev_render.c
Normal file
@ -0,0 +1,524 @@
|
||||
/*
|
||||
* uterm - Linux User-Space Terminal fbdev module
|
||||
*
|
||||
* Copyright (c) 2011-2013 David Herrmann <dh.herrmann@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* FBDEV module rendering functions
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "log.h"
|
||||
#include "uterm_fbdev_internal.h"
|
||||
#include "uterm_video.h"
|
||||
#include "uterm_video_internal.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "fbdev_render"
|
||||
|
||||
static int clamp_value(int val, int low, int up)
|
||||
{
|
||||
if (val < low)
|
||||
return low;
|
||||
else if (val > up)
|
||||
return up;
|
||||
else
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint_fast32_t xrgb32_to_device(struct uterm_display *disp,
|
||||
uint32_t pixel)
|
||||
{
|
||||
uint8_t r, g, b, nr, ng, nb;
|
||||
int i;
|
||||
uint_fast32_t res;
|
||||
struct fbdev_display *fbdev = disp->data;
|
||||
|
||||
r = (pixel >> 16) & 0xff;
|
||||
g = (pixel >> 8) & 0xff;
|
||||
b = (pixel >> 0) & 0xff;
|
||||
|
||||
if (disp->flags & DISPLAY_DITHERING) {
|
||||
/* This is some very basic dithering which simply does small
|
||||
* rotations in the lower pixel bits. TODO: Let's take a look
|
||||
* at Floyd-Steinberg dithering which should give much better
|
||||
* results. It is slightly slower, though.
|
||||
* Or even better would be some Sierra filter like the Sierra
|
||||
* LITE. */
|
||||
fbdev->dither_r = r - fbdev->dither_r;
|
||||
fbdev->dither_g = g - fbdev->dither_g;
|
||||
fbdev->dither_b = b - fbdev->dither_b;
|
||||
r = clamp_value(fbdev->dither_r, 0, 255) >> (8 - fbdev->len_r);
|
||||
g = clamp_value(fbdev->dither_g, 0, 255) >> (8 - fbdev->len_g);
|
||||
b = clamp_value(fbdev->dither_b, 0, 255) >> (8 - fbdev->len_b);
|
||||
nr = r << (8 - fbdev->len_r);
|
||||
ng = g << (8 - fbdev->len_g);
|
||||
nb = b << (8 - fbdev->len_b);
|
||||
|
||||
for (i = fbdev->len_r; i < 8; i <<= 1)
|
||||
nr |= nr >> i;
|
||||
for (i = fbdev->len_g; i < 8; i <<= 1)
|
||||
ng |= ng >> i;
|
||||
for (i = fbdev->len_b; i < 8; i <<= 1)
|
||||
nb |= nb >> i;
|
||||
|
||||
fbdev->dither_r = nr - fbdev->dither_r;
|
||||
fbdev->dither_g = ng - fbdev->dither_g;
|
||||
fbdev->dither_b = nb - fbdev->dither_b;
|
||||
|
||||
res = r << fbdev->off_r;
|
||||
res |= g << fbdev->off_g;
|
||||
res |= b << fbdev->off_b;
|
||||
} else {
|
||||
res = (r >> (8 - fbdev->len_r)) << fbdev->off_r;
|
||||
res |= (g >> (8 - fbdev->len_g)) << fbdev->off_g;
|
||||
res |= (b >> (8 - fbdev->len_b)) << fbdev->off_b;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int uterm_fbdev_display_blit(struct uterm_display *disp,
|
||||
const struct uterm_video_buffer *buf,
|
||||
unsigned int x, unsigned int y)
|
||||
{
|
||||
unsigned int tmp;
|
||||
uint8_t *dst, *src;
|
||||
unsigned int width, height, i;
|
||||
uint32_t val;
|
||||
struct fbdev_display *fbdev = disp->data;
|
||||
|
||||
if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
|
||||
return -EINVAL;
|
||||
if (!buf || !video_is_awake(disp->video))
|
||||
return -EINVAL;
|
||||
if (buf->format != UTERM_FORMAT_XRGB32)
|
||||
return -EINVAL;
|
||||
|
||||
tmp = x + buf->width;
|
||||
if (tmp < x || x >= fbdev->xres)
|
||||
return -EINVAL;
|
||||
if (tmp > fbdev->xres)
|
||||
width = fbdev->xres - x;
|
||||
else
|
||||
width = buf->width;
|
||||
|
||||
tmp = y + buf->height;
|
||||
if (tmp < y || y >= fbdev->yres)
|
||||
return -EINVAL;
|
||||
if (tmp > fbdev->yres)
|
||||
height = fbdev->yres - y;
|
||||
else
|
||||
height = buf->height;
|
||||
|
||||
if (!(disp->flags & DISPLAY_DBUF) || fbdev->bufid)
|
||||
dst = fbdev->map;
|
||||
else
|
||||
dst = &fbdev->map[fbdev->yres * fbdev->stride];
|
||||
dst = &dst[y * fbdev->stride + x * fbdev->Bpp];
|
||||
src = buf->data;
|
||||
|
||||
if (fbdev->xrgb32) {
|
||||
while (height--) {
|
||||
memcpy(dst, src, 4 * width);
|
||||
dst += fbdev->stride;
|
||||
src += buf->stride;
|
||||
}
|
||||
} else if (fbdev->Bpp == 2) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
val = ((uint32_t*)src)[i];
|
||||
((uint16_t*)dst)[i] = xrgb32_to_device(disp, val);
|
||||
}
|
||||
dst += fbdev->stride;
|
||||
src += buf->stride;
|
||||
}
|
||||
} else if (fbdev->Bpp == 4) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
val = ((uint32_t*)src)[i];
|
||||
((uint32_t*)dst)[i] = xrgb32_to_device(disp, val);
|
||||
}
|
||||
dst += fbdev->stride;
|
||||
src += buf->stride;
|
||||
}
|
||||
} else {
|
||||
log_debug("invalid Bpp");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uterm_fbdev_display_blend(struct uterm_display *disp,
|
||||
const struct uterm_video_buffer *buf,
|
||||
unsigned int x, unsigned int y,
|
||||
uint8_t fr, uint8_t fg, uint8_t fb,
|
||||
uint8_t br, uint8_t bg, uint8_t bb)
|
||||
{
|
||||
unsigned int tmp;
|
||||
uint8_t *dst, *src;
|
||||
unsigned int width, height, i;
|
||||
unsigned int r, g, b;
|
||||
uint32_t val;
|
||||
struct fbdev_display *fbdev = disp->data;
|
||||
|
||||
if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
|
||||
return -EINVAL;
|
||||
if (!buf || !video_is_awake(disp->video))
|
||||
return -EINVAL;
|
||||
if (buf->format != UTERM_FORMAT_GREY)
|
||||
return -EINVAL;
|
||||
|
||||
tmp = x + buf->width;
|
||||
if (tmp < x || x >= fbdev->xres)
|
||||
return -EINVAL;
|
||||
if (tmp > fbdev->xres)
|
||||
width = fbdev->xres - x;
|
||||
else
|
||||
width = buf->width;
|
||||
|
||||
tmp = y + buf->height;
|
||||
if (tmp < y || y >= fbdev->yres)
|
||||
return -EINVAL;
|
||||
if (tmp > fbdev->yres)
|
||||
height = fbdev->yres - y;
|
||||
else
|
||||
height = buf->height;
|
||||
|
||||
if (!(disp->flags & DISPLAY_DBUF) || fbdev->bufid)
|
||||
dst = fbdev->map;
|
||||
else
|
||||
dst = &fbdev->map[fbdev->yres * fbdev->stride];
|
||||
dst = &dst[y * fbdev->stride + x * fbdev->Bpp];
|
||||
src = buf->data;
|
||||
|
||||
/* Division by 256 instead of 255 increases
|
||||
* speed by like 20% on slower machines.
|
||||
* Downside is, full white is 254/254/254
|
||||
* instead of 255/255/255. */
|
||||
if (fbdev->xrgb32) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
if (src[i] == 0) {
|
||||
r = br;
|
||||
g = bg;
|
||||
b = bb;
|
||||
} else if (src[i] == 255) {
|
||||
r = fr;
|
||||
g = fg;
|
||||
b = fb;
|
||||
} else {
|
||||
r = fr * src[i] +
|
||||
br * (255 - src[i]);
|
||||
r /= 256;
|
||||
g = fg * src[i] +
|
||||
bg * (255 - src[i]);
|
||||
g /= 256;
|
||||
b = fb * src[i] +
|
||||
bb * (255 - src[i]);
|
||||
b /= 256;
|
||||
}
|
||||
val = (r << 16) | (g << 8) | b;
|
||||
((uint32_t*)dst)[i] = val;
|
||||
}
|
||||
dst += fbdev->stride;
|
||||
src += buf->stride;
|
||||
}
|
||||
} else if (fbdev->Bpp == 2) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
if (src[i] == 0) {
|
||||
r = br;
|
||||
g = bg;
|
||||
b = bb;
|
||||
} else if (src[i] == 255) {
|
||||
r = fr;
|
||||
g = fg;
|
||||
b = fb;
|
||||
} else {
|
||||
r = fr * src[i] +
|
||||
br * (255 - src[i]);
|
||||
r /= 256;
|
||||
g = fg * src[i] +
|
||||
bg * (255 - src[i]);
|
||||
g /= 256;
|
||||
b = fb * src[i] +
|
||||
bb * (255 - src[i]);
|
||||
b /= 256;
|
||||
}
|
||||
val = (r << 16) | (g << 8) | b;
|
||||
((uint16_t*)dst)[i] = xrgb32_to_device(disp,
|
||||
val);
|
||||
}
|
||||
dst += fbdev->stride;
|
||||
src += buf->stride;
|
||||
}
|
||||
} else if (fbdev->Bpp == 4) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
if (src[i] == 0) {
|
||||
r = br;
|
||||
g = bg;
|
||||
b = bb;
|
||||
} else if (src[i] == 255) {
|
||||
r = fr;
|
||||
g = fg;
|
||||
b = fb;
|
||||
} else {
|
||||
r = fr * src[i] +
|
||||
br * (255 - src[i]);
|
||||
r /= 256;
|
||||
g = fg * src[i] +
|
||||
bg * (255 - src[i]);
|
||||
g /= 256;
|
||||
b = fb * src[i] +
|
||||
bb * (255 - src[i]);
|
||||
b /= 256;
|
||||
}
|
||||
val = (r << 16) | (g << 8) | b;
|
||||
((uint32_t*)dst)[i] = xrgb32_to_device(disp,
|
||||
val);
|
||||
}
|
||||
dst += fbdev->stride;
|
||||
src += buf->stride;
|
||||
}
|
||||
} else {
|
||||
log_warning("invalid Bpp");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uterm_fbdev_display_fake_blendv(struct uterm_display *disp,
|
||||
const struct uterm_video_blend_req *req,
|
||||
size_t num)
|
||||
{
|
||||
unsigned int tmp;
|
||||
uint8_t *dst, *src;
|
||||
unsigned int width, height, i, j;
|
||||
unsigned int r, g, b;
|
||||
uint32_t val;
|
||||
struct fbdev_display *fbdev = disp->data;
|
||||
|
||||
if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
|
||||
return -EINVAL;
|
||||
if (!req || !video_is_awake(disp->video))
|
||||
return -EINVAL;
|
||||
|
||||
for (j = 0; j < num; ++j, ++req) {
|
||||
if (!req->buf)
|
||||
continue;
|
||||
|
||||
if (req->buf->format != UTERM_FORMAT_GREY)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
tmp = req->x + req->buf->width;
|
||||
if (tmp < req->x || req->x >= fbdev->xres)
|
||||
return -EINVAL;
|
||||
if (tmp > fbdev->xres)
|
||||
width = fbdev->xres - req->x;
|
||||
else
|
||||
width = req->buf->width;
|
||||
|
||||
tmp = req->y + req->buf->height;
|
||||
if (tmp < req->y || req->y >= fbdev->yres)
|
||||
return -EINVAL;
|
||||
if (tmp > fbdev->yres)
|
||||
height = fbdev->yres - req->y;
|
||||
else
|
||||
height = req->buf->height;
|
||||
|
||||
if (!(disp->flags & DISPLAY_DBUF) || fbdev->bufid)
|
||||
dst = fbdev->map;
|
||||
else
|
||||
dst = &fbdev->map[fbdev->yres * fbdev->stride];
|
||||
dst = &dst[req->y * fbdev->stride + req->x * fbdev->Bpp];
|
||||
src = req->buf->data;
|
||||
|
||||
/* Division by 256 instead of 255 increases
|
||||
* speed by like 20% on slower machines.
|
||||
* Downside is, full white is 254/254/254
|
||||
* instead of 255/255/255. */
|
||||
if (fbdev->xrgb32) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
if (src[i] == 0) {
|
||||
r = req->br;
|
||||
g = req->bg;
|
||||
b = req->bb;
|
||||
} else if (src[i] == 255) {
|
||||
r = req->fr;
|
||||
g = req->fg;
|
||||
b = req->fb;
|
||||
} else {
|
||||
r = req->fr * src[i] +
|
||||
req->br * (255 - src[i]);
|
||||
r /= 256;
|
||||
g = req->fg * src[i] +
|
||||
req->bg * (255 - src[i]);
|
||||
g /= 256;
|
||||
b = req->fb * src[i] +
|
||||
req->bb * (255 - src[i]);
|
||||
b /= 256;
|
||||
}
|
||||
val = (r << 16) | (g << 8) | b;
|
||||
((uint32_t*)dst)[i] = val;
|
||||
}
|
||||
dst += fbdev->stride;
|
||||
src += req->buf->stride;
|
||||
}
|
||||
} else if (fbdev->Bpp == 2) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
if (src[i] == 0) {
|
||||
r = req->br;
|
||||
g = req->bg;
|
||||
b = req->bb;
|
||||
} else if (src[i] == 255) {
|
||||
r = req->fr;
|
||||
g = req->fg;
|
||||
b = req->fb;
|
||||
} else {
|
||||
r = req->fr * src[i] +
|
||||
req->br * (255 - src[i]);
|
||||
r /= 256;
|
||||
g = req->fg * src[i] +
|
||||
req->bg * (255 - src[i]);
|
||||
g /= 256;
|
||||
b = req->fb * src[i] +
|
||||
req->bb * (255 - src[i]);
|
||||
b /= 256;
|
||||
}
|
||||
val = (r << 16) | (g << 8) | b;
|
||||
((uint16_t*)dst)[i] =
|
||||
xrgb32_to_device(disp, val);
|
||||
}
|
||||
dst += fbdev->stride;
|
||||
src += req->buf->stride;
|
||||
}
|
||||
} else if (fbdev->Bpp == 4) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
if (src[i] == 0) {
|
||||
r = req->br;
|
||||
g = req->bg;
|
||||
b = req->bb;
|
||||
} else if (src[i] == 255) {
|
||||
r = req->fr;
|
||||
g = req->fg;
|
||||
b = req->fb;
|
||||
} else {
|
||||
r = req->fr * src[i] +
|
||||
req->br * (255 - src[i]);
|
||||
r /= 256;
|
||||
g = req->fg * src[i] +
|
||||
req->bg * (255 - src[i]);
|
||||
g /= 256;
|
||||
b = req->fb * src[i] +
|
||||
req->bb * (255 - src[i]);
|
||||
b /= 256;
|
||||
}
|
||||
val = (r << 16) | (g << 8) | b;
|
||||
((uint32_t*)dst)[i] =
|
||||
xrgb32_to_device(disp, val);
|
||||
}
|
||||
dst += fbdev->stride;
|
||||
src += req->buf->stride;
|
||||
}
|
||||
} else {
|
||||
log_warning("invalid Bpp");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uterm_fbdev_display_fill(struct uterm_display *disp,
|
||||
uint8_t r, uint8_t g, uint8_t b,
|
||||
unsigned int x, unsigned int y,
|
||||
unsigned int width, unsigned int height)
|
||||
{
|
||||
unsigned int tmp, i;
|
||||
uint8_t *dst;
|
||||
uint32_t full_val, rgb32;
|
||||
struct fbdev_display *fbdev = disp->data;
|
||||
|
||||
if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
|
||||
return -EINVAL;
|
||||
if (!video_is_awake(disp->video))
|
||||
return -EINVAL;
|
||||
|
||||
tmp = x + width;
|
||||
if (tmp < x || x >= fbdev->xres)
|
||||
return -EINVAL;
|
||||
if (tmp > fbdev->xres)
|
||||
width = fbdev->xres - x;
|
||||
tmp = y + height;
|
||||
if (tmp < y || y >= fbdev->yres)
|
||||
return -EINVAL;
|
||||
if (tmp > fbdev->yres)
|
||||
height = fbdev->yres - y;
|
||||
|
||||
if (!(disp->flags & DISPLAY_DBUF) || fbdev->bufid)
|
||||
dst = fbdev->map;
|
||||
else
|
||||
dst = &fbdev->map[fbdev->yres * fbdev->stride];
|
||||
dst = &dst[y * fbdev->stride + x * fbdev->Bpp];
|
||||
|
||||
full_val = ((r & 0xff) >> (8 - fbdev->len_r)) << fbdev->off_r;
|
||||
full_val |= ((g & 0xff) >> (8 - fbdev->len_g)) << fbdev->off_g;
|
||||
full_val |= ((b & 0xff) >> (8 - fbdev->len_b)) << fbdev->off_b;
|
||||
|
||||
if (fbdev->Bpp == 2) {
|
||||
if (disp->flags & DISPLAY_DITHERING) {
|
||||
rgb32 = (r & 0xff) << 16;
|
||||
rgb32 |= (g & 0xff) << 8;
|
||||
rgb32 |= (b & 0xff) << 0;
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i)
|
||||
((uint16_t*)dst)[i] = xrgb32_to_device(disp, rgb32);
|
||||
dst += fbdev->stride;
|
||||
}
|
||||
} else {
|
||||
full_val &= 0xffff;
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i)
|
||||
((uint16_t*)dst)[i] = full_val;
|
||||
dst += fbdev->stride;
|
||||
}
|
||||
}
|
||||
} else if (fbdev->Bpp == 4) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i)
|
||||
((uint32_t*)dst)[i] = full_val;
|
||||
dst += fbdev->stride;
|
||||
}
|
||||
} else {
|
||||
log_error("invalid Bpp");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* uterm - Linux User-Space Terminal
|
||||
* uterm - Linux User-Space Terminal fbdev module
|
||||
*
|
||||
* Copyright (c) 2011-2013 David Herrmann <dh.herrmann@googlemail.com>
|
||||
*
|
||||
@ -37,45 +37,12 @@
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include "log.h"
|
||||
#include "uterm_fbdev_internal.h"
|
||||
#include "uterm_video.h"
|
||||
#include "uterm_video_internal.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "video_fbdev"
|
||||
|
||||
struct fbdev_mode {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
};
|
||||
|
||||
struct fbdev_display {
|
||||
char *node;
|
||||
int fd;
|
||||
bool pending_intro;
|
||||
|
||||
struct fb_fix_screeninfo finfo;
|
||||
struct fb_var_screeninfo vinfo;
|
||||
unsigned int rate;
|
||||
|
||||
unsigned int bufid;
|
||||
size_t xres;
|
||||
size_t yres;
|
||||
size_t len;
|
||||
uint8_t *map;
|
||||
unsigned int stride;
|
||||
|
||||
bool xrgb32;
|
||||
unsigned int Bpp;
|
||||
unsigned int off_r;
|
||||
unsigned int off_g;
|
||||
unsigned int off_b;
|
||||
unsigned int len_r;
|
||||
unsigned int len_g;
|
||||
unsigned int len_b;
|
||||
int_fast32_t dither_r;
|
||||
int_fast32_t dither_g;
|
||||
int_fast32_t dither_b;
|
||||
};
|
||||
|
||||
static int mode_init(struct uterm_mode *mode)
|
||||
{
|
||||
struct fbdev_mode *fbdev;
|
||||
@ -464,491 +431,6 @@ static int display_swap(struct uterm_display *disp)
|
||||
return display_schedule_vblank_timer(disp);
|
||||
}
|
||||
|
||||
static int clamp_value(int val, int low, int up)
|
||||
{
|
||||
if (val < low)
|
||||
return low;
|
||||
else if (val > up)
|
||||
return up;
|
||||
else
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint_fast32_t xrgb32_to_device(struct uterm_display *disp,
|
||||
uint32_t pixel)
|
||||
{
|
||||
uint8_t r, g, b, nr, ng, nb;
|
||||
int i;
|
||||
uint_fast32_t res;
|
||||
struct fbdev_display *fbdev = disp->data;
|
||||
|
||||
r = (pixel >> 16) & 0xff;
|
||||
g = (pixel >> 8) & 0xff;
|
||||
b = (pixel >> 0) & 0xff;
|
||||
|
||||
if (disp->flags & DISPLAY_DITHERING) {
|
||||
/* This is some very basic dithering which simply does small
|
||||
* rotations in the lower pixel bits. TODO: Let's take a look
|
||||
* at Floyd-Steinberg dithering which should give much better
|
||||
* results. It is slightly slower, though.
|
||||
* Or even better would be some Sierra filter like the Sierra
|
||||
* LITE. */
|
||||
fbdev->dither_r = r - fbdev->dither_r;
|
||||
fbdev->dither_g = g - fbdev->dither_g;
|
||||
fbdev->dither_b = b - fbdev->dither_b;
|
||||
r = clamp_value(fbdev->dither_r, 0, 255) >> (8 - fbdev->len_r);
|
||||
g = clamp_value(fbdev->dither_g, 0, 255) >> (8 - fbdev->len_g);
|
||||
b = clamp_value(fbdev->dither_b, 0, 255) >> (8 - fbdev->len_b);
|
||||
nr = r << (8 - fbdev->len_r);
|
||||
ng = g << (8 - fbdev->len_g);
|
||||
nb = b << (8 - fbdev->len_b);
|
||||
|
||||
for (i = fbdev->len_r; i < 8; i <<= 1)
|
||||
nr |= nr >> i;
|
||||
for (i = fbdev->len_g; i < 8; i <<= 1)
|
||||
ng |= ng >> i;
|
||||
for (i = fbdev->len_b; i < 8; i <<= 1)
|
||||
nb |= nb >> i;
|
||||
|
||||
fbdev->dither_r = nr - fbdev->dither_r;
|
||||
fbdev->dither_g = ng - fbdev->dither_g;
|
||||
fbdev->dither_b = nb - fbdev->dither_b;
|
||||
|
||||
res = r << fbdev->off_r;
|
||||
res |= g << fbdev->off_g;
|
||||
res |= b << fbdev->off_b;
|
||||
} else {
|
||||
res = (r >> (8 - fbdev->len_r)) << fbdev->off_r;
|
||||
res |= (g >> (8 - fbdev->len_g)) << fbdev->off_g;
|
||||
res |= (b >> (8 - fbdev->len_b)) << fbdev->off_b;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int display_blit(struct uterm_display *disp,
|
||||
const struct uterm_video_buffer *buf,
|
||||
unsigned int x, unsigned int y)
|
||||
{
|
||||
unsigned int tmp;
|
||||
uint8_t *dst, *src;
|
||||
unsigned int width, height, i;
|
||||
uint32_t val;
|
||||
struct fbdev_display *fbdev = disp->data;
|
||||
|
||||
if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
|
||||
return -EINVAL;
|
||||
if (!buf || !video_is_awake(disp->video))
|
||||
return -EINVAL;
|
||||
if (buf->format != UTERM_FORMAT_XRGB32)
|
||||
return -EINVAL;
|
||||
|
||||
tmp = x + buf->width;
|
||||
if (tmp < x || x >= fbdev->xres)
|
||||
return -EINVAL;
|
||||
if (tmp > fbdev->xres)
|
||||
width = fbdev->xres - x;
|
||||
else
|
||||
width = buf->width;
|
||||
|
||||
tmp = y + buf->height;
|
||||
if (tmp < y || y >= fbdev->yres)
|
||||
return -EINVAL;
|
||||
if (tmp > fbdev->yres)
|
||||
height = fbdev->yres - y;
|
||||
else
|
||||
height = buf->height;
|
||||
|
||||
if (!(disp->flags & DISPLAY_DBUF) || fbdev->bufid)
|
||||
dst = fbdev->map;
|
||||
else
|
||||
dst = &fbdev->map[fbdev->yres * fbdev->stride];
|
||||
dst = &dst[y * fbdev->stride + x * fbdev->Bpp];
|
||||
src = buf->data;
|
||||
|
||||
if (fbdev->xrgb32) {
|
||||
while (height--) {
|
||||
memcpy(dst, src, 4 * width);
|
||||
dst += fbdev->stride;
|
||||
src += buf->stride;
|
||||
}
|
||||
} else if (fbdev->Bpp == 2) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
val = ((uint32_t*)src)[i];
|
||||
((uint16_t*)dst)[i] = xrgb32_to_device(disp, val);
|
||||
}
|
||||
dst += fbdev->stride;
|
||||
src += buf->stride;
|
||||
}
|
||||
} else if (fbdev->Bpp == 4) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
val = ((uint32_t*)src)[i];
|
||||
((uint32_t*)dst)[i] = xrgb32_to_device(disp, val);
|
||||
}
|
||||
dst += fbdev->stride;
|
||||
src += buf->stride;
|
||||
}
|
||||
} else {
|
||||
log_debug("invalid Bpp");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int display_blend(struct uterm_display *disp,
|
||||
const struct uterm_video_buffer *buf,
|
||||
unsigned int x, unsigned int y,
|
||||
uint8_t fr, uint8_t fg, uint8_t fb,
|
||||
uint8_t br, uint8_t bg, uint8_t bb)
|
||||
{
|
||||
unsigned int tmp;
|
||||
uint8_t *dst, *src;
|
||||
unsigned int width, height, i;
|
||||
unsigned int r, g, b;
|
||||
uint32_t val;
|
||||
struct fbdev_display *fbdev = disp->data;
|
||||
|
||||
if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
|
||||
return -EINVAL;
|
||||
if (!buf || !video_is_awake(disp->video))
|
||||
return -EINVAL;
|
||||
if (buf->format != UTERM_FORMAT_GREY)
|
||||
return -EINVAL;
|
||||
|
||||
tmp = x + buf->width;
|
||||
if (tmp < x || x >= fbdev->xres)
|
||||
return -EINVAL;
|
||||
if (tmp > fbdev->xres)
|
||||
width = fbdev->xres - x;
|
||||
else
|
||||
width = buf->width;
|
||||
|
||||
tmp = y + buf->height;
|
||||
if (tmp < y || y >= fbdev->yres)
|
||||
return -EINVAL;
|
||||
if (tmp > fbdev->yres)
|
||||
height = fbdev->yres - y;
|
||||
else
|
||||
height = buf->height;
|
||||
|
||||
if (!(disp->flags & DISPLAY_DBUF) || fbdev->bufid)
|
||||
dst = fbdev->map;
|
||||
else
|
||||
dst = &fbdev->map[fbdev->yres * fbdev->stride];
|
||||
dst = &dst[y * fbdev->stride + x * fbdev->Bpp];
|
||||
src = buf->data;
|
||||
|
||||
/* Division by 256 instead of 255 increases
|
||||
* speed by like 20% on slower machines.
|
||||
* Downside is, full white is 254/254/254
|
||||
* instead of 255/255/255. */
|
||||
if (fbdev->xrgb32) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
if (src[i] == 0) {
|
||||
r = br;
|
||||
g = bg;
|
||||
b = bb;
|
||||
} else if (src[i] == 255) {
|
||||
r = fr;
|
||||
g = fg;
|
||||
b = fb;
|
||||
} else {
|
||||
r = fr * src[i] +
|
||||
br * (255 - src[i]);
|
||||
r /= 256;
|
||||
g = fg * src[i] +
|
||||
bg * (255 - src[i]);
|
||||
g /= 256;
|
||||
b = fb * src[i] +
|
||||
bb * (255 - src[i]);
|
||||
b /= 256;
|
||||
}
|
||||
val = (r << 16) | (g << 8) | b;
|
||||
((uint32_t*)dst)[i] = val;
|
||||
}
|
||||
dst += fbdev->stride;
|
||||
src += buf->stride;
|
||||
}
|
||||
} else if (fbdev->Bpp == 2) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
if (src[i] == 0) {
|
||||
r = br;
|
||||
g = bg;
|
||||
b = bb;
|
||||
} else if (src[i] == 255) {
|
||||
r = fr;
|
||||
g = fg;
|
||||
b = fb;
|
||||
} else {
|
||||
r = fr * src[i] +
|
||||
br * (255 - src[i]);
|
||||
r /= 256;
|
||||
g = fg * src[i] +
|
||||
bg * (255 - src[i]);
|
||||
g /= 256;
|
||||
b = fb * src[i] +
|
||||
bb * (255 - src[i]);
|
||||
b /= 256;
|
||||
}
|
||||
val = (r << 16) | (g << 8) | b;
|
||||
((uint16_t*)dst)[i] = xrgb32_to_device(disp,
|
||||
val);
|
||||
}
|
||||
dst += fbdev->stride;
|
||||
src += buf->stride;
|
||||
}
|
||||
} else if (fbdev->Bpp == 4) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
if (src[i] == 0) {
|
||||
r = br;
|
||||
g = bg;
|
||||
b = bb;
|
||||
} else if (src[i] == 255) {
|
||||
r = fr;
|
||||
g = fg;
|
||||
b = fb;
|
||||
} else {
|
||||
r = fr * src[i] +
|
||||
br * (255 - src[i]);
|
||||
r /= 256;
|
||||
g = fg * src[i] +
|
||||
bg * (255 - src[i]);
|
||||
g /= 256;
|
||||
b = fb * src[i] +
|
||||
bb * (255 - src[i]);
|
||||
b /= 256;
|
||||
}
|
||||
val = (r << 16) | (g << 8) | b;
|
||||
((uint32_t*)dst)[i] = xrgb32_to_device(disp,
|
||||
val);
|
||||
}
|
||||
dst += fbdev->stride;
|
||||
src += buf->stride;
|
||||
}
|
||||
} else {
|
||||
log_warning("invalid Bpp");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int display_fake_blendv(struct uterm_display *disp,
|
||||
const struct uterm_video_blend_req *req,
|
||||
size_t num)
|
||||
{
|
||||
unsigned int tmp;
|
||||
uint8_t *dst, *src;
|
||||
unsigned int width, height, i, j;
|
||||
unsigned int r, g, b;
|
||||
uint32_t val;
|
||||
struct fbdev_display *fbdev = disp->data;
|
||||
|
||||
if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
|
||||
return -EINVAL;
|
||||
if (!req || !video_is_awake(disp->video))
|
||||
return -EINVAL;
|
||||
|
||||
for (j = 0; j < num; ++j, ++req) {
|
||||
if (!req->buf)
|
||||
continue;
|
||||
|
||||
if (req->buf->format != UTERM_FORMAT_GREY)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
tmp = req->x + req->buf->width;
|
||||
if (tmp < req->x || req->x >= fbdev->xres)
|
||||
return -EINVAL;
|
||||
if (tmp > fbdev->xres)
|
||||
width = fbdev->xres - req->x;
|
||||
else
|
||||
width = req->buf->width;
|
||||
|
||||
tmp = req->y + req->buf->height;
|
||||
if (tmp < req->y || req->y >= fbdev->yres)
|
||||
return -EINVAL;
|
||||
if (tmp > fbdev->yres)
|
||||
height = fbdev->yres - req->y;
|
||||
else
|
||||
height = req->buf->height;
|
||||
|
||||
if (!(disp->flags & DISPLAY_DBUF) || fbdev->bufid)
|
||||
dst = fbdev->map;
|
||||
else
|
||||
dst = &fbdev->map[fbdev->yres * fbdev->stride];
|
||||
dst = &dst[req->y * fbdev->stride + req->x * fbdev->Bpp];
|
||||
src = req->buf->data;
|
||||
|
||||
/* Division by 256 instead of 255 increases
|
||||
* speed by like 20% on slower machines.
|
||||
* Downside is, full white is 254/254/254
|
||||
* instead of 255/255/255. */
|
||||
if (fbdev->xrgb32) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
if (src[i] == 0) {
|
||||
r = req->br;
|
||||
g = req->bg;
|
||||
b = req->bb;
|
||||
} else if (src[i] == 255) {
|
||||
r = req->fr;
|
||||
g = req->fg;
|
||||
b = req->fb;
|
||||
} else {
|
||||
r = req->fr * src[i] +
|
||||
req->br * (255 - src[i]);
|
||||
r /= 256;
|
||||
g = req->fg * src[i] +
|
||||
req->bg * (255 - src[i]);
|
||||
g /= 256;
|
||||
b = req->fb * src[i] +
|
||||
req->bb * (255 - src[i]);
|
||||
b /= 256;
|
||||
}
|
||||
val = (r << 16) | (g << 8) | b;
|
||||
((uint32_t*)dst)[i] = val;
|
||||
}
|
||||
dst += fbdev->stride;
|
||||
src += req->buf->stride;
|
||||
}
|
||||
} else if (fbdev->Bpp == 2) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
if (src[i] == 0) {
|
||||
r = req->br;
|
||||
g = req->bg;
|
||||
b = req->bb;
|
||||
} else if (src[i] == 255) {
|
||||
r = req->fr;
|
||||
g = req->fg;
|
||||
b = req->fb;
|
||||
} else {
|
||||
r = req->fr * src[i] +
|
||||
req->br * (255 - src[i]);
|
||||
r /= 256;
|
||||
g = req->fg * src[i] +
|
||||
req->bg * (255 - src[i]);
|
||||
g /= 256;
|
||||
b = req->fb * src[i] +
|
||||
req->bb * (255 - src[i]);
|
||||
b /= 256;
|
||||
}
|
||||
val = (r << 16) | (g << 8) | b;
|
||||
((uint16_t*)dst)[i] =
|
||||
xrgb32_to_device(disp, val);
|
||||
}
|
||||
dst += fbdev->stride;
|
||||
src += req->buf->stride;
|
||||
}
|
||||
} else if (fbdev->Bpp == 4) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
if (src[i] == 0) {
|
||||
r = req->br;
|
||||
g = req->bg;
|
||||
b = req->bb;
|
||||
} else if (src[i] == 255) {
|
||||
r = req->fr;
|
||||
g = req->fg;
|
||||
b = req->fb;
|
||||
} else {
|
||||
r = req->fr * src[i] +
|
||||
req->br * (255 - src[i]);
|
||||
r /= 256;
|
||||
g = req->fg * src[i] +
|
||||
req->bg * (255 - src[i]);
|
||||
g /= 256;
|
||||
b = req->fb * src[i] +
|
||||
req->bb * (255 - src[i]);
|
||||
b /= 256;
|
||||
}
|
||||
val = (r << 16) | (g << 8) | b;
|
||||
((uint32_t*)dst)[i] =
|
||||
xrgb32_to_device(disp, val);
|
||||
}
|
||||
dst += fbdev->stride;
|
||||
src += req->buf->stride;
|
||||
}
|
||||
} else {
|
||||
log_warning("invalid Bpp");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int display_fill(struct uterm_display *disp,
|
||||
uint8_t r, uint8_t g, uint8_t b,
|
||||
unsigned int x, unsigned int y,
|
||||
unsigned int width, unsigned int height)
|
||||
{
|
||||
unsigned int tmp, i;
|
||||
uint8_t *dst;
|
||||
uint32_t full_val, rgb32;
|
||||
struct fbdev_display *fbdev = disp->data;
|
||||
|
||||
if (!disp->video || !(disp->flags & DISPLAY_ONLINE))
|
||||
return -EINVAL;
|
||||
if (!video_is_awake(disp->video))
|
||||
return -EINVAL;
|
||||
|
||||
tmp = x + width;
|
||||
if (tmp < x || x >= fbdev->xres)
|
||||
return -EINVAL;
|
||||
if (tmp > fbdev->xres)
|
||||
width = fbdev->xres - x;
|
||||
tmp = y + height;
|
||||
if (tmp < y || y >= fbdev->yres)
|
||||
return -EINVAL;
|
||||
if (tmp > fbdev->yres)
|
||||
height = fbdev->yres - y;
|
||||
|
||||
if (!(disp->flags & DISPLAY_DBUF) || fbdev->bufid)
|
||||
dst = fbdev->map;
|
||||
else
|
||||
dst = &fbdev->map[fbdev->yres * fbdev->stride];
|
||||
dst = &dst[y * fbdev->stride + x * fbdev->Bpp];
|
||||
|
||||
full_val = ((r & 0xff) >> (8 - fbdev->len_r)) << fbdev->off_r;
|
||||
full_val |= ((g & 0xff) >> (8 - fbdev->len_g)) << fbdev->off_g;
|
||||
full_val |= ((b & 0xff) >> (8 - fbdev->len_b)) << fbdev->off_b;
|
||||
|
||||
if (fbdev->Bpp == 2) {
|
||||
if (disp->flags & DISPLAY_DITHERING) {
|
||||
rgb32 = (r & 0xff) << 16;
|
||||
rgb32 |= (g & 0xff) << 8;
|
||||
rgb32 |= (b & 0xff) << 0;
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i)
|
||||
((uint16_t*)dst)[i] = xrgb32_to_device(disp, rgb32);
|
||||
dst += fbdev->stride;
|
||||
}
|
||||
} else {
|
||||
full_val &= 0xffff;
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i)
|
||||
((uint16_t*)dst)[i] = full_val;
|
||||
dst += fbdev->stride;
|
||||
}
|
||||
}
|
||||
} else if (fbdev->Bpp == 4) {
|
||||
while (height--) {
|
||||
for (i = 0; i < width; ++i)
|
||||
((uint32_t*)dst)[i] = full_val;
|
||||
dst += fbdev->stride;
|
||||
}
|
||||
} else {
|
||||
log_error("invalid Bpp");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct display_ops fbdev_display_ops = {
|
||||
.init = NULL,
|
||||
.destroy = NULL,
|
||||
@ -957,11 +439,11 @@ static const struct display_ops fbdev_display_ops = {
|
||||
.set_dpms = display_set_dpms,
|
||||
.use = NULL,
|
||||
.swap = display_swap,
|
||||
.blit = display_blit,
|
||||
.blend = display_blend,
|
||||
.blendv = display_fake_blendv,
|
||||
.fake_blendv = display_fake_blendv,
|
||||
.fill = display_fill,
|
||||
.blit = uterm_fbdev_display_blit,
|
||||
.blend = uterm_fbdev_display_blend,
|
||||
.blendv = uterm_fbdev_display_fake_blendv,
|
||||
.fake_blendv = uterm_fbdev_display_fake_blendv,
|
||||
.fill = uterm_fbdev_display_fill,
|
||||
};
|
||||
|
||||
static void intro_idle_event(struct ev_eloop *eloop, void *unused, void *data)
|
Loading…
x
Reference in New Issue
Block a user