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