Serenity Operating System
at master 177 lines 6.2 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2021, the SerenityOS developers. 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <Clipboard/ClipboardClientEndpoint.h> 9#include <Clipboard/ClipboardServerEndpoint.h> 10#include <LibGUI/Clipboard.h> 11#include <LibGfx/Bitmap.h> 12#include <LibIPC/ConnectionToServer.h> 13 14namespace GUI { 15 16class ConnectionToClipboardServer final 17 : public IPC::ConnectionToServer<ClipboardClientEndpoint, ClipboardServerEndpoint> 18 , public ClipboardClientEndpoint { 19 IPC_CLIENT_CONNECTION(ConnectionToClipboardServer, "/tmp/session/%sid/portal/clipboard"sv) 20 21private: 22 ConnectionToClipboardServer(NonnullOwnPtr<Core::LocalSocket> socket) 23 : IPC::ConnectionToServer<ClipboardClientEndpoint, ClipboardServerEndpoint>(*this, move(socket)) 24 { 25 } 26 27 virtual void clipboard_data_changed(DeprecatedString const& mime_type) override 28 { 29 Clipboard::the().clipboard_data_changed({}, mime_type); 30 } 31}; 32 33static RefPtr<ConnectionToClipboardServer> s_connection; 34 35static ConnectionToClipboardServer& connection() 36{ 37 return *s_connection; 38} 39 40void Clipboard::initialize(Badge<Application>) 41{ 42 s_connection = ConnectionToClipboardServer::try_create().release_value_but_fixme_should_propagate_errors(); 43} 44 45Clipboard& Clipboard::the() 46{ 47 static bool s_destructed = false; 48 static ScopeGuard destructed_guard([] { 49 s_destructed = true; 50 }); 51 VERIFY(!s_destructed); // Catch use-after-free 52 53 static Clipboard s_the; 54 return s_the; 55} 56 57Clipboard::DataAndType Clipboard::fetch_data_and_type() const 58{ 59 auto response = connection().get_clipboard_data(); 60 auto type = response.mime_type(); 61 auto metadata = response.metadata().entries(); 62 if (!response.data().is_valid()) 63 return { {}, type, metadata }; 64 auto data = ByteBuffer::copy(response.data().data<void>(), response.data().size()); 65 if (data.is_error()) 66 return {}; 67 68 return { data.release_value(), type, metadata }; 69} 70 71RefPtr<Gfx::Bitmap> Clipboard::DataAndType::as_bitmap() const 72{ 73 if (mime_type != "image/x-serenityos") 74 return nullptr; 75 76 auto width = metadata.get("width").value_or("0").to_uint(); 77 if (!width.has_value() || width.value() == 0) 78 return nullptr; 79 80 auto height = metadata.get("height").value_or("0").to_uint(); 81 if (!height.has_value() || height.value() == 0) 82 return nullptr; 83 84 auto scale = metadata.get("scale").value_or("0").to_uint(); 85 if (!scale.has_value() || scale.value() == 0) 86 return nullptr; 87 88 auto pitch = metadata.get("pitch").value_or("0").to_uint(); 89 if (!pitch.has_value() || pitch.value() == 0) 90 return nullptr; 91 92 auto format = metadata.get("format").value_or("0").to_uint(); 93 if (!format.has_value() || format.value() == 0) 94 return nullptr; 95 96 if (!Gfx::is_valid_bitmap_format(format.value())) 97 return nullptr; 98 auto bitmap_format = (Gfx::BitmapFormat)format.value(); 99 // We cannot handle indexed bitmaps, as the palette would be lost. 100 // Thankfully, everything that copies bitmaps also transforms them to RGB beforehand. 101 if (Gfx::determine_storage_format(bitmap_format) == Gfx::StorageFormat::Indexed8) 102 return nullptr; 103 104 // We won't actually write to the clipping_bitmap, so casting away the const is okay. 105 auto clipping_data = const_cast<u8*>(data.data()); 106 auto clipping_bitmap_or_error = Gfx::Bitmap::create_wrapper(bitmap_format, { (int)width.value(), (int)height.value() }, scale.value(), pitch.value(), clipping_data); 107 if (clipping_bitmap_or_error.is_error()) 108 return nullptr; 109 auto clipping_bitmap = clipping_bitmap_or_error.release_value_but_fixme_should_propagate_errors(); 110 111 auto bitmap_or_error = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, { (int)width.value(), (int)height.value() }, scale.value()); 112 if (bitmap_or_error.is_error()) 113 return nullptr; 114 auto bitmap = bitmap_or_error.release_value_but_fixme_should_propagate_errors(); 115 116 for (int y = 0; y < clipping_bitmap->physical_height(); ++y) { 117 for (int x = 0; x < clipping_bitmap->physical_width(); ++x) { 118 auto pixel = clipping_bitmap->get_pixel(x, y); 119 bitmap->set_pixel(x, y, pixel); 120 } 121 } 122 123 return bitmap; 124} 125 126void Clipboard::set_data(ReadonlyBytes data, DeprecatedString const& type, HashMap<DeprecatedString, DeprecatedString> const& metadata) 127{ 128 if (data.is_empty()) { 129 connection().async_set_clipboard_data({}, type, metadata); 130 return; 131 } 132 133 auto buffer_or_error = Core::AnonymousBuffer::create_with_size(data.size()); 134 if (buffer_or_error.is_error()) { 135 dbgln("GUI::Clipboard::set_data() failed to create a buffer"); 136 return; 137 } 138 auto buffer = buffer_or_error.release_value(); 139 memcpy(buffer.data<void>(), data.data(), data.size()); 140 connection().async_set_clipboard_data(move(buffer), type, metadata); 141} 142 143void Clipboard::set_bitmap(Gfx::Bitmap const& bitmap, HashMap<DeprecatedString, DeprecatedString> const& additional_metadata) 144{ 145 HashMap<DeprecatedString, DeprecatedString> metadata(additional_metadata); 146 metadata.set("width", DeprecatedString::number(bitmap.width())); 147 metadata.set("height", DeprecatedString::number(bitmap.height())); 148 metadata.set("scale", DeprecatedString::number(bitmap.scale())); 149 metadata.set("format", DeprecatedString::number((int)bitmap.format())); 150 metadata.set("pitch", DeprecatedString::number(bitmap.pitch())); 151 set_data({ bitmap.scanline(0), bitmap.size_in_bytes() }, "image/x-serenityos", metadata); 152} 153 154void Clipboard::clear() 155{ 156 connection().async_set_clipboard_data({}, {}, {}); 157} 158 159void Clipboard::clipboard_data_changed(Badge<ConnectionToClipboardServer>, DeprecatedString const& mime_type) 160{ 161 if (on_change) 162 on_change(mime_type); 163 for (auto* client : m_clients) 164 client->clipboard_content_did_change(mime_type); 165} 166 167Clipboard::ClipboardClient::ClipboardClient() 168{ 169 Clipboard::the().register_client({}, *this); 170} 171 172Clipboard::ClipboardClient::~ClipboardClient() 173{ 174 Clipboard::the().unregister_client({}, *this); 175} 176 177}