diff --git a/CMakeLists.txt b/CMakeLists.txt index b8a8fa8..3c4dbd0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,162 +1,173 @@ cmake_minimum_required(VERSION 3.5) set(KF5_VERSION "5.71.0") # handled by release scripts project(Solid VERSION ${KF5_VERSION}) include(FeatureSummary) find_package(ECM 5.70.0 NO_MODULE) set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules") feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include(KDEInstallDirs) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(ECMQtDeclareLoggingCategory) set(REQUIRED_QT_VERSION 5.12.0) find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Xml Gui) if (NOT ANDROID) find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED DBus) endif() if(WIN32) find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Network) endif() include(ECMGenerateExportHeader) include(CMakePackageConfigHelpers) include(ECMSetupVersion) include(ECMGenerateHeaders) include(ECMMarkNonGuiExecutable) include(ECMAddQch) find_package(FLEX REQUIRED) set_package_properties(FLEX PROPERTIES URL "http://flex.sourceforge.net" DESCRIPTION "Fast Lexical Analyzer" TYPE REQUIRED PURPOSE "Required for the Predicate parser" ) find_package(BISON REQUIRED) set_package_properties(BISON PROPERTIES URL "http://www.gnu.org/software/bison" DESCRIPTION "general-purpose parser generator" TYPE REQUIRED PURPOSE "Required for the Predicate parser" ) +find_package(IMobileDevice) +set_package_properties(IMobileDevice PROPERTIES + TYPE OPTIONAL + PURPOSE "Needed to build the iOS device support backend" + ) + include(ECMPoQmTools) set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].") option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF) add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)") ecm_setup_version(PROJECT VARIABLE_PREFIX SOLID VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/solid_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5SolidConfigVersion.cmake" SOVERSION 5) # TODO: Remove these remove_definitions(-DQT_NO_CAST_FROM_ASCII) remove_definitions(-DQT_NO_CAST_FROM_BYTEARRAY) add_definitions(-DQT_NO_FOREACH) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050d00) if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") ecm_install_po_files_as_qm(po) endif() option(WITH_NEW_SOLID_JOB "WIP: base Job class" Off) add_feature_info(Solid::Job WITH_NEW_SOLID_JOB "WIP: Base class for Solid Asynchronous apis") option(WITH_NEW_POWER_ASYNC_API "WIP: Asynchronous API for power management" Off) add_feature_info(Solid::PowerManagement WITH_NEW_POWER_ASYNC_API "WIP: Asynchronous API for power management") option(WITH_NEW_POWER_ASYNC_FREEDESKTOP "WIP: Freedesktop backend for the asynchronous api" Off) add_feature_info(Solid::PowerManagement WITH_NEW_POWER_ASYNC_FREEDESKTOP "WIP: Freedesktop backend for the asynchronous api") include(SolidBackendsMacros) add_device_backends_begin() add_device_backend(fakehw) if (CMAKE_SYSTEM_NAME MATCHES Linux) option(UDEV_DISABLED "Allows disabling UDev usage on Linux builds" OFF) if (NOT UDEV_DISABLED) find_package(UDev REQUIRED) set_package_properties(UDev PROPERTIES TYPE REQUIRED PURPOSE "Allows Solid to use UDev to provide information about devices on Linux" ) endif() if (UDev_FOUND) set(UDEV_FOUND TRUE) # for config-solid.h add_device_backend(udev) endif() add_device_backend(udisks2) add_device_backend(fstab) add_device_backend(upower) elseif (APPLE) find_package(IOKit REQUIRED) add_device_backend(iokit) elseif (WIN32) add_device_backend(win) elseif (NOT ANDROID) add_device_backend(upower) add_device_backend(fstab) if (CMAKE_SYSTEM_NAME MATCHES FreeBSD) # FIXME: this should work on more Unix systems option(EXPERIMENTAL_BSDISKS "Use UDisks2/bsdisks backend instead of HAL to manage disk devices" OFF) else () set(EXPERIMENTAL_BSDISKS FALSE) endif() if(EXPERIMENTAL_BSDISKS) add_device_backend(udisks2) else() add_device_backend(hal) endif() endif() + +if(IMobileDevice_FOUND) + add_device_backend(imobile) +endif() + add_device_backends_cmake() add_subdirectory(src) if (TARGET Qt5::DBus AND BUILD_TESTING) add_subdirectory(autotests) endif() # create a Config.cmake and a ConfigVersion.cmake file and install them set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5Solid") if (BUILD_QCH) ecm_install_qch_export( TARGETS KF5Solid_QCH FILE KF5SolidQchTargets.cmake DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KF5SolidQchTargets.cmake\")") endif() configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/KF5SolidConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KF5SolidConfig.cmake" PATH_VARS KDE_INSTALL_DBUSINTERFACEDIR INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5SolidConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KF5SolidConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT KF5SolidTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KF5SolidTargets.cmake NAMESPACE KF5:: ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/solid_version.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5} COMPONENT Devel ) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/cmake/FindIMobileDevice.cmake b/cmake/FindIMobileDevice.cmake new file mode 100644 index 0000000..4dcf957 --- /dev/null +++ b/cmake/FindIMobileDevice.cmake @@ -0,0 +1,61 @@ +#.rst: +# FindIMobileDevice +# -------- +# +# Try to find the imobiledevice library, once done this will define: +# +# ``IMobileDevice_FOUND`` +# System has libimobiledevice. +# +# ``IMobileDevice_INCLUDE_DIRS`` +# The libimobiledevice include directory. +# +# ``IMobileDevice_LIBRARIES`` +# The libimobiledevice libraries. +# +# ``IMobileDevice_VERSION`` +# The libimobiledevice version. +# +# If ``IMobileDevice_FOUND`` is TRUE, the following imported target +# will be available: +# +# ``IMobileDevice::IMobileDevice`` +# The libimobiledevice library + +#============================================================================= +# SPDX-FileCopyrightText: 2020 Kai Uwe Broulik +# +# SPDX-License-Identifier: BSD-3-Clause +#============================================================================= + +find_package(PkgConfig QUIET) +pkg_check_modules(PC_libimobiledevice QUIET libimobiledevice) + +find_path(IMobileDevice_INCLUDE_DIRS NAMES libimobiledevice/libimobiledevice.h HINTS ${PC_libimobiledevice_INCLUDE_DIRS}) +find_library(IMobileDevice_LIBRARIES NAMES imobiledevice HINTS ${PC_libimobiledevice_LIBRARY_DIRS}) + +set(IMobileDevice_VERSION ${PC_libimobiledevice_VERSION}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(IMobileDevice + FOUND_VAR IMobileDevice_FOUND + REQUIRED_VARS IMobileDevice_INCLUDE_DIRS IMobileDevice_LIBRARIES + VERSION_VAR IMobileDevice_VERSION +) + +mark_as_advanced(IMobileDevice_INCLUDE_DIRS IMobileDevice_LIBRARIES) + +if(IMobileDevice_FOUND AND NOT TARGET IMobileDevice::IMobileDevice) + add_library(IMobileDevice::IMobileDevice UNKNOWN IMPORTED) + set_target_properties(IMobileDevice::IMobileDevice PROPERTIES + IMPORTED_LOCATION "${IMobileDevice_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${IMobileDevice_INCLUDE_DIRS}" + INTERFACE_COMPILE_DEFINITIONS "${PC_libimobiledevice_CFLAGS_OTHER}" + ) +endif() + +include(FeatureSummary) +set_package_properties(IMobileDevice PROPERTIES + DESCRIPTION "library to communicate with iOS devices" + URL "https://www.libimobiledevice.org/" +) diff --git a/src/solid/devices/backends/imobile/CMakeLists.txt b/src/solid/devices/backends/imobile/CMakeLists.txt new file mode 100644 index 0000000..cc2704e --- /dev/null +++ b/src/solid/devices/backends/imobile/CMakeLists.txt @@ -0,0 +1,17 @@ +set(backend_sources + imobile.cpp + imobiledevice.cpp + imobilemanager.cpp + imobiledeviceinterface.cpp + imobileportablemediaplayer.cpp +) +set(backend_libs IMobileDevice::IMobileDevice) + +ecm_qt_declare_logging_category(backend_sources + HEADER imobile-debug.h + IDENTIFIER Solid::Backends::IMobile::IMOBILE + DEFAULT_SEVERITY Warning + CATEGORY_NAME org.kde.solid.imobile + DESCRIPTION "IMobileDevice (Solid)" + EXPORT SOLID +) diff --git a/src/solid/devices/backends/imobile/imobile.cpp b/src/solid/devices/backends/imobile/imobile.cpp new file mode 100644 index 0000000..b2a410c --- /dev/null +++ b/src/solid/devices/backends/imobile/imobile.cpp @@ -0,0 +1,15 @@ +/* + SPDX-FileCopyrightText: 2020 Kai Uwe Broulik + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "imobile.h" + +// FIXME why does relying on this give me a linker error? +//using namespace Solid::Backends::IMobile; + +QString Solid::Backends::IMobile::udiPrefix() +{ + return QStringLiteral("/org/kde/solid/imobile"); +} diff --git a/src/solid/devices/backends/imobile/imobile.h b/src/solid/devices/backends/imobile/imobile.h new file mode 100644 index 0000000..790353b --- /dev/null +++ b/src/solid/devices/backends/imobile/imobile.h @@ -0,0 +1,51 @@ +/* + SPDX-FileCopyrightText: 2020 Kai Uwe Broulik + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#ifndef SOLID_BACKENDS_IMOBILE_H +#define SOLID_BACKENDS_IMOBILE_H + +#include +#include + +#include +#include + +namespace Solid +{ +namespace Backends +{ +namespace IMobile +{ + +QString udiPrefix(); + +struct AfcClientCleanup +{ + static inline void cleanup(afc_client_t afcClient) { + if (afcClient) { + afc_client_free(afcClient); + } + } +}; +// Why can't you use lambdas for this... +// QScopedPointer also needs the dereferenced type, not a point to it, hence using _private +using ScopedAfcClientPtr = QScopedPointer; + +struct LockdowndClientCleanup +{ + static inline void cleanup(lockdownd_client_t lockdowndClient) { + if (lockdowndClient) { + lockdownd_client_free(lockdowndClient); + } + } +}; +using ScopedLockdowndClientPtr = QScopedPointer; + +} // namespace IMobile +} // namespace Backends +} // namespace Solid + +#endif // SOLID_BACKENDS_IMOBILE_H diff --git a/src/solid/devices/backends/imobile/imobiledevice.cpp b/src/solid/devices/backends/imobile/imobiledevice.cpp new file mode 100644 index 0000000..4c49693 --- /dev/null +++ b/src/solid/devices/backends/imobile/imobiledevice.cpp @@ -0,0 +1,156 @@ +/* + SPDX-FileCopyrightText: 2020 Kai Uwe Broulik + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "imobiledevice.h" + +#include + +#include "imobile-debug.h" + +#include "imobile.h" +#include "imobileportablemediaplayer.h" + +#include +#include + +using namespace Solid::Backends::IMobile; + +IMobileDevice::IMobileDevice(const QString &udi) + : Solid::Ifaces::Device() + , m_udi(udi) +{ + const QString deviceId = udi.mid(udiPrefix().length() + 1); + + idevice_new(&m_device, deviceId.toUtf8().constData()); + if (!m_device) { + qCWarning(IMOBILE) << "Failed to create device instance for" << deviceId; + return; + } + + lockdownd_client_t lockdowndClient = nullptr; + auto ret = lockdownd_client_new_with_handshake(m_device, &lockdowndClient, "kde_solid_imobile"); + if (ret != LOCKDOWN_E_SUCCESS || !lockdowndClient) { + qCWarning(IMOBILE) << "Failed to create lockdownd client for" << deviceId << "make sure device is trusted and unlocked"; + return; + } + + ScopedLockdowndClientPtr lockdowndClientPtr(lockdowndClient); + + lockdownd_service_descriptor_t service = nullptr; + ret = lockdownd_start_service(lockdowndClientPtr.data(), "com.apple.afc", &service); + if (ret != LOCKDOWN_E_SUCCESS || !service) { + qCWarning(IMOBILE) << "Failed to start AFC service on" << deviceId; + return; + } + + char *name = nullptr; + auto lockdownRet = lockdownd_get_device_name(lockdowndClientPtr.data(), &name); + if (lockdownRet != LOCKDOWN_E_SUCCESS) { + qCWarning(IMOBILE) << "Failed to get device name for" << deviceId; + } else if (name) { + m_name = QString::fromUtf8(name); + free(name); + } + + afc_client_t afcClient = nullptr; + auto afcRet = afc_client_new(m_device, service, &afcClient); + if (afcRet != AFC_E_SUCCESS || !afcClient) { + qCWarning(IMOBILE) << "Failed to create AFC client on" << deviceId; + return; + } + + ScopedAfcClientPtr afcClientPtr(afcClient); + m_hasAfc = true; + + char *model = nullptr; + if (afc_get_device_info_key(afcClientPtr.data(), "Model", &model) != AFC_E_SUCCESS) { + qCWarning(IMOBILE) << "Failed to get device model for" << deviceId; + } else if (model) { + m_model = QString::fromUtf8(model); + free(model); + } +} + +IMobileDevice::~IMobileDevice() +{ + if (m_device) { + idevice_free(m_device); + m_device = nullptr; + } +} + +QString IMobileDevice::udi() const +{ + return m_udi; +} + +QString IMobileDevice::parentUdi() const +{ + return udiPrefix(); +} + +QString IMobileDevice::vendor() const +{ + return QCoreApplication::translate("imobiledevice", "Apple", "Company name"); +} + +QString IMobileDevice::product() const +{ + return m_model; +} + +QString IMobileDevice::icon() const +{ + if (m_model.contains(QLatin1String("iPod"))) { + // We can assume iPod running iOS/supporting imobiledevice is an iPod touch? + return QStringLiteral("multimedia-player-apple-ipod-touch"); + } else if (m_model.contains(QLatin1String("iPad"))) { + return QStringLiteral("computer-apple-ipad"); + // TODO Watch, TV, etc? :) + } else { + return QStringLiteral("phone-apple-iphone"); + } +} + +QStringList IMobileDevice::emblems() const +{ + return {}; +} + +QString IMobileDevice::description() const +{ + return m_name; +} + +bool IMobileDevice::queryDeviceInterface(const Solid::DeviceInterface::Type &type) const +{ + switch (type) { + // TOOD would be cool to support GenericInterface for reading + // arbitrary plist configuration, cf. what ideviceinfo tool does + + case Solid::DeviceInterface::PortableMediaPlayer: + return m_hasAfc; + + default: + return false; + } +} + +QObject *IMobileDevice::createDeviceInterface(const Solid::DeviceInterface::Type &type) +{ + if (!queryDeviceInterface(type)) { + return nullptr; + } + + switch (type) { + case Solid::DeviceInterface::PortableMediaPlayer: + return new PortableMediaPlayer(this); + + default: + Q_UNREACHABLE(); + return nullptr; + } +} diff --git a/src/solid/devices/backends/imobile/imobiledevice.h b/src/solid/devices/backends/imobile/imobiledevice.h new file mode 100644 index 0000000..8286b4c --- /dev/null +++ b/src/solid/devices/backends/imobile/imobiledevice.h @@ -0,0 +1,62 @@ +/* + SPDX-FileCopyrightText: 2020 Kai Uwe Broulik + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#ifndef SOLID_BACKENDS_IMOBILE_IMOBILEDEVICE_H +#define SOLID_BACKENDS_IMOBILE_IMOBILEDEVICE_H + +//#include "udev.h" + +#include +#include + +#include +#include + +namespace Solid +{ +namespace Backends +{ +namespace IMobile +{ + +class IMobileDevice : public Solid::Ifaces::Device +{ + Q_OBJECT + +public: + explicit IMobileDevice(const QString &udi); + ~IMobileDevice() override; + + QString udi() const override; + QString parentUdi() const override; + + QString vendor() const override; + QString product() const override; + QString icon() const override; + QStringList emblems() const override; + QString description() const override; + + bool queryDeviceInterface(const Solid::DeviceInterface::Type &type) const override; + + QObject *createDeviceInterface(const Solid::DeviceInterface::Type &type) override; + +private: + QString m_udi; + + QString m_name; + QString m_model; + + bool m_hasAfc = false; + + idevice_t m_device = nullptr; + +}; + +} // namespace IMobile +} // namespace Backends +} // namespace Solid + +#endif // SOLID_BACKENDS_IMOBILE_IMOBILEDEVICE_H diff --git a/src/solid/devices/backends/imobile/imobiledeviceinterface.cpp b/src/solid/devices/backends/imobile/imobiledeviceinterface.cpp new file mode 100644 index 0000000..62c1528 --- /dev/null +++ b/src/solid/devices/backends/imobile/imobiledeviceinterface.cpp @@ -0,0 +1,19 @@ +/* + SPDX-FileCopyrightText: 2020 Kai Uwe Broulik + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "imobiledeviceinterface.h" + +#include "imobiledevice.h" + +using namespace Solid::Backends::IMobile; + +DeviceInterface::DeviceInterface(IMobileDevice *device) + : QObject(device) + , m_device(device) +{ +} + +DeviceInterface::~DeviceInterface() = default; diff --git a/src/solid/devices/backends/imobile/imobiledeviceinterface.h b/src/solid/devices/backends/imobile/imobiledeviceinterface.h new file mode 100644 index 0000000..3d6391f --- /dev/null +++ b/src/solid/devices/backends/imobile/imobiledeviceinterface.h @@ -0,0 +1,42 @@ +/* + SPDX-FileCopyrightText: 2020 Kai Uwe Broulik + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#ifndef SOLID_BACKENDS_IMOBILE_IMOBILEDEVICEINTERFACE_H +#define SOLID_BACKENDS_IMOBILE_IMOBILEDEVICEINTERFACE_H + +#include +//#include "udevdevice.h" + +#include + +namespace Solid +{ +namespace Backends +{ +namespace IMobile +{ + +class IMobileDevice; + +class DeviceInterface : public QObject, virtual public Solid::Ifaces::DeviceInterface +{ + Q_OBJECT + Q_INTERFACES(Solid::Ifaces::DeviceInterface) + +public: + explicit DeviceInterface(IMobileDevice *device); + ~DeviceInterface() override; + +protected: + IMobileDevice *m_device; + +}; + +} +} +} + +#endif // SOLID_BACKENDS_IMOBILE_IMOBILEDEVICEINTERFACE_H diff --git a/src/solid/devices/backends/imobile/imobilemanager.cpp b/src/solid/devices/backends/imobile/imobilemanager.cpp new file mode 100644 index 0000000..b94aabf --- /dev/null +++ b/src/solid/devices/backends/imobile/imobilemanager.cpp @@ -0,0 +1,132 @@ +/* + SPDX-FileCopyrightText: 2020 Kai Uwe Broulik + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "imobilemanager.h" + +#include "imobile-debug.h" + +#include "../shared/rootdevice.h" +#include "imobile.h" +#include "imobiledevice.h" + +#include + +using namespace Solid::Backends::IMobile; +using namespace Solid::Backends::Shared; + +Manager::Manager(QObject *parent) + : Solid::Ifaces::DeviceManager(parent) +{ + auto ret = idevice_event_subscribe([](const idevice_event_t *event, void *user_data) { + static_cast(user_data)->onDeviceEvent(event); + }, this); + if (ret != IDEVICE_E_SUCCESS) { + qCWarning(IMOBILE) << "Failed to subscribe to device events"; + } + + char **devices = nullptr; + int count = 0; + + ret = idevice_get_device_list(&devices, &count); + if (ret != IDEVICE_E_SUCCESS && ret != IDEVICE_E_NO_DEVICE) { + qCWarning(IMOBILE) << "Failed to get list of iOS devices" << ret; + return; + } + + m_deviceUdis.reserve(count); + + for (int i = 0; i < count; ++i) { + m_deviceUdis.append(udiPrefix() + QLatin1Char('/') + QString::fromLatin1(devices[i])); + } + + if (devices) { + idevice_device_list_free(devices); + } +} + +Manager::~Manager() +{ + idevice_event_unsubscribe(); +} + +QObject *Manager::createDevice(const QString &udi) +{ + if (udi == udiPrefix()) { + RootDevice *root = new RootDevice(udi); + root->setProduct(tr("iDevice")); + root->setDescription(tr("iOS devices")); + root->setIcon("phone-apple-iphone"); + return root; + } + + if (m_deviceUdis.contains(udi)) { + return new IMobileDevice(udi); + } + + return nullptr; +} + +QStringList Manager::devicesFromQuery(const QString &parentUdi, Solid::DeviceInterface::Type type) +{ + QStringList devices; + + if (!parentUdi.isEmpty() || type != Solid::DeviceInterface::Unknown) { + for (const QString &udi : m_deviceUdis) { + IMobileDevice device(udi); + if (!device.queryDeviceInterface(type)) { + continue; + } + + if (!parentUdi.isEmpty() && device.parentUdi() != parentUdi) { + continue; + } + + devices << udi; + } + } + + return devices; +} + +QStringList Manager::allDevices() +{ + return m_deviceUdis; +} + +QSet Manager::supportedInterfaces() const +{ + return { + Solid::DeviceInterface::PortableMediaPlayer + }; +} + +QString Manager::udiPrefix() const +{ + return Solid::Backends::IMobile::udiPrefix(); +} + +void Manager::onDeviceEvent(const idevice_event_t *event) +{ + const QString udi = udiPrefix() + QLatin1Char('/') + QString::fromLatin1(event->udid); + + switch (event->event) { + case IDEVICE_DEVICE_ADD: + if (!m_deviceUdis.contains(udi)) { + m_deviceUdis.append(udi); + emit deviceAdded(udi); + } + return; + case IDEVICE_DEVICE_REMOVE: + if (m_deviceUdis.removeOne(udi)) { + emit deviceRemoved(udi); + } + return; + case IDEVICE_DEVICE_PAIRED: + return; + } + + qCDebug(IMOBILE) << "Unhandled device event" << event->event << "for" << event->udid; +} diff --git a/src/solid/devices/backends/imobile/imobilemanager.h b/src/solid/devices/backends/imobile/imobilemanager.h new file mode 100644 index 0000000..9b89c8a --- /dev/null +++ b/src/solid/devices/backends/imobile/imobilemanager.h @@ -0,0 +1,46 @@ +/* + SPDX-FileCopyrightText: 2020 Kai Uwe Broulik + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#ifndef SOLID_BACKENDS_IMOBILE_IMOBILEMANAGER_H +#define SOLID_BACKENDS_IMOBILE_IMOBILEMANAGER_H + +#include + +#include + +namespace Solid +{ +namespace Backends +{ +namespace IMobile +{ + +class Manager : public Solid::Ifaces::DeviceManager +{ + Q_OBJECT + +public: + explicit Manager(QObject *parent); + ~Manager() override; + + QObject *createDevice(const QString &udi) override; + QStringList devicesFromQuery(const QString &parentUdi, Solid::DeviceInterface::Type type) override; + QStringList allDevices() override; + QSet supportedInterfaces() const override; + QString udiPrefix() const override; + + void onDeviceEvent(const idevice_event_t *event); + +private: + QStringList m_deviceUdis; + +}; + +} // namespace IMobile +} // namespace Backends +} // namespace Solid + +#endif // SOLID_BACKENDS_IMOBILE_IMOBILEMANAGER_H diff --git a/src/solid/devices/backends/imobile/imobileportablemediaplayer.cpp b/src/solid/devices/backends/imobile/imobileportablemediaplayer.cpp new file mode 100644 index 0000000..0074231 --- /dev/null +++ b/src/solid/devices/backends/imobile/imobileportablemediaplayer.cpp @@ -0,0 +1,51 @@ +/* + SPDX-FileCopyrightText: 2020 Kai Uwe Broulik + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "imobileportablemediaplayer.h" + +#include "imobile.h" +#include "imobiledevice.h" + +using namespace Solid::Backends::IMobile; + +PortableMediaPlayer::PortableMediaPlayer(IMobileDevice *device) + : DeviceInterface(device) +{ + +} + +PortableMediaPlayer::~PortableMediaPlayer() +{ + +} + +QStringList PortableMediaPlayer::supportedProtocols() const +{ + return { + // We only create an IMobile PortableMediaPlayer when AFC works anyway + QStringLiteral("afc") + }; +} + +QStringList PortableMediaPlayer::supportedDrivers(QString protocol) const +{ + Q_UNUSED(protocol) + return { + // libimobiledevice goes via usbmuxd + QStringLiteral("usbmux") + }; +} + +QVariant PortableMediaPlayer::driverHandle(const QString &driver) const +{ + if (driver == QLatin1String("usbmux")) { + // as per docs "usbmux" should return the device UDID + // which we use as part of our UDI + return m_device->udi().mid(udiPrefix().length() + 1); + } + + return {}; +} diff --git a/src/solid/devices/backends/imobile/imobileportablemediaplayer.h b/src/solid/devices/backends/imobile/imobileportablemediaplayer.h new file mode 100644 index 0000000..8882939 --- /dev/null +++ b/src/solid/devices/backends/imobile/imobileportablemediaplayer.h @@ -0,0 +1,44 @@ +/* + SPDX-FileCopyrightText: 2010 Rafael Fernández López + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#ifndef SOLID_BACKENDS_IMOBILE_IMOBILEPORTABLEMEDIAPLAYER_H +#define SOLID_BACKENDS_IMOBILE_IMOBILEPORTABLEMEDIAPLAYER_H + +#include + +#include + +#include "imobiledeviceinterface.h" + +namespace Solid +{ +namespace Backends +{ +namespace IMobile +{ + +class IMobileDevice; + +class PortableMediaPlayer : public DeviceInterface, virtual public Solid::Ifaces::PortableMediaPlayer +{ + Q_OBJECT + Q_INTERFACES(Solid::Ifaces::PortableMediaPlayer) + +public: + PortableMediaPlayer(IMobileDevice *device); + ~PortableMediaPlayer() override; + + QStringList supportedProtocols() const override; + QStringList supportedDrivers(QString protocol = QString()) const override; + QVariant driverHandle(const QString &driver) const override; + +}; + +} // namespace IMobile +} // namespace Backends +} // namespace Solid + +#endif // SOLID_BACKENDS_IMOBILE_IMOBILEPORTABLEMEDIAPLAYER_H diff --git a/src/solid/devices/managerbase.cpp b/src/solid/devices/managerbase.cpp index 01cc51f..3520ac9 100644 --- a/src/solid/devices/managerbase.cpp +++ b/src/solid/devices/managerbase.cpp @@ -1,88 +1,94 @@ /* SPDX-FileCopyrightText: 2006-2007 Kevin Ottens SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #include "managerbase_p.h" #include #include // do *not* use other defines than BUILD_DEVICE_BACKEND_$backend to include // the managers, and keep an alphabetical order #ifdef BUILD_DEVICE_BACKEND_fakehw #include "backends/fakehw/fakemanager.h" #endif #ifdef BUILD_DEVICE_BACKEND_fstab #include "backends/fstab/fstabmanager.h" #endif #ifdef BUILD_DEVICE_BACKEND_hal #include "backends/hal/halmanager.h" #endif +#ifdef BUILD_DEVICE_BACKEND_imobile +#include "backends/imobile/imobilemanager.h" +#endif #ifdef BUILD_DEVICE_BACKEND_iokit #include "backends/iokit/iokitmanager.h" #endif #ifdef BUILD_DEVICE_BACKEND_udev #include "backends/udev/udevmanager.h" #endif #ifdef BUILD_DEVICE_BACKEND_udisks2 #include "backends/udisks2/udisksmanager.h" #endif #ifdef BUILD_DEVICE_BACKEND_upower #include "backends/upower/upowermanager.h" #endif #ifdef BUILD_DEVICE_BACKEND_win #include "backends/win/windevicemanager.h" #endif Solid::ManagerBasePrivate::ManagerBasePrivate() { } Solid::ManagerBasePrivate::~ManagerBasePrivate() { qDeleteAll(m_backends); } // do *not* use other defines than BUILD_DEVICE_BACKEND_$backend to add // the managers, and keep an alphabetical order void Solid::ManagerBasePrivate::loadBackends() { QString solidFakeXml(QString::fromLocal8Bit(qgetenv("SOLID_FAKEHW"))); if (!solidFakeXml.isEmpty()) { #ifdef BUILD_DEVICE_BACKEND_fakehw m_backends << new Solid::Backends::Fake::FakeManager(nullptr, solidFakeXml); #endif } else { #ifdef BUILD_DEVICE_BACKEND_fstab m_backends << new Solid::Backends::Fstab::FstabManager(nullptr); #endif #ifdef BUILD_DEVICE_BACKEND_hal m_backends << new Solid::Backends::Hal::HalManager(nullptr); #endif +#ifdef BUILD_DEVICE_BACKEND_imobile + m_backends << new Solid::Backends::IMobile::Manager(nullptr); +#endif #ifdef BUILD_DEVICE_BACKEND_iokit m_backends << new Solid::Backends::IOKit::IOKitManager(nullptr); #endif #ifdef BUILD_DEVICE_BACKEND_udev m_backends << new Solid::Backends::UDev::UDevManager(nullptr); #endif #ifdef BUILD_DEVICE_BACKEND_udisks2 m_backends << new Solid::Backends::UDisks2::Manager(nullptr); #endif #ifdef BUILD_DEVICE_BACKEND_upower m_backends << new Solid::Backends::UPower::UPowerManager(nullptr); #endif #ifdef BUILD_DEVICE_BACKEND_win m_backends << new Solid::Backends::Win::WinDeviceManager(nullptr); #endif } } QList Solid::ManagerBasePrivate::managerBackends() const { return m_backends; }