[Spice-devel] [RFC] [PATCH vd_agent] DND: Add drag-and-drop support

Dunrong Huang riegamaths at gmail.com
Mon Nov 5 01:02:26 PST 2012


For now, there are two types of messages needed to be handled

1) VD_AGENT_DND_START: when user drags a file and drop to spice
client, spice client will send this message to notify guest agent
to prepare for receiving file, file name and file size are included
in message's data.

2) VD_AGENT_DND_DATA: the message is used to transfer file content.

Signed-off-by: Dunrong Huang <riegamaths at gmail.com>
---
 src/vdagent.c                | 110 +++++++++++++++++++++++++++++++++++++++++++
 src/vdagentd-proto-strings.h |   2 +
 src/vdagentd-proto.h         |   2 +
 src/vdagentd.c               |  28 +++++++++++
 4 files changed, 142 insertions(+)

diff --git a/src/vdagent.c b/src/vdagent.c
index 0af82b1..ae47df1 100644
--- a/src/vdagent.c
+++ b/src/vdagent.c
@@ -33,6 +33,8 @@
 #include <signal.h>
 #include <sys/select.h>
 #include <sys/stat.h>
+#include <sys/types.h>
+#include <pwd.h>
 #include <spice/vd_agent.h>
 
 #include "udscs.h"
@@ -40,6 +42,12 @@
 #include "vdagentd-proto-strings.h"
 #include "vdagent-x11.h"
 
+typedef struct AgentData {
+    int dnd_fd;
+    int file_size;
+    int read_bytes;
+} AgentData;
+
 static const char *portdev = "/dev/virtio-ports/com.redhat.spice.0";
 static int debug = 0;
 static struct vdagent_x11 *x11 = NULL;
@@ -47,9 +55,90 @@ static struct udscs_connection *client = NULL;
 static int quit = 0;
 static int version_mismatch = 0;
 
+static const char *get_home_dir()
+{
+    static const char *home = NULL;
+    struct passwd *p;
+
+    if (home)
+        return home;
+
+    home = getenv ("HOME");
+    if (!home) {
+        p = getpwuid(getuid());
+        home = p->pw_dir;
+    }
+
+    return home ? home : "";
+}
+
+static void vdagent_dnd_start(const char *file_name, int file_size)
+{
+    AgentData *dnd;
+    char path[256];             /* file path, limit to 256 chars */
+
+    if (!file_name || file_size <= 0) {
+        return ;
+    }
+
+    syslog(LOG_DEBUG, "DND start, file:%s\n", file_name);
+    snprintf(path, sizeof(path), "%s/Desktop/%s", get_home_dir(), file_name);
+
+    dnd = udscs_get_user_data(client);
+    if (!dnd)
+        return ;
+    if (dnd->dnd_fd > 0) {
+        /* FIXME: close old file descriptor or failed this dnd ? */
+        close(dnd->dnd_fd);
+    }
+
+    dnd->dnd_fd = open(path, O_CREAT | O_WRONLY, 0644);
+    if (dnd->dnd_fd == -1) {
+        syslog(LOG_ERR, "Create file error:%s\n", strerror(errno));
+    }
+    dnd->file_size = file_size;
+    dnd->read_bytes = 0;
+}
+
+static void vdagent_dnd_get_data(const char *data, int size)
+{
+    AgentData *dnd;
+    int len;
+
+    if (!data || size <= 0) {
+        return ;
+    }
+
+    dnd = udscs_get_user_data(client);
+    if (!dnd)
+        return ;
+
+    if (dnd->dnd_fd == -1) {
+        syslog(LOG_ERR, "file has not been created\n");
+        return ;
+    }
+
+    len = write(dnd->dnd_fd, data, size);
+    if (len == -1) {
+        syslog(LOG_ERR, "write file error:%s\n", strerror(errno));
+        return ;
+    }
+
+    dnd->read_bytes += size;
+    if (dnd->read_bytes >= dnd->file_size) {
+        syslog(LOG_DEBUG, "file write done\n");
+        close(dnd->dnd_fd);
+        dnd->dnd_fd = -1;
+        dnd->file_size = 0;
+        dnd->read_bytes = 0;
+    }
+}
+
 void daemon_read_complete(struct udscs_connection **connp,
     struct udscs_message_header *header, uint8_t *data)
 {
+    VDAgentDndMessage *msg;
+
     switch (header->type) {
     case VDAGENTD_MONITORS_CONFIG:
         vdagent_x11_set_monitor_config(x11, (VDAgentMonitorsConfig *)data);
@@ -82,6 +171,16 @@ void daemon_read_complete(struct udscs_connection **connp,
             version_mismatch = 1;
         }
         break;
+    case VDAGENTD_DND_START:
+        msg = (VDAgentDndMessage *)data;
+        /* vdagent_dnd_start(file_name, file_size); */
+        vdagent_dnd_start(msg->file_name, msg->file_size);
+        free(data);
+        break;
+    case VDAGENTD_DND_DATA:
+        vdagent_dnd_get_data(data, header->size);
+        free(data);
+        break;
     default:
         syslog(LOG_ERR, "Unknown message from vdagentd type: %d, ignoring",
                header->type);
@@ -100,6 +199,17 @@ int client_setup(int reconnect)
         }
         sleep(1);
     }
