1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
use cairo::{Context, Format, ImageSurface, Surface};
use pango::{FontDescription, Layout, LayoutExt};
use pangocairo::CairoContextExt;
use geometry::Geometry;
use picture::Picture;
use std::sync::Arc;
use color::Color;
use cairo_sys;
use bar::Bar;
use error::*;
use util;
use xcb;
#[derive(Clone)]
pub struct Text {
pub(crate) arc: Arc<Picture>,
}
impl Text {
pub fn new(
bar: &Bar,
content: &str,
font: Option<&FontDescription>,
color: Option<Color>,
) -> Result<Self> {
if content.is_empty() {
return Err("Text content empty".into());
}
let lifetime_elongater;
let font = if let Some(font) = font {
font
} else {
if let Some(ref font_name) = bar.font {
lifetime_elongater = FontDescription::from_string(font_name);
} else {
lifetime_elongater = FontDescription::new();
}
&lifetime_elongater
};
let conn = Arc::clone(&bar.conn);
let (w, h) = (text_width(content, font)?, bar.geometry.height);
let pix = conn.generate_id();
xtry!(create_pixmap_checked, &conn, 32, pix, bar.window, w, h);
let rect = &[xcb::Rectangle::new(0, 0, w, h)];
xtry!(poly_fill_rectangle_checked, &conn, pix, bar.gcontext, rect);
let mut visualtype = find_visualtype32(&util::screen(&conn)?)
.ok_or_else(|| ErrorKind::ScreenDepthError(()))?;
let surface = unsafe {
Surface::from_raw_full(cairo_sys::cairo_xcb_surface_create(
(conn.get_raw_conn() as *mut cairo_sys::xcb_connection_t),
pix,
(&mut visualtype.base as *mut xcb::ffi::xcb_visualtype_t)
as *mut cairo_sys::xcb_visualtype_t,
i32::from(w),
i32::from(h),
))
};
let context = Context::new(&surface);
let layout = layout(&context, content, font);
let color = if let Some(color) = color {
color.as_fractions()
} else {
bar.color.as_fractions()
};
context.set_source_rgba(color.0, color.1, color.2, color.3);
let (_, text_height) = layout.get_pixel_size();
let text_y = (f64::from(h) - f64::from(text_height)) / 2.;
context.move_to(0., text_y);
context.show_pango_layout(&layout);
let picture = conn.generate_id();
xtry!(@render create_picture_checked, &conn, picture, pix, bar.format32, &[]);
xcb::free_pixmap(&conn, pix);
Ok(Self {
arc: Arc::new(Picture {
conn,
xid: picture,
geometry: Geometry::new(0, 0, w, h),
}),
})
}
}
fn text_width(text: &str, font: &FontDescription) -> Result<(u16)> {
let surface = ImageSurface::create(Format::ARgb32, 0, 0)
.map_err(|e| format!("Unable to create dummy layout for font size: {:?}", e))?;
let context = Context::new(&surface);
let layout = layout(&context, text, font);
let width = layout.get_pixel_size().0;
Ok(width as u16)
}
fn layout(context: &Context, text: &str, font: &FontDescription) -> Layout {
let layout = context.create_pango_layout();
layout.set_text(text);
layout.set_font_description(font);
layout
}
fn find_visualtype32<'s>(screen: &xcb::Screen<'s>) -> Option<xcb::Visualtype> {
for depth in screen.allowed_depths() {
if depth.depth() == 32 {
let visual = depth.visuals().next();
if let Some(visual) = visual {
return Some(visual);
}
}
}
None
}