From d8ca4945581577f570c02ad46878571c48a08c79 Mon Sep 17 00:00:00 2001
From: Chris Chamberlain <chris@chamberlain.id.au>
Date: Fri, 10 Dec 2021 21:14:57 +0100
Subject: backend/drm: add wlr_drm_backend_monitor

This helper is responsible for listening for new DRM devices and
create new child DRM backends as necessary.
---
 backend/backend.c         |  3 ++
 backend/drm/meson.build   |  1 +
 backend/drm/monitor.c     | 94 +++++++++++++++++++++++++++++++++++++++++++++++
 backend/session/session.c |  6 +--
 4 files changed, 101 insertions(+), 3 deletions(-)
 create mode 100644 backend/drm/monitor.c

(limited to 'backend')

diff --git a/backend/backend.c b/backend/backend.c
index 34c1e366..24150b13 100644
--- a/backend/backend.c
+++ b/backend/backend.c
@@ -21,6 +21,7 @@
 
 #if WLR_HAS_DRM_BACKEND
 #include <wlr/backend/drm.h>
+#include "backend/drm/monitor.h"
 #endif
 
 #if WLR_HAS_LIBINPUT_BACKEND
@@ -375,6 +376,8 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) {
 		return NULL;
 	}
 
+	drm_backend_monitor_create(backend, primary_drm, multi->session);
+
 	return backend;
 #endif
 
diff --git a/backend/drm/meson.build b/backend/drm/meson.build
index b076b472..cc791f36 100644
--- a/backend/drm/meson.build
+++ b/backend/drm/meson.build
@@ -4,6 +4,7 @@ wlr_files += files(
 	'cvt.c',
 	'drm.c',
 	'legacy.c',
+	'monitor.c',
 	'properties.c',
 	'renderer.c',
 	'util.c',
diff --git a/backend/drm/monitor.c b/backend/drm/monitor.c
new file mode 100644
index 00000000..539e7925
--- /dev/null
+++ b/backend/drm/monitor.c
@@ -0,0 +1,94 @@
+#include <wlr/util/log.h>
+#include <stdlib.h>
+#include "backend/drm/monitor.h"
+#include "backend/multi.h"
+#include "backend/session/session.h"
+
+static void drm_backend_monitor_destroy(struct wlr_drm_backend_monitor* monitor) {
+	wl_list_remove(&monitor->session_add_drm_card.link);
+	wl_list_remove(&monitor->session_destroy.link);
+	wl_list_remove(&monitor->primary_drm_destroy.link);
+	wl_list_remove(&monitor->multi_destroy.link);
+	free(monitor);
+}
+
+static void handle_add_drm_card(struct wl_listener *listener, void *data) {
+	struct wlr_session_add_event *event = data;
+	struct wlr_drm_backend_monitor *backend_monitor =
+		wl_container_of(listener, backend_monitor, session_add_drm_card);
+
+	struct wlr_device *dev =
+		session_open_if_kms(backend_monitor->session, event->path);
+	if (!dev) {
+		wlr_log(WLR_ERROR, "Unable to open %s as DRM device", event->path);
+		return;
+	}
+
+	wlr_log(WLR_DEBUG, "Creating DRM backend for %s after hotplug", event->path);
+	struct wlr_backend *child_drm = wlr_drm_backend_create(
+		backend_monitor->session->display, backend_monitor->session,
+		dev, backend_monitor->primary_drm);
+	if (!child_drm) {
+		wlr_log(WLR_ERROR, "Failed to create DRM backend after hotplug");
+		return;
+	}
+
+	if (!wlr_multi_backend_add(backend_monitor->multi, child_drm)) {
+		wlr_log(WLR_ERROR, "Failed to add new drm backend to multi backend");
+		wlr_backend_destroy(child_drm);
+		return;
+	}
+
+	if (!wlr_backend_start(child_drm)) {
+		wlr_log(WLR_ERROR, "Failed to start new child DRM backend");
+		wlr_backend_destroy(child_drm);
+	}
+}
+
+static void handle_session_destroy(struct wl_listener *listener, void *data) {
+	struct wlr_drm_backend_monitor *backend_monitor =
+		wl_container_of(listener, backend_monitor, session_destroy);
+	drm_backend_monitor_destroy(backend_monitor);
+}
+
+static void handle_primary_drm_destroy(struct wl_listener *listener, void *data) {
+	struct wlr_drm_backend_monitor *backend_monitor =
+		wl_container_of(listener, backend_monitor, primary_drm_destroy);
+	drm_backend_monitor_destroy(backend_monitor);
+}
+
+static void handle_multi_destroy(struct wl_listener *listener, void *data) {
+	struct wlr_drm_backend_monitor *backend_monitor =
+		wl_container_of(listener, backend_monitor, multi_destroy);
+	drm_backend_monitor_destroy(backend_monitor);
+}
+
+struct wlr_drm_backend_monitor *drm_backend_monitor_create(
+		struct wlr_backend *multi,
+		struct wlr_backend *primary_drm,
+		struct wlr_session *session) {
+	struct wlr_drm_backend_monitor *monitor =
+		calloc(1, sizeof(struct wlr_drm_backend_monitor));
+	if (!monitor) {
+		wlr_log_errno(WLR_ERROR, "Allocation failed");
+		return NULL;
+	}
+
+	monitor->multi = multi;
+	monitor->primary_drm = primary_drm;
+	monitor->session = session;
+
+	monitor->session_add_drm_card.notify = handle_add_drm_card;
+	wl_signal_add(&session->events.add_drm_card, &monitor->session_add_drm_card);
+
+	monitor->session_destroy.notify = handle_session_destroy;
+	wl_signal_add(&session->events.destroy, &monitor->session_destroy);
+
+	monitor->primary_drm_destroy.notify = handle_primary_drm_destroy;
+	wl_signal_add(&primary_drm->events.destroy, &monitor->primary_drm_destroy);
+
+	monitor->multi_destroy.notify = handle_multi_destroy;
+	wl_signal_add(&multi->events.destroy, &monitor->multi_destroy);
+
+	return monitor;
+}
diff --git a/backend/session/session.c b/backend/session/session.c
index e83a8b4c..7d6d080d 100644
--- a/backend/session/session.c
+++ b/backend/session/session.c
@@ -370,7 +370,7 @@ bool wlr_session_change_vt(struct wlr_session *session, unsigned vt) {
 
 /* Tests if 'path' is KMS compatible by trying to open it. Returns the opened
  * device on success. */
-static struct wlr_device *open_if_kms(struct wlr_session *restrict session,
+struct wlr_device *session_open_if_kms(struct wlr_session *restrict session,
 		const char *restrict path) {
 	if (!path) {
 		return NULL;
@@ -406,7 +406,7 @@ static ssize_t explicit_find_gpus(struct wlr_session *session,
 			break;
 		}
 
-		ret[i] = open_if_kms(session, ptr);
+		ret[i] = session_open_if_kms(session, ptr);
 		if (!ret[i]) {
 			wlr_log(WLR_ERROR, "Unable to open %s as DRM device", ptr);
 		} else {
@@ -542,7 +542,7 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session,
 		}
 
 		struct wlr_device *wlr_dev =
-			open_if_kms(session, udev_device_get_devnode(dev));
+			session_open_if_kms(session, udev_device_get_devnode(dev));
 		if (!wlr_dev) {
 			udev_device_unref(dev);
 			continue;
-- 
cgit v1.2.3