diff --git a/src/document/document.cpp b/src/document/document.cpp index eae2e82f..a9e894ed 100644 --- a/src/document/document.cpp +++ b/src/document/document.cpp @@ -163,6 +163,10 @@ init_document(struct cache_entry *cached, struct document_options *options) init_list(document->timeouts); #endif +#ifdef CONFIG_LIBSIXEL + init_list(document->images); +#endif + #ifdef CONFIG_COMBINE document->comb_x = -1; document->comb_y = -1; diff --git a/src/document/document.h b/src/document/document.h index d9d75b17..9de0b73f 100644 --- a/src/document/document.h +++ b/src/document/document.h @@ -21,6 +21,7 @@ struct ecmascript_timeout; struct el_form_control; struct frame_desc; struct frameset_desc; +struct image; struct module; struct screen_char; @@ -305,6 +306,9 @@ struct document { struct el_box clipboard_box; enum clipboard_status clipboard_status; +#ifdef CONFIG_LIBSIXEL + LIST_OF(struct image) images; +#endif }; #define document_has_frames(document_) ((document_) && (document_)->frame_desc) diff --git a/src/document/plain/renderer.c b/src/document/plain/renderer.c index 2afd8d2d..8dc9776f 100644 --- a/src/document/plain/renderer.c +++ b/src/document/plain/renderer.c @@ -5,6 +5,7 @@ #endif #include +#include #include #include @@ -25,6 +26,7 @@ #include "protocol/uri.h" #include "terminal/color.h" #include "terminal/draw.h" +#include "terminal/sixel.h" #include "util/color.h" #include "util/error.h" #include "util/memory.h" @@ -560,7 +562,7 @@ add_document_line(struct plain_renderer *renderer, prev_char = line_pos > 0 ? line[line_pos - 1] : '\0'; next_char = (line_pos + charlen < width) ? - line[line_pos + charlen] : '\0'; + line[line_pos + charlen] : '\0'; /* Do not expand tabs that precede back-spaces; this saves the * back-space code some trouble. */ @@ -639,10 +641,45 @@ add_document_line(struct plain_renderer *renderer, if (template_->attr) template_->attr |= pos->attr; } else if (line_char == 27) { - decode_esc_color(line, &line_pos, width, +#ifdef CONFIG_LIBSIXEL + if (line_pos + 1 < width && line[line_pos + 1] == 'P' && line_pos + 2 < width && line[line_pos + 2] == 'q') { + while (1) { + char *end = (char *)memchr(line + line_pos + 1, 27, width - line_pos - 1); + + if (end == NULL) { + break; + } + if (end[1] == '\\') { + struct string pixels; + + if (!init_string(&pixels)) { + break; + } + add_bytes_to_string(&pixels, line + line_pos, end + 2 - line - line_pos); + int ile = add_image_to_document(document, &pixels, lineno) + 1; + + realloc_line(document, pos - startpos, lineno); + + for (int i = 0; i < ile; i++) { + realloc_line(document, 0, lineno + i); + } + renderer->lineno += ile; + lineno += ile; + line_pos = end + 2 - line; + startpos = pos = realloc_line(document, width, lineno); + goto zero; + } else { + line_pos = end - line; + } + } + } else +#endif + { + decode_esc_color(line, &line_pos, width, &saved_renderer_template, doc_opts->color_mode, &was_reversed); - *template_ = saved_renderer_template; + *template_ = saved_renderer_template; + } } else { int added_chars = 0; @@ -700,6 +737,7 @@ add_document_line(struct plain_renderer *renderer, next: line_pos += charlen; cells += cell; +zero: } mem_free(line); @@ -749,11 +787,11 @@ add_document_lines(struct plain_renderer *renderer) int last_space = 0; int tab_spaces = 0; int step = 0; - int cells = 0; + int cells = 0; /* End of line detection: We handle \r, \r\n and \n types. */ - for (width = 0; (width < length) && - (cells < renderer->max_width);) { + for (width = 0; (width < length) && + (cells < renderer->max_width);) { if (source[width] == ASCII_CR) step++; if (source[width + step] == ASCII_LF) diff --git a/src/terminal/meson.build b/src/terminal/meson.build index f4dab617..40b4faa9 100644 --- a/src/terminal/meson.build +++ b/src/terminal/meson.build @@ -1,6 +1,9 @@ if conf_data.get('CONFIG_MOUSE') srcs += files('mouse.c') endif +if conf_data.get('CONFIG_LIBSIXEL') + srcs += files('sixel.c') +endif if conf_data.get('CONFIG_TERMINFO') srcs += files('terminfo.c') endif diff --git a/src/terminal/screen.c b/src/terminal/screen.c index bc9ca45a..5383d134 100644 --- a/src/terminal/screen.c +++ b/src/terminal/screen.c @@ -20,6 +20,7 @@ #include "terminal/hardio.h" #include "terminal/kbd.h" #include "terminal/screen.h" +#include "terminal/sixel.h" #include "terminal/terminal.h" #ifdef CONFIG_TERMINFO #include "terminal/terminfo.h" @@ -675,7 +676,7 @@ done_screen_drivers(struct module *xxx) /** Adds the term code for positioning the cursor at @a x and @a y to * @a string. The template term code is: "\033[;H" */ -static inline struct string * +struct string * add_cursor_move_to_string(struct string *screen, int y, int x) { #ifdef CONFIG_TERMINFO @@ -1372,6 +1373,8 @@ add_char_true(struct string *screen, struct screen_driver *driver, } \ } +#include + /*! Updating of the terminal screen is done by checking what needs to * be updated using the last screen. */ void @@ -1453,6 +1456,9 @@ redraw_screen(struct terminal *term) copy_screen_chars(screen->last_image, screen->image, term->width * term->height); screen->was_dirty = 0; +#ifdef CONFIG_LIBSIXEL + try_to_draw_images(term); +#endif } void diff --git a/src/terminal/screen.h b/src/terminal/screen.h index f353b806..5894e3cf 100644 --- a/src/terminal/screen.h +++ b/src/terminal/screen.h @@ -8,6 +8,7 @@ extern "C" { struct bitfield; struct module; struct screen_char; +struct string; struct terminal; /** The terminal's screen manages */ @@ -50,6 +51,8 @@ void erase_screen(struct terminal *term); /** Meeep! */ void beep_terminal(struct terminal *term); +struct string *add_cursor_move_to_string(struct string *screen, int y, int x); + extern struct module terminal_screen_module; #ifdef __cplusplus diff --git a/src/terminal/sixel.c b/src/terminal/sixel.c new file mode 100644 index 00000000..6e78640f --- /dev/null +++ b/src/terminal/sixel.c @@ -0,0 +1,110 @@ +/** Terminal sixel routines. + * @file */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include "elinks.h" + +#include "document/document.h" +#include "osdep/osdep.h" +#include "terminal/hardio.h" +#include "terminal/screen.h" +#include "terminal/sixel.h" +#include "terminal/terminal.h" + +/* encode settings object */ +struct sixel_decoder { + unsigned int ref; + char *input; + char *output; + sixel_allocator_t *allocator; +}; + +void +try_to_draw_images(struct terminal *term) +{ + struct image *im; + + foreach (im, term->images) { + struct string text; + + if (!init_string(&text)) { + return; + } + add_cursor_move_to_string(&text, im->y + 1, im->x + 1); + add_string_to_string(&text, &im->sixel); + + if (text.length) { + if (term->master) want_draw(); + hard_write(term->fdout, text.source, text.length); + if (term->master) done_draw(); + } + done_string(&text); + } +} + +void +delete_image(struct image *im) +{ + del_from_list(im); + done_string(&im->sixel); + mem_free(im); +} + +int +add_image_to_document(struct document *doc, struct string *pixels, int lineno) +{ + struct image *im = mem_calloc(1, sizeof(*im)); + + if (!im) { + return 0; + } + sixel_decoder_t *decoder = NULL; + SIXELSTATUS status = sixel_decoder_new(&decoder, NULL); + + if (status != SIXEL_OK) { + return 0; + } + + unsigned char *indexed_pixels = NULL; + unsigned char *palette = NULL; + int ncolors; + int width; + int height; + int ile = 0; + + status = sixel_decode_raw( + (unsigned char *)pixels->source, + pixels->length, + &indexed_pixels, + &width, + &height, + &palette, + &ncolors, + decoder->allocator + ); + + if (SIXEL_FAILED(status)) { + goto end; + } + im->y = lineno + 1; + im->x = 0; + im->width = width; + im->height = height; + *(&im->sixel) = *pixels; + ile = (height + 15) / 16; + add_to_list(doc->images, im); +end: + sixel_allocator_free(decoder->allocator, indexed_pixels); + sixel_allocator_free(decoder->allocator, palette); + sixel_decoder_unref(decoder); + + return ile; +} diff --git a/src/terminal/sixel.h b/src/terminal/sixel.h new file mode 100644 index 00000000..77b3ae3c --- /dev/null +++ b/src/terminal/sixel.h @@ -0,0 +1,38 @@ +#ifndef EL__TERMINAL_SIXEL_H +#define EL__TERMINAL_SIXEL_H + +#include "util/lists.h" +#include "util/string.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef CONFIG_LIBSIXEL +struct document; +struct terminal; + +struct image { + LIST_HEAD(struct image); + struct string sixel; + int x; + int y; + int width; + int height; +}; + +void delete_image(struct image *im); + +void try_to_draw_images(struct terminal *term); + +/* return height of image in terminal rows */ +int add_image_to_document(struct document *doc, struct string *pixels, int lineno); + + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EL__TERMINAL_SIXEL_H */ diff --git a/src/terminal/terminal.cpp b/src/terminal/terminal.cpp index fb4ad1ed..fa6c4fad 100644 --- a/src/terminal/terminal.cpp +++ b/src/terminal/terminal.cpp @@ -35,6 +35,7 @@ #include "terminal/hardio.h" #include "terminal/kbd.h" #include "terminal/screen.h" +#include "terminal/sixel.h" #include "terminal/terminal.h" #ifdef CONFIG_TERMINFO #include "terminal/terminfo.h" @@ -117,6 +118,9 @@ init_term(int fdin, int fdout) #endif init_list(term->windows); +#ifdef CONFIG_LIBSIXEL + init_list(term->images); +#endif term->fdin = fdin; term->fdout = fdout; term->master = (term->fdout == get_output_handle()); @@ -184,6 +188,11 @@ destroy_terminal(struct terminal *term) while (!list_empty(term->windows)) delete_window((struct window *)term->windows.next); +#ifdef CONFIG_LIBSIXEL + while (!list_empty(term->images)) { + delete_image((struct image *)term->images.next); + } +#endif /* mem_free_if(term->cwd); */ mem_free_if(term->title); if (term->screen) done_screen(term->screen); diff --git a/src/terminal/terminal.h b/src/terminal/terminal.h index 36cb69d0..d8d6e2c1 100644 --- a/src/terminal/terminal.h +++ b/src/terminal/terminal.h @@ -172,6 +172,10 @@ struct terminal { void *textarea_data; struct term_event_mouse prev_mouse_event; + +#ifdef CONFIG_LIBSIXEL + LIST_OF(struct image) images; +#endif }; #define do_not_ignore_next_mouse_event(term) \ diff --git a/src/viewer/text/draw.c b/src/viewer/text/draw.c index 8975b4a2..49104d88 100644 --- a/src/viewer/text/draw.c +++ b/src/viewer/text/draw.c @@ -5,6 +5,7 @@ #include "config.h" #endif +#include #include #include #ifdef HAVE_UNISTD_H @@ -29,6 +30,7 @@ #include "session/location.h" #include "session/session.h" #include "terminal/draw.h" +#include "terminal/sixel.h" #include "terminal/tab.h" #include "terminal/terminal.h" #include "util/error.h" @@ -458,6 +460,32 @@ draw_doc(struct session *ses, struct document_view *doc_view, int active) if (vs->current_link == -1) vs->current_link = 0; } +#ifdef CONFIG_LIBSIXEL + while (!list_empty(term->images)) { + delete_image((struct image *)term->images.next); + } + + if (list_empty(term->images)) { + struct image *im; + + foreach (im, doc_view->document->images) { + if (im) { + struct image *im_copy = mem_calloc(1, sizeof(*im_copy)); + + if (im_copy) { + if (init_string(&im_copy->sixel)) { + add_string_to_string(&im_copy->sixel, &im->sixel); + im_copy->x = im->x; + im_copy->y = im->y; + im_copy->width = im->width; + im_copy->height = im->height; + add_to_list(term->images, im_copy); + } + } + } + } + } +#endif } static void