From dfd880881ef35204ca1307d1dee401bbb9e56c71 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 11 Dec 2011 01:56:48 +0200 Subject: [PATCH] vt: add support for automatic VT switching Add two functions to enter/leave our VT object. This allows to implement to expected behavior of automatically switching to the kmscon when it is running on a new tty, and switching back to the tty we came from when the program finishes. Presumably this behavior will be controlled by a config variable or command line argument later on (like Xorg's -novtswitch). There's a bit of a subtlety in this because of VT_PROCESS. We need permission from ourselves to switch in/out of out VT; this is done when processing SIGUSR[12] in the eloop. We therefore must dispatch the loop at least once after switching out. The usual case is demonstrated in test_vt.c. Signed-off-by: Ran Benita Signed-off-by: David Herrmann --- src/vt.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- src/vt.h | 2 ++ tests/test_vt.c | 14 +++++++++ 3 files changed, 105 insertions(+), 7 deletions(-) diff --git a/src/vt.c b/src/vt.c index dd69462..e71da3d 100644 --- a/src/vt.c +++ b/src/vt.c @@ -78,6 +78,8 @@ struct kmscon_vt { unsigned long ref; int fd; + int num; + int saved_num; struct termios saved_attribs; kmscon_vt_cb cb; void *data; @@ -101,6 +103,8 @@ int kmscon_vt_new(struct kmscon_vt **out, kmscon_vt_cb cb, void *data) memset(vt, 0, sizeof(*vt)); vt->ref = 1; vt->fd = -1; + vt->num = -1; + vt->saved_num = -1; vt->cb = cb; vt->data = data; @@ -177,11 +181,14 @@ static void vt_input(struct kmscon_fd *fd, int mask, void *data) tcflush(vt->fd, TCIFLUSH); } -static int open_tty(int id) +static int open_tty(int id, int *tty_fd, int *tty_num) { int fd; char filename[16]; + if (!tty_fd || !tty_num) + return -EINVAL; + if (id == KMSCON_VT_NEW) { fd = open("/dev/tty0", O_NONBLOCK | O_NOCTTY); if (fd < 0) { @@ -210,23 +217,37 @@ static int open_tty(int id) return -errno; } - return fd; + *tty_fd = fd; + *tty_num = id; + return 0; } int kmscon_vt_open(struct kmscon_vt *vt, int id) { struct termios raw_attribs; struct vt_mode mode; - int ret, fd; + struct vt_stat vts; + int ret; sigset_t mask; if (vt->fd >= 0) return -EALREADY; - fd = open_tty(id); - if (fd < 0) - return fd; - vt->fd = fd; + ret = open_tty(id, &vt->fd, &vt->num); + if (ret) + return ret; + + /* + * Get the number of the VT which is active now, so we have something + * to switch back to in kmscon_vt_switch_leave. + */ + ret = ioctl(vt->fd, VT_GETSTATE, &vts); + if (ret) { + log_warning("vt: cannot find the current VT\n"); + vt->saved_num = -1; + } else { + vt->saved_num = vts.v_active; + } if (tcgetattr(vt->fd, &vt->saved_attribs) < 0) { log_err("vt: cannot get terminal attributes\n"); @@ -289,6 +310,8 @@ void kmscon_vt_close(struct kmscon_vt *vt) kmscon_vt_disconnect_eloop(vt); close(vt->fd); vt->fd = -1; + vt->num = -1; + vt->saved_num = -1; } int kmscon_vt_connect_eloop(struct kmscon_vt *vt, struct kmscon_eloop *loop) @@ -339,3 +362,62 @@ void kmscon_vt_disconnect_eloop(struct kmscon_vt *vt) vt->sig2 = NULL; vt->efd = NULL; } + +/* Switch to this VT and make it the active VT. */ +int kmscon_vt_enter(struct kmscon_vt *vt) +{ + int ret; + + if (!vt || vt->fd < 0 || vt->num < 0) + return -EINVAL; + + ret = ioctl(vt->fd, VT_ACTIVATE, vt->num); + if (ret) { + log_warning("vt: cannot enter VT\n"); + return -EFAULT; + } + + log_debug("vt: enter VT on demand\n"); + return 0; +} + +/* + * Switch back to the VT from which we started. + * Note: The VT switch needs to be acknowledged by us so we need to react on + * SIGUSR. This function returns -EINPROGRESS if we started the VT switch but + * still needs to react on SIGUSR. Make sure you call the eloop dispatcher again + * if you get -EINPROGRESS here. + * + * Returns 0 if we don't know the previous VT or if the previous VT is already + * active. Returns -EINPROGRESS if we started the VT switch. Returns <0 on + * failure. + */ +int kmscon_vt_leave(struct kmscon_vt *vt) +{ + int ret; + struct vt_stat vts; + + if (!vt || vt->fd < 0) + return -EINVAL; + + if (vt->saved_num < 0) + return 0; + + ret = ioctl(vt->fd, VT_GETSTATE, &vts); + if (ret) { + log_warning("vt: cannot find current VT\n"); + return -EFAULT; + } + + if (vts.v_active != vt->num) + return 0; + + ret = ioctl(vt->fd, VT_ACTIVATE, vt->saved_num); + if (ret) { + log_warning("vt: cannot leave VT\n"); + return -EFAULT; + } + + log_debug("vt: leave VT on demand\n"); + return -EINPROGRESS; +} diff --git a/src/vt.h b/src/vt.h index 4fff235..7a22af7 100644 --- a/src/vt.h +++ b/src/vt.h @@ -64,4 +64,6 @@ void kmscon_vt_close(struct kmscon_vt *vt); int kmscon_vt_connect_eloop(struct kmscon_vt *vt, struct kmscon_eloop *loop); void kmscon_vt_disconnect_eloop(struct kmscon_vt *vt); +int kmscon_vt_enter(struct kmscon_vt *vt); +int kmscon_vt_leave(struct kmscon_vt *vt); #endif /* KMSCON_VT_H */ diff --git a/tests/test_vt.c b/tests/test_vt.c index 6dcf60d..7931133 100644 --- a/tests/test_vt.c +++ b/tests/test_vt.c @@ -28,6 +28,9 @@ * Test VT Layer * This opens a new VT and prints some text on it. You can then change the VT * and change back. This is only to test the VT subsystem and event engine. + * This automatically switches to the new VT. Currently, the display gets + * freezed then because we aren't painting to the framebuffer yet. Use + * ctrl+alt+FX (or some equivalent) to switch back to X/VT. */ #include @@ -85,6 +88,10 @@ int main(int argc, char **argv) goto err_vt; } + ret = kmscon_vt_enter(vt); + if (ret) + log_warning("Cannot switch to VT\n"); + while (!terminate) { ret = kmscon_eloop_dispatch(loop, -1); if (ret) { @@ -93,6 +100,13 @@ int main(int argc, char **argv) } } + log_debug("Terminating\n"); + + /* switch back to previous VT but wait for eloop to process SIGUSR0 */ + ret = kmscon_vt_leave(vt); + if (ret == -EINPROGRESS) + kmscon_eloop_dispatch(loop, 1000); + err_vt: kmscon_vt_unref(vt); err_sig: -- 2.7.4