From: Damjan Jovanovic Subject: [PATCH 1/2] winex11.drv: copy and paste files to/from X11 apps (try 3) Message-Id: Date: Wed, 7 May 2014 11:46:44 +0200 Import and export HDROP as text/uri-list, so we can copy and paste files bidirectionally between X11 apps and Wine apps. This patch is based on the import functions from xdnd.c, which are deleted from there in the next patch. Try 3 uses array doubling (instead of counting exact URI sizes and allocating precisely as much memory as is needed), leading to less code. Damjan Jovanovic --- dlls/winex11.drv/clipboard.c | 302 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 301 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/clipboard.c b/dlls/winex11.drv/clipboard.c index 730b6c9..8dbe25a 100644 --- a/dlls/winex11.drv/clipboard.c +++ b/dlls/winex11.drv/clipboard.c @@ -5,6 +5,7 @@ * 1996 Alex Korobka * 1999 Noel Borthwick * 2003 Ulrich Czekalla for CodeWeavers + * 2014 Damjan Jovanovic * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -78,6 +79,9 @@ #include "windef.h" #include "winbase.h" +#include "shlobj.h" +#include "shellapi.h" +#include "shlwapi.h" #include "x11drv.h" #include "wine/list.h" #include "wine/debug.h" @@ -144,6 +148,7 @@ static HANDLE X11DRV_CLIPBOARD_ImportImageBmp(Display *d, Window w, Atom prop); static HANDLE X11DRV_CLIPBOARD_ImportXAString(Display *d, Window w, Atom prop); static HANDLE X11DRV_CLIPBOARD_ImportUTF8(Display *d, Window w, Atom prop); static HANDLE X11DRV_CLIPBOARD_ImportCompoundText(Display *d, Window w, Atom prop); +static HANDLE X11DRV_CLIPBOARD_ImportTextUriList(Display *display, Window w, Atom prop); static HANDLE X11DRV_CLIPBOARD_ExportClipboardData(Display *display, Window requestor, Atom aTarget, Atom rprop, LPWINE_CLIPDATA lpData, LPDWORD lpBytes); static HANDLE X11DRV_CLIPBOARD_ExportString(Display *display, Window requestor, Atom aTarget, @@ -158,6 +163,8 @@ static HANDLE X11DRV_CLIPBOARD_ExportEnhMetaFile(Display *display, Window reques Atom rprop, LPWINE_CLIPDATA lpdata, LPDWORD lpBytes); static HANDLE X11DRV_CLIPBOARD_ExportTextHtml(Display *display, Window requestor, Atom aTarget, Atom rprop, LPWINE_CLIPDATA lpdata, LPDWORD lpBytes); +static HANDLE X11DRV_CLIPBOARD_ExportHDROP(Display *display, Window requestor, Atom aTarget, + Atom rprop, LPWINE_CLIPDATA lpdata, LPDWORD lpBytes); static WINE_CLIPFORMAT *X11DRV_CLIPBOARD_InsertClipboardFormat(UINT id, Atom prop); static BOOL X11DRV_CLIPBOARD_RenderSynthesizedText(Display *display, UINT wFormatID); static void X11DRV_CLIPBOARD_FreeData(LPWINE_CLIPDATA lpData); @@ -201,7 +208,7 @@ static const struct /* If UTF8_STRING is not available, attempt COMPOUND_TEXT */ { CF_UNICODETEXT, XATOM_COMPOUND_TEXT, X11DRV_CLIPBOARD_ImportCompoundText, X11DRV_CLIPBOARD_ExportString }, { CF_ENHMETAFILE, XATOM_WCF_ENHMETAFILE, X11DRV_CLIPBOARD_ImportEnhMetaFile, X11DRV_CLIPBOARD_ExportEnhMetaFile }, - { CF_HDROP, XATOM_WCF_HDROP, X11DRV_CLIPBOARD_ImportClipboardData, X11DRV_CLIPBOARD_ExportClipboardData }, + { CF_HDROP, XATOM_text_uri_list, X11DRV_CLIPBOARD_ImportTextUriList, X11DRV_CLIPBOARD_ExportHDROP }, { CF_LOCALE, XATOM_WCF_LOCALE, X11DRV_CLIPBOARD_ImportClipboardData, X11DRV_CLIPBOARD_ExportClipboardData }, { CF_DIBV5, XATOM_WCF_DIBV5, X11DRV_CLIPBOARD_ImportClipboardData, X11DRV_CLIPBOARD_ExportClipboardData }, { CF_OWNERDISPLAY, XATOM_WCF_OWNERDISPLAY, X11DRV_CLIPBOARD_ImportClipboardData, X11DRV_CLIPBOARD_ExportClipboardData }, @@ -1004,6 +1011,90 @@ static HGLOBAL create_dib_from_bitmap(HBITMAP hBmp) } +/*********************************************************************** + * uri_to_dos + * + * Converts a text/uri-list URI to DOS filename. + */ +static WCHAR* uri_to_dos(char *encodedURI) +{ + WCHAR *ret = NULL; + int i; + int j = 0; + char *uri = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(encodedURI) + 1); + if (uri == NULL) + return NULL; + for (i = 0; encodedURI[i]; ++i) + { + if (encodedURI[i] == '%') + { + if (encodedURI[i+1] && encodedURI[i+2]) + { + char buffer[3]; + int number; + buffer[0] = encodedURI[i+1]; + buffer[1] = encodedURI[i+2]; + buffer[2] = '\0'; + sscanf(buffer, "%x", &number); + uri[j++] = number; + i += 2; + } + else + { + WARN("invalid URI encoding in %s\n", debugstr_a(encodedURI)); + HeapFree(GetProcessHeap(), 0, uri); + return NULL; + } + } + else + uri[j++] = encodedURI[i]; + } + + /* Read http://www.freedesktop.org/wiki/Draganddropwarts and cry... */ + if (strncmp(uri, "file:/", 6) == 0) + { + if (uri[6] == '/') + { + if (uri[7] == '/') + { + /* file:///path/to/file (nautilus, thunar) */ + ret = wine_get_dos_file_name(&uri[7]); + } + else if (uri[7]) + { + /* file://hostname/path/to/file (X file drag spec) */ + char hostname[256]; + char *path = strchr(&uri[7], '/'); + if (path) + { + *path = '\0'; + if (strcmp(&uri[7], "localhost") == 0) + { + *path = '/'; + ret = wine_get_dos_file_name(path); + } + else if (gethostname(hostname, sizeof(hostname)) == 0) + { + if (strcmp(hostname, &uri[7]) == 0) + { + *path = '/'; + ret = wine_get_dos_file_name(path); + } + } + } + } + } + else if (uri[6]) + { + /* file:/path/to/file (konqueror) */ + ret = wine_get_dos_file_name(&uri[5]); + } + } + HeapFree(GetProcessHeap(), 0, uri); + return ret; +} + + /************************************************************************** * X11DRV_CLIPBOARD_RenderSynthesizedDIB * @@ -1477,6 +1568,92 @@ static HANDLE X11DRV_CLIPBOARD_ImportEnhMetaFile(Display *display, Window w, Ato /************************************************************************** + * X11DRV_CLIPBOARD_ImportTextUriList + * + * Import text/uri-list. + */ +static HANDLE X11DRV_CLIPBOARD_ImportTextUriList(Display *display, Window w, Atom prop) +{ + char *uriList; + unsigned long len; + char *uri; + WCHAR *path; + WCHAR *out = NULL; + int size = 0; + int capacity = 4096; + int start = 0; + int end = 0; + HANDLE handle = NULL; + + if (!X11DRV_CLIPBOARD_ReadProperty(display, w, prop, (LPBYTE*)&uriList, &len)) + return 0; + + out = HeapAlloc(GetProcessHeap(), 0, capacity * sizeof(WCHAR)); + if (out == NULL) + return 0; + + while (end < len) + { + while (end < len && uriList[end] != '\r') + ++end; + if (end < (len - 1) && uriList[end+1] != '\n') + { + WARN("URI list line doesn't end in \\r\\n\n"); + break; + } + + uri = HeapAlloc(GetProcessHeap(), 0, end - start + 1); + if (uri == NULL) + break; + lstrcpynA(uri, &uriList[start], end - start + 1); + path = uri_to_dos(uri); + TRACE("converted URI %s to DOS path %s\n", debugstr_a(uri), debugstr_w(path)); + HeapFree(GetProcessHeap(), 0, uri); + + if (path) + { + int pathSize = strlenW(path) + 1; + if (pathSize > capacity-size) + { + capacity = 2*capacity + pathSize; + out = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, out, (capacity + 1)*sizeof(WCHAR)); + if (out == NULL) + goto done; + } + memcpy(&out[size], path, pathSize * sizeof(WCHAR)); + size += pathSize; + done: + HeapFree(GetProcessHeap(), 0, path); + if (out == NULL) + break; + } + + start = end + 2; + end = start; + } + if (out && end >= len) + { + DROPFILES *dropFiles; + handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(DROPFILES) + (size + 1)*sizeof(WCHAR)); + if (handle) + { + dropFiles = (DROPFILES*) GlobalLock(handle); + dropFiles->pFiles = sizeof(DROPFILES); + dropFiles->pt.x = 0; + dropFiles->pt.y = 0; + dropFiles->fNC = 0; + dropFiles->fWide = TRUE; + out[size] = '\0'; + memcpy(((char*)dropFiles) + dropFiles->pFiles, out, (size + 1)*sizeof(WCHAR)); + GlobalUnlock(handle); + } + } + HeapFree(GetProcessHeap(), 0, out); + return handle; +} + + +/************************************************************************** * X11DRV_ImportClipbordaData * * Generic import clipboard data routine. @@ -1984,6 +2161,129 @@ end: /************************************************************************** + * X11DRV_CLIPBOARD_ExportHDROP + * + * Export CF_HDROP format to text/uri-list. + */ +static HANDLE X11DRV_CLIPBOARD_ExportHDROP(Display *display, Window requestor, Atom aTarget, + Atom rprop, LPWINE_CLIPDATA lpdata, LPDWORD lpBytes) +{ + HDROP hDrop; + UINT i; + UINT numFiles; + UINT currentSize = 32; + HGLOBAL hClipData = NULL; + char *text_uri_list = NULL; + UINT next = 0; + BOOLEAN succeeded = FALSE; + + *lpBytes = 0; + + if (!X11DRV_CLIPBOARD_RenderFormat(display, lpdata)) + { + ERR("Failed to export %04x format\n", lpdata->wFormatID); + return 0; + } + + hClipData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, currentSize); + if (hClipData == NULL) + { + ERR("out of memory\n"); + succeeded = FALSE; + goto end; + } + text_uri_list = (char*) GlobalLock(hClipData); + hDrop = (HDROP) lpdata->hData; + numFiles = DragQueryFileW(hDrop, 0xFFFFFFFF, NULL, 0); + for (i = 0; i < numFiles; i++) + { + UINT dosFilenameSize; + WCHAR *dosFilename; + succeeded = FALSE; + dosFilenameSize = 1 + DragQueryFileW(hDrop, i, NULL, 0); + dosFilename = HeapAlloc(GetProcessHeap(), 0, dosFilenameSize*sizeof(WCHAR)); + if (dosFilename) + { + char *unixFilename; + DragQueryFileW(hDrop, i, dosFilename, dosFilenameSize); + unixFilename = wine_get_unix_file_name(dosFilename); + if (unixFilename) + { + UINT unixFilenameSize; + UINT uriSize; + unixFilenameSize = lstrlenA(unixFilename); + uriSize = 8 + /* file:/// */ + 3 * (unixFilenameSize - 1) + /* "%xy" per char except first '/' */ + 2; /* \r\n */ + if ((next + uriSize) > currentSize) + { + HGLOBAL bigger; + UINT biggerSize; + GlobalUnlock(hClipData); + biggerSize = (2*currentSize > (next + uriSize)) ? 2*currentSize : (next + uriSize); + bigger = GlobalReAlloc(hClipData, biggerSize, 0); + if (bigger) + { + hClipData = bigger; + currentSize = biggerSize; + text_uri_list = GlobalLock(hClipData); + } + else + text_uri_list = NULL; + } + if ((next + uriSize) <= currentSize) + { + UINT u; + text_uri_list[next++] = 'f'; + text_uri_list[next++] = 'i'; + text_uri_list[next++] = 'l'; + text_uri_list[next++] = 'e'; + text_uri_list[next++] = ':'; + text_uri_list[next++] = '/'; + text_uri_list[next++] = '/'; + text_uri_list[next++] = '/'; + /* URL encode everything - unnecessary, but easier/lighter than linking in shlwapi, and can't hurt */ + for (u = 1; unixFilename[u]; u++) + { + static const char *hex_table = "0123456789abcdef"; + text_uri_list[next++] = '%'; + text_uri_list[next++] = hex_table[unixFilename[u] >> 4]; + text_uri_list[next++] = hex_table[unixFilename[u] & 0xf]; + } + text_uri_list[next++] = '\r'; + text_uri_list[next++] = '\n'; + succeeded = TRUE; + } + HeapFree(GetProcessHeap(), 0, unixFilename); + } + HeapFree(GetProcessHeap(), 0, dosFilename); + } + if (!succeeded) + { + ERR("out of memory\n"); + goto end; + } + } + +end: + if (succeeded) + { + GlobalUnlock(hClipData); + *lpBytes = next; + return hClipData; + } + else + { + if (text_uri_list) + GlobalUnlock(hClipData); + GlobalFree(hClipData); + *lpBytes = 0; + return 0; + } +} + + +/************************************************************************** * X11DRV_CLIPBOARD_QueryTargets */ static BOOL X11DRV_CLIPBOARD_QueryTargets(Display *display, Window w, Atom selection,