2 * kmscon - VT compatibility layer
4 * Copyright (c) 2011 David Herrmann <dh.herrmann@googlemail.com>
5 * Copyright (c) 2011 University of Tuebingen
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files
9 * (the "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
15 * The above copyright notice and this permission notice shall be included
16 * in all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 * This kmscon-VT API is based on the wayland-compositor demo:
30 * Copyright © 2010 Intel Corporation
32 * Permission to use, copy, modify, distribute, and sell this software and
33 * its documentation for any purpose is hereby granted without fee, provided
34 * that the above copyright notice appear in all copies and that both that
35 * copyright notice and this permission notice appear in supporting
36 * documentation, and that the name of the copyright holders not be used in
37 * advertising or publicity pertaining to distribution of the software
38 * without specific, written prior permission. The copyright holders make
39 * no representations about the suitability of this software for any
40 * purpose. It is provided "as is" without express or implied warranty.
42 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
43 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
44 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
45 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
46 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
47 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
48 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
53 * Although we do not use the VT for drawing or anything, we set it to graphical
54 * mode to avoid side effects.
55 * This behavior is copied from wayland. If someone has more insight in VT
56 * handling, they may adjust this code.
69 #include <sys/ioctl.h>
83 struct termios saved_attribs;
87 struct ev_signal *sig1;
88 struct ev_signal *sig2;
92 int kmscon_vt_new(struct kmscon_vt **out, kmscon_vt_cb cb, void *data)
99 vt = malloc(sizeof(*vt));
103 memset(vt, 0, sizeof(*vt));
111 log_debug("vt: create VT object\n");
116 void kmscon_vt_ref(struct kmscon_vt *vt)
124 void kmscon_vt_unref(struct kmscon_vt *vt)
132 log_debug("vt: destroy VT object\n");
137 static void vt_enter(struct ev_signal *sig, int signum, void *data)
139 struct kmscon_vt *vt = data;
141 if (!vt || vt->fd < 0)
144 log_debug("vt: entering VT\n");
146 ioctl(vt->fd, VT_RELDISP, VT_ACKACQ);
148 if (ioctl(vt->fd, KDSETMODE, KD_GRAPHICS))
149 log_warn("vt: cannot set graphics mode on vt\n");
152 vt->cb(vt, KMSCON_VT_ENTER, vt->data);
155 static void vt_leave(struct ev_signal *sig, int signum, void *data)
157 struct kmscon_vt *vt = data;
159 if (!vt || vt->fd < 0)
162 if (vt->cb && !vt->cb(vt, KMSCON_VT_LEAVE, vt->data)) {
163 log_debug("vt: leaving VT denied\n");
164 ioctl(vt->fd, VT_RELDISP, 0);
166 log_debug("vt: leaving VT\n");
167 ioctl(vt->fd, VT_RELDISP, 1);
168 if (ioctl(vt->fd, KDSETMODE, KD_TEXT))
169 log_warn("vt: cannot set text mode on vt\n");
173 static void vt_input(struct ev_fd *fd, int mask, void *data)
175 struct kmscon_vt *vt = data;
177 if (!vt || vt->fd < 0)
180 /* we ignore input from the VT because we get it from evdev */
181 tcflush(vt->fd, TCIFLUSH);
184 static int connect_eloop(struct kmscon_vt *vt, struct ev_eloop *eloop)
188 if (!vt || !eloop || vt->fd < 0)
191 ret = ev_eloop_new_signal(eloop, &vt->sig1, SIGUSR1, vt_leave, vt);
195 ret = ev_eloop_new_signal(eloop, &vt->sig2, SIGUSR2, vt_enter, vt);
199 ret = ev_eloop_new_fd(eloop, &vt->efd, vt->fd, EV_READABLE,
207 ev_eloop_rm_signal(vt->sig2);
210 ev_eloop_rm_signal(vt->sig1);
215 static void disconnect_eloop(struct kmscon_vt *vt)
220 ev_eloop_rm_signal(vt->sig1);
221 ev_eloop_rm_signal(vt->sig2);
222 ev_eloop_rm_fd(vt->efd);
228 static int open_tty(int id, int *tty_fd, int *tty_num)
233 if (!tty_fd || !tty_num)
236 if (id == KMSCON_VT_NEW) {
237 fd = open("/dev/tty0", O_NONBLOCK | O_NOCTTY | O_CLOEXEC);
239 fd = open("/dev/tty1", O_NONBLOCK | O_NOCTTY |
242 log_err("vt: cannot find unused tty\n");
247 if (ioctl(fd, VT_OPENQRY, &id) || id <= 0) {
249 log_err("vt: cannot find unused tty\n");
255 snprintf(filename, sizeof(filename), "/dev/tty%d", id);
256 filename[sizeof(filename) - 1] = 0;
257 log_debug("vt: using tty %s\n", filename);
259 fd = open(filename, O_RDWR | O_NOCTTY | O_CLOEXEC);
261 log_err("vt: cannot open vt\n");
270 int kmscon_vt_open(struct kmscon_vt *vt, int id, struct ev_eloop *eloop)
272 struct termios raw_attribs;
281 ret = open_tty(id, &vt->fd, &vt->num);
285 ret = connect_eloop(vt, eloop);
290 * Get the number of the VT which is active now, so we have something
291 * to switch back to in kmscon_vt_switch_leave.
293 ret = ioctl(vt->fd, VT_GETSTATE, &vts);
295 log_warn("vt: cannot find the current VT\n");
298 vt->saved_num = vts.v_active;
301 if (tcgetattr(vt->fd, &vt->saved_attribs) < 0) {
302 log_err("vt: cannot get terminal attributes\n");
307 /* Ignore control characters and disable echo */
308 raw_attribs = vt->saved_attribs;
309 cfmakeraw(&raw_attribs);
311 /* Fix up line endings to be normal (cfmakeraw hoses them) */
312 raw_attribs.c_oflag |= OPOST | OCRNL;
314 if (tcsetattr(vt->fd, TCSANOW, &raw_attribs) < 0)
315 log_warn("vt: cannot put terminal into raw mode\n");
317 if (ioctl(vt->fd, KDSETMODE, KD_GRAPHICS)) {
318 log_err("vt: cannot set graphics mode\n");
323 memset(&mode, 0, sizeof(mode));
324 mode.mode = VT_PROCESS;
325 mode.relsig = SIGUSR1;
326 mode.acqsig = SIGUSR2;
328 if (ioctl(vt->fd, VT_SETMODE, &mode)) {
329 log_err("vt: cannot take control of vt handling\n");
335 sigaddset(&mask, SIGUSR1);
336 sigaddset(&mask, SIGUSR2);
337 sigprocmask(SIG_BLOCK, &mask, NULL);
342 ioctl(vt->fd, KDSETMODE, KD_TEXT);
344 tcsetattr(vt->fd, TCSANOW, &vt->saved_attribs);
346 disconnect_eloop(vt);
353 void kmscon_vt_close(struct kmscon_vt *vt)
355 if (!vt || vt->fd < 0)
358 ioctl(vt->fd, KDSETMODE, KD_TEXT);
359 tcsetattr(vt->fd, TCSANOW, &vt->saved_attribs);
360 disconnect_eloop(vt);
368 /* Switch to this VT and make it the active VT. */
369 int kmscon_vt_enter(struct kmscon_vt *vt)
373 if (!vt || vt->fd < 0 || vt->num < 0)
376 ret = ioctl(vt->fd, VT_ACTIVATE, vt->num);
378 log_warn("vt: cannot enter VT\n");
382 log_debug("vt: enter VT on demand\n");
387 * Switch back to the VT from which we started.
388 * Note: The VT switch needs to be acknowledged by us so we need to react on
389 * SIGUSR. This function returns -EINPROGRESS if we started the VT switch but
390 * still needs to react on SIGUSR. Make sure you call the eloop dispatcher again
391 * if you get -EINPROGRESS here.
393 * Returns 0 if we don't know the previous VT or if the previous VT is already
394 * active. Returns -EINPROGRESS if we started the VT switch. Returns <0 on
397 int kmscon_vt_leave(struct kmscon_vt *vt)
402 if (!vt || vt->fd < 0)
405 if (vt->saved_num < 0)
408 ret = ioctl(vt->fd, VT_GETSTATE, &vts);
410 log_warn("vt: cannot find current VT\n");
414 if (vts.v_active != vt->num)
417 ret = ioctl(vt->fd, VT_ACTIVATE, vt->saved_num);
419 log_warn("vt: cannot leave VT\n");
423 log_debug("vt: leave VT on demand\n");