From 3f6bc12e0b84690196b5871b3da93f50384816c7 Mon Sep 17 00:00:00 2001 From: Joonas Javanainen Date: Mon, 8 Jul 2019 17:47:36 +0300 Subject: [PATCH] Pull clipboard support from 0.1-dev --- CHANGELOG.markdown | 1 + imgui-examples/Cargo.lock | 62 +++++++++++ imgui-examples/Cargo.toml | 3 +- imgui-examples/examples/support/clipboard.rs | 19 ++++ imgui-examples/examples/support/mod.rs | 8 ++ src/clipboard.rs | 109 +++++++++++++++++++ src/context.rs | 13 +++ src/lib.rs | 2 + 8 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 imgui-examples/examples/support/clipboard.rs create mode 100644 src/clipboard.rs diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index 3b900c1..0f257dd 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -12,6 +12,7 @@ - Support for navigation input system - Support for backend/renderer name strings - Support for saving/loading INI settings manually +- Pluggable clipboard support ### Changed diff --git a/imgui-examples/Cargo.lock b/imgui-examples/Cargo.lock index 90cc457..b8ccad1 100644 --- a/imgui-examples/Cargo.lock +++ b/imgui-examples/Cargo.lock @@ -98,6 +98,26 @@ dependencies = [ "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "clipboard" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clipboard-win 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "objc-foundation 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "objc_id 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "x11-clipboard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "clipboard-win" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cloudabi" version = "0.0.3" @@ -388,6 +408,7 @@ dependencies = [ name = "imgui-examples" version = "0.0.0" dependencies = [ + "clipboard 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "glium 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", "imgui 0.1.0-pre", @@ -599,6 +620,24 @@ dependencies = [ "malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "objc_id 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ordered-float" version = "1.0.2" @@ -1090,6 +1129,14 @@ dependencies = [ "x11-dl 2.18.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "x11-clipboard" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "xcb 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "x11-dl" version = "2.18.3" @@ -1100,6 +1147,15 @@ dependencies = [ "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "xcb" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "xdg" version = "2.2.0" @@ -1125,6 +1181,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d" "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" "checksum cgl 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "55e7ec0b74fe5897894cbc207092c577e87c52f8a59e8ca8d97ef37551f60a49" +"checksum clipboard 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "25a904646c0340239dcf7c51677b33928bf24fdf424b79a57909c0109075b2e7" +"checksum clipboard-win 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3a093d6fed558e5fe24c3dfc85a68bb68f1c824f440d3ba5aca189e2998786b" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum cocoa 0.18.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cf79daa4e11e5def06e55306aa3601b87de6b5149671529318da048f67cdd77b" "checksum color_quant 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd" @@ -1178,6 +1236,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" "checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" "checksum objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "31d20fd2b37e07cf5125be68357b588672e8cefe9a96f8c17a9d46053b3e590d" +"checksum objc-foundation 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +"checksum objc_id 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" "checksum ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518" "checksum osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" "checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" @@ -1234,6 +1294,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum winit 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d233301129ddd33260b47f76900b50e154b7254546e2edba0e5468a1a5fe4de3" +"checksum x11-clipboard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "89bd49c06c9eb5d98e6ba6536cf64ac9f7ee3a009b2f53996d405b3944f6bcea" "checksum x11-dl 2.18.3 (registry+https://github.com/rust-lang/crates.io-index)" = "940586acb859ea05c53971ac231685799a7ec1dee66ac0bccc0e6ad96e06b4e3" +"checksum xcb 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5e917a3f24142e9ff8be2414e36c649d47d6cc2ba81f16201cdef96e533e02de" "checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" "checksum xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5" diff --git a/imgui-examples/Cargo.toml b/imgui-examples/Cargo.toml index 6b11ef1..9123149 100644 --- a/imgui-examples/Cargo.toml +++ b/imgui-examples/Cargo.toml @@ -10,8 +10,9 @@ license = "MIT/Apache-2.0" publish = false [dev-dependencies] +clipboard = "0.5" glium = { version = "0.25", default-features = true } +image = "0.21" imgui = { version = "0.1.0-pre", path = "../" } imgui-glium-renderer = { version = "0.1.0-pre", path = "../imgui-glium-renderer" } imgui-winit-support = { version = "0.1.0-pre", path = "../imgui-winit-support" } -image = "0.21" diff --git a/imgui-examples/examples/support/clipboard.rs b/imgui-examples/examples/support/clipboard.rs new file mode 100644 index 0000000..5f1cc34 --- /dev/null +++ b/imgui-examples/examples/support/clipboard.rs @@ -0,0 +1,19 @@ +use clipboard::{ClipboardContext, ClipboardProvider}; +use imgui::{ClipboardBackend, ImStr, ImString}; + +pub struct ClipboardSupport(ClipboardContext); + +pub fn init() -> Option { + ClipboardContext::new() + .ok() + .map(|ctx| ClipboardSupport(ctx)) +} + +impl ClipboardBackend for ClipboardSupport { + fn get(&mut self) -> Option { + self.0.get_contents().ok().map(|text| text.into()) + } + fn set(&mut self, text: &ImStr) { + let _ = self.0.set_contents(text.to_str().to_owned()); + } +} diff --git a/imgui-examples/examples/support/mod.rs b/imgui-examples/examples/support/mod.rs index bf591e2..c5308ae 100644 --- a/imgui-examples/examples/support/mod.rs +++ b/imgui-examples/examples/support/mod.rs @@ -5,6 +5,8 @@ use imgui_glium_renderer::GliumRenderer; use imgui_winit_support::{HiDpiMode, WinitPlatform}; use std::time::Instant; +mod clipboard; + pub struct System { pub events_loop: glutin::EventsLoop, pub display: glium::Display, @@ -30,6 +32,12 @@ pub fn init(title: &str) -> System { let mut imgui = Context::create(); imgui.set_ini_filename(None); + if let Some(backend) = clipboard::init() { + imgui.set_clipboard_backend(Box::new(backend)); + } else { + eprintln!("Failed to initialize clipboard"); + } + let mut platform = WinitPlatform::init(&mut imgui); { let gl_window = display.gl_window(); diff --git a/src/clipboard.rs b/src/clipboard.rs new file mode 100644 index 0000000..5b61380 --- /dev/null +++ b/src/clipboard.rs @@ -0,0 +1,109 @@ +use std::fmt; +use std::os::raw::{c_char, c_void}; +use std::panic::catch_unwind; +use std::process; +use std::ptr; + +use crate::string::{ImStr, ImString}; +use crate::Ui; + +pub trait ClipboardBackend { + fn get(&mut self) -> Option; + fn set(&mut self, value: &ImStr); +} + +pub(crate) struct ClipboardContext { + backend: Box, + // this is needed to keep ownership of the value when the raw C callback is called + last_value: ImString, +} + +impl ClipboardContext { + pub fn new(backend: Box) -> ClipboardContext { + ClipboardContext { + backend, + last_value: ImString::default(), + } + } +} + +impl fmt::Debug for ClipboardContext { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "ClipboardContext({:?}, {:?})", + &(*self.backend) as *const _, + self.last_value + ) + } +} + +pub(crate) unsafe extern "C" fn get_clipboard_text(user_data: *mut c_void) -> *const c_char { + let result = catch_unwind(|| { + let ctx = &mut *(user_data as *mut ClipboardContext); + match ctx.backend.get() { + Some(text) => { + ctx.last_value = text; + ctx.last_value.as_ptr() + } + None => ptr::null(), + } + }); + result.unwrap_or_else(|_| { + eprintln!("Clipboard getter panicked"); + process::abort(); + }) +} + +pub(crate) unsafe extern "C" fn set_clipboard_text(user_data: *mut c_void, text: *const c_char) { + let result = catch_unwind(|| { + let ctx = &mut *(user_data as *mut ClipboardContext); + let text = ImStr::from_ptr_unchecked(text); + ctx.backend.set(text); + }); + result.unwrap_or_else(|_| { + eprintln!("Clipboard setter panicked"); + process::abort(); + }); +} + +impl<'ui> Ui<'ui> { + /// Returns the current clipboard contents as text or None if clipboard cannot be accessed or + /// it is empty + pub fn clipboard_text(&self) -> Option { + let io = self.io(); + io.get_clipboard_text_fn.and_then(|get_clipboard_text_fn| { + // Bypass FFI if we end up calling our own function anyway + if get_clipboard_text_fn == get_clipboard_text { + let ctx = unsafe { &mut *(io.clipboard_user_data as *mut ClipboardContext) }; + ctx.backend.get() + } else { + unsafe { + let text_ptr = get_clipboard_text_fn(io.clipboard_user_data); + if text_ptr.is_null() || *text_ptr == b'\0' as c_char { + None + } else { + Some(ImStr::from_ptr_unchecked(text_ptr).to_owned()) + } + } + } + }) + } + /// Sets the clipboard contents. + /// + /// Does nothing if the clipboard cannot be accessed. + pub fn set_clipboard_text(&self, text: &ImStr) { + let io = self.io(); + if let Some(set_clipboard_text_fn) = io.set_clipboard_text_fn { + // Bypass FFI if we end up calling our own function anyway + if set_clipboard_text_fn == set_clipboard_text { + let ctx = unsafe { &mut *(io.clipboard_user_data as *mut ClipboardContext) }; + ctx.backend.set(text); + } else { + unsafe { + set_clipboard_text_fn(io.clipboard_user_data, text.as_ptr()); + } + } + } + } +} diff --git a/src/context.rs b/src/context.rs index d36fd12..48a94c0 100644 --- a/src/context.rs +++ b/src/context.rs @@ -5,6 +5,7 @@ use std::ops::Drop; use std::ptr; use std::rc::Rc; +use crate::clipboard::{ClipboardBackend, ClipboardContext}; use crate::fonts::atlas::{FontAtlas, FontAtlasRefMut, FontId, SharedFontAtlas}; use crate::io::Io; use crate::string::{ImStr, ImString}; @@ -54,6 +55,7 @@ pub struct Context { log_filename: Option, platform_name: Option, renderer_name: Option, + clipboard_ctx: Option>, } lazy_static! { @@ -170,6 +172,15 @@ impl Context { let data = unsafe { CStr::from_ptr(sys::igSaveIniSettingsToMemory(ptr::null_mut())) }; buf.push_str(&data.to_string_lossy()); } + pub fn set_clipboard_backend(&mut self, backend: Box) { + use std::borrow::BorrowMut; + let mut clipboard_ctx = Box::new(ClipboardContext::new(backend)); + let io = self.io_mut(); + io.set_clipboard_text_fn = Some(crate::clipboard::set_clipboard_text); + io.get_clipboard_text_fn = Some(crate::clipboard::get_clipboard_text); + io.clipboard_user_data = clipboard_ctx.borrow_mut() as *mut ClipboardContext as *mut _; + self.clipboard_ctx.replace(clipboard_ctx); + } fn create_internal(shared_font_atlas: Option>>) -> Self { let _guard = CTX_MUTEX.lock(); assert!( @@ -186,6 +197,7 @@ impl Context { log_filename: None, platform_name: None, renderer_name: None, + clipboard_ctx: None, } } fn is_current_context(&self) -> bool { @@ -263,6 +275,7 @@ impl SuspendedContext { log_filename: None, platform_name: None, renderer_name: None, + clipboard_ctx: None, }; if ctx.is_current_context() { // Oops, the context was activated -> deactivate diff --git a/src/lib.rs b/src/lib.rs index 946ffe4..5493211 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ use std::str; use std::thread; pub use self::child_frame::ChildFrame; +pub use self::clipboard::*; pub use self::color_editors::{ ColorButton, ColorEdit, ColorEditMode, ColorFormat, ColorPicker, ColorPickerMode, ColorPreview, EditableColor, @@ -52,6 +53,7 @@ pub use self::window_draw_list::{ChannelsSplit, ImColor, WindowDrawList}; use internal::RawCast; mod child_frame; +mod clipboard; mod color_editors; mod context; mod drag;