1use std::{
4 any::Any,
5 cell::{Cell, RefCell},
6 io,
7 panic::AssertUnwindSafe,
8 ptr,
9 rc::Rc,
10};
11
12use crate::ffi;
13use libc::{c_double, c_uchar, c_uint, c_void};
14
15use crate::{Error, Surface, UserDataKey};
16
17macro_rules! for_stream_constructors {
18 ($constructor_ffi: ident) => {
19 pub fn for_stream<W: io::Write + 'static>(
26 width: f64,
27 height: f64,
28 stream: W,
29 ) -> Result<Self, crate::error::Error> {
30 Ok(Self(Surface::_for_stream(
31 ffi::$constructor_ffi,
32 width,
33 height,
34 stream,
35 )?))
36 }
37
38 pub unsafe fn for_raw_stream<W: io::Write + 'static>(
51 width: f64,
52 height: f64,
53 stream: *mut W,
54 ) -> Result<Self, crate::error::Error> {
55 unsafe {
56 Ok(Self(Surface::_for_raw_stream(
57 ffi::$constructor_ffi,
58 width,
59 height,
60 stream,
61 )?))
62 }
63 }
64 };
65}
66
67impl Surface {
68 pub(crate) fn _for_stream<W: io::Write + 'static>(
69 constructor: Constructor,
70 width: f64,
71 height: f64,
72 stream: W,
73 ) -> Result<Self, Error> {
74 let env_rc = Rc::new(CallbackEnvironment {
75 mutable: RefCell::new(MutableCallbackEnvironment {
76 stream: Some((Box::new(stream), None)),
77 unwind_payload: None,
78 }),
79 saw_already_borrowed: Cell::new(false),
80 });
81 let env: *const CallbackEnvironment = &*env_rc;
82 unsafe {
83 let ptr = constructor(Some(write_callback::<W>), env as *mut c_void, width, height);
84 let surface = Surface::from_raw_full(ptr)?;
85 surface.set_user_data(&STREAM_CALLBACK_ENVIRONMENT, env_rc)?;
86 Ok(surface)
87 }
88 }
89
90 pub(crate) unsafe fn _for_raw_stream<W: io::Write + 'static>(
91 constructor: Constructor,
92 width: f64,
93 height: f64,
94 stream: *mut W,
95 ) -> Result<Self, Error> {
96 Self::_for_stream(
97 constructor,
98 width,
99 height,
100 RawStream(ptr::NonNull::new(stream).expect("NULL stream passed")),
101 )
102 }
103
104 pub fn finish_output_stream(&self) -> Result<Box<dyn Any>, StreamWithError> {
125 self.finish();
126
127 let env = self
128 .user_data_ptr(&STREAM_CALLBACK_ENVIRONMENT)
129 .expect("surface without an output stream");
130
131 let env = unsafe { &*env.as_ptr() };
137
138 if env.saw_already_borrowed.get() {
139 panic!("The output stream’s RefCell was already borrowed when cairo attempted a write")
140 }
141
142 let mut mutable = env.mutable.borrow_mut();
143 if let Some(payload) = mutable.unwind_payload.take() {
144 std::panic::resume_unwind(payload)
145 }
146
147 let (stream, io_error) = mutable
148 .stream
149 .take()
150 .expect("output stream was already taken");
151 if let Some(error) = io_error {
152 Err(StreamWithError { stream, error })
153 } else {
154 Ok(stream)
155 }
156 }
157}
158
159pub struct StreamWithError {
160 pub stream: Box<dyn Any>,
161 pub error: io::Error,
162}
163
164impl std::fmt::Debug for StreamWithError {
165 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
166 self.error.fmt(f)
167 }
168}
169
170impl std::fmt::Display for StreamWithError {
171 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
172 self.error.fmt(f)
173 }
174}
175
176impl From<StreamWithError> for io::Error {
177 fn from(e: StreamWithError) -> Self {
178 e.error
179 }
180}
181
182pub(crate) type Constructor = unsafe extern "C" fn(
183 ffi::cairo_write_func_t,
184 *mut c_void,
185 c_double,
186 c_double,
187) -> *mut ffi::cairo_surface_t;
188
189static STREAM_CALLBACK_ENVIRONMENT: UserDataKey<CallbackEnvironment> = UserDataKey::new();
190
191struct CallbackEnvironment {
192 mutable: RefCell<MutableCallbackEnvironment>,
193 saw_already_borrowed: Cell<bool>,
194}
195
196struct MutableCallbackEnvironment {
197 stream: Option<(Box<dyn Any>, Option<io::Error>)>,
198 unwind_payload: Option<Box<dyn Any + Send + 'static>>,
199}
200
201extern "C" fn write_callback<W: io::Write + 'static>(
204 env: *mut c_void,
205 data: *const c_uchar,
206 length: c_uint,
207) -> ffi::cairo_status_t {
208 let env: *const CallbackEnvironment = env as _;
210
211 let env: &CallbackEnvironment = unsafe { &*env };
215
216 match env.mutable.try_borrow_mut() {
217 Ok(mut mutable) => {
218 if let MutableCallbackEnvironment {
219 stream:
220 Some((
221 stream,
222 io_error @ None,
224 )),
225 unwind_payload: unwind_payload @ None,
226 } = &mut *mutable
227 {
228 let stream = unsafe { AnyExt::downcast_mut_unchecked::<W>(&mut **stream) };
231 let data = unsafe {
233 if data.is_null() || length == 0 {
234 &[]
235 } else {
236 std::slice::from_raw_parts(data, length as usize)
237 }
238 };
239 let result = std::panic::catch_unwind(AssertUnwindSafe(|| stream.write_all(data)));
242 match result {
243 Ok(Ok(())) => return ffi::STATUS_SUCCESS,
244 Ok(Err(error)) => {
245 *io_error = Some(error);
246 }
247 Err(payload) => {
248 *unwind_payload = Some(payload);
249 }
250 }
251 }
252 }
253 _ => env.saw_already_borrowed.set(true),
254 }
255 Error::WriteError.into()
256}
257
258struct RawStream<W>(ptr::NonNull<W>);
259
260impl<W: io::Write> io::Write for RawStream<W> {
261 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
262 unsafe { (*self.0.as_ptr()).write(buf) }
263 }
264 fn flush(&mut self) -> io::Result<()> {
265 unsafe { (*self.0.as_ptr()).flush() }
266 }
267 fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
268 unsafe { (*self.0.as_ptr()).write_all(buf) }
269 }
270}
271
272trait AnyExt {
273 unsafe fn downcast_mut_unchecked<T>(&mut self) -> &mut T {
275 unsafe {
276 let ptr = self as *mut Self as *mut T;
277 &mut *ptr
278 }
279 }
280}
281impl AnyExt for dyn Any {}