diff --git a/backend/session/logind.c b/backend/session/logind.c index 393924e8..852b6ae5 100644 --- a/backend/session/logind.c +++ b/backend/session/logind.c @@ -35,7 +35,9 @@ struct logind_session { char *id; char *path; + char *seat_path; + bool can_graphical; // specifies whether a drm device was taken // if so, the session will be (de)activated with the drm fd, // otherwise with the dbus PropertiesChanged on "active" signal @@ -175,6 +177,34 @@ out: return ret >= 0; } +static bool find_seat_path(struct logind_session *session) { + int ret; + sd_bus_message *msg = NULL; + sd_bus_error error = SD_BUS_ERROR_NULL; + + ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", + "/org/freedesktop/login1", "org.freedesktop.login1.Manager", + "GetSeat", &error, &msg, "s", session->base.seat); + if (ret < 0) { + wlr_log(WLR_ERROR, "Failed to get seat path: %s", error.message); + goto out; + } + + const char *path; + ret = sd_bus_message_read(msg, "o", &path); + if (ret < 0) { + wlr_log(WLR_ERROR, "Could not parse seat path: %s", error.message); + goto out; + } + session->seat_path = strdup(path); + +out: + sd_bus_error_free(&error); + sd_bus_message_unref(msg); + + return ret >= 0; +} + static bool session_activate(struct logind_session *session) { int ret; sd_bus_message *msg = NULL; @@ -236,6 +266,7 @@ static void logind_session_destroy(struct wlr_session *base) { sd_bus_unref(session->bus); free(session->id); free(session->path); + free(session->seat_path); free(session); } @@ -328,7 +359,7 @@ error: return 0; } -static int properties_changed(sd_bus_message *msg, void *userdata, +static int session_properties_changed(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error) { struct logind_session *session = userdata; int ret = 0; @@ -437,6 +468,95 @@ error: return 0; } +static int seat_properties_changed(sd_bus_message *msg, void *userdata, + sd_bus_error *ret_error) { + struct logind_session *session = userdata; + int ret = 0; + + // if we have a drm fd we don't depend on this + if (session->has_drm) { + return 0; + } + + // PropertiesChanged arg 1: interface + const char *interface; + ret = sd_bus_message_read_basic(msg, 's', &interface); // skip path + if (ret < 0) { + goto error; + } + + if (strcmp(interface, "org.freedesktop.login1.Seat") != 0) { + // not interesting for us; ignore + wlr_log(WLR_DEBUG, "ignoring PropertiesChanged from %s", interface); + return 0; + } + + // PropertiesChanged arg 2: changed properties with values + ret = sd_bus_message_enter_container(msg, 'a', "{sv}"); + if (ret < 0) { + goto error; + } + + const char *s; + while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) { + ret = sd_bus_message_read_basic(msg, 's', &s); + if (ret < 0) { + goto error; + } + + if (strcmp(s, "CanGraphical") == 0) { + int ret; + ret = sd_bus_message_enter_container(msg, 'v', "b"); + if (ret < 0) { + goto error; + } + + ret = sd_bus_message_read_basic(msg, 'b', &session->can_graphical); + if (ret < 0) { + goto error; + } + + return 0; + } else { + sd_bus_message_skip(msg, "{sv}"); + } + + ret = sd_bus_message_exit_container(msg); + if (ret < 0) { + goto error; + } + } + + if (ret < 0) { + goto error; + } + + ret = sd_bus_message_exit_container(msg); + if (ret < 0) { + goto error; + } + + // PropertiesChanged arg 3: changed properties without values + sd_bus_message_enter_container(msg, 'a', "s"); + while ((ret = sd_bus_message_read_basic(msg, 's', &s)) > 0) { + if (strcmp(s, "CanGraphical") == 0) { + session->can_graphical = sd_seat_can_graphical(session->base.seat); + return 0; + } + } + + if (ret < 0) { + goto error; + } + + return 0; + +error: + wlr_log(WLR_ERROR, "Failed to parse D-Bus PropertiesChanged: %s", + strerror(-ret)); + return 0; +} + static bool add_signal_matches(struct logind_session *session) { int ret; @@ -471,7 +591,15 @@ static bool add_signal_matches(struct logind_session *session) { ret = sd_bus_match_signal(session->bus, NULL, "org.freedesktop.login1", session->path, "org.freedesktop.DBus.Properties", "PropertiesChanged", - properties_changed, session); + session_properties_changed, session); + if (ret < 0) { + wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret)); + return false; + } + + ret = sd_bus_match_signal(session->bus, NULL, "org.freedesktop.login1", + session->seat_path, "org.freedesktop.DBus.Properties", + "PropertiesChanged", seat_properties_changed, session); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret)); return false; @@ -656,6 +784,12 @@ static struct wlr_session *logind_session_create(struct wl_display *disp) { goto error; } + if (!find_seat_path(session)) { + sd_bus_unref(session->bus); + free(session->path); + goto error; + } + if (!add_signal_matches(session)) { goto error_bus; } @@ -672,6 +806,21 @@ static struct wlr_session *logind_session_create(struct wl_display *disp) { goto error_bus; } + // Check for CanGraphical first + session->can_graphical = sd_seat_can_graphical(session->base.seat); + if (!session->can_graphical) { + wlr_log(WLR_INFO, "Waiting for 'CanGraphical' on seat %s", session->base.seat); + } + + while (!session->can_graphical) { + ret = wl_event_loop_dispatch(event_loop, -1); + if (ret < 0) { + wlr_log(WLR_ERROR, "Polling error waiting for 'CanGraphical' on seat %s", + session->base.seat); + goto error_bus; + } + } + wlr_log(WLR_INFO, "Successfully loaded logind session"); session->base.impl = &session_logind; @@ -680,6 +829,7 @@ static struct wlr_session *logind_session_create(struct wl_display *disp) { error_bus: sd_bus_unref(session->bus); free(session->path); + free(session->seat_path); error: free(session->id);