1pub(crate) mod error;
2pub mod gl;
3mod mat4;
4mod position;
5mod url;
6
7pub use ::beamterm_data::{DebugSpacePattern, FontAtlasData, GlyphEffect};
8pub use beamterm_data::FontStyle;
9pub use error::Error;
10pub use gl::{
11 Atlas, CellData, CellDynamic, CellIterator, CellQuery, Drawable, FontAtlas, GlState, GlyphSlot,
12 GlyphTracker, RenderContext, SelectionMode, SelectionTracker, StaticFontAtlas, TerminalGrid,
13 select,
14};
15pub use position::CursorPosition;
16use unicode_width::UnicodeWidthStr;
17pub use url::{UrlMatch, find_url_at_cursor};
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum GlslVersion {
22 Es300,
24 Gl330,
26}
27
28impl GlslVersion {
29 pub fn vertex_preamble(&self) -> &'static str {
30 match self {
31 Self::Es300 => "#version 300 es\nprecision highp float;\n",
32 Self::Gl330 => "#version 330 core\n",
33 }
34 }
35
36 pub fn fragment_preamble(&self) -> &'static str {
37 match self {
38 Self::Es300 => "#version 300 es\nprecision mediump float;\nprecision highp int;\n",
39 Self::Gl330 => "#version 330 core\n",
40 }
41 }
42}
43
44pub fn is_emoji(s: &str) -> bool {
51 match emojis::get(s) {
52 Some(emoji) => {
53 if emoji.as_str().contains('\u{FE0F}') { s.contains('\u{FE0F}') } else { true }
57 },
58 None => false,
59 }
60}
61
62pub fn is_double_width(grapheme: &str) -> bool {
64 grapheme.len() > 1 && (is_emoji(grapheme) || grapheme.width() == 2)
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 #[test]
72 fn test_is_emoji() {
73 assert!(is_emoji("\u{1F680}"));
75 assert!(is_emoji("\u{1F600}"));
76 assert!(is_emoji("\u{23E9}"));
77 assert!(is_emoji("\u{23EA}"));
78
79 assert!(is_emoji("\u{25B6}\u{FE0F}"));
81
82 assert!(!is_emoji("\u{25B6}"));
84 assert!(!is_emoji("\u{25C0}"));
85 assert!(!is_emoji("\u{23ED}"));
86 assert!(!is_emoji("\u{23F9}"));
87 assert!(!is_emoji("\u{23EE}"));
88 assert!(!is_emoji("\u{25AA}"));
89 assert!(!is_emoji("\u{25AB}"));
90 assert!(!is_emoji("\u{25FC}"));
91
92 assert!(!is_emoji("A"));
94 assert!(!is_emoji("\u{2588}"));
95 }
96
97 #[test]
98 fn test_is_double_width() {
99 assert!(is_double_width("\u{1F600}"));
101 assert!(is_double_width(
102 "\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}"
103 )); [
106 "\u{231A}", "\u{231B}", "\u{23E9}", "\u{23F3}", "\u{2614}", "\u{2615}", "\u{2648}",
107 "\u{2653}", "\u{267F}", "\u{2693}", "\u{26A1}", "\u{26AA}", "\u{26AB}", "\u{26BD}",
108 "\u{26BE}", "\u{26C4}", "\u{26C5}", "\u{26CE}", "\u{26D4}", "\u{26EA}", "\u{26F2}",
109 "\u{26F3}", "\u{26F5}", "\u{26FA}", "\u{26FD}", "\u{25FE}", "\u{2B1B}", "\u{2B1C}",
110 "\u{2B50}", "\u{2B55}", "\u{3030}", "\u{303D}", "\u{3297}", "\u{3299}",
111 ]
112 .iter()
113 .for_each(|s| {
114 assert!(is_double_width(s), "Failed for emoji: {s}");
115 });
116
117 assert!(is_double_width("\u{25B6}\u{FE0F}"));
119 assert!(is_double_width("\u{25C0}\u{FE0F}"));
120
121 assert!(!is_double_width("\u{23F8}"));
123 assert!(!is_double_width("\u{23FA}"));
124 assert!(!is_double_width("\u{25AA}"));
125 assert!(!is_double_width("\u{25AB}"));
126 assert!(!is_double_width("\u{25B6}"));
127 assert!(!is_double_width("\u{25C0}"));
128 assert!(!is_double_width("\u{25FB}"));
129 assert!(!is_double_width("\u{2934}"));
130 assert!(!is_double_width("\u{2935}"));
131 assert!(!is_double_width("\u{2B05}"));
132 assert!(!is_double_width("\u{2B07}"));
133 assert!(!is_double_width("\u{26C8}"));
134
135 assert!(is_double_width("\u{4E2D}"));
137 assert!(is_double_width("\u{65E5}"));
138
139 assert!(!is_double_width("A"));
141 assert!(!is_double_width("\u{2192}"));
142 }
143
144 #[test]
145 fn test_font_atlas_config_deserialization() {
146 let _ = FontAtlasData::default();
147 }
148}