blob: 92ffcd924663ad9955be8b17c605a76c45de8e8c [file] [log] [blame]
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -07001/*
2 * Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7#include <ctype.h>
8#include <libtsm.h>
9#include <paths.h>
10#include <stdio.h>
Dominik Behr93899452014-08-18 22:16:21 -070011#include <sys/select.h>
David Sodmanbf3f2842014-11-12 08:26:58 -080012#include <sys/types.h>
13#include <sys/wait.h>
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070014
15#include "font.h"
16#include "input.h"
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070017#include "shl_pty.h"
18#include "term.h"
19#include "util.h"
20#include "video.h"
David Sodmanbbcb0522014-09-19 10:34:07 -070021#include "dbus_interface.h"
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070022
23struct term {
24 struct tsm_screen *screen;
25 struct tsm_vte *vte;
26 struct shl_pty *pty;
27 int pty_bridge;
28 int pid;
29 tsm_age_t age;
30 int char_x, char_y;
31 int pitch;
32 uint32_t *dst_image;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070033};
34
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070035static void __attribute__ ((noreturn)) term_run_child()
36{
37 char **argv = (char *[]) {
David Sodmanbf3f2842014-11-12 08:26:58 -080038 getenv("SHELL") ? : "/bin/bash",
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070039 "-il",
40 NULL
41 };
42
43 printf("Welcome to frecon!\n");
44 printf("running %s\n", argv[0]);
45 /* XXX figure out how to fix "top" for xterm-256color */
46 setenv("TERM", "xterm", 1);
47 execve(argv[0], argv, environ);
48 exit(1);
49}
50
51static int term_draw_cell(struct tsm_screen *screen, uint32_t id,
David Sodmanbbcb0522014-09-19 10:34:07 -070052 const uint32_t *ch, size_t len,
53 unsigned int cwidth, unsigned int posx,
54 unsigned int posy,
55 const struct tsm_screen_attr *attr,
56 tsm_age_t age, void *data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070057{
David Sodmanbbcb0522014-09-19 10:34:07 -070058 terminal_t *terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070059 uint32_t front_color, back_color;
60
David Sodmanbbcb0522014-09-19 10:34:07 -070061 if (age && terminal->term->age && age <= terminal->term->age)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070062 return 0;
63
64 front_color = (attr->fr << 16) | (attr->fg << 8) | attr->fb;
65 back_color = (attr->br << 16) | (attr->bg << 8) | attr->bb;
66
67 if (attr->inverse) {
68 uint32_t tmp = front_color;
69 front_color = back_color;
70 back_color = tmp;
71 }
72
73 if (len)
David Sodmanbbcb0522014-09-19 10:34:07 -070074 font_render(terminal->term->dst_image, posx, posy, terminal->term->pitch, *ch,
75 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070076 else
David Sodmanbbcb0522014-09-19 10:34:07 -070077 font_fillchar(terminal->term->dst_image, posx, posy, terminal->term->pitch,
78 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070079
80 return 0;
81}
82
David Sodmanbbcb0522014-09-19 10:34:07 -070083void term_redraw(terminal_t *terminal)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070084{
David Sodmanbbcb0522014-09-19 10:34:07 -070085 uint32_t *video_buffer;
86 video_buffer = video_lock(terminal->video);
87 if (video_buffer != NULL) {
88 terminal->term->dst_image = video_buffer;
89 terminal->term->age =
90 tsm_screen_draw(terminal->term->screen, term_draw_cell, terminal);
91 video_unlock(terminal->video);
92 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070093}
94
David Sodmanbbcb0522014-09-19 10:34:07 -070095void term_key_event(terminal_t* terminal, uint32_t keysym, int32_t unicode)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070096{
97
David Sodmanbbcb0522014-09-19 10:34:07 -070098 if (tsm_vte_handle_keyboard(terminal->term->vte, keysym, 0, 0, unicode))
99 tsm_screen_sb_reset(terminal->term->screen);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700100
David Sodmanbbcb0522014-09-19 10:34:07 -0700101 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700102}
103
104static void term_read_cb(struct shl_pty *pty, char *u8, size_t len, void *data)
105{
David Sodmanbbcb0522014-09-19 10:34:07 -0700106 terminal_t *terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700107
David Sodmanbbcb0522014-09-19 10:34:07 -0700108 tsm_vte_input(terminal->term->vte, u8, len);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700109
David Sodmanbbcb0522014-09-19 10:34:07 -0700110 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700111}
112
113static void term_write_cb(struct tsm_vte *vte, const char *u8, size_t len,
David Sodmanbbcb0522014-09-19 10:34:07 -0700114 void *data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700115{
116 struct term *term = data;
117 int r;
118
119 r = shl_pty_write(term->pty, u8, len);
120 if (r < 0)
David Sodmanbbcb0522014-09-19 10:34:07 -0700121 LOG(ERROR, "OOM in pty-write (%d)", r);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700122
123 shl_pty_dispatch(term->pty);
124}
125
126static const char *sev2str_table[] = {
127 "FATAL",
128 "ALERT",
129 "CRITICAL",
130 "ERROR",
131 "WARNING",
132 "NOTICE",
133 "INFO",
134 "DEBUG"
135};
136
137static const char *sev2str(unsigned int sev)
138{
139 if (sev > 7)
140 return "DEBUG";
141
142 return sev2str_table[sev];
143}
144
Dominik Behr00003502014-08-15 16:42:37 -0700145#ifdef __clang__
146__attribute__((__format__ (__printf__, 7, 0)))
147#endif
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700148static void log_tsm(void *data, const char *file, int line, const char *fn,
David Sodmanbbcb0522014-09-19 10:34:07 -0700149 const char *subs, unsigned int sev, const char *format,
150 va_list args)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700151{
152 fprintf(stderr, "%s: %s: ", sev2str(sev), subs);
153 vfprintf(stderr, format, args);
154 fprintf(stderr, "\n");
155}
156
David Sodmanbf3f2842014-11-12 08:26:58 -0800157terminal_t* term_init()
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700158{
159 const int scrollback_size = 200;
160 uint32_t char_width, char_height;
David Sodmanbbcb0522014-09-19 10:34:07 -0700161 int status;
162 terminal_t *new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700163
David Sodmanbbcb0522014-09-19 10:34:07 -0700164 new_terminal = (terminal_t*)calloc(1, sizeof(*new_terminal));
David Sodmanbf3f2842014-11-12 08:26:58 -0800165 if (!new_terminal)
166 return NULL;
David Sodmanbbcb0522014-09-19 10:34:07 -0700167
David Sodmanbf3f2842014-11-12 08:26:58 -0800168 new_terminal->video = video_init();
169 if (!new_terminal->video) {
170 term_close(new_terminal);
171 return NULL;
172 }
173
174 new_terminal->term = (struct term*)calloc(1, sizeof(*new_terminal->term));
175 if (!new_terminal->term) {
176 term_close(new_terminal);
177 return NULL;
178 }
179
180 font_init(video_getscaling(new_terminal->video));
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700181 font_get_size(&char_width, &char_height);
182
David Sodmanbf3f2842014-11-12 08:26:58 -0800183 new_terminal->term->char_x =
184 video_getwidth(new_terminal->video) / char_width;
185 new_terminal->term->char_y =
186 video_getheight(new_terminal->video) / char_height;
187 new_terminal->term->pitch = video_getpitch(new_terminal->video);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700188
David Sodmanbbcb0522014-09-19 10:34:07 -0700189 status = tsm_screen_new(&new_terminal->term->screen,
190 log_tsm, new_terminal->term);
191 if (new_terminal < 0) {
192 term_close(new_terminal);
193 return NULL;
194 }
195
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700196
David Sodmanbbcb0522014-09-19 10:34:07 -0700197 tsm_screen_set_max_sb(new_terminal->term->screen, scrollback_size);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700198
David Sodmanbbcb0522014-09-19 10:34:07 -0700199 status = tsm_vte_new(&new_terminal->term->vte, new_terminal->term->screen,
200 term_write_cb, new_terminal->term, log_tsm, new_terminal->term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700201
David Sodmanbbcb0522014-09-19 10:34:07 -0700202 if (status < 0) {
203 term_close(new_terminal);
204 return NULL;
205 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700206
David Sodmanbbcb0522014-09-19 10:34:07 -0700207 new_terminal->term->pty_bridge = shl_pty_bridge_new();
208 if (new_terminal->term->pty_bridge < 0) {
209 term_close(new_terminal);
210 return NULL;
211 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700212
David Sodmanbbcb0522014-09-19 10:34:07 -0700213 status = shl_pty_open(&new_terminal->term->pty,
214 term_read_cb, new_terminal, new_terminal->term->char_x,
215 new_terminal->term->char_y);
216 if (status < 0) {
217 term_close(new_terminal);
218 return NULL;
219 } else if (status == 0) {
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700220 term_run_child();
221 exit(1);
222 }
223
David Sodmanbbcb0522014-09-19 10:34:07 -0700224 status = shl_pty_bridge_add(new_terminal->term->pty_bridge, new_terminal->term->pty);
225 if (status) {
226 shl_pty_close(new_terminal->term->pty);
227 term_close(new_terminal);
228 return NULL;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700229 }
230
David Sodmanbbcb0522014-09-19 10:34:07 -0700231 new_terminal->term->pid = shl_pty_get_child(new_terminal->term->pty);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700232
David Sodmanbbcb0522014-09-19 10:34:07 -0700233 status = tsm_screen_resize(new_terminal->term->screen,
234 new_terminal->term->char_x, new_terminal->term->char_y);
235 if (status < 0) {
236 shl_pty_close(new_terminal->term->pty);
237 term_close(new_terminal);
238 return NULL;
239 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700240
David Sodmanbbcb0522014-09-19 10:34:07 -0700241 status = shl_pty_resize(new_terminal->term->pty, new_terminal->term->char_x, new_terminal->term->char_y);
242 if (status < 0) {
243 shl_pty_close(new_terminal->term->pty);
244 term_close(new_terminal);
245 return NULL;
246 }
David Sodmanbf3f2842014-11-12 08:26:58 -0800247 new_terminal->active = true;
248 video_setmode(new_terminal->video);
249 term_redraw(new_terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700250
David Sodmanbbcb0522014-09-19 10:34:07 -0700251 return new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700252}
253
David Sodmanbbcb0522014-09-19 10:34:07 -0700254void term_set_dbus(terminal_t *term, dbus_t* dbus)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700255{
David Sodmanbbcb0522014-09-19 10:34:07 -0700256 term->dbus = dbus;
257}
258
259void term_close(terminal_t *term)
260{
261 if (!term)
262 return;
263
David Sodmanbf3f2842014-11-12 08:26:58 -0800264 if (term->video) {
265 video_close(term->video);
266 term->video = NULL;
267 }
268
David Sodmanbbcb0522014-09-19 10:34:07 -0700269 if (term->term) {
270 free(term->term);
271 term->term = NULL;
272 }
273
274 free(term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700275}
David Sodmanbf3f2842014-11-12 08:26:58 -0800276
277bool term_is_child_done(terminal_t* terminal)
278{
279 int status;
280 int ret;
281 ret = waitpid(terminal->term->pid, &status, WNOHANG);
282
283 return ret != 0;
284}
285
286void term_page_up(terminal_t* terminal)
287{
288 tsm_screen_sb_page_up(terminal->term->screen, 1);
289 term_redraw(terminal);
290}
291
292void term_page_down(terminal_t* terminal)
293{
294 tsm_screen_sb_page_down(terminal->term->screen, 1);
295 term_redraw(terminal);
296}
297
298void term_line_up(terminal_t* terminal)
299{
300 tsm_screen_sb_up(terminal->term->screen, 1);
301 term_redraw(terminal);
302}
303
304void term_line_down(terminal_t* terminal)
305{
306 tsm_screen_sb_down(terminal->term->screen, 1);
307 term_redraw(terminal);
308}
309
310bool term_is_valid(terminal_t* terminal)
311{
312 return ((terminal != NULL) && (terminal->term != NULL));
313}
314
315int term_fd(terminal_t* terminal)
316{
317 if (term_is_valid(terminal))
318 return terminal->term->pty_bridge;
319 else
320 return -1;
321}
322
323void term_dispatch_io(terminal_t* terminal, fd_set* read_set)
324{
325 if (term_is_valid(terminal))
326 if (FD_ISSET(terminal->term->pty_bridge, read_set))
327 shl_pty_bridge_dispatch(terminal->term->pty_bridge, 0);
328}
329
330bool term_exception(terminal_t* terminal, fd_set* exception_set)
331{
332 if (term_is_valid(terminal)) {
333 if (terminal->term->pty_bridge >= 0) {
334 return FD_ISSET(terminal->term->pty_bridge,
335 exception_set);
336 }
337 }
338
339 return false;
340}
341
342bool term_is_active(terminal_t* terminal)
343{
344 if (term_is_valid(terminal))
345 return terminal->active;
346
347 return false;
348}
349
350void term_add_fd(terminal_t* terminal, fd_set* read_set, fd_set* exception_set)
351{
352 if (term_is_valid(terminal)) {
353 if (terminal->term->pty_bridge >= 0) {
354 FD_SET(terminal->term->pty_bridge, read_set);
355 FD_SET(terminal->term->pty_bridge, exception_set);
356 }
357 }
358}