UI: Allow copy & paste images in linux/Wayland #119117
|
@ -108,6 +108,9 @@ static bool has_libdecor = true;
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#include "IMB_imbuf.hh"
|
||||
#include "IMB_imbuf_types.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Forward Declarations
|
||||
* \{ */
|
||||
|
@ -7473,6 +7476,153 @@ void GHOST_SystemWayland::putClipboard(const char *buffer, bool selection) const
|
|||
}
|
||||
}
|
||||
|
||||
static constexpr const char *ghost_wl_mime_img_png = "image/png";
|
||||
|
||||
GHOST_TSuccess GHOST_SystemWayland::hasClipboardImage(void) const
|
||||
{
|
||||
GWL_Seat *seat = gwl_display_seat_active_get(display_);
|
||||
if (UNLIKELY(!seat)) {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
GWL_DataOffer *data_offer = seat->data_offer_copy_paste;
|
||||
if (data_offer) {
|
||||
if (data_offer->types.count(ghost_wl_mime_img_png)) {
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
uint *GHOST_SystemWayland::getClipboardImage(int *r_width, int *r_height) const
|
||||
{
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_server_guard{*server_mutex};
|
||||
#endif
|
||||
|
||||
GWL_Seat *seat = gwl_display_seat_active_get(display_);
|
||||
if (UNLIKELY(!seat)) {
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
std::mutex &mutex = seat->data_offer_copy_paste_mutex;
|
||||
mutex.lock();
|
||||
bool mutex_locked = true;
|
||||
|
||||
uint *rgba = nullptr;
|
||||
|
||||
GWL_DataOffer *data_offer = seat->data_offer_copy_paste;
|
||||
if (data_offer) {
|
||||
/* Check if the source offers a supported mime type.
|
||||
* This check could be skipped, because the paste option is not supposed to be enabled
|
||||
* otherwise. */
|
||||
if (data_offer->types.count(ghost_wl_mime_img_png)) {
|
||||
/* Receive the clipboard in a thread, performing round-trips while waiting,
|
||||
* so pasting content from own 'primary->data_source' doesn't hang. */
|
||||
struct ThreadResult {
|
||||
char *data = nullptr;
|
||||
std::atomic<bool> done = false;
|
||||
} thread_result;
|
||||
|
||||
size_t data_len = 0;
|
||||
auto read_clipboard_fn = [&data_len](GWL_DataOffer *data_offer,
|
||||
const char *mime_receive,
|
||||
std::mutex *mutex,
|
||||
ThreadResult *thread_result) {
|
||||
thread_result->data = read_buffer_from_data_offer(data_offer,
|
||||
mime_receive,
|
||||
mutex,
|
||||
true,
|
||||
&data_len);
|
||||
thread_result->done = true;
|
||||
};
|
||||
std::thread read_thread(read_clipboard_fn,
|
||||
data_offer,
|
||||
ghost_wl_mime_img_png,
|
||||
&mutex,
|
||||
&thread_result);
|
||||
read_thread.detach();
|
||||
|
||||
while (!thread_result.done) {
|
||||
wl_display_roundtrip(display_->wl.display);
|
||||
}
|
||||
|
||||
/* Generate the image buffer with the recieved data */
|
||||
ImBuf *ibuf = IMB_ibImageFromMemory((uint8_t *)thread_result.data,
|
||||
data_len,
|
||||
IB_rect,
|
||||
nullptr,
|
||||
"<clipboard>");
|
||||
if (ibuf) {
|
||||
*r_width = ibuf->x;
|
||||
*r_height = ibuf->y;
|
||||
const uint64_t byte_count = uint64_t(ibuf->x) * ibuf->y * 4;
|
||||
rgba = (uint *)malloc(byte_count);
|
||||
std::memcpy(rgba, ibuf->byte_buffer.data, byte_count);
|
||||
IMB_freeImBuf(ibuf);
|
||||
}
|
||||
|
||||
/* After reading the data offer, the mutex gets unlocked */
|
||||
mutex_locked = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mutex_locked) {
|
||||
mutex.unlock();
|
||||
}
|
||||
return rgba;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_SystemWayland::putClipboardImage(uint *rgba, int width, int height) const
|
||||
{
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_server_guard{*server_mutex};
|
||||
#endif
|
||||
|
||||
/* Create a wl_data_source object */
|
||||
GWL_Seat *seat = gwl_display_seat_active_get(display_);
|
||||
if (UNLIKELY(!seat)) {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
std::lock_guard lock(seat->data_source_mutex);
|
||||
|
||||
GWL_DataSource *data_source = seat->data_source;
|
||||
|
||||
/* Load buffer into an ImBuf and convert to PNG */
|
||||
ImBuf *ibuf = IMB_allocFromBuffer(reinterpret_cast<uint8_t *>(rgba), nullptr, width, height, 32);
|
||||
ibuf->ftype = IMB_FTYPE_PNG;
|
||||
ibuf->foptions.quality = 15;
|
||||
if (!IMB_saveiff(ibuf, "<memory>", IB_rect | IB_mem)) {
|
||||
IMB_freeImBuf(ibuf);
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
/* Copy ImBuf encoded_buffer to data source */
|
||||
GWL_SimpleBuffer *imgbuffer = &data_source->buffer_out;
|
||||
gwl_simple_buffer_free_data(imgbuffer);
|
||||
imgbuffer->data_size = ibuf->encoded_buffer_size;
|
||||
char *data = static_cast<char *>(malloc(imgbuffer->data_size));
|
||||
std::memcpy(data, ibuf->encoded_buffer.data, ibuf->encoded_buffer_size);
|
||||
imgbuffer->data = data;
|
||||
|
||||
data_source->wl.source =
|
||||
wl_data_device_manager_create_data_source(display_->wl.data_device_manager);
|
||||
wl_data_source_add_listener(data_source->wl.source, &data_source_listener, seat);
|
||||
|
||||
/* Advertise the mime types supported */
|
||||
wl_data_source_offer(data_source->wl.source, "image/png");
|
||||
|
||||
if (seat->wl.data_device) {
|
||||
wl_data_device_set_selection(seat->wl.data_device,
|
||||
data_source->wl.source,
|
||||
seat->data_source_serial);
|
||||
}
|
||||
|
||||
IMB_freeImBuf(ibuf);
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
uint8_t GHOST_SystemWayland::getNumDisplays() const
|
||||
{
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
|
@ -8048,9 +8198,7 @@ GHOST_TCapabilityFlag GHOST_SystemWayland::getCapabilities() const
|
|||
* is negligible. */
|
||||
GHOST_kCapabilityGPUReadFrontBuffer |
|
||||
/* This WAYLAND back-end has not yet implemented desktop color sample. */
|
||||
GHOST_kCapabilityDesktopSample |
|
||||
/* This WAYLAND back-end has not yet implemented image copy/paste. */
|
||||
GHOST_kCapabilityClipboardImages));
|
||||
GHOST_kCapabilityDesktopSample));
|
||||
}
|
||||
|
||||
bool GHOST_SystemWayland::cursor_grab_use_software_display_get(const GHOST_TGrabCursorMode mode)
|
||||
|
|
|
@ -159,6 +159,27 @@ class GHOST_SystemWayland : public GHOST_System {
|
|||
|
||||
void putClipboard(const char *buffer, bool selection) const override;
|
||||
|
||||
/**
|
||||
* Returns GHOST_kSuccess if the clipboard contains an image.
|
||||
*/
|
||||
GHOST_TSuccess hasClipboardImage(void) const override;
|
||||
|
||||
/**
|
||||
* Get image data from the Clipboard
|
||||
* \param r_width: the returned image width in pixels.
|
||||
* \param r_height: the returned image height in pixels.
|
||||
* \return pointer uint array in RGBA byte order. Caller must free.
|
||||
*/
|
||||
uint *getClipboardImage(int *r_width, int *r_height) const override;
|
||||
|
||||
/**
|
||||
* Put image data to the Clipboard
|
||||
* \param rgba: uint array in RGBA byte order.
|
||||
* \param width: the image width in pixels.
|
||||
* \param height: the image height in pixels.
|
||||
*/
|
||||
GHOST_TSuccess putClipboardImage(uint *rgba, int width, int height) const override;
|
||||
|
||||
uint8_t getNumDisplays() const override;
|
||||
|
||||
uint64_t getMilliSeconds() const override;
|
||||
|
|
|
@ -211,7 +211,7 @@ class IMAGE_MT_image(Menu):
|
|||
|
||||
layout.separator()
|
||||
|
||||
if sys.platform[:3] == "win":
|
||||
if sys.platform[:3] in ["win", "lin"]:
|
||||
layout.operator("image.clipboard_copy", text="Copy")
|
||||
layout.operator("image.clipboard_paste", text="Paste")
|
||||
layout.separator()
|
||||
|
|
Loading…
Reference in New Issue