diff --git a/src/lib/platform/MSWindowsDropTarget.cpp b/src/lib/platform/MSWindowsDropTarget.cpp
new file mode 100644
index 00000000..9b1dfd58
--- /dev/null
+++ b/src/lib/platform/MSWindowsDropTarget.cpp
@@ -0,0 +1,178 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2014 Bolton Software Ltd.
+ *
+ * This package is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * found in the file COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "platform/MSWindowsDropTarget.h"
+
+#include "base/Log.h"
+#include "common/common.h"
+
+#include
+#include
+
+void getDropData(IDataObject *pDataObject);
+
+CMSWindowsDropTarget* CMSWindowsDropTarget::s_instance = NULL;
+
+CMSWindowsDropTarget::CMSWindowsDropTarget() :
+ m_refCount(1),
+ m_allowDrop(false)
+{
+ s_instance = this;
+}
+
+CMSWindowsDropTarget::~CMSWindowsDropTarget()
+{
+}
+
+CMSWindowsDropTarget&
+CMSWindowsDropTarget::instance()
+{
+ assert(s_instance != NULL);
+ return *s_instance;
+}
+
+HRESULT
+CMSWindowsDropTarget::DragEnter(IDataObject* dataObject, DWORD keyState, POINTL point, DWORD* effect)
+{
+ // check if data object contain drop
+ m_allowDrop = queryDataObject(dataObject);
+ if (m_allowDrop) {
+ getDropData(dataObject);
+ }
+
+ *effect = DROPEFFECT_NONE;
+
+ return S_OK;
+}
+
+HRESULT
+CMSWindowsDropTarget::DragOver(DWORD keyState, POINTL point, DWORD* effect)
+{
+ *effect = DROPEFFECT_NONE;
+
+ return S_OK;
+}
+
+HRESULT
+CMSWindowsDropTarget::DragLeave(void)
+{
+ return S_OK;
+}
+
+HRESULT
+CMSWindowsDropTarget::Drop(IDataObject* dataObject, DWORD keyState, POINTL point, DWORD* effect)
+{
+ *effect = DROPEFFECT_NONE;
+
+ return S_OK;
+}
+
+bool
+CMSWindowsDropTarget::queryDataObject(IDataObject* dataObject)
+{
+ // check if it supports CF_HDROP using a HGLOBAL
+ FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+
+ return dataObject->QueryGetData(&fmtetc) == S_OK ? true : false;
+}
+
+void
+CMSWindowsDropTarget::setDraggingFilename(char* const filename)
+{
+ m_dragFilename = filename;
+}
+
+std::string
+CMSWindowsDropTarget::getDraggingFilename()
+{
+ return m_dragFilename;
+}
+
+void
+CMSWindowsDropTarget::clearDraggingFilename()
+{
+ m_dragFilename.clear();
+}
+
+void
+getDropData(IDataObject* dataObject)
+{
+ // construct a FORMATETC object
+ FORMATETC fmtEtc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ STGMEDIUM stgMed;
+
+ // See if the dataobject contains any DROP stored as a HGLOBAL
+ if(dataObject->QueryGetData(&fmtEtc) == S_OK) {
+ if(dataObject->GetData(&fmtEtc, &stgMed) == S_OK) {
+ // get data here
+ PVOID data = GlobalLock(stgMed.hGlobal);
+
+ // data object global handler contains:
+ // DROPFILESfilename1 filename2 two spaces as the end
+ // TODO: get multiple filenames
+ wchar_t* wcData = (wchar_t*)((LPBYTE)data + sizeof(DROPFILES));
+
+ // convert wchar to char
+ char* filename = new char[wcslen(wcData) + 1];
+ filename[wcslen(wcData)] = '\0';
+ wcstombs(filename, wcData, wcslen(wcData));
+
+ CMSWindowsDropTarget::instance().setDraggingFilename(filename);
+
+ GlobalUnlock(stgMed.hGlobal);
+
+ // release the data using the COM API
+ ReleaseStgMedium(&stgMed);
+
+ delete[] filename;
+ }
+ }
+}
+
+HRESULT __stdcall
+CMSWindowsDropTarget::QueryInterface (REFIID iid, void ** object)
+{
+ if (iid == IID_IDropTarget || iid == IID_IUnknown) {
+ AddRef();
+ *object = this;
+ return S_OK;
+ }
+ else {
+ *object = 0;
+ return E_NOINTERFACE;
+ }
+}
+
+ULONG __stdcall
+CMSWindowsDropTarget::AddRef(void)
+{
+ return InterlockedIncrement(&m_refCount);
+}
+
+ULONG __stdcall
+CMSWindowsDropTarget::Release(void)
+{
+ LONG count = InterlockedDecrement(&m_refCount);
+
+ if (count == 0) {
+ delete this;
+ return 0;
+ }
+ else {
+ return count;
+ }
+}
diff --git a/src/lib/platform/MSWindowsDropTarget.h b/src/lib/platform/MSWindowsDropTarget.h
new file mode 100644
index 00000000..9e4f1be8
--- /dev/null
+++ b/src/lib/platform/MSWindowsDropTarget.h
@@ -0,0 +1,59 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2014 Bolton Software Ltd.
+ *
+ * This package is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * found in the file COPYING that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include
+#define WIN32_LEAN_AND_MEAN
+#include
+#include
+
+class CMSWindowsScreen;
+
+class CMSWindowsDropTarget : public IDropTarget {
+public:
+ CMSWindowsDropTarget();
+ ~CMSWindowsDropTarget();
+
+ // IUnknown implementation
+ HRESULT __stdcall QueryInterface(REFIID iid, void** object);
+ ULONG __stdcall AddRef(void);
+ ULONG __stdcall Release(void);
+
+ // IDropTarget implementation
+ HRESULT __stdcall DragEnter(IDataObject* dataObject, DWORD keyState, POINTL point, DWORD* effect);
+ HRESULT __stdcall DragOver(DWORD keyState, POINTL point, DWORD* effect);
+ HRESULT __stdcall DragLeave(void);
+ HRESULT __stdcall Drop(IDataObject* dataObject, DWORD keyState, POINTL point, DWORD* effect);
+
+ void setDraggingFilename(char* const);
+ std::string getDraggingFilename();
+ void clearDraggingFilename();
+
+ static CMSWindowsDropTarget&
+ instance();
+
+private:
+ bool queryDataObject(IDataObject* dataObject);
+
+ long m_refCount;
+ bool m_allowDrop;
+ std::string m_dragFilename;
+
+ static CMSWindowsDropTarget*
+ s_instance;
+};
diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp
index 3422fd03..9d0081bb 100644
--- a/src/lib/platform/MSWindowsScreen.cpp
+++ b/src/lib/platform/MSWindowsScreen.cpp
@@ -18,6 +18,7 @@
#include "platform/MSWindowsScreen.h"
+#include "platform/MSWindowsDropTarget.h"
#include "client/Client.h"
#include "platform/MSWindowsClipboard.h"
#include "platform/MSWindowsDesks.h"
@@ -118,7 +119,9 @@ CMSWindowsScreen::CMSWindowsScreen(
m_keyState(NULL),
m_hasMouse(GetSystemMetrics(SM_MOUSEPRESENT) != 0),
m_showingMouse(false),
- m_events(events)
+ m_events(events),
+ m_dropWindow(NULL),
+ m_dropWindowSize(20)
{
assert(s_windowInstance != NULL);
assert(s_screen == NULL);
@@ -157,6 +160,11 @@ CMSWindowsScreen::CMSWindowsScreen(
else {
LOG((CLOG_ERR "failed to get desktop path, no drop target available, error=%d", GetLastError()));
}
+
+ OleInitialize(0);
+ m_dropWindow = createDropWindow(m_class, "DropWindow");
+ m_dropTarget = new CMSWindowsDropTarget();
+ RegisterDragDrop(m_dropWindow, m_dropTarget);
}
catch (...) {
delete m_keyState;
@@ -190,6 +198,11 @@ CMSWindowsScreen::~CMSWindowsScreen()
destroyWindow(m_window);
destroyClass(m_class);
+ RevokeDragDrop(m_dropWindow);
+ m_dropTarget->Release();
+ OleUninitialize();
+ destroyWindow(m_dropWindow);
+
s_screen = NULL;
}
@@ -345,32 +358,33 @@ CMSWindowsScreen::leave()
m_isOnScreen = false;
forceShowCursor();
- if (isDraggingStarted()) {
- CString& draggingFilename = getDraggingFilename();
- size_t size = draggingFilename.size();
+ if (isDraggingStarted() && !m_isPrimary) {
+ m_sendDragThread = new CThread(
+ new TMethodJob(
+ this,
+ &CMSWindowsScreen::sendDragThread));
+ }
- if (!m_isPrimary) {
- // TODO: fake these keys properly
- fakeKeyDown(kKeyEscape, 8192, 1);
- fakeKeyUp(1);
+ return true;
+}
- fakeMouseButton(kButtonLeft, false);
+void
+CMSWindowsScreen::sendDragThread(void*)
+{
+ CString& draggingFilename = getDraggingFilename();
+ size_t size = draggingFilename.size();
- if (draggingFilename.empty() == false) {
- CClientApp& app = CClientApp::instance();
- CClient* client = app.getClientPtr();
- UInt32 fileCount = 1;
- LOG((CLOG_DEBUG "send dragging info to server: %s", draggingFilename.c_str()));
- client->draggingInfoSending(fileCount, draggingFilename, size);
- LOG((CLOG_DEBUG "send dragging file to server"));
- client->sendFileToServer(draggingFilename.c_str());
- }
- }
-
- m_draggingStarted = false;
+ if (draggingFilename.empty() == false) {
+ CClientApp& app = CClientApp::instance();
+ CClient* client = app.getClientPtr();
+ UInt32 fileCount = 1;
+ LOG((CLOG_DEBUG "send dragging info to server: %s", draggingFilename.c_str()));
+ client->draggingInfoSending(fileCount, draggingFilename, size);
+ LOG((CLOG_DEBUG "send dragging file to server"));
+ client->sendFileToServer(draggingFilename.c_str());
}
- return true;
+ m_draggingStarted = false;
}
bool
@@ -858,6 +872,29 @@ CMSWindowsScreen::createWindow(ATOM windowClass, const char* name) const
return window;
}
+HWND
+CMSWindowsScreen::createDropWindow(ATOM windowClass, const char* name) const
+{
+ HWND window = CreateWindowEx(
+ WS_EX_TOPMOST |
+ WS_EX_TRANSPARENT |
+ WS_EX_ACCEPTFILES,
+ reinterpret_cast(m_class),
+ name,
+ WS_POPUP,
+ 0, 0, m_dropWindowSize, m_dropWindowSize,
+ NULL, NULL,
+ s_windowInstance,
+ NULL);
+
+ if (window == NULL) {
+ LOG((CLOG_ERR "failed to create drop window: %d", GetLastError()));
+ throw XScreenOpenFailure();
+ }
+
+ return window;
+}
+
void
CMSWindowsScreen::destroyWindow(HWND hwnd) const
{
@@ -1801,9 +1838,48 @@ CString&
CMSWindowsScreen::getDraggingFilename()
{
if (m_draggingStarted) {
- char filename[MAX_PATH];
- m_shellEx.getDraggingFilename(filename);
- m_draggingFilename = filename;
+ m_dropTarget->clearDraggingFilename();
+ m_draggingFilename.clear();
+
+ int halfSize = m_dropWindowSize / 2;
+
+ SInt32 xPos = m_isPrimary ? m_xCursor : m_xCenter;
+ SInt32 yPos = m_isPrimary ? m_yCursor : m_yCenter;
+ xPos = (xPos - halfSize) < 0 ? 0 : xPos - halfSize;
+ yPos = (yPos - halfSize) < 0 ? 0 : yPos - halfSize;
+ SetWindowPos(
+ m_dropWindow,
+ HWND_TOPMOST,
+ xPos,
+ yPos,
+ m_dropWindowSize,
+ m_dropWindowSize,
+ SWP_SHOWWINDOW);
+
+ // TODO: fake these keys properly
+ fakeKeyDown(kKeyEscape, 8192, 1);
+ fakeKeyUp(1);
+ fakeMouseButton(kButtonLeft, false);
+
+ CString filename;
+ DOUBLE timeout = ARCH->time() + .5f;
+ while (ARCH->time() < timeout) {
+ ARCH->sleep(.05f);
+ filename = m_dropTarget->getDraggingFilename();
+ if (!filename.empty()) {
+ break;
+ }
+ }
+
+ ShowWindow(m_dropWindow, SW_HIDE);
+
+ if (!filename.empty()) {
+ m_draggingFilename = filename;
+ }
+
+ if (m_draggingFilename.empty()) {
+ LOG((CLOG_DEBUG "failed to get drag file name from OLE"));
+ }
}
return m_draggingFilename;
diff --git a/src/lib/platform/MSWindowsScreen.h b/src/lib/platform/MSWindowsScreen.h
index 4350cd7f..d3543a6c 100644
--- a/src/lib/platform/MSWindowsScreen.h
+++ b/src/lib/platform/MSWindowsScreen.h
@@ -34,6 +34,7 @@ class CMSWindowsDesks;
class CMSWindowsKeyState;
class CMSWindowsScreenSaver;
class CThread;
+class CMSWindowsDropTarget;
//! Implementation of IPlatformScreen for Microsoft Windows
class CMSWindowsScreen : public CPlatformScreen {
@@ -135,6 +136,7 @@ private:
ATOM createDeskWindowClass(bool isPrimary) const;
void destroyClass(ATOM windowClass) const;
HWND createWindow(ATOM windowClass, const char* name) const;
+ HWND createDropWindow(ATOM windowClass, const char* name) const;
void destroyWindow(HWND) const;
// convenience function to send events
@@ -216,6 +218,9 @@ private: // HACK
bool isModifierRepeat(KeyModifierMask oldState,
KeyModifierMask state, WPARAM wParam) const;
+ // send drag info and data back to server
+ void sendDragThread(void*);
+
private:
struct CHotKeyItem {
public:
@@ -325,4 +330,11 @@ private:
IEventQueue* m_events;
CString m_desktopPath;
+
+ CMSWindowsDropTarget*
+ m_dropTarget;
+ HWND m_dropWindow;
+ const int m_dropWindowSize;
+
+ CThread* m_sendDragThread;
};
diff --git a/src/lib/platform/OSXScreen.cpp b/src/lib/platform/OSXScreen.cpp
index 0f19bf28..86e86856 100644
--- a/src/lib/platform/OSXScreen.cpp
+++ b/src/lib/platform/OSXScreen.cpp
@@ -2102,6 +2102,11 @@ COSXScreen::getDraggingFilename()
CString fileList(info);
m_draggingFilename = fileList;
}
+
+ // fake a escape key down and up then left mouse button up
+ fakeKeyDown(kKeyEscape, 8192, 1);
+ fakeKeyUp(1);
+ fakeMouseButton(kButtonLeft, false);
}
return m_draggingFilename;
}
diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp
index 99c38eab..186d294d 100644
--- a/src/lib/server/Server.cpp
+++ b/src/lib/server/Server.cpp
@@ -85,7 +85,9 @@ CServer::CServer(
m_sendFileThread(NULL),
m_writeToDropDirThread(NULL),
m_ignoreFileTransfer(false),
- m_enableDragDrop(enableDragDrop)
+ m_enableDragDrop(enableDragDrop),
+ m_getDragInfoThread(NULL),
+ m_waitDragInfoThread(true)
{
// must have a primary client and it must have a canonical name
assert(m_primaryClient != NULL);
@@ -1658,6 +1660,9 @@ CServer::onMouseDown(ButtonID id)
// relay
m_active->mouseDown(id);
+
+ // reset this variable back to default value true
+ m_waitDragInfoThread = true;
}
void
@@ -1759,46 +1764,76 @@ CServer::onMouseMovePrimary(SInt32 x, SInt32 y)
// should we switch or not?
if (isSwitchOkay(newScreen, dir, x, y, xc, yc)) {
- if (m_enableDragDrop && m_screen->isDraggingStarted() && m_active != newScreen) {
- CString& dragFileList = m_screen->getDraggingFilename();
- size_t size = dragFileList.size() + 1;
- char* fileList = NULL;
- UInt32 fileCount = 1;
- if (dragFileList.empty() == false) {
- fileList = new char[size];
- memcpy(fileList, dragFileList.c_str(), size);
- fileList[size - 1] = '\0';
+ if (m_enableDragDrop
+ && m_screen->isDraggingStarted()
+ && m_active != newScreen
+ && m_waitDragInfoThread) {
+ if (m_getDragInfoThread == NULL) {
+ m_getDragInfoThread = new CThread(
+ new TMethodJob(
+ this,
+ &CServer::getDragInfoThread));
+ }
+ return false;
+ }
+
+ if (m_getDragInfoThread == NULL) {
+ // switch screen
+ switchScreen(newScreen, x, y, false);
+
+ // send drag file info to client if there is any
+ if (m_dragFileList.size() > 0) {
+ sendDragInfo(newScreen);
+ m_dragFileList.clear();
}
- // fake a escape key down and up then left mouse button up
- m_screen->keyDown(kKeyEscape, 8192, 1);
- m_screen->keyUp(kKeyEscape, 8192, 1);
- m_screen->mouseUp(kButtonLeft);
+ m_waitDragInfoThread = true;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+CServer::getDragInfoThread(void*)
+{
+ m_dragFileList.clear();
+ CString& dragFileList = m_screen->getDraggingFilename();
+ if (!dragFileList.empty()) {
+ m_dragFileList.push_back(dragFileList);
+ }
#if defined(__APPLE__)
-
- // on mac it seems that after faking a LMB up, system would signal back
- // to synergy a mouse up event, which doesn't happen on windows. as a
- // result, synergy would send dragging file to client twice. This variable
- // is used to ignore the first file sending.
- m_ignoreFileTransfer = true;
+ // on mac it seems that after faking a LMB up, system would signal back
+ // to synergy a mouse up event, which doesn't happen on windows. as a
+ // result, synergy would send dragging file to client twice. This variable
+ // is used to ignore the first file sending.
+ m_ignoreFileTransfer = true;
#endif
-
- if (dragFileList.empty() == false) {
- LOG((CLOG_DEBUG2 "sending drag information to client"));
- LOG((CLOG_DEBUG3 "dragging file list: %s", fileList));
- LOG((CLOG_DEBUG3 "dragging file list string size: %i", size));
- newScreen->draggingInfoSending(fileCount, fileList, size);
- }
- }
- // switch screen
- switchScreen(newScreen, x, y, false);
+ m_waitDragInfoThread = false;
+ m_getDragInfoThread = NULL;
+}
- return true;
- }
- else {
- return false;
+void
+CServer::sendDragInfo(CBaseClientProxy* newScreen)
+{
+ // TODO: support multiple files dragging
+ CString& dragFile = m_dragFileList.at(0);
+ size_t size = dragFile.size() + 1;
+ char* fileList = NULL;
+ UInt32 fileCount = 1;
+ if (dragFile.empty() == false) {
+ fileList = new char[size];
+ memcpy(fileList, dragFile.c_str(), size);
+ fileList[size - 1] = '\0';
+
+ LOG((CLOG_DEBUG2 "sending drag information to client"));
+ LOG((CLOG_DEBUG3 "dragging file list: %s", fileList));
+ LOG((CLOG_DEBUG3 "dragging file list string size: %i", size));
+ newScreen->draggingInfoSending(fileCount, fileList, size);
}
}
diff --git a/src/lib/server/Server.h b/src/lib/server/Server.h
index 3b897634..925bdce8 100644
--- a/src/lib/server/Server.h
+++ b/src/lib/server/Server.h
@@ -365,6 +365,12 @@ private:
// thread function for writing file to drop directory
void writeToDropDirThread(void*);
+ // thread function for getting drag filename
+ void getDragInfoThread(void*);
+
+ // send drag info to new client screen
+ void sendDragInfo(CBaseClientProxy* newScreen);
+
public:
bool m_mock;
@@ -472,4 +478,7 @@ private:
CString m_dragFileExt;
bool m_ignoreFileTransfer;
bool m_enableDragDrop;
+
+ CThread* m_getDragInfoThread;
+ bool m_waitDragInfoThread;
};