$(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
#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 kmscon_app {
- struct ev_eloop *eloop;
- struct ev_eloop *vt_eloop;
- unsigned int vt_exit_count;
+struct app_video {
+ struct shl_dlist list;
+ struct app_seat *seat;
- struct uterm_vt_master *vtm;
- struct uterm_monitor *mon;
- struct shl_dlist seats;
+ char *node;
+ struct uterm_video *video;
};
-struct kmscon_seat {
+struct app_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;
+ char *name;
+ struct kmscon_seat *seat;
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)
-{
- struct kmscon_app *app = data;
+struct kmscon_app {
+ struct ev_eloop *eloop;
+ struct ev_eloop *vt_eloop;
+ unsigned int vt_exit_count;
- ev_eloop_exit(app->eloop);
- log_info("terminating due to caught signal %d", info->ssi_signo);
-}
+ struct uterm_vt_master *vtm;
+ struct uterm_monitor *mon;
+ struct shl_dlist seats;
+};
-static int vt_event(struct uterm_vt *vt, unsigned int action, void *data)
+static void app_seat_event(struct kmscon_seat *s, unsigned int event,
+ 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;
}
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)
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);
- log_debug("new graphics device on seat %s", seat->sname);
- return;
+ shl_dlist_link(&seat->videos, &vid->list);
+ *out = vid;
+ return 0;
+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;
-}
-
-static void seat_rm_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;
-
- 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;
- }
+ return ret;
}
-static void seat_hotplug_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;
-
- uterm_video_poll(vid->video);
- 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 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);
}
{
int ret;
+ shl_dlist_init(&app->seats);
+
ret = ev_eloop_new(&app->eloop, log_llog);
- if (ret)
+ if (ret) {
+ log_error("cannot create eloop object: %d", ret);
goto err_app;
+ }
ret = ev_eloop_register_signal_cb(app->eloop, SIGTERM,
- sig_generic, app);
- if (ret)
+ 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,
- sig_generic, app);
- if (ret)
+ 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)
+ 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)
+ if (ret) {
+ log_error("cannot create VT master: %d", ret);
goto err_app;
+ }
- shl_dlist_init(&app->seats);
-
- ret = uterm_monitor_new(&app->mon, app->eloop, monitor_event, app);
- if (ret)
+ 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;
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
#include <string.h>
#include "eloop.h"
#include "kmscon_conf.h"
+#include "kmscon_seat.h"
#include "kmscon_terminal.h"
#include "log.h"
#include "pty.h"
bool opened;
bool awake;
+ struct kmscon_session *session;
+
struct shl_dlist screens;
unsigned int min_cols;
unsigned int min_rows;
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)
/* 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)
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,
}
}
+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)
{
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));
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) {
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;
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:
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;
-}
#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 */