]> granicus.if.org Git - transmission/commitdiff
Add ActiveQt-based COM interop helper
authorMike Gelfand <mikedld@mikedld.com>
Wed, 16 Dec 2015 20:01:03 +0000 (20:01 +0000)
committerMike Gelfand <mikedld@mikedld.com>
Wed, 16 Dec 2015 20:01:03 +0000 (20:01 +0000)
CMakeLists.txt
qt/Application.cc
qt/CMakeLists.txt
qt/ComInteropHelper.cc [new file with mode: 0644]
qt/ComInteropHelper.h [new file with mode: 0644]
qt/InteropHelper.cc [new file with mode: 0644]
qt/InteropHelper.h [new file with mode: 0644]
qt/InteropObject.h
qt/transmission-qt.idl [new file with mode: 0644]
qt/transmission-qt.tlb.rc [new file with mode: 0644]

index 925229d22a4db80c5cd2bfb4a2cb69485acf5fce..ed4a06c7632d0048a77802459d26404bfe2dc107 100644 (file)
@@ -223,7 +223,8 @@ if(ENABLE_QT)
 
     set(QT_TARGETS)
     if(USE_QT5)
-        set(QT5_REQUIRED_MODULES Core Gui Widgets Network DBus LinguistTools)
+        set(QT5_REQUIRED_MODULES Core Gui Widgets Network LinguistTools)
+        set(QT5_OPTIONAL_MODULES DBus AxContainer AxServer)
         foreach(M ${QT5_REQUIRED_MODULES})
             find_package(Qt5${M} QUIET)
             if(Qt5${M}_FOUND)
@@ -235,9 +236,18 @@ if(ENABLE_QT)
                 break()
             endif()
         endforeach()
+        if(QT_TARGETS)
+            foreach(M ${QT5_OPTIONAL_MODULES})
+                find_package(Qt5${M} QUIET)
+                if(Qt5${M}_FOUND)
+                    list(APPEND QT_TARGETS Qt5::${M})
+                endif()
+            endforeach()
+        endif()
     else()
-        set(QT4_REQUIRED_MODULES QtCore QtGui QtNetwork QtDBus)
-        find_package(Qt4 4.6.2 QUIET COMPONENTS ${QT4_REQUIRED_MODULES})
+        set(QT4_REQUIRED_MODULES QtCore QtGui QtNetwork)
+        set(QT4_OPTIONAL_MODULES QtDBus QAxContainer QAxServer)
+        find_package(Qt4 4.6.2 QUIET COMPONENTS ${QT4_REQUIRED_MODULES} OPTIONAL_COMPONENTS ${QT4_OPTIONAL_MODULES})
         foreach(M ${QT4_REQUIRED_MODULES})
             string(TOUPPER "${M}" M_UPPER)
             if(QT_${M_UPPER}_FOUND)
@@ -247,6 +257,14 @@ if(ENABLE_QT)
                 break()
             endif()
         endforeach()
+        if (QT_TARGETS)
+            foreach(M ${QT4_OPTIONAL_MODULES})
+                string(TOUPPER "${M}" M_UPPER)
+                if(QT_${M_UPPER}_FOUND)
+                    list(APPEND QT_TARGETS Qt4::${M})
+                endif()
+            endforeach()
+        endif()
     endif()
 
     set(QT_FOUND ON)
index 51e56dafd687ec12ec2048767a670163995a2ed6..cdde7773b81ed430e7d0b0cb43facfa95f5f4d50 100644 (file)
@@ -10,9 +10,6 @@
 #include <ctime>
 #include <iostream>
 
-#include <QDBusConnection>
-#include <QDBusMessage>
-#include <QDBusReply>
 #include <QIcon>
 #include <QLibraryInfo>
 #include <QMessageBox>
 #include <QRect>
 #include <QSystemTrayIcon>
 
+#ifdef QT_DBUS_LIB
+  #include <QDBusConnection>
+  #include <QDBusMessage>
+  #include <QDBusReply>
+#endif
+
 #include <libtransmission/transmission.h>
 #include <libtransmission/tr-getopt.h>
 #include <libtransmission/utils.h>
