4 * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files
8 * (the "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 * A seat is a single session that is self-hosting and provides all the
29 * interaction for a single logged-in user.
37 #include "kmscon_conf.h"
38 #include "kmscon_dummy.h"
39 #include "kmscon_seat.h"
40 #include "kmscon_terminal.h"
41 #include "shl_dlist.h"
43 #include "uterm_input.h"
44 #include "uterm_video.h"
47 #define LOG_SUBSYSTEM "seat"
49 struct kmscon_session {
50 struct shl_dlist list;
52 struct kmscon_seat *seat;
58 struct ev_timer *timer;
60 kmscon_session_cb_t cb;
64 struct kmscon_display {
65 struct shl_dlist list;
66 struct kmscon_seat *seat;
67 struct uterm_display *disp;
71 enum kmscon_async_schedule {
78 struct ev_eloop *eloop;
79 struct uterm_vt_master *vtm;
80 struct conf_ctx *conf_ctx;
81 struct kmscon_conf_t *conf;
84 struct uterm_input *input;
86 struct shl_dlist displays;
89 struct shl_dlist sessions;
93 struct kmscon_session *current_sess;
94 struct kmscon_session *scheduled_sess;
95 struct kmscon_session *dummy_sess;
97 unsigned int async_schedule;
103 static int session_call(struct kmscon_session *sess, unsigned int event,
104 struct uterm_display *disp)
106 struct kmscon_session_event ev;
111 memset(&ev, 0, sizeof(ev));
114 return sess->cb(sess, &ev, sess->data);
117 static int session_call_activate(struct kmscon_session *sess)
119 log_debug("activate session %p", sess);
120 return session_call(sess, KMSCON_SESSION_ACTIVATE, NULL);
123 static int session_call_deactivate(struct kmscon_session *sess)
125 log_debug("deactivate session %p", sess);
126 return session_call(sess, KMSCON_SESSION_DEACTIVATE, NULL);
129 static void session_call_display_new(struct kmscon_session *sess,
130 struct uterm_display *disp)
132 session_call(sess, KMSCON_SESSION_DISPLAY_NEW, disp);
135 static void session_call_display_gone(struct kmscon_session *sess,
136 struct uterm_display *disp)
138 session_call(sess, KMSCON_SESSION_DISPLAY_GONE, disp);
141 static void session_call_display_refresh(struct kmscon_session *sess,
142 struct uterm_display *disp)
144 session_call(sess, KMSCON_SESSION_DISPLAY_REFRESH, disp);
147 static void activate_display(struct kmscon_display *d)
150 struct shl_dlist *iter, *tmp;
151 struct kmscon_session *s;
152 struct kmscon_seat *seat = d->seat;
154 if (d->activated || !d->seat->awake || !d->seat->foreground)
157 /* TODO: We always use the default mode for new displays but we should
158 * rather allow the user to specify different modes in the configuration
160 if (uterm_display_get_state(d->disp) == UTERM_DISPLAY_INACTIVE) {
161 ret = uterm_display_activate(d->disp, NULL);
167 ret = uterm_display_set_dpms(d->disp, UTERM_DPMS_ON);
169 log_warning("cannot set DPMS state to on for display: %d",
172 shl_dlist_for_each_safe(iter, tmp, &seat->sessions) {
173 s = shl_dlist_entry(iter, struct kmscon_session, list);
174 session_call_display_new(s, d->disp);
179 static int seat_go_foreground(struct kmscon_seat *seat, bool force)
182 struct shl_dlist *iter;
183 struct kmscon_display *d;
185 if (seat->foreground)
187 if (!seat->awake || (!force && seat->current_sess))
191 ret = seat->cb(seat, KMSCON_SEAT_FOREGROUND, seat->data);
193 log_warning("cannot put seat %s into foreground: %d",
199 seat->foreground = true;
201 shl_dlist_for_each(iter, &seat->displays) {
202 d = shl_dlist_entry(iter, struct kmscon_display, list);
209 static int seat_go_background(struct kmscon_seat *seat, bool force)
213 if (!seat->foreground)
215 if (!seat->awake || (!force && seat->current_sess))
219 ret = seat->cb(seat, KMSCON_SEAT_BACKGROUND, seat->data);
221 log_warning("cannot put seat %s into background: %d",
227 seat->foreground = false;
231 static int seat_go_asleep(struct kmscon_seat *seat, bool force)
237 if (seat->current_sess || seat->foreground) {
239 seat->foreground = false;
240 seat->current_sess = NULL;
248 ret = seat->cb(seat, KMSCON_SEAT_SLEEP, seat->data);
250 log_warning("cannot put seat %s asleep: %d",
258 uterm_input_sleep(seat->input);
263 static int seat_go_awake(struct kmscon_seat *seat)
271 ret = seat->cb(seat, KMSCON_SEAT_WAKE_UP, seat->data);
273 log_warning("cannot wake up seat %s: %d", seat->name,
280 uterm_input_wake_up(seat->input);
285 static int seat_run(struct kmscon_seat *seat)
288 struct kmscon_session *session;
292 if (seat->current_sess)
295 if (!seat->scheduled_sess) {
296 log_debug("no session scheduled to run (num %zu)",
297 seat->session_count);
300 session = seat->scheduled_sess;
302 if (session->foreground && !seat->foreground) {
303 ret = seat_go_foreground(seat, false);
305 log_warning("cannot put seat %s into foreground for session %p",
306 seat->name, session);
309 } else if (!session->foreground && seat->foreground) {
310 ret = seat_go_background(seat, false);
312 log_warning("cannot put seat %s into background for session %p",
313 seat->name, session);
318 ret = session_call_activate(session);
320 log_warning("cannot activate session %p: %d", session, ret);
324 seat->current_sess = session;
329 static void session_deactivate(struct kmscon_session *sess)
331 if (sess->seat->current_sess != sess)
334 sess->seat->async_schedule = SCHEDULE_SWITCH;
335 sess->deactivating = false;
336 sess->seat->current_sess = NULL;
339 static int seat_pause(struct kmscon_seat *seat, bool force)
343 if (!seat->current_sess)
346 seat->current_sess->deactivating = true;
347 ret = session_call_deactivate(seat->current_sess);
349 if (ret == -EINPROGRESS)
350 log_debug("pending deactivation for session %p",
353 log_warning("cannot deactivate session %p: %d",
354 seat->current_sess, ret);
359 session_deactivate(seat->current_sess);
364 static void seat_reschedule(struct kmscon_seat *seat)
366 struct shl_dlist *iter, *start;
367 struct kmscon_session *sess;
369 if (seat->scheduled_sess && seat->scheduled_sess->enabled)
372 if (seat->current_sess && seat->current_sess->enabled) {
373 seat->scheduled_sess = seat->current_sess;
377 if (seat->current_sess)
378 start = &seat->current_sess->list;
380 start = &seat->sessions;
382 shl_dlist_for_each_but_one(iter, start, &seat->sessions) {
383 sess = shl_dlist_entry(iter, struct kmscon_session, list);
384 if (sess == seat->dummy_sess || !sess->enabled)
386 seat->scheduled_sess = sess;
390 if (seat->dummy_sess && seat->dummy_sess->enabled)
391 seat->scheduled_sess = seat->dummy_sess;
393 seat->scheduled_sess = NULL;
396 static bool seat_has_schedule(struct kmscon_seat *seat)
398 return seat->scheduled_sess &&
399 seat->scheduled_sess != seat->current_sess;
402 static int seat_switch(struct kmscon_seat *seat)
406 seat->async_schedule = SCHEDULE_SWITCH;
407 ret = seat_pause(seat, false);
411 return seat_run(seat);
414 static void seat_next(struct kmscon_seat *seat)
416 struct shl_dlist *cur, *iter;
417 struct kmscon_session *s, *next;
419 if (seat->current_sess)
420 cur = &seat->current_sess->list;
421 else if (seat->session_count)
422 cur = &seat->sessions;
427 if (!seat->current_sess && seat->dummy_sess &&
428 seat->dummy_sess->enabled)
429 next = seat->dummy_sess;
431 shl_dlist_for_each_but_one(iter, cur, &seat->sessions) {
432 s = shl_dlist_entry(iter, struct kmscon_session, list);
433 if (!s->enabled || seat->dummy_sess == s)
443 seat->scheduled_sess = next;
447 static void seat_prev(struct kmscon_seat *seat)
449 struct shl_dlist *cur, *iter;
450 struct kmscon_session *s, *prev;
452 if (seat->current_sess)
453 cur = &seat->current_sess->list;
454 else if (seat->session_count)
455 cur = &seat->sessions;
460 if (!seat->current_sess && seat->dummy_sess &&
461 seat->dummy_sess->enabled)
462 prev = seat->dummy_sess;
464 shl_dlist_for_each_reverse_but_one(iter, cur, &seat->sessions) {
465 s = shl_dlist_entry(iter, struct kmscon_session, list);
466 if (!s->enabled || seat->dummy_sess == s)
476 seat->scheduled_sess = prev;
480 static int seat_add_display(struct kmscon_seat *seat,
481 struct uterm_display *disp)
483 struct kmscon_display *d;
485 log_debug("add display %p to seat %s", disp, seat->name);
487 d = malloc(sizeof(*d));
490 memset(d, 0, sizeof(*d));
494 uterm_display_ref(d->disp);
495 shl_dlist_link(&seat->displays, &d->list);
500 static void seat_remove_display(struct kmscon_seat *seat,
501 struct kmscon_display *d)
503 struct shl_dlist *iter, *tmp;
504 struct kmscon_session *s;
506 log_debug("remove display %p from seat %s", d->disp, seat->name);
508 shl_dlist_unlink(&d->list);
511 shl_dlist_for_each_safe(iter, tmp, &seat->sessions) {
512 s = shl_dlist_entry(iter, struct kmscon_session, list);
513 session_call_display_gone(s, d->disp);
517 uterm_display_unref(d->disp);
521 static void seat_refresh_display(struct kmscon_seat *seat,
522 struct kmscon_display *d)
524 struct shl_dlist *iter;
525 struct kmscon_session *s;
527 log_debug("refresh display %p from seat %s", d->disp, seat->name);
530 shl_dlist_for_each(iter, &seat->sessions) {
531 s = shl_dlist_entry(iter, struct kmscon_session, list);
532 session_call_display_refresh(s, d->disp);
537 static int seat_vt_event(struct uterm_vt *vt, struct uterm_vt_event *ev,
540 struct kmscon_seat *seat = data;
543 switch (ev->action) {
544 case UTERM_VT_ACTIVATE:
545 ret = seat_go_awake(seat);
550 case UTERM_VT_DEACTIVATE:
551 seat->async_schedule = SCHEDULE_VT;
552 ret = seat_pause(seat, false);
555 ret = seat_go_background(seat, false);
558 ret = seat_go_asleep(seat, false);
564 seat->cb(seat, KMSCON_SEAT_HUP, seat->data);
571 static void seat_input_event(struct uterm_input *input,
572 struct uterm_input_event *ev,
575 struct kmscon_seat *seat = data;
576 struct kmscon_session *s;
579 if (ev->handled || !seat->awake)
582 if (conf_grab_matches(seat->conf->grab_session_next,
583 ev->mods, ev->num_syms, ev->keysyms)) {
585 if (!seat->conf->session_control)
590 if (conf_grab_matches(seat->conf->grab_session_prev,
591 ev->mods, ev->num_syms, ev->keysyms)) {
593 if (!seat->conf->session_control)
598 if (conf_grab_matches(seat->conf->grab_session_dummy,
599 ev->mods, ev->num_syms, ev->keysyms)) {
601 if (!seat->conf->session_control)
603 seat->scheduled_sess = seat->dummy_sess;
607 if (conf_grab_matches(seat->conf->grab_session_close,
608 ev->mods, ev->num_syms, ev->keysyms)) {
610 if (!seat->conf->session_control)
612 s = seat->current_sess;
615 if (s == seat->dummy_sess)
618 /* First time this is invoked on a session, we simply try
619 * unloading it. If it fails, we give it some time. If this is
620 * invoked a second time, we notice that we already tried
621 * removing it and so we go straight to unregistering the
622 * session unconditionally. */
623 if (!s->deactivating) {
624 seat->async_schedule = SCHEDULE_UNREGISTER;
625 ret = seat_pause(seat, false);
630 kmscon_session_unregister(s);
633 if (conf_grab_matches(seat->conf->grab_terminal_new,
634 ev->mods, ev->num_syms, ev->keysyms)) {
636 if (!seat->conf->session_control)
638 ret = kmscon_terminal_register(&s, seat,
639 uterm_vt_get_num(seat->vt));
640 if (ret == -EOPNOTSUPP) {
641 log_notice("terminal support not compiled in");
643 log_error("cannot register terminal session: %d", ret);
646 seat->scheduled_sess = s;
653 int kmscon_seat_new(struct kmscon_seat **out,
654 struct conf_ctx *main_conf,
655 struct ev_eloop *eloop,
656 struct uterm_vt_master *vtm,
657 unsigned int vt_types,
658 const char *seatname,
662 struct kmscon_seat *seat;
666 if (!out || !eloop || !vtm || !seatname)
669 seat = malloc(sizeof(*seat));
672 memset(seat, 0, sizeof(*seat));
677 shl_dlist_init(&seat->displays);
678 shl_dlist_init(&seat->sessions);
680 seat->name = strdup(seatname);
682 log_error("cannot copy string");
687 ret = kmscon_conf_new(&seat->conf_ctx);
689 log_error("cannot create seat configuration object: %d", ret);
692 seat->conf = conf_ctx_get_mem(seat->conf_ctx);
694 ret = kmscon_conf_load_seat(seat->conf_ctx, main_conf, seat->name);
696 log_error("cannot parse seat configuration on seat %s: %d",
701 /* TODO: The XKB-API currently requires zero-terminated strings as
702 * keymap input. Hence, we have to read it in instead of using mmap().
703 * We should fix this upstream! */
705 if (seat->conf->xkb_keymap && *seat->conf->xkb_keymap) {
706 ret = shl_read_file(seat->conf->xkb_keymap, &keymap, NULL);
708 log_error("cannot read keymap file %s: %d",
709 seat->conf->xkb_keymap, ret);
712 ret = uterm_input_new(&seat->input, seat->eloop,
713 seat->conf->xkb_model,
714 seat->conf->xkb_layout,
715 seat->conf->xkb_variant,
716 seat->conf->xkb_options,
718 seat->conf->xkb_repeat_delay,
719 seat->conf->xkb_repeat_rate);
725 ret = uterm_input_register_cb(seat->input, seat_input_event, seat);
729 ret = uterm_vt_allocate(seat->vtm, &seat->vt,
730 vt_types, seat->name,
731 seat->input, seat->conf->vt, seat_vt_event,
736 ev_eloop_ref(seat->eloop);
737 uterm_vt_master_ref(seat->vtm);
742 uterm_input_unregister_cb(seat->input, seat_input_event, seat);
744 uterm_input_unref(seat->input);
746 kmscon_conf_free(seat->conf_ctx);
754 void kmscon_seat_free(struct kmscon_seat *seat)
756 struct kmscon_display *d;
757 struct kmscon_session *s;
763 ret = seat_pause(seat, true);
765 log_warning("destroying seat %s while session %p is active",
766 seat->name, seat->current_sess);
768 ret = seat_go_asleep(seat, true);
770 log_warning("destroying seat %s while still awake: %d",
773 while (!shl_dlist_empty(&seat->sessions)) {
774 s = shl_dlist_entry(seat->sessions.next,
775 struct kmscon_session,
777 kmscon_session_unregister(s);
780 while (!shl_dlist_empty(&seat->displays)) {
781 d = shl_dlist_entry(seat->displays.next,
782 struct kmscon_display,
784 seat_remove_display(seat, d);
787 uterm_vt_deallocate(seat->vt);
788 uterm_input_unregister_cb(seat->input, seat_input_event, seat);
789 uterm_input_unref(seat->input);
790 kmscon_conf_free(seat->conf_ctx);
792 uterm_vt_master_unref(seat->vtm);
793 ev_eloop_unref(seat->eloop);
797 void kmscon_seat_startup(struct kmscon_seat *seat)
800 struct kmscon_session *s;
805 ret = kmscon_dummy_register(&s, seat);
806 if (ret == -EOPNOTSUPP) {
807 log_notice("dummy sessions not compiled in");
809 log_error("cannot register dummy session: %d", ret);
811 seat->dummy_sess = s;
812 kmscon_session_enable(s);
815 if (seat->conf->terminal_session) {
816 ret = kmscon_terminal_register(&s, seat,
817 uterm_vt_get_num(seat->vt));
818 if (ret == -EOPNOTSUPP)
819 log_notice("terminal support not compiled in");
821 log_error("cannot register terminal session");
823 kmscon_session_enable(s);
826 if (seat->conf->switchvt ||
827 uterm_vt_get_type(seat->vt) == UTERM_VT_FAKE)
828 uterm_vt_activate(seat->vt);
831 int kmscon_seat_add_display(struct kmscon_seat *seat,
832 struct uterm_display *disp)
837 return seat_add_display(seat, disp);
840 void kmscon_seat_remove_display(struct kmscon_seat *seat,
841 struct uterm_display *disp)
843 struct shl_dlist *iter;
844 struct kmscon_display *d;
849 shl_dlist_for_each(iter, &seat->displays) {
850 d = shl_dlist_entry(iter, struct kmscon_display, list);
854 seat_remove_display(seat, d);
859 void kmscon_seat_refresh_display(struct kmscon_seat *seat,
860 struct uterm_display *disp)
862 struct shl_dlist *iter;
863 struct kmscon_display *d;
868 shl_dlist_for_each(iter, &seat->displays) {
869 d = shl_dlist_entry(iter, struct kmscon_display, list);
873 seat_refresh_display(seat, d);
878 int kmscon_seat_add_input(struct kmscon_seat *seat, const char *node)
883 uterm_input_add_dev(seat->input, node);
887 void kmscon_seat_remove_input(struct kmscon_seat *seat, const char *node)
892 uterm_input_remove_dev(seat->input, node);
895 const char *kmscon_seat_get_name(struct kmscon_seat *seat)
903 struct uterm_input *kmscon_seat_get_input(struct kmscon_seat *seat)
911 struct ev_eloop *kmscon_seat_get_eloop(struct kmscon_seat *seat)
919 struct conf_ctx *kmscon_seat_get_conf(struct kmscon_seat *seat)
924 return seat->conf_ctx;
927 void kmscon_seat_schedule(struct kmscon_seat *seat, unsigned int id)
929 struct shl_dlist *iter;
930 struct kmscon_session *s, *next;
935 next = seat->dummy_sess;
936 shl_dlist_for_each(iter, &seat->sessions) {
937 s = shl_dlist_entry(iter, struct kmscon_session, list);
938 if (!s->enabled || seat->dummy_sess == s ||
939 seat->current_sess == s)
947 seat->scheduled_sess = next;
948 if (seat_has_schedule(seat))
952 int kmscon_seat_register_session(struct kmscon_seat *seat,
953 struct kmscon_session **out,
954 kmscon_session_cb_t cb,
957 struct kmscon_session *sess;
958 struct shl_dlist *iter;
959 struct kmscon_display *d;
964 if (seat->conf->session_max &&
965 seat->session_count >= seat->conf->session_max) {
966 log_warning("maximum number of sessions reached (%d), dropping new session",
967 seat->conf->session_max);
971 sess = malloc(sizeof(*sess));
973 log_error("cannot allocate memory for new session on seat %s",
978 log_debug("register session %p", sess);
980 memset(sess, 0, sizeof(*sess));
985 sess->foreground = true;
987 /* register new sessions next to the current one */
988 if (seat->current_sess)
989 shl_dlist_link(&seat->current_sess->list, &sess->list);
991 shl_dlist_link_tail(&seat->sessions, &sess->list);
993 ++seat->session_count;
996 shl_dlist_for_each(iter, &seat->displays) {
997 d = shl_dlist_entry(iter, struct kmscon_display, list);
998 session_call_display_new(sess, d->disp);
1004 void kmscon_session_ref(struct kmscon_session *sess)
1006 if (!sess || !sess->ref)
1012 void kmscon_session_unref(struct kmscon_session *sess)
1014 if (!sess || !sess->ref || --sess->ref)
1017 kmscon_session_unregister(sess);
1021 void kmscon_session_unregister(struct kmscon_session *sess)
1023 struct kmscon_seat *seat;
1025 bool forced = false;
1027 if (!sess || !sess->seat)
1030 log_debug("unregister session %p", sess);
1033 sess->enabled = false;
1034 if (seat->dummy_sess == sess)
1035 seat->dummy_sess = NULL;
1036 seat_reschedule(seat);
1038 if (seat->current_sess == sess) {
1039 ret = seat_pause(seat, true);
1042 log_warning("unregistering active session %p; skipping automatic session-switch",
1047 shl_dlist_unlink(&sess->list);
1048 --seat->session_count;
1051 session_call(sess, KMSCON_SESSION_UNREGISTER, NULL);
1052 kmscon_session_unref(sess);
1054 /* If this session was active and we couldn't deactivate it, then it
1055 * might still have resources allocated that couldn't get freed. In this
1056 * case we should not automatically switch to the next session as it is
1057 * very likely that it will not be able to start.
1058 * Instead, we stay inactive and wait for user/external input to switch
1059 * to another session. This delay will then hopefully be long enough so
1060 * all resources got freed. */
1065 bool kmscon_session_is_registered(struct kmscon_session *sess)
1067 return sess && sess->seat;
1070 bool kmscon_session_is_active(struct kmscon_session *sess)
1072 return sess && sess->seat && sess->seat->current_sess == sess;
1075 int kmscon_session_set_foreground(struct kmscon_session *sess)
1077 struct kmscon_seat *seat;
1082 if (sess->foreground)
1086 if (seat && seat->current_sess == sess && !seat->foreground) {
1087 ret = seat_go_foreground(seat, true);
1092 sess->foreground = true;
1096 int kmscon_session_set_background(struct kmscon_session *sess)
1098 struct kmscon_seat *seat;
1103 if (!sess->foreground)
1107 if (seat && seat->current_sess == sess && seat->foreground) {
1108 ret = seat_go_background(seat, true);
1113 sess->foreground = false;
1117 void kmscon_session_schedule(struct kmscon_session *sess)
1119 struct kmscon_seat *seat;
1121 if (!sess || !sess->seat)
1125 seat->scheduled_sess = sess;
1126 seat_reschedule(seat);
1127 if (seat_has_schedule(seat))
1131 void kmscon_session_enable(struct kmscon_session *sess)
1133 if (!sess || sess->enabled)
1136 log_debug("enable session %p", sess);
1137 sess->enabled = true;
1139 (!sess->seat->current_sess ||
1140 sess->seat->current_sess == sess->seat->dummy_sess)) {
1141 sess->seat->scheduled_sess = sess;
1142 if (seat_has_schedule(sess->seat))
1143 seat_switch(sess->seat);
1147 void kmscon_session_disable(struct kmscon_session *sess)
1149 if (!sess || !sess->enabled)
1152 log_debug("disable session %p", sess);
1153 sess->enabled = false;
1156 bool kmscon_session_is_enabled(struct kmscon_session *sess)
1158 return sess && sess->enabled;
1161 void kmscon_session_notify_deactivated(struct kmscon_session *sess)
1163 struct kmscon_seat *seat;
1167 if (!sess || !sess->seat)
1171 if (seat->current_sess != sess)
1174 sched = seat->async_schedule;
1175 log_debug("session %p notified core about deactivation (schedule: %u)",
1177 session_deactivate(sess);
1178 seat_reschedule(seat);
1180 if (sched == SCHEDULE_VT) {
1181 ret = seat_go_background(seat, false);
1184 ret = seat_go_asleep(seat, false);
1187 uterm_vt_retry(seat->vt);
1188 } else if (sched == SCHEDULE_UNREGISTER) {
1189 kmscon_session_unregister(sess);