arcdps_imgui/
clipboard.rs
1use std::ffi::{CStr, CString};
2use std::fmt;
3use std::os::raw::{c_char, c_void};
4use std::panic::catch_unwind;
5use std::process;
6use std::ptr;
7
8use crate::Ui;
10
11pub trait ClipboardBackend: 'static {
13 fn get(&mut self) -> Option<String>;
16 fn set(&mut self, value: &str);
18}
19
20pub(crate) struct ClipboardContext {
21 backend: Box<dyn ClipboardBackend>,
22 last_value: CString,
24}
25
26impl ClipboardContext {
27 pub fn new<T: ClipboardBackend>(backend: T) -> ClipboardContext {
30 ClipboardContext {
31 backend: Box::new(backend) as Box<dyn ClipboardBackend>,
32 last_value: CString::default(),
33 }
34 }
35
36 pub fn dummy() -> ClipboardContext {
37 Self {
38 backend: Box::new(DummyClipboardContext),
39 last_value: CString::default(),
40 }
41 }
42}
43
44pub struct DummyClipboardContext;
45impl ClipboardBackend for DummyClipboardContext {
46 fn get(&mut self) -> Option<String> {
47 None
48 }
49
50 fn set(&mut self, _: &str) {
51 }
53}
54
55impl fmt::Debug for ClipboardContext {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 f.debug_struct("ClipboardContext")
58 .field("backend", &(&(*self.backend) as *const _))
60 .field("last_value", &self.last_value)
61 .finish()
62 }
63}
64
65pub(crate) unsafe extern "C" fn get_clipboard_text(user_data: *mut c_void) -> *const c_char {
66 let result = catch_unwind(|| {
67 let ctx = &mut *(user_data as *mut ClipboardContext);
68 match ctx.backend.get() {
69 Some(text) => {
70 ctx.last_value = CString::new(text).unwrap();
71 ctx.last_value.as_ptr()
72 }
73 None => ptr::null(),
74 }
75 });
76 result.unwrap_or_else(|_| {
77 eprintln!("Clipboard getter panicked");
78 process::abort();
79 })
80}
81
82pub(crate) unsafe extern "C" fn set_clipboard_text(user_data: *mut c_void, text: *const c_char) {
83 let result = catch_unwind(|| {
84 let ctx = &mut *(user_data as *mut ClipboardContext);
85 let text = CStr::from_ptr(text).to_owned();
86 ctx.backend.set(text.to_str().unwrap());
87 });
88 result.unwrap_or_else(|_| {
89 eprintln!("Clipboard setter panicked");
90 process::abort();
91 });
92}
93
94#[allow(clippy::fn_address_comparisons)] impl<'ui> Ui<'ui> {
97 pub fn clipboard_text(&self) -> Option<String> {
100 let io = self.io();
101 io.get_clipboard_text_fn.and_then(|get_clipboard_text_fn| {
102 if get_clipboard_text_fn == get_clipboard_text {
104 let ctx = unsafe { &mut *(io.clipboard_user_data as *mut ClipboardContext) };
105 ctx.backend.get()
106 } else {
107 unsafe {
108 let text_ptr = get_clipboard_text_fn(io.clipboard_user_data);
109 if text_ptr.is_null() || *text_ptr == b'\0' as c_char {
110 None
111 } else {
112 Some(
113 CStr::from_ptr(text_ptr)
114 .to_owned()
115 .to_str()
116 .ok()?
117 .to_owned(),
118 )
119 }
120 }
121 }
122 })
123 }
124
125 pub fn set_clipboard_text(&self, text: impl AsRef<str>) {
129 let io = self.io();
130 if let Some(set_clipboard_text_fn) = io.set_clipboard_text_fn {
131 if set_clipboard_text_fn == set_clipboard_text {
133 let ctx = unsafe { &mut *(io.clipboard_user_data as *mut ClipboardContext) };
134 ctx.backend.set(text.as_ref());
135 } else {
136 unsafe {
137 set_clipboard_text_fn(io.clipboard_user_data, self.scratch_txt(text));
138 }
139 }
140 }
141 }
142}