Index: uspace/srv/hid/output/port/kfb.c
===================================================================
--- uspace/srv/hid/output/port/kfb.c	(revision 52c4264a632a597d2252dcb23b0c56f28a711f57)
+++ uspace/srv/hid/output/port/kfb.c	(revision 52c4264a632a597d2252dcb23b0c56f28a711f57)
@@ -0,0 +1,787 @@
+/*
+ * Copyright (c) 2008 Martin Decky
+ * Copyright (c) 2006 Jakub Vana
+ * Copyright (c) 2006 Ondrej Palkovsky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file
+ */
+
+#include <abi/fb/visuals.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include <screenbuffer.h>
+#include <imgmap.h>
+#include <errno.h>
+#include <bool.h>
+#include <sysinfo.h>
+#include <ddi.h>
+#include <malloc.h>
+#include <mem.h>
+#include <as.h>
+#include <align.h>
+#include "../gfx/font-8x16.h"
+#include "../fb.h"
+#include "kfb.h"
+
+#define DEFAULT_BGCOLOR  0xffffff
+#define DEFAULT_FGCOLOR  0x000000
+
+#define FB_POS(x, y)  ((y) * kfb.scanline + (x) * kfb.pixel_bytes)
+
+#define COL2X(col)  ((col) * FONT_WIDTH)
+#define ROW2Y(row)  ((row) * FONT_SCANLINES)
+#define X2COL(x)    ((x) / FONT_WIDTH)
+#define Y2ROW(y)    ((y) / FONT_SCANLINES)
+
+#define GLYPH_POS(glyph, y, inverted) \
+	(((glyph) + (inverted) * FONT_GLYPHS) * (kfb.glyph_bytes) + (y) * (kfb.glyph_scanline))
+
+#define POINTER_WIDTH   11
+#define POINTER_HEIGHT  18
+
+static uint8_t pointer[] = {
+	0x01, 0x00, 0x03, 0x00, 0x05, 0x00, 0x09, 0x00, 0x11, 0x00, 0x21, 0x00,
+	0x41, 0x00, 0x81, 0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0x04, 0x01, 0x03,
+	0x81, 0x00, 0x89, 0x00, 0x15, 0x01, 0x23, 0x01, 0x21, 0x01, 0xc0, 0x00
+};
+
+static uint8_t pointer_mask[] = {
+	0x01, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x3f, 0x00,
+	0x7f, 0x00, 0xff, 0x00, 0xff, 0x01, 0xff, 0x03, 0xff, 0x07, 0xff, 0x03,
+	0xff, 0x00, 0xff, 0x00, 0xf7, 0x01, 0xe3, 0x01, 0xe1, 0x01, 0xc0, 0x00
+};
+
+/** Function to draw a character. */
+typedef void (*draw_char_t)(sysarg_t, sysarg_t, bool, wchar_t, pixel_t,
+    pixel_t);
+
+typedef struct {
+	sysarg_t width;
+	sysarg_t height;
+	size_t offset;
+	size_t scanline;
+	visual_t visual;
+	
+	size_t size;
+	uint8_t *addr;
+	
+	pixel2visual_t pixel2visual;
+	visual2pixel_t visual2pixel;
+	visual_mask_t visual_mask;
+	size_t pixel_bytes;
+	
+	sysarg_t pointer_x;
+	sysarg_t pointer_y;
+	bool pointer_visible;
+	imgmap_t *pointer_imgmap;
+	
+	/*
+	 * Pre-rendered mask for rendering
+	 * glyphs. Specific for the framebuffer
+	 * visual.
+	 */
+	
+	size_t glyph_scanline;
+	size_t glyph_bytes;
+	uint8_t *glyphs;
+	
+	uint8_t *backbuf;
+} kfb_t;
+
+typedef struct {
+	/**
+	 * Character drawing function for this viewport.
+	 * Different viewports might use different drawing
+	 * functions depending on whether their scanlines
+	 * are aligned on a word boundary.
+	 */
+	draw_char_t draw_char;
+} kfb_vp_t;
+
+static kfb_t kfb;
+
+static pixel_t color_table[16] = {
+	[COLOR_BLACK]       = 0x000000,
+	[COLOR_BLUE]        = 0x0000f0,
+	[COLOR_GREEN]       = 0x00f000,
+	[COLOR_CYAN]        = 0x00f0f0,
+	[COLOR_RED]         = 0xf00000,
+	[COLOR_MAGENTA]     = 0xf000f0,
+	[COLOR_YELLOW]      = 0xf0f000,
+	[COLOR_WHITE]       = 0xf0f0f0,
+	
+	[COLOR_BLACK + 8]   = 0x000000,
+	[COLOR_BLUE + 8]    = 0x0000ff,
+	[COLOR_GREEN + 8]   = 0x00ff00,
+	[COLOR_CYAN + 8]    = 0x00ffff,
+	[COLOR_RED + 8]     = 0xff0000,
+	[COLOR_MAGENTA + 8] = 0xff00ff,
+	[COLOR_YELLOW + 8]  = 0xffff00,
+	[COLOR_WHITE + 8]   = 0xffffff,
+};
+
+static void put_pixel(sysarg_t x, sysarg_t y, pixel_t pixel)
+{
+	if ((x >= kfb.width) || (y >= kfb.height))
+		return;
+	
+	kfb.pixel2visual(kfb.addr + FB_POS(x, y), pixel);
+}
+
+static pixel_t get_pixel(sysarg_t x, sysarg_t y)
+{
+	if ((x >= kfb.width) || (y >= kfb.height))
+		return 0;
+	
+	return kfb.visual2pixel(kfb.addr + FB_POS(x, y));
+}
+
+static void pointer_show(void)
+{
+	if (kfb.pointer_visible) {
+		for (sysarg_t y = 0; y < POINTER_HEIGHT; y++) {
+			for (sysarg_t x = 0; x < POINTER_WIDTH; x++) {
+				sysarg_t dx = kfb.pointer_x + x;
+				sysarg_t dy = kfb.pointer_y + y;
+				
+				pixel_t pixel = get_pixel(dx, dy);
+				imgmap_put_pixel(kfb.pointer_imgmap, x, y, pixel);
+				
+				size_t offset = y * ((POINTER_WIDTH - 1) / 8 + 1) + x / 8;
+				bool visible = pointer_mask[offset] & (1 << (x % 8));
+				
+				if (visible) {
+					pixel = (pointer[offset] & (1 << (x % 8))) ?
+					    0x000000 : 0xffffff;
+					put_pixel(dx, dy, pixel);
+				}
+			}
+		}
+	}
+}
+
+static void pointer_hide(void)
+{
+	if (kfb.pointer_visible) {
+		for (sysarg_t y = 0; y < POINTER_HEIGHT; y++) {
+			for (sysarg_t x = 0; x < POINTER_WIDTH; x++) {
+				sysarg_t dx = kfb.pointer_x + x;
+				sysarg_t dy = kfb.pointer_y + y;
+				
+				pixel_t pixel =
+				    imgmap_get_pixel(kfb.pointer_imgmap, x, y);
+				put_pixel(dx, dy, pixel);
+			}
+		}
+	}
+}
+
+/** Draw a filled rectangle.
+ *
+ */
+static void draw_filled_rect(sysarg_t x1, sysarg_t y1, sysarg_t x2, sysarg_t y2,
+    pixel_t color)
+{
+	if ((y1 >= y2) || (x1 >= x2))
+		return;
+	
+	uint8_t cbuf[4];
+	kfb.pixel2visual(cbuf, color);
+	
+	for (sysarg_t y = y1; y < y2; y++) {
+		uint8_t *dst = kfb.addr + FB_POS(x1, y);
+		
+		for (sysarg_t x = x1; x < x2; x++) {
+			memcpy(dst, cbuf, kfb.pixel_bytes);
+			dst += kfb.pixel_bytes;
+		}
+	}
+}
+
+static void vp_put_pixel(fbvp_t *vp, sysarg_t x, sysarg_t y, pixel_t pixel)
+{
+	put_pixel(vp->x + x, vp->y + y, pixel);
+}
+
+static void attrs_rgb(char_attrs_t attrs, pixel_t *bgcolor, pixel_t *fgcolor)
+{
+	switch (attrs.type) {
+	case CHAR_ATTR_STYLE:
+		switch (attrs.val.style) {
+		case STYLE_NORMAL:
+			*bgcolor = color_table[COLOR_WHITE];
+			*fgcolor = color_table[COLOR_BLACK];
+			break;
+		case STYLE_EMPHASIS:
+			*bgcolor = color_table[COLOR_WHITE];
+			*fgcolor = color_table[COLOR_RED];
+			break;
+		case STYLE_INVERTED:
+			*bgcolor = color_table[COLOR_BLACK];
+			*fgcolor = color_table[COLOR_WHITE];
+			break;
+		case STYLE_SELECTED:
+			*bgcolor = color_table[COLOR_RED];
+			*fgcolor = color_table[COLOR_WHITE];
+			break;
+		}
+		break;
+	case CHAR_ATTR_INDEX:
+		*bgcolor = color_table[(attrs.val.index.bgcolor & 7) |
+		    ((attrs.val.index.attr & CATTR_BRIGHT) ? 8 : 0)];
+		*fgcolor = color_table[(attrs.val.index.fgcolor & 7) |
+		    ((attrs.val.index.attr & CATTR_BRIGHT) ? 8 : 0)];
+		break;
+	case CHAR_ATTR_RGB:
+		*bgcolor = attrs.val.rgb.bgcolor;
+		*fgcolor = attrs.val.rgb.fgcolor;
+		break;
+	}
+}
+
+/** Draw a character, takes advantage of alignment.
+ *
+ * This version can only be used if the following conditions are met:
+ *
+ *   - word size is divisible by pixel_bytes
+ *   - cell scanline size is divisible by word size
+ *   - cell scanlines are word-aligned
+ *
+ * It makes use of the pre-rendered mask to process (possibly) several
+ * pixels at once (word size / pixel_bytes pixels at a time are processed)
+ * making it very fast. Most notably this version is not applicable at 24 bits
+ * per pixel.
+ *
+ * @param x        X coordinate of top-left corner on screen.
+ * @param y        Y coordinate of top-left corner on screen.
+ * @param inverted Draw inverted character.
+ * @param ch       Character to draw.
+ * @param bgcolor  Background color.
+ * @param fbgcolor Foreground color.
+ *
+ */
+static void draw_char_aligned(sysarg_t x, sysarg_t y, bool inverted, wchar_t ch,
+    pixel_t bgcolor, pixel_t fgcolor)
+{
+	size_t word_size = sizeof(unsigned long);
+	
+	/*
+	 * Prepare a pair of words, one filled with foreground-color
+	 * pattern and the other filled with background-color pattern.
+	 */
+	unsigned long fg_buf;
+	unsigned long bg_buf;
+	
+	for (size_t i = 0; i < word_size / kfb.pixel_bytes; i++) {
+		kfb.pixel2visual(&((uint8_t *) &bg_buf)[i * kfb.pixel_bytes],
+		    bgcolor);
+		kfb.pixel2visual(&((uint8_t *) &fg_buf)[i * kfb.pixel_bytes],
+		    fgcolor);
+	}
+	
+	/* Pointer to the current position in the mask. */
+	unsigned long *maskp =
+	    (unsigned long *) &kfb.glyphs[GLYPH_POS(
+	    fb_font_glyph(ch), 0, inverted)];
+	
+	/* Pointer to the current position on the screen. */
+	unsigned long *dst =
+	    (unsigned long *) &kfb.addr[FB_POS(x, y)];
+	
+	/* Width of the character cell in words. */
+	size_t ww = FONT_WIDTH * kfb.pixel_bytes / word_size;
+	
+	/* Offset to add when moving to another screen scanline. */
+	size_t d_add = kfb.scanline - FONT_WIDTH * kfb.pixel_bytes;
+	
+	for (size_t yd = 0; yd < FONT_SCANLINES; yd++) {
+		/*
+		 * Now process the cell scanline, combining foreground
+		 * and background color patters using the pre-rendered mask.
+		 */
+		for (size_t i = 0; i < ww; i++) {
+			unsigned long mask = *maskp++;
+			*dst++ = (fg_buf & mask) | (bg_buf & ~mask);
+		}
+		
+		/* Move to the beginning of the next scanline of the cell. */
+		dst = (unsigned long *) ((uint8_t *) dst + d_add);
+	}
+}
+
+/** Draw a character, fallback version.
+ *
+ * This version does not make use of the pre-rendered mask, it uses
+ * the font bitmap directly. It works always, but it is slower.
+ *
+ * @param x        X coordinate of top-left corner on screen.
+ * @param y        Y coordinate of top-left corner on screen.
+ * @param inverted Draw inverted character.
+ * @param ch       Character to draw.
+ * @param bgcolor  Background color.
+ * @param fgcolor  Foreground color.
+ *
+ */
+static void draw_char_fallback(sysarg_t x, sysarg_t y, bool inverted,
+    wchar_t ch, pixel_t bgcolor, pixel_t fgcolor)
+{
+	/* Character glyph */
+	uint16_t glyph = fb_font_glyph(ch);
+	
+	/* Pre-render the foreground and background color pixels. */
+	uint8_t fg_buf[4];
+	uint8_t bg_buf[4];
+	
+	if (inverted) {
+		kfb.pixel2visual(bg_buf, fgcolor);
+		kfb.pixel2visual(fg_buf, bgcolor);
+	} else {
+		kfb.pixel2visual(bg_buf, bgcolor);
+		kfb.pixel2visual(fg_buf, fgcolor);
+	}
+	
+	/* Pointer to the current position on the screen. */
+	uint8_t *dst = (uint8_t *) &kfb.addr[FB_POS(x, y)];
+	
+	/* Offset to add when moving to another screen scanline. */
+	size_t d_add = kfb.scanline - FONT_WIDTH * kfb.pixel_bytes;
+	
+	for (size_t yd = 0; yd < FONT_SCANLINES; yd++) {
+		/* Byte containing bits of the glyph scanline. */
+		uint8_t byte = fb_font[glyph][yd];
+		
+		for (size_t i = 0; i < FONT_WIDTH; i++) {
+			/* Choose color based on the current bit. */
+			uint8_t *src = (byte & 0x80) ? fg_buf : bg_buf;
+			
+			/* Copy the pixel. */
+			for (size_t j = 0; j < kfb.pixel_bytes; j++)
+				*dst++ = *src++;
+			
+			/* Move to the next bit. */
+			byte <<= 1;
+		}
+		
+		/* Move to the beginning of the next scanline of the cell. */
+		dst += d_add;
+	}
+}
+
+/** Draw the character at the specified position in viewport.
+ *
+ * @param vp     Viewport.
+ * @param col    Screen position relative to viewport.
+ * @param row    Screen position relative to viewport.
+ *
+ */
+static void draw_vp_char(fbvp_t *vp, sysarg_t col, sysarg_t row)
+{
+	kfb_vp_t *kfb_vp = (kfb_vp_t *) vp->data;
+	
+	sysarg_t x = vp->x + COL2X(col);
+	sysarg_t y = vp->y + ROW2Y(row);
+	
+	charfield_t *field = screenbuffer_field_at(vp->backbuf, col, row);
+	
+	pixel_t bgcolor = 0;
+	pixel_t fgcolor = 0;
+	attrs_rgb(field->attrs, &bgcolor, &fgcolor);
+	
+	bool inverted = (vp->cursor_flash) &&
+	    screenbuffer_cursor_at(vp->backbuf, col, row);
+	
+	(*kfb_vp->draw_char)(x, y, inverted, field->ch, bgcolor, fgcolor);
+}
+
+static int kfb_yield(fbdev_t *dev)
+{
+	if (kfb.backbuf == NULL) {
+		kfb.backbuf =
+		    malloc(kfb.width * kfb.height * kfb.pixel_bytes);
+		if (kfb.backbuf == NULL)
+			return ENOMEM;
+	}
+	
+	for (sysarg_t y = 0; y < kfb.height; y++)
+		memcpy(kfb.backbuf + y * kfb.width * kfb.pixel_bytes,
+		    kfb.addr + FB_POS(0, y), kfb.width * kfb.pixel_bytes);
+	
+	return EOK;
+}
+
+static int kfb_claim(fbdev_t *dev)
+{
+	if (kfb.backbuf == NULL)
+		return ENOENT;
+	
+	for (sysarg_t y = 0; y < kfb.height; y++)
+		memcpy(kfb.addr + FB_POS(0, y),
+		    kfb.backbuf + y * kfb.width * kfb.pixel_bytes,
+		    kfb.width * kfb.pixel_bytes);
+	
+	return EOK;
+}
+
+static void kfb_pointer_update(struct fbdev *dev, sysarg_t x, sysarg_t y,
+    bool visible)
+{
+	pointer_hide();
+	
+	kfb.pointer_x = x;
+	kfb.pointer_y = y;
+	kfb.pointer_visible = visible;
+	
+	pointer_show();
+}
+
+static int kfb_get_resolution(fbdev_t *dev, sysarg_t *width, sysarg_t *height)
+{
+	*width = kfb.width;
+	*height = kfb.height;
+	return EOK;
+}
+
+static void kfb_font_metrics(fbdev_t *dev, sysarg_t width, sysarg_t height,
+    sysarg_t *cols, sysarg_t *rows)
+{
+	*cols = X2COL(width);
+	*rows = Y2ROW(height);
+}
+
+static int kfb_vp_create(fbdev_t *dev, fbvp_t *vp)
+{
+	kfb_vp_t *kfb_vp = malloc(sizeof(kfb_vp_t));
+	if (kfb_vp == NULL)
+		return ENOMEM;
+	
+	/*
+	 * Conditions necessary to select aligned glyph
+	 * drawing variants:
+	 *  - word size is divisible by number of bytes per pixel
+	 *  - cell scanline size is divisible by word size
+	 *  - cell scanlines are word-aligned
+	 *
+	 */
+	size_t word_size = sizeof(unsigned long);
+	
+	if (((word_size % kfb.pixel_bytes) == 0)
+	    && ((FONT_WIDTH * kfb.pixel_bytes) % word_size == 0)
+	    && ((vp->x * kfb.pixel_bytes) % word_size == 0)
+	    && (kfb.scanline % word_size == 0))
+		kfb_vp->draw_char = draw_char_aligned;
+	else
+		kfb_vp->draw_char = draw_char_fallback;
+	
+	vp->attrs.type = CHAR_ATTR_RGB;
+	vp->attrs.val.rgb.bgcolor = DEFAULT_BGCOLOR;
+	vp->attrs.val.rgb.fgcolor = DEFAULT_FGCOLOR;
+	vp->data = (void *) kfb_vp;
+	
+	return EOK;
+}
+
+static void kfb_vp_destroy(fbdev_t *dev, fbvp_t *vp)
+{
+	free(vp->data);
+}
+
+static void kfb_vp_clear(fbdev_t *dev, fbvp_t *vp)
+{
+	pointer_hide();
+	
+	for (sysarg_t row = 0; row < vp->rows; row++) {
+		for (sysarg_t col = 0; col < vp->cols; col++) {
+			charfield_t *field =
+			    screenbuffer_field_at(vp->backbuf, col, row);
+			
+			field->ch = 0;
+			field->attrs = vp->attrs;
+		}
+	}
+	
+	pixel_t bgcolor = 0;
+	pixel_t fgcolor = 0;
+	attrs_rgb(vp->attrs, &bgcolor, &fgcolor);
+	
+	draw_filled_rect(vp->x, vp->y, vp->x + vp->width,
+	    vp->y + vp->height, bgcolor);
+	
+	pointer_show();
+}
+
+static console_caps_t kfb_vp_get_caps(fbdev_t *dev, fbvp_t *vp)
+{
+	return (CONSOLE_CAP_STYLE | CONSOLE_CAP_INDEXED | CONSOLE_CAP_RGB);
+}
+
+static void kfb_vp_cursor_update(fbdev_t *dev, fbvp_t *vp, sysarg_t prev_col,
+    sysarg_t prev_row, sysarg_t col, sysarg_t row, bool visible)
+{
+	pointer_hide();
+	draw_vp_char(vp, prev_col, prev_row);
+	draw_vp_char(vp, col, row);
+	pointer_show();
+}
+
+static void kfb_vp_cursor_flash(fbdev_t *dev, fbvp_t *vp, sysarg_t col,
+    sysarg_t row)
+{
+	pointer_hide();
+	draw_vp_char(vp, col, row);
+	pointer_show();
+}
+
+static void kfb_vp_char_update(fbdev_t *dev, fbvp_t *vp, sysarg_t col,
+    sysarg_t row)
+{
+	pointer_hide();
+	draw_vp_char(vp, col, row);
+	pointer_show();
+}
+
+static void kfb_vp_imgmap_damage(fbdev_t *dev, fbvp_t *vp, imgmap_t *imgmap,
+    sysarg_t x0, sysarg_t y0, sysarg_t width, sysarg_t height)
+{
+	pointer_hide();
+	
+	for (sysarg_t y = 0; y < height; y++) {
+		for (sysarg_t x = 0; x < width; x++) {
+			pixel_t pixel = imgmap_get_pixel(imgmap, x0 + x, y0 + y);
+			vp_put_pixel(vp, x0 + x, y0 + y, pixel);
+		}
+	}
+	
+	pointer_show();
+}
+
+static fbdev_ops_t kfb_ops = {
+	.yield = kfb_yield,
+	.claim = kfb_claim,
+	.pointer_update = kfb_pointer_update,
+	.get_resolution = kfb_get_resolution,
+	.font_metrics = kfb_font_metrics,
+	.vp_create = kfb_vp_create,
+	.vp_destroy = kfb_vp_destroy,
+	.vp_clear = kfb_vp_clear,
+	.vp_get_caps = kfb_vp_get_caps,
+	.vp_cursor_update = kfb_vp_cursor_update,
+	.vp_cursor_flash = kfb_vp_cursor_flash,
+	.vp_char_update = kfb_vp_char_update,
+	.vp_imgmap_damage = kfb_vp_imgmap_damage
+};
+
+/** Render glyphs
+ *
+ * Convert glyphs from device independent font
+ * description to current visual representation.
+ *
+ */
+static void render_glyphs(size_t sz)
+{
+	memset(kfb.glyphs, 0, sz);
+	
+	for (unsigned int glyph = 0; glyph < FONT_GLYPHS; glyph++) {
+		for (unsigned int y = 0; y < FONT_SCANLINES; y++) {
+			for (unsigned int x = 0; x < FONT_WIDTH; x++) {
+				kfb.visual_mask(
+				    &kfb.glyphs[GLYPH_POS(glyph, y, false) + x * kfb.pixel_bytes],
+				    (fb_font[glyph][y] & (1 << (7 - x))) ? true : false);
+				kfb.visual_mask(
+				    &kfb.glyphs[GLYPH_POS(glyph, y, true) + x * kfb.pixel_bytes],
+				    (fb_font[glyph][y] & (1 << (7 - x))) ? false : true);
+			}
+		}
+	}
+}
+
+int kfb_init(void)
+{
+	sysarg_t present;
+	int rc = sysinfo_get_value("fb", &present);
+	if (rc != EOK)
+		present = false;
+	
+	if (!present)
+		return ENOENT;
+	
+	sysarg_t kind;
+	rc = sysinfo_get_value("fb.kind", &kind);
+	if (rc != EOK)
+		kind = (sysarg_t) -1;
+	
+	if (kind != 1)
+		return EINVAL;
+	
+	sysarg_t paddr;
+	rc = sysinfo_get_value("fb.address.physical", &paddr);
+	if (rc != EOK)
+		return rc;
+	
+	sysarg_t offset;
+	rc = sysinfo_get_value("fb.offset", &offset);
+	if (rc != EOK)
+		offset = 0;
+	
+	sysarg_t width;
+	rc = sysinfo_get_value("fb.width", &width);
+	if (rc != EOK)
+		return rc;
+	
+	sysarg_t height;
+	rc = sysinfo_get_value("fb.height", &height);
+	if (rc != EOK)
+		return rc;
+	
+	sysarg_t scanline;
+	rc = sysinfo_get_value("fb.scanline", &scanline);
+	if (rc != EOK)
+		return rc;
+	
+	sysarg_t visual;
+	rc = sysinfo_get_value("fb.visual", &visual);
+	if (rc != EOK)
+		return rc;
+	
+	kfb.width = width;
+	kfb.height = height;
+	kfb.offset = offset;
+	kfb.scanline = scanline;
+	kfb.visual = visual;
+	
+	switch (visual) {
+	case VISUAL_INDIRECT_8:
+		kfb.pixel2visual = pixel2bgr_323;
+		kfb.visual2pixel = bgr_323_2pixel;
+		kfb.visual_mask = visual_mask_323;
+		kfb.pixel_bytes = 1;
+		break;
+	case VISUAL_RGB_5_5_5_LE:
+		kfb.pixel2visual = pixel2rgb_555_le;
+		kfb.visual2pixel = rgb_555_le_2pixel;
+		kfb.visual_mask = visual_mask_555;
+		kfb.pixel_bytes = 2;
+		break;
+	case VISUAL_RGB_5_5_5_BE:
+		kfb.pixel2visual = pixel2rgb_555_be;
+		kfb.visual2pixel = rgb_555_be_2pixel;
+		kfb.visual_mask = visual_mask_555;
+		kfb.pixel_bytes = 2;
+		break;
+	case VISUAL_RGB_5_6_5_LE:
+		kfb.pixel2visual = pixel2rgb_565_le;
+		kfb.visual2pixel = rgb_565_le_2pixel;
+		kfb.visual_mask = visual_mask_565;
+		kfb.pixel_bytes = 2;
+		break;
+	case VISUAL_RGB_5_6_5_BE:
+		kfb.pixel2visual = pixel2rgb_565_be;
+		kfb.visual2pixel = rgb_565_be_2pixel;
+		kfb.visual_mask = visual_mask_565;
+		kfb.pixel_bytes = 2;
+		break;
+	case VISUAL_RGB_8_8_8:
+		kfb.pixel2visual = pixel2rgb_888;
+		kfb.visual2pixel = rgb_888_2pixel;
+		kfb.visual_mask = visual_mask_888;
+		kfb.pixel_bytes = 3;
+		break;
+	case VISUAL_BGR_8_8_8:
+		kfb.pixel2visual = pixel2bgr_888;
+		kfb.visual2pixel = bgr_888_2pixel;
+		kfb.visual_mask = visual_mask_888;
+		kfb.pixel_bytes = 3;
+		break;
+	case VISUAL_RGB_8_8_8_0:
+		kfb.pixel2visual = pixel2rgb_8880;
+		kfb.visual2pixel = rgb_8880_2pixel;
+		kfb.visual_mask = visual_mask_8880;
+		kfb.pixel_bytes = 4;
+		break;
+	case VISUAL_RGB_0_8_8_8:
+		kfb.pixel2visual = pixel2rgb_0888;
+		kfb.visual2pixel = rgb_0888_2pixel;
+		kfb.visual_mask = visual_mask_0888;
+		kfb.pixel_bytes = 4;
+		break;
+	case VISUAL_BGR_0_8_8_8:
+		kfb.pixel2visual = pixel2bgr_0888;
+		kfb.visual2pixel = bgr_0888_2pixel;
+		kfb.visual_mask = visual_mask_0888;
+		kfb.pixel_bytes = 4;
+		break;
+	case VISUAL_BGR_8_8_8_0:
+		kfb.pixel2visual = pixel2bgr_8880;
+		kfb.visual2pixel = bgr_8880_2pixel;
+		kfb.visual_mask = visual_mask_8880;
+		kfb.pixel_bytes = 4;
+		break;
+	default:
+		return EINVAL;
+	}
+	
+	kfb.glyph_scanline = FONT_WIDTH * kfb.pixel_bytes;
+	kfb.glyph_bytes = kfb.glyph_scanline * FONT_SCANLINES;
+	
+	size_t sz = 2 * FONT_GLYPHS * kfb.glyph_bytes;
+	kfb.glyphs = (uint8_t *) malloc(sz);
+	if (kfb.glyphs == NULL)
+		return EINVAL;
+	
+	render_glyphs(sz);
+	
+	kfb.size = scanline * height;
+	
+	rc = physmem_map((void *) paddr + offset,
+	    ALIGN_UP(kfb.size, PAGE_SIZE) >> PAGE_WIDTH,
+	    AS_AREA_READ | AS_AREA_WRITE, (void *) &kfb.addr);
+	if (rc != EOK) {
+		free(kfb.glyphs);
+		return rc;
+	}
+	
+	kfb.pointer_x = 0;
+	kfb.pointer_y = 0;
+	kfb.pointer_visible = false;
+	kfb.pointer_imgmap = imgmap_create(POINTER_WIDTH, POINTER_HEIGHT,
+	    VISUAL_RGB_0_8_8_8, IMGMAP_FLAG_NONE);
+	
+	kfb.backbuf = NULL;
+	
+	fbdev_t *dev = fbdev_register(&kfb_ops, (void *) &kfb);
+	if (dev == NULL) {
+		free(kfb.glyphs);
+		as_area_destroy(kfb.addr);
+		return EINVAL;
+	}
+	
+	return EOK;
+}
+
+/**
+ * @}
+ */
