mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 19:27:35 +00: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
10 changed files with 94 additions and 15 deletions
|
@ -15,7 +15,7 @@ GClipboard::GClipboard()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
String GClipboard::data() const
|
GClipboard::DataAndType GClipboard::data_and_type() const
|
||||||
{
|
{
|
||||||
WSAPI_ClientMessage request;
|
WSAPI_ClientMessage request;
|
||||||
request.type = WSAPI_ClientMessage::Type::GetClipboardContents;
|
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");
|
dbgprintf("GClipboard::data() clipping contents size is greater than shared buffer size\n");
|
||||||
return {};
|
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;
|
WSAPI_ClientMessage request;
|
||||||
request.type = WSAPI_ClientMessage::Type::SetClipboardContents;
|
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());
|
shared_buffer->share_with(GWindowServerConnection::the().server_pid());
|
||||||
request.clipboard.shared_buffer_id = shared_buffer->shared_buffer_id();
|
request.clipboard.shared_buffer_id = shared_buffer->shared_buffer_id();
|
||||||
request.clipboard.contents_size = data.length();
|
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);
|
auto response = GWindowServerConnection::the().sync_request(request, WSAPI_ServerMessage::Type::DidSetClipboardContents);
|
||||||
ASSERT(response.clipboard.shared_buffer_id == shared_buffer->shared_buffer_id());
|
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
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Badge.h>
|
||||||
|
#include <AK/Function.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
|
|
||||||
|
class GWindowServerConnection;
|
||||||
|
|
||||||
class GClipboard {
|
class GClipboard {
|
||||||
public:
|
public:
|
||||||
static GClipboard& the();
|
static GClipboard& the();
|
||||||
|
|
||||||
String data() const;
|
String data() const { return data_and_type().data; }
|
||||||
void set_data(const StringView&);
|
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:
|
private:
|
||||||
GClipboard();
|
GClipboard();
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <LibCore/CObject.h>
|
#include <LibCore/CObject.h>
|
||||||
#include <LibGUI/GAction.h>
|
#include <LibGUI/GAction.h>
|
||||||
#include <LibGUI/GApplication.h>
|
#include <LibGUI/GApplication.h>
|
||||||
|
#include <LibGUI/GClipboard.h>
|
||||||
#include <LibGUI/GDesktop.h>
|
#include <LibGUI/GDesktop.h>
|
||||||
#include <LibGUI/GMenu.h>
|
#include <LibGUI/GMenu.h>
|
||||||
#include <LibGUI/GWidget.h>
|
#include <LibGUI/GWidget.h>
|
||||||
|
@ -244,6 +245,11 @@ void GWindowServerConnection::postprocess_bundles(Vector<IncomingMessageBundle>&
|
||||||
continue;
|
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) {
|
if (event.type == WSAPI_ServerMessage::Error) {
|
||||||
dbgprintf("GEventLoop got error message from server\n");
|
dbgprintf("GEventLoop got error message from server\n");
|
||||||
dbgprintf(" - error message: %s\n", String(event.text, event.text_length).characters());
|
dbgprintf(" - error message: %s\n", String(event.text, event.text_length).characters());
|
||||||
|
|
|
@ -112,6 +112,7 @@ struct WSAPI_ServerMessage {
|
||||||
DidSetResolution,
|
DidSetResolution,
|
||||||
DidSetWindowHasAlphaChannel,
|
DidSetWindowHasAlphaChannel,
|
||||||
ScreenRectChanged,
|
ScreenRectChanged,
|
||||||
|
ClipboardContentsChanged,
|
||||||
|
|
||||||
__Begin_WM_Events__,
|
__Begin_WM_Events__,
|
||||||
WM_WindowRemoved,
|
WM_WindowRemoved,
|
||||||
|
|
|
@ -87,6 +87,19 @@ void WSClientConnection::notify_about_new_screen_rect(const Rect& rect)
|
||||||
post_message(message);
|
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)
|
void WSClientConnection::event(CEvent& event)
|
||||||
{
|
{
|
||||||
if (static_cast<WSEvent&>(event).is_client_request()) {
|
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));
|
CEventLoop::current().post_event(*this, make<WSAPIGetWindowRectRequest>(client_id(), message.window_id));
|
||||||
break;
|
break;
|
||||||
case WSAPI_ClientMessage::Type::SetClipboardContents:
|
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;
|
break;
|
||||||
case WSAPI_ClientMessage::Type::GetClipboardContents:
|
case WSAPI_ClientMessage::Type::GetClipboardContents:
|
||||||
CEventLoop::current().post_event(*this, make<WSAPIGetClipboardContentsRequest>(client_id()));
|
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");
|
post_error("WSAPISetClipboardContentsRequest: Bad shared buffer ID");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
WSClipboard::the().set_data(*shared_buffer, request.size());
|
WSClipboard::the().set_data(*shared_buffer, request.size(), request.data_type());
|
||||||
WSAPI_ServerMessage response;
|
WSAPI_ServerMessage response;
|
||||||
response.type = WSAPI_ServerMessage::Type::DidSetClipboardContents;
|
response.type = WSAPI_ServerMessage::Type::DidSetClipboardContents;
|
||||||
response.clipboard.shared_buffer_id = shared_buffer->shared_buffer_id();
|
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&)
|
void WSClientConnection::handle_request(const WSAPIGetClipboardContentsRequest&)
|
||||||
{
|
{
|
||||||
|
auto& clipboard = WSClipboard::the();
|
||||||
WSAPI_ServerMessage response;
|
WSAPI_ServerMessage response;
|
||||||
response.type = WSAPI_ServerMessage::Type::DidGetClipboardContents;
|
response.type = WSAPI_ServerMessage::Type::DidGetClipboardContents;
|
||||||
response.clipboard.shared_buffer_id = -1;
|
response.clipboard.shared_buffer_id = -1;
|
||||||
response.clipboard.contents_size = 0;
|
response.clipboard.contents_size = 0;
|
||||||
if (WSClipboard::the().size()) {
|
if (clipboard.size()) {
|
||||||
// FIXME: Optimize case where an app is copy/pasting within itself.
|
// 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.
|
// 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..
|
// 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);
|
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->seal();
|
||||||
shared_buffer->share_with(client_pid());
|
shared_buffer->share_with(client_pid());
|
||||||
response.clipboard.shared_buffer_id = shared_buffer->shared_buffer_id();
|
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.
|
// 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.
|
// 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);
|
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);
|
post_message(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ public:
|
||||||
void for_each_window(Callback);
|
void for_each_window(Callback);
|
||||||
|
|
||||||
void notify_about_new_screen_rect(const Rect&);
|
void notify_about_new_screen_rect(const Rect&);
|
||||||
|
void notify_about_clipboard_contents_changed();
|
||||||
void post_paint_message(WSWindow&);
|
void post_paint_message(WSWindow&);
|
||||||
|
|
||||||
WSMenu* find_menu_by_id(int menu_id)
|
WSMenu* find_menu_by_id(int menu_id)
|
||||||
|
|
|
@ -36,9 +36,13 @@ void WSClipboard::clear()
|
||||||
m_contents_size = 0;
|
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_shared_buffer = move(data);
|
||||||
m_contents_size = contents_size;
|
m_contents_size = contents_size;
|
||||||
|
m_data_type = data_type;
|
||||||
|
|
||||||
|
if (on_content_change)
|
||||||
|
on_content_change();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Function.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <SharedBuffer.h>
|
#include <SharedBuffer.h>
|
||||||
|
|
||||||
|
@ -13,15 +14,19 @@ public:
|
||||||
return m_shared_buffer;
|
return m_shared_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const String& data_type() const { return m_data_type; }
|
||||||
const u8* data() const;
|
const u8* data() const;
|
||||||
int size() const;
|
int size() const;
|
||||||
|
|
||||||
void clear();
|
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:
|
private:
|
||||||
WSClipboard();
|
WSClipboard();
|
||||||
|
|
||||||
|
String m_data_type;
|
||||||
RefPtr<SharedBuffer> m_shared_buffer;
|
RefPtr<SharedBuffer> m_shared_buffer;
|
||||||
int m_contents_size { 0 };
|
int m_contents_size { 0 };
|
||||||
};
|
};
|
||||||
|
|
|
@ -504,19 +504,22 @@ private:
|
||||||
|
|
||||||
class WSAPISetClipboardContentsRequest final : public WSAPIClientRequest {
|
class WSAPISetClipboardContentsRequest final : public WSAPIClientRequest {
|
||||||
public:
|
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)
|
: WSAPIClientRequest(WSEvent::APISetClipboardContentsRequest, client_id)
|
||||||
, m_shared_buffer_id(shared_buffer_id)
|
, m_shared_buffer_id(shared_buffer_id)
|
||||||
, m_size(size)
|
, m_size(size)
|
||||||
|
, m_data_type(data_type)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int shared_buffer_id() const { return m_shared_buffer_id; }
|
int shared_buffer_id() const { return m_shared_buffer_id; }
|
||||||
int size() const { return m_size; }
|
int size() const { return m_size; }
|
||||||
|
const String& data_type() const { return m_data_type; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_shared_buffer_id { 0 };
|
int m_shared_buffer_id { 0 };
|
||||||
int m_size { 0 };
|
int m_size { 0 };
|
||||||
|
String m_data_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
class WSAPIGetClipboardContentsRequest final : public WSAPIClientRequest {
|
class WSAPIGetClipboardContentsRequest final : public WSAPIClientRequest {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include "WSClipboard.h"
|
||||||
#include <Kernel/KeyCode.h>
|
#include <Kernel/KeyCode.h>
|
||||||
#include <Kernel/MousePacket.h>
|
#include <Kernel/MousePacket.h>
|
||||||
#include <LibCore/CLocalSocket.h>
|
#include <LibCore/CLocalSocket.h>
|
||||||
|
@ -47,6 +48,12 @@ WSEventLoop::WSEventLoop()
|
||||||
|
|
||||||
m_mouse_notifier = make<CNotifier>(m_mouse_fd, CNotifier::Read);
|
m_mouse_notifier = make<CNotifier>(m_mouse_fd, CNotifier::Read);
|
||||||
m_mouse_notifier->on_ready_to_read = [this] { drain_mouse(); };
|
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()
|
WSEventLoop::~WSEventLoop()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue