URI: 
       Added ansi buffer document view. - icy_draw - icy_draw is the successor to mystic draw. fork / mirror
  HTML git clone https://git.drkhsh.at/icy_draw.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 4f9a896a06ffec2867f0f614a5c16e82b06cbc2d
   DIR parent e10878b922fa31d8ec418f7f6fcc1f18ab04b266
  HTML Author: Mike Krueger <mkrueger@posteo.de>
       Date:   Thu,  8 Dec 2022 19:47:44 +0100
       
       Added ansi buffer document view.
       
       Diffstat:
         A src/ui/ansi_editor/buffer_view.rs   |     576 +++++++++++++++++++++++++++++++
         R src/ui/buffer_view/buffer_view.sha… |       0 
         A src/ui/ansi_editor/key_maps.rs      |      67 +++++++++++++++++++++++++++++++
         A src/ui/ansi_editor/mod.rs           |     289 +++++++++++++++++++++++++++++++
         A src/ui/ansi_editor/render.rs        |     676 +++++++++++++++++++++++++++++++
         R src/ui/buffer_view/render.shader.f… |       0 
         A src/ui/ansi_editor/sixel.rs         |     217 +++++++++++++++++++++++++++++++
         R src/ui/buffer_view/sixel.shader.fr… |       0 
         D src/ui/buffer_view/mod.rs           |     585 -------------------------------
         D src/ui/buffer_view/render.rs        |     676 -------------------------------
         D src/ui/buffer_view/selection.rs     |      43 ------------------------------
         D src/ui/buffer_view/sixel.rs         |     215 -------------------------------
         M src/ui/main_window.rs               |      26 ++++++++++++++++++++------
         M src/ui/mod.rs                       |       2 +-
       
       14 files changed, 1846 insertions(+), 1526 deletions(-)
       ---
   DIR diff --git a/src/ui/ansi_editor/buffer_view.rs b/src/ui/ansi_editor/buffer_view.rs
       @@ -0,0 +1,576 @@
       +use std::{cmp::{max, min}, fs, path::PathBuf, sync::Arc};
       +use eframe::{epaint::{Vec2, Rect, mutex::Mutex}, egui::{CursorIcon, self, ScrollArea}};
       +use glow::NativeTexture;
       +
       +use icy_engine::{Buffer, BufferParser, CallbackAction, Caret, EngineResult, Position, SaveOptions, Selection, AnsiParser};
       +
       +use crate::{Document, TerminalResult, ui::ansi_editor::{create_buffer_texture, create_palette_texture, create_font_texture}};
       +
       +use super::SixelCacheEntry;
       +
       +pub struct Blink {
       +    is_on: bool,
       +    last_blink: u128,
       +    blink_rate: u128,
       +}
       +
       +impl Blink {
       +    pub fn new(blink_rate: u128) -> Self {
       +        Self {
       +            is_on: false,
       +            last_blink: 0,
       +            blink_rate,
       +        }
       +    }
       +
       +    pub fn is_on(&self) -> bool {
       +        self.is_on
       +    }
       +
       +    pub fn update(&mut self, cur_ms: u128) -> bool {
       +        if cur_ms - self.last_blink > self.blink_rate {
       +            self.is_on = !self.is_on;
       +            self.last_blink = cur_ms;
       +            true
       +        } else {
       +            false
       +        }
       +    }
       +}
       +
       +pub struct BufferView {
       +    pub buf: Buffer,
       +    pub sixel_cache: Vec<SixelCacheEntry>,
       +    pub caret: Caret,
       +
       +    pub caret_blink: Blink,
       +    pub character_blink: Blink,
       +
       +    pub scale: f32,
       +    pub scroll_back_line: i32,
       +
       +    pub button_pressed: bool,
       +
       +    pub selection_opt: Option<Selection>,
       +
       +    pub program: glow::Program,
       +    pub vertex_array: glow::VertexArray,
       +
       +    pub redraw_view: bool,
       +
       +    pub redraw_palette: bool,
       +    pub colors: usize,
       +
       +    pub redraw_font: bool,
       +    pub fonts: usize,
       +    //scaling: Scaling,
       +    // post_processing: PostProcessing,
       +
       +    pub font_texture: NativeTexture,
       +    pub buffer_texture: NativeTexture,
       +    pub palette_texture: NativeTexture,
       +    pub framebuffer: glow::NativeFramebuffer,
       +    pub render_texture: NativeTexture,
       +    pub render_buffer_size: Vec2,
       +    pub draw_program: glow::NativeProgram,
       +    pub sixel_shader: glow::NativeProgram,
       +    pub sixel_render_texture: NativeTexture,
       +}
       +
       +impl BufferView {
       +    pub fn new(gl: &Arc<glow::Context>, buf: Buffer) -> Self {
       +        use glow::HasContext as _;
       +        unsafe {
       +            let sixel_shader = gl.create_program().expect("Cannot create program");
       +            let (vertex_shader_source, fragment_shader_source) = (
       +                r#"#version 330
       +const float low  =  -1.0;
       +const float high = 1.0;
       +
       +const vec2 verts[6] = vec2[6](
       +    vec2(low, high),
       +    vec2(high, high),
       +    vec2(high, low),
       +
       +    vec2(low, high),
       +    vec2(low, low),
       +    vec2(high, low)
       +);
       +
       +void main() {
       +    vec2 vert = verts[gl_VertexID];
       +    gl_Position = vec4(vert, 0.3, 1.0);
       +}
       +"#,
       +                include_str!("sixel.shader.frag"),
       +            );
       +            let shader_sources = [
       +                (glow::VERTEX_SHADER, vertex_shader_source),
       +                (glow::FRAGMENT_SHADER, fragment_shader_source),
       +            ];
       +
       +            let shaders: Vec<_> = shader_sources
       +                .iter()
       +                .map(|(shader_type, shader_source)| {
       +                    let shader = gl
       +                        .create_shader(*shader_type)
       +                        .expect("Cannot create shader");
       +                    gl.shader_source(
       +                        shader,
       +                        shader_source, /*&format!("{}\n{}", shader_version, shader_source)*/
       +                    );
       +                    gl.compile_shader(shader);
       +                    if !gl.get_shader_compile_status(shader) {
       +                        panic!("{}", gl.get_shader_info_log(shader));
       +                    }
       +                    gl.attach_shader(sixel_shader, shader);
       +                    shader
       +                })
       +                .collect();
       +
       +            gl.link_program(sixel_shader);
       +            if !gl.get_program_link_status(sixel_shader) {
       +                panic!("{}", gl.get_program_info_log(sixel_shader));
       +            }
       +
       +            for shader in shaders {
       +                gl.detach_shader(sixel_shader, shader);
       +                gl.delete_shader(shader);
       +            }
       +
       +            let draw_program = gl.create_program().expect("Cannot create program");
       +            let (vertex_shader_source, fragment_shader_source) = (
       +                r#"#version 330
       +    const float low  =  -1.0;
       +    const float high = 1.0;
       +    
       +    const vec2 verts[6] = vec2[6](
       +        vec2(low, high),
       +        vec2(high, high),
       +        vec2(high, low),
       +    
       +        vec2(low, high),
       +        vec2(low, low),
       +        vec2(high, low)
       +    );
       +    
       +    void main() {
       +        vec2 vert = verts[gl_VertexID];
       +        gl_Position = vec4(vert, 0.3, 1.0);
       +    }
       +    "#,
       +                include_str!("render.shader.frag"),
       +            );
       +            let shader_sources = [
       +                (glow::VERTEX_SHADER, vertex_shader_source),
       +                (glow::FRAGMENT_SHADER, fragment_shader_source),
       +            ];
       +
       +            let shaders: Vec<_> = shader_sources
       +                .iter()
       +                .map(|(shader_type, shader_source)| {
       +                    let shader = gl
       +                        .create_shader(*shader_type)
       +                        .expect("Cannot create shader");
       +                    gl.shader_source(
       +                        shader,
       +                        shader_source, /*&format!("{}\n{}", shader_version, shader_source)*/
       +                    );
       +                    gl.compile_shader(shader);
       +                    if !gl.get_shader_compile_status(shader) {
       +                        panic!("{}", gl.get_shader_info_log(shader));
       +                    }
       +                    gl.attach_shader(draw_program, shader);
       +                    shader
       +                })
       +                .collect();
       +
       +            gl.link_program(draw_program);
       +            if !gl.get_program_link_status(draw_program) {
       +                panic!("{}", gl.get_program_info_log(draw_program));
       +            }
       +
       +            for shader in shaders {
       +                gl.detach_shader(draw_program, shader);
       +                gl.delete_shader(shader);
       +            }
       +
       +            let program = gl.create_program().expect("Cannot create program");
       +
       +            let (vertex_shader_source, fragment_shader_source) = (
       +                r#"#version 330
       +const float low  =  -1.0;
       +const float high = 1.0;
       +
       +const vec2 verts[6] = vec2[6](
       +    vec2(low, high),
       +    vec2(high, high),
       +    vec2(high, low),
       +
       +    vec2(low, high),
       +    vec2(low, low),
       +    vec2(high, low)
       +);
       +
       +void main() {
       +    vec2 vert = verts[gl_VertexID];
       +    gl_Position = vec4(vert, 0.3, 1.0);
       +}
       +"#,
       +                include_str!("buffer_view.shader.frag"),
       +            );
       +            let shader_sources = [
       +                (glow::VERTEX_SHADER, vertex_shader_source),
       +                (glow::FRAGMENT_SHADER, fragment_shader_source),
       +            ];
       +
       +            let shaders: Vec<_> = shader_sources
       +                .iter()
       +                .map(|(shader_type, shader_source)| {
       +                    let shader = gl
       +                        .create_shader(*shader_type)
       +                        .expect("Cannot create shader");
       +                    gl.shader_source(
       +                        shader,
       +                        shader_source, /*&format!("{}\n{}", shader_version, shader_source)*/
       +                    );
       +                    gl.compile_shader(shader);
       +                    if !gl.get_shader_compile_status(shader) {
       +                        panic!("{}", gl.get_shader_info_log(shader));
       +                    }
       +                    gl.attach_shader(program, shader);
       +                    shader
       +                })
       +                .collect();
       +
       +            gl.link_program(program);
       +            if !gl.get_program_link_status(program) {
       +                panic!("{}", gl.get_program_info_log(program));
       +            }
       +
       +            for shader in shaders {
       +                gl.detach_shader(program, shader);
       +                gl.delete_shader(shader);
       +            }
       +
       +            let vertex_array = gl
       +                .create_vertex_array()
       +                .expect("Cannot create vertex array");
       +            let buffer_texture = gl.create_texture().unwrap();
       +            create_buffer_texture(gl, &buf, 0, buffer_texture);
       +            gl.tex_parameter_i32(
       +                glow::TEXTURE_2D_ARRAY,
       +                glow::TEXTURE_MIN_FILTER,
       +                glow::NEAREST as i32,
       +            );
       +            gl.tex_parameter_i32(
       +                glow::TEXTURE_2D_ARRAY,
       +                glow::TEXTURE_MAG_FILTER,
       +                glow::NEAREST as i32,
       +            );
       +            gl.tex_parameter_i32(
       +                glow::TEXTURE_2D_ARRAY,
       +                glow::TEXTURE_WRAP_S,
       +                glow::CLAMP_TO_EDGE as i32,
       +            );
       +            gl.tex_parameter_i32(
       +                glow::TEXTURE_2D_ARRAY,
       +                glow::TEXTURE_WRAP_T,
       +                glow::CLAMP_TO_EDGE as i32,
       +            );
       +
       +            let palette_texture = gl.create_texture().unwrap();
       +            create_palette_texture(gl, &buf, palette_texture);
       +            gl.tex_parameter_i32(
       +                glow::TEXTURE_2D,
       +                glow::TEXTURE_MIN_FILTER,
       +                glow::NEAREST as i32,
       +            );
       +            gl.tex_parameter_i32(
       +                glow::TEXTURE_2D,
       +                glow::TEXTURE_MAG_FILTER,
       +                glow::NEAREST as i32,
       +            );
       +            gl.tex_parameter_i32(
       +                glow::TEXTURE_2D,
       +                glow::TEXTURE_WRAP_S,
       +                glow::CLAMP_TO_EDGE as i32,
       +            );
       +            gl.tex_parameter_i32(
       +                glow::TEXTURE_2D,
       +                glow::TEXTURE_WRAP_T,
       +                glow::CLAMP_TO_EDGE as i32,
       +            );
       +
       +            let font_texture = gl.create_texture().unwrap();
       +            create_font_texture(gl, &buf, font_texture);
       +            gl.tex_parameter_i32(
       +                glow::TEXTURE_2D_ARRAY,
       +                glow::TEXTURE_MIN_FILTER,
       +                glow::NEAREST as i32,
       +            );
       +            gl.tex_parameter_i32(
       +                glow::TEXTURE_2D_ARRAY,
       +                glow::TEXTURE_MAG_FILTER,
       +                glow::NEAREST as i32,
       +            );
       +            gl.tex_parameter_i32(
       +                glow::TEXTURE_2D_ARRAY,
       +                glow::TEXTURE_WRAP_S,
       +                glow::CLAMP_TO_EDGE as i32,
       +            );
       +            gl.tex_parameter_i32(
       +                glow::TEXTURE_2D_ARRAY,
       +                glow::TEXTURE_WRAP_T,
       +                glow::CLAMP_TO_EDGE as i32,
       +            );
       +            let colors = buf.palette.colors.len();
       +            let fonts = buf.font_table.len();
       +            let framebuffer = gl.create_framebuffer().unwrap();
       +
       +            gl.bind_framebuffer(glow::FRAMEBUFFER, Some(framebuffer));
       +            let render_texture = gl.create_texture().unwrap();
       +            let render_buffer_size = Vec2::new(
       +                buf.get_font_dimensions().width as f32 * buf.get_buffer_width() as f32,
       +                buf.get_font_dimensions().height as f32 * buf.get_buffer_height() as f32,
       +            );
       +
       +            let filter = glow::NEAREST as i32; /*match options.scaling {
       +                Scaling::Nearest => glow::NEAREST as i32,
       +                Scaling::Linear => glow::LINEAR as i32,
       +            };*/
       +            gl.bind_texture(glow::TEXTURE_2D, Some(render_texture));
       +            gl.tex_image_2d(
       +                glow::TEXTURE_2D,
       +                0,
       +                glow::RGBA as i32,
       +                render_buffer_size.x as i32,
       +                render_buffer_size.y as i32,
       +                0,
       +                glow::RGBA,
       +                glow::UNSIGNED_BYTE,
       +                None,
       +            );
       +            gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, filter);
       +            gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, filter);
       +            gl.tex_parameter_i32(
       +                glow::TEXTURE_2D,
       +                glow::TEXTURE_WRAP_S,
       +                glow::CLAMP_TO_EDGE as i32,
       +            );
       +            gl.tex_parameter_i32(
       +                glow::TEXTURE_2D,
       +                glow::TEXTURE_WRAP_T,
       +                glow::CLAMP_TO_EDGE as i32,
       +            );
       +
       +            let depth_buffer = gl.create_renderbuffer().unwrap();
       +            gl.bind_renderbuffer(glow::RENDERBUFFER, Some(depth_buffer));
       +            gl.renderbuffer_storage(
       +                glow::RENDERBUFFER,
       +                glow::DEPTH_COMPONENT,
       +                render_buffer_size.x as i32,
       +                render_buffer_size.y as i32,
       +            );
       +            gl.framebuffer_renderbuffer(
       +                glow::FRAMEBUFFER,
       +                glow::DEPTH_ATTACHMENT,
       +                glow::RENDERBUFFER,
       +                Some(depth_buffer),
       +            );
       +            gl.framebuffer_texture(
       +                glow::FRAMEBUFFER,
       +                glow::COLOR_ATTACHMENT0,
       +                Some(render_texture),
       +                0,
       +            );
       +
       +            gl.bind_framebuffer(glow::FRAMEBUFFER, None);
       +
       +            let sixel_render_texture = gl.create_texture().unwrap();
       +            let render_buffer_size = Vec2::new(
       +                buf.get_font_dimensions().width as f32 * buf.get_buffer_width() as f32,
       +                buf.get_font_dimensions().height as f32 * buf.get_buffer_height() as f32,
       +            );
       +
       +            gl.bind_texture(glow::TEXTURE_2D, Some(sixel_render_texture));
       +            gl.tex_image_2d(
       +                glow::TEXTURE_2D,
       +                0,
       +                glow::RGBA as i32,
       +                render_buffer_size.x as i32,
       +                render_buffer_size.y as i32,
       +                0,
       +                glow::RGBA,
       +                glow::UNSIGNED_BYTE,
       +                None,
       +            );
       +            gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, filter);
       +            gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, filter);
       +            gl.tex_parameter_i32(
       +                glow::TEXTURE_2D,
       +                glow::TEXTURE_WRAP_S,
       +                glow::CLAMP_TO_EDGE as i32,
       +            );
       +            gl.tex_parameter_i32(
       +                glow::TEXTURE_2D,
       +                glow::TEXTURE_WRAP_T,
       +                glow::CLAMP_TO_EDGE as i32,
       +            );
       +
       +            let depth_buffer = gl.create_renderbuffer().unwrap();
       +            gl.bind_renderbuffer(glow::RENDERBUFFER, Some(depth_buffer));
       +            gl.renderbuffer_storage(
       +                glow::RENDERBUFFER,
       +                glow::DEPTH_COMPONENT,
       +                render_buffer_size.x as i32,
       +                render_buffer_size.y as i32,
       +            );
       +            gl.framebuffer_renderbuffer(
       +                glow::FRAMEBUFFER,
       +                glow::DEPTH_ATTACHMENT,
       +                glow::RENDERBUFFER,
       +                Some(depth_buffer),
       +            );
       +            gl.framebuffer_texture(
       +                glow::FRAMEBUFFER,
       +                glow::COLOR_ATTACHMENT0,
       +                Some(sixel_render_texture),
       +                0,
       +            );
       +
       +            gl.bind_framebuffer(glow::FRAMEBUFFER, None);
       +
       +            Self {
       +                buf,
       +                caret: Caret::default(),
       +                caret_blink: Blink::new((1000.0 / 1.875) as u128 / 2),
       +                character_blink: Blink::new((1000.0 / 1.8) as u128),
       +                scale: 1.0,
       +                sixel_cache: Vec::new(),
       +                button_pressed: false,
       +                redraw_view: false,
       +                redraw_palette: false,
       +                redraw_font: false,
       +                scroll_back_line: 0,
       +                selection_opt: None,
       +                colors,
       +                fonts,
       +                program,
       +                draw_program,
       +                vertex_array,
       +                font_texture,
       +                buffer_texture,
       +                palette_texture,
       +                // scaling: options.scaling,
       +                // post_processing: options.post_processing,
       +
       +                framebuffer,
       +                render_texture,
       +                render_buffer_size,
       +
       +                sixel_shader,
       +                sixel_render_texture,
       +            }
       +        }
       +    }
       +
       +    pub fn clear(&mut self) {
       +        self.caret.ff(&mut self.buf);
       +    }
       +
       +    pub fn get_copy_text(&mut self, buffer_parser: &Box<dyn BufferParser>) -> Option<String> {
       +        let Some(selection) = &self.selection_opt else {
       +            return None;
       +        };
       +
       +        let mut res = String::new();
       +        if selection.block_selection {
       +            let start = Position::new(
       +                min(selection.anchor_pos.x, selection.lead_pos.x),
       +                min(selection.anchor_pos.y, selection.lead_pos.y),
       +            );
       +            let end = Position::new(
       +                max(selection.anchor_pos.x, selection.lead_pos.x),
       +                max(selection.anchor_pos.y, selection.lead_pos.y),
       +            );
       +            for y in start.y..=end.y {
       +                for x in start.x..end.x {
       +                    let ch = self.buf.get_char(Position::new(x, y)).unwrap();
       +                    res.push(buffer_parser.to_unicode(ch.ch));
       +                }
       +                res.push('\n');
       +            }
       +        } else {
       +            let (start, end) = if selection.anchor_pos < selection.lead_pos {
       +                (selection.anchor_pos, selection.lead_pos)
       +            } else {
       +                (selection.lead_pos, selection.anchor_pos)
       +            };
       +            if start.y != end.y {
       +                for x in start.x..self.buf.get_line_length(start.y) {
       +                    let ch = self.buf.get_char(Position::new(x, start.y)).unwrap();
       +                    res.push(buffer_parser.to_unicode(ch.ch));
       +                }
       +                res.push('\n');
       +                for y in start.y + 1..end.y {
       +                    for x in 0..self.buf.get_line_length(y) {
       +                        let ch = self.buf.get_char(Position::new(x, y)).unwrap();
       +                        res.push(buffer_parser.to_unicode(ch.ch));
       +                    }
       +                    res.push('\n');
       +                }
       +                for x in 0..=end.x {
       +                    let ch = self.buf.get_char(Position::new(x, end.y)).unwrap();
       +                    res.push(buffer_parser.to_unicode(ch.ch));
       +                }
       +            } else {
       +                for x in start.x..=end.x {
       +                    let ch = self.buf.get_char(Position::new(x, start.y)).unwrap();
       +                    res.push(buffer_parser.to_unicode(ch.ch));
       +                }
       +            }
       +        }
       +        self.selection_opt = None;
       +        Some(res)
       +    }
       +
       +    pub fn redraw_view(&mut self) {
       +        self.redraw_view = true;
       +    }
       +
       +    pub fn redraw_palette(&mut self) {
       +        self.redraw_palette = true;
       +    }
       +
       +    pub fn redraw_font(&mut self) {
       +        self.redraw_font = true;
       +    }
       +
       +    pub fn print_char(
       +        &mut self,
       +        parser: &mut Box<dyn BufferParser>,
       +        c: char,
       +    ) -> EngineResult<CallbackAction> {
       +        let res = parser.print_char(&mut self.buf, &mut self.caret, c);
       +        self.redraw_view();
       +        res
       +    }
       +
       +    pub fn destroy(&self, gl: &glow::Context) {
       +        use glow::HasContext as _;
       +        unsafe {
       +            gl.delete_program(self.program);
       +            gl.delete_vertex_array(self.vertex_array);
       +        }
       +    }
       +/*
       +    pub fn set_scaling(&mut self, scaling: Scaling) {
       +        self.scaling = scaling;
       +        self.render_buffer_size = Vec2::new(0., 0.);
       +    }
       +    pub fn set_post_processing(&mut self, post_processing: PostProcessing) {
       +        self.post_processing = post_processing;
       +    }*/
       +}
       +
   DIR diff --git a/src/ui/buffer_view/buffer_view.shader.frag b/src/ui/ansi_editor/buffer_view.shader.frag
   DIR diff --git a/src/ui/ansi_editor/key_maps.rs b/src/ui/ansi_editor/key_maps.rs
       @@ -0,0 +1,67 @@
       +use eframe::egui::Key;
       +
       +pub const CTRL_MOD: u32 = 0b1000_0000_0000_0000_0000;
       +pub const SHIFT_MOD: u32 = 0b0100_0000_0000_0000_0000;
       +
       +pub static ANSI_KEY_MAP: &[(u32, &[u8])] = &[
       +    (Key::Escape as u32, &[0x1B]),
       +    (Key::Home as u32, b"\x1b[H"),
       +    (Key::Insert as u32, b"\x1b[@"),
       +    (Key::Backspace as u32, &[8]),
       +    (Key::Enter as u32, &[b'\r']),
       +    (Key::Tab as u32, &[9]),
       +    (Key::Tab as u32 | SHIFT_MOD, b"\x1b[Z"),
       +    (Key::Delete as u32, &[127]),
       +    (Key::Insert as u32, b"\x1b[@"),
       +    (Key::End as u32, b"\x1b[K"),
       +    (Key::PageUp as u32, b"\x1b[V"),
       +    (Key::PageDown as u32, b"\x1b[U"),
       +    (Key::F1 as u32, b"\x1b[OP"),
       +    (Key::F2 as u32, b"\x1b[OQ"),
       +    (Key::F3 as u32, b"\x1b[OR"),
       +    (Key::F4 as u32, b"\x1b[OS"),
       +    (Key::F5 as u32, b"\x1b[OT"),
       +    (Key::F6 as u32, b"\x1b[17~"),
       +    (Key::F7 as u32, b"\x1b[18~"),
       +    (Key::F8 as u32, b"\x1b[19~"),
       +    (Key::F9 as u32, b"\x1b[20~"),
       +    (Key::F10 as u32, b"\x1b[21~"),
       +    (Key::F11 as u32, b"\x1b[23~"),
       +    (Key::F12 as u32, b"\x1b[24~"),
       +    (Key::ArrowUp as u32, b"\x1b[A"),
       +    (Key::ArrowDown as u32, b"\x1b[B"),
       +    (Key::ArrowRight as u32, b"\x1b[C"),
       +    (Key::ArrowLeft as u32, b"\x1b[D"),
       +    (Key::A as u32 | CTRL_MOD, &[1]),
       +    (Key::B as u32 | CTRL_MOD, &[2]),
       +    (Key::C as u32 | CTRL_MOD, &[3]),
       +    (Key::D as u32 | CTRL_MOD, &[4]),
       +    (Key::E as u32 | CTRL_MOD, &[5]),
       +    (Key::F as u32 | CTRL_MOD, &[6]),
       +    (Key::G as u32 | CTRL_MOD, &[7]),
       +    (Key::H as u32 | CTRL_MOD, &[8]),
       +    (Key::I as u32 | CTRL_MOD, &[9]),
       +    (Key::J as u32 | CTRL_MOD, &[10]),
       +    (Key::K as u32 | CTRL_MOD, &[11]),
       +    (Key::L as u32 | CTRL_MOD, &[12]),
       +    (Key::M as u32 | CTRL_MOD, &[13]),
       +    (Key::N as u32 | CTRL_MOD, &[14]),
       +    (Key::O as u32 | CTRL_MOD, &[15]),
       +    (Key::P as u32 | CTRL_MOD, &[16]),
       +    (Key::Q as u32 | CTRL_MOD, &[17]),
       +    (Key::R as u32 | CTRL_MOD, &[18]),
       +    (Key::S as u32 | CTRL_MOD, &[19]),
       +    (Key::T as u32 | CTRL_MOD, &[20]),
       +    (Key::U as u32 | CTRL_MOD, &[21]),
       +    (Key::V as u32 | CTRL_MOD, &[22]),
       +    (Key::W as u32 | CTRL_MOD, &[23]),
       +    (Key::X as u32 | CTRL_MOD, &[24]),
       +    (Key::Y as u32 | CTRL_MOD, &[25]),
       +    (Key::Z as u32 | CTRL_MOD, &[26]),
       +    (Key::Num2 as u32 | CTRL_MOD, &[0]),
       +    (Key::Num3 as u32 | CTRL_MOD, &[0x1B]),
       +    (Key::Num4 as u32 | CTRL_MOD, &[0x1C]),
       +    (Key::Num5 as u32 | CTRL_MOD, &[0x1D]),
       +    (Key::Num6 as u32 | CTRL_MOD, &[0x1E]),
       +    (Key::Num7 as u32 | CTRL_MOD, &[0x1F]),
       +];
   DIR diff --git a/src/ui/ansi_editor/mod.rs b/src/ui/ansi_editor/mod.rs
       @@ -0,0 +1,289 @@
       +use std::{cmp::{max}, fs, path::PathBuf, sync::Arc};
       +use eframe::{epaint::{Vec2, Rect, mutex::Mutex}, egui::{CursorIcon, self, ScrollArea, PointerButton}};
       +use icy_engine::{Buffer, SaveOptions, AnsiParser, Selection, BufferParser};
       +
       +pub mod render;
       +pub use render::*;
       +
       +pub mod sixel;
       +pub use sixel::*;
       +
       +pub mod buffer_view;
       +pub use buffer_view::*;
       +
       +pub mod key_maps;
       +pub use key_maps::*;
       +
       +use crate::{Document, TerminalResult};
       +
       +
       +pub struct AnsiEditor {
       +    is_dirty: bool,
       +    buffer_view: Arc<Mutex<BufferView>>,
       +    buffer_parser: Box<dyn BufferParser>
       +}
       +
       +impl AnsiEditor {
       +    pub fn new(gl: &Arc<glow::Context>, buf: Buffer) -> Self {
       +        let buffer_view = Arc::new(Mutex::new(BufferView::new(gl, buf)));
       +        let mut buffer_parser = AnsiParser::new();
       +
       +        Self {
       +            is_dirty: false,
       +            buffer_view,
       +            buffer_parser: Box::new(buffer_parser)
       +        }
       +    }
       +
       +    pub fn output_string(&mut self, str: &str) {
       +        for ch in str.chars() {
       +            let translated_char = self.buffer_parser.from_unicode(ch);
       +            if let Err(err) = self.print_char(translated_char as u8) {
       +                eprintln!("{}", err);
       +            }
       +        }
       +    }
       +
       +    pub fn print_char(&mut self, c: u8) -> Result<(), Box<dyn std::error::Error>> {
       +        let result = self
       +            .buffer_view
       +            .lock()
       +            .print_char(&mut self.buffer_parser, unsafe {
       +                char::from_u32_unchecked(c as u32)
       +            })?;
       +        self.buffer_view.lock().redraw_view();
       +        Ok(())
       +    }
       +
       +
       +}
       +
       +impl Document for AnsiEditor {
       +    fn get_title(&self) -> String {
       +        if let Some(file_name) = &self.buffer_view.lock().buf.file_name {
       +            file_name.file_name().unwrap().to_str().unwrap().to_string()
       +        } else {
       +            "Untitled".to_string()
       +        }
       +    }
       +
       +    fn is_dirty(&self) -> bool {
       +        self.is_dirty
       +    }
       +
       +    fn save(&mut self, file_name: &str) -> TerminalResult<()> {
       +        let file =  PathBuf::from(file_name);
       +        let options = SaveOptions::new();
       +        let bytes = self.buffer_view.lock().buf.to_bytes(file.extension().unwrap().to_str().unwrap(), &options)?;
       +        fs::write(file_name, bytes)?;
       +        self.is_dirty = false;
       +        Ok(())
       +    }
       +
       +    fn show_ui(&mut self, ui: &mut eframe::egui::Ui) {
       +        let size = ui.max_rect().size();
       +        let buf_w = self.buffer_view.lock().buf.get_buffer_width();
       +        let buf_h = self.buffer_view.lock().buf.get_buffer_height();
       +        // let h = max(buf_h, buffer_view.lock().buf.get_real_buffer_height());
       +        let font_dimensions = self.buffer_view.lock().buf.get_font_dimensions();
       +
       +        let mut scale_x = (size.x - 4.0) / font_dimensions.width as f32 / buf_w as f32;
       +        let mut scale_y = size.y / font_dimensions.height as f32 / buf_h as f32;
       +
       +        if scale_x < scale_y {
       +            scale_y = scale_x;
       +        } else {
       +            scale_x = scale_y;
       +        }
       +
       +        let char_size = Vec2::new(
       +            font_dimensions.width as f32 * scale_x,
       +            font_dimensions.height as f32 * scale_y,
       +        );
       +
       +        let rect_w = buf_w as f32 * char_size.x;
       +        let rect_h = buf_h as f32 * char_size.y;
       +        let top_margin_height = ui.min_rect().top();
       +
       +        let output = ScrollArea::vertical()
       +            .auto_shrink([false; 2])
       +            .stick_to_bottom(true)
       +            .show_viewport(ui, |ui, viewport| {
       +                let (draw_area, mut response) = ui.allocate_at_least(size, egui::Sense::click());
       +
       +                let rect = Rect::from_min_size(
       +                    draw_area.left_top()
       +                        + Vec2::new(
       +                            (-4.0 + draw_area.width() - rect_w) / 2.,
       +                            (-top_margin_height
       +                                + viewport.top()
       +                                + (draw_area.height() - rect_h) / 2.)
       +                                .floor(),
       +                        )
       +                        .ceil(),
       +                    Vec2::new(rect_w, rect_h),
       +                );
       +                let real_height = self.buffer_view.lock().buf.get_real_buffer_height();
       +                let max_lines = max(0, real_height - buf_h);
       +                ui.set_height(scale_y * max_lines as f32 * font_dimensions.height as f32);
       +
       +                let first_line = (viewport.top() / char_size.y) as i32;
       +                let scroll_back_line = max(0, max_lines - first_line);
       +
       +                if scroll_back_line != self.buffer_view.lock().scroll_back_line {
       +                    self.buffer_view.lock().scroll_back_line = scroll_back_line;
       +                    self.buffer_view.lock().redraw_view();
       +                }
       +                
       +                let buffer_view  = self.buffer_view.clone();
       +                let callback = egui::PaintCallback {
       +                    rect,
       +                    callback: std::sync::Arc::new(egui_glow::CallbackFn::new(
       +                        move |_info, painter| {
       +                            buffer_view.lock().update_buffer(painter.gl());
       +                            buffer_view.lock().paint(painter.gl(), rect);
       +                        },
       +                    )),
       +                };
       +
       +                ui.painter().add(callback);
       +                response = response.context_menu(terminal_context_menu);
       +
       +                let events = ui.input().events.clone();
       +                for e in &events {
       +                    match e {
       +                        egui::Event::PointerButton {
       +                            button: PointerButton::Middle,
       +                            pressed: true,
       +                            ..
       +                        }
       +                        | egui::Event::Copy => {
       +                            let buffer_view = self.buffer_view.clone();
       +                            let mut l = buffer_view.lock();
       +                            if let Some(txt) = l.get_copy_text(&self.buffer_parser) {
       +                                ui.output().copied_text = txt;
       +                            }
       +                        }
       +                        egui::Event::Cut => {}
       +                        egui::Event::Paste(text) => {
       +                            self.output_string(text);
       +                        }
       +                        egui::Event::CompositionEnd(text) | egui::Event::Text(text) => {
       +                            self.output_string(text);
       +                            response.mark_changed();
       +                        }
       +
       +                        egui::Event::PointerButton {
       +                            pos,
       +                            button: PointerButton::Primary,
       +                            pressed: true,
       +                            modifiers,
       +                        } => {
       +                            if rect.contains(*pos) {
       +                                let buffer_view = self.buffer_view.clone();
       +                                let click_pos =
       +                                    (*pos - rect.min - Vec2::new(0., top_margin_height))
       +                                        / char_size
       +                                        + Vec2::new(0.0, first_line as f32);
       +                                buffer_view.lock().selection_opt =
       +                                    Some(Selection::new((click_pos.x, click_pos.y)));
       +                                buffer_view
       +                                    .lock()
       +                                    .selection_opt
       +                                    .as_mut()
       +                                    .unwrap()
       +                                    .block_selection = modifiers.alt;
       +                            }
       +                        }
       +
       +                        egui::Event::PointerButton {
       +                            button: PointerButton::Primary,
       +                            pressed: false,
       +                            ..
       +                        } => {
       +                            let buffer_view = self.buffer_view.clone();
       +                            let mut l = buffer_view.lock();
       +                            if let Some(sel) = &mut l.selection_opt {
       +                                sel.locked = true;
       +                            }
       +                        }
       +
       +                        egui::Event::PointerMoved(pos) => {
       +                            let buffer_view = self.buffer_view.clone();
       +                            let mut l = buffer_view.lock();
       +                            if let Some(sel) = &mut l.selection_opt {
       +                                if !sel.locked {
       +                                    let click_pos =
       +                                        (*pos - rect.min - Vec2::new(0., top_margin_height))
       +                                            / char_size
       +                                            + Vec2::new(0.0, first_line as f32);
       +                                    sel.set_lead((click_pos.x, click_pos.y));
       +                                    sel.block_selection = ui.input().modifiers.alt;
       +                                    l.redraw_view();
       +                                }
       +                            }
       +                        }
       +                        /*egui::Event::KeyRepeat { key, modifiers }
       +                        | */egui::Event::Key {
       +                            key,
       +                            pressed: true,
       +                            modifiers,
       +                        } => {
       +                            let mut key_code = *key as u32;
       +                            if modifiers.ctrl || modifiers.command {
       +                                key_code |= CTRL_MOD;
       +                            }
       +                            if modifiers.shift {
       +                                key_code |= SHIFT_MOD;
       +                            }
       +                            for (k, m) in ANSI_KEY_MAP {
       +                                if *k == key_code {
       +                                    //self.handled_char = true;
       +                                    for c in *m {
       +                                        if let Err(err) = self.print_char(*c) {
       +                                            eprintln!("{}", err);
       +                                        }
       +                                    }
       +                                    response.mark_changed();
       +                                    ui.input_mut().consume_key(*modifiers, *key);
       +                                    break;
       +                                }
       +                            }
       +                        }
       +                        _ => {}
       +                    }
       +                } 
       +                if response.hovered() {
       +                    let hover_pos_opt = ui.input().pointer.hover_pos();
       +                    if let Some(hover_pos) = hover_pos_opt {
       +                        if rect.contains(hover_pos) {
       +                            ui.output().cursor_icon = CursorIcon::Text;
       +                        }
       +                    }
       +                }
       +                response.dragged = false;
       +                response.drag_released = true;
       +                response.is_pointer_button_down_on = false;
       +                response.interact_pointer_pos = None;
       +                response
       +            });
       +
       +    }
       +}
       +
       +fn terminal_context_menu(ui: &mut egui::Ui) {
       +    ui.input_mut().events.clear();
       +
       +    if ui.button("Copy").clicked() {
       +        ui.input_mut().events.push(egui::Event::Copy);
       +        ui.close_menu();
       +    }
       +
       +    if ui.button("Paste").clicked() {
       +       /* let mut ctx: ClipboardContext = ClipboardProvider::new().unwrap();
       +        if let Ok(text) = ctx.get_contents() {
       +            ui.input_mut().events.push(egui::Event::Paste(text));
       +        }
       +        ui.close_menu();*/
       +    }
       +}
   DIR diff --git a/src/ui/ansi_editor/render.rs b/src/ui/ansi_editor/render.rs
       @@ -0,0 +1,676 @@
       +use eframe::epaint::{Rect, Vec2};
       +use glow::NativeTexture;
       +use icy_engine::Buffer;
       +use std::{
       +    cmp::max,
       +    time::{SystemTime, UNIX_EPOCH}, sync::Arc,
       +};
       +
       +use super::BufferView;
       +
       +impl BufferView {
       +    pub fn paint(&self, gl: &glow::Context, rect: Rect) {
       +        use glow::HasContext as _;
       +        unsafe {
       +            gl.bind_framebuffer(glow::FRAMEBUFFER, Some(self.framebuffer));
       +            gl.framebuffer_texture(
       +                glow::FRAMEBUFFER,
       +                glow::COLOR_ATTACHMENT0,
       +                Some(self.render_texture),
       +                0,
       +            );
       +            gl.bind_texture(glow::TEXTURE_2D, Some(self.render_texture));
       +            gl.viewport(
       +                0,
       +                0,
       +                self.render_buffer_size.x as i32,
       +                self.render_buffer_size.y as i32,
       +            );
       +            gl.clear(glow::COLOR_BUFFER_BIT | glow::DEPTH_BUFFER_BIT);
       +            gl.clear_color(0., 0., 0., 1.0);
       +
       +            gl.use_program(Some(self.program));
       +            gl.uniform_2_f32(
       +                gl.get_uniform_location(self.program, "u_resolution")
       +                    .as_ref(),
       +                self.render_buffer_size.x,
       +                self.render_buffer_size.y,
       +            );
       +            gl.uniform_2_f32(
       +                gl.get_uniform_location(self.program, "u_position").as_ref(),
       +                0.0,
       +                0.0,
       +            );
       +
       +            let sbl = (self.buf.get_first_visible_line() - self.scroll_back_line) as f32;
       +            gl.uniform_4_f32(
       +                gl.get_uniform_location(self.program, "u_caret_position")
       +                    .as_ref(),
       +                self.caret.get_position().x as f32,
       +                self.caret.get_position().y as f32 - sbl,
       +                if self.caret_blink.is_on() && self.caret.is_visible {
       +                    1.0
       +                } else {
       +                    0.0
       +                },
       +                if self.caret.insert_mode { 1.0 } else { 0.0 }, // shape
       +            );
       +
       +            gl.uniform_1_f32(
       +                gl.get_uniform_location(self.program, "u_blink").as_ref(),
       +                if self.character_blink.is_on() {
       +                    1.0
       +                } else {
       +                    0.0
       +                },
       +            );
       +
       +            gl.uniform_2_f32(
       +                gl.get_uniform_location(self.program, "u_terminal_size")
       +                    .as_ref(),
       +                self.buf.get_buffer_width() as f32 - 0.0001,
       +                self.buf.get_buffer_height() as f32 - 0.0001,
       +            );
       +
       +            gl.uniform_1_i32(gl.get_uniform_location(self.program, "u_fonts").as_ref(), 0);
       +            gl.uniform_1_i32(
       +                gl.get_uniform_location(self.program, "u_palette").as_ref(),
       +                2,
       +            );
       +            gl.uniform_1_i32(
       +                gl.get_uniform_location(self.program, "u_buffer").as_ref(),
       +                4,
       +            );
       +
       +            match &self.selection_opt {
       +                Some(sel) => {
       +                    if sel.is_empty() {
       +                        gl.uniform_4_f32(
       +                            gl.get_uniform_location(self.program, "u_selection")
       +                                .as_ref(),
       +                            0.0,
       +                            0.0,
       +                            0.0,
       +                            0.0,
       +                        );
       +                        gl.uniform_1_f32(
       +                            gl.get_uniform_location(self.program, "u_selection_attr")
       +                                .as_ref(),
       +                            -1.0,
       +                        );
       +                    } else {
       +                        if sel.anchor.1.floor() < sel.lead.1.floor()
       +                            || sel.anchor.1.floor() == sel.lead.1.floor()
       +                                && sel.anchor.0 < sel.lead.0
       +                        {
       +                            gl.uniform_4_f32(
       +                                gl.get_uniform_location(self.program, "u_selection")
       +                                    .as_ref(),
       +                                sel.anchor.0.floor(),
       +                                sel.anchor.1.floor() - sbl,
       +                                sel.lead.0.floor(),
       +                                sel.lead.1.floor() - sbl,
       +                            );
       +                        } else {
       +                            gl.uniform_4_f32(
       +                                gl.get_uniform_location(self.program, "u_selection")
       +                                    .as_ref(),
       +                                sel.lead.0.floor(),
       +                                sel.lead.1.floor() - sbl,
       +                                sel.anchor.0.floor(),
       +                                sel.anchor.1.floor() - sbl,
       +                            );
       +                        }
       +                        if sel.block_selection {
       +                            gl.uniform_1_f32(
       +                                gl.get_uniform_location(self.program, "u_selection_attr")
       +                                    .as_ref(),
       +                                1.0,
       +                            );
       +                        } else {
       +                            gl.uniform_1_f32(
       +                                gl.get_uniform_location(self.program, "u_selection_attr")
       +                                    .as_ref(),
       +                                0.0,
       +                            );
       +                        }
       +                    }
       +                }
       +                None => {
       +                    gl.uniform_4_f32(
       +                        gl.get_uniform_location(self.program, "u_selection")
       +                            .as_ref(),
       +                        0.0,
       +                        0.0,
       +                        0.0,
       +                        0.0,
       +                    );
       +                    gl.uniform_1_f32(
       +                        gl.get_uniform_location(self.program, "u_selection_attr")
       +                            .as_ref(),
       +                        -1.0,
       +                    );
       +                }
       +            }
       +
       +            gl.active_texture(glow::TEXTURE0);
       +            gl.bind_texture(glow::TEXTURE_2D, Some(self.font_texture));
       +            gl.active_texture(glow::TEXTURE0 + 2);
       +            gl.bind_texture(glow::TEXTURE_2D, Some(self.palette_texture));
       +            gl.active_texture(glow::TEXTURE0 + 4);
       +            gl.bind_texture(glow::TEXTURE_2D, Some(self.buffer_texture));
       +
       +            gl.bind_vertex_array(Some(self.vertex_array));
       +            gl.draw_arrays(glow::TRIANGLES, 0, 3);
       +            gl.draw_arrays(glow::TRIANGLES, 3, 3);
       +
       +            // draw sixels
       +            let mut render_texture = self.render_texture;
       +            let mut sixel_render_texture = self.sixel_render_texture;
       +
       +            for sixel in &self.sixel_cache {
       +                if let Some(sixel_tex) = sixel.texture_opt {
       +                    gl.bind_framebuffer(glow::FRAMEBUFFER, Some(self.framebuffer));
       +                    gl.framebuffer_texture(
       +                        glow::FRAMEBUFFER,
       +                        glow::COLOR_ATTACHMENT0,
       +                        Some(sixel_render_texture),
       +                        0,
       +                    );
       +                    gl.bind_texture(glow::TEXTURE_2D, Some(sixel_render_texture));
       +
       +                    gl.viewport(
       +                        0,
       +                        0,
       +                        self.render_buffer_size.x as i32,
       +                        self.render_buffer_size.y as i32,
       +                    );
       +
       +                    gl.use_program(Some(self.sixel_shader));
       +                    gl.uniform_1_i32(
       +                        gl.get_uniform_location(self.sixel_shader, "u_render_texture")
       +                            .as_ref(),
       +                        4,
       +                    );
       +                    gl.uniform_1_i32(
       +                        gl.get_uniform_location(self.sixel_shader, "u_sixel")
       +                            .as_ref(),
       +                        2,
       +                    );
       +
       +                    gl.active_texture(glow::TEXTURE0 + 4);
       +                    gl.bind_texture(glow::TEXTURE_2D, Some(render_texture));
       +
       +                    gl.active_texture(glow::TEXTURE0 + 2);
       +                    gl.bind_texture(glow::TEXTURE_2D, Some(sixel_tex));
       +
       +                    gl.uniform_2_f32(
       +                        gl.get_uniform_location(self.sixel_shader, "u_resolution")
       +                            .as_ref(),
       +                        self.render_buffer_size.x,
       +                        self.render_buffer_size.y,
       +                    );
       +
       +                    let x = sixel.pos.x as f32 * self.buf.get_font_dimensions().width as f32;
       +                    let y = sixel.pos.y as f32 * self.buf.get_font_dimensions().height as f32;
       +
       +                    let w = sixel.size.width as f32;
       +                    let h = sixel.size.height as f32;
       +
       +                    gl.uniform_4_f32(
       +                        gl.get_uniform_location(self.sixel_shader, "u_sixel_rectangle")
       +                            .as_ref(),
       +                        x,
       +                        y,
       +                        x + w,
       +                        y + h,
       +                    );
       +
       +                    gl.bind_vertex_array(Some(self.vertex_array));
       +                    gl.draw_arrays(glow::TRIANGLES, 0, 3);
       +                    gl.draw_arrays(glow::TRIANGLES, 3, 3);
       +
       +                    let tmp = render_texture;
       +                    render_texture = sixel_render_texture;
       +                    sixel_render_texture = tmp;
       +                }
       +            }
       +
       +            // draw Framebuffer
       +            gl.bind_framebuffer(glow::FRAMEBUFFER, None);
       +            gl.clear(glow::COLOR_BUFFER_BIT | glow::DEPTH_BUFFER_BIT);
       +            gl.viewport(
       +                rect.left() as i32,
       +                rect.top() as i32,
       +                rect.width() as i32,
       +                rect.height() as i32,
       +            );
       +            gl.use_program(Some(self.draw_program));
       +            gl.active_texture(glow::TEXTURE0);
       +            gl.uniform_1_i32(
       +                gl.get_uniform_location(self.draw_program, "u_render_texture")
       +                    .as_ref(),
       +                0,
       +            );
       +            gl.bind_texture(glow::TEXTURE_2D, Some(render_texture));
       +            gl.uniform_1_f32(
       +                gl.get_uniform_location(self.draw_program, "u_effect")
       +                    .as_ref(),
       +                    0.0
       +                    /* 
       +                match self.post_processing {
       +                    crate::ui::main_window::PostProcessing::None => 0.0,
       +                    crate::ui::main_window::PostProcessing::CRT1 => 1.0,
       +                },*/
       +            );
       +
       +            gl.uniform_2_f32(
       +                gl.get_uniform_location(self.draw_program, "u_resolution")
       +                    .as_ref(),
       +                rect.width(),
       +                rect.height(),
       +            );
       +            gl.uniform_2_f32(
       +                gl.get_uniform_location(self.draw_program, "u_position")
       +                    .as_ref(),
       +                rect.left(),
       +                rect.top(),
       +            );
       +
       +            gl.bind_vertex_array(Some(self.vertex_array));
       +            gl.draw_arrays(glow::TRIANGLES, 0, 3);
       +            gl.draw_arrays(glow::TRIANGLES, 3, 3);
       +        }
       +    }
       +
       +    pub fn update_buffer(&mut self, gl: &Arc<glow::Context>) {
       +        self.update_sixels(gl);
       +
       +        let start = SystemTime::now();
       +        let since_the_epoch = start
       +            .duration_since(UNIX_EPOCH)
       +            .expect("Time went backwards");
       +        let cur_ms = since_the_epoch.as_millis();
       +        if self.caret_blink.update(cur_ms) || self.character_blink.update(cur_ms) {
       +            self.redraw_view = true;
       +        }
       +
       +        if self.redraw_view {
       +            self.redraw_view = false;
       +            create_buffer_texture(gl, &self.buf, self.scroll_back_line, self.buffer_texture);
       +        }
       +
       +        if self.redraw_palette || self.colors != self.buf.palette.colors.len() {
       +            self.redraw_palette = false;
       +            create_palette_texture(gl, &self.buf, self.palette_texture);
       +            self.colors = self.buf.palette.colors.len();
       +        }
       +
       +        if self.redraw_font || self.fonts != self.buf.font_table.len() {
       +            self.redraw_font = false;
       +            create_font_texture(gl, &self.buf, self.palette_texture);
       +            self.fonts = self.buf.font_table.len();
       +        }
       +        let buf = &self.buf;
       +        let render_buffer_size = Vec2::new(
       +            buf.get_font_dimensions().width as f32 * buf.get_buffer_width() as f32,
       +            buf.get_font_dimensions().height as f32 * buf.get_buffer_height() as f32,
       +        );
       +
       +        if render_buffer_size != self.render_buffer_size {
       +            unsafe {
       +                use glow::HasContext as _;
       +                gl.bind_framebuffer(glow::FRAMEBUFFER, Some(self.framebuffer));
       +                gl.delete_texture(self.render_texture);
       +                gl.delete_texture(self.sixel_render_texture);
       +
       +                let scale_filter = glow::NEAREST as i32; /*match self.scaling {
       +                    Scaling::Nearest => glow::NEAREST as i32,
       +                    Scaling::Linear => glow::LINEAR as i32,
       +                };*/
       +                let sixel_render_texture = gl.create_texture().unwrap();
       +
       +                gl.bind_texture(glow::TEXTURE_2D, Some(sixel_render_texture));
       +                gl.tex_image_2d(
       +                    glow::TEXTURE_2D,
       +                    0,
       +                    glow::RGBA as i32,
       +                    render_buffer_size.x as i32,
       +                    render_buffer_size.y as i32,
       +                    0,
       +                    glow::RGBA,
       +                    glow::UNSIGNED_BYTE,
       +                    None,
       +                );
       +                gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, scale_filter);
       +                gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, scale_filter);
       +                gl.tex_parameter_i32(
       +                    glow::TEXTURE_2D,
       +                    glow::TEXTURE_WRAP_S,
       +                    glow::CLAMP_TO_EDGE as i32,
       +                );
       +                gl.tex_parameter_i32(
       +                    glow::TEXTURE_2D,
       +                    glow::TEXTURE_WRAP_T,
       +                    glow::CLAMP_TO_EDGE as i32,
       +                );
       +
       +                let render_texture = gl.create_texture().unwrap();
       +                gl.bind_texture(glow::TEXTURE_2D, Some(render_texture));
       +                gl.tex_image_2d(
       +                    glow::TEXTURE_2D,
       +                    0,
       +                    glow::RGBA as i32,
       +                    render_buffer_size.x as i32,
       +                    render_buffer_size.y as i32,
       +                    0,
       +                    glow::RGBA,
       +                    glow::UNSIGNED_BYTE,
       +                    None,
       +                );
       +                gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, scale_filter);
       +                gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, scale_filter);
       +                gl.tex_parameter_i32(
       +                    glow::TEXTURE_2D,
       +                    glow::TEXTURE_WRAP_S,
       +                    glow::CLAMP_TO_EDGE as i32,
       +                );
       +                gl.tex_parameter_i32(
       +                    glow::TEXTURE_2D,
       +                    glow::TEXTURE_WRAP_T,
       +                    glow::CLAMP_TO_EDGE as i32,
       +                );
       +
       +                let depth_buffer = gl.create_renderbuffer().unwrap();
       +                gl.bind_renderbuffer(glow::RENDERBUFFER, Some(depth_buffer));
       +                gl.renderbuffer_storage(
       +                    glow::RENDERBUFFER,
       +                    glow::DEPTH_COMPONENT,
       +                    render_buffer_size.x as i32,
       +                    render_buffer_size.y as i32,
       +                );
       +                gl.framebuffer_renderbuffer(
       +                    glow::FRAMEBUFFER,
       +                    glow::DEPTH_ATTACHMENT,
       +                    glow::RENDERBUFFER,
       +                    Some(depth_buffer),
       +                );
       +                gl.framebuffer_texture(
       +                    glow::FRAMEBUFFER,
       +                    glow::COLOR_ATTACHMENT0,
       +                    Some(render_texture),
       +                    0,
       +                );
       +
       +                gl.bind_framebuffer(glow::FRAMEBUFFER, None);
       +                self.render_texture = render_texture;
       +                self.sixel_render_texture = sixel_render_texture;
       +                self.render_buffer_size = render_buffer_size;
       +            }
       +        }
       +    }
       +}
       +
       +fn conv_color(c: u32, colors: u32) -> u8 {
       +    let r = ((255 * c) / colors) as u8;
       +    r
       +}
       +
       +pub fn create_palette_texture(gl: &Arc<glow::Context>, buf: &Buffer, palette_texture: NativeTexture) {
       +    let mut palette_data = Vec::new();
       +    for i in 0..buf.palette.colors.len() {
       +        let (r, g, b) = buf.palette.colors[i].get_rgb();
       +        palette_data.push(r);
       +        palette_data.push(g);
       +        palette_data.push(b);
       +        palette_data.push(0xFF);
       +    }
       +    unsafe {
       +        use glow::HasContext as _;
       +        gl.bind_texture(glow::TEXTURE_2D, Some(palette_texture));
       +        gl.tex_image_2d(
       +            glow::TEXTURE_2D,
       +            0,
       +            glow::RGB as i32,
       +            buf.palette.colors.len() as i32,
       +            1,
       +            0,
       +            glow::RGBA,
       +            glow::UNSIGNED_BYTE,
       +            Some(&palette_data),
       +        );
       +    }
       +}
       +
       +pub fn create_font_texture(gl: &Arc<glow::Context>, buf: &Buffer, font_texture: NativeTexture) {
       +    let w = buf.font_table[0].size.width as usize;
       +    let h = buf.font_table[0].size.height as usize;
       +
       +    let mut font_data = Vec::new();
       +    let chars_in_line = 16;
       +    let line_width = w * chars_in_line * 4;
       +    let height = h * 256 / chars_in_line;
       +
       +    font_data.resize(line_width * height * buf.font_table.len(), 0);
       +
       +    for i in 0..buf.font_table.len() {
       +        for ch in 0..256usize {
       +            let glyph = buf.font_table[i]
       +                .get_glyph(unsafe { char::from_u32_unchecked(ch as u32) })
       +                .unwrap();
       +
       +            let x = ch % chars_in_line;
       +            let y = ch / chars_in_line;
       +
       +            let offset = x * w * 4 + y * h * line_width + i * line_width * height;
       +            for y in 0..h {
       +                let scan_line = glyph.data[y];
       +                let mut po = offset + y * line_width;
       +
       +                for x in 0..w {
       +                    if scan_line & (128 >> x) != 0 {
       +                        // unroll
       +                        font_data[po] = 0xFF;
       +                        po += 1;
       +                        font_data[po] = 0xFF;
       +                        po += 1;
       +                        font_data[po] = 0xFF;
       +                        po += 1;
       +                        font_data[po] = 0xFF;
       +                        po += 1;
       +                    } else {
       +                        po += 4;
       +                    }
       +                }
       +            }
       +        }
       +    }
       +
       +    unsafe {
       +        use glow::HasContext as _;
       +        gl.active_texture(glow::TEXTURE0);
       +        gl.bind_texture(glow::TEXTURE_2D_ARRAY, Some(font_texture));
       +        gl.tex_image_3d(
       +            glow::TEXTURE_2D_ARRAY,
       +            0,
       +            glow::RGB as i32,
       +            line_width as i32 / 4,
       +            height as i32,
       +            buf.font_table.len() as i32,
       +            0,
       +            glow::RGBA,
       +            glow::UNSIGNED_BYTE,
       +            Some(&font_data),
       +        );
       +    }
       +}
       +
       +pub fn create_buffer_texture(
       +    gl: &Arc<glow::Context>,
       +    buf: &Buffer,
       +    scroll_back_line: i32,
       +    buffer_texture: NativeTexture,
       +) {
       +    let first_line = max(
       +        0,
       +        buf.layers[0].lines.len() as i32 - buf.get_buffer_height(),
       +    );
       +    let mut buffer_data = Vec::with_capacity(
       +        2 * buf.get_buffer_width() as usize * 4 * buf.get_real_buffer_height() as usize,
       +    );
       +    let colors = buf.palette.colors.len() as u32 - 1;
       +    let mut y = 0;
       +
       +    while y < buf.get_buffer_height() {
       +        let mut is_double_height = false;
       +
       +        for x in 0..buf.get_buffer_width() {
       +            let ch = buf
       +                .get_char_xy(x, first_line - scroll_back_line + y)
       +                .unwrap_or_default();
       +
       +            if ch.attribute.is_double_height() {
       +                is_double_height = true;
       +            }
       +            if ch.attribute.is_concealed() {
       +                buffer_data.push(' ' as u8);
       +            } else {
       +                buffer_data.push(ch.ch as u8);
       +            }
       +            if ch.attribute.is_bold() {
       +                buffer_data.push(conv_color(ch.attribute.get_foreground() + 8, colors));
       +            } else {
       +                buffer_data.push(conv_color(ch.attribute.get_foreground(), colors));
       +            }
       +            buffer_data.push(conv_color(ch.attribute.get_background(), colors));
       +            if buf.font_table.len() > 0 {
       +                buffer_data.push(
       +                    (255.0 * ch.get_font_page() as f32 / (buf.font_table.len() - 1) as f32) as u8,
       +                );
       +            } else {
       +                buffer_data.push(0);
       +            }
       +        }
       +
       +        if is_double_height {
       +            for x in 0..buf.get_buffer_width() {
       +                let ch = buf
       +                    .get_char_xy(x, first_line - scroll_back_line + y)
       +                    .unwrap_or_default();
       +
       +                if !ch.attribute.is_double_height() {
       +                    buffer_data.push(' ' as u8);
       +                } else {
       +                    buffer_data.push(ch.ch as u8);
       +                }
       +
       +                if ch.attribute.is_bold() {
       +                    buffer_data.push(conv_color(ch.attribute.get_foreground() + 8, colors));
       +                } else {
       +                    buffer_data.push(conv_color(ch.attribute.get_foreground(), colors));
       +                }
       +
       +                buffer_data.push(conv_color(ch.attribute.get_background(), colors));
       +
       +                if buf.font_table.len() > 0 {
       +                    buffer_data.push(
       +                        (255.0 * ch.get_font_page() as f32 / (buf.font_table.len() - 1) as f32)
       +                            as u8,
       +                    );
       +                } else {
       +                    buffer_data.push(0);
       +                }
       +            }
       +        }
       +
       +        if is_double_height {
       +            y += 2;
       +        } else {
       +            y += 1;
       +        }
       +    }
       +    y = 0;
       +    while y < buf.get_buffer_height() {
       +        let mut is_double_height = false;
       +
       +        for x in 0..buf.get_buffer_width() {
       +            let ch = buf
       +                .get_char_xy(x, first_line - scroll_back_line + y)
       +                .unwrap_or_default();
       +
       +            let mut attr = if ch.attribute.is_double_underlined() {
       +                3
       +            } else if ch.attribute.is_underlined() {
       +                1
       +            } else {
       +                0
       +            };
       +            if ch.attribute.is_crossed_out() {
       +                attr |= 4
       +            }
       +
       +            if ch.attribute.is_double_height() {
       +                is_double_height = true;
       +                attr |= 8
       +            }
       +
       +            buffer_data.push(attr);
       +            buffer_data.push(attr);
       +            buffer_data.push(attr);
       +            buffer_data.push(if ch.attribute.is_blinking() { 255 } else { 0 });
       +        }
       +
       +        if is_double_height {
       +            for x in 0..buf.get_buffer_width() {
       +                let ch = buf
       +                    .get_char_xy(x, first_line - scroll_back_line + y)
       +                    .unwrap_or_default();
       +                let mut attr = if ch.attribute.is_double_underlined() {
       +                    3
       +                } else if ch.attribute.is_underlined() {
       +                    1
       +                } else {
       +                    0
       +                };
       +                if ch.attribute.is_crossed_out() {
       +                    attr |= 4
       +                }
       +
       +                if ch.attribute.is_double_height() {
       +                    is_double_height = true;
       +                    attr |= 8;
       +                    attr |= 16;
       +                }
       +
       +                buffer_data.push(attr);
       +                buffer_data.push(attr);
       +                buffer_data.push(attr);
       +                buffer_data.push(if ch.attribute.is_blinking() { 255 } else { 0 });
       +            }
       +        }
       +
       +        if is_double_height {
       +            y += 2;
       +        } else {
       +            y += 1;
       +        }
       +    }
       +    unsafe {
       +        use glow::HasContext as _;
       +        gl.active_texture(glow::TEXTURE0 + 4);
       +
       +        gl.bind_texture(glow::TEXTURE_2D_ARRAY, Some(buffer_texture));
       +        gl.tex_image_3d(
       +            glow::TEXTURE_2D_ARRAY,
       +            0,
       +            glow::RGBA32F as i32,
       +            buf.get_buffer_width(),
       +            buf.get_buffer_height(),
       +            2,
       +            0,
       +            glow::RGBA,
       +            glow::UNSIGNED_BYTE,
       +            Some(&buffer_data),
       +        );
       +    }
       +}
   DIR diff --git a/src/ui/buffer_view/render.shader.frag b/src/ui/ansi_editor/render.shader.frag
   DIR diff --git a/src/ui/ansi_editor/sixel.rs b/src/ui/ansi_editor/sixel.rs
       @@ -0,0 +1,217 @@
       +use std::sync::Arc;
       +
       +use glow::{HasContext, NativeTexture};
       +use icy_engine::{Position, SixelReadStatus};
       +
       +use super::BufferView;
       +
       +pub struct SixelCacheEntry {
       +    pub status: SixelReadStatus,
       +    pub old_line: i32,
       +    pub data_opt: Option<Vec<u8>>,
       +
       +    pub pos: Position,
       +    pub size: icy_engine::Size<i32>,
       +
       +    pub texture_opt: Option<NativeTexture>,
       +}
       +
       +impl SixelCacheEntry {
       +    pub fn rect(&self) -> icy_engine::Rectangle {
       +        icy_engine::Rectangle {
       +            start: self.pos,
       +            size: self.size,
       +        }
       +    }
       +}
       +
       +impl BufferView {
       +    pub fn update_sixels(&mut self, gl: &Arc<glow::Context>) -> bool {
       +        let buffer = &self.buf;
       +        let l = buffer.layers[0].sixels.len();
       +        if l == 0 {
       +            for sx in &self.sixel_cache {
       +                if let Some(tex) = sx.texture_opt {
       +                    unsafe {
       +                        gl.delete_texture(tex);
       +                    }
       +                }
       +            }
       +            self.sixel_cache.clear();
       +        }
       +
       +        let mut res = false;
       +        let mut i = 0;
       +        while i < l {
       +            let sixel = &buffer.layers[0].sixels[i];
       +
       +            if sixel.width() == 0 || sixel.height() == 0 {
       +                i += 1;
       +                continue;
       +            }
       +
       +            let mut old_line = 0;
       +            let current_line = match sixel.read_status {
       +                SixelReadStatus::Position(_, y) => y * 6,
       +                SixelReadStatus::Error | SixelReadStatus::Finished => sixel.height() as i32,
       +                _ => 0,
       +            };
       +
       +            if let Some(entry) = self.sixel_cache.get(i) {
       +                old_line = entry.old_line;
       +                if let SixelReadStatus::Position(_, _) = sixel.read_status {
       +                    if old_line + 5 * 6 >= current_line {
       +                        i += 1;
       +                        continue;
       +                    }
       +                }
       +                if entry.status == sixel.read_status {
       +                    i += 1;
       +                    continue;
       +                }
       +            }
       +            res = true;
       +            let data_len = (sixel.height() * sixel.width() * 4) as usize;
       +            let mut removed_index = -1;
       +            let mut v = if self.sixel_cache.len() > i {
       +                let mut entry = self.sixel_cache.remove(i);
       +                // old_handle = entry.image_opt;
       +                removed_index = i as i32;
       +                if let Some(ptr) = &mut entry.data_opt {
       +                    if ptr.len() < data_len {
       +                        ptr.resize(data_len, 0);
       +                    }
       +                    entry.data_opt.take().unwrap()
       +                } else {
       +                    let mut data = Vec::with_capacity(data_len);
       +                    data.resize(data_len, 0);
       +                    data
       +                }
       +            } else {
       +                let mut data = Vec::with_capacity(data_len);
       +                data.resize(data_len, 0);
       +                data
       +            };
       +
       +            let mut i = old_line as usize * sixel.width() as usize * 4;
       +
       +            for y in old_line..current_line {
       +                for x in 0..sixel.width() {
       +                    let column = &sixel.picture[x as usize];
       +                    let data = if let Some(col) = column.get(y as usize) {
       +                        if let Some(col) = col {
       +                            let (r, g, b) = col.get_rgb();
       +                            [r, g, b, 0xFF]
       +                        } else {
       +                            // todo: bg color may differ here
       +                            [0, 0, 0, 0xFF]
       +                        }
       +                    } else {
       +                        [0, 0, 0, 0xFF]
       +                    };
       +                    if i >= v.len() {
       +                        v.extend_from_slice(&data);
       +                    } else {
       +                        v[i] = data[0];
       +                        v[i + 1] = data[1];
       +                        v[i + 2] = data[2];
       +                        v[i + 3] = data[3];
       +                    }
       +                    i += 4;
       +                }
       +            }
       +            let (texture_opt, data_opt, clear) = match sixel.read_status {
       +                SixelReadStatus::Finished | SixelReadStatus::Error => unsafe {
       +                    let texture = gl.create_texture().unwrap();
       +                    gl.active_texture(glow::TEXTURE0 + 6);
       +                    gl.bind_texture(glow::TEXTURE_2D, Some(texture));
       +                    gl.tex_parameter_i32(
       +                        glow::TEXTURE_2D,
       +                        glow::TEXTURE_MIN_FILTER,
       +                        glow::NEAREST as i32,
       +                    );
       +                    gl.tex_parameter_i32(
       +                        glow::TEXTURE_2D,
       +                        glow::TEXTURE_MAG_FILTER,
       +                        glow::NEAREST as i32,
       +                    );
       +                    gl.tex_parameter_i32(
       +                        glow::TEXTURE_2D,
       +                        glow::TEXTURE_WRAP_S,
       +                        glow::CLAMP_TO_EDGE as i32,
       +                    );
       +                    gl.tex_parameter_i32(
       +                        glow::TEXTURE_2D,
       +                        glow::TEXTURE_WRAP_T,
       +                        glow::CLAMP_TO_EDGE as i32,
       +                    );
       +                    gl.tex_image_2d(
       +                        glow::TEXTURE_2D,
       +                        0,
       +                        glow::RGB as i32,
       +                        sixel.width() as i32,
       +                        sixel.height() as i32,
       +                        0,
       +                        glow::RGBA,
       +                        glow::UNSIGNED_BYTE,
       +                        Some(&v),
       +                    );
       +                    (Some(texture), None, true)
       +                },
       +                _ => (None, Some(v), false),
       +            };
       +
       +            let new_entry = SixelCacheEntry {
       +                status: sixel.read_status,
       +                old_line: current_line,
       +                data_opt,
       +                pos: sixel.position,
       +                size: icy_engine::Size {
       +                    width: sixel.width() as i32,
       +                    height: sixel.height() as i32,
       +                },
       +                texture_opt,
       +            };
       +
       +            if removed_index < 0 {
       +                self.sixel_cache.push(new_entry);
       +                if clear {
       +                    self.clear_invisible_sixel_cache(gl, self.sixel_cache.len() - 1);
       +                    break;
       +                }
       +            } else {
       +                self.sixel_cache.insert(removed_index as usize, new_entry);
       +                if clear {
       +                    self.clear_invisible_sixel_cache(gl, removed_index as usize);
       +                    break;
       +                }
       +            }
       +        }
       +        res
       +    }
       +
       +    pub fn clear_invisible_sixel_cache(&mut self, gl: &Arc<glow::Context>, j: usize) {
       +        // remove cache entries that are removed by the engine
       +        if j > 0 {
       +            let cur_rect = self.sixel_cache[j].rect();
       +            let mut i = j - 1;
       +            loop {
       +                let other_rect = self.sixel_cache[i].rect();
       +                if cur_rect.contains(other_rect) {
       +                    let t = self.sixel_cache.remove(i);
       +                    if let Some(texture) = &t.texture_opt {
       +                        unsafe {
       +                            gl.delete_texture(*texture);
       +                        }
       +                    }
       +
       +                    self.buf.layers[0].sixels.remove(i);
       +                }
       +                if i == 0 {
       +                    break;
       +                }
       +                i -= 1;
       +            }
       +        }
       +    }
       +}
   DIR diff --git a/src/ui/buffer_view/sixel.shader.frag b/src/ui/ansi_editor/sixel.shader.frag
   DIR diff --git a/src/ui/buffer_view/mod.rs b/src/ui/buffer_view/mod.rs
       @@ -1,585 +0,0 @@
       -use std::cmp::{max, min};
       -use eframe::epaint::Vec2;
       -use glow::NativeTexture;
       -
       -use icy_engine::{Buffer, BufferParser, CallbackAction, Caret, EngineResult, Position};
       -
       -pub mod render;
       -pub use render::*;
       -
       -pub mod sixel;
       -pub use sixel::*;
       -
       -pub mod selection;
       -pub use selection::*;
       -
       -pub struct Blink {
       -    is_on: bool,
       -    last_blink: u128,
       -    blink_rate: u128,
       -}
       -
       -impl Blink {
       -    pub fn new(blink_rate: u128) -> Self {
       -        Self {
       -            is_on: false,
       -            last_blink: 0,
       -            blink_rate,
       -        }
       -    }
       -
       -    pub fn is_on(&self) -> bool {
       -        self.is_on
       -    }
       -
       -    pub fn update(&mut self, cur_ms: u128) -> bool {
       -        if cur_ms - self.last_blink > self.blink_rate {
       -            self.is_on = !self.is_on;
       -            self.last_blink = cur_ms;
       -            true
       -        } else {
       -            false
       -        }
       -    }
       -}
       -
       -pub struct BufferView {
       -    pub buf: Buffer,
       -    sixel_cache: Vec<SixelCacheEntry>,
       -    pub caret: Caret,
       -
       -    pub caret_blink: Blink,
       -    pub character_blink: Blink,
       -
       -    pub scale: f32,
       -    pub scroll_back_line: i32,
       -
       -    pub button_pressed: bool,
       -
       -    pub selection_opt: Option<Selection>,
       -
       -    program: glow::Program,
       -    vertex_array: glow::VertexArray,
       -
       -    redraw_view: bool,
       -
       -    redraw_palette: bool,
       -    colors: usize,
       -
       -    redraw_font: bool,
       -    fonts: usize,
       -    //scaling: Scaling,
       -    // post_processing: PostProcessing,
       -
       -    font_texture: NativeTexture,
       -    buffer_texture: NativeTexture,
       -    palette_texture: NativeTexture,
       -    framebuffer: glow::NativeFramebuffer,
       -    render_texture: NativeTexture,
       -    render_buffer_size: Vec2,
       -    draw_program: glow::NativeProgram,
       -    sixel_shader: glow::NativeProgram,
       -    sixel_render_texture: NativeTexture,
       -}
       -
       -impl BufferView {
       -    pub fn new(gl: &glow::Context) -> Self {
       -        let mut buf = Buffer::create(80, 25);
       -        buf.layers[0].is_transparent = false;
       -        buf.is_terminal_buffer = true;
       -
       -        use glow::HasContext as _;
       -
       -        unsafe {
       -            let sixel_shader = gl.create_program().expect("Cannot create program");
       -            let (vertex_shader_source, fragment_shader_source) = (
       -                r#"#version 330
       -const float low  =  -1.0;
       -const float high = 1.0;
       -
       -const vec2 verts[6] = vec2[6](
       -    vec2(low, high),
       -    vec2(high, high),
       -    vec2(high, low),
       -
       -    vec2(low, high),
       -    vec2(low, low),
       -    vec2(high, low)
       -);
       -
       -void main() {
       -    vec2 vert = verts[gl_VertexID];
       -    gl_Position = vec4(vert, 0.3, 1.0);
       -}
       -"#,
       -                include_str!("sixel.shader.frag"),
       -            );
       -            let shader_sources = [
       -                (glow::VERTEX_SHADER, vertex_shader_source),
       -                (glow::FRAGMENT_SHADER, fragment_shader_source),
       -            ];
       -
       -            let shaders: Vec<_> = shader_sources
       -                .iter()
       -                .map(|(shader_type, shader_source)| {
       -                    let shader = gl
       -                        .create_shader(*shader_type)
       -                        .expect("Cannot create shader");
       -                    gl.shader_source(
       -                        shader,
       -                        shader_source, /*&format!("{}\n{}", shader_version, shader_source)*/
       -                    );
       -                    gl.compile_shader(shader);
       -                    if !gl.get_shader_compile_status(shader) {
       -                        panic!("{}", gl.get_shader_info_log(shader));
       -                    }
       -                    gl.attach_shader(sixel_shader, shader);
       -                    shader
       -                })
       -                .collect();
       -
       -            gl.link_program(sixel_shader);
       -            if !gl.get_program_link_status(sixel_shader) {
       -                panic!("{}", gl.get_program_info_log(sixel_shader));
       -            }
       -
       -            for shader in shaders {
       -                gl.detach_shader(sixel_shader, shader);
       -                gl.delete_shader(shader);
       -            }
       -
       -            let draw_program = gl.create_program().expect("Cannot create program");
       -            let (vertex_shader_source, fragment_shader_source) = (
       -                r#"#version 330
       -    const float low  =  -1.0;
       -    const float high = 1.0;
       -    
       -    const vec2 verts[6] = vec2[6](
       -        vec2(low, high),
       -        vec2(high, high),
       -        vec2(high, low),
       -    
       -        vec2(low, high),
       -        vec2(low, low),
       -        vec2(high, low)
       -    );
       -    
       -    void main() {
       -        vec2 vert = verts[gl_VertexID];
       -        gl_Position = vec4(vert, 0.3, 1.0);
       -    }
       -    "#,
       -                include_str!("render.shader.frag"),
       -            );
       -            let shader_sources = [
       -                (glow::VERTEX_SHADER, vertex_shader_source),
       -                (glow::FRAGMENT_SHADER, fragment_shader_source),
       -            ];
       -
       -            let shaders: Vec<_> = shader_sources
       -                .iter()
       -                .map(|(shader_type, shader_source)| {
       -                    let shader = gl
       -                        .create_shader(*shader_type)
       -                        .expect("Cannot create shader");
       -                    gl.shader_source(
       -                        shader,
       -                        shader_source, /*&format!("{}\n{}", shader_version, shader_source)*/
       -                    );
       -                    gl.compile_shader(shader);
       -                    if !gl.get_shader_compile_status(shader) {
       -                        panic!("{}", gl.get_shader_info_log(shader));
       -                    }
       -                    gl.attach_shader(draw_program, shader);
       -                    shader
       -                })
       -                .collect();
       -
       -            gl.link_program(draw_program);
       -            if !gl.get_program_link_status(draw_program) {
       -                panic!("{}", gl.get_program_info_log(draw_program));
       -            }
       -
       -            for shader in shaders {
       -                gl.detach_shader(draw_program, shader);
       -                gl.delete_shader(shader);
       -            }
       -
       -            let program = gl.create_program().expect("Cannot create program");
       -
       -            let (vertex_shader_source, fragment_shader_source) = (
       -                r#"#version 330
       -const float low  =  -1.0;
       -const float high = 1.0;
       -
       -const vec2 verts[6] = vec2[6](
       -    vec2(low, high),
       -    vec2(high, high),
       -    vec2(high, low),
       -
       -    vec2(low, high),
       -    vec2(low, low),
       -    vec2(high, low)
       -);
       -
       -void main() {
       -    vec2 vert = verts[gl_VertexID];
       -    gl_Position = vec4(vert, 0.3, 1.0);
       -}
       -"#,
       -                include_str!("buffer_view.shader.frag"),
       -            );
       -            let shader_sources = [
       -                (glow::VERTEX_SHADER, vertex_shader_source),
       -                (glow::FRAGMENT_SHADER, fragment_shader_source),
       -            ];
       -
       -            let shaders: Vec<_> = shader_sources
       -                .iter()
       -                .map(|(shader_type, shader_source)| {
       -                    let shader = gl
       -                        .create_shader(*shader_type)
       -                        .expect("Cannot create shader");
       -                    gl.shader_source(
       -                        shader,
       -                        shader_source, /*&format!("{}\n{}", shader_version, shader_source)*/
       -                    );
       -                    gl.compile_shader(shader);
       -                    if !gl.get_shader_compile_status(shader) {
       -                        panic!("{}", gl.get_shader_info_log(shader));
       -                    }
       -                    gl.attach_shader(program, shader);
       -                    shader
       -                })
       -                .collect();
       -
       -            gl.link_program(program);
       -            if !gl.get_program_link_status(program) {
       -                panic!("{}", gl.get_program_info_log(program));
       -            }
       -
       -            for shader in shaders {
       -                gl.detach_shader(program, shader);
       -                gl.delete_shader(shader);
       -            }
       -
       -            let vertex_array = gl
       -                .create_vertex_array()
       -                .expect("Cannot create vertex array");
       -            let buffer_texture = gl.create_texture().unwrap();
       -            create_buffer_texture(gl, &buf, 0, buffer_texture);
       -            gl.tex_parameter_i32(
       -                glow::TEXTURE_2D_ARRAY,
       -                glow::TEXTURE_MIN_FILTER,
       -                glow::NEAREST as i32,
       -            );
       -            gl.tex_parameter_i32(
       -                glow::TEXTURE_2D_ARRAY,
       -                glow::TEXTURE_MAG_FILTER,
       -                glow::NEAREST as i32,
       -            );
       -            gl.tex_parameter_i32(
       -                glow::TEXTURE_2D_ARRAY,
       -                glow::TEXTURE_WRAP_S,
       -                glow::CLAMP_TO_EDGE as i32,
       -            );
       -            gl.tex_parameter_i32(
       -                glow::TEXTURE_2D_ARRAY,
       -                glow::TEXTURE_WRAP_T,
       -                glow::CLAMP_TO_EDGE as i32,
       -            );
       -
       -            let palette_texture = gl.create_texture().unwrap();
       -            create_palette_texture(gl, &buf, palette_texture);
       -            gl.tex_parameter_i32(
       -                glow::TEXTURE_2D,
       -                glow::TEXTURE_MIN_FILTER,
       -                glow::NEAREST as i32,
       -            );
       -            gl.tex_parameter_i32(
       -                glow::TEXTURE_2D,
       -                glow::TEXTURE_MAG_FILTER,
       -                glow::NEAREST as i32,
       -            );
       -            gl.tex_parameter_i32(
       -                glow::TEXTURE_2D,
       -                glow::TEXTURE_WRAP_S,
       -                glow::CLAMP_TO_EDGE as i32,
       -            );
       -            gl.tex_parameter_i32(
       -                glow::TEXTURE_2D,
       -                glow::TEXTURE_WRAP_T,
       -                glow::CLAMP_TO_EDGE as i32,
       -            );
       -
       -            let font_texture = gl.create_texture().unwrap();
       -            create_font_texture(gl, &buf, font_texture);
       -            gl.tex_parameter_i32(
       -                glow::TEXTURE_2D_ARRAY,
       -                glow::TEXTURE_MIN_FILTER,
       -                glow::NEAREST as i32,
       -            );
       -            gl.tex_parameter_i32(
       -                glow::TEXTURE_2D_ARRAY,
       -                glow::TEXTURE_MAG_FILTER,
       -                glow::NEAREST as i32,
       -            );
       -            gl.tex_parameter_i32(
       -                glow::TEXTURE_2D_ARRAY,
       -                glow::TEXTURE_WRAP_S,
       -                glow::CLAMP_TO_EDGE as i32,
       -            );
       -            gl.tex_parameter_i32(
       -                glow::TEXTURE_2D_ARRAY,
       -                glow::TEXTURE_WRAP_T,
       -                glow::CLAMP_TO_EDGE as i32,
       -            );
       -            let colors = buf.palette.colors.len();
       -            let fonts = buf.font_table.len();
       -            let framebuffer = gl.create_framebuffer().unwrap();
       -
       -            gl.bind_framebuffer(glow::FRAMEBUFFER, Some(framebuffer));
       -            let render_texture = gl.create_texture().unwrap();
       -            let render_buffer_size = Vec2::new(
       -                buf.get_font_dimensions().width as f32 * buf.get_buffer_width() as f32,
       -                buf.get_font_dimensions().height as f32 * buf.get_buffer_height() as f32,
       -            );
       -
       -            let filter = glow::NEAREST as i32; /*match options.scaling {
       -                Scaling::Nearest => glow::NEAREST as i32,
       -                Scaling::Linear => glow::LINEAR as i32,
       -            };*/
       -            gl.bind_texture(glow::TEXTURE_2D, Some(render_texture));
       -            gl.tex_image_2d(
       -                glow::TEXTURE_2D,
       -                0,
       -                glow::RGBA as i32,
       -                render_buffer_size.x as i32,
       -                render_buffer_size.y as i32,
       -                0,
       -                glow::RGBA,
       -                glow::UNSIGNED_BYTE,
       -                None,
       -            );
       -            gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, filter);
       -            gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, filter);
       -            gl.tex_parameter_i32(
       -                glow::TEXTURE_2D,
       -                glow::TEXTURE_WRAP_S,
       -                glow::CLAMP_TO_EDGE as i32,
       -            );
       -            gl.tex_parameter_i32(
       -                glow::TEXTURE_2D,
       -                glow::TEXTURE_WRAP_T,
       -                glow::CLAMP_TO_EDGE as i32,
       -            );
       -
       -            let depth_buffer = gl.create_renderbuffer().unwrap();
       -            gl.bind_renderbuffer(glow::RENDERBUFFER, Some(depth_buffer));
       -            gl.renderbuffer_storage(
       -                glow::RENDERBUFFER,
       -                glow::DEPTH_COMPONENT,
       -                render_buffer_size.x as i32,
       -                render_buffer_size.y as i32,
       -            );
       -            gl.framebuffer_renderbuffer(
       -                glow::FRAMEBUFFER,
       -                glow::DEPTH_ATTACHMENT,
       -                glow::RENDERBUFFER,
       -                Some(depth_buffer),
       -            );
       -            gl.framebuffer_texture(
       -                glow::FRAMEBUFFER,
       -                glow::COLOR_ATTACHMENT0,
       -                Some(render_texture),
       -                0,
       -            );
       -
       -            gl.bind_framebuffer(glow::FRAMEBUFFER, None);
       -
       -            let sixel_render_texture = gl.create_texture().unwrap();
       -            let render_buffer_size = Vec2::new(
       -                buf.get_font_dimensions().width as f32 * buf.get_buffer_width() as f32,
       -                buf.get_font_dimensions().height as f32 * buf.get_buffer_height() as f32,
       -            );
       -
       -            gl.bind_texture(glow::TEXTURE_2D, Some(sixel_render_texture));
       -            gl.tex_image_2d(
       -                glow::TEXTURE_2D,
       -                0,
       -                glow::RGBA as i32,
       -                render_buffer_size.x as i32,
       -                render_buffer_size.y as i32,
       -                0,
       -                glow::RGBA,
       -                glow::UNSIGNED_BYTE,
       -                None,
       -            );
       -            gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, filter);
       -            gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, filter);
       -            gl.tex_parameter_i32(
       -                glow::TEXTURE_2D,
       -                glow::TEXTURE_WRAP_S,
       -                glow::CLAMP_TO_EDGE as i32,
       -            );
       -            gl.tex_parameter_i32(
       -                glow::TEXTURE_2D,
       -                glow::TEXTURE_WRAP_T,
       -                glow::CLAMP_TO_EDGE as i32,
       -            );
       -
       -            let depth_buffer = gl.create_renderbuffer().unwrap();
       -            gl.bind_renderbuffer(glow::RENDERBUFFER, Some(depth_buffer));
       -            gl.renderbuffer_storage(
       -                glow::RENDERBUFFER,
       -                glow::DEPTH_COMPONENT,
       -                render_buffer_size.x as i32,
       -                render_buffer_size.y as i32,
       -            );
       -            gl.framebuffer_renderbuffer(
       -                glow::FRAMEBUFFER,
       -                glow::DEPTH_ATTACHMENT,
       -                glow::RENDERBUFFER,
       -                Some(depth_buffer),
       -            );
       -            gl.framebuffer_texture(
       -                glow::FRAMEBUFFER,
       -                glow::COLOR_ATTACHMENT0,
       -                Some(sixel_render_texture),
       -                0,
       -            );
       -
       -            gl.bind_framebuffer(glow::FRAMEBUFFER, None);
       -
       -            Self {
       -                buf,
       -                caret: Caret::default(),
       -                caret_blink: Blink::new((1000.0 / 1.875) as u128 / 2),
       -                character_blink: Blink::new((1000.0 / 1.8) as u128),
       -                scale: 1.0,
       -                sixel_cache: Vec::new(),
       -                button_pressed: false,
       -                redraw_view: false,
       -                redraw_palette: false,
       -                redraw_font: false,
       -                scroll_back_line: 0,
       -                selection_opt: None,
       -                colors,
       -                fonts,
       -                program,
       -                draw_program,
       -                vertex_array,
       -                font_texture,
       -                buffer_texture,
       -                palette_texture,
       -                // scaling: options.scaling,
       -                // post_processing: options.post_processing,
       -
       -                framebuffer,
       -                render_texture,
       -                render_buffer_size,
       -
       -                sixel_shader,
       -                sixel_render_texture,
       -            }
       -        }
       -    }
       -
       -    pub fn clear(&mut self) {
       -        self.caret.ff(&mut self.buf);
       -    }
       -
       -    pub fn get_copy_text(&mut self, buffer_parser: &Box<dyn BufferParser>) -> Option<String> {
       -        let Some(selection) = &self.selection_opt else {
       -            return None;
       -        };
       -
       -        let mut res = String::new();
       -        if selection.block_selection {
       -            let start = Position::new(
       -                min(selection.anchor_pos.x, selection.lead_pos.x),
       -                min(selection.anchor_pos.y, selection.lead_pos.y),
       -            );
       -            let end = Position::new(
       -                max(selection.anchor_pos.x, selection.lead_pos.x),
       -                max(selection.anchor_pos.y, selection.lead_pos.y),
       -            );
       -            for y in start.y..=end.y {
       -                for x in start.x..end.x {
       -                    let ch = self.buf.get_char(Position::new(x, y)).unwrap();
       -                    res.push(buffer_parser.to_unicode(ch.ch));
       -                }
       -                res.push('\n');
       -            }
       -        } else {
       -            let (start, end) = if selection.anchor_pos < selection.lead_pos {
       -                (selection.anchor_pos, selection.lead_pos)
       -            } else {
       -                (selection.lead_pos, selection.anchor_pos)
       -            };
       -            if start.y != end.y {
       -                for x in start.x..self.buf.get_line_length(start.y) {
       -                    let ch = self.buf.get_char(Position::new(x, start.y)).unwrap();
       -                    res.push(buffer_parser.to_unicode(ch.ch));
       -                }
       -                res.push('\n');
       -                for y in start.y + 1..end.y {
       -                    for x in 0..self.buf.get_line_length(y) {
       -                        let ch = self.buf.get_char(Position::new(x, y)).unwrap();
       -                        res.push(buffer_parser.to_unicode(ch.ch));
       -                    }
       -                    res.push('\n');
       -                }
       -                for x in 0..=end.x {
       -                    let ch = self.buf.get_char(Position::new(x, end.y)).unwrap();
       -                    res.push(buffer_parser.to_unicode(ch.ch));
       -                }
       -            } else {
       -                for x in start.x..=end.x {
       -                    let ch = self.buf.get_char(Position::new(x, start.y)).unwrap();
       -                    res.push(buffer_parser.to_unicode(ch.ch));
       -                }
       -            }
       -        }
       -        self.selection_opt = None;
       -        Some(res)
       -    }
       -
       -    pub fn redraw_view(&mut self) {
       -        self.redraw_view = true;
       -    }
       -
       -    pub fn redraw_palette(&mut self) {
       -        self.redraw_palette = true;
       -    }
       -
       -    pub fn redraw_font(&mut self) {
       -        self.redraw_font = true;
       -    }
       -
       -    pub fn print_char(
       -        &mut self,
       -        parser: &mut Box<dyn BufferParser>,
       -        c: char,
       -    ) -> EngineResult<CallbackAction> {
       -        let res = parser.print_char(&mut self.buf, &mut self.caret, c);
       -        self.redraw_view();
       -        res
       -    }
       -
       -    pub fn destroy(&self, gl: &glow::Context) {
       -        use glow::HasContext as _;
       -        unsafe {
       -            gl.delete_program(self.program);
       -            gl.delete_vertex_array(self.vertex_array);
       -        }
       -    }
       -/*
       -    pub fn set_scaling(&mut self, scaling: Scaling) {
       -        self.scaling = scaling;
       -        self.render_buffer_size = Vec2::new(0., 0.);
       -    }
       -    pub fn set_post_processing(&mut self, post_processing: PostProcessing) {
       -        self.post_processing = post_processing;
       -    }*/
       -}
   DIR diff --git a/src/ui/buffer_view/render.rs b/src/ui/buffer_view/render.rs
       @@ -1,676 +0,0 @@
       -use eframe::epaint::{Rect, Vec2};
       -use glow::NativeTexture;
       -use icy_engine::Buffer;
       -use std::{
       -    cmp::max,
       -    time::{SystemTime, UNIX_EPOCH},
       -};
       -
       -use super::BufferView;
       -
       -impl BufferView {
       -    pub fn paint(&self, gl: &glow::Context, rect: Rect) {
       -        use glow::HasContext as _;
       -        unsafe {
       -            gl.bind_framebuffer(glow::FRAMEBUFFER, Some(self.framebuffer));
       -            gl.framebuffer_texture(
       -                glow::FRAMEBUFFER,
       -                glow::COLOR_ATTACHMENT0,
       -                Some(self.render_texture),
       -                0,
       -            );
       -            gl.bind_texture(glow::TEXTURE_2D, Some(self.render_texture));
       -            gl.viewport(
       -                0,
       -                0,
       -                self.render_buffer_size.x as i32,
       -                self.render_buffer_size.y as i32,
       -            );
       -            gl.clear(glow::COLOR_BUFFER_BIT | glow::DEPTH_BUFFER_BIT);
       -            gl.clear_color(0., 0., 0., 1.0);
       -
       -            gl.use_program(Some(self.program));
       -            gl.uniform_2_f32(
       -                gl.get_uniform_location(self.program, "u_resolution")
       -                    .as_ref(),
       -                self.render_buffer_size.x,
       -                self.render_buffer_size.y,
       -            );
       -            gl.uniform_2_f32(
       -                gl.get_uniform_location(self.program, "u_position").as_ref(),
       -                0.0,
       -                0.0,
       -            );
       -
       -            let sbl = (self.buf.get_first_visible_line() - self.scroll_back_line) as f32;
       -            gl.uniform_4_f32(
       -                gl.get_uniform_location(self.program, "u_caret_position")
       -                    .as_ref(),
       -                self.caret.get_position().x as f32,
       -                self.caret.get_position().y as f32 - sbl,
       -                if self.caret_blink.is_on() && self.caret.is_visible {
       -                    1.0
       -                } else {
       -                    0.0
       -                },
       -                if self.caret.insert_mode { 1.0 } else { 0.0 }, // shape
       -            );
       -
       -            gl.uniform_1_f32(
       -                gl.get_uniform_location(self.program, "u_blink").as_ref(),
       -                if self.character_blink.is_on() {
       -                    1.0
       -                } else {
       -                    0.0
       -                },
       -            );
       -
       -            gl.uniform_2_f32(
       -                gl.get_uniform_location(self.program, "u_terminal_size")
       -                    .as_ref(),
       -                self.buf.get_buffer_width() as f32 - 0.0001,
       -                self.buf.get_buffer_height() as f32 - 0.0001,
       -            );
       -
       -            gl.uniform_1_i32(gl.get_uniform_location(self.program, "u_fonts").as_ref(), 0);
       -            gl.uniform_1_i32(
       -                gl.get_uniform_location(self.program, "u_palette").as_ref(),
       -                2,
       -            );
       -            gl.uniform_1_i32(
       -                gl.get_uniform_location(self.program, "u_buffer").as_ref(),
       -                4,
       -            );
       -
       -            match &self.selection_opt {
       -                Some(sel) => {
       -                    if sel.is_empty() {
       -                        gl.uniform_4_f32(
       -                            gl.get_uniform_location(self.program, "u_selection")
       -                                .as_ref(),
       -                            0.0,
       -                            0.0,
       -                            0.0,
       -                            0.0,
       -                        );
       -                        gl.uniform_1_f32(
       -                            gl.get_uniform_location(self.program, "u_selection_attr")
       -                                .as_ref(),
       -                            -1.0,
       -                        );
       -                    } else {
       -                        if sel.anchor.y.floor() < sel.lead.y.floor()
       -                            || sel.anchor.y.floor() == sel.lead.y.floor()
       -                                && sel.anchor.x < sel.lead.x
       -                        {
       -                            gl.uniform_4_f32(
       -                                gl.get_uniform_location(self.program, "u_selection")
       -                                    .as_ref(),
       -                                sel.anchor.x.floor(),
       -                                sel.anchor.y.floor() - sbl,
       -                                sel.lead.x.floor(),
       -                                sel.lead.y.floor() - sbl,
       -                            );
       -                        } else {
       -                            gl.uniform_4_f32(
       -                                gl.get_uniform_location(self.program, "u_selection")
       -                                    .as_ref(),
       -                                sel.lead.x.floor(),
       -                                sel.lead.y.floor() - sbl,
       -                                sel.anchor.x.floor(),
       -                                sel.anchor.y.floor() - sbl,
       -                            );
       -                        }
       -                        if sel.block_selection {
       -                            gl.uniform_1_f32(
       -                                gl.get_uniform_location(self.program, "u_selection_attr")
       -                                    .as_ref(),
       -                                1.0,
       -                            );
       -                        } else {
       -                            gl.uniform_1_f32(
       -                                gl.get_uniform_location(self.program, "u_selection_attr")
       -                                    .as_ref(),
       -                                0.0,
       -                            );
       -                        }
       -                    }
       -                }
       -                None => {
       -                    gl.uniform_4_f32(
       -                        gl.get_uniform_location(self.program, "u_selection")
       -                            .as_ref(),
       -                        0.0,
       -                        0.0,
       -                        0.0,
       -                        0.0,
       -                    );
       -                    gl.uniform_1_f32(
       -                        gl.get_uniform_location(self.program, "u_selection_attr")
       -                            .as_ref(),
       -                        -1.0,
       -                    );
       -                }
       -            }
       -
       -            gl.active_texture(glow::TEXTURE0);
       -            gl.bind_texture(glow::TEXTURE_2D, Some(self.font_texture));
       -            gl.active_texture(glow::TEXTURE0 + 2);
       -            gl.bind_texture(glow::TEXTURE_2D, Some(self.palette_texture));
       -            gl.active_texture(glow::TEXTURE0 + 4);
       -            gl.bind_texture(glow::TEXTURE_2D, Some(self.buffer_texture));
       -
       -            gl.bind_vertex_array(Some(self.vertex_array));
       -            gl.draw_arrays(glow::TRIANGLES, 0, 3);
       -            gl.draw_arrays(glow::TRIANGLES, 3, 3);
       -
       -            // draw sixels
       -            let mut render_texture = self.render_texture;
       -            let mut sixel_render_texture = self.sixel_render_texture;
       -
       -            for sixel in &self.sixel_cache {
       -                if let Some(sixel_tex) = sixel.texture_opt {
       -                    gl.bind_framebuffer(glow::FRAMEBUFFER, Some(self.framebuffer));
       -                    gl.framebuffer_texture(
       -                        glow::FRAMEBUFFER,
       -                        glow::COLOR_ATTACHMENT0,
       -                        Some(sixel_render_texture),
       -                        0,
       -                    );
       -                    gl.bind_texture(glow::TEXTURE_2D, Some(sixel_render_texture));
       -
       -                    gl.viewport(
       -                        0,
       -                        0,
       -                        self.render_buffer_size.x as i32,
       -                        self.render_buffer_size.y as i32,
       -                    );
       -
       -                    gl.use_program(Some(self.sixel_shader));
       -                    gl.uniform_1_i32(
       -                        gl.get_uniform_location(self.sixel_shader, "u_render_texture")
       -                            .as_ref(),
       -                        4,
       -                    );
       -                    gl.uniform_1_i32(
       -                        gl.get_uniform_location(self.sixel_shader, "u_sixel")
       -                            .as_ref(),
       -                        2,
       -                    );
       -
       -                    gl.active_texture(glow::TEXTURE0 + 4);
       -                    gl.bind_texture(glow::TEXTURE_2D, Some(render_texture));
       -
       -                    gl.active_texture(glow::TEXTURE0 + 2);
       -                    gl.bind_texture(glow::TEXTURE_2D, Some(sixel_tex));
       -
       -                    gl.uniform_2_f32(
       -                        gl.get_uniform_location(self.sixel_shader, "u_resolution")
       -                            .as_ref(),
       -                        self.render_buffer_size.x,
       -                        self.render_buffer_size.y,
       -                    );
       -
       -                    let x = sixel.pos.x as f32 * self.buf.get_font_dimensions().width as f32;
       -                    let y = sixel.pos.y as f32 * self.buf.get_font_dimensions().height as f32;
       -
       -                    let w = sixel.size.width as f32;
       -                    let h = sixel.size.height as f32;
       -
       -                    gl.uniform_4_f32(
       -                        gl.get_uniform_location(self.sixel_shader, "u_sixel_rectangle")
       -                            .as_ref(),
       -                        x,
       -                        y,
       -                        x + w,
       -                        y + h,
       -                    );
       -
       -                    gl.bind_vertex_array(Some(self.vertex_array));
       -                    gl.draw_arrays(glow::TRIANGLES, 0, 3);
       -                    gl.draw_arrays(glow::TRIANGLES, 3, 3);
       -
       -                    let tmp = render_texture;
       -                    render_texture = sixel_render_texture;
       -                    sixel_render_texture = tmp;
       -                }
       -            }
       -
       -            // draw Framebuffer
       -            gl.bind_framebuffer(glow::FRAMEBUFFER, None);
       -            gl.clear(glow::COLOR_BUFFER_BIT | glow::DEPTH_BUFFER_BIT);
       -            gl.viewport(
       -                rect.left() as i32,
       -                rect.top() as i32,
       -                rect.width() as i32,
       -                rect.height() as i32,
       -            );
       -            gl.use_program(Some(self.draw_program));
       -            gl.active_texture(glow::TEXTURE0);
       -            gl.uniform_1_i32(
       -                gl.get_uniform_location(self.draw_program, "u_render_texture")
       -                    .as_ref(),
       -                0,
       -            );
       -            gl.bind_texture(glow::TEXTURE_2D, Some(render_texture));
       -            gl.uniform_1_f32(
       -                gl.get_uniform_location(self.draw_program, "u_effect")
       -                    .as_ref(),
       -                    0.0
       -                    /* 
       -                match self.post_processing {
       -                    crate::ui::main_window::PostProcessing::None => 0.0,
       -                    crate::ui::main_window::PostProcessing::CRT1 => 1.0,
       -                },*/
       -            );
       -
       -            gl.uniform_2_f32(
       -                gl.get_uniform_location(self.draw_program, "u_resolution")
       -                    .as_ref(),
       -                rect.width(),
       -                rect.height(),
       -            );
       -            gl.uniform_2_f32(
       -                gl.get_uniform_location(self.draw_program, "u_position")
       -                    .as_ref(),
       -                rect.left(),
       -                rect.top(),
       -            );
       -
       -            gl.bind_vertex_array(Some(self.vertex_array));
       -            gl.draw_arrays(glow::TRIANGLES, 0, 3);
       -            gl.draw_arrays(glow::TRIANGLES, 3, 3);
       -        }
       -    }
       -
       -    pub fn update_buffer(&mut self, gl: &glow::Context) {
       -        self.update_sixels(gl);
       -
       -        let start = SystemTime::now();
       -        let since_the_epoch = start
       -            .duration_since(UNIX_EPOCH)
       -            .expect("Time went backwards");
       -        let cur_ms = since_the_epoch.as_millis();
       -        if self.caret_blink.update(cur_ms) || self.character_blink.update(cur_ms) {
       -            self.redraw_view = true;
       -        }
       -
       -        if self.redraw_view {
       -            self.redraw_view = false;
       -            create_buffer_texture(gl, &self.buf, self.scroll_back_line, self.buffer_texture);
       -        }
       -
       -        if self.redraw_palette || self.colors != self.buf.palette.colors.len() {
       -            self.redraw_palette = false;
       -            create_palette_texture(gl, &self.buf, self.palette_texture);
       -            self.colors = self.buf.palette.colors.len();
       -        }
       -
       -        if self.redraw_font || self.fonts != self.buf.font_table.len() {
       -            self.redraw_font = false;
       -            create_font_texture(gl, &self.buf, self.palette_texture);
       -            self.fonts = self.buf.font_table.len();
       -        }
       -        let buf = &self.buf;
       -        let render_buffer_size = Vec2::new(
       -            buf.get_font_dimensions().width as f32 * buf.get_buffer_width() as f32,
       -            buf.get_font_dimensions().height as f32 * buf.get_buffer_height() as f32,
       -        );
       -
       -        if render_buffer_size != self.render_buffer_size {
       -            unsafe {
       -                use glow::HasContext as _;
       -                gl.bind_framebuffer(glow::FRAMEBUFFER, Some(self.framebuffer));
       -                gl.delete_texture(self.render_texture);
       -                gl.delete_texture(self.sixel_render_texture);
       -
       -                let scale_filter = glow::NEAREST as i32; /*match self.scaling {
       -                    Scaling::Nearest => glow::NEAREST as i32,
       -                    Scaling::Linear => glow::LINEAR as i32,
       -                };*/
       -                let sixel_render_texture = gl.create_texture().unwrap();
       -
       -                gl.bind_texture(glow::TEXTURE_2D, Some(sixel_render_texture));
       -                gl.tex_image_2d(
       -                    glow::TEXTURE_2D,
       -                    0,
       -                    glow::RGBA as i32,
       -                    render_buffer_size.x as i32,
       -                    render_buffer_size.y as i32,
       -                    0,
       -                    glow::RGBA,
       -                    glow::UNSIGNED_BYTE,
       -                    None,
       -                );
       -                gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, scale_filter);
       -                gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, scale_filter);
       -                gl.tex_parameter_i32(
       -                    glow::TEXTURE_2D,
       -                    glow::TEXTURE_WRAP_S,
       -                    glow::CLAMP_TO_EDGE as i32,
       -                );
       -                gl.tex_parameter_i32(
       -                    glow::TEXTURE_2D,
       -                    glow::TEXTURE_WRAP_T,
       -                    glow::CLAMP_TO_EDGE as i32,
       -                );
       -
       -                let render_texture = gl.create_texture().unwrap();
       -                gl.bind_texture(glow::TEXTURE_2D, Some(render_texture));
       -                gl.tex_image_2d(
       -                    glow::TEXTURE_2D,
       -                    0,
       -                    glow::RGBA as i32,
       -                    render_buffer_size.x as i32,
       -                    render_buffer_size.y as i32,
       -                    0,
       -                    glow::RGBA,
       -                    glow::UNSIGNED_BYTE,
       -                    None,
       -                );
       -                gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, scale_filter);
       -                gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, scale_filter);
       -                gl.tex_parameter_i32(
       -                    glow::TEXTURE_2D,
       -                    glow::TEXTURE_WRAP_S,
       -                    glow::CLAMP_TO_EDGE as i32,
       -                );
       -                gl.tex_parameter_i32(
       -                    glow::TEXTURE_2D,
       -                    glow::TEXTURE_WRAP_T,
       -                    glow::CLAMP_TO_EDGE as i32,
       -                );
       -
       -                let depth_buffer = gl.create_renderbuffer().unwrap();
       -                gl.bind_renderbuffer(glow::RENDERBUFFER, Some(depth_buffer));
       -                gl.renderbuffer_storage(
       -                    glow::RENDERBUFFER,
       -                    glow::DEPTH_COMPONENT,
       -                    render_buffer_size.x as i32,
       -                    render_buffer_size.y as i32,
       -                );
       -                gl.framebuffer_renderbuffer(
       -                    glow::FRAMEBUFFER,
       -                    glow::DEPTH_ATTACHMENT,
       -                    glow::RENDERBUFFER,
       -                    Some(depth_buffer),
       -                );
       -                gl.framebuffer_texture(
       -                    glow::FRAMEBUFFER,
       -                    glow::COLOR_ATTACHMENT0,
       -                    Some(render_texture),
       -                    0,
       -                );
       -
       -                gl.bind_framebuffer(glow::FRAMEBUFFER, None);
       -                self.render_texture = render_texture;
       -                self.sixel_render_texture = sixel_render_texture;
       -                self.render_buffer_size = render_buffer_size;
       -            }
       -        }
       -    }
       -}
       -
       -fn conv_color(c: u32, colors: u32) -> u8 {
       -    let r = ((255 * c) / colors) as u8;
       -    r
       -}
       -
       -pub fn create_palette_texture(gl: &glow::Context, buf: &Buffer, palette_texture: NativeTexture) {
       -    let mut palette_data = Vec::new();
       -    for i in 0..buf.palette.colors.len() {
       -        let (r, g, b) = buf.palette.colors[i].get_rgb();
       -        palette_data.push(r);
       -        palette_data.push(g);
       -        palette_data.push(b);
       -        palette_data.push(0xFF);
       -    }
       -    unsafe {
       -        use glow::HasContext as _;
       -        gl.bind_texture(glow::TEXTURE_2D, Some(palette_texture));
       -        gl.tex_image_2d(
       -            glow::TEXTURE_2D,
       -            0,
       -            glow::RGB as i32,
       -            buf.palette.colors.len() as i32,
       -            1,
       -            0,
       -            glow::RGBA,
       -            glow::UNSIGNED_BYTE,
       -            Some(&palette_data),
       -        );
       -    }
       -}
       -
       -pub fn create_font_texture(gl: &glow::Context, buf: &Buffer, font_texture: NativeTexture) {
       -    let w = buf.font_table[0].size.width as usize;
       -    let h = buf.font_table[0].size.height as usize;
       -
       -    let mut font_data = Vec::new();
       -    let chars_in_line = 16;
       -    let line_width = w * chars_in_line * 4;
       -    let height = h * 256 / chars_in_line;
       -
       -    font_data.resize(line_width * height * buf.font_table.len(), 0);
       -
       -    for i in 0..buf.font_table.len() {
       -        for ch in 0..256usize {
       -            let glyph = buf.font_table[i]
       -                .get_glyph(unsafe { char::from_u32_unchecked(ch as u32) })
       -                .unwrap();
       -
       -            let x = ch % chars_in_line;
       -            let y = ch / chars_in_line;
       -
       -            let offset = x * w * 4 + y * h * line_width + i * line_width * height;
       -            for y in 0..h {
       -                let scan_line = glyph.data[y];
       -                let mut po = offset + y * line_width;
       -
       -                for x in 0..w {
       -                    if scan_line & (128 >> x) != 0 {
       -                        // unroll
       -                        font_data[po] = 0xFF;
       -                        po += 1;
       -                        font_data[po] = 0xFF;
       -                        po += 1;
       -                        font_data[po] = 0xFF;
       -                        po += 1;
       -                        font_data[po] = 0xFF;
       -                        po += 1;
       -                    } else {
       -                        po += 4;
       -                    }
       -                }
       -            }
       -        }
       -    }
       -
       -    unsafe {
       -        use glow::HasContext as _;
       -        gl.active_texture(glow::TEXTURE0);
       -        gl.bind_texture(glow::TEXTURE_2D_ARRAY, Some(font_texture));
       -        gl.tex_image_3d(
       -            glow::TEXTURE_2D_ARRAY,
       -            0,
       -            glow::RGB as i32,
       -            line_width as i32 / 4,
       -            height as i32,
       -            buf.font_table.len() as i32,
       -            0,
       -            glow::RGBA,
       -            glow::UNSIGNED_BYTE,
       -            Some(&font_data),
       -        );
       -    }
       -}
       -
       -pub fn create_buffer_texture(
       -    gl: &glow::Context,
       -    buf: &Buffer,
       -    scroll_back_line: i32,
       -    buffer_texture: NativeTexture,
       -) {
       -    let first_line = max(
       -        0,
       -        buf.layers[0].lines.len() as i32 - buf.get_buffer_height(),
       -    );
       -    let mut buffer_data = Vec::with_capacity(
       -        2 * buf.get_buffer_width() as usize * 4 * buf.get_real_buffer_height() as usize,
       -    );
       -    let colors = buf.palette.colors.len() as u32 - 1;
       -    let mut y = 0;
       -
       -    while y < buf.get_buffer_height() {
       -        let mut is_double_height = false;
       -
       -        for x in 0..buf.get_buffer_width() {
       -            let ch = buf
       -                .get_char_xy(x, first_line - scroll_back_line + y)
       -                .unwrap_or_default();
       -
       -            if ch.attribute.is_double_height() {
       -                is_double_height = true;
       -            }
       -            if ch.attribute.is_concealed() {
       -                buffer_data.push(' ' as u8);
       -            } else {
       -                buffer_data.push(ch.ch as u8);
       -            }
       -            if ch.attribute.is_bold() {
       -                buffer_data.push(conv_color(ch.attribute.get_foreground() + 8, colors));
       -            } else {
       -                buffer_data.push(conv_color(ch.attribute.get_foreground(), colors));
       -            }
       -            buffer_data.push(conv_color(ch.attribute.get_background(), colors));
       -            if buf.font_table.len() > 0 {
       -                buffer_data.push(
       -                    (255.0 * ch.get_font_page() as f32 / (buf.font_table.len() - 1) as f32) as u8,
       -                );
       -            } else {
       -                buffer_data.push(0);
       -            }
       -        }
       -
       -        if is_double_height {
       -            for x in 0..buf.get_buffer_width() {
       -                let ch = buf
       -                    .get_char_xy(x, first_line - scroll_back_line + y)
       -                    .unwrap_or_default();
       -
       -                if !ch.attribute.is_double_height() {
       -                    buffer_data.push(' ' as u8);
       -                } else {
       -                    buffer_data.push(ch.ch as u8);
       -                }
       -
       -                if ch.attribute.is_bold() {
       -                    buffer_data.push(conv_color(ch.attribute.get_foreground() + 8, colors));
       -                } else {
       -                    buffer_data.push(conv_color(ch.attribute.get_foreground(), colors));
       -                }
       -
       -                buffer_data.push(conv_color(ch.attribute.get_background(), colors));
       -
       -                if buf.font_table.len() > 0 {
       -                    buffer_data.push(
       -                        (255.0 * ch.get_font_page() as f32 / (buf.font_table.len() - 1) as f32)
       -                            as u8,
       -                    );
       -                } else {
       -                    buffer_data.push(0);
       -                }
       -            }
       -        }
       -
       -        if is_double_height {
       -            y += 2;
       -        } else {
       -            y += 1;
       -        }
       -    }
       -    y = 0;
       -    while y < buf.get_buffer_height() {
       -        let mut is_double_height = false;
       -
       -        for x in 0..buf.get_buffer_width() {
       -            let ch = buf
       -                .get_char_xy(x, first_line - scroll_back_line + y)
       -                .unwrap_or_default();
       -
       -            let mut attr = if ch.attribute.is_double_underlined() {
       -                3
       -            } else if ch.attribute.is_underlined() {
       -                1
       -            } else {
       -                0
       -            };
       -            if ch.attribute.is_crossed_out() {
       -                attr |= 4
       -            }
       -
       -            if ch.attribute.is_double_height() {
       -                is_double_height = true;
       -                attr |= 8
       -            }
       -
       -            buffer_data.push(attr);
       -            buffer_data.push(attr);
       -            buffer_data.push(attr);
       -            buffer_data.push(if ch.attribute.is_blinking() { 255 } else { 0 });
       -        }
       -
       -        if is_double_height {
       -            for x in 0..buf.get_buffer_width() {
       -                let ch = buf
       -                    .get_char_xy(x, first_line - scroll_back_line + y)
       -                    .unwrap_or_default();
       -                let mut attr = if ch.attribute.is_double_underlined() {
       -                    3
       -                } else if ch.attribute.is_underlined() {
       -                    1
       -                } else {
       -                    0
       -                };
       -                if ch.attribute.is_crossed_out() {
       -                    attr |= 4
       -                }
       -
       -                if ch.attribute.is_double_height() {
       -                    is_double_height = true;
       -                    attr |= 8;
       -                    attr |= 16;
       -                }
       -
       -                buffer_data.push(attr);
       -                buffer_data.push(attr);
       -                buffer_data.push(attr);
       -                buffer_data.push(if ch.attribute.is_blinking() { 255 } else { 0 });
       -            }
       -        }
       -
       -        if is_double_height {
       -            y += 2;
       -        } else {
       -            y += 1;
       -        }
       -    }
       -    unsafe {
       -        use glow::HasContext as _;
       -        gl.active_texture(glow::TEXTURE0 + 4);
       -
       -        gl.bind_texture(glow::TEXTURE_2D_ARRAY, Some(buffer_texture));
       -        gl.tex_image_3d(
       -            glow::TEXTURE_2D_ARRAY,
       -            0,
       -            glow::RGBA32F as i32,
       -            buf.get_buffer_width(),
       -            buf.get_buffer_height(),
       -            2,
       -            0,
       -            glow::RGBA,
       -            glow::UNSIGNED_BYTE,
       -            Some(&buffer_data),
       -        );
       -    }
       -}
   DIR diff --git a/src/ui/buffer_view/selection.rs b/src/ui/buffer_view/selection.rs
       @@ -1,43 +0,0 @@
       -use eframe::epaint::Vec2;
       -use icy_engine::Position;
       -
       -#[derive(Debug, Clone)]
       -pub struct Selection {
       -    pub anchor: Vec2,
       -    pub lead: Vec2,
       -    pub block_selection: bool,
       -
       -    pub anchor_pos: Position,
       -    pub lead_pos: Position,
       -
       -    pub locked: bool,
       -}
       -
       -impl Default for Selection {
       -    fn default() -> Self {
       -        Selection::new(Vec2::default())
       -    }
       -}
       -
       -impl Selection {
       -    pub fn new(pos: Vec2) -> Self {
       -        Self {
       -            anchor: pos,
       -            lead: pos,
       -            anchor_pos: Position::new(pos.x as i32, pos.y as i32),
       -            lead_pos: Position::new(pos.x as i32, pos.y as i32),
       -            block_selection: false,
       -            locked: false,
       -        }
       -    }
       -    pub fn is_empty(&self) -> bool {
       -        self.anchor_pos == self.lead_pos
       -    }
       -}
       -
       -impl Selection {
       -    pub fn set_lead(&mut self, lead: Vec2) {
       -        self.lead = lead;
       -        self.lead_pos = Position::new(lead.x as i32, lead.y as i32);
       -    }
       -}
   DIR diff --git a/src/ui/buffer_view/sixel.rs b/src/ui/buffer_view/sixel.rs
       @@ -1,215 +0,0 @@
       -use glow::{HasContext, NativeTexture};
       -use icy_engine::{Position, SixelReadStatus};
       -
       -use super::BufferView;
       -
       -pub struct SixelCacheEntry {
       -    pub status: SixelReadStatus,
       -    pub old_line: i32,
       -    pub data_opt: Option<Vec<u8>>,
       -
       -    pub pos: Position,
       -    pub size: icy_engine::Size<i32>,
       -
       -    pub texture_opt: Option<NativeTexture>,
       -}
       -
       -impl SixelCacheEntry {
       -    pub fn rect(&self) -> icy_engine::Rectangle {
       -        icy_engine::Rectangle {
       -            start: self.pos,
       -            size: self.size,
       -        }
       -    }
       -}
       -
       -impl BufferView {
       -    pub fn update_sixels(&mut self, gl: &glow::Context) -> bool {
       -        let buffer = &self.buf;
       -        let l = buffer.layers[0].sixels.len();
       -        if l == 0 {
       -            for sx in &self.sixel_cache {
       -                if let Some(tex) = sx.texture_opt {
       -                    unsafe {
       -                        gl.delete_texture(tex);
       -                    }
       -                }
       -            }
       -            self.sixel_cache.clear();
       -        }
       -
       -        let mut res = false;
       -        let mut i = 0;
       -        while i < l {
       -            let sixel = &buffer.layers[0].sixels[i];
       -
       -            if sixel.width() == 0 || sixel.height() == 0 {
       -                i += 1;
       -                continue;
       -            }
       -
       -            let mut old_line = 0;
       -            let current_line = match sixel.read_status {
       -                SixelReadStatus::Position(_, y) => y * 6,
       -                SixelReadStatus::Error | SixelReadStatus::Finished => sixel.height() as i32,
       -                _ => 0,
       -            };
       -
       -            if let Some(entry) = self.sixel_cache.get(i) {
       -                old_line = entry.old_line;
       -                if let SixelReadStatus::Position(_, _) = sixel.read_status {
       -                    if old_line + 5 * 6 >= current_line {
       -                        i += 1;
       -                        continue;
       -                    }
       -                }
       -                if entry.status == sixel.read_status {
       -                    i += 1;
       -                    continue;
       -                }
       -            }
       -            res = true;
       -            let data_len = (sixel.height() * sixel.width() * 4) as usize;
       -            let mut removed_index = -1;
       -            let mut v = if self.sixel_cache.len() > i {
       -                let mut entry = self.sixel_cache.remove(i);
       -                // old_handle = entry.image_opt;
       -                removed_index = i as i32;
       -                if let Some(ptr) = &mut entry.data_opt {
       -                    if ptr.len() < data_len {
       -                        ptr.resize(data_len, 0);
       -                    }
       -                    entry.data_opt.take().unwrap()
       -                } else {
       -                    let mut data = Vec::with_capacity(data_len);
       -                    data.resize(data_len, 0);
       -                    data
       -                }
       -            } else {
       -                let mut data = Vec::with_capacity(data_len);
       -                data.resize(data_len, 0);
       -                data
       -            };
       -
       -            let mut i = old_line as usize * sixel.width() as usize * 4;
       -
       -            for y in old_line..current_line {
       -                for x in 0..sixel.width() {
       -                    let column = &sixel.picture[x as usize];
       -                    let data = if let Some(col) = column.get(y as usize) {
       -                        if let Some(col) = col {
       -                            let (r, g, b) = col.get_rgb();
       -                            [r, g, b, 0xFF]
       -                        } else {
       -                            // todo: bg color may differ here
       -                            [0, 0, 0, 0xFF]
       -                        }
       -                    } else {
       -                        [0, 0, 0, 0xFF]
       -                    };
       -                    if i >= v.len() {
       -                        v.extend_from_slice(&data);
       -                    } else {
       -                        v[i] = data[0];
       -                        v[i + 1] = data[1];
       -                        v[i + 2] = data[2];
       -                        v[i + 3] = data[3];
       -                    }
       -                    i += 4;
       -                }
       -            }
       -            let (texture_opt, data_opt, clear) = match sixel.read_status {
       -                SixelReadStatus::Finished | SixelReadStatus::Error => unsafe {
       -                    let texture = gl.create_texture().unwrap();
       -                    gl.active_texture(glow::TEXTURE0 + 6);
       -                    gl.bind_texture(glow::TEXTURE_2D, Some(texture));
       -                    gl.tex_parameter_i32(
       -                        glow::TEXTURE_2D,
       -                        glow::TEXTURE_MIN_FILTER,
       -                        glow::NEAREST as i32,
       -                    );
       -                    gl.tex_parameter_i32(
       -                        glow::TEXTURE_2D,
       -                        glow::TEXTURE_MAG_FILTER,
       -                        glow::NEAREST as i32,
       -                    );
       -                    gl.tex_parameter_i32(
       -                        glow::TEXTURE_2D,
       -                        glow::TEXTURE_WRAP_S,
       -                        glow::CLAMP_TO_EDGE as i32,
       -                    );
       -                    gl.tex_parameter_i32(
       -                        glow::TEXTURE_2D,
       -                        glow::TEXTURE_WRAP_T,
       -                        glow::CLAMP_TO_EDGE as i32,
       -                    );
       -                    gl.tex_image_2d(
       -                        glow::TEXTURE_2D,
       -                        0,
       -                        glow::RGB as i32,
       -                        sixel.width() as i32,
       -                        sixel.height() as i32,
       -                        0,
       -                        glow::RGBA,
       -                        glow::UNSIGNED_BYTE,
       -                        Some(&v),
       -                    );
       -                    (Some(texture), None, true)
       -                },
       -                _ => (None, Some(v), false),
       -            };
       -
       -            let new_entry = SixelCacheEntry {
       -                status: sixel.read_status,
       -                old_line: current_line,
       -                data_opt,
       -                pos: sixel.position,
       -                size: icy_engine::Size {
       -                    width: sixel.width() as i32,
       -                    height: sixel.height() as i32,
       -                },
       -                texture_opt,
       -            };
       -
       -            if removed_index < 0 {
       -                self.sixel_cache.push(new_entry);
       -                if clear {
       -                    self.clear_invisible_sixel_cache(gl, self.sixel_cache.len() - 1);
       -                    break;
       -                }
       -            } else {
       -                self.sixel_cache.insert(removed_index as usize, new_entry);
       -                if clear {
       -                    self.clear_invisible_sixel_cache(gl, removed_index as usize);
       -                    break;
       -                }
       -            }
       -        }
       -        res
       -    }
       -
       -    pub fn clear_invisible_sixel_cache(&mut self, gl: &glow::Context, j: usize) {
       -        // remove cache entries that are removed by the engine
       -        if j > 0 {
       -            let cur_rect = self.sixel_cache[j].rect();
       -            let mut i = j - 1;
       -            loop {
       -                let other_rect = self.sixel_cache[i].rect();
       -                if cur_rect.contains(other_rect) {
       -                    let t = self.sixel_cache.remove(i);
       -                    if let Some(texture) = &t.texture_opt {
       -                        unsafe {
       -                            gl.delete_texture(*texture);
       -                        }
       -                    }
       -
       -                    self.buf.layers[0].sixels.remove(i);
       -                }
       -                if i == 0 {
       -                    break;
       -                }
       -                i -= 1;
       -            }
       -        }
       -    }
       -}
   DIR diff --git a/src/ui/main_window.rs b/src/ui/main_window.rs
       @@ -1,27 +1,34 @@
       -use std::{path::PathBuf, fs};
       +use std::{path::PathBuf, fs, sync::Arc};
        
        use eframe::egui::{self, menu, TopBottomPanel};
        use egui_dock::{DockArea, Style,  Tree};
       -use icy_engine::BitFont;
       +use glow::Context;
       +use icy_engine::{BitFont, Buffer};
        use rfd::FileDialog;
        
        use crate::{Document, FontEditor};
        
       +use super::ansi_editor::AnsiEditor;
       +
        pub struct MainWindow {
            tree: Tree<(String, Box<dyn Document>)>,
       +    gl: Arc<Context>
        }
        
        impl MainWindow {
       -    pub fn new(_cc: &eframe::CreationContext<'_>) -> Self {
       -        let mut tree = Tree::new(Vec::new());
       +    pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
       +        let tree = Tree::new(Vec::new());
        
                let view = MainWindow {
       -            tree
       +            tree,
       +            gl: cc.gl.clone().unwrap()
                };
                view
            }
        
            pub fn open_file(&mut self, path: PathBuf) {
       +        let full_path = path.to_str().unwrap().to_string();
       +
                if let Some(ext) = path.extension()  {
                    match ext.to_str().unwrap() { 
                        "psf" => {
       @@ -32,13 +39,20 @@ impl MainWindow {
                                }
                                let file_name_str = file_name.unwrap().to_str().unwrap().to_string();
                                if let Ok(font) = BitFont::from_bytes(&file_name_str, &data) {
       -                            self.tree.push_to_focused_leaf((path.to_str().unwrap().to_string(), Box::new(FontEditor::new(font))));
       +                            self.tree.push_to_focused_leaf((full_path, Box::new(FontEditor::new(font))));
       +                            return;
                                }
                            }
                        }
                        _ => {}
                    }
                }
       +        let buf = Buffer::load_buffer(&path).unwrap();
       +        
       +        let editor = AnsiEditor::new(&self.gl, buf);
       +
       +        self.tree.push_to_focused_leaf((full_path, Box::new(editor)));
       +
            }
        }
        
   DIR diff --git a/src/ui/mod.rs b/src/ui/mod.rs
       @@ -1,4 +1,4 @@
       -mod buffer_view;
       +mod ansi_editor;
        mod terminal_window;
        mod main_window;
        use std::error::Error;