@@ -27,8 +30,8 @@
 
 #include "AddData.h"
 #include "Application.h"
-#include "DBusInteropHelper.h"
 #include "Formatter.h"
+#include "InteropHelper.h"
 #include "MainWindow.h"
 #include "OptionsDialog.h"
 #include "Prefs.h"
@@ -157,7 +160,7 @@ Application::Application (int& argc, char ** argv):
 
   // try to delegate the work to an existing copy of Transmission
   // before starting ourselves...
-  DBusInteropHelper interopClient;
+  InteropHelper interopClient;
   if (interopClient.isConnected ())
     {
       bool delegated = false;
@@ -175,11 +178,7 @@ Application::Application (int& argc, char ** argv):
               default:                break;
             }
 
-          if (metainfo.isEmpty ())
-            continue;
-
-          const QVariant result = interopClient.addMetainfo (metainfo);
-          if (result.isValid () && result.toBool ())
+          if (!metainfo.isEmpty () && interopClient.addMetainfo (metainfo))
             delegated = true;
         }
 
@@ -294,7 +293,7 @@ Application::Application (int& argc, char ** argv):
   for (const QString& filename: filenames)
     addTorrent (filename);
 
-  DBusInteropHelper::registerObject (this);
+  InteropHelper::registerObject (this);
 }
 
 void
@@ -542,6 +541,7 @@ Application::raise ()
 bool
 Application::notifyApp (const QString& title, const QString& body) const
 {
+#ifdef QT_DBUS_LIB
   const QLatin1String dbusServiceName ("org.freedesktop.Notifications");
   const QLatin1String dbusInterfaceName ("org.freedesktop.Notifications");
   const QLatin1String dbusPath ("/org/freedesktop/Notifications");
@@ -564,6 +564,7 @@ Application::notifyApp (const QString& title, const QString& body) const
       if (replyMsg.isValid () && replyMsg.value () > 0)
         return true;
     }
+#endif
 
   myWindow->trayIcon ().showMessage (title, body);
   return true;
@@ -582,6 +583,8 @@ int
 tr_main (int    argc,
          char * argv[])
 {
+  InteropHelper::initialize ();
+
   Application app (argc, argv);
   return app.exec ();
 }
index 67d4e9b56010bd4bc47bd2bfc3f74cf4c9140bc4..56c4a37514fabdee4f5ef5f70290d15b6ce941fe 100644 (file)
@@ -22,11 +22,26 @@ else()
     endmacro()
 endif()
 
