[Spice-devel] [PATCH vdagent] vdagent: add image copy-paste support

Arnon Gilboa agilboa at redhat.com
Tue Nov 23 07:56:54 PST 2010


-currently png & bmp
-using wspice libs cximage.lib & png.lib
-jpg & tiff will follow
---
 vdagent/vdagent.cpp    |  214 ++++++++++++++++++++++++++++++++---------------
 vdagent/vdagent.vcproj |   21 +++--
 2 files changed, 159 insertions(+), 76 deletions(-)

diff --git a/vdagent/vdagent.cpp b/vdagent/vdagent.cpp
index c210016..1ebf1e4 100644
--- a/vdagent/vdagent.cpp
+++ b/vdagent/vdagent.cpp
@@ -18,25 +18,41 @@
 #include "vdcommon.h"
 #include "desktop_layout.h"
 #include "display_setting.h"
+#include "ximage.h"
 #include <lmcons.h>
+#include <set>
 
 #define VD_AGENT_LOG_PATH       TEXT("%svdagent.log")
 #define VD_AGENT_WINCLASS_NAME  TEXT("VDAGENT")
 #define VD_INPUT_INTERVAL_MS    20
 #define VD_TIMER_ID             1
 #define VD_CLIPBOARD_TIMEOUT_MS 10000
+#define VD_CLIPBOARD_FORMAT_MAX_TYPES 16
 
+//FIXME: extract format/type stuff to win_vdagent_common for use by windows\platform.cpp as well
 typedef struct VDClipboardFormat {
     uint32_t format;
-    uint32_t type;
+    uint32_t types[VD_CLIPBOARD_FORMAT_MAX_TYPES];
 } VDClipboardFormat;
 
 VDClipboardFormat clipboard_formats[] = {
-    {CF_UNICODETEXT, VD_AGENT_CLIPBOARD_UTF8_TEXT},
-    {0, 0}};
+    {CF_UNICODETEXT, {VD_AGENT_CLIPBOARD_UTF8_TEXT, 0}},
+    //FIXME: support more image types
+    {CF_DIB, {VD_AGENT_CLIPBOARD_IMAGE_PNG, VD_AGENT_CLIPBOARD_IMAGE_BMP, 0}},
+};
 
 #define clipboard_formats_count (sizeof(clipboard_formats) / sizeof(clipboard_formats[0]))
 
+typedef struct ImageType {
+    uint32_t type;
+    DWORD cximage_format;
+} ImageType;
+
+static ImageType image_types[] = {
+    {VD_AGENT_CLIPBOARD_IMAGE_PNG, CXIMAGE_FORMAT_PNG},
+    {VD_AGENT_CLIPBOARD_IMAGE_BMP, CXIMAGE_FORMAT_BMP},
+};
+
 class VDAgent {
 public:
     static VDAgent* get();
@@ -61,13 +77,15 @@ private:
     void on_clipboard_release();
     DWORD get_buttons_change(DWORD last_buttons_state, DWORD new_buttons_state,
                              DWORD mask, DWORD down_flag, DWORD up_flag);
+    static HGLOBAL utf8_alloc(LPCSTR data, int size);
     static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
     static VOID CALLBACK read_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlap);
     static VOID CALLBACK write_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlap);
     static DWORD WINAPI event_thread_proc(LPVOID param);
     static void dispatch_message(VDAgentMessage* msg, uint32_t port);
-    static uint32_t get_clipboard_format(uint32_t type);
-    static uint32_t get_clipboard_type(uint32_t format);
+    uint32_t get_clipboard_format(uint32_t type);
+    uint32_t get_clipboard_type(uint32_t format);
+    DWORD get_cximage_format(uint32_t type);
     enum { owner_none, owner_guest, owner_client };
     void set_clipboard_owner(int new_owner);
     uint8_t* write_lock(DWORD bytes = 0);
@@ -113,6 +131,8 @@ private:
     uint32_t *_client_caps;
     uint32_t _client_caps_size;
 
+    std::set<uint32_t> _grab_types;
+
     VDLog* _log;
 };
 
