diff --git a/Cargo.lock b/Cargo.lock index 8534450..8dd674e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -239,6 +239,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "1.3.2" @@ -587,6 +593,17 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + [[package]] name = "crossbeam-epoch" version = "0.9.14" @@ -650,6 +667,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -1009,6 +1032,22 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "exr" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd2162b720141a91a054640662d3edce3d50a944a50ffca5313cd951abb35b4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + [[package]] name = "fancy-regex" version = "0.7.1" @@ -1029,6 +1068,19 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "flume" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin 0.9.8", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1271,6 +1323,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gif" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "h2" version = "0.3.16" @@ -1290,6 +1352,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +dependencies = [ + "crunchy", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1427,6 +1498,7 @@ dependencies = [ name = "iamb" version = "0.0.7" dependencies = [ + "arboard", "bitflags 1.3.2", "chrono", "clap", @@ -1436,6 +1508,7 @@ dependencies = [ "emojis", "gethostname 0.4.1", "html5ever", + "image", "lazy_static 1.4.0", "markup5ever_rcdom", "matrix-sdk", @@ -1506,9 +1579,13 @@ dependencies = [ "bytemuck", "byteorder", "color_quant", + "exr", + "gif", + "jpeg-decoder", "num-rational", "num-traits", "png", + "scoped_threadpool", "tiff", ] @@ -1617,6 +1694,9 @@ name = "jpeg-decoder" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" +dependencies = [ + "rayon", +] [[package]] name = "js-sys" @@ -1657,6 +1737,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "libc" version = "0.2.140" @@ -2044,6 +2130,15 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom 0.2.8", +] + [[package]] name = "new_debug_unreachable" version = "1.0.4" @@ -2730,6 +2825,28 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + [[package]] name = "rdrand" version = "0.4.0" @@ -2824,7 +2941,7 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", + "spin 0.5.2", "untrusted", "web-sys", "winapi", @@ -3039,6 +3156,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scoped_threadpool" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" + [[package]] name = "scopeguard" version = "1.1.0" @@ -3194,6 +3317,12 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +[[package]] +name = "simd-adler32" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" + [[package]] name = "siphasher" version = "0.3.10" @@ -3262,6 +3391,15 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "spki" version = "0.5.4" @@ -4341,3 +4479,12 @@ dependencies = [ "syn 1.0.109", "synstructure", ] + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] diff --git a/Cargo.toml b/Cargo.toml index 34d4edd..8bfe922 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ categories = ["command-line-utilities"] rust-version = "1.66" [dependencies] +arboard = "3.2.0" bitflags = "1.3.2" chrono = "0.4" clap = {version = "4.0", features = ["derive"]} @@ -23,6 +24,7 @@ dirs = "4.0.0" emojis = "~0.5.2" gethostname = "0.4.1" html5ever = "0.26.0" +image = "0.24.5" markup5ever_rcdom = "0.2.0" mime = "^0.3.16" mime_guess = "^2.0.4" diff --git a/src/base.rs b/src/base.rs index 4686c79..ff9fee1 100644 --- a/src/base.rs +++ b/src/base.rs @@ -192,6 +192,7 @@ pub enum RoomAction { pub enum SendAction { Submit, Upload(String), + UploadImage(usize, usize, Cow<'static, [u8]>), } #[derive(Clone, Debug, Eq, PartialEq)] @@ -360,6 +361,12 @@ pub enum IambError { #[error("Verification request error: {0}")] VerificationRequestError(#[from] matrix_sdk::encryption::identities::RequestVerificationError), + + #[error("Image error: {0}")] + Image(#[from] image::ImageError), + + #[error("Could not use system clipboard data")] + Clipboard, } impl From for UIError { diff --git a/src/windows/room/chat.rs b/src/windows/room/chat.rs index 24194a7..e06d414 100644 --- a/src/windows/room/chat.rs +++ b/src/windows/room/chat.rs @@ -4,6 +4,7 @@ use std::fs; use std::ops::Deref; use std::path::{Path, PathBuf}; +use modalkit::editing::store::RegisterError; use tokio; use matrix_sdk::{ @@ -471,6 +472,36 @@ impl ChatState { let msg = MessageType::Text(msg); let msg = RoomMessageEventContent::new(msg); + (resp.event_id, msg) + }, + SendAction::UploadImage(width, height, bytes) => { + // Convert to png because arboard does not give us the mime type. + let bytes = + image::ImageBuffer::from_raw(width as _, height as _, bytes.into_owned()) + .ok_or(IambError::Clipboard) + .and_then(|imagebuf| { + let dynimage = image::DynamicImage::ImageRgba8(imagebuf); + let bytes = Vec::::new(); + let mut buff = std::io::Cursor::new(bytes); + dynimage.write_to(&mut buff, image::ImageOutputFormat::Png)?; + Ok(buff.into_inner()) + }) + .map_err(IambError::from)?; + let mime = mime::IMAGE_PNG; + + let name = Cow::from(format!("Clipboard.png")); + let config = AttachmentConfig::new(); + + let resp = room + .send_attachment(name.as_ref(), &mime, bytes.as_ref(), config) + .await + .map_err(IambError::from)?; + + // Mock up the local echo message for the scrollback. + let msg = TextMessageEventContent::plain(format!("[Attached File: {name}]")); + let msg = MessageType::Text(msg); + let msg = RoomMessageEventContent::new(msg); + (resp.event_id, msg) }, }; @@ -617,6 +648,15 @@ impl Editable for ChatState { // Run command again. delegate!(self, w => w.editor_command(act, ctx, store)) }, + Err(EditError::Register(RegisterError::ClipboardImage(data))) => { + let msg = "Do you really want to upload the image from your system clipboard?"; + let send = + IambAction::Send(SendAction::UploadImage(data.width, data.height, data.bytes)); + let prompt = PromptYesNo::new(msg, vec![Action::from(send)]); + let prompt = Box::new(prompt); + + Err(EditError::NeedConfirm(prompt)) + }, res @ Err(_) => res, } }