Hi,

There still is quite some work to do, but I think that this can be
committed as is, and then build from there. I believe fixing up things
incrementally is better then doing one monster patch. So ack.

Note I'm working on this now, I will post set of patches on top of this
one before sunday.

Regards,

Hans


On 09/29/2010 02:17 PM, Arnon Gilboa wrote:
-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
---
  client/application.cpp      |    6 -
  client/application.h        |    2 -
  client/platform.h           |   13 +-
  client/red_client.cpp       |  159 ++++++++++--------
  client/red_client.h         |   30 +++-
  client/windows/platform.cpp |  264 +++++++++++++++++++++++------
  client/x11/Makefile.am      |    2 +
  client/x11/platform.cpp     |  387 +++++++++++++++++++++++++++++--------------
  configure.ac                |    3 +-
  9 files changed, 603 insertions(+), 263 deletions(-)

diff --git a/client/application.cpp b/client/application.cpp
index 490cd8c..a13d5e8 100644
--- a/client/application.cpp
+++ b/client/application.cpp
@@ -1325,12 +1325,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 dde37fc..36ae86e 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 6288380..4727889 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 79f5e6d..562b826 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 ba8b4ee..3ccb8e3 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 ae49d02..95876dc 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 101f6dd..f570a10 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,6 +203,7 @@ spicec_LDADD =                                              
\
        $(ALSA_LIBS)                                    \
        $(GL_LIBS)                                      \
        $(XRANDR_LIBS)                                  \
+       $(XFIXES_LIBS)                                  \
        $(MISC_X_LIBS)                                  \
        $(CEGUI_LIBS)                                   \
        -lrt
diff --git a/client/x11/platform.cpp b/client/x11/platform.cpp
index cc1502b..9d1112d 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>
@@ -56,6 +57,7 @@
  #include "res.h"
  #include "cursor.h"
  #include "process_loop.h"
+#include<spice/vd_agent.h>

  #define DWORD uint32_t
  #define BOOL bool
@@ -64,7 +66,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;
@@ -94,24 +96,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() {}
@@ -132,12 +149,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()));
@@ -763,6 +796,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;
  }

@@ -778,13 +816,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);
  }

@@ -873,7 +917,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());
@@ -1045,26 +1089,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());
  }

@@ -1172,7 +1214,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());
  }
@@ -1734,8 +1776,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;
@@ -2193,7 +2234,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];
@@ -2211,49 +2254,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)
  {
@@ -2281,28 +2362,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: {
@@ -2317,17 +2415,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;
@@ -2336,7 +2434,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;
              {
@@ -2345,33 +2443,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;
      }
@@ -2463,6 +2572,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;
@@ -2673,6 +2791,7 @@ void Platform::init()
      init_kbd();
      init_xrandr();
      init_xrender();
+    init_xfixes();
      init_XIM();

      struct sigaction act;
@@ -3009,22 +3128,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;
+    Lock lock(clipboard_lock);
+    uint32_t format = get_clipboard_format(type);
+
+    if (!format) {
+        LOG_INFO("Unsupported clipboard type %u", type);
+        return false;
      }
-    clipboard_listener = listener;
-    XConvertSelection(x_display, XA_PRIMARY, utf8_atom, clipboard_prop,
-        platform_win, CurrentTime);
+    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];
@@ -3032,22 +3164,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 3369dde..77fdf6e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -189,9 +189,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,
_______________________________________________
Spice-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/spice-devel

Reply via email to