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/>.
30 static int terminal_write_fn(term_screen *screen, void *userdata, const void *buf, size_t size) {
31 Terminal *t = userdata;
35 r = pty_write(t->pty, buf, size);
43 static int terminal_pty_fn(Pty *pty, void *userdata, unsigned int event, const void *ptr, size_t size) {
44 Terminal *t = userdata;
49 log_debug("PTY child exited");
50 t->pty = pty_unref(t->pty);
53 r = term_screen_feed_text(t->screen, ptr, size);
55 log_error_errno(r, "Cannot update screen state: %m");
57 workspace_dirty(t->workspace);
64 int terminal_new(Terminal **out, Workspace *w) {
65 _cleanup_(terminal_freep) Terminal *t = NULL;
70 t = new0(Terminal, 1);
75 LIST_PREPEND(terminals_by_workspace, w->terminal_list, t);
77 r = term_parser_new(&t->parser, true);
81 r = term_screen_new(&t->screen, terminal_write_fn, t, NULL, NULL);
85 r = term_screen_set_answerback(t->screen, "systemd-console");
95 Terminal *terminal_free(Terminal *t) {
102 (void)pty_signal(t->pty, SIGHUP);
106 term_screen_unref(t->screen);
107 term_parser_free(t->parser);
108 LIST_REMOVE(terminals_by_workspace, t->workspace->terminal_list, t);
114 void terminal_resize(Terminal *t) {
115 uint32_t width, height, fw, fh;
120 width = t->workspace->width;
121 height = t->workspace->height;
122 fw = unifont_get_width(t->workspace->manager->uf);
123 fh = unifont_get_height(t->workspace->manager->uf);
125 width = (fw > 0) ? width / fw : 0;
126 height = (fh > 0) ? height / fh : 0;
129 r = pty_resize(t->pty, width, height);
131 log_error_errno(r, "Cannot resize pty: %m");
134 r = term_screen_resize(t->screen, width, height);
136 log_error_errno(r, "Cannot resize screen: %m");
139 void terminal_run(Terminal *t) {
147 pid = pty_fork(&t->pty,
148 t->workspace->manager->event,
151 term_screen_get_width(t->screen),
152 term_screen_get_height(t->screen));
154 log_error_errno(pid, "Cannot fork PTY: %m");
156 } else if (pid == 0) {
159 char **argv = (char*[]){
160 (char*)getenv("SHELL") ? : (char*)_PATH_BSHELL,
164 setenv("TERM", "xterm-256color", 1);
165 setenv("COLORTERM", "systemd-console", 1);
167 execve(argv[0], argv, environ);
168 log_error_errno(errno, "Cannot exec %s (%d): %m", argv[0], -errno);
173 static void terminal_feed_keyboard(Terminal *t, idev_data *data) {
174 idev_data_keyboard *kdata = &data->keyboard;
177 if (!data->resync && (kdata->value == 1 || kdata->value == 2)) {
178 assert_cc(TERM_KBDMOD_CNT == (int)IDEV_KBDMOD_CNT);
179 assert_cc(TERM_KBDMOD_IDX_SHIFT == (int)IDEV_KBDMOD_IDX_SHIFT &&
180 TERM_KBDMOD_IDX_CTRL == (int)IDEV_KBDMOD_IDX_CTRL &&
181 TERM_KBDMOD_IDX_ALT == (int)IDEV_KBDMOD_IDX_ALT &&
182 TERM_KBDMOD_IDX_LINUX == (int)IDEV_KBDMOD_IDX_LINUX &&
183 TERM_KBDMOD_IDX_CAPS == (int)IDEV_KBDMOD_IDX_CAPS);
185 r = term_screen_feed_keyboard(t->screen,
192 log_error_errno(r, "Cannot feed keyboard data to screen: %m");
196 void terminal_feed(Terminal *t, idev_data *data) {
197 switch (data->type) {
198 case IDEV_DATA_KEYBOARD:
199 terminal_feed_keyboard(t, data);
204 static void terminal_fill(uint8_t *dst,
211 for (j = 0; j < height; ++j) {
214 for (i = 0; i < width; ++i)
221 static void terminal_blend(uint8_t *dst,
231 for (j = 0; j < height; ++j) {
234 for (i = 0; i < width; ++i) {
235 if (!src || src[i / 8] & (1 << (7 - i % 8)))
249 const grdev_display_target *target;
252 uint32_t cell_height;
254 } TerminalDrawContext;
256 static int terminal_draw_cell(term_screen *screen,
260 const term_attr *attr,
263 unsigned int ch_width) {
264 TerminalDrawContext *ctx = userdata;
265 const grdev_display_target *target = ctx->target;
266 grdev_fb *fb = target->back;
267 uint32_t xpos, ypos, width, height;
274 r = unifont_lookup(ctx->uf, &g, *ch);
276 r = unifont_lookup(ctx->uf, &g, 0xfffd);
278 unifont_fallback(&g);
281 xpos = x * ctx->cell_width;
282 ypos = y * ctx->cell_height;
284 if (xpos >= fb->width || ypos >= fb->height)
287 width = MIN(fb->width - xpos, ctx->cell_width * ch_width);
288 height = MIN(fb->height - ypos, ctx->cell_height);
290 term_attr_to_argb32(attr, &fg, &bg, NULL);
295 dst += fb->strides[0] * ypos + sizeof(uint32_t) * xpos;
305 terminal_fill(dst + sizeof(uint32_t) * g.width,
310 if (height > g.height)
311 terminal_fill(dst + fb->strides[0] * g.height,
330 bool terminal_draw(Terminal *t, const grdev_display_target *target) {
331 TerminalDrawContext ctx = { };
337 /* start up terminal on first frame */
341 ctx.uf = t->workspace->manager->uf;
342 ctx.cell_width = unifont_get_width(ctx.uf);
343 ctx.cell_height = unifont_get_height(ctx.uf);
347 /* if the frontbuffer is new enough, no reason to redraw */
348 age = term_screen_get_age(t->screen);
349 if (age != 0 && age <= target->front->data.u64)
352 /* force flip if no frontbuffer is set, yet */
356 term_screen_draw(t->screen, terminal_draw_cell, &ctx, &target->back->data.u64);