On Wed, Sep 19, 2012 at 01:47:25PM +0200, David Herrmann wrote: > TSM (Terminal-emulator State Machine) is a library developed together with > "kmscon" which implements a terminal-emulation layer. It has no external > depedencies and thus does not do any rendering. > It was solely developed to allow writing terminal emulators for different > platforms but still providing the same environment for applications > running in it. It is similar to libvte but without the huge GTK > dependency. > > TSM is developed in the kmscon repository (github.com/dvdhrm/kmscon) but I > created a single header file containing the whole library. As it is 6k > lines long, you need to get it here: https://gist.github.com/3749173 > Simply save the file as ./clients/tsm.h before applying this patch. You > can also install the TSM library from the kmscon repository and simply > adjust the include-line: > from: #include "tsm.h" > to: #include <tsm_vte.h>
Sounds very useful, will be interesting to see where this goes. I was hoping to find something like tsm.h when I originally did weston-terminal, so if this works out well, we could consider just making wlterm the wayland terminal. Kristian > The terminal emulator itself is pretty simple and slightly based on the > exisiting weston-terminal code. > > This code is just an example how TSM can be used to write a simple > terminal emulator. I don't want this to be applied to the repository. But > I will improve this code and resend a patch with an optional TSM > dependency so wlterm can be built from the weston-repository. > > The code base is still under heavy development so this is just some > preview and it will take a while until this is finished. > > Signed-off-by: David Herrmann <[email protected]> > --- > clients/.gitignore | 1 + > clients/Makefile.am | 4 + > clients/wlterm.c | 464 > ++++++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 469 insertions(+) > create mode 100644 clients/wlterm.c > > diff --git a/clients/.gitignore b/clients/.gitignore > index 6ed849d..29c8765 100644 > --- a/clients/.gitignore > +++ b/clients/.gitignore > @@ -32,5 +32,6 @@ weston-screensaver > weston-screenshooter > weston-tablet-shell > weston-terminal > +wlterm > workspaces-client-protocol.h > workspaces-protocol.c > diff --git a/clients/Makefile.am b/clients/Makefile.am > index 85fc95c..1b05fc9 100644 > --- a/clients/Makefile.am > +++ b/clients/Makefile.am > @@ -56,6 +56,7 @@ clients_programs = \ > clickdot \ > editor \ > keyboard \ > + wlterm \ > $(full_gl_client_programs) > > desktop_shell = weston-desktop-shell > @@ -122,6 +123,9 @@ keyboard_SOURCES = \ > input-method-client-protocol.h > keyboard_LDADD = $(toolkit_libs) > > +wlterm_SOURCES = wlterm.c > +wlterm_LDADD = $(toolkit_libs) -lutil > + > weston_info_SOURCES = \ > weston-info.c \ > ../shared/os-compatibility.c \ > diff --git a/clients/wlterm.c b/clients/wlterm.c > new file mode 100644 > index 0000000..7816eb0 > --- /dev/null > +++ b/clients/wlterm.c > @@ -0,0 +1,464 @@ > +/* > + * wlterm - Wayland Terminal > + * > + * Copyright (c) 2011-2012 David Herrmann <[email protected]> > + * > + * 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. > + */ > + > +/* > + * This is based on the "weston-terminal" code from: > + * > + * Copyright 2008 Kristian Hoegsberg > + * > + * Permission to use, copy, modify, distribute, and sell this software and > its > + * documentation for any purpose is hereby granted without fee, provided that > + * the above copyright notice appear in all copies and that both that > copyright > + * notice and this permission notice appear in supporting documentation, and > + * that the name of the copyright holders not be used in advertising or > + * publicity pertaining to distribution of the software without specific, > + * written prior permission. The copyright holders make no representations > + * about the suitability of this software for any purpose. It is provided > "as > + * is" without express or implied warranty. > + * > + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS > SOFTWARE, > + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO > + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR > + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF > USE, > + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER > + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR > PERFORMANCE > + * OF THIS SOFTWARE. > + */ > + > +#include <cairo.h> > +#include <errno.h> > +#include <fcntl.h> > +#include <pty.h> > +#include <stdbool.h> > +#include <stdint.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <sys/epoll.h> > +#include <unistd.h> > +#include <wayland-client.h> > +#include "../shared/config-parser.h" > +#include "window.h" > +#include "tsm.h" > + > +struct wlterm { > + struct display *disp; > + struct window *wnd; > + struct widget *wid; > + int margin; > + > + struct tsm_screen *scr; > + struct tsm_vte *vte; > + > + int master; > + struct task io_task; > + > + cairo_scaled_font_t *font; > + cairo_font_extents_t font_extents; > +}; > + > +static void wlterm_destroy(struct wlterm *term); > + > +static void key_handler(struct window *window, struct input *input, > + uint32_t time, uint32_t key, uint32_t sym, > + enum wl_keyboard_key_state state, void *data) > +{ > + struct wlterm *term = data; > + uint32_t modifiers, ucs4; > + unsigned int mods; > + > + if (state != WL_KEYBOARD_KEY_STATE_PRESSED) > + return; > + > + modifiers = input_get_modifiers(input); > + mods = 0; > + if (modifiers & MOD_CONTROL_MASK) > + mods |= TSM_CONTROL_MASK; > + if (modifiers & MOD_SHIFT_MASK) > + mods |= TSM_SHIFT_MASK; > + if (modifiers & MOD_ALT_MASK) > + mods |= TSM_MOD1_MASK; > + > + ucs4 = xkb_keysym_to_utf32(sym) ? : TSM_VTE_INVALID; > + > + if (tsm_vte_handle_keyboard(term->vte, sym, mods, ucs4)) > + window_schedule_redraw(term->wnd); > +} > + > +static void keyboard_focus_handler(struct window *window, > + struct input *device, void *data) > +{ > + struct wlterm *term = data; > + > + window_schedule_redraw(term->wnd); > +} > + > +struct wldraw { > + struct wlterm *term; > + cairo_t *cr; > + unsigned int x_adv; > + unsigned int y_adv; > + unsigned int x_off; > + unsigned int y_off; > + int ascent; > +}; > + > +static int draw_cell(struct tsm_screen *scr, > + uint32_t id, > + const uint32_t *ch, > + size_t ulen, > + unsigned int posx, > + unsigned int posy, > + const struct tsm_screen_attr *attr, > + void *data) > +{ > + struct wldraw *d = data; > + struct wlterm *term = d->term; > + unsigned int x, y; > + cairo_t *cr = d->cr; > + char buf[4]; > + size_t len; > + cairo_glyph_t glyphs[8], *g = glyphs; > + int num_glyphs = 8; > + > + x = d->x_adv * posx + d->x_off; > + y = d->y_adv * posy + d->y_off; > + cairo_move_to(cr, x, y); > + > + len = tsm_ucs4_to_utf8(*ch, buf); > + > + if (attr->inverse) { > + cairo_set_source_rgb(cr, > + attr->fr / 255.0, > + attr->fg / 255.0, > + attr->fb / 255.0); > + cairo_rectangle(cr, x, y, d->x_adv, d->y_adv); > + cairo_fill(cr); > + cairo_set_source_rgb(cr, > + attr->br / 255.0, > + attr->bg / 255.0, > + attr->bb / 255.0); > + } else { > + cairo_set_source_rgb(cr, > + attr->br / 255.0, > + attr->bg / 255.0, > + attr->bb / 255.0); > + cairo_rectangle(cr, x, y, d->x_adv, d->y_adv); > + cairo_fill(cr); > + cairo_set_source_rgb(cr, > + attr->fr / 255.0, > + attr->fg / 255.0, > + attr->fb / 255.0); > + } > + > + cairo_scaled_font_text_to_glyphs(term->font, x, y + d->ascent, > + buf, len, > + &g, &num_glyphs, > + NULL, NULL, NULL); > + cairo_show_glyphs(cr, glyphs, num_glyphs); > + > + return 0; > +} > + > +static void redraw_handler(struct widget *widget, void *data) > +{ > + struct wlterm *term = data; > + cairo_surface_t *surface; > + cairo_t *cr; > + struct rectangle alloc; > + int side_margin, top_margin; > + unsigned int cols, rows, xad, yad; > + struct wldraw d; > + > + /* get terminal data */ > + surface = window_get_surface(term->wnd); > + cr = cairo_create(surface); > + widget_get_allocation(term->wid, &alloc); > + xad = term->font_extents.max_x_advance; > + yad = term->font_extents.height; > + cols = tsm_screen_get_width(term->scr); > + rows = tsm_screen_get_height(term->scr); > + > + /* draw global background */ > + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); > + cairo_set_source_rgb(cr, 0, 0, 0); > + cairo_rectangle(cr, alloc.x, alloc.y, alloc.width, alloc.height); > + cairo_fill(cr); > + > + /* prepare brush */ > + cairo_set_scaled_font(cr, term->font); > + side_margin = (alloc.width - cols * xad) / 2; > + top_margin = (alloc.height - rows * yad) / 2; > + cairo_set_source_rgba(cr, 1, 1, 1, 1); > + cairo_set_line_width(cr, 1.0); > + > + /* paint glyph foreground */ > + memset(&d, 0, sizeof(d)); > + d.term = term; > + d.cr = cr; > + d.x_adv = xad; > + d.y_adv = yad; > + d.x_off = alloc.x + side_margin; > + d.y_off = alloc.y + top_margin; > + d.ascent = term->font_extents.ascent; > + tsm_screen_draw(term->scr, NULL, draw_cell, NULL, &d); > + > + /* render and cleanup */ > + cairo_destroy(cr); > + cairo_surface_destroy(surface); > +} > + > +static void wlterm_resize(struct wlterm *term, unsigned int cols, > + unsigned int rows) > +{ > + struct winsize ws; > + > + tsm_screen_resize(term->scr, cols, rows); > + > + memset(&ws, 0, sizeof(ws)); > + ws.ws_col = cols; > + ws.ws_row = rows; > + ioctl(term->master, TIOCSWINSZ, &ws); > +} > + > +static void resize_handler(struct widget *widget, > + int32_t width, int32_t height, void *data) > +{ > + struct wlterm *term = data; > + int32_t cols, rows, m; > + > + m = 2 * term->margin; > + cols = (width - m) / (int32_t) term->font_extents.max_x_advance; > + rows = (height - m) / (int32_t) term->font_extents.height; > + > + wlterm_resize(term, cols, rows); > +} > + > +static void vte_event(struct tsm_vte *vte, const char *u8, size_t len, > + void *data) > +{ > + struct wlterm *term = data; > + > + if (write(term->master, u8, len) < 0) { > + /* TODO: this is nonblocking so avoid abort() here! */ > + abort(); > + } > +} > + > +static void > +io_handler(struct task *task, uint32_t events) > +{ > + struct wlterm *term = container_of(task, struct wlterm, io_task); > + char buffer[256]; > + int len; > + > + if (events & EPOLLHUP) { > + fprintf(stderr, "pty fd HUP (%d): %m\n", errno); > + /* TODO: exit */ > + return; > + } > + > + len = read(term->master, buffer, sizeof(buffer)); > + if (len < 0) { > + fprintf(stderr, "pty fd error (%d): %m\n", errno); > + /* TODO: exit */ > + } else { > + tsm_vte_input(term->vte, buffer, len); > + window_schedule_redraw(term->wnd); > + } > +} > + > +static int pty_start(struct wlterm *term, const char *path) > +{ > + int master; > + pid_t pid; > + > + pid = forkpty(&master, NULL, NULL, NULL); > + if (pid == 0) { > + setenv("TERM", "xterm-256color", 1); > + setenv("COLORTERM", "xterm-256color", 1); > + execl(path, path, NULL); > + fprintf(stderr, "exec failed: %m\n"); > + exit(EXIT_FAILURE); > + } else if (pid < 0) { > + fprintf(stderr, "failed to fork and create pty (%d): %m\n", > + errno); > + return -errno; > + } > + > + term->master = master; > + fcntl(master, F_SETFL, O_NONBLOCK); > + term->io_task.run = io_handler; > + display_watch_fd(term->disp, term->master, > + EPOLLIN | EPOLLHUP, &term->io_task); > + wlterm_resize(term, 80, 24); > + > + return 0; > +} > + > +static void pty_close(struct wlterm *term) > +{ > + close(term->master); > +} > + > +static int wlterm_create(struct wlterm **out, struct display *disp) > +{ > + struct wlterm *term; > + int ret; > + cairo_surface_t *surface; > + cairo_t *cr; > + > + if (!out || !disp) > + return -EINVAL; > + > + term = malloc(sizeof(*term)); > + if (!term) > + return -ENOMEM; > + memset(term, 0, sizeof(*term)); > + term->disp = disp; > + term->margin = 5; > + > + term->wnd = window_create(term->disp); > + if (!term->wnd) { > + fprintf(stderr, "cannot create window (%d): %m\n", > + errno); > + ret = -EFAULT; > + goto err_free; > + } > + > + term->wid = frame_create(term->wnd, term); > + if (!term->wid) { > + fprintf(stderr, "cannot create widget (%d): %m\n", > + errno); > + ret = -EFAULT; > + goto err_wnd; > + } > + widget_set_transparent(term->wid, 0); > + > + ret = tsm_screen_new(&term->scr, NULL); > + if (ret) { > + fprintf(stderr, "cannot create TSM screen: %d\n", ret); > + goto err_wid; > + } > + > + ret = tsm_vte_new(&term->vte, term->scr, vte_event, term, NULL); > + if (ret) { > + fprintf(stderr, "cannot create TSM VTE machine: %d\n", ret); > + goto err_scr; > + } > + > + ret = pty_start(term, "/bin/bash"); > + if (ret) > + goto err_vte; > + > + /* get (toy) font */ > + surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0); > + cr = cairo_create(surface); > + cairo_set_font_size(cr, 14); > + cairo_select_font_face(cr, "Monospace", > + CAIRO_FONT_SLANT_NORMAL, > + CAIRO_FONT_WEIGHT_NORMAL); > + term->font = cairo_get_scaled_font(cr); > + cairo_scaled_font_reference(term->font); > + cairo_font_extents(cr, &term->font_extents); > + cairo_destroy(cr); > + cairo_surface_destroy(surface); > + > + /* set TSM options */ > + tsm_screen_set_max_sb(term->scr, 2000); > + > + /* set Window options */ > + window_set_user_data(term->wnd, term); > + window_set_title(term->wnd, "Wayland Terminal"); > + window_set_key_handler(term->wnd, key_handler); > + window_set_keyboard_focus_handler(term->wnd, > + keyboard_focus_handler); > + > + /* set widget options */ > + widget_set_redraw_handler(term->wid, redraw_handler); > + widget_set_resize_handler(term->wid, resize_handler); > + widget_schedule_resize(term->wid, 400, 400); > + > + *out = term; > + return 0; > + > +err_vte: > + tsm_vte_unref(term->vte); > +err_scr: > + tsm_screen_unref(term->scr); > +err_wid: > + widget_destroy(term->wid); > +err_wnd: > + window_destroy(term->wnd); > +err_free: > + free(term); > + return ret; > +} > + > +static void wlterm_destroy(struct wlterm *term) > +{ > + if (!term) > + return; > + > + cairo_scaled_font_destroy(term->font); > + pty_close(term); > + tsm_vte_unref(term->vte); > + tsm_screen_unref(term->scr); > + widget_destroy(term->wid); > + window_destroy(term->wnd); > + free(term); > +} > + > +int main(int argc, char **argv) > +{ > + struct display *display; > + struct wlterm *term; > + int ret; > + > + display = display_create(argc, argv); > + if (!display) { > + fprintf(stderr, "cannot create display (%d): %m\n", > + errno); > + ret = -EFAULT; > + goto err_out; > + } > + > + ret = wlterm_create(&term, display); > + if (ret) > + goto err_display; > + > + display_run(display); > + ret = 0; > + > + wlterm_destroy(term); > +err_display: > + display_destroy(display); > +err_out: > + ret = abs(ret); > + if (ret) > + fprintf(stderr, "application failed with (%d): %s\n", > + ret, strerror(ret)); > + return ret; > +} > -- > 1.7.12 > _______________________________________________ wayland-devel mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/wayland-devel
