123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- use config::Config;
- use freetype as ft;
- use fontconfig_sys::*;
- use ui::ext;
- use ui::ext::ConnectionExt;
- use ui::util;
- use ui::x11;
- use xcb;
- use std::cell::RefCell;
- use std::collections::HashMap;
- use std::ffi::{CString, CStr};
- use std::mem;
- use std::ptr;
- use std::sync::{Once, ONCE_INIT};
- static FC_INIT: Once = ONCE_INIT;
- struct Font {
- pub id: xcb::render::Glyphset,
- face: ft::Face<'static>,
- char_widths: RefCell<HashMap<char, u16>>
- }
- impl Font {
- pub fn new(conn: &xcb::Connection, face: ft::Face<'static>) -> Font {
- let format = conn.get_pict_format(ext::PictFormat::ARGB32);
- let id = conn.generate_id();
- xcb::render::create_glyph_set(conn, id, format);
- Font {
- id: id,
- face: face,
- char_widths: RefCell::new(HashMap::new())
- }
- }
- pub fn load_glyph(&self, conn: &xcb::Connection, charcode: char) -> Option<u16> {
- if self.char_widths.borrow().contains_key(&charcode) {
- return self.char_widths.borrow().get(&charcode).map(|r| *r)
- }
- if self.face.get_char_index(charcode as usize) == 0 {
- return None
- }
- match self.face.load_char(charcode as usize, ft::face::RENDER | ft::face::TARGET_LCD) {
- Ok(_) => {
- let glyph = self.face.glyph();
- let bitmap = glyph.bitmap();
- let pixel_width = (bitmap.width() / 3) as usize;
- let pixel_height = bitmap.rows() as usize;
- let info = xcb::render::Glyphinfo::new(
- pixel_width as u16,
- pixel_height as u16,
- -glyph.bitmap_left() as i16,
- glyph.bitmap_top() as i16,
- (glyph.advance().x / 64) as i16,
- (glyph.advance().y / 64) as i16
- );
- let stride = bitmap.width() as usize + (pixel_width & 3); // round up to 4 somehow
- let source = bitmap.buffer();
- let pixels = pixel_height * pixel_width;
- let mut buf = Vec::with_capacity(pixels * 4);
- for i in 0..pixel_height {
- let offset = i * stride;
- for j in 0..pixel_width {
- let offset = offset + j * 3;
- buf.push(source[offset+2]);
- buf.push(source[offset+1]);
- buf.push(source[offset]);
- buf.push(0);
- }
- }
- let width = (glyph.advance().x / 64) as u16;
- xcb::render::add_glyphs(conn, self.id, &[charcode as u32], &[info], buf.as_slice());
- self.char_widths.borrow_mut().insert(charcode, width);
- Some(width)
- },
- _ => None
- }
- }
- pub fn baseline_offset(&self, height: u16) -> i16 {
- self.face.load_char('X' as usize, ft::face::LoadFlag::empty()).unwrap();
- let ascender = self.face.glyph().metrics().horiBearingY / 64;
- (height as i16 - ascender as i16) / 2
- }
- }
- pub struct TextGroup<'a> {
- pub text: &'a str,
- pub glyphset: xcb::render::Glyphset
- }
- pub struct RenderableText<'a> {
- pub width: u16,
- groups: Vec<TextGroup<'a>>
- }
- impl<'a> RenderableText<'a> {
- pub fn render(&self, conn: &xcb::Connection, pen: xcb::render::Picture, pic: xcb::render::Picture, x: u16, y: u16) {
- let mut cmds = util::GlyphCmd::new();
- let mut default_glyphset = 0;
- cmds.set_position(x, y);
- for group in self.groups.iter() {
- default_glyphset = group.glyphset;
- cmds.set_glyphset(group.glyphset);
- cmds.add_text(group.text);
- }
- xcb::render::composite_glyphs_32(
- conn,
- xcb::render::PICT_OP_OVER as u8,
- pen,
- pic,
- 0,
- default_glyphset,
- 0, 0,
- cmds.as_ref()
- );
- }
- }
- pub struct FontLoader {
- conn: x11::Connection,
- library: ft::Library,
- fonts: Vec<Font>
- }
- impl FontLoader {
- pub fn new(conn: x11::Connection) -> Self {
- FC_INIT.call_once(|| unsafe {
- assert_eq!(FcInit(), 1);
- });
- let library = ft::Library::init().unwrap();
- unsafe {
- ft::ffi::FT_Library_SetLcdFilter(library.raw(), ft::ffi::FT_LCD_FILTER_DEFAULT);
- };
- FontLoader {
- conn: conn,
- library: library,
- fonts: vec![]
- }
- }
- pub fn from_config(conn: x11::Connection, cfg: &Config) -> Self {
- let val = cfg.lookup("bar.fonts").unwrap();
- let fonts = val.as_slice().unwrap();
- let fonts = fonts.iter().flat_map(|elem| elem.as_str());
- let mut font_loader = Self::new(conn);
- for font in fonts {
- font_loader.load(font);
- }
- font_loader
- }
- fn find_face(&self, name: &str) -> Option<ft::Face<'static>> {
- unsafe {
- let slice = CString::new(name.to_string()).unwrap();
- let pattern = FcNameParse(slice.as_ptr() as *const u8);
- FcConfigSubstitute(ptr::null_mut(), pattern, FcMatchPattern);
- FcDefaultSubstitute(pattern);
- let mut result: FcResult = mem::uninitialized();
- let font = FcFontMatch(ptr::null_mut(), pattern, &mut result);
- if !font.is_null() {
- let field = CString::new("file").unwrap();
- let mut ret: *mut FcChar8 = mem::uninitialized();
- if FcPatternGetString(font, field.as_ptr(), 0, &mut ret) == FcResultMatch {
- let file = CStr::from_ptr(ret as *const i8);
- let path = file.to_string_lossy().to_string();
- let face = self.library.new_face(&path, 0).unwrap();
- let field = CString::new("dpi").unwrap();
- let mut dpi = 0.0;
- if FcPatternGetDouble(font, field.as_ptr(), 0, &mut dpi) != FcResultMatch {
- dpi = 75.0;
- }
- let field = CString::new("pixelsize").unwrap();
- let mut pixel_size = 0.0;
- if FcPatternGetDouble(font, field.as_ptr(), 0, &mut pixel_size) == FcResultMatch {
- face.set_pixel_sizes((96.0 / dpi * pixel_size) as u32, 0).unwrap();
- }
- return Some(face)
- }
- }
- None
- }
- }
- pub fn load(&mut self, name: &str) {
- if let Some(face) = self.find_face(name) {
- let font = Font::new(&self.conn, face);
- self.fonts.push(font);
- }
- else {
- panic!("Could not load font {}", name);
- }
- }
- pub fn default_offset(&self, height: u16) -> i16 {
- self.fonts[0].baseline_offset(height)
- }
- fn glyphset_and_width_for_char(&self, charcode: char) -> Option<(xcb::render::Glyphset, u16)> {
- for font in self.fonts.iter() {
- if let Some(width) = font.load_glyph(&self.conn, charcode) {
- return Some((font.id, width))
- }
- }
- None
- }
- pub fn create_renderable_text<'a>(&self, string: &'a str) -> RenderableText<'a> {
- let mut ret = vec![];
- let mut prev = 0;
- let mut id = 0;
- let mut width = 0;
- for (i, charcode) in string.char_indices() {
- if let Some((this_id, char_width)) = self.glyphset_and_width_for_char(charcode) {
- width += char_width;
- if i == 0 {
- id = this_id;
- }
- else if id != this_id {
- ret.push(TextGroup {
- text: &string[prev..i],
- glyphset: id
- });
- id = this_id;
- prev = i;
- }
- }
- }
- ret.push(TextGroup {
- text: &string[prev..string.len()],
- glyphset: id
- });
- RenderableText {
- width: width,
- groups: ret
- }
- }
- }
|