mirror of
https://github.com/tauri-apps/tao.git
synced 2026-01-31 00:35:16 +01:00
refactor!: remove clipboard impl (#797)
This commit is contained in:
5
.changes/clipboard.md
Normal file
5
.changes/clipboard.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"tao": "minor"
|
||||
---
|
||||
|
||||
**Breaking change**: Removed clipboard implementation. Use `arboard` crate instead.
|
||||
@@ -1,98 +0,0 @@
|
||||
// Copyright 2014-2021 The winit contributors
|
||||
// Copyright 2021-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! The `Clipboard` struct and associated types.
|
||||
//!
|
||||
//! ## Platform-specific
|
||||
//!
|
||||
//! - **Android / iOS:** Unsupported
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
//! let mut cliboard = Clipboard::new();
|
||||
//! cliboard.write_text("This is injected from tao!!!")
|
||||
//! let content = cliboard.read_text();
|
||||
//! ```
|
||||
//!
|
||||
|
||||
use crate::platform_impl::Clipboard as ClipboardPlatform;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
/// Object that allows you to access the `Clipboard` instance.
|
||||
pub struct Clipboard(ClipboardPlatform);
|
||||
|
||||
impl Clipboard {
|
||||
/// Creates a new `Clipboard` instance.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Android / iOS:** Unsupported
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Writes the text into the clipboard as plain text.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Android / iOS:** Unsupported
|
||||
pub fn write_text(&mut self, s: impl AsRef<str>) {
|
||||
self.0.write_text(s);
|
||||
}
|
||||
|
||||
/// The content in the clipboard as plain text.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Android / iOS:** Unsupported
|
||||
pub fn read_text(&self) -> Option<String> {
|
||||
self.0.read_text()
|
||||
}
|
||||
}
|
||||
|
||||
/// Identifier of a clipboard format.
|
||||
pub(crate) type FormatId = &'static str;
|
||||
|
||||
/// Object that allows you to access the `ClipboardFormat`.
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct ClipboardFormat {
|
||||
pub(crate) identifier: FormatId,
|
||||
pub(crate) data: Vec<u8>,
|
||||
}
|
||||
|
||||
// todo add more formats
|
||||
impl ClipboardFormat {
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
pub const TEXT: &'static str = "public.utf8-plain-text";
|
||||
#[cfg(any(target_os = "windows", target_os = "android"))]
|
||||
pub const TEXT: &'static str = "text/plain";
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
pub const TEXT: &'static str = "UTF8_STRING";
|
||||
}
|
||||
|
||||
impl ClipboardFormat {
|
||||
pub fn new(identifier: FormatId, data: impl Into<Vec<u8>>) -> Self {
|
||||
let data = data.into();
|
||||
ClipboardFormat { identifier, data }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for ClipboardFormat {
|
||||
fn from(src: String) -> ClipboardFormat {
|
||||
let data = src.into_bytes();
|
||||
ClipboardFormat::new(ClipboardFormat::TEXT, data)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for ClipboardFormat {
|
||||
fn from(src: &str) -> ClipboardFormat {
|
||||
src.to_string().into()
|
||||
}
|
||||
}
|
||||
@@ -165,7 +165,6 @@ extern crate bitflags;
|
||||
#[macro_use]
|
||||
extern crate objc;
|
||||
|
||||
pub mod clipboard;
|
||||
pub mod dpi;
|
||||
#[macro_use]
|
||||
pub mod error;
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright 2014-2021 The winit contributors
|
||||
// Copyright 2021-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Clipboard;
|
||||
impl Clipboard {
|
||||
pub(crate) fn write_text(&mut self, _s: impl AsRef<str>) {}
|
||||
pub(crate) fn read_text(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -28,8 +28,6 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
mod clipboard;
|
||||
pub use clipboard::Clipboard;
|
||||
pub mod ndk_glue;
|
||||
use ndk_glue::{Event, Rect};
|
||||
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright 2014-2021 The winit contributors
|
||||
// Copyright 2021-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Clipboard;
|
||||
impl Clipboard {
|
||||
pub(crate) fn write_text(&mut self, _s: impl AsRef<str>) {}
|
||||
pub(crate) fn read_text(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,6 @@ macro_rules! assert_main_thread {
|
||||
}
|
||||
|
||||
mod app_state;
|
||||
mod clipboard;
|
||||
mod event_loop;
|
||||
mod ffi;
|
||||
mod keycode;
|
||||
@@ -86,7 +85,6 @@ use std::fmt;
|
||||
|
||||
pub(crate) use self::event_loop::PlatformSpecificEventLoopAttributes;
|
||||
pub use self::{
|
||||
clipboard::Clipboard,
|
||||
event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget},
|
||||
keycode::{keycode_from_scancode, keycode_to_scancode},
|
||||
monitor::{MonitorHandle, VideoMode},
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
// Copyright 2014-2021 The winit contributors
|
||||
// Copyright 2021-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use gdk::Atom;
|
||||
use gtk::{TargetEntry, TargetFlags};
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Clipboard;
|
||||
|
||||
const CLIPBOARD_TARGETS: [&str; 5] = [
|
||||
"UTF8_STRING",
|
||||
"TEXT",
|
||||
"STRING",
|
||||
"text/plain;charset=utf-8",
|
||||
"text/plain",
|
||||
];
|
||||
|
||||
impl Clipboard {
|
||||
pub(crate) fn write_text(&mut self, string: impl AsRef<str>) {
|
||||
let string = string.as_ref().to_string();
|
||||
|
||||
let display = gdk::Display::default().unwrap();
|
||||
let clipboard = gtk::Clipboard::default(&display).unwrap();
|
||||
|
||||
let targets: Vec<TargetEntry> = CLIPBOARD_TARGETS
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, target)| TargetEntry::new(target, TargetFlags::all(), i as u32))
|
||||
.collect();
|
||||
|
||||
clipboard.set_with_data(&targets, move |_, selection, _| {
|
||||
selection.set(&selection.target(), 8i32, string.as_bytes());
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn read_text(&self) -> Option<String> {
|
||||
let display = gdk::Display::default().unwrap();
|
||||
let clipboard = gtk::Clipboard::default(&display).unwrap();
|
||||
|
||||
for target in &CLIPBOARD_TARGETS {
|
||||
let atom = Atom::intern(target);
|
||||
if let Some(selection) = clipboard.wait_for_contents(&atom) {
|
||||
return String::from_utf8(selection.data()).ok();
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
|
||||
mod clipboard;
|
||||
mod device;
|
||||
mod event_loop;
|
||||
mod icon;
|
||||
@@ -23,10 +22,7 @@ mod window;
|
||||
pub mod taskbar;
|
||||
pub mod x11;
|
||||
|
||||
pub use self::{
|
||||
clipboard::Clipboard,
|
||||
keycode::{keycode_from_scancode, keycode_to_scancode},
|
||||
};
|
||||
pub use self::keycode::{keycode_from_scancode, keycode_to_scancode};
|
||||
pub(crate) use event_loop::PlatformSpecificEventLoopAttributes;
|
||||
pub use event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget};
|
||||
pub use icon::PlatformIcon;
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright 2014-2021 The winit contributors
|
||||
// Copyright 2021-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cocoa::{
|
||||
appkit::NSPasteboardTypeString,
|
||||
base::{id, nil, BOOL, YES},
|
||||
foundation::{NSInteger, NSString},
|
||||
};
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Clipboard;
|
||||
|
||||
impl Clipboard {
|
||||
pub(crate) fn write_text(&mut self, s: impl AsRef<str>) {
|
||||
let s = s.as_ref();
|
||||
unsafe {
|
||||
let nsstring = NSString::alloc(nil).init_str(s);
|
||||
let pasteboard: id = msg_send![class!(NSPasteboard), generalPasteboard];
|
||||
let _: NSInteger = msg_send![pasteboard, clearContents];
|
||||
let result: BOOL = msg_send![pasteboard, setString: nsstring forType: NSPasteboardTypeString];
|
||||
if result != YES {
|
||||
#[cfg(debug_assertions)]
|
||||
println!("failed to set clipboard");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn read_text(&self) -> Option<String> {
|
||||
unsafe {
|
||||
let pasteboard: id = msg_send![class!(NSPasteboard), generalPasteboard];
|
||||
let contents: id = msg_send![pasteboard, stringForType: NSPasteboardTypeString];
|
||||
if contents.is_null() {
|
||||
None
|
||||
} else {
|
||||
let slice = std::slice::from_raw_parts(contents.UTF8String() as *const _, contents.len());
|
||||
let result = std::str::from_utf8_unchecked(slice);
|
||||
Some(result.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@
|
||||
mod app;
|
||||
mod app_delegate;
|
||||
mod app_state;
|
||||
mod clipboard;
|
||||
mod event;
|
||||
mod event_loop;
|
||||
mod ffi;
|
||||
@@ -26,7 +25,6 @@ use std::{fmt, ops::Deref, sync::Arc};
|
||||
pub(crate) use self::event_loop::PlatformSpecificEventLoopAttributes;
|
||||
pub use self::{
|
||||
app_delegate::{get_aux_state_mut, AuxDelegateState},
|
||||
clipboard::Clipboard,
|
||||
event::KeyEventExtra,
|
||||
event_loop::{EventLoop, EventLoopWindowTarget, Proxy as EventLoopProxy},
|
||||
keycode::{keycode_from_scancode, keycode_to_scancode},
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
// Copyright 2014-2021 The winit contributors
|
||||
// Copyright 2021-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::util;
|
||||
use crate::clipboard::{ClipboardFormat, FormatId};
|
||||
use std::{ffi::OsStr, os::windows::ffi::OsStrExt, ptr};
|
||||
use windows::{
|
||||
core::{PCWSTR, PWSTR},
|
||||
Win32::{
|
||||
Foundation::{HANDLE, HGLOBAL, HWND},
|
||||
System::{
|
||||
DataExchange::{
|
||||
CloseClipboard, EmptyClipboard, GetClipboardData, OpenClipboard, RegisterClipboardFormatW,
|
||||
SetClipboardData,
|
||||
},
|
||||
Memory::{GlobalAlloc, GlobalLock, GlobalUnlock, GMEM_MOVEABLE},
|
||||
Ole::CF_UNICODETEXT,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Clipboard;
|
||||
|
||||
impl Clipboard {
|
||||
pub fn write_text(&mut self, s: impl AsRef<str>) {
|
||||
let s = s.as_ref();
|
||||
let format: ClipboardFormat = s.into();
|
||||
self.put_formats(&[format])
|
||||
}
|
||||
|
||||
pub(crate) fn read_text(&self) -> Option<String> {
|
||||
with_clipboard(|| unsafe {
|
||||
let handle = GetClipboardData(CF_UNICODETEXT.0 as _).unwrap_or_default();
|
||||
if handle.is_invalid() {
|
||||
None
|
||||
} else {
|
||||
let unic_str = PWSTR::from_raw(GlobalLock(HGLOBAL(handle.0)) as *mut _);
|
||||
let mut len = 0;
|
||||
while *unic_str.0.offset(len) != 0 {
|
||||
len += 1;
|
||||
}
|
||||
let utf16_slice = std::slice::from_raw_parts(unic_str.0, len as usize);
|
||||
let result = String::from_utf16(utf16_slice);
|
||||
if let Ok(result) = result {
|
||||
GlobalUnlock(HGLOBAL(handle.0));
|
||||
return Some(result);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
|
||||
pub(crate) fn put_formats(&mut self, formats: &[ClipboardFormat]) {
|
||||
with_clipboard(|| unsafe {
|
||||
EmptyClipboard();
|
||||
|
||||
for format in formats {
|
||||
let handle = make_handle(format);
|
||||
let format_id = match get_format_id(format.identifier) {
|
||||
Some(id) => id,
|
||||
None => {
|
||||
#[cfg(debug_assertions)]
|
||||
println!("failed to register clipboard format {}", &format.identifier);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if let Err(err) = SetClipboardData(format_id, handle) {
|
||||
#[cfg(debug_assertions)]
|
||||
println!(
|
||||
"failed to set clipboard for fmt {}, error: {}",
|
||||
&format.identifier, err
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn get_format_id(format: FormatId) -> Option<u32> {
|
||||
if let Some((id, _)) = STANDARD_FORMATS.iter().find(|(_, s)| s == &format) {
|
||||
return Some(*id);
|
||||
}
|
||||
match format {
|
||||
ClipboardFormat::TEXT => Some(CF_UNICODETEXT.0 as _),
|
||||
other => register_identifier(other),
|
||||
}
|
||||
}
|
||||
|
||||
fn register_identifier(ident: &str) -> Option<u32> {
|
||||
unsafe {
|
||||
let clipboard_format = util::encode_wide(ident);
|
||||
|
||||
let pb_format = RegisterClipboardFormatW(PCWSTR::from_raw(clipboard_format.as_ptr()));
|
||||
if pb_format == 0 {
|
||||
#[cfg(debug_assertions)]
|
||||
println!(
|
||||
"failed to register clipboard format '{}'; error {}.",
|
||||
ident,
|
||||
windows::core::Error::from_win32().code().0
|
||||
);
|
||||
return None;
|
||||
}
|
||||
Some(pb_format)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn make_handle(format: &ClipboardFormat) -> HANDLE {
|
||||
HANDLE(if format.identifier == ClipboardFormat::TEXT {
|
||||
let s: &OsStr = std::str::from_utf8_unchecked(&format.data).as_ref();
|
||||
let wstr: Vec<u16> = s.encode_wide().chain(Some(0)).collect();
|
||||
let handle = GlobalAlloc(GMEM_MOVEABLE, wstr.len() * std::mem::size_of::<u16>()).unwrap();
|
||||
let locked = GlobalLock(HGLOBAL(handle.0)) as *mut _;
|
||||
ptr::copy_nonoverlapping(wstr.as_ptr(), locked, wstr.len());
|
||||
GlobalUnlock(handle);
|
||||
handle.0
|
||||
} else {
|
||||
let handle = GlobalAlloc(GMEM_MOVEABLE, format.data.len() * std::mem::size_of::<u8>()).unwrap();
|
||||
let locked = GlobalLock(HGLOBAL(handle.0)) as *mut _;
|
||||
ptr::copy_nonoverlapping(format.data.as_ptr(), locked, format.data.len());
|
||||
GlobalUnlock(HGLOBAL(handle.0));
|
||||
handle.0
|
||||
})
|
||||
}
|
||||
|
||||
fn with_clipboard<V>(f: impl FnOnce() -> V) -> Option<V> {
|
||||
unsafe {
|
||||
if !OpenClipboard(HWND::default()).as_bool() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let result = f();
|
||||
|
||||
CloseClipboard();
|
||||
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-ca/windows/win32/dataxchg/standard-clipboard-formats
|
||||
static STANDARD_FORMATS: &[(u32, &str)] = &[
|
||||
(1, "CF_TEXT"),
|
||||
(2, "CF_BITMAP"),
|
||||
(3, "CF_METAFILEPICT"),
|
||||
(4, "CF_SYLK"),
|
||||
(5, "CF_DIF"),
|
||||
(6, "CF_TIFF"),
|
||||
(7, "CF_OEMTEXT"),
|
||||
(8, "CF_DIB"),
|
||||
(9, "CF_PALETTE"),
|
||||
(10, "CF_PENDATA"),
|
||||
(11, "CF_RIFF"),
|
||||
(12, "CF_WAVE"),
|
||||
(13, "CF_UNICODETEXT"),
|
||||
(14, "CF_ENHMETAFILE"),
|
||||
(15, "CF_HDROP"),
|
||||
(16, "CF_LOCALE"),
|
||||
(17, "CF_DIBV5"),
|
||||
(0x0080, "CF_OWNERDISPLAY"),
|
||||
(0x0081, "CF_DSPTEXT"),
|
||||
(0x0082, "CF_DSPBITMAP"),
|
||||
(0x0083, "CF_DSPMETAFILEPICT"),
|
||||
(0x008E, "CF_DSPENHMETAFILE"),
|
||||
(0x0200, "CF_PRIVATEFIRST"),
|
||||
(0x02FF, "CF_PRIVATELAST"),
|
||||
(0x0300, "CF_GDIOBJFIRST"),
|
||||
(0x03FF, "CF_GDIOBJLAST"),
|
||||
];
|
||||
@@ -10,7 +10,6 @@ use windows::Win32::{
|
||||
};
|
||||
|
||||
pub(crate) use self::{
|
||||
clipboard::Clipboard,
|
||||
event_loop::{
|
||||
EventLoop, EventLoopProxy, EventLoopWindowTarget, PlatformSpecificEventLoopAttributes,
|
||||
},
|
||||
@@ -138,7 +137,6 @@ impl WindowId {
|
||||
|
||||
#[macro_use]
|
||||
mod util;
|
||||
mod clipboard;
|
||||
mod dark_mode;
|
||||
mod dpi;
|
||||
mod drop_handler;
|
||||
|
||||
Reference in New Issue
Block a user