tty: Fall back to K_RAW if K_OFF is not available
[platform/upstream/weston.git] / src / tty.c
1 /*
2  * Copyright © 2010 Intel Corporation
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and
5  * its documentation for any purpose is hereby granted without fee, provided
6  * that the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of the copyright holders not be used in
9  * advertising or publicity pertaining to distribution of the software
10  * without specific, written prior permission.  The copyright holders make
11  * no representations about the suitability of this software for any
12  * purpose.  It is provided "as is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  */
22
23 #include <termios.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <signal.h>
30 #include <linux/kd.h>
31 #include <linux/vt.h>
32 #include <linux/major.h>
33 #include <sys/ioctl.h>
34
35 #include "compositor.h"
36
37 /* Introduced in 2.6.38 */
38 #ifndef K_OFF
39 #define K_OFF 0x04
40 #endif
41
42 struct tty {
43         struct weston_compositor *compositor;
44         int fd;
45         struct termios terminal_attributes;
46
47         struct wl_event_source *input_source;
48         struct wl_event_source *vt_source;
49         tty_vt_func_t vt_func;
50         int vt, starting_vt, has_vt;
51         int kb_mode;
52 };
53
54 static int vt_handler(int signal_number, void *data)
55 {
56         struct tty *tty = data;
57
58         if (tty->has_vt) {
59                 tty->vt_func(tty->compositor, TTY_LEAVE_VT);
60                 tty->has_vt = 0;
61
62                 ioctl(tty->fd, VT_RELDISP, 1);
63         } else {
64                 ioctl(tty->fd, VT_RELDISP, VT_ACKACQ);
65
66                 tty->vt_func(tty->compositor, TTY_ENTER_VT);
67                 tty->has_vt = 1;
68         }
69
70         return 1;
71 }
72
73 static int
74 on_tty_input(int fd, uint32_t mask, void *data)
75 {
76         struct tty *tty = data;
77
78         /* Ignore input to tty.  We get keyboard events from evdev */
79         tcflush(tty->fd, TCIFLUSH);
80
81         return 1;
82 }
83
84 static int
85 try_open_vt(struct tty *tty)
86 {
87         int tty0, fd;
88         char filename[16];
89
90         tty0 = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
91         if (tty0 < 0) {
92                 fprintf(stderr, "could not open tty0: %m\n");
93                 return -1;
94         }
95
96         if (ioctl(tty0, VT_OPENQRY, &tty->vt) < 0 || tty->vt == -1) {
97                 fprintf(stderr, "could not open tty0: %m\n");
98                 close(tty0);
99                 return -1;
100         }
101
102         close(tty0);
103         snprintf(filename, sizeof filename, "/dev/tty%d", tty->vt);
104         fprintf(stderr, "compositor: using new vt %s\n", filename);
105         fd = open(filename, O_RDWR | O_NOCTTY | O_CLOEXEC);
106         if (fd < 0)
107                 return fd;
108
109         return fd;
110 }
111
112 int
113 tty_activate_vt(struct tty *tty, int vt)
114 {
115         return ioctl(tty->fd, VT_ACTIVATE, vt);
116 }
117
118 struct tty *
119 tty_create(struct weston_compositor *compositor, tty_vt_func_t vt_func,
120            int tty_nr)
121 {
122         struct termios raw_attributes;
123         struct vt_mode mode = { 0 };
124         int ret;
125         struct tty *tty;
126         struct wl_event_loop *loop;
127         struct stat buf;
128         char filename[16];
129         struct vt_stat vts;
130
131         tty = malloc(sizeof *tty);
132         if (tty == NULL)
133                 return NULL;
134
135         memset(tty, 0, sizeof *tty);
136         tty->compositor = compositor;
137         tty->vt_func = vt_func;
138
139         tty->fd = weston_environment_get_fd("WESTON_TTY_FD");
140         if (tty->fd < 0)
141                 tty->fd = STDIN_FILENO;
142
143         if (tty_nr > 0) {
144                 snprintf(filename, sizeof filename, "/dev/tty%d", tty_nr);
145                 fprintf(stderr, "compositor: using %s\n", filename);
146                 tty->fd = open(filename, O_RDWR | O_NOCTTY | O_CLOEXEC);
147                 tty->vt = tty_nr;
148         } else if (fstat(tty->fd, &buf) == 0 &&
149                    major(buf.st_rdev) == TTY_MAJOR &&
150                    minor(buf.st_rdev) > 0) {
151                 if (tty->fd == STDIN_FILENO)
152                         tty->fd = fcntl(STDIN_FILENO, F_DUPFD_CLOEXEC, 0);
153                 tty->vt = minor(buf.st_rdev);
154         } else {
155                 /* Fall back to try opening a new VT.  This typically
156                  * requires root. */
157                 tty->fd = try_open_vt(tty);
158         }
159
160         if (tty->fd <= 0) {
161                 fprintf(stderr, "failed to open tty: %m\n");
162                 free(tty);
163                 return NULL;
164         }
165
166         if (ioctl(tty->fd, VT_GETSTATE, &vts) == 0)
167                 tty->starting_vt = vts.v_active;
168         else
169                 tty->starting_vt = tty->vt;
170
171         if (tty->starting_vt != tty->vt) {
172                 if (ioctl(tty->fd, VT_ACTIVATE, tty->vt) < 0 ||
173                     ioctl(tty->fd, VT_WAITACTIVE, tty->vt) < 0) {
174                         fprintf(stderr, "failed to swtich to new vt\n");
175                         return NULL;
176                 }
177         }
178
179         if (tcgetattr(tty->fd, &tty->terminal_attributes) < 0) {
180                 fprintf(stderr, "could not get terminal attributes: %m\n");
181                 goto err;
182         }
183
184         /* Ignore control characters and disable echo */
185         raw_attributes = tty->terminal_attributes;
186         cfmakeraw(&raw_attributes);
187
188         /* Fix up line endings to be normal (cfmakeraw hoses them) */
189         raw_attributes.c_oflag |= OPOST | OCRNL;
190
191         if (tcsetattr(tty->fd, TCSANOW, &raw_attributes) < 0)
192                 fprintf(stderr, "could not put terminal into raw mode: %m\n");
193
194         loop = wl_display_get_event_loop(compositor->wl_display);
195
196         ioctl(tty->fd, KDGKBMODE, &tty->kb_mode);
197         ret = ioctl(tty->fd, KDSKBMODE, K_OFF);
198         if (ret) {
199                 ret = ioctl(tty->fd, KDSKBMODE, K_RAW);
200                 if (ret)
201                         goto err_attr;
202
203                 tty->input_source = wl_event_loop_add_fd(loop, tty->fd,
204                                                          WL_EVENT_READABLE,
205                                                          on_tty_input, tty);
206                 if (!tty->input_source)
207                         goto err_kdkbmode;
208         }
209
210         if (ret) {
211                 fprintf(stderr, "failed to set K_OFF keyboard mode on tty: %m\n");
212                 goto err_attr;
213         }
214
215         ret = ioctl(tty->fd, KDSETMODE, KD_GRAPHICS);
216         if (ret) {
217                 fprintf(stderr, "failed to set KD_GRAPHICS mode on tty: %m\n");
218                 goto err_kdkbmode;
219         }
220
221         tty->has_vt = 1;
222         mode.mode = VT_PROCESS;
223         mode.relsig = SIGUSR1;
224         mode.acqsig = SIGUSR1;
225         if (ioctl(tty->fd, VT_SETMODE, &mode) < 0) {
226                 fprintf(stderr, "failed to take control of vt handling\n");
227                 goto err_kdmode;
228         }
229
230         tty->vt_source =
231                 wl_event_loop_add_signal(loop, SIGUSR1, vt_handler, tty);
232         if (!tty->vt_source)
233                 goto err_vtmode;
234
235         return tty;
236
237 err_vtmode:
238         ioctl(tty->fd, VT_SETMODE, &mode);
239
240 err_kdmode:
241         ioctl(tty->fd, KDSETMODE, KD_TEXT);
242
243 err_kdkbmode:
244         if (tty->input_source)
245                 wl_event_source_remove(tty->input_source);
246         ioctl(tty->fd, KDSKBMODE, tty->kb_mode);
247
248 err_attr:
249         tcsetattr(tty->fd, TCSANOW, &tty->terminal_attributes);
250
251 err:
252         close(tty->fd);
253         free(tty);
254         return NULL;
255 }
256
257 void
258 tty_destroy(struct tty *tty)
259 {
260         struct vt_mode mode = { 0 };
261
262         if (tty->input_source)
263                 wl_event_source_remove(tty->input_source);
264
265         if (ioctl(tty->fd, KDSKBMODE, tty->kb_mode))
266                 fprintf(stderr, "failed to restore keyboard mode: %m\n");
267
268         if (ioctl(tty->fd, KDSETMODE, KD_TEXT))
269                 fprintf(stderr,
270                         "failed to set KD_TEXT mode on tty: %m\n");
271
272         if (tcsetattr(tty->fd, TCSANOW, &tty->terminal_attributes) < 0)
273                 fprintf(stderr,
274                         "could not restore terminal to canonical mode\n");
275
276         mode.mode = VT_AUTO;
277         if (ioctl(tty->fd, VT_SETMODE, &mode) < 0)
278                 fprintf(stderr, "could not reset vt handling\n");
279
280         if (tty->has_vt && tty->vt != tty->starting_vt) {
281                 ioctl(tty->fd, VT_ACTIVATE, tty->starting_vt);
282                 ioctl(tty->fd, VT_WAITACTIVE, tty->starting_vt);
283         }
284
285         wl_event_source_remove(tty->vt_source);
286
287         close(tty->fd);
288
289         free(tty);
290 }