1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
29 static int terminal_write_fn(term_screen *screen, void *userdata, const void *buf, size_t size) {
30 Terminal *t = userdata;
34 r = pty_write(t->pty, buf, size);
42 static int terminal_pty_fn(Pty *pty, void *userdata, unsigned int event, const void *ptr, size_t size) {
43 Terminal *t = userdata;
48 log_debug("PTY child exited");
49 t->pty = pty_unref(t->pty);
52 r = term_screen_feed_text(t->screen, ptr, size);
54 log_error_errno(r, "Cannot update screen state: %m");
56 workspace_dirty(t->workspace);
63 int terminal_new(Terminal **out, Workspace *w) {
64 _cleanup_(terminal_freep) Terminal *t = NULL;
69 t = new0(Terminal, 1);
74 LIST_PREPEND(terminals_by_workspace, w->terminal_list, t);
76 r = term_parser_new(&t->parser, true);
80 r = term_screen_new(&t->screen, terminal_write_fn, t, NULL, NULL);
84 r = term_screen_set_answerback(t->screen, "systemd-console");
94 Terminal *terminal_free(Terminal *t) {
101 (void) pty_signal(t->pty, SIGHUP);
105 term_screen_unref(t->screen);
106 term_parser_free(t->parser);
107 LIST_REMOVE(terminals_by_workspace, t->workspace->terminal_list, t);
113 void terminal_resize(Terminal *t) {
114 uint32_t width, height, fw, fh;
119 width = t->workspace->width;
120 height = t->workspace->height;
121 fw = unifont_get_width(t->workspace->manager->uf);
122 fh = unifont_get_height(t->workspace->manager->uf);
124 width = (fw > 0) ? width / fw : 0;
125 height = (fh > 0) ? height / fh : 0;
128 r = pty_resize(t->pty, width, height);
130 log_error_errno(r, "Cannot resize pty: %m");
133 r = term_screen_resize(t->screen, width, height);
135 log_error_errno(r, "Cannot resize screen: %m");
138 void terminal_run(Terminal *t) {
146 pid = pty_fork(&t->pty,
147 t->workspace->manager->event,
150 term_screen_get_width(t->screen),
151 term_screen_get_height(t->screen));
153 log_error_errno(pid, "Cannot fork PTY: %m");
155 } else if (pid == 0) {
158 char **argv = (char*[]){
159 (char*)getenv("SHELL") ? : (char*)_PATH_BSHELL,
163 setenv("TERM", "xterm-256color", 1);
164 setenv("COLORTERM", "systemd-console", 1);
166 execve(argv[0], argv, environ);
167 log_error_errno(errno, "Cannot exec %s (%d): %m", argv[0], -errno);
172 static void terminal_feed_keyboard(Terminal *t, idev_data *data) {
173 idev_data_keyboard *kdata = &data->keyboard;
176 if (!data->resync && (kdata->value == 1 || kdata->value == 2)) {
177 assert_cc(TERM_KBDMOD_CNT == (int)IDEV_KBDMOD_CNT);
178 assert_cc(TERM_KBDMOD_IDX_SHIFT == (int)IDEV_KBDMOD_IDX_SHIFT &&
179 TERM_KBDMOD_IDX_CTRL == (int)IDEV_KBDMOD_IDX_CTRL &&
180 TERM_KBDMOD_IDX_ALT == (int)IDEV_KBDMOD_IDX_ALT &&
181 TERM_KBDMOD_IDX_LINUX == (int)IDEV_KBDMOD_IDX_LINUX &&
182 TERM_KBDMOD_IDX_CAPS == (int)IDEV_KBDMOD_IDX_CAPS);
184 r = term_screen_feed_keyboard(t->screen,
191 log_error_errno(r, "Cannot feed keyboard data to screen: %m");
195 void terminal_feed(Terminal *t, idev_data *data) {
196 switch (data->type) {
197 case IDEV_DATA_KEYBOARD:
198 terminal_feed_keyboard(t, data);
203 static void terminal_fill(uint8_t *dst,
210 for (j = 0; j < height; ++j) {
213 for (i = 0; i < width; ++i)
220 static void terminal_blend(uint8_t *dst,
230 for (j = 0; j < height; ++j) {
233 for (i = 0; i < width; ++i) {
234 if (!src || src[i / 8] & (1 << (7 - i % 8)))
248 const grdev_display_target *target;
251 uint32_t cell_height;
253 } TerminalDrawContext;
255 static int terminal_draw_cell(term_screen *screen,
259 const term_attr *attr,
262 unsigned int ch_width) {
263 TerminalDrawContext *ctx = userdata;
264 const grdev_display_target *target = ctx->target;
265 grdev_fb *fb = target->back;
266 uint32_t xpos, ypos, width, height;
273 r = unifont_lookup(ctx->uf, &g, *ch);
275 r = unifont_lookup(ctx->uf, &g, 0xfffd);
277 unifont_fallback(&g);
280 xpos = x * ctx->cell_width;
281 ypos = y * ctx->cell_height;
283 if (xpos >= fb->width || ypos >= fb->height)
286 width = MIN(fb->width - xpos, ctx->cell_width * ch_width);
287 height = MIN(fb->height - ypos, ctx->cell_height);
289 term_attr_to_argb32(attr, &fg, &bg, NULL);
294 dst += fb->strides[0] * ypos + sizeof(uint32_t) * xpos;
304 terminal_fill(dst + sizeof(uint32_t) * g.width,
309 if (height > g.height)
310 terminal_fill(dst + fb->strides[0] * g.height,
329 bool terminal_draw(Terminal *t, const grdev_display_target *target) {
330 TerminalDrawContext ctx = { };
336 /* start up terminal on first frame */
340 ctx.uf = t->workspace->manager->uf;
341 ctx.cell_width = unifont_get_width(ctx.uf);
342 ctx.cell_height = unifont_get_height(ctx.uf);
346 /* if the frontbuffer is new enough, no reason to redraw */
347 age = term_screen_get_age(t->screen);
348 if (age != 0 && age <= target->front->data.u64)
351 /* force flip if no frontbuffer is set, yet */
355 term_screen_draw(t->screen, terminal_draw_cell, &ctx, &target->back->data.u64);