uterm: split fbdev module into different files
authorDavid Herrmann <dh.herrmann@googlemail.com>
Fri, 11 Jan 2013 15:24:37 +0000 (16:24 +0100)
committerDavid Herrmann <dh.herrmann@googlemail.com>
Fri, 11 Jan 2013 15:24:37 +0000 (16:24 +0100)
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>
Makefile.am
src/uterm_fbdev_internal.h [new file with mode: 0644]
src/uterm_fbdev_render.c [new file with mode: 0644]
src/uterm_fbdev_video.c [moved from src/uterm_video_fbdev.c with 53% similarity]

index 7255391..3f8e993 100644 (file)
@@ -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
diff --git a/src/uterm_fbdev_internal.h b/src/uterm_fbdev_internal.h
new file mode 100644 (file)
index 0000000..f4e51e8
--- /dev/null
@@ -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 */
diff --git a/src/uterm_fbdev_render.c b/src/uterm_fbdev_render.c
new file mode 100644 (file)
index 0000000..b9fdd6e
--- /dev/null
@@ -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;
+}
similarity index 53%
rename from src/uterm_video_fbdev.c
rename to src/uterm_fbdev_video.c
index 5424577..95132bd 100644 (file)
@@ -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>
  *
 #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)