From 5e84c99ff12e6108b25ef8e9bc725445d5978a10 Mon Sep 17 00:00:00 2001
From: kkostiuk <kateryna.kostiuk@savoirfairelinux.com>
Date: Mon, 18 Oct 2021 12:09:08 -0400
Subject: [PATCH] build: add macOS

- adapt build for macOS
- fix video on macOS

Change-Id: Ie325f3cd2955248c26ca858d37fcdc0d88c2e70d
---
 .gitignore                    |  3 ++
 CMakeLists.txt                | 81 +++++++++++++++++++++++++++++++++--
 src/MainApplicationWindow.qml |  4 +-
 src/connectivitymonitor.cpp   | 50 +++++++++++----------
 src/distantrenderer.cpp       |  8 ++++
 src/mainapplication.cpp       |  2 +-
 src/previewrenderer.cpp       | 35 ++++++++++++---
 7 files changed, 149 insertions(+), 34 deletions(-)

diff --git a/.gitignore b/.gitignore
index 5c45aba0f..f90d5ba53 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,3 +30,6 @@ build-local/
 # auto-gen files
 resources.qrc
 src/constant/JamiResources.qml
+
+# macOS
+.DS_Store
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ceb7ffa5d..4f88333c3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.7)
+cmake_minimum_required(VERSION 3.11)
 
 project(jami-qt)
 
@@ -23,6 +23,18 @@ set(CMAKE_INSTALL_RPATH_USE_LINK_PATH true)
 set(QML_RESOURCES ${PROJECT_SOURCE_DIR}/resources.qrc)
 set(QML_RESOURCES_QML ${PROJECT_SOURCE_DIR}/qml.qrc)
 
+if (APPLE)
+    include(FetchContent)
+    FetchContent_Declare(
+        libqrencode
+        GIT_REPOSITORY https://github.com/fukuchi/libqrencode.git
+        GIT_TAG v4.1.1
+    )
+    FetchContent_Populate(libqrencode)
+    add_subdirectory(${libqrencode_SOURCE_DIR} ${libqrencode_BINARY_DIR})
+    include_directories(${libqrencode_SOURCE_DIR})
+endif()
+
 # Resource auto-gen
 # check files in the resource directory and force a reconfigure if it changes
 file(GLOB_RECURSE
@@ -253,7 +265,7 @@ if(MSVC)
     include_directories(${DRING_SRC_PATH}
                         ${LRC_SRC_PATH}
                         ${QRENCODE})
-else()
+elseif (NOT APPLE)
     list(APPEND COMMON_SOURCES
                 ${SRC_DIR}/xrectsel.c
                 ${SRC_DIR}/dbuserrorhandler.cpp)
@@ -335,6 +347,42 @@ else()
     find_library(ringclient ringclient ${LRCLIBDIR} NO_DEFAULT_PATH)
     find_library(qrencode qrencode)
     find_library(X11 X11)
+else()
+    if(NOT DEFINED LRC)
+        if(EXISTS ${PROJECT_SOURCE_DIR}/../install/lrc)
+            set(LRC ${PROJECT_SOURCE_DIR}/../install/lrc)
+        endif()
+    endif()
+    if(DEFINED LRC)
+        if(EXISTS ${LRC}/include/libringclient)
+            set(LRC_SRC_PATH ${LRC}/include/libringclient)
+        else()
+            set(LRC_SRC_PATH ${LRC}/src)
+        endif()
+        if(NOT DEFINED LRCLIB)
+            set(LRCLIB ${LRC})
+        endif()
+        find_library(ringclient ringclient
+            PATHS ${LRCLIB}
+            PATH_SUFFIXES lib build build-local NO_DEFAULT_PATH)
+        set(LRC_LIB_NAME ${ringclient})
+    else()
+        find_package(LibRingClient REQUIRED)
+        if (LibRingClient_FOUND)
+            set(LRC_SRC_PATH ${LIB_RING_CLIENT_INCLUDE_DIR})
+            set(LRC_LIB_NAME ${LIB_RING_CLIENT_LIBRARY})
+        else()
+            message("lrc not found!")
+        endif()
+    endif()
+
+    message("Will expect lrc headers in ${LRC_SRC_PATH}")
+
+    include_directories(${LRC_SRC_PATH})
+
+    set(JAMI_DATA_PREFIX "${CMAKE_INSTALL_PREFIX}/share")
+
+    find_library(ringclient ringclient ${LRCLIBDIR} NO_DEFAULT_PATH)
 endif()
 
 # Qt find package
@@ -441,7 +489,7 @@ if(MSVC)
 
     # executable name
     set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "Jami")
