diff --git a/single-pixel-buffer-v1.xml b/single-pixel-buffer-v1.xml
new file mode 100644
index 0000000..b157155
--- /dev/null
+++ b/single-pixel-buffer-v1.xml
@@ -0,0 +1,69 @@
+
+
+
+ Copyright © 2022 Simon Ser
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+
+
+ This protocol extension allows clients to create single-pixel buffers.
+
+ Compositors supporting this protocol extension should also support the
+ viewporter protocol extension. Clients may use viewporter to scale a
+ single-pixel buffer to a desired size.
+
+ Warning! The protocol described in this file is currently in the testing
+ phase. Backward compatible changes may be added together with the
+ corresponding interface version bump. Backward incompatible changes can
+ only be done by creating a new major version of the extension.
+
+
+
+
+ The wp_single_pixel_buffer_manager_v1 interface is a factory for
+ single-pixel buffers.
+
+
+
+
+ Destroy the wp_single_pixel_buffer_manager_v1 object.
+
+ The child objects created via this interface are unaffected.
+
+
+
+
+
+ Create a single-pixel buffer from four 32-bit RGBA values.
+
+ Unless specified in another protocol extension, the RGBA values use
+ pre-multiplied alpha.
+
+ The width and height of the buffer are 1.
+
+
+
+
+
+
+
+
+
diff --git a/single_pixel_buffer.c b/single_pixel_buffer.c
new file mode 100644
index 0000000..95dee4e
--- /dev/null
+++ b/single_pixel_buffer.c
@@ -0,0 +1,228 @@
+/* Wayland compositor running on top of an X server.
+
+Copyright (C) 2022 to various contributors.
+
+This file is part of 12to11.
+
+12to11 is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+12to11 is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with 12to11. If not, see . */
+
+#include "compositor.h"
+#include "single-pixel-buffer-v1.h"
+#include "string.h"
+
+typedef struct _Buffer Buffer;
+
+struct _Buffer
+{
+ /* The ExtBuffer associated with this buffer. */
+ ExtBuffer buffer;
+
+ /* The rendering buffer associated with this buffer. */
+ RenderBuffer render_buffer;
+
+ /* The wl_resource corresponding to this buffer. */
+ struct wl_resource *resource;
+
+ /* The number of references to this buffer. */
+ int refcount;
+};
+
+/* The global wp_single_pixel_buffer_manager_v1 resource. */
+static struct wl_global *single_pixel_buffer_global;
+
+static void
+DestroyBuffer (struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy (resource);
+}
+
+static const struct wl_buffer_interface single_pixel_buffer_impl =
+ {
+ .destroy = DestroyBuffer,
+ };
+
+static void
+RetainBuffer (Buffer *buffer)
+{
+ buffer->refcount++;
+}
+
+static void
+DereferenceBuffer (Buffer *buffer)
+{
+ if (--buffer->refcount)
+ return;
+
+ RenderFreeSinglePixelBuffer (buffer->render_buffer);
+ ExtBufferDestroy (&buffer->buffer);
+ XLFree (buffer);
+}
+
+static void
+ReleaseBufferFunc (ExtBuffer *buffer)
+{
+ if (((Buffer *) buffer)->resource)
+ wl_buffer_send_release (((Buffer *) buffer)->resource);
+}
+
+static void
+RetainBufferFunc (ExtBuffer *buffer)
+{
+ RetainBuffer ((Buffer *) buffer);
+}
+
+static void
+DereferenceBufferFunc (ExtBuffer *buffer)
+{
+ DereferenceBuffer ((Buffer *) buffer);
+}
+
+static RenderBuffer
+GetBufferFunc (ExtBuffer *buffer)
+{
+ return ((Buffer *) buffer)->render_buffer;
+}
+
+static unsigned int
+WidthFunc (ExtBuffer *buffer)
+{
+ /* Single pixel buffers are always 1x1. */
+ return 1;
+}
+
+static unsigned int
+HeightFunc (ExtBuffer *buffer)
+{
+ /* Single pixel buffers are always 1x1. */
+ return 1;
+}
+
+static void
+PrintBuffer (Buffer *buffer)
+{
+ /* Not implemented. */
+}
+
+static void
+PrintBufferFunc (ExtBuffer *buffer)
+{
+ PrintBuffer ((Buffer *) buffer);
+}
+
+static void
+HandleResourceDestroy (struct wl_resource *resource)
+{
+ Buffer *buffer;
+
+ buffer = wl_resource_get_user_data (resource);
+ buffer->resource = NULL;
+
+ DereferenceBuffer (buffer);
+}
+
+
+
+static void
+Destroy (struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy (resource);
+}
+
+void
+CreateU32RgbaBuffer (struct wl_client *client, struct wl_resource *resource,
+ uint32_t id, uint32_t r, uint32_t g, uint32_t b,
+ uint32_t a)
+{
+ Buffer *buffer;
+ Bool error;
+
+ buffer = XLSafeMalloc (sizeof *buffer);
+
+ if (!buffer)
+ {
+ wl_resource_post_no_memory (resource);
+ return;
+ }
+
+ memset (buffer, 0, sizeof *buffer);
+ buffer->resource = wl_resource_create (client, &wl_buffer_interface,
+ wl_resource_get_version (resource),
+ id);
+
+ if (!buffer->resource)
+ {
+ out_of_memory:
+ wl_resource_post_no_memory (resource);
+ XLFree (buffer);
+ return;
+ }
+
+ /* Now, create the render target. */
+ error = False;
+ buffer->render_buffer = RenderBufferFromSinglePixel (r, g, b, a,
+ &error);
+
+ if (error)
+ /* We probably ran out of memory. */
+ goto out_of_memory;
+
+ buffer->refcount = 1;
+
+ /* Initialize function pointers. */
+ buffer->buffer.funcs.retain = RetainBufferFunc;
+ buffer->buffer.funcs.dereference = DereferenceBufferFunc;
+ buffer->buffer.funcs.get_buffer = GetBufferFunc;
+ buffer->buffer.funcs.width = WidthFunc;
+ buffer->buffer.funcs.height = HeightFunc;
+ buffer->buffer.funcs.release = ReleaseBufferFunc;
+ buffer->buffer.funcs.print_buffer = PrintBufferFunc;
+
+ wl_resource_set_implementation (buffer->resource, &single_pixel_buffer_impl,
+ buffer, HandleResourceDestroy);
+}
+
+static const struct wp_single_pixel_buffer_manager_v1_interface manager_impl =
+ {
+ .destroy = Destroy,
+ .create_u32_rgba_buffer = CreateU32RgbaBuffer,
+ };
+
+static void
+HandleBind (struct wl_client *client, void *data, uint32_t version,
+ uint32_t id)
+{
+ struct wl_resource *resource;
+
+ resource = wl_resource_create (client,
+ &wp_single_pixel_buffer_manager_v1_interface,
+ version, id);
+
+ if (!resource)
+ {
+ wl_client_post_no_memory (client);
+ return;
+ }
+
+ wl_resource_set_implementation (resource, &manager_impl,
+ NULL, NULL);
+}
+
+void
+XLInitSinglePixelBuffer (void)
+{
+ single_pixel_buffer_global
+ = wl_global_create (compositor.wl_display,
+ &wp_single_pixel_buffer_manager_v1_interface,
+ 1, NULL, HandleBind);
+}