diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /dtrans/source/win32/clipb | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dtrans/source/win32/clipb')
-rw-r--r-- | dtrans/source/win32/clipb/APNDataObject.hxx | 72 | ||||
-rw-r--r-- | dtrans/source/win32/clipb/MtaOleClipb.cxx | 736 | ||||
-rw-r--r-- | dtrans/source/win32/clipb/MtaOleClipb.hxx | 117 | ||||
-rw-r--r-- | dtrans/source/win32/clipb/WinClipbImpl.cxx | 210 | ||||
-rw-r--r-- | dtrans/source/win32/clipb/WinClipbImpl.hxx | 106 | ||||
-rw-r--r-- | dtrans/source/win32/clipb/WinClipboard.cxx | 239 | ||||
-rw-r--r-- | dtrans/source/win32/clipb/WinClipboard.hxx | 123 | ||||
-rw-r--r-- | dtrans/source/win32/clipb/wcbentry.cxx | 82 |
8 files changed, 1685 insertions, 0 deletions
diff --git a/dtrans/source/win32/clipb/APNDataObject.hxx b/dtrans/source/win32/clipb/APNDataObject.hxx new file mode 100644 index 000000000..c48d14117 --- /dev/null +++ b/dtrans/source/win32/clipb/APNDataObject.hxx @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_DTRANS_SOURCE_WIN32_CLIPB_APNDATAOBJECT_HXX +#define INCLUDED_DTRANS_SOURCE_WIN32_CLIPB_APNDATAOBJECT_HXX + +/* + an APartment Neutral dataobject wrapper; this wrapper of an IDataObject + pointer can be used from any apartment without RPC_E_WRONG_THREAD + which normally occurs if an apartment tries to use an interface + pointer of another apartment; we use containment to hold the original + DataObject +*/ +class CAPNDataObject : public IDataObject +{ +public: + CAPNDataObject( IDataObjectPtr rIDataObject ); + ~CAPNDataObject( ); + + //IUnknown interface methods + + STDMETHODIMP QueryInterface(REFIID iid, LPVOID* ppvObject); + STDMETHODIMP_( ULONG ) AddRef( ); + STDMETHODIMP_( ULONG ) Release( ); + + // IDataObject interface methods + + STDMETHODIMP GetData( LPFORMATETC pFormatetc, LPSTGMEDIUM pmedium ); + STDMETHODIMP GetDataHere( LPFORMATETC pFormatetc, LPSTGMEDIUM pmedium ); + STDMETHODIMP QueryGetData( LPFORMATETC pFormatetc ); + STDMETHODIMP GetCanonicalFormatEtc( LPFORMATETC pFormatectIn, LPFORMATETC pFormatetcOut ); + STDMETHODIMP SetData( LPFORMATETC pFormatetc, LPSTGMEDIUM pmedium, BOOL fRelease ); + STDMETHODIMP EnumFormatEtc( DWORD dwDirection, IEnumFORMATETC** ppenumFormatetc ); + STDMETHODIMP DAdvise( LPFORMATETC pFormatetc, DWORD advf, LPADVISESINK pAdvSink, DWORD* pdwConnection ); + STDMETHODIMP DUnadvise( DWORD dwConnection ); + STDMETHODIMP EnumDAdvise( LPENUMSTATDATA* ppenumAdvise ); + + operator IDataObject*( ); + +private: + HRESULT MarshalIDataObjectIntoCurrentApartment( IDataObject** ppIDataObj ); + +private: + IDataObjectPtr m_rIDataObjectOrg; + HGLOBAL m_hGlobal; + LONG m_nRefCnt; + +// prevent copy and assignment +private: + CAPNDataObject( const CAPNDataObject& theOther ); + CAPNDataObject& operator=( const CAPNDataObject& theOther ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dtrans/source/win32/clipb/MtaOleClipb.cxx b/dtrans/source/win32/clipb/MtaOleClipb.cxx new file mode 100644 index 000000000..70fce3bc6 --- /dev/null +++ b/dtrans/source/win32/clipb/MtaOleClipb.cxx @@ -0,0 +1,736 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +/* + MtaOleClipb.cxx - documentation + + This class setup a single threaded apartment (sta) thread to deal with + the ole clipboard, which runs only in an sta thread. + The consequence is that callback from the ole clipboard are in the + context of this sta thread. In the soffice applications this may lead + to problems because they all use the one and only mutex called + SolarMutex. + In order to transfer clipboard requests to our sta thread we use a + hidden window and forward these requests via window messages. +*/ + +#include <osl/diagnose.h> +#include <sal/log.hxx> + +#include "MtaOleClipb.hxx" +#include <osl/thread.h> + +#include <wchar.h> +#include <process.h> + +#include <systools/win32/comtools.hxx> + +// namespace directives + +using osl::MutexGuard; +using osl::ClearableMutexGuard; + +namespace /* private */ +{ + wchar_t CLIPSRV_DLL_NAME[] = L"sysdtrans.dll"; + wchar_t g_szWndClsName[] = L"MtaOleReqWnd###"; + + // messages constants + + const sal_uInt32 MSG_SETCLIPBOARD = WM_USER + 0x0001; + const sal_uInt32 MSG_GETCLIPBOARD = WM_USER + 0x0002; + const sal_uInt32 MSG_REGCLIPVIEWER = WM_USER + 0x0003; + const sal_uInt32 MSG_FLUSHCLIPBOARD = WM_USER + 0x0004; + const sal_uInt32 MSG_SHUTDOWN = WM_USER + 0x0005; + + const sal_uInt32 MAX_WAITTIME = 10000; // msec + const sal_uInt32 MAX_WAIT_SHUTDOWN = 10000; // msec + + const bool MANUAL_RESET = true; + const bool INIT_NONSIGNALED = false; + + /* Cannot use osl conditions because they are blocking + without waking up on messages sent by another thread + this leads to deadlocks because we are blocking the + communication between inter-thread marshalled COM + pointers. + COM Proxy-Stub communication uses SendMessages for + synchronization purposes. + */ + class Win32Condition + { + public: + Win32Condition() = default; + + ~Win32Condition() { CloseHandle(m_hEvent); } + + // wait infinite for own event (or abort event) be signaled + // leave messages sent through + bool wait(HANDLE hEvtAbort) + { + const HANDLE hWaitArray[2] = { m_hEvent, hEvtAbort }; + while (true) + { + DWORD dwResult + = MsgWaitForMultipleObjects(2, hWaitArray, FALSE, INFINITE, QS_SENDMESSAGE); + + switch (dwResult) + { + case WAIT_OBJECT_0: // wait successful + return true; + + case WAIT_OBJECT_0 + 1: // wait aborted + return false; + + case WAIT_OBJECT_0 + 2: + { + /* PeekMessage processes all messages in the SendMessage + queue that's what we want, messages from the PostMessage + queue stay untouched */ + MSG msg; + PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE); + + break; + } + + default: // WAIT_FAILED? + return false; + } + } + } + + // set the event + void set() { SetEvent(m_hEvent); } + + private: + HANDLE m_hEvent = CreateEventW(nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr); + + // prevent copy/assignment + Win32Condition(const Win32Condition&) = delete; + Win32Condition& operator=(const Win32Condition&) = delete; + }; + + // we use one condition for every request + + struct MsgCtx + { + Win32Condition aCondition; + HRESULT hr; + }; + +} /* namespace private */ + +// static member initialization + +CMtaOleClipboard* CMtaOleClipboard::s_theMtaOleClipboardInst = nullptr; + +// marshal an IDataObject + +//inline +static HRESULT MarshalIDataObjectInStream( IDataObject* pIDataObject, LPSTREAM* ppStream ) +{ + OSL_ASSERT( nullptr != pIDataObject ); + OSL_ASSERT( nullptr != ppStream ); + + *ppStream = nullptr; + return CoMarshalInterThreadInterfaceInStream( + __uuidof(IDataObject), //The IID of interface to be marshalled + pIDataObject, //The interface pointer + ppStream //IStream pointer + ); +} + +// unmarshal an IDataObject + +//inline +static HRESULT UnmarshalIDataObjectAndReleaseStream( LPSTREAM lpStream, IDataObject** ppIDataObject ) +{ + OSL_ASSERT( nullptr != lpStream ); + OSL_ASSERT( nullptr != ppIDataObject ); + + *ppIDataObject = nullptr; + return CoGetInterfaceAndReleaseStream( + lpStream, + __uuidof(IDataObject), + reinterpret_cast<LPVOID*>(ppIDataObject)); +} + +// helper class to ensure that the calling thread has com initialized + +namespace { + +class CAutoComInit +{ +public: + /* + to be safe we call CoInitializeEx + although it is not necessary if + the calling thread was created + using osl_CreateThread because + this function calls CoInitializeEx + for every thread it creates + */ + CAutoComInit( ) : m_hResult( CoInitializeEx( nullptr, COINIT_APARTMENTTHREADED ) ) + { + if ( S_OK == m_hResult ) + OSL_FAIL( + "com was not yet initialized, the thread was not created using osl_createThread" ); + else if ( FAILED( m_hResult ) && !( RPC_E_CHANGED_MODE == m_hResult ) ) + OSL_FAIL( + "com could not be initialized, maybe the thread was not created using osl_createThread" ); + } + + ~CAutoComInit( ) + { + /* + we only call CoUninitialize when + CoInitialize returned S_FALSE, what + means that com was already initialize + for that thread so we keep the balance + if CoInitialize returned S_OK what means + com was not yet initialized we better + let com initialized or we may run into + the realm of undefined behaviour + */ + if ( m_hResult == S_FALSE ) + CoUninitialize( ); + } + +private: + HRESULT m_hResult; +}; + +} + +// ctor + +CMtaOleClipboard::CMtaOleClipboard( ) : + m_hOleThread( nullptr ), + m_uOleThreadId( 0 ), + // signals that the thread was successfully setup + m_hEvtThrdReady(CreateEventW( nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr )), + m_hwndMtaOleReqWnd( nullptr ), + // signals that the window is destroyed - to stop waiting any winproc result + m_hEvtWndDisposed(CreateEventW(nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr)), + m_MtaOleReqWndClassAtom( 0 ), + m_pfncClipViewerCallback( nullptr ), + m_bRunClipboardNotifierThread( true ), + m_hClipboardChangedEvent( m_hClipboardChangedNotifierEvents[0] ), + m_hTerminateClipboardChangedNotifierEvent( m_hClipboardChangedNotifierEvents[1] ), + m_ClipboardChangedEventCount( 0 ) +{ + OSL_ASSERT( nullptr != m_hEvtThrdReady ); + SAL_WARN_IF(!m_hEvtWndDisposed, "dtrans", "CreateEventW failed: m_hEvtWndDisposed is nullptr"); + + s_theMtaOleClipboardInst = this; + + m_hOleThread = reinterpret_cast<HANDLE>(_beginthreadex( + nullptr, 0, CMtaOleClipboard::oleThreadProc, this, 0, &m_uOleThreadId )); + OSL_ASSERT( nullptr != m_hOleThread ); + + // setup the clipboard changed notifier thread + + m_hClipboardChangedNotifierEvents[0] = CreateEventW( nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr ); + OSL_ASSERT( nullptr != m_hClipboardChangedNotifierEvents[0] ); + + m_hClipboardChangedNotifierEvents[1] = CreateEventW( nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr ); + OSL_ASSERT( nullptr != m_hClipboardChangedNotifierEvents[1] ); + + unsigned uThreadId; + m_hClipboardChangedNotifierThread = reinterpret_cast<HANDLE>(_beginthreadex( + nullptr, 0, CMtaOleClipboard::clipboardChangedNotifierThreadProc, this, 0, &uThreadId )); + + OSL_ASSERT( nullptr != m_hClipboardChangedNotifierThread ); +} + +// dtor + +CMtaOleClipboard::~CMtaOleClipboard( ) +{ + // block calling threads out + if ( nullptr != m_hEvtThrdReady ) + ResetEvent( m_hEvtThrdReady ); + + // terminate the clipboard changed notifier thread + m_bRunClipboardNotifierThread = false; + SetEvent( m_hTerminateClipboardChangedNotifierEvent ); + + // unblock whoever could still wait for event processing + if (m_hEvtWndDisposed) + SetEvent(m_hEvtWndDisposed); + + sal_uInt32 dwResult = WaitForSingleObject( + m_hClipboardChangedNotifierThread, MAX_WAIT_SHUTDOWN ); + + OSL_ENSURE( dwResult == WAIT_OBJECT_0, "clipboard notifier thread could not terminate" ); + + if ( nullptr != m_hClipboardChangedNotifierThread ) + CloseHandle( m_hClipboardChangedNotifierThread ); + + if ( nullptr != m_hClipboardChangedNotifierEvents[0] ) + CloseHandle( m_hClipboardChangedNotifierEvents[0] ); + + if ( nullptr != m_hClipboardChangedNotifierEvents[1] ) + CloseHandle( m_hClipboardChangedNotifierEvents[1] ); + + // end the thread + // because DestroyWindow can only be called + // from within the thread that created the window + sendMessage( MSG_SHUTDOWN ); + + // wait for thread shutdown + dwResult = WaitForSingleObject( m_hOleThread, MAX_WAIT_SHUTDOWN ); + OSL_ENSURE( dwResult == WAIT_OBJECT_0, "OleThread could not terminate" ); + + if ( nullptr != m_hOleThread ) + CloseHandle( m_hOleThread ); + + if ( nullptr != m_hEvtThrdReady ) + CloseHandle( m_hEvtThrdReady ); + + if (m_hEvtWndDisposed) + CloseHandle(m_hEvtWndDisposed); + + if ( m_MtaOleReqWndClassAtom ) + UnregisterClassW( g_szWndClsName, nullptr ); + + OSL_ENSURE( ( nullptr == m_pfncClipViewerCallback ), + "Clipboard viewer not properly unregistered" ); +} + +HRESULT CMtaOleClipboard::flushClipboard( ) +{ + if ( !WaitForThreadReady( ) ) + { + OSL_FAIL( "clipboard sta thread not ready" ); + return E_FAIL; + } + + OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId, + "flushClipboard from within clipboard sta thread called" ); + + MsgCtx aMsgCtx; + + const bool bWaitSuccess = postMessage(MSG_FLUSHCLIPBOARD, 0, reinterpret_cast<LPARAM>(&aMsgCtx)) + && aMsgCtx.aCondition.wait(m_hEvtWndDisposed); + + return bWaitSuccess ? aMsgCtx.hr : E_ABORT; +} + +HRESULT CMtaOleClipboard::getClipboard( IDataObject** ppIDataObject ) +{ + OSL_PRECOND( nullptr != ppIDataObject, "invalid parameter" ); + OSL_PRECOND( GetCurrentThreadId( ) != m_uOleThreadId, "getClipboard from within clipboard sta thread called" ); + + if ( !WaitForThreadReady( ) ) + { + OSL_FAIL( "clipboard sta thread not ready" ); + return E_FAIL; + } + + CAutoComInit comAutoInit; + + LPSTREAM lpStream; + + *ppIDataObject = nullptr; + + MsgCtx aMsgCtx; + + const bool bWaitSuccess = postMessage(MSG_GETCLIPBOARD, reinterpret_cast<WPARAM>(&lpStream), + reinterpret_cast<LPARAM>(&aMsgCtx)) + && aMsgCtx.aCondition.wait(m_hEvtWndDisposed); + + HRESULT hr = bWaitSuccess ? aMsgCtx.hr : E_ABORT; + + if ( SUCCEEDED( hr ) ) + { + hr = UnmarshalIDataObjectAndReleaseStream( lpStream, ppIDataObject ); + OSL_ENSURE( SUCCEEDED( hr ), "unmarshalling clipboard data object failed" ); + } + + return hr; +} + +// this is an asynchronous method that's why we don't wait until the +// request is completed + +HRESULT CMtaOleClipboard::setClipboard( IDataObject* pIDataObject ) +{ + if ( !WaitForThreadReady( ) ) + { + OSL_FAIL( "clipboard sta thread not ready" ); + return E_FAIL; + } + + CAutoComInit comAutoInit; + + OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId, "setClipboard from within the clipboard sta thread called" ); + + // because we marshall this request + // into the sta thread we better + // acquire the interface here so + // that the object will not be + // destroyed before the ole clipboard + // can acquire it + // remember: pIDataObject may be NULL + // which is a request to clear the + // current clipboard content + if ( pIDataObject ) + pIDataObject->AddRef( ); + + postMessage( + MSG_SETCLIPBOARD, + reinterpret_cast< WPARAM >( pIDataObject ) ); + + // because this is an asynchronous function + // the return value is useless + return S_OK; +} + +// register a clipboard viewer + +bool CMtaOleClipboard::registerClipViewer( LPFNC_CLIPVIEWER_CALLBACK_t pfncClipViewerCallback ) +{ + if ( !WaitForThreadReady( ) ) + { + OSL_FAIL( "clipboard sta thread not ready" ); + return false; + } + + OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId, "registerClipViewer from within the OleThread called" ); + + MsgCtx aMsgCtx; + + if (postMessage(MSG_REGCLIPVIEWER, reinterpret_cast<WPARAM>(pfncClipViewerCallback), + reinterpret_cast<LPARAM>(&aMsgCtx))) + aMsgCtx.aCondition.wait(m_hEvtWndDisposed); + + return false; +} + +// register a clipboard viewer + +bool CMtaOleClipboard::onRegisterClipViewer( LPFNC_CLIPVIEWER_CALLBACK_t pfncClipViewerCallback ) +{ + bool bRet = false; + + // we need exclusive access because the clipboard changed notifier + // thread also accesses this variable + MutexGuard aGuard( m_pfncClipViewerCallbackMutex ); + + // register if not yet done + if ( ( nullptr != pfncClipViewerCallback ) && ( nullptr == m_pfncClipViewerCallback ) ) + { + // SetClipboardViewer sends a WM_DRAWCLIPBOARD message we ignore + // this message if we register ourself as clip viewer + m_bInRegisterClipViewer = true; + bRet = AddClipboardFormatListener(m_hwndMtaOleReqWnd); + m_bInRegisterClipViewer = false; + + // save the new callback function + m_pfncClipViewerCallback = pfncClipViewerCallback; + } + else if ( ( nullptr == pfncClipViewerCallback ) && ( nullptr != m_pfncClipViewerCallback ) ) + { + m_pfncClipViewerCallback = nullptr; + + // unregister if input parameter is NULL and we previously registered + // as clipboard viewer + bRet = RemoveClipboardFormatListener(m_hwndMtaOleReqWnd); + } + + return bRet; +} + +HRESULT CMtaOleClipboard::onSetClipboard( IDataObject* pIDataObject ) +{ + return OleSetClipboard( pIDataObject ); +} + +HRESULT CMtaOleClipboard::onGetClipboard( LPSTREAM* ppStream ) +{ + OSL_ASSERT(nullptr != ppStream); + + IDataObjectPtr pIDataObject; + + // forward the request to the OleClipboard + HRESULT hr = OleGetClipboard( &pIDataObject ); + if ( SUCCEEDED( hr ) ) + { + hr = MarshalIDataObjectInStream(pIDataObject.get(), ppStream); + OSL_ENSURE(SUCCEEDED(hr), "marshalling cliboard data object failed"); + } + return hr; +} + +// flush the ole-clipboard + +HRESULT CMtaOleClipboard::onFlushClipboard( ) +{ + return OleFlushClipboard(); +} + +// handle clipboard update event + +LRESULT CMtaOleClipboard::onClipboardUpdate() +{ + // we don't send a notification if we are + // registering ourself as clipboard + if ( !m_bInRegisterClipViewer ) + { + MutexGuard aGuard( m_ClipboardChangedEventCountMutex ); + + m_ClipboardChangedEventCount++; + SetEvent( m_hClipboardChangedEvent ); + } + + return 0; +} + +// SendMessage so we don't need to supply the HWND if we send +// something to our wrapped window + +LRESULT CMtaOleClipboard::sendMessage( UINT msg, WPARAM wParam, LPARAM lParam ) +{ + return ::SendMessageW( m_hwndMtaOleReqWnd, msg, wParam, lParam ); +} + +// PostMessage so we don't need to supply the HWND if we send +// something to our wrapped window + +bool CMtaOleClipboard::postMessage( UINT msg, WPARAM wParam, LPARAM lParam ) +{ + bool const ret = PostMessageW(m_hwndMtaOleReqWnd, msg, wParam, lParam); + SAL_WARN_IF(!ret, "dtrans", "ERROR: PostMessage() failed!"); + return ret; +} + +// the window proc + +LRESULT CALLBACK CMtaOleClipboard::mtaOleReqWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + LRESULT lResult = 0; + + // get a connection to the class-instance via the static member + CMtaOleClipboard* pImpl = CMtaOleClipboard::s_theMtaOleClipboardInst; + OSL_ASSERT( nullptr != pImpl ); + + switch( uMsg ) + { + case MSG_SETCLIPBOARD: + { + IDataObject* pIDataObject = reinterpret_cast< IDataObject* >( wParam ); + CMtaOleClipboard::onSetClipboard( pIDataObject ); + + // in setClipboard we did acquire the + // interface pointer in order to prevent + // destruction of the object before the + // ole clipboard can acquire the interface + // now we release the interface so that + // our lostOwnership mechanism works + // remember: pIDataObject may be NULL + if ( pIDataObject ) + pIDataObject->Release( ); + } + break; + + case MSG_GETCLIPBOARD: + { + MsgCtx* aMsgCtx = reinterpret_cast< MsgCtx* >( lParam ); + OSL_ASSERT( aMsgCtx ); + + aMsgCtx->hr = CMtaOleClipboard::onGetClipboard( reinterpret_cast< LPSTREAM* >(wParam) ); + aMsgCtx->aCondition.set( ); + } + break; + + case MSG_FLUSHCLIPBOARD: + { + MsgCtx* aMsgCtx = reinterpret_cast< MsgCtx* >( lParam ); + OSL_ASSERT( aMsgCtx ); + + aMsgCtx->hr = CMtaOleClipboard::onFlushClipboard( ); + aMsgCtx->aCondition.set( ); + } + break; + + case MSG_REGCLIPVIEWER: + { + MsgCtx* pMsgCtx = reinterpret_cast<MsgCtx*>(lParam); + SAL_WARN_IF(!pMsgCtx, "dtrans", "pMsgCtx is nullptr"); + + pImpl->onRegisterClipViewer( + reinterpret_cast<CMtaOleClipboard::LPFNC_CLIPVIEWER_CALLBACK_t>(wParam)); + pMsgCtx->aCondition.set(); + } + break; + + case WM_CLIPBOARDUPDATE: + lResult = pImpl->onClipboardUpdate(); + break; + + case MSG_SHUTDOWN: + DestroyWindow( pImpl->m_hwndMtaOleReqWnd ); + break; + + // force the sta thread to end + case WM_DESTROY: + SetEvent(pImpl->m_hEvtWndDisposed); // stop waiting for conditions set by this wndproc + PostQuitMessage( 0 ); + break; + + default: + lResult = DefWindowProcW( hWnd, uMsg, wParam, lParam ); + break; + } + + return lResult; +} + +void CMtaOleClipboard::createMtaOleReqWnd( ) +{ + WNDCLASSEXW wcex; + + HINSTANCE hInst = GetModuleHandleW( CLIPSRV_DLL_NAME ); + OSL_ENSURE( nullptr != hInst, "The name of the clipboard service dll must have changed" ); + + ZeroMemory( &wcex, sizeof(wcex) ); + + wcex.cbSize = sizeof(wcex); + wcex.style = 0; + wcex.lpfnWndProc = CMtaOleClipboard::mtaOleReqWndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInst; + wcex.hIcon = nullptr; + wcex.hCursor = nullptr; + wcex.hbrBackground = nullptr; + wcex.lpszMenuName = nullptr; + wcex.lpszClassName = g_szWndClsName; + wcex.hIconSm = nullptr; + + m_MtaOleReqWndClassAtom = RegisterClassExW( &wcex ); + + if ( 0 != m_MtaOleReqWndClassAtom ) + m_hwndMtaOleReqWnd = CreateWindowW( + g_szWndClsName, nullptr, 0, 0, 0, 0, 0, nullptr, nullptr, hInst, nullptr ); +} + +unsigned int CMtaOleClipboard::run( ) +{ + HRESULT hr = OleInitialize( nullptr ); + OSL_ASSERT( SUCCEEDED( hr ) ); + + createMtaOleReqWnd( ); + + unsigned int nRet; + + if ( IsWindow( m_hwndMtaOleReqWnd ) ) + { + if ( nullptr != m_hEvtThrdReady ) + SetEvent( m_hEvtThrdReady ); + + // pumping messages + MSG msg; + while( GetMessageW( &msg, nullptr, 0, 0 ) ) + DispatchMessageW( &msg ); + + nRet = 0; + } + else + nRet = ~0U; + + OleUninitialize( ); + + return nRet; +} + +unsigned int WINAPI CMtaOleClipboard::oleThreadProc( LPVOID pParam ) +{ + osl_setThreadName("CMtaOleClipboard::run()"); + + CMtaOleClipboard* pInst = + static_cast<CMtaOleClipboard*>( pParam ); + OSL_ASSERT( nullptr != pInst ); + + return pInst->run( ); +} + +unsigned int WINAPI CMtaOleClipboard::clipboardChangedNotifierThreadProc( LPVOID pParam ) +{ + osl_setThreadName("CMtaOleClipboard::clipboardChangedNotifierThreadProc()"); + CMtaOleClipboard* pInst = static_cast< CMtaOleClipboard* >( pParam ); + OSL_ASSERT( nullptr != pInst ); + + CoInitialize( nullptr ); + + // assuming we don't need a lock for + // a boolean variable like m_bRun... + while ( pInst->m_bRunClipboardNotifierThread ) + { + // process window messages because of CoInitialize + MSG Msg; + while (PeekMessageW(&Msg, nullptr, 0, 0, PM_REMOVE)) + DispatchMessageW(&Msg); + + // wait for clipboard changed or terminate event + MsgWaitForMultipleObjects(2, pInst->m_hClipboardChangedNotifierEvents, false, INFINITE, + QS_ALLINPUT | QS_ALLPOSTMESSAGE); + + ClearableMutexGuard aGuard( pInst->m_ClipboardChangedEventCountMutex ); + + if ( pInst->m_ClipboardChangedEventCount > 0 ) + { + pInst->m_ClipboardChangedEventCount--; + if ( 0 == pInst->m_ClipboardChangedEventCount ) + ResetEvent( pInst->m_hClipboardChangedEvent ); + + aGuard.clear( ); + + // nobody should touch m_pfncClipViewerCallback while we do + MutexGuard aClipViewerGuard( pInst->m_pfncClipViewerCallbackMutex ); + + // notify all clipboard listener + if ( pInst->m_pfncClipViewerCallback ) + pInst->m_pfncClipViewerCallback( ); + } + else + aGuard.clear( ); + } + + CoUninitialize( ); + + return 0; +} + +bool CMtaOleClipboard::WaitForThreadReady( ) const +{ + bool bRet = false; + + if ( nullptr != m_hEvtThrdReady ) + { + DWORD dwResult = WaitForSingleObject( + m_hEvtThrdReady, MAX_WAITTIME ); + bRet = ( dwResult == WAIT_OBJECT_0 ); + } + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dtrans/source/win32/clipb/MtaOleClipb.hxx b/dtrans/source/win32/clipb/MtaOleClipb.hxx new file mode 100644 index 000000000..bf71ac26e --- /dev/null +++ b/dtrans/source/win32/clipb/MtaOleClipb.hxx @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_DTRANS_SOURCE_WIN32_CLIPB_MTAOLECLIPB_HXX +#define INCLUDED_DTRANS_SOURCE_WIN32_CLIPB_MTAOLECLIPB_HXX + +#include <sal/types.h> +#include <osl/mutex.hxx> + +#include <objidl.h> + +// the Mta-Ole clipboard class is for internal use only! +// only one instance of this class should be created, the +// user has to ensure this! +// the class is not thread-safe because it will be used +// only from within the clipboard service and the methods +// of the clipboard service are already synchronized + +class CMtaOleClipboard +{ +public: + typedef void ( WINAPI *LPFNC_CLIPVIEWER_CALLBACK_t )( void ); + +public: + CMtaOleClipboard( ); + ~CMtaOleClipboard( ); + + // clipboard functions + HRESULT setClipboard( IDataObject* pIDataObject ); + HRESULT getClipboard( IDataObject** ppIDataObject ); + HRESULT flushClipboard( ); + + // register/unregister a clipboard viewer; there can only + // be one at a time; parameter NULL means unregister + // a clipboard viewer + // returns true on success else false; use GetLastError( ) in + // false case + bool registerClipViewer( LPFNC_CLIPVIEWER_CALLBACK_t pfncClipViewerCallback ); + +private: + unsigned int run( ); + + // create a hidden window which serves as a request target; so we + // guarantee synchronization + void createMtaOleReqWnd( ); + + // message support + bool postMessage( UINT msg, WPARAM wParam = 0, LPARAM lParam = 0 ); + LRESULT sendMessage( UINT msg, WPARAM wParam = 0, LPARAM lParam = 0 ); + + // message handler functions; remember these functions are called + // from a different thread context! + + static HRESULT onSetClipboard( IDataObject* pIDataObject ); + static HRESULT onGetClipboard( LPSTREAM* ppStream ); + static HRESULT onFlushClipboard( ); + bool onRegisterClipViewer( LPFNC_CLIPVIEWER_CALLBACK_t pfncClipViewerCallback ); + + // win32 clipboard listener support + LRESULT onClipboardUpdate(); + + static LRESULT CALLBACK mtaOleReqWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); + static unsigned int WINAPI oleThreadProc( LPVOID pParam ); + + static unsigned int WINAPI clipboardChangedNotifierThreadProc( LPVOID pParam ); + + bool WaitForThreadReady( ) const; + +private: + HANDLE m_hOleThread; + unsigned m_uOleThreadId; + HANDLE m_hEvtThrdReady; + HWND m_hwndMtaOleReqWnd; + HANDLE m_hEvtWndDisposed; + ATOM m_MtaOleReqWndClassAtom; + LPFNC_CLIPVIEWER_CALLBACK_t m_pfncClipViewerCallback; + bool m_bInRegisterClipViewer; + + bool m_bRunClipboardNotifierThread; + HANDLE m_hClipboardChangedNotifierThread; + HANDLE m_hClipboardChangedNotifierEvents[2]; + HANDLE& m_hClipboardChangedEvent; + HANDLE& m_hTerminateClipboardChangedNotifierEvent; + osl::Mutex m_ClipboardChangedEventCountMutex; + sal_Int32 m_ClipboardChangedEventCount; + + osl::Mutex m_pfncClipViewerCallbackMutex; + + static CMtaOleClipboard* s_theMtaOleClipboardInst; + +// not allowed +private: + CMtaOleClipboard( const CMtaOleClipboard& ); + CMtaOleClipboard& operator=( const CMtaOleClipboard& ); + + friend LRESULT CALLBACK mtaOleReqWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dtrans/source/win32/clipb/WinClipbImpl.cxx b/dtrans/source/win32/clipb/WinClipbImpl.cxx new file mode 100644 index 000000000..8d59be7ca --- /dev/null +++ b/dtrans/source/win32/clipb/WinClipbImpl.cxx @@ -0,0 +1,210 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <osl/diagnose.h> +#include "WinClipbImpl.hxx" + +#include <systools/win32/comtools.hxx> +#include "../../inc/DtObjFactory.hxx" +#include "../dtobj/APNDataObject.hxx" +#include "../dtobj/DOTransferable.hxx" +#include "WinClipboard.hxx" +#include <com/sun/star/datatransfer/clipboard/RenderingCapabilities.hpp> +#include "../dtobj/XNotifyingDataObject.hxx" + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <ole2.h> +#include <objidl.h> + +using namespace osl; +using namespace std; +using namespace cppu; + +using namespace com::sun::star::uno; +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::datatransfer::clipboard; +using namespace com::sun::star::datatransfer::clipboard::RenderingCapabilities; + +// definition of static members +CWinClipbImpl* CWinClipbImpl::s_pCWinClipbImpl = nullptr; +osl::Mutex CWinClipbImpl::s_aMutex; + +CWinClipbImpl::CWinClipbImpl( const OUString& aClipboardName, CWinClipboard* theWinClipboard ) : + m_itsName( aClipboardName ), + m_pWinClipboard( theWinClipboard ), + m_pCurrentClipContent( nullptr ) +{ + OSL_ASSERT( nullptr != m_pWinClipboard ); + + // necessary to reassociate from + // the static callback function + s_pCWinClipbImpl = this; + registerClipboardViewer( ); +} + +CWinClipbImpl::~CWinClipbImpl( ) +{ + { + MutexGuard aGuard(s_aMutex); + s_pCWinClipbImpl = nullptr; + } + + unregisterClipboardViewer( ); +} + +Reference< XTransferable > CWinClipbImpl::getContents( ) +{ + // use the shortcut or create a transferable from + // system clipboard + { + MutexGuard aGuard(m_ClipContentMutex); + + if (nullptr != m_pCurrentClipContent) + { + return m_pCurrentClipContent->m_XTransferable; + } + + // Content cached? + if (m_foreignContent.is()) + return m_foreignContent; + + // release the mutex, so that the variable may be + // changed by other threads + } + + Reference< XTransferable > rClipContent; + + // get the current dataobject from clipboard + IDataObjectPtr pIDataObject; + HRESULT hr = m_MtaOleClipboard.getClipboard( &pIDataObject ); + + if ( SUCCEEDED( hr ) ) + { + // create an apartment neutral dataobject and initialize it with a + // com smart pointer to the IDataObject from clipboard + IDataObjectPtr pIDo( new CAPNDataObject( pIDataObject ) ); + + // remember pIDo destroys itself due to the smart pointer + rClipContent = CDOTransferable::create( m_pWinClipboard->m_xContext, pIDo ); + + MutexGuard aGuard(m_ClipContentMutex); + m_foreignContent = rClipContent; + } + + return rClipContent; +} + +void CWinClipbImpl::setContents( + const Reference< XTransferable >& xTransferable, + const Reference< XClipboardOwner >& xClipboardOwner ) +{ + IDataObjectPtr pIDataObj; + + if ( xTransferable.is( ) ) + { + { + MutexGuard aGuard(m_ClipContentMutex); + + m_foreignContent.clear(); + + m_pCurrentClipContent + = new CXNotifyingDataObject(CDTransObjFactory::createDataObjFromTransferable( + m_pWinClipboard->m_xContext, xTransferable), + xTransferable, xClipboardOwner, this); + } + + pIDataObj = IDataObjectPtr( m_pCurrentClipContent ); + } + + m_MtaOleClipboard.setClipboard(pIDataObj.get()); +} + +OUString CWinClipbImpl::getName( ) +{ + return m_itsName; +} + +sal_Int8 CWinClipbImpl::getRenderingCapabilities( ) +{ + return ( Delayed | Persistant ); +} + +void CWinClipbImpl::flushClipboard( ) +{ + // actually it should be ClearableMutexGuard aGuard( m_ClipContentMutex ); + // but it does not work since FlushClipboard does a callback and frees DataObject + // which results in a deadlock in onReleaseDataObject. + // FlushClipboard had to be synchron in order to prevent shutdown until all + // clipboard-formats are rendered. + // The request is needed to prevent flushing if we are not clipboard owner (it is + // not known what happens if we flush but aren't clipboard owner). + // It may be possible to move the request to the clipboard STA thread by saving the + // DataObject and call OleIsCurrentClipboard before flushing. + + if ( nullptr != m_pCurrentClipContent ) + m_MtaOleClipboard.flushClipboard( ); +} + +void CWinClipbImpl::registerClipboardViewer( ) +{ + m_MtaOleClipboard.registerClipViewer( CWinClipbImpl::onClipboardContentChanged ); +} + +void CWinClipbImpl::unregisterClipboardViewer( ) +{ + m_MtaOleClipboard.registerClipViewer( nullptr ); +} + +void CWinClipbImpl::dispose() +{ + OSL_ENSURE( !m_pCurrentClipContent, "Clipboard was not flushed before shutdown!" ); +} + +void WINAPI CWinClipbImpl::onClipboardContentChanged() +{ + MutexGuard aGuard( s_aMutex ); + + // reassociation to instance through static member + if ( nullptr != s_pCWinClipbImpl ) + { + s_pCWinClipbImpl->m_foreignContent.clear(); + s_pCWinClipbImpl->m_pWinClipboard->notifyAllClipboardListener( ); + } +} + +void CWinClipbImpl::onReleaseDataObject( CXNotifyingDataObject* theCaller ) +{ + OSL_ASSERT( nullptr != theCaller ); + + if ( theCaller ) + theCaller->lostOwnership( ); + + // if the current caller is the one we currently + // hold, then set it to NULL because an external + // source must be the clipboardowner now + MutexGuard aGuard( m_ClipContentMutex ); + + if ( m_pCurrentClipContent == theCaller ) + m_pCurrentClipContent = nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dtrans/source/win32/clipb/WinClipbImpl.hxx b/dtrans/source/win32/clipb/WinClipbImpl.hxx new file mode 100644 index 000000000..cd5878e6b --- /dev/null +++ b/dtrans/source/win32/clipb/WinClipbImpl.hxx @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_DTRANS_SOURCE_WIN32_CLIPB_WINCLIPBIMPL_HXX +#define INCLUDED_DTRANS_SOURCE_WIN32_CLIPB_WINCLIPBIMPL_HXX + +#include <sal/types.h> +#include <rtl/ustring.hxx> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardListener.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardOwner.hpp> +#include "MtaOleClipb.hxx" + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> + +class CWinClipboard; +class CXNotifyingDataObject; + +// impl class to avoid deadlocks between XTDataObject +// and the clipboard implementation + +class CWinClipbImpl +{ +public: + ~CWinClipbImpl( ); + +protected: + CWinClipbImpl( const OUString& aClipboardName, CWinClipboard* theWinClipboard ); + + /// @throws css::uno::RuntimeException + css::uno::Reference< css::datatransfer::XTransferable > getContents( ); + + /// @throws css::uno::RuntimeException + void setContents( + const css::uno::Reference< css::datatransfer::XTransferable >& xTransferable, + const css::uno::Reference< css::datatransfer::clipboard::XClipboardOwner >& xClipboardOwner ); + + /// @throws css::uno::RuntimeException + OUString getName( ); + + // XClipboardEx + + /// @throws css::uno::RuntimeException + static sal_Int8 getRenderingCapabilities( ); + + // XFlushableClipboard + + /// @throws css::uno::RuntimeException + void flushClipboard( ); + + // XComponent + + /// @throws css::uno::RuntimeException + void dispose( ); + + // member functions + + void registerClipboardViewer( ); + void unregisterClipboardViewer( ); + + static void WINAPI onClipboardContentChanged(); + +private: + void onReleaseDataObject( CXNotifyingDataObject* theCaller ); + +private: + OUString m_itsName; + CMtaOleClipboard m_MtaOleClipboard; + CWinClipboard* m_pWinClipboard; + CXNotifyingDataObject* m_pCurrentClipContent; + com::sun::star::uno::Reference< com::sun::star::datatransfer::XTransferable > m_foreignContent; + osl::Mutex m_ClipContentMutex; + + static osl::Mutex s_aMutex; + static CWinClipbImpl* s_pCWinClipbImpl; + +private: + CWinClipbImpl( const CWinClipbImpl& ); + CWinClipbImpl& operator=( const CWinClipbImpl& ); + + friend class CWinClipboard; + friend class CXNotifyingDataObject; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dtrans/source/win32/clipb/WinClipboard.cxx b/dtrans/source/win32/clipb/WinClipboard.cxx new file mode 100644 index 000000000..919065a9f --- /dev/null +++ b/dtrans/source/win32/clipb/WinClipboard.cxx @@ -0,0 +1,239 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <osl/diagnose.h> +#include "WinClipboard.hxx" +#include <com/sun/star/datatransfer/clipboard/ClipboardEvent.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <cppuhelper/supportsservice.hxx> +#include "WinClipbImpl.hxx" + +using namespace osl; +using namespace std; +using namespace cppu; + +using namespace com::sun::star::uno; +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::datatransfer::clipboard; +using namespace com::sun::star::lang; + +#define WINCLIPBOARD_IMPL_NAME "com.sun.star.datatransfer.clipboard.ClipboardW32" + +namespace +{ + Sequence< OUString > WinClipboard_getSupportedServiceNames() + { + Sequence< OUString > aRet { "com.sun.star.datatransfer.clipboard.SystemClipboard" }; + return aRet; + } +} + +/*XEventListener,*/ +CWinClipboard::CWinClipboard( const Reference< XComponentContext >& rxContext, const OUString& aClipboardName ) : + WeakComponentImplHelper< XSystemClipboard, XFlushableClipboard, XServiceInfo >( m_aCbListenerMutex ), + m_xContext( rxContext ) +{ + m_pImpl.reset( new CWinClipbImpl( aClipboardName, this ) ); +} + +// XClipboard + +// to avoid unnecessary traffic we check first if there is a clipboard +// content which was set via setContent, in this case we don't need +// to query the content from the clipboard, create a new wrapper object +// and so on, we simply return the original XTransferable instead of our +// DOTransferable + +Reference< XTransferable > SAL_CALL CWinClipboard::getContents( ) +{ + MutexGuard aGuard( m_aMutex ); + + if ( rBHelper.bDisposed ) + throw DisposedException("object is already disposed", + static_cast< XClipboardEx* >( this ) ); + + if ( nullptr != m_pImpl.get( ) ) + return m_pImpl->getContents( ); + + return Reference< XTransferable >( ); +} + +void SAL_CALL CWinClipboard::setContents( const Reference< XTransferable >& xTransferable, + const Reference< XClipboardOwner >& xClipboardOwner ) +{ + MutexGuard aGuard( m_aMutex ); + + if ( rBHelper.bDisposed ) + throw DisposedException("object is already disposed", + static_cast< XClipboardEx* >( this ) ); + + if ( nullptr != m_pImpl.get( ) ) + m_pImpl->setContents( xTransferable, xClipboardOwner ); +} + +OUString SAL_CALL CWinClipboard::getName( ) +{ + if ( rBHelper.bDisposed ) + throw DisposedException("object is already disposed", + static_cast< XClipboardEx* >( this ) ); + + if ( nullptr != m_pImpl.get( ) ) + return m_pImpl->getName( ); + + return OUString(); +} + +// XFlushableClipboard + +void SAL_CALL CWinClipboard::flushClipboard( ) +{ + MutexGuard aGuard( m_aMutex ); + + if ( rBHelper.bDisposed ) + throw DisposedException("object is already disposed", + static_cast< XClipboardEx* >( this ) ); + + if ( nullptr != m_pImpl.get( ) ) + m_pImpl->flushClipboard( ); +} + +// XClipboardEx + +sal_Int8 SAL_CALL CWinClipboard::getRenderingCapabilities( ) +{ + if ( rBHelper.bDisposed ) + throw DisposedException("object is already disposed", + static_cast< XClipboardEx* >( this ) ); + + if ( nullptr != m_pImpl.get( ) ) + return CWinClipbImpl::getRenderingCapabilities( ); + + return 0; +} + +// XClipboardNotifier + +void SAL_CALL CWinClipboard::addClipboardListener( const Reference< XClipboardListener >& listener ) +{ + if ( rBHelper.bDisposed ) + throw DisposedException("object is already disposed", + static_cast< XClipboardEx* >( this ) ); + + // check input parameter + if ( !listener.is( ) ) + throw IllegalArgumentException("empty reference", + static_cast< XClipboardEx* >( this ), + 1 ); + + rBHelper.aLC.addInterface( cppu::UnoType<decltype(listener)>::get(), listener ); +} + +void SAL_CALL CWinClipboard::removeClipboardListener( const Reference< XClipboardListener >& listener ) +{ + if ( rBHelper.bDisposed ) + throw DisposedException("object is already disposed", + static_cast< XClipboardEx* >( this ) ); + + // check input parameter + if ( !listener.is( ) ) + throw IllegalArgumentException("empty reference", + static_cast< XClipboardEx* >( this ), + 1 ); + + rBHelper.aLC.removeInterface( cppu::UnoType<decltype(listener)>::get(), listener ); +} + +void CWinClipboard::notifyAllClipboardListener( ) +{ + if ( !rBHelper.bDisposed ) + { + ClearableMutexGuard aGuard( rBHelper.rMutex ); + if ( !rBHelper.bDisposed ) + { + aGuard.clear( ); + + OInterfaceContainerHelper* pICHelper = rBHelper.aLC.getContainer( + cppu::UnoType<XClipboardListener>::get()); + + if ( pICHelper ) + { + try + { + OInterfaceIteratorHelper iter(*pICHelper); + Reference<XTransferable> rXTransf(m_pImpl->getContents()); + ClipboardEvent aClipbEvent(static_cast<XClipboard*>(this), rXTransf); + + while(iter.hasMoreElements()) + { + try + { + Reference<XClipboardListener> xCBListener(iter.next(), UNO_QUERY); + if (xCBListener.is()) + xCBListener->changedContents(aClipbEvent); + } + catch(RuntimeException&) + { + OSL_FAIL( "RuntimeException caught" ); + } + } + } + catch(const css::lang::DisposedException&) + { + OSL_FAIL("Service Manager disposed"); + + // no further clipboard changed notifications + m_pImpl->unregisterClipboardViewer(); + } + + } // end if + } // end if + } // end if +} + +// overwritten base class method which will be +// called by the base class dispose method + +void SAL_CALL CWinClipboard::disposing() +{ + // do my own stuff + m_pImpl->dispose( ); + + // force destruction of the impl class + m_pImpl.reset(); +} + +// XServiceInfo + +OUString SAL_CALL CWinClipboard::getImplementationName( ) +{ + return WINCLIPBOARD_IMPL_NAME; +} + +sal_Bool SAL_CALL CWinClipboard::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL CWinClipboard::getSupportedServiceNames( ) +{ + return WinClipboard_getSupportedServiceNames(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dtrans/source/win32/clipb/WinClipboard.hxx b/dtrans/source/win32/clipb/WinClipboard.hxx new file mode 100644 index 000000000..db3f1ab18 --- /dev/null +++ b/dtrans/source/win32/clipb/WinClipboard.hxx @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_DTRANS_SOURCE_WIN32_CLIPB_WINCLIPBOARD_HXX +#define INCLUDED_DTRANS_SOURCE_WIN32_CLIPB_WINCLIPBOARD_HXX + +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <cppuhelper/compbase.hxx> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardEx.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardOwner.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardListener.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardNotifier.hpp> +#include <com/sun/star/datatransfer/clipboard/XSystemClipboard.hpp> +#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <osl/conditn.hxx> + +#include <memory> + +// forward +class CWinClipbImpl; + +// implements the XClipboard[Ex] ... interfaces +// for the clipboard viewer mechanism we need a static callback function +// and a static member to reassociate from this static function to the +// class instance +// watch out: we are using only one static member variable and not a list +// because we assume to be instantiated only once +// this will be assured by a OneInstanceFactory of the service and not +// by this class! + +// helper class, so that the mutex is constructed +// before the constructor of WeakComponentImplHelper +// will be called and initialized with this mutex +class CWinClipboardDummy +{ +protected: + osl::Mutex m_aMutex; + osl::Mutex m_aCbListenerMutex; +}; + +class CWinClipboard : + public CWinClipboardDummy, + public cppu::WeakComponentImplHelper< + css::datatransfer::clipboard::XSystemClipboard, + css::datatransfer::clipboard::XFlushableClipboard, + css::lang::XServiceInfo > +{ +public: + CWinClipboard( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const OUString& aClipboardName ); + + // XClipboard + + virtual css::uno::Reference< css::datatransfer::XTransferable > SAL_CALL getContents( ) override; + + virtual void SAL_CALL setContents( + const css::uno::Reference< css::datatransfer::XTransferable >& xTransferable, + const css::uno::Reference< css::datatransfer::clipboard::XClipboardOwner >& xClipboardOwner ) override; + + virtual OUString SAL_CALL getName( ) override; + + // XFlushableClipboard + + virtual void SAL_CALL flushClipboard( ) override; + + // XClipboardEx + + virtual sal_Int8 SAL_CALL getRenderingCapabilities( ) override; + + // XClipboardNotifier + + virtual void SAL_CALL addClipboardListener( + const css::uno::Reference< css::datatransfer::clipboard::XClipboardListener >& listener ) override; + + virtual void SAL_CALL removeClipboardListener( + const css::uno::Reference< css::datatransfer::clipboard::XClipboardListener >& listener ) override; + + // overwrite base class method, which is called + // by base class dispose function + + virtual void SAL_CALL disposing() override; + + // XServiceInfo + + virtual OUString SAL_CALL getImplementationName( ) override; + + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + +private: + void notifyAllClipboardListener( ); + +private: + std::unique_ptr< CWinClipbImpl > m_pImpl; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + friend class CWinClipbImpl; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dtrans/source/win32/clipb/wcbentry.cxx b/dtrans/source/win32/clipb/wcbentry.cxx new file mode 100644 index 000000000..e91bde9c0 --- /dev/null +++ b/dtrans/source/win32/clipb/wcbentry.cxx @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <cppuhelper/factory.hxx> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/container/XSet.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include "WinClipboard.hxx" + +#define WINCLIPBOARD_SERVICE_NAME "com.sun.star.datatransfer.clipboard.SystemClipboard" + +#define WINCLIPBOARD_IMPL_NAME "com.sun.star.datatransfer.clipboard.ClipboardW32" + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::registry; +using namespace ::cppu; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::datatransfer::clipboard; + +namespace +{ + + // functions to create a new Clipboard instance; is needed by factory helper implementation + // @param rServiceManager - service manager, useful if the component needs other uno services + // so we should give it to every UNO-Implementation component + + Reference< XInterface > createInstance( const Reference< XMultiServiceFactory >& rServiceManager ) + { + return static_cast<XClipboard*>( + new CWinClipboard(comphelper::getComponentContext(rServiceManager), "")); + } +} + +extern "C" +{ + +// component_getFactory +// returns a factory to create XFilePicker-Services + +SAL_DLLPUBLIC_EXPORT void* sysdtrans_component_getFactory( const char* pImplName, void* pSrvManager, void* /*pRegistryKey*/ ) +{ + void* pRet = nullptr; + + if ( pSrvManager && ( 0 == rtl_str_compare( pImplName, WINCLIPBOARD_IMPL_NAME ) ) ) + { + Sequence< OUString > aSNS { WINCLIPBOARD_SERVICE_NAME }; + + //OUString( FPS_IMPL_NAME ) + Reference< XSingleServiceFactory > xFactory ( createOneInstanceFactory( + static_cast< XMultiServiceFactory* > ( pSrvManager ), + OUString::createFromAscii( pImplName ), + createInstance, + aSNS ) ); + if ( xFactory.is() ) + { + xFactory->acquire(); + pRet = xFactory.get(); + } + } + + return pRet; +} + +} // extern "C" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |