summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorx-yl <kylepereira@mail.com>2021-07-12 18:16:19 +0400
committerAndreas Kling <kling@serenityos.org>2021-07-14 12:33:07 +0200
commitc8b13bd053c32f59bf75b35941d2e938b9ff96b8 (patch)
tree25b0179c372420fad1acd876426a93bab823d9d2 /Userland
parentd4bb6a1a1ee30dafb1f03b08390adc2e3716855b (diff)
downloadserenity-c8b13bd053c32f59bf75b35941d2e938b9ff96b8.zip
SpiceAgent: Support copying and pasting images
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Services/SpiceAgent/ClipboardServerConnection.cpp60
-rw-r--r--Userland/Services/SpiceAgent/ClipboardServerConnection.h2
-rw-r--r--Userland/Services/SpiceAgent/SpiceAgent.cpp88
-rw-r--r--Userland/Services/SpiceAgent/SpiceAgent.h1
4 files changed, 143 insertions, 8 deletions
diff --git a/Userland/Services/SpiceAgent/ClipboardServerConnection.cpp b/Userland/Services/SpiceAgent/ClipboardServerConnection.cpp
index 6c7ed5c522..3dffb40659 100644
--- a/Userland/Services/SpiceAgent/ClipboardServerConnection.cpp
+++ b/Userland/Services/SpiceAgent/ClipboardServerConnection.cpp
@@ -5,4 +5,64 @@
*/
#include "ClipboardServerConnection.h"
+#include <AK/ByteBuffer.h>
+#include <AK/Function.h>
+#include <LibGfx/Bitmap.h>
+// Copied from LibGUI/Clipboard.cpp
+RefPtr<Gfx::Bitmap> ClipboardServerConnection::get_bitmap()
+{
+ auto clipping = get_clipboard_data();
+
+ if (clipping.mime_type() != "image/x-serenityos")
+ return nullptr;
+
+ HashMap<String, String> const& metadata = clipping.metadata().entries();
+ auto width = metadata.get("width").value_or("0").to_uint();
+ if (!width.has_value() || width.value() == 0)
+ return nullptr;
+
+ auto height = metadata.get("height").value_or("0").to_uint();
+ if (!height.has_value() || height.value() == 0)
+ return nullptr;
+
+ auto scale = metadata.get("scale").value_or("0").to_uint();
+ if (!scale.has_value() || scale.value() == 0)
+ return nullptr;
+
+ auto pitch = metadata.get("pitch").value_or("0").to_uint();
+ if (!pitch.has_value() || pitch.value() == 0)
+ return nullptr;
+
+ auto format = metadata.get("format").value_or("0").to_uint();
+ if (!format.has_value() || format.value() == 0)
+ return nullptr;
+
+ auto data = ByteBuffer::copy(clipping.data().data<void>(), clipping.data().size());
+ auto clipping_bitmap = Gfx::Bitmap::create_wrapper((Gfx::BitmapFormat)format.value(), { (int)width.value(), (int)height.value() }, scale.value(), pitch.value(), data.data());
+ auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, { (int)width.value(), (int)height.value() }, scale.value());
+
+ for (int y = 0; y < clipping_bitmap->physical_height(); ++y) {
+ for (int x = 0; x < clipping_bitmap->physical_width(); ++x) {
+ auto pixel = clipping_bitmap->get_pixel(x, y);
+ bitmap->set_pixel(x, y, pixel);
+ }
+ }
+
+ return bitmap;
+}
+
+// Copied from LibGUI/Clipboard.cpp
+void ClipboardServerConnection::set_bitmap(Gfx::Bitmap const& bitmap)
+{
+ HashMap<String, String> metadata;
+ metadata.set("width", String::number(bitmap.width()));
+ metadata.set("height", String::number(bitmap.height()));
+ metadata.set("scale", String::number(bitmap.scale()));
+ metadata.set("format", String::number((int)bitmap.format()));
+ metadata.set("pitch", String::number(bitmap.pitch()));
+ ReadonlyBytes data { bitmap.scanline(0), bitmap.size_in_bytes() };
+ auto buffer = Core::AnonymousBuffer::create_with_size(bitmap.size_in_bytes());
+ memcpy(buffer.data<u8>(), data.data(), data.size());
+ this->async_set_clipboard_data(buffer, "image/x-serenityos", metadata);
+}
diff --git a/Userland/Services/SpiceAgent/ClipboardServerConnection.h b/Userland/Services/SpiceAgent/ClipboardServerConnection.h
index cad8065ffe..6c0bba4eb0 100644
--- a/Userland/Services/SpiceAgent/ClipboardServerConnection.h
+++ b/Userland/Services/SpiceAgent/ClipboardServerConnection.h
@@ -18,6 +18,8 @@ class ClipboardServerConnection final
C_OBJECT(ClipboardServerConnection);
Function<void()> on_data_changed;
+ RefPtr<Gfx::Bitmap> get_bitmap();
+ void set_bitmap(Gfx::Bitmap const& bitmap);
private:
ClipboardServerConnection()
diff --git a/Userland/Services/SpiceAgent/SpiceAgent.cpp b/Userland/Services/SpiceAgent/SpiceAgent.cpp
index 082800d0d3..782de719b3 100644
--- a/Userland/Services/SpiceAgent/SpiceAgent.cpp
+++ b/Userland/Services/SpiceAgent/SpiceAgent.cpp
@@ -9,6 +9,12 @@
#include <AK/String.h>
#include <LibC/memory.h>
#include <LibC/unistd.h>
+#include <LibGfx/BMPLoader.h>
+#include <LibGfx/BMPWriter.h>
+#include <LibGfx/Bitmap.h>
+#include <LibGfx/JPGLoader.h>
+#include <LibGfx/PNGLoader.h>
+#include <LibGfx/PNGWriter.h>
SpiceAgent::SpiceAgent(int fd, ClipboardServerConnection& connection)
: m_fd(fd)
@@ -23,13 +29,32 @@ SpiceAgent::SpiceAgent(int fd, ClipboardServerConnection& connection)
m_just_set_clip = false;
return;
}
- auto grab_buffer = ClipboardGrab::make_buffer({ ClipboardType::Text });
+ auto mime = m_clipboard_connection.get_clipboard_data().mime_type();
+ Optional<ClipboardType> type = mime_type_to_clipboard_type(mime);
+ if (!type.has_value())
+ return;
+
+ auto grab_buffer = ClipboardGrab::make_buffer({ *type });
send_message(grab_buffer);
};
auto buffer = AnnounceCapabilities::make_buffer(true, { Capability::ClipboardByDemand });
send_message(buffer);
}
+Optional<SpiceAgent::ClipboardType> SpiceAgent::mime_type_to_clipboard_type(const String& mime)
+{
+ if (mime == "text/plain")
+ return ClipboardType::Text;
+ else if (mime == "image/jpeg")
+ return ClipboardType::JPG;
+ else if (mime == "image/bmp")
+ return ClipboardType::BMP;
+ else if (mime == "image/png" || mime == "image/x-serenityos")
+ return ClipboardType::PNG;
+ else
+ return {};
+}
+
void SpiceAgent::on_message_received()
{
ChunkHeader header {};
@@ -47,19 +72,50 @@ void SpiceAgent::on_message_received()
break;
}
case (u32)MessageType::ClipboardRequest: {
- auto clip_data = m_clipboard_connection.get_clipboard_data().data();
- ByteBuffer byte_buffer = ByteBuffer::copy(clip_data.data<void>(), clip_data.size());
- auto clipboard_buffer = Clipboard::make_buffer(ClipboardType::Text, byte_buffer);
+ auto* request_message = reinterpret_cast<ClipboardRequest*>(message->data);
+ auto clipboard = m_clipboard_connection.get_clipboard_data();
+ auto& mime = clipboard.mime_type();
+ ByteBuffer byte_buffer;
+ if (mime == "image/x-serenityos") {
+ auto bitmap = m_clipboard_connection.get_bitmap();
+ byte_buffer = Gfx::PNGWriter::encode(*bitmap);
+ } else {
+ auto clip_data = clipboard.data();
+ byte_buffer = ByteBuffer::copy(clip_data.data<void>(), clip_data.size());
+ }
+ auto clipboard_buffer = Clipboard::make_buffer((ClipboardType)request_message->type, byte_buffer);
send_message(clipboard_buffer);
break;
}
case (u32)MessageType::ClipboardGrab: {
- auto request_buffer = ClipboardRequest::make_buffer(ClipboardType::Text);
+ auto* grab_message = reinterpret_cast<ClipboardGrab*>(message->data);
+ auto found_type = ClipboardType::None;
+ for (size_t i = 0; i < (message->size / 4); i++) {
+ auto type = (ClipboardType)grab_message->types[i];
+ if (found_type == ClipboardType::None) {
+ found_type = static_cast<ClipboardType>(type);
+ } else if (found_type == ClipboardType::Text) {
+ switch (type) {
+ case ClipboardType::PNG:
+ case ClipboardType::BMP:
+ case ClipboardType::JPG:
+ found_type = type;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if (found_type == ClipboardType::None)
+ return;
+
+ auto request_buffer = ClipboardRequest::make_buffer(found_type);
send_message(request_buffer);
break;
}
case (u32)MessageType::Clipboard: {
auto* clipboard_message = reinterpret_cast<Clipboard*>(message->data);
+ auto type = (ClipboardType)clipboard_message->type;
auto data_buffer = ByteBuffer::create_uninitialized(message->size - sizeof(u32));
const auto total_bytes = message->size - sizeof(Clipboard);
@@ -74,9 +130,25 @@ void SpiceAgent::on_message_received()
}
m_just_set_clip = true;
- auto anon_buffer = Core::AnonymousBuffer::create_with_size(data_buffer.size());
- memcpy(anon_buffer.data<void>(), data_buffer.data(), data_buffer.size());
- m_clipboard_connection.async_set_clipboard_data(anon_buffer, "text/plain", {});
+ if (type == ClipboardType::Text) {
+ auto anon_buffer = Core::AnonymousBuffer::create_with_size(data_buffer.size());
+ memcpy(anon_buffer.data<void>(), data_buffer.data(), data_buffer.size());
+ m_clipboard_connection.async_set_clipboard_data(anon_buffer, "text/plain", {});
+ return;
+ } else {
+ RefPtr<Gfx::Bitmap> bitmap;
+ if (type == ClipboardType::PNG) {
+ bitmap = Gfx::load_png_from_memory(data_buffer.data(), data_buffer.size());
+ } else if (type == ClipboardType::BMP) {
+ bitmap = Gfx::load_bmp_from_memory(data_buffer.data(), data_buffer.size());
+ } else if (type == ClipboardType::JPG) {
+ bitmap = Gfx::load_jpg_from_memory(data_buffer.data(), data_buffer.size());
+ } else {
+ dbgln("Unknown clipboard type: {}", (u32)type);
+ return;
+ }
+ m_clipboard_connection.set_bitmap(*bitmap);
+ }
break;
}
default:
diff --git a/Userland/Services/SpiceAgent/SpiceAgent.h b/Userland/Services/SpiceAgent/SpiceAgent.h
index 24bef40b69..8fe64cb31f 100644
--- a/Userland/Services/SpiceAgent/SpiceAgent.h
+++ b/Userland/Services/SpiceAgent/SpiceAgent.h
@@ -124,4 +124,5 @@ private:
bool m_just_set_clip { false };
void read_n(void* dest, size_t n);
static Message* initialize_headers(u8* data, size_t additional_data_size, MessageType type);
+ static Optional<ClipboardType> mime_type_to_clipboard_type(const String& mime);
};