commit 2a4a013

shrub  ·  2026-08-09 23:01:22 +0000 UTC
parent ac63183
make drm optional, add fb video on linux fbdev
27 files changed,  +690, -42
+7, -4
 1@@ -1,7 +1,7 @@
 2 neuswc
 3 ------
 4 
 5-neuswc is a fork of [swc](https://github.com/michaelforney/swc/) created by [wayland.fyi](https://wayland.fyi). it provides an easy C library interface to make a wayland compositor. it is much smaller than alternatives like wlroots, and easier to use. it is also portable to linux, freebsd, netbsd, and openbsd. if you want tohelp with that (or anything else) submit a patch to the [mailing list](https://lists.sr.ht/~shrub900/neuswc)
 6+neuswc is a fork of [swc](https://github.com/michaelforney/swc/) created by [wayland.fyi](https://wayland.fyi). it provides an easy C library interface to make a wayland compositor. it is much smaller than alternatives like wlroots, and easier to use. it is also portable to linux, freebsd, netbsd, and openbsd. if you want to help with that (or anything else) submit a patch to the [mailing list](https://lists.sr.ht/~shrub900/neuswc)
 7 
 8 you can view a list of some compositors made with neuswc at the [wayland.fyi website.](https://wayland.fyi)
 9 
10@@ -20,6 +20,7 @@ neu features
11 - screenshots
12 - layer shell support
13 - window decorations
14+- framebuffer support
15 
16 neuswc is in active development, we plan to add many more features, and increase compatibility with new wayland protocols.
17 
18@@ -32,7 +33,8 @@ you will need:
19 - pkg-config
20 - wayland-scanner, wayland-server, wayland-client
21 - wayland-server, wayland-client
22-- libdrm, pixman, xkbcommon
23+- libdrm (if  building with DRM support) 
24+- pixman, xkbcommon
25 - [neuwld](https://git.sr.ht/~shrub900/neuwld)
26 - libinput on Linux and wscons on BSD
27 - xcb, xcb-composite, xcb-ewmh and xcb-icccm if you want Xwayland support.
28@@ -52,5 +54,6 @@ an extremely large thank you to [michael forney](https://mforney.org) for creati
29 
30 repositories
31 ------------
32-[srcdump](https://srcdump.net/shrub/neuswc)
33-[sourcehut](https://git.sr.ht/~shrub900/neuswc)
34+
35+- [srcdump](https://srcdump.net/shrub/neuswc)
36+- [sourcehut](https://git.sr.ht/~shrub900/neuswc)
+8, -0
 1@@ -50,3 +50,11 @@ device_is_drm(dev_t rdev)
 2 {
 3 	return major(rdev) == DRM_MAJOR;
 4 }
 5+
 6+#ifdef ENABLE_FBDEV
 7+bool
 8+device_is_fbdev(dev_t rdev)
 9+{
10+	return major(rdev) == FB_MAJOR;
11+}
12+#endif
+4, -0
1@@ -33,4 +33,8 @@ bool device_is_tty(dev_t);
2 
3 bool device_is_drm(dev_t);
4 
5+#ifdef ENABLE_FBDEV
6+bool device_is_fbdev(dev_t);
7+#endif
8+
9 #endif
+21, -1
 1@@ -34,6 +34,7 @@
 2 #include <poll.h>
 3 #include <signal.h>
 4 #include <spawn.h>
 5+#include <stdarg.h>
 6 #include <stdbool.h>
 7 #include <stdio.h>
 8 #include <stdlib.h>
 9@@ -69,7 +70,9 @@
10 #include <dev/wscons/wsdisplay_usl_io.h>
11 #endif
12 
13+#ifdef ENABLE_DRM
14 #include <xf86drm.h>
15+#endif
16 
17 #define ARRAY_LENGTH(array) (sizeof(array) / sizeof(array)[0])
18 
19@@ -81,7 +84,9 @@ deactivate(void);
20 static bool nflag;
21 static int sigfd[2], sock[2];
22 static int input_fds[128], num_input_fds;
23+#ifdef ENABLE_DRM
24 static int drm_fds[16], num_drm_fds;
25+#endif
26 static int tty_fd;
27 static bool active;
28 
29@@ -123,6 +128,7 @@ die(const char *format, ...)
30 static void
31 start_devices(void)
32 {
33+#ifdef ENABLE_DRM
34 	int i;
35 
36 	for (i = 0; i < num_drm_fds; ++i) {
37@@ -130,6 +136,7 @@ start_devices(void)
38 			die("failed to set DRM master");
39 		}
40 	}
41+#endif
42 }
43 
44 static void
45@@ -137,11 +144,13 @@ stop_devices(bool fatal)
46 {
47 	int i;
48 
49+#ifdef ENABLE_DRM
50 	for (i = 0; i < num_drm_fds; ++i) {
51 		if (drmDropMaster(drm_fds[i]) < 0 && fatal) {
52 			die("drmDropMaster:");
53 		}
54 	}
55+#endif
56 	for (i = 0; i < num_input_fds; ++i) {
57 #ifdef EVIOCREVOKE
58 		if (ioctl(input_fds[i], EVIOCREVOKE, 0) < 0 && errno != ENODEV &&
59@@ -278,13 +287,24 @@ handle_socket_data(int socket)
60 			#endif
61 			input_fds[num_input_fds++] = fd;
62 		} else if (device_is_drm(st.st_rdev)) {
63+#ifdef ENABLE_DRM
64 			if (num_drm_fds == ARRAY_LENGTH(drm_fds)) {
65 				fprintf(stderr, "too many DRM devices opened\n");
66 				goto fail;
67 			}
68 			drm_fds[num_drm_fds++] = fd;
69+#else
70+			fprintf(stderr, "DRM device requested by non-DRM backend\n");
71+			goto fail;
72+#endif
73+#ifdef ENABLE_FBDEV
74+		} else if (device_is_fbdev(st.st_rdev)) {
75+			if (!active) {
76+				goto fail;
77+			}
78+#endif
79 		} else {
80-			fprintf(stderr, "requested fd is not a DRM or input device\n");
81+			fprintf(stderr, "requested fd is not a video or input device\n");
82 			goto fail;
83 		}
84 		break;
+1, -1
1@@ -1,7 +1,7 @@
2 launch_protocol = files('protocol.c')
3 executable('swc-launch',
4   ['launch.c', launch_protocol, 'devmajor-@0@.c'.format(os)],
5-  dependencies: requires_private[0], # libdrm
6+  dependencies: video_backend == 'drm' ? drm : [],
7   install: true,
8   install_mode: 'rwsr-xr-x',
9 )
+17, -0
 1@@ -0,0 +1,17 @@
 2+#ifndef SWC_BACKEND_H
 3+#define SWC_BACKEND_H
 4+
 5+#include <stdint.h>
 6+
 7+struct wld_context;
 8+struct wld_renderer;
 9+
10+/* rendering state shared by the drm and fb backends */
11+struct swc_backend {
12+	struct wld_context *context;
13+	struct wld_renderer *renderer;
14+	uint32_t cursor_width;
15+	uint32_t cursor_height;
16+};
17+
18+#endif
+26, -15
  1@@ -28,9 +28,12 @@
  2  */
  3 
  4 #include "compositor.h"
  5+#include "backend.h"
  6 #include "data_device_manager.h"
  7 #include "decor.h"
  8+#ifdef ENABLE_DRM
  9 #include "drm.h"
 10+#endif
 11 #include "event.h"
 12 #include "internal.h"
 13 #include "launch.h"
 14@@ -52,7 +55,9 @@
 15 #include <limits.h>
 16 #include <stdio.h>
 17 #include <stdlib.h>
 18+#ifdef ENABLE_DRM
 19 #include <wld/drm.h>
 20+#endif
 21 #include <wld/wld.h>
 22 #include <xkbcommon/xkbcommon-keysyms.h>
 23 
 24@@ -214,8 +219,14 @@ target_new(struct screen *screen)
 25 	}
 26 
 27 	target->surface =
 28-	    wld_create_surface(swc.drm->context, geom->width, geom->height,
 29-	                       WLD_FORMAT_XRGB8888, WLD_DRM_FLAG_SCANOUT);
 30+	    wld_create_surface(swc.backend->context, geom->width, geom->height,
 31+	                       WLD_FORMAT_XRGB8888,
 32+#ifdef ENABLE_DRM
 33+	                       WLD_DRM_FLAG_SCANOUT
 34+#else
 35+	                       WLD_FLAG_MAP
 36+#endif
 37+	    );
 38 
 39 	if (!target->surface) {
 40 		goto error1;
 41@@ -290,7 +301,7 @@ repaint_view(struct target *target, struct compositor_view *view,
 42 		pixman_region32_translate(&buffer_damage,
 43 		                          -geom->x + view->buffer_offset_x,
 44 		                          -geom->y + view->buffer_offset_y);
 45-		wld_copy_region(swc.drm->renderer, view->buffer, buf_x - target_geom->x,
 46+		wld_copy_region(swc.backend->renderer, view->buffer, buf_x - target_geom->x,
 47 		                buf_y - target_geom->y, &buffer_damage);
 48 	}
 49 
 50@@ -321,12 +332,12 @@ repaint_view(struct target *target, struct compositor_view *view,
 51 	if (view->border.outwidth > 0 && pixman_region32_not_empty(&out_border)) {
 52 		pixman_region32_translate(&out_border, -target_geom->x,
 53 		                          -target_geom->y);
 54-		wld_fill_region(swc.drm->renderer, view->border.outcolor, &out_border);
 55+		wld_fill_region(swc.backend->renderer, view->border.outcolor, &out_border);
 56 	}
 57 
 58 	if (view->border.inwidth > 0 && pixman_region32_not_empty(&in_border)) {
 59 		pixman_region32_translate(&in_border, -target_geom->x, -target_geom->y);
 60-		wld_fill_region(swc.drm->renderer, view->border.incolor, &in_border);
 61+		wld_fill_region(swc.backend->renderer, view->border.incolor, &in_border);
 62 	}
 63 
 64 	pixman_region32_fini(&border_damage);
 65@@ -336,7 +347,7 @@ repaint_view(struct target *target, struct compositor_view *view,
 66 
 67 	if ((view->decor.top || view->decor.right || view->decor.bottom ||
 68 	     view->decor.left)) {
 69-		decor_repaint(swc.drm->renderer, target_geom, view, damage);
 70+		decor_repaint(swc.backend->renderer, target_geom, view, damage);
 71 	}
 72 }
 73 
 74@@ -400,12 +411,12 @@ renderer_repaint(struct target *target, pixman_region32_t *damage,
 75 	      target->view->geometry.x, target->view->geometry.y,
 76 	      target->view->geometry.width, target->view->geometry.height);
 77 
 78-	wld_set_target_surface(swc.drm->renderer, target->surface);
 79+	wld_set_target_surface(swc.backend->renderer, target->surface);
 80 
 81 	if (pixman_region32_not_empty(base_damage)) {
 82 		pixman_region32_translate(base_damage, -target->view->geometry.x,
 83 		                          -target->view->geometry.y);
 84-		wld_fill_region(swc.drm->renderer, DEFAULT_BG,
 85+		wld_fill_region(swc.backend->renderer, DEFAULT_BG,
 86 		                base_damage);
 87 	}
 88 
 89@@ -416,9 +427,9 @@ renderer_repaint(struct target *target, pixman_region32_t *damage,
 90 		}
 91 	}
 92 
 93-	draw_overlays(swc.drm->renderer, target_geom);
 94+	draw_overlays(swc.backend->renderer, target_geom);
 95 
 96-	wld_flush(swc.drm->renderer);
 97+	wld_flush(swc.backend->renderer);
 98 }
 99 
100 static int
101@@ -427,7 +438,7 @@ renderer_attach(struct compositor_view *view, struct wld_buffer *client_buffer)
102 	struct wld_buffer *buffer;
103 	bool was_proxy = view->buffer != view->base.buffer;
104 	bool needs_proxy =
105-	    client_buffer && !(wld_capabilities(swc.drm->renderer, client_buffer) &
106+	    client_buffer && !(wld_capabilities(swc.backend->renderer, client_buffer) &
107 	                       WLD_CAPABILITY_READ);
108 	bool resized = view->buffer && client_buffer &&
109 	               (view->buffer->width != client_buffer->width ||
110@@ -440,7 +451,7 @@ renderer_attach(struct compositor_view *view, struct wld_buffer *client_buffer)
111 			if (!was_proxy || resized) {
112 				DEBUG("Creating a proxy buffer\n");
113 				buffer = wld_create_buffer(
114-				    swc.drm->context, client_buffer->width,
115+				    swc.backend->context, client_buffer->width,
116 				    client_buffer->height, client_buffer->format, WLD_FLAG_MAP);
117 
118 				if (!buffer) {
119@@ -1656,9 +1667,9 @@ update_screen(struct screen *screen)
120 
121 		pixman_region32_t full;
122 		pixman_region32_init_rect(&full, 0, 0, geom->width, geom->height);
123-		wld_set_target_surface(swc.drm->renderer, target->surface);
124-		wld_copy_region(swc.drm->renderer, zoomed, 0, 0, &full);
125-		wld_flush(swc.drm->renderer);
126+		wld_set_target_surface(swc.backend->renderer, target->surface);
127+		wld_copy_region(swc.backend->renderer, zoomed, 0, 0, &full);
128+		wld_flush(swc.backend->renderer);
129 		pixman_region32_fini(&full);
130 
131 		wld_buffer_unreference(zoomed);
+2, -1
 1@@ -1,4 +1,5 @@
 2 #include "decor.h"
 3+#include "backend.h"
 4 
 5 #include "compositor.h"
 6 #include "drm.h"
 7@@ -210,7 +211,7 @@ copy_decor_part(struct decor_part_buffer *dst, const struct swc_decor_part *src)
 8 	dst->height = src->height;
 9 	dst->stride = src->stride;
10 	/* DRM renderers only allow read support for their native buffers not pixman/shmbuffers */
11-	dst->buffer = wld_create_buffer(swc.drm->context, src->width, src->height,
12+	dst->buffer = wld_create_buffer(swc.backend->context, src->width, src->height,
13 	                                WLD_FORMAT_ARGB8888, WLD_FLAG_MAP);
14 	if (!dst->buffer) {
15 		free(dst->data);
+5, -0
 1@@ -290,6 +290,9 @@ drm_initialize(void)
 2 		val = 64;
 3 	}
 4 	swc.drm->cursor_h = val;
 5+	swc.backend = &swc.drm->backend;
 6+	swc.backend->cursor_width = swc.drm->cursor_w;
 7+	swc.backend->cursor_height = swc.drm->cursor_h;
 8 
 9 	drm.path = drmGetRenderDeviceNameFromFd(swc.drm->fd);
10 	if (!drm.path) {
11@@ -306,6 +309,8 @@ drm_initialize(void)
12 		ERROR("Could not create WLD DRM renderer\n");
13 		goto error2;
14 	}
15+	swc.backend->context = swc.drm->context;
16+	swc.backend->renderer = swc.drm->renderer;
17 
18 	drm.event_source = wl_event_loop_add_fd(
19 	    swc.event_loop, swc.drm->fd, WL_EVENT_READABLE, &handle_data, NULL);
+3, -0
 1@@ -1,6 +1,8 @@
 2 #ifndef SWC_DRM_H
 3 #define SWC_DRM_H
 4 
 5+#include "backend.h"
 6+
 7 #include <stdbool.h>
 8 #include <stdint.h>
 9 
10@@ -12,6 +14,7 @@ struct drm_handler {
11 };
12 
13 struct swc_drm {
14+	struct swc_backend backend;
15 	int fd;
16 	uint32_t cursor_w, cursor_h;
17 	struct wld_context *context;
+142, -0
  1@@ -0,0 +1,142 @@
  2+#include "fb.h"
  3+#include "internal.h"
  4+#include "output.h"
  5+#include "pointer.h"
  6+#include "screen.h"
  7+#include "seat.h"
  8+#include "util.h"
  9+
 10+#include <stdlib.h>
 11+#include <string.h>
 12+#include <wayland-server.h>
 13+#include <wld/pixman.h>
 14+#include <wld/wld.h>
 15+
 16+struct swc_fb swc_fb;
 17+
 18+/* for the cursor */ 
 19+static uint32_t
 20+blend(uint32_t background, uint32_t foreground)
 21+{
 22+	uint32_t alpha = foreground >> 24;
 23+	uint32_t inverse = 255 - alpha;
 24+	uint32_t red = ((foreground >> 16) & 0xff) +
 25+	               (((background >> 16) & 0xff) * inverse + 127) / 255;
 26+	uint32_t green = ((foreground >> 8) & 0xff) +
 27+	                 (((background >> 8) & 0xff) * inverse + 127) / 255;
 28+	uint32_t blue = (foreground & 0xff) +
 29+	                ((background & 0xff) * inverse + 127) / 255;
 30+	return (MIN(red, 255) << 16) | (MIN(green, 255) << 8) | MIN(blue, 255);
 31+}
 32+
 33+bool
 34+fb_initialize(void)
 35+{
 36+	if (!fbdev_initialize(&swc_fb)) {
 37+		return false;
 38+	}
 39+	swc_fb.pitch = (size_t)swc_fb.width * sizeof(*swc_fb.pixels);
 40+	swc_fb.pixels = calloc(swc_fb.height, swc_fb.pitch);
 41+	if (!swc_fb.pixels) {
 42+		goto error0;
 43+	}
 44+	swc_fb.backend.context = wld_pixman_create_context();
 45+	if (!swc_fb.backend.context) {
 46+		goto error1;
 47+	}
 48+	swc_fb.backend.renderer = wld_create_renderer(swc_fb.backend.context);
 49+	if (!swc_fb.backend.renderer) {
 50+		goto error2;
 51+	}
 52+	swc_fb.backend.cursor_width = 64;
 53+	swc_fb.backend.cursor_height = 64;
 54+	swc.backend = &swc_fb.backend;
 55+	return true;
 56+
 57+error2:
 58+	wld_destroy_context(swc_fb.backend.context);
 59+error1:
 60+	free(swc_fb.pixels);
 61+error0:
 62+	fbdev_finalize(&swc_fb);
 63+	return false;
 64+}
 65+
 66+void
 67+fb_finalize(void)
 68+{
 69+	wld_destroy_renderer(swc_fb.backend.renderer);
 70+	wld_destroy_context(swc_fb.backend.context);
 71+	free(swc_fb.pixels);
 72+	fbdev_finalize(&swc_fb);
 73+}
 74+
 75+bool
 76+fb_create_screens(struct wl_list *screens)
 77+{
 78+	struct output *output;
 79+	struct screen *screen;
 80+
 81+	output = output_new_fb(swc_fb.width, swc_fb.height, "fbdev-0");
 82+	if (!output) {
 83+		return false;
 84+	}
 85+	screen = screen_new(output);
 86+	if (!screen) {
 87+		output_destroy(output);
 88+		return false;
 89+	}
 90+	screen->id = 0;
 91+	output->screen = screen;
 92+	wl_list_insert(screens, &screen->link);
 93+	return true;
 94+}
 95+
 96+bool
 97+fb_present(struct wld_buffer *buffer, int32_t origin_x, int32_t origin_y)
 98+{
 99+	struct pointer *pointer = swc.seat ? swc.seat->pointer : NULL;
100+	struct wld_buffer *cursor = NULL;
101+	int32_t cursor_x = 0, cursor_y = 0;
102+	uint32_t width, height, x, y;
103+	bool cursor_mapped = false;
104+
105+	if (!buffer || !wld_map(buffer)) {
106+		return false;
107+	}
108+	if (pointer && pointer->cursor.view.buffer) {
109+		cursor = pointer->cursor.buffer;
110+		cursor_x = pointer->cursor.view.geometry.x - origin_x;
111+		cursor_y = pointer->cursor.view.geometry.y - origin_y;
112+		cursor_mapped = wld_map(cursor);
113+	}
114+
115+	width = buffer->width < swc_fb.width ? buffer->width : swc_fb.width;
116+	height = buffer->height < swc_fb.height ? buffer->height : swc_fb.height;
117+	if (width != swc_fb.width || height != swc_fb.height) {
118+		memset(swc_fb.pixels, 0, swc_fb.height * swc_fb.pitch);
119+	}
120+	for (y = 0; y < height; ++y) {
121+		uint32_t *source = (uint32_t *)((uint8_t *)buffer->map +
122+		                                (size_t)y * buffer->pitch);
123+		uint32_t *destination = (uint32_t *)((uint8_t *)swc_fb.pixels +
124+		                                     (size_t)y * swc_fb.pitch);
125+		for (x = 0; x < width; ++x) {
126+			uint32_t pixel = source[x];
127+			int32_t cx = (int32_t)x - cursor_x;
128+			int32_t cy = (int32_t)y - cursor_y;
129+			if (cursor_mapped && cx >= 0 && cy >= 0 &&
130+			    (uint32_t)cx < cursor->width && (uint32_t)cy < cursor->height) {
131+				uint32_t foreground = *(uint32_t *)((uint8_t *)cursor->map +
132+				    (size_t)cy * cursor->pitch + (size_t)cx * 4);
133+				pixel = blend(pixel, foreground);
134+			}
135+			destination[x] = pixel;
136+		}
137+	}
138+	if (cursor_mapped) {
139+		wld_unmap(cursor);
140+	}
141+	wld_unmap(buffer);
142+	return fbdev_present(&swc_fb);
143+}
+32, -0
 1@@ -0,0 +1,32 @@
 2+#ifndef SWC_FB_H
 3+#define SWC_FB_H
 4+
 5+#include "backend.h"
 6+
 7+#include <stdbool.h>
 8+#include <stddef.h>
 9+#include <stdint.h>
10+
11+struct wl_list;
12+struct wld_buffer;
13+
14+struct swc_fb {
15+	struct swc_backend backend;
16+	uint32_t width;
17+	uint32_t height;
18+	uint32_t *pixels;
19+	size_t pitch;
20+};
21+
22+extern struct swc_fb swc_fb;
23+
24+bool fb_initialize(void);
25+void fb_finalize(void);
26+bool fb_create_screens(struct wl_list *screens);
27+bool fb_present(struct wld_buffer *buffer, int32_t x, int32_t y);
28+
29+bool fbdev_initialize(struct swc_fb *fb);
30+void fbdev_finalize(struct swc_fb *fb);
31+bool fbdev_present(struct swc_fb *fb);
32+
33+#endif
+185, -0
  1@@ -0,0 +1,185 @@
  2+#include "fb.h"
  3+#include "launch.h"
  4+#include "util.h"
  5+
  6+#include <fcntl.h>
  7+#include <linux/fb.h>
  8+#include <stdint.h>
  9+#include <string.h>
 10+#include <sys/ioctl.h>
 11+#include <sys/mman.h>
 12+#include <unistd.h>
 13+
 14+struct fb_channel {
 15+	uint8_t offset;
 16+	uint8_t length;
 17+};
 18+
 19+static struct {
 20+	int fd;
 21+	void *memory;
 22+	size_t memory_length;
 23+	size_t memory_offset;
 24+	uint32_t pitch;
 25+	uint8_t bits_per_pixel;
 26+	struct fb_channel red;
 27+	struct fb_channel green;
 28+	struct fb_channel blue;
 29+} fbdev;
 30+
 31+static struct fb_channel
 32+channel_from_fbdev(struct fb_bitfield field)
 33+{
 34+	return (struct fb_channel) {
 35+	    .offset = field.offset,
 36+	    .length = field.length,
 37+	};
 38+}
 39+
 40+bool
 41+fbdev_initialize(struct swc_fb *fb)
 42+{
 43+	struct fb_fix_screeninfo fixed;
 44+	struct fb_var_screeninfo variable;
 45+	size_t visible_offset;
 46+
 47+	memset(&fbdev, 0, sizeof(fbdev));
 48+	fbdev.fd = launch_open_device(FBDEV_DEVICE, O_RDWR | O_CLOEXEC);
 49+	if (fbdev.fd < 0) {
 50+		ERROR("Could not open framebuffer device %s\n", FBDEV_DEVICE);
 51+		return false;
 52+	}
 53+	if (ioctl(fbdev.fd, FBIOGET_FSCREENINFO, &fixed) < 0 ||
 54+	    ioctl(fbdev.fd, FBIOGET_VSCREENINFO, &variable) < 0) {
 55+		ERROR("Could not query framebuffer device %s\n", FBDEV_DEVICE);
 56+		goto error;
 57+	}
 58+	if (variable.bits_per_pixel != 16 && variable.bits_per_pixel != 24 &&
 59+	    variable.bits_per_pixel != 32) {
 60+		ERROR("Unsupported fbdev depth: %u bits per pixel\n",
 61+		      variable.bits_per_pixel);
 62+		goto error;
 63+	}
 64+	if (fixed.type != FB_TYPE_PACKED_PIXELS ||
 65+	    (fixed.visual != FB_VISUAL_TRUECOLOR &&
 66+	     fixed.visual != FB_VISUAL_DIRECTCOLOR)) {
 67+		ERROR("Unsupported fbdev framebuffer type or visual\n");
 68+		goto error;
 69+	}
 70+
 71+	if (!variable.xres || !variable.yres || variable.xres > UINT16_MAX ||
 72+	    variable.yres > UINT16_MAX ||
 73+	    variable.red.msb_right || variable.green.msb_right ||
 74+	    variable.blue.msb_right ||
 75+	    variable.red.length > 16 || variable.green.length > 16 ||
 76+	    variable.blue.length > 16 ||
 77+	    variable.red.offset + variable.red.length > variable.bits_per_pixel ||
 78+	    variable.green.offset + variable.green.length > variable.bits_per_pixel ||
 79+	    variable.blue.offset + variable.blue.length > variable.bits_per_pixel) {
 80+		ERROR("Unsupported fbdev geometry or channel layout\n");
 81+		goto error;
 82+	}
 83+	fbdev.memory_length = fixed.smem_len;
 84+	fbdev.memory = mmap(NULL, fbdev.memory_length, PROT_READ | PROT_WRITE,
 85+	                  MAP_SHARED, fbdev.fd, 0);
 86+	if (fbdev.memory == MAP_FAILED) {
 87+		fbdev.memory = NULL;
 88+		ERROR("Could not map framebuffer device %s\n", FBDEV_DEVICE);
 89+		goto error;
 90+	}
 91+
 92+	fb->width = variable.xres;
 93+	fb->height = variable.yres;
 94+	fbdev.pitch = fixed.line_length;
 95+	fbdev.bits_per_pixel = variable.bits_per_pixel;
 96+	fbdev.red = channel_from_fbdev(variable.red);
 97+	fbdev.green = channel_from_fbdev(variable.green);
 98+	fbdev.blue = channel_from_fbdev(variable.blue);
 99+	visible_offset = (size_t)variable.yoffset * fbdev.pitch +
100+	                 (size_t)variable.xoffset * variable.bits_per_pixel / 8;
101+	if (fixed.line_length < (size_t)variable.xres * variable.bits_per_pixel / 8 ||
102+	    visible_offset >= fbdev.memory_length ||
103+	    (size_t)(variable.yres - 1) * fixed.line_length >
104+	        fbdev.memory_length - visible_offset ||
105+	    (size_t)variable.xres * variable.bits_per_pixel / 8 >
106+	        fbdev.memory_length - visible_offset -
107+	            (size_t)(variable.yres - 1) * fixed.line_length) {
108+		ERROR("fbdev visible buffer lies outside its mapping\n");
109+		goto error;
110+	}
111+	fbdev.memory_offset = visible_offset;
112+	return true;
113+
114+error:
115+	if (fbdev.memory) {
116+		munmap(fbdev.memory, fbdev.memory_length);
117+	}
118+	close(fbdev.fd);
119+	fbdev.fd = -1;
120+	return false;
121+}
122+
123+void
124+fbdev_finalize(struct swc_fb *fb)
125+{
126+	(void)fb;
127+	munmap(fbdev.memory, fbdev.memory_length);
128+	close(fbdev.fd);
129+}
130+
131+static uint32_t
132+channel(uint8_t value, struct fb_channel field)
133+{
134+	uint32_t maximum;
135+
136+	if (!field.length) {
137+		return 0;
138+	}
139+	maximum = field.length >= 32 ? UINT32_MAX : (1u << field.length) - 1;
140+	return (((uint32_t)value * maximum + 127) / 255) << field.offset;
141+}
142+
143+static uint32_t
144+native_pixel(uint32_t pixel)
145+{
146+	return channel(pixel >> 16, fbdev.red) |
147+	       channel(pixel >> 8, fbdev.green) |
148+	       channel(pixel, fbdev.blue);
149+}
150+
151+static void
152+store_pixel(uint8_t *destination, uint32_t pixel)
153+{
154+	uint32_t native = native_pixel(pixel);
155+
156+	if (fbdev.bits_per_pixel == 24) {
157+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
158+		destination[0] = native >> 16;
159+		destination[1] = native >> 8;
160+		destination[2] = native;
161+#else
162+		destination[0] = native;
163+		destination[1] = native >> 8;
164+		destination[2] = native >> 16;
165+#endif
166+	} else {
167+		memcpy(destination, &native, fbdev.bits_per_pixel / 8);
168+	}
169+}
170+
171+bool
172+fbdev_present(struct swc_fb *fb)
173+{
174+	uint32_t bytes = fbdev.bits_per_pixel / 8;
175+	uint32_t x, y;
176+
177+	for (y = 0; y < fb->height; ++y) {
178+		uint32_t *source = (uint32_t *)((uint8_t *)fb->pixels + y * fb->pitch);
179+		uint8_t *destination = (uint8_t *)fbdev.memory + fbdev.memory_offset +
180+		                       (size_t)y * fbdev.pitch;
181+		for (x = 0; x < fb->width; ++x) {
182+			store_pixel(destination + (size_t)x * bytes, source[x]);
183+		}
184+	}
185+	return true;
186+}
+3, -0
 1@@ -44,7 +44,10 @@ struct swc {
 2 	struct wl_list screens;
 3 	struct swc_compositor *const compositor;
 4 	struct swc_shm *shm;
 5+#ifdef ENABLE_DRM
 6 	struct swc_drm *const drm;
 7+#endif
 8+	struct swc_backend *backend;
 9 	struct wl_global *data_device_manager;
10 	struct wl_global *kde_decoration_manager;
11 	struct wl_global *layer_shell;
+3, -3
 1@@ -7,8 +7,6 @@ libswc_src = files(
 2   'data_device.c',
 3   'data_device_manager.c',
 4   'decor.c',
 5-  'dmabuf.c',
 6-  'drm.c',
 7   'input.c',
 8   'kde_decoration.c',
 9   'keyboard.c',
10@@ -18,7 +16,6 @@ libswc_src = files(
11   'output.c',
12   'panel.c',
13   'panel_manager.c',
14-  'plane.c',
15   'pointer.c',
16   'primary_plane.c',
17   'region.c',
18@@ -44,6 +41,9 @@ libswc_src = files(
19   input_backend == 'wscons' ? 'seat-ws.c' : [],
20 
21   xwayland.found() ? ['xserver.c', 'xwm.c'] : [],  
22+
23+  video_backend == 'drm' ? ['dmabuf.c', 'drm.c', 'plane.c'] : [],
24+  video_backend == 'fb' ? ['fb.c', 'fb_fbdev.c'] : [],
25 )
26 libswc_src += [cursor, launch_protocol]
27 
+12, -0
 1@@ -23,6 +23,7 @@
 2 
 3 #include "mode.h"
 4 
 5+#ifdef ENABLE_DRM
 6 bool
 7 mode_initialize(struct mode *mode, drmModeModeInfo *mode_info)
 8 {
 9@@ -33,6 +34,17 @@ mode_initialize(struct mode *mode, drmModeModeInfo *mode_info)
10 	mode->info = *mode_info;
11 	return true;
12 }
13+#endif
14+
15+void
16+mode_initialize_simple(struct mode *mode, uint16_t width, uint16_t height,
17+                       uint32_t refresh)
18+{
19+	mode->width = width;
20+	mode->height = height;
21+	mode->refresh = refresh;
22+	mode->preferred = true;
23+}
24 
25 bool
26 mode_equal(const struct mode *mode1, const struct mode *mode2)
+9, -0
 1@@ -27,7 +27,9 @@
 2 #include <stdbool.h>
 3 #include <stddef.h>
 4 #include <stdint.h>
 5+#ifdef ENABLE_DRM
 6 #include <xf86drmMode.h>
 7+#endif
 8 
 9 struct mode {
10 	uint16_t width, height;
11@@ -35,11 +37,18 @@ struct mode {
12 
13 	bool preferred;
14 
15+#ifdef ENABLE_DRM
16 	drmModeModeInfo info;
17+#endif
18 };
19 
20+#ifdef ENABLE_DRM
21 bool
22 mode_initialize(struct mode *mode, drmModeModeInfo *mode_info);
23+#endif
24+void
25+mode_initialize_simple(struct mode *mode, uint16_t width, uint16_t height,
26+                       uint32_t refresh);
27 bool
28 mode_equal(const struct mode *mode1, const struct mode *mode2);
29 
+38, -1
 1@@ -1,15 +1,19 @@
 2 #include "output.h"
 3+#ifdef ENABLE_DRM
 4 #include "drm.h"
 5+#endif
 6 #include "internal.h"
 7 #include "mode.h"
 8 #include "screen.h"
 9 #include "util.h"
10 
11+#ifdef ENABLE_DRM
12 #include <drm.h>
13+#include <xf86drm.h>
14+#endif
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18-#include <xf86drm.h>
19 
20 static const struct wl_output_interface output_impl = {
21     .release = destroy_resource,
22@@ -65,6 +69,7 @@ bind_output(struct wl_client *client, void *data, uint32_t version, uint32_t id)
23 	}
24 }
25 
26+#ifdef ENABLE_DRM
27 struct output *
28 output_new(drmModeConnectorPtr connector)
29 {
30@@ -132,6 +137,38 @@ error1:
31 error0:
32 	return NULL;
33 }
34+#endif
35+
36+struct output *
37+output_new_fb(uint32_t width, uint32_t height, const char *name)
38+{
39+	struct output *output;
40+	struct mode *mode;
41+
42+	output = calloc(1, sizeof(*output));
43+	if (!output) {
44+		return NULL;
45+	}
46+	output->global = wl_global_create(swc.display, &wl_output_interface, 4,
47+	                                  output, &bind_output);
48+	if (!output->global) {
49+		free(output);
50+		return NULL;
51+	}
52+	wl_list_init(&output->resources);
53+	wl_array_init(&output->modes);
54+	pixman_region32_init(&output->current_damage);
55+	pixman_region32_init(&output->previous_damage);
56+	mode = wl_array_add(&output->modes, sizeof(*mode));
57+	if (!mode) {
58+		output_destroy(output);
59+		return NULL;
60+	}
61+	mode_initialize_simple(mode, width, height, 60000);
62+	output->preferred_mode = mode;
63+	snprintf(output->name, sizeof(output->name), "%s", name);
64+	return output;
65+}
66 
67 void
68 output_destroy(struct output *output)
+8, -0
 1@@ -4,7 +4,9 @@
 2 #include <pixman.h>
 3 #include <stdint.h>
 4 #include <wayland-util.h>
 5+#ifdef ENABLE_DRM
 6 #include <xf86drmMode.h>
 7+#endif
 8 
 9 struct wl_display;
10 
11@@ -21,16 +23,22 @@ struct output {
12 
13 	pixman_region32_t current_damage, previous_damage;
14 
15+#ifdef ENABLE_DRM
16 	/* The DRM connector corresponding to this output */
17 	uint32_t connector;
18+#endif
19 
20 	struct wl_global *global;
21 	struct wl_list resources;
22 	struct wl_list link;
23 };
24 
25+#ifdef ENABLE_DRM
26 struct output *
27 output_new(drmModeConnector *connector);
28+#endif
29+struct output *
30+output_new_fb(uint32_t width, uint32_t height, const char *name);
31 void
32 output_destroy(struct output *output);
33 
+23, -6
 1@@ -22,11 +22,14 @@
 2  */
 3 
 4 #include "pointer.h"
 5+#include "backend.h"
 6 #include "compositor.h"
 7 #include "cursor/cursor_data.h"
 8 #include "event.h"
 9 #include "internal.h"
10+#ifdef ENABLE_DRM
11 #include "plane.h"
12+#endif
13 #include "screen.h"
14 #include "seat.h"
15 #include "shm.h"
16@@ -175,7 +178,9 @@ attach(struct view *view, struct wld_buffer *buffer)
17 {
18 	struct pointer *pointer = wl_container_of(view, pointer, cursor.view);
19 	struct surface *surface = pointer->cursor.surface;
20+#ifdef ENABLE_DRM
21 	struct screen *screen;
22+#endif
23 
24 	if (surface && !pixman_region32_not_empty(&surface->state.damage)) {
25 		return 0;
26@@ -203,12 +208,15 @@ attach(struct view *view, struct wld_buffer *buffer)
27 		view_update_screens(view);
28 	}
29 
30-	wl_list_for_each(screen, &swc.screens, link)
31-	{
32+#ifdef ENABLE_DRM
33+	wl_list_for_each(screen, &swc.screens, link) {
34 		view_attach(&screen->planes.cursor->view,
35 		            buffer ? pointer->cursor.buffer : NULL);
36 		view_update(&screen->planes.cursor->view);
37 	}
38+#else
39+	compositor_damage_all();
40+#endif
41 
42 	return 0;
43 }
44@@ -216,18 +224,23 @@ attach(struct view *view, struct wld_buffer *buffer)
45 static bool
46 move(struct view *view, int32_t x, int32_t y)
47 {
48+#ifdef ENABLE_DRM
49 	struct screen *screen;
50+#endif
51 
52 	if (view_set_position(view, x, y)) {
53 		view_update_screens(view);
54 	}
55 
56-	wl_list_for_each(screen, &swc.screens, link)
57-	{
58+#ifdef ENABLE_DRM
59+	wl_list_for_each(screen, &swc.screens, link) {
60 		view_move(&screen->planes.cursor->view, view->geometry.x,
61 		          view->geometry.y);
62 		view_update(&screen->planes.cursor->view);
63 	}
64+#else
65+	compositor_damage_all();
66+#endif
67 
68 	return true;
69 }
70@@ -509,7 +522,8 @@ pointer_initialize(struct pointer *pointer)
71 	pointer->cursor.surface = NULL;
72 	pointer->cursor.destroy_listener.notify = &handle_cursor_surface_destroy;
73 	pointer->cursor.buffer = wld_create_buffer(
74-	    swc.drm->context, swc.drm->cursor_w, swc.drm->cursor_h,
75+	    swc.backend->context, swc.backend->cursor_width,
76+	    swc.backend->cursor_height,
77 	    WLD_FORMAT_ARGB8888, WLD_FLAG_MAP | WLD_FLAG_CURSOR);
78 	pointer->cursor.internal_buffer = NULL;
79 
80@@ -519,8 +533,11 @@ pointer_initialize(struct pointer *pointer)
81 
82 	pointer_set_cursor(pointer, cursor_left_ptr);
83 
84+#ifdef ENABLE_DRM
85 	wl_list_for_each(screen, &swc.screens, link)
86-	    view_attach(&screen->planes.cursor->view, pointer->cursor.buffer);
87+		if (screen->planes.cursor)
88+			view_attach(&screen->planes.cursor->view, pointer->cursor.buffer);
89+#endif
90 
91 	input_focus_initialize(&pointer->focus, &pointer->focus_handler);
92 	pixman_region32_init(&pointer->region);
+42, -2
  1@@ -22,17 +22,24 @@
  2  */
  3 
  4 #include "primary_plane.h"
  5-#include "drm.h"
  6 #include "event.h"
  7+#ifdef ENABLE_DRM
  8+#include "drm.h"
  9+#else
 10+#include "compositor.h"
 11+#include "fb.h"
 12+#endif
 13 #include "internal.h"
 14 #include "launch.h"
 15 #include "util.h"
 16 
 17+#ifdef ENABLE_DRM
 18 #include <errno.h>
 19 #include <wld/drm.h>
 20-#include <wld/wld.h>
 21 #include <xf86drm.h>
 22 #include <xf86drmMode.h>
 23+#endif
 24+#include <wld/wld.h>
 25 
 26 static bool
 27 update(struct view *view)
 28@@ -52,6 +59,7 @@ static int
 29 attach(struct view *view, struct wld_buffer *buffer)
 30 {
 31 	struct primary_plane *plane = wl_container_of(view, plane, view);
 32+#ifdef ENABLE_DRM
 33 	uint32_t fb;
 34 	int ret;
 35 
 36@@ -80,6 +88,13 @@ attach(struct view *view, struct wld_buffer *buffer)
 37 	}
 38 
 39 	return 0;
 40+#else
 41+	if (!fb_present(buffer, view->geometry.x, view->geometry.y)) {
 42+		return -1;
 43+	}
 44+	wl_event_loop_add_idle(swc.event_loop, &send_frame, plane);
 45+	return 0;
 46+#endif
 47 }
 48 
 49 static bool
 50@@ -95,12 +110,14 @@ static const struct view_impl view_impl = {
 51     .move = move,
 52 };
 53 
 54+#ifdef ENABLE_DRM
 55 static void
 56 handle_page_flip(struct drm_handler *handler, uint32_t time)
 57 {
 58 	struct primary_plane *plane = wl_container_of(handler, plane, drm_handler);
 59 	view_frame(&plane->view, time);
 60 }
 61+#endif
 62 
 63 static void
 64 handle_swc_event(struct wl_listener *listener, void *data)
 65@@ -111,16 +128,25 @@ handle_swc_event(struct wl_listener *listener, void *data)
 66 
 67 	switch (event->type) {
 68 	case SWC_EVENT_ACTIVATED:
 69+#ifdef ENABLE_DRM
 70 		plane->need_modeset = true;
 71+#else
 72+		compositor_damage_all();
 73+#endif
 74 		break;
 75 	}
 76 }
 77 
 78 bool
 79+#ifdef ENABLE_DRM
 80 primary_plane_initialize(struct primary_plane *plane, uint32_t crtc,
 81                          struct mode *mode, uint32_t *connectors,
 82                          uint32_t num_connectors)
 83+#else
 84+primary_plane_initialize(struct primary_plane *plane, struct mode *mode)
 85+#endif
 86 {
 87+#ifdef ENABLE_DRM
 88 	uint32_t *plane_connectors;
 89 
 90 	if (!(plane->original_crtc_state = drmModeGetCrtc(swc.drm->fd, crtc))) {
 91@@ -148,22 +174,36 @@ primary_plane_initialize(struct primary_plane *plane, uint32_t crtc,
 92 	plane->drm_handler.page_flip = &handle_page_flip;
 93 	plane->swc_listener.notify = &handle_swc_event;
 94 	plane->mode = *mode;
 95+#else
 96+	view_initialize(&plane->view, &view_impl);
 97+	plane->view.geometry.width = mode->width;
 98+	plane->view.geometry.height = mode->height;
 99+	plane->mode = *mode;
100+#endif
101+	plane->swc_listener.notify = &handle_swc_event;
102 	wl_signal_add(&swc.event_signal, &plane->swc_listener);
103 
104 	return true;
105 
106+#ifdef ENABLE_DRM
107 error1:
108 	drmModeFreeCrtc(plane->original_crtc_state);
109 error0:
110 	return false;
111+#endif
112 }
113 
114 void
115 primary_plane_finalize(struct primary_plane *plane)
116 {
117+	wl_list_remove(&plane->swc_listener.link);
118+#ifdef ENABLE_DRM
119 	wl_array_release(&plane->connectors);
120 	drmModeCrtcPtr crtc = plane->original_crtc_state;
121 	drmModeSetCrtc(swc.drm->fd, crtc->crtc_id, crtc->buffer_id, crtc->x,
122 	               crtc->y, NULL, 0, &crtc->mode);
123 	drmModeFreeCrtc(crtc);
124+#else
125+	(void)plane;
126+#endif
127 }
+12, -1
 1@@ -24,29 +24,40 @@
 2 #ifndef SWC_PRIMARY_PLANE_H
 3 #define SWC_PRIMARY_PLANE_H
 4 
 5-#include "drm.h"
 6 #include "mode.h"
 7 #include "view.h"
 8+#ifdef ENABLE_DRM
 9+#include "drm.h"
10+#endif
11 
12 #include <stdbool.h>
13 #include <stdint.h>
14 #include <wayland-server.h>
15 
16 struct primary_plane {
17+#ifdef ENABLE_DRM
18 	uint32_t crtc;
19 	drmModeCrtcPtr original_crtc_state;
20+#endif
21 	struct mode mode;
22 	struct view view;
23+#ifdef ENABLE_DRM
24 	struct wl_array connectors;
25 	bool need_modeset;
26 	struct drm_handler drm_handler;
27+#endif
28 	struct wl_listener swc_listener;
29 };
30 
31+#ifdef ENABLE_DRM
32 bool
33 primary_plane_initialize(struct primary_plane *plane, uint32_t crtc,
34                          struct mode *mode, uint32_t *connectors,
35                          uint32_t num_connectors);
36+#else
37+bool
38+primary_plane_initialize(struct primary_plane *plane, struct mode *mode);
39+#endif
40 void
41 primary_plane_finalize(struct primary_plane *plane);
42 
+31, -3
 1@@ -22,12 +22,18 @@
 2  */
 3 
 4 #include "screen.h"
 5+#ifdef ENABLE_DRM
 6 #include "drm.h"
 7+#else
 8+#include "fb.h"
 9+#endif
10 #include "event.h"
11 #include "internal.h"
12 #include "mode.h"
13 #include "output.h"
14+#ifdef ENABLE_DRM
15 #include "plane.h"
16+#endif
17 #include "pointer.h"
18 #include "util.h"
19 
20@@ -65,7 +71,13 @@ screens_initialize(void)
21 {
22 	wl_list_init(&swc.screens);
23 
24-	if (!drm_create_screens(&swc.screens)) {
25+	if (!
26+#ifdef ENABLE_DRM
27+	    drm_create_screens(&swc.screens)
28+#else
29+	    fb_create_screens(&swc.screens)
30+#endif
31+	) {
32 		return false;
33 	}
34 
35@@ -103,7 +115,11 @@ bind_screen(struct wl_client *client, void *data, uint32_t version, uint32_t id)
36 }
37 
38 struct screen *
39+#ifdef ENABLE_DRM
40 screen_new(uint32_t crtc, struct output *output, struct plane *cursor_plane)
41+#else
42+screen_new(struct output *output)
43+#endif
44 {
45 	struct screen *screen;
46 	int32_t x = 0;
47@@ -124,8 +140,8 @@ screen_new(uint32_t crtc, struct output *output, struct plane *cursor_plane)
48 		goto error1;
49 	}
50 
51+#ifdef ENABLE_DRM
52 	screen->crtc = crtc;
53-
54 	if (!primary_plane_initialize(&screen->planes.primary,
55 	                              crtc,
56 	                              output->preferred_mode,
57@@ -137,6 +153,14 @@ screen_new(uint32_t crtc, struct output *output, struct plane *cursor_plane)
58 
59 	cursor_plane->screen = screen;
60 	screen->planes.cursor = cursor_plane;
61+#else
62+	if (!primary_plane_initialize(&screen->planes.primary,
63+	                              output->preferred_mode)) {
64+		ERROR("Failed to initialize primary plane\n");
65+		goto error2;
66+	}
67+	screen->planes.cursor = NULL;
68+#endif
69 
70 	screen->handler = &null_handler;
71 	wl_signal_init(&screen->destroy_signal);
72@@ -176,7 +200,11 @@ screen_destroy(struct screen *screen)
73 	wl_list_for_each_safe(output, next, &screen->outputs, link)
74 	    output_destroy(output);
75 	primary_plane_finalize(&screen->planes.primary);
76-	plane_destroy(screen->planes.cursor);
77+#ifdef ENABLE_DRM
78+	if (screen->planes.cursor) {
79+		plane_destroy(screen->planes.cursor);
80+	}
81+#endif
82 	free(screen);
83 }
84 
+7, -0
 1@@ -51,7 +51,9 @@ struct screen {
 2 
 3 	struct wl_signal destroy_signal;
 4 	uint8_t id;
 5+#ifdef ENABLE_DRM
 6 	uint32_t crtc;
 7+#endif
 8 
 9 	struct {
10 		struct primary_plane primary;
11@@ -71,8 +73,13 @@ screens_initialize(void);
12 void
13 screens_finalize(void);
14 
15+#ifdef ENABLE_DRM
16 struct screen *
17 screen_new(uint32_t crtc, struct output *output, struct plane *cursor_plane);
18+#else
19+struct screen *
20+screen_new(struct output *output);
21+#endif
22 void
23 screen_destroy(struct screen *screen);
24 
+24, -2
 1@@ -25,7 +25,11 @@
 2 #include "bindings.h"
 3 #include "compositor.h"
 4 #include "data_device_manager.h"
 5+#ifdef ENABLE_DRM
 6 #include "drm.h"
 7+#else
 8+#include "fb.h"
 9+#endif
10 #include "event.h"
11 #include "internal.h"
12 #include "kde_decoration.h"
13@@ -53,7 +57,9 @@
14 extern struct swc_launch swc_launch;
15 extern const struct swc_bindings swc_bindings;
16 extern struct swc_compositor swc_compositor;
17+#ifdef ENABLE_DRM
18 extern struct swc_drm swc_drm;
19+#endif
20 #ifdef ENABLE_XWAYLAND
21 extern struct swc_xserver swc_xserver;
22 #endif
23@@ -63,7 +69,9 @@ extern struct pointer_handler screens_pointer_handler;
24 struct swc swc = {
25     .bindings = &swc_bindings,
26     .compositor = &swc_compositor,
27+#ifdef ENABLE_DRM
28     .drm = &swc_drm,
29+#endif
30 #ifdef ENABLE_XWAYLAND
31     .xserver = &swc_xserver,
32 #endif
33@@ -160,8 +168,14 @@ swc_initialize(struct wl_display *display, struct wl_event_loop *event_loop,
34 		goto error0;
35 	}
36 
37-	if (!drm_initialize()) {
38-		ERROR("Could not initialize DRM\n");
39+	if (!
40+#ifdef ENABLE_DRM
41+	    drm_initialize()
42+#else
43+	    fb_initialize()
44+#endif
45+	) {
46+		ERROR("Could not initialize video backend\n");
47 		goto error1;
48 	}
49 
50@@ -302,7 +316,11 @@ error4:
51 error3:
52 	shm_destroy(swc.shm);
53 error2:
54+#ifdef ENABLE_DRM
55 	drm_finalize();
56+#else
57+	fb_finalize();
58+#endif
59 error1:
60 	launch_finalize();
61 error0:
62@@ -329,6 +347,10 @@ swc_finalize(void)
63 	screens_finalize();
64 	bindings_finalize();
65 	shm_destroy(swc.shm);
66+#ifdef ENABLE_DRM
67 	drm_finalize();
68+#else
69+	fb_finalize();
70+#endif
71 	launch_finalize();
72 }
+23, -2
 1@@ -31,14 +31,34 @@ requires = [
 2   dependency('wayland-server', version: '>=1.6.0'),
 3 ]
 4 requires_private = [
 5-  dependency('libdrm'),
 6   dependency('pixman-1'),
 7   dependency('wayland-protocols'),
 8   dependency('wld'),
 9   dependency('xkbcommon'),
10 ]
11 
12-xwayland = dependency('xwayland', required: get_option('xwayland'))
13+video_backend = get_option('video')
14+drm = disabler()
15+if video_backend == 'drm'
16+  drm = dependency('libdrm')
17+  requires_private += drm
18+  add_project_arguments('-DENABLE_DRM=1', language: 'c')
19+elif video_backend == 'fb'
20+  if os != 'linux'
21+    error('The fb video backend is currently supported only on Linux')
22+  endif
23+  add_project_arguments([
24+    '-DENABLE_FBDEV=1',
25+    '-DFBDEV_DEVICE="@0@"'.format(get_option('fbdev-device')),
26+  ], language: 'c')
27+endif
28+
29+if video_backend != 'drm' and get_option('xwayland').enabled()
30+  error('Xwayland requires the DRM video backend')
31+endif
32+xwayland = video_backend == 'drm' \
33+  ? dependency('xwayland', required: get_option('xwayland')) \
34+  : disabler()
35 xwayland_deps = [
36   'xcb',
37   'xcb-composite',
38@@ -93,4 +113,5 @@ endif
39 summary({
40   'Xwayland support': xwayland.found(),
41   'Input backend': input_backend,
42+  'Video backend': video_backend,
43 }, bool_yn: true)
+2, -0
1@@ -1,5 +1,7 @@
2 option('xwayland', type: 'feature', value: 'auto', description: 'Automatic Xwayland handling')
3 option('input', type: 'combo', value: 'auto', choices: ['auto', 'libinput', 'wscons'], description: 'Input backend to use')
4+option('video', type: 'combo', value: 'drm', choices: ['drm', 'fb'], description: 'Video backend to use')
5+option('fbdev-device', type: 'string', value: '/dev/fb0', description: 'Linux framebuffer device')
6 option('udev', type: 'feature', value: 'auto', description: 'libinput libudev support')
7 option('extra', type: 'boolean', value: true, description: 'Build swc utility applications')
8 option('example', type: 'boolean', value: false, description: 'Build swc example compositor')