From 9f92e524648a9bf8451ba0f764a94a0a7e3f946b Mon Sep 17 00:00:00 2001 From: Caoimhe Date: Sat, 13 May 2023 15:24:53 +0100 Subject: [PATCH] SpiceAgent: Add support for copying images to the clipboard --- Userland/Services/SpiceAgent/Message.cpp | 44 ++++++++++++++++----- Userland/Services/SpiceAgent/Message.h | 4 ++ Userland/Services/SpiceAgent/SpiceAgent.cpp | 25 +++++++++++- Userland/Services/SpiceAgent/SpiceAgent.h | 1 + 4 files changed, 63 insertions(+), 11 deletions(-) diff --git a/Userland/Services/SpiceAgent/Message.cpp b/Userland/Services/SpiceAgent/Message.cpp index f446125129..ffd5a0817d 100644 --- a/Userland/Services/SpiceAgent/Message.cpp +++ b/Userland/Services/SpiceAgent/Message.cpp @@ -11,6 +11,38 @@ namespace SpiceAgent { +ErrorOr clipboard_data_type_to_mime_type(ClipboardDataType data_type) +{ + switch (data_type) { + case ClipboardDataType::Text: + return "text/plain"_string; + + case ClipboardDataType::PNG: + return "image/png"_string; + + case ClipboardDataType::BMP: + return "image/bitmap"_string; + + case ClipboardDataType::JPG: + return "image/jpeg"_string; + + case ClipboardDataType::TIFF: + return "image/tiff"_string; + + default: + return Error::from_string_literal("Unable to determine mime type!"); + } +} + +ErrorOr clipboard_data_type_from_raw_value(u32 value) +{ + if (value >= to_underlying(ClipboardDataType::__End)) { + return Error::from_string_literal("Unsupported clipboard type"); + } + + return static_cast(value); +} + ErrorOr AnnounceCapabilitiesMessage::read_from_stream(AK::Stream& stream) { // If this message is a capabilities request, we don't have to parse anything else. @@ -57,11 +89,7 @@ ErrorOr ClipboardGrabMessage::read_from_stream(AK::Stream& auto types = Vector(); while (!stream.is_eof()) { auto value = TRY(stream.read_value()); - if (value >= to_underlying(ClipboardDataType::__End)) { - return Error::from_string_literal("Unsupported clipboard type"); - } - - types.append(static_cast(value)); + types.append(TRY(clipboard_data_type_from_raw_value(value))); } return ClipboardGrabMessage(types); @@ -88,11 +116,7 @@ ErrorOr ClipboardGrabMessage::debug_description() ErrorOr ClipboardRequestMessage::read_from_stream(AK::Stream& stream) { auto value = TRY(stream.read_value()); - if (value >= to_underlying(ClipboardDataType::__End)) { - return Error::from_string_literal("Unsupported clipboard type"); - } - - auto type = static_cast(value); + auto type = TRY(clipboard_data_type_from_raw_value(value)); return ClipboardRequestMessage(type); } diff --git a/Userland/Services/SpiceAgent/Message.h b/Userland/Services/SpiceAgent/Message.h index 43799993a9..6a411b74d7 100644 --- a/Userland/Services/SpiceAgent/Message.h +++ b/Userland/Services/SpiceAgent/Message.h @@ -9,6 +9,7 @@ #include #include #include +#include #include namespace SpiceAgent { @@ -50,6 +51,9 @@ enum class ClipboardDataType : u32 { __End }; +ErrorOr clipboard_data_type_to_mime_type(ClipboardDataType type); +ErrorOr clipboard_data_type_from_raw_value(u32 value); + class Message { public: // The spice protocol headers contain a bit of documentation about these, but nothing major: diff --git a/Userland/Services/SpiceAgent/SpiceAgent.cpp b/Userland/Services/SpiceAgent/SpiceAgent.cpp index 33e0bf663f..f0e52268c4 100644 --- a/Userland/Services/SpiceAgent/SpiceAgent.cpp +++ b/Userland/Services/SpiceAgent/SpiceAgent.cpp @@ -8,6 +8,7 @@ #include "SpiceAgent.h" #include #include +#include namespace SpiceAgent { @@ -111,11 +112,33 @@ ErrorOr SpiceAgent::did_receive_clipboard_message(ClipboardMessage& messag case ClipboardDataType::Text: { // The default mime_type for set_data is `text/plain`. GUI::Clipboard::the().set_data(message.contents()); - return {}; + break; } + + // For the image formats, let's try to find a decoder from LibGfx. + case ClipboardDataType::PNG: + case ClipboardDataType::BMP: + case ClipboardDataType::JPG: + case ClipboardDataType::TIFF: { + auto mime_type = TRY(clipboard_data_type_to_mime_type(message.data_type())); + + // FIXME: It should be trivial to make `try_create_for_raw_bytes` take a `StringView` instead of a direct `DeprecatedString`. + auto decoder = Gfx::ImageDecoder::try_create_for_raw_bytes(message.contents(), mime_type.to_deprecated_string()); + if (!decoder || (decoder->frame_count() == 0)) { + return Error::from_string_literal("Failed to find a suitable decoder for a pasted image!"); + } + + auto frame = TRY(decoder->frame(0)); + GUI::Clipboard::the().set_bitmap(*frame.image); + + break; + } + default: return Error::from_string_literal("Unsupported clipboard data type!"); } + + return {}; } ErrorOr SpiceAgent::read_message_buffer() diff --git a/Userland/Services/SpiceAgent/SpiceAgent.h b/Userland/Services/SpiceAgent/SpiceAgent.h index d877f55d4a..7eb9809f14 100644 --- a/Userland/Services/SpiceAgent/SpiceAgent.h +++ b/Userland/Services/SpiceAgent/SpiceAgent.h @@ -11,6 +11,7 @@ #include "Message.h" #include "MessageHeader.h" #include +#include #include #include #include