+set(ENABLE_COM_INTEROP OFF)
+if(MSVC AND ((Qt5AxContainer_FOUND AND Qt5AxServer_FOUND) OR (QT_QAXCONTAINER_FOUND AND QT_QAXSERVER_FOUND)))
+    set(ENABLE_COM_INTEROP ON)
+endif()
+
+set(ENABLE_DBUS_INTEROP OFF)
+if(Qt5DBus_FOUND OR QT_QTDBUS_FOUND)
+    set(ENABLE_DBUS_INTEROP ON)
+endif()
+
+if(NOT ENABLE_COM_INTEROP AND NOT ENABLE_DBUS_INTEROP)
+    message(FATAL_ERROR "Neither D-Bus nor COM interop is possible")
+endif()
+
 set(${PROJECT_NAME}_SOURCES
     AboutDialog.cc
     AddData.cc
     Application.cc
     ColumnResizer.cc
+    ComInteropHelper.cc
     DBusInteropHelper.cc
     DetailsDialog.cc
     FaviconCache.cc
@@ -42,6 +57,7 @@ set(${PROJECT_NAME}_SOURCES
     Formatter.cc
     FreeSpaceLabel.cc
     IconToolButton.cc
+    InteropHelper.cc
     InteropObject.cc
     LicenseDialog.cc
     MainWindow.cc
@@ -69,12 +85,20 @@ set(${PROJECT_NAME}_SOURCES
     WatchDir.cc
 )
 
+if (NOT ENABLE_COM_INTEROP)
+    set_source_files_properties(ComInteropHelper.cc PROPERTIES HEADER_FILE_ONLY ON)
+endif()
+if (NOT ENABLE_DBUS_INTEROP)
+    set_source_files_properties(DBusInteropHelper.cc PROPERTIES HEADER_FILE_ONLY ON)
+endif()
+
 set(${PROJECT_NAME}_HEADERS
     AboutDialog.h
     AddData.h
     Application.h
     BaseDialog.h
     ColumnResizer.h
+    ComInteropHelper.h
     CustomVariantType.h
     DBusInteropHelper.h
     DetailsDialog.h
@@ -91,6 +115,7 @@ set(${PROJECT_NAME}_HEADERS
     Formatter.h
     FreeSpaceLabel.h
     IconToolButton.h
+    InteropHelper.h
     InteropObject.h
     LicenseDialog.h
     MainWindow.h
@@ -182,17 +207,28 @@ include_directories(
     ${EVENT2_INCLUDE_DIRS}
 )
 
-add_definitions(
-    "-DTRANSLATIONS_DIR=\"${CMAKE_INSTALL_FULL_DATADIR}/${TR_NAME}/translations\""
-    -DQT_NO_CAST_FROM_ASCII
-)
-
 tr_win32_app_info(${PROJECT_NAME}_WIN32_RC_FILE
     "Transmission Qt Client"
     "transmission-qt"
     "transmission-qt.exe"
     "qtr.ico")
 
+if(ENABLE_COM_INTEROP)
+    find_program(MIDL_EXECUTABLE midl)
+    add_custom_command(
+        OUTPUT
+            ${CMAKE_CURRENT_BINARY_DIR}/transmission-qt.tlb
+        COMMAND
+            ${MIDL_EXECUTABLE} /tlb ${CMAKE_CURRENT_BINARY_DIR}/transmission-qt.tlb transmission-qt.idl
+        DEPENDS
+            transmission-qt.idl
+        WORKING_DIRECTORY
+            ${CMAKE_CURRENT_SOURCE_DIR}
+    )
+    list(APPEND ${PROJECT_NAME}_WIN32_RC_FILE transmission-qt.tlb.rc transmission-qt.idl ${CMAKE_CURRENT_BINARY_DIR}/transmission-qt.tlb)
+    set_source_files_properties(transmission-qt.idl ${CMAKE_CURRENT_BINARY_DIR}/transmission-qt.tlb PROPERTIES HEADER_FILE_ONLY ON)
+endif()
+
 add_executable(${TR_NAME}-qt WIN32
     ${${PROJECT_NAME}_SOURCES}
     ${${PROJECT_NAME}_UI_SOURCES}
@@ -209,6 +245,12 @@ target_link_libraries(${TR_NAME}-qt
     ${EVENT2_LIBRARIES}
 )
 
+target_compile_definitions(${TR_NAME}-qt PRIVATE
+    "TRANSLATIONS_DIR=\"${CMAKE_INSTALL_FULL_DATADIR}/${TR_NAME}/translations\""
+    QT_NO_CAST_FROM_ASCII
+    $<$<BOOL:${ENABLE_COM_INTEROP}>:ENABLE_COM_INTEROP>
+    $<$<BOOL:${ENABLE_DBUS_INTEROP}>:ENABLE_DBUS_INTEROP>)
+
 if(MSVC)
     tr_append_target_property(${TR_NAME}-qt LINK_FLAGS "/ENTRY:mainCRTStartup")
 endif()
diff --git a/qt/ComInteropHelper.cc b/qt/ComInteropHelper.cc
new file mode 100644 (file)
index 0000000..b27b59a
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * This file Copyright (C) 2015 Mnemosyne LLC
+ *
+ * It may be used under the GNU GPL versions 2 or 3
+ * or any future license endorsed by Mnemosyne LLC.
+ *
+ * $Id$
+ */
+
+#include <windows.h>
+#include <objbase.h>
+
+#include <QAxFactory>
+#include <QAxObject>
+#include <QString>
+#include <QVariant>
+
+#include "ComInteropHelper.h"
+#include "InteropObject.h"
+
+QAXFACTORY_BEGIN("{1e405fc2-1a3a-468b-8bd6-bfbb58770390}", "{792d1aac-53cc-4dc9-bc29-e5295fdb93a9}")
+  QAXCLASS(InteropObject)
+QAXFACTORY_END()
+
+// These are ActiveQt internals; declaring here as I don't like their WinMain much...
+extern HANDLE qAxInstance;
+extern bool qAxOutProcServer;
+extern wchar_t qAxModuleFilename[MAX_PATH];
+extern QString qAxInit();
+
+ComInteropHelper::ComInteropHelper ():
+  m_client (new QAxObject (QLatin1String ("Transmission.QtClient")))
+{
+}
+
+ComInteropHelper::~ComInteropHelper ()
+{
+}
+
+bool
+ComInteropHelper::isConnected () const
+{
+  return !m_client->isNull ();
+}
+    
+QVariant
+ComInteropHelper::addMetainfo (const QString& metainfo)
+{
+  return m_client->dynamicCall ("AddMetainfo(QString)", metainfo);
+}
+
+void
+ComInteropHelper::initialize ()
+{
+  qAxOutProcServer = true;
+  ::GetModuleFileNameW (0, qAxModuleFilename, MAX_PATH);
+  qAxInstance = ::GetModuleHandleW (NULL);
+
+  ::CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
+  qAxInit ();
+}
+
+void
+ComInteropHelper::registerObject (QObject * parent)
+{
+  QAxFactory::startServer();
+  QAxFactory::registerActiveObject(new InteropObject (parent));
+}
diff --git a/qt/ComInteropHelper.h b/qt/ComInteropHelper.h
new file mode 100644 (file)
index 0000000..9f7322b
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * This file Copyright (C) 2015 Mnemosyne LLC
+ *
+ * It may be used under the GNU GPL versions 2 or 3
+ * or any future license endorsed by Mnemosyne LLC.
+ *
+ * $Id$
+ */
+
+#ifndef QTR_COM_INTEROP_HELPER_H
+#define QTR_COM_INTEROP_HELPER_H
+
+#include <memory>
+
+class QAxObject;
+class QObject;
+class QString;
+class QVariant;
+
+class ComInteropHelper
+{
+  public:
+    ComInteropHelper ();
+    ~ComInteropHelper ();
+
+    bool isConnected () const;
+
+    QVariant addMetainfo (const QString& metainfo);
+
+    static void initialize ();
+    static void registerObject (QObject * parent);
+
+  private:
+    std::unique_ptr<QAxObject> m_client;
+};
+
+#endif // QTR_COM_INTEROP_HELPER_H
diff --git a/qt/InteropHelper.cc b/qt/InteropHelper.cc
new file mode 100644 (file)
index 0000000..bf38d64
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * This file Copyright (C) 2015 Mnemosyne LLC
+ *
+ * It may be used under the GNU GPL versions 2 or 3
+ * or any future license endorsed by Mnemosyne LLC.
+ *
+ * $Id$
+ */
+
+#include <QVariant>
+
+#include "InteropHelper.h"
+
+bool
+InteropHelper::isConnected () const
+{
+#ifdef ENABLE_DBUS_INTEROP
+  if (myDbusClient.isConnected ())
+    return true;
+#endif
+
+#ifdef ENABLE_COM_INTEROP
+  if (myComClient.isConnected ())
+    return true;
+#endif
+
+  return false;
+}
+
+bool
+InteropHelper::addMetainfo (const QString& metainfo)
+{
+#ifdef ENABLE_DBUS_INTEROP
+  {
+    const QVariant response = myDbusClient.addMetainfo (metainfo);
+    if (response.isValid () && response.toBool ())
+      return true;
+  }
+#endif
+
+#ifdef ENABLE_COM_INTEROP
+  {
+    const QVariant response = myComClient.addMetainfo (metainfo);
+    if (response.isValid () && response.toBool ())
+      return true;
+  }
+#endif
+
+  return false;
+}
+
+void
+InteropHelper::initialize ()
+{
+#ifdef ENABLE_COM_INTEROP
+  ComInteropHelper::initialize ();
+#endif
+}
+
+void
+InteropHelper::registerObject (QObject * parent)
+{
+#ifdef ENABLE_DBUS_INTEROP
+  DBusInteropHelper::registerObject (parent);
+#endif
+
+#ifdef ENABLE_COM_INTEROP
+  ComInteropHelper::registerObject (parent);
+#endif
+}
diff --git a/qt/InteropHelper.h b/qt/InteropHelper.h
new file mode 100644 (file)
index 0000000..a395a80
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * This file Copyright (C) 2015 Mnemosyne LLC
+ *
+ * It may be used under the GNU GPL versions 2 or 3
+ * or any future license endorsed by Mnemosyne LLC.
+ *
+ * $Id$
+ */
+
+#ifndef QTR_INTEROP_HELPER_H
+#define QTR_INTEROP_HELPER_H
+
+#ifdef ENABLE_COM_INTEROP
+  #include "ComInteropHelper.h"
+#endif
+#ifdef ENABLE_DBUS_INTEROP
+  #include "DBusInteropHelper.h"
+#endif
+
+class QAxObject;
+class QString;
+class QVariant;
+
+class InteropHelper
+{
+  public:
+    bool isConnected () const;
+
+    bool addMetainfo (const QString& metainfo);
+
+    static void initialize ();
+    static void registerObject (QObject * parent);
+
+  private:
+#ifdef ENABLE_DBUS_INTEROP
+    DBusInteropHelper myDbusClient;
+#endif
+#ifdef ENABLE_COM_INTEROP
+    ComInteropHelper myComClient;
+#endif
+};
+
+#endif // QTR_INTEROP_HELPER_H
index 4ae1184811780cee004d6f72afe9306ab4f0dd1e..83237fb97da2842bea8255acd60d265535502045 100644 (file)
 class InteropObject: public QObject
 {
     Q_OBJECT
+
+#ifdef ENABLE_DBUS_INTEROP
     Q_CLASSINFO ("D-Bus Interface", "com.transmissionbt.Transmission")
+#endif
+
+#ifdef ENABLE_COM_INTEROP
+    Q_CLASSINFO ("ClassID", "{0e2c952c-0597-491f-ba26-249d7e6fab49}")
+    Q_CLASSINFO ("InterfaceID", "{9402f54f-4906-4f20-ad73-afcfeb5b228d}")
+    Q_CLASSINFO ("RegisterObject", "yes")
+    Q_CLASSINFO ("CoClassAlias", "QtClient")
+    Q_CLASSINFO ("Description", "Transmission Qt Client Class")
+#endif
 
   public:
     InteropObject (QObject * parent = nullptr);
diff --git a/qt/transmission-qt.idl b/qt/transmission-qt.idl
new file mode 100644 (file)
index 0000000..80e1637
--- /dev/null
@@ -0,0 +1,33 @@
+import "ocidl.idl";
+
+[
+       uuid(1E405FC2-1A3A-468B-8BD6-BFBB58770390),
+       version(1.0),
+       helpstring("Transmission Qt Client Type Library 1.0")
+]
+library TransmissionLib
+{
+       [
+               uuid(9402F54F-4906-4F20-AD73-AFCFEB5B228D),
+               helpstring("QtClient Interface")
+       ]
+       dispinterface IQtClient
+       {
+       properties:
+
+       methods:
+               [id(1)] VARIANT_BOOL PresentWindow();
+               [id(2)] VARIANT_BOOL AddMetainfo([in] BSTR p_metainfo);
+       };
+
+       [
+               aggregatable,
+               appobject,
+               helpstring("Transmission Qt Client Class"),
+               uuid(0E2C952C-0597-491F-BA26-249D7E6FAB49)
+       ]
+       coclass QtClient
+       {
+               [default] dispinterface IQtClient;
+       };
+};
diff --git a/qt/transmission-qt.tlb.rc b/qt/transmission-qt.tlb.rc
new file mode 100644 (file)
index 0000000..07c0d7e
--- /dev/null
@@ -0,0 +1,6 @@
+#include "winresrc.h"
+
+#pragma code_page(1252)
+
+LANGUAGE 0, 0
+1 TYPELIB "transmission-qt.tlb"