mirror of
https://github.com/wez/wezterm.git
synced 2024-11-26 16:34:23 +03:00
Render text on sdl using harfbuzz and freetype
This commit is contained in:
parent
c53ca64c33
commit
d82860ef5a
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/Cargo.lock
|
||||||
|
/target/
|
||||||
|
**/*.rs.bk
|
17
.rustfmt.toml
Normal file
17
.rustfmt.toml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Run `rustfmt --config-help` to see available config options.
|
||||||
|
# Please keep these in alphabetical order.
|
||||||
|
closure_block_indent_threshold = 1
|
||||||
|
condense_wildcard_suffixes = true
|
||||||
|
error_on_line_overflow = false
|
||||||
|
error_on_line_overflow_comments = false
|
||||||
|
# Too many derives can cause line length limits to be exceeded. rustfmt-nightly
|
||||||
|
# 0.2.5 doesn't appear to pay attention to this.
|
||||||
|
merge_derives = false
|
||||||
|
reorder_imported_names = true
|
||||||
|
reorder_imports = true
|
||||||
|
reorder_imports_in_group = true
|
||||||
|
# Override the Nuclide/Atom default tab spacing.
|
||||||
|
tab_spaces = 4
|
||||||
|
use_try_shorthand = true
|
||||||
|
write_mode = "replace"
|
||||||
|
max_width = 80
|
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["Wez Furlong <wez@wezfurlong.org>"]
|
||||||
|
name = "wterm"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
failure = "0.1.1"
|
||||||
|
hexdump = "0.1.0"
|
||||||
|
sdl2 = {version="0.31.0", features=["bundled"]}
|
||||||
|
vte = "0.3.2"
|
||||||
|
|
||||||
|
[dependencies.font]
|
||||||
|
path = "font"
|
10
font/Cargo.toml
Normal file
10
font/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["Wez Furlong <wez@wezfurlong.org>"]
|
||||||
|
name = "font"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
failure = "0.1.1"
|
||||||
|
freetype = "0.3.0"
|
||||||
|
harfbuzz-sys = "0.1.15"
|
||||||
|
servo-fontconfig = "0.4.0"
|
111
font/src/ft/ftwrap.rs
Normal file
111
font/src/ft/ftwrap.rs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
//! Higher level freetype bindings
|
||||||
|
use failure::Error;
|
||||||
|
pub use freetype::freetype::*;
|
||||||
|
use std::ffi::CString;
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
/// Translate an error and value into a result
|
||||||
|
fn ft_result<T>(err: FT_Error, t: T) -> Result<T, Error> {
|
||||||
|
if err.succeeded() {
|
||||||
|
Ok(t)
|
||||||
|
} else {
|
||||||
|
Err(format_err!("FreeType error {:?}", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Face {
|
||||||
|
pub face: FT_Face,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Face {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
FT_Done_Face(self.face);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Face {
|
||||||
|
pub fn set_char_size(
|
||||||
|
&mut self,
|
||||||
|
char_width: FT_F26Dot6,
|
||||||
|
char_height: FT_F26Dot6,
|
||||||
|
horz_resolution: FT_UInt,
|
||||||
|
vert_resolution: FT_UInt,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
ft_result(
|
||||||
|
unsafe {
|
||||||
|
FT_Set_Char_Size(
|
||||||
|
self.face,
|
||||||
|
char_width,
|
||||||
|
char_height,
|
||||||
|
horz_resolution,
|
||||||
|
vert_resolution,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_and_render_glyph(
|
||||||
|
&mut self,
|
||||||
|
glyph_index: FT_UInt,
|
||||||
|
load_flags: FT_Int32,
|
||||||
|
render_mode: FT_Render_Mode,
|
||||||
|
) -> Result<&FT_GlyphSlotRec_, Error> {
|
||||||
|
unsafe {
|
||||||
|
let res = FT_Load_Glyph(self.face, glyph_index, load_flags);
|
||||||
|
if res.succeeded() {
|
||||||
|
FT_Render_Glyph((*self.face).glyph, render_mode);
|
||||||
|
}
|
||||||
|
ft_result(res, &*(*self.face).glyph)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Library {
|
||||||
|
lib: FT_Library,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Library {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
FT_Done_FreeType(self.lib);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Library {
|
||||||
|
pub fn new() -> Result<Library, Error> {
|
||||||
|
let mut lib = ptr::null_mut();
|
||||||
|
let res = unsafe { FT_Init_FreeType(&mut lib as *mut _) };
|
||||||
|
let lib = ft_result(res, lib)?;
|
||||||
|
Ok(Library { lib })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_face<P>(
|
||||||
|
&self,
|
||||||
|
path: P,
|
||||||
|
face_index: FT_Long,
|
||||||
|
) -> Result<Face, Error>
|
||||||
|
where
|
||||||
|
P: Into<Vec<u8>>,
|
||||||
|
{
|
||||||
|
let mut face = ptr::null_mut();
|
||||||
|
let path =CString::new(path.into())?;
|
||||||
|
|
||||||
|
let res = unsafe {
|
||||||
|
FT_New_Face(self.lib, path.as_ptr(), face_index, &mut face as *mut _)
|
||||||
|
};
|
||||||
|
Ok(Face { face: ft_result(res, face)? })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_lcd_filter(
|
||||||
|
&mut self,
|
||||||
|
filter: FT_LcdFilter,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
unsafe {
|
||||||
|
ft_result(FT_Library_SetLcdFilter(self.lib, filter), ())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
167
font/src/ft/hbwrap.rs
Normal file
167
font/src/ft/hbwrap.rs
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
//! Higher level harfbuzz bindings
|
||||||
|
use failure::Error;
|
||||||
|
pub use harfbuzz_sys::*;
|
||||||
|
use std::mem;
|
||||||
|
use std::ptr;
|
||||||
|
use std::slice;
|
||||||
|
|
||||||
|
pub fn language_from_string(s: &str) -> Result<hb_language_t, Error> {
|
||||||
|
unsafe {
|
||||||
|
let lang = hb_language_from_string(s.as_ptr() as *const i8, s.len() as i32);
|
||||||
|
ensure!(
|
||||||
|
!lang.is_null(),
|
||||||
|
"failed to convert {} to language"
|
||||||
|
);
|
||||||
|
Ok(lang)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn feature_from_string(s: &str) -> Result<hb_feature_t, Error> {
|
||||||
|
unsafe {
|
||||||
|
let mut feature = mem::zeroed();
|
||||||
|
ensure!(
|
||||||
|
hb_feature_from_string(
|
||||||
|
s.as_ptr() as *const i8,
|
||||||
|
s.len() as i32,
|
||||||
|
&mut feature as *mut _,
|
||||||
|
) != 0,
|
||||||
|
"failed to create feature from {}",
|
||||||
|
s
|
||||||
|
);
|
||||||
|
Ok(feature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Font {
|
||||||
|
font: *mut hb_font_t,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Font {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
hb_font_destroy(self.font);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Font {
|
||||||
|
/// Create a harfbuzz face from a freetype font
|
||||||
|
pub fn new(face: &::ft::ftwrap::Face) -> Font {
|
||||||
|
// hb_ft_font_create_referenced always returns a
|
||||||
|
// pointer to something, or derefs a nullptr internally
|
||||||
|
// if everything fails, so there's nothing for us to
|
||||||
|
// test here.
|
||||||
|
Font { font: unsafe { hb_ft_font_create_referenced(face.face) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform shaping. On entry, Buffer holds the text to shape.
|
||||||
|
/// Once done, Buffer holds the output glyph and position info
|
||||||
|
pub fn shape(&mut self, buf: &mut Buffer, features: Option<&[hb_feature_t]>) {
|
||||||
|
unsafe {
|
||||||
|
if let Some(features) = features {
|
||||||
|
hb_shape(self.font, buf.buf, features.as_ptr(), features.len() as u32)
|
||||||
|
} else {
|
||||||
|
hb_shape(self.font, buf.buf, ptr::null(), 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Buffer {
|
||||||
|
buf: *mut hb_buffer_t,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Buffer {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
hb_buffer_destroy(self.buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Buffer {
|
||||||
|
/// Create a new buffer
|
||||||
|
pub fn new() -> Result<Buffer, Error> {
|
||||||
|
let buf = unsafe { hb_buffer_create() };
|
||||||
|
ensure!(
|
||||||
|
unsafe { hb_buffer_allocation_successful(buf) } != 0,
|
||||||
|
"hb_buffer_create failed"
|
||||||
|
);
|
||||||
|
Ok(Buffer { buf })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reset the buffer back to its initial post-creation state
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
hb_buffer_reset(self.buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_direction(&mut self, direction: hb_direction_t) {
|
||||||
|
unsafe {
|
||||||
|
hb_buffer_set_direction(self.buf, direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_script(&mut self, script: hb_script_t) {
|
||||||
|
unsafe {
|
||||||
|
hb_buffer_set_script(self.buf, script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_language(&mut self, lang: hb_language_t) {
|
||||||
|
unsafe {
|
||||||
|
hb_buffer_set_language(self.buf, lang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, codepoint: hb_codepoint_t, cluster: u32) {
|
||||||
|
unsafe {
|
||||||
|
hb_buffer_add(self.buf, codepoint, cluster);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_utf8(&mut self, buf: &[u8]) {
|
||||||
|
unsafe {
|
||||||
|
hb_buffer_add_utf8(
|
||||||
|
self.buf,
|
||||||
|
buf.as_ptr() as *const i8,
|
||||||
|
buf.len() as i32,
|
||||||
|
0,
|
||||||
|
buf.len() as i32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_str(&mut self, s: &str) {
|
||||||
|
unsafe {
|
||||||
|
hb_buffer_add_utf8(
|
||||||
|
self.buf,
|
||||||
|
s.as_ptr() as *const i8,
|
||||||
|
s.len() as i32,
|
||||||
|
0,
|
||||||
|
s.len() as i32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns glyph information. This is only valid after calling
|
||||||
|
/// font->shape() on this buffer instance.
|
||||||
|
pub fn glyph_infos(&self) -> &[hb_glyph_info_t] {
|
||||||
|
unsafe {
|
||||||
|
let mut len : u32 = 0;
|
||||||
|
let info = hb_buffer_get_glyph_infos(self.buf, &mut len as *mut _);
|
||||||
|
slice::from_raw_parts(info, len as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns glyph positions. This is only valid after calling
|
||||||
|
/// font->shape() on this buffer instance.
|
||||||
|
pub fn glyph_positions(&self) -> &[hb_glyph_position_t] {
|
||||||
|
unsafe {
|
||||||
|
let mut len : u32 = 0;
|
||||||
|
let pos = hb_buffer_get_glyph_positions(self.buf, &mut len as *mut _);
|
||||||
|
slice::from_raw_parts(pos, len as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
font/src/ft/mod.rs
Normal file
17
font/src/ft/mod.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use failure::Error;
|
||||||
|
pub mod ftwrap;
|
||||||
|
pub mod hbwrap;
|
||||||
|
use self::ftwrap::Library;
|
||||||
|
|
||||||
|
pub struct FTEngine {
|
||||||
|
lib: Library,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl ::FontEngine for FTEngine {
|
||||||
|
fn new() -> Result<FTEngine, Error> {
|
||||||
|
Ok(FTEngine {
|
||||||
|
lib: Library::new()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
47
font/src/lib.rs
Normal file
47
font/src/lib.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate failure;
|
||||||
|
|
||||||
|
extern crate harfbuzz_sys;
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
extern crate fontconfig; // from servo-fontconfig
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
extern crate freetype;
|
||||||
|
|
||||||
|
use failure::Error;
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
pub mod ft;
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
pub use ft::FTEngine as Engine;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct FontDescription {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A user provided font description that can be used
|
||||||
|
/// to lookup a font
|
||||||
|
impl FontDescription {
|
||||||
|
pub fn new<S>(name: S) -> FontDescription
|
||||||
|
where
|
||||||
|
S: Into<String>,
|
||||||
|
{
|
||||||
|
FontDescription {
|
||||||
|
name: name.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait FontEngine {
|
||||||
|
fn new() -> Result<Self, Error>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
assert_eq!(2 + 2, 4);
|
||||||
|
}
|
||||||
|
}
|
207
src/main.rs
Normal file
207
src/main.rs
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate failure;
|
||||||
|
extern crate hexdump;
|
||||||
|
|
||||||
|
use failure::Error;
|
||||||
|
extern crate font;
|
||||||
|
extern crate sdl2;
|
||||||
|
use sdl2::event::{Event, WindowEvent};
|
||||||
|
use sdl2::keyboard::Keycode;
|
||||||
|
use sdl2::pixels::{Color, PixelFormatEnum};
|
||||||
|
use sdl2::rect::Rect;
|
||||||
|
use sdl2::render::{BlendMode, Texture, TextureCreator};
|
||||||
|
|
||||||
|
use font::ft::ftwrap;
|
||||||
|
use font::ft::hbwrap;
|
||||||
|
use std::mem;
|
||||||
|
use std::slice;
|
||||||
|
|
||||||
|
struct Glyph<'a> {
|
||||||
|
tex: Texture<'a>,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
x_advance: i32,
|
||||||
|
y_advance: i32,
|
||||||
|
x_offset: i32,
|
||||||
|
y_offset: i32,
|
||||||
|
bearing_x: i32,
|
||||||
|
bearing_y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Glyph<'a> {
|
||||||
|
fn new<T>(
|
||||||
|
texture_creator: &'a TextureCreator<T>,
|
||||||
|
glyph: &ftwrap::FT_GlyphSlotRec_,
|
||||||
|
pos: &hbwrap::hb_glyph_position_t,
|
||||||
|
) -> Result<Glyph<'a>, Error> {
|
||||||
|
let mode: ftwrap::FT_Pixel_Mode =
|
||||||
|
unsafe { mem::transmute(glyph.bitmap.pixel_mode as u32) };
|
||||||
|
|
||||||
|
// pitch is the number of bytes per source row
|
||||||
|
let pitch = glyph.bitmap.pitch.abs() as usize;
|
||||||
|
|
||||||
|
match mode {
|
||||||
|
ftwrap::FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
|
||||||
|
let width = (glyph.bitmap.width as usize) / 3;
|
||||||
|
let height = glyph.bitmap.rows as usize;
|
||||||
|
let mut tex = texture_creator
|
||||||
|
.create_texture_static(
|
||||||
|
Some(PixelFormatEnum::BGR24),
|
||||||
|
width as u32,
|
||||||
|
height as u32,
|
||||||
|
)
|
||||||
|
.map_err(failure::err_msg)?;
|
||||||
|
let data = unsafe {
|
||||||
|
slice::from_raw_parts(glyph.bitmap.buffer, height * pitch)
|
||||||
|
};
|
||||||
|
tex.update(None, data, pitch)?;
|
||||||
|
|
||||||
|
Ok(Glyph {
|
||||||
|
tex,
|
||||||
|
width: width as u32,
|
||||||
|
height: height as u32,
|
||||||
|
x_advance: pos.x_advance / 64,
|
||||||
|
y_advance: pos.y_advance / 64,
|
||||||
|
x_offset: pos.x_offset / 64,
|
||||||
|
y_offset: pos.y_offset / 64,
|
||||||
|
bearing_x: glyph.bitmap_left,
|
||||||
|
bearing_y: glyph.bitmap_top,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
mode @ _ => bail!("unhandled pixel mode: {:?}", mode),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn glyphs_for_text<'a, T>(
|
||||||
|
texture_creator: &'a TextureCreator<T>,
|
||||||
|
s: &str,
|
||||||
|
) -> Result<Vec<Glyph<'a>>, Error> {
|
||||||
|
let mut lib = ftwrap::Library::new()?;
|
||||||
|
lib.set_lcd_filter(
|
||||||
|
ftwrap::FT_LcdFilter::FT_LCD_FILTER_DEFAULT,
|
||||||
|
)?;
|
||||||
|
let mut face =
|
||||||
|
lib.new_face("/home/wez/.fonts/OperatorMonoLig-Book.otf", 0)?;
|
||||||
|
face.set_char_size(0, 36 * 64, 96, 96)?;
|
||||||
|
let mut font = hbwrap::Font::new(&face);
|
||||||
|
let lang = hbwrap::language_from_string("en")?;
|
||||||
|
let mut buf = hbwrap::Buffer::new()?;
|
||||||
|
buf.set_script(hbwrap::HB_SCRIPT_LATIN);
|
||||||
|
buf.set_direction(hbwrap::HB_DIRECTION_LTR);
|
||||||
|
buf.set_language(lang);
|
||||||
|
buf.add_str(s);
|
||||||
|
let features = vec![
|
||||||
|
// kerning
|
||||||
|
hbwrap::feature_from_string("kern")?,
|
||||||
|
// ligatures
|
||||||
|
hbwrap::feature_from_string("liga")?,
|
||||||
|
// contextual ligatures
|
||||||
|
hbwrap::feature_from_string("clig")?,
|
||||||
|
];
|
||||||
|
font.shape(&mut buf, Some(features.as_slice()));
|
||||||
|
|
||||||
|
let infos = buf.glyph_infos();
|
||||||
|
let positions = buf.glyph_positions();
|
||||||
|
let mut result = Vec::new();
|
||||||
|
|
||||||
|
for (i, info) in infos.iter().enumerate() {
|
||||||
|
let pos = &positions[i];
|
||||||
|
println!(
|
||||||
|
"info {} glyph_pos={}, cluster={} x_adv={} y_adv={} x_off={} y_off={}",
|
||||||
|
i,
|
||||||
|
info.codepoint,
|
||||||
|
info.cluster,
|
||||||
|
pos.x_advance,
|
||||||
|
pos.y_advance,
|
||||||
|
pos.x_offset,
|
||||||
|
pos.y_offset
|
||||||
|
);
|
||||||
|
|
||||||
|
let glyph = face.load_and_render_glyph(
|
||||||
|
info.codepoint,
|
||||||
|
(ftwrap::FT_LOAD_COLOR) as i32,
|
||||||
|
ftwrap::FT_Render_Mode::FT_RENDER_MODE_LCD,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
|
||||||
|
let g = Glyph::new(texture_creator, glyph, pos)?;
|
||||||
|
|
||||||
|
/*
|
||||||
|
println!(
|
||||||
|
"width={} height={} advx={} advy={} bearing={},{}",
|
||||||
|
g.width,
|
||||||
|
g.height,
|
||||||
|
g.x_advance,
|
||||||
|
g.y_advance,
|
||||||
|
g.bearing_x,
|
||||||
|
g.bearing_y
|
||||||
|
); */
|
||||||
|
|
||||||
|
result.push(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run() -> Result<(), Error> {
|
||||||
|
|
||||||
|
let sdl_context = sdl2::init().map_err(failure::err_msg)?;
|
||||||
|
let video_subsys = sdl_context.video().map_err(failure::err_msg)?;
|
||||||
|
let window = video_subsys
|
||||||
|
.window("wterm", 1024, 768)
|
||||||
|
.resizable()
|
||||||
|
.opengl()
|
||||||
|
.build()?;
|
||||||
|
let mut canvas = window.into_canvas().build()?;
|
||||||
|
let texture_creator = canvas.texture_creator();
|
||||||
|
let glyphs = glyphs_for_text(&texture_creator, "foo->bar();")?;
|
||||||
|
|
||||||
|
for event in sdl_context
|
||||||
|
.event_pump()
|
||||||
|
.map_err(failure::err_msg)?
|
||||||
|
.wait_iter()
|
||||||
|
{
|
||||||
|
match event {
|
||||||
|
Event::KeyDown { keycode: Some(Keycode::Escape), .. } |
|
||||||
|
Event::Quit { .. } => break,
|
||||||
|
Event::Window { win_event: WindowEvent::Resized(..), .. } => {
|
||||||
|
println!("resize");
|
||||||
|
}
|
||||||
|
Event::Window { win_event: WindowEvent::Exposed, .. } => {
|
||||||
|
println!("exposed");
|
||||||
|
canvas.set_draw_color(Color::RGBA(0, 0, 0, 255));
|
||||||
|
canvas.clear();
|
||||||
|
canvas.set_blend_mode(BlendMode::Blend);
|
||||||
|
|
||||||
|
let mut x = 10i32;
|
||||||
|
let mut y = 100i32;
|
||||||
|
for g in glyphs.iter() {
|
||||||
|
canvas
|
||||||
|
.copy(
|
||||||
|
&g.tex,
|
||||||
|
Some(Rect::new(0, 0, g.width, g.height)),
|
||||||
|
Some(Rect::new(
|
||||||
|
x + g.x_offset - g.bearing_x,
|
||||||
|
y - (g.y_offset + g.bearing_y as i32) as i32,
|
||||||
|
g.width,
|
||||||
|
g.height,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.map_err(failure::err_msg)?;
|
||||||
|
x += g.x_advance;
|
||||||
|
y += g.y_advance;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.present();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
run().unwrap();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user