-else()
+elseif (NOT APPLE)
     target_link_libraries(${PROJECT_NAME} PRIVATE
                           ${QML_LIBS}
                           ${LRC_LIB_NAME}
@@ -552,6 +600,33 @@ else()
 
     add_custom_target(uninstall
         COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
+else()
+    target_link_libraries(${PROJECT_NAME} PRIVATE
+                          ${QML_LIBS}
+                          ${LRC_LIB_NAME}
+                          qrencode)
+
+     # translations
+     if(Qt${QT_VERSION_MAJOR}LinguistTools_FOUND)
+         message("Releasing and copying translation files")
+         file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/share/ring/translations/")
+         file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/share/libringclient/translations/")
+         file(GLOB TS_CLIENT_FILES ${PROJECT_SOURCE_DIR}/translations/*.ts)
+         file(GLOB TS_LRC_FILES ${LRC}/translations/*.ts)
+         set_source_files_properties(${TS_CLIENT_FILES} PROPERTIES OUTPUT_LOCATION
+                                     "${CMAKE_BINARY_DIR}/share/ring/translations")
+         set_source_files_properties(${TS_LRC_FILES} PROPERTIES OUTPUT_LOCATION
+                                     "${CMAKE_BINARY_DIR}/share/libringclient/translations")
+
+         qt_add_translation(QM_CLIENT_FILES ${TS_CLIENT_FILES})
+         qt_add_translation(QM_LRC_FILES ${TS_LRC_FILES})
+         target_sources(${PROJECT_NAME} PRIVATE ${QM_CLIENT_FILES})
+         target_sources(${PROJECT_NAME} PRIVATE ${QM_LRC_FILES})
+         install(DIRECTORY "${CMAKE_BINARY_DIR}/share/ring/translations/"
+                            DESTINATION ${CMAKE_INSTALL_PREFIX}/share/ring/translations)
+         install(DIRECTORY "${CMAKE_BINARY_DIR}/share/libringclient/translations/"
+                            DESTINATION ${CMAKE_INSTALL_PREFIX}/share/libringclient/translations)
+      endif()
 endif()
 
 
diff --git a/src/MainApplicationWindow.qml b/src/MainApplicationWindow.qml
index 85e19cf72..7707c3989 100644
--- a/src/MainApplicationWindow.qml
+++ b/src/MainApplicationWindow.qml
@@ -184,7 +184,7 @@ ApplicationWindow {
 
     Connections {
         target: {
-            if (Qt.platform.os !== "windows")
+            if (Qt.platform.os !== "windows" && Qt.platform.os !== "macos")
                 return DBusErrorHandler
             return null
         }
@@ -214,7 +214,7 @@ ApplicationWindow {
 
         JamiQmlUtils.mainApplicationScreen = root.screen
 
-        if (Qt.platform.os !== "windows")
+        if (Qt.platform.os !== "windows" && Qt.platform.os !== "macos")
             DBusErrorHandler.setActive(true)
     }
 }
diff --git a/src/connectivitymonitor.cpp b/src/connectivitymonitor.cpp
index d75b6bb4f..b02410dfb 100644
--- a/src/connectivitymonitor.cpp
+++ b/src/connectivitymonitor.cpp
@@ -16,14 +16,13 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef _WIN32
+#if !defined(_WIN32) && !defined(__APPLE__)
 #include <glib.h>
 #include <gio/gio.h>
 #ifdef USE_LIBNM
 #include <NetworkManager.h>
 #endif
 #endif
-
 #include "connectivitymonitor.h"
 #include <QDebug>
 
@@ -166,25 +165,34 @@ ConnectivityMonitor::~ConnectivityMonitor()
     destroy();
     CoUninitialize();
 }
-
+#elif defined(Q_OS_MACOS)
+ConnectivityMonitor::ConnectivityMonitor(QObject* parent)
+    : QObject(parent)
+{}
+bool
+ConnectivityMonitor::isOnline()
+{
+    return false;
+}
+ConnectivityMonitor::~ConnectivityMonitor()
+{
+    qDebug() << "Destroying connectivity monitor";
+}
 #else
-
 #ifdef USE_LIBNM
 static void
-logConnectionInfo(NMActiveConnection *connection)
+logConnectionInfo(NMActiveConnection* connection)
 {
     if (connection) {
-        qDebug() << "primary network connection:"
-                 << nm_active_connection_get_uuid(connection)
-                 << "default: "
-                 << (nm_active_connection_get_default(connection) ? "yes" : "no");
+        qDebug() << "primary network connection:" << nm_active_connection_get_uuid(connection)
+                 << "default: " << (nm_active_connection_get_default(connection) ? "yes" : "no");
     } else {
         qWarning() << "no primary network connection detected, check network settings";
     }
 }
 
 static void
-primaryConnectionChanged(NMClient *nm, GParamSpec*, ConnectivityMonitor * cm)
+primaryConnectionChanged(NMClient* nm, GParamSpec*, ConnectivityMonitor* cm)
 {
     auto connection = nm_client_get_primary_connection(nm);
     logConnectionInfo(connection);
@@ -192,27 +200,25 @@ primaryConnectionChanged(NMClient *nm, GParamSpec*, ConnectivityMonitor * cm)
 }
 
 static void
-nmClientCallback(G_GNUC_UNUSED GObject *source_object,
-                 GAsyncResult *result,
-                 ConnectivityMonitor * cm)
+nmClientCallback(G_GNUC_UNUSED GObject* source_object, GAsyncResult* result, ConnectivityMonitor* cm)
 {
     GError* error = nullptr;
     if (auto nm_client = nm_client_new_finish(result, &error)) {
         qDebug() << "NetworkManager client initialized, version: "
                  << nm_client_get_version(nm_client)
-                 << ", daemon running:"
-                 << (nm_client_get_nm_running(nm_client) ? "yes" : "no")
+                 << ", daemon running:" << (nm_client_get_nm_running(nm_client) ? "yes" : "no")
                  << ", networking enabled:"
                  << (nm_client_networking_get_enabled(nm_client) ? "yes" : "no");
 
         auto connection = nm_client_get_primary_connection(nm_client);
         logConnectionInfo(connection);
-        g_signal_connect(nm_client, "notify::active-connections",
-                         G_CALLBACK(primaryConnectionChanged), cm);
+        g_signal_connect(nm_client,
+                         "notify::active-connections",
+                         G_CALLBACK(primaryConnectionChanged),
+                         cm);
 
     } else {
-        qWarning() << "error initializing NetworkManager client: "
-                   << error->message;
+        qWarning() << "error initializing NetworkManager client: " << error->message;
         g_clear_error(&error);
     }
 }
@@ -221,10 +227,9 @@ nmClientCallback(G_GNUC_UNUSED GObject *source_object,
 ConnectivityMonitor::ConnectivityMonitor(QObject* parent)
     : QObject(parent)
 {
-
-    GCancellable * cancellable = g_cancellable_new();
+    GCancellable* cancellable = g_cancellable_new();
 #ifdef USE_LIBNM
-    nm_client_new_async(cancellable, (GAsyncReadyCallback)nmClientCallback, this);
+    nm_client_new_async(cancellable, (GAsyncReadyCallback) nmClientCallback, this);
 #endif
 }
 
@@ -233,7 +238,6 @@ ConnectivityMonitor::~ConnectivityMonitor()
     qDebug() << "Destroying connectivity monitor";
 }
 
-
 bool
 ConnectivityMonitor::isOnline()
 {
diff --git a/src/distantrenderer.cpp b/src/distantrenderer.cpp
index 78f04862b..cd45339c5 100644
--- a/src/distantrenderer.cpp
+++ b/src/distantrenderer.cpp
@@ -104,10 +104,18 @@ DistantRenderer::paint(QPainter* painter)
         if (distantImage) {
             painter->setRenderHint(QPainter::Antialiasing);
             painter->setRenderHint(QPainter::SmoothPixmapTransform);
+#if defined(Q_OS_MACOS)
 
+            auto scaledDistant = distantImage
+                                     ->scaled(size().toSize(),
+                                              Qt::KeepAspectRatio,
+                                              Qt::SmoothTransformation)
+                                     .rgbSwapped();
+#else
             auto scaledDistant = distantImage->scaled(size().toSize(),
                                                       Qt::KeepAspectRatio,
                                                       Qt::SmoothTransformation);
+#endif
             auto tempScaledWidth = static_cast<int>(scaledWidth_ * 1000);
             auto tempScaledHeight = static_cast<int>(scaledHeight_ * 1000);
             auto tempXOffset = xOffset_;
diff --git a/src/mainapplication.cpp b/src/mainapplication.cpp
index 23b638ad5..c6b7ede9a 100644
--- a/src/mainapplication.cpp
+++ b/src/mainapplication.cpp
@@ -189,7 +189,7 @@ MainApplication::init()
             connectivityMonitor_.get(),
             results[opts::MUTEDAEMON].toBool());
 
-#ifdef Q_OS_UNIX
+#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
     using namespace Interfaces;
     GlobalInstances::setDBusErrorHandler(std::make_unique<DBusErrorHandler>());
     auto dBusErrorHandlerQObject = dynamic_cast<QObject*>(&GlobalInstances::dBusErrorHandler());
diff --git a/src/previewrenderer.cpp b/src/previewrenderer.cpp
index 3db0759fa..2e783710e 100644
--- a/src/previewrenderer.cpp
+++ b/src/previewrenderer.cpp
@@ -69,9 +69,18 @@ PreviewRenderer::paint(QPainter* painter)
 
             // If the given size is empty, this function returns a null image.
             QImage scaledPreview;
-            scaledPreview = previewImage->scaled(size().toSize(),
-                                                 Qt::KeepAspectRatio,
-                                                 Qt::SmoothTransformation);
+#if defined(Q_OS_MACOS)
+
+            scaledPreview = previewImage
+                                ->scaled(size().toSize(),
+                                         Qt::KeepAspectRatio,
+                                         Qt::SmoothTransformation)
+                                .rgbSwapped();
+#else
+                scaledPreview = previewImage->scaled(size().toSize(),
+                                                     Qt::KeepAspectRatio,
+                                                     Qt::SmoothTransformation);
+#endif
             painter->drawImage(QRect(0, 0, scaledPreview.width(), scaledPreview.height()),
                                scaledPreview);
         } else {
@@ -106,7 +115,11 @@ VideoCallPreviewRenderer::paint(QPainter* painter)
                                  / static_cast<qreal>(previewImage->width());
             setProperty("previewImageScalingFactor", scalingFactor);
             QImage scaledPreview;
-            scaledPreview = previewImage->scaled(size().toSize(), Qt::KeepAspectRatio);
+#if defined(Q_OS_MACOS)
+            scaledPreview = previewImage->scaled(size().toSize(), Qt::KeepAspectRatio).rgbSwapped();
+#else
+                scaledPreview = previewImage->scaled(size().toSize(), Qt::KeepAspectRatio);
+#endif
             painter->drawImage(QRect(0, 0, scaledPreview.width(), scaledPreview.height()),
                                scaledPreview);
         }
@@ -130,7 +143,12 @@ QString
 PhotoboothPreviewRender::takePhoto(int size)
 {
     if (auto previewImage = lrcInstance_->renderer()->getPreviewFrame(get_rendererId())) {
+#if defined(Q_OS_MACOS)
+        return Utils::byteArrayToBase64String(
+            Utils::QImageToByteArray(previewImage->copy().rgbSwapped()));
+#else
         return Utils::byteArrayToBase64String(Utils::QImageToByteArray(previewImage->copy()));
+#endif
     }
     return {};
 }
@@ -143,8 +161,15 @@ PhotoboothPreviewRender::paint(QPainter* painter)
     lrcInstance_->renderer()->drawFrame(get_rendererId(), [this, painter](QImage* previewImage) {
         if (previewImage) {
             QImage scaledPreview;
+#if defined(Q_OS_MACOS)
+
             scaledPreview = Utils::getCirclePhoto(*previewImage,
-                                                  height() <= width() ? height() : width());
+                                                  height() <= width() ? height() : width())
+                                .rgbSwapped();
+#else
+                scaledPreview = Utils::getCirclePhoto(*previewImage,
+                                                      height() <= width() ? height() : width());
+#endif
             painter->drawImage(QRect(0, 0, scaledPreview.width(), scaledPreview.height()),
                                scaledPreview);
         }
-- 
GitLab