SDFC64IQZWAAG36UV4GVSAER5S3FISBQEE2NVGI4G6ODD6CCGBIAC UKCN3P5Y6TTOW5W6LWI5KKLCEVIIX2477YBVIVFWOBVTXXIYUGFAC 2YSJ7XT5CAKSD7QGFPAISZ55LEMHOEPYZSXNMMDTSUZ5COVI5XYQC FVK7DUVNDKO6AVNV7XI7YFG6GIRAEBYHLSRHR75AEF24PNSPLL5AC KLGRQAFUQPJP75NVLS5MZZ3CY4TGNVGB4K2YHQSLYVGAPKCLBLUAC 5S34ANJMEKYNSGROMFRDP22UA2FK2ENSGOBB4MPSHS7PG2BW6DLAC T2MYIZ6BIXHBKWZ3EJIYV65STSDAVKZHAPLTFXF27YBQIJCI7U7AC PTQ26KY65NDVZ35EG66SARWRGGYKDJ2BT3EVK4RJFS2J5AQUX6OQC V2265WPBM2LBE4BGEFB3HYCZXA5C5MJKQ5IVPFWY3ARCVIAYBZGQC X6QJYZJZSS7YSRCDOPQ2I2Y7U7VJA4HE5P74ZYENROR7FRUPPXWQC B66IOHMWFZINTWC57Q5OD4YPBVQNBN3YUM3ZKX4MTDKHYGH5JHFAC 576YRB7XSKS6Q265TEJQS7HUGMKSGHGDRGVP6WBZS7XMK6RVJRNQC XKAEHFMZP4PDXPJ2GZTRAJL7SCHJAH7EXRG2TWEBOFVOFUFJQ6RAC CTPHEJ2S7MMSZTNVO4LFEYHMMTKYWB5PC54RMVJHMATN5TY45GPQC T5T2PZSJVBHIPHG7VRXZSH36WGSTR4I44BOI335X4F637E7GUWHAC LSPMIVEH5IYUPBMB7DL5VTM2HOMYDBA5ZY3GIW6ZFTCDGTUUDFFAC HKVKPIIWK6AB4DWCS6SB47RYEXTD4QAQIGDFNQGA3TDJEIAXBZKQC AFZQUL4VKEEDRSA5CV6YXN7PMX6PQ2PMLECTG53S4HZKTNCAIMXQC L5ZNI6R53WDZIOXNECV262TURPIPWL522TVF25U5RYHOTSY7BHZAC DZSYKTTKQ6DUGPMCPS7FQRSE5FO4FEHY2L3UWB73A7Y7Y6GDHWCAC let rb_face =rustybuzz::Face::from_slice(unsafe { &*(&data[..] as *const [u8]) }, 0).ok_or("Failed to load rustybuzz font")?;let fd_face = fontdue::Font::from_bytes(&data[..], FontSettings::default())?;
let rb_face = rustybuzz::Face::from_slice(unsafe { &*(data.as_slice() as *const [u8]) },0,).ok_or("Failed to load rustybuzz font")?;let fd_face =fontdue::Font::from_bytes(data.as_slice(), FontSettings::default())?;
fn render_segment(rs: &mut RenderState,fs: &mut FontState,font: FontIndex,face: &json_ui::Face,default_face: &json_ui::Face,ubuf: UnicodeBuffer,pos: &mut (i32, i32),) -> UnicodeBuffer {let glyph_buffer = rustybuzz::shape(&fs.faces[font].rb_face, &[], ubuf);let ginfo = glyph_buffer.glyph_infos();let gpos = glyph_buffer.glyph_positions();let upe = fs.faces[font].rb_face.units_per_em();let buf = rs.pixels.frame_mut();let font_size = fs.font_size as i32;
fn render_segment(&mut self,font: FontIndex,face: &json_ui::Face,default_face: &json_ui::Face,glyph_buffer: &GlyphBuffer,pos: &mut (i32, i32),) {let ginfo = glyph_buffer.glyph_infos();let gpos = glyph_buffer.glyph_positions();let upe = self.fonts.faces[font].rb_face.units_per_em();let buf = self.pixels.frame_mut();let font_size = self.fonts.font_size as i32;
for i in 0..glyph_buffer.len() {let (metrics, data) = fs.rasterize_glyph(font, ginfo[i].glyph_id as u16);let x_off = gpos[i].x_offset * font_size / upe + metrics.xmin;let y_off = gpos[i].y_offset * font_size / upe - metrics.ymin - metrics.height as i32;
for i in 0..glyph_buffer.len() {let (metrics, data) = self.fonts.rasterize_glyph(font, ginfo[i].glyph_id as u16);let x_off = gpos[i].x_offset * font_size / upe + metrics.xmin;let y_off = gpos[i].y_offset * font_size / upe - metrics.ymin - metrics.height as i32;
for row in 0..metrics.height {for column in 0..metrics.width {let x = pos.0 + x_off + column as i32;let y = pos.1 + y_off + row as i32;let bidx = ((x + y * rs.size.0 as i32) * 4) as usize;let sidx = (column + row * metrics.width) * 3;if bidx >= buf.len() {break;}if sidx >= buf.len() {continue;}if x > rs.size.0 as i32 {break;
for row in 0..metrics.height {for column in 0..metrics.width {let x = pos.0 + x_off + column as i32;let y = pos.1 + y_off + row as i32;let bidx = ((x + y * self.size.0 as i32) * 4) as usize;let sidx = (column + row * metrics.width) * 3;if bidx >= buf.len() {break;}if sidx >= buf.len() {continue;}if x > self.size.0 as i32 {break;}let fg = &face.fg.to_rgba(default_face.fg.to_rgba([255, 255, 255, 255]));let bg = &face.bg.to_rgba(default_face.bg.to_rgba([0, 0, 0, 255]));for i in 0..3 {buf[bidx + i] = u8::max(buf[bidx + i],(bg[i] as i32+ (fg[i] as i32 - bg[i] as i32) * data[sidx + i] as i32 / 255)as u8,);}buf[bidx + 3] = 255;
let lerp = |col1, col2, x| {(col1 as i32 + (col2 as i32 - col1 as i32) * x as i32 / 255) as u8};let fg = &face.fg.to_rgba(default_face.fg.to_rgba((255, 255, 255, 255)));let bg = &face.bg.to_rgba(default_face.bg.to_rgba((0, 0, 0, 255)));// TODO: Handle light themes properlybuf[bidx] = u8::max(buf[bidx], lerp(bg.0, fg.0, data[sidx]));buf[bidx + 1] = u8::max(buf[bidx + 1], lerp(bg.1, fg.1, data[sidx + 1]));buf[bidx + 2] = u8::max(buf[bidx + 2], lerp(bg.2, fg.2, data[sidx + 2]));buf[bidx + 3] = 255;
pub fn render_line(rs: &mut RenderState,fs: &mut FontState,line: &[json_ui::Atom],default_face: &json_ui::Face,pos: &mut (i32, i32),) -> Result<(), Box<dyn Error>> {let mut ubuf = UnicodeBuffer::new();let mut curr_face = None;let mut curr_font = 0;for atom in line {let face = &atom.face;for c in atom.contents.chars() {let font = fs.faces.iter().position(|i| i.rb_face.glyph_index(c).is_some()).unwrap_or(0);if curr_face != Some(face) || curr_font != font || c == '\n' {ubuf = render_segment(rs,fs,curr_font,curr_face.unwrap_or(face),default_face,ubuf,pos,);curr_face = Some(face);curr_font = font;ubuf.clear();
fn make_runs<'a>(line: &'a [json_ui::Atom],fonts: &[FontData],) -> Vec<(&'a str, &'a json_ui::Face, FontIndex)> {let mut res = Vec::new();let mut curr_font = 0;for atom in line {let mut start = 0;for (i, c) in atom.contents.char_indices() {let font = fonts.iter().position(|i| i.rb_face.glyph_index(c).is_some()).unwrap_or(0);if curr_font != font || c == '\n' {res.push((&atom.contents[start..i], &atom.face, curr_font));curr_font = font;start = i;}if c == '\n' {start += 1;}
Ok(())}
pub fn render(&mut self, ui: &Ui) -> Result<(), Box<dyn Error>> {let bg = ui.default_face.bg.to_rgba([0, 0, 0, 255]);for c in self.pixels.frame_mut().chunks_exact_mut(4) {c.copy_from_slice(&bg);}
pub fn redraw(rs: &mut RenderState,fs: &mut FontState,lines: &[json_ui::Line],status_line: &[json_ui::Atom],mode_line: &[json_ui::Atom],default_face: &json_ui::Face,) -> Result<(), Box<dyn Error>> {let bg = default_face.bg.to_rgba((0, 0, 0, 255));for c in rs.pixels.frame_mut().chunks_exact_mut(4) {c[0] = bg.0;c[1] = bg.1;c[2] = bg.2;c[3] = bg.3;}
let mut pos = (0, self.fonts.line_height() as i32);let mut ubuf = UnicodeBuffer::new();for line in &ui.lines {for (contents, face, font) in Self::make_runs(line, &self.fonts.faces) {ubuf.push_str(contents);
let mut pos = (0, fs.line_height() as i32);for line in lines {render_line(rs, fs, line, default_face, &mut pos)?;
let glyph_buffer = rustybuzz::shape(&self.fonts.faces[font].rb_face, &[], ubuf);self.render_segment(font, face, &ui.default_face, &glyph_buffer, &mut pos);ubuf = glyph_buffer.clear();}pos.0 = 0;pos.1 += self.fonts.line_height() as i32;}pos.1 = self.size.1 as i32 - self.fonts.line_height() as i32;
pos.1 += fs.line_height() as i32;
for (contents, face, font) in Self::make_runs(&ui.prompt_line, &self.fonts.faces) {ubuf.push_str(contents);let glyph_buffer = rustybuzz::shape(&self.fonts.faces[font].rb_face, &[], ubuf);self.render_segment(font, face, &ui.line_face, &glyph_buffer, &mut pos);ubuf = glyph_buffer.clear();}let mode_line = Self::make_runs(&ui.mode_line, &self.fonts.faces);let mode_line: Vec<_> = mode_line.into_iter().map(|(contents, face, font)| {let mut ubuf = UnicodeBuffer::new();ubuf.push_str(contents);let glyph_buffer = rustybuzz::shape(&self.fonts.faces[font].rb_face, &[], ubuf);let size = glyph_buffer.glyph_positions().iter().map(|gpos| {gpos.x_advance * self.fonts.font_size as i32/ self.fonts.faces[font].rb_face.units_per_em()}).sum::<i32>();(glyph_buffer, font, face, size)}).collect();let text_width: i32 = mode_line.iter().map(|(_, _, _, size)| size).sum();pos.0 = self.size.0 as i32 - text_width;for (glyph_buffer, font, face, _) in mode_line {self.render_segment(font, face, &ui.line_face, &glyph_buffer, &mut pos);}self.pixels.render().unwrap();Ok(())
pos.1 = rs.size.1 as i32 - fs.line_height() as i32;render_line(rs, fs, status_line, default_face, &mut pos)?;render_line(rs, fs, mode_line, default_face, &mut pos)?;rs.pixels.render().unwrap();Ok(())
struct App {render_state: Option<RenderState>,font_state: FontState,modifiers: Modifiers,kakoune: Kakoune,
#[derive(Default)]struct Ui {
lines: Vec::new(),status_line: Vec::new(),mode_line: Vec::new(),default_face: json_ui::Face {fg: json_ui::Color::BuiltinColor(json_ui::BuiltinColor::White),bg: json_ui::Color::BuiltinColor(json_ui::BuiltinColor::Black),attributes: Vec::new(),},
if let Some(rs) = self.render_state.as_mut() {redraw(rs,&mut self.font_state,&self.lines,&self.status_line,&self.mode_line,&self.default_face,).unwrap();
if let Some(render) = self.render_state.as_mut() {render.render(&self.ui).unwrap();
if let Some(rs) = self.render_state.as_mut() {rs.pixels.resize_buffer(size.width, size.height).unwrap();rs.pixels.resize_surface(size.width, size.height).unwrap();
if let Some(render) = self.render_state.as_mut() {render.pixels.resize_buffer(size.width, size.height).unwrap();render.pixels.resize_surface(size.width, size.height).unwrap();render.size = size.into();
self.lines = lines;self.default_face = default_face;if let Some(rs) = &self.render_state {rs.window.request_redraw();
self.ui.lines = lines;self.ui.default_face = default_face;if let Some(render) = &self.render_state {render.window.request_redraw();
Request::DrawStatus(status_line, mode_line, _default_face) => {// TODO: Handle default_face properlyself.status_line = status_line;self.mode_line = mode_line;if let Some(rs) = &self.render_state {rs.window.request_redraw();
Request::DrawStatus(prompt_line, mode_line, default_face) => {self.ui.prompt_line = prompt_line;self.ui.mode_line = mode_line;self.ui.line_face = default_face;if let Some(render) = &self.render_state {render.window.request_redraw();
BuiltinColor::Black => (0, 0, 0, 255),BuiltinColor::Red => (255, 0, 0, 255),BuiltinColor::Green => (0, 255, 0, 255),BuiltinColor::Yellow => (255, 255, 0, 255),BuiltinColor::Blue => (0, 0, 255, 0),BuiltinColor::Magenta => (255, 0, 255, 255),BuiltinColor::Cyan => (0, 255, 255, 255),BuiltinColor::White => (255, 255, 255, 255),BuiltinColor::BrightBlack => (0, 0, 0, 255),BuiltinColor::BrightRed => (255, 0, 0, 255),BuiltinColor::BrightGreen => (0, 255, 0, 255),BuiltinColor::BrightYellow => (255, 255, 0, 255),BuiltinColor::BrightBlue => (0, 0, 255, 0),BuiltinColor::BrightMagenta => (255, 0, 255, 255),BuiltinColor::BrightCyan => (0, 255, 255, 255),BuiltinColor::BrightWhite => (255, 255, 255, 255),
BuiltinColor::Black => [0, 0, 0, 255],BuiltinColor::Red => [255, 0, 0, 255],BuiltinColor::Green => [0, 255, 0, 255],BuiltinColor::Yellow => [255, 255, 0, 255],BuiltinColor::Blue => [0, 0, 255, 0],BuiltinColor::Magenta => [255, 0, 255, 255],BuiltinColor::Cyan => [0, 255, 255, 255],BuiltinColor::White => [255, 255, 255, 255],BuiltinColor::BrightBlack => [0, 0, 0, 255],BuiltinColor::BrightRed => [255, 0, 0, 255],BuiltinColor::BrightGreen => [0, 255, 0, 255],BuiltinColor::BrightYellow => [255, 255, 0, 255],BuiltinColor::BrightBlue => [0, 0, 255, 0],BuiltinColor::BrightMagenta => [255, 0, 255, 255],BuiltinColor::BrightCyan => [0, 255, 255, 255],BuiltinColor::BrightWhite => [255, 255, 255, 255],
}}#[cfg(test)]mod test {use super::*;#[test]fn deserialize_color() {assert_eq!(serde_json::from_str::<Color>("\"red\"").unwrap(),Color::BuiltinColor(BuiltinColor::Red));assert_eq!(serde_json::from_str::<Color>("\"default\"").unwrap(),Color::BuiltinColor(BuiltinColor::Default));assert_eq!(serde_json::from_str::<Color>("\"rgb:223ffa\"").unwrap(),Color::Rgb(String::from("rgb:223ffa")));