Index: abi/include/abi/ipc/interfaces.h
===================================================================
--- abi/include/abi/ipc/interfaces.h	(revision d548fc0e28b66bd85d62aea9c93bea619d7f4520)
+++ abi/include/abi/ipc/interfaces.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -187,5 +187,11 @@
 	    FOURCC_COMPACT('i', 'p', 'c', 't') | IFACE_EXCHANGE_SERIALIZE,
 	INTERFACE_PCI =
-	    FOURCC_COMPACT('p', 'c', 'i', ' ') | IFACE_EXCHANGE_SERIALIZE
+	    FOURCC_COMPACT('p', 'c', 'i', ' ') | IFACE_EXCHANGE_SERIALIZE,
+	INTERFACE_DISPLAY =
+	    FOURCC_COMPACT('d', 's', 'p', 'l') | IFACE_EXCHANGE_SERIALIZE,
+	INTERFACE_DISPLAY_CB =
+	    FOURCC_COMPACT('d', 's', 'p', 'l') | IFACE_EXCHANGE_SERIALIZE | IFACE_MOD_CALLBACK,
+	INTERFACE_GC =
+	    FOURCC_COMPACT('g', 'f', 'x', 'c') | IFACE_EXCHANGE_SERIALIZE,
 } iface_t;
 
