summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnon Gilboa <agilboa@redhat.com>2010-10-01 16:06:10 +0200
committerHans de Goede <hdegoede@redhat.com>2010-10-01 16:06:10 +0200
commitc909198eca202f1a2424ce739c541a740dd61281 (patch)
treee7d47f0b137fcb987ca298616bbaf083df6feb97
parent6a26992410c6bc8824e047e8a844a90d5fae46c1 (diff)
downloadspice-c909198eca202f1a2424ce739c541a740dd61281.tar.gz
spice-c909198eca202f1a2424ce739c541a740dd61281.tar.xz
spice-c909198eca202f1a2424ce739c541a740dd61281.zip
client: support clipboard/selection-owner model (v2)
-includes most of Hans' review fixes (up to the SelectionRequest comment [4]) & X11 wips sent by Hans (10x!) -use the VD_AGENT_CLIPBOARD_* types in the platform code -add ifs for VD_AGENT_CAP_CLIPBOARD_BY_DEMAND in both sides -support the GRAB/REQUEST/DATA/RELEASE verbs in both ways -pasting clipboard data is now "only-by-demand" from both sides (client and agent), whose behavior is symmetric -client and agent don't read or send the contents of the clipboard unnecessarily (e.g. copy, internal paste, repeating paste, focus change) -set client as clipboard listener instead of application -add atexit(cleanup) in win platform linux: -instead of clipboard atom selection instead of XA_PRIMARY -enable USE_XRANDR_1_2 and support clipboard in MultyMonScreen -send utf8 with no null termination, remove ++size -add xfixes in configure.ac & Makefile.am windows: -bonus: support image cut & paste, currently only on windows not done yet: -clipboards formats are still uint32_t, not mime types stores as strings -platform_win is still used, not the root window -not replaced the ugly windows CF_DIB in agent/winclient
-rw-r--r--client/application.cpp6
-rw-r--r--client/application.h2
-rw-r--r--client/platform.h13
-rw-r--r--client/red_client.cpp159
-rw-r--r--client/red_client.h30
-rw-r--r--client/windows/platform.cpp264
-rw-r--r--client/x11/Makefile.am2
-rw-r--r--client/x11/platform.cpp389
-rw-r--r--configure.ac3
9 files changed, 602 insertions, 266 deletions
diff --git a/client/application.cpp b/client/application.cpp
index 12580679..c5d34ff0 100644
--- a/client/application.cpp
+++ b/client/application.cpp
@@ -1318,12 +1318,6 @@ 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()
diff --git a/client/application.h b/client/application.h
index dde37fca..36ae86e8 100644
--- a/client/application.h
+++ b/client/application.h
@@ -141,7 +141,6 @@ typedef std::list<GUIBarrier*> GUIBarriers;
class Application : public ProcessLoop,
public Platform::EventListener,
public Platform::DisplayModeListener,
- public Platform::ClipboardListener,
public CommandTarget {
public:
@@ -191,7 +190,6 @@ 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();
diff --git a/client/platform.h b/client/platform.h
index 6288380a..47278893 100644
--- a/client/platform.h
+++ b/client/platform.h
@@ -121,13 +121,10 @@ public:
class ClipboardListener;
static void set_clipboard_listener(ClipboardListener* listener);
- enum {
- CLIPBOARD_UTF8_TEXT = 1,
- };
-
+ static bool set_clipboard_owner(uint32_t type);
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);
+ static bool request_clipboard_notification(uint32_t type);
+ static void release_clipboard();
};
class Platform::EventListener {
@@ -141,7 +138,9 @@ public:
class Platform::ClipboardListener {
public:
virtual ~ClipboardListener() {}
- virtual void on_clipboard_change() = 0;
+ virtual void on_clipboard_grab(uint32_t type) = 0;
+ virtual void on_clipboard_request(uint32_t type) = 0;
+ virtual void on_clipboard_notify(uint32_t type, uint8_t* data, int32_t size) = 0;
};
class Platform::RecordClient {
diff --git a/client/red_client.cpp b/client/red_client.cpp
index 79f5e6d6..562b8269 100644
--- a/client/red_client.cpp
+++ b/client/red_client.cpp
@@ -81,9 +81,18 @@ uint32_t default_agent_caps[] = {
(1 << VD_AGENT_CAP_REPLY)
};
-void ClipboardEvent::response(AbstractProcessLoop& events_loop)
+void ClipboardGrabEvent::response(AbstractProcessLoop& events_loop)
{
- static_cast<RedClient*>(events_loop.get_owner())->send_agent_clipboard();
+ VDAgentClipboardGrab grab = {_type};
+ static_cast<RedClient*>(events_loop.get_owner())->send_agent_clipboard_message(
+ VD_AGENT_CLIPBOARD_GRAB, sizeof(grab), &grab);
+}
+
+void ClipboardRequestEvent::response(AbstractProcessLoop& events_loop)
+{
+ VDAgentClipboardRequest request = {_type};
+ static_cast<RedClient*>(events_loop.get_owner())->send_agent_clipboard_message(
+ VD_AGENT_CLIPBOARD_REQUEST, sizeof(request), &request);
}
Migrate::Migrate(RedClient& client)
@@ -339,6 +348,7 @@ RedClient::RedClient(Application& application)
, _migrate (*this)
, _glz_window (0, _glz_debug)
{
+ Platform::set_clipboard_listener(this);
MainChannelLoop* message_loop = static_cast<MainChannelLoop*>(get_message_handler());
uint32_t default_caps_size = SPICE_N_ELEMENTS(default_agent_caps);
@@ -542,6 +552,7 @@ bool RedClient::abort_channels()
bool RedClient::abort()
{
if (!_aborting) {
+ Platform::set_clipboard_listener(NULL);
Lock lock(_sync_lock);
_aborting = true;
_sync_condition.notify_all();
@@ -674,8 +685,8 @@ void RedClient::send_agent_announce_capabilities(bool request)
VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MOUSE_STATE);
VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MONITORS_CONFIG);
VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_REPLY);
- VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD);
VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_DISPLAY_CONFIG);
+ VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND);
ASSERT(_agent_tokens)
_agent_tokens--;
post_message(message);
@@ -784,16 +795,6 @@ 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::do_send_agent_clipboard()
{
uint32_t size;
@@ -802,8 +803,7 @@ void RedClient::do_send_agent_clipboard()
(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);
+ 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);
@@ -817,14 +817,47 @@ void RedClient::do_send_agent_clipboard()
}
}
-//FIXME: currently supports text only; better name - poll_clipboard?
-void RedClient::send_agent_clipboard()
+void RedClient::send_agent_clipboard_message(uint32_t message_type, uint32_t size, void* data)
+{
+ Message* message = new Message(SPICE_MSGC_MAIN_AGENT_DATA);
+ VDAgentMessage* msg = (VDAgentMessage*)
+ spice_marshaller_reserve_space(message->marshaller(), sizeof(VDAgentMessage) + size);
+ msg->protocol = VD_AGENT_PROTOCOL;
+ msg->type = message_type;
+ msg->opaque = 0;
+ msg->size = size;
+ if (size && data) {
+ memcpy(msg->data, data, size);
+ }
+ ASSERT(_agent_tokens)
+ _agent_tokens--;
+ post_message(message);
+}
+
+void RedClient::on_clipboard_grab(uint32_t type)
+{
+ if (!_agent_caps || !VD_AGENT_HAS_CAPABILITY(_agent_caps, _agent_caps_size,
+ VD_AGENT_CAP_CLIPBOARD_BY_DEMAND)) {
+ return;
+ }
+ AutoRef<ClipboardGrabEvent> event(new ClipboardGrabEvent(type));
+ get_process_loop().push_event(*event);
+}
+
+void RedClient::on_clipboard_request(uint32_t type)
{
- //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 (!_agent_caps || !VD_AGENT_HAS_CAPABILITY(_agent_caps, _agent_caps_size,
+ VD_AGENT_CAP_CLIPBOARD_BY_DEMAND)) {
+ return;
+ }
+ AutoRef<ClipboardRequestEvent> event(new ClipboardRequestEvent(type));
+ get_process_loop().push_event(*event);
+}
- if (!clip_size || !_agent_connected) {
+void RedClient::on_clipboard_notify(uint32_t type, uint8_t* data, int32_t size)
+{
+ ASSERT(size && data);
+ if (!_agent_connected) {
return;
}
if (_agent_out_msg) {
@@ -832,32 +865,20 @@ void RedClient::send_agent_clipboard()
return;
}
_agent_out_msg_pos = 0;
- _agent_out_msg_size = sizeof(VDAgentMessage) + sizeof(VDAgentClipboard) + clip_size;
+ _agent_out_msg_size = sizeof(VDAgentMessage) + sizeof(VDAgentClipboard) + 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;
+ _agent_out_msg->size = sizeof(VDAgentClipboard) + 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;
- }
-
+ clipboard->type = type;
+ memcpy(clipboard->data, data, size);
if (_agent_tokens) {
do_send_agent_clipboard();
}
}
-void RedClient::on_clipboard_change()
-{
- AutoRef<ClipboardEvent> event(new ClipboardEvent());
- get_process_loop().push_event(*event);
-}
-
void RedClient::set_mouse_mode(uint32_t supported_modes, uint32_t current_mode)
{
if (current_mode != _mouse_mode) {
@@ -884,17 +905,6 @@ 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();
@@ -1055,25 +1065,7 @@ void RedClient::handle_agent_data(RedPeer::InMessage* message)
}
if (_agent_msg_pos == sizeof(VDAgentMessage) + _agent_msg->size) {
DBG(0, "agent msg end");
- switch (_agent_msg->type) {
- case VD_AGENT_ANNOUNCE_CAPABILITIES: {
- on_agent_announce_capabilities(
- (VDAgentAnnounceCapabilities*)_agent_msg_data,
- _agent_msg->size);
- break;
- }
- case VD_AGENT_REPLY: {
- 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);
- }
+ dispatch_agent_message(_agent_msg, _agent_msg_data);
delete[] _agent_msg_data;
_agent_msg_data = NULL;
_agent_msg_pos = 0;
@@ -1081,6 +1073,39 @@ void RedClient::handle_agent_data(RedPeer::InMessage* message)
}
}
+void RedClient::dispatch_agent_message(VDAgentMessage* msg, void* data)
+{
+ switch (msg->type) {
+ case VD_AGENT_ANNOUNCE_CAPABILITIES: {
+ on_agent_announce_capabilities((VDAgentAnnounceCapabilities*)data, msg->size);
+ break;
+ }
+ case VD_AGENT_REPLY: {
+ on_agent_reply((VDAgentReply*)data);
+ break;
+ }
+ case VD_AGENT_CLIPBOARD: {
+ VDAgentClipboard* clipboard = (VDAgentClipboard*)data;
+ Platform::set_clipboard_data(clipboard->type, clipboard->data,
+ msg->size - sizeof(VDAgentClipboard));
+ break;
+ }
+ case VD_AGENT_CLIPBOARD_GRAB:
+ Platform::set_clipboard_owner(((VDAgentClipboardGrab*)data)->type);
+ break;
+ case VD_AGENT_CLIPBOARD_REQUEST:
+ if (!Platform::request_clipboard_notification(((VDAgentClipboardRequest*)data)->type)) {
+ send_agent_clipboard_message(VD_AGENT_CLIPBOARD_RELEASE);
+ }
+ break;
+ case VD_AGENT_CLIPBOARD_RELEASE:
+ Platform::release_clipboard();
+ break;
+ default:
+ DBG(0, "Unsupported message type %u size %u", msg->type, msg->size);
+ }
+}
+
void RedClient::handle_agent_tokens(RedPeer::InMessage* message)
{
SpiceMsgMainAgentTokens *token = (SpiceMsgMainAgentTokens *)message->data();
diff --git a/client/red_client.h b/client/red_client.h
index ba8b4ee9..3ccb8e32 100644
--- a/client/red_client.h
+++ b/client/red_client.h
@@ -144,16 +144,31 @@ public:
uint32_t _color_depth;
};
-class ClipboardEvent : public Event {
+class ClipboardGrabEvent : public Event {
public:
+ ClipboardGrabEvent(uint32_t type) : _type (type) {}
virtual void response(AbstractProcessLoop& events_loop);
+
+private:
+ uint32_t _type;
};
-class RedClient: public RedChannel {
+class ClipboardRequestEvent : public Event {
+public:
+ ClipboardRequestEvent(uint32_t type) : _type (type) {}
+ virtual void response(AbstractProcessLoop& events_loop);
+
+private:
+ uint32_t _type;
+};
+
+class RedClient: public RedChannel,
+ public Platform::ClipboardListener {
public:
friend class RedChannel;
friend class Migrate;
- friend class ClipboardEvent;
+ friend class ClipboardGrabEvent;
+ friend class ClipboardRequestEvent;
RedClient(Application& application);
~RedClient();
@@ -192,7 +207,10 @@ 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 on_clipboard_grab(uint32_t type);
+ void on_clipboard_request(uint32_t type);
+ void on_clipboard_notify(uint32_t type, uint8_t* data, int32_t size);
+
void for_each_channel(ForEachChannelFunc& func);
void on_mouse_capture_trigger(RedScreen& screen);
@@ -228,13 +246,13 @@ private:
void handle_agent_data(RedPeer::InMessage* message);
void handle_agent_tokens(RedPeer::InMessage* message);
void handle_migrate_switch_host(RedPeer::InMessage* message);
+ void dispatch_agent_message(VDAgentMessage* msg, void* data);
void on_agent_reply(VDAgentReply* reply);
void on_agent_announce_capabilities(VDAgentAnnounceCapabilities* caps,
uint32_t msg_size);
- void on_agent_clipboard(VDAgentClipboard* clipboard, uint32_t size);
- void send_agent_clipboard();
void do_send_agent_clipboard();
+ void send_agent_clipboard_message(uint32_t message_type, uint32_t size = 0, void* data = NULL);
ChannelFactory* find_factory(uint32_t type);
void create_channel(uint32_t type, uint32_t id);
diff --git a/client/windows/platform.cpp b/client/windows/platform.cpp
index ae49d028..95876dc5 100644
--- a/client/windows/platform.cpp
+++ b/client/windows/platform.cpp
@@ -32,6 +32,7 @@
#include "playback.h"
#include "cursor.h"
#include "named_pipe.h"
+#include <spice/vd_agent.h>
int gdi_handlers = 0;
extern HINSTANCE instance;
@@ -45,9 +46,40 @@ public:
static DefaultEventListener default_event_listener;
static Platform::EventListener* event_listener = &default_event_listener;
-static HWND paltform_win;
+static HWND platform_win = NULL;
static ProcessLoop* main_loop = NULL;
+class DefaultClipboardListener: public Platform::ClipboardListener {
+public:
+ virtual void on_clipboard_grab(uint32_t type) {}
+ virtual void on_clipboard_request(uint32_t type) {}
+ virtual void on_clipboard_notify(uint32_t type, uint8_t* data, int32_t size) {}
+};
+
+static DefaultClipboardListener default_clipboard_listener;
+static Platform::ClipboardListener* clipboard_listener = &default_clipboard_listener;
+
+// The next window in the clipboard viewer chain, which is refered in all clipboard events.
+static HWND next_clipboard_viewer_win = NULL;
+static HANDLE clipboard_event = NULL;
+
+// clipboard_changer says whether the client was the last one to change cliboard, for loop
+// prevention. It's initialized to true so we ignore the first clipboard change event which
+// happens right when we call SetClipboardViewer().
+static bool clipboard_changer = true;
+
+static const int CLIPBOARD_TIMEOUT_MS = 10000;
+
+typedef struct ClipboardFormat {
+ uint32_t format;
+ uint32_t type;
+} ClipboardFormat;
+
+static ClipboardFormat clipboard_formats[] = {
+ {CF_UNICODETEXT, VD_AGENT_CLIPBOARD_UTF8_TEXT},
+ {CF_DIB, VD_AGENT_CLIPBOARD_BITMAP},
+ {0, 0}};
+
static const unsigned long MODAL_LOOP_TIMER_ID = 1;
static const int MODAL_LOOP_DEFAULT_TIMEOUT = 100;
static bool modal_loop_active = false;
@@ -59,6 +91,32 @@ void Platform::send_quit_request()
main_loop->quit(0);
}
+static uint32_t get_clipboard_type(uint32_t format) {
+ ClipboardFormat* iter;
+
+ for (iter = clipboard_formats; iter->type && iter->format != format; iter++);
+ return iter->type;
+}
+
+static uint32_t get_clipboard_format(uint32_t type) {
+ ClipboardFormat* iter;
+
+ for (iter = clipboard_formats; iter->format && iter->type != type; iter++);
+ return iter->format;
+}
+
+static uint32_t get_available_clipboard_type()
+{
+ uint32_t type = 0;
+
+ for (ClipboardFormat* iter = clipboard_formats; iter->format && !type; iter++) {
+ if (IsClipboardFormatAvailable(iter->format)) {
+ type = iter->type;
+ }
+ }
+ return type;
+}
+
static LRESULT CALLBACK PlatformWinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
@@ -82,6 +140,44 @@ static LRESULT CALLBACK PlatformWinProc(HWND hWnd, UINT message, WPARAM wParam,
case WM_DISPLAYCHANGE:
event_listener->on_monitors_change();
break;
+ case WM_CHANGECBCHAIN:
+ if (next_clipboard_viewer_win == (HWND)wParam) {
+ next_clipboard_viewer_win = (HWND)lParam;
+ } else if (next_clipboard_viewer_win) {
+ SendMessage(next_clipboard_viewer_win, message, wParam, lParam);
+ }
+ break;
+ case WM_DRAWCLIPBOARD:
+ if (!clipboard_changer) {
+ uint32_t type = get_available_clipboard_type();
+ if (type) {
+ clipboard_listener->on_clipboard_grab(type);
+ } else {
+ LOG_INFO("Unsupported clipboard format");
+ }
+ } else {
+ clipboard_changer = false;
+ }
+ if (next_clipboard_viewer_win) {
+ SendMessage(next_clipboard_viewer_win, message, wParam, lParam);
+ }
+ break;
+ case WM_RENDERFORMAT: {
+ // In delayed rendering, Windows requires us to SetClipboardData before we return from
+ // handling WM_RENDERFORMAT. Therefore, we try our best by sending CLIPBOARD_REQUEST to the
+ // agent, while waiting alertably for a while (hoping for good) for receiving CLIPBOARD data
+ // or CLIPBOARD_RELEASE from the agent, which both will signal clipboard_event.
+ uint32_t type = get_clipboard_type(wParam);
+ if (!type) {
+ LOG_INFO("Unsupported clipboard format %u", wParam);
+ break;
+ }
+ clipboard_listener->on_clipboard_request(type);
+ DWORD start_tick = GetTickCount();
+ while (WaitForSingleObjectEx(clipboard_event, 1000, TRUE) != WAIT_OBJECT_0 &&
+ GetTickCount() < start_tick + CLIPBOARD_TIMEOUT_MS);
+ break;
+ }
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
@@ -92,7 +188,6 @@ static void create_message_wind()
{
WNDCLASSEX wclass;
ATOM class_atom;
- HWND window;
const LPCWSTR class_name = L"spicec_platform_wclass";
@@ -113,11 +208,16 @@ static void create_message_wind()
THROW("register class failed");
}
- if (!(window = CreateWindow(class_name, L"", 0, 0, 0, 0, 0, NULL, NULL, instance, NULL))) {
+ if (!(platform_win = CreateWindow(class_name, L"", 0, 0, 0, 0, 0, NULL, NULL, instance, NULL))) {
THROW("create message window failed");
}
- paltform_win = window;
+ if (!(next_clipboard_viewer_win = SetClipboardViewer(platform_win)) && GetLastError()) {
+ THROW("set clipboard viewer failed");
+ }
+ if (!(clipboard_event = CreateEvent(NULL, FALSE, FALSE, NULL))) {
+ THROW("create clipboard event failed");
+ }
}
NamedPipe::ListenerRef NamedPipe::create(const char *name, ListenerInterface& listener_interface)
@@ -460,9 +560,16 @@ void Platform::path_append(std::string& path, const std::string& partial_path)
path += partial_path;
}
+static void cleanup()
+{
+ ChangeClipboardChain(platform_win, next_clipboard_viewer_win);
+ CloseHandle(clipboard_event);
+}
+
void Platform::init()
{
create_message_wind();
+ atexit(cleanup);
}
void Platform::set_process_loop(ProcessLoop& main_process_loop)
@@ -733,7 +840,7 @@ static bool set_modal_loop_timer()
the enterance to the loop*/
}
- if (!SetTimer(paltform_win, MODAL_LOOP_TIMER_ID, timeout, NULL)) {
+ if (!SetTimer(platform_win, MODAL_LOOP_TIMER_ID, timeout, NULL)) {
return false;
}
return true;
@@ -745,99 +852,152 @@ void WinPlatform::exit_modal_loop()
LOG_INFO("not inside the loop");
return;
}
- KillTimer(paltform_win, MODAL_LOOP_TIMER_ID);
+ KillTimer(platform_win, MODAL_LOOP_TIMER_ID);
modal_loop_active = false;
}
-void Platform::set_clipboard_listener(ClipboardListener* listener)
+bool Platform::set_clipboard_owner(uint32_t type)
{
- //FIXME: call only on change, use statics
- listener->on_clipboard_change();
+ uint32_t format = get_clipboard_format(type);
+
+ if (!format) {
+ LOG_INFO("Unsupported clipboard type %u", type);
+ return false;
+ }
+ if (!OpenClipboard(platform_win)) {
+ return false;
+ }
+ clipboard_changer = true;
+ EmptyClipboard();
+ SetClipboardData(format, NULL);
+ CloseClipboard();
+ return true;
}
-UINT get_format(uint32_t type)
+void Platform::set_clipboard_listener(ClipboardListener* listener)
{
- switch (type) {
- case Platform::CLIPBOARD_UTF8_TEXT:
- return CF_UNICODETEXT;
- default:
- return 0;
- }
+ clipboard_listener = listener ? listener : &default_clipboard_listener;
}
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;
+ int clip_len;
+ UINT format;
+ bool ret = false;
- //LOG_INFO("type %u size %d %s", type, size, data);
- if (!format || !OpenClipboard(paltform_win)) {
+ // Get the required clipboard size
+ switch (type) {
+ case VD_AGENT_CLIPBOARD_UTF8_TEXT:
+ // Received utf8 string is not null-terminated
+ if (!(clip_len = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)data, size, NULL, 0))) {
+ return false;
+ }
+ clip_len++;
+ clip_size = clip_len * sizeof(WCHAR);
+ break;
+ case VD_AGENT_CLIPBOARD_BITMAP:
+ clip_size = size;
+ break;
+ default:
+ LOG_INFO("Unsupported clipboard type %u", type);
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();
+
+ // Allocate and lock clipboard memory
+ if (!(clip_data = GlobalAlloc(GMEM_DDESHARE, clip_size))) {
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);
+
+ // Translate data and set clipboard content
+ switch (type) {
+ case VD_AGENT_CLIPBOARD_UTF8_TEXT:
+ ret = !!MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)data, size, (LPWSTR)clip_buf, clip_len);
+ ((LPWSTR)clip_buf)[clip_len - 1] = L'\0';
+ break;
+ case VD_AGENT_CLIPBOARD_BITMAP:
+ memcpy(clip_buf, data, size);
+ ret = true;
+ break;
+ }
GlobalUnlock(clip_data);
- if (ret) {
- EmptyClipboard();
- ret = !!SetClipboardData(format, clip_data);
+ if (!ret) {
+ return false;
+ }
+ format = get_clipboard_format(type);
+ if (SetClipboardData(format, clip_data)) {
+ SetEvent(clipboard_event);
+ return true;
}
+ // We retry clipboard open-empty-set-close only when there is a timeout in WM_RENDERFORMAT
+ if (!OpenClipboard(platform_win)) {
+ return false;
+ }
+ EmptyClipboard();
+ ret = !!SetClipboardData(format, clip_data);
CloseClipboard();
return ret;
}
-bool Platform::get_clipboard_data(uint32_t type, uint8_t* data, int32_t size)
+bool Platform::request_clipboard_notification(uint32_t type)
{
- UINT format = get_format(type);
+ UINT format = get_clipboard_format(type);
HANDLE clip_data;
LPVOID clip_buf;
- bool ret;
+ bool ret = false;
- LOG_INFO("type %u size %d", type, size);
- if (!format || !IsClipboardFormatAvailable(format) || !OpenClipboard(paltform_win)) {
+ if (!format || !IsClipboardFormatAvailable(format) || !OpenClipboard(platform_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;
+ switch (type) {
+ case VD_AGENT_CLIPBOARD_UTF8_TEXT: {
+ size_t len = wcslen((wchar_t*)clip_buf);
+ int utf8_size = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)clip_buf, len, NULL, 0, NULL, NULL);
+ if (!utf8_size) {
+ break;
+ }
+ uint8_t* utf8_data = new uint8_t[utf8_size];
+ if (WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)clip_buf, len, (LPSTR)utf8_data, utf8_size,
+ NULL, NULL)) {
+ clipboard_listener->on_clipboard_notify(type, utf8_data, utf8_size);
+ ret = true;
+ }
+ delete[] (uint8_t *)utf8_data;
+ break;
}
- if (!(clip_data = GetClipboardData(format)) || !(clip_buf = GlobalLock(clip_data))) {
- CloseClipboard();
- return 0;
+ case VD_AGENT_CLIPBOARD_BITMAP: {
+ size_t clip_size = GlobalSize(clip_data);
+ if (!clip_size) {
+ break;
+ }
+ clipboard_listener->on_clipboard_notify(type, (uint8_t*)clip_buf, clip_size);
+ ret = true;
+ break;
}
- clip_size = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)clip_buf, -1, NULL, 0, NULL, NULL);
+ default:
+ LOG_INFO("Unsupported clipboard type %u", type);
+ }
+
GlobalUnlock(clip_data);
CloseClipboard();
- return clip_size;
+ return ret;
}
+void Platform::release_clipboard()
+{
+ SetEvent(clipboard_event);
+}
static bool has_console = false;
diff --git a/client/x11/Makefile.am b/client/x11/Makefile.am
index a992aa17..02aa8eb2 100644
--- a/client/x11/Makefile.am
+++ b/client/x11/Makefile.am
@@ -22,6 +22,7 @@ INCLUDES = \
$(CELT051_CFLAGS) \
$(SSL_CFLAGS) \
$(XRANDR_CFLAGS) \
+ $(XFIXES_CFLAGS) \
$(MISC_X_CFLAGS) \
$(CEGUI_CFLAGS) \
$(WARN_CFLAGS) \
@@ -202,5 +203,6 @@ spicec_LDADD = \
$(ALSA_LIBS) \
$(GL_LIBS) \
$(XRANDR_LIBS) \
+ $(XFIXES_LIBS) \
$(MISC_X_LIBS) \
$(CEGUI_LIBS)
diff --git a/client/x11/platform.cpp b/client/x11/platform.cpp
index ca5f1d57..302b7510 100644
--- a/client/x11/platform.cpp
+++ b/client/x11/platform.cpp
@@ -28,6 +28,7 @@
#include <X11/extensions/XKB.h>
#include <X11/extensions/Xrender.h>
#include <X11/extensions/XShm.h>
+#include <X11/extensions/Xfixes.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/epoll.h>
@@ -58,6 +59,7 @@
#include "res.h"
#include "cursor.h"
#include "process_loop.h"
+#include <spice/vd_agent.h>
#define DWORD uint32_t
#define BOOL bool
@@ -96,24 +98,39 @@ static int xrandr_minor = 0;
static bool using_xrender_0_5 = false;
+static bool using_xfixes_1_0 = false;
+
+static int xfixes_event_base;
+static int xfixes_error_base;
+
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_type = 0;
static int32_t clipboard_data_size = 0;
static int32_t clipboard_data_space = 0;
+static int32_t clipboard_request_type = 0;
+static bool clipboard_changer = false;
+static XEvent clipboard_event;
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
+static Atom targets_atom;
static Bool handle_x_error = false;
static int x_error_code;
+typedef struct ClipboardFormat {
+ uint32_t format;
+ uint32_t type;
+} ClipboardFormat;
+
+static ClipboardFormat clipboard_formats[] = {
+ {0, 0},
+ {0, 0}};
+
class DefaultEventListener: public Platform::EventListener {
public:
virtual void on_app_activated() {}
@@ -134,12 +151,28 @@ static Platform::DisplayModeListener* display_mode_listener = &default_display_m
class DefaultClipboardListener: public Platform::ClipboardListener {
public:
- void on_clipboard_change() {}
+ void on_clipboard_grab(uint32_t type) {}
+ void on_clipboard_request(uint32_t type) {}
+ void on_clipboard_notify(uint32_t type, uint8_t* data, int32_t size) {}
};
static DefaultClipboardListener default_clipboard_listener;
static Platform::ClipboardListener* clipboard_listener = &default_clipboard_listener;
+static uint32_t get_clipboard_type(uint32_t format) {
+ ClipboardFormat* iter;
+
+ for (iter = clipboard_formats; iter->type && iter->format != format; iter++);
+ return iter->type;
+}
+
+static uint32_t get_clipboard_format(uint32_t type) {
+ ClipboardFormat* iter;
+
+ for (iter = clipboard_formats; iter->format && iter->type != type; iter++);
+ return iter->format;
+}
+
NamedPipe::ListenerRef NamedPipe::create(const char *name, ListenerInterface& listener_interface)
{
ASSERT(main_loop && main_loop->is_same_thread(pthread_self()));
@@ -771,6 +804,11 @@ static void intern_clipboard_atoms()
clipboard_prop = XInternAtom(x_display, "CLIPBOARD", False);
incr_atom = XInternAtom(x_display, "INCR", False);
utf8_atom = XInternAtom(x_display, "UTF8_STRING", False);
+ targets_atom = XInternAtom(x_display, "TARGETS", False);
+
+ clipboard_formats[0].format = utf8_atom;
+ clipboard_formats[0].type = VD_AGENT_CLIPBOARD_UTF8_TEXT;
+
interned = true;
}
@@ -786,13 +824,19 @@ DynamicScreen::DynamicScreen(Display* display, int screen, int& next_mon_id)
platform_win = XCreateSimpleWindow(display, RootWindow(display, screen), 0, 0, 1, 1, 0, 0, 0);
XSelectInput(display, platform_win, StructureNotifyMask);
XRRSelectInput(display, platform_win, RRScreenChangeNotifyMask);
+ if (using_xfixes_1_0) {
+ XFixesSelectSelectionInput(display, platform_win, clipboard_prop,
+ XFixesSetSelectionOwnerNotifyMask |
+ XFixesSelectionWindowDestroyNotifyMask |
+ XFixesSelectionClientCloseNotifyMask);
+ }
Monitor::self_monitors_change++;
process_monitor_configure_events(platform_win);
Monitor::self_monitors_change--;
- XPlatform::set_win_proc(platform_win, root_win_proc);
intern_clipboard_atoms();
+ XPlatform::set_win_proc(platform_win, root_win_proc);
X_DEBUG_SYNC(display);
}
@@ -881,7 +925,7 @@ bool DynamicScreen::set_screen_size(int size_index)
Monitor::self_monitors_change++;
/*what status*/
XRRSetScreenConfig(get_display(), config, root_window, size_index, rotation, CurrentTime);
- process_monitor_configure_events(root_window);
+ process_monitor_configure_events(platform_win);
Monitor::self_monitors_change--;
XRRFreeScreenConfigInfo(config);
X_DEBUG_SYNC(get_display());
@@ -1053,26 +1097,24 @@ MultyMonScreen::MultyMonScreen(Display* display, int screen, int& next_mon_id)
throw;
}
- XSelectInput(display, root_window, StructureNotifyMask);
+ platform_win = XCreateSimpleWindow(display, RootWindow(display, screen), 0, 0, 1, 1, 0, 0, 0);
+ XSelectInput(display, platform_win, StructureNotifyMask);
X_DEBUG_SYNC(get_display());
- XRRSelectInput(display, root_window, RRScreenChangeNotifyMask);
+ XRRSelectInput(display, platform_win, RRScreenChangeNotifyMask);
X_DEBUG_SYNC(get_display());
- intern_clipboard_atoms();
- XPlatform::set_win_proc(root_window, root_win_proc);
+ if (using_xfixes_1_0) {
+ XFixesSelectSelectionInput(display, platform_win, clipboard_prop,
+ XFixesSetSelectionOwnerNotifyMask |
+ XFixesSelectionWindowDestroyNotifyMask |
+ XFixesSelectionClientCloseNotifyMask);
+ }
XMonitor::inc_change_ref();
- process_monitor_configure_events(root_window);
+ process_monitor_configure_events(platform_win);
XMonitor::dec_change_ref();
- 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;
+ intern_clipboard_atoms();
+ XPlatform::set_win_proc(platform_win, root_win_proc);
X_DEBUG_SYNC(get_display());
}
@@ -1180,7 +1222,7 @@ void MultyMonScreen::restore()
(*iter)->revert();
}
enable();
- process_monitor_configure_events(root_window);
+ process_monitor_configure_events(platform_win);
XMonitor::dec_change_ref();
X_DEBUG_SYNC(get_display());
}
@@ -1742,8 +1784,7 @@ bool MultyMonScreen::set_monitor_mode(XMonitor& monitor, const XRRModeInfo& mode
monitor.set_mode(mode_info);
set_size(screen_width, screen_height);
enable();
- Window root_window = RootWindow(get_display(), get_screen());
- process_monitor_configure_events(root_window);
+ process_monitor_configure_events(platform_win);
XMonitor::dec_change_ref();
X_DEBUG_SYNC(get_display());
return true;
@@ -2201,7 +2242,9 @@ static void ensure_clipboard_data_space(uint32_t size)
static void realloc_clipboard_data_space(uint32_t size)
{
- if (size <= clipboard_data_space) return;
+ 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];
@@ -2219,49 +2262,87 @@ static void update_clipboard(unsigned long size, uint8_t* data)
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) {
+// Function based on xsel 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. false if no data is available, 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,
+ xsel->target, &target, &format, &length, &bytesafter, &value);
+ if (target != xsel->target) {
+ LOG_INFO("target %d != %u requested", target, xsel->target);
+ clipboard_data_size = 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) {
+ LOG_INFO("Retrieved non-8-bit data");
+ clipboard_data_size = 0;
+ return false;
+ }
if (clipboard_data_size + length > clipboard_data_space) {
- realloc_clipboard_data_space(clipboard_data_size + length);
+ realloc_clipboard_data_space(clipboard_data_size + length);
}
- strncpy ((char*)clipboard_data + clipboard_data_size, (char*)value, length);
+ memcpy((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;
+ LOG_INFO("Appended %d bytes to buffer", length);
+ return true;
}
+// FIXME: use INCR for large data transfers
+static void send_selection_notify(Atom type)
+{
+ Window requestor_win = clipboard_event.xselectionrequest.requestor;
+ Atom prop = clipboard_event.xselectionrequest.property;
+ XEvent res;
+
+ if (type != None) {
+ XChangeProperty(x_display, requestor_win, prop, type, 8, PropModeReplace,
+ (unsigned char *)clipboard_data, clipboard_data_size);
+ res.xselection.property = prop;
+ } else {
+ res.xselection.property = None;
+ }
+ res.xselection.type = SelectionNotify;
+ res.xselection.display = clipboard_event.xselectionrequest.display;
+ res.xselection.requestor = requestor_win;
+ res.xselection.selection = clipboard_event.xselectionrequest.selection;
+ res.xselection.target = clipboard_event.xselectionrequest.target;
+ res.xselection.time = clipboard_event.xselectionrequest.time;
+ XSendEvent(x_display, requestor_win, 0, 0, &res);
+ XFlush(x_display);
+}
+
+static void send_targets(XEvent& request_event)
+{
+ XEvent res;
+ /* FIXME add MULTIPLE */
+ /* FIXME add (and support) all 3 utf8 atom variations (see linux agent) */
+ Atom targets[2] = { targets_atom, utf8_atom };
+
+ Window requestor_win = request_event.xselectionrequest.requestor;
+ Atom prop = request_event.xselectionrequest.property;
+ XChangeProperty(x_display, requestor_win, prop, XA_ATOM, 32,
+ PropModeReplace, (unsigned char *)&targets,
+ sizeof(targets)/sizeof(Atom));
+
+ res.xselection.property = prop;
+ res.xselection.type = SelectionNotify;
+ res.xselection.display = request_event.xselectionrequest.display;
+ res.xselection.requestor = requestor_win;
+ res.xselection.selection = request_event.xselectionrequest.selection;
+ res.xselection.target = targets_atom;
+ res.xselection.time = request_event.xselectionrequest.time;
+ XSendEvent(x_display, requestor_win, 0, 0, &res);
+ XFlush(x_display);
+}
static void root_win_proc(XEvent& event)
{
@@ -2289,28 +2370,45 @@ static void root_win_proc(XEvent& event)
event_listener->on_monitors_change();
return;
}
-
+ if (event.type == XFixesSelectionNotify + xfixes_event_base) {
+ XFixesSelectionNotifyEvent* selection_event = (XFixesSelectionNotifyEvent *)&event;
+ if (selection_event->subtype != XFixesSetSelectionOwnerNotify) {
+ // FIXME: for some reason the XFixesSelectionWindowDestroyNotify/ClientCloseNotify
+ // events which can help for sending CLIPBOARD_RELEASE to the agent are not received
+ LOG_INFO("Unsupported selection event %u", selection_event->subtype);
+ return;
+ }
+ LOG_INFO("XFixesSetSelectionOwnerNotify %u", clipboard_changer);
+ if (clipboard_changer) {
+ clipboard_changer = false;
+ return;
+ }
+ // FIXME: use actual type
+ clipboard_listener->on_clipboard_grab(VD_AGENT_CLIPBOARD_UTF8_TEXT);
+ return;
+ }
switch (event.type) {
case SelectionRequest: {
- //FIXME: support multi-chunk
Lock lock(clipboard_lock);
- if (clipboard_data_size == 0) {
- return;
+ XSelectionRequestEvent* selection_request = (XSelectionRequestEvent*)&event;
+
+ if (selection_request->target == targets_atom) {
+ send_targets(event);
+ break;
}
- 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);
+
+ uint32_t type = get_clipboard_type(selection_request->target);
+ if (!type) {
+ LOG_INFO("Unsupported selection type %s", XGetAtomName(x_display, selection_request->target));
+ send_selection_notify(None);
+ break;
+ }
+ if (clipboard_data_size > 0) {
+ send_selection_notify(selection_request->target);
+ break;
+ }
+ clipboard_event = event;
+ clipboard_listener->on_clipboard_request(type);
break;
}
case SelectionClear: {
@@ -2325,17 +2423,17 @@ static void root_win_proc(XEvent& event)
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);
+ event.xselection.target, &type, &format, &len, &size, &data);
if (size == 0) {
+ LOG_INFO("XGetWindowProperty(size) failed");
break;
}
- if (XGetWindowProperty(x_display, platform_win, clipboard_prop, 0, size,
- False, AnyPropertyType, &type, &format, &len, &dummy, &data) != Success) {
- LOG_INFO("XGetWindowProperty failed");
+ if (type != event.xselection.target && type != incr_atom) {
+ LOG_INFO("type %d != %u requested", type, event.xselection.target);
break;
}
- LOG_INFO("data: %s len: %u", data, len);
{
Lock lock(clipboard_lock);
clipboard_data_size = 0;
@@ -2344,7 +2442,7 @@ static void root_win_proc(XEvent& event)
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);
+ XSelectInput(x_display, requestor_win, PropertyChangeMask);
XDeleteProperty(x_display, requestor_win, prop);
waiting_for_property_notify = true;
{
@@ -2353,33 +2451,44 @@ static void root_win_proc(XEvent& event)
}
break;
}
+ if (XGetWindowProperty(x_display, platform_win, clipboard_prop, 0, size, True,
+ event.xselection.target, &type, &format, &len,
+ &dummy, &data) != Success) {
+ LOG_INFO("XGetWindowProperty(data) failed");
+ break;
+ }
+ if (type != event.xselection.target) {
+ LOG_INFO("type %d != %u requested", type, event.xselection.target);
+ break;
+ }
{
Lock lock(clipboard_lock);
- update_clipboard(++size, data);
+ update_clipboard(size, data);
}
+ clipboard_listener->on_clipboard_notify(clipboard_request_type, clipboard_data,
+ clipboard_data_size);
+ clipboard_request_type = 0;
XFree(data);
- clipboard_listener->on_clipboard_change();
break;
}
- case PropertyNotify:
- if (!waiting_for_property_notify) {
+ case PropertyNotify: {
+ if (!waiting_for_property_notify || event.xproperty.state != PropertyNewValue) {
break;
}
+ bool finished_incr = false;
{
- 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();
- }
+ 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_notify(clipboard_request_type, clipboard_data,
+ clipboard_data_size);
+ clipboard_request_type = 0;
}
break;
+ }
default:
return;
}
@@ -2471,6 +2580,15 @@ static void init_xrender()
XRenderQueryVersion(x_display, &major, &minor) && (major > 0 || minor >= 5);
}
+static void init_xfixes()
+{
+ int major;
+ int minor;
+
+ using_xfixes_1_0 = XFixesQueryExtension(x_display, &xfixes_event_base, &xfixes_error_base) &&
+ XFixesQueryVersion(x_display, &major, &minor) && major >= 1;
+}
+
unsigned int get_modifier_mask(KeySym modifier)
{
int mask = 0;
@@ -2681,6 +2799,7 @@ void Platform::init()
init_kbd();
init_xrandr();
init_xrender();
+ init_xfixes();
init_XIM();
struct sigaction act;
@@ -3017,26 +3136,35 @@ LocalCursor* Platform::create_default_cursor()
return new XDefaultCursor();
}
-void Platform::set_clipboard_listener(ClipboardListener* listener)
+bool Platform::set_clipboard_owner(uint32_t type)
{
- //FIXME: XA_CLIPBOARD(x_display)
- if (XGetSelectionOwner(x_display, XA_PRIMARY) == None) {
- return;
- }
- clipboard_listener = listener;
- /* Seems platform_win can be NULL, we'll just ignore that for now.
- This will be fixed when the rest of cut and paste lands */
- if (platform_win) {
- XConvertSelection(x_display, XA_PRIMARY, utf8_atom, clipboard_prop,
- platform_win, CurrentTime);
+ Lock lock(clipboard_lock);
+ uint32_t format = get_clipboard_format(type);
+
+ if (!format) {
+ LOG_INFO("Unsupported clipboard type %u", type);
+ return false;
}
+ clipboard_changer = true;
+ clipboard_data_size = 0;
+ XSetSelectionOwner(x_display, clipboard_prop, platform_win, CurrentTime);
+ return true;
+}
+
+void Platform::set_clipboard_listener(ClipboardListener* listener)
+{
+ clipboard_listener = listener ? listener : &default_clipboard_listener;
}
bool Platform::set_clipboard_data(uint32_t type, const uint8_t* data, int32_t size)
{
Lock lock(clipboard_lock);
+ uint32_t format = get_clipboard_format(type);
- LOG_INFO("type %u size %u data %s", type, size, data);
+ if (!format) {
+ LOG_INFO("Unsupported clipboard type %u", type);
+ return false;
+ }
if (size > clipboard_data_space) {
delete clipboard_data;
clipboard_data = new uint8_t[size];
@@ -3044,22 +3172,33 @@ bool Platform::set_clipboard_data(uint32_t type, const uint8_t* data, int32_t si
}
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");
+ clipboard_data_type = type;
+ send_selection_notify(format);
return true;
}
-bool Platform::get_clipboard_data(uint32_t type, uint8_t* data, int32_t size)
+bool Platform::request_clipboard_notification(uint32_t type)
{
- //FIXME: check type
- memcpy(data, clipboard_data, size);
+ uint32_t format = get_clipboard_format(type);
+
+ if (!format) {
+ LOG_INFO("Unsupported clipboard type %u", type);
+ return false;
+ }
+ if (XGetSelectionOwner(x_display, clipboard_prop) == None) {
+ LOG_INFO("No owner for the selection");
+ return false;
+ }
+ if (clipboard_request_type) {
+ LOG_INFO("XConvertSelection request is already pending");
+ return false;
+ }
+ clipboard_request_type = type;
+ XConvertSelection(x_display, clipboard_prop, format, clipboard_prop, platform_win, CurrentTime);
return true;
}
-int32_t Platform::get_clipboard_data_size(uint32_t type)
+void Platform::release_clipboard()
{
- //FIXME: check type
- return clipboard_data_size;
+ XSetSelectionOwner(x_display, clipboard_prop, None, CurrentTime);
}
-
diff --git a/configure.ac b/configure.ac
index 4fea09c4..d0ad8552 100644
--- a/configure.ac
+++ b/configure.ac
@@ -211,9 +211,10 @@ AC_SUBST(GL_LIBS)
SPICE_NONPKGCONFIG_LIBS+=" $GL_LIBS"
PKG_CHECK_MODULES(XRANDR, xrandr)
+PKG_CHECK_MODULES(XFIXES, xfixes)
AC_SUBST(XRANDR_CFLAGS)
AC_SUBST(XRANDR_LIBS)
-SPICE_REQUIRES+=" xrandr"
+SPICE_REQUIRES+=" xrandr xfixes"
PKG_CHECK_MODULES(XRANDR12,
xrandr >= 1.2,