@@ -162,6 +182,7 @@ VDAgent::VDAgent()
     ZeroMemory(&_input, sizeof(INPUT));
     ZeroMemory(&_pipe_state, sizeof(VDPipeState));
     MUTEX_INIT(_write_mutex);
+
     _singleton = this;
 }
 
@@ -498,10 +519,7 @@ bool VDAgent::handle_mon_config(VDAgentMonitorsConfig* mon_config, uint32_t port
 
 bool VDAgent::handle_clipboard(VDAgentClipboard* clipboard, uint32_t size)
 {
-    HGLOBAL clip_data;
-    LPVOID clip_buf;
-    int clip_size;
-    int clip_len;
+    HANDLE clip_data;
     UINT format;
     bool ret = false;
 
@@ -514,40 +532,22 @@ bool VDAgent::handle_clipboard(VDAgentClipboard* clipboard, uint32_t size)
         SetEvent(_clipboard_event);
         return false;
     }
-    // Get the required clipboard size
     switch (clipboard->type) {
     case VD_AGENT_CLIPBOARD_UTF8_TEXT:
-        // Received utf8 string is not null-terminated   
-        if (!(clip_len = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)clipboard->data, size, NULL, 0))) {
-            return false;
-        }
-        clip_len++;
-        clip_size = clip_len * sizeof(WCHAR);
+        clip_data = utf8_alloc((LPCSTR)clipboard->data, size);
+        break;
+    case VD_AGENT_CLIPBOARD_IMAGE_PNG:
+    case VD_AGENT_CLIPBOARD_IMAGE_BMP: {
+        DWORD cximage_format = get_cximage_format(clipboard->type);
+        ASSERT(cximage_format);
+        CxImage image(clipboard->data, size, cximage_format);
+        clip_data = image.CopyToHandle();
         break;
+    }
     default:
         vd_printf("Unsupported clipboard type %u", clipboard->type);
         return true;
     }
-    // Allocate and lock clipboard memory
-    if (!(clip_data = GlobalAlloc(GMEM_DDESHARE, clip_size))) {
-        return false;
-    }
-    if (!(clip_buf = GlobalLock(clip_data))) {
-        GlobalFree(clip_data);
-        return false;
-    }
-    // Translate data and set clipboard content
-    switch (clipboard->type) {
-    case VD_AGENT_CLIPBOARD_UTF8_TEXT:
-        ret = !!MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)clipboard->data, size, (LPWSTR)clip_buf,
-                                    clip_len);
-        ((LPWSTR)clip_buf)[clip_len - 1] = L'\0';
-        break;
-    }
-    GlobalUnlock(clip_data);
-    if (!ret) {
-        return false;
-    }
     format = get_clipboard_format(clipboard->type);
     if (SetClipboardData(format, clip_data)) {
         SetEvent(_clipboard_event);
@@ -563,6 +563,36 @@ bool VDAgent::handle_clipboard(VDAgentClipboard* clipboard, uint32_t size)
     return ret;
 }
 