Index: uspace/app/gfxdemo/doc/doxygroups.h
===================================================================
--- uspace/app/gfxdemo/doc/doxygroups.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/app/gfxdemo/doc/doxygroups.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,4 @@
+/** @addtogroup vdemo vdemo
+ * @brief Demo application
+ * @ingroup apps
+ */
Index: uspace/app/gfxdemo/gfxdemo.c
===================================================================
--- uspace/app/gfxdemo/gfxdemo.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/app/gfxdemo/gfxdemo.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,527 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup gfxdemo
+ * @{
+ */
+/** @file Graphic demo
+ */
+
+#include <canvas.h>
+#include <congfx/console.h>
+#include <draw/surface.h>
+#include <display.h>
+#include <fibril.h>
+#include <guigfx/canvas.h>
+#include <gfx/bitmap.h>
+#include <gfx/color.h>
+#include <gfx/render.h>
+#include <io/console.h>
+#include <io/pixelmap.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <str.h>
+#include <window.h>
+
+static void wnd_kbd_event(void *, kbd_event_t *);
+
+static display_wnd_cb_t wnd_cb = {
+	.kbd_event = wnd_kbd_event
+};
+
+static bool quit = false;
+
+/** Clear screen.
+ *
+ * @param gc Graphic context
+ * @param w Screen width
+ * @param h Screen height
+ */
+static errno_t clear_scr(gfx_context_t *gc, gfx_coord_t w, gfx_coord_t h)
+{
+	gfx_color_t *color = NULL;
+	gfx_rect_t rect;
+	errno_t rc;
+
+	rc = gfx_color_new_rgb_i16(0, 0, 0, &color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_set_color(gc, color);
+	if (rc != EOK)
+		goto error;
+
+	rect.p0.x = 0;
+	rect.p0.y = 0;
+	rect.p1.x = w;
+	rect.p1.y = h;
+
+	rc = gfx_fill_rect(gc, &rect);
+	if (rc != EOK)
+		goto error;
+
+	gfx_color_delete(color);
+	return EOK;
+error:
+	if (color != NULL)
+		gfx_color_delete(color);
+	return rc;
+}
+
+/** Run rectangle demo on a graphic context.
+ *
+ * @param gc Graphic context
+ * @param w Width
+ * @param h Height
+ */
+static errno_t demo_rects(gfx_context_t *gc, gfx_coord_t w, gfx_coord_t h)
+{
+	gfx_color_t *color = NULL;
+	gfx_rect_t rect;
+	int i, j;
+	errno_t rc;
+
+	rc = clear_scr(gc, w, h);
+	if (rc != EOK)
+		return rc;
+
+	for (j = 0; j < 10; j++) {
+		rc = gfx_color_new_rgb_i16(rand() % 0x10000, rand() % 0x10000,
+		    rand() % 0x10000, &color);
+		if (rc != EOK)
+			return rc;
+
+		rc = gfx_set_color(gc, color);
+		if (rc != EOK)
+			return rc;
+
+		for (i = 0; i < 10; i++) {
+			rect.p0.x = rand() % (w - 1);
+			rect.p0.y = rand() % (h - 1);
+			rect.p1.x = rect.p0.x + rand() % (w - 1 - rect.p0.x);
+			rect.p1.y = rect.p0.y + rand() % (h - 1 - rect.p0.y);
+
+			rc = gfx_fill_rect(gc, &rect);
+			if (rc != EOK)
+				return rc;
+		}
+
+		gfx_color_delete(color);
+
+		fibril_usleep(500 * 1000);
+
+		if (quit)
+			break;
+	}
+
+	return EOK;
+}
+
+/** Fill bitmap with tartan pattern.
+ *
+ * @param bitmap Bitmap
+ * @param w Bitmap width
+ * @param h Bitmap height
+ * @return EOK on success or an error code
+ */
+static errno_t bitmap_tartan(gfx_bitmap_t *bitmap, gfx_coord_t w, gfx_coord_t h)
+{
+	int i, j;
+	pixelmap_t pixelmap;
+	gfx_bitmap_alloc_t alloc;
+	errno_t rc;
+
+	rc = gfx_bitmap_get_alloc(bitmap, &alloc);
+	if (rc != EOK)
+		return rc;
+
+	/* In absence of anything else, use pixelmap */
+	pixelmap.width = w;
+	pixelmap.height = h;
+	pixelmap.data = alloc.pixels;
+
+	for (i = 0; i < w; i++) {
+		for (j = 0; j < h; j++) {
+			pixelmap_put_pixel(&pixelmap, i, j,
+			    PIXEL(255, (i % 30) < 3 ? 255 : 0,
+			    (j % 30) < 3 ? 255 : 0, i / 2));
+		}
+	}
+
+	return EOK;
+}
+
+/** Fill bitmap with moire pattern.
+ *
+ * @param bitmap Bitmap
+ * @param w Bitmap width
+ * @param h Bitmap height
+ * @return EOK on success or an error code
+ */
+static errno_t bitmap_moire(gfx_bitmap_t *bitmap, gfx_coord_t w, gfx_coord_t h)
+{
+	int i, j;
+	int k;
+	pixelmap_t pixelmap;
+	gfx_bitmap_alloc_t alloc;
+	errno_t rc;
+
+	rc = gfx_bitmap_get_alloc(bitmap, &alloc);
+	if (rc != EOK)
+		return rc;
+
+	/* In absence of anything else, use pixelmap */
+	pixelmap.width = w;
+	pixelmap.height = h;
+	pixelmap.data = alloc.pixels;
+
+	for (i = 0; i < w; i++) {
+		for (j = 0; j < h; j++) {
+			k = i * i + j * j;
+			pixelmap_put_pixel(&pixelmap, i, j,
+			    PIXEL(255, k, k, k));
+		}
+	}
+
+	return EOK;
+}
+
+/** Run bitmap demo on a graphic context.
+ *
+ * @param gc Graphic context
+ * @param w Width
+ * @param h Height
+ */
+static errno_t demo_bitmap(gfx_context_t *gc, gfx_coord_t w, gfx_coord_t h)
+{
+	gfx_bitmap_t *bitmap;
+	gfx_bitmap_params_t params;
+	int i, j;
+	gfx_coord2_t offs;
+	gfx_rect_t srect;
+	errno_t rc;
+
+	rc = clear_scr(gc, w, h);
+	if (rc != EOK)
+		return rc;
+
+	params.rect.p0.x = 0;
+	params.rect.p0.y = 0;
+	params.rect.p1.x = w;
+	params.rect.p1.y = h;
+
+	rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
+	if (rc != EOK)
+		return rc;
+
+	rc = bitmap_tartan(bitmap, w, h);
+	if (rc != EOK)
+		goto error;
+
+	for (j = 0; j < 10; j++) {
+		for (i = 0; i < 5; i++) {
+			srect.p0.x = rand() % (w - 40);
+			srect.p0.y = rand() % (h - 20);
+			srect.p1.x = srect.p0.x + rand() % (w - srect.p0.x);
+			srect.p1.y = srect.p0.y + rand() % (h - srect.p0.y);
+			offs.x = 0;
+			offs.y = 0;
+
+			rc = gfx_bitmap_render(bitmap, &srect, &offs);
+			if (rc != EOK)
+				goto error;
+			fibril_usleep(250 * 1000);
+
+			if (quit)
+				break;
+		}
+	}
+
+	gfx_bitmap_destroy(bitmap);
+
+	return EOK;
+error:
+	gfx_bitmap_destroy(bitmap);
+	return rc;
+}
+
+/** Run second bitmap demo on a graphic context.
+ *
+ * @param gc Graphic context
+ * @param w Width
+ * @param h Height
+ */
+static errno_t demo_bitmap2(gfx_context_t *gc, gfx_coord_t w, gfx_coord_t h)
+{
+	gfx_bitmap_t *bitmap;
+	gfx_bitmap_params_t params;
+	int i, j;
+	gfx_coord2_t offs;
+	errno_t rc;
+
+	rc = clear_scr(gc, w, h);
+	if (rc != EOK)
+		return rc;
+
+	params.rect.p0.x = 0;
+	params.rect.p0.y = 0;
+	params.rect.p1.x = 40;
+	params.rect.p1.y = 20;
+
+	rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
+	if (rc != EOK)
+		return rc;
+
+	rc = bitmap_moire(bitmap, 40, 20);
+	if (rc != EOK)
+		goto error;
+
+	for (j = 0; j < 10; j++) {
+		for (i = 0; i < 10; i++) {
+			offs.x = rand() % (w - 40);
+			offs.y = rand() % (h - 20);
+
+			rc = gfx_bitmap_render(bitmap, NULL, &offs);
+			if (rc != EOK)
+				goto error;
+		}
+
+		fibril_usleep(500 * 1000);
+
+		if (quit)
+			break;
+	}
+
+	gfx_bitmap_destroy(bitmap);
+
+	return EOK;
+error:
+	gfx_bitmap_destroy(bitmap);
+	return rc;
+}
+
+/** Run demo loop on a graphic context.
+ *
+ * @param gc Graphic context
+ * @param w Width
+ * @param h Height
+ */
+static errno_t demo_loop(gfx_context_t *gc, gfx_coord_t w, gfx_coord_t h)
+{
+	errno_t rc;
+
+	while (!quit) {
+		rc = demo_rects(gc, w, h);
+		if (rc != EOK)
+			return rc;
+
+		rc = demo_bitmap(gc, w, h);
+		if (rc != EOK)
+			return rc;
+
+		rc = demo_bitmap2(gc, w, h);
+		if (rc != EOK)
+			return rc;
+	}
+
+	return EOK;
+}
+
+/** Run demo on console. */
+static errno_t demo_console(void)
+{
+	console_ctrl_t *con = NULL;
+	console_gc_t *cgc = NULL;
+	gfx_context_t *gc;
+	errno_t rc;
+
+	printf("Init console..\n");
+	con = console_init(stdin, stdout);
+	if (con == NULL)
+		return EIO;
+
+	printf("Create console GC\n");
+	rc = console_gc_create(con, stdout, &cgc);
+	if (rc != EOK)
+		return rc;
+
+	gc = console_gc_get_ctx(cgc);
+
+	rc = demo_loop(gc, 80, 25);
+	if (rc != EOK)
+		return rc;
+
+	rc = console_gc_delete(cgc);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+/** Run demo on canvas. */
+static errno_t demo_canvas(void)
+{
+	canvas_gc_t *cgc = NULL;
+	gfx_context_t *gc;
+	window_t *window = NULL;
+	pixel_t *pixbuf = NULL;
+	surface_t *surface = NULL;
+	canvas_t *canvas = NULL;
+	gfx_coord_t vw, vh;
+	errno_t rc;
+
+	printf("Init canvas..\n");
+
+	window = window_open("comp:0/winreg", NULL,
+	    WINDOW_MAIN | WINDOW_DECORATED, "GFX Demo");
+	if (window == NULL) {
+		printf("Error creating window.\n");
+		return -1;
+	}
+
+	vw = 400;
+	vh = 300;
+
+	pixbuf = calloc(vw * vh, sizeof(pixel_t));
+	if (pixbuf == NULL) {
+		printf("Error allocating memory for pixel buffer.\n");
+		return ENOMEM;
+	}
+
+	surface = surface_create(vw, vh, pixbuf, 0);
+	if (surface == NULL) {
+		printf("Error creating surface.\n");
+		return EIO;
+	}
+
+	canvas = create_canvas(window_root(window), NULL, vw, vh,
+	    surface);
+	if (canvas == NULL) {
+		printf("Error creating canvas.\n");
+		return EIO;
+	}
+
+	window_resize(window, 0, 0, vw + 10, vh + 30, WINDOW_PLACEMENT_ANY);
+	window_exec(window);
+
+	printf("Create canvas GC\n");
+	rc = canvas_gc_create(canvas, surface, &cgc);
+	if (rc != EOK)
+		return rc;
+
+	gc = canvas_gc_get_ctx(cgc);
+
+	rc = demo_loop(gc, 400, 300);
+	if (rc != EOK)
+		return rc;
+
+	rc = canvas_gc_delete(cgc);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+/** Run demo on display server. */
+static errno_t demo_display(void)
+{
+	display_t *display = NULL;
+	gfx_context_t *gc;
+	display_window_t *window = NULL;
+	errno_t rc;
+
+	printf("Init display..\n");
+
+	rc = display_open(NULL, &display);
+	if (rc != EOK) {
+		printf("Error opening display.\n");
+		return rc;
+	}
+
+	rc = display_window_create(display, &wnd_cb, NULL, &window);
+	if (rc != EOK) {
+		printf("Error creating window.\n");
+		return rc;
+	}
+
+	rc = display_window_get_gc(window, &gc);
+	if (rc != EOK) {
+		printf("Error getting graphics context.\n");
+	}
+
+	rc = demo_loop(gc, 400, 300);
+	if (rc != EOK)
+		return rc;
+
+	rc = gfx_context_delete(gc);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+static void wnd_kbd_event(void *arg, kbd_event_t *event)
+{
+	printf("Keyboard event type=%d key=%d\n", event->type, event->key);
+	quit = true;
+}
+
+static void print_syntax(void)
+{
+	printf("syntax: gfxdemo {canvas|console|display}\n");
+}
+
+int main(int argc, char *argv[])
+{
+	errno_t rc;
+
+	if (argc < 2) {
+		print_syntax();
+		return 1;
+	}
+
+	if (str_cmp(argv[1], "console") == 0) {
+		rc = demo_console();
+		if (rc != EOK)
+			return 1;
+	} else if (str_cmp(argv[1], "canvas") == 0) {
+		rc = demo_canvas();
+		if (rc != EOK)
+			return 1;
+	} else if (str_cmp(argv[1], "display") == 0) {
+		rc = demo_display();
+		if (rc != EOK)
+			return 1;
+	} else {
+		print_syntax();
+		return 1;
+	}
+}
+
+/** @}
+ */
Index: uspace/app/gfxdemo/meson.build
===================================================================
--- uspace/app/gfxdemo/meson.build	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/app/gfxdemo/meson.build	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,32 @@
+#
+# Copyright (c) 2019 Jiri Svoboda
+# 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.
+#
+
+deps = [ 'gfx', 'guigfx', 'congfx', 'ipcgfx', 'display' ]
+src = files(
+	'gfxdemo.c',
+)
Index: uspace/app/meson.build
===================================================================
--- uspace/app/meson.build	(revision d548fc0e28b66bd85d62aea9c93bea619d7f4520)
+++ uspace/app/meson.build	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -45,4 +45,5 @@
 	'fontviewer',
 	'getterm',
+	'gfxdemo',
 	'gunzip',
 	'hbench',
Index: uspace/lib/c/include/ipc/services.h
===================================================================
--- uspace/lib/c/include/ipc/services.h	(revision d548fc0e28b66bd85d62aea9c93bea619d7f4520)
+++ uspace/lib/c/include/ipc/services.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -54,4 +54,5 @@
 #define SERVICE_NAME_CLIPBOARD "clipboard"
 #define SERVICE_NAME_CORECFG  "corecfg"
+#define SERVICE_NAME_DISPLAY  "hid/display"
 #define SERVICE_NAME_DHCP     "net/dhcp"
 #define SERVICE_NAME_DNSR     "net/dnsr"
Index: uspace/lib/congfx/doc/doxygroups.h
===================================================================
--- uspace/lib/congfx/doc/doxygroups.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/congfx/doc/doxygroups.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,3 @@
+/** @addtogroup libcongfx libcongfx
+ * @ingroup libs
+ */
Index: uspace/lib/congfx/include/congfx/console.h
===================================================================
--- uspace/lib/congfx/include/congfx/console.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/congfx/include/congfx/console.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libcongfx
+ * @{
+ */
+/**
+ * @file GFX console backend
+ */
+
+#ifndef _CONGFX_CONSOLE_H
+#define _CONGFX_CONSOLE_H
+
+#include <io/console.h>
+#include <stdio.h>
+#include <types/congfx/console.h>
+#include <types/gfx/context.h>
+#include <types/gfx/ops/context.h>
+
+extern gfx_context_ops_t console_gc_ops;
+
+extern errno_t console_gc_create(console_ctrl_t *, FILE *, console_gc_t **);
+extern errno_t console_gc_delete(console_gc_t *);
+extern gfx_context_t *console_gc_get_ctx(console_gc_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/congfx/include/types/congfx/console.h
===================================================================
--- uspace/lib/congfx/include/types/congfx/console.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/congfx/include/types/congfx/console.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libcongfx
+ * @{
+ */
+/**
+ * @file GFX console backend
+ */
+
+#ifndef _CONGFX_TYPES_CONSOLE_H
+#define _CONGFX_TYPES_CONSOLE_H
+
+struct console_gc;
+typedef struct console_gc console_gc_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/congfx/meson.build
===================================================================
--- uspace/lib/congfx/meson.build	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/congfx/meson.build	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,32 @@
+#
+# Copyright (c) 2015 Jiri Svoboda
+# 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.
+#
+
+deps = [ 'gfx' ]
+src = files(
+	'src/console.c',
+)
Index: uspace/lib/congfx/private/console.h
===================================================================
--- uspace/lib/congfx/private/console.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/congfx/private/console.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libcongfx
+ * @{
+ */
+/**
+ * @file GFX console backend structure
+ *
+ */
+
+#ifndef _CONGFX_PRIVATE_CONSOLE_H
+#define _CONGFX_PRIVATE_CONSOLE_H
+
+#include <gfx/context.h>
+#include <io/console.h>
+#include <io/pixel.h>
+#include <stdio.h>
+
+/** Actual structure of graphics context.
+ *
+ * This is private to libcongfx. It is not visible to clients nor backends.
+ */
+struct console_gc {
+	/** Base graphic context */
+	gfx_context_t *gc;
+	/** Console control structure */
+	console_ctrl_t *con;
+	/** File for printing characters */
+	FILE *fout;
+	/** Current drawing color */
+	pixel_t clr;
+};
+
+/** Bitmap in console GC */
+typedef struct {
+	/** Containing console GC */
+	struct console_gc *cgc;
+	/** Allocation info */
+	gfx_bitmap_alloc_t alloc;
+	/** @c true if we allocated the bitmap, @c false if allocated by caller */
+	bool myalloc;
+	/** Rectangle covered by bitmap */
+	gfx_rect_t rect;
+} console_gc_bitmap_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/congfx/src/console.c
===================================================================
--- uspace/lib/congfx/src/console.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/congfx/src/console.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libcongfx
+ * @{
+ */
+/**
+ * @file GFX console backend
+ *
+ * This implements a graphics context over a classic console interface.
+ * This is just for experimentation purposes. In the end we want the
+ * console to actually directly suport GFX interface.
+ */
+
+#include <congfx/console.h>
+#include <gfx/context.h>
+#include <gfx/coord.h>
+#include <gfx/render.h>
+#include <io/pixel.h>
+#include <io/pixelmap.h>
+#include <stdlib.h>
+#include "../private/console.h"
+#include "../private/color.h"
+
+static errno_t console_gc_set_color(void *, gfx_color_t *);
+static errno_t console_gc_fill_rect(void *, gfx_rect_t *);
+static errno_t console_gc_bitmap_create(void *, gfx_bitmap_params_t *,
+    gfx_bitmap_alloc_t *, void **);
+static errno_t console_gc_bitmap_destroy(void *);
+static errno_t console_gc_bitmap_render(void *, gfx_rect_t *, gfx_coord2_t *);
+static errno_t console_gc_bitmap_get_alloc(void *, gfx_bitmap_alloc_t *);
+
+gfx_context_ops_t console_gc_ops = {
+	.set_color = console_gc_set_color,
+	.fill_rect = console_gc_fill_rect,
+	.bitmap_create = console_gc_bitmap_create,
+	.bitmap_destroy = console_gc_bitmap_destroy,
+	.bitmap_render = console_gc_bitmap_render,
+	.bitmap_get_alloc = console_gc_bitmap_get_alloc
+};
+
+/** Set color on console GC.
+ *
+ * Set drawing color on console GC.
+ *
+ * @param arg Console GC
+ * @param color Color
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t console_gc_set_color(void *arg, gfx_color_t *color)
+{
+	console_gc_t *cgc = (console_gc_t *) arg;
+
+	cgc->clr = PIXEL(0, color->r >> 8, color->g >> 8, color->b >> 8);
+	return EOK;
+}
+
+/** Fill rectangle on console GC.
+ *
+ * @param arg Console GC
+ * @param rect Rectangle
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t console_gc_fill_rect(void *arg, gfx_rect_t *rect)
+{
+	console_gc_t *cgc = (console_gc_t *) arg;
+	int rv;
+	gfx_coord_t x, y;
+
+	// XXX We should handle p0.x > p1.x and p0.y > p1.y
+
+	console_set_rgb_color(cgc->con, cgc->clr, cgc->clr);
+
+	for (y = rect->p0.y; y < rect->p1.y; y++) {
+		console_set_pos(cgc->con, rect->p0.x, y);
+
+		for (x = rect->p0.x; x < rect->p1.x; x++) {
+			rv = fputc('X', cgc->fout);
+			if (rv < 0)
+				return EIO;
+		}
+
+		console_flush(cgc->con);
+	}
+
+	return EOK;
+}
+
+/** Create console GC.
+ *
+ * Create graphics context for rendering into a console.
+ *
+ * @param con Console object
+ * @param fout File to which characters are written (console)
+ * @param rgc Place to store pointer to new GC.
+ *
+ * @return EOK on success or an error code
+ */
+errno_t console_gc_create(console_ctrl_t *con, FILE *fout,
+    console_gc_t **rgc)
+{
+	console_gc_t *cgc = NULL;
+	gfx_context_t *gc = NULL;
+	errno_t rc;
+
+	cgc = calloc(1, sizeof(console_gc_t));
+	if (cgc == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	rc = gfx_context_new(&console_gc_ops, cgc, &gc);
+	if (rc != EOK)
+		goto error;
+
+	cgc->gc = gc;
+	cgc->con = con;
+	cgc->fout = fout;
+	*rgc = cgc;
+	return EOK;
+error:
+	if (cgc != NULL)
+		free(cgc);
+	gfx_context_delete(gc);
+	return rc;
+}
+
+/** Delete console GC.
+ *
+ * @param cgc Console GC
+ */
+errno_t console_gc_delete(console_gc_t *cgc)
+{
+	errno_t rc;
+
+	rc = gfx_context_delete(cgc->gc);
+	if (rc != EOK)
+		return rc;
+
+	free(cgc);
+	return EOK;
+}
+
+/** Get generic graphic context from console GC.
+ *
+ * @param cgc Console GC
+ * @return Graphic context
+ */
+gfx_context_t *console_gc_get_ctx(console_gc_t *cgc)
+{
+	return cgc->gc;
+}
+
+/** Create bitmap in console GC.
+ *
+ * @param arg console GC
+ * @param params Bitmap params
+ * @param alloc Bitmap allocation info or @c NULL
+ * @param rbm Place to store pointer to new bitmap
+ * @return EOK on success or an error code
+ */
+errno_t console_gc_bitmap_create(void *arg, gfx_bitmap_params_t *params,
+    gfx_bitmap_alloc_t *alloc, void **rbm)
+{
+	console_gc_t *cgc = (console_gc_t *) arg;
+	console_gc_bitmap_t *cbm = NULL;
+	gfx_coord2_t dim;
+	errno_t rc;
+
+	cbm = calloc(1, sizeof(console_gc_bitmap_t));
+	if (cbm == NULL)
+		return ENOMEM;
+
+	gfx_coord2_subtract(&params->rect.p1, &params->rect.p0, &dim);
+	cbm->rect = params->rect;
+
+	if (alloc == NULL) {
+		cbm->alloc.pitch = dim.x * sizeof(uint32_t);
+		cbm->alloc.off0 = 0;
+		cbm->alloc.pixels = calloc(dim.x * dim.y, sizeof(uint32_t));
+		if (cbm->alloc.pixels == NULL) {
+			rc = ENOMEM;
+			goto error;
+		}
+
+		cbm->myalloc = true;
+	} else {
+		cbm->alloc = *alloc;
+	}
+
+	cbm->cgc = cgc;
+	*rbm = (void *)cbm;
+	return EOK;
+error:
+	if (cbm != NULL)
+		free(cbm);
+	return rc;
+}
+
+/** Destroy bitmap in console GC.
+ *
+ * @param bm Bitmap
+ * @return EOK on success or an error code
+ */
+static errno_t console_gc_bitmap_destroy(void *bm)
+{
+	console_gc_bitmap_t *cbm = (console_gc_bitmap_t *)bm;
+	if (cbm->myalloc)
+		free(cbm->alloc.pixels);
+	free(cbm);
+	return EOK;
+}
+
+/** Render bitmap in console GC.
+ *
+ * @param bm Bitmap
+ * @param srect0 Source rectangle or @c NULL
+ * @param offs0 Offset or @c NULL
+ * @return EOK on success or an error code
+ */
+static errno_t console_gc_bitmap_render(void *bm, gfx_rect_t *srect0,
+    gfx_coord2_t *offs0)
+{
+	console_gc_bitmap_t *cbm = (console_gc_bitmap_t *)bm;
+	gfx_coord_t x, y;
+	int rv;
+	pixel_t clr;
+	pixelmap_t pixelmap;
+	gfx_rect_t srect;
+	gfx_rect_t drect;
+	gfx_coord2_t offs;
+
+	if (srect0 != NULL)
+		srect = *srect0;
+	else
+		srect = cbm->rect;
+
+	if (offs0 != NULL) {
+		offs = *offs0;
+	} else {
+		offs.x = 0;
+		offs.y = 0;
+	}
+
+	// XXX Add function to translate rectangle
+	gfx_rect_translate(&offs, &srect, &drect);
+
+	pixelmap.width = cbm->rect.p1.x - cbm->rect.p0.x;
+	pixelmap.height = cbm->rect.p1.y = cbm->rect.p1.y;
+	pixelmap.data = cbm->alloc.pixels;
+
+	for (y = drect.p0.y; y < drect.p1.y; y++) {
+		console_set_pos(cbm->cgc->con, drect.p0.x, y);
+
+		for (x = drect.p0.x; x < drect.p1.x; x++) {
+			clr = pixelmap_get_pixel(&pixelmap,
+			    x - offs.x - cbm->rect.p0.x,
+			    y - offs.y - cbm->rect.p0.y);
+			console_set_rgb_color(cbm->cgc->con, clr, clr);
+
+			rv = fputc('X', cbm->cgc->fout);
+			if (rv < 0)
+				return EIO;
+
+			console_flush(cbm->cgc->con);
+		}
+	}
+
+	return EOK;
+}
+
+/** Get allocation info for bitmap in console GC.
+ *
+ * @param bm Bitmap
+ * @param alloc Place to store allocation info
+ * @return EOK on success or an error code
+ */
+static errno_t console_gc_bitmap_get_alloc(void *bm, gfx_bitmap_alloc_t *alloc)
+{
+	console_gc_bitmap_t *cbm = (console_gc_bitmap_t *)bm;
+	*alloc = cbm->alloc;
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/display/doc/doxygroups.h
===================================================================
--- uspace/lib/display/doc/doxygroups.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/display/doc/doxygroups.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,3 @@
+/** @addtogroup libdisplay libdisplay
+ * @ingroup libs
+ */
Index: uspace/lib/display/include/disp_srv.h
===================================================================
--- uspace/lib/display/include/disp_srv.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/display/include/disp_srv.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2012 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libdisplay
+ * @{
+ */
+/** @file
+ */
+
+#ifndef _LIBDISPLAY_DISP_SRV_H_
+#define _LIBDISPLAY_DISP_SRV_H_
+
+#include <async.h>
+#include <errno.h>
+#include <types/display/event.h>
+
+typedef struct display_ops display_ops_t;
+
+/** Display server structure (per client session) */
+typedef struct {
+	async_sess_t *client_sess;
+	display_ops_t *ops;
+	void *arg;
+} display_srv_t;
+
+struct display_ops {
+	errno_t (*window_create)(void *, sysarg_t *);
+	errno_t (*window_destroy)(void *, sysarg_t);
+	errno_t (*get_event)(void *, sysarg_t *, display_wnd_ev_t *);
+};
+
+extern void display_conn(ipc_call_t *, display_srv_t *);
+extern void display_srv_initialize(display_srv_t *);
+extern void display_srv_ev_pending(display_srv_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/display/include/display.h
===================================================================
--- uspace/lib/display/include/display.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/display/include/display.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libdisplay
+ * @{
+ */
+/** @file
+ */
+
+#ifndef _LIBDISPLAY_DISPLAY_H_
+#define _LIBDISPLAY_DISPLAY_H_
+
+#include <errno.h>
+#include <gfx/context.h>
+#include <stdbool.h>
+#include "types/display.h"
+
+extern errno_t display_open(const char *, display_t **);
+extern void display_close(display_t *);
+extern errno_t display_window_create(display_t *, display_wnd_cb_t *,
+    void *, display_window_t **);
+extern errno_t display_window_destroy(display_window_t *);
+extern errno_t display_window_get_gc(display_window_t *, gfx_context_t **);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/display/include/display/event.h
===================================================================
--- uspace/lib/display/include/display/event.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/display/include/display/event.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libdisplay
+ * @{
+ */
+/** @file
+ */
+
+#ifndef _LIBDISPLAY_DISPLAY_EVENT_H_
+#define _LIBDISPLAY_DISPLAY_EVENT_H_
+
+#include <types/display/event.h>
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/display/include/ipc/display.h
===================================================================
--- uspace/lib/display/include/ipc/display.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/display/include/ipc/display.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libcipc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef _LIBC_IPC_DISPLAY_H_
+#define _LIBC_IPC_DISPLAY_H_
+
+#include <ipc/common.h>
+
+typedef enum {
+	DISPLAY_CALLBACK_CREATE = IPC_FIRST_USER_METHOD,
+	DISPLAY_WINDOW_CREATE,
+	DISPLAY_WINDOW_DESTROY,
+	DISPLAY_GET_EVENT
+} display_request_t;
+
+typedef enum {
+	DISPLAY_EV_PENDING = IPC_FIRST_USER_METHOD
+} display_event_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/display/include/types/display.h
===================================================================
--- uspace/lib/display/include/types/display.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/display/include/types/display.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef _LIBDISPLAY_TYPES_DISPLAY_H_
+#define _LIBDISPLAY_TYPES_DISPLAY_H_
+
+#include <async.h>
+#include <fibril_synch.h>
+#include <io/kbd_event.h>
+#include <ipc/devman.h>
+#include <stdint.h>
+
+/** Display server session */
+typedef struct {
+	/** Session with display server */
+	async_sess_t *sess;
+	/** Synchronize access to display object */
+	fibril_mutex_t lock;
+	/** @c true if callback handler terminated */
+	bool cb_done;
+	/** Signalled when cb_done or ev_pending is changed */
+	fibril_condvar_t cv;
+	/** Windows (of display_window_t) */
+	list_t windows;
+} display_t;
+
+/** Display window callbacks */
+typedef struct {
+	void (*kbd_event)(void *, kbd_event_t *);
+} display_wnd_cb_t;
+
+/** Display window */
+typedef struct {
+	/** Display associated with the window */
+	display_t *display;
+	/** Link to @c display->windows */
+	link_t lwindows;
+	/** Window ID */
+	sysarg_t id;
+	/** Callback functions */
+	display_wnd_cb_t *cb;
+	/** Argument to callback functions */
+	void *cb_arg;
+} display_window_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/display/include/types/display/event.h
===================================================================
--- uspace/lib/display/include/types/display/event.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/display/include/types/display/event.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef _LIBDISPLAY_TYPES_DISPLAY_EVENT_H_
+#define _LIBDISPLAY_TYPES_DISPLAY_EVENT_H_
+
+#include <io/kbd_event.h>
+
+typedef struct {
+	kbd_event_t kbd_event;
+} display_wnd_ev_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/display/meson.build
===================================================================
--- uspace/lib/display/meson.build	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/display/meson.build	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,38 @@
+#
+# Copyright (c) 2019 Jiri Svoboda
+# 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.
+#
+
+deps = [ 'gfx', 'ipcgfx' ]
+src = files(
+	'src/display.c',
+	'src/disp_srv.c',
+)
+
+test_src = files(
+	'test/display.c',
+	'test/main.c',
+)
Index: uspace/lib/display/src/disp_srv.c
===================================================================
--- uspace/lib/display/src/disp_srv.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/display/src/disp_srv.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/**
+ * @file
+ * @brief Display protocol server stub
+ */
+
+#include <disp_srv.h>
+#include <display/event.h>
+#include <errno.h>
+#include <io/log.h>
+#include <ipc/display.h>
+#include <mem.h>
+#include <stdlib.h>
+#include <stddef.h>
+
+#include <stdio.h>
+static void display_callback_create_srv(display_srv_t *srv, ipc_call_t *call)
+{
+	printf("display_callback_create_srv\n");
+
+	async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
+	if (sess == NULL) {
+		async_answer_0(call, ENOMEM);
+		return;
+	}
+
+	srv->client_sess = sess;
+	async_answer_0(call, EOK);
+}
+
+static void display_window_create_srv(display_srv_t *srv, ipc_call_t *icall)
+{
+	sysarg_t wnd_id;
+	errno_t rc;
+
+	printf("display_window_create_srv\n");
+
+	if (srv->ops->window_create == NULL) {
+		async_answer_0(icall, ENOTSUP);
+		return;
+	}
+
+	rc = srv->ops->window_create(srv->arg, &wnd_id);
+	async_answer_1(icall, rc, wnd_id);
+}
+
+static void display_window_destroy_srv(display_srv_t *srv, ipc_call_t *icall)
+{
+	sysarg_t wnd_id;
+	errno_t rc;
+
+	printf("display_window_destroy_srv\n");
+
+	wnd_id = ipc_get_arg1(icall);
+
+	if (srv->ops->window_create == NULL) {
+		async_answer_0(icall, ENOTSUP);
+		return;
+	}
+
+	rc = srv->ops->window_destroy(srv->arg, wnd_id);
+	async_answer_0(icall, rc);
+}
+
+static void display_get_event_srv(display_srv_t *srv, ipc_call_t *icall)
+{
+	sysarg_t wnd_id;
+	display_wnd_ev_t event;
+	ipc_call_t call;
+	size_t size;
+	errno_t rc;
+
+	printf("display_get_event_srv\n");
+
+	if (srv->ops->get_event == NULL) {
+		async_answer_0(icall, ENOTSUP);
+		return;
+	}
+
+	rc = srv->ops->get_event(srv->arg, &wnd_id, &event);
+	if (rc != EOK) {
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	/* Transfer event data */
+	if (!async_data_read_receive(&call, &size)) {
+		async_answer_0(icall, EREFUSED);
+		return;
+	}
+
+	if (size != sizeof(event)) {
+		async_answer_0(icall, EREFUSED);
+		async_answer_0(&call, EREFUSED);
+		return;
+	}
+
+	rc = async_data_read_finalize(&call, &event, sizeof(event));
+	if (rc != EOK) {
+		async_answer_0(icall, rc);
+		async_answer_0(&call, rc);
+		return;
+	}
+
+	async_answer_1(icall, EOK, wnd_id);
+}
+
+void display_conn(ipc_call_t *icall, display_srv_t *srv)
+{
+	/* Accept the connection */
+	async_accept_0(icall);
+	printf("display_conn\n");
+
+	while (true) {
+		ipc_call_t call;
+
+		async_get_call(&call);
+		sysarg_t method = ipc_get_imethod(&call);
+
+		if (!method) {
+			/* The other side has hung up */
+			async_answer_0(&call, EOK);
+			break;
+		}
+
+		printf("display_conn method=%lu\n", method);
+		switch (method) {
+		case DISPLAY_CALLBACK_CREATE:
+			display_callback_create_srv(srv, &call);
+			break;
+		case DISPLAY_WINDOW_CREATE:
+			display_window_create_srv(srv, &call);
+			break;
+		case DISPLAY_WINDOW_DESTROY:
+			display_window_destroy_srv(srv, &call);
+			break;
+		case DISPLAY_GET_EVENT:
+			display_get_event_srv(srv, &call);
+			break;
+		default:
+			async_answer_0(&call, ENOTSUP);
+		}
+	}
+
+	/* Hang up callback session */
+	if (srv->client_sess != NULL) {
+		async_hangup(srv->client_sess);
+		srv->client_sess = NULL;
+	}
+}
+
+/** Initialize display server structure
+ *
+ * @param srv Display server structure to initialize
+ */
+void display_srv_initialize(display_srv_t *srv)
+{
+	memset(srv, 0, sizeof(*srv));
+}
+
+/** Send 'pending' event to client.
+ *
+ * @param srv Display server structure
+ */
+void display_srv_ev_pending(display_srv_t *srv)
+{
+	async_exch_t *exch;
+
+	printf("display_srv_ev_pending()\n");
+
+	exch = async_exchange_begin(srv->client_sess);
+	async_msg_0(exch, DISPLAY_EV_PENDING);
+	async_exchange_end(exch);
+}
+
+/** @}
+ */
Index: uspace/lib/display/src/display.c
===================================================================
--- uspace/lib/display/src/display.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/display/src/display.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+#include <async.h>
+#include <display.h>
+#include <display/event.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <ipc/display.h>
+#include <ipc/services.h>
+#include <ipcgfx/client.h>
+#include <loc.h>
+#include <stdlib.h>
+
+static errno_t display_callback_create(display_t *);
+static void display_cb_conn(ipc_call_t *, void *);
+static errno_t display_get_window(display_t *, sysarg_t, display_window_t **);
+
+/** Open display service.
+ *
+ * @param dsname Display service name or @c NULL to use default display
+ * @param rdisplay Place to store pointer to display session
+ * @return EOK on success or an error code
+ */
+errno_t display_open(const char *dsname, display_t **rdisplay)
+{
+	service_id_t display_svc;
+	display_t *display;
+	errno_t rc;
+
+	display = calloc(1, sizeof(display_t));
+	if (display == NULL)
+		return ENOMEM;
+
+	fibril_mutex_initialize(&display->lock);
+	fibril_condvar_initialize(&display->cv);
+	list_initialize(&display->windows);
+
+	if (dsname == NULL)
+		dsname = SERVICE_NAME_DISPLAY;
+
+	rc = loc_service_get_id(dsname, &display_svc, IPC_FLAG_BLOCKING);
+	if (rc != EOK) {
+		free(display);
+		return ENOENT;
+	}
+
+	display->sess = loc_service_connect(display_svc, INTERFACE_DISPLAY,
+	    IPC_FLAG_BLOCKING);
+	if (display->sess == NULL) {
+		free(display);
+		return ENOENT;
+	}
+
+	rc = display_callback_create(display);
+	if (rc != EOK) {
+		async_hangup(display->sess);
+		free(display);
+		return EIO;
+	}
+
+	*rdisplay = display;
+	return EOK;
+}
+
+/** Create callback connection from display service.
+ *
+ * @param display Display session
+ * @return EOK on success or an error code
+ */
+static errno_t display_callback_create(display_t *display)
+{
+	async_exch_t *exch = async_exchange_begin(display->sess);
+
+	aid_t req = async_send_0(exch, DISPLAY_CALLBACK_CREATE, NULL);
+
+	port_id_t port;
+	errno_t rc = async_create_callback_port(exch, INTERFACE_DISPLAY_CB, 0, 0,
+	    display_cb_conn, display, &port);
+
+	async_exchange_end(exch);
+
+	if (rc != EOK)
+		return rc;
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	return retval;
+}
+
+/** Close display service.
+ *
+ * @param display Display session
+ */
+void display_close(display_t *display)
+{
+	async_hangup(display->sess);
+
+	/* Wait for callback handler to terminate */
+
+	fibril_mutex_lock(&display->lock);
+	while (!display->cb_done)
+		fibril_condvar_wait(&display->cv, &display->lock);
+	fibril_mutex_unlock(&display->lock);
+
+	free(display);
+}
+
+/** Create a display window.
+ *
+ * @param display Display
+ * @param cb Callback functions
+ * @param cb_arg Argument to callback functions
+ * @param rwindow Place to store pointer to new window
+ * @return EOK on success or an error code
+ */
+errno_t display_window_create(display_t *display, display_wnd_cb_t *cb,
+    void *cb_arg, display_window_t **rwindow)
+{
+	display_window_t *window;
+	async_exch_t *exch;
+	sysarg_t wnd_id;
+	errno_t rc;
+
+	window = calloc(1, sizeof(display_window_t));
+	if (window == NULL)
+		return ENOMEM;
+
+	exch = async_exchange_begin(display->sess);
+	rc = async_req_0_1(exch, DISPLAY_WINDOW_CREATE, &wnd_id);
+
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		free(window);
+		return rc;
+	}
+
+	window->display = display;
+	window->id = wnd_id;
+	window->cb = cb;
+	window->cb_arg = cb_arg;
+
+	list_append(&window->lwindows, &display->windows);
+	*rwindow = window;
+	return EOK;
+}
+
+/** Destroy display window.
+ *
+ * @param window Window
+ * @return EOK on success or an error code. In both cases @a window must
+ *         not be accessed anymore
+ */
+errno_t display_window_destroy(display_window_t *window)
+{
+	async_exch_t *exch;
+	errno_t rc;
+
+	exch = async_exchange_begin(window->display->sess);
+	rc = async_req_1_0(exch, DISPLAY_WINDOW_DESTROY, window->id);
+
+	async_exchange_end(exch);
+
+	list_remove(&window->lwindows);
+	free(window);
+	return rc;
+}
+
+/** Create graphics context for drawing into a window.
+ *
+ * @param window Window
+ * @param rgc Place to store pointer to new graphics context
+ */
+errno_t display_window_get_gc(display_window_t *window, gfx_context_t **rgc)
+{
+	async_sess_t *sess;
+	async_exch_t *exch;
+	ipc_gc_t *gc;
+	errno_t rc;
+
+	exch = async_exchange_begin(window->display->sess);
+	sess = async_connect_me_to(exch, INTERFACE_GC, 0, window->id);
+	if (sess == NULL) {
+		async_exchange_end(exch);
+		return EIO;
+	}
+
+	async_exchange_end(exch);
+
+	rc = ipc_gc_create(sess, &gc);
+	if (rc != EOK) {
+		async_hangup(sess);
+		return ENOMEM;
+	}
+
+	*rgc = ipc_gc_get_ctx(gc);
+	return EOK;
+}
+
+/** Get display event.
+ *
+ * @param display Display
+ * @param rwindow Place to store pointe to window that received event
+ * @param event Place to store event
+ * @return EOK on success or an error code
+ */
+static errno_t display_get_event(display_t *display, display_window_t **rwindow,
+    display_wnd_ev_t *event)
+{
+	async_exch_t *exch;
+	ipc_call_t answer;
+	aid_t req;
+	errno_t rc;
+	sysarg_t wnd_id;
+	display_window_t *window;
+
+	exch = async_exchange_begin(display->sess);
+	req = async_send_0(exch, DISPLAY_GET_EVENT, &answer);
+	rc = async_data_read_start(exch, event, sizeof(*event));
+	async_exchange_end(exch);
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	async_wait_for(req, &rc);
+	if (rc != EOK)
+		return rc;
+
+	wnd_id = ipc_get_arg1(&answer);
+	rc = display_get_window(display, wnd_id, &window);
+	if (rc != EOK)
+		return EIO;
+
+	*rwindow = window;
+	return EOK;
+}
+
+/** Display events are pending.
+ *
+ * @param display Display
+ * @param icall Call data
+ */
+static void display_ev_pending(display_t *display, ipc_call_t *icall)
+{
+	errno_t rc;
+	display_window_t *window = NULL;
+	display_wnd_ev_t event;
+
+	while (true) {
+		rc = display_get_event(display, &window, &event);
+		if (rc != EOK)
+			break;
+
+		if (window->cb->kbd_event != NULL)
+			window->cb->kbd_event(window->cb_arg, &event.kbd_event);
+	}
+
+	async_answer_0(icall, EOK);
+}
+
+/** Callback connection handler.
+ *
+ * @param icall Connect call data
+ * @param arg   Argument, display_t *
+ */
+static void display_cb_conn(ipc_call_t *icall, void *arg)
+{
+	display_t *display = (display_t *) arg;
+
+	while (true) {
+		ipc_call_t call;
+		async_get_call(&call);
+
+		if (!ipc_get_imethod(&call)) {
+			/* Hangup */
+			async_answer_0(&call, EOK);
+			goto out;
+		}
+
+		switch (ipc_get_imethod(&call)) {
+		case DISPLAY_EV_PENDING:
+			display_ev_pending(display, &call);
+			break;
+		default:
+			async_answer_0(&call, ENOTSUP);
+			break;
+		}
+	}
+
+out:
+	fibril_mutex_lock(&display->lock);
+	display->cb_done = true;
+	fibril_mutex_unlock(&display->lock);
+	fibril_condvar_broadcast(&display->cv);
+}
+
+/** Find window by ID.
+ *
+ * @param display Display
+ * @param wnd_id Window ID
+ * @param rwindow Place to store pointer to window
+ * @return EOK on success, ENOENT if not found
+ */
+static errno_t display_get_window(display_t *display, sysarg_t wnd_id,
+    display_window_t **rwindow)
+{
+	link_t *link;
+	display_window_t *window;
+
+	link = list_first(&display->windows);
+	while (link != NULL) {
+		window = list_get_instance(link, display_window_t, lwindows);
+		if (window->id == wnd_id) {
+			*rwindow = window;
+			return EOK;
+		}
+
+		link = list_next(link, &display->windows);
+	}
+
+	return ENOENT;
+}
+
+/** @}
+ */
Index: uspace/lib/display/test/display.c
===================================================================
--- uspace/lib/display/test/display.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/display/test/display.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,506 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+#include <async.h>
+#include <errno.h>
+#include <display.h>
+#include <disp_srv.h>
+#include <fibril_synch.h>
+#include <gfx/color.h>
+#include <gfx/context.h>
+#include <gfx/render.h>
+#include <ipcgfx/server.h>
+#include <loc.h>
+#include <pcut/pcut.h>
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(display);
+
+static const char *test_display_server = "test-display";
+static const char *test_display_svc = "test/display";
+
+static void test_display_conn(ipc_call_t *, void *);
+static void test_kbd_event(void *, kbd_event_t *);
+
+static errno_t test_window_create(void *, sysarg_t *);
+static errno_t test_window_destroy(void *, sysarg_t);
+static errno_t test_get_event(void *, sysarg_t *, display_wnd_ev_t *);
+
+static errno_t test_gc_set_color(void *, gfx_color_t *);
+
+static display_ops_t test_display_srv_ops = {
+	.window_create = test_window_create,
+	.window_destroy = test_window_destroy,
+	.get_event = test_get_event
+};
+
+static display_wnd_cb_t test_display_wnd_cb = {
+	.kbd_event = test_kbd_event
+};
+
+static gfx_context_ops_t test_gc_ops = {
+	.set_color = test_gc_set_color
+};
+
+/** Describes to the server how to respond to our request and pass tracking
+ * data back to the client.
+ */
+typedef struct {
+	errno_t rc;
+	sysarg_t wnd_id;
+	display_wnd_ev_t event;
+	display_wnd_ev_t revent;
+	int event_cnt;
+	bool window_create_called;
+	bool window_destroy_called;
+	bool get_event_called;
+	bool set_color_called;
+	bool kbd_event_called;
+	fibril_condvar_t kbd_event_cv;
+	fibril_mutex_t kbd_event_lock;
+	display_srv_t *srv;
+} test_response_t;
+
+/** display_open(), display_close() work for valid display service */
+PCUT_TEST(open_close)
+{
+	errno_t rc;
+	service_id_t sid;
+	display_t *disp = NULL;
+	test_response_t resp;
+
+	async_set_fallback_port_handler(test_display_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_display_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_display_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = display_open(test_display_svc, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(disp);
+
+	display_close(disp);
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** display_window_create() with server returning error response works */
+PCUT_TEST(window_create_failure)
+{
+	errno_t rc;
+	service_id_t sid;
+	display_t *disp = NULL;
+	display_window_t *wnd;
+	test_response_t resp;
+
+	async_set_fallback_port_handler(test_display_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_display_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_display_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = display_open(test_display_svc, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(disp);
+
+	wnd = NULL;
+	resp.rc = ENOMEM;
+	resp.window_create_called = false;
+	rc = display_window_create(disp, &test_display_wnd_cb, NULL, &wnd);
+	PCUT_ASSERT_TRUE(resp.window_create_called);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+	PCUT_ASSERT_NULL(wnd);
+
+	display_close(disp);
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** display_window_create() and display_window_destroy() with success
+ *
+ * with server returning success,
+ */
+PCUT_TEST(window_create_destroy_success)
+{
+	errno_t rc;
+	service_id_t sid;
+	display_t *disp = NULL;
+	display_window_t *wnd;
+	test_response_t resp;
+
+	async_set_fallback_port_handler(test_display_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_display_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_display_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = display_open(test_display_svc, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(disp);
+
+	wnd = NULL;
+	resp.rc = EOK;
+	resp.window_create_called = false;
+	rc = display_window_create(disp, &test_display_wnd_cb, NULL, &wnd);
+	PCUT_ASSERT_TRUE(resp.window_create_called);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(wnd);
+
+	resp.window_destroy_called = false;
+	rc = display_window_destroy(wnd);
+	PCUT_ASSERT_TRUE(resp.window_destroy_called);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	display_close(disp);
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** display_window_create() with server returning error response works. */
+PCUT_TEST(window_destroy_failure)
+{
+	errno_t rc;
+	service_id_t sid;
+	display_t *disp = NULL;
+	display_window_t *wnd;
+	test_response_t resp;
+
+	async_set_fallback_port_handler(test_display_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_display_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_display_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = display_open(test_display_svc, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(disp);
+
+	resp.rc = EOK;
+	resp.window_create_called = false;
+	rc = display_window_create(disp, &test_display_wnd_cb, NULL, &wnd);
+	PCUT_ASSERT_TRUE(resp.window_create_called);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(wnd);
+
+	resp.rc = EIO;
+	resp.window_destroy_called = false;
+	rc = display_window_destroy(wnd);
+	PCUT_ASSERT_TRUE(resp.window_destroy_called);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+
+	display_close(disp);
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** display_window_get_gc with server returning failure */
+PCUT_TEST(window_get_gc_failure)
+{
+	errno_t rc;
+	service_id_t sid;
+	display_t *disp = NULL;
+	display_window_t *wnd;
+	test_response_t resp;
+	gfx_context_t *gc;
+
+	async_set_fallback_port_handler(test_display_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_display_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_display_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = display_open(test_display_svc, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(disp);
+
+	wnd = NULL;
+	resp.rc = EOK;
+	rc = display_window_create(disp, &test_display_wnd_cb, NULL, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(wnd);
+
+	gc = NULL;
+	resp.rc = ENOMEM;
+	rc = display_window_get_gc(wnd, &gc);
+	/* async_connect_me_to() does not return specific error */
+	PCUT_ASSERT_ERRNO_VAL(EIO, rc);
+	PCUT_ASSERT_NULL(gc);
+
+	resp.rc = EOK;
+	rc = display_window_destroy(wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	display_close(disp);
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** display_window_get_gc with server returning success */
+PCUT_TEST(window_get_gc_success)
+{
+	errno_t rc;
+	service_id_t sid;
+	display_t *disp = NULL;
+	display_window_t *wnd;
+	test_response_t resp;
+	gfx_context_t *gc;
+	gfx_color_t *color;
+
+	async_set_fallback_port_handler(test_display_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_display_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_display_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = display_open(test_display_svc, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(disp);
+
+	wnd = NULL;
+	resp.rc = EOK;
+	rc = display_window_create(disp, &test_display_wnd_cb, NULL, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(wnd);
+
+	gc = NULL;
+	rc = display_window_get_gc(wnd, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	rc = gfx_color_new_rgb_i16(0, 0, 0, &color);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	resp.set_color_called = false;
+	rc = gfx_set_color(gc, color);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(resp.set_color_called);
+
+	gfx_color_delete(color);
+
+	rc = display_window_destroy(wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	display_close(disp);
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Keyboard event can be delivered from server to client callback function */
+PCUT_TEST(kbd_event_deliver)
+{
+	errno_t rc;
+	service_id_t sid;
+	display_t *disp = NULL;
+	display_window_t *wnd;
+	test_response_t resp;
+	gfx_context_t *gc;
+
+	async_set_fallback_port_handler(test_display_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_display_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_display_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = display_open(test_display_svc, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(disp);
+	PCUT_ASSERT_NOT_NULL(resp.srv);
+
+	wnd = NULL;
+	resp.rc = EOK;
+	rc = display_window_create(disp, &test_display_wnd_cb, (void *) &resp,
+	    &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(wnd);
+
+	gc = NULL;
+	rc = display_window_get_gc(wnd, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	resp.event_cnt = 1;
+	resp.event.kbd_event.type = KEY_PRESS;
+	resp.event.kbd_event.key = KC_ENTER;
+	resp.event.kbd_event.mods = 0;
+	resp.event.kbd_event.c = L'\0';
+	resp.wnd_id = wnd->id;
+	resp.kbd_event_called = false;
+	fibril_mutex_initialize(&resp.kbd_event_lock);
+	fibril_condvar_initialize(&resp.kbd_event_cv);
+	display_srv_ev_pending(resp.srv);
+
+	/* Wait for the event handler to be called. */
+	fibril_mutex_lock(&resp.kbd_event_lock);
+	while (!resp.kbd_event_called) {
+		fibril_condvar_wait(&resp.kbd_event_cv, &resp.kbd_event_lock);
+	}
+	fibril_mutex_unlock(&resp.kbd_event_lock);
+
+	/* Verify that the event was delivered correctly */
+	PCUT_ASSERT_EQUALS(resp.event.kbd_event.type,
+	    resp.revent.kbd_event.type);
+	PCUT_ASSERT_EQUALS(resp.event.kbd_event.key,
+	    resp.revent.kbd_event.key);
+	PCUT_ASSERT_EQUALS(resp.event.kbd_event.mods,
+	    resp.revent.kbd_event.mods);
+	PCUT_ASSERT_EQUALS(resp.event.kbd_event.c,
+	    resp.revent.kbd_event.c);
+
+	rc = display_window_destroy(wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	display_close(disp);
+
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Test display service connection.
+ *
+ * This is very similar to connection handler in the display server.
+ * XXX This should be folded into display_srv, if possible
+ */
+static void test_display_conn(ipc_call_t *icall, void *arg)
+{
+	test_response_t *resp = (test_response_t *) arg;
+	display_srv_t srv;
+	sysarg_t wnd_id;
+	sysarg_t svc_id;
+	gfx_context_t *gc;
+	errno_t rc;
+
+	svc_id = ipc_get_arg2(icall);
+	wnd_id = ipc_get_arg3(icall);
+
+	if (svc_id != 0) {
+		/* Set up protocol structure */
+		display_srv_initialize(&srv);
+		srv.ops = &test_display_srv_ops;
+		srv.arg = arg;
+		resp->srv = &srv;
+
+		/* Handle connection */
+		display_conn(icall, &srv);
+
+		resp->srv = NULL;
+	} else {
+		(void) wnd_id;
+
+		if (resp->rc != EOK) {
+			async_answer_0(icall, resp->rc);
+			return;
+		}
+
+		rc = gfx_context_new(&test_gc_ops, arg, &gc);
+		if (rc != EOK) {
+			async_answer_0(icall, ENOMEM);
+			return;
+		}
+
+		/* Window GC connection */
+		gc_conn(icall, gc);
+	}
+}
+
+static void test_kbd_event(void *arg, kbd_event_t *event)
+{
+	test_response_t *resp = (test_response_t *) arg;
+
+	resp->revent.kbd_event = *event;
+
+	fibril_mutex_lock(&resp->kbd_event_lock);
+	resp->kbd_event_called = true;
+	fibril_condvar_broadcast(&resp->kbd_event_cv);
+	fibril_mutex_unlock(&resp->kbd_event_lock);
+}
+
+static errno_t test_window_create(void *arg, sysarg_t *rwnd_id)
+{
+	test_response_t *resp = (test_response_t *) arg;
+
+	resp->window_create_called = true;
+	if (resp->rc == EOK)
+		*rwnd_id = resp->wnd_id;
+
+	return resp->rc;
+}
+
+static errno_t test_window_destroy(void *arg, sysarg_t wnd_id)
+{
+	test_response_t *resp = (test_response_t *) arg;
+
+	resp->window_destroy_called = true;
+	return resp->rc;
+}
+
+static errno_t test_get_event(void *arg, sysarg_t *wnd_id, display_wnd_ev_t *event)
+{
+	test_response_t *resp = (test_response_t *) arg;
+
+	resp->get_event_called = true;
+	if (resp->event_cnt > 0) {
+		--resp->event_cnt;
+		*wnd_id = resp->wnd_id;
+		*event = resp->event;
+		return EOK;
+	}
+
+	return ENOENT;
+}
+
+static errno_t test_gc_set_color(void *arg, gfx_color_t *color)
+{
+	test_response_t *resp = (test_response_t *) arg;
+
+	resp->set_color_called = true;
+	return resp->rc;
+}
+
+PCUT_EXPORT(display);
Index: uspace/lib/display/test/main.c
===================================================================
--- uspace/lib/display/test/main.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/display/test/main.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+#include <pcut/pcut.h>
+
+PCUT_INIT;
+
+PCUT_IMPORT(display);
+
+PCUT_MAIN();
Index: uspace/lib/gfx/doc/doxygroups.h
===================================================================
--- uspace/lib/gfx/doc/doxygroups.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/doc/doxygroups.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,3 @@
+/** @addtogroup libgfx libgfx
+ * @ingroup libs
+ */
Index: uspace/lib/gfx/include/gfx/bitmap.h
===================================================================
--- uspace/lib/gfx/include/gfx/bitmap.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/include/gfx/bitmap.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libgfx
+ * @{
+ */
+/**
+ * @file Bitmap
+ */
+
+#ifndef _GFX_BITMAP_H
+#define _GFX_BITMAP_H
+
+#include <errno.h>
+#include <types/gfx/context.h>
+#include <types/gfx/coord.h>
+#include <types/gfx/bitmap.h>
+
+extern errno_t gfx_bitmap_create(gfx_context_t *, gfx_bitmap_params_t *,
+    gfx_bitmap_alloc_t *, gfx_bitmap_t **);
+extern errno_t gfx_bitmap_destroy(gfx_bitmap_t *);
+extern errno_t gfx_bitmap_render(gfx_bitmap_t *, gfx_rect_t *, gfx_coord2_t *);
+extern errno_t gfx_bitmap_get_alloc(gfx_bitmap_t *, gfx_bitmap_alloc_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/gfx/include/gfx/color.h
===================================================================
--- uspace/lib/gfx/include/gfx/color.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/include/gfx/color.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libgfx
+ * @{
+ */
+/**
+ * @file Color operations
+ */
+
+#ifndef _GFX_COLOR_H
+#define _GFX_COLOR_H
+
+#include <errno.h>
+#include <stdint.h>
+#include <types/gfx/color.h>
+
+extern errno_t gfx_color_new_rgb_i16(uint16_t, uint16_t,
+    uint16_t, gfx_color_t **);
+extern void gfx_color_delete(gfx_color_t *);
+extern void gfx_color_get_rgb_i16(gfx_color_t *, uint16_t *, uint16_t *,
+    uint16_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/gfx/include/gfx/context.h
===================================================================
--- uspace/lib/gfx/include/gfx/context.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/include/gfx/context.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libgfx
+ * @{
+ */
+/**
+ * @file Graphic context
+ *
+ * A graphics context is the target of rendering operations. It can carry
+ * some additional state (hence context). It is an abstract interface,
+ * to be implemented by various backends (drivers).
+ */
+
+#ifndef _GFX_CONTEXT_H
+#define _GFX_CONTEXT_H
+
+#include <errno.h>
+#include <types/gfx/context.h>
+#include <types/gfx/ops/context.h>
+
+extern errno_t gfx_context_new(gfx_context_ops_t *, void *,
+    gfx_context_t **);
+extern errno_t gfx_context_delete(gfx_context_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/gfx/include/gfx/coord.h
===================================================================
--- uspace/lib/gfx/include/gfx/coord.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/include/gfx/coord.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libgfx
+ * @{
+ */
+/**
+ * @file Graphic coordinates
+ */
+
+#ifndef _GFX_COORD_H
+#define _GFX_COORD_H
+
+#include <types/gfx/coord.h>
+
+extern void gfx_coord2_add(gfx_coord2_t *, gfx_coord2_t *, gfx_coord2_t *);
+extern void gfx_coord2_subtract(gfx_coord2_t *, gfx_coord2_t *, gfx_coord2_t *);
+extern void gfx_rect_translate(gfx_coord2_t *, gfx_rect_t *, gfx_rect_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/gfx/include/gfx/render.h
===================================================================
--- uspace/lib/gfx/include/gfx/render.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/include/gfx/render.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libgfx
+ * @{
+ */
+/**
+ * @file Rendering operations
+ */
+
+#ifndef _GFX_RENDER_H
+#define _GFX_RENDER_H
+
+#include <errno.h>
+#include <types/gfx/color.h>
+#include <types/gfx/coord.h>
+#include <types/gfx/context.h>
+
+extern errno_t gfx_set_color(gfx_context_t *, gfx_color_t *);
+extern errno_t gfx_fill_rect(gfx_context_t *, gfx_rect_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/gfx/include/types/gfx/bitmap.h
===================================================================
--- uspace/lib/gfx/include/types/gfx/bitmap.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/include/types/gfx/bitmap.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libgfx
+ * @{
+ */
+/**
+ * @file Color types
+ */
+
+#ifndef _GFX_TYPES_BITMAP_H
+#define _GFX_TYPES_BITMAP_H
+
+#include <errno.h>
+#include <stddef.h>
+#include <types/gfx/coord.h>
+
+struct gfx_bitmap;
+typedef struct gfx_bitmap gfx_bitmap_t;
+
+/** Bitmap parameters */
+typedef struct {
+	/** Rectangle represented in pixel array */
+	gfx_rect_t rect;
+} gfx_bitmap_params_t;
+
+/** Bitmap allocation info */
+typedef struct {
+	/** Byte offset from one successive line to the next */
+	int pitch;
+	/** Byte offset of first pixel */
+	size_t off0;
+	/** Pixel array */
+	void *pixels;
+} gfx_bitmap_alloc_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/gfx/include/types/gfx/color.h
===================================================================
--- uspace/lib/gfx/include/types/gfx/color.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/include/types/gfx/color.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libgfx
+ * @{
+ */
+/**
+ * @file Color types
+ */
+
+#ifndef _GFX_TYPES_COLOR_H
+#define _GFX_TYPES_COLOR_H
+
+#include <errno.h>
+#include <stdint.h>
+
+struct gfx_color;
+typedef struct gfx_color gfx_color_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/gfx/include/types/gfx/context.h
===================================================================
--- uspace/lib/gfx/include/types/gfx/context.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/include/types/gfx/context.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libgfx
+ * @{
+ */
+/**
+ * @file Graphics context
+ *
+ * A graphics context is the target of rendering operations. It can carry
+ * some additional state (hence context). It is an abstract interface,
+ * to be implemented by various backends (drivers).
+ */
+
+#ifndef _GFX_TYPES_CONTEXT_H
+#define _GFX_TYPES_CONTEXT_H
+
+struct gfx_context;
+typedef struct gfx_context gfx_context_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/gfx/include/types/gfx/coord.h
===================================================================
--- uspace/lib/gfx/include/types/gfx/coord.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/include/types/gfx/coord.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libgfx
+ * @{
+ */
+/**
+ * @file Graphic coordinates
+ */
+
+#ifndef _GFX_TYPES_COORD_H
+#define _GFX_TYPES_COORD_H
+
+#include <errno.h>
+#include <stdint.h>
+
+typedef int gfx_coord_t;
+
+typedef struct {
+	gfx_coord_t x;
+	gfx_coord_t y;
+} gfx_coord2_t;
+
+typedef struct {
+	gfx_coord2_t p0;
+	gfx_coord2_t p1;
+} gfx_rect_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/gfx/include/types/gfx/ops/context.h
===================================================================
--- uspace/lib/gfx/include/types/gfx/ops/context.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/include/types/gfx/ops/context.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libgfx
+ * @{
+ */
+/**
+ * @file Graphics context ops
+ *
+ * The ops structure describing an implementation of a graphics context.
+ */
+
+#ifndef _GFX_TYPES_OPS_CONTEXT_H
+#define _GFX_TYPES_OPS_CONTEXT_H
+
+#include <errno.h>
+#include <types/gfx/bitmap.h>
+#include <types/gfx/color.h>
+#include <types/gfx/coord.h>
+#include <types/gfx/context.h>
+
+/** Graphics context ops */
+typedef struct {
+	/** Set drawing collor */
+	errno_t (*set_color)(void *, gfx_color_t *);
+	/** Fill rectangle using the current drawing color */
+	errno_t (*fill_rect)(void *, gfx_rect_t *);
+	/** Create bitmap */
+	errno_t (*bitmap_create)(void *, gfx_bitmap_params_t *,
+	    gfx_bitmap_alloc_t *, void **);
+	/** Destroy bitmap */
+	errno_t (*bitmap_destroy)(void *);
+	/** Render bitmap */
+	errno_t (*bitmap_render)(void *, gfx_rect_t *, gfx_coord2_t *);
+	/** Get bitmap allocation info */
+	errno_t (*bitmap_get_alloc)(void *, gfx_bitmap_alloc_t *);
+} gfx_context_ops_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/gfx/meson.build
===================================================================
--- uspace/lib/gfx/meson.build	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/meson.build	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,43 @@
+#
+# Copyright (c) 2019 Jiri Svoboda
+# 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.
+#
+
+src = files(
+	'src/bitmap.c',
+	'src/color.c',
+	'src/coord.c',
+	'src/context.c',
+	'src/render.c'
+)
+
+test_src = files(
+	'test/bitmap.c',
+	'test/color.c',
+	'test/coord.c',
+	'test/main.c',
+	'test/render.c',
+)
Index: uspace/lib/gfx/private/bitmap.h
===================================================================
--- uspace/lib/gfx/private/bitmap.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/private/bitmap.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libgfx
+ * @{
+ */
+/**
+ * @file Bitmap structure
+ *
+ */
+
+#ifndef _GFX_PRIVATE_COLOR_H
+#define _GFX_PRIVATE_COLOR_H
+
+#include <types/gfx/context.h>
+
+/** Bitmap
+ *
+ * This is private to libgfx. It is not visible to clients nor backends.
+ */
+struct gfx_bitmap {
+	/** Graphics context of the bitmap */
+	gfx_context_t *gc;
+	/** GC-private data */
+	void *gc_priv;
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/gfx/private/color.h
===================================================================
--- uspace/lib/gfx/private/color.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/private/color.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libgfx
+ * @{
+ */
+/**
+ * @file Color structure
+ *
+ */
+
+#ifndef _GFX_PRIVATE_COLOR_H
+#define _GFX_PRIVATE_COLOR_H
+
+/** Actual structure of graphics color.
+ *
+ * This is private to libgfx. It is not visible to clients nor backends.
+ */
+struct gfx_color {
+	uint16_t r;
+	uint16_t g;
+	uint16_t b;
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/gfx/private/context.h
===================================================================
--- uspace/lib/gfx/private/context.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/private/context.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libgfx
+ * @{
+ */
+/**
+ * @file Graphics context structure
+ *
+ */
+
+#ifndef _GFX_PRIVATE_CONTEXT_H
+#define _GFX_PRIVATE_CONTEXT_H
+
+#include <types/gfx/ops/context.h>
+
+/** Actual structure of graphics context.
+ *
+ * This is private to libgfx. It is not visible to clients nor backends.
+ */
+struct gfx_context {
+	/** Graphics context ops */
+	gfx_context_ops_t *ops;
+	/** Instance argument */
+	void *arg;
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/gfx/src/bitmap.c
===================================================================
--- uspace/lib/gfx/src/bitmap.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/src/bitmap.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libgfx
+ * @{
+ */
+/**
+ * @file Bitmap
+ */
+
+#include <gfx/bitmap.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include "../private/bitmap.h"
+#include "../private/context.h"
+
+/** Allocate bitmap in a graphics context.
+ *
+ * @param gc Graphic context
+ * @param params Bitmap parameters
+ * @param alloc Bitmap allocation info or @c NULL to let GC allocate
+ *               pixel storage
+ * @param rbitmap Place to store pointer to new bitmap
+ *
+ * @return EOK on success, EINVAL if parameters are invald,
+ *         ENOMEM if insufficient resources, EIO if grahic device connection
+ *         was lost
+ */
+errno_t gfx_bitmap_create(gfx_context_t *gc, gfx_bitmap_params_t *params,
+    gfx_bitmap_alloc_t *alloc, gfx_bitmap_t **rbitmap)
+{
+	void *bm_priv;
+	gfx_bitmap_t *bitmap;
+	errno_t rc;
+
+	bitmap = calloc(1, sizeof(gfx_bitmap_t));
+	if (bitmap == NULL)
+		return ENOMEM;
+
+	rc = gc->ops->bitmap_create(gc->arg, params, alloc, &bm_priv);
+	if (rc != EOK)
+		return rc;
+
+	bitmap->gc = gc;
+	bitmap->gc_priv = bm_priv;
+	*rbitmap = bitmap;
+	return EOK;
+}
+
+/** Destroy bitmap from graphics context.
+ *
+ * @param bitmap Bitmap
+ *
+ * @return EOK on success, EIO if grahic device connection was lost
+ */
+errno_t gfx_bitmap_destroy(gfx_bitmap_t *bitmap)
+{
+	errno_t rc;
+
+	rc = bitmap->gc->ops->bitmap_destroy(bitmap->gc_priv);
+	if (rc != EOK)
+		return rc;
+
+	free(bitmap);
+	return EOK;
+}
+
+/** Render bitmap in graphics context.
+ *
+ * @param bitmap Bitmap
+ * @param srect Source rectangle or @c NULL to render entire bitmap
+ * @param offs Bitmap offset or @c NULL for zero offset
+ *
+ * @return EOK on success, EIO if grahic device connection was lost
+ */
+errno_t gfx_bitmap_render(gfx_bitmap_t *bitmap, gfx_rect_t *srect,
+    gfx_coord2_t *offs)
+{
+	return bitmap->gc->ops->bitmap_render(bitmap->gc_priv, srect, offs);
+}
+
+/** Get bitmap allocation info.
+ *
+ * @param bitmap Bitmap
+ * @param alloc Allocation info structure to fill in
+ *
+ * @return EOK on success, EIO if grahic device connection was lost
+ */
+errno_t gfx_bitmap_get_alloc(gfx_bitmap_t *bitmap, gfx_bitmap_alloc_t *alloc)
+{
+	return bitmap->gc->ops->bitmap_get_alloc(bitmap->gc_priv, alloc);
+}
+
+/** @}
+ */
Index: uspace/lib/gfx/src/color.c
===================================================================
--- uspace/lib/gfx/src/color.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/src/color.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libgfx
+ * @{
+ */
+/**
+ * @file Color operations
+ */
+
+#include <gfx/color.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include "../private/color.h"
+
+/** Create new 16-bit per channel RGB color.
+ *
+ * Create a new RGB color where the R, G, B components have 16 bits
+ * of precision each.
+ *
+ * @param r Red component
+ * @param g Green component
+ * @param b Blue component
+ * @param rcolor Place to store pointer to new color
+ *
+ * @return EOK on success or an error code, ENOMEM if out of resources,
+ *         EIO if the graphic device connection was lost
+ */
+errno_t gfx_color_new_rgb_i16(uint16_t r, uint16_t g, uint16_t b,
+    gfx_color_t **rcolor)
+{
+	gfx_color_t *color;
+
+	color = calloc(1, sizeof(gfx_color_t));
+	if (color == NULL)
+		return ENOMEM;
+
+	color->r = r;
+	color->g = g;
+	color->b = b;
+
+	*rcolor = color;
+	return EOK;
+}
+
+/** Delete color.
+ *
+ * @param color Color
+ */
+void gfx_color_delete(gfx_color_t *color)
+{
+	free(color);
+}
+
+/** Convert color to 16-bit RGB coordinates.
+ *
+ * @param color Color
+ * @param r Place to store red coordinate
+ * @param g Place to store green coordinate
+ * @param b Place to store blue coordinate
+ */
+void gfx_color_get_rgb_i16(gfx_color_t *color, uint16_t *r, uint16_t *g,
+    uint16_t *b)
+{
+	*r = color->r;
+	*g = color->g;
+	*b = color->b;
+}
+
+/** @}
+ */
Index: uspace/lib/gfx/src/context.c
===================================================================
--- uspace/lib/gfx/src/context.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/src/context.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libgfx
+ * @{
+ */
+/**
+ * @file Graphics context
+ */
+
+#include <gfx/context.h>
+#include <stdlib.h>
+#include "../private/context.h"
+
+/** Create new graphics context.
+ *
+ * Create new graphics context with the specified ops and argument.
+ *
+ * @param ops Graphics context ops
+ * @param arg Instance argument
+ * @param rgc Place to store pointer to new graphics context on success
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t gfx_context_new(gfx_context_ops_t *ops, void *arg,
+    gfx_context_t **rgc)
+{
+	gfx_context_t *gc;
+
+	gc = calloc(1, sizeof(gfx_context_t));
+	if (gc == NULL)
+		return ENOMEM;
+
+	gc->ops = ops;
+	gc->arg = arg;
+	*rgc = gc;
+	return EOK;
+}
+
+/** Delete graphics context.
+ *
+ * @param gc Graphics context or @c NULL
+ */
+errno_t gfx_context_delete(gfx_context_t *gc)
+{
+	if (gc == NULL)
+		return EOK;
+
+	free(gc);
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/gfx/src/coord.c
===================================================================
--- uspace/lib/gfx/src/coord.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/src/coord.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libgfx
+ * @{
+ */
+/**
+ * @file Coordinates
+ */
+
+#include <gfx/coord.h>
+
+/** Add two vectors.
+ *
+ * @param a First vector
+ * @param b Second vector
+ * @param d Destination
+ */
+void gfx_coord2_add(gfx_coord2_t *a, gfx_coord2_t *b, gfx_coord2_t *d)
+{
+	d->x = a->x + b->x;
+	d->y = a->y + b->y;
+}
+
+/** Subtract two vectors.
+ *
+ * @param a First vector
+ * @param b Second vector
+ * @param d Destination
+ */
+void gfx_coord2_subtract(gfx_coord2_t *a, gfx_coord2_t *b, gfx_coord2_t *d)
+{
+	d->x = a->x - b->x;
+	d->y = a->y - b->y;
+}
+
+/** Move (translate) rectangle.
+ *
+ * @param trans Translation
+ * @param src Source rectangle
+ * @param dest Destination rectangle
+ */
+void gfx_rect_translate(gfx_coord2_t *trans, gfx_rect_t *src, gfx_rect_t *dest)
+{
+	gfx_coord2_add(trans, &src->p0, &dest->p0);
+	gfx_coord2_add(trans, &src->p1, &dest->p1);
+}
+
+/** @}
+ */
Index: uspace/lib/gfx/src/render.c
===================================================================
--- uspace/lib/gfx/src/render.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/src/render.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libgfx
+ * @{
+ */
+/**
+ * @file Rendering operations
+ */
+
+#include <gfx/render.h>
+#include "../private/context.h"
+
+/** Set drawing color.
+ *
+ * @param gc Graphic context
+ * @param color Color
+ *
+ * @return EOK on success, ENOMEM if insufficient resources,
+ *         EIO if grahic device connection was lost
+ */
+errno_t gfx_set_color(gfx_context_t *gc, gfx_color_t *color)
+{
+	return gc->ops->set_color(gc->arg, color);
+}
+
+/** Fill rectangle using the current drawing color.
+ *
+ * @param gc Graphic context
+ * @param rect Rectangle
+ *
+ * @return EOK on success, ENOMEM if insufficient resources,
+ *         EIO if grahic device connection was lost
+ */
+errno_t gfx_fill_rect(gfx_context_t *gc, gfx_rect_t *rect)
+{
+	return gc->ops->fill_rect(gc->arg, rect);
+}
+
+/** @}
+ */
Index: uspace/lib/gfx/test/bitmap.c
===================================================================
--- uspace/lib/gfx/test/bitmap.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/test/bitmap.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+#include <gfx/bitmap.h>
+#include <gfx/context.h>
+#include <mem.h>
+#include <pcut/pcut.h>
+#include <stdbool.h>
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(bitmap);
+
+static errno_t testgc_bitmap_create(void *, gfx_bitmap_params_t *,
+    gfx_bitmap_alloc_t *, void **);
+static errno_t testgc_bitmap_destroy(void *);
+static errno_t testgc_bitmap_render(void *, gfx_rect_t *, gfx_coord2_t *);
+static errno_t testgc_bitmap_get_alloc(void *, gfx_bitmap_alloc_t *);
+
+static gfx_context_ops_t ops = {
+	.bitmap_create = testgc_bitmap_create,
+	.bitmap_destroy = testgc_bitmap_destroy,
+	.bitmap_render = testgc_bitmap_render,
+	.bitmap_get_alloc = testgc_bitmap_get_alloc
+};
+
+typedef struct {
+	bool bm_created;
+	bool bm_destroyed;
+	gfx_bitmap_params_t bm_params;
+	void *bm_pixels;
+	gfx_rect_t bm_srect;
+	gfx_coord2_t bm_offs;
+	bool bm_rendered;
+	bool bm_got_alloc;
+} test_gc_t;
+
+typedef struct {
+	test_gc_t *tgc;
+	gfx_bitmap_alloc_t alloc;
+	bool myalloc;
+} testgc_bitmap_t;
+
+enum {
+	alloc_pitch = 42,
+	alloc_off0 = 33
+};
+
+PCUT_TEST(create_destroy)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	gfx_bitmap_params_t params;
+	gfx_bitmap_t *bitmap = NULL;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	params.rect.p0.x = 1;
+	params.rect.p0.y = 2;
+	params.rect.p1.x = 3;
+	params.rect.p1.y = 4;
+
+	rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(bitmap);
+	PCUT_ASSERT_TRUE(tgc.bm_created);
+	PCUT_ASSERT_EQUALS(params.rect.p0.x, tgc.bm_params.rect.p0.x);
+	PCUT_ASSERT_EQUALS(params.rect.p0.y, tgc.bm_params.rect.p0.y);
+	PCUT_ASSERT_EQUALS(params.rect.p1.x, tgc.bm_params.rect.p1.x);
+	PCUT_ASSERT_EQUALS(params.rect.p1.y, tgc.bm_params.rect.p1.y);
+
+	rc = gfx_bitmap_destroy(bitmap);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(tgc.bm_destroyed);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+PCUT_TEST(render)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	gfx_bitmap_params_t params;
+	gfx_bitmap_t *bitmap = NULL;
+	gfx_rect_t srect;
+	gfx_coord2_t offs;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	srect.p0.x = 1;
+	srect.p0.y = 2;
+	srect.p1.x = 3;
+	srect.p1.y = 4;
+	offs.x = 5;
+	offs.y = 6;
+
+	rc = gfx_bitmap_render(bitmap, &srect, &offs);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(tgc.bm_rendered);
+	PCUT_ASSERT_EQUALS(srect.p0.x, tgc.bm_srect.p0.x);
+	PCUT_ASSERT_EQUALS(srect.p0.y, tgc.bm_srect.p0.y);
+	PCUT_ASSERT_EQUALS(srect.p1.x, tgc.bm_srect.p1.x);
+	PCUT_ASSERT_EQUALS(srect.p1.y, tgc.bm_srect.p1.y);
+	PCUT_ASSERT_EQUALS(offs.x, tgc.bm_offs.x);
+	PCUT_ASSERT_EQUALS(offs.y, tgc.bm_offs.y);
+
+	rc = gfx_bitmap_destroy(bitmap);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+PCUT_TEST(get_alloc)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	gfx_bitmap_params_t params;
+	gfx_bitmap_t *bitmap = NULL;
+	gfx_bitmap_alloc_t alloc;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_bitmap_get_alloc(bitmap, &alloc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(tgc.bm_got_alloc);
+	PCUT_ASSERT_EQUALS(alloc_pitch, alloc.pitch);
+	PCUT_ASSERT_EQUALS(alloc_off0, alloc.off0);
+	PCUT_ASSERT_EQUALS(tgc.bm_pixels, alloc.pixels);
+
+	rc = gfx_bitmap_destroy(bitmap);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+static errno_t testgc_bitmap_create(void *arg, gfx_bitmap_params_t *params,
+    gfx_bitmap_alloc_t *alloc, void **rbm)
+{
+	test_gc_t *tgc = (test_gc_t *) arg;
+	testgc_bitmap_t *tbm;
+
+	tbm = calloc(1, sizeof(testgc_bitmap_t));
+	if (tbm == NULL)
+		return ENOMEM;
+
+	if (alloc == NULL) {
+		tbm->alloc.pitch = alloc_pitch;
+		tbm->alloc.off0 = alloc_off0;
+		tbm->alloc.pixels = calloc(1, 420);
+		tbm->myalloc = true;
+		if (tbm->alloc.pixels == NULL) {
+			free(tbm);
+			return ENOMEM;
+		}
+	} else {
+		tbm->alloc = *alloc;
+	}
+
+	tbm->tgc = tgc;
+	tgc->bm_created = true;
+	tgc->bm_params = *params;
+	tgc->bm_pixels = tbm->alloc.pixels;
+	*rbm = (void *)tbm;
+	return EOK;
+}
+
+static errno_t testgc_bitmap_destroy(void *bm)
+{
+	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
+	if (tbm->myalloc)
+		free(tbm->alloc.pixels);
+	tbm->tgc->bm_destroyed = true;
+	free(tbm);
+	return EOK;
+}
+
+static errno_t testgc_bitmap_render(void *bm, gfx_rect_t *srect,
+    gfx_coord2_t *offs)
+{
+	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
+	tbm->tgc->bm_rendered = true;
+	tbm->tgc->bm_srect = *srect;
+	tbm->tgc->bm_offs = *offs;
+	return EOK;
+}
+
+static errno_t testgc_bitmap_get_alloc(void *bm, gfx_bitmap_alloc_t *alloc)
+{
+	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
+	*alloc = tbm->alloc;
+	tbm->tgc->bm_got_alloc = true;
+	return EOK;
+}
+
+PCUT_EXPORT(bitmap);
Index: uspace/lib/gfx/test/color.c
===================================================================
--- uspace/lib/gfx/test/color.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/test/color.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+#include <errno.h>
+#include <gfx/color.h>
+#include <pcut/pcut.h>
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(color);
+
+PCUT_TEST(init_rgb_i16)
+{
+	errno_t rc;
+	gfx_color_t *color;
+
+	/* White */
+	rc = gfx_color_new_rgb_i16(0xffff, 0xffff, 0xffff, &color);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gfx_color_delete(color);
+}
+
+PCUT_EXPORT(color);
Index: uspace/lib/gfx/test/coord.c
===================================================================
--- uspace/lib/gfx/test/coord.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/test/coord.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+#include <gfx/coord.h>
+#include <pcut/pcut.h>
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(coord);
+
+extern void gfx_coord2_add(gfx_coord2_t *, gfx_coord2_t *, gfx_coord2_t *);
+extern void gfx_coord2_subtract(gfx_coord2_t *, gfx_coord2_t *, gfx_coord2_t *);
+extern void gfx_rect_translate(gfx_coord2_t *, gfx_rect_t *, gfx_rect_t *);
+
+/** gfx_coord2_add should add two coordinate vectors */
+PCUT_TEST(coord2_add)
+{
+	gfx_coord2_t a, b;
+	gfx_coord2_t d;
+
+	a.x = 10;
+	a.y = 11;
+	b.x = 20;
+	b.y = 22;
+
+	gfx_coord2_add(&a, &b, &d);
+
+	PCUT_ASSERT_EQUALS(a.x + b.x, d.x);
+	PCUT_ASSERT_EQUALS(a.y + b.y, d.y);
+}
+
+/** gfx_coord2_subtract should subtract two coordinate vectors */
+PCUT_TEST(coord2_subtract)
+{
+	gfx_coord2_t a, b;
+	gfx_coord2_t d;
+
+	a.x = 10;
+	a.y = 11;
+	b.x = 20;
+	b.y = 22;
+
+	gfx_coord2_subtract(&a, &b, &d);
+
+	PCUT_ASSERT_EQUALS(a.x - b.x, d.x);
+	PCUT_ASSERT_EQUALS(a.y - b.y, d.y);
+}
+
+/** gfx_rect_translate should translate rectangle */
+PCUT_TEST(rect_translate)
+{
+	gfx_coord2_t offs;
+	gfx_rect_t srect;
+	gfx_rect_t drect;
+
+	offs.x = 5;
+	offs.y = 6;
+
+	srect.p0.x = 10;
+	srect.p0.y = 11;
+	srect.p1.x = 20;
+	srect.p1.y = 22;
+
+	gfx_rect_translate(&offs, &srect, &drect);
+
+	PCUT_ASSERT_EQUALS(offs.x + srect.p0.x, drect.p0.x);
+	PCUT_ASSERT_EQUALS(offs.y + srect.p0.y, drect.p0.y);
+	PCUT_ASSERT_EQUALS(offs.x + srect.p1.x, drect.p1.x);
+	PCUT_ASSERT_EQUALS(offs.y + srect.p1.y, drect.p1.y);
+}
+
+PCUT_EXPORT(coord);
Index: uspace/lib/gfx/test/main.c
===================================================================
--- uspace/lib/gfx/test/main.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/test/main.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017 Jiri Svoboda
+ * 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.
+ */
+
+#include <pcut/pcut.h>
+
+PCUT_INIT;
+
+PCUT_IMPORT(bitmap);
+PCUT_IMPORT(color);
+PCUT_IMPORT(coord);
+PCUT_IMPORT(render);
+
+PCUT_MAIN();
Index: uspace/lib/gfx/test/render.c
===================================================================
--- uspace/lib/gfx/test/render.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/gfx/test/render.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+#include <gfx/color.h>
+#include <gfx/context.h>
+#include <gfx/render.h>
+#include <pcut/pcut.h>
+#include <mem.h>
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(render);
+
+static errno_t testgc_set_color(void *, gfx_color_t *);
+static errno_t testgc_fill_rect(void *, gfx_rect_t *);
+
+static gfx_context_ops_t ops = {
+	.set_color = testgc_set_color,
+	.fill_rect = testgc_fill_rect
+};
+
+/** Test graphics context data */
+typedef struct {
+	gfx_color_t *dclr;
+	gfx_rect_t *rect;
+} test_gc_t;
+
+PCUT_TEST(set_color)
+{
+	errno_t rc;
+	gfx_color_t *color;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+
+	memset(&tgc, 0, sizeof(tgc));
+
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_color_new_rgb_i16(0xffff, 0xffff, 0xffff, &color);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_set_color(gc, color);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_EQUALS(color, tgc.dclr);
+	PCUT_ASSERT_NULL(tgc.rect);
+
+	gfx_color_delete(color);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+PCUT_TEST(fill_rect)
+{
+	errno_t rc;
+	gfx_color_t *color;
+	gfx_rect_t rect;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+
+	memset(&tgc, 0, sizeof(tgc));
+
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_color_new_rgb_i16(0xffff, 0xffff, 0xffff, &color);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_set_color(gc, color);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_EQUALS(color, tgc.dclr);
+
+	rc = gfx_fill_rect(gc, &rect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_EQUALS(&rect, tgc.rect);
+
+	gfx_color_delete(color);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+static errno_t testgc_set_color(void *arg, gfx_color_t *color)
+{
+	test_gc_t *tgc = (test_gc_t *) arg;
+
+	/* Technically we should copy the data */
+	tgc->dclr = color;
+	return EOK;
+}
+
+static errno_t testgc_fill_rect(void *arg, gfx_rect_t *rect)
+{
+	test_gc_t *tgc = (test_gc_t *) arg;
+
+	/* Technically we should copy the data */
+	tgc->rect = rect;
+	return EOK;
+}
+
+PCUT_EXPORT(render);
Index: uspace/lib/guigfx/doc/doxygroups.h
===================================================================
--- uspace/lib/guigfx/doc/doxygroups.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/guigfx/doc/doxygroups.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,3 @@
+/** @addtogroup libguigfx libguigfx
+ * @ingroup libs
+ */
Index: uspace/lib/guigfx/include/guigfx/canvas.h
===================================================================
--- uspace/lib/guigfx/include/guigfx/canvas.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/guigfx/include/guigfx/canvas.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libguigfx
+ * @{
+ */
+/**
+ * @file GFX canvas backend
+ */
+
+#ifndef _GUIGFX_CANVAS_H
+#define _GUIGFX_CANVAS_H
+
+#include <canvas.h>
+#include <types/gfx/context.h>
+#include <types/gfx/ops/context.h>
+#include <types/guigfx/canvas.h>
+
+extern gfx_context_ops_t canvas_gc_ops;
+
+extern errno_t canvas_gc_create(canvas_t *, surface_t *, canvas_gc_t **);
+extern errno_t canvas_gc_delete(canvas_gc_t *);
+extern gfx_context_t *canvas_gc_get_ctx(canvas_gc_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/guigfx/include/types/guigfx/canvas.h
===================================================================
--- uspace/lib/guigfx/include/types/guigfx/canvas.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/guigfx/include/types/guigfx/canvas.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libguigfx
+ * @{
+ */
+/**
+ * @file GFX canvas backend
+ */
+
+#ifndef _GUIGFX_TYPES_CANVAS_H
+#define _GUIGFX_TYPES_CANVAS_H
+
+struct canvas_gc;
+typedef struct canvas_gc canvas_gc_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/guigfx/meson.build
===================================================================
--- uspace/lib/guigfx/meson.build	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/guigfx/meson.build	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,32 @@
+#
+# Copyright (c) 2019 Jiri Svoboda
+# 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.
+#
+
+deps = [ 'gui', 'gfx' ]
+src = files(
+	'src/canvas.c'
+)
Index: uspace/lib/guigfx/private/canvas.h
===================================================================
--- uspace/lib/guigfx/private/canvas.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/guigfx/private/canvas.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libguigfx
+ * @{
+ */
+/**
+ * @file GFX canvas backend structure
+ *
+ */
+
+#ifndef _GUIGFX_PRIVATE_CANVAS_H
+#define _GUIGFX_PRIVATE_CANVAS_H
+
+#include <canvas.h>
+#include <draw/surface.h>
+#include <gfx/bitmap.h>
+#include <gfx/context.h>
+#include <gfx/coord.h>
+#include <io/pixel.h>
+
+/** Actual structure of graphics context.
+ *
+ * This is private to libguigfx. It is not visible to clients nor backends.
+ */
+struct canvas_gc {
+	/** Base graphic context */
+	gfx_context_t *gc;
+	/** Canvas */
+	canvas_t *canvas;
+	/** Surface */
+	surface_t *surface;
+	/** Current drawing color */
+	pixel_t color;
+};
+
+/** Bitmap in canvas GC */
+typedef struct {
+	/** Containing canvas GC */
+	struct canvas_gc *cgc;
+	/** Allocation info */
+	gfx_bitmap_alloc_t alloc;
+	/** @c true if we allocated the bitmap, @c false if allocated by caller */
+	bool myalloc;
+	/** Surface */
+	surface_t *surface;
+	/** Rectangle covered by bitmap */
+	gfx_rect_t rect;
+} canvas_gc_bitmap_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/guigfx/src/canvas.c
===================================================================
--- uspace/lib/guigfx/src/canvas.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/guigfx/src/canvas.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libguigfx
+ * @{
+ */
+/**
+ * @file GFX canvas backend
+ *
+ * This implements a graphics context over a libgui canvas.
+ * This is just for experimentation purposes and its kind of backwards.
+ */
+
+#include <draw/drawctx.h>
+#include <draw/source.h>
+#include <gfx/color.h>
+#include <gfx/context.h>
+#include <gfx/render.h>
+#include <guigfx/canvas.h>
+#include <io/pixel.h>
+#include <stdlib.h>
+#include <transform.h>
+#include "../private/canvas.h"
+//#include "../../private/color.h"
+
+static errno_t canvas_gc_set_color(void *, gfx_color_t *);
+static errno_t canvas_gc_fill_rect(void *, gfx_rect_t *);
+static errno_t canvas_gc_bitmap_create(void *, gfx_bitmap_params_t *,
+    gfx_bitmap_alloc_t *, void **);
+static errno_t canvas_gc_bitmap_destroy(void *);
+static errno_t canvas_gc_bitmap_render(void *, gfx_rect_t *, gfx_coord2_t *);
+static errno_t canvas_gc_bitmap_get_alloc(void *, gfx_bitmap_alloc_t *);
+
+gfx_context_ops_t canvas_gc_ops = {
+	.set_color = canvas_gc_set_color,
+	.fill_rect = canvas_gc_fill_rect,
+	.bitmap_create = canvas_gc_bitmap_create,
+	.bitmap_destroy = canvas_gc_bitmap_destroy,
+	.bitmap_render = canvas_gc_bitmap_render,
+	.bitmap_get_alloc = canvas_gc_bitmap_get_alloc
+};
+
+/** Set color on canvas GC.
+ *
+ * Set drawing color on canvas GC.
+ *
+ * @param arg Canvas GC
+ * @param color Color
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t canvas_gc_set_color(void *arg, gfx_color_t *color)
+{
+	canvas_gc_t *cgc = (canvas_gc_t *) arg;
+	uint16_t r, g, b;
+
+	gfx_color_get_rgb_i16(color, &r, &g, &b);
+	cgc->color = PIXEL(0, r >> 8, g >> 8, b >> 8);
+	return EOK;
+}
+
+/** Fill rectangle on canvas GC.
+ *
+ * @param arg Canvas GC
+ * @param rect Rectangle
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t canvas_gc_fill_rect(void *arg, gfx_rect_t *rect)
+{
+	canvas_gc_t *cgc = (canvas_gc_t *) arg;
+	gfx_coord_t x, y;
+
+	// XXX We should handle p0.x > p1.x and p0.y > p1.y
+
+	for (y = rect->p0.y; y < rect->p1.y; y++) {
+		for (x = rect->p0.x; x < rect->p1.x; x++) {
+			surface_put_pixel(cgc->surface, x, y, cgc->color);
+		}
+	}
+
+	update_canvas(cgc->canvas, cgc->surface);
+
+	return EOK;
+}
+
+/** Create canvas GC.
+ *
+ * Create graphics context for rendering into a canvas.
+ *
+ * @param con Canvas object
+ * @param fout File to which characters are written (canvas)
+ * @param rgc Place to store pointer to new GC.
+ *
+ * @return EOK on success or an error code
+ */
+errno_t canvas_gc_create(canvas_t *canvas, surface_t *surface,
+    canvas_gc_t **rgc)
+{
+	canvas_gc_t *cgc = NULL;
+	gfx_context_t *gc = NULL;
+	errno_t rc;
+
+	cgc = calloc(1, sizeof(canvas_gc_t));
+	if (cgc == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	rc = gfx_context_new(&canvas_gc_ops, cgc, &gc);
+	if (rc != EOK)
+		goto error;
+
+	cgc->gc = gc;
+	cgc->canvas = canvas;
+	cgc->surface = surface;
+	*rgc = cgc;
+	return EOK;
+error:
+	if (cgc != NULL)
+		free(cgc);
+	gfx_context_delete(gc);
+	return rc;
+}
+
+/** Delete canvas GC.
+ *
+ * @param cgc Canvas GC
+ */
+errno_t canvas_gc_delete(canvas_gc_t *cgc)
+{
+	errno_t rc;
+
+	rc = gfx_context_delete(cgc->gc);
+	if (rc != EOK)
+		return rc;
+
+	free(cgc);
+	return EOK;
+}
+
+/** Get generic graphic context from canvas GC.
+ *
+ * @param cgc Canvas GC
+ * @return Graphic context
+ */
+gfx_context_t *canvas_gc_get_ctx(canvas_gc_t *cgc)
+{
+	return cgc->gc;
+}
+
+/** Create bitmap in canvas GC.
+ *
+ * @param arg Canvas GC
+ * @param params Bitmap params
+ * @param alloc Bitmap allocation info or @c NULL
+ * @param rbm Place to store pointer to new bitmap
+ * @return EOK on success or an error code
+ */
+errno_t canvas_gc_bitmap_create(void *arg, gfx_bitmap_params_t *params,
+    gfx_bitmap_alloc_t *alloc, void **rbm)
+{
+	canvas_gc_t *cgc = (canvas_gc_t *) arg;
+	canvas_gc_bitmap_t *cbm = NULL;
+	gfx_coord2_t dim;
+	errno_t rc;
+
+	cbm = calloc(1, sizeof(canvas_gc_bitmap_t));
+	if (cbm == NULL)
+		return ENOMEM;
+
+	gfx_coord2_subtract(&params->rect.p1, &params->rect.p0, &dim);
+	cbm->rect = params->rect;
+
+	if (alloc == NULL) {
+		cbm->surface = surface_create(dim.x, dim.y, NULL, 0);
+		if (cbm->surface == NULL) {
+			rc = ENOMEM;
+			goto error;
+		}
+
+		cbm->alloc.pitch = dim.x * sizeof(uint32_t);
+		cbm->alloc.off0 = 0;
+		cbm->alloc.pixels = surface_direct_access(cbm->surface);
+		cbm->myalloc = true;
+	} else {
+		cbm->surface = surface_create(dim.x, dim.y, alloc->pixels, 0);
+		if (cbm->surface == NULL) {
+			rc = ENOMEM;
+			goto error;
+		}
+
+		cbm->alloc = *alloc;
+	}
+
+	cbm->cgc = cgc;
+	*rbm = (void *)cbm;
+	return EOK;
+error:
+	if (cbm != NULL)
+		free(cbm);
+	return rc;
+}
+
+/** Destroy bitmap in canvas GC.
+ *
+ * @param bm Bitmap
+ * @return EOK on success or an error code
+ */
+static errno_t canvas_gc_bitmap_destroy(void *bm)
+{
+	canvas_gc_bitmap_t *cbm = (canvas_gc_bitmap_t *)bm;
+	if (cbm->myalloc)
+		surface_destroy(cbm->surface);
+	// XXX if !cbm->myalloc, surface is leaked - no way to destroy it
+	// without destroying the pixel buffer
+	free(cbm);
+	return EOK;
+}
+
+/** Render bitmap in canvas GC.
+ *
+ * @param bm Bitmap
+ * @param srect0 Source rectangle or @c NULL
+ * @param offs0 Offset or @c NULL
+ * @return EOK on success or an error code
+ */
+static errno_t canvas_gc_bitmap_render(void *bm, gfx_rect_t *srect0,
+    gfx_coord2_t *offs0)
+{
+	canvas_gc_bitmap_t *cbm = (canvas_gc_bitmap_t *)bm;
+	gfx_rect_t srect;
+	gfx_rect_t drect;
+	gfx_coord2_t offs;
+	gfx_coord2_t dim;
+
+	if (srect0 != NULL)
+		srect = *srect0;
+	else
+		srect = cbm->rect;
+
+	if (offs0 != NULL) {
+		offs = *offs0;
+	} else {
+		offs.x = 0;
+		offs.y = 0;
+	}
+
+	/* Destination rectangle */
+	gfx_rect_translate(&offs, &srect, &drect);
+
+	gfx_coord2_subtract(&drect.p1, &drect.p0, &dim);
+
+	transform_t transform;
+	transform_identity(&transform);
+	transform_translate(&transform, offs.x - cbm->rect.p0.x,
+	    offs.y - cbm->rect.p0.y);
+
+	source_t source;
+	source_init(&source);
+	source_set_transform(&source, transform);
+	source_set_texture(&source, cbm->surface,
+	    PIXELMAP_EXTEND_TRANSPARENT_BLACK);
+
+	drawctx_t drawctx;
+	drawctx_init(&drawctx, cbm->cgc->surface);
+
+	drawctx_set_source(&drawctx, &source);
+	drawctx_transfer(&drawctx, drect.p0.x, drect.p0.y, dim.x, dim.y);
+
+	update_canvas(cbm->cgc->canvas, cbm->cgc->surface);
+	return EOK;
+}
+
+/** Get allocation info for bitmap in canvas GC.
+ *
+ * @param bm Bitmap
+ * @param alloc Place to store allocation info
+ * @return EOK on success or an error code
+ */
+static errno_t canvas_gc_bitmap_get_alloc(void *bm, gfx_bitmap_alloc_t *alloc)
+{
+	canvas_gc_bitmap_t *cbm = (canvas_gc_bitmap_t *)bm;
+	*alloc = cbm->alloc;
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/ipcgfx/doc/doxygroups.h
===================================================================
--- uspace/lib/ipcgfx/doc/doxygroups.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/ipcgfx/doc/doxygroups.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,3 @@
+/** @addtogroup libcongfx libcongfx
+ * @ingroup libs
+ */
Index: uspace/lib/ipcgfx/include/ipcgfx/client.h
===================================================================
--- uspace/lib/ipcgfx/include/ipcgfx/client.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/ipcgfx/include/ipcgfx/client.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libipcgfx
+ * @{
+ */
+/**
+ * @file GFX IPC backend
+ */
+
+#ifndef _IPCGFX_CLIENT_H
+#define _IPCGFX_CLIENT_H
+
+#include <async.h>
+#include <types/ipcgfx/client.h>
+#include <types/gfx/context.h>
+#include <types/gfx/ops/context.h>
+
+extern gfx_context_ops_t ipc_gc_ops;
+
+extern errno_t ipc_gc_create(async_sess_t *, ipc_gc_t **);
+extern errno_t ipc_gc_delete(ipc_gc_t *);
+extern gfx_context_t *ipc_gc_get_ctx(ipc_gc_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ipcgfx/include/ipcgfx/ipc/gc.h
===================================================================
--- uspace/lib/ipcgfx/include/ipcgfx/ipc/gc.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/ipcgfx/include/ipcgfx/ipc/gc.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libipcgfx
+ * @{
+ */
+/** @file
+ */
+
+#ifndef _IPCGFX_IPC_GC_H_
+#define _IPCGFX_IPC_GC_H_
+
+#include <ipc/common.h>
+
+typedef enum {
+	GC_SET_RGB_COLOR = IPC_FIRST_USER_METHOD,
+	GC_FILL_RECT,
+	GC_BITMAP_CREATE,
+	GC_BITMAP_DESTROY,
+	GC_BITMAP_RENDER,
+	GC_BITMAP_GET_ALLOC
+} gc_request_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ipcgfx/include/ipcgfx/server.h
===================================================================
--- uspace/lib/ipcgfx/include/ipcgfx/server.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/ipcgfx/include/ipcgfx/server.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libipcgfx
+ * @{
+ */
+/**
+ * @file GFX IPC server
+ */
+
+#ifndef _IPCGFX_SERVER_H
+#define _IPCGFX_SERVER_H
+
+#include <async.h>
+#include <errno.h>
+#include <gfx/context.h>
+
+extern errno_t gc_conn(ipc_call_t *icall, gfx_context_t *gc);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ipcgfx/include/types/ipcgfx/client.h
===================================================================
--- uspace/lib/ipcgfx/include/types/ipcgfx/client.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/ipcgfx/include/types/ipcgfx/client.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libipcgfx
+ * @{
+ */
+/**
+ * @file GFX IPC backend
+ */
+
+#ifndef _IPCGFX_TYPES_CLIENT_H
+#define _IPCGFX_TYPES_CLIENT_H
+
+struct ipc_gc;
+typedef struct ipc_gc ipc_gc_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ipcgfx/meson.build
===================================================================
--- uspace/lib/ipcgfx/meson.build	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/ipcgfx/meson.build	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,38 @@
+#
+# Copyright (c) 2019 Jiri Svoboda
+# 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.
+#
+
+deps = [ 'gfx' ]
+src = files(
+	'src/client.c',
+	'src/server.c'
+)
+
+test_src = files(
+	'test/ipcgfx.c',
+	'test/main.c',
+)
Index: uspace/lib/ipcgfx/private/client.h
===================================================================
--- uspace/lib/ipcgfx/private/client.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/ipcgfx/private/client.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libipcgfx
+ * @{
+ */
+/**
+ * @file GFX console backend structure
+ *
+ */
+
+#ifndef _IPCGFX_PRIVATE_CLIENT_H
+#define _IPCGFX_PRIVATE_CLIENT_H
+
+#include <async.h>
+#include <gfx/context.h>
+
+/** Actual structure of graphics context.
+ *
+ * This is private to libcongfx. It is not visible to clients nor backends.
+ */
+struct ipc_gc {
+	/** Base graphic context */
+	gfx_context_t *gc;
+	/** Session with GFX server */
+	async_sess_t *sess;
+};
+
+/** Bitmap in IPC GC */
+typedef struct {
+	/** Containing IPC GC */
+	struct ipc_gc *ipcgc;
+	/** Allocation info */
+	gfx_bitmap_alloc_t alloc;
+	/** @c true if we allocated the bitmap, @c false if allocated by caller */
+	bool myalloc;
+	/** Rectangle covered by bitmap */
+	gfx_rect_t rect;
+	/** Server bitmap ID */
+	sysarg_t bmp_id;
+} ipc_gc_bitmap_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ipcgfx/private/server.h
===================================================================
--- uspace/lib/ipcgfx/private/server.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/ipcgfx/private/server.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libipcgfx
+ * @{
+ */
+/**
+ * @file IPC GFX backend structure
+ *
+ */
+
+#ifndef _IPCGFX_PRIVATE_SERVER_H
+#define _IPCGFX_PRIVATE_SERVER_H
+
+#include <adt/list.h>
+#include <async.h>
+#include <gfx/bitmap.h>
+#include <gfx/context.h>
+#include <gfx/coord.h>
+
+/** Server-side of IPC GC connection.
+ */
+typedef struct {
+	/** Graphics context to serve */
+	gfx_context_t *gc;
+	/** List of bitmaps (ipc_gc_srv_bitmap_t) */
+	list_t bitmaps;
+	/** Next bitmap ID to allocate */
+	sysarg_t next_bmp_id;
+} ipc_gc_srv_t;
+
+/** Bitmap in canvas GC */
+typedef struct {
+	/** Containing canvas GC */
+	ipc_gc_srv_t *srvgc;
+	/** Link to srvgc->bitmaps */
+	link_t lbitmaps;
+	/** Backing bitmap */
+	gfx_bitmap_t *bmp;
+	/** Bitmap ID */
+	sysarg_t bmp_id;
+} ipc_gc_srv_bitmap_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ipcgfx/src/client.c
===================================================================
--- uspace/lib/ipcgfx/src/client.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/ipcgfx/src/client.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libipcgfx
+ * @{
+ */
+/**
+ * @file GFX IPC backend
+ *
+ * This implements a graphics context via HelenOS IPC.
+ */
+
+#include <as.h>
+#include <ipcgfx/client.h>
+#include <ipcgfx/ipc/gc.h>
+#include <gfx/color.h>
+#include <gfx/coord.h>
+#include <gfx/context.h>
+#include <stdlib.h>
+#include "../private/client.h"
+
+static errno_t ipc_gc_set_color(void *, gfx_color_t *);
+static errno_t ipc_gc_fill_rect(void *, gfx_rect_t *);
+static errno_t ipc_gc_bitmap_create(void *, gfx_bitmap_params_t *,
+    gfx_bitmap_alloc_t *, void **);
+static errno_t ipc_gc_bitmap_destroy(void *);
+static errno_t ipc_gc_bitmap_render(void *, gfx_rect_t *, gfx_coord2_t *);
+static errno_t ipc_gc_bitmap_get_alloc(void *, gfx_bitmap_alloc_t *);
+
+gfx_context_ops_t ipc_gc_ops = {
+	.set_color = ipc_gc_set_color,
+	.fill_rect = ipc_gc_fill_rect,
+	.bitmap_create = ipc_gc_bitmap_create,
+	.bitmap_destroy = ipc_gc_bitmap_destroy,
+	.bitmap_render = ipc_gc_bitmap_render,
+	.bitmap_get_alloc = ipc_gc_bitmap_get_alloc
+};
+
+#include <stdio.h>
+
+/** Set color on IPC GC.
+ *
+ * Set drawing color on IPC GC.
+ *
+ * @param arg IPC GC
+ * @param color Color
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t ipc_gc_set_color(void *arg, gfx_color_t *color)
+{
+	ipc_gc_t *ipcgc = (ipc_gc_t *) arg;
+	async_exch_t *exch;
+	uint16_t r, g, b;
+	errno_t rc;
+
+	printf("ipc_gc_set_color\n");
+	gfx_color_get_rgb_i16(color, &r, &g, &b);
+
+	exch = async_exchange_begin(ipcgc->sess);
+	rc = async_req_3_0(exch, GC_SET_RGB_COLOR, r, g, b);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+/** Fill rectangle on IPC GC.
+ *
+ * @param arg IPC GC
+ * @param rect Rectangle
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t ipc_gc_fill_rect(void *arg, gfx_rect_t *rect)
+{
+	ipc_gc_t *ipcgc = (ipc_gc_t *) arg;
+	async_exch_t *exch;
+	errno_t rc;
+
+	printf("ipc_gc_fill_rect\n");
+	exch = async_exchange_begin(ipcgc->sess);
+	rc = async_req_4_0(exch, GC_FILL_RECT, rect->p0.x, rect->p0.y,
+	    rect->p1.x, rect->p1.y);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+/** Create bitmap in IPC GC.
+ *
+ * @param arg IPC GC
+ * @param params Bitmap params
+ * @param alloc Bitmap allocation info or @c NULL
+ * @param rbm Place to store pointer to new bitmap
+ * @return EOK on success or an error code
+ */
+errno_t ipc_gc_bitmap_create(void *arg, gfx_bitmap_params_t *params,
+    gfx_bitmap_alloc_t *alloc, void **rbm)
+{
+	ipc_gc_t *ipcgc = (ipc_gc_t *) arg;
+	ipc_gc_bitmap_t *ipcbm = NULL;
+	gfx_coord2_t dim;
+	async_exch_t *exch = NULL;
+	ipc_call_t answer;
+	aid_t req;
+	errno_t rc;
+
+	ipcbm = calloc(1, sizeof(ipc_gc_bitmap_t));
+	if (ipcbm == NULL)
+		return ENOMEM;
+
+	gfx_coord2_subtract(&params->rect.p1, &params->rect.p0, &dim);
+	ipcbm->rect = params->rect;
+
+	if (alloc == NULL) {
+		ipcbm->alloc.pitch = dim.x * sizeof(uint32_t);
+		ipcbm->alloc.off0 = 0;
+		ipcbm->alloc.pixels = as_area_create(AS_AREA_ANY,
+		    dim.x * dim.y * sizeof(uint32_t), AS_AREA_READ |
+		    AS_AREA_WRITE | AS_AREA_CACHEABLE, AS_AREA_UNPAGED);
+		if (ipcbm->alloc.pixels == NULL) {
+			rc = ENOMEM;
+			goto error;
+		}
+
+		ipcbm->myalloc = true;
+	} else {
+		/*
+		 * XXX We could allow this if the pixels point to a shareable
+		 * area or we could do a copy of the data when rendering
+		 */
+		rc = ENOTSUP;
+		goto error;
+	}
+
+	exch = async_exchange_begin(ipcgc->sess);
+	req = async_send_0(exch, GC_BITMAP_CREATE, &answer);
+	rc = async_data_write_start(exch, params, sizeof (gfx_bitmap_params_t));
+	if (rc != EOK) {
+		async_forget(req);
+		goto error;
+	}
+
+	rc = async_share_out_start(exch, ipcbm->alloc.pixels,
+	    AS_AREA_READ | AS_AREA_CACHEABLE);
+	if (rc != EOK) {
+		async_forget(req);
+		goto error;
+	}
+	async_exchange_end(exch);
+	exch = NULL;
+
+	async_wait_for(req, &rc);
+	if (rc != EOK)
+		goto error;
+
+	ipcbm->ipcgc = ipcgc;
+	ipcbm->bmp_id = ipc_get_arg1(&answer);
+	*rbm = (void *)ipcbm;
+	return EOK;
+error:
+	if (exch != NULL)
+		async_exchange_end(exch);
+	if (ipcbm != NULL) {
+		if (ipcbm->alloc.pixels != NULL)
+			as_area_destroy(ipcbm->alloc.pixels);
+		free(ipcbm);
+	}
+	return rc;
+}
+
+/** Destroy bitmap in IPC GC.
+ *
+ * @param bm Bitmap
+ * @return EOK on success or an error code
+ */
+static errno_t ipc_gc_bitmap_destroy(void *bm)
+{
+	ipc_gc_bitmap_t *ipcbm = (ipc_gc_bitmap_t *)bm;
+	async_exch_t *exch;
+	errno_t rc;
+
+	printf("ipc_gc_bitmap_destroy\n");
+
+	exch = async_exchange_begin(ipcbm->ipcgc->sess);
+	rc = async_req_1_0(exch, GC_BITMAP_DESTROY, ipcbm->bmp_id);
+	async_exchange_end(exch);
+
+	if (rc != EOK)
+		return rc;
+
+	if (ipcbm->myalloc)
+		as_area_destroy(ipcbm->alloc.pixels);
+	free(ipcbm);
+	return EOK;
+}
+
+/** Render bitmap in IPC GC.
+ *
+ * @param bm Bitmap
+ * @param srect0 Source rectangle or @c NULL
+ * @param offs0 Offset or @c NULL
+ * @return EOK on success or an error code
+ */
+static errno_t ipc_gc_bitmap_render(void *bm, gfx_rect_t *srect0,
+    gfx_coord2_t *offs0)
+{
+	ipc_gc_bitmap_t *ipcbm = (ipc_gc_bitmap_t *)bm;
+	gfx_rect_t srect;
+	gfx_rect_t drect;
+	gfx_coord2_t offs;
+	async_exch_t *exch = NULL;
+	ipc_call_t answer;
+	aid_t req;
+	errno_t rc;
+
+	if (srect0 != NULL)
+		srect = *srect0;
+	else
+		srect = ipcbm->rect;
+
+	if (offs0 != NULL) {
+		offs = *offs0;
+	} else {
+		offs.x = 0;
+		offs.y = 0;
+	}
+
+	/* Destination rectangle */
+	gfx_rect_translate(&offs, &srect, &drect);
+
+	exch = async_exchange_begin(ipcbm->ipcgc->sess);
+	req = async_send_3(exch, GC_BITMAP_RENDER, ipcbm->bmp_id, offs.x,
+	    offs.y, &answer);
+
+	rc = async_data_write_start(exch, &srect, sizeof (gfx_rect_t));
+	if (rc != EOK) {
+		async_forget(req);
+		goto error;
+	}
+
+	async_exchange_end(exch);
+	exch = NULL;
+
+	async_wait_for(req, &rc);
+	if (rc != EOK)
+		goto error;
+
+	return EOK;
+error:
+	if (exch != NULL)
+		async_exchange_end(exch);
+	return rc;
+}
+
+/** Get allocation info for bitmap in IPC GC.
+ *
+ * @param bm Bitmap
+ * @param alloc Place to store allocation info
+ * @return EOK on success or an error code
+ */
+static errno_t ipc_gc_bitmap_get_alloc(void *bm, gfx_bitmap_alloc_t *alloc)
+{
+	ipc_gc_bitmap_t *ipcbm = (ipc_gc_bitmap_t *)bm;
+	*alloc = ipcbm->alloc;
+	return EOK;
+}
+
+/** Create IPC GC.
+ *
+ * Create graphics context for rendering via IPC.
+ *
+ * @param sess Async session
+ * @param rgc Place to store pointer to new GC.
+ *
+ * @return EOK on success or an error code
+ */
+errno_t ipc_gc_create(async_sess_t *sess, ipc_gc_t **rgc)
+{
+	ipc_gc_t *ipcgc = NULL;
+	gfx_context_t *gc = NULL;
+	errno_t rc;
+
+	ipcgc = calloc(1, sizeof(ipc_gc_t));
+	if (ipcgc == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	rc = gfx_context_new(&ipc_gc_ops, ipcgc, &gc);
+	if (rc != EOK)
+		goto error;
+
+	ipcgc->gc = gc;
+	ipcgc->sess = sess;
+	*rgc = ipcgc;
+	return EOK;
+error:
+	if (ipcgc != NULL)
+		free(ipcgc);
+	gfx_context_delete(gc);
+	return rc;
+}
+
+/** Delete IPC GC.
+ *
+ * @param ipcgc IPC GC
+ */
+errno_t ipc_gc_delete(ipc_gc_t *ipcgc)
+{
+	errno_t rc;
+
+	rc = gfx_context_delete(ipcgc->gc);
+	if (rc != EOK)
+		return rc;
+
+	free(ipcgc);
+	return EOK;
+}
+
+/** Get generic graphic context from IPC GC.
+ *
+ * @param ipcgc IPC GC
+ * @return Graphic context
+ */
+gfx_context_t *ipc_gc_get_ctx(ipc_gc_t *ipcgc)
+{
+	return ipcgc->gc;
+}
+
+/** @}
+ */
Index: uspace/lib/ipcgfx/src/server.c
===================================================================
--- uspace/lib/ipcgfx/src/server.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/ipcgfx/src/server.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup libipcgfx
+ * @{
+ */
+/**
+ * @file GFX IPC server
+ *
+ * Serve a graphics context via HelenOS IPC.
+ */
+
+#include <as.h>
+#include <errno.h>
+#include <gfx/bitmap.h>
+#include <gfx/color.h>
+#include <gfx/render.h>
+#include <ipc/bd.h>
+#include <ipcgfx/ipc/gc.h>
+#include <ipcgfx/server.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "../private/server.h"
+
+static ipc_gc_srv_bitmap_t *gc_bitmap_lookup(ipc_gc_srv_t *, sysarg_t);
+
+static void gc_set_rgb_color_srv(ipc_gc_srv_t *srvgc, ipc_call_t *call)
+{
+	uint16_t r, g, b;
+	gfx_color_t *color;
+	errno_t rc;
+
+	r = ipc_get_arg1(call);
+	g = ipc_get_arg2(call);
+	b = ipc_get_arg3(call);
+
+	rc = gfx_color_new_rgb_i16(r, g, b, &color);
+	if (rc != EOK) {
+		async_answer_0(call, ENOMEM);
+		return;
+	}
+
+	rc = gfx_set_color(srvgc->gc, color);
+	async_answer_0(call, rc);
+	printf("done with rgb_color_srv\n");
+}
+
+static void gc_fill_rect_srv(ipc_gc_srv_t *srvgc, ipc_call_t *call)
+{
+	gfx_rect_t rect;
+	errno_t rc;
+
+	rect.p0.x = ipc_get_arg1(call);
+	rect.p0.y = ipc_get_arg2(call);
+	rect.p1.x = ipc_get_arg3(call);
+	rect.p1.y = ipc_get_arg4(call);
+
+	rc = gfx_fill_rect(srvgc->gc, &rect);
+	async_answer_0(call, rc);
+}
+
+static void gc_bitmap_create_srv(ipc_gc_srv_t *srvgc, ipc_call_t *icall)
+{
+	gfx_bitmap_params_t params;
+	gfx_bitmap_alloc_t alloc;
+	gfx_bitmap_t *bitmap;
+	gfx_coord2_t dim;
+	ipc_gc_srv_bitmap_t *srvbmp = NULL;
+	ipc_call_t call;
+	size_t size;
+	unsigned int flags;
+	void *pixels;
+	errno_t rc;
+
+	if (!async_data_write_receive(&call, &size)) {
+		async_answer_0(&call, EREFUSED);
+		async_answer_0(icall, EREFUSED);
+		return;
+	}
+
+	if (size != sizeof(gfx_bitmap_params_t)) {
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	rc = async_data_write_finalize(&call, &params, size);
+	if (rc != EOK) {
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	/* Bitmap dimensions */
+	gfx_coord2_subtract(&params.rect.p1, &params.rect.p0, &dim);
+
+	if (!async_share_out_receive(&call, &size, &flags)) {
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	/* Check size */
+	if (size != PAGES2SIZE(SIZE2PAGES(dim.x * dim.y * sizeof(uint32_t)))) {
+		printf("size=%zu, expected=%zu\n", size, dim.x * dim.y * sizeof(uint32_t));
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	rc = async_share_out_finalize(&call, &pixels);
+	if (rc != EOK || pixels == AS_MAP_FAILED) {
+		async_answer_0(icall, ENOMEM);
+		return;
+	}
+
+	alloc.pitch = dim.x * sizeof(uint32_t);
+	alloc.off0 = 0;
+	alloc.pixels = pixels;
+
+	srvbmp = calloc(1, sizeof(ipc_gc_srv_bitmap_t));
+	if (srvbmp == NULL) {
+		as_area_destroy(pixels);
+		async_answer_0(icall, ENOMEM);
+		return;
+	}
+
+	rc = gfx_bitmap_create(srvgc->gc, &params, &alloc, &bitmap);
+	if (rc != EOK) {
+		free(srvbmp);
+		as_area_destroy(pixels);
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	srvbmp->srvgc = srvgc;
+	list_append(&srvbmp->lbitmaps, &srvgc->bitmaps);
+	srvbmp->bmp = bitmap;
+	srvbmp->bmp_id = srvgc->next_bmp_id++;
+	printf("gc_bitmap_create_srv: storing bmp_id=%lu\n", srvbmp->bmp_id);
+
+	async_answer_1(icall, EOK, srvbmp->bmp_id);
+}
+
+static void gc_bitmap_destroy_srv(ipc_gc_srv_t *srvgc, ipc_call_t *call)
+{
+	sysarg_t bmp_id;
+	ipc_gc_srv_bitmap_t *bitmap;
+	errno_t rc;
+
+	bmp_id = ipc_get_arg1(call);
+
+	bitmap = gc_bitmap_lookup(srvgc, bmp_id);
+	if (bitmap == NULL) {
+		async_answer_0(call, ENOENT);
+		return;
+	}
+
+	rc = gfx_bitmap_destroy(bitmap->bmp);
+	if (rc != EOK) {
+		async_answer_0(call, rc);
+		return;
+	}
+
+	list_remove(&bitmap->lbitmaps);
+	free(bitmap);
+
+	async_answer_0(call, rc);
+}
+
+static void gc_bitmap_render_srv(ipc_gc_srv_t *srvgc, ipc_call_t *icall)
+{
+	ipc_gc_srv_bitmap_t *bitmap;
+	sysarg_t bmp_id;
+	gfx_rect_t srect;
+	gfx_coord2_t offs;
+	ipc_call_t call;
+	size_t size;
+	errno_t rc;
+
+	if (!async_data_write_receive(&call, &size)) {
+		async_answer_0(&call, EREFUSED);
+		async_answer_0(icall, EREFUSED);
+		return;
+	}
+
+	if (size != sizeof(gfx_rect_t)) {
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	rc = async_data_write_finalize(&call, &srect, size);
+	if (rc != EOK) {
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	bmp_id = ipc_get_arg1(icall);
+	offs.x = ipc_get_arg2(icall);
+	offs.y = ipc_get_arg3(icall);
+
+	bitmap = gc_bitmap_lookup(srvgc, bmp_id);
+	if (bitmap == NULL) {
+		async_answer_0(icall, ENOENT);
+		return;
+	}
+
+	rc = gfx_bitmap_render(bitmap->bmp, &srect, &offs);
+	async_answer_0(icall, rc);
+}
+
+errno_t gc_conn(ipc_call_t *icall, gfx_context_t *gc)
+{
+	ipc_gc_srv_t srvgc;
+
+	/* Accept the connection */
+	async_accept_0(icall);
+
+	printf("gc_conn: accepted connection\n");
+	srvgc.gc = gc;
+	list_initialize(&srvgc.bitmaps);
+	srvgc.next_bmp_id = 1;
+
+	while (true) {
+		ipc_call_t call;
+		async_get_call(&call);
+		sysarg_t method = ipc_get_imethod(&call);
+
+		if (!method) {
+			/* The other side has hung up */
+			async_answer_0(&call, EOK);
+			break;
+		}
+
+		switch (method) {
+		case GC_SET_RGB_COLOR:
+			printf("gc_conn: set_rgb_color\n");
+			gc_set_rgb_color_srv(&srvgc, &call);
+			printf("gc_conn: done set_rgb_color\n");
+			break;
+		case GC_FILL_RECT:
+			printf("gc_conn: fill_rect_srv\n");
+			gc_fill_rect_srv(&srvgc, &call);
+			printf("gc_conn: done fill_rect_srv\n");
+			break;
+		case GC_BITMAP_CREATE:
+			printf("gc_conn: bitmap_create_srv\n");
+			gc_bitmap_create_srv(&srvgc, &call);
+			printf("gc_conn: done bitmap_create_srv\n");
+			break;
+		case GC_BITMAP_DESTROY:
+			printf("gc_conn: bitmap_destroy_srv\n");
+			gc_bitmap_destroy_srv(&srvgc, &call);
+			printf("gc_conn: done bitmap_destroy_srv\n");
+			break;
+		case GC_BITMAP_RENDER:
+			printf("gc_conn: bitmap_render_srv\n");
+			gc_bitmap_render_srv(&srvgc, &call);
+			printf("gc_conn: done bitmap_render_srv\n");
+			break;
+		default:
+			printf("gc_conn: answer einval\n");
+			async_answer_0(&call, EINVAL);
+			printf("gc_conn: done answer einval\n");
+		}
+	}
+
+	return EOK;
+}
+
+static ipc_gc_srv_bitmap_t *gc_bitmap_lookup(ipc_gc_srv_t *srvgc,
+    sysarg_t bmp_id)
+{
+	link_t *link;
+	ipc_gc_srv_bitmap_t *bmp;
+
+	link = list_first(&srvgc->bitmaps);
+	while (link != NULL) {
+		bmp = list_get_instance(link, ipc_gc_srv_bitmap_t, lbitmaps);
+		printf("gc_bitmap_lookup: %lu==%lu?\n", bmp->bmp_id, bmp_id);
+		if (bmp->bmp_id == bmp_id)
+			return bmp;
+		link = list_next(link, &srvgc->bitmaps);
+	}
+
+	return NULL;
+}
+
+/** @}
+ */
Index: uspace/lib/ipcgfx/test/ipcgfx.c
===================================================================
--- uspace/lib/ipcgfx/test/ipcgfx.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/ipcgfx/test/ipcgfx.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,793 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+#include <async.h>
+#include <errno.h>
+#include <gfx/bitmap.h>
+#include <gfx/color.h>
+#include <gfx/coord.h>
+#include <gfx/context.h>
+#include <gfx/render.h>
+#include <ipcgfx/client.h>
+#include <ipcgfx/server.h>
+#include <loc.h>
+#include <pcut/pcut.h>
+#include <stdint.h>
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(ipcgfx);
+
+static const char *test_ipcgfx_server = "test-ipcgfx";
+static const char *test_ipcgfx_svc = "test/ipcgfx";
+
+static void test_ipcgc_conn(ipc_call_t *, void *);
+
+static errno_t test_gc_set_color(void *, gfx_color_t *);
+static errno_t test_gc_fill_rect(void *, gfx_rect_t *);
+static errno_t test_gc_bitmap_create(void *, gfx_bitmap_params_t *,
+    gfx_bitmap_alloc_t *, void **);
+static errno_t test_gc_bitmap_destroy(void *);
+static errno_t test_gc_bitmap_render(void *, gfx_rect_t *, gfx_coord2_t *);
+static errno_t test_gc_bitmap_get_alloc(void *, gfx_bitmap_alloc_t *);
+
+static gfx_context_ops_t test_gc_ops = {
+	.set_color = test_gc_set_color,
+	.fill_rect = test_gc_fill_rect,
+	.bitmap_create = test_gc_bitmap_create,
+	.bitmap_destroy = test_gc_bitmap_destroy,
+	.bitmap_render = test_gc_bitmap_render,
+	.bitmap_get_alloc = test_gc_bitmap_get_alloc
+};
+
+/** Describes to the server how to respond to our request and pass tracking
+ * data back to the client.
+ */
+typedef struct {
+	errno_t rc;
+
+	bool set_color_called;
+	uint16_t set_color_r;
+	uint16_t set_color_g;
+	uint16_t set_color_b;
+
+	bool fill_rect_called;
+	gfx_rect_t fill_rect_rect;
+
+	bool bitmap_create_called;
+	gfx_bitmap_params_t bitmap_create_params;
+	gfx_bitmap_alloc_t bitmap_create_alloc;
+
+	bool bitmap_destroy_called;
+
+	bool bitmap_render_called;
+	gfx_rect_t bitmap_render_srect;
+	gfx_coord2_t bitmap_render_offs;
+
+	bool bitmap_get_alloc_called;
+} test_response_t;
+
+/** Bitmap in test GC */
+typedef struct {
+	test_response_t *resp;
+} test_bitmap_t;
+
+/** gfx_set_color with server returning failure */
+PCUT_TEST(set_color_failure)
+{
+	errno_t rc;
+	service_id_t sid;
+	test_response_t resp;
+	gfx_context_t *gc;
+	gfx_color_t *color;
+	async_sess_t *sess;
+	ipc_gc_t *ipcgc;
+
+	async_set_fallback_port_handler(test_ipcgc_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_ipcgfx_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_ipcgfx_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	sess = loc_service_connect(sid, INTERFACE_GC, 0);
+	PCUT_ASSERT_NOT_NULL(sess);
+
+	rc = ipc_gc_create(sess, &ipcgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = ipc_gc_get_ctx(ipcgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	rc = gfx_color_new_rgb_i16(1, 2, 3, &color);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	resp.rc = ENOMEM;
+	resp.set_color_called = false;
+	rc = gfx_set_color(gc, color);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+	PCUT_ASSERT_TRUE(resp.set_color_called);
+	PCUT_ASSERT_EQUALS(1, resp.set_color_r);
+	PCUT_ASSERT_EQUALS(2, resp.set_color_g);
+	PCUT_ASSERT_EQUALS(3, resp.set_color_b);
+
+	gfx_color_delete(color);
+
+	ipc_gc_delete(ipcgc);
+	async_hangup(sess);
+
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_set_color with server returning success */
+PCUT_TEST(set_color_success)
+{
+	errno_t rc;
+	service_id_t sid;
+	test_response_t resp;
+	gfx_context_t *gc;
+	gfx_color_t *color;
+	async_sess_t *sess;
+	ipc_gc_t *ipcgc;
+
+	async_set_fallback_port_handler(test_ipcgc_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_ipcgfx_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_ipcgfx_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	sess = loc_service_connect(sid, INTERFACE_GC, 0);
+	PCUT_ASSERT_NOT_NULL(sess);
+
+	rc = ipc_gc_create(sess, &ipcgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = ipc_gc_get_ctx(ipcgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	rc = gfx_color_new_rgb_i16(1, 2, 3, &color);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	resp.rc = EOK;
+	resp.set_color_called = false;
+	rc = gfx_set_color(gc, color);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(resp.set_color_called);
+	PCUT_ASSERT_EQUALS(1, resp.set_color_r);
+	PCUT_ASSERT_EQUALS(2, resp.set_color_g);
+	PCUT_ASSERT_EQUALS(3, resp.set_color_b);
+
+	gfx_color_delete(color);
+
+	ipc_gc_delete(ipcgc);
+	async_hangup(sess);
+
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_fill_rect with server returning failure */
+PCUT_TEST(fill_rect_failure)
+{
+	errno_t rc;
+	service_id_t sid;
+	test_response_t resp;
+	gfx_context_t *gc;
+	gfx_rect_t rect;
+	async_sess_t *sess;
+	ipc_gc_t *ipcgc;
+
+	async_set_fallback_port_handler(test_ipcgc_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_ipcgfx_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_ipcgfx_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	sess = loc_service_connect(sid, INTERFACE_GC, 0);
+	PCUT_ASSERT_NOT_NULL(sess);
+
+	rc = ipc_gc_create(sess, &ipcgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = ipc_gc_get_ctx(ipcgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	resp.rc = ENOMEM;
+	resp.set_color_called = false;
+	rect.p0.x = 1;
+	rect.p0.y = 2;
+	rect.p1.x = 3;
+	rect.p1.y = 4;
+	rc = gfx_fill_rect(gc, &rect);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+	PCUT_ASSERT_TRUE(resp.fill_rect_called);
+	PCUT_ASSERT_EQUALS(rect.p0.x, resp.fill_rect_rect.p0.x);
+	PCUT_ASSERT_EQUALS(rect.p0.y, resp.fill_rect_rect.p0.y);
+	PCUT_ASSERT_EQUALS(rect.p1.x, resp.fill_rect_rect.p1.x);
+	PCUT_ASSERT_EQUALS(rect.p1.y, resp.fill_rect_rect.p1.y);
+
+	ipc_gc_delete(ipcgc);
+	async_hangup(sess);
+
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_fill_rect with server returning success */
+PCUT_TEST(fill_rect_success)
+{
+	errno_t rc;
+	service_id_t sid;
+	test_response_t resp;
+	gfx_context_t *gc;
+	gfx_rect_t rect;
+	async_sess_t *sess;
+	ipc_gc_t *ipcgc;
+
+	async_set_fallback_port_handler(test_ipcgc_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_ipcgfx_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_ipcgfx_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	sess = loc_service_connect(sid, INTERFACE_GC, 0);
+	PCUT_ASSERT_NOT_NULL(sess);
+
+	rc = ipc_gc_create(sess, &ipcgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = ipc_gc_get_ctx(ipcgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	resp.rc = EOK;
+	resp.set_color_called = false;
+	rect.p0.x = 1;
+	rect.p0.y = 2;
+	rect.p1.x = 3;
+	rect.p1.y = 4;
+	rc = gfx_fill_rect(gc, &rect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(resp.fill_rect_called);
+	PCUT_ASSERT_EQUALS(rect.p0.x, resp.fill_rect_rect.p0.x);
+	PCUT_ASSERT_EQUALS(rect.p0.y, resp.fill_rect_rect.p0.y);
+	PCUT_ASSERT_EQUALS(rect.p1.x, resp.fill_rect_rect.p1.x);
+	PCUT_ASSERT_EQUALS(rect.p1.y, resp.fill_rect_rect.p1.y);
+
+	ipc_gc_delete(ipcgc);
+	async_hangup(sess);
+
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_bitmap_create with server returning failure */
+PCUT_TEST(bitmap_create_failure)
+{
+	errno_t rc;
+	service_id_t sid;
+	test_response_t resp;
+	gfx_context_t *gc;
+	gfx_bitmap_params_t params;
+	gfx_bitmap_t *bitmap;
+	async_sess_t *sess;
+	ipc_gc_t *ipcgc;
+
+	async_set_fallback_port_handler(test_ipcgc_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_ipcgfx_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_ipcgfx_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	sess = loc_service_connect(sid, INTERFACE_GC, 0);
+	PCUT_ASSERT_NOT_NULL(sess);
+
+	rc = ipc_gc_create(sess, &ipcgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = ipc_gc_get_ctx(ipcgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	resp.rc = ENOMEM;
+	resp.bitmap_create_called = false;
+	params.rect.p0.x = 1;
+	params.rect.p0.y = 2;
+	params.rect.p1.x = 3;
+	params.rect.p1.y = 4;
+	bitmap = NULL;
+	rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+	PCUT_ASSERT_TRUE(resp.bitmap_create_called);
+	PCUT_ASSERT_EQUALS(params.rect.p0.x, resp.bitmap_create_params.rect.p0.x);
+	PCUT_ASSERT_EQUALS(params.rect.p0.y, resp.bitmap_create_params.rect.p0.y);
+	PCUT_ASSERT_EQUALS(params.rect.p1.x, resp.bitmap_create_params.rect.p1.x);
+	PCUT_ASSERT_EQUALS(params.rect.p1.y, resp.bitmap_create_params.rect.p1.y);
+	PCUT_ASSERT_EQUALS((params.rect.p1.x - params.rect.p0.x) *
+	    sizeof(uint32_t), (unsigned) resp.bitmap_create_alloc.pitch);
+	PCUT_ASSERT_EQUALS(0, resp.bitmap_create_alloc.off0);
+	PCUT_ASSERT_NOT_NULL(resp.bitmap_create_alloc.pixels);
+	PCUT_ASSERT_NULL(bitmap);
+
+	ipc_gc_delete(ipcgc);
+	async_hangup(sess);
+
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_bitmap_create and gfx_bitmap_destroy with server returning success */
+PCUT_TEST(bitmap_create_destroy_success)
+{
+	errno_t rc;
+	service_id_t sid;
+	test_response_t resp;
+	gfx_context_t *gc;
+	gfx_bitmap_params_t params;
+	gfx_bitmap_t *bitmap;
+	async_sess_t *sess;
+	ipc_gc_t *ipcgc;
+
+	async_set_fallback_port_handler(test_ipcgc_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_ipcgfx_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_ipcgfx_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	sess = loc_service_connect(sid, INTERFACE_GC, 0);
+	PCUT_ASSERT_NOT_NULL(sess);
+
+	rc = ipc_gc_create(sess, &ipcgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = ipc_gc_get_ctx(ipcgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	resp.rc = EOK;
+	resp.bitmap_create_called = false;
+	params.rect.p0.x = 1;
+	params.rect.p0.y = 2;
+	params.rect.p1.x = 3;
+	params.rect.p1.y = 4;
+	bitmap = NULL;
+	rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(resp.bitmap_create_called);
+	PCUT_ASSERT_EQUALS(params.rect.p0.x, resp.bitmap_create_params.rect.p0.x);
+	PCUT_ASSERT_EQUALS(params.rect.p0.y, resp.bitmap_create_params.rect.p0.y);
+	PCUT_ASSERT_EQUALS(params.rect.p1.x, resp.bitmap_create_params.rect.p1.x);
+	PCUT_ASSERT_EQUALS(params.rect.p1.y, resp.bitmap_create_params.rect.p1.y);
+	PCUT_ASSERT_EQUALS((params.rect.p1.x - params.rect.p0.x) *
+	    sizeof(uint32_t), (unsigned) resp.bitmap_create_alloc.pitch);
+	PCUT_ASSERT_EQUALS(0, resp.bitmap_create_alloc.off0);
+	PCUT_ASSERT_NOT_NULL(resp.bitmap_create_alloc.pixels);
+	PCUT_ASSERT_NOT_NULL(bitmap);
+
+	resp.bitmap_destroy_called = false;
+	rc = gfx_bitmap_destroy(bitmap);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(resp.bitmap_destroy_called);
+
+	ipc_gc_delete(ipcgc);
+	async_hangup(sess);
+
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_bitmap_destroy with server returning failure */
+PCUT_TEST(bitmap_destroy_failure)
+{
+	errno_t rc;
+	service_id_t sid;
+	test_response_t resp;
+	gfx_context_t *gc;
+	gfx_bitmap_params_t params;
+	gfx_bitmap_t *bitmap;
+	async_sess_t *sess;
+	ipc_gc_t *ipcgc;
+
+	async_set_fallback_port_handler(test_ipcgc_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_ipcgfx_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_ipcgfx_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	sess = loc_service_connect(sid, INTERFACE_GC, 0);
+	PCUT_ASSERT_NOT_NULL(sess);
+
+	rc = ipc_gc_create(sess, &ipcgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = ipc_gc_get_ctx(ipcgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	resp.rc = EOK;
+	params.rect.p0.x = 1;
+	params.rect.p0.y = 2;
+	params.rect.p1.x = 3;
+	params.rect.p1.y = 4;
+	rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(bitmap);
+
+	resp.rc = EIO;
+	resp.bitmap_destroy_called = false;
+	rc = gfx_bitmap_destroy(bitmap);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+	PCUT_ASSERT_TRUE(resp.bitmap_destroy_called);
+
+	ipc_gc_delete(ipcgc);
+	async_hangup(sess);
+
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_bitmap_render with server returning failure */
+PCUT_TEST(bitmap_render_failure)
+{
+	errno_t rc;
+	service_id_t sid;
+	test_response_t resp;
+	gfx_context_t *gc;
+	gfx_bitmap_params_t params;
+	gfx_bitmap_t *bitmap;
+	gfx_rect_t srect;
+	gfx_coord2_t offs;
+	async_sess_t *sess;
+	ipc_gc_t *ipcgc;
+
+	async_set_fallback_port_handler(test_ipcgc_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_ipcgfx_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_ipcgfx_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	sess = loc_service_connect(sid, INTERFACE_GC, 0);
+	PCUT_ASSERT_NOT_NULL(sess);
+
+	rc = ipc_gc_create(sess, &ipcgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = ipc_gc_get_ctx(ipcgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	resp.rc = EOK;
+	params.rect.p0.x = 1;
+	params.rect.p0.y = 2;
+	params.rect.p1.x = 3;
+	params.rect.p1.y = 4;
+	rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(bitmap);
+
+	resp.rc = EIO;
+	srect.p0.x = 1;
+	srect.p0.y = 2;
+	srect.p1.x = 3;
+	srect.p1.y = 4;
+	rc = gfx_bitmap_render(bitmap, &srect, &offs);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+	PCUT_ASSERT_TRUE(resp.bitmap_render_called);
+	PCUT_ASSERT_EQUALS(srect.p0.x, resp.bitmap_render_srect.p0.x);
+	PCUT_ASSERT_EQUALS(srect.p0.y, resp.bitmap_render_srect.p0.y);
+	PCUT_ASSERT_EQUALS(srect.p1.x, resp.bitmap_render_srect.p1.x);
+	PCUT_ASSERT_EQUALS(srect.p1.y, resp.bitmap_render_srect.p1.y);
+	PCUT_ASSERT_EQUALS(offs.x, resp.bitmap_render_offs.x);
+	PCUT_ASSERT_EQUALS(offs.y, resp.bitmap_render_offs.y);
+
+	resp.rc = EOK;
+	rc = gfx_bitmap_destroy(bitmap);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ipc_gc_delete(ipcgc);
+	async_hangup(sess);
+
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_bitmap_render with server returning success */
+PCUT_TEST(bitmap_render_success)
+{
+	errno_t rc;
+	service_id_t sid;
+	test_response_t resp;
+	gfx_context_t *gc;
+	gfx_bitmap_params_t params;
+	gfx_bitmap_t *bitmap;
+	gfx_rect_t srect;
+	gfx_coord2_t offs;
+	async_sess_t *sess;
+	ipc_gc_t *ipcgc;
+
+	async_set_fallback_port_handler(test_ipcgc_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_ipcgfx_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_ipcgfx_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	sess = loc_service_connect(sid, INTERFACE_GC, 0);
+	PCUT_ASSERT_NOT_NULL(sess);
+
+	rc = ipc_gc_create(sess, &ipcgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = ipc_gc_get_ctx(ipcgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	resp.rc = EOK;
+	params.rect.p0.x = 1;
+	params.rect.p0.y = 2;
+	params.rect.p1.x = 3;
+	params.rect.p1.y = 4;
+	rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(bitmap);
+
+	resp.rc = EOK;
+	srect.p0.x = 1;
+	srect.p0.y = 2;
+	srect.p1.x = 3;
+	srect.p1.y = 4;
+	rc = gfx_bitmap_render(bitmap, &srect, &offs);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(resp.bitmap_render_called);
+	PCUT_ASSERT_EQUALS(srect.p0.x, resp.bitmap_render_srect.p0.x);
+	PCUT_ASSERT_EQUALS(srect.p0.y, resp.bitmap_render_srect.p0.y);
+	PCUT_ASSERT_EQUALS(srect.p1.x, resp.bitmap_render_srect.p1.x);
+	PCUT_ASSERT_EQUALS(srect.p1.y, resp.bitmap_render_srect.p1.y);
+	PCUT_ASSERT_EQUALS(offs.x, resp.bitmap_render_offs.x);
+	PCUT_ASSERT_EQUALS(offs.y, resp.bitmap_render_offs.y);
+
+	resp.rc = EOK;
+	rc = gfx_bitmap_destroy(bitmap);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ipc_gc_delete(ipcgc);
+	async_hangup(sess);
+
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_bitmap_get_alloc - server is not currently involved */
+PCUT_TEST(bitmap_get_alloc)
+{
+	errno_t rc;
+	service_id_t sid;
+	test_response_t resp;
+	gfx_context_t *gc;
+	gfx_bitmap_params_t params;
+	gfx_bitmap_t *bitmap;
+	gfx_bitmap_alloc_t alloc;
+	async_sess_t *sess;
+	ipc_gc_t *ipcgc;
+
+	async_set_fallback_port_handler(test_ipcgc_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_ipcgfx_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_ipcgfx_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	sess = loc_service_connect(sid, INTERFACE_GC, 0);
+	PCUT_ASSERT_NOT_NULL(sess);
+
+	rc = ipc_gc_create(sess, &ipcgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = ipc_gc_get_ctx(ipcgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	resp.rc = EOK;
+	params.rect.p0.x = 1;
+	params.rect.p0.y = 2;
+	params.rect.p1.x = 3;
+	params.rect.p1.y = 4;
+	rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(bitmap);
+
+	rc = gfx_bitmap_get_alloc(bitmap, &alloc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_EQUALS((params.rect.p1.x - params.rect.p0.x) *
+	    sizeof(uint32_t), (unsigned) alloc.pitch);
+	PCUT_ASSERT_EQUALS(0, alloc.off0);
+	PCUT_ASSERT_NOT_NULL(alloc.pixels);
+
+	resp.rc = EOK;
+	rc = gfx_bitmap_destroy(bitmap);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ipc_gc_delete(ipcgc);
+	async_hangup(sess);
+
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+static void test_ipcgc_conn(ipc_call_t *icall, void *arg)
+{
+	gfx_context_t *gc;
+	errno_t rc;
+
+	rc = gfx_context_new(&test_gc_ops, arg, &gc);
+	if (rc != EOK) {
+		async_answer_0(icall, ENOMEM);
+		return;
+	}
+
+	/* Window GC connection */
+	gc_conn(icall, gc);
+}
+
+/** Set color in test GC.
+ *
+ * Set drawing color in test GC.
+ *
+ * @param arg Test GC
+ * @param color Color
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t test_gc_set_color(void *arg, gfx_color_t *color)
+{
+	test_response_t *resp = (test_response_t *) arg;
+
+	resp->set_color_called = true;
+	gfx_color_get_rgb_i16(color, &resp->set_color_r, &resp->set_color_g,
+	    &resp->set_color_b);
+	return resp->rc;
+}
+
+/** Fill rectangle in test GC.
+ *
+ * @param arg Test GC
+ * @param rect Rectangle
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t test_gc_fill_rect(void *arg, gfx_rect_t *rect)
+{
+	test_response_t *resp = (test_response_t *) arg;
+
+	resp->fill_rect_called = true;
+	resp->fill_rect_rect = *rect;
+	return resp->rc;
+}
+
+/** Create bitmap in test GC.
+ *
+ * @param arg Test GC
+ * @param params Bitmap params
+ * @param alloc Bitmap allocation info or @c NULL
+ * @param rbm Place to store pointer to new bitmap
+ * @return EOK on success or an error code
+ */
+errno_t test_gc_bitmap_create(void *arg, gfx_bitmap_params_t *params,
+    gfx_bitmap_alloc_t *alloc, void **rbm)
+{
+	test_response_t *resp = (test_response_t *) arg;
+	test_bitmap_t *bitmap;
+
+	resp->bitmap_create_called = true;
+	resp->bitmap_create_params = *params;
+	resp->bitmap_create_alloc = *alloc;
+
+	if (resp->rc != EOK)
+		return resp->rc;
+
+	bitmap = calloc(1, sizeof(test_bitmap_t));
+	if (bitmap == NULL)
+		return ENOMEM;
+
+	bitmap->resp = resp;
+	*rbm = (void *) bitmap;
+	return EOK;
+}
+
+/** Destroy bitmap in test GC.
+ *
+ * @param bm Bitmap
+ * @return EOK on success or an error code
+ */
+static errno_t test_gc_bitmap_destroy(void *bm)
+{
+	test_bitmap_t *bitmap = (test_bitmap_t *) bm;
+	test_response_t *resp = bitmap->resp;
+
+	resp->bitmap_destroy_called = true;
+	if (resp->rc != EOK)
+		return resp->rc;
+
+	free(bitmap);
+	return EOK;
+}
+
+/** Render bitmap in test GC.
+ *
+ * @param bm Bitmap
+ * @param srect0 Source rectangle or @c NULL
+ * @param offs0 Offset or @c NULL
+ * @return EOK on success or an error code
+ */
+static errno_t test_gc_bitmap_render(void *bm, gfx_rect_t *srect0,
+    gfx_coord2_t *offs0)
+{
+	test_bitmap_t *bitmap = (test_bitmap_t *) bm;
+	test_response_t *resp = bitmap->resp;
+
+	resp->bitmap_render_called = true;
+	resp->bitmap_render_srect = *srect0;
+	resp->bitmap_render_offs = *offs0;
+	return resp->rc;
+}
+
+/** Get allocation info for bitmap in test GC.
+ *
+ * @param bm Bitmap
+ * @param alloc Place to store allocation info
+ * @return EOK on success or an error code
+ */
+static errno_t test_gc_bitmap_get_alloc(void *bm, gfx_bitmap_alloc_t *alloc)
+{
+	/* Currently IPC GC does not pass this method to the server */
+	return ENOTSUP;
+}
+
+PCUT_EXPORT(ipcgfx);
Index: uspace/lib/ipcgfx/test/main.c
===================================================================
--- uspace/lib/ipcgfx/test/main.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/lib/ipcgfx/test/main.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+#include <pcut/pcut.h>
+
+PCUT_INIT;
+
+PCUT_IMPORT(ipcgfx);
+
+PCUT_MAIN();
Index: uspace/lib/meson.build
===================================================================
--- uspace/lib/meson.build	(revision d548fc0e28b66bd85d62aea9c93bea619d7f4520)
+++ uspace/lib/meson.build	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -55,4 +55,5 @@
 	'fmtutil',
 	'fs',
+	'gfx',
 	'graph',
 	'http',
@@ -85,5 +86,10 @@
 	'virtio',
 
+	'congfx',
+	'guigfx',
 	'ieee80211',
+	'ipcgfx',
+
+	'display'
 ]
 
Index: uspace/srv/hid/display/client.c
===================================================================
--- uspace/srv/hid/display/client.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/srv/hid/display/client.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup display
+ * @{
+ */
+/**
+ * @file Display server client
+ */
+
+#include <adt/list.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "client.h"
+#include "display.h"
+#include "window.h"
+
+/** Create client.
+ *
+ * @param display Parent display
+ * @param cb Client callbacks
+ * @param cb_arg Callback argument
+ * @param rclient Place to store pointer to new client.
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t ds_client_create(ds_display_t *display, ds_client_cb_t *cb,
+    void *cb_arg, ds_client_t **rclient)
+{
+	ds_client_t *client;
+
+	client = calloc(1, sizeof(ds_client_t));
+	if (client == NULL)
+		return ENOMEM;
+
+	list_initialize(&client->windows);
+	list_initialize(&client->events);
+	client->cb = cb;
+	client->cb_arg = cb_arg;
+
+	ds_display_add_client(display, client);
+
+	*rclient = client;
+	return EOK;
+}
+
+/** Destroy client.
+ *
+ * @param client Client
+ */
+void ds_client_destroy(ds_client_t *client)
+{
+	ds_window_t *window;
+
+	window = ds_client_first_window(client);
+	while (window != NULL) {
+		ds_window_destroy(window);
+		window = ds_client_first_window(client);
+	}
+
+	assert(list_empty(&client->windows));
+	ds_display_remove_client(client);
+	free(client);
+}
+
+/** Add window to client.
+ *
+ * @param client client
+ * @param wnd Window
+ * @return EOK on success, ENOMEM if there are no free window identifiers
+ */
+errno_t ds_client_add_window(ds_client_t *client, ds_window_t *wnd)
+{
+	assert(wnd->client == NULL);
+	assert(!link_used(&wnd->lwindows));
+
+	wnd->client = client;
+	wnd->id = client->display->next_wnd_id++;
+	list_append(&wnd->lwindows, &client->windows);
+
+	return EOK;
+}
+
+/** Remove window from client.
+ *
+ * @param wnd Window
+ */
+void ds_client_remove_window(ds_window_t *wnd)
+{
+	list_remove(&wnd->lwindows);
+	wnd->client = NULL;
+}
+
+/** Find window by ID.
+ *
+ * @param client Client
+ * @param id Window ID
+ */
+#include <stdio.h>
+ds_window_t *ds_client_find_window(ds_client_t *client, ds_wnd_id_t id)
+{
+	ds_window_t *wnd;
+
+	// TODO Make this faster
+	printf("ds_client_find_window: id=0x%lx\n", id);
+	wnd = ds_client_first_window(client);
+	while (wnd != NULL) {
+		printf("ds_client_find_window: wnd=%p wnd->id=0x%lx\n", wnd, wnd->id);
+		if (wnd->id == id)
+			return wnd;
+		wnd = ds_client_next_window(wnd);
+	}
+
+	return NULL;
+}
+
+/** Get first window in client.
+ *
+ * @param client Client
+ * @return First window or @c NULL if there is none
+ */
+ds_window_t *ds_client_first_window(ds_client_t *client)
+{
+	link_t *link = list_first(&client->windows);
+
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, ds_window_t, lwindows);
+}
+
+/** Get next window in client.
+ *
+ * @param wnd Current window
+ * @return Next window or @c NULL if there is none
+ */
+ds_window_t *ds_client_next_window(ds_window_t *wnd)
+{
+	link_t *link = list_next(&wnd->lwindows, &wnd->client->windows);
+
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, ds_window_t, lwindows);
+}
+
+/** Get next event from client event queue.
+ *
+ * @param client Client
+ * @param ewindow Place to store pointer to window receiving the event
+ * @param event Place to store event
+ * @return Graphic context
+ */
+errno_t ds_client_get_event(ds_client_t *client, ds_window_t **ewindow,
+    display_wnd_ev_t *event)
+{
+	link_t *link;
+	ds_window_ev_t *wevent;
+
+	link = list_first(&client->events);
+	if (link == NULL)
+		return ENOENT;
+
+	wevent = list_get_instance(link, ds_window_ev_t, levents);
+	list_remove(link);
+
+	*ewindow = wevent->window;
+	*event = wevent->event;
+	free(wevent);
+	return EOK;
+}
+
+/** Post keyboard event to the client's message queue.
+ *
+ * @param client Client
+ * @param ewindow Window that the message is targetted to
+ * @param event Event
+ *
+ * @return EOK on success or an error code
+ */
+errno_t ds_client_post_kbd_event(ds_client_t *client, ds_window_t *ewindow,
+    kbd_event_t *event)
+{
+	ds_window_ev_t *wevent;
+
+	wevent = calloc(1, sizeof(ds_window_ev_t));
+	if (wevent == NULL)
+		return ENOMEM;
+
+	wevent->window = ewindow;
+	wevent->event.kbd_event = *event;
+	list_append(&wevent->levents, &client->events);
+
+	/* Notify the client */
+	// TODO Do not send more than once until client drains the queue
+	client->cb->ev_pending(client->cb_arg);
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/srv/hid/display/client.h
===================================================================
--- uspace/srv/hid/display/client.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/srv/hid/display/client.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup display
+ * @{
+ */
+/**
+ * @file Display server client
+ */
+
+#ifndef CLIENT_H
+#define CLIENT_H
+
+#include "types/display/client.h"
+#include "types/display/display.h"
+
+extern errno_t ds_client_create(ds_display_t *, ds_client_cb_t *, void *,
+    ds_client_t **);
+extern void ds_client_destroy(ds_client_t *);
+extern errno_t ds_client_add_window(ds_client_t *, ds_window_t *);
+extern void ds_client_remove_window(ds_window_t *);
+extern ds_window_t *ds_client_find_window(ds_client_t *, ds_wnd_id_t);
+extern ds_window_t *ds_client_first_window(ds_client_t *);
+extern ds_window_t *ds_client_next_window(ds_window_t *);
+extern errno_t ds_client_get_event(ds_client_t *, ds_window_t **,
+    display_wnd_ev_t *);
+extern errno_t ds_client_post_kbd_event(ds_client_t *, ds_window_t *,
+    kbd_event_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/hid/display/display.c
===================================================================
--- uspace/srv/hid/display/display.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/srv/hid/display/display.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup display
+ * @{
+ */
+/**
+ * @file Display management
+ */
+
+#include <disp_srv.h>
+#include <errno.h>
+#include <gfx/context.h>
+#include <io/log.h>
+#include <stdlib.h>
+#include "client.h"
+#include "window.h"
+#include "display.h"
+
+static errno_t disp_window_create(void *, sysarg_t *);
+static errno_t disp_window_destroy(void *, sysarg_t);
+static errno_t disp_get_event(void *, sysarg_t *, display_wnd_ev_t *);
+
+display_ops_t display_srv_ops = {
+	.window_create = disp_window_create,
+	.window_destroy = disp_window_destroy,
+	.get_event = disp_get_event
+};
+
+static errno_t disp_window_create(void *arg, sysarg_t *rwnd_id)
+{
+	errno_t rc;
+	ds_client_t *client = (ds_client_t *) arg;
+	ds_window_t *wnd;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "disp_window_create()");
+
+	rc = ds_window_create(client, &wnd);
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "disp_window_create() - ds_window_create -> %d", rc);
+	if (rc != EOK)
+		return rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "disp_window_create() -> EOK, id=%zu",
+	    wnd->id);
+
+	wnd->dpos.x = ((wnd->id - 1) & 1) * 400;
+	wnd->dpos.y = ((wnd->id - 1) & 2) / 2 * 300;
+
+	*rwnd_id = wnd->id;
+	return EOK;
+}
+
+static errno_t disp_window_destroy(void *arg, sysarg_t wnd_id)
+{
+	ds_client_t *client = (ds_client_t *) arg;
+	ds_window_t *wnd;
+
+	wnd = ds_client_find_window(client, wnd_id);
+	if (wnd == NULL)
+		return ENOENT;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "disp_window_destroy()");
+	ds_client_remove_window(wnd);
+	ds_window_destroy(wnd);
+	return EOK;
+}
+
+static errno_t disp_get_event(void *arg, sysarg_t *wnd_id,
+    display_wnd_ev_t *event)
+{
+	ds_client_t *client = (ds_client_t *) arg;
+	ds_window_t *wnd;
+	errno_t rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "disp_window_get_event()");
+
+	rc = ds_client_get_event(client, &wnd, event);
+	if (rc != EOK)
+		return rc;
+
+	*wnd_id = wnd->id;
+	return EOK;
+}
+
+/** Create display.
+ *
+ * @param gc Graphics context for displaying output
+ * @param rdisp Place to store pointer to new display.
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t ds_display_create(gfx_context_t *gc, ds_display_t **rdisp)
+{
+	ds_display_t *disp;
+
+	disp = calloc(1, sizeof(ds_display_t));
+	if (disp == NULL)
+		return ENOMEM;
+
+	list_initialize(&disp->clients);
+	disp->gc = gc;
+	disp->next_wnd_id = 1;
+	*rdisp = disp;
+	return EOK;
+}
+
+/** Destroy display.
+ *
+ * @param disp Display
+ */
+void ds_display_destroy(ds_display_t *disp)
+{
+	assert(list_empty(&disp->clients));
+	free(disp);
+}
+
+/** Add client to display.
+ *
+ * @param disp Display
+ * @param client client
+ */
+void ds_display_add_client(ds_display_t *disp, ds_client_t *client)
+{
+	assert(client->display == NULL);
+	assert(!link_used(&client->lclients));
+
+	client->display = disp;
+	list_append(&client->lclients, &disp->clients);
+}
+
+/** Remove client from display.
+ *
+ * @param client client
+ */
+void ds_display_remove_client(ds_client_t *client)
+{
+	list_remove(&client->lclients);
+	client->display = NULL;
+}
+
+/** Get first client in display.
+ *
+ * @param disp Display
+ * @return First client or @c NULL if there is none
+ */
+ds_client_t *ds_display_first_client(ds_display_t *disp)
+{
+	link_t *link = list_first(&disp->clients);
+
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, ds_client_t, lclients);
+}
+
+/** Get next client in display.
+ *
+ * @param client Current client
+ * @return Next client or @c NULL if there is none
+ */
+ds_client_t *ds_display_next_client(ds_client_t *client)
+{
+	link_t *link = list_next(&client->lclients, &client->display->clients);
+
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, ds_client_t, lclients);
+}
+
+/** Find window in all clients by ID.
+ *
+ * XXX This is just a hack needed to match GC connection to a window,
+ * as we don't have a good safe way to pass the GC endpoint to our client
+ * on demand.
+ *
+ * @param display Display
+ * @param id Window ID
+ */
+#include <stdio.h>
+ds_window_t *ds_display_find_window(ds_display_t *display, ds_wnd_id_t id)
+{
+	ds_client_t *client;
+	ds_window_t *wnd;
+
+	printf("ds_display_find_window: id=0x%lx\n", id);
+
+	client = ds_display_first_client(display);
+	while (client != NULL) {
+		printf("ds_display_find_window: client=%p\n", client);
+		wnd = ds_client_find_window(client, id);
+		if (wnd != NULL) {
+			printf("ds_display_find_window: found wnd=%p id=0x%lx\n",
+			    wnd, wnd->id);
+			return wnd;
+		}
+		client = ds_display_next_client(client);
+	}
+
+	printf("ds_display_find_window: not found\n");
+	return NULL;
+}
+
+errno_t ds_display_post_kbd_event(ds_display_t *display, kbd_event_t *event)
+{
+	ds_client_t *client;
+	ds_window_t *wnd;
+
+	// XXX Correctly determine destination window
+
+	client = ds_display_first_client(display);
+	if (client == NULL)
+		return EOK;
+
+	wnd = ds_client_first_window(client);
+	if (wnd == NULL)
+		return EOK;
+
+	return ds_client_post_kbd_event(client, wnd, event);
+}
+
+/** @}
+ */
Index: uspace/srv/hid/display/display.h
===================================================================
--- uspace/srv/hid/display/display.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/srv/hid/display/display.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup display
+ * @{
+ */
+/**
+ * @file
+ * @brief
+ */
+
+#ifndef DISPLAY_H
+#define DISPLAY_H
+
+#include <disp_srv.h>
+#include <errno.h>
+#include <gfx/context.h>
+#include <io/kbd_event.h>
+#include "types/display/client.h"
+#include "types/display/display.h"
+
+extern display_ops_t display_srv_ops;
+
+extern errno_t ds_display_create(gfx_context_t *, ds_display_t **);
+extern void ds_display_destroy(ds_display_t *);
+extern void ds_display_add_client(ds_display_t *, ds_client_t *);
+extern void ds_display_remove_client(ds_client_t *);
+extern ds_client_t *ds_display_first_client(ds_display_t *);
+extern ds_client_t *ds_display_next_client(ds_client_t *);
+extern ds_window_t *ds_display_find_window(ds_display_t *, ds_wnd_id_t);
+extern errno_t ds_display_post_kbd_event(ds_display_t *, kbd_event_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/hid/display/doc/doxygroups.h
===================================================================
--- uspace/srv/hid/display/doc/doxygroups.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/srv/hid/display/doc/doxygroups.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,4 @@
+/** @addtogroup display display
+ * @ingroup srvs
+ * @brief Display server
+ */
Index: uspace/srv/hid/display/main.c
===================================================================
--- uspace/srv/hid/display/main.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/srv/hid/display/main.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution1s 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.
+ */
+
+/** @addtogroup display
+ * @{
+ */
+/**
+ * @file Display server main
+ */
+
+#include <async.h>
+#include <disp_srv.h>
+#include <errno.h>
+#include <gfx/context.h>
+#include <str_error.h>
+#include <io/log.h>
+#include <ipc/services.h>
+#include <ipcgfx/server.h>
+#include <loc.h>
+#include <stdio.h>
+#include <task.h>
+#include "client.h"
+#include "display.h"
+#include "main.h"
+#include "output.h"
+#include "window.h"
+
+static void display_client_conn(ipc_call_t *, void *);
+static void display_client_ev_pending(void *);
+
+static ds_client_cb_t display_client_cb = {
+	.ev_pending = display_client_ev_pending
+};
+
+static void display_kbd_event(void *arg, kbd_event_t *event)
+{
+	ds_display_t *disp = (ds_display_t *) arg;
+
+	printf("display_kbd_event\n");
+	ds_display_post_kbd_event(disp, event);
+}
+
+static void display_client_ev_pending(void *arg)
+{
+	display_srv_t *srv = (display_srv_t *) arg;
+	printf("display_client_ev_pending\n");
+	display_srv_ev_pending(srv);
+}
+
+/** Initialize display server */
+static errno_t display_srv_init(void)
+{
+	ds_display_t *disp = NULL;
+	gfx_context_t *gc = NULL;
+	errno_t rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "display_srv_init()");
+
+	rc = ds_display_create(NULL, &disp);
+	if (rc != EOK)
+		goto error;
+
+	rc = output_init(display_kbd_event, (void *) disp, &gc);
+	if (rc != EOK)
+		goto error;
+
+	disp->gc = gc;
+#if 0
+	rc = ds_input_open(disp);
+	if (rc != EOK)
+		goto error;
+#endif
+	async_set_fallback_port_handler(display_client_conn, disp);
+
+	rc = loc_server_register(NAME);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering server: %s.", str_error(rc));
+		rc = EEXIST;
+	}
+
+	service_id_t sid;
+	rc = loc_service_register(SERVICE_NAME_DISPLAY, &sid);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering service: %s.", str_error(rc));
+		rc = EEXIST;
+		goto error;
+	}
+
+	return EOK;
+error:
+#if 0
+	if (disp->input != NULL)
+		ds_input_close(disp);
+#endif
+	if (gc != NULL)
+		gfx_context_delete(gc);
+	if (disp != NULL)
+		ds_display_destroy(disp);
+	return rc;
+}
+
+/** Handle client connection to display server */
+static void display_client_conn(ipc_call_t *icall, void *arg)
+{
+	display_srv_t srv;
+	sysarg_t wnd_id;
+	sysarg_t svc_id;
+	ds_client_t *client = NULL;
+	ds_window_t *wnd;
+	ds_display_t *disp = (ds_display_t *) arg;
+	gfx_context_t *gc;
+	errno_t rc;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "display_client_conn arg1=%zu arg2=%zu arg3=%zu arg4=%zu.",
+	    ipc_get_arg1(icall), ipc_get_arg2(icall), ipc_get_arg3(icall),
+	    ipc_get_arg4(icall));
+
+	(void) icall;
+	(void) arg;
+
+	svc_id = ipc_get_arg2(icall);
+	wnd_id = ipc_get_arg3(icall);
+
+	if (svc_id != 0) {
+		/* Create client object */
+		rc = ds_client_create(disp, &display_client_cb, &srv, &client);
+		if (rc != EOK) {
+			async_answer_0(icall, ENOMEM);
+			return;
+		}
+
+		/* Set up protocol structure */
+		display_srv_initialize(&srv);
+		srv.ops = &display_srv_ops;
+		srv.arg = client;
+
+		/* Handle connection */
+		display_conn(icall, &srv);
+
+		ds_client_destroy(client);
+	} else {
+		/* Window GC connection */
+
+		wnd = ds_display_find_window(disp, wnd_id);
+		if (wnd == NULL) {
+			async_answer_0(icall, ENOENT);
+			return;
+		}
+
+		gc = ds_window_get_ctx(wnd);
+		gc_conn(icall, gc);
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	errno_t rc;
+
+	printf("%s: Display server\n", NAME);
+
+	if (log_init(NAME) != EOK) {
+		printf(NAME ": Failed to initialize logging.\n");
+		return 1;
+	}
+
+	rc = display_srv_init();
+	if (rc != EOK)
+		return 1;
+
+	printf(NAME ": Accepting connections.\n");
+	task_retval(0);
+	async_manager();
+
+	return 0;
+}
+
+/** @}
+ */
Index: uspace/srv/hid/display/main.h
===================================================================
--- uspace/srv/hid/display/main.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/srv/hid/display/main.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup display
+ * @{
+ */
+/** @file Display server main
+ */
+
+#ifndef MAIN_H
+#define MAIN_H
+
+#define NAME "display"
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/hid/display/meson.build
===================================================================
--- uspace/srv/hid/display/meson.build	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/srv/hid/display/meson.build	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,47 @@
+#
+# Copyright (c) 2019 Jiri Svoboda
+# 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.
+#
+
+deps = [ 'ipcgfx', 'display', 'guigfx' ]
+
+src = files(
+	'client.c',
+	'display.c',
+	'main.c',
+	'output.c',
+	'window.c',
+)
+
+test_src = files(
+	'client.c',
+	'display.c',
+	'window.c',
+	'test/client.c',
+	'test/display.c',
+	'test/main.c',
+	'test/window.c',
+)
Index: uspace/srv/hid/display/output.c
===================================================================
--- uspace/srv/hid/display/output.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/srv/hid/display/output.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup display
+ * @{
+ */
+/**
+ * @file Display server output
+ */
+
+#include <errno.h>
+#include <gfx/context.h>
+#include <guigfx/canvas.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <window.h>
+#include "output.h"
+
+static void (*kbd_ev_handler)(void *, kbd_event_t *);
+static void *kbd_ev_arg;
+
+static void on_keyboard_event(widget_t *widget, void *data)
+{
+	printf("Keyboard event\n");
+	kbd_ev_handler(kbd_ev_arg, (kbd_event_t *) data);
+}
+
+errno_t output_init(void (*kbd_event_handler)(void *, kbd_event_t *),
+    void *arg, gfx_context_t **rgc)
+{
+	canvas_gc_t *cgc = NULL;
+	window_t *window = NULL;
+	pixel_t *pixbuf = NULL;
+	surface_t *surface = NULL;
+	canvas_t *canvas = NULL;
+	gfx_coord_t vw, vh;
+	errno_t rc;
+
+	printf("Init canvas..\n");
+	kbd_ev_handler = kbd_event_handler;
+	kbd_ev_arg = arg;
+
+	window = window_open("comp:0/winreg", NULL,
+	    WINDOW_MAIN | WINDOW_DECORATED, "Display Server");
+	if (window == NULL) {
+		printf("Error creating window.\n");
+		return -1;
+	}
+
+	vw = 800;
+	vh = 600;
+
+	pixbuf = calloc(vw * vh, sizeof(pixel_t));
+	if (pixbuf == NULL) {
+		printf("Error allocating memory for pixel buffer.\n");
+		return ENOMEM;
+	}
+
+	surface = surface_create(vw, vh, pixbuf, 0);
+	if (surface == NULL) {
+		printf("Error creating surface.\n");
+		return EIO;
+	}
+
+	canvas = create_canvas(window_root(window), NULL, vw, vh,
+	    surface);
+	if (canvas == NULL) {
+		printf("Error creating canvas.\n");
+		return EIO;
+	}
+
+	sig_connect(&canvas->keyboard_event, NULL, on_keyboard_event);
+
+	window_resize(window, 0, 0, vw + 10, vh + 30, WINDOW_PLACEMENT_ANY);
+	window_exec(window);
+
+	printf("Create canvas GC\n");
+	rc = canvas_gc_create(canvas, surface, &cgc);
+	if (rc != EOK)
+		return rc;
+
+	*rgc = canvas_gc_get_ctx(cgc);
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/srv/hid/display/output.h
===================================================================
--- uspace/srv/hid/display/output.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/srv/hid/display/output.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup display
+ * @{
+ */
+/**
+ * @file Display server output
+ */
+
+#ifndef OUTPUT_H
+#define OUTPUT_H
+
+#include <gfx/context.h>
+
+extern errno_t output_init(void (*)(void *, kbd_event_t *), void *,
+    gfx_context_t **);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/hid/display/test/client.c
===================================================================
--- uspace/srv/hid/display/test/client.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/srv/hid/display/test/client.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+#include <errno.h>
+#include <pcut/pcut.h>
+#include <stdio.h>
+#include <str.h>
+
+#include "../client.h"
+#include "../display.h"
+#include "../window.h"
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(client);
+
+static void test_ds_ev_pending(void *);
+
+static ds_client_cb_t test_ds_client_cb = {
+	.ev_pending = test_ds_ev_pending
+};
+
+static void test_ds_ev_pending(void *arg)
+{
+	bool *called_cb = (bool *) arg;
+	printf("test_ds_ev_pending\n");
+	*called_cb = true;
+
+}
+
+/** Client creation and destruction. */
+PCUT_TEST(client_create_destroy)
+{
+	ds_display_t *disp;
+	ds_client_t *client;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, &test_ds_client_cb, NULL, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ds_client_destroy(client);
+	ds_display_destroy(disp);
+}
+
+/** Test ds_client_find_window().
+ *
+ * ds_client_add_window() and ds_client_remove_window() are indirectly
+ * tested too as part of creating and destroying the window
+ */
+PCUT_TEST(client_find_window)
+{
+	ds_display_t *disp;
+	ds_client_t *client;
+	ds_window_t *w0;
+	ds_window_t *w1;
+	ds_window_t *wnd;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, &test_ds_client_cb, NULL, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_window_create(client, &w0);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_window_create(client, &w1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	wnd = ds_client_find_window(client, w0->id);
+	PCUT_ASSERT_EQUALS(w0, wnd);
+
+	wnd = ds_client_find_window(client, w1->id);
+	PCUT_ASSERT_EQUALS(w1, wnd);
+
+	wnd = ds_client_find_window(client, 0);
+	PCUT_ASSERT_NULL(wnd);
+
+	wnd = ds_client_find_window(client, w1->id + 1);
+	PCUT_ASSERT_NULL(wnd);
+
+	ds_window_destroy(w0);
+	ds_window_destroy(w1);
+	ds_client_destroy(client);
+	ds_display_destroy(disp);
+}
+
+/** Test ds_client_first_window() / ds_client_next_window. */
+PCUT_TEST(client_first_next_window)
+{
+	ds_display_t *disp;
+	ds_client_t *client;
+	ds_window_t *w0;
+	ds_window_t *w1;
+	ds_window_t *wnd;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, &test_ds_client_cb, NULL, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_window_create(client, &w0);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_window_create(client, &w1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	wnd = ds_client_first_window(client);
+	PCUT_ASSERT_EQUALS(w0, wnd);
+
+	wnd = ds_client_next_window(w0);
+	PCUT_ASSERT_EQUALS(w1, wnd);
+
+	wnd = ds_client_next_window(w1);
+	PCUT_ASSERT_NULL(wnd);
+
+	ds_window_destroy(w0);
+	ds_window_destroy(w1);
+	ds_client_destroy(client);
+	ds_display_destroy(disp);
+}
+
+/** Test ds_client_get_event(), ds_client_post_kbd_event(). */
+PCUT_TEST(display_get_post_kbd_event)
+{
+	ds_display_t *disp;
+	ds_client_t *client;
+	ds_window_t *wnd;
+	kbd_event_t event;
+	ds_window_t *rwindow;
+	display_wnd_ev_t revent;
+	bool called_cb = NULL;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, &test_ds_client_cb, &called_cb, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_window_create(client, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	event.type = KEY_PRESS;
+	event.key = KC_ENTER;
+	event.mods = 0;
+	event.c = L'\0';
+
+	PCUT_ASSERT_FALSE(called_cb);
+
+	rc = ds_client_get_event(client, &rwindow, &revent);
+	PCUT_ASSERT_ERRNO_VAL(ENOENT, rc);
+
+	rc = ds_client_post_kbd_event(client, wnd, &event);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(called_cb);
+
+	rc = ds_client_get_event(client, &rwindow, &revent);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_EQUALS(wnd, rwindow);
+	PCUT_ASSERT_EQUALS(event.type, revent.kbd_event.type);
+	PCUT_ASSERT_EQUALS(event.key, revent.kbd_event.key);
+	PCUT_ASSERT_EQUALS(event.mods, revent.kbd_event.mods);
+	PCUT_ASSERT_EQUALS(event.c, revent.kbd_event.c);
+
+	rc = ds_client_get_event(client, &rwindow, &revent);
+	PCUT_ASSERT_ERRNO_VAL(ENOENT, rc);
+
+	ds_window_destroy(wnd);
+	ds_client_destroy(client);
+	ds_display_destroy(disp);
+}
+
+/** Test client being destroyed while still having a window.
+ *
+ * This can happen if client forgets to destroy window or if the client
+ * is disconnected (or terminated).
+ */
+PCUT_TEST(client_leftover_window)
+{
+	ds_display_t *disp;
+	ds_client_t *client;
+	ds_window_t *wnd;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, NULL, NULL, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_window_create(client, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ds_client_destroy(client);
+	ds_display_destroy(disp);
+}
+
+PCUT_EXPORT(client);
Index: uspace/srv/hid/display/test/display.c
===================================================================
--- uspace/srv/hid/display/test/display.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/srv/hid/display/test/display.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+#include <errno.h>
+#include <pcut/pcut.h>
+#include <stdio.h>
+#include <str.h>
+
+#include "../client.h"
+#include "../display.h"
+#include "../window.h"
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(display);
+
+static void test_ds_ev_pending(void *);
+
+static ds_client_cb_t test_ds_client_cb = {
+	.ev_pending = test_ds_ev_pending
+};
+
+static void test_ds_ev_pending(void *arg)
+{
+	bool *called_cb = (bool *) arg;
+	printf("test_ds_ev_pending\n");
+	*called_cb = true;
+
+}
+
+/** Display creation and destruction. */
+PCUT_TEST(display_create_destroy)
+{
+	ds_display_t *disp;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ds_display_destroy(disp);
+}
+
+/** Basic client operation. */
+PCUT_TEST(display_client)
+{
+	ds_display_t *disp;
+	ds_client_t *client;
+	ds_client_t *c0, *c1;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, &test_ds_client_cb, NULL, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	c0 = ds_display_first_client(disp);
+	PCUT_ASSERT_EQUALS(c0, client);
+
+	c1 = ds_display_next_client(c0);
+	PCUT_ASSERT_NULL(c1);
+
+	ds_client_destroy(client);
+	ds_display_destroy(disp);
+}
+
+/** Test ds_display_find_window(). */
+PCUT_TEST(display_find_window)
+{
+	ds_display_t *disp;
+	ds_client_t *client;
+	ds_window_t *w0;
+	ds_window_t *w1;
+	ds_window_t *wnd;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, &test_ds_client_cb, NULL, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_window_create(client, &w0);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_window_create(client, &w1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	wnd = ds_display_find_window(disp, w0->id);
+	PCUT_ASSERT_EQUALS(w0, wnd);
+
+	wnd = ds_display_find_window(disp, w1->id);
+	PCUT_ASSERT_EQUALS(w1, wnd);
+
+	wnd = ds_display_find_window(disp, 0);
+	PCUT_ASSERT_NULL(wnd);
+
+	wnd = ds_display_find_window(disp, w1->id + 1);
+	PCUT_ASSERT_NULL(wnd);
+
+	ds_window_destroy(w0);
+	ds_window_destroy(w1);
+	ds_client_destroy(client);
+	ds_display_destroy(disp);
+}
+
+/** Test ds_display_post_kbd_event(). */
+PCUT_TEST(display_post_kbd_event)
+{
+	ds_display_t *disp;
+	ds_client_t *client;
+	ds_window_t *wnd;
+	kbd_event_t event;
+	bool called_cb = NULL;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, &test_ds_client_cb, &called_cb, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_window_create(client, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	event.type = KEY_PRESS;
+	event.key = KC_ENTER;
+	event.mods = 0;
+	event.c = L'\0';
+
+	PCUT_ASSERT_FALSE(called_cb);
+
+	rc = ds_display_post_kbd_event(disp, &event);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(called_cb);
+
+	ds_window_destroy(wnd);
+	ds_client_destroy(client);
+	ds_display_destroy(disp);
+}
+
+PCUT_EXPORT(display);
Index: uspace/srv/hid/display/test/main.c
===================================================================
--- uspace/srv/hid/display/test/main.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/srv/hid/display/test/main.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+#include <pcut/pcut.h>
+
+PCUT_INIT;
+
+PCUT_IMPORT(client);
+PCUT_IMPORT(display);
+PCUT_IMPORT(window);
+
+PCUT_MAIN();
Index: uspace/srv/hid/display/test/window.c
===================================================================
--- uspace/srv/hid/display/test/window.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/srv/hid/display/test/window.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+#include <errno.h>
+#include <pcut/pcut.h>
+#include <stdio.h>
+#include <str.h>
+
+#include "../client.h"
+#include "../display.h"
+#include "../window.h"
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(window);
+
+/** Test ds_window_get_ctx(). */
+PCUT_TEST(window_get_ctx)
+{
+	ds_display_t *disp;
+	ds_client_t *client;
+	ds_window_t *wnd;
+	gfx_context_t *gc;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, NULL, NULL, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_window_create(client, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = ds_window_get_ctx(wnd);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	ds_window_destroy(wnd);
+	ds_client_destroy(client);
+	ds_display_destroy(disp);
+}
+
+PCUT_EXPORT(window);
Index: uspace/srv/hid/display/types/display/client.h
===================================================================
--- uspace/srv/hid/display/types/display/client.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/srv/hid/display/types/display/client.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup display
+ * @{
+ */
+/**
+ * @file Display server client type
+ */
+
+#ifndef TYPES_DISPLAY_CLIENT_H
+#define TYPES_DISPLAY_CLIENT_H
+
+#include <adt/list.h>
+
+typedef sysarg_t ds_wnd_id_t;
+
+/** Display server client callbacks */
+typedef struct {
+	void (*ev_pending)(void *);
+} ds_client_cb_t;
+
+/** Display server client */
+typedef struct ds_client {
+	/** Parent display */
+	struct ds_display *display;
+	/** Callbacks */
+	ds_client_cb_t *cb;
+	/** Callback argument */
+	void *cb_arg;
+	/** Link to @c display->clients */
+	link_t lclients;
+	/** Windows (of ds_window_t) */
+	list_t windows;
+	/** Event queue (of ds_window_ev_t) */
+	list_t events;
+} ds_client_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/hid/display/types/display/display.h
===================================================================
--- uspace/srv/hid/display/types/display/display.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/srv/hid/display/types/display/display.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup display
+ * @{
+ */
+/**
+ * @file Display server display type
+ */
+
+#ifndef TYPES_DISPLAY_DISPLAY_H
+#define TYPES_DISPLAY_DISPLAY_H
+
+#include <adt/list.h>
+#include <gfx/context.h>
+#include <io/input.h>
+#include "window.h"
+
+/** Display server display */
+typedef struct ds_display {
+	/** Clients (of ds_client_t) */
+	list_t clients;
+	/** Output GC */
+	gfx_context_t *gc;
+
+	/** Next ID to assign to a window.
+	 *
+	 * XXX Window IDs need to be unique per display just because
+	 * we don't have a way to match GC connection to the proper
+	 * client. Really this should be in ds_client_t and the ID
+	 * space should be per client.
+	 */
+	ds_wnd_id_t next_wnd_id;
+	/** Input service */
+	input_t *input;
+} ds_display_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/hid/display/types/display/window.h
===================================================================
--- uspace/srv/hid/display/types/display/window.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/srv/hid/display/types/display/window.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup display
+ * @{
+ */
+/**
+ * @file Display server window type
+ */
+
+#ifndef TYPES_DISPLAY_WINDOW_H
+#define TYPES_DISPLAY_WINDOW_H
+
+#include <adt/list.h>
+#include <display/event.h>
+#include <gfx/context.h>
+#include <gfx/coord.h>
+
+typedef sysarg_t ds_wnd_id_t;
+
+/** Display server window */
+typedef struct ds_window {
+	/** Parent client */
+	struct ds_client *client;
+	/** Link to @c client->windows */
+	link_t lwindows;
+	/** Display position */
+	gfx_coord2_t dpos;
+	/** Window ID */
+	ds_wnd_id_t id;
+	/** Graphic context */
+	gfx_context_t *gc;
+} ds_window_t;
+
+/** Window event queue entry */
+typedef struct {
+	/** Link to event queue */
+	link_t levents;
+	/** Window to which the event is delivered */
+	ds_window_t *window;
+	/** Event */
+	display_wnd_ev_t event;
+} ds_window_ev_t;
+
+/** Bitmap in display server window GC */
+typedef struct {
+	/** Containing window */
+	ds_window_t *wnd;
+	/** Display bitmap */
+	gfx_bitmap_t *bitmap;
+} ds_window_bitmap_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/hid/display/window.c
===================================================================
--- uspace/srv/hid/display/window.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/srv/hid/display/window.c	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup display
+ * @{
+ */
+/**
+ * @file GFX window backend
+ *
+ * This implements a graphics context over display server window.
+ */
+
+#include <gfx/bitmap.h>
+#include <gfx/color.h>
+#include <gfx/coord.h>
+#include <gfx/context.h>
+#include <gfx/render.h>
+#include <io/log.h>
+#include <stdlib.h>
+#include "client.h"
+#include "display.h"
+#include "window.h"
+
+static errno_t ds_window_set_color(void *, gfx_color_t *);
+static errno_t ds_window_fill_rect(void *, gfx_rect_t *);
+static errno_t ds_window_bitmap_create(void *, gfx_bitmap_params_t *,
+    gfx_bitmap_alloc_t *, void **);
+static errno_t ds_window_bitmap_destroy(void *);
+static errno_t ds_window_bitmap_render(void *, gfx_rect_t *, gfx_coord2_t *);
+static errno_t ds_window_bitmap_get_alloc(void *, gfx_bitmap_alloc_t *);
+
+gfx_context_ops_t ds_window_ops = {
+	.set_color = ds_window_set_color,
+	.fill_rect = ds_window_fill_rect,
+	.bitmap_create = ds_window_bitmap_create,
+	.bitmap_destroy = ds_window_bitmap_destroy,
+	.bitmap_render = ds_window_bitmap_render,
+	.bitmap_get_alloc = ds_window_bitmap_get_alloc
+};
+
+/** Set color on window GC.
+ *
+ * Set drawing color on window GC.
+ *
+ * @param arg Console GC
+ * @param color Color
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t ds_window_set_color(void *arg, gfx_color_t *color)
+{
+	ds_window_t *wnd = (ds_window_t *) arg;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "gc_set_color gc=%p", wnd->client->display->gc);
+	return gfx_set_color(wnd->client->display->gc, color);
+}
+
+/** Fill rectangle on window GC.
+ *
+ * @param arg Window GC
+ * @param rect Rectangle
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t ds_window_fill_rect(void *arg, gfx_rect_t *rect)
+{
+	ds_window_t *wnd = (ds_window_t *) arg;
+	gfx_rect_t drect;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "gc_fill_rect");
+	gfx_rect_translate(&wnd->dpos, rect, &drect);
+	return gfx_fill_rect(wnd->client->display->gc, &drect);
+}
+
+/** Create bitmap in canvas GC.
+ *
+ * @param arg Canvas GC
+ * @param params Bitmap params
+ * @param alloc Bitmap allocation info or @c NULL
+ * @param rbm Place to store pointer to new bitmap
+ * @return EOK on success or an error code
+ */
+errno_t ds_window_bitmap_create(void *arg, gfx_bitmap_params_t *params,
+    gfx_bitmap_alloc_t *alloc, void **rbm)
+{
+	ds_window_t *wnd = (ds_window_t *) arg;
+	ds_window_bitmap_t *cbm = NULL;
+	errno_t rc;
+
+	cbm = calloc(1, sizeof(ds_window_bitmap_t));
+	if (cbm == NULL)
+		return ENOMEM;
+
+	rc = gfx_bitmap_create(wnd->client->display->gc, params, alloc,
+	    &cbm->bitmap);
+	if (rc != EOK)
+		goto error;
+
+	cbm->wnd = wnd;
+	*rbm = (void *)cbm;
+	return EOK;
+error:
+	if (cbm != NULL)
+		free(cbm);
+	return rc;
+}
+
+/** Destroy bitmap in canvas GC.
+ *
+ * @param bm Bitmap
+ * @return EOK on success or an error code
+ */
+static errno_t ds_window_bitmap_destroy(void *bm)
+{
+	ds_window_bitmap_t *cbm = (ds_window_bitmap_t *)bm;
+
+	gfx_bitmap_destroy(cbm->bitmap);
+	free(cbm);
+	return EOK;
+}
+
+/** Render bitmap in canvas GC.
+ *
+ * @param bm Bitmap
+ * @param srect0 Source rectangle or @c NULL
+ * @param offs0 Offset or @c NULL
+ * @return EOK on success or an error code
+ */
+static errno_t ds_window_bitmap_render(void *bm, gfx_rect_t *srect0,
+    gfx_coord2_t *offs0)
+{
+	ds_window_bitmap_t *cbm = (ds_window_bitmap_t *)bm;
+	gfx_coord2_t doffs;
+
+	if (offs0 != NULL)
+		gfx_coord2_add(&cbm->wnd->dpos, offs0, &doffs);
+	else
+		doffs = cbm->wnd->dpos;
+
+	return gfx_bitmap_render(cbm->bitmap, srect0, &doffs);
+}
+
+/** Get allocation info for bitmap in canvas GC.
+ *
+ * @param bm Bitmap
+ * @param alloc Place to store allocation info
+ * @return EOK on success or an error code
+ */
+static errno_t ds_window_bitmap_get_alloc(void *bm, gfx_bitmap_alloc_t *alloc)
+{
+	ds_window_bitmap_t *cbm = (ds_window_bitmap_t *)bm;
+
+	return gfx_bitmap_get_alloc(cbm->bitmap, alloc);
+}
+
+/** Create window.
+ *
+ * Create graphics context for rendering into a window.
+ *
+ * @param client Client owning the window
+ * @param rgc Place to store pointer to new GC.
+ *
+ * @return EOK on success or an error code
+ */
+errno_t ds_window_create(ds_client_t *client, ds_window_t **rgc)
+{
+	ds_window_t *wnd = NULL;
+	gfx_context_t *gc = NULL;
+	errno_t rc;
+
+	wnd = calloc(1, sizeof(ds_window_t));
+	if (wnd == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	rc = gfx_context_new(&ds_window_ops, wnd, &gc);
+	if (rc != EOK)
+		goto error;
+
+	ds_client_add_window(client, wnd);
+
+	wnd->gc = gc;
+	*rgc = wnd;
+	return EOK;
+error:
+	if (wnd != NULL)
+		free(wnd);
+	gfx_context_delete(gc);
+	return rc;
+}
+
+/** Delete window GC.
+ *
+ * @param wnd Window GC
+ */
+void ds_window_destroy(ds_window_t *wnd)
+{
+	ds_client_remove_window(wnd);
+	(void) gfx_context_delete(wnd->gc);
+
+	free(wnd);
+}
+
+/** Get generic graphic context from window.
+ *
+ * @param wnd Window
+ * @return Graphic context
+ */
+gfx_context_t *ds_window_get_ctx(ds_window_t *wnd)
+{
+	return wnd->gc;
+}
+
+/** @}
+ */
Index: uspace/srv/hid/display/window.h
===================================================================
--- uspace/srv/hid/display/window.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
+++ uspace/srv/hid/display/window.h	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup display
+ * @{
+ */
+/**
+ * @file Display server window
+ */
+
+#ifndef WINDOW_H
+#define WINDOW_H
+
+#include <display/event.h>
+#include <errno.h>
+#include <types/gfx/context.h>
+#include <types/gfx/ops/context.h>
+#include "types/display/display.h"
+#include "types/display/window.h"
+
+extern gfx_context_ops_t window_gc_ops;
+
+extern errno_t ds_window_create(ds_client_t *, ds_window_t **);
+extern void ds_window_destroy(ds_window_t *);
+extern gfx_context_t *ds_window_get_ctx(ds_window_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/meson.build
===================================================================
--- uspace/srv/meson.build	(revision d548fc0e28b66bd85d62aea9c93bea619d7f4520)
+++ uspace/srv/meson.build	(revision 84876aa4f80a43fe7bb9c01fbe376d900db1cbe4)
@@ -45,4 +45,5 @@
 	'hid/compositor',
 	'hid/console',
+	'hid/display',
 	'hid/input',
 	'hid/isdv4_tablet',
