WindowServer+LibGUI: Store a "data type" with the clipboard content

This will allow us to distinguish between different types of data
stored on the clipboard.
This commit is contained in:
Andreas Kling 2019-09-14 09:19:05 +02:00
parent 9d2c4d223a
commit c543ee5c5b
Notes: sideshowbarker 2024-07-19 12:07:18 +09:00
10 changed files with 94 additions and 15 deletions

View File

@ -15,7 +15,7 @@ GClipboard::GClipboard()
{
}
String GClipboard::data() const
GClipboard::DataAndType GClipboard::data_and_type() const
{
WSAPI_ClientMessage request;
request.type = WSAPI_ClientMessage::Type::GetClipboardContents;
@ -31,10 +31,12 @@ String GClipboard::data() const
dbgprintf("GClipboard::data() clipping contents size is greater than shared buffer size\n");
return {};
}
return String((const char*)shared_buffer->data(), response.clipboard.contents_size);
auto data = String((const char*)shared_buffer->data(), response.clipboard.contents_size);
auto type = String(response.text, response.text_length);
return { data, type };
}
void GClipboard::set_data(const StringView& data)
void GClipboard::set_data(const StringView& data, const String& type)
{
WSAPI_ClientMessage request;
request.type = WSAPI_ClientMessage::Type::SetClipboardContents;
@ -51,6 +53,18 @@ void GClipboard::set_data(const StringView& data)
shared_buffer->share_with(GWindowServerConnection::the().server_pid());
request.clipboard.shared_buffer_id = shared_buffer->shared_buffer_id();
request.clipboard.contents_size = data.length();
ASSERT(type.length() < (ssize_t)sizeof(request.text));
if (!type.is_null())
strcpy(request.text, type.characters());
request.text_length = type.length();
auto response = GWindowServerConnection::the().sync_request(request, WSAPI_ServerMessage::Type::DidSetClipboardContents);
ASSERT(response.clipboard.shared_buffer_id == shared_buffer->shared_buffer_id());
}
void GClipboard::did_receive_clipboard_contents_changed(Badge<GWindowServerConnection>, const String& data_type)
{
if (on_content_change)
on_content_change(data_type);
}

View File

