Index: uspace/app/gfxdemo/gfxdemo.c
===================================================================
--- uspace/app/gfxdemo/gfxdemo.c	(revision 9be2358aadbef3865ee2b3ab546d8b1f56d79cd6)
+++ uspace/app/gfxdemo/gfxdemo.c	(revision 00e8290f885b3dc03a5c5a191983eb336d1e852c)
@@ -33,5 +33,8 @@
  */
 
+#include <canvas.h>
+#include <draw/surface.h>
 #include <fibril.h>
+#include <gfx/backend/canvas.h>
 #include <gfx/backend/console.h>
 #include <gfx/color.h>
@@ -39,13 +42,51 @@
 #include <io/console.h>
 #include <stdlib.h>
-
-int main(int argc, char *argv[])
+#include <str.h>
+#include <window.h>
+
+/** Run rectangle demo on a graphic context.
+ *
+ * @param gc Graphic context
+ */
+static errno_t demo_rects(gfx_context_t *gc, int w, int h)
+{
+	gfx_color_t *color = NULL;
+	gfx_rect_t rect;
+	int i;
+	errno_t rc;
+
+	while (true) {
+		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);
+	}
+}
+
+/** Run demo on console. */
+static errno_t demo_console(void)
 {
 	console_ctrl_t *con = NULL;
-	gfx_color_t *color = NULL;
 	console_gc_t *cgc = NULL;
 	gfx_context_t *gc;
-	gfx_rect_t rect;
-	int i;
 	errno_t rc;
 
@@ -53,44 +94,120 @@
 	con = console_init(stdin, stdout);
 	if (con == NULL)
-		return 1;
+		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_rects(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)
+{
+	console_ctrl_t *con = NULL;
+	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;
+	int vw, vh;
+	errno_t rc;
+
+	printf("Init canvas..\n");
+	con = console_init(stdin, stdout);
+	if (con == NULL)
+		return EIO;
+
+	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 -1;
+	}
+
+	surface = surface_create(vw, vh, pixbuf, 0);
+	if (surface == NULL) {
+		printf("Error creating surface.\n");
+		return -1;
+	}
+
+	canvas = create_canvas(window_root(window), NULL, vw, vh,
+	    surface);
+	if (canvas == NULL) {
+		printf("Error creating canvas.\n");
+		return -1;
+	}
+
+//	sig_connect(&canvas->keyboard_event, NULL, wnd_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;
+
+	gc = canvas_gc_get_ctx(cgc);
+
+	rc = demo_rects(gc, 400, 300);
+	if (rc != EOK)
+		return rc;
+
+	rc = canvas_gc_delete(cgc);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+static void print_syntax(void)
+{
+	printf("syntax: gfxdemo {canvas|console}\n");
+}
+
+int main(int argc, char *argv[])
+{
+	errno_t rc;
+
+	if (argc < 2) {
+		print_syntax();
 		return 1;
-
-	gc = console_gc_get_ctx(cgc);
-
-	while (true) {
-		rc = gfx_color_new_rgb_i16(rand() % 0x10000, rand() % 0x10000,
-		    rand() % 0x10000, &color);
+	}
+
+	if (str_cmp(argv[1], "console") == 0) {
+		rc = demo_console();
 		if (rc != EOK)
 			return 1;
-
-		rc = gfx_set_color(gc, color);
+	} else if (str_cmp(argv[1], "canvas") == 0) {
+		rc = demo_canvas();
 		if (rc != EOK)
 			return 1;
-
-		for (i = 0; i < 10; i++) {
-			rect.p0.x = rand() % 79;
-			rect.p0.y = rand() % 24;
-			rect.p1.x = rect.p0.x + rand() % (79 - rect.p0.x);
-			rect.p1.y = rect.p0.y + rand() % (24 - rect.p0.y);
-
-			rc = gfx_fill_rect(gc, &rect);
-			if (rc != EOK)
-				return 1;
-		}
-
-		gfx_color_delete(color);
-
-		fibril_usleep(500 * 1000);
-	}
-
-	rc = console_gc_delete(cgc);
-	if (rc != EOK)
+	} else {
+		print_syntax();
 		return 1;
-
-	return 0;
+	}
 }
 
Index: uspace/lib/gfx/include/gfx/backend/canvas.h
===================================================================
--- uspace/lib/gfx/include/gfx/backend/canvas.h	(revision 00e8290f885b3dc03a5c5a191983eb336d1e852c)
+++ uspace/lib/gfx/include/gfx/backend/canvas.h	(revision 00e8290f885b3dc03a5c5a191983eb336d1e852c)
@@ -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 GFX canvas backend
+ */
+
+#ifndef _GFX_BACKEND_CANVAS_H
+#define _GFX_BACKEND_CANVAS_H
+
+#include <canvas.h>
+#include <types/gfx/backend/canvas.h>
+#include <types/gfx/context.h>
+#include <types/gfx/ops/context.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/gfx/include/types/gfx/backend/canvas.h
===================================================================
--- uspace/lib/gfx/include/types/gfx/backend/canvas.h	(revision 00e8290f885b3dc03a5c5a191983eb336d1e852c)
+++ uspace/lib/gfx/include/types/gfx/backend/canvas.h	(revision 00e8290f885b3dc03a5c5a191983eb336d1e852c)
@@ -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 libgfx
+ * @{
+ */
+/**
+ * @file GFX canvas backend
+ */
+
+#ifndef _GFX_TYPES_BACKEND_CANVAS_H
+#define _GFX_TYPES_BACKEND_CANVAS_H
+
+struct canvas_gc;
+typedef struct canvas_gc canvas_gc_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/gfx/meson.build
===================================================================
--- uspace/lib/gfx/meson.build	(revision 9be2358aadbef3865ee2b3ab546d8b1f56d79cd6)
+++ uspace/lib/gfx/meson.build	(revision 00e8290f885b3dc03a5c5a191983eb336d1e852c)
@@ -27,5 +27,7 @@
 #
 
+deps = [ 'gui' ]
 src = files(
+	'src/backend/canvas.c',
 	'src/backend/console.c',
 	'src/color.c',
Index: uspace/lib/gfx/private/backend/canvas.h
===================================================================
--- uspace/lib/gfx/private/backend/canvas.h	(revision 00e8290f885b3dc03a5c5a191983eb336d1e852c)
+++ uspace/lib/gfx/private/backend/canvas.h	(revision 00e8290f885b3dc03a5c5a191983eb336d1e852c)
@@ -0,0 +1,63 @@
+/*
+ * 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 GFX canvas backend structure
+ *
+ */
+
+#ifndef _GFX_PRIVATE_BACKEND_CANVAS_H
+#define _GFX_PRIVATE_BACKEND_CANVAS_H
+
+#include <canvas.h>
+#include <draw/surface.h>
+#include <io/pixel.h>
+#include "../context.h"
+
+/** Actual structure of graphics context.
+ *
+ * This is private to libgfx. 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;
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/gfx/src/backend/canvas.c
===================================================================
--- uspace/lib/gfx/src/backend/canvas.c	(revision 00e8290f885b3dc03a5c5a191983eb336d1e852c)
+++ uspace/lib/gfx/src/backend/canvas.c	(revision 00e8290f885b3dc03a5c5a191983eb336d1e852c)
@@ -0,0 +1,163 @@
+/*
+ * 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 GFX canvas backend
+ *
+ * This implements a graphics context over a libgui canvas.
+ * This is just for experimentation purposes and its kind of backwards.
+ */
+
+#include <gfx/backend/canvas.h>
+#include <gfx/context.h>
+#include <gfx/render.h>
+#include <io/pixel.h>
+#include <stdlib.h>
+#include "../../private/backend/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 *);
+
+gfx_context_ops_t canvas_gc_ops = {
+	.set_color = canvas_gc_set_color,
+	.fill_rect = canvas_gc_fill_rect
+};
+
+/** 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;
+
+	cgc->color = PIXEL(0, color->r >> 8, color->g >> 8, color->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;
+	int 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;
+}
+
+/** @}
+ */
Index: uspace/lib/meson.build
===================================================================
--- uspace/lib/meson.build	(revision 9be2358aadbef3865ee2b3ab546d8b1f56d79cd6)
+++ uspace/lib/meson.build	(revision 00e8290f885b3dc03a5c5a191983eb336d1e852c)
@@ -52,5 +52,4 @@
 	'fmtutil',
 	'fs',
-	'gfx',
 	'graph',
 	'http',
@@ -83,4 +82,5 @@
 	'virtio',
 
+	'gfx',
 	'ieee80211',
 ]
