backend/drm: Remove automatic reset on VT switch

Instead of trying to restore the drm state when the session is activated
again, just disconnect all outputs when the session is deactivated. The
scan that triggers on session activation will rediscover the connectors.
This commit is contained in:
Kenny Levinsen 2024-10-28 12:51:27 +01:00
parent 3df1528a8f
commit 0f255b46fc
3 changed files with 8 additions and 104 deletions

View File

@ -117,11 +117,18 @@ static void handle_session_active(struct wl_listener *listener, void *data) {
wlr_log(WLR_INFO, "DRM FD %s", session->active ? "resumed" : "paused"); wlr_log(WLR_INFO, "DRM FD %s", session->active ? "resumed" : "paused");
if (!session->active) { if (!session->active) {
// Disconnect any active connectors so that the client will modeset and
// rerender when the session is activated again.
struct wlr_drm_connector *conn;
wl_list_for_each(conn, &drm->connectors, link) {
if (conn->status == DRM_MODE_CONNECTED) {
wlr_output_destroy(&conn->output);
}
}
return; return;
} }
scan_drm_connectors(drm, NULL); scan_drm_connectors(drm, NULL);
restore_drm_device(drm);
} }
static void handle_dev_change(struct wl_listener *listener, void *data) { static void handle_dev_change(struct wl_listener *listener, void *data) {

View File

@ -1851,108 +1851,6 @@ void scan_drm_leases(struct wlr_drm_backend *drm) {
drmFree(list); drmFree(list);
} }
static void build_current_connector_state(struct wlr_output_state *state,
struct wlr_drm_connector *conn) {
bool enabled = conn->status != DRM_MODE_DISCONNECTED && conn->output.enabled;
wlr_output_state_init(state);
wlr_output_state_set_enabled(state, enabled);
if (!enabled) {
return;
}
if (conn->output.current_mode != NULL) {
wlr_output_state_set_mode(state, conn->output.current_mode);
} else {
wlr_output_state_set_custom_mode(state,
conn->output.width, conn->output.height, conn->output.refresh);
}
}
/**
* Check whether we need to perform a full reset after a VT switch.
*
* If any connector or plane has a different CRTC, we need to perform a full
* reset to restore our mapping. We couldn't avoid a full reset even if we
* used a single KMS atomic commit to apply our state: the kernel rejects
* commits which migrate a plane from one CRTC to another without going through
* an intermediate state where the plane is disabled.
*/
static bool skip_reset_for_restore(struct wlr_drm_backend *drm) {
struct wlr_drm_connector *conn;
wl_list_for_each(conn, &drm->connectors, link) {
drmModeConnector *drm_conn = drmModeGetConnectorCurrent(drm->fd, conn->id);
if (drm_conn == NULL) {
return false;
}
struct wlr_drm_crtc *crtc = connector_get_current_crtc(conn, drm_conn);
drmModeFreeConnector(drm_conn);
if (crtc != NULL && conn->crtc != crtc) {
return false;
}
}
for (size_t i = 0; i < drm->num_planes; i++) {
struct wlr_drm_plane *plane = &drm->planes[i];
drmModePlane *drm_plane = drmModeGetPlane(drm->fd, plane->id);
if (drm_plane == NULL) {
return false;
}
uint32_t crtc_id = drm_plane->crtc_id;
drmModeFreePlane(drm_plane);
struct wlr_drm_crtc *crtc = NULL;
for (size_t i = 0; i < drm->num_crtcs; i++) {
if (drm->crtcs[i].id == crtc_id) {
crtc = &drm->crtcs[i];
break;
}
}
if (crtc == NULL) {
continue;
}
bool ok = false;
switch (plane->type) {
case DRM_PLANE_TYPE_PRIMARY:
ok = crtc->primary == plane;
break;
case DRM_PLANE_TYPE_CURSOR:
ok = crtc->cursor == plane;
break;
}
if (!ok) {
return false;
}
}
return true;
}
void restore_drm_device(struct wlr_drm_backend *drm) {
// The previous DRM master leaves KMS in an undefined state. We need
// to restore our own state, but be careful to avoid invalid
// configurations. The connector/CRTC mapping may have changed, so
// first disable all CRTCs, then light up the ones we were using
// before the VT switch.
// TODO: better use the atomic API to improve restoration after a VT switch
if (!skip_reset_for_restore(drm) && !drm->iface->reset(drm)) {
wlr_log(WLR_ERROR, "Failed to reset state after VT switch");
}
struct wlr_drm_connector *conn;
wl_list_for_each(conn, &drm->connectors, link) {
struct wlr_output_state state;
build_current_connector_state(&state, conn);
if (!drm_connector_commit_state(conn, &state, false)) {
wlr_drm_conn_log(conn, WLR_ERROR, "Failed to restore state after VT switch");
}
wlr_output_state_finish(&state);
}
}
bool commit_drm_device(struct wlr_drm_backend *drm, bool commit_drm_device(struct wlr_drm_backend *drm,
const struct wlr_backend_output_state *output_states, size_t output_states_len, const struct wlr_backend_output_state *output_states, size_t output_states_len,
bool test_only) { bool test_only) {

View File

@ -225,7 +225,6 @@ void scan_drm_connectors(struct wlr_drm_backend *state,
void scan_drm_leases(struct wlr_drm_backend *drm); void scan_drm_leases(struct wlr_drm_backend *drm);
bool commit_drm_device(struct wlr_drm_backend *drm, bool commit_drm_device(struct wlr_drm_backend *drm,
const struct wlr_backend_output_state *states, size_t states_len, bool test_only); const struct wlr_backend_output_state *states, size_t states_len, bool test_only);
void restore_drm_device(struct wlr_drm_backend *drm);
int handle_drm_event(int fd, uint32_t mask, void *data); int handle_drm_event(int fd, uint32_t mask, void *data);
void destroy_drm_connector(struct wlr_drm_connector *conn); void destroy_drm_connector(struct wlr_drm_connector *conn);
bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn); bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn);