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

Reply via email to