dear_imgui/
clipboard.rs

1use std::ffi::{CStr, CString};
2use std::fmt;
3use std::os::raw::c_char;
4use std::ptr;
5
6/// Trait for clipboard backends
7pub trait ClipboardBackend: 'static {
8    /// Returns the current clipboard contents as an owned string, or None if the
9    /// clipboard is empty or inaccessible
10    fn get(&mut self) -> Option<String>;
11    /// Sets the clipboard contents to the given string slice.
12    fn set(&mut self, value: &str);
13}
14
15pub(crate) struct ClipboardContext {
16    backend: Box<dyn ClipboardBackend>,
17    // This is needed to keep ownership of the value when the raw C callback is called
18    last_value: CString,
19}
20
21impl ClipboardContext {
22    /// Creates a new ClipboardContext
23    pub fn new<T: ClipboardBackend>(backend: T) -> ClipboardContext {
24        ClipboardContext {
25            backend: Box::new(backend),
26            last_value: CString::default(),
27        }
28    }
29
30    /// Creates a dummy clipboard context that doesn't actually interact with the system clipboard
31    pub fn dummy() -> ClipboardContext {
32        Self {
33            backend: Box::new(DummyClipboardBackend),
34            last_value: CString::default(),
35        }
36    }
37}
38
39/// Non-functioning placeholder clipboard backend
40pub struct DummyClipboardBackend;
41
42impl ClipboardBackend for DummyClipboardBackend {
43    fn get(&mut self) -> Option<String> {
44        None
45    }
46
47    fn set(&mut self, _: &str) {
48        // Do nothing
49    }
50}
51
52/// C callback functions for Dear ImGui clipboard integration
53pub(crate) unsafe extern "C" fn get_clipboard_text(
54    _user_data: *mut crate::sys::ImGuiContext,
55) -> *const c_char {
56    let result = std::panic::catch_unwind(|| {
57        let user_data = unsafe { (*crate::sys::igGetPlatformIO_Nil()).Platform_ClipboardUserData };
58
59        let ctx = unsafe { &mut *(user_data as *mut ClipboardContext) };
60        match ctx.backend.get() {
61            Some(text) => {
62                ctx.last_value = CString::new(text).unwrap();
63                ctx.last_value.as_ptr()
64            }
65            None => ptr::null(),
66        }
67    });
68    result.unwrap_or_else(|_| {
69        eprintln!("Clipboard getter panicked");
70        std::process::abort();
71    })
72}
73
74pub(crate) unsafe extern "C" fn set_clipboard_text(
75    _user_data: *mut crate::sys::ImGuiContext,
76    text: *const c_char,
77) {
78    let result = std::panic::catch_unwind(|| {
79        let user_data = unsafe { (*crate::sys::igGetPlatformIO_Nil()).Platform_ClipboardUserData };
80
81        let ctx = unsafe { &mut *(user_data as *mut ClipboardContext) };
82        let text = unsafe { CStr::from_ptr(text) }.to_owned();
83        ctx.backend.set(text.to_str().unwrap());
84    });
85    result.unwrap_or_else(|_| {
86        eprintln!("Clipboard setter panicked");
87        std::process::abort();
88    });
89}
90
91impl fmt::Debug for ClipboardContext {
92    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93        f.debug_struct("ClipboardContext")
94            .field("backend", &(&(*self.backend) as *const _))
95            .field("last_value", &self.last_value)
96            .finish()
97    }
98}