[Spice-commits] 2 commits - client/application.cpp client/application.h client/platform.h client/red_client.cpp client/red_client.h client/windows client/x11 server/reds.c

Alon Levy alon at kemper.freedesktop.org
Mon Jul 19 00:30:57 PDT 2010


 client/application.cpp      |    6 
 client/application.h        |    2 
 client/platform.h           |   17 ++
 client/red_client.cpp       |   91 ++++++++++++++
 client/red_client.h         |    7 +
 client/windows/platform.cpp |   90 ++++++++++++++
 client/x11/platform.cpp     |  274 ++++++++++++++++++++++++++++++++++++++++++--
 server/reds.c               |   28 +++-
 8 files changed, 497 insertions(+), 18 deletions(-)

New commits:
commit 5cc9b924cb202e7fcfa4ac846b381aac162182b6
Author: Alon Levy <alevy at redhat.com>
Date:   Tue Jul 13 11:07:01 2010 +0300

    server vdi port: prevent recursive calls to read_from_vdi_port (required for spice-vmc)

diff --git a/server/reds.c b/server/reds.c
index b244371..97a47ae 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -1169,7 +1169,7 @@ void vdi_read_buf_release(uint8_t *data, void *opaque)
     VDIReadBuf *buf = (VDIReadBuf *)opaque;
 
     ring_add(&reds->agent_state.read_bufs, &buf->link);
-    read_from_vdi_port();
+    //read_from_vdi_port(); // XXX WTF? - ask arnon. should we be calling this here?? (causes recursion of read_from_vdi_port..) 
 }
 
 static void dispatch_vdi_port_data(int port, VDIReadBuf *buf)
@@ -1198,27 +1198,37 @@ static void dispatch_vdi_port_data(int port, VDIReadBuf *buf)
 
 static int read_from_vdi_port(void)
 {
+    // FIXME: UGLY HACK. Result of spice-vmc vmc_read triggering flush of throttled data, and recalling this.
+    static int inside_call = 0;
+    int quit_loop = 0;
     VDIPortState *state = &reds->agent_state;
     SpiceVDIPortInterface *sif;
     VDIReadBuf *dispatch_buf;
     int total = 0;
     int n;
+    if (inside_call) {
+        return 0;
+    }
+    inside_call = 1;
 
     if (!reds->agent_state.connected || reds->mig_target) {
+        inside_call = 0;
         return 0;
     }
 
     sif = SPICE_CONTAINEROF(vdagent->base.sif, SpiceVDIPortInterface, base);
-    while (reds->agent_state.connected) {
+    while (!quit_loop && reds->agent_state.connected) {
         switch (state->read_state) {
         case VDI_PORT_READ_STATE_READ_HADER:
             n = sif->read(vdagent, state->recive_pos, state->recive_len);
             if (!n) {
-                return total;
+                quit_loop = 1;
+                break;
             }
             total += n;
             if ((state->recive_len -= n)) {
                 state->recive_pos += n;
+                quit_loop = 1;
                 break;
             }
             state->message_recive_len = state->vdi_chunk_header.size;
@@ -1227,12 +1237,14 @@ static int read_from_vdi_port(void)
             RingItem *item;
 
             if (!(item = ring_get_head(&state->read_bufs))) {
-                return total;
+                quit_loop = 1;
+                break;
             }
 
             if (state->vdi_chunk_header.port == VDP_CLIENT_PORT) {
                 if (!state->send_tokens) {
-                    return total;
+                    quit_loop = 1;
+                    break;
                 }
                 --state->send_tokens;
             }
@@ -1248,7 +1260,8 @@ static int read_from_vdi_port(void)
         case VDI_PORT_READ_STATE_READ_DATA:
             n = sif->read(vdagent, state->recive_pos, state->recive_len);
             if (!n) {
-                return total;
+                quit_loop = 1;
+                break;
             }
             total += n;
             if ((state->recive_len -= n)) {
@@ -1268,6 +1281,7 @@ static int read_from_vdi_port(void)
             dispatch_vdi_port_data(state->vdi_chunk_header.port, dispatch_buf);
         }
     }
+    inside_call = 0;
     return total;
 }
 
@@ -1616,7 +1630,7 @@ static void reds_main_handle_message(void *opaque, size_t size, uint32_t type, v
         }
         agent_start = (SpiceMsgcMainAgentTokens *)message;
         reds->agent_state.client_agent_started = TRUE;
-        reds->agent_state.send_tokens = agent_start->num_tokens;
+        reds->agent_state.send_tokens = agent_start->num_tokens; // TODO: sanitize? coming from guest!
         read_from_vdi_port();
         break;
     }
commit ce03f5449d68b2f0201dce409a81280300120069
Author: Arnon Gilboa <agilboa at redhat.com>
Date:   Mon Jul 19 10:29:47 2010 +0300

    client: add clipboard support
    
     * windows - untested
     * linux - small strings both ways, large implemented differently:
      * client to guest - support INCR
      * guest to client - we supply a single possibly very large property
     * requires server changes in next patch to work with spice-vmc

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


More information about the Spice-commits mailing list