diff --git a/.gitignore b/.gitignore index e407031..25d3e5b 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ docs/reference/kmscon.????* docs/reference/*.stamp docs/reference/version.xml docs/reference/*/ +uvtd wlterm docs/man/*.1 docs/man/*.3 diff --git a/Makefile.am b/Makefile.am index 382da61..0d6a9a9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ # # Kmscon - Global Makefile -# Copyright (c) 2012 David Herrmann +# Copyright (c) 2012-2013 David Herrmann # # @@ -30,7 +30,7 @@ LIBUTERM_AGE = 0 ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} AM_MAKEFLAGS = --no-print-directory AUTOMAKE_OPTIONS = color-tests -AM_DISTCHECK_CONFIGURE_FLAGS = --enable-wlterm +AM_DISTCHECK_CONFIGURE_FLAGS = --enable-wlterm --enable-uvtd SUBDIRS = . @@ -687,6 +687,27 @@ wlterm_LDADD = \ libshl.la \ -lpthread +# +# uvtd +# + +if BUILD_ENABLE_UVTD +bin_PROGRAMS += uvtd +endif + +uvtd_SOURCES = \ + src/uvtd_main.c \ + src/uvtd_seat.h \ + src/uvtd_seat.c +uvtd_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(XKBCOMMON_CFLAGS) +uvtd_LDADD = \ + $(XKBCOMMON_LIBS) \ + libeloop.la \ + libshl.la \ + libuterm.la + # # Tests # diff --git a/configure.ac b/configure.ac index 96f7c5f..a9ff70b 100644 --- a/configure.ac +++ b/configure.ac @@ -188,6 +188,16 @@ if test "x$enable_wlterm" = "x" ; then fi AC_MSG_RESULT([$enable_wlterm]) +# uvtd +AC_MSG_CHECKING([whether user wants uvtd]) +AC_ARG_ENABLE([uvtd], + [AS_HELP_STRING([--enable-uvtd], + [build uvtd])]) +if test "x$enable_uvtd" = "x" ; then + enable_uvtd="no (default)" +fi +AC_MSG_RESULT([$enable_uvtd]) + # debug AC_MSG_CHECKING([whether to build with debugging on]) AC_ARG_ENABLE([debug], @@ -803,6 +813,30 @@ else wlterm_missing="enable-wlterm" fi +# uvtd +uvtd_avail=no +uvtd_missing="" +if test ! "x$enable_uvtd" = "xno" ; then + uvtd_avail=yes + if test "x$uvt_avail" = "xno" ; then + uvtd_avail=no + uvtd_missing="$uvt_missing,$uvtd_missing" + fi + + if test "x$eloop_avail" = "xno" ; then + uvtd_avail=no + uvtd_missing="$eloop_missing,$uvtd_missing" + fi + + if test "x$uvtd_avail" = "xno" ; then + if test "x$enable_uvtd" = "xyes" ; then + AC_ERROR([missing for uvtd: $uvtd_missing]) + fi + fi +else + uvtd_missing="enable-uvtd" +fi + # # Enable all required modules # We now know which modules can be built by checking the *_avail variables set @@ -811,6 +845,16 @@ fi # needs them. This is done top-down of course. # +# uvtd +uvtd_enabled=no +if test "x$uvtd_avail" = "xyes" ; then + if test "x${enable_uvtd% *}" = "xyes" ; then + uvtd_enabled=yes + enable_eloop=yes + enable_uvt=yes + fi +fi + # wlterm wlterm_enabled=no if test "x$wlterm_avail" = "xyes" ; then @@ -1194,6 +1238,10 @@ AM_CONDITIONAL([BUILD_ENABLE_KMSCON], AM_CONDITIONAL([BUILD_ENABLE_WLTERM], [test "x$wlterm_enabled" = "xyes"]) +# uvtd +AM_CONDITIONAL([BUILD_ENABLE_UVTD], + [test "x$uvtd_enabled" = "xyes"]) + # # Miscellaneous Checks # All checks below are independent of module checking or depend on the results @@ -1278,6 +1326,7 @@ AC_MSG_NOTICE([Build configuration: Applications and Libraries: kmscon: $kmscon_enabled ($kmscon_avail: $kmscon_missing) wlterm: $wlterm_enabled ($wlterm_avail: $wlterm_missing) + uvtd: $uvtd_enabled ($uvtd_avail: $uvtd_missing) uterm: $uterm_enabled ($uterm_avail: $uterm_missing) tsm: $tsm_enabled ($tsm_avail: $tsm_missing) uvt: $uvt_enabled ($uvt_avail: $uvt_missing) diff --git a/src/uvtd_main.c b/src/uvtd_main.c new file mode 100644 index 0000000..0dc7a83 --- /dev/null +++ b/src/uvtd_main.c @@ -0,0 +1,243 @@ +/* + * uvtd - User-space VT daemon + * + * Copyright (c) 2012-2013 David Herrmann + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "eloop.h" +#include "shl_dlist.h" +#include "shl_log.h" +#include "uterm_input.h" +#include "uterm_monitor.h" +#include "uvtd_seat.h" + +struct app_seat { + struct shl_dlist list; + struct uvtd_app *app; + struct uterm_monitor_seat *useat; + struct uvtd_seat *seat; +}; + +struct uvtd_app { + struct ev_eloop *eloop; + struct uterm_monitor *mon; + struct shl_dlist seats; +}; + +static void app_seat_event(struct uvtd_seat *seat, unsigned int ev, void *data) +{ +} + +static int app_seat_new(struct uvtd_app *app, const char *sname, + struct uterm_monitor_seat *useat) +{ + struct app_seat *seat; + int ret; + + seat = malloc(sizeof(*seat)); + if (!seat) + return -ENOMEM; + + log_debug("new seat %p on %s", seat, sname); + + memset(seat, 0, sizeof(*seat)); + seat->app = app; + seat->useat = useat; + + ret = uvtd_seat_new(&seat->seat, sname, app->eloop, app_seat_event, + seat); + if (ret) + goto err_free; + + uterm_monitor_set_seat_data(seat->useat, seat); + shl_dlist_link(&app->seats, &seat->list); + return 0; + +err_free: + free(seat); + return ret; +} + +static void app_seat_free(struct app_seat *seat) +{ + log_debug("free seat %p", seat); + + shl_dlist_unlink(&seat->list); + uterm_monitor_set_seat_data(seat->useat, NULL); + uvtd_seat_free(seat->seat); + free(seat); +} + +static void app_monitor_event(struct uterm_monitor *mon, + struct uterm_monitor_event *ev, + void *data) +{ + struct uvtd_app *app = data; + struct app_seat *seat; + int ret; + + switch (ev->type) { + case UTERM_MONITOR_NEW_SEAT: + ret = app_seat_new(app, ev->seat_name, ev->seat); + if (ret) + return; + break; + case UTERM_MONITOR_FREE_SEAT: + if (ev->seat_data) + app_seat_free(ev->seat_data); + break; + case UTERM_MONITOR_NEW_DEV: + seat = ev->seat_data; + if (!seat) + return; + + switch (ev->dev_type) { + case UTERM_MONITOR_INPUT: + log_debug("new input device %s on seat %p", + ev->dev_node, seat); + break; + } + break; + case UTERM_MONITOR_FREE_DEV: + seat = ev->seat_data; + if (!seat) + return; + + switch (ev->dev_type) { + case UTERM_MONITOR_INPUT: + log_debug("free input device %s on seat %p", + ev->dev_node, seat); + break; + } + break; + } +} + +static void app_sig_generic(struct ev_eloop *eloop, + struct signalfd_siginfo *info, + void *data) +{ + struct uvtd_app *app = data; + + log_info("terminating due to caught signal %d", info->ssi_signo); + ev_eloop_exit(app->eloop); +} + +static void app_sig_ignore(struct ev_eloop *eloop, + struct signalfd_siginfo *info, + void *data) +{ +} + +static void destroy_app(struct uvtd_app *app) +{ + uterm_monitor_unref(app->mon); + ev_eloop_unregister_signal_cb(app->eloop, SIGPIPE, app_sig_ignore, + app); + ev_eloop_unregister_signal_cb(app->eloop, SIGINT, app_sig_generic, + app); + ev_eloop_unregister_signal_cb(app->eloop, SIGTERM, app_sig_generic, + app); + ev_eloop_unref(app->eloop); +} + +static int setup_app(struct uvtd_app *app) +{ + int ret; + + shl_dlist_init(&app->seats); + + ret = ev_eloop_new(&app->eloop, log_llog, NULL); + if (ret) { + log_error("cannot create eloop object: %d", ret); + goto err_app; + } + + ret = ev_eloop_register_signal_cb(app->eloop, SIGTERM, + app_sig_generic, app); + if (ret) { + log_error("cannot register SIGTERM signal handler: %d", ret); + goto err_app; + } + + ret = ev_eloop_register_signal_cb(app->eloop, SIGINT, + app_sig_generic, app); + if (ret) { + log_error("cannot register SIGINT signal handler: %d", ret); + goto err_app; + } + + ret = ev_eloop_register_signal_cb(app->eloop, SIGPIPE, + app_sig_ignore, app); + if (ret) { + log_error("cannot register SIGPIPE signal handler: %d", ret); + goto err_app; + } + + ret = uterm_monitor_new(&app->mon, app->eloop, app_monitor_event, app); + if (ret) { + log_error("cannot create device monitor: %d", ret); + goto err_app; + } + + log_debug("scanning for devices..."); + uterm_monitor_scan(app->mon); + + return 0; + +err_app: + destroy_app(app); + return ret; +} + +int main(int argc, char **argv) +{ + int ret; + struct uvtd_app app; + + log_set_config(&LOG_CONFIG_INFO(1, 1)); + log_print_init("uvtd"); + + memset(&app, 0, sizeof(app)); + + ret = setup_app(&app); + if (ret) + goto err_out; + + ev_eloop_run(app.eloop, -1); + + ret = 0; + destroy_app(&app); +err_out: + if (ret) + log_err("cannot initialize uvtd, errno %d: %s", + ret, strerror(-ret)); + log_info("exiting"); + return -ret; +} diff --git a/src/uvtd_seat.c b/src/uvtd_seat.c new file mode 100644 index 0000000..2ef3180 --- /dev/null +++ b/src/uvtd_seat.c @@ -0,0 +1,594 @@ +/* + * uvtd - User-space VT daemon + * + * Copyright (c) 2012-2013 David Herrmann + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Seats + * Each set of input+output devices form a single seat. Each seat is independent + * of each other and there can be exactly one user per seat interacting with the + * system. + * Per seat, we have multiple sessions. But only one session can be active at a + * time per seat. We allow external sessions, so session activation/deactivation + * may be asynchronous. + * + * A seat object manages all the sessions for a single seat. As long as a seat + * is asleep, no session is active. If you wake it up, the seat manager + * automatically schedules a session. You can then request other sessions to be + * scheduled and the seat manager will try to deactivate the current session and + * reactivate the new session. + * + * Note that session deactivation may be asynchronous (unless forced). So some + * calls might return -EINPROGRESS if the session-deactivation is pending. This + * shouldn't bother the user as the session will notify back soon that the + * deactivation was successfull. However, if it doesn't the user can chose to + * perform any other action and we will retry the operation. As a last resort, + * you can always kill the session by unregistering it or forcing a + * deactivation. + * "async_schedule" tracks the task that requested the deactivation of a + * session. So when the session notifies us that it got deactivated, we know + * what the user wanted and can perform the requested task now. + */ + +#include +#include +#include +#include "eloop.h" +#include "shl_dlist.h" +#include "shl_log.h" +#include "uvtd_seat.h" + +#define LOG_SUBSYSTEM "seat" + +struct uvtd_session { + struct shl_dlist list; + unsigned long ref; + struct uvtd_seat *seat; + + bool enabled; + bool deactivating; + + uvtd_session_cb_t cb; + void *data; +}; + +/* task that requested the pending session-deactivation */ +enum uvtd_async_schedule { + SCHEDULE_NONE, /* default, causes a reschedule */ + SCHEDULE_SWITCH, /* causes a reschedule */ + SCHEDULE_SLEEP, /* puts the seat asleep */ + SCHEDULE_UNREGISTER, /* unregisters the session */ +}; + +struct uvtd_seat { + struct ev_eloop *eloop; + char *name; + + size_t session_count; + struct shl_dlist sessions; + + bool awake; + struct uvtd_session *current_sess; + struct uvtd_session *scheduled_sess; + struct uvtd_session *dummy_sess; + + unsigned int async_schedule; + + uvtd_seat_cb_t cb; + void *data; +}; + +static int session_call(struct uvtd_session *sess, unsigned int event) +{ + if (!sess->cb) + return 0; + + return sess->cb(sess, event, sess->data); +} + +static int session_call_activate(struct uvtd_session *sess) +{ + log_debug("activate session %p", sess); + return session_call(sess, UVTD_SESSION_ACTIVATE); +} + +static int session_call_deactivate(struct uvtd_session *sess) +{ + log_debug("deactivate session %p", sess); + return session_call(sess, UVTD_SESSION_DEACTIVATE); +} + +/* drop the current session as if it was successfully deactivated */ +static void seat_yield(struct uvtd_seat *seat) +{ + if (!seat->current_sess) + return; + + seat->current_sess->deactivating = false; + seat->current_sess = NULL; + seat->async_schedule = SCHEDULE_NONE; +} + +static int seat_go_asleep(struct uvtd_seat *seat, bool force) +{ + int ret = 0; + + if (!seat->awake) + return 0; + + if (seat->current_sess) { + ret = -EBUSY; + if (!force) + return ret; + + seat_yield(seat); + } + + seat->awake = false; + + if (seat->cb) + seat->cb(seat, UVTD_SEAT_SLEEP, seat->data); + + return ret; +} + +static void seat_go_awake(struct uvtd_seat *seat) +{ + if (seat->awake) + return; + + seat->awake = true; +} + +static int seat_run(struct uvtd_seat *seat) +{ + int ret; + struct uvtd_session *session; + + if (!seat->awake) + return -EBUSY; + if (seat->current_sess) + return 0; + + if (!seat->scheduled_sess) { + log_debug("no session scheduled to run (num: %zu)", + seat->session_count); + return -ENOENT; + } + session = seat->scheduled_sess; + + /* TODO: unregister session and try next on failure */ + ret = session_call_activate(session); + if (ret) { + log_warning("cannot activate session %p: %d", session, ret); + return ret; + } + + seat->current_sess = session; + + return 0; +} + +static int seat_pause(struct uvtd_seat *seat, bool force, unsigned int async) +{ + int ret; + + if (!seat->current_sess) + return 0; + + /* TODO: pass \force to the session */ + seat->current_sess->deactivating = true; + ret = session_call_deactivate(seat->current_sess); + if (ret) { + if (!force && ret == -EINPROGRESS) { + seat->async_schedule = async; + log_debug("pending deactivation for session %p", + seat->current_sess); + } else { + log_warning("cannot deactivate session %p (%d): %d", + seat->current_sess, force, ret); + } + + if (!force) + return ret; + } + + seat_yield(seat); + return ret; +} + +static void seat_reschedule(struct uvtd_seat *seat) +{ + struct shl_dlist *iter, *start; + struct uvtd_session *sess; + + if (seat->scheduled_sess && seat->scheduled_sess->enabled) + return; + + if (seat->current_sess && seat->current_sess->enabled) { + seat->scheduled_sess = seat->current_sess; + return; + } + + if (seat->current_sess) + start = &seat->current_sess->list; + else + start = &seat->sessions; + + shl_dlist_for_each_but_one(iter, start, &seat->sessions) { + sess = shl_dlist_entry(iter, struct uvtd_session, list); + + if (sess != seat->dummy_sess && sess->enabled) { + seat->scheduled_sess = sess; + return; + } + } + + if (seat->dummy_sess && seat->dummy_sess->enabled) + seat->scheduled_sess = seat->dummy_sess; + else + seat->scheduled_sess = NULL; +} + +static bool seat_has_schedule(struct uvtd_seat *seat) +{ + return seat->scheduled_sess && + seat->scheduled_sess != seat->current_sess; +} + +static int seat_switch(struct uvtd_seat *seat) +{ + int ret; + + ret = seat_pause(seat, false, SCHEDULE_SWITCH); + if (ret) + return ret; + + return seat_run(seat); +} + +static void seat_schedule(struct uvtd_seat *seat, struct uvtd_session *sess) +{ + seat->scheduled_sess = sess; + seat_reschedule(seat); + if (seat_has_schedule(seat)) + seat_switch(seat); +} + +static void seat_next(struct uvtd_seat *seat, bool reverse) +{ + struct shl_dlist *cur, *iter; + struct uvtd_session *s, *next; + + if (seat->current_sess) + cur = &seat->current_sess->list; + else if (seat->session_count) + cur = &seat->sessions; + else + return; + + next = NULL; + if (!seat->current_sess && seat->dummy_sess && + seat->dummy_sess->enabled) + next = seat->dummy_sess; + + if (reverse) { + shl_dlist_for_each_reverse_but_one(iter, cur, + &seat->sessions) { + s = shl_dlist_entry(iter, struct uvtd_session, list); + + if (s->enabled && seat->dummy_sess != s) { + next = s; + break; + } + } + } else { + shl_dlist_for_each_but_one(iter, cur, &seat->sessions) { + s = shl_dlist_entry(iter, struct uvtd_session, list); + + if (s->enabled && seat->dummy_sess != s) { + next = s; + break; + } + } + } + + if (!next) + return; + + seat_schedule(seat, next); +} + +int uvtd_seat_new(struct uvtd_seat **out, const char *seatname, + struct ev_eloop *eloop, uvtd_seat_cb_t cb, void *data) +{ + struct uvtd_seat *seat; + int ret; + + if (!out || !eloop || !seatname) + return -EINVAL; + + seat = malloc(sizeof(*seat)); + if (!seat) + return -ENOMEM; + memset(seat, 0, sizeof(*seat)); + seat->eloop = eloop; + seat->cb = cb; + seat->data = data; + shl_dlist_init(&seat->sessions); + + seat->name = strdup(seatname); + if (!seat->name) { + ret = -ENOMEM; + goto err_free; + } + + ev_eloop_ref(seat->eloop); + *out = seat; + return 0; + +err_free: + free(seat); + return ret; +} + +void uvtd_seat_free(struct uvtd_seat *seat) +{ + struct uvtd_session *s; + int ret; + + if (!seat) + return; + + ret = seat_pause(seat, true, SCHEDULE_NONE); + if (ret) + log_warning("destroying seat %s while session %p is active", + seat->name, seat->current_sess); + + ret = seat_go_asleep(seat, true); + if (ret) + log_warning("destroying seat %s while still awake: %d", + seat->name, ret); + + while (!shl_dlist_empty(&seat->sessions)) { + s = shl_dlist_entry(seat->sessions.next, struct uvtd_session, + list); + uvtd_session_unregister(s); + } + + free(seat->name); + ev_eloop_unref(seat->eloop); + free(seat); +} + +const char *uvtd_seat_get_name(struct uvtd_seat *seat) +{ + return seat ? seat->name : NULL; +} + +struct ev_eloop *uvtd_seat_get_eloop(struct uvtd_seat *seat) +{ + return seat ? seat->eloop : NULL; +} + +int uvtd_seat_sleep(struct uvtd_seat *seat, bool force) +{ + int ret, err = 0; + + if (!seat) + return -EINVAL; + + ret = seat_pause(seat, force, SCHEDULE_SLEEP); + if (ret) { + if (force) + err = ret; + else + return ret; + } + + ret = seat_go_asleep(seat, force); + if (ret) { + if (force) + err = ret; + else + return ret; + } + + return err; +} + +void uvtd_seat_wake_up(struct uvtd_seat *seat) +{ + if (!seat) + return; + + seat_go_awake(seat); + seat_run(seat); +} + +int uvtd_seat_register_session(struct uvtd_seat *seat, + struct uvtd_session **out, + unsigned int id, uvtd_session_cb_t cb, + void *data) +{ + struct uvtd_session *sess; + + if (!seat || !out) + return -EINVAL; + + sess = malloc(sizeof(*sess)); + if (!sess) + return -ENOMEM; + + log_debug("register session %p", sess); + + memset(sess, 0, sizeof(*sess)); + sess->ref = 1; + sess->seat = seat; + sess->cb = cb; + sess->data = data; + + /* TODO: add support for \ids */ + /* register new sessions next to the current one */ + if (seat->current_sess) + shl_dlist_link(&seat->current_sess->list, &sess->list); + else + shl_dlist_link_tail(&seat->sessions, &sess->list); + + ++seat->session_count; + *out = sess; + return 0; +} + +void uvtd_session_ref(struct uvtd_session *sess) +{ + if (!sess || !sess->ref) + return; + + ++sess->ref; +} + +void uvtd_session_unref(struct uvtd_session *sess) +{ + if (!sess || !sess->ref || --sess->ref) + return; + + uvtd_session_unregister(sess); + free(sess); +} + +void uvtd_session_unregister(struct uvtd_session *sess) +{ + struct uvtd_seat *seat; + int ret; + bool forced = false; + + if (!sess || !sess->seat) + return; + + log_debug("unregister session %p", sess); + + seat = sess->seat; + sess->enabled = false; + if (seat->dummy_sess == sess) + seat->dummy_sess = NULL; + seat_reschedule(seat); + + if (seat->current_sess == sess) { + ret = seat_pause(seat, true, SCHEDULE_NONE); + if (ret) { + forced = true; + log_warning("unregistering active session %p; skipping automatic session-switch", + sess); + } + } + + shl_dlist_unlink(&sess->list); + --seat->session_count; + sess->seat = NULL; + + session_call(sess, UVTD_SESSION_UNREGISTER); + uvtd_session_unref(sess); + + /* If this session was active and we couldn't deactivate it, then it + * might still have resources allocated that couldn't get freed. In this + * case we should not automatically switch to the next session as it is + * very likely that it will not be able to start. + * Instead, we stay inactive and wait for user/external input to switch + * to another session. This delay will then hopefully be long enough so + * all resources got freed. */ + if (!forced) + seat_run(seat); +} + +bool uvtd_session_is_registered(struct uvtd_session *sess) +{ + return sess && sess->seat; +} + +bool uvtd_session_is_active(struct uvtd_session *sess) +{ + return sess && sess->seat && sess->seat->current_sess == sess; +} + +void uvtd_session_schedule(struct uvtd_session *sess) +{ + if (!sess || !sess->seat) + return; + + seat_schedule(sess->seat, sess); +} + +void uvtd_session_enable(struct uvtd_session *sess) +{ + if (!sess || sess->enabled) + return; + + log_debug("enable session %p", sess); + sess->enabled = true; + + if (sess->seat && + (!sess->seat->current_sess || + sess->seat->current_sess == sess->seat->dummy_sess)) + seat_schedule(sess->seat, sess); +} + +void uvtd_session_disable(struct uvtd_session *sess) +{ + if (!sess || !sess->enabled) + return; + + log_debug("disable session %p", sess); + sess->enabled = false; +} + +bool uvtd_session_is_enabled(struct uvtd_session *sess) +{ + return sess && sess->enabled; +} + +void uvtd_session_notify_deactivated(struct uvtd_session *sess) +{ + struct uvtd_seat *seat; + unsigned int sched; + + if (!sess || !sess->seat) + return; + + seat = sess->seat; + if (seat->current_sess != sess) + return; + + sched = seat->async_schedule; + log_debug("session %p notified core about deactivation (schedule: %u)", + sess, sched); + seat_yield(seat); + seat_reschedule(seat); + + if (sched == SCHEDULE_SLEEP) + seat_go_asleep(seat, false); + else if (sched == SCHEDULE_UNREGISTER) + uvtd_session_unregister(sess); + else + seat_run(seat); +} diff --git a/src/uvtd_seat.h b/src/uvtd_seat.h new file mode 100644 index 0000000..534d773 --- /dev/null +++ b/src/uvtd_seat.h @@ -0,0 +1,96 @@ +/* + * uvtd - User-space VT daemon + * + * Copyright (c) 2012-2013 David Herrmann + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Seats + * Each set of input+output devices form a single seat. Each seat is independent + * of each other and there can be exactly one user per seat interacting with the + * system. + * Per seat, we have multiple sessions. But only one session can be active at a + * time per seat. We allow external sessions, so session activation/deactivation + * may be asynchronous. + */ + +#ifndef UVTD_SEAT_H +#define UVTD_SEAT_H + +#include +#include "eloop.h" + +/* sessions */ + +struct uvtd_session; + +enum uvtd_session_event_type { + UVTD_SESSION_ACTIVATE, + UVTD_SESSION_DEACTIVATE, + UVTD_SESSION_UNREGISTER, +}; + +typedef int (*uvtd_session_cb_t) (struct uvtd_session *session, + unsigned int event, + void *data); + +void uvtd_session_ref(struct uvtd_session *sess); +void uvtd_session_unref(struct uvtd_session *sess); +void uvtd_session_unregister(struct uvtd_session *sess); +bool uvtd_session_is_registered(struct uvtd_session *sess); + +bool uvtd_session_is_active(struct uvtd_session *sess); +void uvtd_session_schedule(struct uvtd_session *sess); + +void uvtd_session_enable(struct uvtd_session *sess); +void uvtd_session_disable(struct uvtd_session *sess); +bool uvtd_session_is_enabled(struct uvtd_session *sess); + +void uvtd_session_notify_deactivated(struct uvtd_session *sess); + +/* seats */ + +struct uvtd_seat; + +enum uvtd_seat_event { + UVTD_SEAT_SLEEP, +}; + +typedef void (*uvtd_seat_cb_t) (struct uvtd_seat *seat, unsigned int event, + void *data); + +int uvtd_seat_new(struct uvtd_seat **out, const char *seatname, + struct ev_eloop *eloop, uvtd_seat_cb_t cb, void *data); +void uvtd_seat_free(struct uvtd_seat *seat); + +const char *uvtd_seat_get_name(struct uvtd_seat *seat); +struct ev_eloop *uvtd_seat_get_eloop(struct uvtd_seat *seat); +int uvtd_seat_sleep(struct uvtd_seat *seat, bool force); +void uvtd_seat_wake_up(struct uvtd_seat *seat); +void uvtd_seat_schedule(struct uvtd_seat *seat, unsigned int id); + +int uvtd_seat_register_session(struct uvtd_seat *seat, + struct uvtd_session **out, + unsigned int id, uvtd_session_cb_t cb, + void *data); + +#endif /* UVTD_SEAT_H */