From ce1b62db14baa10b7309c821e5a4b91d232e9055 Mon Sep 17 00:00:00 2001 From: jerry Date: Fri, 30 Aug 2013 14:38:43 +0000 Subject: [PATCH] Feature to drag a file from Windows to Mac: - On Mac client main thread is used for cocoa application in order to simulate drag. - Send dragging file dir from Windows server to Mac client while dragging after switching screen. - Dragging information sending is immature now (need to support multi files dragging in the future). - Used Cocoa function to monitor dragg pasteboard. - Changed Mac client to use another thread for event queue instead of the main thread. - Change fileRecieveComplete to fileRecieveCompleted. --- src/lib/base/CEventQueue.cpp | 6 + src/lib/base/CEventQueue.h | 2 + src/lib/base/CEventTypes.cpp | 2 +- src/lib/base/CEventTypes.h | 6 +- src/lib/base/CSimpleEventQueueBuffer.h | 1 + src/lib/base/IEventQueue.h | 2 + src/lib/base/IEventQueueBuffer.h | 2 + src/lib/client/CClient.cpp | 39 ++- src/lib/client/CClient.h | 12 +- src/lib/client/CServerProxy.cpp | 18 +- src/lib/client/CServerProxy.h | 1 + src/lib/platform/CMSWindowsEventQueueBuffer.h | 2 + src/lib/platform/CMSWindowsScreen.cpp | 2 +- src/lib/platform/CMSWindowsScreen.h | 2 +- src/lib/platform/CMakeLists.txt | 14 + src/lib/platform/COSXDragSimulator.h | 32 +++ src/lib/platform/COSXDragSimulator.m | 64 +++++ src/lib/platform/COSXDragView.h | 28 ++ src/lib/platform/COSXDragView.m | 90 +++++++ src/lib/platform/COSXEventQueueBuffer.cpp | 10 +- src/lib/platform/COSXEventQueueBuffer.h | 2 + src/lib/platform/COSXPasteboardPeeker.h | 31 +++ src/lib/platform/COSXPasteboardPeeker.m | 35 +++ src/lib/platform/COSXScreen.cpp | 107 +++++++- src/lib/platform/COSXScreen.h | 24 +- src/lib/platform/CXWindowsEventQueueBuffer.h | 2 + src/lib/platform/CXWindowsScreen.cpp | 2 +- src/lib/platform/CXWindowsScreen.h | 2 +- src/lib/server/CBaseClientProxy.h | 1 + src/lib/server/CClientProxy.h | 1 + src/lib/server/CClientProxy1_0.cpp | 7 + src/lib/server/CClientProxy1_0.h | 1 + src/lib/server/CClientProxy1_5.cpp | 12 +- src/lib/server/CClientProxy1_5.h | 1 + src/lib/server/CPrimaryClient.cpp | 6 + src/lib/server/CPrimaryClient.h | 1 + src/lib/server/CServer.cpp | 27 +- src/lib/server/CServer.h | 4 +- src/lib/synergy/CClientApp.cpp | 21 ++ src/lib/synergy/CClientApp.h | 1 + src/lib/synergy/CDragInformation.cpp | 57 ++++ src/lib/synergy/CDragInformation.h | 34 +++ src/lib/synergy/CMakeLists.txt | 2 + src/lib/synergy/CPlatformScreen.h | 245 +++++++++--------- src/lib/synergy/CScreen.cpp | 24 ++ src/lib/synergy/CScreen.h | 14 + src/lib/synergy/IPlatformScreen.h | 10 +- src/lib/synergy/ISecondaryScreen.h | 2 +- src/lib/synergy/ProtocolTypes.cpp | 1 + src/lib/synergy/ProtocolTypes.h | 6 + src/test/integtests/NetworkTests.cpp | 40 +-- src/test/unittests/synergy/CMockEventQueue.h | 1 + 52 files changed, 875 insertions(+), 184 deletions(-) create mode 100644 src/lib/platform/COSXDragSimulator.h create mode 100644 src/lib/platform/COSXDragSimulator.m create mode 100644 src/lib/platform/COSXDragView.h create mode 100644 src/lib/platform/COSXDragView.m create mode 100644 src/lib/platform/COSXPasteboardPeeker.h create mode 100644 src/lib/platform/COSXPasteboardPeeker.m create mode 100644 src/lib/synergy/CDragInformation.cpp create mode 100644 src/lib/synergy/CDragInformation.h diff --git a/src/lib/base/CEventQueue.cpp b/src/lib/base/CEventQueue.cpp index edd3177a..f733d76b 100644 --- a/src/lib/base/CEventQueue.cpp +++ b/src/lib/base/CEventQueue.cpp @@ -594,3 +594,9 @@ CEventQueue::CTimer::operator<(const CTimer& t) const { return m_time < t.m_time; } + +void +CEventQueue::cacheCurrentEventQueueRef() +{ + m_buffer->cacheCurrentEventQueueRef(); +} diff --git a/src/lib/base/CEventQueue.h b/src/lib/base/CEventQueue.h index fbe5a74c..fa691d10 100644 --- a/src/lib/base/CEventQueue.h +++ b/src/lib/base/CEventQueue.h @@ -60,6 +60,8 @@ public: virtual CEvent::Type getRegisteredType(const CString& name) const; void* getSystemTarget(); + + void cacheCurrentEventQueueRef(); private: UInt32 saveEvent(const CEvent& event); diff --git a/src/lib/base/CEventTypes.cpp b/src/lib/base/CEventTypes.cpp index ab5792a1..1e72b2ab 100644 --- a/src/lib/base/CEventTypes.cpp +++ b/src/lib/base/CEventTypes.cpp @@ -177,7 +177,7 @@ REGISTER_EVENT(IScreen, clipboardGrabbed) REGISTER_EVENT(IScreen, suspend) REGISTER_EVENT(IScreen, resume) REGISTER_EVENT(IScreen, fileChunkSending) -REGISTER_EVENT(IScreen, fileRecieveComplete) +REGISTER_EVENT(IScreen, fileRecieveCompleted) // // CIpcServer diff --git a/src/lib/base/CEventTypes.h b/src/lib/base/CEventTypes.h index b815220f..a8748508 100644 --- a/src/lib/base/CEventTypes.h +++ b/src/lib/base/CEventTypes.h @@ -629,7 +629,7 @@ public: m_suspend(CEvent::kUnknown), m_resume(CEvent::kUnknown), m_fileChunkSending(CEvent::kUnknown), - m_fileRecieveComplete(CEvent::kUnknown) { } + m_fileRecieveCompleted(CEvent::kUnknown) { } //! @name accessors //@{ @@ -674,7 +674,7 @@ public: CEvent::Type fileChunkSending(); //! Completed receiving a file - CEvent::Type fileRecieveComplete(); + CEvent::Type fileRecieveCompleted(); //@} @@ -685,5 +685,5 @@ private: CEvent::Type m_suspend; CEvent::Type m_resume; CEvent::Type m_fileChunkSending; - CEvent::Type m_fileRecieveComplete; + CEvent::Type m_fileRecieveCompleted; }; diff --git a/src/lib/base/CSimpleEventQueueBuffer.h b/src/lib/base/CSimpleEventQueueBuffer.h index b3daa69c..b901f7aa 100644 --- a/src/lib/base/CSimpleEventQueueBuffer.h +++ b/src/lib/base/CSimpleEventQueueBuffer.h @@ -40,6 +40,7 @@ public: virtual CEventQueueTimer* newTimer(double duration, bool oneShot) const; virtual void deleteTimer(CEventQueueTimer*) const; + void cacheCurrentEventQueueRef() { } private: typedef std::deque CEventDeque; diff --git a/src/lib/base/IEventQueue.h b/src/lib/base/IEventQueue.h index dfdd7eb9..6f855c30 100644 --- a/src/lib/base/IEventQueue.h +++ b/src/lib/base/IEventQueue.h @@ -176,6 +176,8 @@ public: virtual CEvent::Type registerTypeOnce(CEvent::Type& type, const char* name) = 0; + + virtual void cacheCurrentEventQueueRef() = 0; //@} //! @name accessors diff --git a/src/lib/base/IEventQueueBuffer.h b/src/lib/base/IEventQueueBuffer.h index 6550d516..08a3f238 100644 --- a/src/lib/base/IEventQueueBuffer.h +++ b/src/lib/base/IEventQueueBuffer.h @@ -66,6 +66,8 @@ public: return at some future time if it's blocked waiting on an event. */ virtual bool addEvent(UInt32 dataID) = 0; + + virtual void cacheCurrentEventQueueRef() = 0; //@} //! @name accessors diff --git a/src/lib/client/CClient.cpp b/src/lib/client/CClient.cpp index fe49bc6e..4aa16e3e 100644 --- a/src/lib/client/CClient.cpp +++ b/src/lib/client/CClient.cpp @@ -85,10 +85,10 @@ CClient::CClient(IEventQueue* events, this, new TMethodEventJob(this, &CClient::handleFileChunkSending)); - m_events->adoptHandler(m_events->forIScreen().fileRecieveComplete(), + m_events->adoptHandler(m_events->forIScreen().fileRecieveCompleted(), this, new TMethodEventJob(this, - &CClient::handleFileRecieveComplete)); + &CClient::handleFileRecieveCompleted)); } CClient::~CClient() @@ -711,17 +711,19 @@ CClient::handleFileChunkSending(const CEvent& event, void*) } void -CClient::handleFileRecieveComplete(const CEvent& event, void*) +CClient::handleFileRecieveCompleted(const CEvent& event, void*) { - onFileRecieveComplete(); + onFileRecieveCompleted(); } void -CClient::onFileRecieveComplete() +CClient::onFileRecieveCompleted() { if (isReceivedFileSizeValid()) { + m_fileTransferDes = m_screen->getDropTarget(); if (!m_fileTransferDes.empty()) { std::fstream file; + m_fileTransferDes.append("/").append(m_dragFileList.at(0)); file.open(m_fileTransferDes.c_str(), std::ios::out | std::ios::binary); if (!file.is_open()) { // TODO: file open failed @@ -730,6 +732,9 @@ CClient::onFileRecieveComplete() file.write(m_receivedFileData.c_str(), m_receivedFileData.size()); file.close(); } + else { + LOG((CLOG_ERR "drop file failed: drop target is empty")); + } } } @@ -752,6 +757,30 @@ CClient::fileChunkReceived(CString data) m_receivedFileData += data; } +void +CClient::dragInfoReceived(UInt32 fileNum, CString data) +{ + CDragInformation::parseDragInfo(m_dragFileList, fileNum, data); + LOG((CLOG_DEBUG "drag information received")); + LOG((CLOG_DEBUG "total drag file number: %i", m_dragFileList.size())); + + for(int i = 0; i < m_dragFileList.size(); ++i) { + LOG((CLOG_DEBUG2 "dragging file %i name: %s", i + 1, m_dragFileList.at(i).c_str())); + } + + if (m_dragFileList.size() == 1) { + m_dragFileExt = CDragInformation::getDragFileExtension(m_dragFileList.at(0)); + } + else if (m_dragFileList.size() > 1) { + m_dragFileExt.clear(); + } + else { + return; + } + + m_screen->startDraggingFiles(m_dragFileExt); +} + bool CClient::isReceivedFileSizeValid() { diff --git a/src/lib/client/CClient.h b/src/lib/client/CClient.h index 525a6b19..989e7e22 100644 --- a/src/lib/client/CClient.h +++ b/src/lib/client/CClient.h @@ -25,6 +25,7 @@ #include "INode.h" #include "CCryptoOptions.h" #include "CEventTypes.h" +#include "CDragInformation.h" class CEventQueueTimer; class CScreen; @@ -100,7 +101,10 @@ public: //! Received a chunk of file data void fileChunkReceived(CString data); - + + //! Received drag information + void dragInfoReceived(UInt32 fileNum, CString data); + //! Create a new thread and use it to send file to Server void sendFileToServer(const char* filename); @@ -193,8 +197,8 @@ private: void handleSuspend(const CEvent& event, void*); void handleResume(const CEvent& event, void*); void handleFileChunkSending(const CEvent&, void*); - void handleFileRecieveComplete(const CEvent&, void*); - void onFileRecieveComplete(); + void handleFileRecieveCompleted(const CEvent&, void*); + void onFileRecieveCompleted(); public: bool m_mock; @@ -223,6 +227,8 @@ private: CString m_receivedFileData; CString m_fileTransferSrc; CString m_fileTransferDes; + CDragFileList m_dragFileList; + CString m_dragFileExt; }; #endif diff --git a/src/lib/client/CServerProxy.cpp b/src/lib/client/CServerProxy.cpp index ff287b3a..41ebb86a 100644 --- a/src/lib/client/CServerProxy.cpp +++ b/src/lib/client/CServerProxy.cpp @@ -301,6 +301,9 @@ CServerProxy::parseMessage(const UInt8* code) else if (memcmp(code, kMsgDFileTransfer, 4) == 0) { fileChunkReceived(); } + else if (memcmp(code, kMsgDDragInfo, 4) == 0) { + dragInfoReceived(); + } else if (memcmp(code, kMsgCClose, 4) == 0) { // server wants us to hangup @@ -865,7 +868,7 @@ void CServerProxy::fileChunkReceived() { // parse - UInt8 mark; + UInt8 mark = 0; CString content; CProtocolUtil::readf(m_stream, kMsgDFileTransfer + 4, &mark, &content); @@ -898,7 +901,7 @@ CServerProxy::fileChunkReceived() break; case kFileEnd: - m_events->addEvent(CEvent(m_events->forIScreen().fileRecieveComplete(), m_client)); + m_events->addEvent(CEvent(m_events->forIScreen().fileRecieveCompleted(), m_client)); if (CLOG->getFilter() >= kDEBUG2) { LOG((CLOG_DEBUG2 "file data transfer finished")); m_elapsedTime += m_stopwatch.getTime(); @@ -911,6 +914,17 @@ CServerProxy::fileChunkReceived() } } +void +CServerProxy::dragInfoReceived() +{ + // parse + UInt32 fileNum = 0; + CString content; + CProtocolUtil::readf(m_stream, kMsgDDragInfo + 4, &fileNum, &content); + + m_client->dragInfoReceived(fileNum, content); +} + void CServerProxy::fileChunkSending(UInt8 mark, char* data, size_t dataSize) { diff --git a/src/lib/client/CServerProxy.h b/src/lib/client/CServerProxy.h index b8976c59..962c0005 100644 --- a/src/lib/client/CServerProxy.h +++ b/src/lib/client/CServerProxy.h @@ -103,6 +103,7 @@ private: void queryInfo(); void infoAcknowledgment(); void fileChunkReceived(); + void dragInfoReceived(); private: typedef EResult (CServerProxy::*MessageParser)(const UInt8*); diff --git a/src/lib/platform/CMSWindowsEventQueueBuffer.h b/src/lib/platform/CMSWindowsEventQueueBuffer.h index 1633202a..cd6e0397 100644 --- a/src/lib/platform/CMSWindowsEventQueueBuffer.h +++ b/src/lib/platform/CMSWindowsEventQueueBuffer.h @@ -40,6 +40,8 @@ public: newTimer(double duration, bool oneShot) const; virtual void deleteTimer(CEventQueueTimer*) const; + virtual void cacheCurrentEventQueueRef() {} + private: DWORD m_thread; UINT m_userEvent; diff --git a/src/lib/platform/CMSWindowsScreen.cpp b/src/lib/platform/CMSWindowsScreen.cpp index 19e40504..d55a79cf 100644 --- a/src/lib/platform/CMSWindowsScreen.cpp +++ b/src/lib/platform/CMSWindowsScreen.cpp @@ -699,7 +699,7 @@ CMSWindowsScreen::fakeMouseButton(ButtonID id, bool press) } void -CMSWindowsScreen::fakeMouseMove(SInt32 x, SInt32 y) const +CMSWindowsScreen::fakeMouseMove(SInt32 x, SInt32 y) { m_desks->fakeMouseMove(x, y); } diff --git a/src/lib/platform/CMSWindowsScreen.h b/src/lib/platform/CMSWindowsScreen.h index 656c147c..00e4f985 100644 --- a/src/lib/platform/CMSWindowsScreen.h +++ b/src/lib/platform/CMSWindowsScreen.h @@ -88,7 +88,7 @@ public: // ISecondaryScreen overrides virtual void fakeMouseButton(ButtonID id, bool press); - virtual void fakeMouseMove(SInt32 x, SInt32 y) const; + virtual void fakeMouseMove(SInt32 x, SInt32 y); virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const; virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const; diff --git a/src/lib/platform/CMakeLists.txt b/src/lib/platform/CMakeLists.txt index f44f4d28..fef544e1 100644 --- a/src/lib/platform/CMakeLists.txt +++ b/src/lib/platform/CMakeLists.txt @@ -70,6 +70,9 @@ elseif (APPLE) COSXScreen.cpp COSXScreenSaver.cpp COSXScreenSaverUtil.m + COSXPasteboardPeeker.m + COSXDragSimulator.m + COSXDragView.m ) elseif (UNIX) @@ -110,9 +113,20 @@ if (UNIX) ) endif() +if (APPLE) + list(APPEND inc + /System/Library/Frameworks + ) +endif() + include_directories(${inc}) add_library(platform STATIC ${src}) if (UNIX) target_link_libraries(platform io net ipc synergy ${libs}) endif() + +if (APPLE) + FIND_LIBRARY(COCOA_LIBRARY Cocoa) + target_link_libraries(platform ${COCOA_LIBRARY}) +endif() diff --git a/src/lib/platform/COSXDragSimulator.h b/src/lib/platform/COSXDragSimulator.h new file mode 100644 index 00000000..5b444d15 --- /dev/null +++ b/src/lib/platform/COSXDragSimulator.h @@ -0,0 +1,32 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2013 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 "common.h" +#import + +#if defined(__cplusplus) +extern "C" { +#endif +void runCocoaApp(); +void fakeDragging(const char* str, int length, int cursorX, int cursorY); +CFStringRef getCocoaDropTarget(); + +#if defined(__cplusplus) +} +#endif diff --git a/src/lib/platform/COSXDragSimulator.m b/src/lib/platform/COSXDragSimulator.m new file mode 100644 index 00000000..ffd67c48 --- /dev/null +++ b/src/lib/platform/COSXDragSimulator.m @@ -0,0 +1,64 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2013 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. + */ + +#import "COSXDragSimulator.h" +#import "COSXDragView.h" +#import +#import +#import + +NSWindow* g_dragWindow = NULL; +COSXDragView* g_dragView = NULL; + +void +runCocoaApp() +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + NSApplication* app = [[NSApplication alloc] init]; + NSWindow* window = [[NSWindow alloc] + initWithContentRect: NSMakeRect(-100, -100, 100, 100) + styleMask: NSTitledWindowMask | NSMiniaturizableWindowMask + backing: NSBackingStoreBuffered + defer: NO]; + [window setTitle: @""]; + [window makeKeyAndOrderFront:nil]; + + COSXDragView* dragView = [[COSXDragView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; + g_dragWindow = window; + g_dragView = dragView; + [window setContentView: dragView]; + + [app run]; + + [pool release]; +} + +void +fakeDragging(const char* str, int length, int cursorX, int cursorY) +{ + dispatch_async(dispatch_get_main_queue(), ^{ + NSRect screen = [[NSScreen mainScreen] frame]; + NSLog ( @"mouseLocation: %d %d", cursorX, cursorY); + NSRect rect = NSMakeRect(cursorX - 50, screen.size.height - cursorY - 50, 100, 100); + [g_dragWindow setFrame:rect display:YES]; + [g_dragWindow makeKeyWindow]; + }); +} + +CFStringRef +getCocoaDropTarget() +{ + return [g_dragView getDropTarget]; +} diff --git a/src/lib/platform/COSXDragView.h b/src/lib/platform/COSXDragView.h new file mode 100644 index 00000000..bf58fcf6 --- /dev/null +++ b/src/lib/platform/COSXDragView.h @@ -0,0 +1,28 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2013 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 . + */ + +#import + +@interface COSXDragView : NSView +{ + NSMutableString* m_dropTarget; +} + +-(CFStringRef)getDropTarget; +-(void)clearDropTarget; + +@end diff --git a/src/lib/platform/COSXDragView.m b/src/lib/platform/COSXDragView.m new file mode 100644 index 00000000..a5ecf9b8 --- /dev/null +++ b/src/lib/platform/COSXDragView.m @@ -0,0 +1,90 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2013 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 . + */ + +#import "COSXDragView.h" + +@implementation COSXDragView + +- (id) +initWithFrame:(NSRect)frame +{ + self = [super initWithFrame:frame]; + m_dropTarget = [[NSMutableString alloc] initWithCapacity:0]; + return self; +} + +- (void) +drawRect:(NSRect)dirtyRect +{ +} + +- (BOOL) +acceptsFirstMouse:(NSEvent *)theEvent +{ + return YES; +} + +- (void) +mouseDown:(NSEvent *)theEvent +{ + NSPoint dragPosition; + NSRect imageLocation; + dragPosition = [self convertPoint:[theEvent locationInWindow] + fromView:nil]; + + dragPosition.x -= 16; + dragPosition.y -= 16; + imageLocation.origin = dragPosition; + imageLocation.size = NSMakeSize(32,32); + [self dragPromisedFilesOfTypes:[NSArray arrayWithObject:@"zip"] + fromRect:imageLocation + source:self + slideBack:NO + event:theEvent]; +} + +- (NSArray*) +namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination +{ + [m_dropTarget setString:@""]; + [m_dropTarget appendString:dropDestination.path]; + NSLog ( @"cocoa drop target: %@", m_dropTarget); + return nil; +} + +-(NSDragOperation) +draggingSourceOperationMaskForLocal:(BOOL)flag +{ + return NSDragOperationCopy; +} + +-(CFStringRef) +getDropTarget +{ + NSMutableString* string; + string = [[NSMutableString alloc] initWithCapacity:0]; + [string appendString:m_dropTarget]; + return (CFStringRef)string; +} + +-(void) +clearDropTarget +{ + [m_dropTarget setString:@""]; +} + +@end diff --git a/src/lib/platform/COSXEventQueueBuffer.cpp b/src/lib/platform/COSXEventQueueBuffer.cpp index b79db653..d6c26117 100644 --- a/src/lib/platform/COSXEventQueueBuffer.cpp +++ b/src/lib/platform/COSXEventQueueBuffer.cpp @@ -90,7 +90,7 @@ bool COSXEventQueueBuffer::addEvent(UInt32 dataID) { EventRef event; - OSStatus error = CreateEvent( + OSStatus error = CreateEvent( kCFAllocatorDefault, 'Syne', dataID, @@ -99,7 +99,7 @@ COSXEventQueueBuffer::addEvent(UInt32 dataID) &event); if (error == noErr) { - error = PostEventToQueue(GetMainEventQueue(), event, + error = PostEventToQueue(m_threadEventQueueRef, event, kEventPriorityStandard); ReleaseEvent(event); } @@ -126,3 +126,9 @@ COSXEventQueueBuffer::deleteTimer(CEventQueueTimer* timer) const { delete timer; } + +void +COSXEventQueueBuffer::cacheCurrentEventQueueRef() +{ + m_threadEventQueueRef = GetCurrentEventQueue(); +} diff --git a/src/lib/platform/COSXEventQueueBuffer.h b/src/lib/platform/COSXEventQueueBuffer.h index b47b5806..6bebbf11 100644 --- a/src/lib/platform/COSXEventQueueBuffer.h +++ b/src/lib/platform/COSXEventQueueBuffer.h @@ -38,10 +38,12 @@ public: virtual CEventQueueTimer* newTimer(double duration, bool oneShot) const; virtual void deleteTimer(CEventQueueTimer*) const; + virtual void cacheCurrentEventQueueRef(); private: EventRef m_event; IEventQueue* m_eventQueue; + EventQueueRef m_threadEventQueueRef; }; #endif diff --git a/src/lib/platform/COSXPasteboardPeeker.h b/src/lib/platform/COSXPasteboardPeeker.h new file mode 100644 index 00000000..0ff35397 --- /dev/null +++ b/src/lib/platform/COSXPasteboardPeeker.h @@ -0,0 +1,31 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2013 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 "common.h" +#import + +#if defined(__cplusplus) +extern "C" { +#endif + +CFStringRef getDraggedFileURL(); + +#if defined(__cplusplus) +} +#endif diff --git a/src/lib/platform/COSXPasteboardPeeker.m b/src/lib/platform/COSXPasteboardPeeker.m new file mode 100644 index 00000000..0ddd6466 --- /dev/null +++ b/src/lib/platform/COSXPasteboardPeeker.m @@ -0,0 +1,35 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2013 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. + */ + +#import "COSXPasteboardPeeker.h" +#import +#import +#import + +CFStringRef +getDraggedFileURL() +{ + NSString* pbName = NSDragPboard; + NSPasteboard* pboard = [NSPasteboard pasteboardWithName:pbName]; + + NSMutableString* string; + string = [[NSMutableString alloc] initWithCapacity:0]; + + NSArray* files = [pboard propertyListForType:NSFilenamesPboardType]; + for (id file in files) { + [string appendString: (NSString*)file]; + } + + return (CFStringRef)string; +} diff --git a/src/lib/platform/COSXScreen.cpp b/src/lib/platform/COSXScreen.cpp index 447f7661..c90a077f 100644 --- a/src/lib/platform/COSXScreen.cpp +++ b/src/lib/platform/COSXScreen.cpp @@ -32,6 +32,8 @@ #include "TMethodEventJob.h" #include "TMethodJob.h" #include "XArch.h" +#include "COSXDragSimulator.h" +#include "COSXPasteboardPeeker.h" #include @@ -95,7 +97,9 @@ COSXScreen::COSXScreen(IEventQueue* events, bool isPrimary, bool autoShowHideCur m_autoShowHideCursor(autoShowHideCursor), m_eventTapRLSR(nullptr), m_eventTapPort(nullptr), - m_pmRootPort(0) + m_pmRootPort(0), + m_fakeDraggingStarted(false), + m_getDropTargetThread(NULL) { try { m_displayID = CGMainDisplayID(); @@ -564,7 +568,7 @@ COSXScreen::fakeMouseButton(ButtonID id, bool press) MouseButtonState state = press ? kMouseButtonDown : kMouseButtonUp; - LOG((CLOG_DEBUG1 "faking mouse button %s", press ? "press" : "release")); + LOG((CLOG_DEBUG1 "faking mouse button id: %d press: %s", id, press ? "pressed" : "released")); MouseButtonEventMapType thisButtonMap = MouseButtonEventMap[index]; CGEventType type = thisButtonMap[state]; @@ -580,11 +584,60 @@ COSXScreen::fakeMouseButton(ButtonID id, bool press) m_lastSingleClickXCursor = m_xCursor; m_lastSingleClickYCursor = m_yCursor; } + if (!press && (id == kButtonLeft)) { + fakeKeyUp(29); + + if (m_fakeDraggingStarted) { + m_getDropTargetThread = new CThread(new TMethodJob( + this, &COSXScreen::getDropTargetThread)); + } + + m_fakeDraggingStarted = false; + } } void -COSXScreen::fakeMouseMove(SInt32 x, SInt32 y) const +COSXScreen::getDropTargetThread(void*) { + char* cstr = NULL; + + // wait for 5 secs for the drop destinaiton string to be filled. + UInt32 timeout = ARCH->time() + 5; + + while (ARCH->time() < timeout) { + CFStringRef cfstr = getCocoaDropTarget(); + cstr = CFStringRefToUTF8String(cfstr); + CFRelease(cfstr); + + if (cstr != NULL) { + break; + } + ARCH->sleep(.1f); + } + + if (cstr != NULL) { + LOG((CLOG_DEBUG "drop target: %s", cstr)); + m_dropTarget = cstr; + } + else { + LOG((CLOG_ERR "failed to get drop target")); + m_dropTarget.clear(); + } + + delete m_getDropTargetThread; +} + +void +COSXScreen::fakeMouseMove(SInt32 x, SInt32 y) +{ + if (m_fakeDraggingStarted) { + // HACK: for some reason the drag icon + // does not follow the cursor unless a key + // is pressed (except esc key) + // TODO: fake this key down properly + fakeKeyDown(kKeyControl_L, 8194, 29); + } + // synthesize event CGPoint pos; pos.x = x; @@ -671,6 +724,12 @@ COSXScreen::showCursor() } m_cursorHidden = false; + + if (m_fakeDraggingStarted) { + // TODO: use real file extension + fakeDragging("txt", 3, m_xCursor, m_yCursor); + fakeMouseButton(kButtonLeft, true); + } } void @@ -1409,8 +1468,8 @@ COSXScreen::getScrollSpeedFactor() const void COSXScreen::enableDragTimer(bool enable) { - UInt32 modifiers; - MouseTrackingResult res; + UInt32 modifiers; + MouseTrackingResult res; if (enable && m_dragTimer == NULL) { m_dragTimer = m_events->newTimer(0.01, NULL); @@ -1430,8 +1489,8 @@ void COSXScreen::handleDrag(const CEvent&, void*) { Point p; - UInt32 modifiers; - MouseTrackingResult res; + UInt32 modifiers; + MouseTrackingResult res; TrackMouseLocationWithOptions(NULL, 0, 0, &p, &modifiers, &res); @@ -1848,10 +1907,16 @@ COSXScreen::handleCGInputEvent(CGEventTapProxy proxy, case kCGEventOtherMouseUp: screen->onMouseButton(false, CGEventGetIntegerValueField(event, kCGMouseEventButtonNumber) + 1); break; - case kCGEventMouseMoved: - case kCGEventLeftMouseDragged: + case kCGEventLeftMouseDragged: { + CFStringRef dragInfo = getDraggedFileURL(); + char* info = CFStringRefToUTF8String(dragInfo); + LOG((CLOG_DEBUG "drag info: %s", info)); + CFRelease(dragInfo); + break; + } case kCGEventRightMouseDragged: case kCGEventOtherMouseDragged: + case kCGEventMouseMoved: pos = CGEventGetLocation(event); screen->onMouseMove(pos.x, pos.y); @@ -1942,3 +2007,27 @@ COSXScreen::CMouseButtonState::getFirstButtonDown() const } return -1; } + +char* +COSXScreen::CFStringRefToUTF8String(CFStringRef aString) +{ + if (aString == NULL) { + return NULL; + } + + CFIndex length = CFStringGetLength(aString); + CFIndex maxSize = CFStringGetMaximumSizeForEncoding( + length, + kCFStringEncodingUTF8); + char* buffer = (char*)malloc(maxSize); + if (CFStringGetCString(aString, buffer, maxSize, kCFStringEncodingUTF8)) { + return buffer; + } + return NULL; +} + +void +COSXScreen::fakeDraggingFiles(CString str) +{ + m_fakeDraggingStarted = true; +} diff --git a/src/lib/platform/COSXScreen.h b/src/lib/platform/COSXScreen.h index 5d8eb196..5c1438ee 100644 --- a/src/lib/platform/COSXScreen.h +++ b/src/lib/platform/COSXScreen.h @@ -79,7 +79,7 @@ public: // ISecondaryScreen overrides virtual void fakeMouseButton(ButtonID id, bool press); - virtual void fakeMouseMove(SInt32 x, SInt32 y) const; + virtual void fakeMouseMove(SInt32 x, SInt32 y); virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const; virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const; @@ -98,6 +98,10 @@ public: virtual void setSequenceNumber(UInt32); virtual bool isPrimary() const; + virtual void fakeDraggingFiles(CString str); + + const CString& getDropTarget() const { return m_dropTarget; } + protected: // IPlatformScreen overrides virtual void handleSystemEvent(const CEvent&, void*); @@ -191,10 +195,16 @@ private: CGEventType type, CGEventRef event, void* refcon); - static CGEventRef handleCGInputEventSecondary(CGEventTapProxy proxy, - CGEventType type, - CGEventRef event, - void* refcon); + static CGEventRef handleCGInputEventSecondary(CGEventTapProxy proxy, + CGEventType type, + CGEventRef event, + void* refcon); + + // convert CFString to char* + static char* CFStringRefToUTF8String(CFStringRef aString); + + void getDropTargetThread(void*); + private: struct CHotKeyItem { public: @@ -334,6 +344,10 @@ private: bool m_autoShowHideCursor; IEventQueue* m_events; + + bool m_fakeDraggingStarted; + CThread* m_getDropTargetThread; + CString m_dropTarget; }; #endif diff --git a/src/lib/platform/CXWindowsEventQueueBuffer.h b/src/lib/platform/CXWindowsEventQueueBuffer.h index 61e67c1d..656e6415 100644 --- a/src/lib/platform/CXWindowsEventQueueBuffer.h +++ b/src/lib/platform/CXWindowsEventQueueBuffer.h @@ -45,6 +45,8 @@ public: newTimer(double duration, bool oneShot) const; virtual void deleteTimer(CEventQueueTimer*) const; + virtual void cacheCurrentEventQueueRef() {} + private: void flush(); diff --git a/src/lib/platform/CXWindowsScreen.cpp b/src/lib/platform/CXWindowsScreen.cpp index 0b8d8c11..1339cf7d 100644 --- a/src/lib/platform/CXWindowsScreen.cpp +++ b/src/lib/platform/CXWindowsScreen.cpp @@ -832,7 +832,7 @@ CXWindowsScreen::fakeMouseButton(ButtonID button, bool press) } void -CXWindowsScreen::fakeMouseMove(SInt32 x, SInt32 y) const +CXWindowsScreen::fakeMouseMove(SInt32 x, SInt32 y) { if (m_xinerama && m_xtestIsXineramaUnaware) { XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y); diff --git a/src/lib/platform/CXWindowsScreen.h b/src/lib/platform/CXWindowsScreen.h index 3dd2850e..e0f677d8 100644 --- a/src/lib/platform/CXWindowsScreen.h +++ b/src/lib/platform/CXWindowsScreen.h @@ -66,7 +66,7 @@ public: // ISecondaryScreen overrides virtual void fakeMouseButton(ButtonID id, bool press); - virtual void fakeMouseMove(SInt32 x, SInt32 y) const; + virtual void fakeMouseMove(SInt32 x, SInt32 y); virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const; virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const; diff --git a/src/lib/server/CBaseClientProxy.h b/src/lib/server/CBaseClientProxy.h index 7a473e4c..985d7ba1 100644 --- a/src/lib/server/CBaseClientProxy.h +++ b/src/lib/server/CBaseClientProxy.h @@ -79,6 +79,7 @@ public: virtual void screensaver(bool activate) = 0; virtual void resetOptions() = 0; virtual void setOptions(const COptionsList& options) = 0; + virtual void draggingInfoSending(UInt32 fileCount, const char* data, size_t dataSize) = 0; virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize) = 0; virtual CString getName() const; diff --git a/src/lib/server/CClientProxy.h b/src/lib/server/CClientProxy.h index e573bc8a..ca9cbf73 100644 --- a/src/lib/server/CClientProxy.h +++ b/src/lib/server/CClientProxy.h @@ -85,6 +85,7 @@ public: virtual void resetOptions() = 0; virtual void setOptions(const COptionsList& options) = 0; virtual void cryptoIv(const UInt8* iv) = 0; + virtual void draggingInfoSending(UInt32 fileCount, const char* data, size_t dataSize) = 0; virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize) = 0; private: diff --git a/src/lib/server/CClientProxy1_0.cpp b/src/lib/server/CClientProxy1_0.cpp index 0002d3a0..531d2997 100644 --- a/src/lib/server/CClientProxy1_0.cpp +++ b/src/lib/server/CClientProxy1_0.cpp @@ -365,6 +365,13 @@ CClientProxy1_0::cryptoIv(const UInt8* iv) LOG((CLOG_DEBUG "cryptoIv not supported")); } +void +CClientProxy1_0::draggingInfoSending(UInt32 fileCount, const char* data, size_t dataSize) +{ + // ignore -- not supported in protocol 1.0 + LOG((CLOG_DEBUG "draggingInfoSending not supported")); +} + void CClientProxy1_0::fileChunkSending(UInt8 mark, char* data, size_t dataSize) { diff --git a/src/lib/server/CClientProxy1_0.h b/src/lib/server/CClientProxy1_0.h index d8f6ea7e..2935fc90 100644 --- a/src/lib/server/CClientProxy1_0.h +++ b/src/lib/server/CClientProxy1_0.h @@ -60,6 +60,7 @@ public: virtual void resetOptions(); virtual void setOptions(const COptionsList& options); virtual void cryptoIv(const UInt8* iv); + virtual void draggingInfoSending(UInt32 fileCount, const char* data, size_t dataSize); virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize); protected: diff --git a/src/lib/server/CClientProxy1_5.cpp b/src/lib/server/CClientProxy1_5.cpp index a1d07a83..f9394810 100644 --- a/src/lib/server/CClientProxy1_5.cpp +++ b/src/lib/server/CClientProxy1_5.cpp @@ -40,6 +40,14 @@ CClientProxy1_5::~CClientProxy1_5() { } +void +CClientProxy1_5::draggingInfoSending(UInt32 fileCount, const char* data, size_t dataSize) +{ + CString info(data, dataSize); + + CProtocolUtil::writef(getStream(), kMsgDDragInfo, fileCount, &info); +} + void CClientProxy1_5::fileChunkSending(UInt8 mark, char* data, size_t dataSize) { @@ -79,7 +87,7 @@ void CClientProxy1_5::fileChunkReceived() { // parse - UInt8 mark; + UInt8 mark = 0; CString content; CProtocolUtil::readf(getStream(), kMsgDFileTransfer + 4, &mark, &content); @@ -113,7 +121,7 @@ CClientProxy1_5::fileChunkReceived() break; case kFileEnd: - m_events->addEvent(CEvent(m_events->forIScreen().fileRecieveComplete(), server)); + m_events->addEvent(CEvent(m_events->forIScreen().fileRecieveCompleted(), server)); if (CLOG->getFilter() >= kDEBUG2) { LOG((CLOG_DEBUG2 "file data transfer finished")); m_elapsedTime += m_stopwatch.getTime(); diff --git a/src/lib/server/CClientProxy1_5.h b/src/lib/server/CClientProxy1_5.h index 7a26baef..35d0c909 100644 --- a/src/lib/server/CClientProxy1_5.h +++ b/src/lib/server/CClientProxy1_5.h @@ -29,6 +29,7 @@ public: CClientProxy1_5(const CString& name, synergy::IStream* adoptedStream, CServer* server, IEventQueue* events); ~CClientProxy1_5(); + virtual void draggingInfoSending(UInt32 fileCount, const char* data, size_t dataSize); virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize); virtual bool parseMessage(const UInt8* code); void fileChunkReceived(); diff --git a/src/lib/server/CPrimaryClient.cpp b/src/lib/server/CPrimaryClient.cpp index d964b348..de07bb26 100644 --- a/src/lib/server/CPrimaryClient.cpp +++ b/src/lib/server/CPrimaryClient.cpp @@ -249,6 +249,12 @@ CPrimaryClient::screensaver(bool) // ignore } +void +CPrimaryClient::draggingInfoSending(UInt32 fileCount, const char* data, size_t dataSize) +{ + // ignore +} + void CPrimaryClient::fileChunkSending(UInt8 mark, char* data, size_t dataSize) { diff --git a/src/lib/server/CPrimaryClient.h b/src/lib/server/CPrimaryClient.h index ff9b05bc..3983b476 100644 --- a/src/lib/server/CPrimaryClient.h +++ b/src/lib/server/CPrimaryClient.h @@ -144,6 +144,7 @@ public: virtual void screensaver(bool activate); virtual void resetOptions(); virtual void setOptions(const COptionsList& options); + virtual void draggingInfoSending(UInt32 fileCount, const char* data, size_t dataSize); virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize); private: diff --git a/src/lib/server/CServer.cpp b/src/lib/server/CServer.cpp index 41c144e2..61bdcff5 100644 --- a/src/lib/server/CServer.cpp +++ b/src/lib/server/CServer.cpp @@ -37,6 +37,7 @@ #include "CThread.h" #include "TMethodJob.h" #include "CFileChunker.h" +#include "CDragInformation.h" #include #include #include @@ -167,10 +168,10 @@ CServer::CServer(CConfig& config, CPrimaryClient* primaryClient, CScreen* screen this, new TMethodEventJob(this, &CServer::handleFileChunkSendingEvent)); - m_events->adoptHandler(m_events->forIScreen().fileRecieveComplete(), + m_events->adoptHandler(m_events->forIScreen().fileRecieveCompleted(), this, new TMethodEventJob(this, - &CServer::handleFileRecieveCompleteEvent)); + &CServer::handleFileRecieveCompletedEvent)); // add connection addClient(m_primaryClient); @@ -1472,9 +1473,9 @@ CServer::handleFileChunkSendingEvent(const CEvent& event, void*) } void -CServer::handleFileRecieveCompleteEvent(const CEvent& event, void*) +CServer::handleFileRecieveCompletedEvent(const CEvent& event, void*) { - onFileRecieveComplete(); + onFileRecieveCompleted(); } void @@ -1737,8 +1738,24 @@ CServer::onMouseMovePrimary(SInt32 x, SInt32 y) // should we switch or not? if (isSwitchOkay(newScreen, dir, x, y, xc, yc)) { + if (m_screen->getDraggingStarted() && m_active != newScreen) { + CString& dragFileList = m_screen->getDraggingFileDir(); + size_t size = dragFileList.size() + 1; + char* fileList = new char[size]; + memcpy(fileList, dragFileList.c_str(), size); + fileList[size - 1] = '\0'; + UInt32 fileCount = 1; + + 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); + m_screen->setDraggingStarted(false); + } + // switch screen switchScreen(newScreen, x, y, false); + return true; } else { @@ -1930,7 +1947,7 @@ CServer::onFileChunkSending(const void* data) } void -CServer::onFileRecieveComplete() +CServer::onFileRecieveCompleted() { if (isReceivedFileSizeValid()) { if (!m_fileTransferDes.empty()) { diff --git a/src/lib/server/CServer.h b/src/lib/server/CServer.h index ccc9a098..04d93867 100644 --- a/src/lib/server/CServer.h +++ b/src/lib/server/CServer.h @@ -310,7 +310,7 @@ private: void handleFakeInputBeginEvent(const CEvent&, void*); void handleFakeInputEndEvent(const CEvent&, void*); void handleFileChunkSendingEvent(const CEvent&, void*); - void handleFileRecieveCompleteEvent(const CEvent&, void*); + void handleFileRecieveCompletedEvent(const CEvent&, void*); // event processing void onClipboardChanged(CBaseClientProxy* sender, @@ -327,7 +327,7 @@ private: void onMouseMoveSecondary(SInt32 dx, SInt32 dy); void onMouseWheel(SInt32 xDelta, SInt32 yDelta); void onFileChunkSending(const void* data); - void onFileRecieveComplete(); + void onFileRecieveCompleted(); // add client to list and attach event handlers for client bool addClient(CBaseClientProxy*); diff --git a/src/lib/synergy/CClientApp.cpp b/src/lib/synergy/CClientApp.cpp index bea5c962..80b1764d 100644 --- a/src/lib/synergy/CClientApp.cpp +++ b/src/lib/synergy/CClientApp.cpp @@ -50,6 +50,10 @@ #include "COSXScreen.h" #endif +#if defined(__APPLE__) +#include "COSXDragSimulator.h" +#endif + #include #include @@ -531,7 +535,17 @@ CClientApp::mainLoop() // later. the timer installed by startClient() will take care of // that. DAEMON_RUNNING(true); + +#if defined(__APPLE__) + CThread thread( + new TMethodJob( + this, &CClientApp::runEventsLoop, + NULL)); + runCocoaApp(); +#else m_events->loop(); +#endif + DAEMON_RUNNING(false); // close down @@ -612,3 +626,10 @@ CClientApp::startNode() m_bye(kExitFailed); } } + +void +CClientApp::runEventsLoop(void*) +{ + m_events->cacheCurrentEventQueueRef(); + m_events->loop(); +} diff --git a/src/lib/synergy/CClientApp.h b/src/lib/synergy/CClientApp.h index 1a5846b8..39f839ec 100644 --- a/src/lib/synergy/CClientApp.h +++ b/src/lib/synergy/CClientApp.h @@ -85,6 +85,7 @@ public: private: virtual bool parseArg(const int& argc, const char* const* argv, int& i); + void runEventsLoop(void*); private: CClient* s_client; diff --git a/src/lib/synergy/CDragInformation.cpp b/src/lib/synergy/CDragInformation.cpp new file mode 100644 index 00000000..7807c77d --- /dev/null +++ b/src/lib/synergy/CDragInformation.cpp @@ -0,0 +1,57 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2013 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 "CDragInformation.h" +#include "CLog.h" + +using namespace std; + +void +CDragInformation::parseDragInfo(CDragFileList& dragFileList, UInt32 fileNum, CString data) +{ + size_t startPos = 0; + size_t findResult1 = 0; + size_t findResult2 = 0; + dragFileList.clear(); + while (fileNum) { + findResult1 = data.find('\0', startPos); + findResult2 = data.find_last_of("\\", findResult1); + + if (findResult1 == startPos) { + //TODO: file number does not match, something goes wrong + break; + } + if (findResult1 - findResult2 > 1) { + dragFileList.push_back(data.substr(findResult2 + 1, findResult1 - findResult2)); + } + startPos = findResult1 + 1; + --fileNum; + } +} + +CString +CDragInformation::getDragFileExtension(CString fileName) +{ + size_t findResult = -1; + findResult = fileName.find_last_of(".", fileName.size()); + if (findResult != -1) { + return fileName.substr(findResult + 1, fileName.size() - findResult - 1); + } + else { + return ""; + } +} diff --git a/src/lib/synergy/CDragInformation.h b/src/lib/synergy/CDragInformation.h new file mode 100644 index 00000000..e8dd03fb --- /dev/null +++ b/src/lib/synergy/CDragInformation.h @@ -0,0 +1,34 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2013 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 "stdvector.h" +#include "CString.h" +#include "BasicTypes.h" + +typedef std::vector CDragFileList; + +class CDragInformation { +public: + CDragInformation() { } + + ~CDragInformation() { } + + static void parseDragInfo(CDragFileList& dragFileList, UInt32 fileNum, CString data); + static CString getDragFileExtension(CString fileName); +}; diff --git a/src/lib/synergy/CMakeLists.txt b/src/lib/synergy/CMakeLists.txt index 0dac4e72..58ac9fc6 100644 --- a/src/lib/synergy/CMakeLists.txt +++ b/src/lib/synergy/CMakeLists.txt @@ -48,6 +48,7 @@ set(inc CArgsBase.h IAppUtil.h CFileChunker.h + CDragInformation.h ) set(src @@ -75,6 +76,7 @@ set(src CAppUtil.cpp CArgsBase.cpp CFileChunker.cpp + CDragInformation.cpp ) if (WIN32) diff --git a/src/lib/synergy/CPlatformScreen.h b/src/lib/synergy/CPlatformScreen.h index ccbbf34f..b58f2223 100644 --- a/src/lib/synergy/CPlatformScreen.h +++ b/src/lib/synergy/CPlatformScreen.h @@ -1,119 +1,126 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Bolton Software Ltd. - * Copyright (C) 2004 Chris Schoeneman - * - * 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 . - */ - -#ifndef CPLATFORMSCREEN_H -#define CPLATFORMSCREEN_H - -#include "IPlatformScreen.h" - -//! Base screen implementation -/*! -This screen implementation is the superclass of all other screen -implementations. It implements a handful of methods and requires -subclasses to implement the rest. -*/ -class CPlatformScreen : public IPlatformScreen { -public: - CPlatformScreen(IEventQueue* events); - virtual ~CPlatformScreen(); - - // IScreen overrides - virtual void* getEventTarget() const = 0; - virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0; - virtual void getShape(SInt32& x, SInt32& y, - SInt32& width, SInt32& height) const = 0; - virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; - - // IPrimaryScreen overrides - virtual void reconfigure(UInt32 activeSides) = 0; - virtual void warpCursor(SInt32 x, SInt32 y) = 0; - virtual UInt32 registerHotKey(KeyID key, - KeyModifierMask mask) = 0; - virtual void unregisterHotKey(UInt32 id) = 0; - virtual void fakeInputBegin() = 0; - virtual void fakeInputEnd() = 0; - virtual SInt32 getJumpZoneSize() const = 0; - virtual bool isAnyMouseButtonDown(UInt32& buttonID) const = 0; - virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; - - // ISecondaryScreen overrides - virtual void fakeMouseButton(ButtonID id, bool press) = 0; - virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; - virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const = 0; - virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const = 0; - - // IKeyState overrides - virtual void updateKeyMap(); - virtual void updateKeyState(); - virtual void setHalfDuplexMask(KeyModifierMask); - virtual void fakeKeyDown(KeyID id, KeyModifierMask mask, - KeyButton button); - virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask, - SInt32 count, KeyButton button); - virtual bool fakeKeyUp(KeyButton button); - virtual void fakeAllKeysUp(); - virtual bool fakeCtrlAltDel(); - virtual bool isKeyDown(KeyButton) const; - virtual KeyModifierMask - getActiveModifiers() const; - virtual KeyModifierMask - pollActiveModifiers() const; - virtual SInt32 pollActiveGroup() const; - virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const; - - virtual CString& getDraggingFileDir() { return m_draggingFileDir; } - - // IPlatformScreen overrides - virtual void enable() = 0; - virtual void disable() = 0; - virtual void enter() = 0; - virtual bool leave() = 0; - virtual bool setClipboard(ClipboardID, const IClipboard*) = 0; - virtual void checkClipboards() = 0; - virtual void openScreensaver(bool notify) = 0; - virtual void closeScreensaver() = 0; - virtual void screensaver(bool activate) = 0; - virtual void resetOptions() = 0; - virtual void setOptions(const COptionsList& options) = 0; - virtual void setSequenceNumber(UInt32) = 0; - virtual bool isPrimary() const = 0; - -protected: - //! Update mouse buttons - /*! - Subclasses must implement this method to update their internal mouse - button mapping and, if desired, state tracking. - */ - virtual void updateButtons() = 0; - - //! Get the key state - /*! - Subclasses must implement this method to return the platform specific - key state object that each subclass must have. - */ - virtual IKeyState* getKeyState() const = 0; - - // IPlatformScreen overrides - virtual void handleSystemEvent(const CEvent& event, void*) = 0; - -protected: - CString m_draggingFileDir; - bool m_draggingStarted; -}; - -#endif +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2012 Bolton Software Ltd. + * Copyright (C) 2004 Chris Schoeneman + * + * 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 . + */ + +#ifndef CPLATFORMSCREEN_H +#define CPLATFORMSCREEN_H + +#include "IPlatformScreen.h" +#include + +//! Base screen implementation +/*! +This screen implementation is the superclass of all other screen +implementations. It implements a handful of methods and requires +subclasses to implement the rest. +*/ +class CPlatformScreen : public IPlatformScreen { +public: + CPlatformScreen(IEventQueue* events); + virtual ~CPlatformScreen(); + + // IScreen overrides + virtual void* getEventTarget() const = 0; + virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const = 0; + virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; + + // IPrimaryScreen overrides + virtual void reconfigure(UInt32 activeSides) = 0; + virtual void warpCursor(SInt32 x, SInt32 y) = 0; + virtual UInt32 registerHotKey(KeyID key, + KeyModifierMask mask) = 0; + virtual void unregisterHotKey(UInt32 id) = 0; + virtual void fakeInputBegin() = 0; + virtual void fakeInputEnd() = 0; + virtual SInt32 getJumpZoneSize() const = 0; + virtual bool isAnyMouseButtonDown(UInt32& buttonID) const = 0; + virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; + + // ISecondaryScreen overrides + virtual void fakeMouseButton(ButtonID id, bool press) = 0; + virtual void fakeMouseMove(SInt32 x, SInt32 y) = 0; + virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const = 0; + virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const = 0; + + // IKeyState overrides + virtual void updateKeyMap(); + virtual void updateKeyState(); + virtual void setHalfDuplexMask(KeyModifierMask); + virtual void fakeKeyDown(KeyID id, KeyModifierMask mask, + KeyButton button); + virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask, + SInt32 count, KeyButton button); + virtual bool fakeKeyUp(KeyButton button); + virtual void fakeAllKeysUp(); + virtual bool fakeCtrlAltDel(); + virtual bool isKeyDown(KeyButton) const; + virtual KeyModifierMask + getActiveModifiers() const; + virtual KeyModifierMask + pollActiveModifiers() const; + virtual SInt32 pollActiveGroup() const; + virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const; + + virtual void setDraggingStarted(bool started) { m_draggingStarted = started; } + virtual bool getDraggingStarted() { return m_draggingStarted; } + virtual CString& getDraggingFileDir() { return m_draggingFileDir; } + + // IPlatformScreen overrides + virtual void enable() = 0; + virtual void disable() = 0; + virtual void enter() = 0; + virtual bool leave() = 0; + virtual bool setClipboard(ClipboardID, const IClipboard*) = 0; + virtual void checkClipboards() = 0; + virtual void openScreensaver(bool notify) = 0; + virtual void closeScreensaver() = 0; + virtual void screensaver(bool activate) = 0; + virtual void resetOptions() = 0; + virtual void setOptions(const COptionsList& options) = 0; + virtual void setSequenceNumber(UInt32) = 0; + virtual bool isPrimary() const = 0; + + virtual void fakeDraggingFiles(CString str) { throw std::runtime_error("fakeDraggingFiles not implemented"); } + virtual const CString& + getDropTarget() const { throw std::runtime_error("getDropTarget not implemented"); } + +protected: + //! Update mouse buttons + /*! + Subclasses must implement this method to update their internal mouse + button mapping and, if desired, state tracking. + */ + virtual void updateButtons() = 0; + + //! Get the key state + /*! + Subclasses must implement this method to return the platform specific + key state object that each subclass must have. + */ + virtual IKeyState* getKeyState() const = 0; + + // IPlatformScreen overrides + virtual void handleSystemEvent(const CEvent& event, void*) = 0; + +protected: + CString m_draggingFileDir; + bool m_draggingStarted; +}; + +#endif diff --git a/src/lib/synergy/CScreen.cpp b/src/lib/synergy/CScreen.cpp index bf07ae67..cebad5e3 100644 --- a/src/lib/synergy/CScreen.cpp +++ b/src/lib/synergy/CScreen.cpp @@ -415,12 +415,36 @@ CScreen::pollActiveModifiers() const return m_screen->pollActiveModifiers(); } +bool +CScreen::getDraggingStarted() const +{ + return m_screen->getDraggingStarted(); +} + +void +CScreen::setDraggingStarted(bool started) +{ + m_screen->setDraggingStarted(started); +} + +void +CScreen::startDraggingFiles(CString str) +{ + m_screen->fakeDraggingFiles(str); +} + CString& CScreen::getDraggingFileDir() const { return m_screen->getDraggingFileDir(); } +const CString& +CScreen::getDropTarget() const +{ + return m_screen->getDropTarget(); +} + void* CScreen::getEventTarget() const { diff --git a/src/lib/synergy/CScreen.h b/src/lib/synergy/CScreen.h index e4e38100..92bfe3dc 100644 --- a/src/lib/synergy/CScreen.h +++ b/src/lib/synergy/CScreen.h @@ -219,6 +219,12 @@ public: */ void fakeInputEnd(); + //! Change dragging status + void setDraggingStarted(bool started); + + //! Fake a files dragging operation + void startDraggingFiles(CString str); + //@} //! @name accessors //@{ @@ -267,10 +273,18 @@ public: */ KeyModifierMask pollActiveModifiers() const; + //! Check if dragging has started. + + bool getDraggingStarted() const; + //! Get dragging file's directory. CString& getDraggingFileDir() const; + //! Get drop target directory. + + const CString& getDropTarget() const; + //@} // IScreen overrides diff --git a/src/lib/synergy/IPlatformScreen.h b/src/lib/synergy/IPlatformScreen.h index b21fb077..73c74c79 100644 --- a/src/lib/synergy/IPlatformScreen.h +++ b/src/lib/synergy/IPlatformScreen.h @@ -130,6 +130,9 @@ public: */ virtual void setSequenceNumber(UInt32) = 0; + //! Change dragging status + virtual void setDraggingStarted(bool started) = 0; + //@} //! @name accessors //@{ @@ -162,7 +165,7 @@ public: // ISecondaryScreen overrides virtual void fakeMouseButton(ButtonID id, bool press) = 0; - virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; + virtual void fakeMouseMove(SInt32 x, SInt32 y) = 0; virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const = 0; virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const = 0; @@ -186,7 +189,12 @@ public: virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const = 0; virtual CString& getDraggingFileDir() = 0; + virtual bool getDraggingStarted() = 0; + virtual void fakeDraggingFiles(CString str) = 0; + virtual const CString& + getDropTarget() const = 0; + protected: //! Handle system event /*! diff --git a/src/lib/synergy/ISecondaryScreen.h b/src/lib/synergy/ISecondaryScreen.h index 248e0710..28016de9 100644 --- a/src/lib/synergy/ISecondaryScreen.h +++ b/src/lib/synergy/ISecondaryScreen.h @@ -44,7 +44,7 @@ public: /*! Synthesize a mouse move to the absolute coordinates \c x,y. */ - virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; + virtual void fakeMouseMove(SInt32 x, SInt32 y) = 0; //! Fake mouse move /*! diff --git a/src/lib/synergy/ProtocolTypes.cpp b/src/lib/synergy/ProtocolTypes.cpp index 58d45beb..264959be 100644 --- a/src/lib/synergy/ProtocolTypes.cpp +++ b/src/lib/synergy/ProtocolTypes.cpp @@ -46,6 +46,7 @@ const char* kMsgDInfo = "DINF%2i%2i%2i%2i%2i%2i%2i"; const char* kMsgDSetOptions = "DSOP%4I"; const char* kMsgDCryptoIv = "DCIV%s"; const char* kMsgDFileTransfer = "DFTR%1i%s"; +const char* kMsgDDragInfo = "DDRG%2i%s"; const char* kMsgQInfo = "QINF"; const char* kMsgEIncompatible = "EICV%2i%2i"; const char* kMsgEBusy = "EBSY"; diff --git a/src/lib/synergy/ProtocolTypes.h b/src/lib/synergy/ProtocolTypes.h index 23ea00ef..fec3b65b 100644 --- a/src/lib/synergy/ProtocolTypes.h +++ b/src/lib/synergy/ProtocolTypes.h @@ -263,6 +263,12 @@ extern const char* kMsgDCryptoIv; // 2 means the file transfer is finished. extern const char* kMsgDFileTransfer; +// drag infomation: primary <-> secondary +// transfer drag infomation. The first 2 bytes are used for storing +// the number of dragging objects. Then the following string consists +// of each object's directory. +extern const char* kMsgDDragInfo; + // // query codes // diff --git a/src/test/integtests/NetworkTests.cpp b/src/test/integtests/NetworkTests.cpp index c5cb1e31..46bca604 100644 --- a/src/test/integtests/NetworkTests.cpp +++ b/src/test/integtests/NetworkTests.cpp @@ -92,16 +92,16 @@ public: void sendMockData(void* eventTarget); void sendToClient_mockData_handleClientConnected(const CEvent&, void* vlistener); - void sendToClient_mockData_fileRecieveComplete(const CEvent&, void*); + void sendToClient_mockData_fileRecieveCompleted(const CEvent&, void*); void sendToClient_mockFile_handleClientConnected(const CEvent&, void* vlistener); - void sendToClient_mockFile_fileRecieveComplete(const CEvent& event, void*); + void sendToClient_mockFile_fileRecieveCompleted(const CEvent& event, void*); void sendToServer_mockData_handleClientConnected(const CEvent&, void* vlistener); - void sendToServer_mockData_fileRecieveComplete(const CEvent& event, void*); + void sendToServer_mockData_fileRecieveCompleted(const CEvent& event, void*); void sendToServer_mockFile_handleClientConnected(const CEvent&, void* vlistener); - void sendToServer_mockFile_fileRecieveComplete(const CEvent& event, void*); + void sendToServer_mockFile_fileRecieveCompleted(const CEvent& event, void*); public: CTestEventQueue m_events; @@ -159,16 +159,16 @@ TEST_F(NetworkTests, sendToClient_mockData) CClient client(&m_events, "stub", serverAddress, clientSocketFactory, NULL, &clientScreen, cryptoOptions); m_events.adoptHandler( - m_events.forIScreen().fileRecieveComplete(), &client, + m_events.forIScreen().fileRecieveCompleted(), &client, new TMethodEventJob( - this, &NetworkTests::sendToClient_mockData_fileRecieveComplete)); + this, &NetworkTests::sendToClient_mockData_fileRecieveCompleted)); client.connect(); m_events.initQuitTimeout(10); m_events.loop(); m_events.removeHandler(m_events.forCClientListener().connected(), &listener); - m_events.removeHandler(m_events.forIScreen().fileRecieveComplete(), &client); + m_events.removeHandler(m_events.forIScreen().fileRecieveCompleted(), &client); m_events.cleanupQuitTimeout(); } @@ -220,16 +220,16 @@ TEST_F(NetworkTests, sendToClient_mockFile) CClient client(&m_events, "stub", serverAddress, clientSocketFactory, NULL, &clientScreen, cryptoOptions); m_events.adoptHandler( - m_events.forIScreen().fileRecieveComplete(), &client, + m_events.forIScreen().fileRecieveCompleted(), &client, new TMethodEventJob( - this, &NetworkTests::sendToClient_mockFile_fileRecieveComplete)); + this, &NetworkTests::sendToClient_mockFile_fileRecieveCompleted)); client.connect(); m_events.initQuitTimeout(10); m_events.loop(); m_events.removeHandler(m_events.forCClientListener().connected(), &listener); - m_events.removeHandler(m_events.forIScreen().fileRecieveComplete(), &client); + m_events.removeHandler(m_events.forIScreen().fileRecieveCompleted(), &client); m_events.cleanupQuitTimeout(); } @@ -281,16 +281,16 @@ TEST_F(NetworkTests, sendToServer_mockData) this, &NetworkTests::sendToServer_mockData_handleClientConnected, &client)); m_events.adoptHandler( - m_events.forIScreen().fileRecieveComplete(), &server, + m_events.forIScreen().fileRecieveCompleted(), &server, new TMethodEventJob( - this, &NetworkTests::sendToServer_mockData_fileRecieveComplete)); + this, &NetworkTests::sendToServer_mockData_fileRecieveCompleted)); client.connect(); m_events.initQuitTimeout(10); m_events.loop(); m_events.removeHandler(m_events.forCClientListener().connected(), &listener); - m_events.removeHandler(m_events.forIScreen().fileRecieveComplete(), &server); + m_events.removeHandler(m_events.forIScreen().fileRecieveCompleted(), &server); m_events.cleanupQuitTimeout(); } @@ -342,16 +342,16 @@ TEST_F(NetworkTests, sendToServer_mockFile) this, &NetworkTests::sendToServer_mockFile_handleClientConnected, &client)); m_events.adoptHandler( - m_events.forIScreen().fileRecieveComplete(), &server, + m_events.forIScreen().fileRecieveCompleted(), &server, new TMethodEventJob( - this, &NetworkTests::sendToServer_mockFile_fileRecieveComplete)); + this, &NetworkTests::sendToServer_mockFile_fileRecieveCompleted)); client.connect(); m_events.initQuitTimeout(10); m_events.loop(); m_events.removeHandler(m_events.forCClientListener().connected(), &listener); - m_events.removeHandler(m_events.forIScreen().fileRecieveComplete(), &server); + m_events.removeHandler(m_events.forIScreen().fileRecieveCompleted(), &server); m_events.cleanupQuitTimeout(); } @@ -374,7 +374,7 @@ NetworkTests::sendToClient_mockData_handleClientConnected(const CEvent&, void* v } void -NetworkTests::sendToClient_mockData_fileRecieveComplete(const CEvent& event, void*) +NetworkTests::sendToClient_mockData_fileRecieveCompleted(const CEvent& event, void*) { CClient* client = reinterpret_cast(event.getTarget()); EXPECT_TRUE(client->isReceivedFileSizeValid()); @@ -401,7 +401,7 @@ NetworkTests::sendToClient_mockFile_handleClientConnected(const CEvent&, void* v } void -NetworkTests::sendToClient_mockFile_fileRecieveComplete(const CEvent& event, void*) +NetworkTests::sendToClient_mockFile_fileRecieveCompleted(const CEvent& event, void*) { CClient* client = reinterpret_cast(event.getTarget()); EXPECT_TRUE(client->isReceivedFileSizeValid()); @@ -417,7 +417,7 @@ NetworkTests::sendToServer_mockData_handleClientConnected(const CEvent&, void* v } void -NetworkTests::sendToServer_mockData_fileRecieveComplete(const CEvent& event, void*) +NetworkTests::sendToServer_mockData_fileRecieveCompleted(const CEvent& event, void*) { CServer* server = reinterpret_cast(event.getTarget()); EXPECT_TRUE(server->isReceivedFileSizeValid()); @@ -433,7 +433,7 @@ NetworkTests::sendToServer_mockFile_handleClientConnected(const CEvent&, void* v } void -NetworkTests::sendToServer_mockFile_fileRecieveComplete(const CEvent& event, void*) +NetworkTests::sendToServer_mockFile_fileRecieveCompleted(const CEvent& event, void*) { CServer* server = reinterpret_cast(event.getTarget()); EXPECT_TRUE(server->isReceivedFileSizeValid()); diff --git a/src/test/unittests/synergy/CMockEventQueue.h b/src/test/unittests/synergy/CMockEventQueue.h index e7e8e87a..bc8f28e2 100644 --- a/src/test/unittests/synergy/CMockEventQueue.h +++ b/src/test/unittests/synergy/CMockEventQueue.h @@ -61,6 +61,7 @@ public: MOCK_METHOD0(forIKeyState, IKeyStateEvents&()); MOCK_METHOD0(forIPrimaryScreen, IPrimaryScreenEvents&()); MOCK_METHOD0(forIScreen, IScreenEvents&()); + MOCK_METHOD0(cacheCurrentEventQueueRef, void()); }; #endif