summaryrefslogtreecommitdiff
path: root/src/wlt_terminal.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/wlt_terminal.c')
-rw-r--r--src/wlt_terminal.c808
1 files changed, 0 insertions, 808 deletions
diff --git a/src/wlt_terminal.c b/src/wlt_terminal.c
deleted file mode 100644
index f90f574..0000000
--- a/src/wlt_terminal.c
+++ /dev/null
@@ -1,808 +0,0 @@
-/*
- * wlt - Terminals
- *
- * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/*
- * Wayland Terminal console helpers
- */
-
-#include <errno.h>
-#include <linux/input.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <wayland-client.h>
-#include <xkbcommon/xkbcommon.h>
-#include "conf.h"
-#include "eloop.h"
-#include "font.h"
-#include "pty.h"
-#include "shl_log.h"
-#include "shl_misc.h"
-#include "tsm_unicode.h"
-#include "tsm_screen.h"
-#include "tsm_vte.h"
-#include "uterm_video.h"
-#include "wlt_main.h"
-#include "wlt_terminal.h"
-#include "wlt_toolkit.h"
-
-#define LOG_SUBSYSTEM "wlt_terminal"
-
-struct wlt_terminal {
- struct ev_eloop *eloop;
- struct wlt_window *wnd;
- struct wlt_display *disp;
- struct wlt_widget *widget;
- struct wlt_shm_buffer buffer;
- struct wlt_rect alloc;
-
- struct tsm_screen *scr;
- struct tsm_vte *vte;
- struct kmscon_pty *pty;
- struct ev_fd *pty_fd;
- bool pty_open;
-
- struct kmscon_font_attr font_attr;
- struct kmscon_font *font_normal;
- unsigned int cols;
- unsigned int rows;
-
- wlt_terminal_cb cb;
- void *data;
-
- int pointer_x;
- int pointer_y;
- bool in_selection;
- bool selection_started;
- int sel_start_x;
- int sel_start_y;
-
- int paste_fd;
- struct ev_fd *paste;
- struct wl_data_source *copy;
- char *copy_buf;
- int copy_len;
-};
-
-static int draw_cell(struct tsm_screen *scr,
- uint32_t id, const uint32_t *ch, size_t len,
- unsigned int chwidth,
- unsigned int posx, unsigned int posy,
- const struct tsm_screen_attr *attr, void *data)
-{
- struct wlt_terminal *term = data;
- const struct kmscon_glyph *glyph;
- int ret;
- unsigned int x, y, tmp, width, height, i, r, g, b;
- uint8_t *dst, *src;
- const struct uterm_video_buffer *buf;
- unsigned int fr, fg, fb, br, bg, bb;
- uint32_t val;
-
- if (!chwidth)
- return 0;
-
- if (!len)
- ret = kmscon_font_render_empty(term->font_normal, &glyph);
- else
- ret = kmscon_font_render(term->font_normal, id, ch, len,
- &glyph);
-
- if (ret) {
- ret = kmscon_font_render_inval(term->font_normal, &glyph);
- if (ret)
- return ret;
- }
-
- buf = &glyph->buf;
- x = posx * term->font_normal->attr.width;
- y = posy * term->font_normal->attr.height;
-
- if (attr->inverse) {
- fr = attr->br;
- fg = attr->bg;
- fb = attr->bb;
- br = attr->fr;
- bg = attr->fg;
- bb = attr->fb;
- } else {
- fr = attr->fr;
- fg = attr->fg;
- fb = attr->fb;
- br = attr->br;
- bg = attr->bg;
- bb = attr->bb;
- }
-
- tmp = x + buf->width;
- if (tmp < x || x >= term->buffer.width)
- return 0;
- if (tmp > term->buffer.width)
- width = term->buffer.width - x;
- else
- width = buf->width;
-
- tmp = y + buf->height;
- if (tmp < y || y >= term->buffer.height)
- return 0;
- if (tmp > term->buffer.height)
- height = term->buffer.height - y;
- else
- height = buf->height;
-
- dst = term->buffer.data;
- dst = &dst[y * term->buffer.stride + x * 4];
- src = buf->data;
-
- /* Division by 256 instead of 255 increases
- * speed by like 20% on slower machines.
- * Downside is, full white is 254/254/254
- * instead of 255/255/255. */
- while (height--) {
- for (i = 0; i < width; ++i) {
- if (src[i] == 0) {
- r = br;
- g = bg;
- b = bb;
- } else if (src[i] == 255) {
- r = fr;
- g = fg;
- b = fb;
- } else {
- r = fr * src[i] +
- br * (255 - src[i]);
- r /= 256;
- g = fg * src[i] +
- bg * (255 - src[i]);
- g /= 256;
- b = fb * src[i] +
- bb * (255 - src[i]);
- b /= 256;
- }
- val = (0xff << 24) | (r << 16) | (g << 8) | b;
- ((uint32_t*)dst)[i] = val;
- }
- dst += term->buffer.stride;
- src += buf->stride;
- }
-
- return 0;
-}
-
-static void draw_background(struct wlt_terminal *term)
-{
- uint8_t *dst;
- uint32_t *line;
- unsigned int i, j, w, h;
-
- /* when maximized, we might have a right and bottom border. So draw
- * a black background for everything beyond grid-size.
- * TODO: we should catch the color from tsm_screen instead of using
- * black background by default. */
- w = term->buffer.width;
- w /= term->font_normal->attr.width;
- w *= term->font_normal->attr.width;
-
- h = term->buffer.height;
- h /= term->font_normal->attr.height;
- h *= term->font_normal->attr.height;
-
- dst = term->buffer.data;
- for (i = 0; i < term->buffer.height; ++i) {
- line = (uint32_t*)dst;
- if (i >= h)
- j = 0;
- else
- j = w;
- for ( ; j < term->buffer.width; ++j)
- line[j] = 0xff << 24;
- dst += term->buffer.stride;
- }
-}
-
-static void widget_redraw(struct wlt_widget *widget, unsigned int flags,
- void *data)
-{
- struct wlt_terminal *term = data;
-
- draw_background(term);
- tsm_screen_draw(term->scr, NULL, draw_cell, NULL, term);
-}
-
-static void widget_resize(struct wlt_widget *widget, unsigned int flags,
- struct wlt_rect *alloc, void *data)
-{
- struct wlt_terminal *term = data;
- int ret;
-
- wlt_window_get_buffer(term->wnd, alloc, &term->buffer);
- memcpy(&term->alloc, alloc, sizeof(*alloc));
-
- /* don't allow children */
- alloc->width = 0;
- alloc->height = 0;
-
- term->cols = term->buffer.width / term->font_normal->attr.width;
- if (term->cols < 1)
- term->cols = 1;
- term->rows = term->buffer.height / term->font_normal->attr.height;
- if (term->rows < 1)
- term->rows = 1;
-
- ret = tsm_screen_resize(term->scr, term->cols, term->rows);
- if (ret)
- log_error("cannot resize TSM screen: %d", ret);
- kmscon_pty_resize(term->pty, term->cols, term->rows);
-}
-
-static void widget_prepare_resize(struct wlt_widget *widget,
- unsigned int flags,
- unsigned int width, unsigned int height,
- unsigned int *min_width,
- unsigned int *min_height,
- unsigned int *new_width,
- unsigned int *new_height,
- void *data)
-{
- struct wlt_terminal *term = data;
- unsigned int w, h;
-
- /* We are a catch-all handler. That is, we use all space that is
- * available. We must be called _last_, which is guaranteed by
- * registering the widget as last widget.
- * All previous handlers put their size constraints into the arguments
- * and we need to make sure to not break them.
- * Every redraw-handler is guaranteed to work for every size, but still,
- * we should try to avoid invalid-sizes to not generate artifacts. */
-
- if (flags & WLT_WINDOW_MAXIMIZED ||
- flags & WLT_WINDOW_FULLSCREEN) {
- /* if maximized, always use requested size */
- *new_width = width;
- *new_height = height;
- } else {
- /* In normal mode, we want the console to "snap" to grid-sizes.
- * That is, resizing is in steps instead of smooth. To guarantee
- * that, we use the font-width/height and try to make the
- * console as big as possible to fit the needed size.
- * However, we also must make sure the minimal size is always
- * guaranteed. */
-
- if (*new_width >= width) {
- *new_width += term->font_normal->attr.width;
- } else {
- w = width - *new_width;
- w /= term->font_normal->attr.width;
- if (!w)
- w = 1;
- w *= term->font_normal->attr.width;
- *new_width += w;
- }
-
- if (*new_width < *min_width) {
- w = *min_width - *new_width;
- w /= term->font_normal->attr.width;
- w += 1;
- w *= term->font_normal->attr.width;
- *new_width += w;
- }
-
- if (*new_height >= height) {
- *new_height += term->font_normal->attr.height;
- } else {
- h = height - *new_height;
- h /= term->font_normal->attr.height;
- if (!h)
- h = 1;
- h *= term->font_normal->attr.height;
- *new_height += h;
- }
-
- if (*new_height < *min_height) {
- h = *min_height - *new_height;
- h /= term->font_normal->attr.height;
- h += 1;
- h *= term->font_normal->attr.height;
- *new_height += h;
- }
- }
-}
-
-static void paste_event(struct ev_fd *fd, int mask, void *data)
-{
- struct wlt_terminal *term = data;
- char buf[4096];
- int ret;
-
- if (mask & EV_READABLE) {
- ret = read(term->paste_fd, buf, sizeof(buf));
- if (ret == 0) {
- goto err_close;
- } else if (ret < 0) {
- if (errno == EAGAIN)
- return;
- log_error("error on paste-fd (%d): %m", errno);
- goto err_close;
- }
-
- kmscon_pty_write(term->pty, buf, ret);
- return;
- }
-
- if (mask & EV_ERR) {
- log_error("error on paste FD");
- goto err_close;
- }
-
- if (mask & EV_HUP)
- goto err_close;
-
- return;
-
-err_close:
- close(term->paste_fd);
- ev_eloop_rm_fd(term->paste);
- term->paste = NULL;
-}
-
-static void copy_target(void *data, struct wl_data_source *w_source,
- const char *target)
-{
-}
-
-static void copy_send(void *data, struct wl_data_source *w_source,
- const char *mime, int32_t fd)
-{
- struct wlt_terminal *term = data;
- int ret;
-
- /* TODO: make this non-blocking */
- ret = write(fd, term->copy_buf, term->copy_len);
- if (ret != term->copy_len)
- log_warning("cannot write whole selection: %d/%d", ret,
- term->copy_len);
- close(fd);
-}
-
-static void copy_cancelled(void *data, struct wl_data_source *w_source)
-{
- struct wlt_terminal *term = data;
-
- wl_data_source_destroy(w_source);
- if (term->copy == w_source)
- term->copy = NULL;
-}
-
-static const struct wl_data_source_listener copy_listener = {
- .target = copy_target,
- .send = copy_send,
- .cancelled = copy_cancelled,
-};
-
-static bool widget_key(struct wlt_widget *widget, unsigned int mask,
- uint32_t sym, uint32_t ascii, uint32_t state,
- bool handled, void *data)
-{
- struct wlt_terminal *term = data;
- uint32_t ucs4;
- struct kmscon_font *font;
- int ret;
-
- if (handled || state != WL_KEYBOARD_KEY_STATE_PRESSED)
- return false;
-
- ucs4 = xkb_keysym_to_utf32(sym) ? : TSM_VTE_INVALID;
-
- if (conf_grab_matches(wlt_conf.grab_scroll_up,
- mask, 1, &sym)) {
- tsm_screen_sb_up(term->scr, 1);
- wlt_window_schedule_redraw(term->wnd);
- return true;
- }
- if (conf_grab_matches(wlt_conf.grab_scroll_down,
- mask, 1, &sym)) {
- tsm_screen_sb_down(term->scr, 1);
- wlt_window_schedule_redraw(term->wnd);
- return true;
- }
- if (conf_grab_matches(wlt_conf.grab_page_up,
- mask, 1, &sym)) {
- tsm_screen_sb_page_up(term->scr, 1);
- wlt_window_schedule_redraw(term->wnd);
- return true;
- }
- if (conf_grab_matches(wlt_conf.grab_page_down,
- mask, 1, &sym)) {
- tsm_screen_sb_page_down(term->scr, 1);
- wlt_window_schedule_redraw(term->wnd);
- return true;
- }
-
- if (conf_grab_matches(wlt_conf.grab_zoom_in,
- mask, 1, &sym)) {
- if (term->font_attr.points + 1 < term->font_attr.points)
- return true;
-
- ++term->font_attr.points;
- ret = kmscon_font_find(&font, &term->font_attr,
- wlt_conf.font_engine);
- if (ret) {
- --term->font_attr.points;
- log_error("cannot create font");
- } else {
- kmscon_font_unref(term->font_normal);
- term->font_normal = font;
- wlt_window_schedule_redraw(term->wnd);
- }
- return true;
- }
- if (conf_grab_matches(wlt_conf.grab_zoom_out,
- mask, 1, &sym)) {
- if (term->font_attr.points - 1 < 1)
- return true;
-
- --term->font_attr.points;
- ret = kmscon_font_find(&font, &term->font_attr,
- wlt_conf.font_engine);
- if (ret) {
- ++term->font_attr.points;
- log_error("cannot create font");
- } else {
- kmscon_font_unref(term->font_normal);
- term->font_normal = font;
- wlt_window_schedule_redraw(term->wnd);
- }
- return true;
- }
-
- if (conf_grab_matches(wlt_conf.grab_paste,
- mask, 1, &sym)) {
- if (term->paste) {
- log_debug("cannot paste selection, previous paste still in progress");
- return true;
- }
-
- ret = wlt_display_get_selection_fd(term->disp,
- "text/plain;charset=utf-8");
- if (ret == -ENOENT) {
- log_debug("no selection to paste");
- return true;
- } else if (ret == -EAGAIN) {
- log_debug("unknown mime-time for pasting selection");
- return true;
- } else if (ret < 0) {
- log_error("cannot paste selection: %d", ret);
- return true;
- }
-
- term->paste_fd = ret;
- ret = ev_eloop_new_fd(term->eloop, &term->paste, ret,
- EV_READABLE, paste_event, term);
- if (ret) {
- close(ret);
- log_error("cannot create eloop fd: %d", ret);
- return true;
- }
-
- return true;
- }
-
- if (conf_grab_matches(wlt_conf.grab_copy,
- mask, 1, &sym)) {
- if (term->copy) {
- wl_data_source_destroy(term->copy);
- free(term->copy_buf);
- term->copy = NULL;
- }
-
- ret = wlt_display_new_data_source(term->disp, &term->copy);
- if (ret) {
- log_error("cannot create data source");
- return true;
- }
-
- term->copy_len = tsm_screen_selection_copy(term->scr,
- &term->copy_buf);
- if (term->copy_len < 0) {
- if (term->copy_len != -ENOENT)
- log_error("cannot copy TSM selection: %d",
- term->copy_len);
- wl_data_source_destroy(term->copy);
- term->copy = NULL;
- return true;
- }
-
- wl_data_source_offer(term->copy, "text/plain;charset=utf-8");
- wl_data_source_add_listener(term->copy, &copy_listener, term);
- wlt_display_set_selection(term->disp, term->copy);
-
- return true;
- }
-
- if (tsm_vte_handle_keyboard(term->vte, sym, ascii, mask, ucs4)) {
- tsm_screen_sb_reset(term->scr);
- wlt_window_schedule_redraw(term->wnd);
- return true;
- }
-
- return false;
-}
-
-static void pointer_motion(struct wlt_widget *widget,
- unsigned int x, unsigned int y, void *data)
-{
- struct wlt_terminal *term = data;
- unsigned int posx, posy;
-
- if (!wlt_rect_contains(&term->alloc, x, y)) {
- term->pointer_x = -1;
- term->pointer_y = -1;
- return;
- } else if (term->pointer_x == x - term->alloc.x &&
- term->pointer_y == y - term->alloc.y) {
- return;
- } else {
- term->pointer_x = x - term->alloc.x;
- term->pointer_y = y - term->alloc.y;
- }
-
- if (term->in_selection) {
- if (!term->selection_started) {
- term->selection_started = true;
- posx = term->sel_start_x / term->font_normal->attr.width;
- posy = term->sel_start_y / term->font_normal->attr.height;
-
- tsm_screen_selection_start(term->scr, posx, posy);
- } else {
- posx = term->pointer_x / term->font_normal->attr.width;
- posy = term->pointer_y / term->font_normal->attr.height;
-
- tsm_screen_selection_target(term->scr, posx, posy);
- }
-
- wlt_window_schedule_redraw(term->wnd);
- }
-}
-
-static void pointer_enter(struct wlt_widget *widget,
- unsigned int x, unsigned int y, void *data)
-{
- struct wlt_terminal *term = data;
-
- pointer_motion(widget, x, y, term);
-}
-
-static void pointer_leave(struct wlt_widget *widget, void *data)
-{
- struct wlt_terminal *term = data;
-
- term->pointer_x = -1;
- term->pointer_y = -1;
-}
-
-static void pointer_button(struct wlt_widget *widget,
- uint32_t button, uint32_t state, void *data)
-{
- struct wlt_terminal *term = data;
-
- if (button != BTN_LEFT)
- return;
-
- if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
- if (!term->in_selection &&
- term->pointer_x >= 0 && term->pointer_y >= 0) {
- term->in_selection = true;
- term->selection_started = false;
-
- term->sel_start_x = term->pointer_x;
- term->sel_start_y = term->pointer_y;
- }
- } else {
- if (term->pointer_x == term->sel_start_x &&
- term->pointer_y == term->sel_start_y) {
- tsm_screen_selection_reset(term->scr);
- wlt_window_schedule_redraw(term->wnd);
- }
-
- term->in_selection = false;
- }
-}
-
-static void vte_event(struct tsm_vte *vte, const char *u8, size_t len,
- void *data)
-{
- struct wlt_terminal *term = data;
-
- kmscon_pty_write(term->pty, u8, len);
-}
-
-static void pty_input(struct kmscon_pty *pty, const char *u8, size_t len,
- void *data)
-{
- struct wlt_terminal *term = data;
-
- if (!len) {
- term->pty_open = false;
- if (term->cb)
- term->cb(term, WLT_TERMINAL_HUP, term->data);
- } else {
- tsm_vte_input(term->vte, u8, len);
- wlt_window_schedule_redraw(term->wnd);
- }
-}
-
-static void pty_event(struct ev_fd *fd, int mask, void *data)
-{
- struct wlt_terminal *term = data;
-
- kmscon_pty_dispatch(term->pty);
-}
-
-static void widget_destroy(struct wlt_widget *widget, void *data)
-{
- struct wlt_terminal *term = data;
-
- if (term->paste) {
- ev_eloop_rm_fd(term->paste);
- close(term->paste_fd);
- }
- tsm_vte_unref(term->vte);
- tsm_screen_unref(term->scr);
- free(term);
-}
-
-int wlt_terminal_new(struct wlt_terminal **out, struct wlt_window *wnd)
-{
- struct wlt_terminal *term;
- int ret;
-
- if (!out || !wnd)
- return -EINVAL;
-
- term = malloc(sizeof(*term));
- if (!term)
- return -ENOMEM;
- memset(term, 0, sizeof(*term));
- term->wnd = wnd;
- term->disp = wlt_window_get_display(wnd);
- term->eloop = wlt_window_get_eloop(wnd);
- term->cols = 80;
- term->rows = 24;
-
- term->font_attr.ppi = wlt_conf.font_ppi;
- term->font_attr.points = wlt_conf.font_size;
- term->font_attr.bold = false;
- term->font_attr.italic = false;
- term->font_attr.width = 0;
- term->font_attr.height = 0;
- strncpy(term->font_attr.name, wlt_conf.font_name,
- KMSCON_FONT_MAX_NAME - 1);
- term->font_attr.name[KMSCON_FONT_MAX_NAME - 1] = 0;
-
- ret = kmscon_font_find(&term->font_normal, &term->font_attr,
- wlt_conf.font_engine);
- if (ret) {
- log_error("cannot create font");
- goto err_free;
- }
-
- ret = tsm_screen_new(&term->scr, log_llog, NULL);
- if (ret) {
- log_error("cannot create tsm-screen object");
- goto err_font;
- }
- tsm_screen_set_max_sb(term->scr, wlt_conf.sb_size);
-
- ret = tsm_vte_new(&term->vte, term->scr, vte_event, term, log_llog,
- NULL);
- if (ret) {
- log_error("cannot create tsm-vte object");
- goto err_scr;
- }
- tsm_vte_set_palette(term->vte, wlt_conf.palette);
-
- ret = kmscon_pty_new(&term->pty, pty_input, term);
- if (ret) {
- log_error("cannot create pty object");
- goto err_vte;
- }
- kmscon_pty_set_term(term->pty, "xterm-256color");
-
- ret = kmscon_pty_set_term(term->pty, wlt_conf.term);
- if (ret)
- goto err_pty;
-
- ret = kmscon_pty_set_argv(term->pty, wlt_conf.argv);
- if (ret)
- goto err_pty;
-
- ret = ev_eloop_new_fd(term->eloop, &term->pty_fd,
- kmscon_pty_get_fd(term->pty),
- EV_READABLE, pty_event, term);
- if (ret)
- goto err_pty;
-
- ret = wlt_window_create_widget(wnd, &term->widget, term);
- if (ret) {
- log_error("cannot create terminal widget");
- goto err_pty_fd;
- }
-
- wlt_widget_set_destroy_cb(term->widget, widget_destroy);
- wlt_widget_set_redraw_cb(term->widget, widget_redraw);
- wlt_widget_set_resize_cb(term->widget, widget_prepare_resize,
- widget_resize);
- wlt_widget_set_keyboard_cb(term->widget, widget_key);
- wlt_widget_set_pointer_cb(term->widget, pointer_enter, pointer_leave,
- pointer_motion, pointer_button);
- *out = term;
- return 0;
-
-err_pty_fd:
- ev_eloop_rm_fd(term->pty_fd);
-err_pty:
- kmscon_pty_unref(term->pty);
-err_vte:
- tsm_vte_unref(term->vte);
-err_scr:
- tsm_screen_unref(term->scr);
-err_font:
- kmscon_font_unref(term->font_normal);
-err_free:
- free(term);
- return ret;
-}
-
-void wlt_terminal_destroy(struct wlt_terminal *term)
-{
- if (!term)
- return;
-
- wlt_widget_destroy(term->widget);
-}
-
-int wlt_terminal_open(struct wlt_terminal *term, wlt_terminal_cb cb,
- void *data)
-{
- int ret;
-
- if (!term)
- return -EINVAL;
-
- if (term->pty_open)
- return -EALREADY;
-
- term->cb = cb;
- term->data = data;
-
- kmscon_pty_close(term->pty);
- ret = kmscon_pty_open(term->pty, term->cols, term->rows);
- if (ret)
- return ret;
-
- term->pty_open = true;
- return 0;
-}