mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2026-01-06 06:50:57 +00:00
client: add clipboard support
* windows - untested * linux - small strings both ways, large implemented differently: * client to guest - support INCR * guest to client - we supply a single possibly very large property * requires server changes in next patch to work with spice-vmc
This commit is contained in:
parent
4f8545ed62
commit
ce03f5449d
@ -1325,6 +1325,12 @@ void Application::on_app_activated()
|
||||
{
|
||||
_active = true;
|
||||
_key_handler->on_focus_in();
|
||||
Platform::set_clipboard_listener(this);
|
||||
}
|
||||
|
||||
void Application::on_clipboard_change()
|
||||
{
|
||||
_client.on_clipboard_change();
|
||||
}
|
||||
|
||||
void Application::on_app_deactivated()
|
||||
|
||||
@ -141,6 +141,7 @@ typedef std::list<GUIBarrier*> GUIBarriers;
|
||||
class Application : public ProcessLoop,
|
||||
public Platform::EventListener,
|
||||
public Platform::DisplayModeListener,
|
||||
public Platform::ClipboardListener,
|
||||
public CommandTarget {
|
||||
public:
|
||||
|
||||
@ -190,6 +191,7 @@ public:
|
||||
virtual void on_app_deactivated();
|
||||
virtual void on_monitors_change();
|
||||
virtual void on_display_mode_change();
|
||||
virtual void on_clipboard_change();
|
||||
void on_connected();
|
||||
void on_disconnected(int spice_error_code);
|
||||
void on_disconnecting();
|
||||
|
||||
@ -117,6 +117,17 @@ public:
|
||||
|
||||
class DisplayModeListener;
|
||||
static void set_display_mode_listner(DisplayModeListener* listener);
|
||||
|
||||
class ClipboardListener;
|
||||
static void set_clipboard_listener(ClipboardListener* listener);
|
||||
|
||||
enum {
|
||||
CLIPBOARD_UTF8_TEXT = 1,
|
||||
};
|
||||
|
||||
static bool set_clipboard_data(uint32_t type, const uint8_t* data, int32_t size);
|
||||
static bool get_clipboard_data(uint32_t type, uint8_t* data, int32_t size);
|
||||
static int32_t get_clipboard_data_size(uint32_t type);
|
||||
};
|
||||
|
||||
class Platform::EventListener {
|
||||
@ -127,6 +138,12 @@ public:
|
||||
virtual void on_monitors_change() = 0;
|
||||
};
|
||||
|
||||
class Platform::ClipboardListener {
|
||||
public:
|
||||
virtual ~ClipboardListener() {}
|
||||
virtual void on_clipboard_change() = 0;
|
||||
};
|
||||
|
||||
class Platform::RecordClient {
|
||||
public:
|
||||
virtual ~RecordClient() {}
|
||||
|
||||
@ -320,6 +320,9 @@ RedClient::RedClient(Application& application)
|
||||
, _agent_msg (new VDAgentMessage)
|
||||
, _agent_msg_data (NULL)
|
||||
, _agent_msg_pos (0)
|
||||
, _agent_out_msg (NULL)
|
||||
, _agent_out_msg_size (0)
|
||||
, _agent_out_msg_pos (0)
|
||||
, _agent_tokens (0)
|
||||
, _agent_timer (new AgentTimer())
|
||||
, _migrate (*this)
|
||||
@ -736,6 +739,73 @@ void RedClient::on_display_mode_change()
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t get_agent_clipboard_type(uint32_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case Platform::CLIPBOARD_UTF8_TEXT:
|
||||
return VD_AGENT_CLIPBOARD_UTF8_TEXT;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void RedClient::post_agent_clipboard()
|
||||
{
|
||||
uint32_t size;
|
||||
|
||||
while (_agent_tokens &&
|
||||
(size = MIN(VD_AGENT_MAX_DATA_SIZE,
|
||||
_agent_out_msg_size - _agent_out_msg_pos))) {
|
||||
Message* message = new Message(SPICE_MSGC_MAIN_AGENT_DATA);
|
||||
void* data =
|
||||
spice_marshaller_reserve_space(message->marshaller(), size);
|
||||
memcpy(data, (uint8_t*)_agent_out_msg + _agent_out_msg_pos, size);
|
||||
_agent_tokens--;
|
||||
post_message(message);
|
||||
_agent_out_msg_pos += size;
|
||||
if (_agent_out_msg_pos == _agent_out_msg_size) {
|
||||
delete[] (uint8_t *)_agent_out_msg;
|
||||
_agent_out_msg = NULL;
|
||||
_agent_out_msg_size = 0;
|
||||
_agent_out_msg_pos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//FIXME: currently supports text only; better name - poll_clipboard?
|
||||
void RedClient::on_clipboard_change()
|
||||
{
|
||||
//FIXME: check connected - assert on disconnect
|
||||
uint32_t clip_type = Platform::CLIPBOARD_UTF8_TEXT;
|
||||
int32_t clip_size = Platform::get_clipboard_data_size(clip_type);
|
||||
|
||||
if (!clip_size || !_agent_connected) {
|
||||
return;
|
||||
}
|
||||
if (_agent_out_msg) {
|
||||
DBG(0, "clipboard change is already pending");
|
||||
return;
|
||||
}
|
||||
_agent_out_msg_pos = 0;
|
||||
_agent_out_msg_size = sizeof(VDAgentMessage) + sizeof(VDAgentClipboard) + clip_size;
|
||||
_agent_out_msg = (VDAgentMessage*)new uint8_t[_agent_out_msg_size];
|
||||
_agent_out_msg->protocol = VD_AGENT_PROTOCOL;
|
||||
_agent_out_msg->type = VD_AGENT_CLIPBOARD;
|
||||
_agent_out_msg->opaque = 0;
|
||||
_agent_out_msg->size = sizeof(VDAgentClipboard) + clip_size;
|
||||
VDAgentClipboard* clipboard = (VDAgentClipboard*)_agent_out_msg->data;
|
||||
clipboard->type = get_agent_clipboard_type(clip_type);
|
||||
if (!Platform::get_clipboard_data(clip_type, clipboard->data, clip_size)) {
|
||||
delete[] (uint8_t *)_agent_out_msg;
|
||||
_agent_out_msg = NULL;
|
||||
_agent_out_msg_size = 0;
|
||||
return;
|
||||
}
|
||||
if (_agent_tokens) {
|
||||
post_agent_clipboard();
|
||||
}
|
||||
}
|
||||
|
||||
void RedClient::set_mouse_mode(uint32_t supported_modes, uint32_t current_mode)
|
||||
{
|
||||
if (current_mode != _mouse_mode) {
|
||||
@ -762,6 +832,17 @@ void RedClient::set_mouse_mode(uint32_t supported_modes, uint32_t current_mode)
|
||||
}
|
||||
}
|
||||
|
||||
void RedClient::on_agent_clipboard(VDAgentClipboard* clipboard, uint32_t size)
|
||||
{
|
||||
switch (clipboard->type) {
|
||||
case VD_AGENT_CLIPBOARD_UTF8_TEXT:
|
||||
Platform::set_clipboard_data(Platform::CLIPBOARD_UTF8_TEXT, clipboard->data, size);
|
||||
break;
|
||||
default:
|
||||
THROW("unexpected vdagent clipboard data type");
|
||||
}
|
||||
}
|
||||
|
||||
void RedClient::handle_init(RedPeer::InMessage* message)
|
||||
{
|
||||
SpiceMsgMainInit *init = (SpiceMsgMainInit *)message->data();
|
||||
@ -776,7 +857,7 @@ void RedClient::handle_init(RedPeer::InMessage* message)
|
||||
Message* msg = new Message(SPICE_MSGC_MAIN_AGENT_START);
|
||||
SpiceMsgcMainAgentStart agent_start;
|
||||
agent_start.num_tokens = ~0;
|
||||
_marshallers->msgc_main_agent_start(msg->marshaller(), &agent_start);
|
||||
_marshallers->msgc_main_agent_start(msg->marshaller(), &agent_start);
|
||||
post_message(msg);
|
||||
}
|
||||
|
||||
@ -900,6 +981,11 @@ void RedClient::handle_agent_data(RedPeer::InMessage* message)
|
||||
on_agent_reply((VDAgentReply*)_agent_msg_data);
|
||||
break;
|
||||
}
|
||||
case VD_AGENT_CLIPBOARD: {
|
||||
on_agent_clipboard((VDAgentClipboard*)_agent_msg_data,
|
||||
_agent_msg->size - sizeof(VDAgentClipboard));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DBG(0, "Unsupported message type %u size %u", _agent_msg->type, _agent_msg->size);
|
||||
}
|
||||
@ -914,6 +1000,9 @@ void RedClient::handle_agent_tokens(RedPeer::InMessage* message)
|
||||
{
|
||||
SpiceMsgMainAgentTokens *token = (SpiceMsgMainAgentTokens *)message->data();
|
||||
_agent_tokens += token->num_tokens;
|
||||
if (_agent_out_msg_pos < _agent_out_msg_size) {
|
||||
post_agent_clipboard();
|
||||
}
|
||||
}
|
||||
|
||||
void RedClient::handle_migrate_switch_host(RedPeer::InMessage* message)
|
||||
|
||||
@ -186,6 +186,7 @@ public:
|
||||
PixmapCache& get_pixmap_cache() {return _pixmap_cache;}
|
||||
uint64_t get_pixmap_cache_size() { return _pixmap_cache_size;}
|
||||
void on_display_mode_change();
|
||||
void on_clipboard_change();
|
||||
void for_each_channel(ForEachChannelFunc& func);
|
||||
void on_mouse_capture_trigger(RedScreen& screen);
|
||||
|
||||
@ -222,6 +223,8 @@ private:
|
||||
void handle_migrate_switch_host(RedPeer::InMessage* message);
|
||||
|
||||
void on_agent_reply(VDAgentReply* reply);
|
||||
void on_agent_clipboard(VDAgentClipboard* clipboard, uint32_t size);
|
||||
void post_agent_clipboard();
|
||||
|
||||
ChannelFactory* find_factory(uint32_t type);
|
||||
void create_channel(uint32_t type, uint32_t id);
|
||||
@ -250,9 +253,13 @@ private:
|
||||
bool _agent_connected;
|
||||
bool _agent_mon_config_sent;
|
||||
bool _agent_disp_config_sent;
|
||||
//FIXME: rename to in/out, extract all agent stuff?
|
||||
VDAgentMessage* _agent_msg;
|
||||
uint8_t* _agent_msg_data;
|
||||
uint32_t _agent_msg_pos;
|
||||
VDAgentMessage* _agent_out_msg;
|
||||
uint32_t _agent_out_msg_size;
|
||||
uint32_t _agent_out_msg_pos;
|
||||
uint32_t _agent_tokens;
|
||||
AutoRef<AgentTimer> _agent_timer;
|
||||
|
||||
|
||||
@ -749,6 +749,96 @@ void WinPlatform::exit_modal_loop()
|
||||
modal_loop_active = false;
|
||||
}
|
||||
|
||||
void Platform::set_clipboard_listener(ClipboardListener* listener)
|
||||
{
|
||||
//FIXME: call only on change, use statics
|
||||
listener->on_clipboard_change();
|
||||
}
|
||||
|
||||
UINT get_format(uint32_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case Platform::CLIPBOARD_UTF8_TEXT:
|
||||
return CF_UNICODETEXT;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool Platform::set_clipboard_data(uint32_t type, const uint8_t* data, int32_t size)
|
||||
{
|
||||
UINT format = get_format(type);
|
||||
HGLOBAL clip_data;
|
||||
LPVOID clip_buf;
|
||||
int clip_size;
|
||||
bool ret;
|
||||
|
||||
//LOG_INFO("type %u size %d %s", type, size, data);
|
||||
if (!format || !OpenClipboard(paltform_win)) {
|
||||
return false;
|
||||
}
|
||||
clip_size = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)data, size, NULL, 0);
|
||||
if (!clip_size || !(clip_data = GlobalAlloc(GMEM_DDESHARE, clip_size * sizeof(WCHAR)))) {
|
||||
CloseClipboard();
|
||||
return false;
|
||||
}
|
||||
if (!(clip_buf = GlobalLock(clip_data))) {
|
||||
GlobalFree(clip_data);
|
||||
CloseClipboard();
|
||||
return false;
|
||||
}
|
||||
ret = !!MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)data, size, (LPWSTR)clip_buf, clip_size);
|
||||
GlobalUnlock(clip_data);
|
||||
if (ret) {
|
||||
EmptyClipboard();
|
||||
ret = !!SetClipboardData(format, clip_data);
|
||||
}
|
||||
CloseClipboard();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Platform::get_clipboard_data(uint32_t type, uint8_t* data, int32_t size)
|
||||
{
|
||||
UINT format = get_format(type);
|
||||
HANDLE clip_data;
|
||||
LPVOID clip_buf;
|
||||
bool ret;
|
||||
|
||||
LOG_INFO("type %u size %d", type, size);
|
||||
if (!format || !IsClipboardFormatAvailable(format) || !OpenClipboard(paltform_win)) {
|
||||
return false;
|
||||
}
|
||||
if (!(clip_data = GetClipboardData(format)) || !(clip_buf = GlobalLock(clip_data))) {
|
||||
CloseClipboard();
|
||||
return false;
|
||||
}
|
||||
ret = !!WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)clip_buf, -1, (LPSTR)data, size, NULL, NULL);
|
||||
GlobalUnlock(clip_data);
|
||||
CloseClipboard();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int32_t Platform::get_clipboard_data_size(uint32_t type)
|
||||
{
|
||||
UINT format = get_format(type);
|
||||
HANDLE clip_data;
|
||||
LPVOID clip_buf;
|
||||
int clip_size;
|
||||
|
||||
if (!format || !IsClipboardFormatAvailable(format) || !OpenClipboard(paltform_win)) {
|
||||
return 0;
|
||||
}
|
||||
if (!(clip_data = GetClipboardData(format)) || !(clip_buf = GlobalLock(clip_data))) {
|
||||
CloseClipboard();
|
||||
return 0;
|
||||
}
|
||||
clip_size = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)clip_buf, -1, NULL, 0, NULL, NULL);
|
||||
GlobalUnlock(clip_data);
|
||||
CloseClipboard();
|
||||
return clip_size;
|
||||
}
|
||||
|
||||
|
||||
static bool has_console = false;
|
||||
|
||||
static void create_console()
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/XKBlib.h>
|
||||
#include <X11/Xresource.h>
|
||||
#include <X11/cursorfont.h>
|
||||
@ -63,7 +64,7 @@
|
||||
//#define X_DEBUG_SYNC(display) XSync(display, False)
|
||||
#define X_DEBUG_SYNC(display)
|
||||
#ifdef HAVE_XRANDR12
|
||||
#define USE_XRANDR_1_2
|
||||
//#define USE_XRANDR_1_2
|
||||
#endif
|
||||
|
||||
static Display* x_display = NULL;
|
||||
@ -76,6 +77,7 @@ static GLXFBConfig **fb_config = NULL;
|
||||
static XIM x_input_method = NULL;
|
||||
static XIC x_input_context = NULL;
|
||||
|
||||
static Window platform_win;
|
||||
static XContext win_proc_context;
|
||||
static ProcessLoop* main_loop = NULL;
|
||||
static int focus_count = 0;
|
||||
@ -95,6 +97,19 @@ static bool using_xrender_0_5 = false;
|
||||
static unsigned int caps_lock_mask = 0;
|
||||
static unsigned int num_lock_mask = 0;
|
||||
|
||||
//FIXME: nicify
|
||||
static uint8_t* clipboard_data = NULL;
|
||||
static int32_t clipboard_data_size = 0;
|
||||
static int32_t clipboard_data_space = 0;
|
||||
static Mutex clipboard_lock;
|
||||
static Atom clipboard_prop;
|
||||
static Atom incr_atom;
|
||||
static Atom utf8_atom;
|
||||
//static Atom clipboard_type_utf8;
|
||||
#ifdef USE_XRANDR_1_2
|
||||
static bool clipboard_inited = false;
|
||||
#endif
|
||||
|
||||
class DefaultEventListener: public Platform::EventListener {
|
||||
public:
|
||||
virtual void on_app_activated() {}
|
||||
@ -113,6 +128,13 @@ public:
|
||||
static DefaultDisplayModeListener default_display_mode_listener;
|
||||
static Platform::DisplayModeListener* display_mode_listener = &default_display_mode_listener;
|
||||
|
||||
class DefaultClipboardListener: public Platform::ClipboardListener {
|
||||
public:
|
||||
void on_clipboard_change() {}
|
||||
};
|
||||
|
||||
static DefaultClipboardListener default_clipboard_listener;
|
||||
static Platform::ClipboardListener* clipboard_listener = &default_clipboard_listener;
|
||||
|
||||
NamedPipe::ListenerRef NamedPipe::create(const char *name, ListenerInterface& listener_interface)
|
||||
{
|
||||
@ -689,6 +711,16 @@ private:
|
||||
bool _out_of_sync;
|
||||
};
|
||||
|
||||
static void intern_clipboard_atoms()
|
||||
{
|
||||
static bool interned = false;
|
||||
if (interned) return;
|
||||
clipboard_prop = XInternAtom(x_display, "CLIPBOARD", False);
|
||||
incr_atom = XInternAtom(x_display, "INCR", False);
|
||||
utf8_atom = XInternAtom(x_display, "UTF8_STRING", False);
|
||||
interned = true;
|
||||
}
|
||||
|
||||
DynamicScreen::DynamicScreen(Display* display, int screen, int& next_mon_id)
|
||||
: XScreen(display, screen)
|
||||
, Monitor(next_mon_id++)
|
||||
@ -697,10 +729,12 @@ DynamicScreen::DynamicScreen(Display* display, int screen, int& next_mon_id)
|
||||
, _out_of_sync (false)
|
||||
{
|
||||
X_DEBUG_SYNC(display);
|
||||
Window root_window = RootWindow(display, screen);
|
||||
XSelectInput(display, root_window, StructureNotifyMask);
|
||||
XRRSelectInput(display, root_window, RRScreenChangeNotifyMask);
|
||||
XPlatform::set_win_proc(root_window, root_win_proc);
|
||||
//FIXME: replace RootWindow() in other refs as well?
|
||||
platform_win = XCreateSimpleWindow(display, RootWindow(display, screen), 0, 0, 1, 1, 0, 0, 0);
|
||||
XSelectInput(display, platform_win, StructureNotifyMask);
|
||||
XRRSelectInput(display, platform_win, RRScreenChangeNotifyMask);
|
||||
XPlatform::set_win_proc(platform_win, root_win_proc);
|
||||
intern_clipboard_atoms();
|
||||
X_DEBUG_SYNC(display);
|
||||
}
|
||||
|
||||
@ -962,9 +996,21 @@ MultyMonScreen::MultyMonScreen(Display* display, int screen, int& next_mon_id)
|
||||
}
|
||||
|
||||
XSelectInput(display, root_window, StructureNotifyMask);
|
||||
X_DEBUG_SYNC(get_display());
|
||||
XRRSelectInput(display, root_window, RRScreenChangeNotifyMask);
|
||||
X_DEBUG_SYNC(get_display());
|
||||
intern_clipboard_atoms();
|
||||
XPlatform::set_win_proc(root_window, root_win_proc);
|
||||
X_DEBUG_SYNC(get_display());
|
||||
//
|
||||
//platform_win = XCreateSimpleWindow(display, RootWindow(display, screen), 0, 0, 1, 1, 0, 0, 0);
|
||||
//XSelectInput(display, platform_win, StructureNotifyMask);
|
||||
//XRRSelectInput(display, platform_win, RRScreenChangeNotifyMask);
|
||||
//XPlatform::set_win_proc(platform_win, root_win_proc);
|
||||
//clipboard_prop = XInternAtom(x_display, "CLIPBOARD", False);
|
||||
//
|
||||
clipboard_inited = true;
|
||||
X_DEBUG_SYNC(get_display());
|
||||
}
|
||||
|
||||
MultyMonScreen::~MultyMonScreen()
|
||||
@ -2079,8 +2125,85 @@ void Platform::path_append(std::string& path, const std::string& partial_path)
|
||||
path += partial_path;
|
||||
}
|
||||
|
||||
static void ensure_clipboard_data_space(uint32_t size)
|
||||
{
|
||||
if (size > clipboard_data_space) {
|
||||
delete clipboard_data;
|
||||
clipboard_data = NULL;
|
||||
clipboard_data = new uint8_t[size];
|
||||
assert(clipboard_data);
|
||||
clipboard_data_space = size;
|
||||
}
|
||||
}
|
||||
|
||||
static void realloc_clipboard_data_space(uint32_t size)
|
||||
{
|
||||
if (size <= clipboard_data_space) return;
|
||||
uint32_t old_alloc = clipboard_data_space;
|
||||
clipboard_data_space = size;
|
||||
uint8_t *newbuf = new uint8_t[clipboard_data_space];
|
||||
assert(newbuf);
|
||||
memcpy(newbuf, clipboard_data, old_alloc);
|
||||
delete[] clipboard_data;
|
||||
clipboard_data = newbuf;
|
||||
}
|
||||
|
||||
static void update_clipboard(unsigned long size, uint8_t* data)
|
||||
{
|
||||
clipboard_data_size = 0;
|
||||
ensure_clipboard_data_space(size);
|
||||
memcpy(clipboard_data, data, size);
|
||||
clipboard_data_size = size;
|
||||
}
|
||||
|
||||
|
||||
/* NOTE: Function taken from xsel, original name get_append_property
|
||||
*
|
||||
* Get a window clipboard property and append its data to the clipboard_data
|
||||
*
|
||||
* Returns true if more data is available for receipt.
|
||||
*
|
||||
* Returns false if no data is availab, or on error.
|
||||
*/
|
||||
static bool
|
||||
get_append_clipboard_data (XSelectionEvent* xsel)
|
||||
{
|
||||
Atom target;
|
||||
int format;
|
||||
unsigned long bytesafter, length;
|
||||
unsigned char * value;
|
||||
|
||||
XGetWindowProperty (x_display, xsel->requestor, clipboard_prop,
|
||||
0L, 1000000, True, (Atom)AnyPropertyType,
|
||||
&target, &format, &length, &bytesafter, &value);
|
||||
|
||||
if (target != utf8_atom) {
|
||||
LOG_INFO ("%s: target %d not UTF8", __func__, target);
|
||||
// realloc clipboard_data to 0?
|
||||
return false;
|
||||
} else if (length == 0) {
|
||||
/* A length of 0 indicates the end of the transfer */
|
||||
LOG_INFO ("Got zero length property; end of INCR transfer");
|
||||
return false;
|
||||
} else if (format == 8) {
|
||||
if (clipboard_data_size + length > clipboard_data_space) {
|
||||
realloc_clipboard_data_space(clipboard_data_size + length);
|
||||
}
|
||||
strncpy ((char*)clipboard_data + clipboard_data_size, (char*)value, length);
|
||||
clipboard_data_size += length;
|
||||
LOG_INFO ("Appended %d bytes to buffer\n", length);
|
||||
} else {
|
||||
LOG_WARN ("Retrieved non-8-bit data\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void root_win_proc(XEvent& event)
|
||||
{
|
||||
static bool waiting_for_property_notify = false;
|
||||
|
||||
#ifdef USE_XRANDR_1_2
|
||||
ASSERT(using_xrandr_1_0 || using_xrandr_1_2);
|
||||
#else
|
||||
@ -2101,14 +2224,102 @@ static void root_win_proc(XEvent& event)
|
||||
(*iter)->set_broken();
|
||||
}
|
||||
event_listener->on_monitors_change();
|
||||
return;
|
||||
}
|
||||
|
||||
/*switch (event.type - xrandr_event_base) {
|
||||
case RRScreenChangeNotify:
|
||||
//XRRScreenChangeNotifyEvent * = (XRRScreenChangeNotifyEvent *) &event;
|
||||
XRRUpdateConfiguration(&event);
|
||||
switch (event.type) {
|
||||
case SelectionRequest: {
|
||||
//FIXME: support multi-chunk
|
||||
Lock lock(clipboard_lock);
|
||||
if (clipboard_data_size == 0) {
|
||||
return;
|
||||
}
|
||||
Window requestor_win = event.xselectionrequest.requestor;
|
||||
Atom prop = event.xselectionrequest.property;
|
||||
XChangeProperty(x_display, requestor_win, prop, utf8_atom, 8, PropModeReplace,
|
||||
(unsigned char *)clipboard_data, clipboard_data_size);
|
||||
XEvent res;
|
||||
res.xselection.property = prop;
|
||||
res.xselection.type = SelectionNotify;
|
||||
res.xselection.display = event.xselectionrequest.display;
|
||||
res.xselection.requestor = requestor_win;
|
||||
res.xselection.selection = event.xselectionrequest.selection;
|
||||
res.xselection.target = event.xselectionrequest.target;
|
||||
res.xselection.time = event.xselectionrequest.time;
|
||||
XSendEvent(x_display, requestor_win, 0, 0, &res);
|
||||
XFlush(x_display);
|
||||
break;
|
||||
}*/
|
||||
}
|
||||
case SelectionClear: {
|
||||
Lock lock(clipboard_lock);
|
||||
clipboard_data_size = 0;
|
||||
break;
|
||||
}
|
||||
case SelectionNotify: {
|
||||
Atom type;
|
||||
int format;
|
||||
unsigned long len;
|
||||
unsigned long size;
|
||||
unsigned long dummy;
|
||||
unsigned char *data;
|
||||
XGetWindowProperty(x_display, platform_win, clipboard_prop, 0, 0, False,
|
||||
AnyPropertyType, &type, &format, &len, &size, &data);
|
||||
if (size == 0) {
|
||||
break;
|
||||
}
|
||||
if (XGetWindowProperty(x_display, platform_win, clipboard_prop, 0, size,
|
||||
False, AnyPropertyType, &type, &format, &len, &dummy, &data) != Success) {
|
||||
LOG_INFO("XGetWindowProperty failed");
|
||||
break;
|
||||
}
|
||||
LOG_INFO("data: %s len: %u", data, len);
|
||||
{
|
||||
Lock lock(clipboard_lock);
|
||||
clipboard_data_size = 0;
|
||||
}
|
||||
if (type == incr_atom) {
|
||||
Window requestor_win = event.xselection.requestor;
|
||||
Atom prop = event.xselection.property; // is this always "CLIPBOARD"?
|
||||
// According to ICCCM spec 2.7.2 INCR Properties, and xsel reference
|
||||
XSelectInput (x_display, requestor_win, PropertyChangeMask);
|
||||
XDeleteProperty(x_display, requestor_win, prop);
|
||||
waiting_for_property_notify = true;
|
||||
{
|
||||
Lock lock(clipboard_lock);
|
||||
ensure_clipboard_data_space(*(uint32_t*)data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
{
|
||||
Lock lock(clipboard_lock);
|
||||
update_clipboard(++size, data);
|
||||
}
|
||||
XFree(data);
|
||||
clipboard_listener->on_clipboard_change();
|
||||
break;
|
||||
}
|
||||
case PropertyNotify:
|
||||
if (!waiting_for_property_notify) {
|
||||
break;
|
||||
}
|
||||
{
|
||||
if (event.xproperty.state != PropertyNewValue) break;
|
||||
bool finished_incr = false;
|
||||
{
|
||||
Lock lock(clipboard_lock);
|
||||
finished_incr = !get_append_clipboard_data(&event.xselection);
|
||||
}
|
||||
if (finished_incr) {
|
||||
waiting_for_property_notify = false;
|
||||
XDeleteProperty(x_display, event.xselection.requestor,
|
||||
clipboard_prop);
|
||||
clipboard_listener->on_clipboard_change();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void process_monitor_configure_events(Window root)
|
||||
@ -2735,3 +2946,46 @@ LocalCursor* Platform::create_default_cursor()
|
||||
{
|
||||
return new XDefaultCursor();
|
||||
}
|
||||
|
||||
void Platform::set_clipboard_listener(ClipboardListener* listener)
|
||||
{
|
||||
//FIXME: XA_CLIPBOARD(x_display)
|
||||
if (XGetSelectionOwner(x_display, XA_PRIMARY) == None) {
|
||||
return;
|
||||
}
|
||||
clipboard_listener = listener;
|
||||
XConvertSelection(x_display, XA_PRIMARY, utf8_atom, clipboard_prop,
|
||||
platform_win, CurrentTime);
|
||||
}
|
||||
|
||||
bool Platform::set_clipboard_data(uint32_t type, const uint8_t* data, int32_t size)
|
||||
{
|
||||
Lock lock(clipboard_lock);
|
||||
|
||||
LOG_INFO("type %u size %u data %s", type, size, data);
|
||||
if (size > clipboard_data_space) {
|
||||
delete clipboard_data;
|
||||
clipboard_data = new uint8_t[size];
|
||||
clipboard_data_space = size;
|
||||
}
|
||||
memcpy(clipboard_data, data, size);
|
||||
clipboard_data_size = size;
|
||||
//FIXME: XA_CLIPBOARD(x_display)
|
||||
XSetSelectionOwner(x_display, XA_PRIMARY, platform_win, CurrentTime);
|
||||
LOG_INFO("XSetSelectionOwner");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Platform::get_clipboard_data(uint32_t type, uint8_t* data, int32_t size)
|
||||
{
|
||||
//FIXME: check type
|
||||
memcpy(data, clipboard_data, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t Platform::get_clipboard_data_size(uint32_t type)
|
||||
{
|
||||
//FIXME: check type
|
||||
return clipboard_data_size;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user