blob: 576232682065f5ff00596a50b29964a7c61537c0 [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>
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -07009#include <stdio.h>
Dominik Behr93899452014-08-18 22:16:21 -070010#include <sys/select.h>
David Sodmanbf3f2842014-11-12 08:26:58 -080011#include <sys/types.h>
12#include <sys/wait.h>
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070013
Dominik Behr580462b2014-11-20 13:50:05 -080014#include "dbus_interface.h"
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070015#include "font.h"
16#include "input.h"
Dominik Behr580462b2014-11-20 13:50:05 -080017#include "main.h"
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070018#include "shl_pty.h"
19#include "term.h"
20#include "util.h"
21#include "video.h"
22
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
Dominik Behr580462b2014-11-20 13:50:05 -080035static void __attribute__ ((noreturn)) term_run_child(unsigned int term_id)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070036{
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070037 printf("Welcome to frecon!\n");
Dominik Behr580462b2014-11-20 13:50:05 -080038 printf("running %s\n", command_flags.exec[term_id][0]);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070039 /* XXX figure out how to fix "top" for xterm-256color */
40 setenv("TERM", "xterm", 1);
Dominik Behr580462b2014-11-20 13:50:05 -080041 execve(command_flags.exec[term_id][0], command_flags.exec[term_id], environ);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070042 exit(1);
43}
44
45static int term_draw_cell(struct tsm_screen *screen, uint32_t id,
David Sodmanbbcb0522014-09-19 10:34:07 -070046 const uint32_t *ch, size_t len,
47 unsigned int cwidth, unsigned int posx,
48 unsigned int posy,
49 const struct tsm_screen_attr *attr,
50 tsm_age_t age, void *data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070051{
David Sodmanbbcb0522014-09-19 10:34:07 -070052 terminal_t *terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070053 uint32_t front_color, back_color;
54
David Sodmanbbcb0522014-09-19 10:34:07 -070055 if (age && terminal->term->age && age <= terminal->term->age)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070056 return 0;
57
58 front_color = (attr->fr << 16) | (attr->fg << 8) | attr->fb;
59 back_color = (attr->br << 16) | (attr->bg << 8) | attr->bb;
60
61 if (attr->inverse) {
62 uint32_t tmp = front_color;
63 front_color = back_color;
64 back_color = tmp;
65 }
66
67 if (len)
David Sodmanbbcb0522014-09-19 10:34:07 -070068 font_render(terminal->term->dst_image, posx, posy, terminal->term->pitch, *ch,
69 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070070 else
David Sodmanbbcb0522014-09-19 10:34:07 -070071 font_fillchar(terminal->term->dst_image, posx, posy, terminal->term->pitch,
72 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070073
74 return 0;
75}
76
David Sodmanbbcb0522014-09-19 10:34:07 -070077void term_redraw(terminal_t *terminal)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070078{
David Sodmanbbcb0522014-09-19 10:34:07 -070079 uint32_t *video_buffer;
80 video_buffer = video_lock(terminal->video);
81 if (video_buffer != NULL) {
82 terminal->term->dst_image = video_buffer;
83 terminal->term->age =
84 tsm_screen_draw(terminal->term->screen, term_draw_cell, terminal);
85 video_unlock(terminal->video);
86 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070087}
88
David Sodmanbbcb0522014-09-19 10:34:07 -070089void term_key_event(terminal_t* terminal, uint32_t keysym, int32_t unicode)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070090{
91
David Sodmanbbcb0522014-09-19 10:34:07 -070092 if (tsm_vte_handle_keyboard(terminal->term->vte, keysym, 0, 0, unicode))
93 tsm_screen_sb_reset(terminal->term->screen);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070094
David Sodmanbbcb0522014-09-19 10:34:07 -070095 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070096}
97
98static void term_read_cb(struct shl_pty *pty, char *u8, size_t len, void *data)
99{
David Sodmanbbcb0522014-09-19 10:34:07 -0700100 terminal_t *terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700101
David Sodmanbbcb0522014-09-19 10:34:07 -0700102 tsm_vte_input(terminal->term->vte, u8, len);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700103
David Sodmanbbcb0522014-09-19 10:34:07 -0700104 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700105}
106
107static void term_write_cb(struct tsm_vte *vte, const char *u8, size_t len,
David Sodmanbbcb0522014-09-19 10:34:07 -0700108 void *data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700109{
110 struct term *term = data;
111 int r;
112
113 r = shl_pty_write(term->pty, u8, len);
114 if (r < 0)
David Sodmanbbcb0522014-09-19 10:34:07 -0700115 LOG(ERROR, "OOM in pty-write (%d)", r);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700116
117 shl_pty_dispatch(term->pty);
118}
119
120static const char *sev2str_table[] = {
121 "FATAL",
122 "ALERT",
123 "CRITICAL",
124 "ERROR",
125 "WARNING",
126 "NOTICE",
127 "INFO",
128 "DEBUG"
129};
130
131static const char *sev2str(unsigned int sev)
132{
133 if (sev > 7)
134 return "DEBUG";
135
136 return sev2str_table[sev];
137}
138
Dominik Behr00003502014-08-15 16:42:37 -0700139#ifdef __clang__
140__attribute__((__format__ (__printf__, 7, 0)))
141#endif
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700142static void log_tsm(void *data, const char *file, int line, const char *fn,
David Sodmanbbcb0522014-09-19 10:34:07 -0700143 const char *subs, unsigned int sev, const char *format,
144 va_list args)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700145{
146 fprintf(stderr, "%s: %s: ", sev2str(sev), subs);
147 vfprintf(stderr, format, args);
148 fprintf(stderr, "\n");
149}
150
Dominik Behr580462b2014-11-20 13:50:05 -0800151terminal_t* term_init(unsigned int term_id)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700152{
153 const int scrollback_size = 200;
154 uint32_t char_width, char_height;
David Sodmanbbcb0522014-09-19 10:34:07 -0700155 int status;
156 terminal_t *new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700157
David Sodmanbbcb0522014-09-19 10:34:07 -0700158 new_terminal = (terminal_t*)calloc(1, sizeof(*new_terminal));
David Sodmanbf3f2842014-11-12 08:26:58 -0800159 if (!new_terminal)
160 return NULL;
David Sodmanbbcb0522014-09-19 10:34:07 -0700161
David Sodmanbf3f2842014-11-12 08:26:58 -0800162 new_terminal->video = video_init();
163 if (!new_terminal->video) {
164 term_close(new_terminal);
165 return NULL;
166 }
167
168 new_terminal->term = (struct term*)calloc(1, sizeof(*new_terminal->term));
169 if (!new_terminal->term) {
170 term_close(new_terminal);
171 return NULL;
172 }
173
174 font_init(video_getscaling(new_terminal->video));
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700175 font_get_size(&char_width, &char_height);
176
David Sodmanbf3f2842014-11-12 08:26:58 -0800177 new_terminal->term->char_x =
178 video_getwidth(new_terminal->video) / char_width;
179 new_terminal->term->char_y =
180 video_getheight(new_terminal->video) / char_height;
181 new_terminal->term->pitch = video_getpitch(new_terminal->video);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700182
David Sodmanbbcb0522014-09-19 10:34:07 -0700183 status = tsm_screen_new(&new_terminal->term->screen,
184 log_tsm, new_terminal->term);
185 if (new_terminal < 0) {
186 term_close(new_terminal);
187 return NULL;
188 }
189
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700190
David Sodmanbbcb0522014-09-19 10:34:07 -0700191 tsm_screen_set_max_sb(new_terminal->term->screen, scrollback_size);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700192
David Sodmanbbcb0522014-09-19 10:34:07 -0700193 status = tsm_vte_new(&new_terminal->term->vte, new_terminal->term->screen,
194 term_write_cb, new_terminal->term, log_tsm, new_terminal->term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700195
David Sodmanbbcb0522014-09-19 10:34:07 -0700196 if (status < 0) {
197 term_close(new_terminal);
198 return NULL;
199 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700200
David Sodmanbbcb0522014-09-19 10:34:07 -0700201 new_terminal->term->pty_bridge = shl_pty_bridge_new();
202 if (new_terminal->term->pty_bridge < 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 status = shl_pty_open(&new_terminal->term->pty,
208 term_read_cb, new_terminal, new_terminal->term->char_x,
209 new_terminal->term->char_y);
210 if (status < 0) {
211 term_close(new_terminal);
212 return NULL;
213 } else if (status == 0) {
Dominik Behr580462b2014-11-20 13:50:05 -0800214 term_run_child(term_id);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700215 exit(1);
216 }
217
David Sodmanbbcb0522014-09-19 10:34:07 -0700218 status = shl_pty_bridge_add(new_terminal->term->pty_bridge, new_terminal->term->pty);
219 if (status) {
220 shl_pty_close(new_terminal->term->pty);
221 term_close(new_terminal);
222 return NULL;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700223 }
224
David Sodmanbbcb0522014-09-19 10:34:07 -0700225 new_terminal->term->pid = shl_pty_get_child(new_terminal->term->pty);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700226
David Sodmanbbcb0522014-09-19 10:34:07 -0700227 status = tsm_screen_resize(new_terminal->term->screen,
228 new_terminal->term->char_x, new_terminal->term->char_y);
229 if (status < 0) {
230 shl_pty_close(new_terminal->term->pty);
231 term_close(new_terminal);
232 return NULL;
233 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700234
David Sodmanbbcb0522014-09-19 10:34:07 -0700235 status = shl_pty_resize(new_terminal->term->pty, new_terminal->term->char_x, new_terminal->term->char_y);
236 if (status < 0) {
237 shl_pty_close(new_terminal->term->pty);
238 term_close(new_terminal);
239 return NULL;
240 }
David Sodmanbf3f2842014-11-12 08:26:58 -0800241 new_terminal->active = true;
242 video_setmode(new_terminal->video);
243 term_redraw(new_terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700244
David Sodmanbbcb0522014-09-19 10:34:07 -0700245 return new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700246}
247
David Sodmanbbcb0522014-09-19 10:34:07 -0700248void term_set_dbus(terminal_t *term, dbus_t* dbus)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700249{
David Sodmanbbcb0522014-09-19 10:34:07 -0700250 term->dbus = dbus;
251}
252
253void term_close(terminal_t *term)
254{
255 if (!term)
256 return;
257
David Sodmanbf3f2842014-11-12 08:26:58 -0800258 if (term->video) {
259 video_close(term->video);
260 term->video = NULL;
261 }
262
David Sodmanbbcb0522014-09-19 10:34:07 -0700263 if (term->term) {
264 free(term->term);
265 term->term = NULL;
266 }
267
268 free(term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700269}
David Sodmanbf3f2842014-11-12 08:26:58 -0800270
271bool term_is_child_done(terminal_t* terminal)
272{
273 int status;
274 int ret;
275 ret = waitpid(terminal->term->pid, &status, WNOHANG);
276
277 return ret != 0;
278}
279
280void term_page_up(terminal_t* terminal)
281{
282 tsm_screen_sb_page_up(terminal->term->screen, 1);
283 term_redraw(terminal);
284}
285
286void term_page_down(terminal_t* terminal)
287{
288 tsm_screen_sb_page_down(terminal->term->screen, 1);
289 term_redraw(terminal);
290}
291
292void term_line_up(terminal_t* terminal)
293{
294 tsm_screen_sb_up(terminal->term->screen, 1);
295 term_redraw(terminal);
296}
297
298void term_line_down(terminal_t* terminal)
299{
300 tsm_screen_sb_down(terminal->term->screen, 1);
301 term_redraw(terminal);
302}
303
304bool term_is_valid(terminal_t* terminal)
305{
306 return ((terminal != NULL) && (terminal->term != NULL));
307}
308
309int term_fd(terminal_t* terminal)
310{
311 if (term_is_valid(terminal))
312 return terminal->term->pty_bridge;
313 else
314 return -1;
315}
316
317void term_dispatch_io(terminal_t* terminal, fd_set* read_set)
318{
319 if (term_is_valid(terminal))
320 if (FD_ISSET(terminal->term->pty_bridge, read_set))
321 shl_pty_bridge_dispatch(terminal->term->pty_bridge, 0);
322}
323
324bool term_exception(terminal_t* terminal, fd_set* exception_set)
325{
326 if (term_is_valid(terminal)) {
327 if (terminal->term->pty_bridge >= 0) {
328 return FD_ISSET(terminal->term->pty_bridge,
329 exception_set);
330 }
331 }
332
333 return false;
334}
335
336bool term_is_active(terminal_t* terminal)
337{
338 if (term_is_valid(terminal))
339 return terminal->active;
340
341 return false;
342}
343
344void term_add_fd(terminal_t* terminal, fd_set* read_set, fd_set* exception_set)
345{
346 if (term_is_valid(terminal)) {
347 if (terminal->term->pty_bridge >= 0) {
348 FD_SET(terminal->term->pty_bridge, read_set);
349 FD_SET(terminal->term->pty_bridge, exception_set);
350 }
351 }
352}