mirror of
https://github.com/albfan/miraclecast.git
synced 2025-03-09 23:38:56 +00:00
run as source
This commit is contained in:
parent
439dac09c5
commit
fba5eb4a11
72 changed files with 9577 additions and 399 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -6,6 +6,7 @@
|
||||||
*.swp
|
*.swp
|
||||||
*.tar.xz
|
*.tar.xz
|
||||||
*.trs
|
*.trs
|
||||||
|
*.pcap
|
||||||
.deps/
|
.deps/
|
||||||
.dirstamp
|
.dirstamp
|
||||||
.libs/
|
.libs/
|
||||||
|
@ -41,4 +42,5 @@ cmake_install.cmake
|
||||||
CMakeCache.txt
|
CMakeCache.txt
|
||||||
libmiracle-shared.a
|
libmiracle-shared.a
|
||||||
install_manifest.txt
|
install_manifest.txt
|
||||||
|
.vimrc
|
||||||
/build/
|
/build/
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
cmake_minimum_required(VERSION 2.8)
|
cmake_minimum_required(VERSION 3.0.2)
|
||||||
|
|
||||||
project(Miraclecast)
|
project(Miraclecast)
|
||||||
|
|
||||||
SET(PACKAGE_NAME miraclecast)
|
SET(PACKAGE_NAME miraclecast)
|
||||||
SET(PACKAGE_VERSION 1)
|
SET(PACKAGE_VERSION 1)
|
||||||
SET(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
|
SET(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
|
||||||
|
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake
|
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
||||||
${CMAKE_MODULE_PATH})
|
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS "-std=gnu11 ${CMAKE_C_FLAGS}")
|
|
||||||
add_definitions(-D_GNU_SOURCE)
|
|
||||||
|
|
||||||
OPTION(ENABLE_SYSTEMD "Enable Systemd" ON)
|
OPTION(ENABLE_SYSTEMD "Enable Systemd" ON)
|
||||||
|
|
||||||
|
@ -41,6 +38,19 @@ set(DATADIR "${CMAKE_INSTALL_PREFIX}/share" CACHE STRING "shared data dir")
|
||||||
pkg_check_modules (GLIB2 REQUIRED glib-2.0)
|
pkg_check_modules (GLIB2 REQUIRED glib-2.0)
|
||||||
pkg_check_modules (UDEV REQUIRED libudev)
|
pkg_check_modules (UDEV REQUIRED libudev)
|
||||||
pkg_check_modules (SYSTEMD REQUIRED libsystemd)
|
pkg_check_modules (SYSTEMD REQUIRED libsystemd)
|
||||||
|
pkg_check_modules (GSTREAMER REQUIRED gstreamer-1.0)
|
||||||
|
pkg_check_modules (GSTREAMER_BASE REQUIRED gstreamer-base-1.0)
|
||||||
|
|
||||||
|
set(CMAKE_C_FLAGS "-std=gnu11 -Wall ${CMAKE_C_FLAGS}")
|
||||||
|
add_definitions(-D_GNU_SOURCE)
|
||||||
|
|
||||||
|
if(CMAKE_COMPILER_IS_GNUCC)
|
||||||
|
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
|
||||||
|
OUTPUT_VARIABLE GCC_VERSION)
|
||||||
|
if(GCC_VERSION VERSION_LESS 4.9)
|
||||||
|
message(FATAL_ERROR "gcc >= 4.9 is requred")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/config.h.cmake ${CMAKE_BINARY_DIR}/config.h)
|
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/config.h.cmake ${CMAKE_BINARY_DIR}/config.h)
|
||||||
|
|
||||||
|
|
17
README.md
17
README.md
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
The MiracleCast project provides software to connect external monitors to your system via Wi-Fi. It is compatible to the Wifi-Display specification also known as Miracast. MiracleCast implements the Display-Source as well as Display-Sink side.
|
The MiracleCast project provides software to connect external monitors to your system via Wi-Fi. It is compatible to the Wifi-Display specification also known as Miracast. MiracleCast implements the Display-Source as well as Display-Sink side.
|
||||||
|
|
||||||
The Display-Source side allows you to connect external displays to your system and stream local content to the device. A lot of effort is put into making this as easy as connecting external displays via HDMI. *Note: This is not implemented yet. Please see [#4](../../issues/4).*
|
The Display-Source side allows you to connect external displays to your system and stream local content to the device. A lot of effort is put into making this as easy as connecting external displays via HDMI.
|
||||||
|
|
||||||
On the other hand, the Display-Sink side allows you to create wifi-capable external displays yourself. You can use it on your embedded devices or even on full desktops to allow other systems to use your device as external display.
|
On the other hand, the Display-Sink side allows you to create wifi-capable external displays yourself. You can use it on your embedded devices or even on full desktops to allow other systems to use your device as external display.
|
||||||
|
|
||||||
|
@ -21,9 +21,20 @@ The MiracleCast projects requires the following software to be installed:
|
||||||
- **glib**: A utility library. Used by the current DHCP implementation. Will be removed once sd-dns gains DHCP-server capabilities.
|
- **glib**: A utility library. Used by the current DHCP implementation. Will be removed once sd-dns gains DHCP-server capabilities.
|
||||||
*required*: ~=glib2-2.38 (might work with older releases, untested..)
|
*required*: ~=glib2-2.38 (might work with older releases, untested..)
|
||||||
|
|
||||||
- **gstreamer**: MiracleCast rely on gstreamer to show cast its output. You can test if all needed is installed launching [res/test-viewer.sh](https://github.com/albfan/miraclecast/blob/master/res/test-viewer.sh)
|
- readline**: A library which is used to provide command line interface to control wifid, sink, etc..
|
||||||
|
|
||||||
- **wpa_supplicant**: MiracleCast spawns wpa_supplicant with a custom config.
|
- **check**: Test-suite for C programs. Used for optional tests of the MiracleCast code base.
|
||||||
|
*optional*: ~=check-0.9.11 (might work with older releases, untested..)
|
||||||
|
|
||||||
|
- **gstreamer**: MiracleCast rely on gstreamer to show cast its output. You can test if all needed is installed launching [res/test-viewer.sh](https://github.com/albfan/miraclecast/blob/master/res/test-viewer.sh).
|
||||||
|
|
||||||
|
- gstreamer plugins: here are the gstreamer plugins you need in order to run sinkctl or dispctl
|
||||||
|
- gstreamer-plugins-base
|
||||||
|
- gstreamer-plugins-good
|
||||||
|
- gstreamer-plugins-bad
|
||||||
|
- gstreamer-plugins-ugly
|
||||||
|
- gstreamer-plugins-vaapi
|
||||||
|
- gstreamer-plugins-libav
|
||||||
|
|
||||||
- **P2P Wi-Fi device** Although widespread these days, there are some devices not compatible with [Wi-Fi Direct](http://en.wikipedia.org/wiki/Wi-Fi_Direct) (prior know as Wi-Fi P2P). Test yours with [res/test-hardware-capabilities.sh](https://github.com/albfan/miraclecast/blob/master/res/test-hardware-capabilities.sh)
|
- **P2P Wi-Fi device** Although widespread these days, there are some devices not compatible with [Wi-Fi Direct](http://en.wikipedia.org/wiki/Wi-Fi_Direct) (prior know as Wi-Fi P2P). Test yours with [res/test-hardware-capabilities.sh](https://github.com/albfan/miraclecast/blob/master/res/test-hardware-capabilities.sh)
|
||||||
|
|
||||||
|
|
65
cmake/FindVala.cmake
Normal file
65
cmake/FindVala.cmake
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
##
|
||||||
|
# Copyright 2009-2010 Jakob Westhoff. All rights reserved.
|
||||||
|
#
|
||||||
|
# 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 above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer.
|
||||||
|
#
|
||||||
|
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer in the documentation
|
||||||
|
# and/or other materials provided with the distribution.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY JAKOB WESTHOFF ``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 JAKOB WESTHOFF OR CONTRIBUTORS 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.
|
||||||
|
#
|
||||||
|
# The views and conclusions contained in the software and documentation are those
|
||||||
|
# of the authors and should not be interpreted as representing official policies,
|
||||||
|
# either expressed or implied, of Jakob Westhoff
|
||||||
|
##
|
||||||
|
|
||||||
|
##
|
||||||
|
# Find module for the Vala compiler (valac)
|
||||||
|
#
|
||||||
|
# This module determines wheter a Vala compiler is installed on the current
|
||||||
|
# system and where its executable is.
|
||||||
|
#
|
||||||
|
# Call the module using "find_package(Vala) from within your CMakeLists.txt.
|
||||||
|
#
|
||||||
|
# The following variables will be set after an invocation:
|
||||||
|
#
|
||||||
|
# VALA_FOUND Whether the vala compiler has been found or not
|
||||||
|
# VALA_EXECUTABLE Full path to the valac executable if it has been found
|
||||||
|
# VALA_VERSION Version number of the available valac
|
||||||
|
##
|
||||||
|
|
||||||
|
|
||||||
|
# Search for the valac executable in the usual system paths.
|
||||||
|
find_program(VALA_EXECUTABLE
|
||||||
|
NAMES valac)
|
||||||
|
|
||||||
|
# Handle the QUIETLY and REQUIRED arguments, which may be given to the find call.
|
||||||
|
# Furthermore set VALA_FOUND to TRUE if Vala has been found (aka.
|
||||||
|
# VALA_EXECUTABLE is set)
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(Vala DEFAULT_MSG VALA_EXECUTABLE)
|
||||||
|
|
||||||
|
mark_as_advanced(VALA_EXECUTABLE)
|
||||||
|
|
||||||
|
# Determine the valac version
|
||||||
|
if(VALA_FOUND)
|
||||||
|
execute_process(COMMAND ${VALA_EXECUTABLE} "--version"
|
||||||
|
OUTPUT_VARIABLE "VALA_VERSION")
|
||||||
|
string(REPLACE "Vala" "" "VALA_VERSION" ${VALA_VERSION})
|
||||||
|
string(STRIP ${VALA_VERSION} "VALA_VERSION")
|
||||||
|
endif(VALA_FOUND)
|
36
cmake/ParseArguments.cmake
Normal file
36
cmake/ParseArguments.cmake
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
##
|
||||||
|
# This is a helper Macro to parse optional arguments in Macros/Functions
|
||||||
|
# It has been taken from the public CMake wiki.
|
||||||
|
# See http://www.cmake.org/Wiki/CMakeMacroParseArguments for documentation and
|
||||||
|
# licensing.
|
||||||
|
##
|
||||||
|
macro(parse_arguments prefix arg_names option_names)
|
||||||
|
set(DEFAULT_ARGS)
|
||||||
|
foreach(arg_name ${arg_names})
|
||||||
|
set(${prefix}_${arg_name})
|
||||||
|
endforeach(arg_name)
|
||||||
|
foreach(option ${option_names})
|
||||||
|
set(${prefix}_${option} FALSE)
|
||||||
|
endforeach(option)
|
||||||
|
|
||||||
|
set(current_arg_name DEFAULT_ARGS)
|
||||||
|
set(current_arg_list)
|
||||||
|
foreach(arg ${ARGN})
|
||||||
|
set(larg_names ${arg_names})
|
||||||
|
list(FIND larg_names "${arg}" is_arg_name)
|
||||||
|
if(is_arg_name GREATER -1)
|
||||||
|
set(${prefix}_${current_arg_name} ${current_arg_list})
|
||||||
|
set(current_arg_name ${arg})
|
||||||
|
set(current_arg_list)
|
||||||
|
else(is_arg_name GREATER -1)
|
||||||
|
set(loption_names ${option_names})
|
||||||
|
list(FIND loption_names "${arg}" is_option)
|
||||||
|
if(is_option GREATER -1)
|
||||||
|
set(${prefix}_${arg} TRUE)
|
||||||
|
else(is_option GREATER -1)
|
||||||
|
set(current_arg_list ${current_arg_list} ${arg})
|
||||||
|
endif(is_option GREATER -1)
|
||||||
|
endif(is_arg_name GREATER -1)
|
||||||
|
endforeach(arg)
|
||||||
|
set(${prefix}_${current_arg_name} ${current_arg_list})
|
||||||
|
endmacro(parse_arguments)
|
314
cmake/ValaPrecompile.cmake
Normal file
314
cmake/ValaPrecompile.cmake
Normal file
|
@ -0,0 +1,314 @@
|
||||||
|
##
|
||||||
|
# Copyright 2009-2010 Jakob Westhoff. All rights reserved.
|
||||||
|
# Copyright 2012 elementary.
|
||||||
|
#
|
||||||
|
# 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 above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer.
|
||||||
|
#
|
||||||
|
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer in the documentation
|
||||||
|
# and/or other materials provided with the distribution.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY JAKOB WESTHOFF ``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 JAKOB WESTHOFF OR CONTRIBUTORS 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.
|
||||||
|
#
|
||||||
|
# The views and conclusions contained in the software and documentation are those
|
||||||
|
# of the authors and should not be interpreted as representing official policies,
|
||||||
|
# either expressed or implied, of Jakob Westhoff
|
||||||
|
##
|
||||||
|
|
||||||
|
include(ParseArguments)
|
||||||
|
find_package(Vala REQUIRED)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Compile vala files to their c equivalents for further processing.
|
||||||
|
#
|
||||||
|
# The "vala_precompile" macro takes care of calling the valac executable on the
|
||||||
|
# given source to produce c files which can then be processed further using
|
||||||
|
# default cmake functions.
|
||||||
|
#
|
||||||
|
# The first parameter provided is a variable, which will be filled with a list
|
||||||
|
# of c files outputted by the vala compiler. This list can than be used in
|
||||||
|
# conjunction with functions like "add_executable" or others to create the
|
||||||
|
# necessary compile rules with CMake.
|
||||||
|
#
|
||||||
|
# The initial variable is followed by a list of .vala files to be compiled.
|
||||||
|
# Please take care to add every vala file belonging to the currently compiled
|
||||||
|
# project or library as Vala will otherwise not be able to resolve all
|
||||||
|
# dependencies.
|
||||||
|
#
|
||||||
|
# The following sections may be specified afterwards to provide certain options
|
||||||
|
# to the vala compiler:
|
||||||
|
#
|
||||||
|
# LIBRARY
|
||||||
|
# Indicates that this is to be compiled as a library.
|
||||||
|
#
|
||||||
|
# PACKAGES
|
||||||
|
# A list of vala packages/libraries to be used during the compile cycle. The
|
||||||
|
# package names are exactly the same, as they would be passed to the valac
|
||||||
|
# "--pkg=" option.
|
||||||
|
#
|
||||||
|
# OPTIONS
|
||||||
|
# A list of optional options to be passed to the valac executable. This can be
|
||||||
|
# used to pass "--thread" for example to enable multi-threading support.
|
||||||
|
#
|
||||||
|
# CUSTOM_VAPIS
|
||||||
|
# A list of custom vapi files to be included for compilation. This can be
|
||||||
|
# useful to include freshly created vala libraries without having to install
|
||||||
|
# them in the system.
|
||||||
|
#
|
||||||
|
# DEPENDS
|
||||||
|
# Additional files that may change the results of the outputed C files.
|
||||||
|
#
|
||||||
|
# GENERATE_VAPI [INTERNAL]
|
||||||
|
# Pass all the needed flags to the compiler to create a vapi for
|
||||||
|
# the compiled library. The provided name will be used for this and a
|
||||||
|
# <provided_name>.vapi file will be created. If INTERNAL is specified,
|
||||||
|
# an internal vapi <provided_name>_internal.vapi will be created as well.
|
||||||
|
# This option implies GENERATE_HEADER, so there is not need use GENERATE_HEADER
|
||||||
|
# in addition to GENERATE_VAPI unless they require different names. Requires
|
||||||
|
# that LIBRARY is set.
|
||||||
|
#
|
||||||
|
# GENERATE_HEADER [INTERNAL]
|
||||||
|
# Let the compiler generate a header file for the compiled code. There will
|
||||||
|
# be a header file being generated called <provided_name>.h. If INTERNAL
|
||||||
|
# is specified, an internal header <provided_name>_internal.h will be created
|
||||||
|
# as well.
|
||||||
|
#
|
||||||
|
# GENERATE_GIR [TYPELIB]
|
||||||
|
# Have the compiler generate a GObject-Introspection repository file with
|
||||||
|
# name: <provided_name>.gir. If TYPELIB is specified, the compiler will also
|
||||||
|
# create a binary typelib using the GI compiler. Requires that LIBRARY is set.
|
||||||
|
#
|
||||||
|
# TYPELIB_OPTIONS
|
||||||
|
# Additional options to pass to the GI compiler. Requires that GENERATE_GIR
|
||||||
|
# TYPELIB is set.
|
||||||
|
#
|
||||||
|
# GENERATE_SYMBOLS
|
||||||
|
# Output a <provided_name>.symbols file containing all the exported symbols.
|
||||||
|
#
|
||||||
|
# The following call is a simple example to the vala_precompile macro showing
|
||||||
|
# an example to every of the optional sections:
|
||||||
|
#
|
||||||
|
# vala_precompile(VALA_C mytargetname
|
||||||
|
# LIBRARY
|
||||||
|
# source1.vala
|
||||||
|
# source2.vala
|
||||||
|
# source3.vala
|
||||||
|
# PACKAGES
|
||||||
|
# gtk+-2.0
|
||||||
|
# gio-1.0
|
||||||
|
# posix
|
||||||
|
# DIRECTORY
|
||||||
|
# gen
|
||||||
|
# OPTIONS
|
||||||
|
# --thread
|
||||||
|
# CUSTOM_VAPIS
|
||||||
|
# some_vapi.vapi
|
||||||
|
# GENERATE_VAPI
|
||||||
|
# myvapi
|
||||||
|
# GENERATE_HEADER
|
||||||
|
# myheader
|
||||||
|
# GENERATE_GIR TYPELIB
|
||||||
|
# mygir
|
||||||
|
# TYPELIB_OPTIONS
|
||||||
|
# --includedir=some/dir
|
||||||
|
# GENERATE_SYMBOLS
|
||||||
|
# mysymbols
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
# Most important is the variable VALA_C which will contain all the generated c
|
||||||
|
# file names after the call.
|
||||||
|
##
|
||||||
|
|
||||||
|
macro(vala_precompile output target_name)
|
||||||
|
parse_arguments(ARGS
|
||||||
|
"TARGET;PACKAGES;OPTIONS;TYPELIB_OPTIONS;DIRECTORY;GENERATE_GIR;GENERATE_SYMBOLS;GENERATE_HEADER;GENERATE_VAPI;CUSTOM_VAPIS;DEPENDS"
|
||||||
|
"LIBRARY" ${ARGN})
|
||||||
|
|
||||||
|
if(ARGS_DIRECTORY)
|
||||||
|
set(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${ARGS_DIRECTORY})
|
||||||
|
else(ARGS_DIRECTORY)
|
||||||
|
set(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
endif(ARGS_DIRECTORY)
|
||||||
|
include_directories(${DIRECTORY})
|
||||||
|
set(vala_pkg_opts "")
|
||||||
|
foreach(pkg ${ARGS_PACKAGES})
|
||||||
|
list(APPEND vala_pkg_opts "--pkg=${pkg}")
|
||||||
|
endforeach(pkg ${ARGS_PACKAGES})
|
||||||
|
set(in_files "")
|
||||||
|
set(out_files "")
|
||||||
|
set(out_files_display "")
|
||||||
|
set(${output} "")
|
||||||
|
|
||||||
|
foreach(src ${ARGS_DEFAULT_ARGS})
|
||||||
|
# this string(REPLACE ...) is a workaround for a strange behavior when
|
||||||
|
# the cmake binary directory is a subdirectory of the source directory
|
||||||
|
# and you include a vala source file from the cmake binary directory.
|
||||||
|
# For a yet to be determined reason, cmake deletes the generated c file
|
||||||
|
# before it is compiled, resulting in an error. We fix this by making
|
||||||
|
# any absolute path that is in the source directory a relative path.
|
||||||
|
string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" src ${src})
|
||||||
|
|
||||||
|
string(REGEX MATCH "^/" IS_MATCHED ${src})
|
||||||
|
if(${IS_MATCHED} MATCHES "/")
|
||||||
|
set(src_file_path ${src})
|
||||||
|
else()
|
||||||
|
set(src_file_path ${CMAKE_CURRENT_SOURCE_DIR}/${src})
|
||||||
|
endif()
|
||||||
|
list(APPEND in_files ${src_file_path})
|
||||||
|
string(REPLACE ".vala" ".c" src ${src})
|
||||||
|
string(REPLACE ".gs" ".c" src ${src})
|
||||||
|
if(${IS_MATCHED} MATCHES "/")
|
||||||
|
get_filename_component(VALA_FILE_NAME ${src} NAME)
|
||||||
|
set(out_file "${CMAKE_CURRENT_BINARY_DIR}/${VALA_FILE_NAME}")
|
||||||
|
list(APPEND out_files "${CMAKE_CURRENT_BINARY_DIR}/${VALA_FILE_NAME}")
|
||||||
|
else()
|
||||||
|
set(out_file "${DIRECTORY}/${src}")
|
||||||
|
list(APPEND out_files "${DIRECTORY}/${src}")
|
||||||
|
endif()
|
||||||
|
list(APPEND ${output} ${out_file})
|
||||||
|
list(APPEND out_files_display "${src}")
|
||||||
|
endforeach(src ${ARGS_DEFAULT_ARGS})
|
||||||
|
|
||||||
|
set(custom_vapi_arguments "")
|
||||||
|
if(ARGS_CUSTOM_VAPIS)
|
||||||
|
foreach(vapi ${ARGS_CUSTOM_VAPIS})
|
||||||
|
if(${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR})
|
||||||
|
list(APPEND custom_vapi_arguments ${vapi})
|
||||||
|
else (${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR})
|
||||||
|
list(APPEND custom_vapi_arguments ${CMAKE_CURRENT_SOURCE_DIR}/${vapi})
|
||||||
|
endif(${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR})
|
||||||
|
endforeach(vapi ${ARGS_CUSTOM_VAPIS})
|
||||||
|
endif(ARGS_CUSTOM_VAPIS)
|
||||||
|
|
||||||
|
set(library_arguments "")
|
||||||
|
if(ARGS_LIBRARY)
|
||||||
|
list(APPEND library_arguments "--library=${target_name}")
|
||||||
|
endif(ARGS_LIBRARY)
|
||||||
|
|
||||||
|
set(vapi_arguments "")
|
||||||
|
if(ARGS_GENERATE_VAPI)
|
||||||
|
parse_arguments(ARGS_GENERATE_VAPI "" "INTERNAL" ${ARGS_GENERATE_VAPI})
|
||||||
|
list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_VAPI_DEFAULT_ARGS}.vapi")
|
||||||
|
list(APPEND out_files_display "${ARGS_GENERATE_VAPI_DEFAULT_ARGS}.vapi")
|
||||||
|
list(APPEND vapi_arguments "--vapi=${ARGS_GENERATE_VAPI_DEFAULT_ARGS}.vapi")
|
||||||
|
list(APPEND vapi_arguments "--vapi-comments")
|
||||||
|
|
||||||
|
# Header and internal header is needed to generate internal vapi
|
||||||
|
if (NOT ARGS_GENERATE_HEADER)
|
||||||
|
set(ARGS_GENERATE_HEADER ${ARGS_GENERATE_VAPI_DEFAULT_ARGS})
|
||||||
|
endif(NOT ARGS_GENERATE_HEADER)
|
||||||
|
|
||||||
|
if(ARGS_GENERATE_VAPI_INTERNAL)
|
||||||
|
list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_VAPI_DEFAULT_ARGS}_internal.vapi")
|
||||||
|
list(APPEND out_files_display "${ARGS_GENERATE_VAPI_DEFAULT_ARGS}_internal.vapi")
|
||||||
|
list(APPEND vapi_arguments "--internal-vapi=${ARGS_GENERATE_VAPI_DEFAULT_ARGS}_internal.vapi")
|
||||||
|
list(APPEND ARGS_GENERATE_HEADER "INTERNAL")
|
||||||
|
endif(ARGS_GENERATE_VAPI_INTERNAL)
|
||||||
|
endif(ARGS_GENERATE_VAPI)
|
||||||
|
|
||||||
|
set(header_arguments "")
|
||||||
|
if(ARGS_GENERATE_HEADER)
|
||||||
|
parse_arguments(ARGS_GENERATE_HEADER "" "INTERNAL" ${ARGS_GENERATE_HEADER})
|
||||||
|
list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_HEADER_DEFAULT_ARGS}.h")
|
||||||
|
list(APPEND out_files_display "${ARGS_GENERATE_HEADER_DEFAULT_ARGS}.h")
|
||||||
|
list(APPEND header_arguments "--header=${ARGS_GENERATE_HEADER_DEFAULT_ARGS}.h")
|
||||||
|
if(ARGS_GENERATE_HEADER_INTERNAL)
|
||||||
|
list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_HEADER_DEFAULT_ARGS}_internal.h")
|
||||||
|
list(APPEND out_files_display "${ARGS_GENERATE_HEADER_DEFAULT_ARGS}_internal.h")
|
||||||
|
list(APPEND header_arguments "--internal-header=${ARGS_GENERATE_HEADER_DEFAULT_ARGS}_internal.h")
|
||||||
|
endif(ARGS_GENERATE_HEADER_INTERNAL)
|
||||||
|
endif(ARGS_GENERATE_HEADER)
|
||||||
|
|
||||||
|
set(gir_arguments "")
|
||||||
|
set(gircomp_command "")
|
||||||
|
if(ARGS_GENERATE_GIR)
|
||||||
|
parse_arguments(ARGS_GENERATE_GIR "" "TYPELIB" ${ARGS_GENERATE_GIR})
|
||||||
|
list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_GIR_DEFAULT_ARGS}.gir")
|
||||||
|
list(APPEND out_files_display "${ARGS_GENERATE_GIR_DEFAULT_ARGS}.gir")
|
||||||
|
list(APPEND gir_arguments "--gir=${ARGS_GENERATE_GIR_DEFAULT_ARGS}.gir")
|
||||||
|
|
||||||
|
if(ARGS_GENERATE_GIR_TYPELIB)
|
||||||
|
include (FindGirCompiler)
|
||||||
|
find_package(GirCompiler REQUIRED)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT
|
||||||
|
"${DIRECTORY}/${ARGS_GENERATE_GIR_DEFAULT_ARGS}.typelib"
|
||||||
|
COMMAND
|
||||||
|
${G_IR_COMPILER_EXECUTABLE}
|
||||||
|
ARGS
|
||||||
|
"${DIRECTORY}/${ARGS_GENERATE_GIR_DEFAULT_ARGS}.gir"
|
||||||
|
"--shared-library=$<TARGET_SONAME_FILE_NAME:${target_name}>"
|
||||||
|
"--output=${DIRECTORY}/${ARGS_GENERATE_GIR_DEFAULT_ARGS}.typelib"
|
||||||
|
${ARGS_TYPELIB_OPTIONS}
|
||||||
|
DEPENDS
|
||||||
|
"${DIRECTORY}/${ARGS_GENERATE_GIR_DEFAULT_ARGS}.gir"
|
||||||
|
COMMENT
|
||||||
|
"Genterating typelib.")
|
||||||
|
|
||||||
|
add_custom_target("${target_name}-typelib"
|
||||||
|
ALL
|
||||||
|
DEPENDS
|
||||||
|
"${DIRECTORY}/${ARGS_GENERATE_GIR_DEFAULT_ARGS}.typelib")
|
||||||
|
endif(ARGS_GENERATE_GIR_TYPELIB)
|
||||||
|
endif(ARGS_GENERATE_GIR)
|
||||||
|
|
||||||
|
set(symbols_arguments "")
|
||||||
|
if(ARGS_GENERATE_SYMBOLS)
|
||||||
|
list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_SYMBOLS}.symbols")
|
||||||
|
list(APPEND out_files_display "${ARGS_GENERATE_SYMBOLS}.symbols")
|
||||||
|
set(symbols_arguments "--symbols=${ARGS_GENERATE_SYMBOLS}.symbols")
|
||||||
|
endif(ARGS_GENERATE_SYMBOLS)
|
||||||
|
|
||||||
|
# Workaround for a bug that would make valac run twice. This file is written
|
||||||
|
# after the vala compiler generates C source code.
|
||||||
|
set(OUTPUT_STAMP ${CMAKE_CURRENT_BINARY_DIR}/${target_name}_valac.stamp)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT
|
||||||
|
${OUTPUT_STAMP}
|
||||||
|
COMMAND
|
||||||
|
${VALA_EXECUTABLE}
|
||||||
|
ARGS
|
||||||
|
"-C"
|
||||||
|
${header_arguments}
|
||||||
|
${library_arguments}
|
||||||
|
${vapi_arguments}
|
||||||
|
${gir_arguments}
|
||||||
|
${symbols_arguments}
|
||||||
|
"-b" ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
"-d" ${DIRECTORY}
|
||||||
|
${vala_pkg_opts}
|
||||||
|
${ARGS_OPTIONS}
|
||||||
|
"--debug"
|
||||||
|
${in_files}
|
||||||
|
${custom_vapi_arguments}
|
||||||
|
COMMAND
|
||||||
|
touch
|
||||||
|
ARGS
|
||||||
|
${OUTPUT_STAMP}
|
||||||
|
DEPENDS
|
||||||
|
${in_files}
|
||||||
|
${ARGS_CUSTOM_VAPIS}
|
||||||
|
${ARGS_DEPENDS}
|
||||||
|
COMMENT
|
||||||
|
"Generating ${out_files_display}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# This command will be run twice for some reason (pass a non-empty string to COMMENT
|
||||||
|
# in order to see it). Since valac is not executed from here, this won't be a problem.
|
||||||
|
add_custom_command(OUTPUT ${out_files} DEPENDS ${OUTPUT_STAMP} COMMENT "")
|
||||||
|
endmacro(vala_precompile)
|
96
cmake/ValaVersion.cmake
Normal file
96
cmake/ValaVersion.cmake
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
##
|
||||||
|
# Copyright 2009-2010 Jakob Westhoff. All rights reserved.
|
||||||
|
#
|
||||||
|
# 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 above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer.
|
||||||
|
#
|
||||||
|
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer in the documentation
|
||||||
|
# and/or other materials provided with the distribution.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY JAKOB WESTHOFF ``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 JAKOB WESTHOFF OR CONTRIBUTORS 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.
|
||||||
|
#
|
||||||
|
# The views and conclusions contained in the software and documentation are those
|
||||||
|
# of the authors and should not be interpreted as representing official policies,
|
||||||
|
# either expressed or implied, of Jakob Westhoff
|
||||||
|
##
|
||||||
|
|
||||||
|
include(ParseArguments)
|
||||||
|
find_package(Vala REQUIRED)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Ensure a certain valac version is available
|
||||||
|
#
|
||||||
|
# The initial argument is the version to check for
|
||||||
|
#
|
||||||
|
# It may be followed by a optional parameter to specifiy a version range. The
|
||||||
|
# following options are valid:
|
||||||
|
#
|
||||||
|
# EXACT
|
||||||
|
# Vala needs to be available in the exact version given
|
||||||
|
#
|
||||||
|
# MINIMUM
|
||||||
|
# The provided version is the minimum version. Therefore Vala needs to be
|
||||||
|
# available in the given version or any higher version
|
||||||
|
#
|
||||||
|
# MAXIMUM
|
||||||
|
# The provided version is the maximum. Therefore Vala needs to be available
|
||||||
|
# in the given version or any version older than this
|
||||||
|
#
|
||||||
|
# If no option is specified the version will be treated as a minimal version.
|
||||||
|
##
|
||||||
|
macro(ensure_vala_version version)
|
||||||
|
parse_arguments(ARGS "" "MINIMUM;MAXIMUM;EXACT" ${ARGN})
|
||||||
|
set(compare_message "")
|
||||||
|
set(error_message "")
|
||||||
|
if(ARGS_MINIMUM)
|
||||||
|
set(compare_message "a minimum ")
|
||||||
|
set(error_message "or greater ")
|
||||||
|
elseif(ARGS_MAXIMUM)
|
||||||
|
set(compare_message "a maximum ")
|
||||||
|
set(error_message "or less ")
|
||||||
|
endif(ARGS_MINIMUM)
|
||||||
|
|
||||||
|
message(STATUS
|
||||||
|
"checking for ${compare_message}Vala version of ${version}"
|
||||||
|
)
|
||||||
|
|
||||||
|
unset(version_accepted)
|
||||||
|
|
||||||
|
# MINIMUM is the default if no option is specified
|
||||||
|
if(ARGS_EXACT)
|
||||||
|
if(${VALA_VERSION} VERSION_EQUAL ${version} )
|
||||||
|
set(version_accepted TRUE)
|
||||||
|
endif(${VALA_VERSION} VERSION_EQUAL ${version})
|
||||||
|
elseif(ARGS_MAXIMUM)
|
||||||
|
if(${VALA_VERSION} VERSION_LESS ${version} OR ${VALA_VERSION} VERSION_EQUAL ${version})
|
||||||
|
set(version_accepted TRUE)
|
||||||
|
endif(${VALA_VERSION} VERSION_LESS ${version} OR ${VALA_VERSION} VERSION_EQUAL ${version})
|
||||||
|
else(ARGS_MAXIMUM)
|
||||||
|
if(${VALA_VERSION} VERSION_GREATER ${version} OR ${VALA_VERSION} VERSION_EQUAL ${version})
|
||||||
|
set(version_accepted TRUE)
|
||||||
|
endif(${VALA_VERSION} VERSION_GREATER ${version} OR ${VALA_VERSION} VERSION_EQUAL ${version})
|
||||||
|
endif(ARGS_EXACT)
|
||||||
|
|
||||||
|
if (NOT version_accepted)
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"Vala version ${version} ${error_message}is required."
|
||||||
|
)
|
||||||
|
endif(NOT version_accepted)
|
||||||
|
|
||||||
|
message(STATUS
|
||||||
|
" found Vala, version ${VALA_VERSION}"
|
||||||
|
)
|
||||||
|
endmacro(ensure_vala_version)
|
11
debian/changelog
vendored
Normal file
11
debian/changelog
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
miraclecast (1.0+r67) experimental; urgency=medium
|
||||||
|
|
||||||
|
* Jenkins Auto Build
|
||||||
|
|
||||||
|
-- <jenkins@45cd608b91cd> Wed, 04 Jan 2017 10:27:21 +0000
|
||||||
|
|
||||||
|
miraclecast (1.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Initial release
|
||||||
|
|
||||||
|
-- Deepin Packages Builder <packages@deepin.com> Mon, 05 Dec 2016 13:43:00 +0800
|
1
debian/compat
vendored
Normal file
1
debian/compat
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
9
|
12
debian/control
vendored
Normal file
12
debian/control
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
Source: miraclecast
|
||||||
|
Section: utils
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Deepin Packages Builder <packages@deepin.com>
|
||||||
|
Build-Depends: debhelper (>= 9), cmake, check, valac, vala-dbus-binding-tool, systemd, libsystemd-dev, libglib2.0-bin, libgtk-3-dev, libgstreamer1.0-dev, libreadline-dev, libudev-dev
|
||||||
|
Standards-Version: 3.9.8
|
||||||
|
Homepage: https://github.com/derekdai/miraclecast
|
||||||
|
|
||||||
|
Package: miraclecast
|
||||||
|
Architecture: any
|
||||||
|
Depends: ${shlibs:Depends}, ${misc:Depends}, libsystemd0, libudev1, libglib2.0-0, libgstreamer1.0-0, gstreamer1.0-pulseaudio, gstreamer1.0-vaapi (>= 1.10.2), gstreamer1.0-plugins-good, gstreamer1.0-plugins-bad, gstreamer1.0-plugins-ugly, mesa-va-drivers, i965-va-driver, vdpau-va-driver
|
||||||
|
Description: Connect external monitors to your system via Wifi-Display specification also known as Miracast
|
38
debian/copyright
vendored
Normal file
38
debian/copyright
vendored
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Upstream-Name: miraclecast
|
||||||
|
Source: <url://example.com>
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: <years> <put author's name and email here>
|
||||||
|
<years> <likewise for another author>
|
||||||
|
License: <special license>
|
||||||
|
<Put the license of the package here indented by 1 space>
|
||||||
|
<This follows the format of Description: lines in control file>
|
||||||
|
.
|
||||||
|
<Including paragraphs>
|
||||||
|
|
||||||
|
# If you want to use GPL v2 or later for the /debian/* files use
|
||||||
|
# the following clauses, or change it to suit. Delete these two lines
|
||||||
|
Files: debian/*
|
||||||
|
Copyright: 2016 Deepin Packages Builder <packages@deepin.com>
|
||||||
|
License: GPL-2+
|
||||||
|
This package is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
.
|
||||||
|
This package is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
.
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
|
.
|
||||||
|
On Debian systems, the complete text of the GNU General
|
||||||
|
Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
|
||||||
|
|
||||||
|
# Please also look if there are files or directories which have a
|
||||||
|
# different copyright/license attached and list them here.
|
||||||
|
# Please avoid picking licenses with terms that are more restrictive than the
|
||||||
|
# packaged work, as it may make Debian's contributions unacceptable upstream.
|
1
debian/postinst
vendored
Normal file
1
debian/postinst
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
systemctl daemon-reload
|
1
debian/postrm
vendored
Normal file
1
debian/postrm
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
systemctl daemon-reload
|
22
debian/rules
vendored
Executable file
22
debian/rules
vendored
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
#!/usr/bin/make -f
|
||||||
|
# See debhelper(7) (uncomment to enable)
|
||||||
|
# output every command that modifies files on the build system.
|
||||||
|
#export DH_VERBOSE = 1
|
||||||
|
|
||||||
|
|
||||||
|
# see FEATURE AREAS in dpkg-buildflags(1)
|
||||||
|
#export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||||
|
|
||||||
|
# see ENVIRONMENT in dpkg-buildflags(1)
|
||||||
|
# package maintainers to append CFLAGS
|
||||||
|
#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
|
||||||
|
# package maintainers to append LDFLAGS
|
||||||
|
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
|
||||||
|
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@
|
||||||
|
|
||||||
|
|
||||||
|
# dh_make generated override targets
|
||||||
|
# This is example for Cmake (See https://bugs.debian.org/641051 )
|
1
debian/source/format
vendored
Normal file
1
debian/source/format
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
3.0 (native)
|
|
@ -52,4 +52,3 @@ subdir('res')
|
||||||
if get_option('build-tests')
|
if get_option('build-tests')
|
||||||
subdir('test')
|
subdir('test')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,130 @@
|
||||||
INSTALL(
|
include(ValaPrecompile)
|
||||||
PROGRAMS miracle-gst gstplayer uibc-viewer
|
pkg_check_modules(GIO2 REQUIRED gio-2.0)
|
||||||
DESTINATION bin
|
pkg_check_modules(GDK3 REQUIRED gdk-3.0)
|
||||||
)
|
pkg_check_modules(GST1 REQUIRED gstreamer-1.0)
|
||||||
|
|
||||||
INSTALL(
|
include_directories(
|
||||||
FILES org.freedesktop.miracle.conf
|
${GST1_INCLUDE_DIRS}
|
||||||
DESTINATION ${SYSCONFDIR}/dbus-1/system.d
|
${GDK3_INCLUDE_DIRS}
|
||||||
)
|
${GIO_INCLUDE_DIRS}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
INSTALL(
|
set(CMAKE_C_FLAGS "-Wno-deprecated-declarations ${CMAKE_C_FLAGS}")
|
||||||
|
set(CMAKE_C_FLAGS "-Wno-unused-but-set-variable ${CMAKE_C_FLAGS}")
|
||||||
|
set(CMAKE_C_FLAGS "-Wno-missing-braces ${CMAKE_C_FLAGS}")
|
||||||
|
if(VALA_VERSION VERSION_GREATER 0.34.0 AND NOT GDK_VERSION_NEWER_THEN_3_22)
|
||||||
|
list(APPEND VALA_EXTRA_OPTIONS -D GDK3_HAS_MONITOR_CLASS)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
vala_precompile(
|
||||||
|
GSTENCODER_SRC gstencoder
|
||||||
|
gstencoder.vala
|
||||||
|
OPTIONS
|
||||||
|
--target-glib=2.50
|
||||||
|
PACKAGES
|
||||||
|
gstreamer-1.0
|
||||||
|
gio-2.0
|
||||||
|
posix
|
||||||
|
)
|
||||||
|
add_executable(gstencoder ${GSTENCODER_SRC})
|
||||||
|
target_link_libraries(
|
||||||
|
gstencoder
|
||||||
|
${GST1_LIBRARIES}
|
||||||
|
${GDK3_LIBRARIES}
|
||||||
|
${GIO2_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
||||||
|
vala_precompile(
|
||||||
|
DISPCTL_SRC dispctl
|
||||||
|
dispctl.vala
|
||||||
|
networkmanager.vala
|
||||||
|
miracle-wifi.vala
|
||||||
|
miracle-wfd.vala
|
||||||
|
GENERATE_HEADER
|
||||||
|
dispctl.h
|
||||||
|
CUSTOM_VAPIS
|
||||||
|
sigint.vapi
|
||||||
|
OPTIONS
|
||||||
|
--target-glib=2.50
|
||||||
|
${VALA_EXTRA_OPTIONS}
|
||||||
|
PACKAGES
|
||||||
|
gio-2.0
|
||||||
|
gdk-3.0
|
||||||
|
)
|
||||||
|
add_executable(miracle-dispctl ${DISPCTL_SRC} sigint.c)
|
||||||
|
target_link_libraries(miracle-dispctl ${GIO2_LIBRARIES} ${GDK3_LIBRARIES})
|
||||||
|
|
||||||
|
########### install files ###############
|
||||||
|
|
||||||
|
install(TARGETS gstencoder DESTINATION bin)
|
||||||
|
|
||||||
|
install(TARGETS miracle-dispctl DESTINATION bin)
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=system_bus_services_dir dbus-1
|
||||||
|
OUTPUT_VARIABLE DBUS_SYSTEM_SERVICES_DIR
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=systemdsystemunitdir systemd
|
||||||
|
OUTPUT_VARIABLE SYSTEMD_SYSTEM_UNIT_DIR
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
miracle-wifid.service.cmake
|
||||||
|
miracle-wifid.service
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
FILES ${CMAKE_CURRENT_BINARY_DIR}/miracle-wifid.service
|
||||||
|
DESTINATION ${SYSTEMD_SYSTEM_UNIT_DIR}/
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink miracle-wifid.service \$ENV{DESTDIR}${SYSTEMD_SYSTEM_UNIT_DIR}/dbus-org.freedesktop.miracle.wifi.service)"
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
miracle-dispd.service.cmake
|
||||||
|
miracle-dispd.service
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
FILES ${CMAKE_CURRENT_BINARY_DIR}/miracle-dispd.service
|
||||||
|
DESTINATION ${SYSTEMD_SYSTEM_UNIT_DIR}/
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink miracle-dispd.service \$ENV{DESTDIR}${SYSTEMD_SYSTEM_UNIT_DIR}/dbus-org.freedesktop.miracle.wfd.service)"
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
org.freedesktop.miracle.wifi.service.cmake
|
||||||
|
org.freedesktop.miracle.wifi.service
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
FILES ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.miracle.wifi.service
|
||||||
|
DESTINATION $ENV{DESTDIR}${DBUS_SYSTEM_SERVICES_DIR}/
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
org.freedesktop.miracle.wfd.service.cmake
|
||||||
|
org.freedesktop.miracle.wfd.service
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
FILES ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.miracle.wfd.service
|
||||||
|
DESTINATION $ENV{DESTDIR}${DBUS_SYSTEM_SERVICES_DIR}/
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
PROGRAMS miracle-gst gstplayer uibc-viewer
|
||||||
|
DESTINATION bin
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
FILES org.freedesktop.miracle.conf
|
||||||
|
DESTINATION ${SYSCONFDIR}/dbus-1/system.d
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
FILES miracle-wifid miracle-sinkctl miracle-wifictl
|
FILES miracle-wifid miracle-sinkctl miracle-wifictl
|
||||||
DESTINATION ${DATADIR}/bash-completion/completions
|
DESTINATION ${DATADIR}/bash-completion/completions
|
||||||
)
|
)
|
||||||
|
|
6
res/create-symlinks
Executable file
6
res/create-symlinks
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/sh
|
||||||
|
SYSTEMD_SYSTEM_UNIT_DIR=${DESTDIR}`pkg-config --variable=systemdsystemunitdir systemd`
|
||||||
|
ln -sfv miracle-wifid.service \
|
||||||
|
${SYSTEMD_SYSTEM_UNIT_DIR}/dbos-org.freedesktop.miracle.wifi.service
|
||||||
|
ln -sfv miracle-dispd.service \
|
||||||
|
${SYSTEMD_SYSTEM_UNIT_DIR}/dbos-org.freedesktop.miracle.wfd.service
|
824
res/dispctl.vala
Normal file
824
res/dispctl.vala
Normal file
|
@ -0,0 +1,824 @@
|
||||||
|
/*
|
||||||
|
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||||
|
*
|
||||||
|
* MiracleCast 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) any later version.
|
||||||
|
*
|
||||||
|
* MiracleCast 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 MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using Org.Freedesktop.NetworkManager;
|
||||||
|
using Org.Freedesktop.Miracle.Wifi;
|
||||||
|
using Org.Freedesktop.Miracle.Wfd;
|
||||||
|
|
||||||
|
const string BUS_NAME_NETWORK_MANAGER = "org.freedesktop.NetworkManager";
|
||||||
|
const string BUS_NAME_WIFID = "org.freedesktop.miracle.wifi";
|
||||||
|
const string BUS_NAME_DISPD = "org.freedesktop.miracle.wfd";
|
||||||
|
const string OBJ_PATH_DEVICE = "/org/freedesktop/NetworkManager/Devices";
|
||||||
|
const string OBJ_PATH_LINK = "/org/freedesktop/miracle/wifi/link";
|
||||||
|
const string OBJ_PATH_PEER = "/org/freedesktop/miracle/wifi/peer";
|
||||||
|
const string OBJ_PATH_SINK = "/org/freedesktop/miracle/wfd/sink";
|
||||||
|
const string OBJ_PATH_SESSION = "/org/freedesktop/miracle/wfd/session";
|
||||||
|
const string IFACE_DEVICE = "org.freedesktop.NetworkManager.Device";
|
||||||
|
const string IFACE_LINK = "org.freedesktop.miracle.wifi.Link";
|
||||||
|
const string IFACE_PEER = "org.freedesktop.miracle.wifi.Peer";
|
||||||
|
const string IFACE_SINK = "org.freedesktop.miracle.wfd.Sink";
|
||||||
|
const string IFACE_SESSION = "org.freedesktop.miracle.wfd.Session";
|
||||||
|
|
||||||
|
errordomain DispCtlError
|
||||||
|
{
|
||||||
|
NO_SUCH_NIC,
|
||||||
|
TIMEOUT,
|
||||||
|
MONITOR_GONE,
|
||||||
|
FORMATION_ERROR,
|
||||||
|
NO_P2P_SUPPORT,
|
||||||
|
}
|
||||||
|
|
||||||
|
private void print(string format, ...)
|
||||||
|
{
|
||||||
|
stderr.printf("%s: ", Environment.get_prgname());
|
||||||
|
stderr.vprintf(format, va_list());
|
||||||
|
stderr.printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// to deal with sd_bus_path_encode/decode()ed path
|
||||||
|
private string decode_path(string s)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
StringBuilder d = new StringBuilder();
|
||||||
|
for(var i = 0; i < s.length; i ++) {
|
||||||
|
if(s.data[i] == (uint8) '_') {
|
||||||
|
c = (char) s.substring(i + 1, 2).to_long(null, 16);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
c = (char) s.data[i];
|
||||||
|
}
|
||||||
|
d.append_c(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.str;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DispCtl : GLib.Application
|
||||||
|
{
|
||||||
|
static string opt_iface;
|
||||||
|
static string opt_wfd_subelems;
|
||||||
|
static string opt_peer_mac;
|
||||||
|
static string opt_display;
|
||||||
|
static string opt_authority;
|
||||||
|
static int opt_monitor_num;
|
||||||
|
static string opt_audio_device;
|
||||||
|
static bool opt_dont_borrow_wnic = false;
|
||||||
|
static bool opt_dont_return_wnic = false;
|
||||||
|
|
||||||
|
protected signal void link_added(string index, Link link);
|
||||||
|
protected signal void link_removed(string index, Link link);
|
||||||
|
protected signal void peer_added(string label, Peer peer);
|
||||||
|
protected signal void peer_removed(string label, Peer peer);
|
||||||
|
protected signal void sink_added(string label, Sink sink);
|
||||||
|
protected signal void sink_removed(string label, Sink sink);
|
||||||
|
protected signal void session_added(string id, Session session);
|
||||||
|
protected signal void session_removed(string id, Session session);
|
||||||
|
|
||||||
|
DBusObjectManagerClient nm;
|
||||||
|
DBusObjectManagerClient wifi;
|
||||||
|
DBusObjectManagerClient wfd;
|
||||||
|
|
||||||
|
HashTable<string, Device> devices;
|
||||||
|
HashTable<string, Link> links;
|
||||||
|
HashTable<string, Peer> peers;
|
||||||
|
HashTable<string, Sink> sinks;
|
||||||
|
HashTable<string, Session> sessions;
|
||||||
|
|
||||||
|
string curr_sink_mac;
|
||||||
|
Gdk.Display display;
|
||||||
|
Peer curr_peer;
|
||||||
|
Sink curr_sink;
|
||||||
|
Session curr_session;
|
||||||
|
|
||||||
|
Cancellable cancellable;
|
||||||
|
|
||||||
|
const GLib.OptionEntry[] option_entries = {
|
||||||
|
{ "interface", 'i', 0, OptionArg.STRING, ref opt_iface, "name of wireless network interface", "WNIC name" },
|
||||||
|
{ "wfd-subelems", 'w', 0, OptionArg.STRING, ref opt_wfd_subelems, "device infomation. default: 000600901c4400c8", "device info subelems" },
|
||||||
|
{ "peer-mac", 'p', 0, OptionArg.STRING, ref opt_peer_mac, "MAC address of target peer", "peer MAC" },
|
||||||
|
{ "authority", 'x', 0, OptionArg.STRING, ref opt_authority, "authority to capture from display. default: XAUTHORITY environment variable", "display authority" },
|
||||||
|
{ "display", 'd', 0, OptionArg.STRING, ref opt_display, "display name. default: DISPLAY environment variable", "display name" },
|
||||||
|
{ "monitor-num", 'm', 0, OptionArg.INT, ref opt_monitor_num, "monitor number. default: -1, primary monitor", "monitor number" },
|
||||||
|
{ "audio-device", 'a', 0, OptionArg.STRING, ref opt_audio_device, "pulseaudio device name", "audio device name" },
|
||||||
|
{ "dont-borrow", 'b', 0, OptionArg.NONE, ref opt_dont_borrow_wnic, "do not acquire the ownership of WNIC before using it", "don't borrow WNIC" },
|
||||||
|
{ "dont-return", 'r', 0, OptionArg.NONE, ref opt_dont_return_wnic, "do not release the ownership of WNIC after using it", "don't release WNIC" },
|
||||||
|
{ null },
|
||||||
|
};
|
||||||
|
|
||||||
|
public DispCtl()
|
||||||
|
{
|
||||||
|
Object(application_id: "org.freedesktop.miracle.DispCtl",
|
||||||
|
flags: ApplicationFlags.FLAGS_NONE);
|
||||||
|
|
||||||
|
devices = new HashTable<string, Device>(str_hash, str_equal);
|
||||||
|
links = new HashTable<string, Link>(str_hash, str_equal);
|
||||||
|
peers = new HashTable<string, Peer>(str_hash, str_equal);
|
||||||
|
sinks = new HashTable<string, Sink>(str_hash, str_equal);
|
||||||
|
sessions = new HashTable<string, Session>(str_hash, str_equal);
|
||||||
|
|
||||||
|
cancellable = new Cancellable();
|
||||||
|
|
||||||
|
add_main_option_entries(option_entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DBusProxy? add_object(string path) throws Error
|
||||||
|
{
|
||||||
|
int sep = path.last_index_of_char('/');
|
||||||
|
string prefix = path.substring(0, sep);
|
||||||
|
string key = path.substring(sep + 1);
|
||||||
|
switch(prefix) {
|
||||||
|
case OBJ_PATH_DEVICE:
|
||||||
|
Device d = Bus.get_proxy_sync(BusType.SYSTEM,
|
||||||
|
BUS_NAME_NETWORK_MANAGER,
|
||||||
|
path);
|
||||||
|
if(is_wnic(d.interface) && !devices.contains(d.interface)) {
|
||||||
|
devices.insert(d.interface, d);
|
||||||
|
return d as DBusProxy;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OBJ_PATH_LINK:
|
||||||
|
key = decode_path(key);
|
||||||
|
Link l = links.lookup(key);
|
||||||
|
if(null == l) {
|
||||||
|
l = Bus.get_proxy_sync(BusType.SYSTEM,
|
||||||
|
BUS_NAME_WIFID,
|
||||||
|
path);
|
||||||
|
links.insert(key, l);
|
||||||
|
info("found wireless interface: %s", l.interface_name);
|
||||||
|
link_added(key, l);
|
||||||
|
}
|
||||||
|
return l as DBusProxy;
|
||||||
|
case OBJ_PATH_PEER:
|
||||||
|
key = decode_path(key);
|
||||||
|
Peer p = peers.lookup(key);
|
||||||
|
if(null == p) {
|
||||||
|
p = Bus.get_proxy_sync(BusType.SYSTEM,
|
||||||
|
BUS_NAME_WIFID,
|
||||||
|
path);
|
||||||
|
peers.insert(key, p);
|
||||||
|
info("peer added: %s (%s)", key, p.friendly_name);
|
||||||
|
peer_added(key, p);
|
||||||
|
}
|
||||||
|
return p as DBusProxy;
|
||||||
|
case OBJ_PATH_SINK:
|
||||||
|
key = decode_path(key);
|
||||||
|
Sink s = sinks.lookup(key);
|
||||||
|
if(null == s) {
|
||||||
|
s = Bus.get_proxy_sync(BusType.SYSTEM,
|
||||||
|
BUS_NAME_DISPD,
|
||||||
|
path);
|
||||||
|
sinks.insert(key, s);
|
||||||
|
info("sink added: %s", key);
|
||||||
|
sink_added(key, s);
|
||||||
|
}
|
||||||
|
return s as DBusProxy;
|
||||||
|
case OBJ_PATH_SESSION:
|
||||||
|
key = decode_path(key);
|
||||||
|
Session s = sessions.lookup(key);
|
||||||
|
if(null == s) {
|
||||||
|
s = Bus.get_proxy_sync(BusType.SYSTEM,
|
||||||
|
BUS_NAME_DISPD,
|
||||||
|
path);
|
||||||
|
sessions.insert(key, s);
|
||||||
|
info("session added: %s", key);
|
||||||
|
session_added(key, s);
|
||||||
|
}
|
||||||
|
return s as DBusProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void remove_object(string path)
|
||||||
|
{
|
||||||
|
int sep = path.last_index_of_char('/');
|
||||||
|
string prefix = path.substring(0, sep);
|
||||||
|
string key = path.substring(sep + 1);
|
||||||
|
switch(prefix) {
|
||||||
|
case OBJ_PATH_DEVICE:
|
||||||
|
devices.remove(key);
|
||||||
|
break;
|
||||||
|
case OBJ_PATH_LINK:
|
||||||
|
key = decode_path(key);
|
||||||
|
Link l = links.lookup(key);
|
||||||
|
if(null == l) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
links.remove(key);
|
||||||
|
link_removed(key, l);
|
||||||
|
break;
|
||||||
|
case OBJ_PATH_PEER:
|
||||||
|
key = decode_path(key);
|
||||||
|
Peer p = peers.lookup(key);
|
||||||
|
if(null == p) {
|
||||||
|
info("removing stray peer: %s", key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(p == curr_peer) {
|
||||||
|
curr_peer = null;
|
||||||
|
}
|
||||||
|
peers.remove(key);
|
||||||
|
peer_removed(key, p);
|
||||||
|
break;
|
||||||
|
case OBJ_PATH_SINK:
|
||||||
|
key = decode_path(key);
|
||||||
|
Sink s = sinks.lookup(key);
|
||||||
|
if(null == s) {
|
||||||
|
info("removing stray sink: %s", key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(s == curr_sink) {
|
||||||
|
curr_sink = null;
|
||||||
|
}
|
||||||
|
sinks.remove(key);
|
||||||
|
sink_removed(key, s);
|
||||||
|
break;
|
||||||
|
case OBJ_PATH_SESSION:
|
||||||
|
key = decode_path(key);
|
||||||
|
Session s = sessions.lookup(key);
|
||||||
|
if(null == s) {
|
||||||
|
info("removing stray session: %s", key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
session_removed(key, s);
|
||||||
|
sessions.remove(key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void on_object_added(DBusObjectManager m, DBusObject o)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
add_object(o.get_object_path());
|
||||||
|
}
|
||||||
|
catch(Error e) {
|
||||||
|
print("failed to fetch information from DBus for object: %s",
|
||||||
|
o.get_object_path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void on_object_removed(DBusObjectManager m, DBusObject o)
|
||||||
|
{
|
||||||
|
remove_object(o.get_object_path());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetch_info_from_dbus() throws Error
|
||||||
|
{
|
||||||
|
info("connecting to wifid...");
|
||||||
|
wifi = new DBusObjectManagerClient.for_bus_sync(
|
||||||
|
BusType.SYSTEM,
|
||||||
|
DBusObjectManagerClientFlags.NONE,
|
||||||
|
BUS_NAME_WIFID,
|
||||||
|
"/org/freedesktop/miracle/wifi",
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
wifi.object_added.connect(on_object_added);
|
||||||
|
wifi.object_removed.connect(on_object_removed);
|
||||||
|
|
||||||
|
info("connecting to nm...");
|
||||||
|
nm = new DBusObjectManagerClient.for_bus_sync(
|
||||||
|
BusType.SYSTEM,
|
||||||
|
DBusObjectManagerClientFlags.NONE,
|
||||||
|
BUS_NAME_NETWORK_MANAGER,
|
||||||
|
"/org/freedesktop",
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
nm.object_added.connect(on_object_added);
|
||||||
|
nm.object_removed.connect(on_object_removed);
|
||||||
|
|
||||||
|
info("connecting to dispd...");
|
||||||
|
wfd = new DBusObjectManagerClient.for_bus_sync(
|
||||||
|
BusType.SYSTEM,
|
||||||
|
DBusObjectManagerClientFlags.NONE,
|
||||||
|
BUS_NAME_DISPD,
|
||||||
|
"/org/freedesktop/miracle/wfd",
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
wfd.object_added.connect(on_object_added);
|
||||||
|
wfd.object_removed.connect(on_object_removed);
|
||||||
|
|
||||||
|
info("fetching from wifid...");
|
||||||
|
foreach(var o in wifi.get_objects()) {
|
||||||
|
add_object(o.get_object_path());
|
||||||
|
}
|
||||||
|
|
||||||
|
info("fetching from nm...");
|
||||||
|
foreach(var o in nm.get_objects()) {
|
||||||
|
add_object(o.get_object_path());
|
||||||
|
}
|
||||||
|
|
||||||
|
info("fetching from wfd...");
|
||||||
|
foreach(var o in wfd.get_objects()) {
|
||||||
|
add_object(o.get_object_path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void acquire_wnic_ownership() throws Error
|
||||||
|
{
|
||||||
|
Device d = find_device_by_name(opt_iface);
|
||||||
|
if(null != d && d.managed) {
|
||||||
|
info("NetworkManager is releasing ownership of %s...", opt_iface);
|
||||||
|
|
||||||
|
d.managed = false;
|
||||||
|
yield wait_prop_changed(d, "Managed");
|
||||||
|
}
|
||||||
|
|
||||||
|
Link l = find_link_by_name(opt_iface);
|
||||||
|
if(null == l) {
|
||||||
|
throw new DispCtlError.NO_SUCH_NIC("no such wireless adapter: %s",
|
||||||
|
opt_iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(l.managed) {
|
||||||
|
info("wifid is releasing ownership of %s...", opt_iface);
|
||||||
|
l.unmanage();
|
||||||
|
yield wait_prop_changed(l, "Managed");
|
||||||
|
}
|
||||||
|
|
||||||
|
info("wifid is acquiring ownership of %s...", opt_iface);
|
||||||
|
l.manage();
|
||||||
|
yield wait_prop_changed(l, "Managed");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void start_p2p_scan() throws Error
|
||||||
|
{
|
||||||
|
Link? l = find_link_by_name(opt_iface);
|
||||||
|
if(l.wfd_subelements != opt_wfd_subelems) {
|
||||||
|
info("update wfd_subelems to broadcast what kind of device we are");
|
||||||
|
|
||||||
|
l.wfd_subelements = opt_wfd_subelems;
|
||||||
|
yield wait_prop_changed(l, "WfdSubelements");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(-1 == l.p2p_state) {
|
||||||
|
throw new DispCtlError.NO_P2P_SUPPORT("link %s has no P2P supporting", l.interface_name);
|
||||||
|
}
|
||||||
|
else if(0 == l.p2p_state) {
|
||||||
|
info("wait for P2P supporting status...");
|
||||||
|
yield wait_prop_changed(l, "P2PState", 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!l.p2p_scanning) {
|
||||||
|
info("start P2P scanning...");
|
||||||
|
l.p2p_scanning = true;
|
||||||
|
yield wait_prop_changed(l, "P2PScanning");
|
||||||
|
}
|
||||||
|
|
||||||
|
print("wait for peer '%s'...", opt_peer_mac);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void wait_for_target_sink() throws Error
|
||||||
|
{
|
||||||
|
if(null != find_sink_by_mac(opt_peer_mac)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong id = sink_added.connect((l, s) => {
|
||||||
|
if(null != find_sink_by_mac(opt_peer_mac)) {
|
||||||
|
wait_for_target_sink.callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var cancel_id = cancellable.cancelled.connect(() => {
|
||||||
|
Idle.add(wait_for_target_sink.callback);
|
||||||
|
});
|
||||||
|
|
||||||
|
yield;
|
||||||
|
|
||||||
|
cancellable.disconnect(cancel_id);
|
||||||
|
disconnect(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void form_p2p_group() throws Error
|
||||||
|
{
|
||||||
|
if(null != curr_sink_mac) {
|
||||||
|
print("already hang out with sink: %s", curr_sink_mac);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sink s = find_sink_by_mac(opt_peer_mac);
|
||||||
|
curr_sink_mac = opt_peer_mac;
|
||||||
|
|
||||||
|
string l = s.peer;
|
||||||
|
l = decode_path(l.substring(l.last_index_of_char('/') + 1));
|
||||||
|
Peer p = peers.lookup(l);
|
||||||
|
|
||||||
|
info("forming P2P group with %s (%s)...", p.p2p_mac, p.friendly_name);
|
||||||
|
|
||||||
|
ulong id = p.formation_failure.connect((r) => {
|
||||||
|
info("failed to form P2P group: %s", r);
|
||||||
|
});
|
||||||
|
p.connect("auto", "");
|
||||||
|
yield wait_prop_changed(p, "Connected", 20);
|
||||||
|
|
||||||
|
(p as Object).disconnect(id);
|
||||||
|
|
||||||
|
curr_peer = p;
|
||||||
|
|
||||||
|
info("P2P group formed");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if GDK3_HAS_MONITOR_CLASS
|
||||||
|
private void get_monitor_geometry(out Gdk.Rectangle g) throws Error
|
||||||
|
{
|
||||||
|
Gdk.Monitor m;
|
||||||
|
if(-1 == opt_monitor_num) {
|
||||||
|
m = display.get_primary_monitor();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m = display.get_monitor(opt_monitor_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(null == m) {
|
||||||
|
throw new DispCtlError.MONITOR_GONE("specified monitor disappeared");
|
||||||
|
}
|
||||||
|
|
||||||
|
g = m.geometry;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
private void get_monitor_geometry(out Gdk.Rectangle g) throws Error
|
||||||
|
{
|
||||||
|
var s = display.get_default_screen();
|
||||||
|
int m = (-1 == opt_monitor_num)
|
||||||
|
? s.get_primary_monitor()
|
||||||
|
: opt_monitor_num;
|
||||||
|
|
||||||
|
if(s.get_n_monitors() <= m) {
|
||||||
|
throw new DispCtlError.MONITOR_GONE("specified monitor disappeared");
|
||||||
|
}
|
||||||
|
|
||||||
|
s.get_monitor_geometry(m, out g);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private unowned string session_state_to_str(int s)
|
||||||
|
{
|
||||||
|
switch(s) {
|
||||||
|
case 1:
|
||||||
|
return "connecting";
|
||||||
|
case 2:
|
||||||
|
return "capabilities exchanging";
|
||||||
|
case 3:
|
||||||
|
return "established";
|
||||||
|
case 4:
|
||||||
|
return "seting up session parameters";
|
||||||
|
case 5:
|
||||||
|
return "paused";
|
||||||
|
case 6:
|
||||||
|
return "playing";
|
||||||
|
case 7:
|
||||||
|
return "tearing down";
|
||||||
|
case 8:
|
||||||
|
return "destroyed";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void establish_session() throws Error
|
||||||
|
{
|
||||||
|
Gdk.Rectangle g;
|
||||||
|
get_monitor_geometry(out g);
|
||||||
|
|
||||||
|
info("establishing display session...");
|
||||||
|
|
||||||
|
curr_sink = find_sink_by_mac(opt_peer_mac);
|
||||||
|
string path = curr_sink.start_session(opt_authority,
|
||||||
|
@"x://$(opt_display)",
|
||||||
|
g.x,
|
||||||
|
g.y,
|
||||||
|
g.width,
|
||||||
|
g.height,
|
||||||
|
null == opt_audio_device ? "" : opt_audio_device);
|
||||||
|
curr_session = add_object(path) as Session;
|
||||||
|
|
||||||
|
var prop_change_id = (curr_session as DBusProxy).g_properties_changed.connect((props) => {
|
||||||
|
string k;
|
||||||
|
Variant v;
|
||||||
|
foreach(var prop in props) {
|
||||||
|
prop.get("{sv}", out k, out v);
|
||||||
|
if(k != "State") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
info("session status: %s", session_state_to_str(v.get_int32()));
|
||||||
|
|
||||||
|
if(6 == v.get_int32()) {
|
||||||
|
Idle.add(establish_session.callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cancellable.set_error_if_cancelled();
|
||||||
|
var cancel_id = cancellable.cancelled.connect(() => {
|
||||||
|
Idle.add(establish_session.callback);
|
||||||
|
});
|
||||||
|
bool timed_out = false;
|
||||||
|
var timeout_src = new TimeoutSource(10);
|
||||||
|
timeout_src.set_callback(() => {
|
||||||
|
timed_out = true;
|
||||||
|
Idle.add(establish_session.callback);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
yield;
|
||||||
|
|
||||||
|
timeout_src.destroy();
|
||||||
|
cancellable.disconnect(cancel_id);
|
||||||
|
(curr_session as DBusProxy).disconnect(prop_change_id);
|
||||||
|
|
||||||
|
if(timed_out) {
|
||||||
|
throw new DispCtlError.TIMEOUT("failed to establish session");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void wait_for_session_ending() throws Error
|
||||||
|
{
|
||||||
|
info("wait for session ending");
|
||||||
|
ulong id = session_removed.connect((id, s) => {
|
||||||
|
wait_for_session_ending.callback();
|
||||||
|
});
|
||||||
|
|
||||||
|
yield;
|
||||||
|
|
||||||
|
disconnect(id);
|
||||||
|
|
||||||
|
info("session ended");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void release_wnic_ownership() throws Error
|
||||||
|
{
|
||||||
|
if(opt_dont_return_wnic) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellable.reset();
|
||||||
|
|
||||||
|
Link l = find_link_by_name(opt_iface);
|
||||||
|
if(null == l) {
|
||||||
|
throw new DispCtlError.NO_SUCH_NIC("no such wireless adapter: %s",
|
||||||
|
opt_iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(l.managed) {
|
||||||
|
info("wifid is releasing ownership of %s...", opt_iface);
|
||||||
|
l.unmanage();
|
||||||
|
yield wait_prop_changed(l, "Managed");
|
||||||
|
}
|
||||||
|
|
||||||
|
Device d = find_device_by_name(opt_iface);
|
||||||
|
if(null != d && !d.managed) {
|
||||||
|
info("NetworkManager is acquiring ownership of %s...", opt_iface);
|
||||||
|
d.managed = true;
|
||||||
|
yield wait_prop_changed(d, "Managed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void start_wireless_display() throws Error
|
||||||
|
{
|
||||||
|
fetch_info_from_dbus();
|
||||||
|
cancellable.set_error_if_cancelled();
|
||||||
|
if(!opt_dont_borrow_wnic) {
|
||||||
|
yield acquire_wnic_ownership();
|
||||||
|
}
|
||||||
|
cancellable.set_error_if_cancelled();
|
||||||
|
yield start_p2p_scan();
|
||||||
|
cancellable.set_error_if_cancelled();
|
||||||
|
yield wait_for_target_sink();
|
||||||
|
cancellable.set_error_if_cancelled();
|
||||||
|
yield form_p2p_group();
|
||||||
|
cancellable.set_error_if_cancelled();
|
||||||
|
yield establish_session();
|
||||||
|
cancellable.set_error_if_cancelled();
|
||||||
|
yield wait_for_session_ending();
|
||||||
|
cancellable.set_error_if_cancelled();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop_wireless_display()
|
||||||
|
{
|
||||||
|
info("received termination request");
|
||||||
|
|
||||||
|
if(null != curr_session) {
|
||||||
|
info("tearing down wireless display...");
|
||||||
|
|
||||||
|
try {
|
||||||
|
curr_session.teardown();
|
||||||
|
wait_prop_changed.begin(curr_sink, "Session", 3, () => {
|
||||||
|
cancellable.cancel();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch(Error e) {
|
||||||
|
warning("failed to tearing down normally: %s", e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cancellable.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool check_options()
|
||||||
|
{
|
||||||
|
if(null == opt_peer_mac) {
|
||||||
|
print("please specify a peer MAC with -p option");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
print("peer-mac=%s", opt_peer_mac);
|
||||||
|
|
||||||
|
if(null == opt_display) {
|
||||||
|
opt_display = Environment.get_variable("DISPLAY");
|
||||||
|
}
|
||||||
|
print("display=%s", opt_display);
|
||||||
|
|
||||||
|
if(null == opt_authority) {
|
||||||
|
opt_authority = Environment.get_variable("XAUTHORITY");
|
||||||
|
}
|
||||||
|
print("authority=%s", opt_authority);
|
||||||
|
|
||||||
|
if(null == opt_iface) {
|
||||||
|
opt_iface = "wlan0";
|
||||||
|
}
|
||||||
|
print("interface=%s", opt_iface);
|
||||||
|
|
||||||
|
if(null == opt_wfd_subelems) {
|
||||||
|
opt_wfd_subelems = "000600901c4400c8";
|
||||||
|
}
|
||||||
|
print("wfd_subelemens=%s", opt_wfd_subelems);
|
||||||
|
|
||||||
|
display = Gdk.Display.open(opt_display);
|
||||||
|
if(null == display) {
|
||||||
|
print("invalid display option: %s", opt_display);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int n_monitors;
|
||||||
|
#if GDK3_HAS_MONITOR_CLASS
|
||||||
|
n_monitors = display.get_n_monitors();
|
||||||
|
#else
|
||||||
|
n_monitors = display.get_default_screen().get_n_monitors();
|
||||||
|
#endif
|
||||||
|
if(-1 > opt_monitor_num || opt_monitor_num >= n_monitors) {
|
||||||
|
print("invalid screen number option: %d", opt_monitor_num);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
print("monitor-num=%d", opt_monitor_num);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void activate()
|
||||||
|
{
|
||||||
|
if(!check_options()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
start_wireless_display.begin((o, r) => {
|
||||||
|
try {
|
||||||
|
start_wireless_display.end(r);
|
||||||
|
}
|
||||||
|
catch(Error e) {
|
||||||
|
print("failed to cast to wireless display: %s", e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
release_wnic_ownership.begin((o, r) => {
|
||||||
|
try {
|
||||||
|
release_wnic_ownership.end(r);
|
||||||
|
}
|
||||||
|
catch(Error e) {
|
||||||
|
print("failed to release ownership of wnic: %s", e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
release();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
hold();
|
||||||
|
}
|
||||||
|
|
||||||
|
private unowned Device? find_device_by_name(string nic_name)
|
||||||
|
{
|
||||||
|
foreach(var d in devices.get_values()) {
|
||||||
|
if(nic_name == d.interface) {
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private unowned Link? find_link_by_name(string nic_name)
|
||||||
|
{
|
||||||
|
foreach(var l in links.get_values()) {
|
||||||
|
if(nic_name == l.interface_name) {
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private unowned Sink? find_sink_by_mac(string m)
|
||||||
|
{
|
||||||
|
foreach(var l in sinks.get_keys()) {
|
||||||
|
if(l.has_prefix(m)) {
|
||||||
|
return sinks.lookup(l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool is_wnic(string nic_name)
|
||||||
|
{
|
||||||
|
return find_link_by_name(nic_name) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void wait_prop_changed<T>(T o,
|
||||||
|
string name,
|
||||||
|
uint timeout = 1) throws Error
|
||||||
|
{
|
||||||
|
ulong prop_changed_id = (o as DBusProxy).g_properties_changed.connect((props) => {
|
||||||
|
string k;
|
||||||
|
Variant v;
|
||||||
|
foreach(var prop in props) {
|
||||||
|
prop.get("{sv}", out k, out v);
|
||||||
|
if(k == name) {
|
||||||
|
wait_prop_changed.callback();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var cancel_id = cancellable.cancelled.connect(() => {
|
||||||
|
Idle.add(wait_prop_changed.callback);
|
||||||
|
});
|
||||||
|
bool timed_out = false;
|
||||||
|
Source timeout_src = null;
|
||||||
|
if(0 < timeout) {
|
||||||
|
timeout_src = new TimeoutSource.seconds(timeout);
|
||||||
|
timeout_src.set_callback(() => {
|
||||||
|
timed_out = true;
|
||||||
|
wait_prop_changed.callback();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
timeout_src.attach(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
yield;
|
||||||
|
|
||||||
|
cancellable.disconnect(cancel_id);
|
||||||
|
if(null != timeout_src) {
|
||||||
|
timeout_src.destroy();
|
||||||
|
}
|
||||||
|
(o as DBusProxy).disconnect(prop_changed_id);
|
||||||
|
|
||||||
|
if(timed_out) {
|
||||||
|
throw new DispCtlError.TIMEOUT("timeout to wait for property %s change",
|
||||||
|
name);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellable.set_error_if_cancelled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(string[]? argv)
|
||||||
|
{
|
||||||
|
Gdk.init(ref argv);
|
||||||
|
Intl.setlocale();
|
||||||
|
Environment.set_prgname(Path.get_basename(argv[0]));
|
||||||
|
|
||||||
|
|
||||||
|
Application app = new DispCtl();
|
||||||
|
Application.set_default(app);
|
||||||
|
|
||||||
|
Sigint.add_watch((app as DispCtl).stop_wireless_display);
|
||||||
|
|
||||||
|
try {
|
||||||
|
app.register();
|
||||||
|
}
|
||||||
|
catch(Error e) {
|
||||||
|
print("failed to startup: %s", e.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int r = app.run(argv);
|
||||||
|
|
||||||
|
print("Bye");
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
426
res/gstencoder.vala
Normal file
426
res/gstencoder.vala
Normal file
|
@ -0,0 +1,426 @@
|
||||||
|
/*
|
||||||
|
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||||
|
*
|
||||||
|
* MiracleCast 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) any later version.
|
||||||
|
*
|
||||||
|
* MiracleCast 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 MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
public enum DispdEncoderConfig
|
||||||
|
{
|
||||||
|
DISPLAY_TYPE, /* string */
|
||||||
|
DISPLAY_NAME, /* string */
|
||||||
|
MONITOR_NUM, /* uint32 */
|
||||||
|
X, /* uint32 */
|
||||||
|
Y, /* uint32 */
|
||||||
|
WIDTH, /* uint32 */
|
||||||
|
HEIGHT, /* uint32 */
|
||||||
|
WINDOW_ID, /* uint32 */
|
||||||
|
FRAMERATE, /* uint32 */
|
||||||
|
SCALE_WIDTH, /* uint32 */
|
||||||
|
SCALE_HEIGHT, /* uint32 */
|
||||||
|
AUDIO_TYPE, /* string */
|
||||||
|
AUDIO_DEV, /* string */
|
||||||
|
PEER_ADDRESS, /* string */
|
||||||
|
RTP_PORT0, /* uint32 */
|
||||||
|
RTP_PORT1, /* uint32 */
|
||||||
|
PEER_RTCP_PORT, /* uint32 */
|
||||||
|
LOCAL_ADDRESS, /* uint32 */
|
||||||
|
LOCAL_RTCP_PORT, /* uint32 */
|
||||||
|
H264_PROFILE,
|
||||||
|
H264_LEVEL,
|
||||||
|
DEBUG_LEVEL,
|
||||||
|
}
|
||||||
|
|
||||||
|
[DBus (name = "org.freedesktop.miracle.encoder.error")]
|
||||||
|
public errordomain DispdEncoderError
|
||||||
|
{
|
||||||
|
CANT_RETURN_UNIQUE_NAME,
|
||||||
|
UNEXPECTED_EOS,
|
||||||
|
ENCODER_ERROR,
|
||||||
|
INVALID_STATE,
|
||||||
|
}
|
||||||
|
|
||||||
|
[DBus(name = "org.freedesktop.miracle.encoder.state")]
|
||||||
|
public enum DispdEncoderState
|
||||||
|
{
|
||||||
|
NULL,
|
||||||
|
CONFIGURED,
|
||||||
|
READY,
|
||||||
|
STARTED,
|
||||||
|
PAUSED,
|
||||||
|
}
|
||||||
|
|
||||||
|
[DBus(name = "org.freedesktop.miracle.encoder")]
|
||||||
|
public interface DispdEncoder : GLib.Object
|
||||||
|
{
|
||||||
|
public const string OBJECT_PATH = "/org/freedesktop/miracle/encoder";
|
||||||
|
|
||||||
|
public abstract DispdEncoderState state { get; protected set; }
|
||||||
|
public abstract signal void error(string reason);
|
||||||
|
|
||||||
|
public abstract void configure(HashTable<DispdEncoderConfig, Variant> configs) throws DispdEncoderError;
|
||||||
|
public abstract void start() throws DispdEncoderError;
|
||||||
|
public abstract void pause() throws DispdEncoderError;
|
||||||
|
public abstract void stop() throws DispdEncoderError;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class GstEncoder : DispdEncoder, GLib.Object
|
||||||
|
{
|
||||||
|
private DBusConnection conn;
|
||||||
|
private HashTable<DispdEncoderConfig, Variant> configs;
|
||||||
|
private Gst.Element pipeline;
|
||||||
|
private Gst.State pipeline_state = Gst.State.NULL;
|
||||||
|
private DispdEncoderState _state = DispdEncoderState.NULL;
|
||||||
|
|
||||||
|
public DispdEncoderState state {
|
||||||
|
get { return _state; }
|
||||||
|
protected set {
|
||||||
|
if(_state == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_state = value;
|
||||||
|
notify_state_changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notify_state_changed()
|
||||||
|
{
|
||||||
|
var builder = new VariantBuilder(VariantType.ARRAY);
|
||||||
|
var invalid_builder = new VariantBuilder(new VariantType ("as"));
|
||||||
|
|
||||||
|
Variant s = state;
|
||||||
|
builder.add ("{sv}", "State", s);
|
||||||
|
|
||||||
|
try {
|
||||||
|
conn.emit_signal(null,
|
||||||
|
"/org/freedesktop/miracle/encoder",
|
||||||
|
"org.freedesktop.DBus.Properties",
|
||||||
|
"PropertiesChanged",
|
||||||
|
new Variant ("(sa{sv}as)",
|
||||||
|
"org.freedesktop.miracle.encoder",
|
||||||
|
builder,
|
||||||
|
invalid_builder));
|
||||||
|
}
|
||||||
|
catch (Error e) {
|
||||||
|
warning("failed to emit signal: %s", e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string gen_scaler_n_converter_desc(uint32 width, uint32 height)
|
||||||
|
{
|
||||||
|
if(null != Gst.ElementFactory.find("vaapih264enc")) {
|
||||||
|
return ("! vaapipostproc " +
|
||||||
|
"scale-method=2 " +
|
||||||
|
"format=3 " +
|
||||||
|
"force-aspect-ratio=true " +
|
||||||
|
"! video/x-raw, " +
|
||||||
|
"format=YV12, " +
|
||||||
|
"width=%u, " +
|
||||||
|
"height=%u ").printf(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
info("since vaapih264enc is not available, vaapipostproc can't be " +
|
||||||
|
"trusted, use videoscale+videoconvert instead");
|
||||||
|
|
||||||
|
return ("! videoscale method=0 add-borders=true " +
|
||||||
|
"! video/x-raw, width=%u, height=%u " +
|
||||||
|
"! videoconvert " +
|
||||||
|
"! video/x-raw, format=YV12 ").printf(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
string gen_encoder_desc(uint32 framerate)
|
||||||
|
{
|
||||||
|
if(null != Gst.ElementFactory.find("vaapih264enc")) {
|
||||||
|
return ("! vaapih264enc " +
|
||||||
|
"rate-control=1 " +
|
||||||
|
"num-slices=1 " + /* in WFD spec, one slice per frame */
|
||||||
|
"max-bframes=0 " + /* in H264 CHP, no bframe supporting */
|
||||||
|
"cabac=true " + /* in H264 CHP, CABAC entropy codeing is supported, but need more processing to decode */
|
||||||
|
"dct8x8=true " + /* in H264 CHP, DTC is supported */
|
||||||
|
"cpb-length=1000 " + /* shortent buffer in order to decrease latency */
|
||||||
|
"keyframe-period=%u ").printf(framerate);
|
||||||
|
}
|
||||||
|
|
||||||
|
info("vaapih264enc not available, use x264enc instead");
|
||||||
|
|
||||||
|
return ("! x264enc pass=4 b-adapt=false key-int-max=%u " +
|
||||||
|
"speed-preset=4 tune=4 ").printf(framerate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void configure(HashTable<DispdEncoderConfig, Variant> configs) throws DispdEncoderError
|
||||||
|
{
|
||||||
|
uint32 framerate = configs.contains(DispdEncoderConfig.FRAMERATE)
|
||||||
|
? configs.get(DispdEncoderConfig.FRAMERATE).get_uint32()
|
||||||
|
: 30;
|
||||||
|
uint32 width = configs.contains(DispdEncoderConfig.WIDTH)
|
||||||
|
? configs.get(DispdEncoderConfig.WIDTH).get_uint32()
|
||||||
|
: 1920;
|
||||||
|
uint32 height = configs.contains(DispdEncoderConfig.HEIGHT)
|
||||||
|
? configs.get(DispdEncoderConfig.HEIGHT).get_uint32()
|
||||||
|
: 1080;
|
||||||
|
StringBuilder desc = new StringBuilder();
|
||||||
|
desc.append_printf(
|
||||||
|
"ximagesrc " +
|
||||||
|
"name=vsrc " +
|
||||||
|
"use-damage=false " +
|
||||||
|
"show-pointer=false " +
|
||||||
|
"startx=%u starty=%u endx=%u endy=%u " +
|
||||||
|
"! video/x-raw, " +
|
||||||
|
"framerate=%u/1 " +
|
||||||
|
"%s" + /* scaling & color space convertion */
|
||||||
|
"%s" + /* encoding */
|
||||||
|
"! h264parse " +
|
||||||
|
"! video/x-h264, " +
|
||||||
|
"alignment=nal, " +
|
||||||
|
"stream-format=byte-stream " +
|
||||||
|
"%s " + /* add queue if audio enabled */
|
||||||
|
"! mpegtsmux " +
|
||||||
|
"name=muxer " +
|
||||||
|
"! rtpmp2tpay " +
|
||||||
|
"! .send_rtp_sink_0 " +
|
||||||
|
"rtpbin " +
|
||||||
|
"name=session " +
|
||||||
|
"rtp-profile=1 " + /* avp */
|
||||||
|
"do-retransmission=true " +
|
||||||
|
"do-sync-event=true " +
|
||||||
|
"do-lost=true " +
|
||||||
|
"ntp-time-source=3 " + /* pipeline clock time */
|
||||||
|
"buffer-mode=0 " +
|
||||||
|
"latency=40 " +
|
||||||
|
"max-misorder-time=50 " +
|
||||||
|
"! application/x-rtp " +
|
||||||
|
"! udpsink " +
|
||||||
|
"sync=false " +
|
||||||
|
"async=false " +
|
||||||
|
"host=\"%s\" " +
|
||||||
|
"port=%u ",
|
||||||
|
configs.contains(DispdEncoderConfig.X)
|
||||||
|
? configs.get(DispdEncoderConfig.X).get_uint32()
|
||||||
|
: 0,
|
||||||
|
configs.contains(DispdEncoderConfig.Y)
|
||||||
|
? configs.get(DispdEncoderConfig.Y).get_uint32()
|
||||||
|
: 0,
|
||||||
|
configs.get(DispdEncoderConfig.X).get_uint32() + width - 1,
|
||||||
|
configs.get(DispdEncoderConfig.Y).get_uint32() + height - 1,
|
||||||
|
framerate,
|
||||||
|
gen_scaler_n_converter_desc(width, height),
|
||||||
|
gen_encoder_desc(framerate),
|
||||||
|
configs.contains(DispdEncoderConfig.AUDIO_TYPE)
|
||||||
|
? "! queue max-size-buffers=0 max-size-bytes=0"
|
||||||
|
: "",
|
||||||
|
configs.contains(DispdEncoderConfig.PEER_ADDRESS)
|
||||||
|
? configs.get(DispdEncoderConfig.PEER_ADDRESS).get_string()
|
||||||
|
: "",
|
||||||
|
configs.contains(DispdEncoderConfig.RTP_PORT0)
|
||||||
|
? configs.get(DispdEncoderConfig.RTP_PORT0).get_uint32()
|
||||||
|
: 16384);
|
||||||
|
if(configs.contains(DispdEncoderConfig.LOCAL_RTCP_PORT)) {
|
||||||
|
desc.append_printf("udpsrc " +
|
||||||
|
"address=\"%s\" " +
|
||||||
|
"port=%u " +
|
||||||
|
"reuse=true " +
|
||||||
|
"! session.recv_rtcp_sink_0 " +
|
||||||
|
"session.send_rtcp_src_0 " +
|
||||||
|
"! udpsink " +
|
||||||
|
"host=\"%s\" " +
|
||||||
|
"port=%u " +
|
||||||
|
"sync=false " +
|
||||||
|
"async=false ",
|
||||||
|
configs.contains(DispdEncoderConfig.LOCAL_ADDRESS)
|
||||||
|
? configs.get(DispdEncoderConfig.LOCAL_ADDRESS).get_string()
|
||||||
|
: "",
|
||||||
|
configs.contains(DispdEncoderConfig.LOCAL_RTCP_PORT)
|
||||||
|
? configs.get(DispdEncoderConfig.LOCAL_RTCP_PORT).get_uint32()
|
||||||
|
: 16385,
|
||||||
|
configs.contains(DispdEncoderConfig.PEER_ADDRESS)
|
||||||
|
? configs.get(DispdEncoderConfig.PEER_ADDRESS).get_string()
|
||||||
|
: "",
|
||||||
|
configs.contains(DispdEncoderConfig.PEER_RTCP_PORT)
|
||||||
|
? configs.get(DispdEncoderConfig.PEER_RTCP_PORT).get_uint32()
|
||||||
|
: 16385);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(configs.contains(DispdEncoderConfig.AUDIO_TYPE)) {
|
||||||
|
desc.append_printf("pulsesrc " +
|
||||||
|
"do-timestamp=true " +
|
||||||
|
"client-name=miraclecast " +
|
||||||
|
"device=\"%s\" " +
|
||||||
|
"! avenc_aac " +
|
||||||
|
"! audio/mpeg, " +
|
||||||
|
"channels=2, " +
|
||||||
|
"rate=48000 " +
|
||||||
|
// "base-profile=lc " +
|
||||||
|
"! queue " +
|
||||||
|
"max-size-buffers=0 " +
|
||||||
|
"max-size-bytes=0 " +
|
||||||
|
"max-size-time=0 " +
|
||||||
|
"! muxer. ",
|
||||||
|
configs.contains(DispdEncoderConfig.AUDIO_DEV)
|
||||||
|
? configs.get(DispdEncoderConfig.AUDIO_DEV).get_string()
|
||||||
|
: "");
|
||||||
|
}
|
||||||
|
|
||||||
|
info("final pipeline description: %s", desc.str);
|
||||||
|
|
||||||
|
this.configs = configs;
|
||||||
|
|
||||||
|
try {
|
||||||
|
pipeline = Gst.parse_launch(desc.str);
|
||||||
|
}
|
||||||
|
catch(Error e) {
|
||||||
|
throw new DispdEncoderError.ENCODER_ERROR("%s", e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
var bus = pipeline.get_bus();
|
||||||
|
bus.add_signal_watch();
|
||||||
|
bus.message.connect(on_pipeline_message);
|
||||||
|
|
||||||
|
pipeline.set_state(Gst.State.READY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() throws DispdEncoderError
|
||||||
|
{
|
||||||
|
check_configs();
|
||||||
|
|
||||||
|
pipeline.set_state(Gst.State.PLAYING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pause() throws DispdEncoderError
|
||||||
|
{
|
||||||
|
check_configs();
|
||||||
|
|
||||||
|
pipeline.set_state(Gst.State.PAUSED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() throws DispdEncoderError
|
||||||
|
{
|
||||||
|
if(null == pipeline) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline.set_state(Gst.State.NULL);
|
||||||
|
state = DispdEncoderState.NULL;
|
||||||
|
defered_terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void prepare() throws Error
|
||||||
|
{
|
||||||
|
conn = yield Bus.get(BusType.SESSION);
|
||||||
|
conn.register_object(DispdEncoder.OBJECT_PATH, this as DispdEncoder);
|
||||||
|
|
||||||
|
#if VALA_0_54
|
||||||
|
string bus_info = "%s\n%s".printf(conn.unique_name,
|
||||||
|
BusType.SESSION.get_address_sync ());
|
||||||
|
#else
|
||||||
|
string bus_info = "%s\n%s".printf(conn.unique_name,
|
||||||
|
BusType.get_address_sync(BusType.SESSION));
|
||||||
|
#endif
|
||||||
|
/* we are ready, tell parent how to communicate with us */
|
||||||
|
ssize_t r = Posix.write(3, (void *) bus_info.data, bus_info.length);
|
||||||
|
if(0 > r) {
|
||||||
|
throw new DispdEncoderError.CANT_RETURN_UNIQUE_NAME("%s",
|
||||||
|
Posix.strerror(Posix.errno));
|
||||||
|
}
|
||||||
|
Posix.fsync(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void defered_terminate()
|
||||||
|
{
|
||||||
|
Timeout.add(100, () => {
|
||||||
|
loop.quit();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void check_configs() throws DispdEncoderError
|
||||||
|
{
|
||||||
|
if(null == configs || null == pipeline) {
|
||||||
|
throw new DispdEncoderError.INVALID_STATE("not configure yet");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void on_pipeline_message(Gst.Message m)
|
||||||
|
{
|
||||||
|
Error e;
|
||||||
|
string d;
|
||||||
|
|
||||||
|
if(m.src != pipeline) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(m.type) {
|
||||||
|
case Gst.MessageType.EOS:
|
||||||
|
error("unexpected EOS");
|
||||||
|
defered_terminate();
|
||||||
|
break;
|
||||||
|
case Gst.MessageType.ERROR:
|
||||||
|
m.parse_error(out e, out d);
|
||||||
|
error("unexpected error: %s\n%s".printf(e.message, d));
|
||||||
|
defered_terminate();
|
||||||
|
break;
|
||||||
|
case Gst.MessageType.STATE_CHANGED:
|
||||||
|
Gst.State oldstate;
|
||||||
|
m.parse_state_changed(out oldstate, out pipeline_state, null);
|
||||||
|
info("pipeline state chagned from %s to %s",
|
||||||
|
oldstate.to_string(),
|
||||||
|
pipeline_state.to_string());
|
||||||
|
switch(pipeline_state) {
|
||||||
|
case Gst.State.READY:
|
||||||
|
state = DispdEncoderState.CONFIGURED;
|
||||||
|
break;
|
||||||
|
case Gst.State.PLAYING:
|
||||||
|
state = DispdEncoderState.STARTED;
|
||||||
|
break;
|
||||||
|
case Gst.State.PAUSED:
|
||||||
|
if(Gst.State.PLAYING == oldstate) {
|
||||||
|
state = DispdEncoderState.PAUSED;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
debug("unhandled message: %s", m.type.to_string());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private MainLoop loop;
|
||||||
|
|
||||||
|
int main(string[] argv)
|
||||||
|
{
|
||||||
|
Gst.init(ref argv);
|
||||||
|
|
||||||
|
var encoder = new GstEncoder();
|
||||||
|
encoder.prepare.begin((o, r) => {
|
||||||
|
try {
|
||||||
|
encoder.prepare.end(r);
|
||||||
|
}
|
||||||
|
catch(Error e) {
|
||||||
|
error("%s", e.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
loop = new MainLoop();
|
||||||
|
loop.run();
|
||||||
|
|
||||||
|
Posix.close(3);
|
||||||
|
|
||||||
|
Gst.deinit();
|
||||||
|
|
||||||
|
info("bye");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -48,7 +48,9 @@ class Player(object):
|
||||||
if title:
|
if title:
|
||||||
self.window.set_title(title)
|
self.window.set_title(title)
|
||||||
|
|
||||||
if hasattr(self,'width') and hasattr(self,'height'):
|
if kwargs.get("fullscreen"):
|
||||||
|
self.window.fullscreen()
|
||||||
|
elif hasattr(self,'width') and hasattr(self,'height'):
|
||||||
self.window.set_default_size(self.width, self.height)
|
self.window.set_default_size(self.width, self.height)
|
||||||
|
|
||||||
self.drawingarea = Gtk.DrawingArea()
|
self.drawingarea = Gtk.DrawingArea()
|
||||||
|
@ -96,10 +98,10 @@ class Player(object):
|
||||||
if scale:
|
if scale:
|
||||||
gstcommand += "videoscale method=1 ! video/x-raw,width="+str(self.width)+",height="+str(self.height)+" ! "
|
gstcommand += "videoscale method=1 ! video/x-raw,width="+str(self.width)+",height="+str(self.height)+" ! "
|
||||||
|
|
||||||
gstcommand += "autovideosink "
|
gstcommand += "autovideosink sync=false "
|
||||||
|
|
||||||
if audio:
|
if audio:
|
||||||
gstcommand += "demuxer. ! queue max-size-buffers=0 max-size-time=0 ! aacparse ! avdec_aac ! audioconvert ! audioresample ! autoaudiosink "
|
gstcommand += "demuxer. ! queue max-size-buffers=0 max-size-time=0 ! aacparse ! avdec_aac ! audioconvert ! audioresample ! autoaudiosink sync=false "
|
||||||
|
|
||||||
self.pipeline = Gst.parse_launch(gstcommand)
|
self.pipeline = Gst.parse_launch(gstcommand)
|
||||||
|
|
||||||
|
@ -206,6 +208,7 @@ if __name__ == '__main__':
|
||||||
parser.add_argument("--log-level", metavar="lvl", help="Maximum level for log messages")
|
parser.add_argument("--log-level", metavar="lvl", help="Maximum level for log messages")
|
||||||
parser.add_argument("-p", "--port", type=int, default=7236, help="Port for rtsp")
|
parser.add_argument("-p", "--port", type=int, default=7236, help="Port for rtsp")
|
||||||
parser.add_argument("-a", "--audio", dest="audio", action="store_true", help="Enable audio support")
|
parser.add_argument("-a", "--audio", dest="audio", action="store_true", help="Enable audio support")
|
||||||
|
parser.add_argument("-f", "--full-screen", dest="fullscreen", action="store_true", help="Enter full screen mode")
|
||||||
parser.add_argument("-s", "--scale", metavar="WxH", help="Scale to resolution")
|
parser.add_argument("-s", "--scale", metavar="WxH", help="Scale to resolution")
|
||||||
parser.add_argument("-d", "--debug", help="Debug")
|
parser.add_argument("-d", "--debug", help="Debug")
|
||||||
parser.add_argument("--uibc", help="Enable UIBC")
|
parser.add_argument("--uibc", help="Enable UIBC")
|
||||||
|
@ -216,7 +219,8 @@ if __name__ == '__main__':
|
||||||
# " default VESA %08X\n"
|
# " default VESA %08X\n"
|
||||||
# " default HH %08X\n"
|
# " default HH %08X\n"
|
||||||
parser.add_argument("-r", "--resolution", help="Resolution")
|
parser.add_argument("-r", "--resolution", help="Resolution")
|
||||||
parser.set_defaults(audio=True)
|
parser.set_defaults(audio=False)
|
||||||
|
parser.set_defaults(fullscreen=False)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
p = Player(**vars(args))
|
p = Player(**vars(args))
|
||||||
|
|
8
res/install-scripts
Executable file
8
res/install-scripts
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/sh
|
||||||
|
RESDIR="${MESON_SOURCE_ROOT}/res"
|
||||||
|
BINDIR="${MESON_INSTALL_DESTDIR_PREFIX}/$1"
|
||||||
|
|
||||||
|
install -dv "$BINDIR"
|
||||||
|
install -v "$RESDIR"/miracle-gst "$BINDIR"
|
||||||
|
install -v "$RESDIR"/gstplayer "$BINDIR"
|
||||||
|
install -v "$RESDIR"/uibc-viewer "$BINDIR"
|
|
@ -1,3 +1,39 @@
|
||||||
|
dbus1 = dependency('dbus-1')
|
||||||
|
systemd = dependency('systemd')
|
||||||
|
|
||||||
|
dbus_system_services_dir = dbus1.get_pkgconfig_variable('system_bus_services_dir')
|
||||||
|
systemd_system_unit_dir = systemd.get_pkgconfig_variable('systemdsystemunitdir')
|
||||||
|
|
||||||
|
conf_data = configuration_data()
|
||||||
|
dbus_name_prefix = 'org.freedesktop.miracle'
|
||||||
|
conf_data.set('dbus_name_prefix', dbus_name_prefix)
|
||||||
|
conf_data.set('exec_path',
|
||||||
|
join_paths(get_option('prefix'), get_option('bindir'))
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_file(input: 'miracle-wifid.service.in',
|
||||||
|
output: 'miracle-wifid.service',
|
||||||
|
configuration: conf_data,
|
||||||
|
install_dir: systemd_system_unit_dir
|
||||||
|
)
|
||||||
|
configure_file(input: 'miracle-dispd.service.in',
|
||||||
|
output: 'miracle-dispd.service',
|
||||||
|
configuration: conf_data,
|
||||||
|
install_dir: systemd_system_unit_dir
|
||||||
|
)
|
||||||
|
# create symlinks for DBus activiated services
|
||||||
|
meson.add_install_script('create-symlinks')
|
||||||
|
|
||||||
|
configure_file(input: 'org.freedesktop.miracle.wifi.service.in',
|
||||||
|
output: 'org.freedesktop.miracle.wifi.service',
|
||||||
|
configuration: conf_data,
|
||||||
|
install_dir: dbus_system_services_dir
|
||||||
|
)
|
||||||
|
configure_file(input: 'org.freedesktop.miracle.wfd.service.in',
|
||||||
|
output: 'org.freedesktop.miracle.wfd.service',
|
||||||
|
configuration: conf_data,
|
||||||
|
install_dir: dbus_system_services_dir
|
||||||
|
)
|
||||||
install_data(
|
install_data(
|
||||||
'org.freedesktop.miracle.conf',
|
'org.freedesktop.miracle.conf',
|
||||||
install_dir: join_paths(get_option('sysconfdir'), 'dbus-1', 'system.d')
|
install_dir: join_paths(get_option('sysconfdir'), 'dbus-1', 'system.d')
|
||||||
|
@ -11,3 +47,33 @@ install_data(
|
||||||
'miracle-wifid', 'miracle-sinkctl', 'miracle-wifictl',
|
'miracle-wifid', 'miracle-sinkctl', 'miracle-wifictl',
|
||||||
install_dir: join_paths(get_option('datadir'), 'bash-completion', 'completions')
|
install_dir: join_paths(get_option('datadir'), 'bash-completion', 'completions')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
gio2 = dependency('gio-2.0')
|
||||||
|
gdk3 = dependency('gdk-3.0')
|
||||||
|
gst1 = dependency('gstreamer-1.0')
|
||||||
|
gst1_base = dependency('gstreamer-base-1.0')
|
||||||
|
|
||||||
|
add_languages('vala')
|
||||||
|
valac = meson.get_compiler('vala')
|
||||||
|
valac_extra_args = []
|
||||||
|
if valac.version().version_compare('>=0.34') and gdk3.version().version_compare('>=3.22')
|
||||||
|
valac_extra_args += ['-D', 'GDK3_HAS_MONITOR_CLASS']
|
||||||
|
endif
|
||||||
|
|
||||||
|
executable('gstencoder', 'gstencoder.vala',
|
||||||
|
dependencies: [gst1, gst1_base, gio2],
|
||||||
|
install: true,
|
||||||
|
vala_args: ['--pkg=posix'])
|
||||||
|
|
||||||
|
miracle_dispctl_src = ['dispctl.vala',
|
||||||
|
'sigint.vapi',
|
||||||
|
'sigint.c',
|
||||||
|
'networkmanager.vala',
|
||||||
|
'miracle-wifi.vala',
|
||||||
|
'miracle-wfd.vala'
|
||||||
|
]
|
||||||
|
executable('miracle-dispctl', miracle_dispctl_src,
|
||||||
|
dependencies: [gio2, gdk3],
|
||||||
|
vala_args: valac_extra_args,
|
||||||
|
install: true
|
||||||
|
)
|
||||||
|
|
18
res/miracle-dispd.service.cmake
Normal file
18
res/miracle-dispd.service.cmake
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Miraclecast WiFi Display Service
|
||||||
|
After=dbus.service
|
||||||
|
Requires=miracle-wifid.service
|
||||||
|
After=miracle-wifid.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
BusName=org.freedesktop.miracle.wfd
|
||||||
|
Environment=LOG_LEVEL=trace
|
||||||
|
ExecStart=@CMAKE_INSTALL_PREFIX@/bin/miracle-dispd
|
||||||
|
CapabilityBoundingSet=CAP_NET_BIND_SERVICE \
|
||||||
|
CAP_SETGID \
|
||||||
|
CAP_SETUID \
|
||||||
|
CAP_SETPCAP
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
Alias=dbus-org.freedesktop.miracle.wfd.service
|
18
res/miracle-dispd.service.in
Normal file
18
res/miracle-dispd.service.in
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Miraclecast WiFi Display Service
|
||||||
|
After=dbus.service
|
||||||
|
Requires=miracle-wifid.service
|
||||||
|
After=miracle-wifid.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
BusName=@dbus_name_prefix@.wfd
|
||||||
|
Environment=LOG_LEVEL=trace
|
||||||
|
ExecStart=@exec_path@/miracle-dispd
|
||||||
|
CapabilityBoundingSet=CAP_NET_BIND_SERVICE \
|
||||||
|
CAP_SETGID \
|
||||||
|
CAP_SETUID \
|
||||||
|
CAP_SETPCAP
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
Alias=dbus-@dbus_name_prefix@.wfd.service
|
50
res/miracle-wfd.vala
Normal file
50
res/miracle-wfd.vala
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/* Generated by vala-dbus-binding-tool 0.4.0. Do not modify! */
|
||||||
|
/* Generated with: /usr/bin/vala-dbus-binding-tool --gdbus --no-synced --rename-namespace=org:Org --rename-namespace=freedesktop:Freedesktop --rename-namespace=miracle:Miracle --rename-namespace=wifi:Wifi --rename-namespace=wfd:Wfd --api-path=/home/derekdai/Projects/miraclecast/demo/dbus */
|
||||||
|
using GLib;
|
||||||
|
|
||||||
|
namespace Org {
|
||||||
|
|
||||||
|
namespace Freedesktop {
|
||||||
|
|
||||||
|
namespace Miracle {
|
||||||
|
|
||||||
|
namespace Wfd {
|
||||||
|
|
||||||
|
[DBus (name = "org.freedesktop.miracle.wfd.Sink")]
|
||||||
|
public interface Sink : GLib.Object {
|
||||||
|
|
||||||
|
[DBus (name = "StartSession", timeout = 120000)]
|
||||||
|
public abstract GLib.ObjectPath start_session(string param0, string param1, uint param2, uint param3, uint param4, uint param5, string param6) throws DBusError, IOError;
|
||||||
|
|
||||||
|
[DBus (name = "Session", timeout = 120000)]
|
||||||
|
public abstract GLib.ObjectPath session { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "Peer", timeout = 120000)]
|
||||||
|
public abstract GLib.ObjectPath peer { owned get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[DBus (name = "org.freedesktop.miracle.wfd.Session")]
|
||||||
|
public interface Session : GLib.Object {
|
||||||
|
|
||||||
|
[DBus (name = "Resume", timeout = 120000)]
|
||||||
|
public abstract void resume() throws DBusError, IOError;
|
||||||
|
|
||||||
|
[DBus (name = "Pause", timeout = 120000)]
|
||||||
|
public abstract void pause() throws DBusError, IOError;
|
||||||
|
|
||||||
|
[DBus (name = "Teardown", timeout = 120000)]
|
||||||
|
public abstract void teardown() throws DBusError, IOError;
|
||||||
|
|
||||||
|
[DBus (name = "Sink", timeout = 120000)]
|
||||||
|
public abstract GLib.ObjectPath sink { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "Url", timeout = 120000)]
|
||||||
|
public abstract string url { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "State", timeout = 120000)]
|
||||||
|
public abstract int state { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
92
res/miracle-wifi.vala
Normal file
92
res/miracle-wifi.vala
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
/* Generated by vala-dbus-binding-tool 0.4.0. Do not modify! */
|
||||||
|
/* Generated with: /usr/bin/vala-dbus-binding-tool --gdbus --no-synced --rename-namespace=org:Org --rename-namespace=freedesktop:Freedesktop --rename-namespace=miracle:Miracle --rename-namespace=wifi:Wifi --rename-namespace=wfd:Wfd --api-path=/home/derekdai/Projects/miraclecast/demo/dbus */
|
||||||
|
using GLib;
|
||||||
|
|
||||||
|
namespace Org {
|
||||||
|
|
||||||
|
namespace Freedesktop {
|
||||||
|
|
||||||
|
namespace Miracle {
|
||||||
|
|
||||||
|
namespace Wifi {
|
||||||
|
|
||||||
|
[DBus (name = "org.freedesktop.miracle.wifi.Peer")]
|
||||||
|
public interface Peer : GLib.Object {
|
||||||
|
|
||||||
|
[DBus (name = "Connect", timeout = 120000)]
|
||||||
|
public abstract void connect(string param0, string param1) throws DBusError, IOError;
|
||||||
|
|
||||||
|
[DBus (name = "Disconnect", timeout = 120000)]
|
||||||
|
public abstract void disconnect() throws DBusError, IOError;
|
||||||
|
|
||||||
|
[DBus (name = "Link", timeout = 120000)]
|
||||||
|
public abstract GLib.ObjectPath link { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "P2PMac", timeout = 120000)]
|
||||||
|
public abstract string p2p_mac { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "FriendlyName", timeout = 120000)]
|
||||||
|
public abstract string friendly_name { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "Connected", timeout = 120000)]
|
||||||
|
public abstract bool connected { get; }
|
||||||
|
|
||||||
|
[DBus (name = "Interface", timeout = 120000)]
|
||||||
|
public abstract string interface { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "LocalAddress", timeout = 120000)]
|
||||||
|
public abstract string local_address { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "RemoteAddress", timeout = 120000)]
|
||||||
|
public abstract string remote_address { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "WfdSubelements", timeout = 120000)]
|
||||||
|
public abstract string wfd_subelements { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "ProvisionDiscovery")]
|
||||||
|
public signal void provision_discovery(string param0, string param1);
|
||||||
|
|
||||||
|
[DBus (name = "GoNegRequest")]
|
||||||
|
public signal void go_neg_request(string param0, string param1);
|
||||||
|
|
||||||
|
[DBus (name = "FormationFailure")]
|
||||||
|
public signal void formation_failure(string param0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DBus (name = "org.freedesktop.miracle.wifi.Link")]
|
||||||
|
public interface Link : GLib.Object {
|
||||||
|
|
||||||
|
[DBus (name = "Manage", timeout = 120000)]
|
||||||
|
public abstract void manage() throws DBusError, IOError;
|
||||||
|
|
||||||
|
[DBus (name = "Unmanage", timeout = 120000)]
|
||||||
|
public abstract void unmanage() throws DBusError, IOError;
|
||||||
|
|
||||||
|
[DBus (name = "InterfaceIndex", timeout = 120000)]
|
||||||
|
public abstract uint interface_index { get; }
|
||||||
|
|
||||||
|
[DBus (name = "MACAddress", timeout = 120000)]
|
||||||
|
public abstract string m_a_c_address { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "InterfaceName", timeout = 120000)]
|
||||||
|
public abstract string interface_name { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "FriendlyName", timeout = 120000)]
|
||||||
|
public abstract string friendly_name { owned get; set; }
|
||||||
|
|
||||||
|
[DBus (name = "Managed", timeout = 120000)]
|
||||||
|
public abstract bool managed { get; }
|
||||||
|
|
||||||
|
[DBus (name = "P2PState", timeout = 120000)]
|
||||||
|
public abstract int p2p_state { get; }
|
||||||
|
|
||||||
|
[DBus (name = "P2PScanning", timeout = 120000)]
|
||||||
|
public abstract bool p2p_scanning { get; set; }
|
||||||
|
|
||||||
|
[DBus (name = "WfdSubelements", timeout = 120000)]
|
||||||
|
public abstract string wfd_subelements { owned get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
res/miracle-wifid.service.cmake
Normal file
19
res/miracle-wifid.service.cmake
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Miraclecast WiFi Daemon
|
||||||
|
After=dbus.service
|
||||||
|
Requires=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
BusName=org.freedesktop.miracle.wifi
|
||||||
|
Environment=PATH=/sbin:/usr/bin
|
||||||
|
ExecStart=@CMAKE_INSTALL_PREFIX@/bin/miracle-wifid \
|
||||||
|
--use-dev \
|
||||||
|
--log-level trace \
|
||||||
|
--lazy-managed
|
||||||
|
CapabilityBoundingSet=CAP_NET_ADMIN \
|
||||||
|
CAP_NET_BIND_SERVICE \
|
||||||
|
CAP_NET_RAW
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
Alias=dbus-org.freedesktop.miracle.wifi.service
|
19
res/miracle-wifid.service.in
Normal file
19
res/miracle-wifid.service.in
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Miraclecast WiFi Daemon
|
||||||
|
After=dbus.service
|
||||||
|
Requires=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
BusName=@dbus_name_prefix@.wifi
|
||||||
|
Environment=PATH=/sbin:/usr/bin
|
||||||
|
ExecStart=@exec_path@/miracle-wifid \
|
||||||
|
--use-dev \
|
||||||
|
--log-level trace \
|
||||||
|
--lazy-managed
|
||||||
|
CapabilityBoundingSet=CAP_NET_ADMIN \
|
||||||
|
CAP_NET_BIND_SERVICE \
|
||||||
|
CAP_NET_RAW
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
Alias=dbus-@dbus_name_prefix@.wifi.service
|
114
res/networkmanager.vala
Normal file
114
res/networkmanager.vala
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
/* Generated by vala-dbus-binding-tool 0.4.0. Do not modify! */
|
||||||
|
/* Generated with: /usr/bin/vala-dbus-binding-tool --gdbus --no-synced --rename-namespace=org:Org --rename-namespace=freedesktop:Freedesktop --rename-namespace=miracle:Miracle --rename-namespace=wifi:Wifi --rename-namespace=wfd:Wfd --api-path=/home/derekdai/Projects/miraclecast/demo/dbus */
|
||||||
|
using GLib;
|
||||||
|
|
||||||
|
namespace Org {
|
||||||
|
|
||||||
|
namespace Freedesktop {
|
||||||
|
|
||||||
|
namespace NetworkManager {
|
||||||
|
|
||||||
|
[DBus (name = "org.freedesktop.NetworkManager.Device")]
|
||||||
|
public interface Device : GLib.Object {
|
||||||
|
|
||||||
|
[DBus (name = "Reapply", timeout = 120000)]
|
||||||
|
public abstract void reapply(GLib.HashTable<string, GLib.HashTable<string, GLib.Variant>> connection, uint64 version_id, uint flags) throws DBusError, IOError;
|
||||||
|
|
||||||
|
[DBus (name = "GetAppliedConnection", timeout = 120000)]
|
||||||
|
public abstract void get_applied_connection(uint flags, out GLib.HashTable<string, GLib.HashTable<string, GLib.Variant>> connection, out uint64 version_id) throws DBusError, IOError;
|
||||||
|
|
||||||
|
[DBus (name = "Disconnect", timeout = 120000)]
|
||||||
|
public abstract void disconnect() throws DBusError, IOError;
|
||||||
|
|
||||||
|
[DBus (name = "Delete", timeout = 120000)]
|
||||||
|
public abstract void delete() throws DBusError, IOError;
|
||||||
|
|
||||||
|
[DBus (name = "StateChanged", timeout = 120000)]
|
||||||
|
public signal void state_changed(uint new_state, uint old_state, uint reason);
|
||||||
|
|
||||||
|
[DBus (name = "Udi", timeout = 120000)]
|
||||||
|
public abstract string udi { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "Interface", timeout = 120000)]
|
||||||
|
public abstract string interface { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "IpInterface", timeout = 120000)]
|
||||||
|
public abstract string ip_interface { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "Driver", timeout = 120000)]
|
||||||
|
public abstract string driver { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "DriverVersion", timeout = 120000)]
|
||||||
|
public abstract string driver_version { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "FirmwareVersion", timeout = 120000)]
|
||||||
|
public abstract string firmware_version { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "Capabilities", timeout = 120000)]
|
||||||
|
public abstract uint capabilities { get; }
|
||||||
|
|
||||||
|
[DBus (name = "Ip4Address", timeout = 120000)]
|
||||||
|
public abstract uint ip4_address { get; }
|
||||||
|
|
||||||
|
[DBus (name = "State", timeout = 120000)]
|
||||||
|
public abstract uint state { get; }
|
||||||
|
|
||||||
|
[DBus (name = "StateReason", timeout = 120000)]
|
||||||
|
public abstract DeviceStateReasonStruct state_reason { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "ActiveConnection", timeout = 120000)]
|
||||||
|
public abstract GLib.ObjectPath active_connection { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "Ip4Config", timeout = 120000)]
|
||||||
|
public abstract GLib.ObjectPath ip4_config { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "Dhcp4Config", timeout = 120000)]
|
||||||
|
public abstract GLib.ObjectPath dhcp4_config { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "Ip6Config", timeout = 120000)]
|
||||||
|
public abstract GLib.ObjectPath ip6_config { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "Dhcp6Config", timeout = 120000)]
|
||||||
|
public abstract GLib.ObjectPath dhcp6_config { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "Managed", timeout = 120000)]
|
||||||
|
public abstract bool managed { get; set; }
|
||||||
|
|
||||||
|
[DBus (name = "Autoconnect", timeout = 120000)]
|
||||||
|
public abstract bool autoconnect { get; set; }
|
||||||
|
|
||||||
|
[DBus (name = "FirmwareMissing", timeout = 120000)]
|
||||||
|
public abstract bool firmware_missing { get; }
|
||||||
|
|
||||||
|
[DBus (name = "NmPluginMissing", timeout = 120000)]
|
||||||
|
public abstract bool nm_plugin_missing { get; }
|
||||||
|
|
||||||
|
[DBus (name = "DeviceType", timeout = 120000)]
|
||||||
|
public abstract uint device_type { get; }
|
||||||
|
|
||||||
|
[DBus (name = "AvailableConnections", timeout = 120000)]
|
||||||
|
public abstract GLib.ObjectPath[] available_connections { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "PhysicalPortId", timeout = 120000)]
|
||||||
|
public abstract string physical_port_id { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "Mtu", timeout = 120000)]
|
||||||
|
public abstract uint mtu { get; }
|
||||||
|
|
||||||
|
[DBus (name = "Metered", timeout = 120000)]
|
||||||
|
public abstract uint metered { get; }
|
||||||
|
|
||||||
|
[DBus (name = "LldpNeighbors", timeout = 120000)]
|
||||||
|
public abstract GLib.HashTable<string, GLib.Variant>[] lldp_neighbors { owned get; }
|
||||||
|
|
||||||
|
[DBus (name = "Real", timeout = 120000)]
|
||||||
|
public abstract bool real { get; }
|
||||||
|
|
||||||
|
public struct DeviceStateReasonStruct {
|
||||||
|
public uint attr1;
|
||||||
|
public uint attr2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0"?> <!--*-nxml-*-->
|
<?xml version="1.0"?> <!--*-nxml-*-->
|
||||||
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
|
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
|
||||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
This file is part of MiracleCast.
|
This file is part of MiracleCast.
|
||||||
|
@ -8,50 +8,26 @@
|
||||||
|
|
||||||
<busconfig>
|
<busconfig>
|
||||||
|
|
||||||
<policy user="root">
|
<policy user="root">
|
||||||
<allow own="org.freedesktop.miracle"/>
|
<allow own="org.freedesktop.miracle"/>
|
||||||
<allow own="org.freedesktop.miracle.wifi"/>
|
<allow own="org.freedesktop.miracle.wifi"/>
|
||||||
<allow send_destination="org.freedesktop.miracle"/>
|
<allow own="org.freedesktop.miracle.wfd"/>
|
||||||
<allow send_destination="org.freedesktop.miracle.wifi"/>
|
<allow send_destination="org.freedesktop.miracle"/>
|
||||||
<allow receive_sender="org.freedesktop.miracle"/>
|
<allow send_destination="org.freedesktop.miracle.wifi"/>
|
||||||
<allow receive_sender="org.freedesktop.miracle.wifi"/>
|
<allow send_destination="org.freedesktop.miracle.wfd"/>
|
||||||
</policy>
|
<allow receive_sender="org.freedesktop.miracle"/>
|
||||||
|
<allow receive_sender="org.freedesktop.miracle.wifi"/>
|
||||||
|
<allow receive_sender="org.freedesktop.miracle.wfd"/>
|
||||||
|
</policy>
|
||||||
|
|
||||||
<policy context="default">
|
<policy context="default">
|
||||||
<deny send_destination="org.freedesktop.miracle"/>
|
<allow send_destination="org.freedesktop.miracle"/>
|
||||||
<deny send_destination="org.freedesktop.miracle.wifi"/>
|
<allow send_destination="org.freedesktop.miracle.wifi"/>
|
||||||
|
<allow send_destination="org.freedesktop.miracle.wfd"/>
|
||||||
|
|
||||||
<allow send_destination="org.freedesktop.miracle"
|
<allow receive_sender="org.freedesktop.miracle"/>
|
||||||
send_interface="org.freedesktop.DBus.Introspectable"/>
|
<allow receive_sender="org.freedesktop.miracle.wifi"/>
|
||||||
<allow send_destination="org.freedesktop.miracle.wifi"
|
<allow receive_sender="org.freedesktop.miracle.wfd"/>
|
||||||
send_interface="org.freedesktop.DBus.Introspectable"/>
|
</policy>
|
||||||
|
|
||||||
<allow send_destination="org.freedesktop.miracle"
|
|
||||||
send_interface="org.freedesktop.DBus.Peer"/>
|
|
||||||
<allow send_destination="org.freedesktop.miracle.wifi"
|
|
||||||
send_interface="org.freedesktop.DBus.Peer"/>
|
|
||||||
|
|
||||||
<allow send_destination="org.freedesktop.miracle"
|
|
||||||
send_interface="org.freedesktop.DBus.ObjectManager"/>
|
|
||||||
<allow send_destination="org.freedesktop.miracle.wifi"
|
|
||||||
send_interface="org.freedesktop.DBus.ObjectManager"/>
|
|
||||||
|
|
||||||
<allow send_destination="org.freedesktop.miracle"
|
|
||||||
send_interface="org.freedesktop.DBus.Properties"
|
|
||||||
send_member="Get"/>
|
|
||||||
<allow send_destination="org.freedesktop.miracle.wifi"
|
|
||||||
send_interface="org.freedesktop.DBus.Properties"
|
|
||||||
send_member="Get"/>
|
|
||||||
|
|
||||||
<allow send_destination="org.freedesktop.miracle"
|
|
||||||
send_interface="org.freedesktop.DBus.Properties"
|
|
||||||
send_member="GetAll"/>
|
|
||||||
<allow send_destination="org.freedesktop.miracle.wifi"
|
|
||||||
send_interface="org.freedesktop.DBus.Properties"
|
|
||||||
send_member="GetAll"/>
|
|
||||||
|
|
||||||
<allow receive_sender="org.freedesktop.miracle"/>
|
|
||||||
<allow receive_sender="org.freedesktop.miracle.wifi"/>
|
|
||||||
</policy>
|
|
||||||
|
|
||||||
</busconfig>
|
</busconfig>
|
||||||
|
|
5
res/org.freedesktop.miracle.wfd.service.cmake
Normal file
5
res/org.freedesktop.miracle.wfd.service.cmake
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[D-BUS Service]
|
||||||
|
Name=org.freedesktop.miracle.wfd
|
||||||
|
Exec=/bin/sh -c 'LOG_LEVEL=trace @CMAKE_INSTALL_PREFIX@/bin/miracle-dispd'
|
||||||
|
User=root
|
||||||
|
SystemdService=dbus-org.freedesktop.miracle.wfd.service
|
5
res/org.freedesktop.miracle.wfd.service.in
Normal file
5
res/org.freedesktop.miracle.wfd.service.in
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[D-BUS Service]
|
||||||
|
Name=@dbus_name_prefix@.wfd
|
||||||
|
Exec=/bin/sh -c 'LOG_LEVEL=trace @exec_path@/miracle-dispd'
|
||||||
|
User=root
|
||||||
|
SystemdService=dbus-@dbus_name_prefix@.wfd.service
|
5
res/org.freedesktop.miracle.wifi.service.cmake
Normal file
5
res/org.freedesktop.miracle.wifi.service.cmake
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[D-BUS Service]
|
||||||
|
Name=org.freedesktop.miracle.wifi
|
||||||
|
Exec=/bin/sh -c 'PATH=/sbin:/usr/bin @CMAKE_INSTALL_PREFIX@/bin/miracle-wifid --use-dev --log-level trace'
|
||||||
|
User=root
|
||||||
|
SystemdService=dbus-org.freedesktop.miracle.wifi.service
|
5
res/org.freedesktop.miracle.wifi.service.in
Normal file
5
res/org.freedesktop.miracle.wifi.service.in
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[D-BUS Service]
|
||||||
|
Name=@dbus_name_prefix@.wifi
|
||||||
|
Exec=/bin/sh -c 'PATH=/sbin:/usr/bin @exec_path@/miracle-wifid --use-dev --log-level trace'
|
||||||
|
User=root
|
||||||
|
SystemdService=dbus-@dbus_name_prefix@.wifi.service
|
64
res/sigint.c
Normal file
64
res/sigint.c
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||||
|
*
|
||||||
|
* MiracleCast 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) any later version.
|
||||||
|
*
|
||||||
|
* MiracleCast 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 MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <sys/signalfd.h>
|
||||||
|
#include "sigint.h"
|
||||||
|
|
||||||
|
typedef struct _SigintDelegate SigintDelegate;
|
||||||
|
|
||||||
|
struct _SigintDelegate
|
||||||
|
{
|
||||||
|
SigintHandler handler;
|
||||||
|
gpointer user_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
static gboolean sigint_on_signal(GIOChannel *c, GIOCondition e, gpointer d)
|
||||||
|
{
|
||||||
|
struct signalfd_siginfo siginfo;
|
||||||
|
g_io_channel_read_chars(c, (gchar *) &siginfo, sizeof(siginfo), NULL, NULL);
|
||||||
|
|
||||||
|
SigintDelegate *delegate = d;
|
||||||
|
(*delegate->handler)(delegate->user_data);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sigint_add_watch(SigintHandler handler, gpointer user_data)
|
||||||
|
{
|
||||||
|
sigset_t mask;
|
||||||
|
sigemptyset(&mask);
|
||||||
|
sigaddset(&mask, SIGINT);
|
||||||
|
sigprocmask(SIG_BLOCK, &mask, NULL);
|
||||||
|
|
||||||
|
SigintDelegate *d = g_malloc(sizeof(SigintDelegate));
|
||||||
|
*d = (SigintDelegate) {
|
||||||
|
handler = handler,
|
||||||
|
user_data = user_data
|
||||||
|
};
|
||||||
|
|
||||||
|
int fd = signalfd(-1, &mask, SFD_CLOEXEC);
|
||||||
|
GIOChannel *c = g_io_channel_unix_new(fd);
|
||||||
|
g_io_channel_set_encoding(c, NULL, NULL);
|
||||||
|
g_io_add_watch_full(c,
|
||||||
|
G_PRIORITY_DEFAULT,
|
||||||
|
G_IO_IN | G_IO_ERR | G_IO_HUP,
|
||||||
|
sigint_on_signal,
|
||||||
|
d,
|
||||||
|
g_free);
|
||||||
|
g_io_channel_unref(c);
|
||||||
|
}
|
29
res/sigint.h
Normal file
29
res/sigint.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||||
|
*
|
||||||
|
* MiracleCast 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) any later version.
|
||||||
|
*
|
||||||
|
* MiracleCast 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 MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#ifndef DISP_SIGFD_H
|
||||||
|
#define DISP_SIGFD_H
|
||||||
|
|
||||||
|
typedef void (*SigintHandler)(gpointer user_data);
|
||||||
|
|
||||||
|
void sigint_add_watch(SigintHandler handler, gpointer user_data);
|
||||||
|
|
||||||
|
#endif /* DISP_SIGFD_H */
|
8
res/sigint.vapi
Normal file
8
res/sigint.vapi
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
[CCode(cheader_filename="sigint.h")]
|
||||||
|
class Sigint
|
||||||
|
{
|
||||||
|
public delegate void Handler();
|
||||||
|
|
||||||
|
public static void add_watch(Handler handler);
|
||||||
|
public static void remove_watch();
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ add_subdirectory(wifi)
|
||||||
add_subdirectory(dhcp)
|
add_subdirectory(dhcp)
|
||||||
add_subdirectory(ctl)
|
add_subdirectory(ctl)
|
||||||
add_subdirectory(uibc)
|
add_subdirectory(uibc)
|
||||||
|
add_subdirectory(disp)
|
||||||
|
|
||||||
set(miracled_SRCS miracled.h miracled.c)
|
set(miracled_SRCS miracled.h miracled.c)
|
||||||
add_executable(miracled ${miracled_SRCS})
|
add_executable(miracled ${miracled_SRCS})
|
||||||
|
|
|
@ -50,4 +50,5 @@ endif(READLINE_FOUND)
|
||||||
|
|
||||||
target_link_libraries(miracle-sinkctl miracle-shared)
|
target_link_libraries(miracle-sinkctl miracle-shared)
|
||||||
|
|
||||||
INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/shared)
|
include_directories(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src
|
||||||
|
${CMAKE_SOURCE_DIR}/src/shared)
|
||||||
|
|
|
@ -39,7 +39,7 @@ static void sink_handle_options(struct ctl_sink *s,
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return cli_vERR(r);
|
return cli_vERR(r);
|
||||||
|
|
||||||
r = rtsp_message_append(rep, "<s>",
|
r = rtsp_message_append(rep, "<&>",
|
||||||
"Public",
|
"Public",
|
||||||
"org.wfa.wfd1.0, GET_PARAMETER, SET_PARAMETER");
|
"org.wfa.wfd1.0, GET_PARAMETER, SET_PARAMETER");
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
|
|
@ -81,7 +81,7 @@ bool check_rtsp_option(struct rtsp_message *m, char *option);
|
||||||
|
|
||||||
#define check_and_response_option(option, response) \
|
#define check_and_response_option(option, response) \
|
||||||
if (check_rtsp_option(m, option)) { \
|
if (check_rtsp_option(m, option)) { \
|
||||||
char option_response[512]; \
|
gchar option_response[512]; \
|
||||||
sprintf(option_response, "%s: %s", option, response); \
|
sprintf(option_response, "%s: %s", option, response); \
|
||||||
r = rtsp_message_append(rep, "{&}", option_response); \
|
r = rtsp_message_append(rep, "{&}", option_response); \
|
||||||
if (r < 0) {\
|
if (r < 0) {\
|
||||||
|
|
|
@ -637,6 +637,7 @@ int ctl_link_set_managed(struct ctl_link *l, bool val)
|
||||||
_sd_bus_message_unref_ sd_bus_message *m = NULL;
|
_sd_bus_message_unref_ sd_bus_message *m = NULL;
|
||||||
_sd_bus_error_free_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
_sd_bus_error_free_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
||||||
_shl_free_ char *node = NULL;
|
_shl_free_ char *node = NULL;
|
||||||
|
const char *method = val ? "Manage" : "Unmanage";
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (!l)
|
if (!l)
|
||||||
|
@ -654,26 +655,8 @@ int ctl_link_set_managed(struct ctl_link *l, bool val)
|
||||||
&m,
|
&m,
|
||||||
"org.freedesktop.miracle.wifi",
|
"org.freedesktop.miracle.wifi",
|
||||||
node,
|
node,
|
||||||
"org.freedesktop.DBus.Properties",
|
"org.freedesktop.miracle.wifi.Link",
|
||||||
"Set");
|
method);
|
||||||
if (r < 0)
|
|
||||||
return cli_log_create(r);
|
|
||||||
|
|
||||||
r = sd_bus_message_append(m, "ss",
|
|
||||||
"org.freedesktop.miracle.wifi.Link",
|
|
||||||
"Managed");
|
|
||||||
if (r < 0)
|
|
||||||
return cli_log_create(r);
|
|
||||||
|
|
||||||
r = sd_bus_message_open_container(m, 'v', "b");
|
|
||||||
if (r < 0)
|
|
||||||
return cli_log_create(r);
|
|
||||||
|
|
||||||
r = sd_bus_message_append(m, "b", val);
|
|
||||||
if (r < 0)
|
|
||||||
return cli_log_create(r);
|
|
||||||
|
|
||||||
r = sd_bus_message_close_container(m);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return cli_log_create(r);
|
return cli_log_create(r);
|
||||||
|
|
||||||
|
@ -823,7 +806,7 @@ static int ctl_wifi_parse_peer(struct ctl_wifi *w,
|
||||||
|
|
||||||
l = ctl_wifi_find_link_by_peer(w, label);
|
l = ctl_wifi_find_link_by_peer(w, label);
|
||||||
if (!l)
|
if (!l)
|
||||||
return 0;
|
return -EINVAL;
|
||||||
|
|
||||||
r = ctl_peer_new(&p, l, label);
|
r = ctl_peer_new(&p, l, label);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
@ -901,6 +884,9 @@ static int ctl_wifi_parse_object(struct ctl_wifi *w,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(label);
|
||||||
|
label = NULL;
|
||||||
|
|
||||||
r = sd_bus_path_decode(t,
|
r = sd_bus_path_decode(t,
|
||||||
"/org/freedesktop/miracle/wifi/peer",
|
"/org/freedesktop/miracle/wifi/peer",
|
||||||
&label);
|
&label);
|
||||||
|
@ -1068,6 +1054,38 @@ static int ctl_wifi_peer_fn(sd_bus_message *m,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ctl_wifi_unlink_all_links(struct ctl_wifi *w)
|
||||||
|
{
|
||||||
|
struct ctl_link *l;
|
||||||
|
while (!shl_dlist_empty(&w->links)) {
|
||||||
|
l = shl_dlist_last_entry(&w->links, struct ctl_link, list);
|
||||||
|
ctl_link_free(l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ctl_wifi_wifid_up_or_down_fn(sd_bus_message *m,
|
||||||
|
void *data,
|
||||||
|
sd_bus_error *err)
|
||||||
|
{
|
||||||
|
struct ctl_wifi *w = data;
|
||||||
|
char *from = NULL, *to = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = sd_bus_message_read(m, "sss", NULL, &from, &to);
|
||||||
|
if(r < 0) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(*from && !*to) {
|
||||||
|
ctl_wifi_unlink_all_links(w);
|
||||||
|
}
|
||||||
|
else if(!*from && *to) {
|
||||||
|
r = ctl_wifi_fetch(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
static int ctl_wifi_init(struct ctl_wifi *w)
|
static int ctl_wifi_init(struct ctl_wifi *w)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
|
@ -1099,6 +1117,18 @@ static int ctl_wifi_init(struct ctl_wifi *w)
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
r = sd_bus_add_match(w->bus, NULL,
|
||||||
|
"type='signal',"
|
||||||
|
"sender='org.freedesktop.DBus',"
|
||||||
|
"path='/org/freedesktop/DBus',"
|
||||||
|
"interface='org.freedesktop.DBus',"
|
||||||
|
"member='NameOwnerChanged',"
|
||||||
|
"arg0namespace='org.freedesktop.miracle.wifi'",
|
||||||
|
ctl_wifi_wifid_up_or_down_fn,
|
||||||
|
w);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1135,21 +1165,52 @@ int ctl_wifi_new(struct ctl_wifi **out, sd_bus *bus)
|
||||||
|
|
||||||
void ctl_wifi_free(struct ctl_wifi *w)
|
void ctl_wifi_free(struct ctl_wifi *w)
|
||||||
{
|
{
|
||||||
struct ctl_link *l;
|
|
||||||
|
|
||||||
if (!w)
|
if (!w)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
while (!shl_dlist_empty(&w->links)) {
|
ctl_wifi_unlink_all_links(w);
|
||||||
l = shl_dlist_last_entry(&w->links, struct ctl_link, list);
|
|
||||||
ctl_link_free(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctl_wifi_destroy(w);
|
ctl_wifi_destroy(w);
|
||||||
sd_bus_unref(w->bus);
|
sd_bus_unref(w->bus);
|
||||||
free(w);
|
free(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ctl_wifi_parse_objects(struct ctl_wifi *w,
|
||||||
|
sd_bus_message *m,
|
||||||
|
bool ignore_link_not_found)
|
||||||
|
{
|
||||||
|
bool again = false;
|
||||||
|
int r = sd_bus_message_enter_container(m, 'a', "{oa{sa{sv}}}");
|
||||||
|
if (r < 0)
|
||||||
|
return cli_log_parser(r);
|
||||||
|
|
||||||
|
while ((r = sd_bus_message_enter_container(m,
|
||||||
|
'e',
|
||||||
|
"oa{sa{sv}}")) > 0) {
|
||||||
|
r = ctl_wifi_parse_object(w, m, true);
|
||||||
|
if(ignore_link_not_found && -EINVAL == r) {
|
||||||
|
r = sd_bus_message_skip(m, "a{sa{sv}}");
|
||||||
|
if(0 > r)
|
||||||
|
return cli_log_parser(r);
|
||||||
|
again = true;
|
||||||
|
}
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_bus_message_exit_container(m);
|
||||||
|
if (r < 0)
|
||||||
|
return cli_log_parser(r);
|
||||||
|
}
|
||||||
|
if (r < 0)
|
||||||
|
return cli_log_parser(r);
|
||||||
|
|
||||||
|
r = sd_bus_message_exit_container(m);
|
||||||
|
if (r < 0)
|
||||||
|
return cli_log_parser(r);
|
||||||
|
|
||||||
|
return again ? -EAGAIN : 0;
|
||||||
|
}
|
||||||
|
|
||||||
int ctl_wifi_fetch(struct ctl_wifi *w)
|
int ctl_wifi_fetch(struct ctl_wifi *w)
|
||||||
{
|
{
|
||||||
_sd_bus_message_unref_ sd_bus_message *m = NULL;
|
_sd_bus_message_unref_ sd_bus_message *m = NULL;
|
||||||
|
@ -1173,29 +1234,20 @@ int ctl_wifi_fetch(struct ctl_wifi *w)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = sd_bus_message_enter_container(m, 'a', "{oa{sa{sv}}}");
|
r = ctl_wifi_parse_objects(w, m, true);
|
||||||
if (r < 0)
|
if(0 <= r || -EAGAIN != r) {
|
||||||
return cli_log_parser(r);
|
goto end;
|
||||||
|
|
||||||
while ((r = sd_bus_message_enter_container(m,
|
|
||||||
'e',
|
|
||||||
"oa{sa{sv}}")) > 0) {
|
|
||||||
r = ctl_wifi_parse_object(w, m, true);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
r = sd_bus_message_exit_container(m);
|
|
||||||
if (r < 0)
|
|
||||||
return cli_log_parser(r);
|
|
||||||
}
|
}
|
||||||
if (r < 0)
|
|
||||||
return cli_log_parser(r);
|
|
||||||
|
|
||||||
r = sd_bus_message_exit_container(m);
|
r = sd_bus_message_rewind(m, true);
|
||||||
if (r < 0)
|
if(0 > r) {
|
||||||
return cli_log_parser(r);
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
r = ctl_wifi_parse_objects(w, m, false);
|
||||||
|
|
||||||
|
end:
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ctl_link *ctl_wifi_find_link(struct ctl_wifi *w,
|
struct ctl_link *ctl_wifi_find_link(struct ctl_wifi *w,
|
||||||
|
|
|
@ -30,7 +30,9 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <systemd/sd-bus.h>
|
#include <systemd/sd-bus.h>
|
||||||
#include "shl_dlist.h"
|
#include "shl_dlist.h"
|
||||||
|
#include "shl_htable.h"
|
||||||
#include "shl_log.h"
|
#include "shl_log.h"
|
||||||
|
#include "wfd.h"
|
||||||
|
|
||||||
/* *sigh* readline doesn't include all their deps, so put them last */
|
/* *sigh* readline doesn't include all their deps, so put them last */
|
||||||
#include <readline/history.h>
|
#include <readline/history.h>
|
||||||
|
@ -262,3 +264,4 @@ void ctl_fn_sink_resolution_set(struct ctl_sink *s);
|
||||||
void cli_fn_help(void);
|
void cli_fn_help(void);
|
||||||
|
|
||||||
#endif /* CTL_CTL_H */
|
#endif /* CTL_CTL_H */
|
||||||
|
|
||||||
|
|
|
@ -279,10 +279,8 @@ static int cmd_run(char **args, unsigned int n)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!l->managed) {
|
if (!l->managed)
|
||||||
cli_printf("link %s not managed\n", l->label);
|
return log_EUNMANAGED();
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
run_on(l);
|
run_on(l);
|
||||||
|
|
||||||
|
@ -314,10 +312,8 @@ static int cmd_bind(char **args, unsigned int n)
|
||||||
if (!l)
|
if (!l)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!l->managed) {
|
if (!l->managed)
|
||||||
cli_printf("link %s not managed\n", l->label);
|
return log_EUNMANAGED();
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
run_on(l);
|
run_on(l);
|
||||||
|
|
||||||
|
@ -859,9 +855,8 @@ static int ctl_main(int argc, char *argv[])
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
left = argc - optind;
|
left = argc - optind;
|
||||||
left = left <= 0 ? 0 : left;
|
r = ctl_interactive(argv + optind, left <= 0 ? 0 : left);
|
||||||
r = ctl_interactive(argv + optind, left);
|
|
||||||
|
|
||||||
/* stop all scans */
|
/* stop all scans */
|
||||||
shl_dlist_for_each(i, &wifi->links) {
|
shl_dlist_for_each(i, &wifi->links) {
|
||||||
|
@ -912,19 +907,19 @@ static int parse_argv(int argc, char *argv[])
|
||||||
|
|
||||||
uibc_option = false;
|
uibc_option = false;
|
||||||
uibc_enabled = false;
|
uibc_enabled = false;
|
||||||
external_player = false;
|
external_player = false;
|
||||||
rstp_port = DEFAULT_RSTP_PORT;
|
rstp_port = DEFAULT_RSTP_PORT;
|
||||||
|
|
||||||
while ((c = getopt_long(argc, argv, "he:p:", options, NULL)) >= 0) {
|
while ((c = getopt_long(argc, argv, "he:p:", options, NULL)) >= 0) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'h':
|
case 'h':
|
||||||
cli_fn_help();
|
cli_fn_help();
|
||||||
return 0;
|
return 0;
|
||||||
case ARG_HELP_COMMANDS:
|
case ARG_HELP_COMMANDS:
|
||||||
return cli_help(cli_cmds, 20);
|
return cli_help(cli_cmds, 20);
|
||||||
case ARG_HELP_RES:
|
case ARG_HELP_RES:
|
||||||
wfd_print_resolutions("");
|
wfd_print_resolutions("");
|
||||||
return 0;
|
return 0;
|
||||||
case ARG_VERSION:
|
case ARG_VERSION:
|
||||||
puts(PACKAGE_STRING);
|
puts(PACKAGE_STRING);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -976,74 +971,74 @@ static int parse_argv(int argc, char *argv[])
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
bool free_argv = false;
|
bool free_argv = false;
|
||||||
|
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
|
|
||||||
GKeyFile* gkf = load_ini_file();
|
GKeyFile* gkf = load_ini_file();
|
||||||
|
|
||||||
gchar** autocmds_free = NULL;
|
gchar** autocmds_free = NULL;
|
||||||
if (gkf) {
|
if (gkf) {
|
||||||
player = g_key_file_get_string (gkf, "sinkctl", "external-player", NULL);
|
player = g_key_file_get_string (gkf, "sinkctl", "external-player", NULL);
|
||||||
if (player) {
|
if (player) {
|
||||||
external_player = true;
|
external_player = true;
|
||||||
}
|
}
|
||||||
gchar* log_level;
|
gchar* log_level;
|
||||||
log_level = g_key_file_get_string (gkf, "sinkctl", "log-journal-level", NULL);
|
log_level = g_key_file_get_string (gkf, "sinkctl", "log-journal-level", NULL);
|
||||||
if (log_level) {
|
if (log_level) {
|
||||||
log_max_sev = log_parse_arg(log_level);
|
log_max_sev = log_parse_arg(log_level);
|
||||||
g_free(log_level);
|
g_free(log_level);
|
||||||
}
|
}
|
||||||
log_level = g_key_file_get_string (gkf, "sinkctl", "log-level", NULL);
|
log_level = g_key_file_get_string (gkf, "sinkctl", "log-level", NULL);
|
||||||
if (log_level) {
|
if (log_level) {
|
||||||
cli_max_sev = log_parse_arg(log_level);
|
cli_max_sev = log_parse_arg(log_level);
|
||||||
g_free(log_level);
|
g_free(log_level);
|
||||||
}
|
}
|
||||||
gchar* rstp_port_str = g_key_file_get_string (gkf, "sinkctl", "rstp-port", NULL);
|
gchar* rstp_port_str = g_key_file_get_string (gkf, "sinkctl", "rstp-port", NULL);
|
||||||
if (rstp_port_str) {
|
if (rstp_port_str) {
|
||||||
rstp_port = atoi(rstp_port_str);
|
rstp_port = atoi(rstp_port_str);
|
||||||
g_free(rstp_port_str);
|
g_free(rstp_port_str);
|
||||||
}
|
}
|
||||||
gchar* autocmd;
|
gchar* autocmd;
|
||||||
autocmd = g_key_file_get_string (gkf, "sinkctl", "autocmd", NULL);
|
autocmd = g_key_file_get_string (gkf, "sinkctl", "autocmd", NULL);
|
||||||
if (autocmd) {
|
if (autocmd) {
|
||||||
gchar** autocmds = g_strsplit(autocmd, " ", -1);
|
gchar** autocmds = g_strsplit(autocmd, " ", -1);
|
||||||
autocmds_free = autocmds;
|
autocmds_free = autocmds;
|
||||||
while (*autocmds) {
|
while (*autocmds) {
|
||||||
if (strcmp(*autocmds, "") != 0) {
|
if (strcmp(*autocmds, "") != 0) {
|
||||||
gchar **newv = malloc((argc + 2) * sizeof(gchar*));
|
gchar **newv = malloc((argc + 2) * sizeof(gchar*));
|
||||||
memmove(newv, argv, sizeof(gchar*) * argc);
|
memmove(newv, argv, sizeof(gchar*) * argc);
|
||||||
newv[argc] = *autocmds;
|
newv[argc] = *autocmds;
|
||||||
newv[argc+1] = NULL;
|
newv[argc+1] = NULL;
|
||||||
argc++;
|
argc++;
|
||||||
if (free_argv) {
|
if (free_argv) {
|
||||||
free(argv);
|
free(argv);
|
||||||
}
|
}
|
||||||
argv = newv;
|
argv = newv;
|
||||||
free_argv = true;
|
free_argv = true;
|
||||||
}
|
}
|
||||||
autocmds++;
|
autocmds++;
|
||||||
}
|
}
|
||||||
g_free(autocmd);
|
g_free(autocmd);
|
||||||
}
|
}
|
||||||
gchar** sinkctl_keys;
|
gchar** sinkctl_keys;
|
||||||
gsize len = 0;
|
gsize len = 0;
|
||||||
protocol_extensions = g_hash_table_new(g_str_hash, g_str_equal);
|
protocol_extensions = g_hash_table_new(g_str_hash, g_str_equal);
|
||||||
|
|
||||||
sinkctl_keys = g_key_file_get_keys (gkf,
|
sinkctl_keys = g_key_file_get_keys (gkf,
|
||||||
"sinkctl",
|
"sinkctl",
|
||||||
&len,
|
&len,
|
||||||
NULL);
|
NULL);
|
||||||
for (int i = 0; i < (int)len; i++) {
|
for (int i = 0; i < (int)len; i++) {
|
||||||
if (g_str_has_prefix(sinkctl_keys[i], "extends.")) {
|
if (g_str_has_prefix(sinkctl_keys[i], "extends.")) {
|
||||||
gchar* orig_key = sinkctl_keys[i];
|
gchar* orig_key = sinkctl_keys[i];
|
||||||
gchar* key = orig_key+8;
|
gchar* key = orig_key+8;
|
||||||
gchar* value = g_key_file_get_string (gkf, "sinkctl", orig_key, NULL);
|
gchar* value = g_key_file_get_string (gkf, "sinkctl", orig_key, NULL);
|
||||||
g_hash_table_insert(protocol_extensions, key, value);
|
g_hash_table_insert(protocol_extensions, key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g_key_file_free(gkf);
|
g_key_file_free(gkf);
|
||||||
}
|
}
|
||||||
|
|
||||||
r = parse_argv(argc, argv);
|
r = parse_argv(argc, argv);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
@ -1058,10 +1053,10 @@ int main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
r = ctl_main(argc, argv);
|
r = ctl_main(argc, argv);
|
||||||
g_strfreev(autocmds_free);
|
g_strfreev(autocmds_free);
|
||||||
if (free_argv) {
|
if (free_argv) {
|
||||||
free(argv);
|
free(argv);
|
||||||
}
|
}
|
||||||
sd_bus_unref(bus);
|
sd_bus_unref(bus);
|
||||||
|
|
||||||
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||||
|
|
632
src/ctl/wfd.c
632
src/ctl/wfd.c
|
@ -20,109 +20,152 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
#include "ctl.h"
|
#include "ctl.h"
|
||||||
|
#include "wfd.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
struct resolution_bitmap {
|
typedef int (*wfd_sube_parse_func)(const char *in, union wfd_sube *out);
|
||||||
int index;
|
|
||||||
int hres;
|
struct wfd_sube_info
|
||||||
int vres;
|
{
|
||||||
int fps;
|
wfd_sube_parse_func parser;
|
||||||
|
uint8_t len;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int wfd_sube_parse_device_info(const char *in, union wfd_sube *out);
|
||||||
|
static int wfd_sube_parse_audio_formats(const char *in, union wfd_sube *out);
|
||||||
|
static int wfd_sube_parse_video_formats(const char *in, union wfd_sube *out);
|
||||||
|
static int wfd_sube_parse_ext_caps(const char *in, union wfd_sube *out);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CEA resolutions and refrash rate bitmap/index table
|
* CEA resolutions and refrash rate bitmap/index table
|
||||||
* also used in native resolution field
|
* also used in native resolution field
|
||||||
*/
|
*/
|
||||||
struct resolution_bitmap resolutions_cea[] = {
|
static const struct wfd_resolution resolutions_cea[] = {
|
||||||
{0, 640, 480, 60}, /* p60 */
|
{0, 640, 480, 60, 1}, /* p60 */
|
||||||
{1, 720, 480, 60}, /* p60 */
|
{1, 720, 480, 60, 1}, /* p60 */
|
||||||
{2, 720, 480, 60}, /* i60 */
|
{2, 720, 480, 60, 0}, /* i60 */
|
||||||
{3, 720, 576, 50}, /* p50 */
|
{3, 720, 576, 50, 1}, /* p50 */
|
||||||
{4, 720, 576, 50}, /* i50 */
|
{4, 720, 576, 50, 0}, /* i50 */
|
||||||
{5, 1280, 720, 30}, /* p30 */
|
{5, 1280, 720, 30, 1}, /* p30 */
|
||||||
{6, 1280, 720, 60}, /* p60 */
|
{6, 1280, 720, 60, 1}, /* p60 */
|
||||||
{7, 1920, 1080, 30}, /* p30 */
|
{7, 1920, 1080, 30, 1}, /* p30 */
|
||||||
{8, 1920, 1080, 60}, /* p60 */
|
{8, 1920, 1080, 60, 1}, /* p60 */
|
||||||
{9, 1920, 1080, 60}, /* i60 */
|
{9, 1920, 1080, 60, 0}, /* i60 */
|
||||||
{10, 1280, 720, 25}, /* p25 */
|
{10, 1280, 720, 25, 1}, /* p25 */
|
||||||
{11, 1280, 720, 50}, /* p50 */
|
{11, 1280, 720, 50, 1}, /* p50 */
|
||||||
{12, 1920, 1080, 25}, /* p25 */
|
{12, 1920, 1080, 25, 1}, /* p25 */
|
||||||
{13, 1920, 1080, 50}, /* p50 */
|
{13, 1920, 1080, 50, 1}, /* p50 */
|
||||||
{14, 1920, 1080, 50}, /* i50 */
|
{14, 1920, 1080, 50, 0}, /* i50 */
|
||||||
{15, 1280, 720, 24}, /* p24 */
|
{15, 1280, 720, 24, 1}, /* p24 */
|
||||||
{16, 1920, 1080, 24}, /* p24 */
|
{16, 1920, 1080, 24, 1}, /* p24 */
|
||||||
{0, 0, 0, 0},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct resolution_bitmap resolutions_vesa[] = {
|
static const struct wfd_resolution resolutions_vesa[] = {
|
||||||
{0, 800, 600, 30}, /* p30 */
|
{0, 800, 600, 30, 1}, /* p30 */
|
||||||
{1, 800, 600, 60}, /* p60 */
|
{1, 800, 600, 60, 1}, /* p60 */
|
||||||
{2, 1024, 768, 30}, /* p30 */
|
{2, 1024, 768, 30, 1}, /* p30 */
|
||||||
{3, 1024, 768, 60}, /* p60 */
|
{3, 1024, 768, 60, 1}, /* p60 */
|
||||||
{4, 1152, 854, 30}, /* p30 */
|
{4, 1152, 854, 30, 1}, /* p30 */
|
||||||
{5, 1152, 854, 60}, /* p60 */
|
{5, 1152, 854, 60, 1}, /* p60 */
|
||||||
{6, 1280, 768, 30}, /* p30 */
|
{6, 1280, 768, 30, 1}, /* p30 */
|
||||||
{7, 1280, 768, 60}, /* p60 */
|
{7, 1280, 768, 60, 1}, /* p60 */
|
||||||
{8, 1280, 800, 30}, /* p30 */
|
{8, 1280, 800, 30, 1}, /* p30 */
|
||||||
{9, 1280, 800, 60}, /* p60 */
|
{9, 1280, 800, 60, 1}, /* p60 */
|
||||||
{10, 1360, 768, 30}, /* p30 */
|
{10, 1360, 768, 30, 1}, /* p30 */
|
||||||
{11, 1360, 768, 60}, /* p60 */
|
{11, 1360, 768, 60, 1}, /* p60 */
|
||||||
{12, 1366, 768, 30}, /* p30 */
|
{12, 1366, 768, 30, 1}, /* p30 */
|
||||||
{13, 1366, 768, 60}, /* p60 */
|
{13, 1366, 768, 60, 1}, /* p60 */
|
||||||
{14, 1280, 1024, 30}, /* p30 */
|
{14, 1280, 1024, 30, 1}, /* p30 */
|
||||||
{15, 1280, 1024, 60}, /* p60 */
|
{15, 1280, 1024, 60, 1}, /* p60 */
|
||||||
{16, 1440, 1050, 30}, /* p30 */
|
{16, 1440, 1050, 30, 1}, /* p30 */
|
||||||
{17, 1440, 1050, 60}, /* p60 */
|
{17, 1440, 1050, 60, 1}, /* p60 */
|
||||||
{18, 1440, 900, 30}, /* p30 */
|
{18, 1440, 900, 30, 1}, /* p30 */
|
||||||
{19, 1440, 900, 60}, /* p60 */
|
{19, 1440, 900, 60, 1}, /* p60 */
|
||||||
{20, 1600, 900, 30}, /* p30 */
|
{20, 1600, 900, 30, 1}, /* p30 */
|
||||||
{21, 1600, 900, 60}, /* p60 */
|
{21, 1600, 900, 60, 1}, /* p60 */
|
||||||
{22, 1600, 1200, 30}, /* p30 */
|
{22, 1600, 1200, 30, 1}, /* p30 */
|
||||||
{23, 1600, 1200, 60}, /* p60 */
|
{23, 1600, 1200, 60, 1}, /* p60 */
|
||||||
{24, 1680, 1024, 30}, /* p30 */
|
{24, 1680, 1024, 30, 1}, /* p30 */
|
||||||
{25, 1680, 1024, 60}, /* p60 */
|
{25, 1680, 1024, 60, 1}, /* p60 */
|
||||||
{26, 1680, 1050, 30}, /* p30 */
|
{26, 1680, 1050, 30, 1}, /* p30 */
|
||||||
{27, 1680, 1050, 60}, /* p60 */
|
{27, 1680, 1050, 60, 1}, /* p60 */
|
||||||
{28, 1920, 1200, 30}, /* p30 */
|
{28, 1920, 1200, 30, 1}, /* p30 */
|
||||||
{0, 0, 0, 0},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct resolution_bitmap resolutions_hh[] = {
|
static const struct wfd_resolution resolutions_hh[] = {
|
||||||
{0, 800, 480, 30}, /* p30 */
|
{0, 800, 480, 30, 1}, /* p30 */
|
||||||
{1, 800, 480, 60}, /* p60 */
|
{1, 800, 480, 60, 1}, /* p60 */
|
||||||
{2, 854, 480, 30}, /* p30 */
|
{2, 854, 480, 30, 1}, /* p30 */
|
||||||
{3, 854, 480, 60}, /* p60 */
|
{3, 854, 480, 60, 1}, /* p60 */
|
||||||
{4, 864, 480, 30}, /* p30 */
|
{4, 864, 480, 30, 1}, /* p30 */
|
||||||
{5, 864, 480, 60}, /* p60 */
|
{5, 864, 480, 60, 1}, /* p60 */
|
||||||
{6, 640, 360, 30}, /* p30 */
|
{6, 640, 360, 30, 1}, /* p30 */
|
||||||
{7, 640, 360, 60}, /* p60 */
|
{7, 640, 360, 60, 1}, /* p60 */
|
||||||
{8, 960, 540, 30}, /* p30 */
|
{8, 960, 540, 30, 1}, /* p30 */
|
||||||
{9, 960, 540, 60}, /* p60 */
|
{9, 960, 540, 60, 1}, /* p60 */
|
||||||
{10, 848, 480, 30}, /* p30 */
|
{10, 848, 480, 30, 1}, /* p30 */
|
||||||
{11, 848, 480, 60}, /* p60 */
|
{11, 848, 480, 60, 1}, /* p60 */
|
||||||
{0, 0, 0, 0},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct wfd_sube_info parser_tbl[WFD_SUBE_ID_RESERVED] = {
|
||||||
|
[WFD_SUBE_ID_DEVICE_INFO] = { .parser = wfd_sube_parse_device_info, .len = 6 },
|
||||||
|
[WFD_SUBE_ID_AUDIO_FORMATS] = { .parser = wfd_sube_parse_audio_formats, .len = 15 },
|
||||||
|
[WFD_SUBE_ID_VIDEO_FORMATS] = { .parser = wfd_sube_parse_video_formats, .len = 21 },
|
||||||
|
[WFD_SUBE_ID_WFD_EXT_CAPS] = { .parser = wfd_sube_parse_ext_caps, .len = 2 },
|
||||||
|
};
|
||||||
|
|
||||||
|
int wfd_get_resolutions(enum wfd_resolution_standard std,
|
||||||
|
int index,
|
||||||
|
struct wfd_resolution *out)
|
||||||
|
{
|
||||||
|
switch(std) {
|
||||||
|
case WFD_RESOLUTION_STANDARD_CEA:
|
||||||
|
if(0 >= index || index >= SHL_ARRAY_LENGTH(resolutions_cea)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*out = resolutions_cea[index];
|
||||||
|
return 0;
|
||||||
|
case WFD_RESOLUTION_STANDARD_VESA:
|
||||||
|
if(0 >= index || index >= SHL_ARRAY_LENGTH(resolutions_vesa)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*out = resolutions_vesa[index];
|
||||||
|
return 0;
|
||||||
|
case WFD_RESOLUTION_STANDARD_HH:
|
||||||
|
if(0 >= index || index >= SHL_ARRAY_LENGTH(resolutions_hh)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*out = resolutions_hh[index];
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
void wfd_print_resolutions(char * prefix)
|
void wfd_print_resolutions(char * prefix)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
printf("%sCEA resolutions:\n", prefix);
|
printf("%sCEA resolutions:\n", prefix);
|
||||||
for (i = 0; resolutions_cea[i].hres != 0; i++) {
|
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_cea); i++) {
|
||||||
printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
|
printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
|
||||||
resolutions_cea[i].index, 1 << resolutions_cea[i].index,
|
resolutions_cea[i].index, 1 << resolutions_cea[i].index,
|
||||||
resolutions_cea[i].hres, resolutions_cea[i].vres,
|
resolutions_cea[i].hres, resolutions_cea[i].vres,
|
||||||
resolutions_cea[i].fps);
|
resolutions_cea[i].fps);
|
||||||
}
|
}
|
||||||
printf("%sVESA resolutions:\n", prefix);
|
printf("%sVESA resolutions:\n", prefix);
|
||||||
for (i = 0; resolutions_vesa[i].hres != 0; i++) {
|
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_vesa); i++) {
|
||||||
printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
|
printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
|
||||||
resolutions_vesa[i].index, 1 << resolutions_vesa[i].index,
|
resolutions_vesa[i].index, 1 << resolutions_vesa[i].index,
|
||||||
resolutions_vesa[i].hres, resolutions_vesa[i].vres,
|
resolutions_vesa[i].hres, resolutions_vesa[i].vres,
|
||||||
resolutions_vesa[i].fps);
|
resolutions_vesa[i].fps);
|
||||||
}
|
}
|
||||||
printf("%sHH resolutions:\n", prefix);
|
printf("%sHH resolutions:\n", prefix);
|
||||||
for (i = 0; resolutions_hh[i].hres != 0; i++) {
|
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_hh); i++) {
|
||||||
printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
|
printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
|
||||||
resolutions_hh[i].index, 1 << resolutions_hh[i].index,
|
resolutions_hh[i].index, 1 << resolutions_hh[i].index,
|
||||||
resolutions_hh[i].hres, resolutions_hh[i].vres,
|
resolutions_hh[i].hres, resolutions_hh[i].vres,
|
||||||
|
@ -141,7 +184,7 @@ void vfd_dump_resolutions(uint32_t cea_mask, uint32_t vesa_mask, uint32_t hh_mas
|
||||||
|
|
||||||
if (cea_mask) {
|
if (cea_mask) {
|
||||||
cli_debug("CEA resolutions:");
|
cli_debug("CEA resolutions:");
|
||||||
for (i = 0; resolutions_cea[i].hres != 0; i++)
|
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_cea); i++)
|
||||||
if ((1 << resolutions_cea[i].index) & cea_mask)
|
if ((1 << resolutions_cea[i].index) & cea_mask)
|
||||||
cli_debug("\t%2d %08x %4dx%4d@%d\n",
|
cli_debug("\t%2d %08x %4dx%4d@%d\n",
|
||||||
resolutions_cea[i].index, 1 << resolutions_cea[i].index,
|
resolutions_cea[i].index, 1 << resolutions_cea[i].index,
|
||||||
|
@ -150,7 +193,7 @@ void vfd_dump_resolutions(uint32_t cea_mask, uint32_t vesa_mask, uint32_t hh_mas
|
||||||
}
|
}
|
||||||
if (vesa_mask) {
|
if (vesa_mask) {
|
||||||
cli_debug("VESA resolutions:");
|
cli_debug("VESA resolutions:");
|
||||||
for (i = 0; resolutions_vesa[i].hres != 0; i++)
|
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_vesa); i++)
|
||||||
if ((1 << resolutions_vesa[i].index) & vesa_mask)
|
if ((1 << resolutions_vesa[i].index) & vesa_mask)
|
||||||
cli_debug("\t%2d %08x %4dx%4d@%d\n",
|
cli_debug("\t%2d %08x %4dx%4d@%d\n",
|
||||||
resolutions_vesa[i].index, 1 << resolutions_vesa[i].index,
|
resolutions_vesa[i].index, 1 << resolutions_vesa[i].index,
|
||||||
|
@ -159,7 +202,7 @@ void vfd_dump_resolutions(uint32_t cea_mask, uint32_t vesa_mask, uint32_t hh_mas
|
||||||
}
|
}
|
||||||
if (hh_mask) {
|
if (hh_mask) {
|
||||||
cli_debug("HH resolutions:");
|
cli_debug("HH resolutions:");
|
||||||
for (i = 0; resolutions_hh[i].hres != 0; i++)
|
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_hh); i++)
|
||||||
if ((1 << resolutions_hh[i].index) & hh_mask)
|
if ((1 << resolutions_hh[i].index) & hh_mask)
|
||||||
cli_debug("\t%2d %08x %4dx%4d@%d\n",
|
cli_debug("\t%2d %08x %4dx%4d@%d\n",
|
||||||
resolutions_hh[i].index, 1 << resolutions_hh[i].index,
|
resolutions_hh[i].index, 1 << resolutions_hh[i].index,
|
||||||
|
@ -175,7 +218,7 @@ int vfd_get_cea_resolution(uint32_t mask, int *hres, int *vres)
|
||||||
if (!mask)
|
if (!mask)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
for (i = 0; resolutions_cea[i].hres != 0; i++) {
|
for (i = SHL_ARRAY_LENGTH(resolutions_cea) - 1; i >= 0; --i) {
|
||||||
if ((1 << resolutions_cea[i].index) & mask) {
|
if ((1 << resolutions_cea[i].index) & mask) {
|
||||||
*vres = resolutions_cea[i].vres;
|
*vres = resolutions_cea[i].vres;
|
||||||
*hres = resolutions_cea[i].hres;
|
*hres = resolutions_cea[i].hres;
|
||||||
|
@ -192,7 +235,7 @@ int vfd_get_vesa_resolution(uint32_t mask, int *hres, int *vres)
|
||||||
if (!mask)
|
if (!mask)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
for (i = 0; resolutions_vesa[i].hres != 0; i++) {
|
for (i = SHL_ARRAY_LENGTH(resolutions_vesa) - 1; i >= 0; --i) {
|
||||||
if ((1 << resolutions_vesa[i].index) & mask) {
|
if ((1 << resolutions_vesa[i].index) & mask) {
|
||||||
*vres = resolutions_vesa[i].vres;
|
*vres = resolutions_vesa[i].vres;
|
||||||
*hres = resolutions_vesa[i].hres;
|
*hres = resolutions_vesa[i].hres;
|
||||||
|
@ -209,7 +252,7 @@ int vfd_get_hh_resolution(uint32_t mask, int *hres, int *vres)
|
||||||
if (!mask)
|
if (!mask)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
for (i = 0; resolutions_hh[i].hres != 0; i++) {
|
for (i = SHL_ARRAY_LENGTH(resolutions_hh); i >= 0; --i) {
|
||||||
if ((1 << resolutions_hh[i].index) & mask) {
|
if ((1 << resolutions_hh[i].index) & mask) {
|
||||||
*vres = resolutions_hh[i].vres;
|
*vres = resolutions_hh[i].vres;
|
||||||
*hres = resolutions_hh[i].hres;
|
*hres = resolutions_hh[i].hres;
|
||||||
|
@ -218,3 +261,436 @@ int vfd_get_hh_resolution(uint32_t mask, int *hres, int *vres)
|
||||||
}
|
}
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int vfd_get_mask_from_resolution(int hres,
|
||||||
|
int vres,
|
||||||
|
enum wfd_resolution_standard *out_std,
|
||||||
|
uint32_t *out_mask)
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
const struct wfd_resolution *entries;
|
||||||
|
size_t n_entries;
|
||||||
|
} tbls[] = {
|
||||||
|
[WFD_RESOLUTION_STANDARD_CEA] = {
|
||||||
|
resolutions_cea,
|
||||||
|
SHL_ARRAY_LENGTH(resolutions_cea)
|
||||||
|
},
|
||||||
|
[WFD_RESOLUTION_STANDARD_VESA] = {
|
||||||
|
resolutions_vesa,
|
||||||
|
SHL_ARRAY_LENGTH(resolutions_vesa)
|
||||||
|
},
|
||||||
|
[WFD_RESOLUTION_STANDARD_HH] = {
|
||||||
|
resolutions_hh,
|
||||||
|
SHL_ARRAY_LENGTH(resolutions_hh)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
enum wfd_resolution_standard std = WFD_RESOLUTION_STANDARD_CEA;
|
||||||
|
uint32_t mask = 0;
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
for(; std <= WFD_RESOLUTION_STANDARD_HH; ++ std) {
|
||||||
|
for(n = 0; n < tbls[std].n_entries; ++ n) {
|
||||||
|
if(hres == tbls[std].entries[n].hres &&
|
||||||
|
vres == tbls[std].entries[n].vres) {
|
||||||
|
mask |= 1 << n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mask) {
|
||||||
|
if(out_mask) {
|
||||||
|
*out_mask = mask;
|
||||||
|
}
|
||||||
|
if(out_std) {
|
||||||
|
*out_std = std;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wfd_sube_parse_device_info(const char *in, union wfd_sube *out)
|
||||||
|
{
|
||||||
|
int r = sscanf(in, "%4hx%4hx%4hx",
|
||||||
|
&out->dev_info.dev_info,
|
||||||
|
&out->dev_info.rtsp_port,
|
||||||
|
&out->dev_info.max_throughput);
|
||||||
|
|
||||||
|
return 3 == r ? 0 : -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wfd_sube_parse_audio_formats(const char *in, union wfd_sube *out)
|
||||||
|
{
|
||||||
|
int r = sscanf(in, "%4x%1hhx%4x%1hhx%4x%1hhx",
|
||||||
|
&out->audio_formats.lpcm_modes,
|
||||||
|
&out->audio_formats.lpcm_dec_latency,
|
||||||
|
&out->audio_formats.aac_modes,
|
||||||
|
&out->audio_formats.aac_dec_latency,
|
||||||
|
&out->audio_formats.ac3_modes,
|
||||||
|
&out->audio_formats.ac3_dec_latency);
|
||||||
|
|
||||||
|
return 6 == r ? 0 : -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wfd_sube_parse_video_formats(const char *in, union wfd_sube *out)
|
||||||
|
{
|
||||||
|
int r = sscanf(in, "%4x%4x%4x%1hhx%1hhx%1hhx%1hhx%2hx%2hx%1hhx",
|
||||||
|
&out->video_formats.cea,
|
||||||
|
&out->video_formats.vesa,
|
||||||
|
&out->video_formats.hh,
|
||||||
|
&out->video_formats.native,
|
||||||
|
&out->video_formats.profiles,
|
||||||
|
&out->video_formats.levels,
|
||||||
|
&out->video_formats.latency,
|
||||||
|
&out->video_formats.min_slice_size,
|
||||||
|
&out->video_formats.slice_enc_params,
|
||||||
|
&out->video_formats.video_frame_rate_ctl);
|
||||||
|
|
||||||
|
return 12 == r ? 0 : -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wfd_sube_parse_ext_caps(const char *in, union wfd_sube *out)
|
||||||
|
{
|
||||||
|
int r = sscanf(in, "%2hx", &out->extended_caps.caps);
|
||||||
|
|
||||||
|
return 1 == r ? 0 : -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wfd_sube_parse(const char *in, union wfd_sube *out)
|
||||||
|
{
|
||||||
|
uint8_t id;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = sscanf(in, "%2hhx", &id);
|
||||||
|
if(1 > r) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return wfd_sube_parse_with_id(id, in + 2, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
int wfd_sube_parse_with_id(enum wfd_sube_id id,
|
||||||
|
const char *in,
|
||||||
|
union wfd_sube *out)
|
||||||
|
{
|
||||||
|
uint16_t len;
|
||||||
|
union wfd_sube sube;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if(SHL_ARRAY_LENGTH(parser_tbl) <= id) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sscanf(in, "%4hx", &len);
|
||||||
|
if(1 > r) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(parser_tbl[id].len != len) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!parser_tbl[id].parser) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = (*parser_tbl[id].parser)(in + 4, &sube);
|
||||||
|
if(0 > r) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
sube.id = id;
|
||||||
|
|
||||||
|
if(out) {
|
||||||
|
*out = sube;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wfd_video_formats_from_string(const char *l,
|
||||||
|
struct wfd_video_formats **out)
|
||||||
|
{
|
||||||
|
_shl_free_ struct wfd_video_formats *f = NULL;
|
||||||
|
uint8_t native, pref_disp_mode_sup;
|
||||||
|
int r, i, n_codecs;
|
||||||
|
const char *p;
|
||||||
|
char max_hres[5], max_vres[5];
|
||||||
|
|
||||||
|
assert(l);
|
||||||
|
|
||||||
|
if(!strncmp("none", l, 4)) {
|
||||||
|
if(out) {
|
||||||
|
*out = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sscanf(l, "%2hhx %2hhx", &native, &pref_disp_mode_sup);
|
||||||
|
if(2 != r) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
l += 6;
|
||||||
|
|
||||||
|
for(p = l, n_codecs = 1; (p = strchrnul(p, ','), *p); ++ n_codecs, ++ p);
|
||||||
|
|
||||||
|
f = malloc(sizeof(*f) + (sizeof(f->h264_codecs[0]) * n_codecs));
|
||||||
|
if(!f) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < n_codecs; i ++) {
|
||||||
|
r = sscanf(l,
|
||||||
|
"%2hhx %2hhx %8x %8x %8x %2hhx %4hx %4hx %2hhx %4s %4s",
|
||||||
|
&f->h264_codecs[i].profile,
|
||||||
|
&f->h264_codecs[i].level,
|
||||||
|
&f->h264_codecs[i].cea_sup,
|
||||||
|
&f->h264_codecs[i].vesa_sup,
|
||||||
|
&f->h264_codecs[i].hh_sup,
|
||||||
|
&f->h264_codecs[i].latency,
|
||||||
|
&f->h264_codecs[i].min_slice_size,
|
||||||
|
&f->h264_codecs[i].slice_enc_params,
|
||||||
|
&f->h264_codecs[i].frame_rate_ctrl_sup,
|
||||||
|
max_hres,
|
||||||
|
max_vres);
|
||||||
|
if(11 != r) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
f->h264_codecs[i].max_hres = !strncmp("none", max_hres, 4)
|
||||||
|
? 0
|
||||||
|
: strtoul(max_hres, NULL, 16);
|
||||||
|
if(errno) {
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
f->h264_codecs[i].max_vres = !strncmp("none", max_vres, 4)
|
||||||
|
? 0
|
||||||
|
: strtoul(max_vres, NULL, 16);
|
||||||
|
if(errno) {
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
l += 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
f->native = native;
|
||||||
|
f->pref_disp_mode_sup = pref_disp_mode_sup;
|
||||||
|
f->n_h264_codecs = n_codecs;
|
||||||
|
|
||||||
|
if(out) {
|
||||||
|
*out = f;
|
||||||
|
f = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char * int16_to_res(int16_t v, char *b)
|
||||||
|
{
|
||||||
|
if(!v) {
|
||||||
|
return "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(b, "%04hX", v);
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wfd_video_formats_to_string(struct wfd_video_formats *f, char **out)
|
||||||
|
{
|
||||||
|
_shl_free_ char *s = NULL;
|
||||||
|
char *p, b1[5], b2[5];
|
||||||
|
size_t len = 6;
|
||||||
|
int r, i;
|
||||||
|
|
||||||
|
assert(f);
|
||||||
|
assert(out);
|
||||||
|
|
||||||
|
len += (f->n_h264_codecs ? f->n_h264_codecs * 60 : 6);
|
||||||
|
p = s = malloc(len);
|
||||||
|
if(!s) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = snprintf(p, len, "%02hhX %02hhX ", f->native, f->pref_disp_mode_sup);
|
||||||
|
if(0 > r) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
p += r;
|
||||||
|
len -= r;
|
||||||
|
|
||||||
|
if(!f->n_h264_codecs) {
|
||||||
|
strcat(p, " none");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < f->n_h264_codecs; ++ i) {
|
||||||
|
r = snprintf(p, len,
|
||||||
|
"%02hhX %02hhX %08X %08X %08X %02hhX %04hX %04hX %02hhX %s %s, ",
|
||||||
|
f->h264_codecs[i].profile,
|
||||||
|
f->h264_codecs[i].level,
|
||||||
|
f->h264_codecs[i].cea_sup,
|
||||||
|
f->h264_codecs[i].vesa_sup,
|
||||||
|
f->h264_codecs[i].hh_sup,
|
||||||
|
f->h264_codecs[i].latency,
|
||||||
|
f->h264_codecs[i].min_slice_size,
|
||||||
|
f->h264_codecs[i].slice_enc_params,
|
||||||
|
f->h264_codecs[i].frame_rate_ctrl_sup,
|
||||||
|
int16_to_res(f->h264_codecs[i].max_hres, b1),
|
||||||
|
int16_to_res(f->h264_codecs[i].max_vres, b2));
|
||||||
|
if(0 > r) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
p += r;
|
||||||
|
len -= r;
|
||||||
|
}
|
||||||
|
|
||||||
|
p[-2] = '\0';
|
||||||
|
|
||||||
|
end:
|
||||||
|
*out = s;
|
||||||
|
s = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wfd_audio_format_from_string(const char *s, enum wfd_audio_format *f)
|
||||||
|
{
|
||||||
|
enum wfd_audio_format t = WFD_AUDIO_FORMAT_UNKNOWN;
|
||||||
|
if(s) {
|
||||||
|
if(!strncmp("LPCM", s, 4)) {
|
||||||
|
t = WFD_AUDIO_FORMAT_LPCM;
|
||||||
|
}
|
||||||
|
else if(!strncmp("AAC", s, 3)) {
|
||||||
|
t = WFD_AUDIO_FORMAT_AAC;
|
||||||
|
}
|
||||||
|
else if(!strncmp("AC3", s, 3)) {
|
||||||
|
t = WFD_AUDIO_FORMAT_AC3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(WFD_AUDIO_FORMAT_UNKNOWN != t) {
|
||||||
|
if(f) {
|
||||||
|
*f = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * wfd_audio_format_to_string(enum wfd_audio_format f)
|
||||||
|
{
|
||||||
|
switch(f) {
|
||||||
|
case WFD_AUDIO_FORMAT_LPCM:
|
||||||
|
return "LPCM";
|
||||||
|
case WFD_AUDIO_FORMAT_AAC:
|
||||||
|
return "AAC";
|
||||||
|
case WFD_AUDIO_FORMAT_AC3:
|
||||||
|
return "AC3";
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int wfd_audio_codecs_from_string(const char *l,
|
||||||
|
struct wfd_audio_codecs **out)
|
||||||
|
{
|
||||||
|
_shl_free_ struct wfd_audio_codecs *c = NULL;
|
||||||
|
_shl_free_ char *f = NULL;
|
||||||
|
int r, i, n_caps;
|
||||||
|
const char *p;
|
||||||
|
|
||||||
|
assert(l);
|
||||||
|
|
||||||
|
if(!strncmp("none", l, 4)) {
|
||||||
|
if(out) {
|
||||||
|
*out = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(p = l, n_caps = 1; (p = strchrnul(p, ','), *p); ++ n_caps, ++ p);
|
||||||
|
|
||||||
|
c = malloc(sizeof(struct wfd_audio_codecs)
|
||||||
|
+ (sizeof(c->caps[0]) * n_caps));
|
||||||
|
|
||||||
|
for(i = 0; i < n_caps; i ++) {
|
||||||
|
r = sscanf(l, "%ms %8x %2hhx",
|
||||||
|
&f,
|
||||||
|
&c->caps[i].modes,
|
||||||
|
&c->caps[i].latency);
|
||||||
|
if(r != 3) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = wfd_audio_format_from_string(f, &c->caps[i].format);
|
||||||
|
if(0 > r) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
l += 16;
|
||||||
|
if(WFD_AUDIO_FORMAT_LPCM == c->caps[i].format) {
|
||||||
|
++ l;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(f);
|
||||||
|
f = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
c->n_caps = n_caps;
|
||||||
|
|
||||||
|
if(out) {
|
||||||
|
*out = c;
|
||||||
|
c = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wfd_audio_codecs_to_string(struct wfd_audio_codecs *c, char **out)
|
||||||
|
{
|
||||||
|
_shl_free_ char *s = NULL;
|
||||||
|
char *p;
|
||||||
|
int r, i;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
assert(c);
|
||||||
|
assert(out);
|
||||||
|
|
||||||
|
len = c->n_caps * 18;
|
||||||
|
p = s = malloc(len);
|
||||||
|
if(!s) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < c->n_caps; i ++) {
|
||||||
|
r = snprintf(p, len, "%s %08X %02hhX, ",
|
||||||
|
wfd_audio_format_to_string(c->caps[i].format),
|
||||||
|
c->caps[i].modes,
|
||||||
|
c->caps[i].latency);
|
||||||
|
if(0 > r) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
p += r;
|
||||||
|
len -= r;
|
||||||
|
}
|
||||||
|
|
||||||
|
p[-2] = '\n';
|
||||||
|
*out = s;
|
||||||
|
s = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
190
src/ctl/wfd.h
190
src/ctl/wfd.h
|
@ -21,9 +21,199 @@
|
||||||
#ifndef WFD_H
|
#ifndef WFD_H
|
||||||
#define WFD_H
|
#define WFD_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define wfd_sube_is_device_info(w) (WFD_SUBE_ID_DEVICE_INFO == (w)->id)
|
||||||
|
|
||||||
|
#define WFD_DEVINFO_DEV_TYPE_MASK (0x3 << 0)
|
||||||
|
#define WFD_DEVINFO_SRC_COUPLED_SINK_MASK (0x1 << 2)
|
||||||
|
#define WFD_DEVINFO_SINK_COUPLED_SINK_MASK (0x1 << 3)
|
||||||
|
#define WFD_DEVINFO_SESSION_MASK (0x3 << 4)
|
||||||
|
#define WFD_DEVINFO_WSD_MASK (0x1 << 6)
|
||||||
|
#define WFD_DEVINFO_PC_MASK (0x1 << 7)
|
||||||
|
#define WFD_DEVINFO_CP_MASK (0x1 << 8)
|
||||||
|
#define WFD_DEVINFO_TIME_SYNC_MASK (0x1 << 9)
|
||||||
|
#define WFD_DEVINFO_PRI_SINK_AUDIO_MASK (0x1 << 10)
|
||||||
|
#define WFD_DEVINFO_SRC_AUDIO_ONLY_MASK (0x1 << 11)
|
||||||
|
|
||||||
|
enum wfd_sube_id {
|
||||||
|
WFD_SUBE_ID_DEVICE_INFO,
|
||||||
|
WFD_SUBE_ID_ASSOCIATED_BSSID,
|
||||||
|
WFD_SUBE_ID_AUDIO_FORMATS,
|
||||||
|
WFD_SUBE_ID_VIDEO_FORMATS,
|
||||||
|
WFD_SUBE_ID_3D_VIDEO_FORMATS,
|
||||||
|
WFD_SUBE_ID_CONTENT_PROTECTION,
|
||||||
|
WFD_SUBE_ID_COUPLED_SINK_INFO,
|
||||||
|
WFD_SUBE_ID_WFD_EXT_CAPS,
|
||||||
|
WFD_SUBE_ID_LOCAL_IP_ADDR,
|
||||||
|
WFD_SUBE_ID_RESERVED,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum wfd_resolution_standard
|
||||||
|
{
|
||||||
|
WFD_RESOLUTION_STANDARD_CEA,
|
||||||
|
WFD_RESOLUTION_STANDARD_VESA,
|
||||||
|
WFD_RESOLUTION_STANDARD_HH,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum wfd_audio_format
|
||||||
|
{
|
||||||
|
WFD_AUDIO_FORMAT_UNKNOWN,
|
||||||
|
WFD_AUDIO_FORMAT_LPCM,
|
||||||
|
WFD_AUDIO_FORMAT_AAC,
|
||||||
|
WFD_AUDIO_FORMAT_AC3
|
||||||
|
};
|
||||||
|
|
||||||
|
union wfd_sube
|
||||||
|
{
|
||||||
|
enum wfd_sube_id id;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
enum wfd_sube_id id;
|
||||||
|
uint16_t dev_info;
|
||||||
|
uint16_t rtsp_port;
|
||||||
|
uint16_t max_throughput;
|
||||||
|
} dev_info;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
enum wfd_sube_id id;
|
||||||
|
uint32_t cea;
|
||||||
|
uint32_t vesa;
|
||||||
|
uint32_t hh;
|
||||||
|
uint8_t native;
|
||||||
|
uint8_t profiles;
|
||||||
|
uint8_t levels;
|
||||||
|
uint8_t latency;
|
||||||
|
uint16_t min_slice_size;
|
||||||
|
uint16_t slice_enc_params;
|
||||||
|
uint8_t video_frame_rate_ctl;
|
||||||
|
} video_formats;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
enum wfd_sube_id id;
|
||||||
|
uint32_t lpcm_modes;
|
||||||
|
uint8_t lpcm_dec_latency;
|
||||||
|
uint32_t aac_modes;
|
||||||
|
uint8_t aac_dec_latency;
|
||||||
|
uint32_t ac3_modes;
|
||||||
|
uint8_t ac3_dec_latency;
|
||||||
|
} audio_formats;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
enum wfd_sube_id id;
|
||||||
|
uint16_t caps;
|
||||||
|
} extended_caps;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wfd_video_formats
|
||||||
|
{
|
||||||
|
uint8_t native;
|
||||||
|
uint8_t pref_disp_mode_sup;
|
||||||
|
size_t n_h264_codecs;
|
||||||
|
struct {
|
||||||
|
uint8_t profile;
|
||||||
|
uint8_t level;
|
||||||
|
uint32_t cea_sup;
|
||||||
|
uint32_t vesa_sup;
|
||||||
|
uint32_t hh_sup;
|
||||||
|
uint8_t latency;
|
||||||
|
uint16_t min_slice_size;
|
||||||
|
uint16_t slice_enc_params;
|
||||||
|
uint8_t frame_rate_ctrl_sup;
|
||||||
|
uint16_t max_hres;
|
||||||
|
uint16_t max_vres;
|
||||||
|
} h264_codecs[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wfd_audio_codecs
|
||||||
|
{
|
||||||
|
size_t n_caps;
|
||||||
|
struct {
|
||||||
|
enum wfd_audio_format format;
|
||||||
|
uint32_t modes;
|
||||||
|
uint8_t latency;
|
||||||
|
} caps[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wfd_resolution
|
||||||
|
{
|
||||||
|
uint16_t index;
|
||||||
|
uint16_t hres;
|
||||||
|
uint16_t vres;
|
||||||
|
uint16_t fps;
|
||||||
|
bool progressive: 1;
|
||||||
|
};
|
||||||
|
|
||||||
void wfd_print_resolutions(char * prefix);
|
void wfd_print_resolutions(char * prefix);
|
||||||
|
int wfd_get_resolutions(enum wfd_resolution_standard std,
|
||||||
|
int index,
|
||||||
|
struct wfd_resolution *out);
|
||||||
int vfd_get_cea_resolution(uint32_t mask, int *hres, int *vres);
|
int vfd_get_cea_resolution(uint32_t mask, int *hres, int *vres);
|
||||||
int vfd_get_vesa_resolution(uint32_t mask, int *hres, int *vres);
|
int vfd_get_vesa_resolution(uint32_t mask, int *hres, int *vres);
|
||||||
int vfd_get_hh_resolution(uint32_t mask, int *hres, int *vres);
|
int vfd_get_hh_resolution(uint32_t mask, int *hres, int *vres);
|
||||||
|
int vfd_get_mask_from_resolution(int hres,
|
||||||
|
int vres,
|
||||||
|
enum wfd_resolution_standard *out_std,
|
||||||
|
uint32_t *out_mask);
|
||||||
|
int wfd_sube_parse(const char *in, union wfd_sube *out);
|
||||||
|
int wfd_sube_parse_with_id(enum wfd_sube_id id,
|
||||||
|
const char *in,
|
||||||
|
union wfd_sube *out);
|
||||||
|
int wfd_video_formats_from_string(const char *l,
|
||||||
|
struct wfd_video_formats **out);
|
||||||
|
static inline void wfd_video_formats_free(struct wfd_video_formats *f) { free(f); }
|
||||||
|
int wfd_video_formats_to_string(struct wfd_video_formats *f, char **out);
|
||||||
|
int wfd_audio_codecs_from_string(const char *l,
|
||||||
|
struct wfd_audio_codecs **out);
|
||||||
|
static inline void wfd_audio_codecs_free(struct wfd_audio_codecs *c) { free(c); }
|
||||||
|
int wfd_audio_codecs_to_string(struct wfd_audio_codecs *c, char **out);
|
||||||
|
int wfd_audio_format_from_string(const char *s, enum wfd_audio_format *f);
|
||||||
|
const char * wfd_audio_format_to_string(enum wfd_audio_format f);
|
||||||
|
|
||||||
|
static inline int wfd_sube_device_get_type(const union wfd_sube *sube)
|
||||||
|
{
|
||||||
|
assert(wfd_sube_is_device_info(sube));
|
||||||
|
|
||||||
|
return (WFD_DEVINFO_DEV_TYPE_MASK & sube->dev_info.dev_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool wfd_sube_device_is_source(const union wfd_sube *sube)
|
||||||
|
{
|
||||||
|
switch(wfd_sube_device_get_type(sube)) {
|
||||||
|
case 0:
|
||||||
|
case 3:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool wfd_sube_device_is_sink(const union wfd_sube *sube)
|
||||||
|
{
|
||||||
|
switch(wfd_sube_device_get_type(sube)) {
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int wfd_sube_src_support_coupled_sink(const union wfd_sube *sube)
|
||||||
|
{
|
||||||
|
assert(WFD_SUBE_ID_DEVICE_INFO == sube->id);
|
||||||
|
|
||||||
|
return !!(WFD_DEVINFO_SRC_COUPLED_SINK_MASK & sube->dev_info.dev_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t wfd_sube_device_get_rtsp_port(const union wfd_sube *sube)
|
||||||
|
{
|
||||||
|
assert(wfd_sube_is_device_info(sube));
|
||||||
|
|
||||||
|
return sube->dev_info.rtsp_port;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* WFD_H */
|
#endif /* WFD_H */
|
||||||
|
|
|
@ -240,6 +240,9 @@ static int cmd_set_friendly_name(char **args, unsigned int n)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!l->managed)
|
||||||
|
return log_EUNMANAGED();
|
||||||
|
|
||||||
return ctl_link_set_friendly_name(l, name);
|
return ctl_link_set_friendly_name(l, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,10 +313,8 @@ static int cmd_p2p_scan(char **args, unsigned int n)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!l->managed) {
|
if (!l->managed)
|
||||||
cli_printf("link %s not managed\n", l->label);
|
return log_EUNMANAGED();
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctl_link_set_p2p_scanning(l, !stop);
|
return ctl_link_set_p2p_scanning(l, !stop);
|
||||||
}
|
}
|
||||||
|
@ -362,10 +363,8 @@ static int cmd_connect(char **args, unsigned int n)
|
||||||
pin = "";
|
pin = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!p->l->managed) {
|
if (!p->l->managed)
|
||||||
cli_printf("link %s not managed\n", p->l->label);
|
return log_EUNMANAGED();
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctl_peer_connect(p, prov, pin);
|
return ctl_peer_connect(p, prov, pin);
|
||||||
}
|
}
|
||||||
|
@ -389,10 +388,8 @@ static int cmd_disconnect(char **args, unsigned int n)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!p->l->managed) {
|
if (!p->l->managed)
|
||||||
cli_printf("link %s not managed\n", p->l->label);
|
return log_EUNMANAGED();
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctl_peer_disconnect(p);
|
return ctl_peer_disconnect(p);
|
||||||
}
|
}
|
||||||
|
@ -605,7 +602,7 @@ static int parse_argv(int argc, char *argv[])
|
||||||
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
|
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'h':
|
case 'h':
|
||||||
cli_fn_help();
|
cli_fn_help();
|
||||||
return 0;
|
return 0;
|
||||||
case ARG_HELP_COMMANDS:
|
case ARG_HELP_COMMANDS:
|
||||||
return cli_help(cli_cmds, 20);
|
return cli_help(cli_cmds, 20);
|
||||||
|
@ -638,22 +635,22 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
|
|
||||||
GKeyFile* gkf = load_ini_file();
|
GKeyFile* gkf = load_ini_file();
|
||||||
|
|
||||||
if (gkf) {
|
if (gkf) {
|
||||||
gchar* log_level;
|
gchar* log_level;
|
||||||
log_level = g_key_file_get_string (gkf, "wifictl", "log-journal-level", NULL);
|
log_level = g_key_file_get_string (gkf, "wifictl", "log-journal-level", NULL);
|
||||||
if (log_level) {
|
if (log_level) {
|
||||||
log_max_sev = log_parse_arg(log_level);
|
log_max_sev = log_parse_arg(log_level);
|
||||||
g_free(log_level);
|
g_free(log_level);
|
||||||
}
|
}
|
||||||
log_level = g_key_file_get_string (gkf, "wifictl", "log-level", NULL);
|
log_level = g_key_file_get_string (gkf, "wifictl", "log-level", NULL);
|
||||||
if (log_level) {
|
if (log_level) {
|
||||||
cli_max_sev = log_parse_arg(log_level);
|
cli_max_sev = log_parse_arg(log_level);
|
||||||
g_free(log_level);
|
g_free(log_level);
|
||||||
}
|
}
|
||||||
g_key_file_free(gkf);
|
g_key_file_free(gkf);
|
||||||
}
|
}
|
||||||
|
|
||||||
r = parse_argv(argc, argv);
|
r = parse_argv(argc, argv);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
|
|
@ -456,7 +456,7 @@ static void init_packet(GDHCPServer *dhcp_server, struct dhcp_packet *packet,
|
||||||
packet->gateway_nip = client_packet->gateway_nip;
|
packet->gateway_nip = client_packet->gateway_nip;
|
||||||
packet->ciaddr = client_packet->ciaddr;
|
packet->ciaddr = client_packet->ciaddr;
|
||||||
dhcp_add_option_uint32(packet, DHCP_SERVER_ID,
|
dhcp_add_option_uint32(packet, DHCP_SERVER_ID,
|
||||||
dhcp_server->server_nip);
|
get_be32(&dhcp_server->server_nip));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_option(gpointer key, gpointer value, gpointer user_data)
|
static void add_option(gpointer key, gpointer value, gpointer user_data)
|
||||||
|
@ -675,7 +675,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
|
||||||
|
|
||||||
server_id_option = dhcp_get_option(&packet, DHCP_SERVER_ID);
|
server_id_option = dhcp_get_option(&packet, DHCP_SERVER_ID);
|
||||||
if (server_id_option) {
|
if (server_id_option) {
|
||||||
uint32_t server_nid = get_be32(server_id_option);
|
uint32_t server_nid = get_unaligned((uint32_t *) server_id_option);
|
||||||
|
|
||||||
if (server_nid != dhcp_server->server_nip)
|
if (server_nid != dhcp_server->server_nip)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
43
src/disp/CMakeLists.txt
Normal file
43
src/disp/CMakeLists.txt
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
find_package(PkgConfig)
|
||||||
|
pkg_check_modules (GLIB2 REQUIRED glib-2.0)
|
||||||
|
link_directories( ${GLIB2_LIBRARY_DIRS})
|
||||||
|
include_directories( ${GLIB2_INCLUDE_DIRS})
|
||||||
|
find_package(Readline REQUIRED)
|
||||||
|
pkg_check_modules (GLIB2 REQUIRED glib-2.0)
|
||||||
|
|
||||||
|
set(miracle-dispd_SRCS
|
||||||
|
../ctl/ctl-cli.c
|
||||||
|
../ctl/ctl-wifi.c
|
||||||
|
../ctl/wfd.c
|
||||||
|
dispd-sink.c
|
||||||
|
dispd-dbus.c
|
||||||
|
dispd-session.c
|
||||||
|
dispd-out-session.c
|
||||||
|
dispd.c
|
||||||
|
dispd-encoder.c
|
||||||
|
dispd-arg.c
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/src/ctl
|
||||||
|
${CMAKE_BINARY_DIR}
|
||||||
|
${CMAKE_SOURCE_DIR}/src
|
||||||
|
${CMAKE_SOURCE_DIR}/src/shared
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(miracle-dispd ${miracle-dispd_SRCS})
|
||||||
|
|
||||||
|
install(TARGETS miracle-dispd DESTINATION bin)
|
||||||
|
|
||||||
|
if(READLINE_FOUND)
|
||||||
|
message(STATUS "Compiling with Readline support")
|
||||||
|
set_property(TARGET miracle-dispd
|
||||||
|
APPEND
|
||||||
|
PROPERTY COMPILE_DEFINITIONS HAVE_READLINE
|
||||||
|
)
|
||||||
|
target_link_libraries(miracle-dispd ${READLINE_LIBRARY})
|
||||||
|
endif(READLINE_FOUND)
|
||||||
|
|
||||||
|
target_link_libraries(miracle-dispd miracle-shared)
|
||||||
|
target_link_libraries(miracle-dispd m)
|
||||||
|
target_link_libraries(miracle-dispd ${GLIB2_LIBRARIES})
|
||||||
|
|
61
src/disp/dispd-arg.c
Normal file
61
src/disp/dispd-arg.c
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||||
|
*
|
||||||
|
* MiracleCast 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) any later version.
|
||||||
|
*
|
||||||
|
* MiracleCast 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 MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <errno.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "dispd-arg.h"
|
||||||
|
|
||||||
|
int dispd_arg_list_new(struct dispd_arg_list **out)
|
||||||
|
{
|
||||||
|
assert(out);
|
||||||
|
|
||||||
|
struct dispd_arg_list *l = calloc(1, sizeof(struct dispd_arg_list));
|
||||||
|
if(!l) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
l->dynamic = true;
|
||||||
|
|
||||||
|
*out = l;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispd_arg_list_clear(struct dispd_arg_list *l)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct dispd_arg *arg;
|
||||||
|
|
||||||
|
if(!l || !l->dynamic) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
arg = l->discrete ? l->argv : l->args;
|
||||||
|
for(i = 0; i < l->len; i ++) {
|
||||||
|
if((DISPD_ARG_STR == arg->type || DISPD_ARG_PTR == arg->type)
|
||||||
|
&& arg->ptr && arg->free) {
|
||||||
|
(*arg->free)(arg->ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(l->discrete) {
|
||||||
|
free(l->argv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
242
src/disp/dispd-arg.h
Normal file
242
src/disp/dispd-arg.h
Normal file
|
@ -0,0 +1,242 @@
|
||||||
|
/*
|
||||||
|
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||||
|
*
|
||||||
|
* MiracleCast 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) any later version.
|
||||||
|
*
|
||||||
|
* MiracleCast 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 MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "shl_macro.h"
|
||||||
|
|
||||||
|
#ifndef DISPD_ARG_H
|
||||||
|
#define DISPD_ARG_H
|
||||||
|
|
||||||
|
#define dispd_arg_i8(_v) { .type = DISPD_ARG_I8, .i8 = (_v) }
|
||||||
|
#define dispd_arg_u8(_v) { .type = DISPD_ARG_U8, .u8 = (_v) }
|
||||||
|
#define dispd_arg_i16(_v) { .type = DISPD_ARG_I16, .i16 = (_v) }
|
||||||
|
#define dispd_arg_u16(_v) { .type = DISPD_ARG_U16, .u16 = (_v) }
|
||||||
|
#define dispd_arg_i32(_v) { .type = DISPD_ARG_I32, .i32 = (_v) }
|
||||||
|
#define dispd_arg_u32(_v) { .type = DISPD_ARG_U32, .u32 = (_v) }
|
||||||
|
#define dispd_arg_i64(_v) { .type = DISPD_ARG_I64, .i64 = (_v) }
|
||||||
|
#define dispd_arg_u64(_v) { .type = DISPD_ARG_U64, .u64 = (_v) }
|
||||||
|
#define dispd_arg_cstr(_v) { .type = DISPD_ARG_CSTR, .ptr = (_v) }
|
||||||
|
#define dispd_arg_cptr(_v) { .type = DISPD_ARG_CPTR, .ptr = (_v) }
|
||||||
|
#define dispd_arg_arg_list(_v) { \
|
||||||
|
.type = DISPD_ARG_ARG_LIST, \
|
||||||
|
.ptr = &(struct dispd_arg_list) dispd_arg_list(_v) \
|
||||||
|
}
|
||||||
|
#define dispd_arg_dict(_k, _v) { \
|
||||||
|
.type = DISPD_ARG_DICT, \
|
||||||
|
.k = (struct dispd_arg[]){_k}, \
|
||||||
|
.v = (struct dispd_arg[]){_v} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#if INT_MAX == INT64_MAX
|
||||||
|
#define dispd_arg_i(_v) dispd_arg_i64(_v)
|
||||||
|
#define dispd_arg_u(_v) dispd_arg_u64(_v)
|
||||||
|
#elif INT_MAX == INT32_MAX
|
||||||
|
#define dispd_arg_i(_v) dispd_arg_i32(_v)
|
||||||
|
#define dispd_arg_u(_v) dispd_arg_u32(_v)
|
||||||
|
#else
|
||||||
|
#error unsupported int size
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define dispd_arg_type_id(_t) _Generic((_t), \
|
||||||
|
int8_t: DISPD_ARG_I8, \
|
||||||
|
uint8_t: DISPD_ARG_U8, \
|
||||||
|
int16_t: DISPD_ARG_I16, \
|
||||||
|
uint16_t: DISPD_ARG_U16, \
|
||||||
|
int32_t: DISPD_ARG_I32, \
|
||||||
|
uint32_t: DISPD_ARG_U32, \
|
||||||
|
int64_t: DISPD_ARG_I64, \
|
||||||
|
uint64_t: DISPD_ARG_U64, \
|
||||||
|
const char *: DISPD_ARG_CSTR, \
|
||||||
|
const dispd_arg_list *: DISPD_ARG_ARG_LIST, \
|
||||||
|
char *: DISPD_ARG_STR, \
|
||||||
|
void *: DISPD_ARG_PTR, \
|
||||||
|
default: DISPD_ARG_CPTR \
|
||||||
|
)
|
||||||
|
|
||||||
|
#define dispd_arg_list(...) { \
|
||||||
|
.argv = (struct dispd_arg[]) { \
|
||||||
|
__VA_ARGS__ \
|
||||||
|
}, \
|
||||||
|
.discrete = true, \
|
||||||
|
.dynamic = false, \
|
||||||
|
.len = (sizeof((struct dispd_arg[]){ __VA_ARGS__ })/sizeof(struct dispd_arg)) \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define dispd_arg_get(_a, _v) ({ \
|
||||||
|
*(_v) = _Generic(*(_v), \
|
||||||
|
int8_t: dispd_arg_get_i8, \
|
||||||
|
uint8_t: dispd_arg_get_u8, \
|
||||||
|
int16_t: dispd_arg_get_i16, \
|
||||||
|
uint16_t: dispd_arg_get_u16, \
|
||||||
|
int32_t: dispd_arg_get_i32, \
|
||||||
|
uint32_t: dispd_arg_get_u32, \
|
||||||
|
int64_t: dispd_arg_get_i64, \
|
||||||
|
uint64_t: dispd_arg_get_u64, \
|
||||||
|
char *: dispd_arg_get_str, \
|
||||||
|
const struct dispd_arg_list *: dispd_arg_get_arg_list, \
|
||||||
|
const char *: dispd_arg_get_cstr, \
|
||||||
|
void *: dispd_arg_get_ptr, \
|
||||||
|
default: dispd_arg_get_cptr \
|
||||||
|
)(_a); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define dispd_arg_get_dictk(_a, _k) ({ \
|
||||||
|
assert(_a); \
|
||||||
|
assert(DISPD_ARG_DICT == (_a)->type); \
|
||||||
|
dispd_arg_get((_a)->k, (_k)); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define dispd_arg_get_dictv(_a, _v) ({ \
|
||||||
|
assert(_a); \
|
||||||
|
assert(DISPD_ARG_DICT == (_a)->type); \
|
||||||
|
dispd_arg_get((_a)->v, (_v)); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define dispd_arg_get_dict(_a, _k, _v) ({ \
|
||||||
|
assert(_a); \
|
||||||
|
assert(DISPD_ARG_DICT == (_a)->type); \
|
||||||
|
dispd_arg_get_dictk(_a, _k); \
|
||||||
|
dispd_arg_get_dictv(_a, _v); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define dispd_arg_list_get(_l, _i, _v) ({ \
|
||||||
|
dispd_arg_get(dispd_arg_list_at((_l), (_i)), (_v)); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define dispd_arg_list_get_dictk(_l, _i, _k) ({ \
|
||||||
|
dispd_arg_get_dictk(dispd_arg_list_at((_l), (_i)), (_k)); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define dispd_arg_list_get_dictv(_l, _i, _v) ({ \
|
||||||
|
dispd_arg_get_dictv(dispd_arg_list_at((_l), (_i)), (_v)); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define dispd_arg_list_get_dict(_l, _i, _k, _v) ({ \
|
||||||
|
dispd_arg_get_dict(dispd_arg_list_at((_l), (_i)), (_k), (_v)); \
|
||||||
|
})
|
||||||
|
|
||||||
|
enum dispd_arg_type
|
||||||
|
{
|
||||||
|
DISPD_ARG_NONE,
|
||||||
|
DISPD_ARG_I8,
|
||||||
|
DISPD_ARG_I16,
|
||||||
|
DISPD_ARG_I32,
|
||||||
|
DISPD_ARG_I64,
|
||||||
|
DISPD_ARG_U8,
|
||||||
|
DISPD_ARG_U16,
|
||||||
|
DISPD_ARG_U32,
|
||||||
|
DISPD_ARG_U64,
|
||||||
|
DISPD_ARG_STR,
|
||||||
|
DISPD_ARG_CSTR,
|
||||||
|
DISPD_ARG_PTR,
|
||||||
|
DISPD_ARG_CPTR,
|
||||||
|
DISPD_ARG_DICT,
|
||||||
|
DISPD_ARG_ARG_LIST,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dispd_arg
|
||||||
|
{
|
||||||
|
enum dispd_arg_type type;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
int8_t i8;
|
||||||
|
uint8_t u8;
|
||||||
|
int16_t i16;
|
||||||
|
uint16_t u16;
|
||||||
|
int32_t i32;
|
||||||
|
uint32_t u32;
|
||||||
|
int64_t i64;
|
||||||
|
uint64_t u64;
|
||||||
|
struct {
|
||||||
|
void *ptr;
|
||||||
|
void (*free)(void *);
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
struct dispd_arg *k;
|
||||||
|
struct dispd_arg *v;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dispd_arg_list
|
||||||
|
{
|
||||||
|
size_t len: sizeof(size_t) - 2;
|
||||||
|
bool discrete: 1;
|
||||||
|
bool dynamic: 1;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct dispd_arg * argv;
|
||||||
|
struct dispd_arg args[0];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
int dispd_arg_list_new(struct dispd_arg_list **out);
|
||||||
|
void dispd_arg_list_clear(struct dispd_arg_list *l);
|
||||||
|
static inline void dispd_arg_list_free(struct dispd_arg_list *l);
|
||||||
|
static inline const struct dispd_arg * dispd_arg_list_at(const struct dispd_arg_list *l,
|
||||||
|
int i);
|
||||||
|
|
||||||
|
static inline enum dispd_arg_type dispd_arg_get_type(struct dispd_arg *a);
|
||||||
|
static inline void dispd_arg_free_ptr(struct dispd_arg *a);
|
||||||
|
static inline void dispd_arg_clear(struct dispd_arg *a);
|
||||||
|
|
||||||
|
static inline int8_t dispd_arg_get_i8(const struct dispd_arg *a);
|
||||||
|
static inline void dispd_arg_set_i8(struct dispd_arg *a, int8_t v);
|
||||||
|
|
||||||
|
static inline uint8_t dispd_arg_get_u8(const struct dispd_arg *a);
|
||||||
|
static inline void dispd_arg_set_u8(struct dispd_arg *a, uint8_t v);
|
||||||
|
|
||||||
|
static inline int16_t dispd_arg_get_i16(const struct dispd_arg *a);
|
||||||
|
static inline void dispd_arg_set_i16(struct dispd_arg *a, int16_t v);
|
||||||
|
|
||||||
|
static inline uint16_t dispd_arg_get_u16(const struct dispd_arg *a);
|
||||||
|
static inline void dispd_arg_set_u16(struct dispd_arg *a, uint16_t v);
|
||||||
|
|
||||||
|
static inline int32_t dispd_arg_get_i32(const struct dispd_arg *a);
|
||||||
|
static inline void dispd_arg_set_i32(struct dispd_arg *a, int32_t v);
|
||||||
|
|
||||||
|
static inline uint32_t dispd_arg_get_u32(const struct dispd_arg *a);
|
||||||
|
static inline void dispd_arg_set_u32(struct dispd_arg *a, uint32_t v);
|
||||||
|
|
||||||
|
static inline int64_t dispd_arg_get_i64(const struct dispd_arg *a);
|
||||||
|
static inline void dispd_arg_set_i64(struct dispd_arg *a, int64_t v);
|
||||||
|
|
||||||
|
static inline uint64_t dispd_arg_get_u64(const struct dispd_arg *a);
|
||||||
|
static inline void dispd_arg_set_u64(struct dispd_arg *a, uint64_t v);
|
||||||
|
|
||||||
|
static inline const char * dispd_arg_get_cstr(const struct dispd_arg *a);
|
||||||
|
static inline void dispd_arg_set_cstr(struct dispd_arg *a, const char * v);
|
||||||
|
|
||||||
|
static inline void dispd_arg_take_str(struct dispd_arg *a, char *v);
|
||||||
|
static inline char * dispd_arg_get_str(const struct dispd_arg *a);
|
||||||
|
static inline int dispd_arg_set_str(struct dispd_arg *a, const char *v);;
|
||||||
|
|
||||||
|
static inline const void * dispd_arg_get_cptr(const struct dispd_arg *a);
|
||||||
|
static inline void dispd_arg_set_cptr(struct dispd_arg *a, const void * v);
|
||||||
|
|
||||||
|
static inline void dispd_arg_take_ptr(struct dispd_arg *a, void *v, void (*f)(void *));
|
||||||
|
static inline void * dispd_arg_get_ptr(const struct dispd_arg *a);
|
||||||
|
|
||||||
|
static inline void dispd_arg_take_arg_list(struct dispd_arg *a, struct dispd_arg_list *l);
|
||||||
|
static inline const struct dispd_arg_list * dispd_arg_get_arg_list(const struct dispd_arg *a);
|
||||||
|
|
||||||
|
#include "dispd-arg.inc"
|
||||||
|
|
||||||
|
#endif /* DISPD_ARG_H */
|
192
src/disp/dispd-arg.inc
Normal file
192
src/disp/dispd-arg.inc
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
/*
|
||||||
|
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||||
|
*
|
||||||
|
* MiracleCast 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) any later version.
|
||||||
|
*
|
||||||
|
* MiracleCast 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 MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#define dispd_arg_getter(_t, _s, _S) \
|
||||||
|
static inline _t dispd_arg_get_##_s(const struct dispd_arg *a) \
|
||||||
|
{ \
|
||||||
|
assert(a); \
|
||||||
|
assert(DISPD_ARG_##_S == a->type); \
|
||||||
|
return a->_s; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define dispd_arg_setter(_t, _s, _S) \
|
||||||
|
static inline void dispd_arg_set_##_s(struct dispd_arg *a, _t v) \
|
||||||
|
{ \
|
||||||
|
assert(a); \
|
||||||
|
assert(!a->type || DISPD_ARG_##_S == a->type); \
|
||||||
|
*a = (struct dispd_arg) { .type = DISPD_ARG_##_S, ._s = v }; \
|
||||||
|
}
|
||||||
|
|
||||||
|
dispd_arg_getter(int8_t, i8, I8)
|
||||||
|
dispd_arg_setter(int8_t, i8, I8)
|
||||||
|
dispd_arg_getter(uint8_t, u8, U8)
|
||||||
|
dispd_arg_setter(uint8_t, u8, U8)
|
||||||
|
dispd_arg_getter(int16_t, i16, I16)
|
||||||
|
dispd_arg_setter(int16_t, i16, I16)
|
||||||
|
dispd_arg_getter(uint16_t, u16, U16)
|
||||||
|
dispd_arg_setter(uint16_t, u16, U16)
|
||||||
|
dispd_arg_getter(int32_t, i32, I32)
|
||||||
|
dispd_arg_setter(int32_t, i32, I32)
|
||||||
|
dispd_arg_getter(uint32_t, u32, U32)
|
||||||
|
dispd_arg_setter(uint32_t, u32, U32)
|
||||||
|
dispd_arg_getter(int64_t, i64, I64)
|
||||||
|
dispd_arg_setter(int64_t, i64, I64)
|
||||||
|
dispd_arg_getter(uint64_t, u64, U64)
|
||||||
|
dispd_arg_setter(uint64_t, u64, U64)
|
||||||
|
|
||||||
|
static inline void dispd_arg_list_free(struct dispd_arg_list *l)
|
||||||
|
{
|
||||||
|
dispd_arg_list_clear(l);
|
||||||
|
free(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const struct dispd_arg * dispd_arg_list_at(const struct dispd_arg_list *l,
|
||||||
|
int i)
|
||||||
|
{
|
||||||
|
assert(l);
|
||||||
|
assert(i >= 0 && i < l->len);
|
||||||
|
return l->discrete ? &l->argv[i] : &l->args[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline enum dispd_arg_type dispd_arg_get_type(struct dispd_arg *a)
|
||||||
|
{
|
||||||
|
assert(a);
|
||||||
|
|
||||||
|
return a->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void dispd_arg_free_ptr(struct dispd_arg *a)
|
||||||
|
{
|
||||||
|
if(!a || (DISPD_ARG_STR != a->type && DISPD_ARG_PTR != a->type)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(a->ptr && a->free) {
|
||||||
|
(*a->free)(a->ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void dispd_arg_clear(struct dispd_arg *a)
|
||||||
|
{
|
||||||
|
if(a) {
|
||||||
|
dispd_arg_free_ptr(a);
|
||||||
|
memset(a, 0, sizeof(*a));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char * dispd_arg_get_cstr(const struct dispd_arg *a)
|
||||||
|
{
|
||||||
|
assert(a);
|
||||||
|
assert(DISPD_ARG_CSTR == a->type || DISPD_ARG_STR == a->type);
|
||||||
|
|
||||||
|
return a->ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void dispd_arg_set_cstr(struct dispd_arg *a, const char * v)
|
||||||
|
{
|
||||||
|
assert(a);
|
||||||
|
assert(!a->type || DISPD_ARG_CSTR == a->type);
|
||||||
|
|
||||||
|
*a = (struct dispd_arg) { .type = DISPD_ARG_CSTR, .ptr = (void *) v };
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline char * dispd_arg_get_str(const struct dispd_arg *a)
|
||||||
|
{
|
||||||
|
assert(a);
|
||||||
|
assert(DISPD_ARG_STR == a->type);
|
||||||
|
|
||||||
|
return a->ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void dispd_arg_take_str(struct dispd_arg *a, char *v)
|
||||||
|
{
|
||||||
|
assert(a);
|
||||||
|
assert(!a->type || DISPD_ARG_STR == a->type || DISPD_ARG_CSTR == a->type);
|
||||||
|
|
||||||
|
dispd_arg_free_ptr(a);
|
||||||
|
*a = (struct dispd_arg) { .type = DISPD_ARG_STR, .ptr = v, .free = free };
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int dispd_arg_set_str(struct dispd_arg *a, const char *v)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
assert(a);
|
||||||
|
assert(!a->type || DISPD_ARG_STR == a->type);
|
||||||
|
|
||||||
|
s = strdup(v);
|
||||||
|
if(!s) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispd_arg_take_str(a, s);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const void * dispd_arg_get_cptr(const struct dispd_arg *a)
|
||||||
|
{
|
||||||
|
assert(a);
|
||||||
|
assert(DISPD_ARG_PTR <= a->type && DISPD_ARG_CPTR == a->type);
|
||||||
|
|
||||||
|
return a->ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void dispd_arg_set_cptr(struct dispd_arg *a, const void * v)
|
||||||
|
{
|
||||||
|
assert(a);
|
||||||
|
assert(!a->type || DISPD_ARG_CSTR == a->type);
|
||||||
|
|
||||||
|
*a = (struct dispd_arg) { .type = DISPD_ARG_CPTR, .ptr = (void *) v };
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void * dispd_arg_get_ptr(const struct dispd_arg *a)
|
||||||
|
{
|
||||||
|
assert(a);
|
||||||
|
assert(DISPD_ARG_PTR == a->type || DISPD_ARG_STR == a->type);
|
||||||
|
|
||||||
|
return a->ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void dispd_arg_take_ptr(struct dispd_arg *a, void *v, void (*f)(void *))
|
||||||
|
{
|
||||||
|
assert(a);
|
||||||
|
assert(!a->type || DISPD_ARG_PTR == a->type);
|
||||||
|
|
||||||
|
dispd_arg_free_ptr(a);
|
||||||
|
*a = (struct dispd_arg) { .type = DISPD_ARG_PTR, .ptr = v, .free = f };
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void dispd_arg_take_arg_list(struct dispd_arg *a, struct dispd_arg_list *l)
|
||||||
|
{
|
||||||
|
assert(a);
|
||||||
|
assert(!a->type || DISPD_ARG_ARG_LIST == a->type);
|
||||||
|
|
||||||
|
dispd_arg_free_ptr(a);
|
||||||
|
*a = (struct dispd_arg) { .type = DISPD_ARG_ARG_LIST,
|
||||||
|
.ptr = l,
|
||||||
|
.free = (void (*)(void *)) dispd_arg_list_free };
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const struct dispd_arg_list * dispd_arg_get_arg_list(const struct dispd_arg *a)
|
||||||
|
{
|
||||||
|
assert(a);
|
||||||
|
assert(DISPD_ARG_ARG_LIST == a->type);
|
||||||
|
|
||||||
|
return a->ptr;
|
||||||
|
}
|
943
src/disp/dispd-dbus.c
Normal file
943
src/disp/dispd-dbus.c
Normal file
|
@ -0,0 +1,943 @@
|
||||||
|
/*
|
||||||
|
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||||
|
*
|
||||||
|
* MiracleCast 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) any later version.
|
||||||
|
*
|
||||||
|
* MiracleCast 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 MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <systemd/sd-bus.h>
|
||||||
|
#include "dispd.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "shl_log.h"
|
||||||
|
#include "dispd-dbus.h"
|
||||||
|
|
||||||
|
#define dispd_dbus_object_added(o, argv...) ({ \
|
||||||
|
const char *ifaces[] = { argv }; \
|
||||||
|
_dispd_dbus_object_added(dispd_dbus_get(), \
|
||||||
|
(o), \
|
||||||
|
ifaces, \
|
||||||
|
SHL_ARRAY_LENGTH(ifaces)); \
|
||||||
|
})
|
||||||
|
#define dispd_dbus_object_removed(o, argv...) ({ \
|
||||||
|
const char *ifaces[] = { argv }; \
|
||||||
|
_dispd_dbus_object_removed(dispd_dbus_get(), \
|
||||||
|
(o), \
|
||||||
|
ifaces, \
|
||||||
|
SHL_ARRAY_LENGTH(ifaces)); \
|
||||||
|
})
|
||||||
|
|
||||||
|
struct dispd_dbus
|
||||||
|
{
|
||||||
|
sd_bus *bus;
|
||||||
|
sd_event *loop;
|
||||||
|
|
||||||
|
bool exposed : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
int dispd_dbus_new(struct dispd_dbus **out, sd_event *loop, sd_bus *bus)
|
||||||
|
{
|
||||||
|
struct dispd_dbus *dispd_dbus = calloc(1, sizeof(struct dispd_dbus));
|
||||||
|
if(!dispd_dbus) {
|
||||||
|
return log_ENOMEM();
|
||||||
|
}
|
||||||
|
|
||||||
|
dispd_dbus->bus = sd_bus_ref(bus);
|
||||||
|
dispd_dbus->loop = sd_event_ref(loop);
|
||||||
|
|
||||||
|
*out = dispd_dbus;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispd_dbus_free(struct dispd_dbus *dispd_dbus)
|
||||||
|
{
|
||||||
|
if(!dispd_dbus) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dispd_dbus->exposed) {
|
||||||
|
sd_bus_release_name(dispd_dbus->bus, "org.freedesktop.miracle.wfd");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dispd_dbus->bus) {
|
||||||
|
sd_bus_unref(dispd_dbus->bus);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dispd_dbus->loop) {
|
||||||
|
sd_event_unref(dispd_dbus->loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(dispd_dbus);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int dispd_dbus_get_sink_path(struct dispd_sink *s, char **out)
|
||||||
|
{
|
||||||
|
int r = sd_bus_path_encode("/org/freedesktop/miracle/wfd/sink",
|
||||||
|
dispd_sink_get_label(s),
|
||||||
|
out);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int dispd_dbus_get_session_path(struct dispd_session *s, char **out)
|
||||||
|
{
|
||||||
|
char buf[64];
|
||||||
|
int r = snprintf(buf, sizeof(buf), "%u", dispd_session_get_id(s));
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_path_encode("/org/freedesktop/miracle/wfd/session",
|
||||||
|
buf,
|
||||||
|
out);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispd_dbus_enum(sd_bus *bus,
|
||||||
|
const char *path,
|
||||||
|
void *userdata,
|
||||||
|
char ***out,
|
||||||
|
sd_bus_error *out_error)
|
||||||
|
{
|
||||||
|
int r = 0, i = 0;
|
||||||
|
char **nodes, *node;
|
||||||
|
struct dispd_sink *sink;
|
||||||
|
struct dispd_session *session;
|
||||||
|
struct dispd *dispd = dispd_get();
|
||||||
|
|
||||||
|
if(strcmp("/org/freedesktop/miracle/wfd", path)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!dispd->n_sinks) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes = malloc((dispd->n_sinks + dispd->n_sessions + 1) * sizeof(char *));
|
||||||
|
if(!nodes) {
|
||||||
|
return log_ENOMEM();
|
||||||
|
}
|
||||||
|
|
||||||
|
dispd_foreach_sink(sink, dispd) {
|
||||||
|
r = dispd_dbus_get_sink_path(sink, &node);
|
||||||
|
if(0 > r) {
|
||||||
|
goto free_nodes;
|
||||||
|
}
|
||||||
|
nodes[i ++] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispd_foreach_session(session, dispd) {
|
||||||
|
r = dispd_dbus_get_session_path(session, &node);
|
||||||
|
if(0 > r) {
|
||||||
|
goto free_nodes;
|
||||||
|
}
|
||||||
|
nodes[i ++] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes[i ++] = NULL;
|
||||||
|
*out = nodes;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
free_nodes:
|
||||||
|
while(i --) {
|
||||||
|
free(nodes[i]);
|
||||||
|
}
|
||||||
|
free(nodes);
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
int _dispd_dbus_object_removed(struct dispd_dbus *dispd_dbus,
|
||||||
|
const char *path,
|
||||||
|
const char **ifaces,
|
||||||
|
size_t n_ifaces)
|
||||||
|
{
|
||||||
|
int i, r;
|
||||||
|
_sd_bus_message_unref_ sd_bus_message *m = NULL;
|
||||||
|
|
||||||
|
if(!dispd_dbus) {
|
||||||
|
return -ECANCELED;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_new_signal(dispd_dbus->bus,
|
||||||
|
&m,
|
||||||
|
"/org/freedesktop/miracle/wfd",
|
||||||
|
"org.freedesktop.DBus.ObjectManager",
|
||||||
|
"InterfacesRemoved");
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_append(m, "o", path);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_open_container(m, 'a', "s");
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < n_ifaces; i ++) {
|
||||||
|
r = sd_bus_message_append(m, "s", ifaces[i]);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_close_container(m);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_send(dispd_dbus->bus, m, NULL);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _dispd_dbus_object_added(struct dispd_dbus *dispd_dbus,
|
||||||
|
const char *path,
|
||||||
|
const char **ifaces,
|
||||||
|
size_t n_ifaces)
|
||||||
|
{
|
||||||
|
int i, r;
|
||||||
|
_sd_bus_message_unref_ sd_bus_message *m = NULL;
|
||||||
|
|
||||||
|
if(!dispd_dbus) {
|
||||||
|
return -ECANCELED;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_new_signal(dispd_dbus->bus,
|
||||||
|
&m,
|
||||||
|
"/org/freedesktop/miracle/wfd",
|
||||||
|
"org.freedesktop.DBus.ObjectManager",
|
||||||
|
"InterfacesAdded");
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_append(m, "o", path);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < n_ifaces; i ++) {
|
||||||
|
r = sd_bus_message_append(m, "{sa{sv}}", ifaces[i], 0);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_close_container(m);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_send(dispd_dbus->bus, m, NULL);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispd_fn_sink_new(struct dispd_sink *s)
|
||||||
|
{
|
||||||
|
_shl_free_ char *path = NULL;
|
||||||
|
int r = dispd_dbus_get_sink_path(s, &path);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_dbus_object_added(path, "org.freedesktop.miracle.wfd.Sink");
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispd_fn_sink_free(struct dispd_sink *s)
|
||||||
|
{
|
||||||
|
_shl_free_ char *path = NULL;
|
||||||
|
int r = dispd_dbus_get_sink_path(s, &path);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_dbus_object_removed(path, "org.freedesktop.miracle.wfd.Sink");
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _dispd_fn_sink_properties_changed(struct dispd_sink *s, char **names)
|
||||||
|
{
|
||||||
|
_shl_free_ char *path = NULL;
|
||||||
|
int r;
|
||||||
|
struct dispd_dbus *dispd_dbus = dispd_dbus_get();
|
||||||
|
|
||||||
|
if(!dispd_dbus) {
|
||||||
|
return log_ERR(-ECANCELED);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_dbus_get_sink_path(s, &path);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_emit_properties_changed_strv(dispd_dbus->bus,
|
||||||
|
path,
|
||||||
|
"org.freedesktop.miracle.wfd.Sink",
|
||||||
|
names);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispd_dbus_find_sink(sd_bus *bus,
|
||||||
|
const char *path,
|
||||||
|
const char *interface,
|
||||||
|
void *userdata,
|
||||||
|
void **ret_found,
|
||||||
|
sd_bus_error *ret_error)
|
||||||
|
{
|
||||||
|
_shl_free_ char *node = NULL;
|
||||||
|
struct dispd_sink *sink;
|
||||||
|
int r = sd_bus_path_decode(path,
|
||||||
|
"/org/freedesktop/miracle/wfd/sink",
|
||||||
|
&node);
|
||||||
|
if(0 >= r || !node) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_find_sink_by_label(dispd_get(), node, &sink);
|
||||||
|
if(r) {
|
||||||
|
*ret_found = sink;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispd_fn_session_new(struct dispd_session *s)
|
||||||
|
{
|
||||||
|
_shl_free_ char *path = NULL;
|
||||||
|
int r = dispd_dbus_get_session_path(s, &path);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_dbus_object_added(path, "org.freedesktop.miracle.wfd.Session");
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispd_fn_session_free(struct dispd_session *s)
|
||||||
|
{
|
||||||
|
_shl_free_ char *path = NULL;
|
||||||
|
int r = dispd_dbus_get_session_path(s, &path);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_dbus_object_removed(path, "org.freedesktop.miracle.wfd.Session");
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispd_dbus_find_session(sd_bus *bus,
|
||||||
|
const char *path,
|
||||||
|
const char *interface,
|
||||||
|
void *userdata,
|
||||||
|
void **ret_found,
|
||||||
|
sd_bus_error *ret_error)
|
||||||
|
{
|
||||||
|
struct dispd_session *s;
|
||||||
|
_shl_free_ char *node = NULL;
|
||||||
|
int r = sd_bus_path_decode(path,
|
||||||
|
"/org/freedesktop/miracle/wfd/session",
|
||||||
|
&node);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_find_session_by_id(dispd_get(),
|
||||||
|
strtoull(node, NULL, 10),
|
||||||
|
&s);
|
||||||
|
if(r) {
|
||||||
|
*ret_found = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_user_runtime_path(char **out, sd_bus *bus, uid_t uid)
|
||||||
|
{
|
||||||
|
_sd_bus_message_unref_ sd_bus_message *rep = NULL;
|
||||||
|
_sd_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
char p[256];
|
||||||
|
char *rp;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert_ret(out);
|
||||||
|
assert_ret(bus);
|
||||||
|
assert_ret(0 < uid);
|
||||||
|
|
||||||
|
r = snprintf(p, sizeof(p), "/org/freedesktop/login1/user/_%d", uid);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_call_method(bus,
|
||||||
|
"org.freedesktop.login1",
|
||||||
|
p,
|
||||||
|
"org.freedesktop.DBus.Properties",
|
||||||
|
"Get",
|
||||||
|
&error,
|
||||||
|
&rep,
|
||||||
|
"ss",
|
||||||
|
"org.freedesktop.login1.User",
|
||||||
|
"RuntimePath");
|
||||||
|
if(0 > r) {
|
||||||
|
log_warning("%s: %s", error.name, error.message);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_read(rep, "v", "s", &rp);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = strdup(rp);
|
||||||
|
if(!*out) {
|
||||||
|
return log_ENOMEM();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispd_dbus_sink_start_session(sd_bus_message *m,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *ret_error)
|
||||||
|
{
|
||||||
|
_dispd_session_unref_ struct dispd_session *sess = NULL;
|
||||||
|
_shl_free_ char *path = NULL, *disp_type_name = NULL, *disp_name = NULL;
|
||||||
|
_sd_bus_creds_unref_ sd_bus_creds *creds = NULL;
|
||||||
|
_shl_free_ char *runtime_path = NULL;
|
||||||
|
struct dispd_sink *sink = userdata;
|
||||||
|
char *disp_params;
|
||||||
|
const char *disp, *disp_auth;
|
||||||
|
const char *audio_dev;
|
||||||
|
struct dispd_rectangle rect;
|
||||||
|
pid_t pid;
|
||||||
|
uid_t uid;
|
||||||
|
gid_t gid;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = sd_bus_message_read(m,
|
||||||
|
"ssuuuus",
|
||||||
|
&disp_auth,
|
||||||
|
&disp,
|
||||||
|
&rect.x,
|
||||||
|
&rect.y,
|
||||||
|
&rect.width,
|
||||||
|
&rect.height,
|
||||||
|
&audio_dev);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sscanf(disp, "%m[^:]://%ms",
|
||||||
|
&disp_type_name,
|
||||||
|
&disp_name);
|
||||||
|
if(r != 2) {
|
||||||
|
return log_EINVAL();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strcmp("x", disp_type_name)) {
|
||||||
|
return log_EINVAL();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_sink_create_session(sink, &sess);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispd_session_set_disp_type(sess, DISPD_DISPLAY_SERVER_TYPE_X);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
disp_params = strchr(disp_name, '?');
|
||||||
|
if(disp_params) {
|
||||||
|
*disp_params ++ = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_session_set_disp_name(sess, disp_name);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_session_set_disp_params(sess, disp_params);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_session_set_disp_auth(sess, disp_auth);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_session_set_disp_dimension(sess, &rect);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(*audio_dev) {
|
||||||
|
r = dispd_session_set_audio_type(sess, DISPD_AUDIO_SERVER_TYPE_PULSE_AUDIO);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_session_set_audio_dev_name(sess, audio_dev);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_PID, &creds);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_creds_get_pid(creds, &pid);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
sd_bus_creds_unref(creds);
|
||||||
|
creds = NULL;
|
||||||
|
|
||||||
|
r = sd_bus_creds_new_from_pid(&creds,
|
||||||
|
pid,
|
||||||
|
SD_BUS_CREDS_UID | SD_BUS_CREDS_GID);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
dispd_session_set_client_pid(sess, gid);
|
||||||
|
|
||||||
|
r = sd_bus_creds_get_uid(creds, &uid);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
dispd_session_set_client_uid(sess, uid);
|
||||||
|
|
||||||
|
sd_bus_creds_get_gid(creds, &gid);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
dispd_session_set_client_gid(sess, gid);
|
||||||
|
|
||||||
|
r = get_user_runtime_path(&runtime_path,
|
||||||
|
sd_bus_message_get_bus(m),
|
||||||
|
uid);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_session_set_runtime_path(sess, runtime_path);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_session_start(sess);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_dbus_get_session_path(sess, &path);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_reply_method_return(m, "o", path);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispd_dbus_sink_get_session(sd_bus *bus,
|
||||||
|
const char *path,
|
||||||
|
const char *interface,
|
||||||
|
const char *property,
|
||||||
|
sd_bus_message *reply,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *ret_error)
|
||||||
|
{
|
||||||
|
struct dispd_sink *s = userdata;
|
||||||
|
_shl_free_ char *session_path = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if(s->session) {
|
||||||
|
r = dispd_dbus_get_session_path(s->session, &session_path);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
session_path = strdup("/");
|
||||||
|
if(!session_path) {
|
||||||
|
return log_ENOMEM();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_append(reply, "o", session_path);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispd_dbus_sink_get_peer(sd_bus *bus,
|
||||||
|
const char *path,
|
||||||
|
const char *interface,
|
||||||
|
const char *property,
|
||||||
|
sd_bus_message *reply,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *ret_error)
|
||||||
|
{
|
||||||
|
struct dispd_sink *s = userdata;
|
||||||
|
_shl_free_ char *peer_path = NULL;
|
||||||
|
int r = sd_bus_path_encode("/org/freedesktop/miracle/wifi/peer",
|
||||||
|
s->label,
|
||||||
|
&peer_path);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_append(reply, "o", peer_path);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//static int dispd_dbus_sink_has_audio(sd_bus *bus,
|
||||||
|
// const char *path,
|
||||||
|
// const char *interface,
|
||||||
|
// const char *property,
|
||||||
|
// sd_bus_message *reply,
|
||||||
|
// void *userdata,
|
||||||
|
// sd_bus_error *ret_error)
|
||||||
|
//{
|
||||||
|
// return 0;
|
||||||
|
//}
|
||||||
|
static int dispd_dbus_session_resume(sd_bus_message *m,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *ret_error)
|
||||||
|
{
|
||||||
|
struct dispd_session *s = userdata;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if(dispd_session_is_established(s)) {
|
||||||
|
r = dispd_session_resume(s);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return -ENOTCONN;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_reply_method_return(m, NULL);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispd_dbus_session_pause(sd_bus_message *m,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *ret_error)
|
||||||
|
{
|
||||||
|
struct dispd_session *s = userdata;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if(dispd_session_is_established(s)) {
|
||||||
|
r = dispd_session_pause(s);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return -ENOTCONN;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_reply_method_return(m, NULL);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispd_dbus_session_teardown(sd_bus_message *m,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *ret_error)
|
||||||
|
{
|
||||||
|
struct dispd_session *s = userdata;
|
||||||
|
int r = 0;
|
||||||
|
|
||||||
|
if(dispd_session_is_established(s)) {
|
||||||
|
r = dispd_session_teardown(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(0 > r) {
|
||||||
|
dispd_session_destroy(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_reply_method_return(m, NULL);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispd_dbus_session_get_sink(sd_bus *bus,
|
||||||
|
const char *path,
|
||||||
|
const char *interface,
|
||||||
|
const char *property,
|
||||||
|
sd_bus_message *reply,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *ret_error)
|
||||||
|
{
|
||||||
|
struct dispd_session *s = userdata;
|
||||||
|
_shl_free_ char *sink_path = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if(dispd_session_get_dir(s) != DISPD_SESSION_DIR_OUT) {
|
||||||
|
sink_path = strdup("/");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dispd_dbus_get_sink_path(dispd_out_session_get_sink(s), &sink_path);
|
||||||
|
}
|
||||||
|
if(!sink_path) {
|
||||||
|
return log_ENOMEM();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_append(reply, "o", sink_path);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispd_dbus_get_session_presentation_url(sd_bus *bus,
|
||||||
|
const char *path,
|
||||||
|
const char *interface,
|
||||||
|
const char *property,
|
||||||
|
sd_bus_message *reply,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *ret_error)
|
||||||
|
{
|
||||||
|
struct dispd_session *s = userdata;
|
||||||
|
int r = sd_bus_message_append(reply,
|
||||||
|
"s",
|
||||||
|
dispd_session_get_stream_url(s) ? : "");
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispd_dbus_get_session_state(sd_bus *bus,
|
||||||
|
const char *path,
|
||||||
|
const char *interface,
|
||||||
|
const char *property,
|
||||||
|
sd_bus_message *reply,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *ret_error)
|
||||||
|
{
|
||||||
|
struct dispd_session *s = userdata;
|
||||||
|
int r = sd_bus_message_append(reply, "i", dispd_session_get_state(s));
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _dispd_fn_session_properties_changed(struct dispd_session *s, char **names)
|
||||||
|
{
|
||||||
|
_shl_free_ char *path = NULL;
|
||||||
|
int r;
|
||||||
|
struct dispd_dbus *dispd_dbus = dispd_dbus_get();
|
||||||
|
|
||||||
|
if(!dispd_dbus) {
|
||||||
|
return -ECANCELED;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_dbus_get_session_path(s, &path);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_emit_properties_changed_strv(dispd_dbus_get()->bus,
|
||||||
|
path,
|
||||||
|
"org.freedesktop.miracle.wfd.Session",
|
||||||
|
names);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispd_dbus_shutdown(sd_bus_message *m,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *ret_error)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
dispd_shutdown(dispd_get());
|
||||||
|
|
||||||
|
r = sd_bus_reply_method_return(m, NULL);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const sd_bus_vtable dispd_dbus_vtable[] = {
|
||||||
|
SD_BUS_VTABLE_START(0),
|
||||||
|
SD_BUS_METHOD("Shutdown", NULL, NULL, dispd_dbus_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_VTABLE_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const sd_bus_vtable dispd_dbus_sink_vtable[] = {
|
||||||
|
SD_BUS_VTABLE_START(0),
|
||||||
|
SD_BUS_METHOD("StartSession", "ssuuuus", "o", dispd_dbus_sink_start_session, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
/*SD_BUS_PROPERTY("AudioFormats", "a{sv}", dispd_dbus_sink_get_audio_formats, 0, SD_BUS_VTABLE_PROPERTY_CONST),*/
|
||||||
|
/*SD_BUS_PROPERTY("VideoFormats", "a{sv}", dispd_dbus_sink_get_video_formats, 0, SD_BUS_VTABLE_PROPERTY_CONST),*/
|
||||||
|
/*SD_BUS_PROPERTY("HasAudio", "b", dispd_dbus_sink_has_audio, 0, SD_BUS_VTABLE_PROPERTY_CONST),*/
|
||||||
|
/*SD_BUS_PROPERTY("HasVideo", "b", dispd_dbus_sink_has_video, 0, SD_BUS_VTABLE_PROPERTY_CONST),*/
|
||||||
|
SD_BUS_PROPERTY("Session", "o", dispd_dbus_sink_get_session, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||||
|
SD_BUS_PROPERTY("Peer", "o", dispd_dbus_sink_get_peer, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
|
SD_BUS_VTABLE_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const sd_bus_vtable dispd_dbus_session_vtable[] = {
|
||||||
|
SD_BUS_VTABLE_START(0),
|
||||||
|
SD_BUS_METHOD("Resume", NULL, NULL, dispd_dbus_session_resume, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("Pause", NULL, NULL, dispd_dbus_session_pause, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("Teardown", NULL, NULL, dispd_dbus_session_teardown, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_PROPERTY("Sink", "o", dispd_dbus_session_get_sink, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
|
SD_BUS_PROPERTY("Url", "s", dispd_dbus_get_session_presentation_url, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||||
|
SD_BUS_PROPERTY("State", "i", dispd_dbus_get_session_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||||
|
SD_BUS_VTABLE_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
int dispd_dbus_expose(struct dispd_dbus *dispd_dbus)
|
||||||
|
{
|
||||||
|
int r = sd_bus_add_object_vtable(dispd_dbus->bus,
|
||||||
|
NULL,
|
||||||
|
"/org/freedesktop/miracle/wfd",
|
||||||
|
"org.freedesktop.miracle.wfd",
|
||||||
|
dispd_dbus_vtable,
|
||||||
|
dispd_dbus);
|
||||||
|
if(0 > r) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_add_fallback_vtable(dispd_dbus->bus,
|
||||||
|
NULL,
|
||||||
|
"/org/freedesktop/miracle/wfd/sink",
|
||||||
|
"org.freedesktop.miracle.wfd.Sink",
|
||||||
|
dispd_dbus_sink_vtable,
|
||||||
|
dispd_dbus_find_sink,
|
||||||
|
dispd_dbus);
|
||||||
|
if(0 > r) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_add_fallback_vtable(dispd_dbus->bus,
|
||||||
|
NULL,
|
||||||
|
"/org/freedesktop/miracle/wfd/session",
|
||||||
|
"org.freedesktop.miracle.wfd.Session",
|
||||||
|
dispd_dbus_session_vtable,
|
||||||
|
dispd_dbus_find_session,
|
||||||
|
dispd_dbus);
|
||||||
|
if(0 > r) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_add_node_enumerator(dispd_dbus->bus,
|
||||||
|
NULL,
|
||||||
|
"/org/freedesktop/miracle/wfd",
|
||||||
|
dispd_dbus_enum,
|
||||||
|
dispd_dbus);
|
||||||
|
if(0 > r) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_add_object_manager(dispd_dbus->bus, NULL, "/org/freedesktop/miracle/wfd");
|
||||||
|
if(0 > r) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_request_name(dispd_dbus->bus, "org.freedesktop.miracle.wfd", 0);
|
||||||
|
if(0 < r) {
|
||||||
|
dispd_dbus->exposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
47
src/disp/dispd-dbus.h
Normal file
47
src/disp/dispd-dbus.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||||
|
*
|
||||||
|
* MiracleCast 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) any later version.
|
||||||
|
*
|
||||||
|
* MiracleCast 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 MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <systemd/sd-bus.h>
|
||||||
|
|
||||||
|
#ifndef CTL_WFD_DBUS_H
|
||||||
|
#define CTL_WFD_DBUS_H
|
||||||
|
|
||||||
|
#define dispd_fn_sink_properties_changed(s, namev...) ({ \
|
||||||
|
char *names[] = { namev, NULL }; \
|
||||||
|
_dispd_fn_sink_properties_changed((s), names); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define dispd_fn_session_properties_changed(s, namev...) ({ \
|
||||||
|
char *names[] = { namev, NULL }; \
|
||||||
|
_dispd_fn_session_properties_changed((s), names); \
|
||||||
|
})
|
||||||
|
|
||||||
|
struct dispd_dbus;
|
||||||
|
struct dispd_session;
|
||||||
|
struct dispd_sink;
|
||||||
|
|
||||||
|
struct dispd_dbus * dispd_dbus_get();
|
||||||
|
int dispd_dbus_new(struct dispd_dbus **out, sd_event *loop, sd_bus *bus);
|
||||||
|
void dispd_dbus_free(struct dispd_dbus *dispd_dbus);
|
||||||
|
int dispd_dbus_expose(struct dispd_dbus *dispd_dbus);
|
||||||
|
int _dispd_fn_sink_properties_changed(struct dispd_sink *s, char **names);
|
||||||
|
int _dispd_fn_session_properties_changed(struct dispd_session *s, char **names);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
982
src/disp/dispd-encoder.c
Normal file
982
src/disp/dispd-encoder.c
Normal file
|
@ -0,0 +1,982 @@
|
||||||
|
/*
|
||||||
|
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||||
|
*
|
||||||
|
* MiracleCast 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) any later version.
|
||||||
|
*
|
||||||
|
* MiracleCast 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 MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <systemd/sd-event.h>
|
||||||
|
#include <systemd/sd-bus.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include "dispd-encoder.h"
|
||||||
|
#include "shl_macro.h"
|
||||||
|
#include "shl_log.h"
|
||||||
|
#include "dispd-session.h"
|
||||||
|
#include "dispd.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
struct dispd_encoder
|
||||||
|
{
|
||||||
|
int ref;
|
||||||
|
|
||||||
|
sd_event *loop;
|
||||||
|
sd_event_source *child_source;
|
||||||
|
sd_event_source *child_term_time_source;
|
||||||
|
sd_event_source *pipe_source;
|
||||||
|
|
||||||
|
sd_bus *bus;
|
||||||
|
sd_bus_slot *name_disappeared_slot;
|
||||||
|
sd_bus_slot *state_change_notify_slot;
|
||||||
|
|
||||||
|
uid_t bus_owner;
|
||||||
|
uid_t bus_group;
|
||||||
|
char *bus_name;
|
||||||
|
|
||||||
|
enum dispd_encoder_state state;
|
||||||
|
dispd_encoder_state_change_handler handler;
|
||||||
|
void *userdata;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int dispd_encoder_new(struct dispd_encoder **out,
|
||||||
|
uid_t bus_owner,
|
||||||
|
gid_t bus_group);
|
||||||
|
static int on_bus_info_readable(sd_event_source *source,
|
||||||
|
int fd,
|
||||||
|
uint32_t events,
|
||||||
|
void *userdata);
|
||||||
|
static void dispd_encoder_set_state(struct dispd_encoder *e,
|
||||||
|
enum dispd_encoder_state state);
|
||||||
|
|
||||||
|
static void dispd_encoder_exec(const char *cmd, int fd, struct dispd_session *s)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
sigset_t mask;
|
||||||
|
char disp[16], runtime_path[256];
|
||||||
|
|
||||||
|
log_info("child forked with pid %d", getpid());
|
||||||
|
|
||||||
|
/* restore to default signal handler */
|
||||||
|
sigemptyset(&mask);
|
||||||
|
sigprocmask(SIG_SETMASK, &mask, NULL);
|
||||||
|
|
||||||
|
snprintf(disp, sizeof(disp), "DISPLAY=%s", dispd_session_get_disp_name(s));
|
||||||
|
snprintf(runtime_path, sizeof(runtime_path), "XDG_RUNTIME_DIR=%s", dispd_session_get_runtime_path(s));
|
||||||
|
|
||||||
|
/* after encoder connected to DBus, write unique name to fd 3,
|
||||||
|
* so we can controll it through DBus
|
||||||
|
*/
|
||||||
|
r = dup2(fd, 3);
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERRNO();
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fd != 3) {
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO drop caps and don't let user raises thier caps
|
||||||
|
r = setgid(dispd_session_get_client_gid(s));
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERRNO();
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = setuid(dispd_session_get_client_uid(s));
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERRNO();
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERRNO();
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = execvpe(cmd,
|
||||||
|
(char *[]){ (char *) cmd, NULL },
|
||||||
|
(char *[]){ disp,
|
||||||
|
runtime_path,
|
||||||
|
"G_MESSAGES_DEBUG=all",
|
||||||
|
NULL
|
||||||
|
});
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERRNO();
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dispd_encoder_close_pipe(struct dispd_encoder *e)
|
||||||
|
{
|
||||||
|
if(!e->pipe_source) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(sd_event_source_get_io_fd(e->pipe_source));
|
||||||
|
|
||||||
|
sd_event_source_unref(e->pipe_source);
|
||||||
|
e->pipe_source = NULL;
|
||||||
|
|
||||||
|
dispd_encoder_unref(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispd_encoder_kill_child(struct dispd_encoder *e)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
if(!e->child_source) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO add timer in case child can't be terminated by SIGTERM
|
||||||
|
sd_event_source_get_child_pid(e->child_source, &pid);
|
||||||
|
r = kill(pid, SIGTERM);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dispd_encoder_notify_state_change(struct dispd_encoder *e,
|
||||||
|
enum dispd_encoder_state state)
|
||||||
|
{
|
||||||
|
assert_vret(e);
|
||||||
|
|
||||||
|
if(!e->handler) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispd_encoder_ref(e);
|
||||||
|
(*e->handler)(e, state, e->userdata);
|
||||||
|
dispd_encoder_unref(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dispd_encoder_cleanup(struct dispd_encoder *e)
|
||||||
|
{
|
||||||
|
if(e->child_source) {
|
||||||
|
sd_event_source_unref(e->child_source);
|
||||||
|
e->child_source = NULL;
|
||||||
|
dispd_encoder_unref(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e->child_term_time_source) {
|
||||||
|
sd_event_source_unref(e->child_term_time_source);
|
||||||
|
e->child_term_time_source = NULL;
|
||||||
|
dispd_encoder_unref(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e->child_source) {
|
||||||
|
sd_event_source_unref(e->child_source);
|
||||||
|
e->child_source = NULL;
|
||||||
|
dispd_encoder_unref(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispd_encoder_close_pipe(e);
|
||||||
|
|
||||||
|
if(e->name_disappeared_slot) {
|
||||||
|
sd_bus_slot_unref(e->name_disappeared_slot);
|
||||||
|
e->name_disappeared_slot = NULL;
|
||||||
|
dispd_encoder_unref(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e->state_change_notify_slot) {
|
||||||
|
sd_bus_slot_unref(e->state_change_notify_slot);
|
||||||
|
e->state_change_notify_slot = NULL;
|
||||||
|
dispd_encoder_unref(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int on_child_terminated(sd_event_source *source,
|
||||||
|
const siginfo_t *si,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
struct dispd_encoder *e = userdata;
|
||||||
|
|
||||||
|
log_info("encoder process %d terminated", si->si_pid);
|
||||||
|
|
||||||
|
dispd_encoder_set_state(e, DISPD_ENCODER_STATE_TERMINATED);
|
||||||
|
dispd_encoder_cleanup(e);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispd_encoder_spawn(struct dispd_encoder **out, struct dispd_session *s)
|
||||||
|
{
|
||||||
|
_dispd_encoder_unref_ struct dispd_encoder *e = NULL;
|
||||||
|
int fds[2] = { -1, -1 };
|
||||||
|
pid_t pid;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert_ret(out);
|
||||||
|
assert_ret(s);
|
||||||
|
|
||||||
|
r = dispd_encoder_new(&e,
|
||||||
|
dispd_session_get_client_uid(s),
|
||||||
|
dispd_session_get_client_gid(s));
|
||||||
|
if(0 > r) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = pipe2(fds, O_NONBLOCK);
|
||||||
|
if(0 > r) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
if(0 > pid) {
|
||||||
|
r = log_ERRNO();
|
||||||
|
goto kill_encoder;
|
||||||
|
}
|
||||||
|
else if(!pid) {
|
||||||
|
close(fds[0]);
|
||||||
|
|
||||||
|
dispd_encoder_exec("gstencoder", fds[1], s);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_event_add_child(dispd_get_loop(),
|
||||||
|
&e->child_source,
|
||||||
|
pid,
|
||||||
|
WEXITED,
|
||||||
|
on_child_terminated,
|
||||||
|
dispd_encoder_ref(e));
|
||||||
|
if(0 > r) {
|
||||||
|
goto close_pipe;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_event_add_io(dispd_get_loop(),
|
||||||
|
&e->pipe_source,
|
||||||
|
fds[0],
|
||||||
|
EPOLLIN,
|
||||||
|
on_bus_info_readable,
|
||||||
|
dispd_encoder_ref(e));
|
||||||
|
if(0 > r) {
|
||||||
|
goto close_pipe;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fds[1]);
|
||||||
|
*out = dispd_encoder_ref(e);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
close_pipe:
|
||||||
|
close(fds[0]);
|
||||||
|
close(fds[1]);
|
||||||
|
kill_encoder:
|
||||||
|
// dispd will do the cleanup
|
||||||
|
kill(pid, SIGKILL);
|
||||||
|
end:
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispd_encoder_new(struct dispd_encoder **out,
|
||||||
|
uid_t bus_owner,
|
||||||
|
gid_t bus_group)
|
||||||
|
{
|
||||||
|
_dispd_encoder_unref_ struct dispd_encoder *e = NULL;
|
||||||
|
|
||||||
|
assert_ret(out);
|
||||||
|
|
||||||
|
e = calloc(1, sizeof(struct dispd_encoder));
|
||||||
|
if(!e) {
|
||||||
|
return log_ENOMEM();
|
||||||
|
}
|
||||||
|
|
||||||
|
e->ref = 1;
|
||||||
|
e->bus_owner = bus_owner;
|
||||||
|
e->bus_group = bus_group;
|
||||||
|
|
||||||
|
*out = dispd_encoder_ref(e);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dispd_encoder * dispd_encoder_ref(struct dispd_encoder *e)
|
||||||
|
{
|
||||||
|
assert_retv(e, e);
|
||||||
|
assert_retv(0 < e->ref, e);
|
||||||
|
|
||||||
|
++ e->ref;
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispd_encoder_unrefp(struct dispd_encoder **e)
|
||||||
|
{
|
||||||
|
if(*e) {
|
||||||
|
dispd_encoder_unref(*e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispd_encoder_unref(struct dispd_encoder *e)
|
||||||
|
{
|
||||||
|
assert_vret(e);
|
||||||
|
assert_vret(0 < e->ref);
|
||||||
|
|
||||||
|
--e->ref;
|
||||||
|
if(e->ref) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* since we encrease ref count at creation of every sources and slots,
|
||||||
|
* once we get here, it means no sources and slots exist anymore */
|
||||||
|
if(e->bus) {
|
||||||
|
sd_bus_detach_event(e->bus);
|
||||||
|
sd_bus_unref(e->bus);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e->bus_name) {
|
||||||
|
free(e->bus_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if(e->bus_addr) {
|
||||||
|
// free(e->bus_addr);
|
||||||
|
// }
|
||||||
|
|
||||||
|
free(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispd_encoder_set_handler(struct dispd_encoder *e,
|
||||||
|
dispd_encoder_state_change_handler handler,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
assert_vret(e);
|
||||||
|
|
||||||
|
e->handler = handler;
|
||||||
|
e->userdata = userdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispd_encoder_state_change_handler dispd_encoder_get_handler(struct dispd_encoder *e)
|
||||||
|
{
|
||||||
|
assert_retv(e, NULL);
|
||||||
|
|
||||||
|
return e->handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum dispd_encoder_state dispd_encoder_get_state(struct dispd_encoder *e)
|
||||||
|
{
|
||||||
|
assert_retv(e, DISPD_ENCODER_STATE_NULL);
|
||||||
|
|
||||||
|
return e->state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char * state_to_name(enum dispd_encoder_state s)
|
||||||
|
{
|
||||||
|
const char *names[] = {
|
||||||
|
"NULL",
|
||||||
|
"SPAWNED",
|
||||||
|
"CONFIGURED",
|
||||||
|
"READY",
|
||||||
|
"STARTED",
|
||||||
|
"PAUSED",
|
||||||
|
"TERMINATED"
|
||||||
|
};
|
||||||
|
|
||||||
|
if(0 > s || DISPD_ENCODER_STATE_TERMINATED < s) {
|
||||||
|
return "unknown encoder state";
|
||||||
|
}
|
||||||
|
|
||||||
|
return names[s];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dispd_encoder_set_state(struct dispd_encoder *e,
|
||||||
|
enum dispd_encoder_state state)
|
||||||
|
{
|
||||||
|
assert_vret(e);
|
||||||
|
|
||||||
|
if(e->state == state) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("state change from %s to %s",
|
||||||
|
state_to_name(e->state),
|
||||||
|
state_to_name(state));
|
||||||
|
|
||||||
|
e->state = state;
|
||||||
|
dispd_encoder_notify_state_change(e, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int on_encoder_properties_changed(sd_bus_message *m,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *ret_error)
|
||||||
|
{
|
||||||
|
struct dispd_encoder *e = userdata;
|
||||||
|
const char *name;
|
||||||
|
int value;
|
||||||
|
enum dispd_encoder_state s;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = sd_bus_message_skip(m, "s");
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_enter_container(m, 'a', "{sv}");
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
while(!sd_bus_message_at_end(m, true)) {
|
||||||
|
r = sd_bus_message_read(m, "{sv}", &name, "i", &value);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strcmp("State", name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(value) {
|
||||||
|
case 0:
|
||||||
|
s = DISPD_ENCODER_STATE_NULL;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
s = DISPD_ENCODER_STATE_CONFIGURED;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
s = DISPD_ENCODER_STATE_READY;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
s = DISPD_ENCODER_STATE_STARTED;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
s = DISPD_ENCODER_STATE_PAUSED;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
s = DISPD_ENCODER_STATE_TERMINATED;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log_error("encoder enter unknown state: %d", value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispd_encoder_set_state(e, s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int on_encoder_disappeared(sd_bus_message *m,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *ret_error)
|
||||||
|
{
|
||||||
|
struct dispd_encoder *e = userdata;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
log_info("encoder %s disappered from bus", e->bus_name);
|
||||||
|
|
||||||
|
r = dispd_encoder_kill_child(e);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
else if(r) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispd_encoder_cleanup(e);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_line(int fd, char *b, size_t len)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
char *p = b;
|
||||||
|
|
||||||
|
assert_ret(0 <= fd);
|
||||||
|
assert_ret(b);
|
||||||
|
assert_ret(len);
|
||||||
|
|
||||||
|
while((p - b) < (len - 1)) {
|
||||||
|
r = read(fd, p, 1);
|
||||||
|
if(0 > r) {
|
||||||
|
if(EINTR == errno) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if(EAGAIN == errno) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
else if(!r || '\n' == *p) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++ p;
|
||||||
|
}
|
||||||
|
|
||||||
|
*p = '\0';
|
||||||
|
|
||||||
|
return p - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int on_bus_info_readable(sd_event_source *source,
|
||||||
|
int fd,
|
||||||
|
uint32_t events,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
struct dispd_encoder *e = userdata;
|
||||||
|
char buf[512];
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = read_line(fd, buf, sizeof(buf) - 1);
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERR(r);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
else if(!r) {
|
||||||
|
log_warning("no bus name returned from encoder");
|
||||||
|
r = -ENOENT;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO remove heading and trailing speces from buf before strdup()
|
||||||
|
log_info("got bus name from encoder: %s", buf);
|
||||||
|
|
||||||
|
e->bus_name = strdup(buf);
|
||||||
|
if(!e->bus_name) {
|
||||||
|
log_vERRNO();
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = read_line(fd, buf, sizeof(buf) - 1);
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERR(r);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
else if(!r) {
|
||||||
|
log_warning("no bus address returned from encoder");
|
||||||
|
r = -ENOENT;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info("got bus address from encoder: %s", buf);
|
||||||
|
|
||||||
|
r = seteuid(e->bus_owner);
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERRNO();
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_new(&e->bus);
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERR(r);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_set_address(e->bus, buf);
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERR(r);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_set_bus_client(e->bus, true);
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERR(r);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_start(e->bus);
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERR(r);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_attach_event(e->bus, dispd_get_loop(), 0);
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERR(r);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = seteuid(0);
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERRNO();
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf),
|
||||||
|
"type='signal',"
|
||||||
|
"sender='%s',"
|
||||||
|
"path='/org/freedesktop/miracle/encoder',"
|
||||||
|
"interface='org.freedesktop.DBus.Properties',"
|
||||||
|
"member='PropertiesChanged',"
|
||||||
|
"arg0='org.freedesktop.miracle.encoder'",
|
||||||
|
e->bus_name);
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERRNO();
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_add_match(e->bus,
|
||||||
|
&e->state_change_notify_slot,
|
||||||
|
buf,
|
||||||
|
on_encoder_properties_changed,
|
||||||
|
dispd_encoder_ref(e));
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERRNO();
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = snprintf(buf, sizeof(buf),
|
||||||
|
"type='signal',"
|
||||||
|
"sender='org.freedesktop.DBus',"
|
||||||
|
"path='/org/freedesktop/DBus',"
|
||||||
|
"interface='org.freedesktop.DBus',"
|
||||||
|
"member='NameOwnerChanged',"
|
||||||
|
"arg0namespace='%s'",
|
||||||
|
e->bus_name);
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERRNO();
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_add_match(e->bus,
|
||||||
|
&e->name_disappeared_slot,
|
||||||
|
buf,
|
||||||
|
on_encoder_disappeared,
|
||||||
|
dispd_encoder_ref(e));
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERRNO();
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispd_encoder_set_state(e, DISPD_ENCODER_STATE_SPAWNED);
|
||||||
|
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
error:
|
||||||
|
seteuid(0);
|
||||||
|
dispd_encoder_kill_child(e);
|
||||||
|
end:
|
||||||
|
dispd_encoder_close_pipe(e);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_append(sd_bus_message *m,
|
||||||
|
enum wfd_encoder_config k,
|
||||||
|
const char *t,
|
||||||
|
...)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
va_list argv;
|
||||||
|
|
||||||
|
assert_ret(m);
|
||||||
|
assert_ret(t);
|
||||||
|
|
||||||
|
r = sd_bus_message_open_container(m, 'e', "iv");
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_append(m, "i", k);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_open_container(m, 'v', t);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
va_start(argv, t);
|
||||||
|
switch(*t) {
|
||||||
|
case 's':
|
||||||
|
r = sd_bus_message_append(m, t, va_arg(argv, char *));
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
r = sd_bus_message_append(m, t, va_arg(argv, uint32_t));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
va_end(argv);
|
||||||
|
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_close_container(m);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_close_container(m);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispd_encoder_configure(struct dispd_encoder *e, struct dispd_session *s)
|
||||||
|
{
|
||||||
|
_cleanup_sd_bus_message_ sd_bus_message *call = NULL;
|
||||||
|
_cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
|
||||||
|
_cleanup_sd_bus_error_ sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
const struct dispd_rectangle *rect;
|
||||||
|
struct dispd_sink *sink;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert_ret(e);
|
||||||
|
assert_ret(e->bus);
|
||||||
|
assert_ret(s);
|
||||||
|
assert_ret(dispd_is_out_session(s));
|
||||||
|
|
||||||
|
r = sd_bus_message_new_method_call(e->bus,
|
||||||
|
&call,
|
||||||
|
e->bus_name,
|
||||||
|
"/org/freedesktop/miracle/encoder",
|
||||||
|
"org.freedesktop.miracle.encoder",
|
||||||
|
"Configure");
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_open_container(call, 'a', "{iv}");
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
sink = dispd_out_session_get_sink(s);
|
||||||
|
r = config_append(call,
|
||||||
|
WFD_ENCODER_CONFIG_PEER_ADDRESS,
|
||||||
|
"s",
|
||||||
|
sink->peer->remote_address);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = config_append(call,
|
||||||
|
WFD_ENCODER_CONFIG_RTP_PORT0,
|
||||||
|
"u",
|
||||||
|
s->stream.rtp_port);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(s->stream.rtcp_port) {
|
||||||
|
r = config_append(call,
|
||||||
|
WFD_ENCODER_CONFIG_PEER_RTCP_PORT,
|
||||||
|
"u",
|
||||||
|
s->stream.rtcp_port);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r = config_append(call,
|
||||||
|
WFD_ENCODER_CONFIG_LOCAL_ADDRESS,
|
||||||
|
"s",
|
||||||
|
sink->peer->local_address);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(s->stream.rtcp_port) {
|
||||||
|
r = config_append(call,
|
||||||
|
WFD_ENCODER_CONFIG_LOCAL_RTCP_PORT,
|
||||||
|
"u",
|
||||||
|
s->stream.rtcp_port);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rect = dispd_session_get_disp_dimension(s);
|
||||||
|
if(rect) {
|
||||||
|
r = config_append(call,
|
||||||
|
WFD_ENCODER_CONFIG_X,
|
||||||
|
"u",
|
||||||
|
rect->x);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = config_append(call,
|
||||||
|
WFD_ENCODER_CONFIG_Y,
|
||||||
|
"u",
|
||||||
|
rect->y);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = config_append(call,
|
||||||
|
WFD_ENCODER_CONFIG_WIDTH,
|
||||||
|
"u",
|
||||||
|
rect->width);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = config_append(call,
|
||||||
|
WFD_ENCODER_CONFIG_HEIGHT,
|
||||||
|
"u",
|
||||||
|
rect->height);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dispd_session_has_audio(s) && dispd_session_get_audio_dev_name(s)) {
|
||||||
|
r = config_append(call,
|
||||||
|
WFD_ENCODER_CONFIG_AUDIO_TYPE,
|
||||||
|
"u",
|
||||||
|
dispd_session_get_audio_type(s));
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = config_append(call,
|
||||||
|
WFD_ENCODER_CONFIG_AUDIO_DEV,
|
||||||
|
"s",
|
||||||
|
dispd_session_get_audio_dev_name(s));
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_close_container(call);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERR(r);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_call(e->bus, call, 0, &error, &reply);
|
||||||
|
if(0 > r) {
|
||||||
|
log_warning("%s: %s", error.name, error.message);
|
||||||
|
log_vERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispd_encoder_call(struct dispd_encoder *e, const char *method)
|
||||||
|
{
|
||||||
|
_cleanup_sd_bus_message_ sd_bus_message *call = NULL;
|
||||||
|
_cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
|
||||||
|
_cleanup_sd_bus_error_ sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert_ret(e);
|
||||||
|
assert_ret(method);
|
||||||
|
assert_ret(e->bus);
|
||||||
|
|
||||||
|
r = sd_bus_message_new_method_call(e->bus,
|
||||||
|
&call,
|
||||||
|
e->bus_name,
|
||||||
|
"/org/freedesktop/miracle/encoder",
|
||||||
|
"org.freedesktop.miracle.encoder",
|
||||||
|
method);
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERR(r);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERR(r);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_call(e->bus, call, 0, &error, &reply);
|
||||||
|
if(0 > r) {
|
||||||
|
log_warning("%s: %s", error.name, error.message);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
dispd_encoder_kill_child(e);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispd_encoder_start(struct dispd_encoder *e)
|
||||||
|
{
|
||||||
|
assert_ret(e);
|
||||||
|
|
||||||
|
return dispd_encoder_call(e, "Start");
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispd_encoder_pause(struct dispd_encoder *e)
|
||||||
|
{
|
||||||
|
assert_ret(e);
|
||||||
|
|
||||||
|
return dispd_encoder_call(e, "Pause");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int on_child_term_timeout(sd_event_source *s,
|
||||||
|
uint64_t usec,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
struct dispd_encoder *e = userdata;
|
||||||
|
|
||||||
|
dispd_encoder_kill_child(e);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispd_encoder_stop(struct dispd_encoder *e)
|
||||||
|
{
|
||||||
|
uint64_t now;
|
||||||
|
sd_event *loop;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert_ret(e);
|
||||||
|
|
||||||
|
r = dispd_encoder_call(e, "Stop");
|
||||||
|
if(0 > r) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
loop = dispd_get_loop();
|
||||||
|
r = sd_event_now(loop, CLOCK_MONOTONIC, &now);
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERR(r);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_event_add_time(loop,
|
||||||
|
&e->child_term_time_source,
|
||||||
|
CLOCK_MONOTONIC,
|
||||||
|
now + (1000 * 1000),
|
||||||
|
0,
|
||||||
|
on_child_term_timeout,
|
||||||
|
dispd_encoder_ref(e));
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERR(r);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
dispd_encoder_kill_child(e);
|
||||||
|
return r;
|
||||||
|
}
|
87
src/disp/dispd-encoder.h
Normal file
87
src/disp/dispd-encoder.h
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||||
|
*
|
||||||
|
* MiracleCast 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) any later version.
|
||||||
|
*
|
||||||
|
* MiracleCast 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 MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "shl_macro.h"
|
||||||
|
|
||||||
|
#ifndef DISPD_ENCODER_H
|
||||||
|
#define DISPD_ENCODER_H
|
||||||
|
|
||||||
|
#define _dispd_encoder_unref_ _shl_cleanup_(dispd_encoder_unrefp)
|
||||||
|
|
||||||
|
enum wfd_encoder_config
|
||||||
|
{
|
||||||
|
WFD_ENCODER_CONFIG_DISPLAY_TYPE, /* string */
|
||||||
|
WFD_ENCODER_CONFIG_DISPLAY_NAME, /* string */
|
||||||
|
WFD_ENCODER_CONFIG_MONITOR_NUM, /* uint32 */
|
||||||
|
WFD_ENCODER_CONFIG_X, /* uint32 */
|
||||||
|
WFD_ENCODER_CONFIG_Y, /* uint32 */
|
||||||
|
WFD_ENCODER_CONFIG_WIDTH, /* uint32 */
|
||||||
|
WFD_ENCODER_CONFIG_HEIGHT, /* uint32 */
|
||||||
|
WFD_ENCODER_CONFIG_WINDOW_ID, /* uint32 */
|
||||||
|
WFD_ENCODER_CONFIG_FRAMERATE, /* uint32 */
|
||||||
|
WFD_ENCODER_CONFIG_SCALE_WIDTH, /* uint32 */
|
||||||
|
WFD_ENCODER_CONFIG_SCALE_HEIGHT, /* uint32 */
|
||||||
|
WFD_ENCODER_CONFIG_AUDIO_TYPE, /* string */
|
||||||
|
WFD_ENCODER_CONFIG_AUDIO_DEV, /* string */
|
||||||
|
WFD_ENCODER_CONFIG_PEER_ADDRESS, /* string */
|
||||||
|
WFD_ENCODER_CONFIG_RTP_PORT0, /* uint32 */
|
||||||
|
WFD_ENCODER_CONFIG_RTP_PORT1, /* uint32 */
|
||||||
|
WFD_ENCODER_CONFIG_PEER_RTCP_PORT, /* uint32 */
|
||||||
|
WFD_ENCODER_CONFIG_LOCAL_ADDRESS, /* uint32 */
|
||||||
|
WFD_ENCODER_CONFIG_LOCAL_RTCP_PORT, /* uint32 */
|
||||||
|
WFD_ENCODER_CONFIG_H264_PROFILE,
|
||||||
|
WFD_ENCODER_CONFIG_H264_LEVEL,
|
||||||
|
WFD_ENCODER_CONFIG_DEBUG_LEVEL,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum dispd_encoder_state
|
||||||
|
{
|
||||||
|
DISPD_ENCODER_STATE_NULL = 0,
|
||||||
|
DISPD_ENCODER_STATE_SPAWNED,
|
||||||
|
DISPD_ENCODER_STATE_CONFIGURED,
|
||||||
|
DISPD_ENCODER_STATE_READY,
|
||||||
|
DISPD_ENCODER_STATE_STARTED,
|
||||||
|
DISPD_ENCODER_STATE_PAUSED,
|
||||||
|
DISPD_ENCODER_STATE_TERMINATED,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dispd_session;
|
||||||
|
struct dispd_encoder;
|
||||||
|
|
||||||
|
typedef void (*dispd_encoder_state_change_handler)(struct dispd_encoder *e,
|
||||||
|
enum dispd_encoder_state state,
|
||||||
|
void *userdata);
|
||||||
|
|
||||||
|
int dispd_encoder_spawn(struct dispd_encoder **out, struct dispd_session *s);
|
||||||
|
struct dispd_encoder * dispd_encoder_ref(struct dispd_encoder *e);
|
||||||
|
void dispd_encoder_unref(struct dispd_encoder *e);
|
||||||
|
void dispd_encoder_unrefp(struct dispd_encoder **e);
|
||||||
|
|
||||||
|
int dispd_encoder_configure(struct dispd_encoder *e, struct dispd_session *s);
|
||||||
|
int dispd_encoder_start(struct dispd_encoder *e);
|
||||||
|
int dispd_encoder_pause(struct dispd_encoder *e);
|
||||||
|
int dispd_encoder_stop(struct dispd_encoder *e);
|
||||||
|
|
||||||
|
void dispd_encoder_set_handler(struct dispd_encoder *e,
|
||||||
|
dispd_encoder_state_change_handler handler,
|
||||||
|
void *userdata);
|
||||||
|
dispd_encoder_state_change_handler dispd_encoder_get_handler(struct dispd_encoder *e);
|
||||||
|
enum dispd_encoder_state dispd_encoder_get_state(struct dispd_encoder *e);
|
||||||
|
|
||||||
|
#endif /* DISPD_ENCODER_H */
|
1004
src/disp/dispd-out-session.c
Normal file
1004
src/disp/dispd-out-session.c
Normal file
File diff suppressed because it is too large
Load diff
1091
src/disp/dispd-session.c
Normal file
1091
src/disp/dispd-session.c
Normal file
File diff suppressed because it is too large
Load diff
146
src/disp/dispd-session.h
Normal file
146
src/disp/dispd-session.h
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
/*
|
||||||
|
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||||
|
*
|
||||||
|
* MiracleCast 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) any later version.
|
||||||
|
*
|
||||||
|
* MiracleCast 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 MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "dispd.h"
|
||||||
|
#include "dispd-arg.h"
|
||||||
|
|
||||||
|
#ifndef DISPD_SESSION_H
|
||||||
|
#define DISPD_SESSION_H
|
||||||
|
|
||||||
|
#define dispd_out_session(s) (assert(dispd_is_out_session(s)), (struct dispd_out_session *) (s))
|
||||||
|
#define dispd_in_session(s) (assert(dispd_is_in_session(s)), (struct dispd_in_session *) (s))
|
||||||
|
|
||||||
|
struct dispd_session;
|
||||||
|
struct dispd_sink;
|
||||||
|
struct rtsp;
|
||||||
|
struct rtsp_message;
|
||||||
|
|
||||||
|
enum rtsp_message_id
|
||||||
|
{
|
||||||
|
RTSP_M_UNKNOWN,
|
||||||
|
RTSP_M1_REQUEST_SINK_OPTIONS,
|
||||||
|
RTSP_M2_REQUEST_SRC_OPTIONS,
|
||||||
|
RTSP_M3_GET_PARAMETER,
|
||||||
|
RTSP_M4_SET_PARAMETER,
|
||||||
|
RTSP_M5_TRIGGER,
|
||||||
|
RTSP_M6_SETUP,
|
||||||
|
RTSP_M7_PLAY,
|
||||||
|
RTSP_M8_TEARDOWN,
|
||||||
|
RTSP_M9_PAUSE,
|
||||||
|
RTSP_M10_SET_ROUTE,
|
||||||
|
RTSP_M11_SET_CONNECTOR_TYPE,
|
||||||
|
RTSP_M12_SET_STANDBY,
|
||||||
|
RTSP_M13_REQUEST_IDR,
|
||||||
|
RTSP_M14_ESTABLISH_UIBC,
|
||||||
|
RTSP_M15_ENABLE_UIBC,
|
||||||
|
RTSP_M16_KEEPALIVE,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum dispd_stream_id
|
||||||
|
{
|
||||||
|
DISPD_STREAM_ID_PRIMARY,
|
||||||
|
DISPD_STREAM_ID_SECONDARY,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum dispd_session_arg_id
|
||||||
|
{
|
||||||
|
DISPD_SESSION_ARG_NEXT_REQUEST,
|
||||||
|
DISPD_SESSION_ARG_NEW_STATE,
|
||||||
|
DISPD_SESSION_ARG_REQUEST_ARGS,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rtsp_dispatch_entry
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
int (*request)(struct dispd_session *s,
|
||||||
|
struct rtsp *bus,
|
||||||
|
const struct dispd_arg_list *args,
|
||||||
|
struct rtsp_message **out);
|
||||||
|
int (*handle_request)(struct dispd_session *s,
|
||||||
|
struct rtsp_message *req,
|
||||||
|
struct rtsp_message **out_rep);
|
||||||
|
};
|
||||||
|
int (*handle_reply)(struct dispd_session *s,
|
||||||
|
struct rtsp_message *m);
|
||||||
|
struct dispd_arg_list rule;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dispd_session_vtable
|
||||||
|
{
|
||||||
|
int (*initiate_io)(struct dispd_session *s, int *out_fd, uint32_t *out_mask);
|
||||||
|
int (*handle_io)(struct dispd_session *s, int error, int *out_fd);
|
||||||
|
int (*initiate_request)(struct dispd_session *s);
|
||||||
|
int (*resume)(struct dispd_session *);
|
||||||
|
int (*pause)(struct dispd_session *);
|
||||||
|
int (*teardown)(struct dispd_session *);
|
||||||
|
void (*destroy)(struct dispd_session *s);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dispd_session
|
||||||
|
{
|
||||||
|
int ref;
|
||||||
|
enum dispd_session_dir dir;
|
||||||
|
enum dispd_session_state state;
|
||||||
|
enum rtsp_message_id last_request;
|
||||||
|
const struct rtsp_dispatch_entry *rtsp_disp_tbl;
|
||||||
|
|
||||||
|
unsigned int id;
|
||||||
|
struct rtsp *rtsp;
|
||||||
|
uint64_t req_cookie;
|
||||||
|
uint16_t rtp_ports[2];
|
||||||
|
struct wfd_video_formats *vformats;
|
||||||
|
struct wfd_audio_codecs *acodecs;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
enum dispd_stream_id id;
|
||||||
|
char *url;
|
||||||
|
uint16_t rtp_port;
|
||||||
|
uint16_t rtcp_port;
|
||||||
|
} stream;
|
||||||
|
|
||||||
|
enum dispd_display_server_type disp_type;
|
||||||
|
char *disp_name;
|
||||||
|
char *disp_params;
|
||||||
|
char *disp_auth;
|
||||||
|
struct dispd_rectangle disp_dimen;
|
||||||
|
enum dispd_audio_server_type audio_type;
|
||||||
|
char *audio_dev_name;
|
||||||
|
|
||||||
|
uid_t client_uid;
|
||||||
|
gid_t client_gid;
|
||||||
|
gid_t client_pid;
|
||||||
|
char *runtime_path;
|
||||||
|
};
|
||||||
|
|
||||||
|
int dispd_session_init(struct dispd_session *s,
|
||||||
|
unsigned int id,
|
||||||
|
enum dispd_session_dir dir,
|
||||||
|
const struct rtsp_dispatch_entry *disp_tbl);
|
||||||
|
int dispd_session_gen_stream_url(struct dispd_session *s,
|
||||||
|
const char *local_addr,
|
||||||
|
enum dispd_stream_id id);
|
||||||
|
int dispd_session_request(struct dispd_session *s,
|
||||||
|
enum rtsp_message_id id,
|
||||||
|
const struct dispd_arg_list *args);
|
||||||
|
void dispd_session_end(struct dispd_session *s);
|
||||||
|
struct dispd_sink * dispd_out_session_get_sink(struct dispd_session *s);
|
||||||
|
void dispd_session_set_state(struct dispd_session *s,
|
||||||
|
enum dispd_session_state state);
|
||||||
|
|
||||||
|
#endif /* DISPD_SESSION_H */
|
173
src/disp/dispd-sink.c
Normal file
173
src/disp/dispd-sink.c
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
/*
|
||||||
|
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||||
|
*
|
||||||
|
* MiracleCast 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) any later version.
|
||||||
|
*
|
||||||
|
* MiracleCast 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 MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <time.h>
|
||||||
|
#include <systemd/sd-event.h>
|
||||||
|
#include "ctl.h"
|
||||||
|
#include "dispd.h"
|
||||||
|
#include "dispd-dbus.h"
|
||||||
|
|
||||||
|
static int dispd_sink_set_session(struct dispd_sink *sink,
|
||||||
|
struct dispd_session *session)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert_ret(sink);
|
||||||
|
|
||||||
|
if(sink->session == session) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(session) {
|
||||||
|
r = dispd_add_session(dispd_get(), session);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sink->session) {
|
||||||
|
dispd_fn_sink_detach(sink);
|
||||||
|
dispd_remove_session_by_id(dispd_get(),
|
||||||
|
dispd_session_get_id(sink->session),
|
||||||
|
NULL);
|
||||||
|
dispd_session_unref(sink->session);
|
||||||
|
}
|
||||||
|
|
||||||
|
sink->session = session ? dispd_session_ref(session) : NULL;
|
||||||
|
dispd_fn_sink_properties_changed(sink, "Session");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispd_sink_new(struct dispd_sink **out,
|
||||||
|
struct ctl_peer *peer,
|
||||||
|
union wfd_sube *sube)
|
||||||
|
{
|
||||||
|
struct dispd_sink *sink;
|
||||||
|
|
||||||
|
assert_ret(out);
|
||||||
|
assert_ret(peer);
|
||||||
|
assert_ret(sube);
|
||||||
|
assert_ret(wfd_sube_device_is_sink(sube));
|
||||||
|
|
||||||
|
sink = calloc(1, sizeof(struct dispd_sink));
|
||||||
|
if(!sink) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
sink->label = strdup(peer->label);
|
||||||
|
if(!sink->label) {
|
||||||
|
dispd_sink_free(sink);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
sink->peer = peer;
|
||||||
|
sink->dev_info = *sube;
|
||||||
|
|
||||||
|
*out = sink;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispd_sink_free(struct dispd_sink *sink)
|
||||||
|
{
|
||||||
|
if(!sink) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispd_sink_set_session(sink, NULL);
|
||||||
|
|
||||||
|
if(sink->label) {
|
||||||
|
free(sink->label);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * dispd_sink_get_label(struct dispd_sink *sink)
|
||||||
|
{
|
||||||
|
assert_retv(sink, NULL);
|
||||||
|
|
||||||
|
return sink->label;
|
||||||
|
}
|
||||||
|
|
||||||
|
const union wfd_sube * dispd_sink_get_dev_info(struct dispd_sink *sink)
|
||||||
|
{
|
||||||
|
assert_retv(sink, NULL);
|
||||||
|
|
||||||
|
return &sink->dev_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ctl_peer * dispd_sink_get_peer(struct dispd_sink *sink)
|
||||||
|
{
|
||||||
|
assert_retv(sink, NULL);
|
||||||
|
|
||||||
|
return sink->peer;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispd_sink_create_session(struct dispd_sink *sink, struct dispd_session **out)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
_dispd_session_unref_ struct dispd_session *sess = NULL;
|
||||||
|
|
||||||
|
assert_ret(sink);
|
||||||
|
assert_ret(out);
|
||||||
|
|
||||||
|
if(dispd_sink_is_session_started(sink)) {
|
||||||
|
return -EALREADY;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_out_session_new(&sess,
|
||||||
|
dispd_alloc_session_id(dispd_get()),
|
||||||
|
sink);
|
||||||
|
if(0 > r) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_sink_set_session(sink, sess);
|
||||||
|
if(0 > r) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = dispd_session_ref(sess);
|
||||||
|
|
||||||
|
dispd_fn_sink_properties_changed(sink, "Session");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispd_fn_out_session_ended(struct dispd_session *s)
|
||||||
|
{
|
||||||
|
struct dispd_sink *sink;
|
||||||
|
|
||||||
|
assert_ret(dispd_is_out_session(s));
|
||||||
|
|
||||||
|
sink = dispd_out_session_get_sink(s);
|
||||||
|
if(sink) {
|
||||||
|
dispd_sink_set_session(sink, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dispd_sink_is_session_started(struct dispd_sink *sink)
|
||||||
|
{
|
||||||
|
assert_retv(sink, false);
|
||||||
|
|
||||||
|
return NULL != sink->session;
|
||||||
|
}
|
545
src/disp/dispd.c
Normal file
545
src/disp/dispd.c
Normal file
|
@ -0,0 +1,545 @@
|
||||||
|
/*
|
||||||
|
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||||
|
*
|
||||||
|
* MiracleCast 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) any later version.
|
||||||
|
*
|
||||||
|
* MiracleCast 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 MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#define LOG_SUBSYSTEM "dispd"
|
||||||
|
|
||||||
|
#include <locale.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <systemd/sd-event.h>
|
||||||
|
#include <systemd/sd-daemon.h>
|
||||||
|
#include "ctl.h"
|
||||||
|
#include "dispd.h"
|
||||||
|
#include "wfd.h"
|
||||||
|
#include "dispd-dbus.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#define CLI_PROMPT "\001" CLI_BLUE "\002" "[dispctl] # " "\001" CLI_DEFAULT "\002"
|
||||||
|
|
||||||
|
#define HISTORY_FILENAME ".miracle-disp.history"
|
||||||
|
|
||||||
|
static int dispd_init(struct dispd *dispd, sd_bus *bus);
|
||||||
|
static void dispd_free(struct dispd *dispd);
|
||||||
|
|
||||||
|
static struct dispd_dbus *dispd_dbus = NULL;
|
||||||
|
|
||||||
|
char* get_cli_prompt()
|
||||||
|
{
|
||||||
|
return CLI_PROMPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get history filename
|
||||||
|
*/
|
||||||
|
|
||||||
|
char* get_history_filename()
|
||||||
|
{
|
||||||
|
return HISTORY_FILENAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dispd_dbus * dispd_dbus_get()
|
||||||
|
{
|
||||||
|
return dispd_dbus;
|
||||||
|
}
|
||||||
|
static struct dispd *dispd = NULL;
|
||||||
|
|
||||||
|
struct ctl_wifi *get_wifi()
|
||||||
|
{
|
||||||
|
return dispd->wifi;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dispd * dispd_get()
|
||||||
|
{
|
||||||
|
return dispd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispd_new(struct dispd **out, sd_event *loop, sd_bus *bus)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
struct dispd *dispd = calloc(1, sizeof(struct dispd));
|
||||||
|
if(!dispd) {
|
||||||
|
r = -ENOMEM;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
shl_htable_init_str(&dispd->sinks);
|
||||||
|
shl_htable_init_uint(&dispd->sessions);
|
||||||
|
dispd->loop = sd_event_ref(loop);
|
||||||
|
|
||||||
|
r = dispd_init(dispd, bus);
|
||||||
|
if(0 > r) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = dispd;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
dispd_free(dispd);
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dispd_free(struct dispd *dispd)
|
||||||
|
{
|
||||||
|
if(!dispd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctl_wifi_free(dispd->wifi);
|
||||||
|
dispd->wifi = NULL;
|
||||||
|
shl_htable_clear_str(&dispd->sinks, NULL, NULL);
|
||||||
|
shl_htable_clear_uint(&dispd->sessions, NULL, NULL);
|
||||||
|
|
||||||
|
if(dispd->loop) {
|
||||||
|
sd_event_unref(dispd->loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(dispd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispd_handle_shutdown(sd_event_source *s,
|
||||||
|
uint64_t usec,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
struct dispd *dispd = userdata;
|
||||||
|
|
||||||
|
sd_event_exit(dispd->loop, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispd_shutdown(struct dispd *dispd)
|
||||||
|
{
|
||||||
|
uint64_t now;
|
||||||
|
int r = sd_event_now(dispd_get_loop(), CLOCK_MONOTONIC, &now);
|
||||||
|
if(0 > r) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_event_add_time(dispd_get_loop(),
|
||||||
|
NULL,
|
||||||
|
CLOCK_MONOTONIC,
|
||||||
|
now + 100 * 1000,
|
||||||
|
0,
|
||||||
|
dispd_handle_shutdown,
|
||||||
|
dispd);
|
||||||
|
if(0 <= r) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
sd_event_exit(dispd->loop, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispd_add_sink(struct dispd *dispd,
|
||||||
|
struct ctl_peer *p,
|
||||||
|
union wfd_sube *sube,
|
||||||
|
struct dispd_sink **out)
|
||||||
|
{
|
||||||
|
_dispd_sink_free_ struct dispd_sink *s = NULL;
|
||||||
|
int r = shl_htable_lookup_str(&dispd->sinks,
|
||||||
|
p->label,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
if(r) {
|
||||||
|
return -EEXIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_sink_new(&s, p, sube);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = shl_htable_insert_str(&dispd->sinks,
|
||||||
|
dispd_sink_to_htable(s),
|
||||||
|
NULL);
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
++dispd->n_sinks;
|
||||||
|
*out = s;
|
||||||
|
s = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispd_find_sink_by_label(struct dispd *dispd,
|
||||||
|
const char *label,
|
||||||
|
struct dispd_sink **out)
|
||||||
|
{
|
||||||
|
char **entry;
|
||||||
|
int r = shl_htable_lookup_str(&dispd->sinks, label, NULL, &entry);
|
||||||
|
if(r && out) {
|
||||||
|
*out = dispd_sink_from_htable(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispd_remove_sink_by_label(struct dispd *dispd,
|
||||||
|
const char *label,
|
||||||
|
struct dispd_sink **out)
|
||||||
|
{
|
||||||
|
char **entry;
|
||||||
|
int r = shl_htable_remove_str(&dispd->sinks, label, NULL, &entry);
|
||||||
|
if(!r) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
--dispd->n_sinks;
|
||||||
|
|
||||||
|
if(out) {
|
||||||
|
*out = dispd_sink_from_htable(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int dispd_alloc_session_id(struct dispd *dispd)
|
||||||
|
{
|
||||||
|
return ++dispd->id_pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispd_add_session(struct dispd *dispd, struct dispd_session *s)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(dispd);
|
||||||
|
assert(s && dispd_session_get_id(s));
|
||||||
|
assert(!dispd_find_session_by_id(dispd, dispd_session_get_id(s), NULL));
|
||||||
|
|
||||||
|
r = shl_htable_insert_uint(&dispd->sessions, dispd_session_to_htable(s));
|
||||||
|
if(0 > r) {
|
||||||
|
return log_ERR(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
++dispd->n_sessions;
|
||||||
|
|
||||||
|
dispd_fn_session_new(s);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispd_find_session_by_id(struct dispd *dispd,
|
||||||
|
unsigned int id,
|
||||||
|
struct dispd_session **out)
|
||||||
|
{
|
||||||
|
unsigned int *entry;
|
||||||
|
int r = shl_htable_lookup_uint(&dispd->sessions, id, &entry);
|
||||||
|
if(r && out) {
|
||||||
|
*out = dispd_session_from_htable(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispd_remove_session_by_id(struct dispd *dispd,
|
||||||
|
unsigned int id,
|
||||||
|
struct dispd_session **out)
|
||||||
|
{
|
||||||
|
unsigned int *entry;
|
||||||
|
struct dispd_session *s;
|
||||||
|
int r = shl_htable_remove_uint(&dispd->sessions, id, &entry);
|
||||||
|
if(!r) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
--dispd->n_sessions;
|
||||||
|
|
||||||
|
s = dispd_session_from_htable(entry);
|
||||||
|
dispd_fn_session_free(s);
|
||||||
|
if(out) {
|
||||||
|
*out = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispd_fetch_info(sd_event_source *s, void *userdata)
|
||||||
|
{
|
||||||
|
struct dispd *dispd = userdata;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
sd_event_source_unref(s);
|
||||||
|
|
||||||
|
r = ctl_wifi_fetch(dispd->wifi);
|
||||||
|
if(0 > r) {
|
||||||
|
log_warning("failed to fetch information about links and peers: %s",
|
||||||
|
strerror(errno));
|
||||||
|
sd_event_exit(dispd->loop, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispd_handle_signal(sd_event_source *s,
|
||||||
|
const struct signalfd_siginfo *ssi,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
siginfo_t siginfo;
|
||||||
|
struct dispd *dispd = userdata;
|
||||||
|
|
||||||
|
if(ssi->ssi_signo == SIGCHLD) {
|
||||||
|
r = waitid(P_PID, ssi->ssi_pid, &siginfo, WNOHANG | WEXITED);
|
||||||
|
if(0 > r) {
|
||||||
|
log_warning("failed to reaping child %d", ssi->ssi_pid);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log_info("child %d exit: %d",
|
||||||
|
ssi->ssi_pid,
|
||||||
|
siginfo.si_code);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sd_event_exit(dispd->loop, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dispd_init(struct dispd *dispd, sd_bus *bus)
|
||||||
|
{
|
||||||
|
int i, r;
|
||||||
|
const int signals[] = {
|
||||||
|
SIGINT, SIGHUP, SIGQUIT, SIGTERM, SIGCHLD,
|
||||||
|
};
|
||||||
|
struct ctl_wifi *wifi;
|
||||||
|
|
||||||
|
for(i = 0; i < SHL_ARRAY_LENGTH(signals); i ++) {
|
||||||
|
sigset_t mask;
|
||||||
|
sigemptyset(&mask);
|
||||||
|
sigaddset(&mask, signals[i]);
|
||||||
|
r = sigprocmask(SIG_BLOCK, &mask, NULL);
|
||||||
|
if(0 > r) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_event_add_signal(dispd->loop,
|
||||||
|
NULL,
|
||||||
|
signals[i],
|
||||||
|
dispd_handle_signal,
|
||||||
|
dispd);
|
||||||
|
if(0 > r) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r = ctl_wifi_new(&wifi, bus);
|
||||||
|
if(0 > r) {
|
||||||
|
log_vENOMEM();
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_event_add_defer(dispd->loop, NULL, dispd_fetch_info, dispd);
|
||||||
|
if(0 > r) {
|
||||||
|
log_vERRNO();
|
||||||
|
|
||||||
|
ctl_wifi_free(wifi);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispd->wifi = wifi;
|
||||||
|
|
||||||
|
end:
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ctl_fn_peer_new(struct ctl_peer *p)
|
||||||
|
{
|
||||||
|
struct dispd_sink *s;
|
||||||
|
union wfd_sube sube;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
log_debug("new peer %s (%s) shows up, wfd_subelems: '%s'",
|
||||||
|
p->label,
|
||||||
|
p->friendly_name,
|
||||||
|
p->wfd_subelements);
|
||||||
|
|
||||||
|
if(!p->wfd_subelements || !*p->wfd_subelements) {
|
||||||
|
log_info("peer %s has no wfd_subelems, ignore it", p->label);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = wfd_sube_parse(p->wfd_subelements, &sube);
|
||||||
|
if(0 > r) {
|
||||||
|
log_debug("peer %s has no valid subelement, ignore it", p->label);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(wfd_sube_device_is_sink(&sube)) {
|
||||||
|
r = dispd_add_sink(dispd_get(), p, &sube, &s);
|
||||||
|
if(0 > r) {
|
||||||
|
log_warning("failed to add sink (%s, '%s'): %s",
|
||||||
|
p->friendly_name,
|
||||||
|
p->p2p_mac,
|
||||||
|
strerror(errno));
|
||||||
|
return log_vERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_fn_sink_new(s);
|
||||||
|
if(0 > r) {
|
||||||
|
log_warning("failed to publish newly added sink (%s): %s",
|
||||||
|
dispd_sink_get_label(s),
|
||||||
|
strerror(errno));
|
||||||
|
return log_vERRNO();
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info("sink %s added", s->label);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(wfd_sube_device_is_source(&sube)) {
|
||||||
|
log_info("source %s ignired", p->label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ctl_fn_peer_free(struct ctl_peer *p)
|
||||||
|
{
|
||||||
|
struct dispd_sink *s;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = dispd_remove_sink_by_label(dispd, p->label, &s);
|
||||||
|
if(r) {
|
||||||
|
dispd_fn_sink_free(s);
|
||||||
|
log_info("sink %s removed", s->label);
|
||||||
|
dispd_sink_free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info("peer %s down", p->label);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ctl_fn_peer_provision_discovery(struct ctl_peer *p,
|
||||||
|
const char *prov,
|
||||||
|
const char *pin)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ctl_fn_peer_go_neg_request(struct ctl_peer *p,
|
||||||
|
const char *prov,
|
||||||
|
const char *pin)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ctl_fn_peer_formation_failure(struct ctl_peer *p, const char *reason)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ctl_fn_peer_connected(struct ctl_peer *p)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ctl_fn_peer_disconnected(struct ctl_peer *p)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ctl_fn_link_new(struct ctl_link *l)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ctl_fn_link_free(struct ctl_link *l)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void cli_fn_help()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
sd_event *event;
|
||||||
|
sd_bus *bus;
|
||||||
|
|
||||||
|
setlocale(LC_ALL, "");
|
||||||
|
setlocale(LC_TIME, "en_US.UTF-8");
|
||||||
|
|
||||||
|
if(getenv("LOG_LEVEL")) {
|
||||||
|
log_max_sev = log_parse_arg(getenv("LOG_LEVEL"));
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_event_default(&event);
|
||||||
|
if(0 > r) {
|
||||||
|
log_warning("can't create default event loop");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_event_set_watchdog(event, true);
|
||||||
|
if (0 > r) {
|
||||||
|
log_warning("unable to start automatic watchdog support: %s", strerror(errno));
|
||||||
|
goto unref_event;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_default_system(&bus);
|
||||||
|
if(0 > r) {
|
||||||
|
log_warning("unable to connect to system DBus: %s", strerror(errno));
|
||||||
|
goto disable_watchdog;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_attach_event(bus, event, 0);
|
||||||
|
if(0 > r) {
|
||||||
|
goto unref_bus;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_dbus_new(&dispd_dbus, event, bus);
|
||||||
|
if(0 > r) {
|
||||||
|
goto bus_detach_event;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_new(&dispd, event, bus);
|
||||||
|
if(0 > r) {
|
||||||
|
goto free_dispd_dbus;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dispd_dbus_expose(dispd_dbus);
|
||||||
|
if(0 > r) {
|
||||||
|
log_warning("unable to publish WFD service: %s", strerror(errno));
|
||||||
|
goto free_dispd;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_notify(false, "READY=1\n"
|
||||||
|
"STATUS=Running..");
|
||||||
|
if (0 > r) {
|
||||||
|
log_warning("unable to notify systemd that we are ready: %s", strerror(errno));
|
||||||
|
goto free_dispd_dbus;
|
||||||
|
}
|
||||||
|
|
||||||
|
sd_event_loop(event);
|
||||||
|
|
||||||
|
sd_notify(false, "STATUS=Exiting..");
|
||||||
|
|
||||||
|
free_dispd:
|
||||||
|
dispd_free(dispd);
|
||||||
|
free_dispd_dbus:
|
||||||
|
dispd_dbus_free(dispd_dbus);
|
||||||
|
bus_detach_event:
|
||||||
|
sd_bus_detach_event(bus);
|
||||||
|
unref_bus:
|
||||||
|
sd_bus_flush_close_unref(bus);
|
||||||
|
disable_watchdog:
|
||||||
|
sd_event_set_watchdog(event, false);
|
||||||
|
unref_event:
|
||||||
|
sd_event_unref(event);
|
||||||
|
end:
|
||||||
|
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
223
src/disp/dispd.h
Normal file
223
src/disp/dispd.h
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
/*
|
||||||
|
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||||
|
*
|
||||||
|
* MiracleCast 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) any later version.
|
||||||
|
*
|
||||||
|
* MiracleCast 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 MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <systemd/sd-event.h>
|
||||||
|
#include "shl_htable.h"
|
||||||
|
#include "ctl.h"
|
||||||
|
#include "wfd.h"
|
||||||
|
|
||||||
|
#ifndef DISP_DISP_H
|
||||||
|
#define DISP_DISP_H
|
||||||
|
|
||||||
|
/* dispd session */
|
||||||
|
#define dispd_session(s) ((struct dispd_session *) (s))
|
||||||
|
#define dispd_is_session(s) ( \
|
||||||
|
(s) && \
|
||||||
|
(DISPD_SESSION_DIR_OUT == dispd_session(s)->dir || \
|
||||||
|
DISPD_SESSION_DIR_IN == dispd_session(s)->dir) \
|
||||||
|
)
|
||||||
|
#define dispd_session_has_id(s) (0 < dispd_session_get_id(s))
|
||||||
|
#define dispd_is_out_session(s) (DISPD_SESSION_DIR_OUT == dispd_session_get_dir(s))
|
||||||
|
#define dispd_is_in_session(s) (DISPD_SESSION_DIR_IN == dispd_session_get_dir(s))
|
||||||
|
#define _dispd_session_unref_ _shl_cleanup_(dispd_session_unrefp)
|
||||||
|
|
||||||
|
struct dispd_sink;
|
||||||
|
struct dispd_session;
|
||||||
|
struct rtsp_dispatch_entry;
|
||||||
|
|
||||||
|
enum dispd_session_dir
|
||||||
|
{
|
||||||
|
DISPD_SESSION_DIR_OUT,
|
||||||
|
DISPD_SESSION_DIR_IN,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum dispd_session_state
|
||||||
|
{
|
||||||
|
DISPD_SESSION_STATE_NULL,
|
||||||
|
DISPD_SESSION_STATE_CONNECTING,
|
||||||
|
DISPD_SESSION_STATE_CAPS_EXCHANGING,
|
||||||
|
DISPD_SESSION_STATE_ESTABLISHED,
|
||||||
|
DISPD_SESSION_STATE_SETTING_UP,
|
||||||
|
DISPD_SESSION_STATE_PAUSED,
|
||||||
|
DISPD_SESSION_STATE_PLAYING,
|
||||||
|
DISPD_SESSION_STATE_TEARING_DOWN,
|
||||||
|
DISPD_SESSION_STATE_DESTROYED,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dispd_rectangle
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum dispd_display_server_type
|
||||||
|
{
|
||||||
|
DISPD_DISPLAY_SERVER_TYPE_UNKNOWN = 0,
|
||||||
|
DISPD_DISPLAY_SERVER_TYPE_X,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum dispd_audio_server_type
|
||||||
|
{
|
||||||
|
DISPD_AUDIO_SERVER_TYPE_UNKNOWN = 0,
|
||||||
|
DISPD_AUDIO_SERVER_TYPE_PULSE_AUDIO,
|
||||||
|
};
|
||||||
|
|
||||||
|
int dispd_out_session_new(struct dispd_session **out,
|
||||||
|
unsigned int id,
|
||||||
|
struct dispd_sink *sink);
|
||||||
|
struct dispd_session * _dispd_session_ref(struct dispd_session *s);
|
||||||
|
#define dispd_session_ref(s) ( \
|
||||||
|
log_debug("dispd_session_ref(%p): %d => %d", (s), *(int *) s, 1 + *(int *) s), \
|
||||||
|
_dispd_session_ref(s) \
|
||||||
|
)
|
||||||
|
void _dispd_session_unref(struct dispd_session *s);
|
||||||
|
#define dispd_session_unref(s) { \
|
||||||
|
log_debug("dispd_session_unref(%p): %d => %d", (s), *(int *) s, *(int *) s - 1); \
|
||||||
|
_dispd_session_unref(s); \
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispd_session_unrefp(struct dispd_session **s);
|
||||||
|
unsigned int * dispd_session_to_htable(struct dispd_session *s);
|
||||||
|
struct dispd_session * dispd_session_from_htable(unsigned int *e);
|
||||||
|
|
||||||
|
int dispd_session_start(struct dispd_session *s);
|
||||||
|
int dispd_session_resume(struct dispd_session *s);
|
||||||
|
int dispd_session_pause(struct dispd_session *s);
|
||||||
|
int dispd_session_teardown(struct dispd_session *s);
|
||||||
|
int dispd_session_destroy(struct dispd_session *s);
|
||||||
|
|
||||||
|
bool dispd_session_is_established(struct dispd_session *s);
|
||||||
|
unsigned int dispd_session_get_id(struct dispd_session *s);
|
||||||
|
const char * dispd_session_get_stream_url(struct dispd_session *s);
|
||||||
|
bool dispd_session_is_state(struct dispd_session *s, enum dispd_session_state state);
|
||||||
|
enum dispd_session_state dispd_session_get_state(struct dispd_session *s);
|
||||||
|
enum dispd_session_dir dispd_session_get_dir(struct dispd_session *s);
|
||||||
|
struct dispd_sink * dispd_out_session_get_sink(struct dispd_session *s);
|
||||||
|
|
||||||
|
enum dispd_display_server_type dispd_session_get_disp_type(struct dispd_session *s);
|
||||||
|
bool dispd_session_has_disp(struct dispd_session *s);
|
||||||
|
int dispd_session_set_disp_type(struct dispd_session *s, enum dispd_display_server_type);
|
||||||
|
const char * dispd_session_get_disp_name(struct dispd_session *s);
|
||||||
|
int dispd_session_set_disp_name(struct dispd_session *s, const char *disp_name);
|
||||||
|
const char * dispd_session_get_disp_params(struct dispd_session *s);
|
||||||
|
int dispd_session_set_disp_params(struct dispd_session *s, const char *disp_params);
|
||||||
|
const char * dispd_session_get_disp_auth(struct dispd_session *s);
|
||||||
|
int dispd_session_set_disp_auth(struct dispd_session *s, const char *disp_auth);
|
||||||
|
const struct dispd_rectangle * dispd_session_get_disp_dimension(struct dispd_session *s);
|
||||||
|
int dispd_session_set_disp_dimension(struct dispd_session *s, const struct dispd_rectangle *rect);
|
||||||
|
enum dispd_audio_server_type dispd_session_get_audio_type(struct dispd_session *s);
|
||||||
|
bool dispd_session_has_audio(struct dispd_session *s);
|
||||||
|
int dispd_session_set_audio_type(struct dispd_session *s, enum dispd_audio_server_type audio_type);
|
||||||
|
const char * dispd_session_get_audio_dev_name(struct dispd_session *s);
|
||||||
|
int dispd_session_set_audio_dev_name(struct dispd_session *s, const char *audio_dev_name);
|
||||||
|
const char * dispd_session_get_runtime_path(struct dispd_session *s);
|
||||||
|
int dispd_session_set_runtime_path(struct dispd_session *s,
|
||||||
|
const char *runtime_path);
|
||||||
|
uid_t dispd_session_get_client_uid(struct dispd_session *s);
|
||||||
|
int dispd_session_set_client_uid(struct dispd_session *s, uid_t uid);
|
||||||
|
uid_t dispd_session_get_client_gid(struct dispd_session *s);
|
||||||
|
int dispd_session_set_client_gid(struct dispd_session *s, uid_t gid);
|
||||||
|
pid_t dispd_session_get_client_pid(struct dispd_session *s);
|
||||||
|
int dispd_session_set_client_pid(struct dispd_session *s, pid_t pid);
|
||||||
|
|
||||||
|
/* dispd sink */
|
||||||
|
#define _dispd_sink_free_ _shl_cleanup_(dispd_sink_freep)
|
||||||
|
#define dispd_sink_to_htable(s) (&(s)->label)
|
||||||
|
#define dispd_sink_from_htable(s) shl_htable_entry(s, struct dispd_sink, label)
|
||||||
|
|
||||||
|
struct dispd_sink
|
||||||
|
{
|
||||||
|
struct ctl_peer *peer;
|
||||||
|
union wfd_sube dev_info;
|
||||||
|
char *label;
|
||||||
|
struct dispd_session *session;
|
||||||
|
|
||||||
|
sd_event_source *session_cleanup_source;
|
||||||
|
};
|
||||||
|
|
||||||
|
int dispd_sink_new(struct dispd_sink **out,
|
||||||
|
struct ctl_peer *peer,
|
||||||
|
union wfd_sube *sube);
|
||||||
|
void dispd_sink_free(struct dispd_sink *sink);
|
||||||
|
static inline void dispd_sink_freep(struct dispd_sink **s)
|
||||||
|
{
|
||||||
|
dispd_sink_free(*s);
|
||||||
|
*s = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispd_sink_create_session(struct dispd_sink *sink, struct dispd_session **out);
|
||||||
|
|
||||||
|
const char * dispd_sink_get_label(struct dispd_sink *sink);
|
||||||
|
const union wfd_sube * dispd_sink_get_dev_info(struct dispd_sink *sink);
|
||||||
|
bool dispd_sink_is_session_started(struct dispd_sink *sink);
|
||||||
|
|
||||||
|
void dispd_sink_handle_session_ended(struct dispd_sink *sink);
|
||||||
|
|
||||||
|
/* wfd handling */
|
||||||
|
#define dispd_foreach_sink(_i, _w) \
|
||||||
|
SHL_HTABLE_FOREACH_MACRO(_i, \
|
||||||
|
&(_w)->sinks, \
|
||||||
|
dispd_sink_from_htable)
|
||||||
|
#define dispd_foreach_session(_i, _w) \
|
||||||
|
SHL_HTABLE_FOREACH_MACRO(_i, \
|
||||||
|
&(_w)->sessions, \
|
||||||
|
dispd_session_from_htable)
|
||||||
|
|
||||||
|
struct dispd
|
||||||
|
{
|
||||||
|
sd_event *loop;
|
||||||
|
struct ctl_wifi *wifi;
|
||||||
|
struct shl_htable sinks;
|
||||||
|
size_t n_sinks;
|
||||||
|
struct shl_htable sessions;
|
||||||
|
size_t n_sessions;
|
||||||
|
unsigned int id_pool;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dispd * dispd_get();
|
||||||
|
void dispd_shutdown(struct dispd *wfd);
|
||||||
|
|
||||||
|
static inline struct sd_event * dispd_get_loop()
|
||||||
|
{
|
||||||
|
return dispd_get()->loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispd_find_sink_by_label(struct dispd *wfd,
|
||||||
|
const char *label,
|
||||||
|
struct dispd_sink **out);
|
||||||
|
int dispd_add_session(struct dispd *wfd, struct dispd_session *s);
|
||||||
|
int dispd_find_session_by_id(struct dispd *wfd,
|
||||||
|
unsigned int id,
|
||||||
|
struct dispd_session **out);
|
||||||
|
int dispd_remove_session_by_id(struct dispd *wfd,
|
||||||
|
unsigned int id,
|
||||||
|
struct dispd_session **out);
|
||||||
|
unsigned int dispd_alloc_session_id(struct dispd *wfd);
|
||||||
|
|
||||||
|
int dispd_fn_session_new(struct dispd_session *s);
|
||||||
|
int dispd_fn_session_free(struct dispd_session *s);
|
||||||
|
int dispd_fn_out_session_ended(struct dispd_session *s);
|
||||||
|
|
||||||
|
int dispd_fn_sink_new(struct dispd_sink *s);
|
||||||
|
int dispd_fn_sink_free(struct dispd_sink *s);
|
||||||
|
int dispd_fn_sink_detach(struct dispd_sink *s);
|
||||||
|
|
||||||
|
#endif /* DISP_DISP_H */
|
25
src/disp/meson.build
Normal file
25
src/disp/meson.build
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
inc = include_directories('../..', '../ctl',)
|
||||||
|
gst1 = dependency('gstreamer-1.0')
|
||||||
|
gst1_base = dependency('gstreamer-base-1.0')
|
||||||
|
deps = [libsystemd, libmiracle_shared_dep, gst1, gst1_base]
|
||||||
|
if readline.found()
|
||||||
|
deps += [readline]
|
||||||
|
endif
|
||||||
|
|
||||||
|
miracle_dispd_src = ['../ctl/ctl-cli.c',
|
||||||
|
'../ctl/ctl-wifi.c',
|
||||||
|
'../ctl/wfd.c',
|
||||||
|
'dispd.c',
|
||||||
|
'dispd-sink.c',
|
||||||
|
'dispd-dbus.c',
|
||||||
|
'dispd-session.c',
|
||||||
|
'dispd-out-session.c',
|
||||||
|
'dispd-arg.c',
|
||||||
|
'dispd-encoder.c'
|
||||||
|
]
|
||||||
|
executable('miracle-dispd',
|
||||||
|
miracle_dispd_src,
|
||||||
|
install: true,
|
||||||
|
include_directories: inc,
|
||||||
|
dependencies: deps
|
||||||
|
)
|
|
@ -1,8 +1,11 @@
|
||||||
|
inc_shared = include_directories('shared')
|
||||||
|
|
||||||
subdir('shared')
|
subdir('shared')
|
||||||
subdir('wifi')
|
subdir('wifi')
|
||||||
subdir('dhcp')
|
subdir('dhcp')
|
||||||
subdir('ctl')
|
subdir('ctl')
|
||||||
subdir('uibc')
|
subdir('uibc')
|
||||||
|
subdir('disp')
|
||||||
|
|
||||||
executable('miracled', 'miracled.c',
|
executable('miracled', 'miracled.c',
|
||||||
dependencies: [libmiracle_shared_dep, m],
|
dependencies: [libmiracle_shared_dep, m],
|
||||||
|
|
|
@ -231,6 +231,11 @@ extern const char *LOG_SUBSYSTEM;
|
||||||
#define log_vEPIPE() \
|
#define log_vEPIPE() \
|
||||||
((void)log_EPIPE())
|
((void)log_EPIPE())
|
||||||
|
|
||||||
|
#define log_EPROTO() \
|
||||||
|
(log_error("protocol error"), -EPROTO)
|
||||||
|
#define log_vEPROTO() \
|
||||||
|
((void)log_EPROTO())
|
||||||
|
|
||||||
#define log_ERRNO() \
|
#define log_ERRNO() \
|
||||||
(log_error("syscall failed (%d): %m", errno), -errno)
|
(log_error("syscall failed (%d): %m", errno), -errno)
|
||||||
#define log_vERRNO() \
|
#define log_vERRNO() \
|
||||||
|
@ -246,4 +251,13 @@ extern const char *LOG_SUBSYSTEM;
|
||||||
#define log_vEUNMANAGED() \
|
#define log_vEUNMANAGED() \
|
||||||
((void)log_EUNMANAGED())
|
((void)log_EUNMANAGED())
|
||||||
|
|
||||||
|
#define assert_ret(c) \
|
||||||
|
if(!(c)) return (log_error("assertion " #c " failed"), -EINVAL)
|
||||||
|
|
||||||
|
#define assert_retv(c, v) \
|
||||||
|
if(!(c)) return (log_error("assertion " #c " failed"), (v))
|
||||||
|
|
||||||
|
#define assert_vret(c) \
|
||||||
|
if(!(c)) { log_error("assertion " #c " failed"); return; }
|
||||||
|
|
||||||
#endif /* SHL_LOG_H */
|
#endif /* SHL_LOG_H */
|
||||||
|
|
|
@ -57,6 +57,11 @@ static inline void cleanup_sd_bus_message(sd_bus_message **ptr)
|
||||||
sd_bus_message_unref(*ptr);
|
sd_bus_message_unref(*ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void cleanup_sd_bus_creds(sd_bus_creds **ptr)
|
||||||
|
{
|
||||||
|
sd_bus_creds_unref(*ptr);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void cleanup_udev_device(struct udev_device **ptr)
|
static inline void cleanup_udev_device(struct udev_device **ptr)
|
||||||
{
|
{
|
||||||
udev_device_unref(*ptr);
|
udev_device_unref(*ptr);
|
||||||
|
@ -71,6 +76,7 @@ static inline void cleanup_udev_enumerate(struct udev_enumerate **ptr)
|
||||||
#define _sd_bus_error_free_ _shl_cleanup_(sd_bus_error_free)
|
#define _sd_bus_error_free_ _shl_cleanup_(sd_bus_error_free)
|
||||||
#define _cleanup_sd_bus_message_ _shl_cleanup_(cleanup_sd_bus_message)
|
#define _cleanup_sd_bus_message_ _shl_cleanup_(cleanup_sd_bus_message)
|
||||||
#define _sd_bus_message_unref_ _shl_cleanup_(cleanup_sd_bus_message)
|
#define _sd_bus_message_unref_ _shl_cleanup_(cleanup_sd_bus_message)
|
||||||
|
#define _sd_bus_creds_unref_ _shl_cleanup_(cleanup_sd_bus_creds)
|
||||||
#define _cleanup_udev_device_ _shl_cleanup_(cleanup_udev_device)
|
#define _cleanup_udev_device_ _shl_cleanup_(cleanup_udev_device)
|
||||||
#define _cleanup_udev_enumerate_ _shl_cleanup_(cleanup_udev_enumerate)
|
#define _cleanup_udev_enumerate_ _shl_cleanup_(cleanup_udev_enumerate)
|
||||||
|
|
||||||
|
|
|
@ -261,12 +261,12 @@ static const sd_bus_vtable peer_dbus_vtable[] = {
|
||||||
"ss",
|
"ss",
|
||||||
NULL,
|
NULL,
|
||||||
peer_dbus_connect,
|
peer_dbus_connect,
|
||||||
0),
|
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
SD_BUS_METHOD("Disconnect",
|
SD_BUS_METHOD("Disconnect",
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
peer_dbus_disconnect,
|
peer_dbus_disconnect,
|
||||||
0),
|
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
SD_BUS_PROPERTY("Link",
|
SD_BUS_PROPERTY("Link",
|
||||||
"o",
|
"o",
|
||||||
peer_dbus_get_link,
|
peer_dbus_get_link,
|
||||||
|
@ -513,6 +513,24 @@ static int link_dbus_get_interface_index(sd_bus *bus,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int link_dbus_get_mac_addr(sd_bus *bus,
|
||||||
|
const char *path,
|
||||||
|
const char *interface,
|
||||||
|
const char *property,
|
||||||
|
sd_bus_message *reply,
|
||||||
|
void *data,
|
||||||
|
sd_bus_error *err)
|
||||||
|
{
|
||||||
|
struct link *l = data;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = sd_bus_message_append_basic(reply, 's', l->mac_addr);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int link_dbus_get_interface_name(sd_bus *bus,
|
static int link_dbus_get_interface_name(sd_bus *bus,
|
||||||
const char *path,
|
const char *path,
|
||||||
const char *interface,
|
const char *interface,
|
||||||
|
@ -589,22 +607,46 @@ static int link_dbus_get_managed(sd_bus *bus,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int link_dbus_set_managed(sd_bus *bus,
|
static int link_dbus_manage(sd_bus_message *msg,
|
||||||
|
void *data,
|
||||||
|
sd_bus_error *err)
|
||||||
|
{
|
||||||
|
struct link *l = data;
|
||||||
|
int r = link_manage(l, true);
|
||||||
|
if(r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return sd_bus_reply_method_return(msg, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int link_dbus_unmanage(sd_bus_message *msg,
|
||||||
|
void *data,
|
||||||
|
sd_bus_error *err)
|
||||||
|
{
|
||||||
|
struct link *l = data;
|
||||||
|
int r = link_manage(l, false);
|
||||||
|
if(r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return sd_bus_reply_method_return(msg, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int link_dbus_get_p2p_state(sd_bus *bus,
|
||||||
const char *path,
|
const char *path,
|
||||||
const char *interface,
|
const char *interface,
|
||||||
const char *property,
|
const char *property,
|
||||||
sd_bus_message *value,
|
sd_bus_message *reply,
|
||||||
void *data,
|
void *data,
|
||||||
sd_bus_error *err)
|
sd_bus_error *err)
|
||||||
{
|
{
|
||||||
struct link *l = data;
|
struct link *l = data;
|
||||||
int val, r;
|
int r;
|
||||||
|
|
||||||
r = sd_bus_message_read(value, "b", &val);
|
r = sd_bus_message_append(reply, "i", link_get_p2p_state(l));
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
return link_set_managed(l, val);
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int link_dbus_get_p2p_scanning(sd_bus *bus,
|
static int link_dbus_get_p2p_scanning(sd_bus *bus,
|
||||||
|
@ -682,11 +724,26 @@ static int link_dbus_set_wfd_subelements(sd_bus *bus,
|
||||||
|
|
||||||
static const sd_bus_vtable link_dbus_vtable[] = {
|
static const sd_bus_vtable link_dbus_vtable[] = {
|
||||||
SD_BUS_VTABLE_START(0),
|
SD_BUS_VTABLE_START(0),
|
||||||
|
SD_BUS_METHOD("Manage",
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
link_dbus_manage,
|
||||||
|
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("Unmanage",
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
link_dbus_unmanage,
|
||||||
|
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
SD_BUS_PROPERTY("InterfaceIndex",
|
SD_BUS_PROPERTY("InterfaceIndex",
|
||||||
"u",
|
"u",
|
||||||
link_dbus_get_interface_index,
|
link_dbus_get_interface_index,
|
||||||
0,
|
0,
|
||||||
SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
|
SD_BUS_PROPERTY("MACAddress",
|
||||||
|
"s",
|
||||||
|
link_dbus_get_mac_addr,
|
||||||
|
0,
|
||||||
|
SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
SD_BUS_PROPERTY("InterfaceName",
|
SD_BUS_PROPERTY("InterfaceName",
|
||||||
"s",
|
"s",
|
||||||
link_dbus_get_interface_name,
|
link_dbus_get_interface_name,
|
||||||
|
@ -697,11 +754,15 @@ static const sd_bus_vtable link_dbus_vtable[] = {
|
||||||
link_dbus_get_friendly_name,
|
link_dbus_get_friendly_name,
|
||||||
link_dbus_set_friendly_name,
|
link_dbus_set_friendly_name,
|
||||||
0,
|
0,
|
||||||
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE | SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
SD_BUS_WRITABLE_PROPERTY("Managed",
|
SD_BUS_PROPERTY("Managed",
|
||||||
"b",
|
"b",
|
||||||
link_dbus_get_managed,
|
link_dbus_get_managed,
|
||||||
link_dbus_set_managed,
|
0,
|
||||||
|
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||||
|
SD_BUS_PROPERTY("P2PState",
|
||||||
|
"i",
|
||||||
|
link_dbus_get_p2p_state,
|
||||||
0,
|
0,
|
||||||
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||||
SD_BUS_WRITABLE_PROPERTY("P2PScanning",
|
SD_BUS_WRITABLE_PROPERTY("P2PScanning",
|
||||||
|
@ -709,13 +770,13 @@ static const sd_bus_vtable link_dbus_vtable[] = {
|
||||||
link_dbus_get_p2p_scanning,
|
link_dbus_get_p2p_scanning,
|
||||||
link_dbus_set_p2p_scanning,
|
link_dbus_set_p2p_scanning,
|
||||||
0,
|
0,
|
||||||
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE | SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
SD_BUS_WRITABLE_PROPERTY("WfdSubelements",
|
SD_BUS_WRITABLE_PROPERTY("WfdSubelements",
|
||||||
"s",
|
"s",
|
||||||
link_dbus_get_wfd_subelements,
|
link_dbus_get_wfd_subelements,
|
||||||
link_dbus_set_wfd_subelements,
|
link_dbus_set_wfd_subelements,
|
||||||
0,
|
0,
|
||||||
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE | SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
SD_BUS_VTABLE_END
|
SD_BUS_VTABLE_END
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -829,14 +890,17 @@ static int manager_dbus_enumerate(sd_bus *bus,
|
||||||
struct peer *p;
|
struct peer *p;
|
||||||
size_t i, peer_cnt;
|
size_t i, peer_cnt;
|
||||||
char **nodes, *node;
|
char **nodes, *node;
|
||||||
int r;
|
|
||||||
|
if(strcmp("/org/freedesktop/miracle/wifi", path)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
peer_cnt = 0;
|
peer_cnt = 0;
|
||||||
MANAGER_FOREACH_LINK(l, m)
|
MANAGER_FOREACH_LINK(l, m)
|
||||||
if (l->public)
|
if (l->public)
|
||||||
peer_cnt += l->peer_cnt;
|
peer_cnt += l->peer_cnt;
|
||||||
|
|
||||||
nodes = malloc(sizeof(*nodes) * (m->link_cnt + peer_cnt + 2));
|
nodes = malloc(sizeof(*nodes) * (m->link_cnt + peer_cnt + 1));
|
||||||
if (!nodes)
|
if (!nodes)
|
||||||
return log_ENOMEM();
|
return log_ENOMEM();
|
||||||
|
|
||||||
|
@ -875,13 +939,6 @@ static int manager_dbus_enumerate(sd_bus *bus,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node = strdup("/org/freedesktop/miracle/wifi");
|
|
||||||
if (!node) {
|
|
||||||
r = log_ENOMEM();
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes[i++] = node;
|
|
||||||
nodes[i] = NULL;
|
nodes[i] = NULL;
|
||||||
*out = nodes;
|
*out = nodes;
|
||||||
|
|
||||||
|
@ -891,7 +948,7 @@ error:
|
||||||
while (i--)
|
while (i--)
|
||||||
free(nodes[i]);
|
free(nodes[i]);
|
||||||
free(nodes);
|
free(nodes);
|
||||||
return r;
|
return log_ENOMEM();
|
||||||
}
|
}
|
||||||
|
|
||||||
int manager_dbus_connect(struct manager *m)
|
int manager_dbus_connect(struct manager *m)
|
||||||
|
|
|
@ -57,6 +57,7 @@ struct peer *link_find_peer_by_label(struct link *l, const char *label)
|
||||||
int link_new(struct manager *m,
|
int link_new(struct manager *m,
|
||||||
unsigned int ifindex,
|
unsigned int ifindex,
|
||||||
const char *ifname,
|
const char *ifname,
|
||||||
|
const char *mac_addr,
|
||||||
struct link **out)
|
struct link **out)
|
||||||
{
|
{
|
||||||
struct link *l;
|
struct link *l;
|
||||||
|
@ -84,6 +85,12 @@ int link_new(struct manager *m,
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
l->mac_addr = strdup(mac_addr);
|
||||||
|
if (!l->mac_addr) {
|
||||||
|
r = log_ENOMEM();
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
r = supplicant_new(l, &l->s);
|
r = supplicant_new(l, &l->s);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -116,7 +123,10 @@ void link_free(struct link *l)
|
||||||
|
|
||||||
log_debug("free link: %s (%u)", l->ifname, l->ifindex);
|
log_debug("free link: %s (%u)", l->ifname, l->ifindex);
|
||||||
|
|
||||||
link_set_managed(l, false);
|
link_manage(l, false);
|
||||||
|
|
||||||
|
link_dbus_removed(l);
|
||||||
|
l->public = false;
|
||||||
|
|
||||||
link_dbus_removed(l);
|
link_dbus_removed(l);
|
||||||
l->public = false;
|
l->public = false;
|
||||||
|
@ -128,9 +138,10 @@ void link_free(struct link *l)
|
||||||
|
|
||||||
supplicant_free(l->s);
|
supplicant_free(l->s);
|
||||||
|
|
||||||
/* link_set_managed(l, false) already removed all peers */
|
/* link_manage(l, false) already removed all peers */
|
||||||
shl_htable_clear_str(&l->peers, NULL, NULL);
|
shl_htable_clear_str(&l->peers, NULL, NULL);
|
||||||
|
|
||||||
|
free(l->mac_addr);
|
||||||
free(l->wfd_subelements);
|
free(l->wfd_subelements);
|
||||||
free(l->friendly_name);
|
free(l->friendly_name);
|
||||||
free(l->ifname);
|
free(l->ifname);
|
||||||
|
@ -181,12 +192,18 @@ int link_set_ip_binary(struct link *l, const char *ip_binary)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int link_get_p2p_state(struct link *l)
|
||||||
|
{
|
||||||
|
return l->p2p_state;
|
||||||
|
}
|
||||||
|
|
||||||
bool link_get_managed(struct link *l)
|
bool link_get_managed(struct link *l)
|
||||||
{
|
{
|
||||||
return l->managed;
|
return l->managed;
|
||||||
}
|
}
|
||||||
|
|
||||||
int link_set_managed(struct link *l, bool set)
|
int link_manage(struct link *l, bool set)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
@ -196,22 +213,33 @@ int link_set_managed(struct link *l, bool set)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (set) {
|
if (set) {
|
||||||
log_info("manage link %s", l->ifname);
|
|
||||||
|
|
||||||
r = supplicant_start(l->s);
|
r = supplicant_start(l->s);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_error("cannot start supplicant on %s", l->ifname);
|
log_error("cannot start supplicant on %s", l->ifname);
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
log_info("acquiring link ownership %s", l->ifname);
|
||||||
} else {
|
} else {
|
||||||
log_info("link %s no longer managed", l->ifname);
|
log_info("droping link ownership %s", l->ifname);
|
||||||
supplicant_stop(l->s);
|
supplicant_stop(l->s);
|
||||||
}
|
}
|
||||||
|
|
||||||
l->managed = set;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void link_supplicant_p2p_state_known(struct link *l, int state)
|
||||||
|
{
|
||||||
|
if (!l)
|
||||||
|
return log_vEINVAL();
|
||||||
|
if (l->p2p_state == state)
|
||||||
|
return;
|
||||||
|
if(-1 > state || 1 < state)
|
||||||
|
return log_vEINVAL();
|
||||||
|
|
||||||
|
l->p2p_state = state;
|
||||||
|
link_dbus_properties_changed(l, "P2PState", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
int link_renamed(struct link *l, const char *ifname)
|
int link_renamed(struct link *l, const char *ifname)
|
||||||
{
|
{
|
||||||
char *t;
|
char *t;
|
||||||
|
@ -244,6 +272,9 @@ int link_set_friendly_name(struct link *l, const char *name)
|
||||||
if (!l || !name || !*name)
|
if (!l || !name || !*name)
|
||||||
return log_EINVAL();
|
return log_EINVAL();
|
||||||
|
|
||||||
|
if (!l->managed)
|
||||||
|
return log_EUNMANAGED();
|
||||||
|
|
||||||
t = strdup(name);
|
t = strdup(name);
|
||||||
if (!t)
|
if (!t)
|
||||||
return log_ENOMEM();
|
return log_ENOMEM();
|
||||||
|
@ -339,18 +370,36 @@ bool link_get_p2p_scanning(struct link *l)
|
||||||
return supplicant_p2p_scanning(l->s);
|
return supplicant_p2p_scanning(l->s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *link_get_mac_addr(struct link *l)
|
||||||
|
{
|
||||||
|
if (!l)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return l->mac_addr;
|
||||||
|
}
|
||||||
|
|
||||||
void link_supplicant_started(struct link *l)
|
void link_supplicant_started(struct link *l)
|
||||||
{
|
{
|
||||||
|
if(l && !l->managed) {
|
||||||
|
l->managed = true;
|
||||||
|
link_dbus_properties_changed(l, "Managed", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
if (!l || l->public)
|
if (!l || l->public)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (l->m->friendly_name && l->managed)
|
if (l->m->friendly_name && l->managed)
|
||||||
link_set_friendly_name(l, l->m->friendly_name);
|
link_set_friendly_name(l, l->m->friendly_name);
|
||||||
log_info("link %s managed", l->ifname);
|
log_info("link %s managed", l->ifname);
|
||||||
}
|
}
|
||||||
|
|
||||||
void link_supplicant_stopped(struct link *l)
|
void link_supplicant_stopped(struct link *l)
|
||||||
{
|
{
|
||||||
|
if(l && l->managed) {
|
||||||
|
l->managed = false;
|
||||||
|
link_dbus_properties_changed(l, "Managed", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
if (!l || !l->public)
|
if (!l || !l->public)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -218,8 +218,10 @@ static void supplicant_group_free(struct supplicant_group *g)
|
||||||
}
|
}
|
||||||
|
|
||||||
LINK_FOREACH_PEER(p, g->s->l)
|
LINK_FOREACH_PEER(p, g->s->l)
|
||||||
if (p->sp->g == g)
|
if (p->sp->g == g) {
|
||||||
supplicant_peer_drop_group(p->sp);
|
supplicant_peer_drop_group(p->sp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
shl_dlist_unlink(&g->list);
|
shl_dlist_unlink(&g->list);
|
||||||
|
|
||||||
|
@ -901,7 +903,13 @@ static void supplicant_parse_peer(struct supplicant *s,
|
||||||
* parse it we _definitely_ have to provide proper data. */
|
* parse it we _definitely_ have to provide proper data. */
|
||||||
r = wpas_message_dict_read(m, "wfd_dev_info", 's', &val);
|
r = wpas_message_dict_read(m, "wfd_dev_info", 's', &val);
|
||||||
if (r >= 0) {
|
if (r >= 0) {
|
||||||
t = strdup(val);
|
/* remove "0x" from the start of wfd_dev_info and prepend
|
||||||
|
* subelement ID and lenght to it to make it compliant
|
||||||
|
* with the format of wfd_subelems */
|
||||||
|
char buf[19] = "000006";
|
||||||
|
t = strdup(strncmp("0x", val, 2)
|
||||||
|
? val
|
||||||
|
: strncat(buf, val + 2, sizeof(buf) - 7));
|
||||||
if (!t) {
|
if (!t) {
|
||||||
log_vENOMEM();
|
log_vENOMEM();
|
||||||
} else {
|
} else {
|
||||||
|
@ -1538,10 +1546,12 @@ static void supplicant_try_ready(struct supplicant *s)
|
||||||
s->has_wfd = false;
|
s->has_wfd = false;
|
||||||
|
|
||||||
s->running = true;
|
s->running = true;
|
||||||
link_supplicant_started(s->l);
|
|
||||||
|
|
||||||
LINK_FOREACH_PEER(p, s->l)
|
LINK_FOREACH_PEER(p, s->l)
|
||||||
peer_supplicant_started(p);
|
peer_supplicant_started(p);
|
||||||
|
|
||||||
|
link_supplicant_started(s->l);
|
||||||
|
link_supplicant_p2p_state_known(s->l, s->has_p2p ? 1 : -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int supplicant_p2p_set_disallow_freq_fn(struct wpas *w,
|
static int supplicant_p2p_set_disallow_freq_fn(struct wpas *w,
|
||||||
|
@ -1716,8 +1726,10 @@ static int supplicant_status_fn(struct wpas *w,
|
||||||
|
|
||||||
if (!p2p_state) {
|
if (!p2p_state) {
|
||||||
log_warning("wpa_supplicant or driver does not support P2P");
|
log_warning("wpa_supplicant or driver does not support P2P");
|
||||||
|
link_supplicant_p2p_state_known(s->l, -1);
|
||||||
} else if (!strcmp(p2p_state, "DISABLED")) {
|
} else if (!strcmp(p2p_state, "DISABLED")) {
|
||||||
log_warning("P2P support disabled on given interface");
|
log_warning("P2P support disabled on given interface");
|
||||||
|
link_supplicant_p2p_state_known(s->l, -1);
|
||||||
} else {
|
} else {
|
||||||
s->has_p2p = true;
|
s->has_p2p = true;
|
||||||
|
|
||||||
|
@ -1743,6 +1755,8 @@ static int supplicant_status_fn(struct wpas *w,
|
||||||
NULL,
|
NULL,
|
||||||
0,
|
0,
|
||||||
NULL);
|
NULL);
|
||||||
|
wpas_message_unref(m);
|
||||||
|
m = NULL;
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_vERR(r);
|
log_vERR(r);
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -1771,6 +1785,8 @@ static int supplicant_status_fn(struct wpas *w,
|
||||||
s,
|
s,
|
||||||
0,
|
0,
|
||||||
NULL);
|
NULL);
|
||||||
|
wpas_message_unref(m);
|
||||||
|
m = NULL;
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_vERR(r);
|
log_vERR(r);
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -1799,6 +1815,8 @@ static int supplicant_status_fn(struct wpas *w,
|
||||||
s,
|
s,
|
||||||
0,
|
0,
|
||||||
NULL);
|
NULL);
|
||||||
|
wpas_message_unref(m);
|
||||||
|
m = NULL;
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_vERR(r);
|
log_vERR(r);
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -1915,6 +1933,7 @@ static void supplicant_stopped(struct supplicant *s)
|
||||||
|
|
||||||
if (s->running) {
|
if (s->running) {
|
||||||
s->running = false;
|
s->running = false;
|
||||||
|
link_supplicant_p2p_state_known(s->l, 0);
|
||||||
link_supplicant_stopped(s->l);
|
link_supplicant_stopped(s->l);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2452,45 +2471,45 @@ static void supplicant_run(struct supplicant *s, const char *binary)
|
||||||
|
|
||||||
static int supplicant_find(char **binary)
|
static int supplicant_find(char **binary)
|
||||||
{
|
{
|
||||||
_shl_free_ char *path = getenv("PATH");
|
_shl_free_ char *path = getenv("PATH");
|
||||||
if(!path) {
|
if(!path) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
path = strdup(path);
|
path = strdup(path);
|
||||||
if(!path) {
|
if(!path) {
|
||||||
return log_ENOMEM();
|
return log_ENOMEM();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct stat bin_stat;
|
struct stat bin_stat;
|
||||||
char *curr = path, *next;
|
char *curr = path, *next;
|
||||||
while(1) {
|
while(1) {
|
||||||
curr = strtok_r(curr, ":", &next);
|
curr = strtok_r(curr, ":", &next);
|
||||||
if(!curr) {
|
if(!curr) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_shl_free_ char *bin = shl_strcat(curr, "/wpa_supplicant");
|
_shl_free_ char *bin = shl_strcat(curr, "/wpa_supplicant");
|
||||||
if (!bin)
|
if (!bin)
|
||||||
return log_ENOMEM();
|
return log_ENOMEM();
|
||||||
|
|
||||||
if(stat(bin, &bin_stat) < 0) {
|
if(stat(bin, &bin_stat) < 0) {
|
||||||
if(ENOENT == errno || ENOTDIR == errno) {
|
if(ENOENT == errno || ENOTDIR == errno) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
return log_ERRNO();
|
return log_ERRNO();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!access(bin, X_OK)) {
|
if (!access(bin, X_OK)) {
|
||||||
*binary = strdup(bin);
|
*binary = strdup(bin);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
end:
|
end:
|
||||||
curr = NULL;
|
curr = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int supplicant_spawn(struct supplicant *s)
|
static int supplicant_spawn(struct supplicant *s)
|
||||||
|
@ -2506,16 +2525,16 @@ static int supplicant_spawn(struct supplicant *s)
|
||||||
|
|
||||||
log_debug("spawn supplicant of %s", s->l->ifname);
|
log_debug("spawn supplicant of %s", s->l->ifname);
|
||||||
|
|
||||||
if (supplicant_find(&binary) < 0) {
|
if (supplicant_find(&binary) < 0) {
|
||||||
if (binary != NULL) {
|
if (binary != NULL) {
|
||||||
log_error("execution of wpas (%s) not possible: %m", binary);
|
log_error("execution of wpas (%s) not possible: %m", binary);
|
||||||
} else {
|
} else {
|
||||||
log_error("execution of wpas not possible: %m");
|
log_error("execution of wpas not possible: %m");
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
log_info("wpa_supplicant found: %s", binary);
|
log_info("wpa_supplicant found: %s", binary);
|
||||||
|
|
||||||
pid = fork();
|
pid = fork();
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
|
|
|
@ -86,6 +86,8 @@ static void manager_add_udev_link(struct manager *m,
|
||||||
struct link *l;
|
struct link *l;
|
||||||
unsigned int ifindex;
|
unsigned int ifindex;
|
||||||
const char *ifname;
|
const char *ifname;
|
||||||
|
const char *mac_addr;
|
||||||
|
char buf[18];
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
ifindex = ifindex_from_udev_device(d);
|
ifindex = ifindex_from_udev_device(d);
|
||||||
|
@ -104,7 +106,11 @@ static void manager_add_udev_link(struct manager *m,
|
||||||
if (shl_startswith(ifname, "p2p-"))
|
if (shl_startswith(ifname, "p2p-"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
r = link_new(m, ifindex, ifname, &l);
|
mac_addr = udev_device_get_property_value(d, "ID_NET_NAME_MAC");
|
||||||
|
mac_addr = mac_addr + strlen(mac_addr) - 12;
|
||||||
|
snprintf(buf, sizeof(buf), "%.2s:%.2s:%.2s:%.2s:%.2s:%.2s", mac_addr, mac_addr + 2, mac_addr + 4, mac_addr + 6, mac_addr + 8, mac_addr + 10);
|
||||||
|
|
||||||
|
r = link_new(m, ifindex, ifname, buf, &l);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -124,7 +130,7 @@ static void manager_add_udev_link(struct manager *m,
|
||||||
bool managed = (!interface_name || !strcmp(interface_name, ifname)) && !lazy_managed;
|
bool managed = (!interface_name || !strcmp(interface_name, ifname)) && !lazy_managed;
|
||||||
#endif
|
#endif
|
||||||
if (managed) {
|
if (managed) {
|
||||||
link_set_managed(l, true);
|
link_manage(l, true);
|
||||||
} else {
|
} else {
|
||||||
log_debug("ignored device: %s", ifname);
|
log_debug("ignored device: %s", ifname);
|
||||||
}
|
}
|
||||||
|
@ -164,12 +170,12 @@ static int manager_udev_fn(sd_event_source *source,
|
||||||
|
|
||||||
#ifdef RELY_UDEV
|
#ifdef RELY_UDEV
|
||||||
if (udev_device_has_tag(d, "miracle") && !lazy_managed)
|
if (udev_device_has_tag(d, "miracle") && !lazy_managed)
|
||||||
link_set_managed(l, true);
|
link_manage(l, true);
|
||||||
else
|
else
|
||||||
link_set_managed(l, false);
|
link_manage(l, false);
|
||||||
#else
|
#else
|
||||||
if ((!interface_name || !strcmp(interface_name, ifname)) && !lazy_managed) {
|
if ((!interface_name || !strcmp(interface_name, ifname)) && !lazy_managed) {
|
||||||
link_set_managed(l, true);
|
link_manage(l, true);
|
||||||
} else {
|
} else {
|
||||||
log_debug("ignored device: %s", ifname);
|
log_debug("ignored device: %s", ifname);
|
||||||
}
|
}
|
||||||
|
@ -188,8 +194,9 @@ static int manager_signal_fn(sd_event_source *source,
|
||||||
struct manager *m = data;
|
struct manager *m = data;
|
||||||
|
|
||||||
if (ssi->ssi_signo == SIGCHLD) {
|
if (ssi->ssi_signo == SIGCHLD) {
|
||||||
|
siginfo_t info;
|
||||||
log_debug("caught SIGCHLD for %ld, reaping child", (long)ssi->ssi_pid);
|
log_debug("caught SIGCHLD for %ld, reaping child", (long)ssi->ssi_pid);
|
||||||
waitid(P_PID, ssi->ssi_pid, NULL, WNOHANG|WEXITED);
|
waitid(P_PID, ssi->ssi_pid, &info, WNOHANG|WEXITED);
|
||||||
return 0;
|
return 0;
|
||||||
} else if (ssi->ssi_signo == SIGPIPE) {
|
} else if (ssi->ssi_signo == SIGPIPE) {
|
||||||
/* ignore SIGPIPE */
|
/* ignore SIGPIPE */
|
||||||
|
@ -595,17 +602,17 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
srand(time(NULL));
|
srand(time(NULL));
|
||||||
|
|
||||||
GKeyFile* gkf = load_ini_file();
|
GKeyFile* gkf = load_ini_file();
|
||||||
|
|
||||||
if (gkf) {
|
if (gkf) {
|
||||||
gchar* log_level;
|
gchar* log_level;
|
||||||
log_level = g_key_file_get_string (gkf, "wifid", "log-level", NULL);
|
log_level = g_key_file_get_string (gkf, "wifid", "log-level", NULL);
|
||||||
if (log_level) {
|
if (log_level) {
|
||||||
log_max_sev = log_parse_arg(log_level);
|
log_max_sev = log_parse_arg(log_level);
|
||||||
g_free(log_level);
|
g_free(log_level);
|
||||||
}
|
}
|
||||||
g_key_file_free(gkf);
|
g_key_file_free(gkf);
|
||||||
}
|
}
|
||||||
|
|
||||||
r = parse_argv(argc, argv);
|
r = parse_argv(argc, argv);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
@ -614,9 +621,9 @@ int main(int argc, char **argv)
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
|
||||||
if (getuid() != 0) {
|
if (getuid() != 0) {
|
||||||
r = EACCES;
|
r = EACCES;
|
||||||
log_notice("Must run as root");
|
log_notice("Must run as root");
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = manager_new(&m);
|
r = manager_new(&m);
|
||||||
|
|
|
@ -129,6 +129,8 @@ struct link {
|
||||||
char *ifname;
|
char *ifname;
|
||||||
char *friendly_name;
|
char *friendly_name;
|
||||||
char *wfd_subelements;
|
char *wfd_subelements;
|
||||||
|
char *mac_addr;
|
||||||
|
int p2p_state; /* 0: unknown, 1: supported, -1: unsupproted */
|
||||||
char *config_methods;
|
char *config_methods;
|
||||||
char *ip_binary;
|
char *ip_binary;
|
||||||
|
|
||||||
|
@ -153,6 +155,7 @@ struct peer *link_find_peer_by_label(struct link *l, const char *label);
|
||||||
int link_new(struct manager *m,
|
int link_new(struct manager *m,
|
||||||
unsigned int ifindex,
|
unsigned int ifindex,
|
||||||
const char *ifname,
|
const char *ifname,
|
||||||
|
const char *mac_addr,
|
||||||
struct link **out);
|
struct link **out);
|
||||||
void link_free(struct link *l);
|
void link_free(struct link *l);
|
||||||
|
|
||||||
|
@ -162,7 +165,7 @@ bool link_is_using_dev(struct link *l);
|
||||||
|
|
||||||
int link_set_ip_binary(struct link *l, const char *ip_binary);
|
int link_set_ip_binary(struct link *l, const char *ip_binary);
|
||||||
|
|
||||||
int link_set_managed(struct link *l, bool set);
|
int link_manage(struct link *l, bool set);
|
||||||
bool link_get_managed(struct link *l);
|
bool link_get_managed(struct link *l);
|
||||||
int link_renamed(struct link *l, const char *ifname);
|
int link_renamed(struct link *l, const char *ifname);
|
||||||
|
|
||||||
|
@ -173,10 +176,14 @@ int link_set_wfd_subelements(struct link *l, const char *val);
|
||||||
const char *link_get_wfd_subelements(struct link *l);
|
const char *link_get_wfd_subelements(struct link *l);
|
||||||
int link_set_p2p_scanning(struct link *l, bool set);
|
int link_set_p2p_scanning(struct link *l, bool set);
|
||||||
bool link_get_p2p_scanning(struct link *l);
|
bool link_get_p2p_scanning(struct link *l);
|
||||||
|
int link_get_p2p_state(struct link *l);
|
||||||
|
const char *link_get_mac_addr(struct link *l);
|
||||||
|
|
||||||
void link_supplicant_started(struct link *l);
|
void link_supplicant_started(struct link *l);
|
||||||
void link_supplicant_stopped(struct link *l);
|
void link_supplicant_stopped(struct link *l);
|
||||||
void link_supplicant_p2p_scan_changed(struct link *l, bool new_value);
|
void link_supplicant_p2p_scan_changed(struct link *l, bool new_value);
|
||||||
|
/* 0: unknown, -1: unsupported, 1: supported */
|
||||||
|
void link_supplicant_p2p_state_known(struct link *l, int state);
|
||||||
|
|
||||||
_shl_sentinel_
|
_shl_sentinel_
|
||||||
void link_dbus_properties_changed(struct link *l, const char *prop, ...);
|
void link_dbus_properties_changed(struct link *l, const char *prop, ...);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue