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: *const 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 {}