@ -1,13 +1,29 @@
#pragma once
#include <AK/Badge.h>
#include <AK/Function.h>
#include <AK/String.h>
class GWindowServerConnection;
class GClipboard {
public:
static GClipboard& the();
String data() const;
void set_data(const StringView&);
String data() const { return data_and_type().data; }
String type() const { return data_and_type().type; }
void set_data(const StringView&, const String& data_type = "text");
struct DataAndType {
String data;
String type;
};
DataAndType data_and_type() const;
void did_receive_clipboard_contents_changed(Badge<GWindowServerConnection>, const String& data_type);
Function<void(const String& data_type)> on_content_change;
private:
GClipboard();

View File

@ -15,6 +15,7 @@
#include <LibCore/CObject.h>
#include <LibGUI/GAction.h>
#include <LibGUI/GApplication.h>
#include <LibGUI/GClipboard.h>
#include <LibGUI/GDesktop.h>
#include <LibGUI/GMenu.h>
#include <LibGUI/GWidget.h>
@ -244,6 +245,11 @@ void GWindowServerConnection::postprocess_bundles(Vector<IncomingMessageBundle>&
continue;
}
if (event.type == WSAPI_ServerMessage::Type::ClipboardContentsChanged) {
GClipboard::the().did_receive_clipboard_contents_changed({}, String(event.text, event.text_length));
continue;
}
if (event.type == WSAPI_ServerMessage::Error) {
dbgprintf("GEventLoop got error message from server\n");
dbgprintf(" - error message: %s\n", String(event.text, event.text_length).characters());

View File

@ -112,6 +112,7 @@ struct WSAPI_ServerMessage {
DidSetResolution,
DidSetWindowHasAlphaChannel,
ScreenRectChanged,
ClipboardContentsChanged,
__Begin_WM_Events__,
WM_WindowRemoved,

View File

@ -87,6 +87,19 @@ void WSClientConnection::notify_about_new_screen_rect(const Rect& rect)
post_message(message);
}
void WSClientConnection::notify_about_clipboard_contents_changed()
{
auto& clipboard = WSClipboard::the();
WSAPI_ServerMessage message;
message.type = WSAPI_ServerMessage::Type::ClipboardContentsChanged;
message.clipboard.shared_buffer_id = -1;
message.clipboard.contents_size = -1;
ASSERT(clipboard.data_type().length() < (ssize_t)sizeof(message.text));
strcpy(message.text, clipboard.data_type().characters());
message.text_length = clipboard.data_type().length();
post_message(message);
}
void WSClientConnection::event(CEvent& event)
{
if (static_cast<WSEvent&>(event).is_client_request()) {
@ -247,7 +260,11 @@ bool WSClientConnection::handle_message(const WSAPI_ClientMessage& message, cons
CEventLoop::current().post_event(*this, make<WSAPIGetWindowRectRequest>(client_id(), message.window_id));
break;
case WSAPI_ClientMessage::Type::SetClipboardContents:
CEventLoop::current().post_event(*this, make<WSAPISetClipboardContentsRequest>(client_id(), message.clipboard.shared_buffer_id, message.clipboard.contents_size));
if (message.text_length > (int)sizeof(message.text)) {
did_misbehave();
return false;
}
CEventLoop::current().post_event(*this, make<WSAPISetClipboardContentsRequest>(client_id(), message.clipboard.shared_buffer_id, message.clipboard.contents_size, String(message.text, message.text_length)));
break;
case WSAPI_ClientMessage::Type::GetClipboardContents:
CEventLoop::current().post_event(*this, make<WSAPIGetClipboardContentsRequest>(client_id()));
@ -661,7 +678,7 @@ void WSClientConnection::handle_request(const WSAPISetClipboardContentsRequest&
post_error("WSAPISetClipboardContentsRequest: Bad shared buffer ID");
return;
}
WSClipboard::the().set_data(*shared_buffer, request.size());
WSClipboard::the().set_data(*shared_buffer, request.size(), request.data_type());
WSAPI_ServerMessage response;
response.type = WSAPI_ServerMessage::Type::DidSetClipboardContents;
response.clipboard.shared_buffer_id = shared_buffer->shared_buffer_id();
@ -670,26 +687,31 @@ void WSClientConnection::handle_request(const WSAPISetClipboardContentsRequest&
void WSClientConnection::handle_request(const WSAPIGetClipboardContentsRequest&)
{
auto& clipboard = WSClipboard::the();
WSAPI_ServerMessage response;
response.type = WSAPI_ServerMessage::Type::DidGetClipboardContents;
response.clipboard.shared_buffer_id = -1;
response.clipboard.contents_size = 0;
if (WSClipboard::the().size()) {
if (clipboard.size()) {
// FIXME: Optimize case where an app is copy/pasting within itself.
// We can just reuse the SharedBuffer then, since it will have the same peer PID.
// It would be even nicer if a SharedBuffer could have an arbitrary number of clients..
RefPtr<SharedBuffer> shared_buffer = SharedBuffer::create_with_size(WSClipboard::the().size());
RefPtr<SharedBuffer> shared_buffer = SharedBuffer::create_with_size(clipboard.size());
ASSERT(shared_buffer);
memcpy(shared_buffer->data(), WSClipboard::the().data(), WSClipboard::the().size());
memcpy(shared_buffer->data(), clipboard.data(), clipboard.size());
shared_buffer->seal();
shared_buffer->share_with(client_pid());
response.clipboard.shared_buffer_id = shared_buffer->shared_buffer_id();
response.clipboard.contents_size = WSClipboard::the().size();
response.clipboard.contents_size = clipboard.size();
// FIXME: This is a workaround for the fact that SharedBuffers will go away if neither side is retaining them.
// After we respond to GetClipboardContents, we have to wait for the client to ref the buffer on his side.
m_last_sent_clipboard_content = move(shared_buffer);
}
ASSERT(clipboard.data_type().length() < (ssize_t)sizeof(response.text));
if (!clipboard.data_type().is_null())
strcpy(response.text, clipboard.data_type().characters());
response.text_length = clipboard.data_type().length();
post_message(response);
}

View File

@ -35,6 +35,7 @@ public:
void for_each_window(Callback);
void notify_about_new_screen_rect(const Rect&);
void notify_about_clipboard_contents_changed();
void post_paint_message(WSWindow&);
WSMenu* find_menu_by_id(int menu_id)

View File

@ -36,9 +36,13 @@ void WSClipboard::clear()
m_contents_size = 0;
}
void WSClipboard::set_data(NonnullRefPtr<SharedBuffer>&& data, int contents_size)
void WSClipboard::set_data(NonnullRefPtr<SharedBuffer>&& data, int contents_size, const String& data_type)
{
dbgprintf("WSClipboard::set_data <- %p (%u bytes)\n", data->data(), contents_size);
dbg() << "WSClipboard::set_data <- [" << data_type << "] " << data->data() << " (" << contents_size << " bytes)";
m_shared_buffer = move(data);
m_contents_size = contents_size;
m_data_type = data_type;
if (on_content_change)
on_content_change();
}

View File

@ -1,5 +1,6 @@
#pragma once
#include <AK/Function.h>
#include <AK/String.h>
#include <SharedBuffer.h>
@ -13,15 +14,19 @@ public:
return m_shared_buffer;
}
const String& data_type() const { return m_data_type; }
const u8* data() const;
int size() const;
void clear();
void set_data(NonnullRefPtr<SharedBuffer>&&, int contents_size);
void set_data(NonnullRefPtr<SharedBuffer>&&, int contents_size, const String& data_type);
Function<void()> on_content_change;
private:
WSClipboard();
String m_data_type;
RefPtr<SharedBuffer> m_shared_buffer;
int m_contents_size { 0 };
};

View File

@ -504,19 +504,22 @@ private:
class WSAPISetClipboardContentsRequest final : public WSAPIClientRequest {
public:
explicit WSAPISetClipboardContentsRequest(int client_id, int shared_buffer_id, int size)
explicit WSAPISetClipboardContentsRequest(int client_id, int shared_buffer_id, int size, const String& data_type)
: WSAPIClientRequest(WSEvent::APISetClipboardContentsRequest, client_id)
, m_shared_buffer_id(shared_buffer_id)
, m_size(size)
, m_data_type(data_type)
{
}
int shared_buffer_id() const { return m_shared_buffer_id; }
int size() const { return m_size; }
const String& data_type() const { return m_data_type; }
private:
int m_shared_buffer_id { 0 };
int m_size { 0 };
String m_data_type;
};
class WSAPIGetClipboardContentsRequest final : public WSAPIClientRequest {

View File

@ -1,3 +1,4 @@
#include "WSClipboard.h"
#include <Kernel/KeyCode.h>
#include <Kernel/MousePacket.h>
#include <LibCore/CLocalSocket.h>
@ -47,6 +48,12 @@ WSEventLoop::WSEventLoop()
m_mouse_notifier = make<CNotifier>(m_mouse_fd, CNotifier::Read);
m_mouse_notifier->on_ready_to_read = [this] { drain_mouse(); };
WSClipboard::the().on_content_change = [&] {
WSClientConnection::for_each_client([&](auto& client) {
client.notify_about_clipboard_contents_changed();
});
};
}
WSEventLoop::~WSEventLoop()