diff --git a/bluez5-support-for-impress-remote.diff b/bluez5-support-for-impress-remote.diff new file mode 100644 index 0000000..fd17535 --- /dev/null +++ b/bluez5-support-for-impress-remote.diff @@ -0,0 +1,851 @@ +From 9481fa2ea3f34746715c6127c190a441794c03a5 Mon Sep 17 00:00:00 2001 +From: Andrzej Hunt +Date: Thu, 10 Apr 2014 21:58:29 +0100 +Subject: [PATCH] fdo#74697 Add Bluez 5 support for impress remote. + +This time we: + - Don't break SAL_WARN with an fprintf like syntax. + - Replace DBUS_TYPE_UNIX_FD with it's definition 'h' as we might + be building on dbus-glib versions that do not support it (however + presumably anyone running bluez 5 will have a dbus version that is + new enough to support this, i.e. purely a build-time issue). + - Remove various C++11'isms. + +Change-Id: I736cad2122cd3789a5c7fb62c39e409d41fc1e32 +Reviewed-on: https://gerrit.libreoffice.org/8924 +Tested-by: Andrzej Hunt +Reviewed-by: Andrzej Hunt +(cherry picked from commit b15666fd7582729c75bd0dd1bd0cb5d7c5a77f0c) +--- + sd/source/ui/remotecontrol/BluetoothServer.cxx | 673 ++++++++++++++++++--- + .../ui/remotecontrol/BufferedStreamSocket.cxx | 2 +- + sd/source/ui/remotecontrol/Communicator.cxx | 2 + + 3 files changed, 605 insertions(+), 72 deletions(-) + +diff --git a/sd/source/ui/remotecontrol/BluetoothServer.cxx b/sd/source/ui/remotecontrol/BluetoothServer.cxx +index 63407a6..a447900 100644 +--- a/sd/source/ui/remotecontrol/BluetoothServer.cxx ++++ b/sd/source/ui/remotecontrol/BluetoothServer.cxx +@@ -13,6 +13,8 @@ + #include + #include + ++#include ++ + #include + + #ifdef LINUX_BLUETOOTH +@@ -90,25 +92,40 @@ struct DBusObject { + } + }; + ++static DBusObject* getBluez5Adapter(DBusConnection *pConnection); ++ + struct sd::BluetoothServer::Impl { + // the glib mainloop running in the thread + GMainContext *mpContext; + DBusConnection *mpConnection; + DBusObject *mpService; + volatile bool mbExitMainloop; ++ enum BluezVersion { BLUEZ4, BLUEZ5, UNKNOWN }; ++ BluezVersion maBluezVersion; + + Impl() + : mpContext( g_main_context_new() ) + , mpConnection( NULL ) + , mpService( NULL ) + , mbExitMainloop( false ) ++ , maBluezVersion( UNKNOWN ) + { } + + DBusObject *getAdapter() + { +- if( !mpService ) ++ if (mpService) ++ { ++ DBusObject* pAdapter = mpService->cloneForInterface( "org.bluez.Adapter" ); ++ return pAdapter; ++ } ++ else if (spServer->mpImpl->maBluezVersion == BLUEZ5) ++ { ++ return getBluez5Adapter(mpConnection); ++ } ++ else ++ { + return NULL; +- return mpService->cloneForInterface( "org.bluez.Adapter" ); ++ } + } + }; + +@@ -156,37 +173,181 @@ sendUnrefAndWaitForReply( DBusConnection *pConnection, DBusMessage *pMsg ) + return pMsg; + } + ++static bool ++isBluez5Available(DBusConnection *pConnection) ++{ ++ DBusMessage *pMsg; ++ ++ // Simplest wasy to check whether we have Bluez 5+ is to check ++ // that we can obtain adapters using the new interfaces. ++ // The first two error checks however don't tell us anything as they should ++ // succeed as long as dbus is working correctly. ++ pMsg = DBusObject( "org.bluez", "/", "org.freedesktop.DBus.ObjectManager" ).getMethodCall( "GetManagedObjects" ); ++ if (!pMsg) ++ { ++ SAL_INFO("sdremote.bluetooth", "No GetManagedObjects call created"); ++ return false; ++ } ++ ++ pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); ++ if (!pMsg) ++ { ++ SAL_INFO("sdremote.bluetooth", "No reply received"); ++ return false; ++ } ++ ++ // If dbus is working correctly and we aren't on bluez 5 this is where we ++ // should actually get the error. ++ if (dbus_message_get_error_name( pMsg )) ++ { ++ SAL_INFO( "sdremote.bluetooth", "GetManagedObjects call failed with \"" ++ << dbus_message_get_error_name( pMsg ) ++ << "\" -- we don't seem to have Bluez 5 available"); ++ return false; ++ } ++ SAL_INFO("sdremote.bluetooth", "GetManagedObjects call seems to have succeeded -- we must be on Bluez 5"); ++ dbus_message_unref(pMsg); ++ return true; ++} ++ ++ ++static DBusObject* ++getBluez5Adapter(DBusConnection *pConnection) ++{ ++ DBusMessage *pMsg; ++ // This returns a list of objects where we need to find the first ++ // org.bluez.Adapter1 . ++ pMsg = DBusObject( "org.bluez", "/", "org.freedesktop.DBus.ObjectManager" ).getMethodCall( "GetManagedObjects" ); ++ if (!pMsg) ++ return NULL; ++ ++ const gchar* pInterfaceType = "org.bluez.Adapter1"; ++ ++ pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); ++ ++ DBusMessageIter aObjectIterator; ++ if (pMsg && dbus_message_iter_init(pMsg, &aObjectIterator)) ++ { ++ if (DBUS_TYPE_ARRAY == dbus_message_iter_get_arg_type(&aObjectIterator)) ++ { ++ DBusMessageIter aObject; ++ dbus_message_iter_recurse(&aObjectIterator, &aObject); ++ do ++ { ++ if (DBUS_TYPE_DICT_ENTRY == dbus_message_iter_get_arg_type(&aObject)) ++ { ++ DBusMessageIter aContainerIter; ++ dbus_message_iter_recurse(&aObject, &aContainerIter); ++ char *pPath = 0; ++ do ++ { ++ if (DBUS_TYPE_OBJECT_PATH == dbus_message_iter_get_arg_type(&aContainerIter)) ++ { ++ dbus_message_iter_get_basic(&aContainerIter, &pPath); ++ SAL_INFO( "sdremote.bluetooth", "Something retrieved: '" ++ << pPath << "' '"); ++ } ++ else if (DBUS_TYPE_ARRAY == dbus_message_iter_get_arg_type(&aContainerIter)) ++ { ++ DBusMessageIter aInnerIter; ++ dbus_message_iter_recurse(&aContainerIter, &aInnerIter); ++ do ++ { ++ if (DBUS_TYPE_DICT_ENTRY == dbus_message_iter_get_arg_type(&aInnerIter)) ++ { ++ DBusMessageIter aInnerInnerIter; ++ dbus_message_iter_recurse(&aInnerIter, &aInnerInnerIter); ++ do ++ { ++ if (DBUS_TYPE_STRING == dbus_message_iter_get_arg_type(&aInnerInnerIter)) ++ { ++ char* pMessage; ++ ++ dbus_message_iter_get_basic(&aInnerInnerIter, &pMessage); ++ if (OString(pMessage) == "org.bluez.Adapter1") ++ { ++ dbus_message_unref(pMsg); ++ if (pPath) ++ { ++ return new DBusObject( "org.bluez", pPath, pInterfaceType ); ++ } ++ assert(false); // We should already have pPath provided for us. ++ } ++ } ++ } ++ while (dbus_message_iter_next(&aInnerInnerIter)); ++ } ++ } ++ while (dbus_message_iter_next(&aInnerIter)); ++ } ++ } ++ while (dbus_message_iter_next(&aContainerIter)); ++ } ++ } ++ while (dbus_message_iter_next(&aObject)); ++ } ++ dbus_message_unref(pMsg); ++ } ++ ++ return NULL; ++} ++ + static DBusObject * +-bluezGetDefaultService( DBusConnection *pConnection ) ++bluez4GetDefaultService( DBusConnection *pConnection ) + { + DBusMessage *pMsg; + DBusMessageIter it; + const gchar* pInterfaceType = "org.bluez.Service"; + ++ // org.bluez.manager only exists for bluez 4. ++ // getMethodCall should return NULL if there is any issue e.g. the ++ // if org.bluez.manager doesn't exist. + pMsg = DBusObject( "org.bluez", "/", "org.bluez.Manager" ).getMethodCall( "DefaultAdapter" ); ++ ++ if (!pMsg) ++ { ++ SAL_WARN("sdremote.bluetooth", "Couldn't retrieve DBusObject for DefaultAdapter"); ++ return NULL; ++ } ++ ++ SAL_INFO("sdremote.bluetooth", "successfully retrieved org.bluez.Manager.DefaultAdapter, attempting to use."); + pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); + + if(!pMsg || !dbus_message_iter_init( pMsg, &it ) ) ++ { + return NULL; ++ } + +- if( DBUS_TYPE_OBJECT_PATH != dbus_message_iter_get_arg_type( &it ) ) +- SAL_INFO( "sdremote.bluetooth", "invalid type of reply to DefaultAdapter: '" +- << dbus_message_iter_get_arg_type( &it ) << "'" ); +- else ++ // This works for Bluez 4 ++ if( DBUS_TYPE_OBJECT_PATH == dbus_message_iter_get_arg_type( &it ) ) + { + const char *pObjectPath = NULL; + dbus_message_iter_get_basic( &it, &pObjectPath ); + SAL_INFO( "sdremote.bluetooth", "DefaultAdapter retrieved: '" +- << pObjectPath << "' '" << pInterfaceType << "'" ); ++ << pObjectPath << "' '" << pInterfaceType << "'" ); ++ dbus_message_unref( pMsg ); + return new DBusObject( "org.bluez", pObjectPath, pInterfaceType ); + } +- dbus_message_unref( pMsg ); +- ++ // Some form of error, e.g. if we have bluez 5 we get a message that ++ // this method doesn't exist. ++ else if ( DBUS_TYPE_STRING == dbus_message_iter_get_arg_type( &it ) ) ++ { ++ const char *pMessage = NULL; ++ dbus_message_iter_get_basic( &it, &pMessage ); ++ SAL_INFO( "sdremote.bluetooth", "Error message: '" ++ << pMessage << "' '" << pInterfaceType << "'" ); ++ } ++ else ++ { ++ SAL_INFO( "sdremote.bluetooth", "invalid type of reply to DefaultAdapter: '" ++ << (const char) dbus_message_iter_get_arg_type( &it ) << "'" ); ++ } ++ dbus_message_unref(pMsg); + return NULL; + } + + static bool +-bluezRegisterServiceRecord( DBusConnection *pConnection, DBusObject *pAdapter, ++bluez4RegisterServiceRecord( DBusConnection *pConnection, DBusObject *pAdapter, + const char *pServiceRecord ) + { + DBusMessage *pMsg; +@@ -443,8 +604,14 @@ extern "C" { + } + } + ++/* ++ * Bluez 4 uses custom methods for setting properties, whereas Bluez 5+ ++ * implements properties using the generic "org.freedesktop.DBus.Properties" ++ * interface -- hence we have a specific Bluez 4 function to deal with the ++ * old style of reading properties. ++ */ + static bool +-getBooleanProperty( DBusConnection *pConnection, DBusObject *pAdapter, ++getBluez4BooleanProperty( DBusConnection *pConnection, DBusObject *pAdapter, + const char *pPropertyName, bool *pBoolean ) + { + *pBoolean = false; +@@ -523,63 +690,391 @@ getBooleanProperty( DBusConnection *pConnection, DBusObject *pAdapter, + return false; + } + +-static void +-setDiscoverable( DBusConnection *pConnection, DBusObject *pAdapter, bool bDiscoverable ) ++/* ++ * This gets an org.freedesktop.DBus.Properties boolean ++ * (as opposed to the old Bluez 4 custom properties methods as visible above). ++ */ ++static bool ++getDBusBooleanProperty( DBusConnection *pConnection, DBusObject *pAdapter, ++ const char *pPropertyName, bool *pBoolean ) + { +- SAL_INFO( "sdremote.bluetooth", "setDiscoverable to " << bDiscoverable ); ++ assert( pAdapter ); + +- bool bPowered = false; +- if( !getBooleanProperty( pConnection, pAdapter, "Powered", &bPowered ) || !bPowered ) +- return; // nothing to do ++ *pBoolean = false; ++ bool bRet = false; + +- DBusMessage *pMsg; +- DBusMessageIter it, varIt; ++ ::boost::scoped_ptr< DBusObject > pProperties ( ++ pAdapter->cloneForInterface( "org.freedesktop.DBus.Properties" ) ); + +- // set timeout to zero +- pMsg = pAdapter->getMethodCall( "SetProperty" ); +- dbus_message_iter_init_append( pMsg, &it ); +- const char *pTimeoutStr = "DiscoverableTimeout"; +- dbus_message_iter_append_basic( &it, DBUS_TYPE_STRING, &pTimeoutStr ); +- dbus_message_iter_open_container( &it, DBUS_TYPE_VARIANT, +- DBUS_TYPE_UINT32_AS_STRING, &varIt ); +- dbus_uint32_t nTimeout = 0; +- dbus_message_iter_append_basic( &varIt, DBUS_TYPE_UINT32, &nTimeout ); +- dbus_message_iter_close_container( &it, &varIt ); +- dbus_connection_send( pConnection, pMsg, NULL ); // async send - why not ? +- dbus_message_unref( pMsg ); ++ DBusMessage *pMsg = pProperties->getMethodCall( "Get" ); + +- // set discoverable value +- pMsg = pAdapter->getMethodCall( "SetProperty" ); +- dbus_message_iter_init_append( pMsg, &it ); +- const char *pDiscoverableStr = "Discoverable"; +- dbus_message_iter_append_basic( &it, DBUS_TYPE_STRING, &pDiscoverableStr ); +- dbus_message_iter_open_container( &it, DBUS_TYPE_VARIANT, +- DBUS_TYPE_BOOLEAN_AS_STRING, &varIt ); +- dbus_bool_t bValue = bDiscoverable; +- dbus_message_iter_append_basic( &varIt, DBUS_TYPE_BOOLEAN, &bValue ); +- dbus_message_iter_close_container( &it, &varIt ); // async send - why not ? +- dbus_connection_send( pConnection, pMsg, NULL ); ++ DBusMessageIter itIn; ++ dbus_message_iter_init_append( pMsg, &itIn ); ++ const char* pInterface = "org.bluez.Adapter1"; ++ dbus_message_iter_append_basic( &itIn, DBUS_TYPE_STRING, &pInterface ); ++ dbus_message_iter_append_basic( &itIn, DBUS_TYPE_STRING, &pPropertyName ); ++ pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); ++ ++ DBusMessageIter it; ++ if( !pMsg || !dbus_message_iter_init( pMsg, &it ) ) ++ { ++ SAL_WARN( "sdremote.bluetooth", "no valid reply / timeout" ); ++ return false; ++ } ++ ++ if( DBUS_TYPE_VARIANT != dbus_message_iter_get_arg_type( &it ) ) ++ { ++ SAL_WARN( "sdremote.bluetooth", "invalid return type" ); ++ } ++ else ++ { ++ DBusMessageIter variantIt; ++ dbus_message_iter_recurse( &it, &variantIt ); ++ ++ if( dbus_message_iter_get_arg_type( &variantIt ) == DBUS_TYPE_BOOLEAN ) ++ { ++ dbus_bool_t bBool = false; ++ dbus_message_iter_get_basic( &variantIt, &bBool ); ++ SAL_INFO( "sdremote.bluetooth", "" << pPropertyName << " is " << bBool ); ++ *pBoolean = bBool; ++ bRet = true; ++ } ++ else ++ { ++ SAL_WARN( "sdremote.bluetooth", "" << pPropertyName << " type " << ++ dbus_message_iter_get_arg_type( &variantIt ) ); ++ } ++ ++ const char* pError = dbus_message_get_error_name( pMsg ); ++ if ( pError ) ++ { ++ SAL_WARN( "sdremote.bluetooth", ++ "Get failed for " << pPropertyName << " on " << ++ pAdapter->maPath << " with error: " << pError ); ++ } ++ } + dbus_message_unref( pMsg ); ++ ++ return bRet; ++} ++ ++static void ++setDBusBooleanProperty( DBusConnection *pConnection, DBusObject *pAdapter, ++ const char *pPropertyName, bool bBoolean ) ++{ ++ assert( pAdapter ); ++ ++ ::boost::scoped_ptr< DBusObject > pProperties( ++ pAdapter->cloneForInterface( "org.freedesktop.DBus.Properties" ) ); ++ ++ DBusMessage *pMsg = pProperties->getMethodCall( "Set" ); ++ ++ DBusMessageIter itIn; ++ dbus_message_iter_init_append( pMsg, &itIn ); ++ const char* pInterface = "org.bluez.Adapter1"; ++ dbus_message_iter_append_basic( &itIn, DBUS_TYPE_STRING, &pInterface ); ++ dbus_message_iter_append_basic( &itIn, DBUS_TYPE_STRING, &pPropertyName ); ++ ++ { ++ DBusMessageIter varIt; ++ dbus_message_iter_open_container( &itIn, DBUS_TYPE_VARIANT, ++ DBUS_TYPE_BOOLEAN_AS_STRING, &varIt ); ++ dbus_bool_t bDBusBoolean = bBoolean; ++ dbus_message_iter_append_basic( &varIt, DBUS_TYPE_BOOLEAN, &bDBusBoolean ); ++ dbus_message_iter_close_container( &itIn, &varIt ); ++ } ++ ++ pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); ++ ++ if( !pMsg ) ++ { ++ SAL_WARN( "sdremote.bluetooth", "no valid reply / timeout" ); ++ } ++ else ++ { ++ const char* pError = dbus_message_get_error_name( pMsg ); ++ if ( pError ) ++ { ++ SAL_WARN( "sdremote.bluetooth", ++ "Set failed for " << pPropertyName << " on " << ++ pAdapter->maPath << " with error: " << pError ); ++ } ++ dbus_message_unref( pMsg ); ++ } ++} ++ ++static bool ++getDiscoverable( DBusConnection *pConnection, DBusObject *pAdapter ) ++{ ++ if (pAdapter->maInterface == "org.bluez.Adapter") // Bluez 4 ++ { ++ bool bDiscoverable; ++ if( getBluez4BooleanProperty(pConnection, pAdapter, "Discoverable", &bDiscoverable ) ) ++ return bDiscoverable; ++ } ++ else if (pAdapter->maInterface == "org.bluez.Adapter1") // Bluez 5 ++ { ++ bool bDiscoverable; ++ if ( getDBusBooleanProperty(pConnection, pAdapter, "Discoverable", &bDiscoverable ) ) ++ return bDiscoverable; ++ } ++ return false; ++} ++ ++static void ++setDiscoverable( DBusConnection *pConnection, DBusObject *pAdapter, bool bDiscoverable ) ++{ ++ SAL_INFO( "sdremote.bluetooth", "setDiscoverable to " << bDiscoverable ); ++ ++ if (pAdapter->maInterface == "org.bluez.Adapter") // Bluez 4 ++ { ++ bool bPowered = false; ++ if( !getBluez4BooleanProperty( pConnection, pAdapter, "Powered", &bPowered ) || !bPowered ) ++ return; // nothing to do ++ ++ DBusMessage *pMsg; ++ DBusMessageIter it, varIt; ++ ++ // set timeout to zero ++ pMsg = pAdapter->getMethodCall( "SetProperty" ); ++ dbus_message_iter_init_append( pMsg, &it ); ++ const char *pTimeoutStr = "DiscoverableTimeout"; ++ dbus_message_iter_append_basic( &it, DBUS_TYPE_STRING, &pTimeoutStr ); ++ dbus_message_iter_open_container( &it, DBUS_TYPE_VARIANT, ++ DBUS_TYPE_UINT32_AS_STRING, &varIt ); ++ dbus_uint32_t nTimeout = 0; ++ dbus_message_iter_append_basic( &varIt, DBUS_TYPE_UINT32, &nTimeout ); ++ dbus_message_iter_close_container( &it, &varIt ); ++ dbus_connection_send( pConnection, pMsg, NULL ); // async send - why not ? ++ dbus_message_unref( pMsg ); ++ ++ // set discoverable value ++ pMsg = pAdapter->getMethodCall( "SetProperty" ); ++ dbus_message_iter_init_append( pMsg, &it ); ++ const char *pDiscoverableStr = "Discoverable"; ++ dbus_message_iter_append_basic( &it, DBUS_TYPE_STRING, &pDiscoverableStr ); ++ dbus_message_iter_open_container( &it, DBUS_TYPE_VARIANT, ++ DBUS_TYPE_BOOLEAN_AS_STRING, &varIt ); ++ dbus_bool_t bValue = bDiscoverable; ++ dbus_message_iter_append_basic( &varIt, DBUS_TYPE_BOOLEAN, &bValue ); ++ dbus_message_iter_close_container( &it, &varIt ); // async send - why not ? ++ dbus_connection_send( pConnection, pMsg, NULL ); ++ dbus_message_unref( pMsg ); ++ } ++ else if (pAdapter->maInterface == "org.bluez.Adapter1") // Bluez 5 ++ { ++ setDBusBooleanProperty(pConnection, pAdapter, "Discoverable", bDiscoverable ); ++ } + } + + static DBusObject * + registerWithDefaultAdapter( DBusConnection *pConnection ) + { + DBusObject *pService; +- pService = bluezGetDefaultService( pConnection ); +- if( !pService ) +- return NULL; +- +- if( !bluezRegisterServiceRecord( pConnection, pService, +- bluetooth_service_record ) ) ++ pService = bluez4GetDefaultService( pConnection ); ++ if( pService ) + { +- delete pService; +- return NULL; ++ if( !bluez4RegisterServiceRecord( pConnection, pService, ++ bluetooth_service_record ) ) ++ { ++ delete pService; ++ return NULL; ++ } + } + + return pService; + } + ++void ProfileUnregisterFunction ++(DBusConnection *connection, void *user_data) ++{ ++ // We specifically don't need to do anything here. ++ (void) connection; ++ (void) user_data; ++} ++ ++DBusHandlerResult ProfileMessageFunction ++(DBusConnection *pConnection, DBusMessage *pMessage, void *user_data) ++{ ++ SAL_INFO("sdremote.bluetooth", "ProfileMessageFunction||" << dbus_message_get_interface(pMessage) << "||" << dbus_message_get_member(pMessage)); ++ DBusHandlerResult aRet = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; ++ ++ if (OString(dbus_message_get_interface(pMessage)).equals("org.bluez.Profile1")) ++ { ++ if (OString(dbus_message_get_member(pMessage)).equals("Release")) ++ { ++ return DBUS_HANDLER_RESULT_HANDLED; ++ } ++ else if (OString(dbus_message_get_member(pMessage)).equals("NewConnection")) ++ { ++ if (!dbus_message_has_signature(pMessage, "oha{sv}")) ++ { ++ SAL_WARN("sdremote.bluetooth", "wrong signature for NewConnection"); ++ } ++ ++ DBusMessageIter it; ++ dbus_message_iter_init(pMessage, &it); ++ ++ char* pPath; ++ dbus_message_iter_get_basic(&it, &pPath); ++ SAL_INFO("sdremote.bluetooth", "Adapter path:" << pPath); ++ ++ if (!dbus_message_iter_next(&it)) ++ SAL_WARN("sdremote.bluetooth", "not enough parameters passed"); ++ ++ // DBUS_TYPE_UNIX_FD == 'h' -- doesn't exist in older versions ++ // of dbus (< 1.3?) hence defined manually for now ++ if ('h' == dbus_message_iter_get_arg_type(&it)) ++ { ++ ++ int nDescriptor; ++ dbus_message_iter_get_basic(&it, &nDescriptor); ++ std::vector* pCommunicators = (std::vector*) user_data; ++ ++ // Bluez gives us non-blocking sockets, but our code relies ++ // on blocking behaviour. ++ fcntl(nDescriptor, F_SETFL, fcntl(nDescriptor, F_GETFL) & ~O_NONBLOCK); ++ ++ SAL_INFO( "sdremote.bluetooth", "connection accepted " << nDescriptor); ++ Communicator* pCommunicator = new Communicator( new BufferedStreamSocket( nDescriptor ) ); ++ pCommunicators->push_back( pCommunicator ); ++ pCommunicator->launch(); ++ } ++ ++ // For some reason an (empty?) reply is expected. ++ DBusMessage* pRet = dbus_message_new_method_return(pMessage); ++ dbus_connection_send(pConnection, pRet, NULL); ++ dbus_message_unref(pRet); ++ ++ // We could read the remote profile version and features here ++ // (i.e. they are provided as part of the DBusMessage), ++ // however for us they are irrelevant (as our protocol handles ++ // equivalent functionality independently of whether we're on ++ // bluetooth or normal network connection). ++ return DBUS_HANDLER_RESULT_HANDLED; ++ } ++ else if (OString(dbus_message_get_member(pMessage)).equals("RequestDisconnection")) ++ { ++ return DBUS_HANDLER_RESULT_HANDLED; ++ } ++ } ++ SAL_WARN("sdremote.bluetooth", "Couldn't handle message correctly."); ++ return aRet; ++ ++} ++ ++static void ++setupBluez5Profile1(DBusConnection* pConnection, std::vector* pCommunicators) ++{ ++ bool bErr; ++ ++ SAL_INFO("sdremote.bluetooth", "Attempting to register our org.bluez.Profile1"); ++ static DBusObjectPathVTable aVTable; ++ aVTable.unregister_function = ProfileUnregisterFunction; ++ aVTable.message_function = ProfileMessageFunction; ++ ++ // dbus_connection_try_register_object_path could be used but only exists for ++ // dbus-glib >= 1.2 -- we really shouldn't be trying this twice in any case. ++ // (dbus_connection_try_register_object_path also returns an error with more ++ // information which could be useful for debugging purposes.) ++ bErr = !dbus_connection_register_object_path(pConnection, "/org/libreoffice/bluez/profile1", &aVTable, pCommunicators); ++ ++ if (bErr) ++ { ++ SAL_WARN("sdremote.bluetooth", "Failed to register Bluez 5 Profile1 callback, bluetooth won't work."); ++ } ++ ++ dbus_connection_flush( pConnection ); ++} ++ ++static void ++unregisterBluez5Profile(DBusConnection* pConnection) ++{ ++ DBusMessage* pMsg = dbus_message_new_method_call("org.bluez", "/org/bluez", ++ "org.bluez.ProfileManager1", "UnregisterProfile"); ++ DBusMessageIter it; ++ dbus_message_iter_init_append(pMsg, &it); ++ ++ const char *pPath = "/org/libreoffice/bluez/profile1"; ++ dbus_message_iter_append_basic(&it, DBUS_TYPE_OBJECT_PATH, &pPath); ++ ++ pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); ++ ++ if (pMsg) ++ dbus_message_unref(pMsg); ++ ++ dbus_connection_unregister_object_path( pConnection, "/org/libreoffice/bluez/profile1"); ++ ++ dbus_connection_flush(pConnection); ++} ++ ++static bool ++registerBluez5Profile(DBusConnection* pConnection, std::vector* pCommunicators) ++{ ++ setupBluez5Profile1(pConnection, pCommunicators); ++ ++ DBusMessage *pMsg; ++ DBusMessageIter it; ++ ++ pMsg = dbus_message_new_method_call("org.bluez", "/org/bluez", ++ "org.bluez.ProfileManager1", "RegisterProfile"); ++ dbus_message_iter_init_append(pMsg, &it); ++ ++ const char *pPath = "/org/libreoffice/bluez/profile1"; ++ dbus_message_iter_append_basic(&it, DBUS_TYPE_OBJECT_PATH, &pPath); ++ const char *pUUID = "spp"; // Bluez translates this to 0x1101 for spp ++ dbus_message_iter_append_basic(&it, DBUS_TYPE_STRING, &pUUID); ++ ++ DBusMessageIter aOptionsIter; ++ dbus_message_iter_open_container(&it, DBUS_TYPE_ARRAY, "{sv}", &aOptionsIter); ++ ++ DBusMessageIter aEntry; ++ ++ { ++ dbus_message_iter_open_container(&aOptionsIter, DBUS_TYPE_DICT_ENTRY, NULL, &aEntry); ++ ++ const char *pString = "Name"; ++ dbus_message_iter_append_basic(&aEntry, DBUS_TYPE_STRING, &pString); ++ ++ const char *pValue = "LibreOffice Impress Remote"; ++ DBusMessageIter aValue; ++ dbus_message_iter_open_container(&aEntry, DBUS_TYPE_VARIANT, "s", &aValue); ++ dbus_message_iter_append_basic(&aValue, DBUS_TYPE_STRING, &pValue); ++ dbus_message_iter_close_container(&aEntry, &aValue); ++ dbus_message_iter_close_container(&aOptionsIter, &aEntry); ++ } ++ ++ dbus_message_iter_close_container(&it, &aOptionsIter); ++ ++ // Other properties that we could set (but don't, since they appear ++ // to be useless for us): ++ // "Service": "0x1101" (not needed, but we used to have it in the manually defined profile). ++ // "Role": setting this to "server" breaks things, although we think we're a server? ++ // "Channel": seems to be dealt with automatically (but we used to use 5 in the manual profile). ++ ++ bool bSuccess = true; ++ ++ pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); ++ ++ DBusError aError; ++ dbus_error_init(&aError); ++ if (pMsg && dbus_set_error_from_message( &aError, pMsg )) ++ { ++ bSuccess = false; ++ SAL_WARN("sdremote.bluetooth", ++ "Failed to register our Profile1 with bluez ProfileManager " ++ << (const char *)(aError.message ? aError.message : "")); ++ } ++ ++ dbus_error_free(&aError); ++ if (pMsg) ++ dbus_message_unref(pMsg); ++ ++ dbus_connection_flush(pConnection); ++ ++ return bSuccess; ++} ++ + #endif // LINUX_BLUETOOTH + + BluetoothServer::BluetoothServer( std::vector* pCommunicators ) +@@ -642,14 +1137,11 @@ void BluetoothServer::doEnsureDiscoverable() + if( !pAdapter ) + return; + +- bool bDiscoverable; +- if( getBooleanProperty( spServer->mpImpl->mpConnection, pAdapter, +- "Discoverable", &bDiscoverable ) ) +- { +- spServer->meWasDiscoverable = bDiscoverable ? DISCOVERABLE : NOT_DISCOVERABLE; +- if( !bDiscoverable ) +- setDiscoverable( spServer->mpImpl->mpConnection, pAdapter, true ); +- } ++ bool bDiscoverable = getDiscoverable(spServer->mpImpl->mpConnection, pAdapter ); ++ ++ spServer->meWasDiscoverable = bDiscoverable ? DISCOVERABLE : NOT_DISCOVERABLE; ++ if( !bDiscoverable ) ++ setDiscoverable( spServer->mpImpl->mpConnection, pAdapter, true ); + + delete pAdapter; + #endif +@@ -690,6 +1182,56 @@ void SAL_CALL BluetoothServer::run() + if( !pConnection ) + return; + ++ ++ // For either implementation we need to poll the dbus fd ++ int fd = -1; ++ GPollFD aDBusFD; ++ if( dbus_connection_get_unix_fd( pConnection, &fd ) && fd >= 0 ) ++ { ++ aDBusFD.fd = fd; ++ aDBusFD.events = G_IO_IN | G_IO_PRI; ++ g_main_context_add_poll( mpImpl->mpContext, &aDBusFD, G_PRIORITY_DEFAULT ); ++ } ++ else ++ SAL_WARN( "sdremote.bluetooth", "failed to poll for incoming dbus signals" ); ++ ++ if (isBluez5Available(pConnection)) ++ { ++ SAL_INFO("sdremote.bluetooth", "Using Bluez 5"); ++ registerBluez5Profile(pConnection, mpCommunicators); ++ mpImpl->mpConnection = pConnection; ++ mpImpl->maBluezVersion = Impl::BLUEZ5; ++ ++ // We don't need to listen to adapter changes anymore -- profile ++ // registration is done globally for the entirety of bluez, so we only ++ // need adapters when setting discovereability, which can be done ++ // dyanmically without the need to listen for changes. ++ ++ // TODO: exit on SD deinit ++ // Probably best to do that in SdModule::~SdModule? ++ while (!mpImpl->mbExitMainloop) ++ { ++ aDBusFD.revents = 0; ++ g_main_context_iteration( mpImpl->mpContext, TRUE ); ++ if( aDBusFD.revents ) ++ { ++ dbus_connection_read_write( pConnection, 0 ); ++ while (DBUS_DISPATCH_DATA_REMAINS == dbus_connection_get_dispatch_status( pConnection )) ++ dbus_connection_dispatch( pConnection ); ++ } ++ } ++ unregisterBluez5Profile( pConnection ); ++ g_main_context_unref( mpImpl->mpContext ); ++ mpImpl->mpConnection = NULL; ++ mpImpl->mpContext = NULL; ++ return; ++ } ++ ++ // Otherwise we could be on Bluez 4 and continue as usual. ++ mpImpl->maBluezVersion = Impl::BLUEZ4; ++ ++ // Try to setup the default adapter, otherwise wait for add/remove signal ++ mpImpl->mpService = registerWithDefaultAdapter( pConnection ); + // listen for connection state and power changes - we need to close + // and re-create our socket code on suspend / resume, enable/disable + DBusError aError; +@@ -705,18 +1247,6 @@ void SAL_CALL BluetoothServer::run() + if( mpImpl->mpService ) + bluezCreateAttachListeningSocket( mpImpl->mpContext, &aSocketFD ); + +- // also poll on our dbus connection +- int fd = -1; +- GPollFD aDBusFD; +- if( dbus_connection_get_unix_fd( pConnection, &fd ) && fd >= 0 ) +- { +- aDBusFD.fd = fd; +- aDBusFD.events = G_IO_IN | G_IO_PRI; +- g_main_context_add_poll( mpImpl->mpContext, &aDBusFD, G_PRIORITY_DEFAULT ); +- } +- else +- SAL_WARN( "sdremote.bluetooth", "failed to poll for incoming dbus signals" ); +- + mpImpl->mpConnection = pConnection; + + while( !mpImpl->mbExitMainloop ) +@@ -779,6 +1309,7 @@ void SAL_CALL BluetoothServer::run() + } + } + ++ unregisterBluez5Profile( pConnection ); + g_main_context_unref( mpImpl->mpContext ); + mpImpl->mpConnection = NULL; + mpImpl->mpContext = NULL; +diff --git a/sd/source/ui/remotecontrol/BufferedStreamSocket.cxx b/sd/source/ui/remotecontrol/BufferedStreamSocket.cxx +index 4b4c1ce..4417e09 100644 +--- a/sd/source/ui/remotecontrol/BufferedStreamSocket.cxx ++++ b/sd/source/ui/remotecontrol/BufferedStreamSocket.cxx +@@ -61,7 +61,7 @@ sal_Int32 BufferedStreamSocket::write( const void* pBuffer, sal_uInt32 n ) + + void BufferedStreamSocket::close() + { +- if( usingCSocket ) ++ if( usingCSocket && mSocket != -1 ) + { + #ifdef WIN32 + ::closesocket( mSocket ); +diff --git a/sd/source/ui/remotecontrol/Communicator.cxx b/sd/source/ui/remotecontrol/Communicator.cxx +index 4b2dc84..d3af697 100644 +--- a/sd/source/ui/remotecontrol/Communicator.cxx ++++ b/sd/source/ui/remotecontrol/Communicator.cxx +@@ -122,6 +122,8 @@ void Communicator::execute() + pTransmitter->join(); + pTransmitter = NULL; + ++ if( mpSocket ) ++ mpSocket->close(); + delete mpSocket; + + +-- +1.8.4.5 + diff --git a/kde4-4.2.3.3-timer-mutex.patch b/kde4-4.2.3.3-timer-mutex.patch new file mode 100644 index 0000000..025550a --- /dev/null +++ b/kde4-4.2.3.3-timer-mutex.patch @@ -0,0 +1,46 @@ +From 7dba6e0a71d090f06a6a1a39e87572674593b48a Mon Sep 17 00:00:00 2001 +From: Jan-Marek Glogowski +Date: Mon, 10 Mar 2014 14:44:05 +0000 +Subject: fdo#73115: Always run timeouts as events + +Right-click popup menus run click events throught the LO main loop. +In case of KDE4 the LO main loop is run by a timer in the main thread, +with Qt::DirectConnection execution. + +If the timeout actually starts a nested event loop for a new dialog, +the timer is blocked, the nested mainloop detects it was started +from the timeout and drops the blocked timout from polling, which +blocks any further LibreOffice event loop processing. + +This changes the timers to Qt::QueuedConnection, so they always +generate an event and are processed by the Qt event loop. + +Change-Id: Ie626b22be3d8f9b8934bcc5e9e0e67a365549cfc +(cherry picked from commit aeda478a02523cec146f6af69710f0391061db56) +Reviewed-on: https://gerrit.libreoffice.org/8514 +Reviewed-by: Caolán McNamara +Tested-by: Caolán McNamara +--- +diff --git a/vcl/unx/kde4/KDEXLib.cxx b/vcl/unx/kde4/KDEXLib.cxx +index b4be6d6..4a9b70b 100644 +--- a/vcl/unx/kde4/KDEXLib.cxx ++++ b/vcl/unx/kde4/KDEXLib.cxx +@@ -67,9 +67,13 @@ KDEXLib::KDEXLib() : + eventLoopType( LibreOfficeEventLoop ), + m_bYieldFrozen( false ) + { +- // the timers created here means they belong to the main thread +- connect( &timeoutTimer, SIGNAL( timeout()), this, SLOT( timeoutActivated())); +- connect( &userEventTimer, SIGNAL( timeout()), this, SLOT( userEventActivated())); ++ // the timers created here means they belong to the main thread. ++ // As the timeoutTimer runs the LO event queue, which may block on a dialog, ++ // the timer has to use a Qt::QueuedConnection, otherwise the nested event ++ // loop will detect the blocking timer and drop it from the polling ++ // freezing LO X11 processing. ++ connect( &timeoutTimer, SIGNAL( timeout()), this, SLOT( timeoutActivated()), Qt::QueuedConnection ); ++ connect( &userEventTimer, SIGNAL( timeout()), this, SLOT( userEventActivated()), Qt::QueuedConnection ); + + // QTimer::start() can be called only in its (here main) thread, so this will + // forward between threads if needed +-- +cgit v0.9.0.2-2-gbebe diff --git a/libreoffice.changes b/libreoffice.changes index 7f5aa80..2efcd02 100644 --- a/libreoffice.changes +++ b/libreoffice.changes @@ -1,3 +1,18 @@ +------------------------------------------------------------------- +Tue Apr 15 08:32:38 UTC 2014 - tchvatal@suse.com + +- Fix haning in KDE by applying upstream fix. Will be included + in next release. +- added patches: + * kde4-4.2.3.3-timer-mutex.patch + +------------------------------------------------------------------- +Mon Apr 14 20:00:00 UTC 2014 - tbehrens@suse.com + +- fix for non-working bluetooth remote control from upstream +- added patches: + * bluez5-support-for-impress-remote.diff + ------------------------------------------------------------------- Fri Apr 11 14:14:14 UTC 2014 - tchvatal@suse.com diff --git a/libreoffice.spec b/libreoffice.spec index 6559693..2bae4d3 100644 --- a/libreoffice.spec +++ b/libreoffice.spec @@ -87,6 +87,10 @@ Patch12: mediawiki-no-broken-help.diff Patch13: jvmfwk-disable-gcj.diff # Fix running wizzards in py2 as the utf is not htere Patch16: wizards-create-temlates-with-python-2.6.diff +# Fix fdo#74697 add Bluez 5 support for impress remote. +Patch17: bluez5-support-for-impress-remote.diff +# PATCH-FIX-UPSTREAM: fix kde hanging in 4.2.3.3 +Patch18: kde4-4.2.3.3-timer-mutex.patch # try to save space by using hardlinks Patch990: install-with-hardlinks.diff BuildRequires: ImageMagick @@ -837,6 +841,8 @@ Provides additional %{langname} translations and resources for %{project}. \ %patch12 %patch13 -p1 %patch16 -p1 +%patch17 -p1 +%patch18 -p1 -R %patch990 -p1 # 256x256 icons tar -xjf %{SOURCE20}