diff --git a/Cargo.lock b/Cargo.lock index daefdd2c..c2bb07e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -713,6 +713,7 @@ dependencies = [ "jay-io-uring", "jay-layout-animation", "jay-logger", + "jay-pango", "jay-pr-caps", "jay-sighand", "jay-time", @@ -886,6 +887,17 @@ dependencies = [ "uapi", ] +[[package]] +name = "jay-pango" +version = "0.1.0" +dependencies = [ + "anyhow", + "jay-geometry", + "repc", + "thiserror", + "uapi", +] + [[package]] name = "jay-pr-caps" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index c18f1c43..c3fa822e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ members = [ "bugs", "logger", "video-types", + "pango", "toml-config", "algorithms", "toml-spec", @@ -87,6 +88,7 @@ jay-pr-caps = { version = "0.1.0", path = "pr-caps" } jay-bugs = { version = "0.1.0", path = "bugs" } jay-logger = { version = "0.1.0", path = "logger" } jay-video-types = { version = "0.1.0", path = "video-types" } +jay-pango = { version = "0.1.0", path = "pango" } uapi = "0.2.13" thiserror = "2.0.11" diff --git a/build/enums.rs b/build/enums.rs index 8be06bf1..753fcb69 100644 --- a/build/enums.rs +++ b/build/enums.rs @@ -12,9 +12,6 @@ mod macros; #[path = "../src/libinput/consts.rs"] mod libinput; -#[path = "../src/pango/consts.rs"] -mod pango; - #[path = "../src/fontconfig/consts.rs"] mod fontconfig; @@ -145,12 +142,6 @@ pub fn main() -> anyhow::Result<()> { "libinput_config_middle_emulation_state", )?; - let mut f = open("pango_tys.rs")?; - write_ty(&mut f, pango::CAIRO_FORMATS, "cairo_format_t")?; - write_ty(&mut f, pango::CAIRO_STATUSES, "cairo_status_t")?; - write_ty(&mut f, pango::CAIRO_OPERATORS, "cairo_operator_t")?; - write_ty(&mut f, pango::PANGO_ELLIPSIZE_MODES, "PangoEllipsizeMode_")?; - let mut f = open("fontconfig_tys.rs")?; write_ty(&mut f, fontconfig::FC_MATCH_KINDS, "FcMatchKind")?; write_ty(&mut f, fontconfig::FC_RESULTS, "FcResult")?; diff --git a/pango/Cargo.toml b/pango/Cargo.toml new file mode 100644 index 00000000..ffea2362 --- /dev/null +++ b/pango/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "jay-pango" +version = "0.1.0" +edition = "2024" +license = "GPL-3.0-only" +build = "build.rs" + +[dependencies] +jay-geometry = { version = "0.1.0", path = "../geometry" } + +thiserror = "2.0.11" +uapi = "0.2.13" + +[build-dependencies] +anyhow = "1.0.79" +repc = "0.1.1" diff --git a/pango/build.rs b/pango/build.rs new file mode 100644 index 00000000..9eaf108a --- /dev/null +++ b/pango/build.rs @@ -0,0 +1,85 @@ +use { + repc::layout::{Type, TypeVariant}, + std::{ + env, + fs::{File, OpenOptions}, + io::{self, BufWriter, Write}, + path::PathBuf, + }, +}; + +#[allow(unused_macros)] +macro_rules! cenum { + ($name:ident, $uc:ident; $($name2:ident = $val:expr,)*) => { + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub struct $name(pub i32); + + impl $name { + pub fn raw(self) -> i32 { + self.0 + } + } + + $( + pub const $name2: $name = $name($val); + )* + + pub const $uc: &[i32] = &[$($val,)*]; + }; +} + +#[path = "src/consts.rs"] +mod consts; + +fn open(s: &str) -> io::Result> { + let mut path = PathBuf::from(env::var("OUT_DIR").unwrap()); + path.push(s); + Ok(BufWriter::new( + OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(path)?, + )) +} + +fn get_target() -> repc::Target { + let rustc_target = env::var("TARGET").unwrap(); + repc::TARGET_MAP + .iter() + .cloned() + .find(|t| t.0 == rustc_target) + .unwrap() + .1 +} + +fn get_enum_ty(variants: Vec) -> anyhow::Result { + let target = get_target(); + let ty = Type { + layout: (), + annotations: vec![], + variant: TypeVariant::Enum(variants), + }; + let ty = repc::compute_layout(target, &ty)?; + assert!(ty.layout.pointer_alignment_bits <= ty.layout.size_bits); + Ok(ty.layout.size_bits) +} + +fn write_ty(f: &mut W, vals: &[i32], ty: &str) -> anyhow::Result<()> { + let variants: Vec<_> = vals.iter().cloned().map(|v| v as i128).collect(); + let size = get_enum_ty(variants)?; + writeln!(f, "#[allow(clippy::allow_attributes, dead_code)]")?; + writeln!(f, "pub type {} = i{};", ty, size)?; + Ok(()) +} + +fn main() -> anyhow::Result<()> { + let mut f = open("pango_tys.rs")?; + write_ty(&mut f, consts::CAIRO_FORMATS, "cairo_format_t")?; + write_ty(&mut f, consts::CAIRO_STATUSES, "cairo_status_t")?; + write_ty(&mut f, consts::CAIRO_OPERATORS, "cairo_operator_t")?; + write_ty(&mut f, consts::PANGO_ELLIPSIZE_MODES, "PangoEllipsizeMode_")?; + + println!("cargo:rerun-if-changed=src/consts.rs"); + Ok(()) +} diff --git a/src/pango/consts.rs b/pango/src/consts.rs similarity index 100% rename from src/pango/consts.rs rename to pango/src/consts.rs diff --git a/pango/src/lib.rs b/pango/src/lib.rs new file mode 100644 index 00000000..c1eba34c --- /dev/null +++ b/pango/src/lib.rs @@ -0,0 +1,438 @@ +#![allow(non_camel_case_types)] + +use { + crate::consts::{CairoFormat, CairoOperator, PangoEllipsizeMode}, + jay_geometry::Rect, + std::{cell::Cell, ptr, rc::Rc}, + thiserror::Error, + uapi::{ + IntoUstr, + c::{self, memset}, + }, +}; + +macro_rules! cenum { + ($name:ident, $uc:ident; $($name2:ident = $val:expr,)*) => { + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub struct $name(pub i32); + + impl $name { + #[allow(dead_code)] + pub fn raw(self) -> i32 { + self.0 + } + } + + $( + pub const $name2: $name = $name($val); + )* + + pub const $uc: &[i32] = &[$($val,)*]; + }; +} + +pub mod consts; + +include!(concat!(env!("OUT_DIR"), "/pango_tys.rs")); + +#[repr(transparent)] +struct cairo_surface_t(u8); +#[repr(transparent)] +struct cairo_t(u8); + +#[link(name = "cairo")] +unsafe extern "C" { + fn cairo_image_surface_create( + format: cairo_format_t, + width: c::c_int, + height: c::c_int, + ) -> *mut cairo_surface_t; + fn cairo_image_surface_create_for_data( + data: *mut u8, + format: cairo_format_t, + width: c::c_int, + height: c::c_int, + stride: c::c_int, + ) -> *mut cairo_surface_t; + fn cairo_image_surface_get_height(surface: *mut cairo_surface_t) -> c::c_int; + fn cairo_image_surface_get_stride(surface: *mut cairo_surface_t) -> c::c_int; + fn cairo_image_surface_get_data(surface: *mut cairo_surface_t) -> *mut u8; + + fn cairo_surface_destroy(surface: *mut cairo_surface_t); + fn cairo_surface_status(surface: *mut cairo_surface_t) -> cairo_status_t; + fn cairo_surface_flush(surface: *mut cairo_surface_t); + + fn cairo_create(surface: *mut cairo_surface_t) -> *mut cairo_t; + fn cairo_status(cairo: *mut cairo_t) -> cairo_status_t; + fn cairo_destroy(cairo: *mut cairo_t); + + fn cairo_set_operator(cr: *mut cairo_t, op: cairo_operator_t); + fn cairo_set_source_rgba(cr: *mut cairo_t, red: f64, green: f64, blue: f64, alpha: f64); + fn cairo_move_to(cr: *mut cairo_t, x: f64, y: f64); + + fn cairo_format_stride_for_width(format: cairo_format_t, width: c::c_int) -> c::c_int; +} + +#[repr(transparent)] +struct PangoContext_(u8); + +#[link(name = "pangocairo-1.0")] +unsafe extern "C" { + fn pango_cairo_create_context(cr: *mut cairo_t) -> *mut PangoContext_; + fn pango_cairo_show_layout(cr: *mut cairo_t, layout: *mut PangoLayout_); +} + +#[repr(transparent)] +struct GObject(u8); + +#[link(name = "gobject-2.0")] +unsafe extern "C" { + fn g_object_unref(object: *mut GObject); +} + +#[repr(transparent)] +struct PangoFontDescription_(u8); +#[repr(transparent)] +struct PangoLayout_(u8); + +#[link(name = "pango-1.0")] +unsafe extern "C" { + fn pango_font_description_from_string(str: *const c::c_char) -> *mut PangoFontDescription_; + fn pango_font_description_free(desc: *mut PangoFontDescription_); + fn pango_font_description_get_size(desc: *mut PangoFontDescription_) -> c::c_int; + fn pango_font_description_set_size(desc: *mut PangoFontDescription_, size: c::c_int); + + fn pango_layout_new(context: *mut PangoContext_) -> *mut PangoLayout_; + fn pango_layout_set_width(layout: *mut PangoLayout_, width: c::c_int); + fn pango_layout_set_ellipsize(layout: *mut PangoLayout_, ellipsize: PangoEllipsizeMode_); + fn pango_layout_set_font_description( + layout: *mut PangoLayout_, + desc: *const PangoFontDescription_, + ); + fn pango_layout_set_text(layout: *mut PangoLayout_, text: *const c::c_char, length: c::c_int); + fn pango_layout_set_markup(layout: *mut PangoLayout_, text: *const c::c_char, length: c::c_int); + fn pango_layout_get_pixel_size( + layout: *mut PangoLayout_, + width: *mut c::c_int, + height: *mut c::c_int, + ); + fn pango_layout_get_extents( + layout: *mut PangoLayout_, + ink_rect: *mut PangoRectangle, + logical_rect: *mut PangoRectangle, + ); + fn pango_layout_get_baseline(layout: *mut PangoLayout_) -> c::c_int; + + fn pango_extents_to_pixels(inclusive: *mut PangoRectangle, nearest: *mut PangoRectangle); +} + +#[derive(Debug, Error)] +pub enum PangoError { + #[error("Could not create an image surface: {0}")] + CreateSurface(u32), + #[error("Could not create a cairo context: {0}")] + CreateCairo(u32), + #[error("Could not create a pangocairo context")] + CreatePangoCairo, + #[error("Could not create a pango layout")] + CreateLayout, + #[error("Could not retrieve image data")] + GetData, +} + +#[repr(C)] +#[derive(Default)] +struct PangoRectangle { + x: c::c_int, + y: c::c_int, + width: c::c_int, + height: c::c_int, +} + +const PANGO_SCALE: i32 = 1024; + +pub struct CairoImageSurface { + s: *mut cairo_surface_t, +} + +impl CairoImageSurface { + pub fn new_image_surface( + format: CairoFormat, + width: i32, + height: i32, + ) -> Result, PangoError> { + unsafe { + let s = cairo_image_surface_create(format.raw() as _, width as _, height as _); + let status = cairo_surface_status(s); + if status != 0 { + return Err(PangoError::CreateSurface(status as _)); + } + Ok(Rc::new(Self { s })) + } + } + + pub unsafe fn new_image_surface_with_data( + format: CairoFormat, + data: *mut u8, + width: i32, + height: i32, + stride: i32, + ) -> Result, PangoError> { + unsafe { + memset(data.cast(), 0, (stride * height) as usize); + let s = cairo_image_surface_create_for_data( + data, + format.raw() as _, + width as _, + height as _, + stride as _, + ); + let status = cairo_surface_status(s); + if status != 0 { + return Err(PangoError::CreateSurface(status as _)); + } + Ok(Rc::new(Self { s })) + } + } + + pub fn create_context(self: &Rc) -> Result, PangoError> { + unsafe { + let c = cairo_create(self.s); + let status = cairo_status(c); + if status != 0 { + return Err(PangoError::CreateCairo(status as _)); + } + Ok(Rc::new(CairoContext { + _s: self.clone(), + c, + })) + } + } + + pub fn flush(&self) { + unsafe { + cairo_surface_flush(self.s); + } + } + + pub fn height(&self) -> i32 { + unsafe { cairo_image_surface_get_height(self.s) as _ } + } + + pub fn stride(&self) -> i32 { + unsafe { cairo_image_surface_get_stride(self.s) as _ } + } + + pub fn data(&self) -> Result<&[Cell], PangoError> { + unsafe { + let d = cairo_image_surface_get_data(self.s); + if d.is_null() { + return Err(PangoError::GetData); + } + let size = self.height() as usize * self.stride() as usize; + Ok(std::slice::from_raw_parts(d.cast(), size)) + } + } +} + +impl Drop for CairoImageSurface { + fn drop(&mut self) { + unsafe { + cairo_surface_destroy(self.s); + } + } +} + +pub struct CairoContext { + _s: Rc, + c: *mut cairo_t, +} + +impl CairoContext { + pub fn create_pango_context(self: &Rc) -> Result, PangoError> { + unsafe { + let p = pango_cairo_create_context(self.c); + if p.is_null() { + return Err(PangoError::CreatePangoCairo); + } + Ok(Rc::new(PangoCairoContext { c: self.clone(), p })) + } + } + + pub fn set_operator(&self, op: CairoOperator) { + unsafe { + cairo_set_operator(self.c, op.raw() as _); + } + } + + pub fn set_source_rgba(&self, r: f64, g: f64, b: f64, a: f64) { + unsafe { + cairo_set_source_rgba(self.c, r, g, b, a); + } + } + + pub fn move_to(&self, x: f64, y: f64) { + unsafe { + cairo_move_to(self.c, x, y); + } + } +} + +impl Drop for CairoContext { + fn drop(&mut self) { + unsafe { + cairo_destroy(self.c); + } + } +} + +pub struct PangoCairoContext { + c: Rc, + p: *mut PangoContext_, +} + +impl PangoCairoContext { + pub fn create_layout(self: &Rc) -> Result { + unsafe { + let l = pango_layout_new(self.p as _); + if l.is_null() { + return Err(PangoError::CreateLayout); + } + Ok(PangoLayout { c: self.clone(), l }) + } + } +} + +impl Drop for PangoCairoContext { + fn drop(&mut self) { + unsafe { + g_object_unref(self.p as _); + } + } +} + +pub struct PangoFontDescription { + s: *mut PangoFontDescription_, +} + +impl PangoFontDescription { + pub fn from_string<'a>(s: impl IntoUstr<'a>) -> Self { + let s = s.into_ustr(); + Self { + s: unsafe { pango_font_description_from_string(s.as_ptr()) }, + } + } + + pub fn size(&self) -> i32 { + unsafe { pango_font_description_get_size(self.s) as _ } + } + + pub fn set_size(&mut self, size: i32) { + unsafe { + pango_font_description_set_size(self.s, size); + } + } +} + +impl Drop for PangoFontDescription { + fn drop(&mut self) { + unsafe { + pango_font_description_free(self.s); + } + } +} + +pub struct PangoLayout { + c: Rc, + l: *mut PangoLayout_, +} + +impl PangoLayout { + pub fn set_width(&self, width: i32) { + unsafe { + pango_layout_set_width(self.l, width as _); + } + } + + pub fn set_ellipsize(&self, ellipsize: PangoEllipsizeMode) { + unsafe { + pango_layout_set_ellipsize(self.l, ellipsize.raw() as _); + } + } + + pub fn set_font_description(&self, fd: &PangoFontDescription) { + unsafe { + pango_layout_set_font_description(self.l, fd.s); + } + } + + pub fn set_text(&self, text: &str) { + unsafe { + pango_layout_set_text(self.l, text.as_ptr() as _, text.len() as _); + } + } + + pub fn set_markup(&self, text: &str) { + unsafe { + pango_layout_set_markup(self.l, text.as_ptr() as _, text.len() as _); + } + } + + pub fn pixel_size(&self) -> (i32, i32) { + unsafe { + let mut w = 0; + let mut h = 0; + pango_layout_get_pixel_size(self.l, &mut w, &mut h); + (w as _, h as _) + } + } + + pub fn inc_pixel_rect(&self) -> Rect { + unsafe { + let mut rect = PangoRectangle::default(); + pango_layout_get_extents(self.l, &mut rect, ptr::null_mut()); + pango_extents_to_pixels(&mut rect, ptr::null_mut()); + Rect::new_sized_saturating(rect.x, rect.y, rect.width, rect.height) + } + } + + pub fn logical_pixel_rect(&self) -> Rect { + unsafe { + let mut rect = PangoRectangle::default(); + pango_layout_get_extents(self.l, ptr::null_mut(), &mut rect); + pango_extents_to_pixels(&mut rect, ptr::null_mut()); + Rect::new_sized_saturating(rect.x, rect.y, rect.width, rect.height) + } + } + + pub fn pixel_baseline(&self) -> i32 { + unsafe { + let res = pango_layout_get_baseline(self.l); + (res as i32 + PANGO_SCALE - 1) / PANGO_SCALE + } + } + + pub fn show_layout(&self) { + unsafe { + pango_cairo_show_layout(self.c.c.c, self.l); + } + } +} + +impl Drop for PangoLayout { + fn drop(&mut self) { + unsafe { + g_object_unref(self.l as _); + } + } +} + +pub fn cairo_size(format: CairoFormat, width: i32, height: i32) -> Option<(i32, usize)> { + let stride = unsafe { cairo_format_stride_for_width(format.raw() as _, width as _) }; + if stride < 0 { + return None; + } + let stride = stride as i32; + let size = height.checked_mul(stride)? as usize; + Some((stride, size)) +} diff --git a/src/pango.rs b/src/pango.rs index cd78b351..521c4b03 100644 --- a/src/pango.rs +++ b/src/pango.rs @@ -1,420 +1 @@ -#![allow(non_camel_case_types)] - -use { - crate::{ - pango::consts::{CairoFormat, CairoOperator, PangoEllipsizeMode}, - rect::Rect, - }, - std::{cell::Cell, ptr, rc::Rc}, - thiserror::Error, - uapi::{ - IntoUstr, - c::{self, memset}, - }, -}; - -pub mod consts; - -include!(concat!(env!("OUT_DIR"), "/pango_tys.rs")); - -#[repr(transparent)] -struct cairo_surface_t(u8); -#[repr(transparent)] -struct cairo_t(u8); - -#[link(name = "cairo")] -unsafe extern "C" { - fn cairo_image_surface_create( - format: cairo_format_t, - width: c::c_int, - height: c::c_int, - ) -> *mut cairo_surface_t; - fn cairo_image_surface_create_for_data( - data: *mut u8, - format: cairo_format_t, - width: c::c_int, - height: c::c_int, - stride: c::c_int, - ) -> *mut cairo_surface_t; - fn cairo_image_surface_get_height(surface: *mut cairo_surface_t) -> c::c_int; - fn cairo_image_surface_get_stride(surface: *mut cairo_surface_t) -> c::c_int; - fn cairo_image_surface_get_data(surface: *mut cairo_surface_t) -> *mut u8; - - fn cairo_surface_destroy(surface: *mut cairo_surface_t); - fn cairo_surface_status(surface: *mut cairo_surface_t) -> cairo_status_t; - fn cairo_surface_flush(surface: *mut cairo_surface_t); - - fn cairo_create(surface: *mut cairo_surface_t) -> *mut cairo_t; - fn cairo_status(cairo: *mut cairo_t) -> cairo_status_t; - fn cairo_destroy(cairo: *mut cairo_t); - - fn cairo_set_operator(cr: *mut cairo_t, op: cairo_operator_t); - fn cairo_set_source_rgba(cr: *mut cairo_t, red: f64, green: f64, blue: f64, alpha: f64); - fn cairo_move_to(cr: *mut cairo_t, x: f64, y: f64); - - fn cairo_format_stride_for_width(format: cairo_format_t, width: c::c_int) -> c::c_int; -} - -#[repr(transparent)] -struct PangoContext_(u8); - -#[link(name = "pangocairo-1.0")] -unsafe extern "C" { - fn pango_cairo_create_context(cr: *mut cairo_t) -> *mut PangoContext_; - fn pango_cairo_show_layout(cr: *mut cairo_t, layout: *mut PangoLayout_); -} - -#[repr(transparent)] -struct GObject(u8); - -#[link(name = "gobject-2.0")] -unsafe extern "C" { - fn g_object_unref(object: *mut GObject); -} - -#[repr(transparent)] -struct PangoFontDescription_(u8); -#[repr(transparent)] -struct PangoLayout_(u8); - -#[link(name = "pango-1.0")] -unsafe extern "C" { - fn pango_font_description_from_string(str: *const c::c_char) -> *mut PangoFontDescription_; - fn pango_font_description_free(desc: *mut PangoFontDescription_); - fn pango_font_description_get_size(desc: *mut PangoFontDescription_) -> c::c_int; - fn pango_font_description_set_size(desc: *mut PangoFontDescription_, size: c::c_int); - - fn pango_layout_new(context: *mut PangoContext_) -> *mut PangoLayout_; - fn pango_layout_set_width(layout: *mut PangoLayout_, width: c::c_int); - fn pango_layout_set_ellipsize(layout: *mut PangoLayout_, ellipsize: PangoEllipsizeMode_); - fn pango_layout_set_font_description( - layout: *mut PangoLayout_, - desc: *const PangoFontDescription_, - ); - fn pango_layout_set_text(layout: *mut PangoLayout_, text: *const c::c_char, length: c::c_int); - fn pango_layout_set_markup(layout: *mut PangoLayout_, text: *const c::c_char, length: c::c_int); - fn pango_layout_get_pixel_size( - layout: *mut PangoLayout_, - width: *mut c::c_int, - height: *mut c::c_int, - ); - fn pango_layout_get_extents( - layout: *mut PangoLayout_, - ink_rect: *mut PangoRectangle, - logical_rect: *mut PangoRectangle, - ); - fn pango_layout_get_baseline(layout: *mut PangoLayout_) -> c::c_int; - - fn pango_extents_to_pixels(inclusive: *mut PangoRectangle, nearest: *mut PangoRectangle); -} - -#[derive(Debug, Error)] -pub enum PangoError { - #[error("Could not create an image surface: {0}")] - CreateSurface(u32), - #[error("Could not create a cairo context: {0}")] - CreateCairo(u32), - #[error("Could not create a pangocairo context")] - CreatePangoCairo, - #[error("Could not create a pango layout")] - CreateLayout, - #[error("Could not retrieve image data")] - GetData, -} - -#[repr(C)] -#[derive(Default)] -struct PangoRectangle { - x: c::c_int, - y: c::c_int, - width: c::c_int, - height: c::c_int, -} - -const PANGO_SCALE: i32 = 1024; - -pub struct CairoImageSurface { - s: *mut cairo_surface_t, -} - -impl CairoImageSurface { - pub fn new_image_surface( - format: CairoFormat, - width: i32, - height: i32, - ) -> Result, PangoError> { - unsafe { - let s = cairo_image_surface_create(format.raw() as _, width as _, height as _); - let status = cairo_surface_status(s); - if status != 0 { - return Err(PangoError::CreateSurface(status as _)); - } - Ok(Rc::new(Self { s })) - } - } - - pub unsafe fn new_image_surface_with_data( - format: CairoFormat, - data: *mut u8, - width: i32, - height: i32, - stride: i32, - ) -> Result, PangoError> { - unsafe { - memset(data.cast(), 0, (stride * height) as usize); - let s = cairo_image_surface_create_for_data( - data, - format.raw() as _, - width as _, - height as _, - stride as _, - ); - let status = cairo_surface_status(s); - if status != 0 { - return Err(PangoError::CreateSurface(status as _)); - } - Ok(Rc::new(Self { s })) - } - } - - pub fn create_context(self: &Rc) -> Result, PangoError> { - unsafe { - let c = cairo_create(self.s); - let status = cairo_status(c); - if status != 0 { - return Err(PangoError::CreateCairo(status as _)); - } - Ok(Rc::new(CairoContext { - _s: self.clone(), - c, - })) - } - } - - pub fn flush(&self) { - unsafe { - cairo_surface_flush(self.s); - } - } - - pub fn height(&self) -> i32 { - unsafe { cairo_image_surface_get_height(self.s) as _ } - } - - pub fn stride(&self) -> i32 { - unsafe { cairo_image_surface_get_stride(self.s) as _ } - } - - pub fn data(&self) -> Result<&[Cell], PangoError> { - unsafe { - let d = cairo_image_surface_get_data(self.s); - if d.is_null() { - return Err(PangoError::GetData); - } - let size = self.height() as usize * self.stride() as usize; - Ok(std::slice::from_raw_parts(d.cast(), size)) - } - } -} - -impl Drop for CairoImageSurface { - fn drop(&mut self) { - unsafe { - cairo_surface_destroy(self.s); - } - } -} - -pub struct CairoContext { - _s: Rc, - c: *mut cairo_t, -} - -impl CairoContext { - pub fn create_pango_context(self: &Rc) -> Result, PangoError> { - unsafe { - let p = pango_cairo_create_context(self.c); - if p.is_null() { - return Err(PangoError::CreatePangoCairo); - } - Ok(Rc::new(PangoCairoContext { c: self.clone(), p })) - } - } - - pub fn set_operator(&self, op: CairoOperator) { - unsafe { - cairo_set_operator(self.c, op.raw() as _); - } - } - - pub fn set_source_rgba(&self, r: f64, g: f64, b: f64, a: f64) { - unsafe { - cairo_set_source_rgba(self.c, r, g, b, a); - } - } - - pub fn move_to(&self, x: f64, y: f64) { - unsafe { - cairo_move_to(self.c, x, y); - } - } -} - -impl Drop for CairoContext { - fn drop(&mut self) { - unsafe { - cairo_destroy(self.c); - } - } -} - -pub struct PangoCairoContext { - c: Rc, - p: *mut PangoContext_, -} - -impl PangoCairoContext { - pub fn create_layout(self: &Rc) -> Result { - unsafe { - let l = pango_layout_new(self.p as _); - if l.is_null() { - return Err(PangoError::CreateLayout); - } - Ok(PangoLayout { c: self.clone(), l }) - } - } -} - -impl Drop for PangoCairoContext { - fn drop(&mut self) { - unsafe { - g_object_unref(self.p as _); - } - } -} - -pub struct PangoFontDescription { - s: *mut PangoFontDescription_, -} - -impl PangoFontDescription { - pub fn from_string<'a>(s: impl IntoUstr<'a>) -> Self { - let s = s.into_ustr(); - Self { - s: unsafe { pango_font_description_from_string(s.as_ptr()) }, - } - } - - pub fn size(&self) -> i32 { - unsafe { pango_font_description_get_size(self.s) as _ } - } - - pub fn set_size(&mut self, size: i32) { - unsafe { - pango_font_description_set_size(self.s, size); - } - } -} - -impl Drop for PangoFontDescription { - fn drop(&mut self) { - unsafe { - pango_font_description_free(self.s); - } - } -} - -pub struct PangoLayout { - c: Rc, - l: *mut PangoLayout_, -} - -impl PangoLayout { - pub fn set_width(&self, width: i32) { - unsafe { - pango_layout_set_width(self.l, width as _); - } - } - - pub fn set_ellipsize(&self, ellipsize: PangoEllipsizeMode) { - unsafe { - pango_layout_set_ellipsize(self.l, ellipsize.raw() as _); - } - } - - pub fn set_font_description(&self, fd: &PangoFontDescription) { - unsafe { - pango_layout_set_font_description(self.l, fd.s); - } - } - - pub fn set_text(&self, text: &str) { - unsafe { - pango_layout_set_text(self.l, text.as_ptr() as _, text.len() as _); - } - } - - pub fn set_markup(&self, text: &str) { - unsafe { - pango_layout_set_markup(self.l, text.as_ptr() as _, text.len() as _); - } - } - - pub fn pixel_size(&self) -> (i32, i32) { - unsafe { - let mut w = 0; - let mut h = 0; - pango_layout_get_pixel_size(self.l, &mut w, &mut h); - (w as _, h as _) - } - } - - pub fn inc_pixel_rect(&self) -> Rect { - unsafe { - let mut rect = PangoRectangle::default(); - pango_layout_get_extents(self.l, &mut rect, ptr::null_mut()); - pango_extents_to_pixels(&mut rect, ptr::null_mut()); - Rect::new_sized_saturating(rect.x, rect.y, rect.width, rect.height) - } - } - - pub fn logical_pixel_rect(&self) -> Rect { - unsafe { - let mut rect = PangoRectangle::default(); - pango_layout_get_extents(self.l, ptr::null_mut(), &mut rect); - pango_extents_to_pixels(&mut rect, ptr::null_mut()); - Rect::new_sized_saturating(rect.x, rect.y, rect.width, rect.height) - } - } - - pub fn pixel_baseline(&self) -> i32 { - unsafe { - let res = pango_layout_get_baseline(self.l); - (res as i32 + PANGO_SCALE - 1) / PANGO_SCALE - } - } - - pub fn show_layout(&self) { - unsafe { - pango_cairo_show_layout(self.c.c.c, self.l); - } - } -} - -impl Drop for PangoLayout { - fn drop(&mut self) { - unsafe { - g_object_unref(self.l as _); - } - } -} - -pub fn cairo_size(format: CairoFormat, width: i32, height: i32) -> Option<(i32, usize)> { - let stride = unsafe { cairo_format_stride_for_width(format.raw() as _, width as _) }; - if stride < 0 { - return None; - } - let stride = stride as i32; - let size = height.checked_mul(stride)? as usize; - Some((stride, size)) -} +pub use jay_pango::*;