diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b01059..62f4b19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,408 +1,411 @@ cmake_minimum_required(VERSION 3.3) find_package(ECM 5.40.0 REQUIRED NO_MODULE) set(CMAKE_CXX_STANDARD 14) set(QT_MIN_VERSION "5.10.0") if(POLICY CMP0071) cmake_policy(SET CMP0071 NEW) endif() project(kaidan) set(APPLICATION_ID "im.kaidan.kaidan") set(APPLICATION_NAME "kaidan") set(APPLICATION_DISPLAY_NAME "Kaidan") if(UBUNTU_TOUCH) set(APPLICATION_NAME "${APPLICATION_ID}") endif() set(VERSION_STRING "0.5.0-dev") set(DEVELOPMENT_BUILD TRUE) set(ANDROID_VERSION_CODE 6) # CMake options option(I18N "Enable i18n support" FALSE) option(STATIC_BUILD "Build Kaidan statically") option(APPIMAGE "Build Kaidan as AppImage (will only work in the appimage script)" FALSE) option(UBUNTU_TOUCH "Building an Ubuntu Touch click (internal use only!)" FALSE) option(CLICK_ARCH "Architecture that will be used in the click's manifest") option(CLICK_DATE "Date used in the version number in the click's manifest") option(QUICK_COMPILER "Use QtQuick compiler to improve performance" FALSE) option(USE_KNOTIFICATIONS "Use KNotifications for displaying notifications" TRUE) option(BUNDLE_ICONS "Bundle breeze icons" FALSE) # Find includes in corresponding build directories set(CMAKE_INCLUDE_CURRENT_DIR ON) # Instruct CMake to run moc, uic and rcc automatically when needed. set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) set(AUTOMOC_MOC_OPTIONS -Muri=${APPLICATION_ID}) # # Dependecies # # CMake module path set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) # CMake modules include include(KDEInstallDirs) include(KDECompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(ECMInstallIcons) include(FeatureSummary) kde_enable_exceptions() # Find packages find_package(Qt5 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS Core Qml Quick Svg Sql QuickControls2 Xml Multimedia Positioning Location) find_package(KF5Kirigami2 REQUIRED) find_package(ZXing REQUIRED COMPONENTS Core) find_package(QXmpp 1.0.0 REQUIRED) # Optional QtQuickCompiler if(QUICK_COMPILER) find_package(Qt5QuickCompiler) set_package_properties(Qt5QuickCompiler PROPERTIES DESCRIPTION "Compile QML at build time" TYPE OPTIONAL ) endif() if(USE_KNOTIFICATIONS) find_package(KF5Notifications REQUIRED) set(__KF5Notifications_LIBRARIES KF5::Notifications) add_definitions(-DHAVE_KNOTIFICATIONS) endif() # Optional QWidget style integration (not on mobile) if(NOT UBUNTU_TOUCH AND NOT ANDROID AND NOT IOS) find_package(Qt5Widgets) set_package_properties(Qt5Widgets PROPERTIES DESCRIPTION "Integration with QWidget based desktop styles" TYPE OPTIONAL ) endif() # Platform-specific if(Qt5Widgets_FOUND) add_definitions(-DHAVE_QWIDGETS -DQAPPLICATION_CLASS=QApplication) set(__Qt5Widgets_LIBRARIES Qt5::Widgets) else() add_definitions(-DQAPPLICATION_CLASS=QGuiApplication) endif() if(ANDROID) find_package(Qt5 REQUIRED COMPONENTS AndroidExtras) endif() if(ANDROID OR WIN32) find_package(PkgConfig REQUIRED) pkg_search_module(OPENSSL REQUIRED openssl IMPORTED_TARGET) message(STATUS "Using OpenSSL ${OPENSSL_VERSION}") endif() # # Load submodules # # Main kaidan sources include("${CMAKE_SOURCE_DIR}/src/CMakeLists.txt") # I18n support if(I18N) include("${CMAKE_SOURCE_DIR}/i18n/CMakeLists.txt") endif() # # Sources / Resources # # Include bundled icons on Ubuntu Touch, Android, Windows, macOS and iOS if(BUNDLE_ICONS OR UBUNTU_TOUCH OR ANDROID OR WIN32 OR APPLE) set(KAIDAN_ICONS_QRC kirigami-icons.qrc) endif() # Bundle images on Android, Windows, macOS and iOS if(ANDROID OR WIN32 OR APPLE) set(KAIDAN_IMAGES_QRC "data/images/images.qrc") endif() # Set app icon if(APPLE) set(KAIDAN_ICNS "${CMAKE_SOURCE_DIR}/misc/macos/kaidan.icns") set_source_files_properties(${KAIDAN_ICNS} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") elseif(WIN32) include(ECMAddAppIcon) file(GLOB WIN_ICONS_SRCS "${CMAKE_SOURCE_DIR}/misc/windows/*kaidan.png") message(STATUS ${WIN_ICONS_SRCS}) ecm_add_app_icon(KAIDAN_ICNS ICONS ${WIN_ICONS_SRCS}) message(STATUS ${KAIDAN_ICNS}) endif() if(QUICK_COMPILER) qtquick_compiler_add_resources(KAIDAN_QML_QRC kaidan_qml.qrc) else() qt5_add_resources(KAIDAN_QML_QRC kaidan_qml.qrc) endif() add_executable(${PROJECT_NAME} MACOSX_BUNDLE WIN32 ${KAIDAN_ICNS} ${KAIDAN_SOURCES} ${KAIDAN_QML_QRC} ${KAIDAN_ICONS_QRC} # only set if enabled ${KAIDAN_IMAGES_QRC} # ${I18N_QRC_CPP} # ) target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Sql Qt5::Qml Qt5::Quick Qt5::Svg Qt5::Network Qt5::Xml Qt5::Multimedia Qt5::Positioning Qt5::Location ZXing::Core QXmpp::QXmpp ${__Qt5Widgets_LIBRARIES} ${__KF5Notifications_LIBRARIES} ) if(ANDROID OR WIN32 OR IOS) target_link_libraries(${PROJECT_NAME} Qt5::QuickControls2 KF5::Kirigami2) endif() if(ANDROID OR WIN32) target_link_libraries(${PROJECT_NAME} PkgConfig::OPENSSL) endif() if(ANDROID) target_link_libraries(${PROJECT_NAME} Qt5::AndroidExtras) endif() if(STATIC_BUILD) add_definitions(-DQXMPP_BUILD) find_package(Perl REQUIRED) set(STATIC_DEPENDENCIES_CMAKE_FILE "${CMAKE_BINARY_DIR}/QtStaticDependencies.cmake") if(EXISTS ${STATIC_DEPENDENCIES_CMAKE_FILE}) file(REMOVE ${STATIC_DEPENDENCIES_CMAKE_FILE}) endif() get_target_property(QT_LIBDIR Qt5::Core LOCATION) get_filename_component(QT_LIBDIR ${QT_LIBDIR} DIRECTORY) macro(CONVERT_PRL_LIBS_TO_CMAKE _qt_component) if(TARGET Qt5::${_qt_component}) get_target_property(_lib_location Qt5::${_qt_component} LOCATION) execute_process(COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/utils/convert-prl-libs-to-cmake.pl --lib ${_lib_location} --libdir ${QT_LIBDIR} --out ${STATIC_DEPENDENCIES_CMAKE_FILE} --component ${_qt_component} --compiler ${CMAKE_CXX_COMPILER_ID} ) endif() endmacro() link_directories(${_qt5_install_prefix}/../) foreach(qt_module Gui Quick QuickControls2 Network Qml Svg Sql QSQLiteDriverPlugin QJpegPlugin QGifPlugin QSvgPlugin QSvgIconPlugin QICOPlugin QGenericEnginePlugin QLocalClientConnectionFactory QTcpServerConnectionFactory) CONVERT_PRL_LIBS_TO_CMAKE(${qt_module}) endforeach() if(WIN32) CONVERT_PRL_LIBS_TO_CMAKE(QWindowsIntegrationPlugin) elseif(IOS) foreach(qt_module QIOSIntegrationPlugin QMacHeifPlugin QMacJp2Plugin QICNSPlugin QTgaPlugin QTiffPlugin QWbmpPlugin QWebpPlugin) CONVERT_PRL_LIBS_TO_CMAKE(${qt_module}) endforeach() endif() if(NOT EXISTS ${STATIC_DEPENDENCIES_CMAKE_FILE}) message(FATAL_ERROR "Unable to find ${STATIC_DEPENDENCIES_CMAKE_FILE}") endif() include(${STATIC_DEPENDENCIES_CMAKE_FILE}) set(QT_QML_PATH ${_qt5Quick_install_prefix}) find_library(KIRIGAMI_PLUGIN kirigamiplugin PATHS ${Kirigami2_INSTALL_PREFIX}/${KDE_INSTALL_QMLDIR}/org/kde/kirigami.2) find_library(QUICK_PLUGIN qtquick2plugin PATHS ${QT_QML_PATH}/qml/QtQuick.2) find_library(LABS_PLATFORM_PLUGIN qtlabsplatformplugin PATHS ${QT_QML_PATH}/qml/Qt/labs/platform) find_library(GRAPHEFFECTS_PLUGIN qtgraphicaleffectsplugin PATHS ${QT_QML_PATH}/qml/QtGraphicalEffects) find_library(GRAPHEFFECTS_PRIVATE_PLUGIN qtgraphicaleffectsprivate PATHS ${QT_QML_PATH}/qml/QtGraphicalEffects/private) find_library(QQC2_PLUGIN qtquickcontrols2plugin PATHS ${QT_QML_PATH}/qml/QtQuick/Controls.2) find_library(QQC2_MATERIAL_PLUGIN qtquickcontrols2materialstyleplugin PATHS ${QT_QML_PATH}/qml/QtQuick/Controls.2/Material) find_library(QQC2_UNIVERSAL_PLUGIN qtquickcontrols2universalstyleplugin PATHS ${QT_QML_PATH}/qml/QtQuick/Controls.2/Universal) find_library(QLAYOUTS_PLUGIN qquicklayoutsplugin PATHS ${QT_QML_PATH}/qml/QtQuick/Layouts) find_library(QWINDOW_PLUGIN windowplugin PATHS ${QT_QML_PATH}/qml/QtQuick/Window.2) find_library(QSHAPES_PLUGIN qmlshapesplugin PATHS ${QT_QML_PATH}/qml/QtQuick/Shapes) find_library(QUICKSHAPES Qt5QuickShapes PATHS ${QT_LIBDIR}) find_library(QTEMPLATES_PLUGIN qtquicktemplates2plugin PATHS ${QT_QML_PATH}/qml/QtQuick/Templates.2) find_library(QMODELS_PLUGIN modelsplugin PATHS ${QT_QML_PATH}/qml/QtQml/Models.2) target_link_libraries(${PROJECT_NAME} ${plugin_libs} Qt5::QSQLiteDriverPlugin Qt5::QJpegPlugin Qt5::QGifPlugin Qt5::QSvgPlugin Qt5::QSvgIconPlugin Qt5::QICOPlugin Qt5::QGenericEnginePlugin Qt5::QLocalClientConnectionFactory Qt5::QTcpServerConnectionFactory ${KIRIGAMI_PLUGIN} ${QUICK_PLUGIN} ${LABS_PLATFORM_PLUGIN} ${GRAPHEFFECTS_PLUGIN} ${GRAPHEFFECTS_PRIVATE_PLUGIN} ${QQC2_PLUGIN} ${QQC2_MATERIAL_PLUGIN} ${QQC2_UNIVERSAL_PLUGIN} ${QLAYOUTS_PLUGIN} ${QWINDOW_PLUGIN} ${QSHAPES_PLUGIN} ${QUICKSHAPES} ${QTEMPLATES_PLUGIN} ${QMODELS_PLUGIN} ${__Qt5Widgets_LIBRARIES} ) if(WIN32) target_link_libraries(${PROJECT_NAME} Qt5::QWindowsIntegrationPlugin ) elseif(IOS) target_link_libraries(${PROJECT_NAME} Qt5::QIOSIntegrationPlugin Qt5::QMacHeifPlugin Qt5::QMacJp2Plugin Qt5::QICNSPlugin Qt5::QTgaPlugin Qt5::QTiffPlugin Qt5::QWbmpPlugin Qt5::QWebpPlugin ) endif() endif() # Set a custom plist file for the app bundle if(APPLE) if(IOS) set_target_properties(${PROJECT_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/misc/ios/Info.plist) else() set_target_properties(${PROJECT_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/misc/macos/Info.plist) endif() endif() # # Global C++ variables # # iOS-specific linker flags if(IOS) set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} "-Wl,-e,_qt_main_wrapper -u _qt_registerPlatformPlugin") endif() if(UBUNTU_TOUCH) set(KAIDAN_COMPILE_DEFINITIONS UBUNTU_TOUCH=True) elseif(APPIMAGE) - set(KAIDAN_COMPILE_DEFINITIONS APPIMAGE=True) + set(KAIDAN_COMPILE_DEFINITIONS + APPIMAGE=True + TARGET_GSTREAMER_PLUGINS="${TARGET_GSTREAMER_PLUGINS}" + ) endif() if(STATIC_BUILD) set(KAIDAN_COMPILE_DEFINITIONS STATIC_BUILD=True ${KAIDAN_COMPILE_DEFINITIONS} ) endif() target_compile_definitions(${PROJECT_NAME} PRIVATE DEBUG_SOURCE_PATH="${CMAKE_SOURCE_DIR}" VERSION_STRING="${VERSION_STRING}" APPLICATION_ID="${APPLICATION_ID}" APPLICATION_NAME="${APPLICATION_NAME}" APPLICATION_DISPLAY_NAME="${APPLICATION_DISPLAY_NAME}" ${KAIDAN_COMPILE_DEFINITIONS} ) # # Install Kaidan # if(ANDROID) configure_file(${CMAKE_SOURCE_DIR}/misc/android/AndroidManifest.xml.in ${CMAKE_SOURCE_DIR}/misc/android/AndroidManifest.xml) endif() if(UBUNTU_TOUCH AND CLICK_ARCH) set(CLICK_VERSION ${VERSION_STRING}) if(DEVELOPMENT_BUILD) set(CLICK_VERSION "${CLICK_VERSION}.${CLICK_DATE}") endif() # will replace ${CLICK_ARCH} with its value configure_file(${CMAKE_SOURCE_DIR}/misc/ubuntu-touch/manifest.json.in ${CMAKE_SOURCE_DIR}/misc/ubuntu-touch/manifest.json) # install kaidan binary install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/bin/${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX} ) # install kaidan media install(DIRECTORY "data/images" DESTINATION "${CMAKE_INSTALL_FULL_DATADIR}/${APPLICATION_ID}" ) # install icon install(FILES "misc/kaidan.svg" "misc/ubuntu-touch/apparmor.json" "misc/ubuntu-touch/kaidan.desktop" "misc/ubuntu-touch/manifest.json" DESTINATION "${CMAKE_INSTALL_PREFIX}" ) elseif(UNIX AND NOT APPLE) # install kaidan binary install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/bin/${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_FULL_BINDIR} ) # install kaidan media install(DIRECTORY "data/images" DESTINATION "${CMAKE_INSTALL_FULL_DATADIR}/${PROJECT_NAME}" ) # install icon (scalable + 128x) install(FILES "misc/kaidan.svg" DESTINATION "${CMAKE_INSTALL_FULL_DATADIR}/icons/hicolor/scalable/apps" ) install(FILES "misc/kaidan-128x128.png" DESTINATION "${CMAKE_INSTALL_FULL_DATADIR}/icons/hicolor/128x128/apps" RENAME "kaidan.png" ) # install desktop file install(FILES "misc/kaidan.desktop" DESTINATION "${KDE_INSTALL_APPDIR}" ) # install metainfo install(FILES "misc/metadata.xml" DESTINATION "${KDE_INSTALL_METAINFODIR}" RENAME "im.kaidan.kaidan.appdata.xml" ) endif() # KNotifications if(USE_KNOTIFICATIONS) install(FILES misc/kaidan.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR}) endif() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/main.cpp b/src/main.cpp index 546565f..57abdf4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,355 +1,386 @@ /* * Kaidan - A user-friendly XMPP client for every device! * * Copyright (C) 2016-2019 Kaidan developers and contributors * (see the LICENSE file for a full list of copyright authors) * * Kaidan is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * In addition, as a special exception, the author of Kaidan gives * permission to link the code of its release with the OpenSSL * project's "OpenSSL" library (or with modified versions of it that * use the same license as the "OpenSSL" library), and distribute the * linked executables. You must obey the GNU General Public License in * all respects for all of the code used other than "OpenSSL". If you * modify this file, you may extend this exception to your version of * the file, but you are not obligated to do so. If you do not wish to * do so, delete this exception statement from your version. * * Kaidan is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Kaidan. If not, see . */ // Qt #include #include #include +#include #include #include #include #include #include #include +#include #include // QXmpp #include "qxmpp-exts/QXmppUploadManager.h" #include // Kaidan #include "AvatarFileStorage.h" #include "EmojiModel.h" #include "Enums.h" #include "Globals.h" #include "Kaidan.h" #include "Message.h" #include "MessageModel.h" #include "PresenceCache.h" #include "QmlUtils.h" #include "RosterModel.h" #include "RosterFilterProxyModel.h" #include "StatusBar.h" #include "UploadManager.h" #include "EmojiModel.h" #include "Utils.h" #include "QrCodeScannerFilter.h" #include "VCardModel.h" #include "CameraModel.h" #include "AudioDeviceModel.h" #include "MediaSettingModel.h" #include "MediaUtils.h" #include "MediaRecorder.h" #ifdef STATIC_BUILD #include "static_plugins.h" #endif #ifndef QAPPLICATION_CLASS #define QAPPLICATION_CLASS QApplication #endif #include QT_STRINGIFY(QAPPLICATION_CLASS) #if !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID) // SingleApplication (Qt5 replacement for QtSingleApplication) #include "singleapp/singleapplication.h" #endif #ifdef STATIC_BUILD #define KIRIGAMI_BUILD_TYPE_STATIC #include "./3rdparty/kirigami/src/kirigamiplugin.h" #endif #ifdef Q_OS_ANDROID #include #endif #ifdef Q_OS_WIN #include #endif enum CommandLineParseResult { CommandLineOk, CommandLineError, CommandLineVersionRequested, CommandLineHelpRequested }; CommandLineParseResult parseCommandLine(QCommandLineParser &parser, QString *errorMessage) { // application description parser.setApplicationDescription(QString(APPLICATION_DISPLAY_NAME) + " - " + QString(APPLICATION_DESCRIPTION)); // add all possible arguments QCommandLineOption helpOption = parser.addHelpOption(); QCommandLineOption versionOption = parser.addVersionOption(); parser.addOption({"disable-xml-log", "Disable output of full XMPP XML stream."}); parser.addOption({{"m", "multiple"}, "Allow multiple instances to be started."}); parser.addPositionalArgument("xmpp-uri", "An XMPP-URI to open (i.e. join a chat).", "[xmpp-uri]"); // parse arguments if (!parser.parse(QGuiApplication::arguments())) { *errorMessage = parser.errorText(); return CommandLineError; } // check for special cases if (parser.isSet(versionOption)) return CommandLineVersionRequested; if (parser.isSet(helpOption)) return CommandLineHelpRequested; // if nothing special happened, return OK return CommandLineOk; } Q_DECL_EXPORT int main(int argc, char *argv[]) { #ifdef Q_OS_WIN if (AttachConsole(ATTACH_PARENT_PROCESS)) { freopen("CONOUT$", "w", stdout); freopen("CONOUT$", "w", stderr); } #endif // initialize random generator qsrand(time(nullptr)); // // App // #ifdef UBUNTU_TOUCH qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "true"); qputenv("QT_QUICK_CONTROLS_MOBILE", "true"); #endif #ifdef APPIMAGE qputenv("OPENSSL_CONF", ""); #endif // name, display name, description QGuiApplication::setApplicationName(APPLICATION_NAME); QGuiApplication::setApplicationDisplayName(APPLICATION_DISPLAY_NAME); QGuiApplication::setApplicationVersion(VERSION_STRING); // attributes QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); // create a qt app #if defined(Q_OS_IOS) || defined(Q_OS_ANDROID) QGuiApplication app(argc, argv); #else SingleApplication app(argc, argv, true); #endif +#ifdef APPIMAGE + QFileInfo executable(QCoreApplication::applicationFilePath()); + + if (executable.isSymLink()) { + executable.setFile(executable.symLinkTarget()); + } + + QString gstreamerPluginsPath; + + // Try to use deployed plugins if any... +#if defined(TARGET_GSTREAMER_PLUGINS) + gstreamerPluginsPath = QString::fromLocal8Bit(TARGET_GSTREAMER_PLUGINS); + + if (!gstreamerPluginsPath.isEmpty()) { + gstreamerPluginsPath = QDir::cleanPath(QString::fromLatin1("%1/../..%2") + .arg(executable.absolutePath(), gstreamerPluginsPath)); + } +#else + qFatal("Please provide the unified directory containing the gstreamer plugins and gst-plugin-scanner."); +#endif + +#if defined(QT_DEBUG) + qputenv("GST_DEBUG", "ERROR:5,WARNING:5,INFO:5,DEBUG:5,LOG:5"); +#endif + qputenv("GST_PLUGIN_PATH_1_0", QByteArray()); + qputenv("GST_PLUGIN_SYSTEM_PATH_1_0", gstreamerPluginsPath.toLocal8Bit()); + qputenv("GST_PLUGIN_SCANNER_1_0", QString::fromLatin1("%1/gst-plugin-scanner").arg(gstreamerPluginsPath).toLocal8Bit()); +#endif + // register qMetaTypes qRegisterMetaType("RosterItem"); qRegisterMetaType("RosterModel*"); qRegisterMetaType("Message"); qRegisterMetaType("MessageModel*"); qRegisterMetaType("AvatarFileStorage*"); qRegisterMetaType("PresenceCache*"); qRegisterMetaType("QXmppPresence"); qRegisterMetaType("Credentials"); qRegisterMetaType("Qt::ApplicationState"); qRegisterMetaType("QXmppClient::State"); qRegisterMetaType("MessageType"); qRegisterMetaType("DisconnectionReason"); qRegisterMetaType("TransferJob*"); qRegisterMetaType("QmlUtils*"); qRegisterMetaType>("QVector"); qRegisterMetaType>("QVector"); qRegisterMetaType>("QHash"); qRegisterMetaType>("std::function"); qRegisterMetaType>("std::function"); qRegisterMetaType("ClientWorker::Credentials"); qRegisterMetaType("QXmppVCardIq"); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); // qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); // Enums for c++ member calls using enums qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); // Qt-Translator QTranslator qtTranslator; qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); QCoreApplication::installTranslator(&qtTranslator); // Kaidan-Translator QTranslator kaidanTranslator; // load the systems locale or none from resources kaidanTranslator.load(QLocale::system().name(), ":/i18n"); QCoreApplication::installTranslator(&kaidanTranslator); // // Command line arguments // // create parser and add a description QCommandLineParser parser; // parse the arguments QString commandLineErrorMessage; switch (parseCommandLine(parser, &commandLineErrorMessage)) { case CommandLineError: qWarning() << commandLineErrorMessage; return 1; case CommandLineVersionRequested: parser.showVersion(); return 0; case CommandLineHelpRequested: parser.showHelp(); return 0; case CommandLineOk: break; } #if !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID) // check if another instance already runs if (app.isSecondary() && !parser.isSet("multiple")) { qDebug().noquote() << QString("Another instance of %1 is already running.") .arg(APPLICATION_DISPLAY_NAME) << "You can enable multiple instances by specifying '--multiple'."; // send a possible link to the primary instance if (!parser.positionalArguments().isEmpty()) app.sendMessage(parser.positionalArguments().first().toUtf8()); return 0; } #endif // // Kaidan back-end // Kaidan kaidan(&app, !parser.isSet("disable-xml-log")); #if !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID) // receive messages from other instances of Kaidan Kaidan::connect(&app, &SingleApplication::receivedMessage, &kaidan, &Kaidan::receiveMessage); #endif // open the XMPP-URI/link (if given) if (!parser.positionalArguments().isEmpty()) kaidan.addOpenUri(parser.positionalArguments().first()); // // QML-GUI // if (QIcon::themeName().isEmpty()) { QIcon::setThemeName("breeze"); } QQmlApplicationEngine engine; // QtQuickControls2 Style if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) { #ifdef Q_OS_WIN const QString defaultStyle = QStringLiteral("Universal"); #else const QString defaultStyle = QStringLiteral("Material"); #endif qDebug() << "QT_QUICK_CONTROLS_STYLE not set, setting to" << defaultStyle; qputenv("QT_QUICK_CONTROLS_STYLE", defaultStyle.toLatin1()); } // QML type bindings #ifdef STATIC_BUILD KirigamiPlugin::getInstance().registerTypes(); #endif qmlRegisterType("StatusBar", 0, 1, "StatusBar"); qmlRegisterType("EmojiModel", 0, 1, "EmojiModel"); qmlRegisterType("EmojiModel", 0, 1, "EmojiProxyModel"); qmlRegisterType(APPLICATION_ID, 1, 0, "QrCodeScannerFilter"); qmlRegisterType(APPLICATION_ID, 1, 0, "VCardModel"); qmlRegisterType(APPLICATION_ID, 1, 0, "RosterFilterProxyModel"); qmlRegisterType(APPLICATION_ID, 1, 0, "CameraModel"); qmlRegisterType(APPLICATION_ID, 1, 0, "AudioDeviceModel"); qmlRegisterType(APPLICATION_ID, 1, 0, "MediaSettingsContainerModel"); qmlRegisterType(APPLICATION_ID, 1, 0, "MediaSettingsResolutionModel"); qmlRegisterType(APPLICATION_ID, 1, 0, "MediaSettingsQualityModel"); qmlRegisterType(APPLICATION_ID, 1, 0, "MediaSettingsImageCodecModel"); qmlRegisterType(APPLICATION_ID, 1, 0, "MediaSettingsAudioCodecModel"); qmlRegisterType(APPLICATION_ID, 1, 0, "MediaSettingsAudioSampleRateModel"); qmlRegisterType(APPLICATION_ID, 1, 0, "MediaSettingsVideoCodecModel"); qmlRegisterType(APPLICATION_ID, 1, 0, "MediaSettingsVideoFrameRateModel"); qmlRegisterType(APPLICATION_ID, 1, 0, "MediaRecorder"); qmlRegisterUncreatableType("EmojiModel", 0, 1, "QAbstractItemModel", "Used by proxy models"); qmlRegisterUncreatableType("EmojiModel", 0, 1, "Emoji", "Used by emoji models"); qmlRegisterUncreatableType(APPLICATION_ID, 1, 0, "TransferJob", "TransferJob type usable"); qmlRegisterUncreatableType(APPLICATION_ID, 1, 0, "QMimeType", "QMimeType type usable"); qmlRegisterUncreatableType(APPLICATION_ID, 1, 0, "CameraInfo", "CameraInfo type usable"); qmlRegisterUncreatableType(APPLICATION_ID, 1, 0, "AudioDeviceInfo", "AudioDeviceInfo type usable"); qmlRegisterUncreatableType(APPLICATION_ID, 1, 0, "MediaSettings", "MediaSettings type usable"); qmlRegisterUncreatableType(APPLICATION_ID, 1, 0, "CommonEncoderSettings", "CommonEncoderSettings type usable"); qmlRegisterUncreatableType(APPLICATION_ID, 1, 0, "ImageEncoderSettings", "ImageEncoderSettings type usable"); qmlRegisterUncreatableType(APPLICATION_ID, 1, 0, "AudioEncoderSettings", "AudioEncoderSettings type usable"); qmlRegisterUncreatableType(APPLICATION_ID, 1, 0, "VideoEncoderSettings", "VideoEncoderSettings type usable"); qmlRegisterUncreatableMetaObject(Enums::staticMetaObject, APPLICATION_ID, 1, 0, "Enums", "Can't create object; only enums defined!"); qmlRegisterSingletonType("MediaUtils", 0, 1, "MediaUtilsInstance", [](QQmlEngine *, QJSEngine *) { QObject *instance = new MediaUtils(qApp); return instance; }); engine.rootContext()->setContextProperty("kaidan", &kaidan); engine.load(QUrl("qrc:/qml/main.qml")); if(engine.rootObjects().isEmpty()) return -1; #ifdef Q_OS_ANDROID QtAndroid::hideSplashScreen(); #endif // enter qt main loop return app.exec(); } diff --git a/utils/build-linux-appimage.sh b/utils/build-linux-appimage.sh index a0b0de2..d80b707 100755 --- a/utils/build-linux-appimage.sh +++ b/utils/build-linux-appimage.sh @@ -1,136 +1,184 @@ #!/bin/bash -e # NOTE: To use this script, you need to set $QT_LINUX to your Qt for Linux installation # Path to Qt installation QT_LINUX=${QT_LINUX:-/usr} echo Using Qt installation from $QT_LINUX # Build type is one of: # Debug, Release, RelWithDebInfo and MinSizeRel BUILD_TYPE="${BUILD_TYPE:-Debug}" +MAKEFLAGS="-j$(nproc)" + KAIDAN_SOURCES=$(dirname "$(readlink -f "${0}")")/.. echo "-- Starting $BUILD_TYPE build of Kaidan --" echo "*****************************************" echo "Fetching dependencies if required" echo "*****************************************" if [ ! -f "$KAIDAN_SOURCES/3rdparty/kirigami/.git" ] || [ ! -f "$KAIDAN_SOURCES/3rdparty/breeze-icons/.git" ]; then echo "Cloning Kirigami and Breeze icons" git submodule update --init fi if [ ! -e "$KAIDAN_SOURCES/3rdparty/qxmpp/.git" ]; then echo "Cloning QXmpp" git clone https://github.com/qxmpp-project/qxmpp.git 3rdparty/qxmpp fi cdnew() { if [ -d "$1" ]; then rm -rf "$1" fi mkdir $1 cd $1 } join_by() { local IFS="$1" shift echo "$*" } if [ ! -f "$KAIDAN_SOURCES/3rdparty/linuxdeployqt/squashfs-root/AppRun" ]; then echo "Downloading linuxdeployqt" wget --continue -P $KAIDAN_SOURCES/3rdparty/ https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage chmod +x $KAIDAN_SOURCES/3rdparty/linuxdeployqt-continuous-x86_64.AppImage echo "Extracting linuxdeployqt" cdnew $KAIDAN_SOURCES/3rdparty/linuxdeployqt $KAIDAN_SOURCES/3rdparty/linuxdeployqt-continuous-x86_64.AppImage --appimage-extract cd $KAIDAN_SOURCES fi export QT_SELECT=qt5 +SOURCE_GSTREAMER_PLUGINS=$(find /usr/lib -name libgstcoreelements.so -exec dirname {} \;) +SOURCE_GSTREAMER_PLUGINS_SCANNER=$(find /usr/lib -name gst-plugin-scanner) +TARGET_GSTREAMER_PLUGINS="/usr/lib/$(basename "${SOURCE_GSTREAMER_PLUGINS}")" + +echo "*****************************************" +echo "Found GStreamer plugins in: ${SOURCE_GSTREAMER_PLUGINS}" +echo "Found GStreamer plugins scanner in: ${SOURCE_GSTREAMER_PLUGINS_SCANNER}" +echo "Target GStreamer plugins in: ${TARGET_GSTREAMER_PLUGINS}" +echo "*****************************************" + if [ ! -f "$KAIDAN_SOURCES/build/bin/kaidan" ]; then echo "*****************************************" echo "Building Kaidan" echo "*****************************************" { cdnew $KAIDAN_SOURCES/build cmake .. \ -DECM_DIR=/usr/share/ECM/cmake \ -DCMAKE_PREFIX_PATH=$QT_LINUX\; \ -DI18N=1 \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_INSTALL_PREFIX=/usr \ - -DQUICK_COMPILER=ON -DAPPIMAGE=ON -DBUNDLE_ICONS=ON + -DQUICK_COMPILER=ON -DAPPIMAGE=ON -DBUNDLE_ICONS=ON \ + -DTARGET_GSTREAMER_PLUGINS="${TARGET_GSTREAMER_PLUGINS}" - make -j$(nproc) + cmake --build . } fi if [ ! -f "$KAIDAN_SOURCES/AppDir/usr/bin/kaidan" ]; then echo "*****************************************" echo "Installing Kaidan" echo "*****************************************" { cd $KAIDAN_SOURCES/build DESTDIR=$KAIDAN_SOURCES/AppDir cmake --build . --target install } fi echo "*****************************************" echo "Packing into AppImage" echo "*****************************************" { cd $KAIDAN_SOURCES export LD_LIBRARY_PATH=$QT_LINUX/lib/:$LD_LIBRARY_PATH export PATH=$QT_LINUX/bin/:$PATH # set qmake binary when using portable Qt; linuxdeployqt will find it on its # own on global installs if [ -f $QT_LINUX/bin/qmake ]; then QMAKE_BINARY="-qmake=$QT_LINUX/bin/qmake" fi export VERSION="continuous" extra_plugins=( # Image formats imageformats/libqsvg.so imageformats/libqjpeg.so imageformats/libqgif.so imageformats/libqwebp.so # Icon formats iconengines/libqsvgicon.so # QtMultimedia audio/libqtaudio_alsa.so audio/libqtmedia_pulse.so playlistformats/libqtmultimedia_m3u.so mediaservice/libgstaudiodecoder.so mediaservice/libgstcamerabin.so mediaservice/libgstmediacapture.so mediaservice/libgstmediaplayer.so # QtLocation geoservices/libqtgeoservices_esri.so geoservices/libqtgeoservices_itemsoverlay.so geoservices/libqtgeoservices_mapbox.so geoservices/libqtgeoservices_mapboxgl.so geoservices/libqtgeoservices_nokia.so geoservices/libqtgeoservices_osm.so # QtPositioning position/libqtposition_geoclue.so position/libqtposition_positionpoll.so position/libqtposition_serialnmea.so ) - $KAIDAN_SOURCES/3rdparty/linuxdeployqt/squashfs-root/AppRun \ - $KAIDAN_SOURCES/AppDir/usr/share/applications/kaidan.desktop \ - -qmldir=$KAIDAN_SOURCES/src/qml/ \ - -qmlimport=/opt/kf5/lib/x86_64-linux-gnu/qml \ - -extra-plugins="$(join_by , "${extra_plugins[@]}")" \ - -appimage -no-copy-copyright-files \ - $QMAKE_BINARY + # We need gstreamer plugins copied first so that their dependencies get deployed as well. + APPDIR=${KAIDAN_SOURCES}/AppDir + COPY_GSTREAMER_SCANNER=0 + + if [ "$(dirname "${SOURCE_GSTREAMER_PLUGINS_SCANNER}")" != "${TARGET_GSTREAMER_PLUGINS}" ]; then + COPY_GSTREAMER_SCANNER=1 + fi + + TARGET_GSTREAMER_PLUGINS="${APPDIR}${TARGET_GSTREAMER_PLUGINS}" + + if [ -d "${SOURCE_GSTREAMER_PLUGINS}" ]; then + echo "Copying ${SOURCE_GSTREAMER_PLUGINS}/libgst* to ${TARGET_GSTREAMER_PLUGINS}..." + mkdir -p "${TARGET_GSTREAMER_PLUGINS}" + cp -fr "${SOURCE_GSTREAMER_PLUGINS}/libgst"* "${TARGET_GSTREAMER_PLUGINS}" + + if [ ${COPY_GSTREAMER_SCANNER} -eq 1 ]; then + echo "Copying ${SOURCE_GSTREAMER_PLUGINS_SCANNER} to ${TARGET_GSTREAMER_PLUGINS}..." + cp -f "${SOURCE_GSTREAMER_PLUGINS_SCANNER}" "${TARGET_GSTREAMER_PLUGINS}" + fi + else + echo "*****************************************" + echo "GStreamer plugins not found!!!" + echo "*****************************************" + exit 42 + fi + + # linuxdeployqt would only deploy existing binaries in AppDir if they are located in AppDir/usr/lib + # We do not want to put gstreamer plugins in this location, so they are not deployed. + # We then do need one -executable=file entry per plugin... + GSTREAMER_PLUGINS= + while IFS= read -r gstreamer_plugin; do + GSTREAMER_PLUGINS="${GSTREAMER_PLUGINS} -executable=\"${gstreamer_plugin}\"" + done < <( find "${TARGET_GSTREAMER_PLUGINS}" -type f ) + + eval $KAIDAN_SOURCES/3rdparty/linuxdeployqt/squashfs-root/AppRun \ + $APPDIR/usr/share/applications/kaidan.desktop \ + -qmldir=$KAIDAN_SOURCES/src/qml/ \ + -qmlimport=/opt/kf5/lib/x86_64-linux-gnu/qml \ + -extra-plugins="$(join_by , "${extra_plugins[@]}")" \ + ${GSTREAMER_PLUGINS} \ + -appimage -no-copy-copyright-files \ + $QMAKE_BINARY }