+HGLOBAL VDAgent::utf8_alloc(LPCSTR data, int size)
+{
+    HGLOBAL handle; 
+    LPVOID buf;
+    int len;
+
+    // Received utf8 string is not null-terminated   
+    if (!(len = MultiByteToWideChar(CP_UTF8, 0, data, size, NULL, 0))) {
+        return NULL;
+    }
+    len++;
+    // Allocate and lock clipboard memory
+    if (!(handle = GlobalAlloc(GMEM_DDESHARE, len * sizeof(WCHAR)))) {
+        return NULL;
+    }
+    if (!(buf = GlobalLock(handle))) {
+        GlobalFree(handle);
+        return NULL;
+    }  
+    // Translate data and set clipboard content
+    if (!(MultiByteToWideChar(CP_UTF8, 0, data, size, (LPWSTR)buf, len))) {
+        GlobalUnlock(handle);
+        GlobalFree(handle);
+        return NULL;
+    }
+    ((LPWSTR)buf)[len - 1] = L'\0';
+    GlobalUnlock(handle);
+    return handle;
+}
+
 void VDAgent::set_display_depth(uint32_t depth)
 {
     size_t display_count;
@@ -798,16 +828,18 @@ bool VDAgent::write_message(uint32_t type, uint32_t size = 0, void* data = NULL)
 
 void VDAgent::on_clipboard_grab()
 {
-    uint32_t* types = new uint32_t[clipboard_formats_count];
+    uint32_t* types = new uint32_t[clipboard_formats_count * VD_CLIPBOARD_FORMAT_MAX_TYPES];
     int count = 0;
 
     if (!VD_AGENT_HAS_CAPABILITY(_client_caps, _client_caps_size,
                                  VD_AGENT_CAP_CLIPBOARD_BY_DEMAND)) {
         return;
-    } 
-    for (VDClipboardFormat* iter = clipboard_formats; iter->format; iter++) {
-        if (IsClipboardFormatAvailable(iter->format)) {
-            types[count++] = iter->type;
+    }
+    for (int i = 0; i < clipboard_formats_count; i++) {
+        if (IsClipboardFormatAvailable(clipboard_formats[i].format)) {
+            for (uint32_t* ptype = clipboard_formats[i].types; *ptype; ptype++) {
+                types[count++] = *ptype;
+            }
         }
     }
     if (count) {
@@ -865,14 +897,14 @@ void VDAgent::on_clipboard_release()
 
 bool VDAgent::handle_clipboard_grab(VDAgentClipboardGrab* clipboard_grab, uint32_t size)
 {
-    bool has_supported_type = false;
-    uint32_t format;
+    std::set<uint32_t> grab_formats;
 
+    _grab_types.clear();
     for (uint32_t i = 0; i < size / sizeof(clipboard_grab->types[0]); i++) {
-        format = get_clipboard_format(clipboard_grab->types[i]);
+        vd_printf("grab %u", clipboard_grab->types[i]);
+        uint32_t format = get_clipboard_format(clipboard_grab->types[i]);
         //On first supported type, open and empty the clipboard
-        if (format && !has_supported_type) {
-            has_supported_type = true;
+        if (format && grab_formats.empty()) {
             if (!OpenClipboard(_hwnd)) {
                 return false;
             }
@@ -880,10 +912,13 @@ bool VDAgent::handle_clipboard_grab(VDAgentClipboardGrab* clipboard_grab, uint32
         }
         //For all supported type set delayed rendering
         if (format) {
-            SetClipboardData(format, NULL);
+            _grab_types.insert(clipboard_grab->types[i]);
+            if (grab_formats.insert(format).second) {
+                SetClipboardData(format, NULL);
+            }
         }
     }
-    if (!has_supported_type) {
+    if (grab_formats.empty()) {
         vd_printf("No supported clipboard types in client grab");
         return true;
     }
@@ -898,9 +933,10 @@ bool VDAgent::handle_clipboard_request(VDAgentClipboardRequest* clipboard_reques
 {
     UINT format;
     HANDLE clip_data;
-    LPVOID clip_buf;
-    int clip_size;
+    uint8_t* new_data = NULL;
+    long new_size;
     size_t len;
+    CxImage image;
 
     if (_clipboard_owner != owner_guest) {
         vd_printf("Received clipboard request from client while clipboard is not owned by guest");
@@ -917,40 +953,60 @@ bool VDAgent::handle_clipboard_request(VDAgentClipboardRequest* clipboard_reques
     if (!IsClipboardFormatAvailable(format) || !OpenClipboard(_hwnd)) {
         return false;
     }
-    if (!(clip_data = GetClipboardData(format)) || !(clip_buf = GlobalLock(clip_data))) {
+    if (!(clip_data = GetClipboardData(format))) {
         CloseClipboard();
         return false;
     }
     switch (clipboard_request->type) {
     case VD_AGENT_CLIPBOARD_UTF8_TEXT:
-        len = wcslen((wchar_t*)clip_buf);
-        clip_size = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)clip_buf, (int)len, NULL, 0, NULL, NULL);
+        if (!(new_data = (uint8_t*)GlobalLock(clip_data))) {
+            break;
+        }
+        len = wcslen((LPCWSTR)new_data);
+        new_size = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)new_data, (int)len, NULL, 0, NULL, NULL);
+        break;
+    case VD_AGENT_CLIPBOARD_IMAGE_PNG:
+    case VD_AGENT_CLIPBOARD_IMAGE_BMP: {
+        DWORD cximage_format = get_cximage_format(clipboard_request->type);
+        ASSERT(cximage_format);
+        if (!image.CreateFromHANDLE(clip_data)) {
+            vd_printf("Image create from handle failed");
+            break;
+        }
+        if (!image.Encode(new_data, new_size, cximage_format)) {
+            vd_printf("Image encode to type %u failed", clipboard_request->type);
+            break;
+        }
+        vd_printf("Image encoded to %u bytes", new_size);
         break;
     }
-
-    if (!clip_size) {
-        GlobalUnlock(clip_data);
+    }
+    if (!new_size) {
         CloseClipboard();
         return false;
     }
     _out_msg_pos = 0;
-    _out_msg_size = sizeof(VDAgentMessage) + sizeof(VDAgentClipboard) + clip_size;
+    _out_msg_size = sizeof(VDAgentMessage) + sizeof(VDAgentClipboard) + new_size;
     _out_msg = (VDAgentMessage*)new uint8_t[_out_msg_size];
     _out_msg->protocol = VD_AGENT_PROTOCOL;
     _out_msg->type = VD_AGENT_CLIPBOARD;
     _out_msg->opaque = 0;
-    _out_msg->size = (uint32_t)(sizeof(VDAgentClipboard) + clip_size);
+    _out_msg->size = (uint32_t)(sizeof(VDAgentClipboard) + new_size);
     VDAgentClipboard* clipboard = (VDAgentClipboard*)_out_msg->data;
     clipboard->type = clipboard_request->type;
 
     switch (clipboard_request->type) {
     case VD_AGENT_CLIPBOARD_UTF8_TEXT:
-        WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)clip_buf, (int)len, (LPSTR)clipboard->data,
-                            clip_size, NULL, NULL);
+        WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)new_data, (int)len, (LPSTR)clipboard->data,
+                            new_size, NULL, NULL);
+        GlobalUnlock(clip_data);
+        break;
+    case VD_AGENT_CLIPBOARD_IMAGE_PNG:
+    case VD_AGENT_CLIPBOARD_IMAGE_BMP:
+        memcpy(clipboard->data, new_data, new_size);
+        image.FreeMemory(new_data);
         break;
     }
-
-    GlobalUnlock(clip_data);
     CloseClipboard();
     write_clipboard();
     return true;
@@ -968,9 +1024,11 @@ void VDAgent::handle_clipboard_release()
 
 uint32_t VDAgent::get_clipboard_format(uint32_t type)
 {
-    for (VDClipboardFormat* iter = clipboard_formats; iter->format && iter->type; iter++) {
-        if (iter->type == type) {
-            return iter->format;
+    for (int i = 0; i < clipboard_formats_count; i++) {
+        for (uint32_t* ptype = clipboard_formats[i].types; *ptype; ptype++) {
+            if (*ptype == type) {
+                return clipboard_formats[i].format; 
+            }
         }
     }
     return 0;
@@ -978,9 +1036,29 @@ uint32_t VDAgent::get_clipboard_format(uint32_t type)
 
 uint32_t VDAgent::get_clipboard_type(uint32_t format)
 {
-    for (VDClipboardFormat* iter = clipboard_formats; iter->format && iter->type; iter++) {
-        if (iter->format == format) {
-            return iter->type;
+    uint32_t* types = NULL;
+
+    for (int i = 0; i < clipboard_formats_count && !types; i++) {
+        if (clipboard_formats[i].format == format) {
+            types = clipboard_formats[i].types;
+        }
+    }
+    if (!types) {
+        return 0;
+    }
+    for (uint32_t* ptype = types; *ptype; ptype++) {
+        if (_grab_types.find(*ptype) != _grab_types.end()) {
+            return *ptype;
+        }
+    }
+    return 0;
+}
+
+DWORD VDAgent::get_cximage_format(uint32_t type)
+{
+    for (int i = 0; i < sizeof(image_types) / sizeof(image_types[0]); i++) {
+        if (image_types[i].type == type) {
+            return image_types[i].cximage_format;
         }
     }
     return 0;
diff --git a/vdagent/vdagent.vcproj b/vdagent/vdagent.vcproj
index 0453c12..ee60f42 100644
--- a/vdagent/vdagent.vcproj
+++ b/vdagent/vdagent.vcproj
@@ -44,7 +44,7 @@
 			<Tool
 				Name="VCCLCompilerTool"
 				Optimization="0"
-				AdditionalIncludeDirectories="..\common;$(SPICE_COMMON_DIR)"
+				AdditionalIncludeDirectories="..\common;&quot;$(SPICE_COMMON_DIR)&quot;;&quot;$(SPICE_LIBS)\include\CxImage&quot;"
 				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS,_WIN32_WINNT=0x0501"
 				MinimalRebuild="true"
 				BasicRuntimeChecks="3"
@@ -65,8 +65,9 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Version.lib"
+				AdditionalDependencies="Version.lib zlibwapiD.lib png_d.lib cximage_d.lib"
 				LinkIncremental="2"
+				AdditionalLibraryDirectories="&quot;$(SPICE_LIBS)\lib&quot;"
 				GenerateDebugInformation="true"
 				SubSystem="2"
 				RandomizedBaseAddress="1"
@@ -121,7 +122,7 @@
 			<Tool
 				Name="VCCLCompilerTool"
 				Optimization="0"
-				AdditionalIncludeDirectories="..\common;$(SPICE_COMMON_DIR)"
+				AdditionalIncludeDirectories="..\common;&quot;$(SPICE_COMMON_DIR)&quot;;&quot;$(SPICE_LIBS)\include\CxImage&quot;"
 				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS,_WIN32_WINNT=0x0501"
 				MinimalRebuild="true"
 				BasicRuntimeChecks="3"
@@ -142,8 +143,10 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Version.lib"
+				AdditionalDependencies="Version.lib zlibwapiD.lib png_d.lib cximage_d.lib"
 				LinkIncremental="2"
+				AdditionalLibraryDirectories="&quot;$(SPICE_LIBS)\lib64&quot;"
+				IgnoreDefaultLibraryNames=""
 				GenerateDebugInformation="true"
 				SubSystem="2"
 				RandomizedBaseAddress="1"
@@ -197,7 +200,7 @@
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="..\common;$(SPICE_COMMON_DIR)"
+				AdditionalIncludeDirectories="..\common;&quot;$(SPICE_COMMON_DIR)&quot;;&quot;$(SPICE_LIBS)\include\CxImage&quot;"
 				AdditionalUsingDirectories=""
 				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS,_WIN32_WINNT=0x0501"
 				RuntimeLibrary="0"
@@ -217,8 +220,9 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Version.lib"
+				AdditionalDependencies="Version.lib zlibwapi.lib png.lib cximage.lib"
 				LinkIncremental="1"
+				AdditionalLibraryDirectories="&quot;$(SPICE_LIBS)\lib&quot;"
 				GenerateDebugInformation="true"
 				SubSystem="2"
 				OptimizeReferences="2"
@@ -275,7 +279,7 @@
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="..\common;$(SPICE_COMMON_DIR)"
+				AdditionalIncludeDirectories="..\common;&quot;$(SPICE_COMMON_DIR)&quot;;&quot;$(SPICE_LIBS)\include\CxImage&quot;"
 				AdditionalUsingDirectories=""
 				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS,_WIN32_WINNT=0x0501"
 				RuntimeLibrary="0"
@@ -295,8 +299,9 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Version.lib"
+				AdditionalDependencies="Version.lib zlibwapi.lib png.lib cximage.lib"
 				LinkIncremental="1"
+				AdditionalLibraryDirectories="&quot;$(SPICE_LIBS)\lib64&quot;"
 				GenerateDebugInformation="true"
 				SubSystem="2"
 				OptimizeReferences="2"
-- 
1.5.5.6



More information about the Spice-devel mailing list