+    if (client) {
+        AgentData *data = malloc(sizeof(AgentData));
+        if (!data) {
+            syslog(LOG_ERR, "Out of memory allocating agent data, disconnecting");
+            return -1;
+        }
+        data->dnd_fd = -1;
+        data->file_size = 0;
+        data->read_bytes = 0;
+        udscs_set_user_data(client, (void *)data);
+    }
     return client == NULL;
 }
 
diff --git a/src/vdagentd-proto-strings.h b/src/vdagentd-proto-strings.h
index f39e25b..2f5689b 100644
--- a/src/vdagentd-proto-strings.h
+++ b/src/vdagentd-proto-strings.h
@@ -30,6 +30,8 @@ static const char * const vdagentd_messages[] = {
         "clipboard data",
         "clipboard release",
         "version",
+        "dnd start",
+        "dnd data",
 };        
 
 #endif
diff --git a/src/vdagentd-proto.h b/src/vdagentd-proto.h
index 08279a3..daf86ad 100644
--- a/src/vdagentd-proto.h
+++ b/src/vdagentd-proto.h
@@ -36,6 +36,8 @@ enum {
     VDAGENTD_CLIPBOARD_DATA,    /* arg1: sel, arg 2: type, data: data */
     VDAGENTD_CLIPBOARD_RELEASE, /* arg1: selection */
     VDAGENTD_VERSION,           /* daemon -> client, data: version string */
+    VDAGENTD_DND_START,         /* daemon -> client, VD_AGENT_DND_START */
+    VDAGENTD_DND_DATA,          /* daemon -> client, VD_AGENT_DND_DATA */
     VDAGENTD_NO_MESSAGES /* Must always be last */
 };
 
diff --git a/src/vdagentd.c b/src/vdagentd.c
index a4db935..96b0480 100644
--- a/src/vdagentd.c
+++ b/src/vdagentd.c
@@ -214,6 +214,30 @@ static void do_client_clipboard(struct vdagent_virtio_port *vport,
                 data, size);
 }
 
+static void dnd_process(struct vdagent_virtio_port *vport,
+                        VDAgentMessage *message_header, uint8_t *data)
+{
+    uint32_t msg_type;
+
+    if (!active_session_conn) {
+        syslog(LOG_WARNING,
+               "Could not find an agent connnection belonging to the "
+               "active session, ignoring client clipboard request");
+        return;
+    }
+
+    switch (message_header->type) {
+    case VD_AGENT_DND_START:
+        msg_type = VDAGENTD_DND_START;
+        break;
+    case VD_AGENT_DND_DATA:
+        msg_type = VDAGENTD_DND_DATA;
+        break;
+    }
+
+    udscs_write(active_session_conn, msg_type, 0, 0, data,  message_header->size);
+}
+
 int virtio_port_read_complete(
         struct vdagent_virtio_port *vport,
         int port_nr,
@@ -283,6 +307,10 @@ int virtio_port_read_complete(
         }
         do_client_clipboard(vport, message_header, data);
         break;
+    case VD_AGENT_DND_START:
+    case VD_AGENT_DND_DATA:
+        dnd_process(vport, message_header, data);
+        break;
     default:
         syslog(LOG_WARNING, "unknown message type %d, ignoring",
                message_header->type);
-- 
1.7.12.4



More information about the Spice-devel mailing list