diff --git a/Makefile.am b/Makefile.am index 7f44cbc..3860ec0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -418,8 +418,8 @@ kmscon_SOURCES = \ $(SHL_MISC) \ src/kmscon_terminal.h \ src/kmscon_terminal.c \ - src/kmscon_ui.h \ - src/kmscon_ui.c \ + src/kmscon_seat.h \ + src/kmscon_seat.c \ src/kmscon_conf.h \ src/kmscon_conf.c \ src/kmscon_main.c diff --git a/src/kmscon_main.c b/src/kmscon_main.c index 6ec9cfb..86095a6 100644 --- a/src/kmscon_main.c +++ b/src/kmscon_main.c @@ -34,13 +34,31 @@ #include "conf.h" #include "eloop.h" #include "kmscon_conf.h" -#include "kmscon_ui.h" +#include "kmscon_seat.h" #include "log.h" #include "shl_dlist.h" #include "shl_misc.h" #include "text.h" #include "uterm.h" +struct app_video { + struct shl_dlist list; + struct app_seat *seat; + + char *node; + struct uterm_video *video; +}; + +struct app_seat { + struct shl_dlist list; + struct kmscon_app *app; + + bool awake; + char *name; + struct kmscon_seat *seat; + struct shl_dlist videos; +}; + struct kmscon_app { struct ev_eloop *eloop; struct ev_eloop *vt_eloop; @@ -51,77 +69,45 @@ struct kmscon_app { struct shl_dlist seats; }; -struct kmscon_seat { - struct shl_dlist list; - struct kmscon_app *app; - - struct uterm_monitor_seat *useat; - char *sname; - - bool awake; - struct uterm_vt *vt; - struct uterm_input *input; - struct kmscon_ui *ui; - struct shl_dlist videos; -}; - -struct kmscon_video { - struct shl_dlist list; - struct uterm_monitor_dev *vdev; - struct uterm_video *video; -}; - -static void sig_generic(struct ev_eloop *eloop, struct signalfd_siginfo *info, - void *data) +static void app_seat_event(struct kmscon_seat *s, unsigned int event, + void *data) { - struct kmscon_app *app = data; - - ev_eloop_exit(app->eloop); - log_info("terminating due to caught signal %d", info->ssi_signo); -} - -static int vt_event(struct uterm_vt *vt, unsigned int action, void *data) -{ - struct kmscon_seat *seat = data; - struct shl_dlist *iter; - struct kmscon_video *vid; + struct app_seat *seat = data; struct kmscon_app *app = seat->app; + struct shl_dlist *iter; + struct app_video *vid; - if (action == UTERM_VT_ACTIVATE) { + switch (event) { + case KMSCON_SEAT_WAKE_UP: seat->awake = true; - uterm_input_wake_up(seat->input); shl_dlist_for_each(iter, &seat->videos) { - vid = shl_dlist_entry(iter, struct kmscon_video, list); + vid = shl_dlist_entry(iter, struct app_video, list); uterm_video_wake_up(vid->video); } - kmscon_ui_wake_up(seat->ui); - } else if (action == UTERM_VT_DEACTIVATE) { - kmscon_ui_sleep(seat->ui); + break; + case KMSCON_SEAT_SLEEP: shl_dlist_for_each(iter, &seat->videos) { - vid = shl_dlist_entry(iter, struct kmscon_video, list); + vid = shl_dlist_entry(iter, struct app_video, list); uterm_video_sleep(vid->video); } - uterm_input_sleep(seat->input); - seat->awake = false; - if (app->vt_exit_count > 0) { log_debug("deactivating VT on exit, %d to go", app->vt_exit_count - 1); if (!--app->vt_exit_count) ev_eloop_exit(app->vt_eloop); } - } - return 0; + seat->awake = false; + break; + } } -static void seat_new(struct kmscon_app *app, - struct uterm_monitor_seat *useat, - const char *sname) +static int app_seat_new(struct kmscon_app *app, struct app_seat **out, + const char *sname) { - struct kmscon_seat *seat; + struct app_seat *seat; int ret; unsigned int i; bool found; @@ -139,101 +125,116 @@ static void seat_new(struct kmscon_app *app, } if (!found) { - log_info("ignoring seat %s as not specified in seat-list", + log_info("ignoring new seat %s as not specified in seat-list", sname); - return; + return -ERANGE; } + log_debug("new seat %s", sname); + seat = malloc(sizeof(*seat)); - if (!seat) - return; + if (!seat) { + log_error("cannot allocate memory for seat %s", sname); + return -ENOMEM; + } memset(seat, 0, sizeof(*seat)); seat->app = app; - seat->useat = useat; shl_dlist_init(&seat->videos); - seat->sname = strdup(sname); - if (!seat->sname) { - log_err("cannot allocate memory for seat name"); + seat->name = strdup(sname); + if (!seat->name) { + log_error("cannot copy seat name on seat %s", sname); + ret = -ENOMEM; goto err_free; } - ret = uterm_input_new(&seat->input, app->eloop, - kmscon_conf.xkb_layout, - kmscon_conf.xkb_variant, - kmscon_conf.xkb_options, - kmscon_conf.xkb_repeat_delay, - kmscon_conf.xkb_repeat_rate); - if (ret) + ret = kmscon_seat_new(&seat->seat, app->eloop, app->vtm, sname, + app_seat_event, seat); + if (ret) { + log_error("cannot create seat object on seat %s: %d", + sname, ret); goto err_name; + } - ret = uterm_vt_allocate(app->vtm, &seat->vt, seat->sname, - seat->input, kmscon_conf.vt, vt_event, seat); - if (ret) - goto err_input; - - ret = kmscon_ui_new(&seat->ui, app->eloop, seat->input, seat->sname); - if (ret) - goto err_vt; - - uterm_monitor_set_seat_data(seat->useat, seat); shl_dlist_link(&app->seats, &seat->list); + *out = seat; + return 0; - log_info("new seat %s", seat->sname); - return; - -err_vt: - uterm_vt_deallocate(seat->vt); -err_input: - uterm_input_unref(seat->input); err_name: - free(seat->sname); + free(seat->name); err_free: free(seat); + return ret; } -static void seat_free(struct kmscon_seat *seat) +static void app_seat_free(struct app_seat *seat) { - log_info("free seat %s", seat->sname); + log_debug("free seat %s", seat->name); shl_dlist_unlink(&seat->list); - uterm_monitor_set_seat_data(seat->useat, NULL); - kmscon_ui_free(seat->ui); - uterm_input_unref(seat->input); - uterm_vt_deallocate(seat->vt); - free(seat->sname); + kmscon_seat_free(seat->seat); + free(seat->name); free(seat); } -static void seat_add_video(struct kmscon_seat *seat, - struct uterm_monitor_dev *dev, - unsigned int type, - const char *node) +static void app_seat_video_event(struct uterm_video *video, + struct uterm_video_hotplug *ev, + void *data) +{ + struct app_video *vid = data; + + switch (ev->action) { + case UTERM_NEW: + kmscon_seat_add_display(vid->seat->seat, ev->display); + break; + case UTERM_GONE: + kmscon_seat_remove_display(vid->seat->seat, ev->display); + break; + } +} + +static int app_seat_add_video(struct app_seat *seat, + struct app_video **out, + unsigned int type, + const char *node) { int ret; unsigned int mode; - struct kmscon_video *vid; + struct app_video *vid; if (kmscon_conf.use_fbdev) { if (type != UTERM_MONITOR_FBDEV && type != UTERM_MONITOR_FBDEV_DRM) { - log_debug("ignoring %s as it is not fbdev device", - node); - return; + log_info("ignoring video device %s on seat %s as it is not an fbdev device", + node, seat->name); + return -ERANGE; } } else { if (type == UTERM_MONITOR_FBDEV_DRM) { - log_debug("ignoring %s as it is a DRM-fbdev device", - node); - return; + log_info("ignoring video device %s on seat %s as it is a DRM-fbdev device", + node, seat->name); + return -ERANGE; } } + log_debug("new video device %s on seat %s", node, seat->name); + vid = malloc(sizeof(*vid)); - if (!vid) - return; + if (!vid) { + log_error("cannot allocate memory for video device %s on seat %s", + node, seat->name); + return -ENOMEM; + } memset(vid, 0, sizeof(*vid)); - vid->vdev = dev; + vid->seat = seat; + + vid->node = strdup(node); + if (!vid->node) { + log_error("cannot copy video device name %s on seat %s", + node, seat->name); + ret = -ENOMEM; + goto err_free; + } if (type == UTERM_MONITOR_DRM) { if (kmscon_conf.dumb) @@ -247,122 +248,153 @@ static void seat_add_video(struct kmscon_seat *seat, ret = uterm_video_new(&vid->video, seat->app->eloop, mode, node); if (ret) { if (mode == UTERM_VIDEO_DRM) { - log_info("cannot create drm device; trying dumb drm mode"); + log_info("cannot create drm device %s on seat %s (%d); trying dumb drm mode", + vid->node, seat->name, ret); ret = uterm_video_new(&vid->video, seat->app->eloop, UTERM_VIDEO_DUMB, node); if (ret) - goto err_free; + goto err_node; } else { - goto err_free; + goto err_node; } } - kmscon_ui_add_video(seat->ui, vid->video); + ret = uterm_video_register_cb(vid->video, app_seat_video_event, vid); + if (ret) { + log_error("cannot register video callback for device %s on seat %s: %d", + vid->node, seat->name, ret); + goto err_video; + } + if (seat->awake) uterm_video_wake_up(vid->video); + shl_dlist_link(&seat->videos, &vid->list); + *out = vid; + return 0; - log_debug("new graphics device on seat %s", seat->sname); - return; - +err_video: + uterm_video_unref(vid->video); +err_node: + free(vid->node); err_free: free(vid); - log_warning("cannot add video object %s on %s", node, seat->sname); - return; + return ret; } -static void seat_rm_video(struct kmscon_seat *seat, - struct uterm_monitor_dev *dev) +static void app_seat_remove_video(struct app_seat *seat, struct app_video *vid) { - struct shl_dlist *iter; - struct kmscon_video *vid; + log_debug("free video device %s on seat %s", vid->node, seat->name); - shl_dlist_for_each(iter, &seat->videos) { - vid = shl_dlist_entry(iter, struct kmscon_video, list); - if (vid->vdev != dev) - continue; - - log_debug("free graphics device on seat %s", seat->sname); - - kmscon_ui_remove_video(seat->ui, vid->video); - uterm_video_unref(vid->video); - shl_dlist_unlink(&vid->list); - free(vid); - - break; - } + shl_dlist_unlink(&vid->list); + uterm_video_unregister_cb(vid->video, app_seat_video_event, vid); + uterm_video_unref(vid->video); + free(vid->node); + free(vid); } -static void seat_hotplug_video(struct kmscon_seat *seat, - struct uterm_monitor_dev *dev) -{ - struct shl_dlist *iter; - struct kmscon_video *vid; - - shl_dlist_for_each(iter, &seat->videos) { - vid = shl_dlist_entry(iter, struct kmscon_video, list); - if (vid->vdev != dev) - continue; - - uterm_video_poll(vid->video); - break; - } -} - -static void monitor_event(struct uterm_monitor *mon, - struct uterm_monitor_event *ev, - void *data) +static void app_monitor_event(struct uterm_monitor *mon, + struct uterm_monitor_event *ev, + void *data) { struct kmscon_app *app = data; - struct kmscon_seat *seat; + struct app_seat *seat; + struct app_video *vid; + int ret; switch (ev->type) { case UTERM_MONITOR_NEW_SEAT: - seat_new(app, ev->seat, ev->seat_name); + ret = app_seat_new(app, &seat, ev->seat_name); + if (ret) + return; + uterm_monitor_set_seat_data(ev->seat, seat); break; case UTERM_MONITOR_FREE_SEAT: if (ev->seat_data) - seat_free(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_DRM: + case UTERM_MONITOR_FBDEV: + case UTERM_MONITOR_FBDEV_DRM: + ret = app_seat_add_video(seat, &vid, ev->dev_type, + ev->dev_node); + if (ret) + return; + uterm_monitor_set_dev_data(ev->dev, vid); break; - if (ev->dev_type == UTERM_MONITOR_DRM || - ev->dev_type == UTERM_MONITOR_FBDEV || - ev->dev_type == UTERM_MONITOR_FBDEV_DRM) - seat_add_video(seat, ev->dev, ev->dev_type, - ev->dev_node); - else if (ev->dev_type == UTERM_MONITOR_INPUT) - uterm_input_add_dev(seat->input, ev->dev_node); + case UTERM_MONITOR_INPUT: + log_debug("new input device %s on seat %s", + ev->dev_node, seat->name); + kmscon_seat_add_input(seat->seat, ev->dev_node); + break; + } break; case UTERM_MONITOR_FREE_DEV: seat = ev->seat_data; if (!seat) + return; + + switch (ev->dev_type) { + case UTERM_MONITOR_DRM: + case UTERM_MONITOR_FBDEV: + case UTERM_MONITOR_FBDEV_DRM: + if (ev->dev_data) + app_seat_remove_video(seat, ev->dev_data); break; - if (ev->dev_type == UTERM_MONITOR_DRM || - ev->dev_type == UTERM_MONITOR_FBDEV || - ev->dev_type == UTERM_MONITOR_FBDEV_DRM) - seat_rm_video(seat, ev->dev); - else if (ev->dev_type == UTERM_MONITOR_INPUT) - uterm_input_remove_dev(seat->input, ev->dev_node); + case UTERM_MONITOR_INPUT: + log_debug("free input device %s on seat %s", + ev->dev_node, seat->name); + kmscon_seat_remove_input(seat->seat, ev->dev_node); + break; + } break; case UTERM_MONITOR_HOTPLUG_DEV: seat = ev->seat_data; if (!seat) + return; + + switch (ev->dev_type) { + case UTERM_MONITOR_DRM: + case UTERM_MONITOR_FBDEV: + case UTERM_MONITOR_FBDEV_DRM: + vid = ev->dev_data; + if (!vid) + return; + + log_debug("video hotplug event on device %s on seat %s", + vid->node, seat->name); + uterm_video_poll(vid->video); break; - seat_hotplug_video(seat, ev->dev); + } break; } } +static void app_sig_generic(struct ev_eloop *eloop, + struct signalfd_siginfo *info, + void *data) +{ + struct kmscon_app *app = data; + + log_info("terminating due to caught signal %d", info->ssi_signo); + ev_eloop_exit(app->eloop); +} + static void destroy_app(struct kmscon_app *app) { uterm_monitor_unref(app->mon); uterm_vt_master_unref(app->vtm); - ev_eloop_unregister_signal_cb(app->eloop, SIGINT, sig_generic, app); - ev_eloop_unregister_signal_cb(app->eloop, SIGTERM, sig_generic, app); ev_eloop_rm_eloop(app->vt_eloop); + 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); } @@ -370,34 +402,47 @@ static int setup_app(struct kmscon_app *app) { int ret; - ret = ev_eloop_new(&app->eloop, log_llog); - if (ret) - goto err_app; - - ret = ev_eloop_register_signal_cb(app->eloop, SIGTERM, - sig_generic, app); - if (ret) - goto err_app; - - ret = ev_eloop_register_signal_cb(app->eloop, SIGINT, - sig_generic, app); - if (ret) - goto err_app; - - ret = ev_eloop_new_eloop(app->eloop, &app->vt_eloop); - if (ret) - goto err_app; - - ret = uterm_vt_master_new(&app->vtm, app->vt_eloop); - if (ret) - goto err_app; - shl_dlist_init(&app->seats); - ret = uterm_monitor_new(&app->mon, app->eloop, monitor_event, app); - if (ret) + ret = ev_eloop_new(&app->eloop, log_llog); + 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_new_eloop(app->eloop, &app->vt_eloop); + if (ret) { + log_error("cannot create VT eloop object: %d", ret); + goto err_app; + } + + ret = uterm_vt_master_new(&app->vtm, app->vt_eloop); + if (ret) { + log_error("cannot create VT master: %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; diff --git a/src/kmscon_seat.c b/src/kmscon_seat.c new file mode 100644 index 0000000..91a8a5d --- /dev/null +++ b/src/kmscon_seat.c @@ -0,0 +1,491 @@ +/* + * Seats + * + * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com> + * + * 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 + * A seat is a single session that is self-hosting and provides all the + * interaction for a single logged-in user. + */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include "eloop.h" +#include "kmscon_conf.h" +#include "kmscon_seat.h" +#include "kmscon_terminal.h" +#include "log.h" +#include "shl_dlist.h" +#include "uterm.h" + +#define LOG_SUBSYSTEM "seat" + +struct kmscon_session { + struct shl_dlist list; + unsigned long ref; + struct kmscon_seat *seat; + + kmscon_session_cb_t cb; + void *data; +}; + +struct kmscon_display { + struct shl_dlist list; + struct kmscon_seat *seat; + struct uterm_display *disp; + bool activated; +}; + +struct kmscon_seat { + struct ev_eloop *eloop; + struct uterm_vt_master *vtm; + + char *name; + bool awake; + struct uterm_input *input; + struct uterm_vt *vt; + struct shl_dlist displays; + + struct shl_dlist sessions; + struct kmscon_session *cur_sess; + + kmscon_seat_cb_t cb; + void *data; +}; + +static void session_call(struct kmscon_session *sess, unsigned int event, + struct uterm_display *disp) +{ + if (!sess->cb) + return; + + sess->cb(sess, event, disp, sess->data); +} + +static void session_wake_up(struct kmscon_session *sess) +{ + session_call(sess, KMSCON_SESSION_WAKE_UP, NULL); +} + +static void session_sleep(struct kmscon_session *sess) +{ + session_call(sess, KMSCON_SESSION_SLEEP, NULL); +} + +static void session_display_new(struct kmscon_session *sess, + struct uterm_display *disp) +{ + session_call(sess, KMSCON_SESSION_DISPLAY_NEW, disp); +} + +static void session_display_gone(struct kmscon_session *sess, + struct uterm_display *disp) +{ + session_call(sess, KMSCON_SESSION_DISPLAY_GONE, disp); +} + +static void session_activate(struct kmscon_session *sess) +{ + struct kmscon_seat *seat = sess->seat; + + if (seat->cur_sess == sess) + return; + + if (seat->cur_sess) { + if (seat->awake) + session_sleep(seat->cur_sess); + seat->cur_sess = NULL; + } + + seat->cur_sess = sess; + if (seat->awake) + session_wake_up(sess); +} + +static void session_deactivate(struct kmscon_session *sess) +{ + struct kmscon_seat *seat = sess->seat; + + if (seat->cur_sess != sess) + return; + + if (seat->awake) + session_sleep(sess); + + if (shl_dlist_empty(&seat->sessions)) { + seat->cur_sess = NULL; + } else { + seat->cur_sess = shl_dlist_entry(seat->sessions.next, + struct kmscon_session, + list); + if (seat->awake) + session_wake_up(seat->cur_sess); + } +} + +static void activate_display(struct kmscon_display *d) +{ + int ret; + struct shl_dlist *iter, *tmp; + struct kmscon_session *s; + struct kmscon_seat *seat = d->seat; + + if (d->activated) + return; + + if (uterm_display_get_state(d->disp) == UTERM_DISPLAY_INACTIVE) { + ret = uterm_display_activate(d->disp, NULL); + if (ret) + return; + + d->activated = true; + + shl_dlist_for_each_safe(iter, tmp, &seat->sessions) { + s = shl_dlist_entry(iter, struct kmscon_session, list); + session_display_new(s, d->disp); + } + + ret = uterm_display_set_dpms(d->disp, UTERM_DPMS_ON); + if (ret) + log_warning("cannot set DPMS state to on for display: %d", + ret); + } +} + +static int seat_add_display(struct kmscon_seat *seat, + struct uterm_display *disp) +{ + struct kmscon_display *d; + + log_debug("add display %p to seat %s", disp, seat->name); + + d = malloc(sizeof(*d)); + if (!d) + return -ENOMEM; + memset(d, 0, sizeof(*d)); + d->disp = disp; + d->seat = seat; + + uterm_display_ref(d->disp); + shl_dlist_link(&seat->displays, &d->list); + activate_display(d); + return 0; +} + +static void seat_remove_display(struct kmscon_seat *seat, + struct kmscon_display *d) +{ + struct shl_dlist *iter, *tmp; + struct kmscon_session *s; + + log_debug("remove display %p from seat %s", d->disp, seat->name); + + shl_dlist_unlink(&d->list); + + if (d->activated) { + shl_dlist_for_each_safe(iter, tmp, &seat->sessions) { + s = shl_dlist_entry(iter, struct kmscon_session, list); + session_display_gone(s, d->disp); + } + } + + uterm_display_unref(d->disp); + free(d); +} + +static int seat_vt_event(struct uterm_vt *vt, unsigned int event, void *data) +{ + struct kmscon_seat *seat = data; + struct shl_dlist *iter; + struct kmscon_display *d; + + switch (event) { + case UTERM_VT_ACTIVATE: + seat->awake = true; + if (seat->cb) + seat->cb(seat, KMSCON_SEAT_WAKE_UP, seat->data); + + uterm_input_wake_up(seat->input); + + shl_dlist_for_each(iter, &seat->displays) { + d = shl_dlist_entry(iter, struct kmscon_display, list); + activate_display(d); + } + + if (seat->cur_sess) + session_wake_up(seat->cur_sess); + break; + case UTERM_VT_DEACTIVATE: + if (seat->cur_sess) + session_sleep(seat->cur_sess); + + uterm_input_sleep(seat->input); + + if (seat->cb) + seat->cb(seat, KMSCON_SEAT_SLEEP, seat->data); + seat->awake = false; + break; + } + + return 0; +} + +int kmscon_seat_new(struct kmscon_seat **out, + struct ev_eloop *eloop, + struct uterm_vt_master *vtm, + const char *seatname, + kmscon_seat_cb_t cb, + void *data) +{ + struct kmscon_seat *seat; + int ret; + struct kmscon_session *s; + + if (!out || !eloop || !vtm || !seatname) + return -EINVAL; + + seat = malloc(sizeof(*seat)); + if (!seat) + return -ENOMEM; + memset(seat, 0, sizeof(*seat)); + seat->eloop = eloop; + seat->vtm = vtm; + seat->cb = cb; + seat->data = data; + shl_dlist_init(&seat->displays); + shl_dlist_init(&seat->sessions); + + seat->name = strdup(seatname); + if (!seat->name) { + log_error("cannot copy string"); + ret = -ENOMEM; + goto err_free; + } + + ret = uterm_input_new(&seat->input, seat->eloop, + kmscon_conf.xkb_layout, + kmscon_conf.xkb_variant, + kmscon_conf.xkb_options, + kmscon_conf.xkb_repeat_delay, + kmscon_conf.xkb_repeat_rate); + if (ret) + goto err_name; + + ret = uterm_vt_allocate(seat->vtm, &seat->vt, seat->name, + seat->input, kmscon_conf.vt, seat_vt_event, + seat); + if (ret) + goto err_input; + + ret = kmscon_terminal_register(&s, seat); + if (ret) + goto err_vt; + + ev_eloop_ref(seat->eloop); + uterm_vt_master_ref(seat->vtm); + *out = seat; + return 0; + +err_vt: + uterm_vt_deallocate(seat->vt); +err_input: + uterm_input_unref(seat->input); +err_name: + free(seat->name); +err_free: + free(seat); + return ret; +} + +void kmscon_seat_free(struct kmscon_seat *seat) +{ + struct kmscon_display *d; + struct kmscon_session *s; + + if (!seat) + return; + + while (!shl_dlist_empty(&seat->sessions)) { + s = shl_dlist_entry(seat->sessions.next, + struct kmscon_session, + list); + kmscon_session_unregister(s); + } + + while (!shl_dlist_empty(&seat->displays)) { + d = shl_dlist_entry(seat->displays.next, + struct kmscon_display, + list); + seat_remove_display(seat, d); + } + + uterm_vt_deallocate(seat->vt); + uterm_input_unref(seat->input); + free(seat->name); + uterm_vt_master_unref(seat->vtm); + ev_eloop_unref(seat->eloop); + free(seat); +} + +int kmscon_seat_add_display(struct kmscon_seat *seat, + struct uterm_display *disp) +{ + if (!seat || !disp) + return -EINVAL; + + return seat_add_display(seat, disp); +} + +void kmscon_seat_remove_display(struct kmscon_seat *seat, + struct uterm_display *disp) +{ + struct shl_dlist *iter; + struct kmscon_display *d; + + shl_dlist_for_each(iter, &seat->displays) { + d = shl_dlist_entry(iter, struct kmscon_display, list); + if (d->disp != disp) + continue; + + seat_remove_display(seat, d); + break; + } +} + +int kmscon_seat_add_input(struct kmscon_seat *seat, const char *node) +{ + if (!seat || !node) + return -EINVAL; + + uterm_input_add_dev(seat->input, node); + return 0; +} + +void kmscon_seat_remove_input(struct kmscon_seat *seat, const char *node) +{ + if (!seat || !node) + return; + + uterm_input_remove_dev(seat->input, node); +} + +const char *kmscon_seat_get_name(struct kmscon_seat *seat) +{ + if (!seat) + return NULL; + + return seat->name; +} + +struct uterm_input *kmscon_seat_get_input(struct kmscon_seat *seat) +{ + if (!seat) + return NULL; + + return seat->input; +} + +struct ev_eloop *kmscon_seat_get_eloop(struct kmscon_seat *seat) +{ + if (!seat) + return NULL; + + return seat->eloop; +} + +int kmscon_seat_register_session(struct kmscon_seat *seat, + struct kmscon_session **out, + kmscon_session_cb_t cb, + void *data) +{ + struct kmscon_session *sess; + + if (!seat || !out) + return -EINVAL; + + sess = malloc(sizeof(*sess)); + if (!sess) { + log_error("cannot allocate memory for new session on seat %s", + seat->name); + return -ENOMEM; + } + memset(sess, 0, sizeof(*sess)); + sess->ref = 1; + sess->seat = seat; + sess->cb = cb; + sess->data = data; + + shl_dlist_link(&seat->sessions, &sess->list); + *out = sess; + + if (!seat->cur_sess) + session_activate(sess); + + return 0; +} + +void kmscon_session_ref(struct kmscon_session *sess) +{ + if (!sess || !sess->ref) + return; + + ++sess->ref; +} + +void kmscon_session_unref(struct kmscon_session *sess) +{ + if (!sess || !sess->ref || --sess->ref) + return; + + kmscon_session_unregister(sess); + free(sess); +} + +void kmscon_session_unregister(struct kmscon_session *sess) +{ + if (!sess || !sess->seat) + return; + + shl_dlist_unlink(&sess->list); + session_deactivate(sess); + sess->seat = NULL; + session_call(sess, KMSCON_SESSION_UNREGISTER, NULL); +} + +void kmscon_session_activate(struct kmscon_session *sess) +{ + if (!sess || !sess->seat) + return; + + session_activate(sess); +} + +void kmscon_session_deactivate(struct kmscon_session *sess) +{ + if (!sess || !sess->seat) + return; + + session_deactivate(sess); +} diff --git a/src/kmscon_seat.h b/src/kmscon_seat.h new file mode 100644 index 0000000..76f8e76 --- /dev/null +++ b/src/kmscon_seat.h @@ -0,0 +1,96 @@ +/* + * Seats + * + * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com> + * + * 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 + * A seat is a single session that is self-hosting and provides all the + * interaction for a single logged-in user. + */ + +#ifndef KMSCON_SEAT_H +#define KMSCON_SEAT_H + +#include <stdlib.h> +#include <unistd.h> +#include "eloop.h" +#include "uterm.h" + +struct kmscon_seat; +struct kmscon_session; + +enum kmscon_seat_event { + KMSCON_SEAT_WAKE_UP, + KMSCON_SEAT_SLEEP, +}; + +typedef void (*kmscon_seat_cb_t) (struct kmscon_seat *seat, + unsigned int event, + void *data); + +enum kmscon_session_event { + KMSCON_SESSION_DISPLAY_NEW, + KMSCON_SESSION_DISPLAY_GONE, + KMSCON_SESSION_WAKE_UP, + KMSCON_SESSION_SLEEP, + KMSCON_SESSION_UNREGISTER, +}; + +typedef void (*kmscon_session_cb_t) (struct kmscon_session *session, + unsigned int event, + struct uterm_display *disp, + void *data); + +int kmscon_seat_new(struct kmscon_seat **out, + struct ev_eloop *eloop, + struct uterm_vt_master *vtm, + const char *seatname, + kmscon_seat_cb_t cb, + void *data); +void kmscon_seat_free(struct kmscon_seat *seat); + +int kmscon_seat_add_display(struct kmscon_seat *seat, + struct uterm_display *disp); +void kmscon_seat_remove_display(struct kmscon_seat *seat, + struct uterm_display *disp); +int kmscon_seat_add_input(struct kmscon_seat *seat, const char *node); +void kmscon_seat_remove_input(struct kmscon_seat *seat, const char *node); + +const char *kmscon_seat_get_name(struct kmscon_seat *seat); +struct uterm_input *kmscon_seat_get_input(struct kmscon_seat *seat); +struct ev_eloop *kmscon_seat_get_eloop(struct kmscon_seat *seat); + +int kmscon_seat_register_session(struct kmscon_seat *seat, + struct kmscon_session **out, + kmscon_session_cb_t cb, + void *data); + +void kmscon_session_ref(struct kmscon_session *sess); +void kmscon_session_unref(struct kmscon_session *sess); +void kmscon_session_unregister(struct kmscon_session *sess); + +void kmscon_session_activate(struct kmscon_session *sess); +void kmscon_session_deactivate(struct kmscon_session *sess); + +#endif /* KMSCON_SEAT_H */ diff --git a/src/kmscon_terminal.c b/src/kmscon_terminal.c index a616899..6e4da08 100644 --- a/src/kmscon_terminal.c +++ b/src/kmscon_terminal.c @@ -36,6 +36,7 @@ #include <string.h> #include "eloop.h" #include "kmscon_conf.h" +#include "kmscon_seat.h" #include "kmscon_terminal.h" #include "log.h" #include "pty.h" @@ -62,6 +63,8 @@ struct kmscon_terminal { bool opened; bool awake; + struct kmscon_session *session; + struct shl_dlist screens; unsigned int min_cols; unsigned int min_rows; @@ -73,9 +76,6 @@ struct kmscon_terminal { struct tsm_vte *vte; struct kmscon_pty *pty; struct ev_fd *ptyfd; - - kmscon_terminal_event_cb cb; - void *data; }; static void redraw(struct kmscon_terminal *term) @@ -180,6 +180,7 @@ static void terminal_resize(struct kmscon_terminal *term, /* shrinking always succeeds */ tsm_screen_resize(term->console, term->min_cols, term->min_rows); kmscon_pty_resize(term->pty, term->min_cols, term->min_rows); + schedule_redraw(term); } static int add_display(struct kmscon_terminal *term, struct uterm_display *disp) @@ -310,45 +311,6 @@ static void rm_display(struct kmscon_terminal *term, struct uterm_display *disp) log_debug("removed display %p from terminal %p", disp, term); free_screen(term, scr, true); - if (shl_dlist_empty(&term->screens) && term->cb) - term->cb(term, KMSCON_TERMINAL_NO_DISPLAY, term->data); -} - -static void rm_all_screens(struct kmscon_terminal *term) -{ - struct shl_dlist *iter; - struct screen *scr; - - while ((iter = term->screens.next) != &term->screens) { - scr = shl_dlist_entry(iter, struct screen, list); - free_screen(term, scr, false); - } - - term->min_cols = 0; - term->min_rows = 0; -} - -static void pty_input(struct kmscon_pty *pty, const char *u8, size_t len, - void *data) -{ - struct kmscon_terminal *term = data; - - if (!len) { - if (term->cb) - term->cb(term, KMSCON_TERMINAL_HUP, term->data); - else - kmscon_terminal_open(term, term->cb, term->data); - } else { - tsm_vte_input(term->vte, u8, len); - schedule_redraw(term); - } -} - -static void pty_event(struct ev_fd *fd, int mask, void *data) -{ - struct kmscon_terminal *term = data; - - kmscon_pty_dispatch(term->pty); } static void input_event(struct uterm_input *input, @@ -403,6 +365,111 @@ static void input_event(struct uterm_input *input, } } +static void rm_all_screens(struct kmscon_terminal *term) +{ + struct shl_dlist *iter; + struct screen *scr; + + while ((iter = term->screens.next) != &term->screens) { + scr = shl_dlist_entry(iter, struct screen, list); + free_screen(term, scr, false); + } + + term->min_cols = 0; + term->min_rows = 0; +} + +static int terminal_open(struct kmscon_terminal *term) +{ + int ret; + unsigned short width, height; + + kmscon_pty_close(term->pty); + tsm_vte_hard_reset(term->vte); + width = tsm_screen_get_width(term->console); + height = tsm_screen_get_height(term->console); + ret = kmscon_pty_open(term->pty, width, height); + if (ret) { + term->opened = false; + return ret; + } + + term->opened = true; + schedule_redraw(term); + return 0; +} + +static void terminal_close(struct kmscon_terminal *term) +{ + kmscon_pty_close(term->pty); + term->opened = false; +} + +static void terminal_destroy(struct kmscon_terminal *term) +{ + log_debug("free terminal object %p", term); + + terminal_close(term); + rm_all_screens(term); + ev_eloop_rm_timer(term->redraw_timer); + ev_timer_unref(term->redraw_timer); + uterm_input_unregister_cb(term->input, input_event, term); + ev_eloop_rm_fd(term->ptyfd); + kmscon_pty_unref(term->pty); + tsm_vte_unref(term->vte); + tsm_screen_unref(term->console); + uterm_input_unref(term->input); + ev_eloop_unref(term->eloop); + free(term); +} + +static void session_event(struct kmscon_session *session, unsigned int event, + struct uterm_display *disp, void *data) +{ + struct kmscon_terminal *term = data; + + switch (event) { + case KMSCON_SESSION_DISPLAY_NEW: + add_display(term, disp); + break; + case KMSCON_SESSION_DISPLAY_GONE: + rm_display(term, disp); + break; + case KMSCON_SESSION_WAKE_UP: + term->awake = true; + if (!term->opened) + terminal_open(term); + schedule_redraw(term); + break; + case KMSCON_SESSION_SLEEP: + term->awake = false; + break; + case KMSCON_SESSION_UNREGISTER: + terminal_destroy(term); + break; + } +} + +static void pty_input(struct kmscon_pty *pty, const char *u8, size_t len, + void *data) +{ + struct kmscon_terminal *term = data; + + if (!len) { + terminal_open(term); + } else { + tsm_vte_input(term->vte, u8, len); + schedule_redraw(term); + } +} + +static void pty_event(struct ev_fd *fd, int mask, void *data) +{ + struct kmscon_terminal *term = data; + + kmscon_pty_dispatch(term->pty); +} + static void write_event(struct tsm_vte *vte, const char *u8, size_t len, void *data) { @@ -411,17 +478,15 @@ static void write_event(struct tsm_vte *vte, const char *u8, size_t len, kmscon_pty_write(term->pty, u8, len); } -int kmscon_terminal_new(struct kmscon_terminal **out, - struct ev_eloop *loop, - struct uterm_input *input, - const char *seat) +int kmscon_terminal_register(struct kmscon_session **out, + struct kmscon_seat *seat) { struct kmscon_terminal *term; int ret; struct itimerspec spec; unsigned long fps; - if (!out || !loop || !input) + if (!out || !seat) return -EINVAL; term = malloc(sizeof(*term)); @@ -430,8 +495,8 @@ int kmscon_terminal_new(struct kmscon_terminal **out, memset(term, 0, sizeof(*term)); term->ref = 1; - term->eloop = loop; - term->input = input; + term->eloop = kmscon_seat_get_eloop(seat); + term->input = kmscon_seat_get_input(seat); shl_dlist_init(&term->screens); if (kmscon_conf.fps) { @@ -473,7 +538,7 @@ int kmscon_terminal_new(struct kmscon_terminal **out, if (ret) goto err_pty; - ret = kmscon_pty_set_seat(term->pty, seat); + ret = kmscon_pty_set_seat(term->pty, kmscon_seat_get_name(seat)); if (ret) goto err_pty; @@ -501,13 +566,21 @@ int kmscon_terminal_new(struct kmscon_terminal **out, if (ret) goto err_timer; + ret = kmscon_seat_register_session(seat, &term->session, session_event, + term); + if (ret) { + log_error("cannot register session for terminal: %d", ret); + goto err_redraw; + } + ev_eloop_ref(term->eloop); uterm_input_ref(term->input); - *out = term; - + *out = term->session; log_debug("new terminal object %p", term); return 0; +err_redraw: + ev_eloop_rm_timer(term->redraw_timer); err_timer: ev_timer_unref(term->redraw_timer); err_input: @@ -524,116 +597,3 @@ err_free: free(term); return ret; } - -void kmscon_terminal_ref(struct kmscon_terminal *term) -{ - if (!term) - return; - - term->ref++; -} - -void kmscon_terminal_unref(struct kmscon_terminal *term) -{ - if (!term || !term->ref) - return; - - if (--term->ref) - return; - - log_debug("free terminal object %p", term); - kmscon_terminal_close(term); - rm_all_screens(term); - ev_eloop_rm_timer(term->redraw_timer); - ev_timer_unref(term->redraw_timer); - uterm_input_unregister_cb(term->input, input_event, term); - ev_eloop_rm_fd(term->ptyfd); - kmscon_pty_unref(term->pty); - tsm_vte_unref(term->vte); - tsm_screen_unref(term->console); - uterm_input_unref(term->input); - ev_eloop_unref(term->eloop); - free(term); -} - -int kmscon_terminal_open(struct kmscon_terminal *term, - kmscon_terminal_event_cb cb, void *data) -{ - int ret; - unsigned short width, height; - - if (!term) - return -EINVAL; - - kmscon_pty_close(term->pty); - tsm_vte_hard_reset(term->vte); - width = tsm_screen_get_width(term->console); - height = tsm_screen_get_height(term->console); - ret = kmscon_pty_open(term->pty, width, height); - if (ret) - return ret; - - term->opened = true; - term->cb = cb; - term->data = data; - return 0; -} - -void kmscon_terminal_close(struct kmscon_terminal *term) -{ - if (!term) - return; - - kmscon_pty_close(term->pty); - term->data = NULL; - term->cb = NULL; - term->opened = false; -} - -void kmscon_terminal_redraw(struct kmscon_terminal *term) -{ - if (!term) - return; - - schedule_redraw(term); -} - -int kmscon_terminal_add_display(struct kmscon_terminal *term, - struct uterm_display *disp) -{ - if (!term || !disp) - return -EINVAL; - - return add_display(term, disp); -} - -void kmscon_terminal_remove_display(struct kmscon_terminal *term, - struct uterm_display *disp) -{ - if (!term || !disp) - return; - - rm_display(term, disp); -} - -void kmscon_terminal_wake_up(struct kmscon_terminal *term) -{ - if (!term || term->awake) - return; - - term->awake = true; - schedule_redraw(term); -} - -void kmscon_terminal_sleep(struct kmscon_terminal *term) -{ - if (!term || !term->awake) - return; - - term->awake = false; -} - -bool kmscon_terminal_is_awake(struct kmscon_terminal *term) -{ - return term && term->awake; -} diff --git a/src/kmscon_terminal.h b/src/kmscon_terminal.h index e41381d..6bdb4bf 100644 --- a/src/kmscon_terminal.h +++ b/src/kmscon_terminal.h @@ -34,41 +34,9 @@ #define KMSCON_TERMINAL_H #include <stdlib.h> -#include "eloop.h" -#include "tsm_screen.h" -#include "uterm.h" +#include "kmscon_seat.h" -struct kmscon_terminal; - -enum kmscon_terminal_etype { - KMSCON_TERMINAL_HUP, /* child closed */ - KMSCON_TERMINAL_NO_DISPLAY, /* no more display connected */ -}; - -typedef void (*kmscon_terminal_event_cb) - (struct kmscon_terminal *term, - enum kmscon_terminal_etype type, - void *data); - -int kmscon_terminal_new(struct kmscon_terminal **out, - struct ev_eloop *loop, - struct uterm_input *input, - const char *seat); -void kmscon_terminal_ref(struct kmscon_terminal *term); -void kmscon_terminal_unref(struct kmscon_terminal *term); - -int kmscon_terminal_open(struct kmscon_terminal *term, - kmscon_terminal_event_cb event_cb, void *data); -void kmscon_terminal_close(struct kmscon_terminal *term); -void kmscon_terminal_redraw(struct kmscon_terminal *term); - -int kmscon_terminal_add_display(struct kmscon_terminal *term, - struct uterm_display *disp); -void kmscon_terminal_remove_display(struct kmscon_terminal *term, - struct uterm_display *disp); - -void kmscon_terminal_wake_up(struct kmscon_terminal *term); -void kmscon_terminal_sleep(struct kmscon_terminal *term); -bool kmscon_terminal_is_awake(struct kmscon_terminal *term); +int kmscon_terminal_register(struct kmscon_session **out, + struct kmscon_seat *seat); #endif /* KMSCON_TERMINAL_H */