mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-10 02:54:54 +03:00
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:
parent
9d2c4d223a
commit
c543ee5c5b
Notes:
sideshowbarker
2024-07-19 12:07:18 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/c543ee5c5bc
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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());
|
||||
|
@ -112,6 +112,7 @@ struct WSAPI_ServerMessage {
|
||||
DidSetResolution,
|
||||
DidSetWindowHasAlphaChannel,
|
||||
ScreenRectChanged,
|
||||
ClipboardContentsChanged,
|
||||
|
||||
__Begin_WM_Events__,
|
||||
WM_WindowRemoved,
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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 };
|
||||
};
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user