diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d93cba3..727642a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,320 +1,290 @@ project(Phonon) cmake_minimum_required(VERSION 2.8.9 FATAL_ERROR) add_definitions(-DPHONON_BUILD_WITH_CMAKE) option(PHONON_ASSERT_STATES "Enable code to assert backend state transitions" ON) option(PHONON_BUILD_EXPERIMENTAL "Build the experimental library" ON) option(PHONON_BUILD_DEMOS "Build the demos" OFF) option(PHONON_BUILD_DECLARATIVE_PLUGIN "Build the Qt Declarative (QML) plugin" OFF) option(PHONON_BUILD_DESCRIPTOR "Builds and installs a library descriptor to be used for ABI checks" OFF) option(PHONON_BUILD_DESIGNER_PLUGIN "Build the Qt Designer plugin" ON) option(PHONON_BUILD_DOC "Build the API documentation" OFF) option(PHONON_INSTALL_QT_COMPAT_HEADERS "Install QtPhonon compatibility headers" OFF) option(PHONON_NO_CAPTURE "Disable the capture capabilities") option(PHONON_NO_DBUS "Deactivate DBus support (used to expose AudioOutputs and for deprecated runtime backend switching)" OFF) -option(PHONON_BUILD_PHONON4QT5 "Build Phonon 4 transitional library for Qt5 (API compatible with regular Phonon4)" OFF) - # The following variables directly influence the library's soname version. # It is highly advised to think twice before changing those. # If you are unsure about the this: http://plan99.net/~mike/writing-shared-libraries.html set(PHONON_LIB_MAJOR_VERSION "4") # Only change on binary incompatible changes set(PHONON_LIB_MINOR_VERSION "10") # Only change on binary compatible changes with new interfaces set(PHONON_LIB_PATCH_VERSION "3") # Bump whenever you feel like it :P set(PHONON_LIB_VERSION "${PHONON_LIB_MAJOR_VERSION}.${PHONON_LIB_MINOR_VERSION}.${PHONON_LIB_PATCH_VERSION}") set(PHONON_LIB_SOVERSION ${PHONON_LIB_MAJOR_VERSION}) -set(PHONON_LIB_SONAME phonon) -set(PHONON_LIB_SONAME_CAMEL Phonon) - -if(PHONON_BUILD_PHONON4QT5) - set(PHONON_LIB_SONAME phonon4qt5) - set(PHONON_LIB_SONAME_CAMEL Phonon4Qt5) -endif(PHONON_BUILD_PHONON4QT5) +set(PHONON_LIB_SONAME phonon4qt5) +set(PHONON_LIB_SONAME_CAMEL Phonon4Qt5) if (PHONON_ASSERT_STATES) message(STATUS "Enabling backend state machine validation.") add_definitions(-DPHONON_ASSERT_STATES) endif (PHONON_ASSERT_STATES) set(PHONON_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") ################################# Requirements ################################# # Core Requirements and CFlag Magic include(cmake/FindPhononInternal.cmake) # Internal is shared with backends, so continue specific finders here... find_package(OpenGL) macro_log_feature(OPENGL_FOUND "OpenGL" "" "" FALSE) set(DBUS_DESC "Needed to enable control of AudioOutputs via DBus") set(DESIGNER_DESC "Needed to build the Qt Designer plugin.") set(GL_DESC "Only recommended when bulding the super experimental QML support.") set(DECL_DESC "Only recommended when bulding the super experimental QML support.") -if (NOT PHONON_BUILD_PHONON4QT5) # Qt 4 - macro_log_feature(QT_QTDBUS_FOUND "Qt4 DBus" ${DBUS_DESC} "http://www.qt.gitorious.net/qt/qt" FALSE) - macro_log_feature(QT_QTDESIGNER_FOUND "Qt4 Designer" ${DESIGNER_DESC} "http://www.qt.gitorious.net/qt/qt" FALSE) - macro_log_feature(QT_QTOPENGL_FOUND "Qt4 OpenGL" ${GL_DESC} "http://www.qt.gitorious.net/qt/qt" FALSE) - macro_log_feature(QT_QTDECLARATIVE_FOUND "Qt4 Declarative" ${DECL_DESC} "http://www.qt.gitorious.net/qt/qt" FALSE) - set(PHONON_PKG_DEPENDENCIES "QtCore QtGui QtDBus") -else (NOT PHONON_BUILD_PHONON4QT5) # Qt 5 - # DBus Audio Output Introspection - find_package(Qt5DBus) - macro_log_feature(Qt5DBus_FOUND "Qt5 DBus (qtbase)" ${DBUS_DESC} "http://www.qt.gitorious.net/qt/qtbase" FALSE) - set(QT_QTDBUS_FOUND ${Qt5DBus_FOUND}) - - # Designer Plugin - find_package(Qt5Designer) - macro_log_feature(Qt5Designer_FOUND "Qt5 Designer (qttools)" ${DESIGNER_DESC} "http://www.qt.gitorious.net/qt/qttools" FALSE) - set(QT_QTDESIGNER_FOUND ${Qt5Designer_FOUND}) - - # VideoGraphicsObject GL Painting - find_package(Qt5OpenGL) - macro_log_feature(Qt5OpenGL_FOUND "Qt5 OpenGL (qtbase)" ${GL_DESC} "http://www.qt.gitorious.net/qt/qtbase" FALSE) - set(QT_QTOPENGL_FOUND ${Qt5OpenGL_FOUND}) - - # Declarative/QML1 - find_package(Qt5Declarative) - macro_log_feature(Qt5Declarative_FOUND "Qt5 Declarative (qtquick1)" ${DECL_DESC} "http://www.qt.gitorious.net/qt/qtquick1" FALSE) - set(QT_QTDECLARATIVE_FOUND ${Qt5Declarative_FOUND}) - - set(PHONON_PKG_DEPENDENCIES "Qt5Core Qt5Gui Qt5DBus") -endif (NOT PHONON_BUILD_PHONON4QT5) -################################################################################ +# DBus Audio Output Introspection +find_package(Qt5DBus) +macro_log_feature(Qt5DBus_FOUND "Qt5 DBus (qtbase)" ${DBUS_DESC} "http://www.qt.gitorious.net/qt/qtbase" FALSE) +set(QT_QTDBUS_FOUND ${Qt5DBus_FOUND}) + +# Designer Plugin +find_package(Qt5Designer) +macro_log_feature(Qt5Designer_FOUND "Qt5 Designer (qttools)" ${DESIGNER_DESC} "http://www.qt.gitorious.net/qt/qttools" FALSE) +set(QT_QTDESIGNER_FOUND ${Qt5Designer_FOUND}) + +# VideoGraphicsObject GL Painting +find_package(Qt5OpenGL) +macro_log_feature(Qt5OpenGL_FOUND "Qt5 OpenGL (qtbase)" ${GL_DESC} "http://www.qt.gitorious.net/qt/qtbase" FALSE) +set(QT_QTOPENGL_FOUND ${Qt5OpenGL_FOUND}) + +# Declarative/QML1 +find_package(Qt5Declarative) +macro_log_feature(Qt5Declarative_FOUND "Qt5 Declarative (qtquick1)" ${DECL_DESC} "http://www.qt.gitorious.net/qt/qtquick1" FALSE) +set(QT_QTDECLARATIVE_FOUND ${Qt5Declarative_FOUND}) + +set(PHONON_PKG_DEPENDENCIES "Qt5Core Qt5Gui Qt5DBus") if(NOT PHONON_BUILD_DECLARATIVE_PLUGIN OR NOT QT_QTOPENGL_FOUND OR NOT OPENGL_FOUND OR NOT QT_QTDECLARATIVE_FOUND) set(PHONON_NO_GRAPHICSVIEW true) else() set(PHONON_NO_GRAPHICSVIEW false) endif() -add_definitions(${QT_DEFINITIONS}) -remove_definitions(-DQT3_SUPPORT_WARNINGS -DQT3_SUPPORT) - -include_directories(${QT_INCLUDES} +include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/includes ${CMAKE_CURRENT_SOURCE_DIR}/phonon ${CMAKE_CURRENT_BINARY_DIR}/phonon) # Convenience variable to hold target link libraries we always need. set(PHONON_LIBS ${PHONON_LIB_SONAME}) # helper macro to make the install paths absolute macro(MAKE_ABS_INSTALL_PATH _absVar _path) if (IS_ABSOLUTE "${_path}") set(${_absVar} "${_path}") else() set(${_absVar} "${CMAKE_INSTALL_PREFIX}/${_path}") endif() endmacro(MAKE_ABS_INSTALL_PATH) include(GNUInstallDirs) set(BUILDSYSTEM_INSTALL_DIR ${CMAKE_INSTALL_FULL_DATAROOTDIR}/${PHONON_LIB_SONAME}/buildsystem/) # Provide a cmake option, which, if set, force the Qt stuff to be installed into the # system Qt directories, which may be outside CMAKE_INSTALL_PREFIX. # By default always install inside CMAKE_INSTALL_PREFIX. option(PHONON_INSTALL_QT_EXTENSIONS_INTO_SYSTEM_QT "Install Qt designer plugins, QML plugins and mkspecs into the system Qt install directory or not" FALSE) if(PHONON_INSTALL_QT_EXTENSIONS_INTO_SYSTEM_QT) # in this case, the values have to be FORCED into the cache set(PHONON_QT_IMPORTS_INSTALL_DIR ${QT_IMPORTS_DIR} CACHE PATH "The directory where Phonon Qt imports (QML) will be installed to." FORCE) set(PHONON_QT_MKSPECS_INSTALL_DIR ${QT_MKSPECS_DIR}/modules CACHE PATH "The directory where Phonon mkspecs will be installed to." FORCE) set(PHONON_QT_PLUGIN_INSTALL_DIR ${QT_PLUGINS_DIR}/designer CACHE PATH "The directory where Phonon Qt plugins will be installed to." FORCE) else(PHONON_INSTALL_QT_EXTENSIONS_INTO_SYSTEM_QT) # in this case, the values can be adjusted by the user e.g. via cmake-gui, so no FORCE # Please do note that these paths are most likely random nonensense depending on what OS and distribution is used, they most likely # need manual adjustment for the actual envrionment. - if(PHONON_BUILD_PHONON4QT5) - set(PHONON_QT_IMPORTS_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/qt5/imports CACHE PATH "The directory where Phonon Qt imports (QML) will be installed to.") - set(PHONON_QT_MKSPECS_INSTALL_DIR share/qt5/mkspecs/modules CACHE PATH "The directory where Phonon mkspecs will be installed to.") - set(PHONON_QT_PLUGIN_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/qt5/plugins/designer CACHE PATH "The directory where Phonon Qt plugins will be installed to." ) - else() - set(PHONON_QT_IMPORTS_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/qt4/imports CACHE PATH "The directory where Phonon Qt imports (QML) will be installed to.") - set(PHONON_QT_MKSPECS_INSTALL_DIR share/qt4/mkspecs/modules CACHE PATH "The directory where Phonon mkspecs will be installed to.") - set(PHONON_QT_PLUGIN_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/qt4/plugins/designer CACHE PATH "The directory where Phonon Qt plugins will be installed to." ) - endif() + set(PHONON_QT_IMPORTS_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/qt5/imports CACHE PATH "The directory where Phonon Qt imports (QML) will be installed to.") + set(PHONON_QT_MKSPECS_INSTALL_DIR share/qt5/mkspecs/modules CACHE PATH "The directory where Phonon mkspecs will be installed to.") + set(PHONON_QT_PLUGIN_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/qt5/plugins/designer CACHE PATH "The directory where Phonon Qt plugins will be installed to." ) endif(PHONON_INSTALL_QT_EXTENSIONS_INTO_SYSTEM_QT) # Check whether the QML plugins will be installed into the system Qt dir, and warn if not: make_abs_install_path(absImportsDir "${PHONON_QT_IMPORTS_INSTALL_DIR}") if(NOT "${absImportsDir}" STREQUAL "${QT_IMPORTS_DIR}") message(STATUS "PHONON_QT_IMPORTS_DIR is set to ${absImportsDir}. The QML plugins for Phonon will not be installed into the Qt system installation directory, which is ${QT_IMPORTS_DIR} . This means the QML plugins file will not be found by default. You can: * switch the cmake option PHONON_INSTALL_QT_EXTENSIONS_INTO_SYSTEM_QT to TRUE * set PHONON_QT_IMPORTS_INSTALL_DIR manually to the system Qt location * at runtime, set the QML_IMPORT_PATH environment variable") endif(NOT "${absImportsDir}" STREQUAL "${QT_IMPORTS_DIR}") # Check whether the mkspecs file will be installed into the system Qt dir, and warn if not: make_abs_install_path(absMkspecsDir "${PHONON_QT_MKSPECS_INSTALL_DIR}") if(NOT "${absMkspecsDir}" STREQUAL "${QT_MKSPECS_DIR}/modules") message(STATUS "PHONON_QT_MKSPECS_INSTALL_DIR is set to ${absMkspecsDir}. The Qt mkspecs file for Phonon will not be installed into the Qt system installation directory, which is ${QT_MKSPECS_DIR}/modules . This means the qt_phonon.pri file will not be found by default. You can: * switch the cmake option PHONON_INSTALL_QT_EXTENSIONS_INTO_SYSTEM_QT to TRUE * set PHONON_QT_MKSPECS_INSTALL_DIR manually to the system Qt location * at runtime, with Qt >= 4.8, set the QMAKEPATH environment variable") endif(NOT "${absMkspecsDir}" STREQUAL "${QT_MKSPECS_DIR}/modules") # Check whether the designer plugins will be installed into the system Qt dir, and warn if not: make_abs_install_path(absPluginDir "${PHONON_QT_PLUGIN_INSTALL_DIR}") if(NOT "${absPluginDir}" STREQUAL "${QT_PLUGINS_DIR}/designer") message(STATUS "PHONON_QT_PLUGIN_INSTALL_DIR is set to ${absPluginDir}. The Qt designer plugins for Phonon will not be installed into the Qt system installation directory, which is ${QT_PLUGINS_DIR}/designer . This means the designer plugins file will not be found by default. You can: * switch the cmake option PHONON_INSTALL_QT_EXTENSIONS_INTO_SYSTEM_QT to TRUE * set PHONON_QT_PLUGINS_INSTALL_DIR manually to the system Qt location * at runtime, set the QT_PLUGIN_PATH environment variable") endif(NOT "${absPluginDir}" STREQUAL "${QT_PLUGINS_DIR}/designer") add_subdirectory(cmake) if(QT_QTDESIGNER_FOUND AND PHONON_BUILD_DESIGNER_PLUGIN) add_subdirectory(designer) endif(QT_QTDESIGNER_FOUND AND PHONON_BUILD_DESIGNER_PLUGIN) if(NOT PHONON_NO_GRAPHICSVIEW) message(STATUS "Not building declarative plugin.") add_subdirectory(declarative) endif() if(PHONON_BUILD_DOC) add_subdirectory(doc) endif(PHONON_BUILD_DOC) add_subdirectory(phonon) add_subdirectory(includes) if(PHONON_BUILD_DEMOS) message(STATUS "Building demos.") #Allows find_package(Phonon) to not die in the demos set(PHONON_BUILDSYSTEM_DIR ${CMAKE_CURRENT_SOURCE_DIR}/cmake) set(PHONON_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/includes/) set(PHONON_LIBRARY ${PHONON_LIB_SONAME}) #Lets the demos find the headers include_directories(${CMAKE_CURRENT_SOURCE_DIR}/includes ${CMAKE_CURRENT_SOURCE_DIR}) add_subdirectory(demos) endif(PHONON_BUILD_DEMOS) if(NOT WIN32) # pkgconfig file make_abs_install_path(ABS_LIB_INSTALL_DIR "${LIB_INSTALL_DIR}") make_abs_install_path(ABS_INCLUDE_INSTALL_DIR "${INCLUDE_INSTALL_DIR}") make_abs_install_path(ABS_BUILDSYSTEM_INSTALL_DIR "${BUILDSYSTEM_INSTALL_DIR}") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/phonon.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/${PHONON_LIB_SONAME}.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PHONON_LIB_SONAME}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) endif(NOT WIN32) set(CMAKECONFIG_INSTALL_DIR ${LIB_INSTALL_DIR}/cmake/${PHONON_LIB_SONAME} ) # figure out the relative path from the installed Config.cmake file to the install prefix (which may be at # runtime different from the chosen CMAKE_INSTALL_PREFIX if under Windows the package was installed anywhere) # This relative path will be configured into the PhononConfig.cmake file(RELATIVE_PATH relInstallDir ${CMAKE_INSTALL_PREFIX}/${CMAKECONFIG_INSTALL_DIR} ${CMAKE_INSTALL_PREFIX} ) make_abs_install_path(absInstallDir ${CMAKECONFIG_INSTALL_DIR}) # Exciting code copy from CMakePackageConfigHelpers to avoid the pain from porting # to it for the 4.x series. # The set variable is used by the PhononConfig.cmake.in to use a refined rootDir # resolution when installed to /usr/lib if("${absInstallDir}" MATCHES "^(/usr)?/lib(64)?/.+") # Handle "/usr move" symlinks created by some Linux distros. set(USR_SYMLINK_RESOLVER " # Use original install prefix when loaded through a \"/usr move\" # cross-prefix symbolic link such as /lib -> /usr/lib. get_filename_component(_realCurr \"\${CMAKE_CURRENT_LIST_DIR}\" REALPATH) get_filename_component(_realOrig \"${absInstallDir}\" REALPATH) if(_realCurr STREQUAL _realOrig) set(rootDir \"${CMAKE_INSTALL_PREFIX}\") endif() unset(_realOrig) unset(_realCurr)") endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/PhononConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${PHONON_LIB_SONAME_CAMEL}Config.cmake @ONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/PhononConfigVersion.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${PHONON_LIB_SONAME_CAMEL}ConfigVersion.cmake @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PHONON_LIB_SONAME_CAMEL}Config.cmake ${CMAKE_CURRENT_BINARY_DIR}/${PHONON_LIB_SONAME_CAMEL}ConfigVersion.cmake DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(EXPORT PhononLibs DESTINATION ${CMAKECONFIG_INSTALL_DIR} NAMESPACE Phonon:: FILE PhononTargets.cmake ) if(PHONON_BUILD_EXPERIMENTAL) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/PhononExperimentalConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${PHONON_LIB_SONAME_CAMEL}ExperimentalConfig.cmake @ONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/PhononExperimentalConfigVersion.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${PHONON_LIB_SONAME_CAMEL}ExperimentalConfigVersion.cmake @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PHONON_LIB_SONAME_CAMEL}ExperimentalConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/${PHONON_LIB_SONAME_CAMEL}ExperimentalConfigVersion.cmake DESTINATION ${CMAKECONFIG_INSTALL_DIR}) install(EXPORT PhononExperimentalLibs DESTINATION ${CMAKECONFIG_INSTALL_DIR} NAMESPACE Phonon:: FILE PhononExperimentalTargets.cmake ) endif() -if (NOT PHONON_BUILD_PHONON4QT5) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/qt_phonon.pri ${CMAKE_CURRENT_BINARY_DIR}/qt_${PHONON_LIB_SONAME}.pri @ONLY) - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qt_${PHONON_LIB_SONAME}.pri DESTINATION ${PHONON_QT_MKSPECS_INSTALL_DIR}) -else (NOT PHONON_BUILD_PHONON4QT5) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/qt5_phonon.pri ${CMAKE_CURRENT_BINARY_DIR}/qt_${PHONON_LIB_SONAME}.pri @ONLY) - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qt_${PHONON_LIB_SONAME}.pri DESTINATION ${PHONON_QT_MKSPECS_INSTALL_DIR}) -endif (NOT PHONON_BUILD_PHONON4QT5) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/qt_phonon.pri ${CMAKE_CURRENT_BINARY_DIR}/qt_${PHONON_LIB_SONAME}.pri @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qt_${PHONON_LIB_SONAME}.pri DESTINATION ${PHONON_QT_MKSPECS_INSTALL_DIR}) # This generates a nice library descriptor to use with [1]. It also spits out # a script that makes installing various versions for an ABI check a lot easier. # Basically the script ends up in your build dir and by running it you will # get phonon installed to MAIN_SOURCE_DIR/../abi/VERSION/prefix. # You can then invoke the ABI check with something like: # abi-compliance-checker.pl -l phonon -d1 4.4.4/usr/4.4.4.xml -d2 4.4.57/usr/4.4.57.xml # [1] http://ispras.linux-foundation.org/index.php/ABI_compliance_checker if(PHONON_BUILD_DESCRIPTOR) set(DESCRIPTOR_FILE_PATH ${CMAKE_CURRENT_BINARY_DIR}/${PHONON_LIB_VERSION}.xml) set(ABI_SCRIPT_FILE_PATH ${CMAKE_CURRENT_BINARY_DIR}/abi-check-install.sh) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/lib-descriptor.xml.cmake ${DESCRIPTOR_FILE_PATH} @ONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/abi-check-install.sh.cmake ${ABI_SCRIPT_FILE_PATH} @ONLY) install(FILES ${DESCRIPTOR_FILE_PATH} DESTINATION ${CMAKE_INSTALL_PREFIX}) endif(PHONON_BUILD_DESCRIPTOR) if(ECM_FOUND AND IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") ecm_install_po_files_as_qm(po) endif() macro_display_feature_log() diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index cd9c1ddc..eca51c8e 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -1,12 +1,11 @@ install(FILES cmake_uninstall.cmake.in COPYING-CMAKE-SCRIPTS FindPackageHandleStandardArgs.cmake FindPhononInternal.cmake MacroEnsureVersion.cmake MacroLogFeature.cmake MacroOptionalFindPackage.cmake MacroPushRequiredVars.cmake PhononMacros.cmake - PhononQt4.cmake PhononQt5.cmake DESTINATION ${BUILDSYSTEM_INSTALL_DIR}) diff --git a/cmake/FindPhononInternal.cmake b/cmake/FindPhononInternal.cmake index 020d1d46..e10b321a 100644 --- a/cmake/FindPhononInternal.cmake +++ b/cmake/FindPhononInternal.cmake @@ -1,123 +1,119 @@ # Defines marcos: # TODO doc # # Copyright (c) 2008, Matthias Kretz # Copyright (c) 2010, Mark Kretschmann # Copyright (c) 2010-2015, Harald Sitter # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # CMake Defaults get_filename_component(phonon_cmake_module_dir ${CMAKE_CURRENT_LIST_FILE} PATH) # Imported from KDE4Defaults.cmake # Keep this portion copy'n'pastable for updatability. # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # Always include srcdir and builddir in include path # This saves typing ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} in about every subdir # since cmake 2.4.0 set(CMAKE_INCLUDE_CURRENT_DIR ON) # put the include dirs which are in the source or build tree # before all other include dirs, so the headers in the sources # are prefered over the already installed ones # since cmake 2.4.1 set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON) #------------------------------------------------------------------------------- # Include Additional Magic include(${phonon_cmake_module_dir}/PhononMacros.cmake) include(${phonon_cmake_module_dir}/MacroLogFeature.cmake) include(${phonon_cmake_module_dir}/MacroOptionalFindPackage.cmake) include(CheckCXXCompilerFlag) include(${phonon_cmake_module_dir}/MacroEnsureVersion.cmake) # Set Installation Directories - TODO, port to ECM's KDEInstallDirs! include(GNUInstallDirs) set(SHARE_INSTALL_PREFIX "${CMAKE_INSTALL_FULL_DATAROOTDIR}") # CACHE PATH "Base directory for files which go to share/") set(INCLUDE_INSTALL_DIR "include" ) # CACHE PATH "The subdirectory to the header prefix") if (PHONON_BUILD_PHONON4QT5) set(INCLUDE_INSTALL_DIR "include/${PHONON_LIB_SONAME}" ) # CACHE PATH "The subdirectory to the header prefix") endif (PHONON_BUILD_PHONON4QT5) set(BIN_INSTALL_DIR "bin" ) # CACHE PATH "The install dir for executables (default ${EXEC_INSTALL_PREFIX}/bin)") set(LIB_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}" ) # CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed" set(PLUGIN_INSTALL_DIR "${LIB_INSTALL_DIR}/kde4" CACHE PATH "The subdirectory relative to the install prefix where plugins will be installed (default is ${LIB_INSTALL_DIR}/kde4)") if (PHONON_BUILD_PHONON4QT5) set(PLUGIN_INSTALL_DIR "${LIB_INSTALL_DIR}/qt5" CACHE PATH "The subdirectory relative to the install prefix where plugins will be installed (default is ${LIB_INSTALL_DIR}/qt5)" FORCE) endif(PHONON_BUILD_PHONON4QT5) set(BACKEND_INSTALL_DIR "${PLUGIN_INSTALL_DIR}/plugins/${PHONON_LIB_SONAME}_backend") if(WIN32) # Imported from Phonon VLC set(BACKEND_INSTALL_DIR "bin/${PHONON_LIB_SONAME}_backend") endif() set(ICON_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/icons" CACHE PATH "The icon install dir (default ${SHARE_INSTALL_PREFIX}/share/icons/)") set(SERVICES_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/kde4/services" CACHE PATH "The install dir for service (desktop, protocol, ...) files") if (PHONON_BUILD_PHONON4QT5) set(SERVICES_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/kde5/services" CACHE PATH "The install dir for service (desktop, protocol, ...) files") endif(PHONON_BUILD_PHONON4QT5) set(DBUS_INTERFACES_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/dbus-1/interfaces" CACHE PATH "The dbus interfaces install dir (default: ${SHARE_INSTALL_PREFIX}/dbus-1/interfaces)") set(DBUS_SERVICES_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/dbus-1/services" CACHE PATH "The dbus services install dir (default: ${SHARE_INSTALL_PREFIX}/dbus-1/services)") set(INSTALL_TARGETS_DEFAULT_ARGS RUNTIME DESTINATION "${BIN_INSTALL_DIR}" LIBRARY DESTINATION "${LIB_INSTALL_DIR}" ARCHIVE DESTINATION "${LIB_INSTALL_DIR}" COMPONENT Devel) # on the Mac support an extra install directory for application bundles if(APPLE) set(INSTALL_TARGETS_DEFAULT_ARGS ${INSTALL_TARGETS_DEFAULT_ARGS} BUNDLE DESTINATION "${BUNDLE_INSTALL_DIR}") set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "${CMAKE_SHARED_MODULE_CREATE_C_FLAGS} -flat_namespace -undefined dynamic_lookup") set(CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS "${CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS} -flat_namespace -undefined dynamic_lookup") set(CMAKE_INSTALL_NAME_DIR ${LIB_INSTALL_DIR}) endif(APPLE) -if (NOT PHONON_BUILD_PHONON4QT5) # Qt4 - include(${phonon_cmake_module_dir}/PhononQt4.cmake) -else (NOT PHONON_BUILD_PHONON4QT5) # Qt5 - include(${phonon_cmake_module_dir}/PhononQt5.cmake) -endif (NOT PHONON_BUILD_PHONON4QT5) +include(${phonon_cmake_module_dir}/PhononQt5.cmake) # - Automoc (using builtin introduced in 2.8.5) # NOTE: the compatiibility macros are actively used by the backends, so they # cannot be dropped unless the backends get a major release removing all # use of them first. message(STATUS "Using CMake automoc builtin") set(CMAKE_AUTOMOC TRUE) # Compatiblity Macros for old automoc nonesense macro(AUTOMOC4_ADD_EXECUTABLE _target_NAME) add_executable(${_target_NAME} ${ARGN}) endmacro(AUTOMOC4_ADD_EXECUTABLE _target_NAME) macro(AUTOMOC4_ADD_LIBRARY _target_NAME _add_executable_param) add_library(${_target_NAME} ${_add_executable_param} ${ARGN}) endmacro(AUTOMOC4_ADD_LIBRARY) diff --git a/cmake/PhononQt4.cmake b/cmake/PhononQt4.cmake deleted file mode 100644 index 4775ea2f..00000000 --- a/cmake/PhononQt4.cmake +++ /dev/null @@ -1,400 +0,0 @@ -# Copyright (c) 2008, Matthias Kretz -# Copyright (c) 2010, Mark Kretschmann -# Copyright (c) 2010-2015, Harald Sitter -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. The name of the author may not be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -# RPATH Handling - -# Set up RPATH handling, so the libs are found if they are installed to a non-standard location. -# By default cmake builds the targets with full RPATH to everything in the build directory, -# but then removes the RPATH when installing. -# These two options below make it set the RPATH of the installed targets to all -# RPATH directories outside the current CMAKE_BINARY_DIR and also the library -# install directory. Alex -set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) -set(_abs_LIB_INSTALL_DIR "${LIB_INSTALL_DIR}") -if (NOT IS_ABSOLUTE "${_abs_LIB_INSTALL_DIR}") - set(_abs_LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}") -endif() -list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${_abs_LIB_INSTALL_DIR}" _isSystemPlatformLibDir) -list(FIND CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "${_abs_LIB_INSTALL_DIR}" _isSystemCxxLibDir) -if("${_isSystemPlatformLibDir}" STREQUAL "-1" AND "${_isSystemCxxLibDir}" STREQUAL "-1") - set(CMAKE_INSTALL_RPATH "${_abs_LIB_INSTALL_DIR}") -endif("${_isSystemPlatformLibDir}" STREQUAL "-1" AND "${_isSystemCxxLibDir}" STREQUAL "-1") - -# Find Qt4 - -# Store CMAKE_MODULE_PATH and then append the current dir to it, so we are sure -# we get the FindQt4.cmake located next to us and not a different one. -# The original CMAKE_MODULE_PATH is restored later on. -set(_phonon_cmake_module_path_back ${CMAKE_MODULE_PATH}) -set(CMAKE_MODULE_PATH ${phonon_cmake_module_dir} ${CMAKE_MODULE_PATH} ) - -if (NOT QT_MIN_VERSION) - set(QT_MIN_VERSION "4.6.0") -endif (NOT QT_MIN_VERSION) -if (${QT_MIN_VERSION} VERSION_LESS "4.6.0") - set(QT_MIN_VERSION "4.6.0") -endif (${QT_MIN_VERSION} VERSION_LESS "4.6.0") - -find_package(Qt4) -macro_log_feature(QT4_FOUND "Qt4" "" "" TRUE) - -# ----- compat -macro (qt5_use_modules target) - set(_deps "") - foreach (arg ${ARGN}) - if (arg STREQUAL "Core") - list(APPEND _deps ${QT_QTCORE_LIBRARY}) - elseif (arg STREQUAL "Gui") - list(APPEND _deps ${QT_QTGUI_LIBRARY}) - elseif (arg STREQUAL "Widgets") - list(APPEND _deps ${QT_QTGUI_LIBRARY}) - elseif (arg STREQUAL "DBus") - list(APPEND _deps ${QT_QTDBUS_LIBRARY}) - elseif (arg STREQUAL "OpenGL") - list(APPEND _deps ${QT_QTOPENGL_LIBRARY}) - elseif (arg STREQUAL "Declarative") - list(APPEND _deps ${QT_QTDECLARATIVE_LIBRARY}) - elseif (arg STREQUAL "Designer") - list(APPEND _deps ${QT_QTDESIGNER_LIBRARY}) - else () - message("qt5_use_modules could not map ${arg} to Qt 4") - endif () - endforeach () - target_link_libraries(${target} ${_deps}) -endmacro (qt5_use_modules target args) - -macro (qt5_add_resources) - qt4_add_resources(${ARGN}) -endmacro (qt5_add_resources) - -macro (qt5_wrap_ui) - qt4_wrap_ui(${ARGN}) -endmacro () - - -# restore the original CMAKE_MODULE_PATH -set(CMAKE_MODULE_PATH ${_phonon_cmake_module_path_back}) - -# Imported from FindKDE4Internal.cmake -# Keep this portion copy'n'pastable for updatability. -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -if (CMAKE_SYSTEM_NAME MATCHES Linux OR CMAKE_SYSTEM_NAME STREQUAL GNU) - if (CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - set ( _KDE4_PLATFORM_DEFINITIONS -D_XOPEN_SOURCE=500 -D_BSD_SOURCE -D_GNU_SOURCE) - set ( CMAKE_SHARED_LINKER_FLAGS "-Wl,--fatal-warnings -Wl,--no-undefined -lc ${CMAKE_SHARED_LINKER_FLAGS}") - set ( CMAKE_MODULE_LINKER_FLAGS "-Wl,--fatal-warnings -Wl,--no-undefined -lc ${CMAKE_MODULE_LINKER_FLAGS}") - - set ( CMAKE_SHARED_LINKER_FLAGS "-Wl,--enable-new-dtags ${CMAKE_SHARED_LINKER_FLAGS}") - set ( CMAKE_MODULE_LINKER_FLAGS "-Wl,--enable-new-dtags ${CMAKE_MODULE_LINKER_FLAGS}") - set ( CMAKE_EXE_LINKER_FLAGS "-Wl,--enable-new-dtags ${CMAKE_EXE_LINKER_FLAGS}") - - # we profile... - if(CMAKE_BUILD_TYPE_TOLOWER MATCHES profile) - set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fprofile-arcs -ftest-coverage") - set (CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage") - endif(CMAKE_BUILD_TYPE_TOLOWER MATCHES profile) - endif (CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - if (CMAKE_C_COMPILER MATCHES "icc") - set ( _KDE4_PLATFORM_DEFINITIONS -D_XOPEN_SOURCE=500 -D_BSD_SOURCE -D_GNU_SOURCE) - set ( CMAKE_SHARED_LINKER_FLAGS "-Wl,--fatal-warnings -Wl,--no-undefined -lc ${CMAKE_SHARED_LINKER_FLAGS}") - set ( CMAKE_MODULE_LINKER_FLAGS "-Wl,--fatal-warnings -Wl,--no-undefined -lc ${CMAKE_MODULE_LINKER_FLAGS}") - endif (CMAKE_C_COMPILER MATCHES "icc") -endif (CMAKE_SYSTEM_NAME MATCHES Linux OR CMAKE_SYSTEM_NAME STREQUAL GNU) - -#------------------------------------------------------------------------------- - - -# Imported from FindKDE4Internal.cmake -# Keep this portion copy'n'pastable for updatability. -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -# this macro is for internal use only. -macro(KDE_CHECK_FLAG_EXISTS FLAG VAR DOC) - if(NOT ${VAR} MATCHES "${FLAG}") - set(${VAR} "${${VAR}} ${FLAG}" CACHE STRING "Flags used by the linker during ${DOC} builds." FORCE) - endif(NOT ${VAR} MATCHES "${FLAG}") -endmacro(KDE_CHECK_FLAG_EXISTS FLAG VAR) - -if (MSVC) - set (KDE4_ENABLE_EXCEPTIONS -EHsc) - - # Qt disables the native wchar_t type, do it too to avoid linking issues - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Zc:wchar_t-" ) - - # make sure that no header adds libcmt by default using #pragma comment(lib, "libcmt.lib") as done by mfc/afx.h - kde_check_flag_exists("/NODEFAULTLIB:libcmt /DEFAULTLIB:msvcrt" CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "Release with Debug Info") - kde_check_flag_exists("/NODEFAULTLIB:libcmt /DEFAULTLIB:msvcrt" CMAKE_EXE_LINKER_FLAGS_RELEASE "release") - kde_check_flag_exists("/NODEFAULTLIB:libcmt /DEFAULTLIB:msvcrt" CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "release minsize") - kde_check_flag_exists("/NODEFAULTLIB:libcmtd /DEFAULTLIB:msvcrtd" CMAKE_EXE_LINKER_FLAGS_DEBUG "debug") -endif(MSVC) - -# This macro is for internal use only -# Return the directories present in gcc's include path. -macro(_DETERMINE_GCC_SYSTEM_INCLUDE_DIRS _lang _result) - set(${_result}) - set(_gccOutput) - file(WRITE "${CMAKE_BINARY_DIR}/CMakeFiles/dummy" "\n" ) - execute_process(COMMAND ${CMAKE_C_COMPILER} -v -E -x ${_lang} -dD dummy - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/CMakeFiles - ERROR_VARIABLE _gccOutput - OUTPUT_VARIABLE _gccStdout ) - file(REMOVE "${CMAKE_BINARY_DIR}/CMakeFiles/dummy") - - if( "${_gccOutput}" MATCHES "> search starts here[^\n]+\n *(.+) *\n *End of (search) list" ) - SET(${_result} ${CMAKE_MATCH_1}) - STRING(REPLACE "\n" " " ${_result} "${${_result}}") - SEPARATE_ARGUMENTS(${_result}) - ENDIF( "${_gccOutput}" MATCHES "> search starts here[^\n]+\n *(.+) *\n *End of (search) list" ) -ENDMACRO(_DETERMINE_GCC_SYSTEM_INCLUDE_DIRS _lang) - -if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES Clang) - _DETERMINE_GCC_SYSTEM_INCLUDE_DIRS(c _dirs) - set(CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES - ${CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES} ${_dirs}) -endif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES Clang) - -if (CMAKE_COMPILER_IS_GNUCXX) - _DETERMINE_GCC_SYSTEM_INCLUDE_DIRS(c++ _dirs) - set(CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES - ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES} ${_dirs}) - - set (KDE4_ENABLE_EXCEPTIONS "-fexceptions -UQT_NO_EXCEPTIONS") - # Select flags. - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -DNDEBUG -DQT_NO_DEBUG") - set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG -DQT_NO_DEBUG") - set(CMAKE_CXX_FLAGS_DEBUG "-g -O2 -fno-reorder-blocks -fno-schedule-insns -fno-inline") - set(CMAKE_CXX_FLAGS_DEBUGFULL "-g3 -fno-inline") - set(CMAKE_CXX_FLAGS_PROFILE "-g3 -fno-inline -ftest-coverage -fprofile-arcs") - set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g -DNDEBUG -DQT_NO_DEBUG") - set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG -DQT_NO_DEBUG") - set(CMAKE_C_FLAGS_DEBUG "-g -O2 -fno-reorder-blocks -fno-schedule-insns -fno-inline") - set(CMAKE_C_FLAGS_DEBUGFULL "-g3 -fno-inline") - set(CMAKE_C_FLAGS_PROFILE "-g3 -fno-inline -ftest-coverage -fprofile-arcs") - - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-long-long -std=iso9899:1990 -Wundef -Wcast-align -Werror-implicit-function-declaration -Wchar-subscripts -Wall -W -Wpointer-arith -Wwrite-strings -Wformat-security -Wmissing-format-attribute -fno-common") - # As of Qt 4.6.x we need to override the new exception macros if we want compile with -fno-exceptions - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnon-virtual-dtor -Wno-long-long -Wundef -Wcast-align -Wchar-subscripts -Wall -W -Wpointer-arith -Wformat-security -fno-exceptions -DQT_NO_EXCEPTIONS -fno-check-new -fno-common") - - if (CMAKE_SYSTEM_NAME MATCHES Linux OR CMAKE_SYSTEM_NAME STREQUAL GNU) - # This should not be needed, as it is also part of _KDE4_PLATFORM_DEFINITIONS below. - # It is kept here nonetheless both for backwards compatibility in case one does not use add_definitions(${KDE4_DEFINITIONS}) - # and also because it is/was needed by glibc for snprintf to be available when building C files. - # See commit 4a44862b2d178c1d2e1eb4da90010d19a1e4a42c. - add_definitions (-D_BSD_SOURCE) - endif (CMAKE_SYSTEM_NAME MATCHES Linux OR CMAKE_SYSTEM_NAME STREQUAL GNU) - - if (CMAKE_SYSTEM_NAME STREQUAL GNU) - set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -pthread") - set (CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -pthread") - endif (CMAKE_SYSTEM_NAME STREQUAL GNU) - - # gcc under Windows - if (MINGW) - set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--export-all-symbols") - set (CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--export-all-symbols") - endif (MINGW) - - check_cxx_compiler_flag(-fPIE HAVE_FPIE_SUPPORT) - if(KDE4_ENABLE_FPIE) - if(HAVE_FPIE_SUPPORT) - set (KDE4_CXX_FPIE_FLAGS "-fPIE") - set (KDE4_PIE_LDFLAGS "-pie") - else(HAVE_FPIE_SUPPORT) - message(STATUS "Your compiler doesn't support the PIE flag") - endif(HAVE_FPIE_SUPPORT) - endif(KDE4_ENABLE_FPIE) - - check_cxx_compiler_flag(-Woverloaded-virtual __KDE_HAVE_W_OVERLOADED_VIRTUAL) - if(__KDE_HAVE_W_OVERLOADED_VIRTUAL) - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Woverloaded-virtual") - endif(__KDE_HAVE_W_OVERLOADED_VIRTUAL) - - # visibility support - check_cxx_compiler_flag(-fvisibility=hidden __KDE_HAVE_GCC_VISIBILITY) - set( __KDE_HAVE_GCC_VISIBILITY ${__KDE_HAVE_GCC_VISIBILITY} CACHE BOOL "GCC support for hidden visibility") - - # get the gcc version - exec_program(${CMAKE_C_COMPILER} ARGS ${CMAKE_C_COMPILER_ARG1} --version OUTPUT_VARIABLE _gcc_version_info) - - string (REGEX MATCH "[345]\\.[0-9]\\.[0-9]" _gcc_version "${_gcc_version_info}") - # gcc on mac just reports: "gcc (GCC) 3.3 20030304 ..." without the patch level, handle this here: - if (NOT _gcc_version) - string (REGEX MATCH ".*\\(GCC\\).* ([34]\\.[0-9]) .*" "\\1.0" _gcc_version "${gcc_on_macos}") - if (gcc_on_macos) - string (REGEX REPLACE ".*\\(GCC\\).* ([34]\\.[0-9]) .*" "\\1.0" _gcc_version "${_gcc_version_info}") - endif (gcc_on_macos) - endif (NOT _gcc_version) - - if (_gcc_version) - macro_ensure_version("4.1.0" "${_gcc_version}" GCC_IS_NEWER_THAN_4_1) - macro_ensure_version("4.2.0" "${_gcc_version}" GCC_IS_NEWER_THAN_4_2) - macro_ensure_version("4.3.0" "${_gcc_version}" GCC_IS_NEWER_THAN_4_3) - endif (_gcc_version) - - set(_GCC_COMPILED_WITH_BAD_ALLOCATOR FALSE) - if (GCC_IS_NEWER_THAN_4_1) - exec_program(${CMAKE_C_COMPILER} ARGS ${CMAKE_C_COMPILER_ARG1} -v OUTPUT_VARIABLE _gcc_alloc_info) - string(REGEX MATCH "(--enable-libstdcxx-allocator=mt)" _GCC_COMPILED_WITH_BAD_ALLOCATOR "${_gcc_alloc_info}") - endif (GCC_IS_NEWER_THAN_4_1) - - if (__KDE_HAVE_GCC_VISIBILITY AND GCC_IS_NEWER_THAN_4_1 AND NOT _GCC_COMPILED_WITH_BAD_ALLOCATOR AND NOT WIN32) - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") - set (KDE4_C_FLAGS "-fvisibility=hidden") - # check that Qt defines Q_DECL_EXPORT as __attribute__ ((visibility("default"))) - # if it doesn't and KDE compiles with hidden default visibiltiy plugins will break - set(_source "#include \n int main()\n {\n #ifndef QT_VISIBILITY_AVAILABLE \n #error QT_VISIBILITY_AVAILABLE is not available\n #endif \n }\n") - set(_source_file ${CMAKE_BINARY_DIR}/CMakeTmp/check_qt_visibility.cpp) - file(WRITE "${_source_file}" "${_source}") - set(_include_dirs "-DINCLUDE_DIRECTORIES:STRING=${QT_INCLUDES}") - - try_compile(_compile_result ${CMAKE_BINARY_DIR} ${_source_file} CMAKE_FLAGS "${_include_dirs}" OUTPUT_VARIABLE _compile_output_var) - - if(NOT _compile_result) - message("${_compile_output_var}") - message(FATAL_ERROR "Qt compiled without support for -fvisibility=hidden. This will break plugins and linking of some applications. Please fix your Qt installation (try passing --reduce-exports to configure).") - endif(NOT _compile_result) - - if (GCC_IS_NEWER_THAN_4_2) - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=return-type -fvisibility-inlines-hidden") - endif (GCC_IS_NEWER_THAN_4_2) - else (__KDE_HAVE_GCC_VISIBILITY AND GCC_IS_NEWER_THAN_4_1 AND NOT _GCC_COMPILED_WITH_BAD_ALLOCATOR AND NOT WIN32) - set (__KDE_HAVE_GCC_VISIBILITY 0) - endif (__KDE_HAVE_GCC_VISIBILITY AND GCC_IS_NEWER_THAN_4_1 AND NOT _GCC_COMPILED_WITH_BAD_ALLOCATOR AND NOT WIN32) - -endif (CMAKE_COMPILER_IS_GNUCXX) - - -if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - _DETERMINE_GCC_SYSTEM_INCLUDE_DIRS(c++ _dirs) - set(CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES - ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES} ${_dirs}) - - # Note that exceptions are enabled by default when building with clang. That - # is, -fno-exceptions is not set in CMAKE_CXX_FLAGS below. This is because a - # lot of code in different KDE modules ends up including code that throws - # exceptions. Most (or all) of the occurrences are in template code that - # never gets instantiated. Contrary to GCC, ICC and MSVC, clang (most likely - # rightfully) complains about that. Trying to work around the issue by - # passing -fdelayed-template-parsing brings other problems, as noted in - # http://lists.kde.org/?l=kde-core-devel&m=138157459706783&w=2. - # The generated code will be slightly bigger, but there is no way to avoid - # it. - set(KDE4_ENABLE_EXCEPTIONS "-fexceptions -UQT_NO_EXCEPTIONS") - - # Select flags. - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -DNDEBUG -DQT_NO_DEBUG") - set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG -DQT_NO_DEBUG") - set(CMAKE_CXX_FLAGS_DEBUG "-g -O2 -fno-inline") - set(CMAKE_CXX_FLAGS_DEBUGFULL "-g3 -fno-inline") - set(CMAKE_CXX_FLAGS_PROFILE "-g3 -fno-inline -ftest-coverage -fprofile-arcs") - set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g -DNDEBUG -DQT_NO_DEBUG") - set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG -DQT_NO_DEBUG") - set(CMAKE_C_FLAGS_DEBUG "-g -O2 -fno-inline") - set(CMAKE_C_FLAGS_DEBUGFULL "-g3 -fno-inline") - set(CMAKE_C_FLAGS_PROFILE "-g3 -fno-inline -ftest-coverage -fprofile-arcs") - - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-long-long -std=iso9899:1990 -Wundef -Wcast-align -Werror-implicit-function-declaration -Wchar-subscripts -Wall -W -Wpointer-arith -Wwrite-strings -Wformat-security -Wmissing-format-attribute -fno-common") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnon-virtual-dtor -Wno-long-long -Wundef -Wcast-align -Wchar-subscripts -Wall -W -Wpointer-arith -Wformat-security -Woverloaded-virtual -fno-common -fvisibility=hidden -Werror=return-type -fvisibility-inlines-hidden") - set(KDE4_C_FLAGS "-fvisibility=hidden") - - # At least kdepim exports one function with C linkage that returns a - # QString in a plugin, but clang does not like that. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-return-type-c-linkage") - - set(KDE4_CXX_FPIE_FLAGS "-fPIE") - set(KDE4_PIE_LDFLAGS "-pie") - - if (CMAKE_SYSTEM_NAME STREQUAL GNU) - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -pthread") - set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -pthread") - endif (CMAKE_SYSTEM_NAME STREQUAL GNU) - - set(__KDE_HAVE_GCC_VISIBILITY TRUE) - - # check that Qt defines Q_DECL_EXPORT as __attribute__ ((visibility("default"))) - # if it doesn't and KDE compiles with hidden default visibiltiy plugins will break - set(_source "#include \n int main()\n {\n #ifndef QT_VISIBILITY_AVAILABLE \n #error QT_VISIBILITY_AVAILABLE is not available\n #endif \n }\n") - set(_source_file ${CMAKE_BINARY_DIR}/CMakeTmp/check_qt_visibility.cpp) - file(WRITE "${_source_file}" "${_source}") - set(_include_dirs "-DINCLUDE_DIRECTORIES:STRING=${QT_INCLUDES}") - try_compile(_compile_result ${CMAKE_BINARY_DIR} ${_source_file} CMAKE_FLAGS "${_include_dirs}" OUTPUT_VARIABLE _compile_output_var) - if(NOT _compile_result) - message("${_compile_output_var}") - message(FATAL_ERROR "Qt compiled without support for -fvisibility=hidden. This will break plugins and linking of some applications. Please fix your Qt installation (try passing --reduce-exports to configure).") - endif(NOT _compile_result) -endif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - - -if (CMAKE_C_COMPILER MATCHES "icc") - - set (KDE4_ENABLE_EXCEPTIONS -fexceptions) - # Select flags. - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") - set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG -DQT_NO_DEBUG") - set(CMAKE_CXX_FLAGS_DEBUG "-O2 -g -fno-inline -noalign") - set(CMAKE_CXX_FLAGS_DEBUGFULL "-g -fno-inline -noalign") - set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g") - set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG -DQT_NO_DEBUG") - set(CMAKE_C_FLAGS_DEBUG "-O2 -g -fno-inline -noalign") - set(CMAKE_C_FLAGS_DEBUGFULL "-g -fno-inline -noalign") - - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ansi -Wall -w1 -Wpointer-arith -fno-common") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ansi -Wall -w1 -Wpointer-arith -fno-exceptions -fno-common") - - # visibility support - set(__KDE_HAVE_ICC_VISIBILITY) -# check_cxx_compiler_flag(-fvisibility=hidden __KDE_HAVE_ICC_VISIBILITY) -# if (__KDE_HAVE_ICC_VISIBILITY) -# set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") -# endif (__KDE_HAVE_ICC_VISIBILITY) - -endif (CMAKE_C_COMPILER MATCHES "icc") - -#------------------------------------------------------------------------------- - - -# Random Stuff - -if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER MATCHES "icc") - set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnon-virtual-dtor -Wno-long-long -ansi -Wundef -Wcast-align -Wchar-subscripts -Wall -W -Wpointer-arith -Wformat-security -fno-check-new -fno-common") -endif (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER MATCHES "icc") - -# For Windows -if(MSVC) - if(CMAKE_COMPILER_2005) - # to avoid a lot of deprecated warnings - add_definitions( -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS ) - endif(CMAKE_COMPILER_2005) -endif(MSVC) - -# Uninstall Target -if (NOT _phonon_uninstall_target_created) - set(_phonon_uninstall_target_created TRUE) - configure_file("${phonon_cmake_module_dir}/cmake_uninstall.cmake.in" "${CMAKE_BINARY_DIR}/cmake_uninstall.cmake" @ONLY) - add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_BINARY_DIR}/cmake_uninstall.cmake") -endif (NOT _phonon_uninstall_target_created) diff --git a/cmake/Qt4Macros.cmake b/cmake/Qt4Macros.cmake deleted file mode 100644 index 1422c592..00000000 --- a/cmake/Qt4Macros.cmake +++ /dev/null @@ -1,414 +0,0 @@ -# This file is included by FindQt4.cmake, don't include it directly. - -#============================================================================= -# Copyright 2005-2009 Kitware, Inc. -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distributed this file outside of CMake, substitute the full -# License text for the above reference.) - - -###################################### -# -# Macros for building Qt files -# -###################################### - - -MACRO (QT4_EXTRACT_OPTIONS _qt4_files _qt4_options) - SET(${_qt4_files}) - SET(${_qt4_options}) - SET(_QT4_DOING_OPTIONS FALSE) - FOREACH(_currentArg ${ARGN}) - IF ("${_currentArg}" STREQUAL "OPTIONS") - SET(_QT4_DOING_OPTIONS TRUE) - ELSE ("${_currentArg}" STREQUAL "OPTIONS") - IF(_QT4_DOING_OPTIONS) - LIST(APPEND ${_qt4_options} "${_currentArg}") - ELSE(_QT4_DOING_OPTIONS) - LIST(APPEND ${_qt4_files} "${_currentArg}") - ENDIF(_QT4_DOING_OPTIONS) - ENDIF ("${_currentArg}" STREQUAL "OPTIONS") - ENDFOREACH(_currentArg) -ENDMACRO (QT4_EXTRACT_OPTIONS) - - -# macro used to create the names of output files preserving relative dirs -MACRO (QT4_MAKE_OUTPUT_FILE infile prefix ext outfile ) - STRING(LENGTH ${CMAKE_CURRENT_BINARY_DIR} _binlength) - STRING(LENGTH ${infile} _infileLength) - SET(_checkinfile ${CMAKE_CURRENT_SOURCE_DIR}) - IF(_infileLength GREATER _binlength) - STRING(SUBSTRING "${infile}" 0 ${_binlength} _checkinfile) - IF(_checkinfile STREQUAL "${CMAKE_CURRENT_BINARY_DIR}") - FILE(RELATIVE_PATH rel ${CMAKE_CURRENT_BINARY_DIR} ${infile}) - ELSE(_checkinfile STREQUAL "${CMAKE_CURRENT_BINARY_DIR}") - FILE(RELATIVE_PATH rel ${CMAKE_CURRENT_SOURCE_DIR} ${infile}) - ENDIF(_checkinfile STREQUAL "${CMAKE_CURRENT_BINARY_DIR}") - ELSE(_infileLength GREATER _binlength) - FILE(RELATIVE_PATH rel ${CMAKE_CURRENT_SOURCE_DIR} ${infile}) - ENDIF(_infileLength GREATER _binlength) - IF(WIN32 AND rel MATCHES "^[a-zA-Z]:") # absolute path - STRING(REGEX REPLACE "^([a-zA-Z]):(.*)$" "\\1_\\2" rel "${rel}") - ENDIF(WIN32 AND rel MATCHES "^[a-zA-Z]:") - SET(_outfile "${CMAKE_CURRENT_BINARY_DIR}/${rel}") - STRING(REPLACE ".." "__" _outfile ${_outfile}) - GET_FILENAME_COMPONENT(outpath ${_outfile} PATH) - GET_FILENAME_COMPONENT(_outfile ${_outfile} NAME_WE) - FILE(MAKE_DIRECTORY ${outpath}) - SET(${outfile} ${outpath}/${prefix}${_outfile}.${ext}) -ENDMACRO (QT4_MAKE_OUTPUT_FILE ) - - -MACRO (QT4_GET_MOC_FLAGS _moc_flags) - SET(${_moc_flags}) - GET_DIRECTORY_PROPERTY(_inc_DIRS INCLUDE_DIRECTORIES) - - FOREACH(_current ${_inc_DIRS}) - IF("${_current}" MATCHES ".framework/?$") - STRING(REGEX REPLACE "/[^/]+.framework" "" framework_path "${_current}") - SET(${_moc_flags} ${${_moc_flags}} "-F${framework_path}") - ELSE("${_current}" MATCHES ".framework/?$") - SET(${_moc_flags} ${${_moc_flags}} "-I${_current}") - ENDIF("${_current}" MATCHES ".framework/?$") - ENDFOREACH(_current ${_inc_DIRS}) - - GET_DIRECTORY_PROPERTY(_defines COMPILE_DEFINITIONS) - FOREACH(_current ${_defines}) - SET(${_moc_flags} ${${_moc_flags}} "-D${_current}") - ENDFOREACH(_current ${_defines}) - - IF(Q_WS_WIN) - SET(${_moc_flags} ${${_moc_flags}} -DWIN32) - ENDIF(Q_WS_WIN) - -ENDMACRO(QT4_GET_MOC_FLAGS) - - -# helper macro to set up a moc rule -MACRO (QT4_CREATE_MOC_COMMAND infile outfile moc_flags moc_options) - # For Windows, create a parameters file to work around command line length limit - IF (WIN32) - # Pass the parameters in a file. Set the working directory to - # be that containing the parameters file and reference it by - # just the file name. This is necessary because the moc tool on - # MinGW builds does not seem to handle spaces in the path to the - # file given with the @ syntax. - GET_FILENAME_COMPONENT(_moc_outfile_name "${outfile}" NAME) - GET_FILENAME_COMPONENT(_moc_outfile_dir "${outfile}" PATH) - IF(_moc_outfile_dir) - SET(_moc_working_dir WORKING_DIRECTORY ${_moc_outfile_dir}) - ENDIF(_moc_outfile_dir) - SET (_moc_parameters_file ${outfile}_parameters) - SET (_moc_parameters ${moc_flags} ${moc_options} -o "${outfile}" "${infile}") - FILE (REMOVE ${_moc_parameters_file}) - FOREACH(arg ${_moc_parameters}) - FILE (APPEND ${_moc_parameters_file} "${arg}\n") - ENDFOREACH(arg) - ADD_CUSTOM_COMMAND(OUTPUT ${outfile} - COMMAND ${QT_MOC_EXECUTABLE} @${_moc_outfile_name}_parameters - DEPENDS ${infile} - ${_moc_working_dir} - VERBATIM) - ELSE (WIN32) - ADD_CUSTOM_COMMAND(OUTPUT ${outfile} - COMMAND ${QT_MOC_EXECUTABLE} - ARGS ${moc_flags} ${moc_options} -o ${outfile} ${infile} - DEPENDS ${infile}) - ENDIF (WIN32) -ENDMACRO (QT4_CREATE_MOC_COMMAND) - - -MACRO (QT4_GENERATE_MOC infile outfile ) -# get include dirs and flags - QT4_GET_MOC_FLAGS(moc_flags) - GET_FILENAME_COMPONENT(abs_infile ${infile} ABSOLUTE) - QT4_CREATE_MOC_COMMAND(${abs_infile} ${outfile} "${moc_flags}" "") - SET_SOURCE_FILES_PROPERTIES(${outfile} PROPERTIES SKIP_AUTOMOC TRUE) # dont run automoc on this file - - MACRO_ADD_FILE_DEPENDENCIES(${abs_infile} ${outfile}) -ENDMACRO (QT4_GENERATE_MOC) - - -# QT4_WRAP_CPP(outfiles inputfile ... ) - -MACRO (QT4_WRAP_CPP outfiles ) - # get include dirs - QT4_GET_MOC_FLAGS(moc_flags) - QT4_EXTRACT_OPTIONS(moc_files moc_options ${ARGN}) - - FOREACH (it ${moc_files}) - GET_FILENAME_COMPONENT(it ${it} ABSOLUTE) - QT4_MAKE_OUTPUT_FILE(${it} moc_ cxx outfile) - QT4_CREATE_MOC_COMMAND(${it} ${outfile} "${moc_flags}" "${moc_options}") - SET(${outfiles} ${${outfiles}} ${outfile}) - ENDFOREACH(it) - -ENDMACRO (QT4_WRAP_CPP) - - -# QT4_WRAP_UI(outfiles inputfile ... ) - -MACRO (QT4_WRAP_UI outfiles ) - QT4_EXTRACT_OPTIONS(ui_files ui_options ${ARGN}) - - FOREACH (it ${ui_files}) - GET_FILENAME_COMPONENT(outfile ${it} NAME_WE) - GET_FILENAME_COMPONENT(infile ${it} ABSOLUTE) - SET(outfile ${CMAKE_CURRENT_BINARY_DIR}/ui_${outfile}.h) - ADD_CUSTOM_COMMAND(OUTPUT ${outfile} - COMMAND ${QT_UIC_EXECUTABLE} - ARGS ${ui_options} -o ${outfile} ${infile} - MAIN_DEPENDENCY ${infile}) - SET(${outfiles} ${${outfiles}} ${outfile}) - ENDFOREACH (it) - -ENDMACRO (QT4_WRAP_UI) - - -# QT4_ADD_RESOURCES(outfiles inputfile ... ) - -MACRO (QT4_ADD_RESOURCES outfiles ) - QT4_EXTRACT_OPTIONS(rcc_files rcc_options ${ARGN}) - - FOREACH (it ${rcc_files}) - GET_FILENAME_COMPONENT(outfilename ${it} NAME_WE) - GET_FILENAME_COMPONENT(infile ${it} ABSOLUTE) - GET_FILENAME_COMPONENT(rc_path ${infile} PATH) - SET(outfile ${CMAKE_CURRENT_BINARY_DIR}/qrc_${outfilename}.cxx) - # parse file for dependencies - # all files are absolute paths or relative to the location of the qrc file - FILE(READ "${infile}" _RC_FILE_CONTENTS) - STRING(REGEX MATCHALL "]*>" "" _RC_FILE "${_RC_FILE}") - STRING(REGEX MATCH "^/|([A-Za-z]:/)" _ABS_PATH_INDICATOR "${_RC_FILE}") - IF(NOT _ABS_PATH_INDICATOR) - SET(_RC_FILE "${rc_path}/${_RC_FILE}") - ENDIF(NOT _ABS_PATH_INDICATOR) - SET(_RC_DEPENDS ${_RC_DEPENDS} "${_RC_FILE}") - ENDFOREACH(_RC_FILE) - ADD_CUSTOM_COMMAND(OUTPUT ${outfile} - COMMAND ${QT_RCC_EXECUTABLE} - ARGS ${rcc_options} -name ${outfilename} -o ${outfile} ${infile} - MAIN_DEPENDENCY ${infile} - DEPENDS ${_RC_DEPENDS}) - SET(${outfiles} ${${outfiles}} ${outfile}) - ENDFOREACH (it) - -ENDMACRO (QT4_ADD_RESOURCES) - - -MACRO(QT4_ADD_DBUS_INTERFACE _sources _interface _basename) - GET_FILENAME_COMPONENT(_infile ${_interface} ABSOLUTE) - SET(_header ${CMAKE_CURRENT_BINARY_DIR}/${_basename}.h) - SET(_impl ${CMAKE_CURRENT_BINARY_DIR}/${_basename}.cpp) - SET(_moc ${CMAKE_CURRENT_BINARY_DIR}/${_basename}.moc) - - GET_SOURCE_FILE_PROPERTY(_nonamespace ${_interface} NO_NAMESPACE) - IF ( _nonamespace ) - SET(_params -N -m) - ELSE ( _nonamespace ) - SET(_params -m) - ENDIF ( _nonamespace ) - - GET_SOURCE_FILE_PROPERTY(_classname ${_interface} CLASSNAME) - IF ( _classname ) - SET(_params ${_params} -c ${_classname}) - ENDIF ( _classname ) - - GET_SOURCE_FILE_PROPERTY(_include ${_interface} INCLUDE) - IF ( _include ) - SET(_params ${_params} -i ${_include}) - ENDIF ( _include ) - - ADD_CUSTOM_COMMAND(OUTPUT ${_impl} ${_header} - COMMAND ${QT_DBUSXML2CPP_EXECUTABLE} ${_params} -p ${_basename} ${_infile} - DEPENDS ${_infile}) - - SET_SOURCE_FILES_PROPERTIES(${_impl} PROPERTIES SKIP_AUTOMOC TRUE) - - QT4_GENERATE_MOC(${_header} ${_moc}) - - SET(${_sources} ${${_sources}} ${_impl} ${_header} ${_moc}) - MACRO_ADD_FILE_DEPENDENCIES(${_impl} ${_moc}) - -ENDMACRO(QT4_ADD_DBUS_INTERFACE) - - -MACRO(QT4_ADD_DBUS_INTERFACES _sources) - FOREACH (_current_FILE ${ARGN}) - GET_FILENAME_COMPONENT(_infile ${_current_FILE} ABSOLUTE) - # get the part before the ".xml" suffix - STRING(REGEX REPLACE "(.*[/\\.])?([^\\.]+)\\.xml" "\\2" _basename ${_current_FILE}) - STRING(TOLOWER ${_basename} _basename) - QT4_ADD_DBUS_INTERFACE(${_sources} ${_infile} ${_basename}interface) - ENDFOREACH (_current_FILE) -ENDMACRO(QT4_ADD_DBUS_INTERFACES) - - -MACRO(QT4_GENERATE_DBUS_INTERFACE _header) # _customName OPTIONS -some -options ) - QT4_EXTRACT_OPTIONS(_customName _qt4_dbus_options ${ARGN}) - - GET_FILENAME_COMPONENT(_in_file ${_header} ABSOLUTE) - GET_FILENAME_COMPONENT(_basename ${_header} NAME_WE) - - IF (_customName) - SET(_target ${CMAKE_CURRENT_BINARY_DIR}/${_customName}) - ELSE (_customName) - SET(_target ${CMAKE_CURRENT_BINARY_DIR}/${_basename}.xml) - ENDIF (_customName) - - ADD_CUSTOM_COMMAND(OUTPUT ${_target} - COMMAND ${QT_DBUSCPP2XML_EXECUTABLE} ${_qt4_dbus_options} ${_in_file} -o ${_target} - DEPENDS ${_in_file} - ) -ENDMACRO(QT4_GENERATE_DBUS_INTERFACE) - - -MACRO(QT4_ADD_DBUS_ADAPTOR _sources _xml_file _include _parentClass) # _optionalBasename _optionalClassName) - GET_FILENAME_COMPONENT(_infile ${_xml_file} ABSOLUTE) - - SET(_optionalBasename "${ARGV4}") - IF (_optionalBasename) - SET(_basename ${_optionalBasename} ) - ELSE (_optionalBasename) - STRING(REGEX REPLACE "(.*[/\\.])?([^\\.]+)\\.xml" "\\2adaptor" _basename ${_infile}) - STRING(TOLOWER ${_basename} _basename) - ENDIF (_optionalBasename) - - SET(_optionalClassName "${ARGV5}") - SET(_header ${CMAKE_CURRENT_BINARY_DIR}/${_basename}.h) - SET(_impl ${CMAKE_CURRENT_BINARY_DIR}/${_basename}.cpp) - SET(_moc ${CMAKE_CURRENT_BINARY_DIR}/${_basename}.moc) - - IF(_optionalClassName) - ADD_CUSTOM_COMMAND(OUTPUT ${_impl} ${_header} - COMMAND ${QT_DBUSXML2CPP_EXECUTABLE} -m -a ${_basename} -c ${_optionalClassName} -i ${_include} -l ${_parentClass} ${_infile} - DEPENDS ${_infile} - ) - ELSE(_optionalClassName) - ADD_CUSTOM_COMMAND(OUTPUT ${_impl} ${_header} - COMMAND ${QT_DBUSXML2CPP_EXECUTABLE} -m -a ${_basename} -i ${_include} -l ${_parentClass} ${_infile} - DEPENDS ${_infile} - ) - ENDIF(_optionalClassName) - - QT4_GENERATE_MOC(${_header} ${_moc}) - SET_SOURCE_FILES_PROPERTIES(${_impl} PROPERTIES SKIP_AUTOMOC TRUE) - MACRO_ADD_FILE_DEPENDENCIES(${_impl} ${_moc}) - - SET(${_sources} ${${_sources}} ${_impl} ${_header} ${_moc}) -ENDMACRO(QT4_ADD_DBUS_ADAPTOR) - - -MACRO(QT4_AUTOMOC) - QT4_GET_MOC_FLAGS(_moc_INCS) - - SET(_matching_FILES ) - FOREACH (_current_FILE ${ARGN}) - - GET_FILENAME_COMPONENT(_abs_FILE ${_current_FILE} ABSOLUTE) - # if "SKIP_AUTOMOC" is set to true, we will not handle this file here. - # This is required to make uic work correctly: - # we need to add generated .cpp files to the sources (to compile them), - # but we cannot let automoc handle them, as the .cpp files don't exist yet when - # cmake is run for the very first time on them -> however the .cpp files might - # exist at a later run. at that time we need to skip them, so that we don't add two - # different rules for the same moc file - GET_SOURCE_FILE_PROPERTY(_skip ${_abs_FILE} SKIP_AUTOMOC) - - IF ( NOT _skip AND EXISTS ${_abs_FILE} ) - - FILE(READ ${_abs_FILE} _contents) - - GET_FILENAME_COMPONENT(_abs_PATH ${_abs_FILE} PATH) - - STRING(REGEX MATCHALL "# *include +[^ ]+\\.moc[\">]" _match "${_contents}") - IF(_match) - FOREACH (_current_MOC_INC ${_match}) - STRING(REGEX MATCH "[^ <\"]+\\.moc" _current_MOC "${_current_MOC_INC}") - - GET_FILENAME_COMPONENT(_basename ${_current_MOC} NAME_WE) - IF(EXISTS ${_abs_PATH}/${_basename}.hpp) - SET(_header ${_abs_PATH}/${_basename}.hpp) - ELSE(EXISTS ${_abs_PATH}/${_basename}.hpp) - SET(_header ${_abs_PATH}/${_basename}.h) - ENDIF(EXISTS ${_abs_PATH}/${_basename}.hpp) - SET(_moc ${CMAKE_CURRENT_BINARY_DIR}/${_current_MOC}) - QT4_CREATE_MOC_COMMAND(${_header} ${_moc} "${_moc_INCS}" "") - MACRO_ADD_FILE_DEPENDENCIES(${_abs_FILE} ${_moc}) - ENDFOREACH (_current_MOC_INC) - ENDIF(_match) - ENDIF ( NOT _skip AND EXISTS ${_abs_FILE} ) - ENDFOREACH (_current_FILE) -ENDMACRO(QT4_AUTOMOC) - - -MACRO(QT4_CREATE_TRANSLATION _qm_files) - QT4_EXTRACT_OPTIONS(_lupdate_files _lupdate_options ${ARGN}) - SET(_my_sources) - SET(_my_dirs) - SET(_my_tsfiles) - SET(_ts_pro) - FOREACH (_file ${_lupdate_files}) - GET_FILENAME_COMPONENT(_ext ${_file} EXT) - GET_FILENAME_COMPONENT(_abs_FILE ${_file} ABSOLUTE) - IF(_ext MATCHES "ts") - LIST(APPEND _my_tsfiles ${_abs_FILE}) - ELSE(_ext MATCHES "ts") - IF(NOT _ext) - LIST(APPEND _my_dirs ${_abs_FILE}) - ELSE(NOT _ext) - LIST(APPEND _my_sources ${_abs_FILE}) - ENDIF(NOT _ext) - ENDIF(_ext MATCHES "ts") - ENDFOREACH(_file) - FOREACH(_ts_file ${_my_tsfiles}) - IF(_my_sources) - # make a .pro file to call lupdate on, so we don't make our commands too - # long for some systems - GET_FILENAME_COMPONENT(_ts_name ${_ts_file} NAME_WE) - SET(_ts_pro ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${_ts_name}_lupdate.pro) - SET(_pro_srcs) - FOREACH(_pro_src ${_my_sources}) - SET(_pro_srcs "${_pro_srcs} \"${_pro_src}\"") - ENDFOREACH(_pro_src ${_my_sources}) - FILE(WRITE ${_ts_pro} "SOURCES = ${_pro_srcs}") - ENDIF(_my_sources) - ADD_CUSTOM_COMMAND(OUTPUT ${_ts_file} - COMMAND ${QT_LUPDATE_EXECUTABLE} - ARGS ${_lupdate_options} ${_ts_pro} ${_my_dirs} -ts ${_ts_file} - DEPENDS ${_my_sources} ${_ts_pro}) - ENDFOREACH(_ts_file) - QT4_ADD_TRANSLATION(${_qm_files} ${_my_tsfiles}) -ENDMACRO(QT4_CREATE_TRANSLATION) - - -MACRO(QT4_ADD_TRANSLATION _qm_files) - FOREACH (_current_FILE ${ARGN}) - GET_FILENAME_COMPONENT(_abs_FILE ${_current_FILE} ABSOLUTE) - GET_FILENAME_COMPONENT(qm ${_abs_FILE} NAME_WE) - GET_SOURCE_FILE_PROPERTY(output_location ${_abs_FILE} OUTPUT_LOCATION) - IF(output_location) - FILE(MAKE_DIRECTORY "${output_location}") - SET(qm "${output_location}/${qm}.qm") - ELSE(output_location) - SET(qm "${CMAKE_CURRENT_BINARY_DIR}/${qm}.qm") - ENDIF(output_location) - - ADD_CUSTOM_COMMAND(OUTPUT ${qm} - COMMAND ${QT_LRELEASE_EXECUTABLE} - ARGS ${_abs_FILE} -qm ${qm} - DEPENDS ${_abs_FILE} - ) - SET(${_qm_files} ${${_qm_files}} ${qm}) - ENDFOREACH (_current_FILE) -ENDMACRO(QT4_ADD_TRANSLATION) diff --git a/declarative/plugin.h b/declarative/plugin.h index f82920cb..69de2318 100644 --- a/declarative/plugin.h +++ b/declarative/plugin.h @@ -1,82 +1,80 @@ /* Copyright (C) 2011 Harald Sitter This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), Nokia Corporation (or its successors, if any) and the KDE Free Qt Foundation, which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef PHONON_DECLARATIVE_PLUGIN_H #define PHONON_DECLARATIVE_PLUGIN_H #include namespace Phonon { /** * \brief The Phonon QtDeclarative Elements. * * QtDeclarative, also known as Qt Quick and QML, enables declarative UI creation. * The Phonon::Declarative namespace contains elements that enable access to * multimedia features from within QML code by simply importing the Phonon * Qt Quick plugin. * * \code * import Phonon 1.0 * \endcode * * A simple application may look like this * \code * import QtQuick 1.0 * import Phonon 1.0 * Item { * Media { * anchors.fill: parent * source: "~/coolvideo.webm" * AudioOutput { volume: 100 } * VideoOutput { anchors.fill: parent } * } * } * \endcode * * The Phonon Qt Quick plugin contains most of Phonon C++'s objects such as: * \list * \li MediaElement (MediaObject) * \li AudioOutputElement (AudioOutput) * \li VideoElement (VideoGraphicsObject) * \li SubtitleElement (MediaController - subtitle features) * \li VolumeFaderEffect (VolumeFaderEffect) * \endlist * * \author Harald Sitter */ namespace Declarative { class Plugin : public QDeclarativeExtensionPlugin { -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) Q_PLUGIN_METADATA(IID "org.kde.phonon.DeclarativePlugin") -#endif public: virtual void registerTypes(const char *uri); }; } // namespace Declarative } // namespace Phonon #endif // PHONON_DECLARATIVE_PLUGIN_H diff --git a/designer/phononcollection.cpp b/designer/phononcollection.cpp index 7cba856d..bbe0ae67 100644 --- a/designer/phononcollection.cpp +++ b/designer/phononcollection.cpp @@ -1,106 +1,100 @@ /* Copyright (C) 2011 Harald Sitter This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), Nokia Corporation (or its successors, if any) and the KDE Free Qt Foundation, which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ /**************************************************************************** ** ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the Qt Designer of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial Usage ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Nokia. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "seeksliderplugin.h" #include "videoplayerplugin.h" #include "videowidgetplugin.h" #include "volumesliderplugin.h" #include #include class PhononCollection: public QObject, public QDesignerCustomWidgetCollectionInterface { Q_OBJECT -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) Q_PLUGIN_METADATA(IID "org.kde.phonon.PhononCollection") -#endif Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) public: explicit PhononCollection(QObject *parent = 0); virtual QList customWidgets() const override; private: QList m_plugins; }; PhononCollection::PhononCollection(QObject *parent) : QObject(parent) { const QString group = QLatin1String("Phonon"); m_plugins.push_back(new SeekSliderPlugin(group, this)); m_plugins.push_back(new VideoPlayerPlugin(group, this)); m_plugins.push_back(new VideoWidgetPlugin(group, this)); m_plugins.push_back(new VolumeSliderPlugin(group, this)); } QList PhononCollection::customWidgets() const { return m_plugins; } -#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) -Q_EXPORT_PLUGIN(PhononCollection) -#endif - #include "phononcollection.moc" diff --git a/phonon/CMakeLists.txt b/phonon/CMakeLists.txt index 08f1cbb6..bca258ad 100644 --- a/phonon/CMakeLists.txt +++ b/phonon/CMakeLists.txt @@ -1,230 +1,210 @@ if(NOT PHONON_NO_GRAPHICSVIEW) message(STATUS "Building graphicsview.") add_subdirectory(graphicsview) else() message(STATUS "Not Building graphicsview.") add_definitions(-DPHONON_NO_GRAPHICSVIEW) endif() if (PHONON_BUILD_EXPERIMENTAL) add_subdirectory(experimental) endif (PHONON_BUILD_EXPERIMENTAL) # ------------------------ Configure File QMake Style ------------------------ # if (PHONON_NO_DBUS OR NOT QT_QTDBUS_FOUND) set(PHONON_NO_DBUS_DEFINE "#define PHONON_NO_DBUS") else() set(PHONON_NO_DBUS_DEFINE "/* #undef PHONON_NO_DBUS */") endif() if (PHONON_NO_CAPTURE) set(PHONON_NO_CAPTURE_DEFINE "#define PHONON_NO_CAPTURE") else() set(PHONON_NO_CAPTURE_DEFINE "/* #undef PHONON_NO_CAPTURE */") endif() file(READ ${CMAKE_CURRENT_SOURCE_DIR}/phononconfig_p.h.in PHONONCONFIG_FILE) string(REGEX REPLACE "\\#include( )+\\\\\"([A-Za-z_]+\\.h)\\\\\"" "#include \"\\2\"" PHONONCONFIG_FILE "${PHONONCONFIG_FILE}") string(REGEX REPLACE "\\#define( )+([A-Za-z_]+)( )+\\\\\"(.+)\\\\\"" "#define \\2 \"\\4\"" PHONONCONFIG_FILE "${PHONONCONFIG_FILE}") string(REGEX REPLACE "\\$\\$\\{(PHONON_NO_DBUS_DEFINE)\\}" "${PHONON_NO_DBUS_DEFINE}" PHONONCONFIG_FILE "${PHONONCONFIG_FILE}") file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/phononconfig_p.h "${PHONONCONFIG_FILE}") set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_CURRENT_BINARY_DIR}/phononconfig_p.h) file(READ ${CMAKE_CURRENT_SOURCE_DIR}/phononnamespace.h.in PHONONNAMESPACE_FILE) string(REGEX REPLACE "\\#include( )+\\\\\"([A-Za-z_]+\\.h)\\\\\"" "#include \"\\2\"" PHONONNAMESPACE_FILE "${PHONONNAMESPACE_FILE}") string(REGEX REPLACE "\\#define( )+([A-Za-z_]+)( )+\\\\\"(.+)\\\\\"" "#define \\2 \"\\4\"" PHONONNAMESPACE_FILE "${PHONONNAMESPACE_FILE}") string(REGEX REPLACE "\\$\\$\\{PHONON_LIB_MAJOR_VERSION\\}" "${PHONON_LIB_MAJOR_VERSION}" PHONONNAMESPACE_FILE "${PHONONNAMESPACE_FILE}") string(REGEX REPLACE "\\$\\$\\{PHONON_LIB_MINOR_VERSION\\}" "${PHONON_LIB_MINOR_VERSION}" PHONONNAMESPACE_FILE "${PHONONNAMESPACE_FILE}") string(REGEX REPLACE "\\$\\$\\{PHONON_LIB_PATCH_VERSION\\}" "${PHONON_LIB_PATCH_VERSION}" PHONONNAMESPACE_FILE "${PHONONNAMESPACE_FILE}") string(REGEX REPLACE "\\$\\$\\{PHONON_NO_CAPTURE_DEFINE\\}" "${PHONON_NO_CAPTURE_DEFINE}" PHONONNAMESPACE_FILE "${PHONONNAMESPACE_FILE}") # We write these to a strange place so the demos can find them properly file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/phononnamespace.h "${PHONONNAMESPACE_FILE}") # Required for demos/cmake/FindPhonon.cmake file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/../includes/phonon/phononnamespace.h "${PHONONNAMESPACE_FILE}") set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_CURRENT_BINARY_DIR}/phononnamespace.h) set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_CURRENT_BINARY_DIR}/../includes/phonon/phononnamespace.h ) # ---------------------------------------------------------------------------- # macro_optional_find_package(PulseAudio 0.9.15) macro_log_feature(PULSEAUDIO_FOUND "PulseAudio" "A cross-platform, networked sound server." "http://www.pulseaudio.org" FALSE "" "Allows audio playback via the PulseAudio soundserver when it is running") macro_optional_find_package(GLIB2) macro_log_feature(GLIB2_FOUND "GLib2" "GLib 2 is required to compile the pulseaudio for Phonon" "http://www.gtk.org/download/" FALSE) if (GLIB2_FOUND AND PULSEAUDIO_FOUND) set(PHONON_PULSESUPPORT TRUE CACHE BOOL "Has Phonon pulseaudio support ?") if(PHONON_PULSESUPPORT) add_definitions(-DHAVE_PULSEAUDIO) endif(PHONON_PULSESUPPORT) include_directories(${GLIB2_INCLUDE_DIR} ${PULSEAUDIO_INCLUDE_DIR}) else(GLIB2_FOUND AND PULSEAUDIO_FOUND) set(PHONON_PULSESUPPORT FALSE CACHE BOOL "Has Phonon pulseaudio support ?") set(PULSEAUDIO_INCLUDE_DIR "") set(PULSEAUDIO_LIBRARY "") set(PULSEAUDIO_MAINLOOP_LIBRARY "") endif(GLIB2_FOUND AND PULSEAUDIO_FOUND) -if (PHONON_BUILD_PHONON4QT5) - message(WARNING "QZeitgeist has not been ported to Qt5, support is disabled.") -else () - macro_optional_find_package(QZeitgeist 0.8) - macro_log_feature(QZEITGEIST_FOUND "QZeitgeist" "Qt bindings for Zeitgeist" "http://projects.kde.org/kdesupport/libqzeitgeist/" FALSE "0.8") - if (QZEITGEIST_FOUND) - add_definitions(-DHAVE_QZEITGEIST) - include_directories(${QZEITGEIST_INCLUDE_DIR}) - endif(QZEITGEIST_FOUND) -endif() - include_directories(${CMAKE_BINARY_DIR}/includes/phonon) set(phonon_LIB_SRCS abstractaudiooutput.cpp abstractaudiooutput_p.cpp abstractmediastream.cpp abstractvideooutput.cpp abstractvideooutput_p.cpp audiodataoutput.cpp audiooutput.cpp audiooutputinterface.cpp backendcapabilities.cpp effect.cpp effectparameter.cpp effectwidget.cpp factory.cpp globalconfig.cpp iodevicestream.cpp mediacontroller.cpp mediaobject.cpp medianode.cpp mediasource.cpp mrl.cpp objectdescription.cpp objectdescriptionmodel.cpp path.cpp phononnamespace.cpp platform.cpp pulsesupport.cpp seekslider.cpp statesvalidator.cpp streaminterface.cpp swiftslider.cpp volumefadereffect.cpp volumeslider.cpp videoplayer.cpp videowidget.cpp ${phonon_VGO_SRCS} ) if(ECM_FOUND) ecm_create_qm_loader(phonon_LIB_SRCS libphonon_qt) endif() if (PHONON_PULSESUPPORT) list(APPEND phonon_LIB_SRCS pulsestream.cpp ) endif (PHONON_PULSESUPPORT) if (QT_QTDBUS_FOUND AND NOT PHONON_NO_DBUS) list(APPEND phonon_LIB_SRCS audiooutputadaptor.cpp ) endif (QT_QTDBUS_FOUND AND NOT PHONON_NO_DBUS) option(PHONON_NO_PLATFORMPLUGIN "Do not use any platform plugin") if (PHONON_NO_PLATFORMPLUGIN) add_definitions(-DQT_NO_PHONON_PLATFORMPLUGIN) endif (PHONON_NO_PLATFORMPLUGIN) add_definitions(-DPHONON_LIBRARY_PATH="${CMAKE_INSTALL_PREFIX}/${PLUGIN_INSTALL_DIR}/plugins") add_definitions(-DPHONON_BACKEND_DIR_SUFFIX="/${PHONON_LIB_SONAME}_backend/") add_library(${PHONON_LIB_SONAME} SHARED ${phonon_LIB_SRCS}) qt5_use_modules(${PHONON_LIB_SONAME} Core Widgets) if(QT_QTDBUS_FOUND AND NOT PHONON_NO_DBUS) qt5_use_modules(${PHONON_LIB_SONAME} DBus) endif(QT_QTDBUS_FOUND AND NOT PHONON_NO_DBUS) if (PHONON_PULSESUPPORT) target_link_libraries(${PHONON_LIB_SONAME} ${GOBJECT_LIBRARIES} ${PULSEAUDIO_LIBRARY} ${PULSEAUDIO_MAINLOOP_LIBRARY}) endif (PHONON_PULSESUPPORT) if(NOT PHONON_NO_GRAPHICSVIEW) qt5_use_modules(${PHONON_LIB_SONAME} OpenGL) target_link_libraries(${PHONON_LIB_SONAME} ${OPENGL_gl_LIBRARY}) endif() -if (QZEITGEIST_FOUND) - target_link_libraries(${PHONON_LIB_SONAME} ${QZEITGEIST_LIBRARY}) -endif(QZEITGEIST_FOUND) - -if (${CMAKE_SYSTEM_NAME} MATCHES "SunOS") - # We need to explicitly link libm to phonon in Solaris - target_link_libraries(${PHONON_LIB_SONAME} m) -endif (${CMAKE_SYSTEM_NAME} MATCHES "SunOS") - set_target_properties(${PHONON_LIB_SONAME} PROPERTIES VERSION ${PHONON_LIB_VERSION} SOVERSION ${PHONON_LIB_SOVERSION} DEFINE_SYMBOL MAKE_PHONON_LIB LINK_INTERFACE_LIBRARIES "") # LINK_INTERFACE_LIBRARIES: By default don't add any linked libraries to the "exported" # link interfaces, so that executables linking against this library # will not automatically add implicit dependencies to their link list. # This reduces inter-package dependencies and makes it easier to remove # dependencies of shared libraries without breaking binary compatibility. target_include_directories(${PHONON_LIB_SONAME} INTERFACE "$") install(TARGETS ${PHONON_LIB_SONAME} EXPORT PhononLibs ${INSTALL_TARGETS_DEFAULT_ARGS} ) install(FILES abstractaudiooutput.h abstractmediastream.h abstractvideooutput.h addoninterface.h audiodataoutput.h audiodataoutputinterface.h audiooutput.h audiooutputinterface.h backendcapabilities.h backendinterface.h effect.h effectinterface.h effectparameter.h effectwidget.h globalconfig.h globaldescriptioncontainer.h mediacontroller.h medianode.h mediaobject.h mediaobjectinterface.h mediasource.h mrl.h objectdescription.h objectdescriptionmodel.h path.h phonon_export.h phonondefs.h platformplugin.h pulsesupport.h seekslider.h streaminterface.h videoplayer.h videowidget.h videowidgetinterface.h volumefadereffect.h volumefaderinterface.h volumeslider.h ${phonon_VGO_HDRS} ${CMAKE_CURRENT_BINARY_DIR}/phononnamespace.h DESTINATION ${INCLUDE_INSTALL_DIR}/phonon COMPONENT Devel) if (QT_QTDBUS_FOUND AND NOT PHONON_NO_DBUS) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/org.kde.Phonon.AudioOutput.xml ${CMAKE_CURRENT_BINARY_DIR}/org.kde.${PHONON_LIB_SONAME_CAMEL}.AudioOutput.xml @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.${PHONON_LIB_SONAME_CAMEL}.AudioOutput.xml DESTINATION ${DBUS_INTERFACES_INSTALL_DIR}) endif (QT_QTDBUS_FOUND AND NOT PHONON_NO_DBUS) diff --git a/phonon/experimental/packetpool.cpp b/phonon/experimental/packetpool.cpp index 8ebe1347..6bfc87b5 100644 --- a/phonon/experimental/packetpool.cpp +++ b/phonon/experimental/packetpool.cpp @@ -1,123 +1,115 @@ /* This file is part of the KDE project Copyright (C) 2007-2008 Matthias Kretz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "packetpool.h" #include "packetpool_p.h" #include "packet.h" #include "packet_p.h" namespace Phonon { int PacketPool::packetSize() const { return d_ptr->packetSize; } int PacketPool::poolSize() const { return d_ptr->poolSize; } -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) int PacketPool::unusedPackets() const { return d_ptr->ringBufferSize.loadAcquire(); } -#else -int PacketPool::unusedPackets() const { return d_ptr->ringBufferSize; } -#endif PacketPoolPrivate::PacketPoolPrivate(int _packetSize, int _poolSize) : freePackets(new PacketPrivate *[_poolSize]), packetMemory(new char[_packetSize * _poolSize]), readPosition(0), writePosition(0), ringBufferSize(_poolSize), packetSize(_packetSize), poolSize(_poolSize) { for (int i = 0; i < _poolSize; ++i) { freePackets[i] = new PacketPrivate(&packetMemory[i * packetSize], this); } } PacketPoolPrivate::~PacketPoolPrivate() { -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) Q_ASSERT(poolSize == ringBufferSize.loadAcquire()); -#else - Q_ASSERT(poolSize == ringBufferSize); -#endif for (int i = 0; i < poolSize; ++i) { delete freePackets[i]; } delete[] freePackets; delete[] packetMemory; } void PacketPoolPrivate::releasePacket(const Packet &packet) { const int _writePos = writePosition.fetchAndAddAcquire(1); int pos = _writePos; while (pos >= poolSize) { pos -= poolSize; } writePosition.testAndSetRelease(_writePos, pos); freePackets[pos] = packet.d_ptr; ringBufferSize.ref(); } Packet PacketPoolPrivate::acquirePacket() { const int s = ringBufferSize.fetchAndAddRelaxed(-1); if (s <= 0) { ringBufferSize.fetchAndAddRelaxed(1); return Packet(); } const int _readPos = readPosition.fetchAndAddRelaxed(1); int pos = _readPos; while (pos >= poolSize) { pos -= poolSize; } readPosition.testAndSetRelease(_readPos, pos); freePackets[pos]->m_size = 0; return Packet(*freePackets[pos]); } PacketPool::PacketPool(int packetSize, int _poolSize) : d_ptr(new PacketPoolPrivate(packetSize, _poolSize)) { d_ptr->ref.ref(); } PacketPool::PacketPool(const PacketPool &rhs) : d_ptr(rhs.d_ptr) { d_ptr->ref.ref(); } PacketPool &PacketPool::operator=(const PacketPool &rhs) { if (d_ptr != rhs.d_ptr) { if (!d_ptr->ref.deref()) { delete d_ptr; } d_ptr = rhs.d_ptr; d_ptr->ref.ref(); } return *this; } PacketPool::~PacketPool() { if (!d_ptr->ref.deref()) { delete d_ptr; } } } // namespace Phonon diff --git a/phonon/factory.cpp b/phonon/factory.cpp index e2817135..0828fda6 100644 --- a/phonon/factory.cpp +++ b/phonon/factory.cpp @@ -1,620 +1,609 @@ /* Copyright (C) 2004-2007 Matthias Kretz Copyright (C) 2011 Harald Sitter This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), Nokia Corporation (or its successors, if any) and the KDE Free Qt Foundation, which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "factory_p.h" #include "backendinterface.h" #include "medianode_p.h" #include "mediaobject.h" #include "audiooutput.h" #include "globalstatic_p.h" #include "objectdescription.h" #include "platformplugin.h" #include "phononconfig_p.h" #include "phononnamespace_p.h" #include #include #include #include #include #include #ifndef PHONON_NO_DBUS #include #endif #include #include #include namespace Phonon { class PlatformPlugin; class FactoryPrivate : public Phonon::Factory::Sender { friend QObject *Factory::backend(bool); Q_OBJECT public: FactoryPrivate(); ~FactoryPrivate(); bool tryCreateBackend(const QString &path); // Implementation depends on Qt version. bool createSuitableBackend(const QString &libPath, const QList &plugins); bool createBackend(); #ifndef QT_NO_PHONON_PLATFORMPLUGIN PlatformPlugin *platformPlugin(); PlatformPlugin *m_platformPlugin; bool m_noPlatformPlugin; #endif //QT_NO_PHONON_PLATFORMPLUGIN QPointer m_backendObject; QList objects; QList mediaNodePrivateList; private Q_SLOTS: /** * This is called via DBUS when the user changes the Phonon Backend. */ #ifndef PHONON_NO_DBUS void phononBackendChanged(); #endif //PHONON_NO_DBUS /** * unregisters the backend object */ void objectDestroyed(QObject *); void objectDescriptionChanged(ObjectDescriptionType); }; PHONON_GLOBAL_STATIC(Phonon::FactoryPrivate, globalFactory) static inline void ensureLibraryPathSet() { #ifdef PHONON_LIBRARY_PATH static bool done = false; if (!done) { done = true; QCoreApplication::addLibraryPath(QLatin1String(PHONON_LIBRARY_PATH)); } #endif // PHONON_LIBRARY_PATH } void Factory::setBackend(QObject *b) { Q_ASSERT(globalFactory->m_backendObject == 0); globalFactory->m_backendObject = b; } bool FactoryPrivate::tryCreateBackend(const QString &path) { QPluginLoader pluginLoader(path); pDebug() << "attempting to load" << path; if (!pluginLoader.load()) { pDebug() << Q_FUNC_INFO << " load failed:" << pluginLoader.errorString(); return false; } pDebug() << pluginLoader.instance(); m_backendObject = pluginLoader.instance(); if (m_backendObject) { return true; } // no backend found, don't leave an unused plugin in memory pluginLoader.unload(); return false; } -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) struct BackendDescriptor { explicit BackendDescriptor(const QString &path) : isValid(false) { QPluginLoader loader(path); iid = loader.metaData().value(QLatin1String("IID")).toString(); const QJsonObject metaData = loader.metaData().value(QLatin1String("MetaData")).toObject(); name = metaData.value(QLatin1String("Name")).toString(); icon = metaData.value(QLatin1String("Icon")).toString(); version = metaData.value(QLatin1String("Version")).toString(); website = metaData.value(QLatin1String("Website")).toString(); preference = metaData.value(QLatin1String("InitialPreference")).toDouble(); pluginPath = path; if (name.isEmpty()) name = QFileInfo(path).baseName(); if (iid.isEmpty()) return; // Not valid. isValid = true; } bool isValid; QString iid; QString name; QString icon; QString version; QString website; int preference; QString pluginPath; /** Implemented for qSort(QList) */ bool operator <(const BackendDescriptor &rhs) const { return this->preference < rhs.preference; } }; bool FactoryPrivate::createSuitableBackend(const QString &libPath, const QList &plugins) { // User configured preference. QList iidPreference; QSettings settings("kde.org", "libphonon"); const int size = settings.beginReadArray("Backends"); for (int i = 0; i < size; ++i) { settings.setArrayIndex(i); iidPreference.append(settings.value("iid").toString()); } settings.endArray(); // Find physical backend plugins. QList foundBackends; foreach (const QString &plugin, plugins) { BackendDescriptor descriptor(libPath + plugin); if (!descriptor.isValid) continue; foundBackends.append(descriptor); } qSort(foundBackends); // Try to pick a preferred backend. foreach (const QString &iid, iidPreference) { // This is slightly inefficient but we only have 2 backends :P // Also using a list requires less overall code than QMap. QList::iterator it; for (it = foundBackends.begin(); it != foundBackends.end(); ++it) { const BackendDescriptor &descriptor = *it; if (descriptor.iid != iid) { continue; } if (tryCreateBackend(descriptor.pluginPath)) { return true; } else { // Drop backends that failed to construct. it = foundBackends.erase(it); if (it == foundBackends.end()) break; } } } // No Preferred backend could be constructed. Try all remaining backends // in order of initial preference. // Note that unconstructable backends have been dropped previously. foreach (const BackendDescriptor &descriptor, foundBackends) { if (tryCreateBackend(descriptor.pluginPath)) return true; } return false; } -#else // Qt 4 -bool FactoryPrivate::createSuitableBackend(const QString &libPath, const QList &plugins) -{ - foreach (const QString &plugin, plugins) { - if (tryCreateBackend(libPath + plugin)) - return true; - } - return false; -} -#endif // This entire function is so terrible to read I hope it implodes some day. bool FactoryPrivate::createBackend() { pDebug() << Q_FUNC_INFO << "Phonon" << PHONON_VERSION_STR << "trying to create backend..."; #ifndef QT_NO_LIBRARY Q_ASSERT(m_backendObject == 0); // If the user defines a backend with PHONON_BACKEND this overrides the // platform plugin (because we cannot influence its lookup priority) and // consequently will try to find/load the defined backend manually. const QByteArray backendEnv = qgetenv("PHONON_BACKEND"); #ifndef QT_NO_PHONON_PLATFORMPLUGIN PlatformPlugin *f = globalFactory->platformPlugin(); if (f && backendEnv.isEmpty()) { // TODO: it would be very groovy if we could add a param, so that the // platform could also try to load the defined backend as preferred choice. m_backendObject = f->createBackend(); } #endif //QT_NO_PHONON_PLATFORMPLUGIN if (!m_backendObject) { ensureLibraryPathSet(); // could not load a backend through the platform plugin. Falling back to the default // (finding the first loadable backend). const QStringList paths = QCoreApplication::libraryPaths(); for (int i = 0; i < paths.count(); ++i) { #ifdef __GNUC__ #warning this actually breaks with qmake.... #endif #ifndef PHONON_BACKEND_DIR_SUFFIX #ifdef __GNUC__ #error PHONON_BACKEND_DIR_SUFFIX must be defined. #endif #endif const QString libPath = paths.at(i) + PHONON_BACKEND_DIR_SUFFIX; const QDir dir(libPath); if (!dir.exists()) { pDebug() << Q_FUNC_INFO << dir.absolutePath() << "does not exist"; continue; } QStringList plugins(dir.entryList(QDir::Files)); #ifdef Q_OS_SYMBIAN /* On Symbian OS we might have two plugins, one which uses Symbian * MMF framework("mmf"), and one which uses Real Networks's * Helix("hxphonon"). We prefer the latter because it's more * sophisticated, so we make sure the Helix backend is attempted * to be loaded first, and the MMF backend is used for backup. */ { const int helix = plugins.indexof(QLatin1String("hxphonon")); if (helix != -1) plugins.move(helix, 0); } #endif if (!backendEnv.isEmpty()) { pDebug() << "trying to load:" << backendEnv << "as first choice"; const int backendIndex = plugins.indexOf(QRegExp(backendEnv + ".*")); if (backendIndex != -1) plugins.move(backendIndex, 0); } // This function implements a very simple trial-and-error loader for // Qt 4 and on top of that a preference system for Qt 5. Therefore // in Qt 5 we have backend preference independent of a platform // plugin. createSuitableBackend(libPath, plugins); if (m_backendObject) { break; } } if (!m_backendObject) { pWarning() << Q_FUNC_INFO << "phonon backend plugin could not be loaded"; return false; } } pDebug() << Q_FUNC_INFO << "Phonon backend" << m_backendObject->property("backendName").toString() << "version" << m_backendObject->property("backendVersion").toString() << "loaded"; connect(m_backendObject, SIGNAL(objectDescriptionChanged(ObjectDescriptionType)), SLOT(objectDescriptionChanged(ObjectDescriptionType))); return true; #else //QT_NO_LIBRARY pWarning() << Q_FUNC_INFO << "Trying to use Phonon with QT_NO_LIBRARY defined. " "That is currently not supported"; return false; #endif } FactoryPrivate::FactoryPrivate() : #ifndef QT_NO_PHONON_PLATFORMPLUGIN m_platformPlugin(0), m_noPlatformPlugin(false), #endif //QT_NO_PHONON_PLATFORMPLUGIN m_backendObject(0) { // Add the post routine to make sure that all other global statics (especially the ones from Qt) // are still available. If the FactoryPrivate dtor is called too late many bad things can happen // as the whole backend might still be alive. qAddPostRoutine(globalFactory.destroy); #ifndef PHONON_NO_DBUS QDBusConnection::sessionBus().connect(QString(), QString(), QLatin1String("org.kde.Phonon.Factory"), QLatin1String("phononBackendChanged"), this, SLOT(phononBackendChanged())); #endif } FactoryPrivate::~FactoryPrivate() { for (int i = 0; i < mediaNodePrivateList.count(); ++i) { mediaNodePrivateList.at(i)->deleteBackendObject(); } if (objects.size() > 0) { pError() << "The backend objects are not deleted as was requested."; qDeleteAll(objects); } delete m_backendObject; #ifndef QT_NO_PHONON_PLATFORMPLUGIN delete m_platformPlugin; #endif //QT_NO_PHONON_PLATFORMPLUGIN } void FactoryPrivate::objectDescriptionChanged(ObjectDescriptionType type) { #ifdef PHONON_METHODTEST Q_UNUSED(type); #else pDebug() << Q_FUNC_INFO << type; switch (type) { case AudioOutputDeviceType: emit availableAudioOutputDevicesChanged(); break; case AudioCaptureDeviceType: emit availableAudioCaptureDevicesChanged(); break; case VideoCaptureDeviceType: emit availableVideoCaptureDevicesChanged(); break; default: break; } //emit capabilitiesChanged(); #endif // PHONON_METHODTEST } Factory::Sender *Factory::sender() { return globalFactory; } bool Factory::isMimeTypeAvailable(const QString &mimeType) { #ifndef QT_NO_PHONON_PLATFORMPLUGIN PlatformPlugin *f = globalFactory->platformPlugin(); if (f) { return f->isMimeTypeAvailable(mimeType); } #else Q_UNUSED(mimeType); #endif //QT_NO_PHONON_PLATFORMPLUGIN return true; // the MIME type might be supported, let BackendCapabilities find out } void Factory::registerFrontendObject(MediaNodePrivate *bp) { globalFactory->mediaNodePrivateList.prepend(bp); // inserted last => deleted first } void Factory::deregisterFrontendObject(MediaNodePrivate *bp) { // The Factory can already be cleaned up while there are other frontend objects still alive. // When those are deleted they'll call deregisterFrontendObject through ~BasePrivate if (!globalFactory.isDestroyed()) { globalFactory->mediaNodePrivateList.removeAll(bp); } } #ifndef PHONON_NO_DBUS void FactoryPrivate::phononBackendChanged() { QMessageBox::information(qApp->activeWindow(), tr("Restart Application"), tr("You changed the backend of the Phonon multimedia system.\n\n" "To apply this change you will need to" " restart '%1'.").arg(qAppName())); emit backendChanged(); } #endif //PHONON_NO_DBUS //X void Factory::freeSoundcardDevices() //X { //X if (globalFactory->backend) { //X globalFactory->backend->freeSoundcardDevices(); //X } //X } void FactoryPrivate::objectDestroyed(QObject * obj) { //pDebug() << Q_FUNC_INFO << obj; objects.removeAll(obj); } #define FACTORY_IMPL(classname) \ QObject *Factory::create ## classname(QObject *parent) \ { \ if (backend()) { \ return registerQObject(qobject_cast(backend())->createObject(BackendInterface::classname##Class, parent)); \ } \ return 0; \ } #define FACTORY_IMPL_1ARG(classname) \ QObject *Factory::create ## classname(int arg1, QObject *parent) \ { \ if (backend()) { \ return registerQObject(qobject_cast(backend())->createObject(BackendInterface::classname##Class, parent, QList() << arg1)); \ } \ return 0; \ } FACTORY_IMPL(MediaObject) #ifndef QT_NO_PHONON_EFFECT FACTORY_IMPL_1ARG(Effect) #endif //QT_NO_PHONON_EFFECT #ifndef QT_NO_PHONON_VOLUMEFADEREFFECT FACTORY_IMPL(VolumeFaderEffect) #endif //QT_NO_PHONON_VOLUMEFADEREFFECT FACTORY_IMPL(AudioOutput) #ifndef QT_NO_PHONON_VIDEO FACTORY_IMPL(VideoWidget) FACTORY_IMPL(VideoGraphicsObject) #endif //QT_NO_PHONON_VIDEO FACTORY_IMPL(AudioDataOutput) #undef FACTORY_IMPL #ifndef QT_NO_PHONON_PLATFORMPLUGIN PlatformPlugin *FactoryPrivate::platformPlugin() { if (m_platformPlugin) { return m_platformPlugin; } if (m_noPlatformPlugin) { return 0; } #ifndef PHONON_NO_DBUS if (!QCoreApplication::instance() || QCoreApplication::applicationName().isEmpty()) { pWarning() << "Phonon needs QCoreApplication::applicationName to be set to export audio output names through the DBUS interface"; } #endif Q_ASSERT(QCoreApplication::instance()); const QByteArray platform_plugin_env = qgetenv("PHONON_PLATFORMPLUGIN"); if (!platform_plugin_env.isEmpty()) { pDebug() << Q_FUNC_INFO << "platform plugin path:" << platform_plugin_env; QPluginLoader pluginLoader(QString::fromLocal8Bit(platform_plugin_env.constData())); if (pluginLoader.load()) { QObject *plInstance = pluginLoader.instance(); if (!plInstance) { pDebug() << Q_FUNC_INFO << "unable to grab root component object for the platform plugin"; } m_platformPlugin = qobject_cast(plInstance); if (m_platformPlugin) { pDebug() << Q_FUNC_INFO << "platform plugin" << m_platformPlugin->applicationName(); return m_platformPlugin; } else { pDebug() << Q_FUNC_INFO << "platform plugin cast fail" << plInstance; } } } const QString suffix(QLatin1String("/phonon_platform/")); ensureLibraryPathSet(); QDir dir; dir.setNameFilters( !qgetenv("KDE_FULL_SESSION").isEmpty() ? QStringList(QLatin1String("kde.*")) : (!qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty() ? QStringList(QLatin1String("gnome.*")) : QStringList()) ); dir.setFilter(QDir::Files); const QStringList libPaths = QCoreApplication::libraryPaths(); forever { for (int i = 0; i < libPaths.count(); ++i) { const QString libPath = libPaths.at(i) + suffix; dir.setPath(libPath); if (!dir.exists()) { continue; } const QStringList files = dir.entryList(QDir::Files); for (int i = 0; i < files.count(); ++i) { pDebug() << "attempting to load" << libPath + files.at(i); QPluginLoader pluginLoader(libPath + files.at(i)); if (!pluginLoader.load()) { pDebug() << Q_FUNC_INFO << " platform plugin load failed:" << pluginLoader.errorString(); continue; } pDebug() << pluginLoader.instance(); QObject *qobj = pluginLoader.instance(); m_platformPlugin = qobject_cast(qobj); pDebug() << m_platformPlugin; if (m_platformPlugin) { connect(qobj, SIGNAL(objectDescriptionChanged(ObjectDescriptionType)), SLOT(objectDescriptionChanged(ObjectDescriptionType))); return m_platformPlugin; } else { delete qobj; pDebug() << Q_FUNC_INFO << dir.absolutePath() << "exists but the platform plugin was not loadable:" << pluginLoader.errorString(); pluginLoader.unload(); } } } if (dir.nameFilters().isEmpty()) { break; } dir.setNameFilters(QStringList()); } pDebug() << Q_FUNC_INFO << "platform plugin could not be loaded"; m_noPlatformPlugin = true; return 0; } PlatformPlugin *Factory::platformPlugin() { return globalFactory->platformPlugin(); } #endif // QT_NO_PHONON_PLATFORMPLUGIN QObject *Factory::backend(bool createWhenNull) { if (globalFactory.isDestroyed()) { return 0; } if (createWhenNull && globalFactory->m_backendObject == 0) { globalFactory->createBackend(); // XXX: might create "reentrancy" problems: // a method calls this method and is called again because the // backendChanged signal is emitted if (globalFactory->m_backendObject) { emit globalFactory->backendChanged(); } } return globalFactory->m_backendObject; } #ifndef QT_NO_PROPERTIES #define GET_STRING_PROPERTY(name) \ QString Factory::name() \ { \ if (globalFactory->m_backendObject) { \ return globalFactory->m_backendObject->property(#name).toString(); \ } \ return QString(); \ } \ GET_STRING_PROPERTY(identifier) GET_STRING_PROPERTY(backendName) GET_STRING_PROPERTY(backendComment) GET_STRING_PROPERTY(backendVersion) GET_STRING_PROPERTY(backendIcon) GET_STRING_PROPERTY(backendWebsite) #endif //QT_NO_PROPERTIES QObject *Factory::registerQObject(QObject *o) { if (o) { QObject::connect(o, SIGNAL(destroyed(QObject*)), globalFactory, SLOT(objectDestroyed(QObject*)), Qt::DirectConnection); globalFactory->objects.append(o); } return o; } } //namespace Phonon #include "factory.moc" #include "moc_factory_p.cpp" // vim: sw=4 ts=4 diff --git a/phonon/globalstatic_p.h b/phonon/globalstatic_p.h index 2e1d0821..1d5379fe 100644 --- a/phonon/globalstatic_p.h +++ b/phonon/globalstatic_p.h @@ -1,343 +1,293 @@ /* This file is part of the KDE libraries Copyright (C) 2007 Matthias Kretz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), Nokia Corporation (or its successors, if any) and the KDE Free Qt Foundation, which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef PHONON_GLOBALSTATIC_P_H #define PHONON_GLOBALSTATIC_P_H #include // // WARNING!! // This code uses undocumented Qt API // Do not copy it to your application! Use only the functions that are here! // Otherwise, it could break when a new version of Qt ships. // namespace Phonon { /** * @internal */ typedef void (*CleanUpFunction)(); /** * @internal * * Helper class for PHONON_GLOBAL_STATIC to clean up the object on library unload or application * shutdown. */ class CleanUpGlobalStatic { public: CleanUpFunction func; inline ~CleanUpGlobalStatic() { func(); } }; } // namespace Phonon #ifdef Q_CC_MSVC /** * @internal * * MSVC seems to give anonymous structs the same name which fails at link time. So instead we name * the struct and hope that by adding the line number to the name it's unique enough to never clash. */ # define PHONON_GLOBAL_STATIC_STRUCT_NAME(NAME) _k_##NAME##__LINE__ #else /** * @internal * * Make the struct of the PHONON_GLOBAL_STATIC anonymous. */ # define PHONON_GLOBAL_STATIC_STRUCT_NAME(NAME) #endif /** * This macro makes it easy to use non-POD types as global statics. * The object is created on first use and creation is threadsafe. * * The object is destructed on library unload or application exit. * Be careful with calling other objects in the destructor of the class * as you have to be sure that they (or objects they depend on) are not already destructed. * * @param TYPE The type of the global static object. Do not add a *. * @param NAME The name of the function to get a pointer to the global static object. * * If you have code that might be called after the global object has been destroyed you can check * for that using the isDestroyed() function. * * If needed (If the destructor of the global object calls other functions that depend on other * global statics (e.g. KConfig::sync) your destructor has to be called before those global statics * are destroyed. A Qt post routine does that.) you can also install a post routine (@ref qAddPostRoutine) to clean up the object * using the destroy() method. If you registered a post routine and the object is destroyed because * of a lib unload you have to call qRemovePostRoutine! * * Example: * @code * class A { * public: * ~A(); * ... * }; * * PHONON_GLOBAL_STATIC(A, globalA) * // The above creates a new globally static variable named 'globalA' which you * // can use as a pointer to an instance of A. * * void doSomething() * { * // The first time you access globalA a new instance of A will be created automatically. * A *a = globalA; * ... * } * * void doSomethingElse() * { * if (globalA.isDestroyed()) { * return; * } * A *a = globalA; * ... * } * * void installPostRoutine() * { * // A post routine can be used to delete the object when QCoreApplication destructs, * // not adding such a post routine will delete the object normally at program unload * qAddPostRoutine(globalA.destroy); * } * * A::~A() * { * // When you install a post routine you have to remove the post routine from the destructor of * // the class used as global static! * qRemovePostRoutine(globalA.destroy); * } * @endcode * * A common case for the need of deletion on lib unload/app shutdown are Singleton classes. Here's * an example how to do it: * @code * class MySingletonPrivate; * class EXPORT_MACRO MySingleton * { * friend class MySingletonPrivate; * public: * static MySingleton *self(); * QString someFunction(); * * private: * MySingleton(); * ~MySingleton(); * }; * @endcode * in the .cpp file: * @code * // This class will be instantiated and referenced as a singleton in this example * class MySingletonPrivate * { * public: * QString foo; * MySingleton instance; * }; * * PHONON_GLOBAL_STATIC(MySingletonPrivate, mySingletonPrivate) * * MySingleton *MySingleton::self() * { * // returns the singleton; automatically creates a new instance if that has not happened yet. * return &mySingletonPrivate->instance; * } * QString MySingleton::someFunction() * { * // Refencing the singleton directly is possible for your convenience * return mySingletonPrivate->foo; * } * @endcode * * Instead of the above you can use also the following pattern (ignore the name of the namespace): * @code * namespace MySingleton * { * EXPORT_MACRO QString someFunction(); * } * @endcode * in the .cpp file: * @code * class MySingletonPrivate * { * public: * QString foo; * }; * * PHONON_GLOBAL_STATIC(MySingletonPrivate, mySingletonPrivate) * * QString MySingleton::someFunction() * { * return mySingletonPrivate->foo; * } * @endcode * * Now code that wants to call someFunction() doesn't have to do * @code * MySingleton::self()->someFunction(); * @endcode * anymore but instead: * @code * MySingleton::someFunction(); * @endcode * * @ingroup KDEMacros */ #define PHONON_GLOBAL_STATIC(TYPE, NAME) PHONON_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ()) /** * @overload * This is the same as PHONON_GLOBAL_STATIC, but can take arguments that are passed * to the object's constructor * * @param TYPE The type of the global static object. Do not add a *. * @param NAME The name of the function to get a pointer to the global static object. * @param ARGS the list of arguments, between brackets * * Example: * @code * class A * { * public: * A(const char *s, int i); * ... * }; * * PHONON_GLOBAL_STATIC_WITH_ARG(A, globalA, ("foo", 0)) * // The above creates a new globally static variable named 'globalA' which you * // can use as a pointer to an instance of A. * * void doSomething() * { * // The first time you access globalA a new instance of A will be created automatically. * A *a = globalA; * ... * } * @endcode * * @ingroup KDEMacros */ -#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) // Qt 4 - - #define PHONON_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \ - static QBasicAtomicPointer _k_static_##NAME = Q_BASIC_ATOMIC_INITIALIZER(0); \ - static bool _k_static_##NAME##_destroyed; \ - static struct PHONON_GLOBAL_STATIC_STRUCT_NAME(NAME) \ - { \ - bool isDestroyed() \ - { \ - return _k_static_##NAME##_destroyed; \ - } \ - inline operator TYPE*() \ - { \ - return operator->(); \ - } \ - inline TYPE *operator->() \ - { \ - TYPE *p = _k_static_##NAME; \ - if (!p) { \ - if (isDestroyed()) { \ - qFatal("Fatal Error: Accessed global static '%s *%s()' after destruction. " \ - "Defined at %s:%d", #TYPE, #NAME, __FILE__, __LINE__); \ - } \ - p = new TYPE ARGS; \ - if (!_k_static_##NAME.testAndSetOrdered(0, p)) { \ - delete p; \ - p = _k_static_##NAME; \ - } else { \ - static Phonon::CleanUpGlobalStatic cleanUpObject = { destroy }; \ - } \ - } \ - return p; \ - } \ - inline TYPE &operator*() \ - { \ - return *operator->(); \ - } \ - static void destroy() \ - { \ - _k_static_##NAME##_destroyed = true; \ - TYPE *x = _k_static_##NAME; \ - _k_static_##NAME = 0; \ - delete x; \ - } \ - } NAME; - -#else // Qt 5 - #define PHONON_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \ static QBasicAtomicPointer _k_static_##NAME = Q_BASIC_ATOMIC_INITIALIZER(0); \ static bool _k_static_##NAME##_destroyed; \ static struct PHONON_GLOBAL_STATIC_STRUCT_NAME(NAME) \ { \ inline bool isDestroyed() const \ { \ return _k_static_##NAME##_destroyed; \ } \ inline bool exists() const \ { \ return _k_static_##NAME.load() != 0; \ } \ inline operator TYPE*() \ { \ return operator->(); \ } \ inline TYPE *operator->() \ { \ if (!_k_static_##NAME.load()) { \ if (isDestroyed()) { \ qFatal("Fatal Error: Accessed global static '%s *%s()' after destruction. " \ "Defined at %s:%d", #TYPE, #NAME, __FILE__, __LINE__); \ } \ TYPE *x = new TYPE ARGS; \ if (!_k_static_##NAME.testAndSetOrdered(0, x) \ && _k_static_##NAME.load() != x ) { \ delete x; \ } else { \ static Phonon::CleanUpGlobalStatic cleanUpObject = { destroy }; \ } \ } \ return _k_static_##NAME.load(); \ } \ inline TYPE &operator*() \ { \ return *operator->(); \ } \ static void destroy() \ { \ _k_static_##NAME##_destroyed = true; \ TYPE *x = _k_static_##NAME.load(); \ _k_static_##NAME.store(0); \ delete x; \ } \ } NAME; -#endif // QT_VERSION < QT_VERSION_CHECK(5, 0, 0) - #endif // PHONON_GLOBALSTATIC_P_H diff --git a/phonon/mediaobject.cpp b/phonon/mediaobject.cpp index 50be96c7..54131ba1 100644 --- a/phonon/mediaobject.cpp +++ b/phonon/mediaobject.cpp @@ -1,786 +1,612 @@ /* This file is part of the KDE project Copyright (C) 2005-2007 Matthias Kretz Copyright (C) 2011 Trever Fischer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), Nokia Corporation (or its successors, if any) and the KDE Free Qt Foundation, which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "mediaobject.h" #include "mediaobject_p.h" #include "factory_p.h" #include "mediaobjectinterface.h" #include "audiooutput.h" #include "phonondefs_p.h" #include "abstractmediastream.h" #include "abstractmediastream_p.h" #include "frontendinterface_p.h" #include #include #include #include #include -#ifdef HAVE_QZEITGEIST -#include -#include -#include -#include -#include -#endif - #include "phononnamespace_p.h" #include "platform_p.h" #include "statesvalidator_p.h" #ifndef PHONON_NO_GRAPHICSVIEW #include #endif #define PHONON_CLASSNAME MediaObject #define PHONON_INTERFACENAME MediaObjectInterface namespace Phonon { PHONON_OBJECT_IMPL MediaObject::~MediaObject() { P_D(MediaObject); if (d->m_backendObject) { switch (state()) { case PlayingState: case BufferingState: case PausedState: stop(); break; case ErrorState: case StoppedState: case LoadingState: break; } } } Phonon::State MediaObject::state() const { P_D(const MediaObject); #ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM if (d->errorOverride) { return d->state; } if (d->ignoreLoadingToBufferingStateChange) { return BufferingState; } if (d->ignoreErrorToLoadingStateChange) { return LoadingState; } #endif // QT_NO_PHONON_ABSTRACTMEDIASTREAM if (!d->m_backendObject) { return d->state; } return INTERFACE_CALL(state()); } PHONON_INTERFACE_SETTER(setTickInterval, tickInterval, qint32) PHONON_INTERFACE_GETTER(qint32, tickInterval, d->tickInterval) PHONON_INTERFACE_GETTER(bool, hasVideo, false) PHONON_INTERFACE_GETTER(bool, isSeekable, false) PHONON_INTERFACE_GETTER(qint64, currentTime, d->currentTime) static inline bool isPlayable(const MediaSource::Type t) { return t != MediaSource::Invalid && t != MediaSource::Empty; } void MediaObject::play() { P_D(MediaObject); if (d->backendObject() && isPlayable(d->mediaSource.type())) { #ifndef PHONON_NO_GRAPHICSVIEW VideoGraphicsObject *vgo = 0; foreach (const Path &path, d->outputPaths) { if (vgo = dynamic_cast(path.sink())) { // Play() is delayed until we had a paint event. if (!vgo->canNegotiate()) { pWarning() << "VideoGraphicsObject not ready to be played because it has not received a paint() call yet, delaying playback."; connect(vgo, SIGNAL(gotPaint()), this, SLOT(play())); return; } } } #endif INTERFACE_CALL(play()); } } void MediaObject::pause() { P_D(MediaObject); if (d->backendObject() && isPlayable(d->mediaSource.type())) { INTERFACE_CALL(pause()); } } void MediaObject::stop() { P_D(MediaObject); if (d->backendObject() && isPlayable(d->mediaSource.type())) { INTERFACE_CALL(stop()); } } void MediaObject::seek(qint64 time) { P_D(MediaObject); if (d->backendObject() && isPlayable(d->mediaSource.type())) { INTERFACE_CALL(seek(time)); } } QString MediaObject::errorString() const { if (state() == Phonon::ErrorState) { P_D(const MediaObject); #ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM if (d->errorOverride) { return d->errorString; } #endif // QT_NO_PHONON_ABSTRACTMEDIASTREAM return INTERFACE_CALL(errorString()); } return QString(); } ErrorType MediaObject::errorType() const { if (state() == Phonon::ErrorState) { P_D(const MediaObject); #ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM if (d->errorOverride) { return d->errorType; } #endif // QT_NO_PHONON_ABSTRACTMEDIASTREAM return INTERFACE_CALL(errorType()); } return Phonon::NoError; } QStringList MediaObject::metaData(Phonon::MetaData f) const { switch (f) { case ArtistMetaData: return metaData(QLatin1String("ARTIST")); case AlbumMetaData: return metaData(QLatin1String("ALBUM")); case TitleMetaData: return metaData(QLatin1String("TITLE")); case DateMetaData: return metaData(QLatin1String("DATE")); case GenreMetaData: return metaData(QLatin1String("GENRE")); case TracknumberMetaData: return metaData(QLatin1String("TRACKNUMBER")); case DescriptionMetaData: return metaData(QLatin1String("DESCRIPTION")); case MusicBrainzDiscIdMetaData: return metaData(QLatin1String("MUSICBRAINZ_DISCID")); } return QStringList(); } QStringList MediaObject::metaData(const QString &key) const { P_D(const MediaObject); return d->metaData.values(key); } QMultiMap MediaObject::metaData() const { P_D(const MediaObject); return d->metaData; } PHONON_INTERFACE_GETTER(qint32, prefinishMark, d->prefinishMark) PHONON_INTERFACE_SETTER(setPrefinishMark, prefinishMark, qint32) PHONON_INTERFACE_GETTER(qint32, transitionTime, d->transitionTime) PHONON_INTERFACE_SETTER(setTransitionTime, transitionTime, qint32) qint64 MediaObject::totalTime() const { P_D(const MediaObject); if (!d->m_backendObject) { return -1; } return INTERFACE_CALL(totalTime()); } qint64 MediaObject::remainingTime() const { P_D(const MediaObject); if (!d->m_backendObject) { return -1; } qint64 ret = INTERFACE_CALL(remainingTime()); if (ret < 0) { return -1; } return ret; } MediaSource MediaObject::currentSource() const { P_D(const MediaObject); return d->mediaSource; } void MediaObject::setCurrentSource(const MediaSource &newSource) { P_D(MediaObject); if (!k_ptr->backendObject()) { d->mediaSource = newSource; return; } pDebug() << Q_FUNC_INFO << newSource.type() << newSource.url() << newSource.deviceName(); stop(); // first call stop as that often is the expected state // for setting a new URL d->mediaSource = newSource; #ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM d->abstractStream = 0; // abstractStream auto-deletes if (d->mediaSource.type() == MediaSource::Stream) { Q_ASSERT(d->mediaSource.stream()); d->mediaSource.stream()->d_func()->setMediaObjectPrivate(d); } #endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM d->playingQueuedSource = false; - d->sendToZeitgeist(StoppedState); INTERFACE_CALL(setSource(d->mediaSource)); - d->sendToZeitgeist(); } void MediaObject::clear() { P_D(MediaObject); d->sourceQueue.clear(); setCurrentSource(MediaSource()); } QList MediaObject::queue() const { P_D(const MediaObject); return d->sourceQueue; } void MediaObject::setQueue(const QList &sources) { P_D(MediaObject); d->sourceQueue.clear(); enqueue(sources); } void MediaObject::setQueue(const QList &urls) { P_D(MediaObject); d->sourceQueue.clear(); enqueue(urls); } void MediaObject::enqueue(const MediaSource &source) { P_D(MediaObject); if (!isPlayable(d->mediaSource.type())) { // the current source is nothing valid so this source needs to become the current one setCurrentSource(source); } else { d->sourceQueue << source; } } void MediaObject::enqueue(const QList &sources) { for (int i = 0; i < sources.count(); ++i) { enqueue(sources.at(i)); } } void MediaObject::enqueue(const QList &urls) { for (int i = 0; i < urls.count(); ++i) { enqueue(urls.at(i)); } } void MediaObject::clearQueue() { P_D(MediaObject); d->sourceQueue.clear(); } -void MediaObjectPrivate::sendToZeitgeist(const QString &event_interpretation, - const QString &event_manifestation, - const QString &event_actor, - const QDateTime &subject_timestamp, - const QUrl &subject_uri, - const QString &subject_text, - const QString &subject_interpretation, - const QString &subject_manifestation, - const QString &subject_mimetype) -{ -#ifdef HAVE_QZEITGEIST - QZeitgeist::DataModel::Subject subject; - QString url = subject_uri.toString(); - QString path = url.left(url.lastIndexOf(QLatin1Char('/'))); - subject.setUri(url); - subject.setText(subject_text); - subject.setInterpretation(subject_interpretation); - subject.setManifestation(subject_manifestation); - subject.setOrigin(path); - subject.setMimeType(subject_mimetype); - - QZeitgeist::DataModel::SubjectList subjects; - subjects << subject; - - QZeitgeist::DataModel::Event event; - event.setTimestamp(subject_timestamp); - event.setInterpretation(event_interpretation); - event.setManifestation(event_manifestation); - event.setActor(event_actor); - event.setSubjects(subjects); - - QZeitgeist::DataModel::EventList events; - events << event; - - QDBusPendingReply reply = - log->insertEvents(events); -#else - Q_UNUSED(event_interpretation) - Q_UNUSED(event_manifestation) - Q_UNUSED(event_actor) - Q_UNUSED(subject_timestamp) - Q_UNUSED(subject_uri) - Q_UNUSED(subject_text) - Q_UNUSED(subject_interpretation) - Q_UNUSED(subject_manifestation) - Q_UNUSED(subject_mimetype) -#endif -} - -void MediaObjectPrivate::sendToZeitgeist(State eventState) -{ -#ifdef HAVE_QZEITGEIST - P_Q(MediaObject); - if (readyForZeitgeist && q->property("PlaybackTracking").toBool()) { - pDebug() << "Current state:" << eventState; - QString eventInterpretation; - switch (eventState) { - case PlayingState: - eventInterpretation = QZeitgeist::Interpretation::Event::ZGAccessEvent; - break; - case ErrorState: - case StoppedState: - eventInterpretation = QZeitgeist::Interpretation::Event::ZGLeaveEvent; - break; - //These states are not signifigant events. - case LoadingState: - case BufferingState: - case PausedState: - return; - break; - } - - QStringList titles = q->metaData(TitleMetaData); - QStringList artists = q->metaData(ArtistMetaData); - QString title; - if (titles.empty()) { - QString file = mediaSource.url().toString(); - title = file.right(file.length()-file.lastIndexOf("/")-1); - } else { - if (artists.empty()) { - title = titles[0]; - } else { - title = QString(QObject::tr("%0 by %1")).arg(titles[0]).arg(artists[0]); - } - } - pDebug() << "Sending" << title << "to zeitgeist"; - - QString mime; - QString subjectInterpretation; - if (q->hasVideo()) { - subjectInterpretation = QZeitgeist::Interpretation::Subject::NFOVideo; - mime = "video/raw"; - } else { - subjectInterpretation = QZeitgeist::Interpretation::Subject::NFOAudio; - mime = "audio/raw"; - } - pDebug() << "Zeitgeist mime type:" << mime; - pDebug() << "Zeitgeist URL:" << mediaSource.url(); - pDebug() << "mediasource type:" << mediaSource.type(); - - QString subjectType; - switch (mediaSource.type()) { - case MediaSource::Empty: - case MediaSource::Invalid: - return; - case MediaSource::Url: - subjectType = QZeitgeist::Manifestation::Subject::NFORemoteDataObject; - break; - case MediaSource::CaptureDevice: - case MediaSource::Disc: - case MediaSource::Stream: - subjectType = QZeitgeist::Manifestation::Subject::NFOMediaStream; - break; - case MediaSource::LocalFile: - subjectType = QZeitgeist::Manifestation::Subject::NFOFileDataObject; - break; - } - - QString eventManifestation; - if (playingQueuedSource) - eventManifestation = QZeitgeist::Manifestation::Event::ZGScheduledActivity; - else - eventManifestation = QZeitgeist::Manifestation::Event::ZGUserActivity; - - sendToZeitgeist(eventInterpretation, - eventManifestation, - QLatin1Literal("application://" ) % Platform::applicationName() % QLatin1Literal(".desktop"), - QDateTime::currentDateTime(), - mediaSource.url(), - title, - subjectInterpretation, - subjectType, - mime); - } - // Unset this so we don't send it again after a pause+play - readyForZeitgeist = false; - playingQueuedSource = false; -#else - Q_UNUSED(eventState) -#endif -} - -void MediaObjectPrivate::sendToZeitgeist() -{ - P_Q(MediaObject); - sendToZeitgeist(q->state()); -} - bool MediaObjectPrivate::aboutToDeleteBackendObject() { //pDebug() << Q_FUNC_INFO; prefinishMark = pINTERFACE_CALL(prefinishMark()); transitionTime = pINTERFACE_CALL(transitionTime()); //pDebug() << Q_FUNC_INFO; if (m_backendObject) { state = pINTERFACE_CALL(state()); currentTime = pINTERFACE_CALL(currentTime()); tickInterval = pINTERFACE_CALL(tickInterval()); } return true; } #ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM void MediaObjectPrivate::streamError(Phonon::ErrorType type, const QString &text) { P_Q(MediaObject); State lastState = q->state(); errorOverride = true; errorType = type; errorString = text; state = ErrorState; QMetaObject::invokeMethod(q, "stateChanged", Qt::QueuedConnection, Q_ARG(Phonon::State, Phonon::ErrorState), Q_ARG(Phonon::State, lastState)); //emit q->stateChanged(ErrorState, lastState); } #endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM // TODO: this needs serious cleanup... void MediaObjectPrivate::_k_stateChanged(Phonon::State newstate, Phonon::State oldstate) { P_Q(MediaObject); - // Zeitgeist --------------------------------------------------------------- - if (newstate == StoppedState) { - readyForZeitgeist = true; - } - pDebug() << "State changed from" << oldstate << "to" << newstate << "-> sending to zeitgeist."; - sendToZeitgeist(newstate); - // AbstractMediaStream fallback stuff -------------------------------------- if (errorOverride) { errorOverride = false; if (newstate == ErrorState) { return; } oldstate = ErrorState; } if (mediaSource.type() != MediaSource::Url) { // special handling only necessary for URLs because of the fallback emit q->stateChanged(newstate, oldstate); return; } // backend MediaObject reached ErrorState, try a KioMediaSource if (newstate == Phonon::ErrorState && !abstractStream) { abstractStream = Platform::createMediaStream(mediaSource.url(), q); if (!abstractStream) { pDebug() << "backend MediaObject reached ErrorState, no KIO fallback available"; emit q->stateChanged(newstate, oldstate); return; } pDebug() << "backend MediaObject reached ErrorState, trying Platform::createMediaStream now"; ignoreLoadingToBufferingStateChange = false; ignoreErrorToLoadingStateChange = false; switch (oldstate) { case Phonon::BufferingState: // play() has already been called, we need to make sure it is called // on the backend with the KioMediaStream MediaSource now, too ignoreLoadingToBufferingStateChange = true; break; case Phonon::LoadingState: ignoreErrorToLoadingStateChange = true; // no extras break; default: pError() << "backend MediaObject reached ErrorState after " << oldstate << ". It seems a KioMediaStream will not help here, trying anyway."; emit q->stateChanged(Phonon::LoadingState, oldstate); break; } abstractStream->d_func()->setMediaObjectPrivate(this); MediaSource mediaSource(abstractStream); mediaSource.setAutoDelete(true); - sendToZeitgeist(StoppedState); pINTERFACE_CALL(setSource(mediaSource)); - sendToZeitgeist(); if (oldstate == Phonon::BufferingState) { q->play(); } return; } else if (ignoreLoadingToBufferingStateChange && abstractStream && oldstate == Phonon::LoadingState) { if (newstate != Phonon::BufferingState) { emit q->stateChanged(newstate, Phonon::BufferingState); } return; } else if (ignoreErrorToLoadingStateChange && abstractStream && oldstate == ErrorState) { if (newstate != LoadingState) { emit q->stateChanged(newstate, Phonon::LoadingState); } return; } emit q->stateChanged(newstate, oldstate); } void MediaObjectPrivate::_k_aboutToFinish() { P_Q(MediaObject); pDebug() << Q_FUNC_INFO; #ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM abstractStream = 0; // abstractStream auto-deletes #endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM if (sourceQueue.isEmpty()) { emit q->aboutToFinish(); if (sourceQueue.isEmpty()) { return; } } mediaSource = sourceQueue.head(); - readyForZeitgeist = false; playingQueuedSource = true; pINTERFACE_CALL(setNextSource(mediaSource)); if (validator) validator->sourceQueued(); } void MediaObjectPrivate::_k_currentSourceChanged(const MediaSource &source) { P_Q(MediaObject); pDebug() << Q_FUNC_INFO; if (!sourceQueue.isEmpty() && sourceQueue.head() == source) sourceQueue.dequeue(); emit q->currentSourceChanged(source); } void MediaObjectPrivate::setupBackendObject() { P_Q(MediaObject); Q_ASSERT(m_backendObject); // Queue *everything* there is. That way the backend always is in a defined state. // If the signals were not queued, and the backend emitted something mid-execution // of whatever it is doing, an API consumer works with an undefined state. // This causes major headaches. If we must enforce implicit execution stop via // signals, they ought to be done in private slots. qRegisterMetaType("MediaSource"); qRegisterMetaType >("QMultiMap"); if (validateStates) validator = new StatesValidator(q); // Parented, and non-invasive to MO. #ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM QObject::connect(m_backendObject, SIGNAL(stateChanged(Phonon::State,Phonon::State)), q, SLOT(_k_stateChanged(Phonon::State,Phonon::State)), Qt::QueuedConnection); #else QObject::connect(m_backendObject, SIGNAL(stateChanged(Phonon::State,Phonon::State)), q, SIGNAL(stateChanged(Phonon::State,Phonon::State)), Qt::QueuedConnection); #endif // QT_NO_PHONON_ABSTRACTMEDIASTREAM #ifndef QT_NO_PHONON_VIDEO QObject::connect(m_backendObject, SIGNAL(hasVideoChanged(bool)), q, SIGNAL(hasVideoChanged(bool)), Qt::QueuedConnection); #endif //QT_NO_PHONON_VIDEO QObject::connect(m_backendObject, SIGNAL(tick(qint64)), q, SIGNAL(tick(qint64)), Qt::QueuedConnection); QObject::connect(m_backendObject, SIGNAL(seekableChanged(bool)), q, SIGNAL(seekableChanged(bool)), Qt::QueuedConnection); QObject::connect(m_backendObject, SIGNAL(bufferStatus(int)), q, SIGNAL(bufferStatus(int)), Qt::QueuedConnection); QObject::connect(m_backendObject, SIGNAL(finished()), q, SIGNAL(finished()), Qt::QueuedConnection); QObject::connect(m_backendObject, SIGNAL(aboutToFinish()), q, SLOT(_k_aboutToFinish()), Qt::QueuedConnection); QObject::connect(m_backendObject, SIGNAL(prefinishMarkReached(qint32)), q, SIGNAL(prefinishMarkReached(qint32)), Qt::QueuedConnection); QObject::connect(m_backendObject, SIGNAL(totalTimeChanged(qint64)), q, SIGNAL(totalTimeChanged(qint64)), Qt::QueuedConnection); QObject::connect(m_backendObject, SIGNAL(metaDataChanged(QMultiMap)), q, SLOT(_k_metaDataChanged(QMultiMap)), Qt::QueuedConnection); QObject::connect(m_backendObject, SIGNAL(currentSourceChanged(MediaSource)), q, SLOT(_k_currentSourceChanged(MediaSource)), Qt::QueuedConnection); // set up attributes pINTERFACE_CALL(setTickInterval(tickInterval)); pINTERFACE_CALL(setPrefinishMark(prefinishMark)); pINTERFACE_CALL(setTransitionTime(transitionTime)); switch(state) { case LoadingState: case StoppedState: case ErrorState: break; case PlayingState: case BufferingState: QTimer::singleShot(0, q, SLOT(_k_resumePlay())); break; case PausedState: QTimer::singleShot(0, q, SLOT(_k_resumePause())); break; } const State backendState = pINTERFACE_CALL(state()); if (state != backendState && state != ErrorState) { // careful: if state is ErrorState we might be switching from a // MediaObject to a ByteStream for KIO fallback. In that case the state // change to ErrorState was already suppressed. pDebug() << "emitting a state change because the backend object has been replaced"; emit q->stateChanged(backendState, state); state = backendState; } #ifndef QT_NO_PHONON_MEDIACONTROLLER for (int i = 0 ; i < interfaceList.count(); ++i) { interfaceList.at(i)->_backendObjectChanged(); } #endif //QT_NO_PHONON_MEDIACONTROLLER // set up attributes if (isPlayable(mediaSource.type())) { - readyForZeitgeist = false; #ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM if (mediaSource.type() == MediaSource::Stream) { Q_ASSERT(mediaSource.stream()); mediaSource.stream()->d_func()->setMediaObjectPrivate(this); } #endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM - sendToZeitgeist(StoppedState); pINTERFACE_CALL(setSource(mediaSource)); - sendToZeitgeist(); } } void MediaObjectPrivate::_k_resumePlay() { qobject_cast(m_backendObject)->play(); if (currentTime > 0) { qobject_cast(m_backendObject)->seek(currentTime); } } void MediaObjectPrivate::_k_resumePause() { pINTERFACE_CALL(pause()); if (currentTime > 0) { qobject_cast(m_backendObject)->seek(currentTime); } } void MediaObjectPrivate::_k_metaDataChanged(const QMultiMap &newMetaData) { metaData = newMetaData; emit q_func()->metaDataChanged(); - pDebug() << "Metadata ready, sending to zeitgeist"; - readyForZeitgeist = true; - sendToZeitgeist(); } void MediaObjectPrivate::phononObjectDestroyed(MediaNodePrivate *bp) { // this method is called from Phonon::Base::~Base(), meaning the AudioPath // dtor has already been called, also virtual functions don't work anymore // (therefore qobject_cast can only downcast from Base) Q_ASSERT(bp); Q_UNUSED(bp); } MediaObject *createPlayer(Phonon::Category category, const MediaSource &source) { MediaObject *mo = new MediaObject; AudioOutput *ao = new AudioOutput(category, mo); createPath(mo, ao); if (isPlayable(source.type())) { mo->setCurrentSource(source); } return mo; } } //namespace Phonon #include "moc_mediaobject.cpp" #undef PHONON_CLASSNAME #undef PHONON_INTERFACENAME // vim: sw=4 tw=100 et diff --git a/phonon/mediaobject.h b/phonon/mediaobject.h index ab29fdc3..cb559475 100644 --- a/phonon/mediaobject.h +++ b/phonon/mediaobject.h @@ -1,666 +1,663 @@ /* This file is part of the KDE project Copyright (C) 2005 Matthias Kretz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), Nokia Corporation (or its successors, if any) and the KDE Free Qt Foundation, which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef Phonon_MEDIAOBJECT_H #define Phonon_MEDIAOBJECT_H #include "medianode.h" #include "mediasource.h" #include "phonon_export.h" #include "phonondefs.h" #include "phononnamespace.h" namespace Phonon { class MediaObjectPrivate; /** \class MediaObject mediaobject.h phonon/MediaObject * \short Interface for media playback of a given URL. * * This class is the most important class in %Phonon. Use it to open a media * file at an arbitrary location, a CD or DVD or to stream media data from * the application to the backend. * * This class controls the state (play, pause, stop, seek) * and you can use it to get a lot of information about the media data. * * Notice that most functions of this class are asynchronous. * That means if you call play() the object only starts playing when the * stateChanged() signal tells you that the object changed into PlayingState. * The states you can expect are documented for those methods. * * A common usage example is the following: * \code * media = new MediaObject(this); * connect(media, SIGNAL(finished()), SLOT(slotFinished()); * media->setCurrentSource("/home/username/music/filename.ogg"); * media->play(); * \endcode * * If you want to play more than one media file (one after another) you can * either tell MediaObject about all those files * \code * media->setCurrentSource(":/sounds/startsound.ogg"); * media->enqueue("/home/username/music/song.mp3"); * media->enqueue(":/sounds/endsound.ogg"); * \endcode * or provide the next file just in time: * \code * media->setCurrentSource(":/sounds/startsound.ogg"); * connect(media, SIGNAL(aboutToFinish()), SLOT(enqueueNextSource())); * } * * void enqueueNextSource() * { * media->enqueue("/home/username/music/song.mp3"); * } * \endcode * - * Some platforms support system-wide tracking of a user's activities. For - * instance, the zeitgeist project (http://zeitgeist-project.com) on Linux. - * * This integration is opt-in only and can be enabled by setting the * PlaybackTracking property to true: * \code * media->setProperty("PlaybackTracking", true); * \endcode * * This kind of information is normally used to provide a universal history * view to the user, such as what songs were played when, regardless of the * media player. This is in addition to any emails read, IM conversations, * websites viewed, etc. * * \ingroup Playback * \ingroup Recording * \author Matthias Kretz */ class PHONON_EXPORT MediaObject : public QObject, public MediaNode { friend class FrontendInterfacePrivate; Q_OBJECT P_DECLARE_PRIVATE(MediaObject) PHONON_OBJECT(MediaObject) /** * \brief Defines the time between media sources. * * A positive transition time defines a gap of silence between queued * media sources. * * A transition time of 0 ms requests gapless playback (sample precise * queueing of the next source). * * A negative transition time defines a crossfade between the queued * media sources. * * Defaults to 0 (gapless playback). * * \warning This feature might not work reliably on every platform. */ Q_PROPERTY(qint32 transitionTime READ transitionTime WRITE setTransitionTime) /** * \brief Get a signal before playback finishes. * * This property specifies the time in milliseconds the * prefinishMarkReached signal is * emitted before the playback finishes. A value of \c 0 disables the * signal. * * Defaults to \c 0 (disabled). * * \warning For some media data the total time cannot be determined * accurately, therefore the accuracy of the prefinishMarkReached signal * can be bad sometimes. Still, it is better to use this method than to * look at totalTime() and currentTime() to emulate the behaviour * because the backend might have more information available than your * application does through totalTime and currentTime. * * \see prefinishMarkReached */ Q_PROPERTY(qint32 prefinishMark READ prefinishMark WRITE setPrefinishMark) /** * \brief The time interval in milliseconds between two ticks. * * The %tick interval is the time that elapses between the emission of two tick signals. * If you set the interval to \c 0 the tick signal gets disabled. * * Defaults to \c 0 (disabled). * * \warning The back-end is free to choose a different tick interval close * to what you asked for. This means that the following code \em may \em fail: * \code * int x = 200; * media->setTickInterval(x); * Q_ASSERT(x == producer->tickInterval()); * \endcode * On the other hand the following is guaranteed: * \code * int x = 200; * media->setTickInterval(x); * Q_ASSERT(x >= producer->tickInterval() && * x <= 2 * producer->tickInterval()); * \endcode * * \see tick */ Q_PROPERTY(qint32 tickInterval READ tickInterval WRITE setTickInterval) public: /** * Destroys the MediaObject. */ ~MediaObject(); /** * Get the current state. * * @return The state of the object. * * @see State * \see stateChanged */ State state() const; /** * Check whether the media data includes a video stream. * * \warning This information cannot be known immediately. It is best * to also listen to the hasVideoChanged signal. * * \code * connect(media, SIGNAL(hasVideoChanged(bool)), hasVideoChanged(bool)); * media->setCurrentSource("somevideo.avi"); * media->hasVideo(); // returns false; * } * * void hasVideoChanged(bool b) * { * // b == true * media->hasVideo(); // returns true; * } * \endcode * * \return \c true if the media contains video data. \c false * otherwise. * * \see hasVideoChanged */ bool hasVideo() const; /** * Check whether the current media may be seeked. * * \warning This information cannot be known immediately. It is best * to also listen to the seekableChanged signal. * * \code * connect(media, SIGNAL(seekableChanged(bool)), seekableChanged(bool)); * media->setCurrentSource("somevideo.avi"); * media->isSeekable(); // returns false; * } * * void seekableChanged(bool b) * { * // b == true * media->isSeekable(); // returns true; * } * \endcode * * \return \c true when the current media may be seeked. \c false * otherwise. * * \see seekableChanged() */ bool isSeekable() const; /** * \brief The time interval in milliseconds between two ticks. * * The %tick interval is the time that elapses between the emission * of two tick signals. * * \returns the tick interval in milliseconds */ qint32 tickInterval() const; /** * Returns the strings associated with the given \p key. * * Backends should use the keys specified in the Ogg Vorbis * documentation: http://xiph.org/vorbis/doc/v-comment.html * * Therefore the following should work with every backend: * * A typical usage looks like this: * \code * setMetaArtist (media->metaData("ARTIST" )); * setMetaAlbum (media->metaData("ALBUM" )); * setMetaTitle (media->metaData("TITLE" )); * setMetaDate (media->metaData("DATE" )); * setMetaGenre (media->metaData("GENRE" )); * setMetaTrack (media->metaData("TRACKNUMBER")); * setMetaComment(media->metaData("DESCRIPTION")); * \endcode * * For Audio CDs you can query * \code * metaData("MUSICBRAINZ_DISCID"); * \endcode * to get a DiscID hash that you can use with the MusicBrainz * service: * http://musicbrainz.org/doc/ClientHOWTO */ QStringList metaData(const QString &key) const; /** * Returns the strings associated with the given \p key. * * Same as above except that the keys are defined in the * Phonon::MetaData enum. */ QStringList metaData(Phonon::MetaData key) const; /** * Returns all meta data. */ QMultiMap metaData() const; /** * Returns a human-readable description of the last error that occurred. */ QString errorString() const; /** * Tells your program what to do about the error. * * \see Phonon::ErrorType */ ErrorType errorType() const; /** * Returns the current media source. * * \see setCurrentSource */ MediaSource currentSource() const; /** * Set the media source the MediaObject should use. * * \param source The MediaSource object to the media data. You can * just as well use a QUrl or QString (for a local file) here. * Setting an empty (invalid) source, will stop and remove the * current source. * * \code * QUrl url("http://www.example.com/music.ogg"); * media->setCurrentSource(url); * \endcode * * \see currentSource */ void setCurrentSource(const MediaSource &source); /** * Returns the queued media sources. This list does not include * the current source (returned by currentSource). */ QList queue() const; /** * Set the MediaSources to play when the current media has finished. * * This function will overwrite the current queue. * * \see clearQueue * \see enqueue */ void setQueue(const QList &sources); /** * Set the MediaSources to play when the current media has finished. * * This function overwrites the current queue. * * \see clearQueue * \see enqueue */ void setQueue(const QList &urls); /** * Appends one source to the queue. Use this function to provide * the next source just in time after the aboutToFinish signal was * emitted. * * \see aboutToFinish * \see setQueue * \see clearQueue */ void enqueue(const MediaSource &source); /** * Appends multiple sources to the queue. * * \see setQueue * \see clearQueue */ void enqueue(const QList &sources); /** * Appends multiple sources to the queue. * * \see setQueue * \see clearQueue */ void enqueue(const QList &urls); /** * Clears the queue of sources. */ void clearQueue(); /** * Get the current time (in milliseconds) of the file currently being played. * * \return The current time in milliseconds. * * \see tick */ qint64 currentTime() const; /** * Get the total time (in milliseconds) of the file currently being played. * * \return The total time in milliseconds. * * \note The total time may change throughout playback as more accurate * calculations become available, so it is recommended to connect * and use the totalTimeChanged signal whenever possible unless * best precision is not of importance. * * \warning The total time is undefined until the MediaObject entered * the PlayingState. A valid total time is always indicated by * emission of the totalTimeChanged signal. * \see totalTimeChanged */ qint64 totalTime() const; /** * Get the remaining time (in milliseconds) of the file currently being played. * * \return The remaining time in milliseconds. */ qint64 remainingTime() const; qint32 prefinishMark() const; void setPrefinishMark(qint32 msecToEnd); qint32 transitionTime() const; void setTransitionTime(qint32 msec); public Q_SLOTS: /** * Sets the tick interval in milliseconds. * * \param newTickInterval the new tick interval in milliseconds. * * \see tickInterval */ void setTickInterval(qint32 newTickInterval); /** * Requests playback of the media data to start. Playback only * starts when stateChanged() signals that it goes into PlayingState, * though. * * \par Possible states right after this call: * \li BufferingState * \li PlayingState * \li ErrorState */ void play(); /** * Requests playback to pause. If it was paused before nothing changes. * If the media cannot be paused, some backends will internally call * stop instead of pause. * * \par Possible states right after this call: * \li PlayingState * \li PausedState * \li StoppedState * \li ErrorState */ void pause(); /** * Requests playback to stop. If it was stopped before nothing changes. * * \par Possible states right after this call: * \li the state it was in before (e.g. PlayingState) * \li StoppedState * \li ErrorState */ void stop(); /** * Requests a seek to the time indicated. * * You can only seek if state() == PlayingState, BufferingState or PausedState. * * The call is asynchronous, so currentTime can still be the old * value right after this method was called. If all you need is a * slider that shows the current position and allows the user to * seek use the class SeekSlider. * * @param time The time in milliseconds where to continue playing. * * \par Possible states right after this call: * \li BufferingState * \li PlayingState * \li ErrorState * * \see SeekSlider */ void seek(qint64 time); /** * Stops and removes all playing and enqueued media sources. * * \see setCurrentSource */ void clear(); Q_SIGNALS: /** * Emitted when the state of the MediaObject has changed. * * @param newstate The state the Player is in now. * @param oldstate The state the Player was in before. */ void stateChanged(Phonon::State newstate, Phonon::State oldstate); /** * This signal gets emitted every tickInterval milliseconds. * * @param time The position of the media file in milliseconds. * * @see setTickInterval, tickInterval */ void tick(qint64 time); /** * This signal is emitted whenever the audio/video data that is * being played is associated with new meta data. E.g. for radio * streams this happens when the next song is played. * * You can get the new meta data with the metaData methods. */ void metaDataChanged(); /** * Emitted whenever the return value of isSeekable() changes. * * Normally you'll check isSeekable() first and then let this signal * tell you whether seeking is possible now or not. That way you * don't have to poll isSeekable(). * * \param isSeekable \p true if the stream is seekable (i.e. calling * seek() works) * \p false if the stream is not seekable (i.e. * all calls to seek() will be ignored) */ void seekableChanged(bool isSeekable); /** * Emitted whenever the return value of hasVideo() changes. * * Normally you'll check hasVideo() first and then let this signal * tell you whether video is available now or not. That way you * don't have to poll hasVideo(). * * \param hasVideo \p true The stream contains video and adding a * VideoWidget will show a video. * \p false There is no video data in the stream and * adding a VideoWidget will show an empty (black) * VideoWidget. */ #ifndef QT_NO_PHONON_VIDEO void hasVideoChanged(bool hasVideo); #endif //QT_NO_PHONON_VIDEO /** * Tells about the status of the buffer. * * You can use this signal to show a progress bar to the user when * in BufferingState: * * \code * progressBar->setRange(0, 100); // this is the default * connect(media, SIGNAL(bufferStatus(int)), progressBar, SLOT(setValue(int))); * \endcode * * \param percentFilled A number between 0 and 100 telling you how * much the buffer is filled. */ // other names: bufferingProgress void bufferStatus(int percentFilled); /** * Emitted when the object has finished playback. * It is not emitted if you call stop(), pause() or * load(), but only on end-of-queue or a critical error. * * \warning This signal is not emitted when the current source has * finished and there's another source in the queue. It is only * emitted when the queue is empty. * * \see currentSourceChanged * \see aboutToFinish * \see prefinishMarkReached */ void finished(); /** * Emitted when the MediaObject makes a transition to the next * MediaSource in the queue(). * * In other words, it is emitted when an individual MediaSource is * finished. * * \param newSource The source that starts to play at the time the * signal is emitted. */ void currentSourceChanged(const Phonon::MediaSource &newSource); /** * Emitted before the playback of the whole queue stops. When this * signal is emitted you still have time to provide the next * MediaSource (using enqueue()) so that playback continues. * * This signal can be used to provide the next MediaSource just in * time for the transition still to work. * * \see enqueue */ void aboutToFinish(); /** * Emitted when there are only \p msecToEnd milliseconds left * for playback. * * \param msecToEnd The remaining time until the playback queue finishes. * * \warning This signal is not emitted when there is another source in the queue. * It is only emitted when the queue is empty. * * \see setPrefinishMark * \see prefinishMark * \see aboutToFinish * \see finished */ void prefinishMarkReached(qint32 msecToEnd); /** * This signal is emitted as soon as the total time of the media file is * known or has changed. For most non-local media data the total * time of the media can only be known after some time. Initially the * totalTime function can not return useful information. You have * to wait for this signal to know the real total time. * * This signal may appear at any given point after a MediaSource was set. * Namely in the LoadingState, BufferingState, PlayingState or PausedState. * * \note When changing the currentSource there is no signal emission until * a reasonable value for the new source has been calculated. * * \param newTotalTime The length of the media file in milliseconds. * * \see totalTime */ void totalTimeChanged(qint64 newTotalTime); protected: //MediaObject(Phonon::MediaObjectPrivate &dd, QObject *parent); private: Q_PRIVATE_SLOT(k_func(), void _k_resumePlay()) Q_PRIVATE_SLOT(k_func(), void _k_resumePause()) Q_PRIVATE_SLOT(k_func(), void _k_metaDataChanged(const QMultiMap &)) #ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM Q_PRIVATE_SLOT(k_func(), void _k_stateChanged(Phonon::State, Phonon::State)) #endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM Q_PRIVATE_SLOT(k_func(), void _k_aboutToFinish()) Q_PRIVATE_SLOT(k_func(), void _k_currentSourceChanged(const MediaSource &)) Q_PRIVATE_SLOT(k_func(), void _k_stateChanged(Phonon::State, Phonon::State)) }; /** * Convenience function to create a MediaObject and AudioOutput connected by * a path. */ PHONON_EXPORT MediaObject *createPlayer(Phonon::Category category, const MediaSource &source = MediaSource()); } //namespace Phonon // vim: sw=4 ts=4 tw=80 #endif // Phonon_MEDIAOBJECT_H diff --git a/phonon/mediaobject_p.h b/phonon/mediaobject_p.h index e6a8ce58..0bae791b 100644 --- a/phonon/mediaobject_p.h +++ b/phonon/mediaobject_p.h @@ -1,162 +1,121 @@ /* This file is part of the KDE project Copyright (C) 2006-2007 Matthias Kretz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), Nokia Corporation (or its successors, if any) and the KDE Free Qt Foundation, which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef MEDIAOBJECT_P_H #define MEDIAOBJECT_P_H #include #include -#ifdef HAVE_QZEITGEIST -#include -#include -#endif #include "medianode_p.h" #include "medianodedestructionhandler_p.h" #include "mediaobject.h" #include "mediasource.h" #include "phonondefs_p.h" namespace Phonon { class FrontendInterfacePrivate; class StatesValidator; class MediaObjectPrivate : public MediaNodePrivate, private MediaNodeDestructionHandler { friend class KioFallbackImpl; friend class AbstractMediaStream; friend class AbstractMediaStreamPrivate; P_DECLARE_PUBLIC(MediaObject) public: virtual QObject *qObject() override { return q_func(); } - /** - * Sends the metadata for this media file to the Zeitgeist tracker - * - * \param eventInterpretation The interpretation of the event - * \param eventManifestation The manifestation type of the event - * \param eventActor The application or entity responsible for emitting the zeitgeist event - * \param eventTimestamp The time - * \param subjectURI The file's URI - * \param subjectText A free-form annotation - * \param subjectInterpretation The interpretation type - * \param subjectManifestation The manifestation type - * \param subjectMimetype The file's mimetype - */ - void sendToZeitgeist(const QString &event_interpretation, - const QString &event_manifestation, - const QString &event_actor, - const QDateTime &subject_timestamp, - const QUrl &subject_uri, - const QString &subject_text, - const QString &subject_interpretation, - const QString &subject_manifestation, - const QString &subject_mimetype); - - void sendToZeitgeist(State); - void sendToZeitgeist(); - QList interfaceList; protected: virtual bool aboutToDeleteBackendObject() override; virtual void createBackendObject() override; virtual void phononObjectDestroyed(MediaNodePrivate *) override; PHONON_EXPORT void setupBackendObject(); void _k_resumePlay(); void _k_resumePause(); void _k_metaDataChanged(const QMultiMap &); void _k_aboutToFinish(); void _k_currentSourceChanged(const MediaSource &); PHONON_EXPORT void _k_stateChanged(Phonon::State, Phonon::State); #ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM void streamError(Phonon::ErrorType, const QString &); #endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM MediaObjectPrivate() : currentTime(0), tickInterval(0), metaData(), errorString(), prefinishMark(0), transitionTime(0), // gapless playback #ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM abstractStream(0), #endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM state(Phonon::LoadingState), - readyForZeitgeist(false), playingQueuedSource(false) #ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM , errorType(Phonon::NormalError), errorOverride(false), ignoreLoadingToBufferingStateChange(false), ignoreErrorToLoadingStateChange(false), validateStates(!(qgetenv("PHONON_ASSERT_STATES").isEmpty())), validator(0) #endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM { -#ifdef HAVE_QZEITGEIST - log = new QZeitgeist::Log(); -#endif } ~MediaObjectPrivate() { -#ifdef HAVE_QZEITGEIST - delete log; -#endif } qint64 currentTime; qint32 tickInterval; QMultiMap metaData; QString errorString; qint32 prefinishMark; qint32 transitionTime; #ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM AbstractMediaStream *abstractStream; #endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM State state #ifdef QT_NO_PHONON_ABSTRACTMEDIASTREAM ; #else : 8; - bool readyForZeitgeist; bool playingQueuedSource; ErrorType errorType : 4; bool errorOverride : 1; bool ignoreLoadingToBufferingStateChange : 1; bool ignoreErrorToLoadingStateChange : 1; #endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM MediaSource mediaSource; QQueue sourceQueue; -#ifdef HAVE_QZEITGEIST - QZeitgeist::Log *log; -#endif bool validateStates; StatesValidator *validator; }; } #endif // MEDIAOBJECT_P_H // vim: sw=4 ts=4 tw=80 diff --git a/phonon/objectdescription.h b/phonon/objectdescription.h index 770e1767..cb000c35 100644 --- a/phonon/objectdescription.h +++ b/phonon/objectdescription.h @@ -1,395 +1,391 @@ /* Copyright (C) 2006-2007 Matthias Kretz Copyright (C) 2011 Harald Sitter This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), Nokia Corporation (or its successors, if any) and the KDE Free Qt Foundation, which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef PHONON_OBJECTDESCRIPTION_H #define PHONON_OBJECTDESCRIPTION_H #include "phonon_export.h" #include #include #include #include #include #include namespace Phonon { class ObjectDescriptionPrivate; /** * Defines the type of information that is contained in a ObjectDescription * object. * * \ingroup Backend */ enum ObjectDescriptionType { /** * Audio output devices. This can be soundcards (with different drivers), soundservers or * other virtual outputs like playback on a different computer on the * network. * * For Hardware devices the backend should use libkaudiodevicelist * (AudioDevice and AudioDeviceEnumerator) which will list removable * devices even when they are unplugged and provide a unique identifier * that can make backends use the same identifiers. */ AudioOutputDeviceType, /** * Lists all processing effects the backend supports. */ EffectType, AudioChannelType, SubtitleType, /** * Audio capture devices. This can be soundcards (with different drivers), soundservers or * other virtual inputs like capture on a different computer on the * network. * * For Hardware devices the backend should use libkaudiodevicelist * (AudioDevice and AudioDeviceEnumerator) which will list removable * devices even when they are unplugged and provide a unique identifier * that can make backends use the same identifiers. */ AudioCaptureDeviceType, /** * Video capture devices. Includes webcams. */ VideoCaptureDeviceType //VideoOutputDeviceType, //AudioCodecType, //VideoCodecType, //ContainerFormatType, //VisualizationType, }; /** \internal * \class ObjectDescriptionData objectdescription.h phonon/ObjectDescription * \brief Data class for objects describing devices or features of the backend. * * \author Matthias Kretz * \see BackendCapabilities */ class PHONON_EXPORT ObjectDescriptionData : public QSharedData //krazy:exclude=dpointer (it's protected, which should be fine for this type of class) { public: /** * Returns \c true if this ObjectDescription describes the same * as \p otherDescription; otherwise returns \c false. */ bool operator==(const ObjectDescriptionData &otherDescription) const; /** * Returns the name of the capture source. * * \return A string that should be presented to the user to * choose the capture source. */ QString name() const; /** * Returns a description of the capture source. This text should * make clear what sound source this is, which is sometimes hard * to describe or understand from just the name. * * \return A string describing the capture source. */ QString description() const; /** * Returns a named property. * * If the property is not set an invalid value is returned. * * \see propertyNames() */ QVariant property(const char *name) const; /** * Returns all names that return valid data when property() is called. * * \see property() */ QList propertyNames() const; /** * Returns \c true if the Tuple is valid (index != -1); otherwise returns * \c false. */ bool isValid() const; /** * A unique identifier for this device/. Used internally * to distinguish between the devices/. * * \return An integer that uniquely identifies every device/ */ int index() const; static ObjectDescriptionData *fromIndex(ObjectDescriptionType type, int index); ~ObjectDescriptionData(); ObjectDescriptionData(ObjectDescriptionPrivate * = 0); ObjectDescriptionData(int index, const QHash &properties); protected: ObjectDescriptionPrivate *const d; private: ObjectDescriptionData &operator=(const ObjectDescriptionData &rhs); }; template class ObjectDescriptionModel; /** \class ObjectDescription objectdescription.h phonon/ObjectDescription * \short Provides a tuple of enduser visible name and description. * * Some parts give the enduser choices, e.g. what source to capture audio from. * These choices are described by the name and description methods of this class * and identified with the id method. Subclasses then define additional * information like which audio and video choices belong together. * * \ingroup Frontend * \author Matthias Kretz */ template class ObjectDescription { public: /** * Returns a new description object that describes the * device/effect/codec/... with the given \p index. */ static inline ObjectDescription fromIndex(int index) { //krazy:exclude=inline return ObjectDescription(QExplicitlySharedDataPointer(ObjectDescriptionData::fromIndex(T, index))); } /** * Returns \c true if this ObjectDescription describes the same * as \p otherDescription; otherwise returns \c false. */ inline bool operator==(const ObjectDescription &otherDescription) const { //krazy:exclude=inline return *d == *otherDescription.d; } /** * Returns \c false if this ObjectDescription describes the same * as \p otherDescription; otherwise returns \c true. */ inline bool operator!=(const ObjectDescription &otherDescription) const { //krazy:exclude=inline return !operator==(otherDescription); } /** * Returns the name of the capture source. * * \return A string that should be presented to the user to * choose the capture source. */ inline QString name() const { return d->name(); } //krazy:exclude=inline /** * Returns a description of the capture source. This text should * make clear what sound source this is, which is sometimes hard * to describe or understand from just the name. * * \return A string describing the capture source. */ inline QString description() const { return d->description(); } //krazy:exclude=inline /** * Returns a named property. * * If the property is not set an invalid value is returned. * * \see propertyNames() */ inline QVariant property(const char *name) const { return d->property(name); } //krazy:exclude=inline /** * Returns all names that return valid data when property() is called. * * \see property() */ inline QList propertyNames() const { return d->propertyNames(); } //krazy:exclude=inline /** * Returns \c true if the Tuple is valid (index != -1); otherwise returns * \c false. */ inline bool isValid() const { return d->isValid(); } //krazy:exclude=inline /** * A unique identifier for this device/. Used internally * to distinguish between the devices/. * * \return An integer that uniquely identifies every device/ */ inline int index() const { return d->index(); } //krazy:exclude=inline ObjectDescription() : d(new ObjectDescriptionData(0)) {} ObjectDescription(int index, const QHash &properties) : d(new ObjectDescriptionData(index, properties)) {} protected: friend class ObjectDescriptionModel; ObjectDescription(const QExplicitlySharedDataPointer &dd) : d(dd) {} QExplicitlySharedDataPointer d; }; template QDebug operator<<(QDebug dbg, const ObjectDescription &d) { dbg.nospace() << "\n{\n"; dbg.nospace() << " index: " << d.index() << "\n"; -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) const QList propertyNames = d.propertyNames(); for (const QByteArray &propertyName : propertyNames) { -#else - Q_FOREACH (const QByteArray &propertyName, d.propertyNames()) { -#endif dbg.nospace() << " " << propertyName << ": " << d.property(propertyName).toString() << "\n"; } dbg.nospace() << "}\n"; return dbg.space(); } /** * \ingroup BackendInformation */ typedef ObjectDescription AudioOutputDevice; /** * \ingroup BackendInformation */ #ifndef PHONON_NO_AUDIOCAPTURE typedef ObjectDescription AudioCaptureDevice; #endif //PHONON_NO_AUDIOCAPTURE /** * \ingroup BackendInformation */ //typedef ObjectDescription VideoOutputDevice; /** * \ingroup BackendInformation */ #ifndef PHONON_NO_VIDEOCAPTURE typedef ObjectDescription VideoCaptureDevice; #endif /** * \ingroup BackendInformation */ #ifndef QT_NO_PHONON_EFFECT typedef ObjectDescription EffectDescription; #endif //QT_NO_PHONON_EFFECT /** * \ingroup BackendInformation */ //typedef ObjectDescription AudioCodecDescription; /** * \ingroup BackendInformation */ //typedef ObjectDescription VideoCodecDescription; /** * \ingroup BackendInformation */ //typedef ObjectDescription ContainerFormatDescription; /** * \ingroup BackendInformation */ //typedef ObjectDescription VisualizationDescription; #ifndef QT_NO_PHONON_MEDIACONTROLLER typedef ObjectDescription AudioChannelDescription; typedef ObjectDescription SubtitleDescription; #endif //QT_NO_PHONON_MEDIACONTROLLER /** * \short Information about how to access a device * \ingroup BackendInformation * * To access a device, one needs the driver name (alsa, oss, pulse for example), * and the device name (dependent on the driver name). This type is a pair of a * driver and a device name. * * \see DeviceAccessList */ typedef QPair DeviceAccess; /** * \short Information about methods for accessing a device * \ingroup BackendInformation * * It is used by the platform plugin or the backend to provide information about how * to access a certain device. To access a device, one needs the driver name (alsa, oss, * pulse for example), and the device name (dependent on the driver name). This type * is essentialy a list of pairs of driver and device names. * * It can be put in an ObjectDescriptionData property list. * * \see DeviceAccess * \see AudioCaptureDevice */ typedef QList DeviceAccessList; void PHONON_EXPORT_DEPRECATED registerMetaTypes(); } //namespace Phonon Q_DECLARE_METATYPE(Phonon::AudioOutputDevice) Q_DECLARE_METATYPE(QList) #ifndef PHONON_NO_AUDIOCAPTURE Q_DECLARE_METATYPE(Phonon::AudioCaptureDevice) Q_DECLARE_METATYPE(QList) #endif //PHONON_NO_AUDIOCAPTURE #ifndef PHONON_NO_VIDEOCAPTURE Q_DECLARE_METATYPE(Phonon::VideoCaptureDevice) Q_DECLARE_METATYPE(QList) #endif //PHONON_NO_VIDEOCAPTURE #ifndef QT_NO_PHONON_EFFECT Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(Phonon::EffectDescription) #endif //QT_NO_PHONON_EFFECT #ifndef QT_NO_PHONON_MEDIACONTROLLER Q_DECLARE_METATYPE(Phonon::AudioChannelDescription) Q_DECLARE_METATYPE(Phonon::SubtitleDescription) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(QList) #endif //QT_NO_PHONON_MEDIACONTROLLER Q_DECLARE_METATYPE(Phonon::DeviceAccess) Q_DECLARE_METATYPE(Phonon::DeviceAccessList) #endif // PHONON_OBJECTDESCRIPTION_H diff --git a/phonon/objectdescriptionmodel.cpp b/phonon/objectdescriptionmodel.cpp index 266e93b7..7f874a6e 100644 --- a/phonon/objectdescriptionmodel.cpp +++ b/phonon/objectdescriptionmodel.cpp @@ -1,426 +1,392 @@ /* This file is part of the KDE project Copyright (C) 2006-2007 Matthias Kretz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), Nokia Corporation (or its successors, if any) and the KDE Free Qt Foundation, which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "objectdescriptionmodel.h" #include "objectdescriptionmodel_p.h" #include "phonondefs_p.h" #include "platform_p.h" #include #include "objectdescription.h" #include "phononnamespace_p.h" #include #include #include #include #include "factory_p.h" #ifndef QT_NO_PHONON_OBJECTDESCRIPTIONMODEL // If this wasn't so terrible ... // ObjectDescriptionModel is a template class. Moc however cannot handle // templates so the solution done here is to *manually* do whatever moc does. -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) // Qt 5 static const uint qt_meta_data_Phonon__ObjectDescriptionModel[] = { // content: 7, // revision 0, // classname 0, 0, // classinfo 0, 0, // methods 0, 0, // properties 0, 0, // enums/sets 0, 0, // constructors 0, // flags 0, // signalCount 0 // eod }; -#else // Qt 4 -static const uint qt_meta_data_Phonon__ObjectDescriptionModel[] = { - - // content: - 1, // revision - 0, // classname - 0, 0, // classinfo - 0, 0, // methods - 0, 0, // properties - 0, 0, // enums/sets - - 0 // eod -}; -#endif -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) // Qt 5 #define P_STATIC_META_STRINGDATA(name, string, stringlen, stringlenplustwo) \ struct qt_meta_stringdata_Phonon__ObjectDescriptionModel_##name##_t { QByteArrayData data[1]; char stringdata[stringlenplustwo]; }; \ static const qt_meta_stringdata_Phonon__ObjectDescriptionModel_##name##_t qt_meta_stringdata_Phonon__ObjectDescriptionModel_##name = { \ { \ Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(stringlen, offsetof(qt_meta_stringdata_Phonon__ObjectDescriptionModel_ ## name ## _t, stringdata) + 0 - 0 * sizeof(QByteArrayData)) \ }, \ string \ }; P_STATIC_META_STRINGDATA(AudioOutputDeviceType, "Phonon::AudioOutputDeviceModel\0", 30, 32) P_STATIC_META_STRINGDATA(AudioCaptureDeviceType, "Phonon::AudioCaptureDeviceModel\0", 31, 33) P_STATIC_META_STRINGDATA(VideoCaptureDeviceType, "Phonon::VideoCaptureDeviceModel\0", 31, 33) P_STATIC_META_STRINGDATA(EffectType, "Phonon::EffectModel\0", 19, 21) P_STATIC_META_STRINGDATA(AudioChannelType, "Phonon::AudioChannelModel\0", 25, 27) P_STATIC_META_STRINGDATA(SubtitleType, "Phonon::SubtitleModel\0", 21, 23) #undef P_STATIC_META_STRINGDATA -#else // Qt 4 - static const char qt_meta_stringdata_Phonon__ObjectDescriptionModel_AudioOutputDeviceType[] = { "Phonon::AudioOutputDevice\0" }; - static const char qt_meta_stringdata_Phonon__ObjectDescriptionModel_AudioCaptureDeviceType[] = { "Phonon::AudioCaptureDevice\0" }; - static const char qt_meta_stringdata_Phonon__ObjectDescriptionModel_VideoCaptureDeviceType[] = { "Phonon::VideoCaptureDevice\0" }; - static const char qt_meta_stringdata_Phonon__ObjectDescriptionModel_EffectType[] = { "Phonon::EffectDescription\0" }; - static const char qt_meta_stringdata_Phonon__ObjectDescriptionModel_AudioChannelType[] = { "Phonon::AudioChannelDescription\0" }; - static const char qt_meta_stringdata_Phonon__ObjectDescriptionModel_SubtitleType[] = { "Phonon::SubtitleDescription\0" }; -#endif -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) // Qt 5 #define OBJECT_DESCRIPTION_MODEL_STATIC_META_OBJECT(X) { \ &QAbstractListModel::staticMetaObject, \ qt_meta_stringdata_Phonon__ObjectDescriptionModel_##X.data, \ qt_meta_data_Phonon__ObjectDescriptionModel, \ 0, 0, 0 } -#else // Qt 4 - #define OBJECT_DESCRIPTION_MODEL_STATIC_META_OBJECT(X) { \ - &QAbstractListModel::staticMetaObject, \ - qt_meta_stringdata_Phonon__ObjectDescriptionModel_ ## X, \ - qt_meta_data_Phonon__ObjectDescriptionModel, \ - 0 } -#endif namespace Phonon { template<> const QMetaObject ObjectDescriptionModel::staticMetaObject = { OBJECT_DESCRIPTION_MODEL_STATIC_META_OBJECT(AudioOutputDeviceType) }; template<> const QMetaObject ObjectDescriptionModel::staticMetaObject = { OBJECT_DESCRIPTION_MODEL_STATIC_META_OBJECT(AudioCaptureDeviceType) }; template<> const QMetaObject ObjectDescriptionModel::staticMetaObject = { OBJECT_DESCRIPTION_MODEL_STATIC_META_OBJECT(VideoCaptureDeviceType) }; template<> const QMetaObject ObjectDescriptionModel::staticMetaObject = { OBJECT_DESCRIPTION_MODEL_STATIC_META_OBJECT(EffectType) }; template<> const QMetaObject ObjectDescriptionModel::staticMetaObject = { OBJECT_DESCRIPTION_MODEL_STATIC_META_OBJECT(AudioChannelType) }; template<> const QMetaObject ObjectDescriptionModel::staticMetaObject = { OBJECT_DESCRIPTION_MODEL_STATIC_META_OBJECT(SubtitleType) }; template const QMetaObject *ObjectDescriptionModel::metaObject() const { return &staticMetaObject; } template void *ObjectDescriptionModel::qt_metacast(const char *_clname) { -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) // Qt 5 qWarning("WARNING: Phonon4Qt5 has not been verified to successfully qt_metacast ObjectDescriptionModels."); -#endif if (!_clname) { return 0; } if (!strcmp(_clname, ObjectDescriptionModel::staticMetaObject.className())) { return static_cast(const_cast *>(this)); } return QAbstractListModel::qt_metacast(_clname); } /* template int ObjectDescriptionModel::qt_metacall(QMetaObject::Call _c, int _id, void **_a) { return QAbstractListModel::qt_metacall(_c, _id, _a); } */ int ObjectDescriptionModelData::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return d->data.size(); } QVariant ObjectDescriptionModelData::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= d->data.size() || index.column() != 0) return QVariant(); switch(role) { case Qt::EditRole: case Qt::DisplayRole: return d->data.at(index.row())->name(); break; case Qt::ToolTipRole: return d->data.at(index.row())->description(); break; case Qt::DecorationRole: { /* Returns an icon if available. Paint a subicon representing the entity (platform * plugin or backend) which discovered this object, if it is specified */ QVariant icon = d->data.at(index.row())->property("icon"); QVariant discovererIcon = d->data.at(index.row())->property("discovererIcon"); if (icon.isValid()) { if (icon.type() == QVariant::String) { icon = Platform::icon(icon.toString()); } if (discovererIcon.type() == QVariant::String) { discovererIcon = Platform::icon(discovererIcon.toString()); } if (icon.type() == QVariant::Icon) { if (discovererIcon.type() == QVariant::Icon) { // Insert the subicon in the top-right corner of the icon QPixmap pixmap = icon.value().pixmap(QSize(64, 64)); QPixmap subPixmap = discovererIcon.value().pixmap(QSize(22, 22)); QPainter painter(&pixmap); painter.drawPixmap(42, 0, subPixmap); return QIcon(pixmap); } else { return icon; } } } } return QVariant(); default: return QVariant(); } } Qt::ItemFlags ObjectDescriptionModelData::flags(const QModelIndex &index) const { if(!index.isValid() || index.row() >= d->data.size() || index.column() != 0) { return Qt::ItemIsDropEnabled; } QVariant available = d->data.at(index.row())->property("available"); if (available.isValid() && available.type() == QVariant::Bool && !available.toBool()) { return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled; } return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled; } QList ObjectDescriptionModelData::tupleIndexOrder() const { QList ret; for (int i = 0; i < d->data.size(); ++i) { ret.append(d->data.at(i)->index()); } return ret; } int ObjectDescriptionModelData::tupleIndexAtPositionIndex(int positionIndex) const { return d->data.at(positionIndex)->index(); } QMimeData *ObjectDescriptionModelData::mimeData(ObjectDescriptionType type, const QModelIndexList &indexes) const { QMimeData *mimeData = new QMimeData; QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); QModelIndexList::const_iterator end = indexes.constEnd(); QModelIndexList::const_iterator index = indexes.constBegin(); for(; index!=end; ++index) { if ((*index).isValid()) { stream << d->data.at((*index).row())->index(); } } //pDebug() << Q_FUNC_INFO << "setting mimeData to" << mimeTypes(type).first() << "=>" << encodedData.toHex(); mimeData->setData(mimeTypes(type).first(), encodedData); return mimeData; } void ObjectDescriptionModelData::moveUp(const QModelIndex &index) { if (!index.isValid() || index.row() >= d->data.size() || index.row() < 1 || index.column() != 0) return; emit d->model->layoutAboutToBeChanged(); QModelIndex above = index.sibling(index.row() - 1, index.column()); d->data.swap(index.row(), above.row()); QModelIndexList from, to; from << index << above; to << above << index; d->model->changePersistentIndexList(from, to); emit d->model->layoutChanged(); } void ObjectDescriptionModelData::moveDown(const QModelIndex &index) { if (!index.isValid() || index.row() >= d->data.size() - 1 || index.column() != 0) return; emit d->model->layoutAboutToBeChanged(); QModelIndex below = index.sibling(index.row() + 1, index.column()); d->data.swap(index.row(), below.row()); QModelIndexList from, to; from << index << below; to << below << index; d->model->changePersistentIndexList(from, to); emit d->model->layoutChanged(); } ObjectDescriptionModelData::ObjectDescriptionModelData(QAbstractListModel *model) : d(new ObjectDescriptionModelDataPrivate(model)) { } ObjectDescriptionModelData::~ObjectDescriptionModelData() { delete d; } void ObjectDescriptionModelData::setModelData(const QList > &newData) { d->model->beginResetModel(); d->data = newData; d->model->endResetModel(); } QList > ObjectDescriptionModelData::modelData() const { return d->data; } QExplicitlySharedDataPointer ObjectDescriptionModelData::modelData(const QModelIndex &index) const { if (!index.isValid() || index.row() >= d->data.size() || index.column() != 0) { return QExplicitlySharedDataPointer(new ObjectDescriptionData(0)); } return d->data.at(index.row()); } Qt::DropActions ObjectDescriptionModelData::supportedDropActions() const { //pDebug() << Q_FUNC_INFO; return Qt::MoveAction; } bool ObjectDescriptionModelData::dropMimeData(ObjectDescriptionType type, const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { Q_UNUSED(action); Q_UNUSED(column); Q_UNUSED(parent); //pDebug() << Q_FUNC_INFO << data << action << row << column << parent; QString format = mimeTypes(type).first(); if (!data->hasFormat(format)) { return false; } if (row == -1) { row = d->data.size(); } QByteArray encodedData = data->data(format); QDataStream stream(&encodedData, QIODevice::ReadOnly); QList > toInsert; while (!stream.atEnd()) { int otherIndex; stream >> otherIndex; ObjectDescriptionData *obj = ObjectDescriptionData::fromIndex(type, otherIndex); if (obj->isValid()) { toInsert << QExplicitlySharedDataPointer(obj); } else { delete obj; } } d->model->beginInsertRows(QModelIndex(), row, row + toInsert.size() - 1); for (int i = 0 ; i < toInsert.count(); ++i) { d->data.insert(row, toInsert.at(i)); } d->model->endInsertRows(); return true; } bool ObjectDescriptionModelData::removeRows(int row, int count, const QModelIndex &parent) { //pDebug() << Q_FUNC_INFO << row << count << parent; if (parent.isValid() || row + count > d->data.size()) { return false; } d->model->beginRemoveRows(parent, row, row + count - 1); for (;count > 0; --count) { d->data.removeAt(row); } d->model->endRemoveRows(); return true; } /* template bool ObjectDescriptionModel::insertRows(int row, int count, const QModelIndex &parent) { pDebug() << Q_FUNC_INFO << row << count << parent; if (parent.isValid() || row < 0 || row > d->data.size()) { return false; } beginInsertRows(parent, row, row + count - 1); for (;count > 0; --count) { d->data.insert(row, ObjectDescription()); } endInsertRows(); return true; } */ QStringList ObjectDescriptionModelData::mimeTypes(ObjectDescriptionType type) const { return QStringList(QLatin1String("application/x-phonon-objectdescription") + QString::number(static_cast(type))); } #if !defined(Q_CC_MSVC) || _MSC_VER > 1300 || defined(Q_CC_INTEL) || defined(Q_CC_MINGW) #define INSTANTIATE_META_FUNCTIONS(type) \ template const QMetaObject *ObjectDescriptionModel::metaObject() const; \ template void *ObjectDescriptionModel::qt_metacast(const char *) INSTANTIATE_META_FUNCTIONS(AudioOutputDeviceType); INSTANTIATE_META_FUNCTIONS(AudioCaptureDeviceType); INSTANTIATE_META_FUNCTIONS(VideoCaptureDeviceType); INSTANTIATE_META_FUNCTIONS(EffectType); INSTANTIATE_META_FUNCTIONS(AudioChannelType); INSTANTIATE_META_FUNCTIONS(SubtitleType); #endif /*INSTANTIATE_META_FUNCTIONS(VideoOutputDeviceType); INSTANTIATE_META_FUNCTIONS(AudioCodecType); INSTANTIATE_META_FUNCTIONS(VideoCodecType); INSTANTIATE_META_FUNCTIONS(ContainerFormatType); INSTANTIATE_META_FUNCTIONS(VisualizationType); */ } // namespace Phonon #endif //QT_NO_PHONON_OBJECTDESCRIPTIONMODEL diff --git a/phonon/platform.cpp b/phonon/platform.cpp index 189f8f01..eea09c49 100644 --- a/phonon/platform.cpp +++ b/phonon/platform.cpp @@ -1,159 +1,157 @@ /* This file is part of the KDE project Copyright (C) 2007 Matthias Kretz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), Nokia Corporation (or its successors, if any) and the KDE Free Qt Foundation, which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "platform_p.h" #include "platformplugin.h" #include "factory_p.h" #include #include #include #include #include namespace Phonon { void Platform::saveVolume(const QString &outputName, qreal volume) { #ifndef QT_NO_PHONON_PLATFORMPLUGIN PlatformPlugin *f = Factory::platformPlugin(); if (f) { f->saveVolume(outputName, volume); } #else Q_UNUSED(outputName); Q_UNUSED(volume); #endif //QT_NO_PHONON_PLATFORMPLUGIN } qreal Platform::loadVolume(const QString &outputName) { #ifndef QT_NO_PHONON_PLATFORMPLUGIN const PlatformPlugin *f = Factory::platformPlugin(); if (f) { return f->loadVolume(outputName); } #else Q_UNUSED(outputName); #endif //QT_NO_PHONON_PLATFORMPLUGIN return 1.0; } AbstractMediaStream *Platform::createMediaStream(const QUrl &url, QObject *parent) { #ifndef QT_NO_PHONON_PLATFORMPLUGIN PlatformPlugin *f = Factory::platformPlugin(); if (f) { return f->createMediaStream(url, parent); } #else Q_UNUSED(url); Q_UNUSED(parent); #endif //QT_NO_PHONON_PLATFORMPLUGIN return 0; } QIcon Platform::icon(const QString &name, QStyle *style) { QIcon ret; #ifndef QT_NO_PHONON_PLATFORMPLUGIN if (const PlatformPlugin *f = Factory::platformPlugin()) { ret = f->icon(name); } #endif //QT_NO_PHONON_PLATFORMPLUGIN // No platform plugin present. Use internal versions. if (ret.isNull()) { if (!style) { style = QApplication::style(); } if (name == QLatin1String("player-volume")) { ret = style->standardPixmap(QStyle::SP_MediaVolume); } else if (name == QLatin1String("player-volume-muted")) { ret = style->standardPixmap(QStyle::SP_MediaVolumeMuted); } } -#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) // QIcon::fromTheme was introduced in 4.6.0. // Still no icon set. Use QIcon to access the platform theme. if (ret.isNull()) ret = QIcon::fromTheme(name); // Now we are getting angry... keep dropping '-foo' parts from the end of // the name until we get something usable or run out of substrings. // (this is a simplified version of fallback lookup as done by KIconLoader; // essentially audio-card-pci can also be provided by audio-card which is // the more generic version). QString choppedName = name; while (ret.isNull() && !choppedName.isEmpty()) { choppedName.resize(choppedName.lastIndexOf(QChar('-'))); ret = QIcon::fromTheme(choppedName); } -#endif return ret; } void Platform::notification(const char *notificationName, const QString &text, const QStringList &actions, QObject *receiver, const char *actionSlot) { #ifndef QT_NO_PHONON_PLATFORMPLUGIN const PlatformPlugin *f = Factory::platformPlugin(); if (f) { f->notification(notificationName, text, actions, receiver, actionSlot); } #else Q_UNUSED(notificationName); Q_UNUSED(text); Q_UNUSED(actions); Q_UNUSED(receiver); Q_UNUSED(actionSlot); #endif //QT_NO_PHONON_PLATFORMPLUGIN } QString Platform::applicationName() { #ifndef QT_NO_PHONON_PLATFORMPLUGIN const PlatformPlugin *f = Factory::platformPlugin(); if (f) { return f->applicationName(); } #endif //QT_NO_PHONON_PLATFORMPLUGIN QString ret = QCoreApplication::applicationName(); if (ret.isEmpty()) ret = QCoreApplication::applicationFilePath(); return ret; } QList > Platform::deviceAccessListFor(const Phonon::AudioOutputDevice &deviceDesc) { #ifndef QT_NO_PHONON_PLATFORMPLUGIN const PlatformPlugin *f = Factory::platformPlugin(); if (f) { return f->deviceAccessListFor(deviceDesc); } #endif //QT_NO_PHONON_PLATFORMPLUGIN return QList >(); } } // namespace Phonon diff --git a/phonon/pulsesupport.cpp b/phonon/pulsesupport.cpp index d3240840..6fc45a6f 100644 --- a/phonon/pulsesupport.cpp +++ b/phonon/pulsesupport.cpp @@ -1,1474 +1,1472 @@ /* Copyright (C) 2010 Colin Guthrie Copyright (C) 2013 Harald Sitter This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), Nokia Corporation (or its successors, if any) and the KDE Free Qt Foundation, which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "pulsesupport.h" #include #include #include #include #include #include #include #ifdef HAVE_PULSEAUDIO #include "pulsestream_p.h" #include #include #include #define HAVE_PULSEAUDIO_DEVICE_MANAGER PA_CHECK_VERSION(0,9,21) #if HAVE_PULSEAUDIO_DEVICE_MANAGER # include #endif #endif // HAVE_PULSEAUDIO #include "phononnamespace_p.h" #include "platform_p.h" #define PA_PROP_PHONON_STREAMID "phonon.streamid" namespace Phonon { QMutex probeMutex; static PulseSupport *s_instance = NULL; static bool s_wasShutDown = false; static bool s_pulseActive = false; #ifdef HAVE_PULSEAUDIO /*** * Prints a conditional debug message based on the current debug level * If obj is provided, classname and objectname will be printed as well * * see debugLevel() */ static int debugLevel() { static int level = -1; if (level < 1) { level = 0; QByteArray pulseenv = qgetenv("PHONON_PULSEAUDIO_DEBUG"); int l = pulseenv.toInt(); if (l > 0) level = (l > 2 ? 2 : l); } return level; } static void logMessage(const QString &message, int priority = 2, QObject *obj=0); static void logMessage(const QString &message, int priority, QObject *obj) { if (debugLevel() > 0) { QString output; if (obj) { // Strip away namespace from className QByteArray className(obj->metaObject()->className()); int nameLength = className.length() - className.lastIndexOf(':') - 1; className = className.right(nameLength); output.sprintf("%s %s (%s %p)", message.toLatin1().constData(), obj->objectName().toLatin1().constData(), className.constData(), obj); } else { output = message; } if (priority <= debugLevel()) { qDebug() << QString::fromLatin1("PulseSupport(%1): %2").arg(priority).arg(output); } } } class AudioDevice { public: inline AudioDevice(QString name, QString desc, QString icon, uint32_t index) : pulseName(name), pulseIndex(index) { properties["name"] = desc; properties["description"] = QLatin1String(""); // We don't have descriptions (well we do, but we use them as the name!) properties["icon"] = icon; properties["available"] = (index != PA_INVALID_INDEX); properties["isAdvanced"] = false; // Nothing is advanced! DeviceAccessList dal; dal.append(DeviceAccess("pulse", desc)); properties["deviceAccessList"] = QVariant::fromValue(dal); } // Needed for QMap inline AudioDevice() {} QString pulseName; uint32_t pulseIndex; QHash properties; }; bool operator!=(const AudioDevice &a, const AudioDevice &b) { return !(a.pulseName == b.pulseName && a.properties == b.properties); } class PulseUserData { public: inline PulseUserData() { } QMap newOutputDevices; QMap > newOutputDevicePriorities; // prio, device QMap newCaptureDevices; QMap > newCaptureDevicePriorities; // prio, device }; static pa_glib_mainloop *s_mainloop = NULL; static pa_context *s_context = NULL; static int s_deviceIndexCounter = 0; static QMap s_outputDeviceIndexes; static QMap s_outputDevices; static QMap > s_outputDevicePriorities; // prio, device static QMap s_outputStreams; static const Phonon::CaptureCategory s_audioCapCategories[] = { Phonon::NoCaptureCategory, Phonon::CommunicationCaptureCategory, Phonon::RecordingCaptureCategory, Phonon::ControlCaptureCategory }; static const int s_audioCapCategoriesCount = sizeof(s_audioCapCategories) / sizeof(Phonon::CaptureCategory); static QMap s_captureDeviceIndexes; static QMap s_captureDevices; static QMap > s_captureDevicePriorities; // prio, device static QMap s_captureStreams; static PulseStream* findStreamByPulseIndex(QMap map, uint32_t index) { QMap::iterator it; for (it = map.begin(); it != map.end(); ++it) if ((*it)->index() == index) return *it; return NULL; } static Phonon::Category pulseRoleToPhononCategory(const char *role, bool *success) { Q_ASSERT(role); Q_ASSERT(success); *success = true; QByteArray r(role); if (r == "none") return Phonon::NoCategory; if (r == "video") return Phonon::VideoCategory; if (r == "music") return Phonon::MusicCategory; if (r == "game") return Phonon::GameCategory; if (r == "event") return Phonon::NotificationCategory; if (r == "phone") return Phonon::CommunicationCategory; if (r == "a11y") return Phonon::AccessibilityCategory; // ^^ "animation" and "production" have no mapping *success = false; return Phonon::NoCategory; } static Phonon::CaptureCategory pulseRoleToPhononCaptureCategory(const char *role, bool *success) { Q_ASSERT(role); Q_ASSERT(success); *success = true; QByteArray r(role); if (r == "none") return Phonon::NoCaptureCategory; if (r == "phone") return Phonon::CommunicationCaptureCategory; if (r == "production") return Phonon::RecordingCaptureCategory; if (r == "a11y") return Phonon::ControlCaptureCategory; *success = false; return Phonon::NoCaptureCategory; } static const QByteArray phononCategoryToPulseRole(Phonon::Category category) { switch (category) { case Phonon::NoCategory: return QByteArray("none"); case Phonon::VideoCategory: return QByteArray("video"); case Phonon::MusicCategory: return QByteArray("music"); case Phonon::GameCategory: return QByteArray("game"); case Phonon::NotificationCategory: return QByteArray("event"); case Phonon::CommunicationCategory: return QByteArray("phone"); case Phonon::AccessibilityCategory: return QByteArray("a11y"); default: return QByteArray(); } } static const QByteArray phononCaptureCategoryToPulseRole(Phonon::CaptureCategory category) { switch (category) { case Phonon::NoCaptureCategory: return QByteArray("none"); case Phonon::CommunicationCaptureCategory: return QByteArray("phone"); case Phonon::RecordingCaptureCategory: return QByteArray("production"); case Phonon::ControlCaptureCategory: return QByteArray("a11y"); default: return QByteArray(); } } static void createGenericDevices() { // OK so we don't have the device manager extension, but we can show a single device and fake it. int index; s_outputDeviceIndexes.clear(); s_outputDevices.clear(); s_outputDevicePriorities.clear(); index = s_deviceIndexCounter++; s_outputDeviceIndexes.insert(QLatin1String("sink:default"), index); s_outputDevices.insert(index, AudioDevice(QLatin1String("sink:default"), QObject::tr("PulseAudio Sound Server"), QLatin1String("audio-backend-pulseaudio"), 0)); for (int i = Phonon::NoCategory; i <= Phonon::LastCategory; ++i) { Phonon::Category cat = static_cast(i); s_outputDevicePriorities[cat].insert(0, index); } s_captureDeviceIndexes.clear(); s_captureDevices.clear(); s_captureDevicePriorities.clear(); index = s_deviceIndexCounter++; s_captureDeviceIndexes.insert(QLatin1String("source:default"), index); s_captureDevices.insert(index, AudioDevice(QLatin1String("source:default"), QObject::tr("PulseAudio Sound Server"), QLatin1String("audio-backend-pulseaudio"), 0)); for (int i = 0; i < s_audioCapCategoriesCount; ++i) { Phonon::CaptureCategory cat = s_audioCapCategories[i]; s_captureDevicePriorities[cat].insert(0, index); } } #if HAVE_PULSEAUDIO_DEVICE_MANAGER static void ext_device_manager_read_cb(pa_context *c, const pa_ext_device_manager_info *info, int eol, void *userdata) { Q_ASSERT(c); Q_ASSERT(userdata); PulseUserData *u = reinterpret_cast(userdata); if (eol < 0) { logMessage(QString::fromLatin1("Failed to initialize device manager extension: %1").arg(pa_strerror(pa_context_errno(c)))); if (s_context != c) { logMessage(QLatin1String("Falling back to single device mode")); // Only create our gerneric devices during the probe phase. createGenericDevices(); // As this is our probe phase, exit immediately pa_context_disconnect(c); } delete u; return; } if (eol) { // We're done reading the data, so order it by priority and copy it into the // static variables where it can then be accessed by those classes that need it. QMap::iterator newdev_it; // Check for new output devices or things changing about known output devices. bool output_changed = false; for (newdev_it = u->newOutputDevices.begin(); newdev_it != u->newOutputDevices.end(); ++newdev_it) { QString name = newdev_it.key(); // The name + index map is always written when a new device is added. Q_ASSERT(s_outputDeviceIndexes.contains(name)); int index = s_outputDeviceIndexes[name]; if (!s_outputDevices.contains(index)) { // This is a totally new device output_changed = true; logMessage(QString("Brand New Output Device Found.")); s_outputDevices.insert(index, *newdev_it); } else if (s_outputDevices[index] != *newdev_it) { // We have this device already, but is it different? output_changed = true; logMessage(QString("Change to Existing Output Device (may be Added/Removed or something else)")); s_outputDevices.remove(index); s_outputDevices.insert(index, *newdev_it); } } // Go through the output devices we know about and see if any are no longer mentioned in the list. QMutableMapIterator output_existing_it(s_outputDeviceIndexes); while (output_existing_it.hasNext()) { output_existing_it.next(); if (!u->newOutputDevices.contains(output_existing_it.key())) { output_changed = true; logMessage(QString("Output Device Completely Removed")); s_outputDevices.remove(output_existing_it.value()); output_existing_it.remove(); } } // Check for new capture devices or things changing about known capture devices. bool capture_changed = false; for (newdev_it = u->newCaptureDevices.begin(); newdev_it != u->newCaptureDevices.end(); ++newdev_it) { QString name = newdev_it.key(); // The name + index map is always written when a new device is added. Q_ASSERT(s_captureDeviceIndexes.contains(name)); int index = s_captureDeviceIndexes[name]; if (!s_captureDevices.contains(index)) { // This is a totally new device capture_changed = true; logMessage(QString("Brand New Capture Device Found.")); s_captureDevices.insert(index, *newdev_it); } else if (s_captureDevices[index] != *newdev_it) { // We have this device already, but is it different? capture_changed = true; logMessage(QString("Change to Existing Capture Device (may be Added/Removed or something else)")); s_captureDevices.remove(index); s_captureDevices.insert(index, *newdev_it); } } // Go through the capture devices we know about and see if any are no longer mentioned in the list. QMutableMapIterator capture_existing_it(s_captureDeviceIndexes); while (capture_existing_it.hasNext()) { capture_existing_it.next(); if (!u->newCaptureDevices.contains(capture_existing_it.key())) { capture_changed = true; logMessage(QString("Capture Device Completely Removed")); s_captureDevices.remove(capture_existing_it.value()); capture_existing_it.remove(); } } // Just copy across the new priority lists as we know they are valid if (s_outputDevicePriorities != u->newOutputDevicePriorities) { output_changed = true; s_outputDevicePriorities = u->newOutputDevicePriorities; } if (s_captureDevicePriorities != u->newCaptureDevicePriorities) { capture_changed = true; s_captureDevicePriorities = u->newCaptureDevicePriorities; } if (s_instance) { // This wont be emitted during the connection probe phase // which is intensional if (output_changed) s_instance->emitObjectDescriptionChanged(AudioOutputDeviceType); if (capture_changed) s_instance->emitObjectDescriptionChanged(AudioCaptureDeviceType); } // We can free the user data as we will not be called again. delete u; // Some debug logMessage(QString("Output Device Priority List:")); for (int i = Phonon::NoCategory; i <= Phonon::LastCategory; ++i) { Phonon::Category cat = static_cast(i); if (s_outputDevicePriorities.contains(cat)) { logMessage(QString(" Phonon Category %1").arg(cat)); int count = 0; foreach (int j, s_outputDevicePriorities[cat]) { QHash &props = s_outputDevices[j].properties; logMessage(QString(" %1. %2 (Available: %3)").arg(++count).arg(props["name"].toString()).arg(props["available"].toBool())); } } } logMessage(QString("Capture Device Priority List:")); for (int i = 0; i < s_audioCapCategoriesCount; ++i) { Phonon::CaptureCategory cat = s_audioCapCategories[i]; if (s_captureDevicePriorities.contains(cat)) { logMessage(QString(" Phonon Category %1").arg(cat)); int count = 0; foreach (int j, s_captureDevicePriorities[cat]) { QHash &props = s_captureDevices[j].properties; logMessage(QString(" %1. %2 (Available: %3)").arg(++count).arg(props["name"].toString()).arg(props["available"].toBool())); } } } // If this is our probe phase, exit now as we're finished reading // our device info and can exit and reconnect if (s_context != c) pa_context_disconnect(c); return; // eol } // If we aren't at eol we expect info to be valid! Q_ASSERT(info); Q_ASSERT(info->name); Q_ASSERT(info->description); Q_ASSERT(info->icon); // QString wrapper QString name(info->name); int index; QMap > *new_prio_map_cats = NULL; // prio, device QMap > *new_prio_map_capcats = NULL; // prio, device QMap *new_devices = NULL; bool isSink = false; bool isSource = false; if (name.startsWith(QLatin1String("sink:"))) { isSink = true; new_devices = &u->newOutputDevices; new_prio_map_cats = &u->newOutputDevicePriorities; if (s_outputDeviceIndexes.contains(name)) index = s_outputDeviceIndexes[name]; else index = s_outputDeviceIndexes[name] = s_deviceIndexCounter++; } else if (name.startsWith(QLatin1String("source:"))) { isSource = true; new_devices = &u->newCaptureDevices; new_prio_map_capcats = &u->newCaptureDevicePriorities; if (s_captureDeviceIndexes.contains(name)) index = s_captureDeviceIndexes[name]; else index = s_captureDeviceIndexes[name] = s_deviceIndexCounter++; } else { // This indicates a bug in pulseaudio. return; } Q_ASSERT(new_devices); Q_ASSERT(!isSink || new_prio_map_cats); Q_ASSERT(!isSource || new_prio_map_capcats); // Add the new device itself. new_devices->insert(name, AudioDevice(name, QString::fromUtf8(info->description), QString::fromUtf8(info->icon), info->index)); // For each role in the priority, map it to a phonon category and store the order. for (uint32_t i = 0; i < info->n_role_priorities; ++i) { pa_ext_device_manager_role_priority_info* role_prio = &info->role_priorities[i]; Q_ASSERT(role_prio->role); bool conversionSuccess; if (isSink) { Phonon::Category cat = pulseRoleToPhononCategory(role_prio->role, &conversionSuccess); if (conversionSuccess) { (*new_prio_map_cats)[cat].insert(role_prio->priority, index); } } if (isSource) { Phonon::CaptureCategory capcat = pulseRoleToPhononCaptureCategory(role_prio->role, &conversionSuccess); if (conversionSuccess) { (*new_prio_map_capcats)[capcat].insert(role_prio->priority, index); } } } } static void ext_device_manager_subscribe_cb(pa_context *c, void *) { Q_ASSERT(c); pa_operation *o; PulseUserData *u = new PulseUserData; if (!(o = pa_ext_device_manager_read(c, ext_device_manager_read_cb, u))) { logMessage(QString::fromLatin1("pa_ext_device_manager_read() failed.")); delete u; return; } pa_operation_unref(o); } #endif static void sink_input_cb(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata) { Q_UNUSED(userdata); Q_ASSERT(c); if (eol < 0) { if (pa_context_errno(c) == PA_ERR_NOENTITY) return; logMessage(QLatin1String("Sink input callback failure")); return; } if (eol > 0) return; Q_ASSERT(i); // loop through (*i) and extract phonon->streamindex... const char *t; if ((t = pa_proplist_gets(i->proplist, PA_PROP_PHONON_STREAMID))) { logMessage(QString::fromLatin1("Found PulseAudio stream index %1 for Phonon Output Stream %2").arg(i->index).arg(QLatin1String(t))); // We only care about our own streams (other phonon processes are irrelevent) if (s_outputStreams.contains(QLatin1String(t))) { PulseStream *stream = s_outputStreams[QString(t)]; stream->setIndex(i->index); stream->setVolume(&i->volume); stream->setMute(!!i->mute); // Find the sink's phonon index and notify whoever cares... if (PA_INVALID_INDEX != i->sink) { QMap::iterator it; for (it = s_outputDevices.begin(); it != s_outputDevices.end(); ++it) { if ((*it).pulseIndex == i->sink) { stream->setDevice(it.key()); break; } } } } } } static void source_output_cb(pa_context *c, const pa_source_output_info *i, int eol, void *userdata) { Q_UNUSED(userdata); Q_ASSERT(c); if (eol < 0) { if (pa_context_errno(c) == PA_ERR_NOENTITY) return; logMessage(QLatin1String("Source output callback failure")); return; } if (eol > 0) return; Q_ASSERT(i); // loop through (*i) and extract phonon->streamindex... const char *t; if ((t = pa_proplist_gets(i->proplist, PA_PROP_PHONON_STREAMID))) { logMessage(QString::fromLatin1("Found PulseAudio stream index %1 for Phonon Capture Stream %2").arg(i->index).arg(QLatin1String(t))); // We only care about our own streams (other phonon processes are irrelevent) if (s_captureStreams.contains(QLatin1String(t))) { PulseStream *stream = s_captureStreams[QString(t)]; stream->setIndex(i->index); //stream->setVolume(&i->volume); //stream->setMute(!!i->mute); // Find the source's phonon index and notify whoever cares... if (PA_INVALID_INDEX != i->source) { QMap::iterator it; for (it = s_captureDevices.begin(); it != s_captureDevices.end(); ++it) { if ((*it).pulseIndex == i->source) { stream->setDevice(it.key()); break; } } } } } } static void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata) { Q_UNUSED(userdata); switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { case PA_SUBSCRIPTION_EVENT_SINK_INPUT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { PulseStream *stream = findStreamByPulseIndex(s_outputStreams, index); if (stream) { logMessage(QString::fromLatin1("Phonon Output Stream %1 is gone at the PA end. Marking it as invalid in our cache as we may reuse it.").arg(stream->uuid())); stream->setIndex(PA_INVALID_INDEX); } } else { pa_operation *o; if (!(o = pa_context_get_sink_input_info(c, index, sink_input_cb, NULL))) { logMessage(QString::fromLatin1("pa_context_get_sink_input_info() failed")); return; } pa_operation_unref(o); } break; case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { PulseStream *stream = findStreamByPulseIndex(s_captureStreams, index); if (stream) { logMessage(QString::fromLatin1("Phonon Capture Stream %1 is gone at the PA end. Marking it as invalid in our cache as we may reuse it.").arg(stream->uuid())); stream->setIndex(PA_INVALID_INDEX); } } else { pa_operation *o; if (!(o = pa_context_get_source_output_info(c, index, source_output_cb, NULL))) { logMessage(QString::fromLatin1("pa_context_get_sink_input_info() failed")); return; } pa_operation_unref(o); } break; } } static QString statename(pa_context_state_t state) { switch (state) { case PA_CONTEXT_UNCONNECTED: return QLatin1String("Unconnected"); case PA_CONTEXT_CONNECTING: return QLatin1String("Connecting"); case PA_CONTEXT_AUTHORIZING: return QLatin1String("Authorizing"); case PA_CONTEXT_SETTING_NAME: return QLatin1String("Setting Name"); case PA_CONTEXT_READY: return QLatin1String("Ready"); case PA_CONTEXT_FAILED: return QLatin1String("Failed"); case PA_CONTEXT_TERMINATED: return QLatin1String("Terminated"); } return QString::fromLatin1("Unknown state: %0").arg(state); } static void context_state_callback(pa_context *c, void *) { Q_ASSERT(c); logMessage(QString::fromLatin1("context_state_callback %1").arg(statename(pa_context_get_state(c)))); pa_context_state_t state = pa_context_get_state(c); if (state == PA_CONTEXT_READY) { // We've connected to PA, so it is active s_pulseActive = true; // Attempt to load things up pa_operation *o; // 1. Register for the stream changes (except during probe) if (s_context == c) { pa_context_set_subscribe_callback(c, subscribe_cb, NULL); if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t) (PA_SUBSCRIPTION_MASK_SINK_INPUT| PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT), NULL, NULL))) { logMessage(QLatin1String("pa_context_subscribe() failed")); return; } pa_operation_unref(o); // In the case of reconnection or simply lagging behind the stream object creation // on startup (due to the probe+reconnect system), we invalidate all loaded streams // and then load up info about all streams. for (QMap::iterator it = s_outputStreams.begin(); it != s_outputStreams.end(); ++it) { PulseStream *stream = *it; logMessage(QString::fromLatin1("Phonon Output Stream %1 is gone at the PA end. Marking it as invalid in our cache as we may reuse it.").arg(stream->uuid())); stream->setIndex(PA_INVALID_INDEX); } if (!(o = pa_context_get_sink_input_info_list(c, sink_input_cb, NULL))) { logMessage(QString::fromLatin1("pa_context_get_sink_input_info_list() failed")); return; } pa_operation_unref(o); for (QMap::iterator it = s_captureStreams.begin(); it != s_captureStreams.end(); ++it) { PulseStream *stream = *it; logMessage(QString::fromLatin1("Phonon Capture Stream %1 is gone at the PA end. Marking it as invalid in our cache as we may reuse it.").arg(stream->uuid())); stream->setIndex(PA_INVALID_INDEX); } if (!(o = pa_context_get_source_output_info_list(c, source_output_cb, NULL))) { logMessage(QString::fromLatin1("pa_context_get_source_output_info_list() failed")); return; } pa_operation_unref(o); } #if HAVE_PULSEAUDIO_DEVICE_MANAGER // 2a. Attempt to initialise Device Manager info (except during probe) if (s_context == c) { pa_ext_device_manager_set_subscribe_cb(c, ext_device_manager_subscribe_cb, NULL); if (!(o = pa_ext_device_manager_subscribe(c, 1, NULL, NULL))) { logMessage(QString::fromLatin1("pa_ext_device_manager_subscribe() failed")); return; } pa_operation_unref(o); } // 3. Attempt to read info from Device Manager PulseUserData *u = new PulseUserData; if (!(o = pa_ext_device_manager_read(c, ext_device_manager_read_cb, u))) { if (s_context != c) { logMessage(QString::fromLatin1("pa_ext_device_manager_read() failed. Attempting to continue without device manager support")); // Only create our gerneric devices during the probe phase. createGenericDevices(); // As this is our probe phase, exit immediately pa_context_disconnect(c); } delete u; return; } pa_operation_unref(o); #else // If we know do not have Device Manager support, we just create our dummy devices now if (s_context != c) { // Only create our gerneric devices during the probe phase. createGenericDevices(); // As this is our probe phase, exit immediately pa_context_disconnect(c); } #endif } else if (!PA_CONTEXT_IS_GOOD(state)) { /// @todo Deal with reconnection... //logMessage(QString("Connection to PulseAudio lost: %1").arg(pa_strerror(pa_context_errno(c)))); // If this is our probe phase, exit our context immediately if (s_context != c) pa_context_disconnect(c); else { pa_context_unref(s_context); s_context = NULL; QTimer::singleShot(50, PulseSupport::getInstance(), SLOT(connectToDaemon())); } } } #endif // HAVE_PULSEAUDIO PulseSupport *PulseSupport::getInstanceOrNull(bool allowNull) { if (s_wasShutDown && allowNull) { return NULL; } if (NULL == s_instance) { /* * In order to prevent the instance being used from multiple threads * prior to it being contructed fully, we need to ensure we obtain a * lock prior to creating it. After we acquire the lock, check to see * if the object is created again before proceeding. */ probeMutex.lock(); if (NULL == s_instance) s_instance = new PulseSupport(); probeMutex.unlock(); } return s_instance; } PulseSupport *PulseSupport::getInstance() { return getInstanceOrNull(false); } void PulseSupport::shutdown() { if (NULL != s_instance) { delete s_instance; s_instance = NULL; s_wasShutDown = true; } } void PulseSupport::debug() { #ifdef HAVE_PULSEAUDIO logMessage(QString::fromLatin1("Have we been initialised yet? %1").arg(s_instance ? "Yes" : "No")); if (s_instance) { logMessage(QString::fromLatin1("Connected to PulseAudio? %1").arg(s_pulseActive ? "Yes" : "No")); logMessage(QString::fromLatin1("PulseAudio support 'Active'? %1").arg(s_instance->isActive() ? "Yes" : "No")); } #endif } PulseSupport::PulseSupport() : QObject() , mEnabled(false) , m_requested(false) { #ifdef HAVE_PULSEAUDIO // To allow for easy debugging, give an easy way to disable this pulseaudio check QByteArray pulseenv = qgetenv("PHONON_PULSEAUDIO_DISABLE"); if (pulseenv.toInt()) { logMessage(QLatin1String("PulseAudio support disabled: PHONON_PULSEAUDIO_DISABLE is set")); return; } if (!QAbstractEventDispatcher::instance() || !QAbstractEventDispatcher::instance()->metaObject()) { qWarning("WARNING: Cannot construct PulseSupport because there is no Eventloop." " May be because of application shutdown."); return; } // We require a glib event loop if (!QByteArray(QAbstractEventDispatcher::instance()->metaObject()->className()).contains("EventDispatcherGlib") && !QByteArray(QAbstractEventDispatcher::instance()->metaObject()->className()).contains("GlibEventDispatcher")) { qWarning("WARNING: Disabling PulseAudio integration for lack of GLib event loop."); return; } // First of all conenct to PA via simple/blocking means and if that succeeds, // use a fully async integrated mainloop method to connect and get proper support. pa_mainloop *p_test_mainloop; if (!(p_test_mainloop = pa_mainloop_new())) { logMessage(QLatin1String("PulseAudio support disabled: Unable to create mainloop")); return; } pa_context *p_test_context; if (!(p_test_context = pa_context_new(pa_mainloop_get_api(p_test_mainloop), "libphonon-probe"))) { logMessage(QLatin1String("PulseAudio support disabled: Unable to create context")); pa_mainloop_free(p_test_mainloop); return; } logMessage(QLatin1String("Probing for PulseAudio...")); // (cg) Convert to PA_CONTEXT_NOFLAGS when PulseAudio 0.9.19 is required if (pa_context_connect(p_test_context, NULL, static_cast(0), NULL) < 0) { logMessage(QString::fromLatin1("PulseAudio support disabled: %1").arg(QString::fromLocal8Bit(pa_strerror(pa_context_errno(p_test_context))))); pa_context_disconnect(p_test_context); pa_context_unref(p_test_context); pa_mainloop_free(p_test_mainloop); return; } pa_context_set_state_callback(p_test_context, &context_state_callback, NULL); for (;;) { pa_mainloop_iterate(p_test_mainloop, 1, NULL); if (!PA_CONTEXT_IS_GOOD(pa_context_get_state(p_test_context))) { logMessage(QLatin1String("PulseAudio probe complete.")); break; } } pa_context_disconnect(p_test_context); pa_context_unref(p_test_context); pa_mainloop_free(p_test_mainloop); if (!s_pulseActive) { logMessage(QLatin1String("PulseAudio support is not available.")); return; } // If we're still here, PA is available. logMessage(QLatin1String("PulseAudio support enabled")); // Now we connect for real using a proper main loop that we can forget // all about processing. s_mainloop = pa_glib_mainloop_new(NULL); Q_ASSERT(s_mainloop); connectToDaemon(); #endif } PulseSupport::~PulseSupport() { #ifdef HAVE_PULSEAUDIO if (s_context) { pa_context_disconnect(s_context); s_context = NULL; } if (s_mainloop) { pa_glib_mainloop_free(s_mainloop); s_mainloop = NULL; } #endif } void PulseSupport::connectToDaemon() { #ifdef HAVE_PULSEAUDIO pa_mainloop_api *api = pa_glib_mainloop_get_api(s_mainloop); s_context = pa_context_new(api, "libphonon"); if (pa_context_connect(s_context, NULL, PA_CONTEXT_NOFAIL, 0) >= 0) pa_context_set_state_callback(s_context, &context_state_callback, NULL); #endif } bool PulseSupport::isActive() { #ifdef HAVE_PULSEAUDIO return mEnabled && isUsed(); #else return false; #endif } bool PulseSupport::isUsed() { return isRequested() && isUsable(); } bool PulseSupport::isUsable() const { return s_pulseActive; } bool PulseSupport::isRequested() const { return m_requested; } void PulseSupport::request(bool requested) { m_requested = requested; } void PulseSupport::enable(bool enabled) { mEnabled = enabled; request(enabled); // compat, enable needs to imply request. #ifdef HAVE_PULSEAUDIO logMessage(QString::fromLocal8Bit("Enabled Breakdown: mEnabled: %1, s_pulseActive %2").arg(mEnabled ? "Yes" : "No" ).arg(s_pulseActive ? "Yes" : "No")); #endif } QList PulseSupport::objectDescriptionIndexes(ObjectDescriptionType type) const { QList list; if (type != AudioOutputDeviceType && type != AudioCaptureDeviceType) return list; #ifdef HAVE_PULSEAUDIO if (s_pulseActive) { switch (type) { case AudioOutputDeviceType: { QMap::iterator it; for (it = s_outputDeviceIndexes.begin(); it != s_outputDeviceIndexes.end(); ++it) { list.append(*it); } break; } case AudioCaptureDeviceType: { QMap::iterator it; for (it = s_captureDeviceIndexes.begin(); it != s_captureDeviceIndexes.end(); ++it) { list.append(*it); } break; } default: break; } } #endif return list; } QHash PulseSupport::objectDescriptionProperties(ObjectDescriptionType type, int index) const { QHash ret; if (type != AudioOutputDeviceType && type != AudioCaptureDeviceType) return ret; #ifndef HAVE_PULSEAUDIO Q_UNUSED(index); #else if (s_pulseActive) { switch (type) { case AudioOutputDeviceType: Q_ASSERT(s_outputDevices.contains(index)); ret = s_outputDevices[index].properties; break; case AudioCaptureDeviceType: Q_ASSERT(s_captureDevices.contains(index)); ret = s_captureDevices[index].properties; break; default: break; } } #endif return ret; } QList PulseSupport::objectIndexesByCategory(ObjectDescriptionType type, Category category) const { QList ret; if (type != AudioOutputDeviceType) return ret; #ifndef HAVE_PULSEAUDIO Q_UNUSED(category); #else if (s_pulseActive) { if (s_outputDevicePriorities.contains(category)) ret = s_outputDevicePriorities[category].values(); } #endif return ret; } QList PulseSupport::objectIndexesByCategory(ObjectDescriptionType type, CaptureCategory category) const { QList ret; if (type != AudioCaptureDeviceType) return ret; #ifndef HAVE_PULSEAUDIO Q_UNUSED(category); #else if (s_pulseActive) { if (s_captureDevicePriorities.contains(category)) ret = s_captureDevicePriorities[category].values(); } #endif return ret; } #ifdef HAVE_PULSEAUDIO static void setDevicePriority(QString role, QStringList list) { logMessage(QString::fromLatin1("Reindexing %1: %2").arg(role).arg(list.join(QLatin1String(", ")))); char **devices; devices = pa_xnew(char *, list.size()+1); int i = 0; foreach (const QString &str, list) { devices[i++] = pa_xstrdup(str.toUtf8().constData()); } devices[list.size()] = NULL; #if HAVE_PULSEAUDIO_DEVICE_MANAGER pa_operation *o; if (!(o = pa_ext_device_manager_reorder_devices_for_role(s_context, role.toUtf8().constData(), (const char**)devices, NULL, NULL))) logMessage(QString::fromLatin1("pa_ext_device_manager_reorder_devices_for_role() failed")); else pa_operation_unref(o); #endif for (i = 0; i < list.size(); ++i) pa_xfree(devices[i]); pa_xfree(devices); } static void setDevicePriority(Category category, QStringList list) { QString role = phononCategoryToPulseRole(category); if (role.isEmpty()) return; setDevicePriority(role, list); } static void setDevicePriority(CaptureCategory category, QStringList list) { QString role = phononCaptureCategoryToPulseRole(category); if (role.isEmpty()) return; setDevicePriority(role, list); } #endif // HAVE_PULSEAUDIO void PulseSupport::setOutputDevicePriorityForCategory(Category category, QList order) { #ifndef HAVE_PULSEAUDIO Q_UNUSED(category); Q_UNUSED(order); #else QStringList list; QList::iterator it; for (it = order.begin(); it != order.end(); ++it) { if (s_outputDevices.contains(*it)) { list << s_outputDeviceIndexes.key(*it); } } setDevicePriority(category, list); #endif } void PulseSupport::setCaptureDevicePriorityForCategory(CaptureCategory category, QList order) { #ifndef HAVE_PULSEAUDIO Q_UNUSED(category); Q_UNUSED(order); #else QStringList list; QList::iterator it; for (it = order.begin(); it != order.end(); ++it) { if (s_captureDevices.contains(*it)) { list << s_captureDeviceIndexes.key(*it); } } setDevicePriority(category, list); #endif } void PulseSupport::setCaptureDevicePriorityForCategory(Category category, QList order) { CaptureCategory cat = categoryToCaptureCategory(category); setCaptureDevicePriorityForCategory(cat, order); } #ifdef HAVE_PULSEAUDIO static PulseStream* register_stream(QMap &map, QString streamUuid, QString role) { logMessage(QString::fromLatin1("Initialising streamindex %1").arg(streamUuid)); PulseStream *stream = new PulseStream(streamUuid, role); map[streamUuid] = stream; // Setup envrionment... // These values are considered static, so we force property overrides for them. if (!Platform::applicationName().isEmpty()) qputenv(QString("PULSE_PROP_OVERRIDE_%1").arg(PA_PROP_APPLICATION_NAME).toUtf8(), Platform::applicationName().toUtf8()); if (!qApp->applicationVersion().isEmpty()) qputenv(QString("PULSE_PROP_OVERRIDE_%1").arg(PA_PROP_APPLICATION_VERSION).toUtf8(), qApp->applicationVersion().toUtf8()); if (!qApp->applicationName().isEmpty()) { QString icon; -#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) if (!qApp->windowIcon().isNull()){ // Try to get the fromTheme() name of the QIcon. icon = qApp->windowIcon().name(); } -#endif if (icon.isEmpty()) { // If we failed to get a proper icon name, use the appname instead. icon = qApp->applicationName().toLower(); } qputenv(QString("PULSE_PROP_OVERRIDE_%1").arg(PA_PROP_APPLICATION_ICON_NAME).toUtf8(), icon.toUtf8()); } return stream; } static PulseStream* register_stream(QMap &map, QString streamUuid, Category category) { QString role = phononCategoryToPulseRole(category); return register_stream(map, streamUuid, role); } static PulseStream* register_stream(QMap &map, QString streamUuid, CaptureCategory category) { QString role = phononCaptureCategoryToPulseRole(category); return register_stream(map, streamUuid, role); } #endif PulseStream *PulseSupport::registerOutputStream(QString streamUuid, Category category) { #ifndef HAVE_PULSEAUDIO Q_UNUSED(streamUuid); Q_UNUSED(category); return NULL; #else return register_stream(s_outputStreams, streamUuid, category); #endif } PulseStream *PulseSupport::registerCaptureStream(QString streamUuid, CaptureCategory category) { #ifndef HAVE_PULSEAUDIO Q_UNUSED(streamUuid); Q_UNUSED(category); return NULL; #else return register_stream(s_captureStreams, streamUuid, category); #endif } PulseStream *PulseSupport::registerCaptureStream(QString streamUuid, Category category) { #ifndef HAVE_PULSEAUDIO Q_UNUSED(streamUuid); Q_UNUSED(category); return NULL; #else return register_stream(s_captureStreams, streamUuid, category); #endif } QHash PulseSupport::streamProperties(QString streamUuid) const { QHash properties; #ifdef HAVE_PULSEAUDIO PulseStream *stream = 0; // Try to find the stream among the known output streams. if (!stream) stream = s_outputStreams.value(streamUuid); // Not an output stream, try capture streams. if (!stream) stream = s_captureStreams.value(streamUuid); // Also no capture stream, start crying and return an empty hash. if (!stream) { qWarning() << Q_FUNC_INFO << "Requested UUID Could not be found. Returning with empty properties."; return properties; } properties[QLatin1String(PA_PROP_PHONON_STREAMID)] = stream->uuid(); properties[QLatin1String(PA_PROP_MEDIA_ROLE)] = stream->role(); // Tear down environment before returning. This is to prevent backends from // being overridden by the environment if present. QHashIterator it(properties); while (it.hasNext()) { it.next(); unsetenv(QString("PULSE_PROP_OVERRIDE_%1").arg(it.key()).toUtf8()); } #endif // HAVE_PULSEAUDIO return properties; } void PulseSupport::setupStreamEnvironment(QString streamUuid) { pDebug() << "Please note that your current Phonon backend is trying to force" " stream dependent PulseAudio properties through envrionment variables." " Slightly unprecise timing in doing so will cause the first" " of two subsequently started AudioOutputs to have disfunct volume" " control. Also see https://bugs.kde.org/show_bug.cgi?id=321288"; const QHash properties = streamProperties(streamUuid); QHashIterator it(properties); while (it.hasNext()) { it.next(); pDebug() << "PULSE_PROP_OVERRIDE_" << it.key() << " = " << it.value(); qputenv(QString("PULSE_PROP_OVERRIDE_%1").arg(it.key()).toUtf8(), it.value().toUtf8()); } } void PulseSupport::emitObjectDescriptionChanged(ObjectDescriptionType type) { if (isUsed()) emit objectDescriptionChanged(type); } bool PulseSupport::setOutputName(QString streamUuid, QString name) { #ifndef HAVE_PULSEAUDIO Q_UNUSED(streamUuid); Q_UNUSED(name); return false; #else logMessage(QString::fromLatin1("Unimplemented: Need to find a way to set either application.name or media.name in SI proplist")); Q_UNUSED(streamUuid); Q_UNUSED(name); return true; #endif } bool PulseSupport::setOutputDevice(QString streamUuid, int device) { #ifndef HAVE_PULSEAUDIO Q_UNUSED(streamUuid); Q_UNUSED(device); return false; #else if (s_outputDevices.size() < 2) return true; if (!s_outputDevices.contains(device)) { logMessage(QString::fromLatin1("Attempting to set Output Device for invalid device id %1.").arg(device)); return false; } const QVariant var = s_outputDevices[device].properties["name"]; logMessage(QString::fromLatin1("Attempting to set Output Device to '%1' for Output Stream %2").arg(var.toString()).arg(streamUuid)); // Attempt to look up the pulse stream index. if (s_outputStreams.contains(streamUuid) && s_outputStreams[streamUuid]->index() != PA_INVALID_INDEX) { logMessage(QString::fromLatin1("... Found in map. Moving now")); uint32_t pulse_device_index = s_outputDevices[device].pulseIndex; uint32_t pulse_stream_index = s_outputStreams[streamUuid]->index(); logMessage(QString::fromLatin1("Moving Pulse Sink Input %1 to '%2' (Pulse Sink %3)").arg(pulse_stream_index).arg(var.toString()).arg(pulse_device_index)); /// @todo Find a way to move the stream without saving it... We don't want to pollute the stream restore db. pa_operation* o; if (!(o = pa_context_move_sink_input_by_index(s_context, pulse_stream_index, pulse_device_index, NULL, NULL))) { logMessage(QString::fromLatin1("pa_context_move_sink_input_by_index() failed")); return false; } pa_operation_unref(o); } else { logMessage(QString::fromLatin1("... Not found in map. We will be notified of the device when the stream appears and we can process any moves needed then")); } return true; #endif } bool PulseSupport::setOutputVolume(QString streamUuid, qreal volume) { #ifndef HAVE_PULSEAUDIO Q_UNUSED(streamUuid); Q_UNUSED(volume); return false; #else logMessage(QString::fromLatin1("Attempting to set volume to %1 for Output Stream %2").arg(volume).arg(streamUuid)); // Attempt to look up the pulse stream index. if (s_outputStreams.contains(streamUuid) && s_outputStreams[streamUuid]->index() != PA_INVALID_INDEX) { PulseStream *stream = s_outputStreams[streamUuid]; uint8_t channels = stream->channels(); if (channels < 1) { logMessage(QString::fromLatin1("Channel count is less than 1. Cannot set volume.")); return false; } pa_cvolume vol; pa_cvolume_set(&vol, channels, (volume * PA_VOLUME_NORM)); logMessage(QString::fromLatin1("Found PA index %1. Calling pa_context_set_sink_input_volume()").arg(stream->index())); pa_operation* o; if (!(o = pa_context_set_sink_input_volume(s_context, stream->index(), &vol, NULL, NULL))) { logMessage(QString::fromLatin1("pa_context_set_sink_input_volume() failed")); return false; } pa_operation_unref(o); } else if (s_outputStreams.contains(streamUuid) && s_outputStreams[streamUuid]->index() == PA_INVALID_INDEX) { logMessage(QString::fromLatin1("Setting volume on an invalid stream ..... this better be intended")); PulseStream *stream = s_outputStreams[streamUuid]; stream->setCachedVolume(volume); } return true; #endif } bool PulseSupport::setOutputMute(QString streamUuid, bool mute) { #ifndef HAVE_PULSEAUDIO Q_UNUSED(streamUuid); Q_UNUSED(mute); return false; #else logMessage(QString::fromLatin1("Attempting to %1 mute for Output Stream %2").arg(mute ? "set" : "unset").arg(streamUuid)); // Attempt to look up the pulse stream index. if (s_outputStreams.contains(streamUuid) && s_outputStreams[streamUuid]->index() != PA_INVALID_INDEX) { PulseStream *stream = s_outputStreams[streamUuid]; logMessage(QString::fromLatin1("Found PA index %1. Calling pa_context_set_sink_input_mute()").arg(stream->index())); pa_operation* o; if (!(o = pa_context_set_sink_input_mute(s_context, stream->index(), (mute ? 1 : 0), NULL, NULL))) { logMessage(QString::fromLatin1("pa_context_set_sink_input_mute() failed")); return false; } pa_operation_unref(o); } return true; #endif } bool PulseSupport::setCaptureDevice(QString streamUuid, int device) { #ifndef HAVE_PULSEAUDIO Q_UNUSED(streamUuid); Q_UNUSED(device); return false; #else if (s_captureDevices.size() < 2) return true; if (!s_captureDevices.contains(device)) { logMessage(QString::fromLatin1("Attempting to set Capture Device for invalid device id %1.").arg(device)); return false; } const QVariant var = s_captureDevices[device].properties["name"]; logMessage(QString::fromLatin1("Attempting to set Capture Device to '%1' for Capture Stream %2").arg(var.toString()).arg(streamUuid)); // Attempt to look up the pulse stream index. if (s_captureStreams.contains(streamUuid) && s_captureStreams[streamUuid]->index() == PA_INVALID_INDEX) { logMessage(QString::fromLatin1("... Found in map. Moving now")); uint32_t pulse_device_index = s_captureDevices[device].pulseIndex; uint32_t pulse_stream_index = s_captureStreams[streamUuid]->index(); logMessage(QString::fromLatin1("Moving Pulse Source Output %1 to '%2' (Pulse Sink %3)").arg(pulse_stream_index).arg(var.toString()).arg(pulse_device_index)); /// @todo Find a way to move the stream without saving it... We don't want to pollute the stream restore db. pa_operation* o; if (!(o = pa_context_move_source_output_by_index(s_context, pulse_stream_index, pulse_device_index, NULL, NULL))) { logMessage(QString::fromLatin1("pa_context_move_source_output_by_index() failed")); return false; } pa_operation_unref(o); } else { logMessage(QString::fromLatin1("... Not found in map. We will be notified of the device when the stream appears and we can process any moves needed then")); } return true; #endif } void PulseSupport::clearStreamCache(QString streamUuid) { #ifndef HAVE_PULSEAUDIO Q_UNUSED(streamUuid); return; #else logMessage(QString::fromLatin1("Clearing stream cache for stream %1").arg(streamUuid)); if (s_outputStreams.contains(streamUuid)) { PulseStream *stream = s_outputStreams[streamUuid]; s_outputStreams.remove(streamUuid); delete stream; } else if (s_captureStreams.contains(streamUuid)) { PulseStream *stream = s_captureStreams[streamUuid]; s_captureStreams.remove(streamUuid); delete stream; } #endif } } // namespace Phonon #include "moc_pulsesupport.cpp"