pub(crate) mod error;
pub mod gl;
mod mat4;
mod position;
mod url;
pub use ::beamterm_data::{
CellSize, DebugSpacePattern, FontAtlasData, GlyphEffect, SerializationError, TerminalSize,
};
pub use beamterm_data::FontStyle;
pub use error::Error;
pub use gl::{
Atlas, CellData, CellDynamic, CellIterator, CellQuery, Drawable, FontAtlas, GlState, GlyphSlot,
GlyphTracker, RenderContext, SelectionMode, SelectionTracker, StaticFontAtlas, TerminalGrid,
select,
};
#[cfg(feature = "native-dynamic-atlas")]
pub use gl::{NativeDynamicAtlas, NativeGlyphRasterizer};
pub use position::CursorPosition;
use unicode_width::UnicodeWidthStr;
pub use url::{UrlMatch, find_url_at_cursor};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum GlslVersion {
Es300,
Gl330,
}
impl GlslVersion {
pub fn vertex_preamble(&self) -> &'static str {
match self {
Self::Es300 => "#version 300 es\nprecision highp float;\n",
Self::Gl330 => "#version 330 core\n",
}
}
pub fn fragment_preamble(&self) -> &'static str {
match self {
Self::Es300 => "#version 300 es\nprecision mediump float;\nprecision highp int;\n",
Self::Gl330 => "#version 330 core\n",
}
}
}
pub fn is_emoji(s: &str) -> bool {
let bytes = s.as_bytes();
let first_byte = match bytes.first() {
Some(&b) => b,
None => return false,
};
if first_byte < 0x80 {
return s.len() > 1 && s.width() >= 2;
}
if first_byte < 0xE0 {
return s.len() > 2 && s.width() >= 2;
}
let first = unsafe { s.chars().next().unwrap_unchecked() };
let first_len = first.len_utf8();
if s.len() == first_len {
return if first_len == 3 {
is_emoji_presentation(first)
} else {
s.width() >= 2 && is_emoji_presentation(first)
};
}
s.width() >= 2
}
pub fn is_double_width(grapheme: &str) -> bool {
grapheme.width() >= 2
}
fn is_emoji_presentation(c: char) -> bool {
let cp = c as u32;
match cp {
0x231A..=0x2B55 => matches!(
cp,
0x231A..=0x231B | 0x23E9..=0x23EC | 0x23F0 | 0x23F3 | 0x25FD..=0x25FE | 0x2614..=0x2615 | 0x2648..=0x2653 | 0x267F | 0x2693 | 0x26A1 | 0x26AA..=0x26AB | 0x26BD..=0x26BE | 0x26C4..=0x26C5 | 0x26CE | 0x26D4 | 0x26EA | 0x26F2..=0x26F3 | 0x26F5 | 0x26FA | 0x26FD | 0x2705 | 0x270A..=0x270B | 0x2728 | 0x274C | 0x274E | 0x2753..=0x2755 | 0x2757 | 0x2795..=0x2797 | 0x27B0 | 0x27BF | 0x2B1B..=0x2B1C | 0x2B50 | 0x2B55 ),
0x1F000..=0x1FFFF => !matches!(
cp,
0x1F200
| 0x1F202..=0x1F219
| 0x1F21B..=0x1F22E
| 0x1F230..=0x1F231
| 0x1F237
| 0x1F23B..=0x1F24F
| 0x1F260..=0x1F265
),
_ => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_emoji() {
assert!(is_emoji("\u{1F680}"));
assert!(is_emoji("\u{1F600}"));
assert!(is_emoji("\u{23E9}"));
assert!(is_emoji("\u{23EA}"));
assert!(is_emoji("\u{25B6}\u{FE0F}"));
assert!(!is_emoji("\u{25B6}"));
assert!(!is_emoji("\u{25C0}"));
assert!(!is_emoji("\u{23ED}"));
assert!(!is_emoji("\u{23F9}"));
assert!(!is_emoji("\u{23EE}"));
assert!(!is_emoji("\u{25AA}"));
assert!(!is_emoji("\u{25AB}"));
assert!(!is_emoji("\u{25FC}"));
assert!(!is_emoji("A"));
assert!(!is_emoji("\u{2588}"));
}
#[test]
fn test_is_double_width() {
assert!(is_double_width("\u{1F600}"));
assert!(is_double_width(
"\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}"
));
[
"\u{231A}", "\u{231B}", "\u{23E9}", "\u{23F3}", "\u{2614}", "\u{2615}", "\u{2648}",
"\u{2653}", "\u{267F}", "\u{2693}", "\u{26A1}", "\u{26AA}", "\u{26AB}", "\u{26BD}",
"\u{26BE}", "\u{26C4}", "\u{26C5}", "\u{26CE}", "\u{26D4}", "\u{26EA}", "\u{26F2}",
"\u{26F3}", "\u{26F5}", "\u{26FA}", "\u{26FD}", "\u{25FE}", "\u{2B1B}", "\u{2B1C}",
"\u{2B50}", "\u{2B55}", "\u{3030}", "\u{303D}", "\u{3297}", "\u{3299}",
]
.iter()
.for_each(|s| {
assert!(is_double_width(s), "Failed for emoji: {s}");
});
assert!(is_double_width("\u{25B6}\u{FE0F}"));
assert!(is_double_width("\u{25C0}\u{FE0F}"));
assert!(!is_double_width("\u{23F8}"));
assert!(!is_double_width("\u{23FA}"));
assert!(!is_double_width("\u{25AA}"));
assert!(!is_double_width("\u{25AB}"));
assert!(!is_double_width("\u{25B6}"));
assert!(!is_double_width("\u{25C0}"));
assert!(!is_double_width("\u{25FB}"));
assert!(!is_double_width("\u{2934}"));
assert!(!is_double_width("\u{2935}"));
assert!(!is_double_width("\u{2B05}"));
assert!(!is_double_width("\u{2B07}"));
assert!(!is_double_width("\u{26C8}"));
assert!(is_double_width("\u{4E2D}"));
assert!(is_double_width("\u{65E5}"));
assert!(!is_double_width("A"));
assert!(!is_double_width("\u{2192}"));
}
#[test]
fn test_font_atlas_config_deserialization() {
let _ = FontAtlasData::default();
}
}