We allow users to specify grabs on the command-line or in config-options. We sometimes want multiple grabs to do the same. Therefore, this changes the grab-parser and handlers to allow this. Furthermore, this adds support for keysyms that are assembled of multiple syms. This isn't needed by any keymap, yet, but might be used in the future. Last but not least, you can also specify modifiers only now. This might be used by subsystems to use the default keysyms but let the user specify what modifiers should be used. However, be careful when specifying modifiers-only for normal grabs as this means the grab is executed everytime the modifier is reported with _any_ key. Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
803 lines
19 KiB
C
803 lines
19 KiB
C
/*
|
|
* 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 "log.h"
|
|
#include "pty.h"
|
|
#include "shl_misc.h"
|
|
#include "text.h"
|
|
#include "tsm_unicode.h"
|
|
#include "tsm_screen.h"
|
|
#include "tsm_vte.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 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 (!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 arguemnts
|
|
* 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, ©_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);
|
|
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);
|
|
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;
|
|
}
|