1
0
Fork 0
mirror of https://github.com/albfan/miraclecast.git synced 2025-03-09 23:38:56 +00:00

Compare commits

..

151 commits
v1.0 ... master

Author SHA1 Message Date
Alberto Fanjul
937747fd4d control null terminated string copying 2024-07-13 19:14:55 +02:00
Alberto Fanjul
c9c8bd7a7a cast free to avoid warnings 2024-07-13 19:14:10 +02:00
Alberto Fanjul
430b1a39b8 Max completion functions allowed are two 2024-07-13 19:13:41 +02:00
Alberto Fanjul
a67a182a05 compare correctly with char 2024-07-13 19:12:52 +02:00
Alberto Fanjul
aecd52a354 manage const pointers 2024-07-13 19:12:27 +02:00
Alberto Fanjul
c664d1ba27 detect empty input to finish app 2024-07-13 19:11:16 +02:00
Alberto Fanjul
faea95fbfa resize output buffer 2024-07-13 19:10:37 +02:00
Alberto Fanjul
1df6392a23 remove unused variables 2024-07-13 19:09:57 +02:00
Alberto Fanjul
210ef327ce Configure usage of systemd or elogind 2024-06-02 20:24:41 +02:00
Mark Muth
c83802fd06 Fix build with GCC 14.1 on Arch
Compiling with `gcc (GCC) 14.1.1 20240507` raised the following warning:

`error: implicit declaration of function ‘gettimeofday’ [-Wimplicit-function-declaration]`

Adding `#include <sys/time.h>` according to `man gettimeofday` fixes #509.
2024-06-02 00:40:23 +02:00
Alberto Fanjul
af6ab257ea Remove systemd and it comes with DEP_LIBS 2023-11-13 00:48:03 +01:00
Alberto Fanjul
fdb8671c40 fix autotools build for libm 2023-07-14 21:55:32 +02:00
Kai Jan Schmidt-Brauns
850a1c6f7c Fix dependency check to be posix-compliant
test with "==" operator works in bash but fails in other shells such as `sh`
2023-02-19 18:06:31 +01:00
Alberto Fanjul
f3debd5678 whitespace 2023-01-22 10:23:32 +01:00
Alberto Fanjul
279303d0d6 fix double free reading custom parameter 2023-01-22 10:17:44 +01:00
Erik Andresen
c803c88572 CMakeLists: Remove duplicate systemd requirement
closes #3
2023-01-08 18:26:23 +01:00
Xiao Qiang Liang
3018aac1f2 Fix cmake build for latest gcc
libraries link order is relevant on latest gcc
2023-01-04 13:52:31 +01:00
Tanish Azad
a36e9bd19f fixed error message 2022-11-28 18:34:41 +01:00
Alberto Fanjul
439dac09c5 Ignore special chars on color prompt 2022-11-20 12:48:40 +01:00
Alberto Fanjul
31f7fbba33 Set custom ctl names 2022-11-20 11:47:35 +01:00
Alberto Fanjul
abc9b2f92c set wfd elements on source selected 2022-11-14 07:14:05 +01:00
Alberto Fanjul
314aa5aed5 Use default rstp port 2022-11-14 07:12:25 +01:00
Alberto Fanjul
66c86f2971 fix memory error settting wfd_video_formats 2022-11-14 07:12:00 +01:00
Alberto Fanjul
788d37d7d2 CLI commands autocompletion 2022-11-13 11:37:22 +01:00
Alberto Fanjul
20816ad138 Add command friendly-name to sink controller 2022-11-08 08:52:53 +01:00
Alberto Fanjul
c215f05d5d Exit CLI if daemon is not available 2022-11-08 08:52:53 +01:00
Alberto Fanjul
f9a61faaa2 Do not prefix command output with time 2022-11-08 08:52:53 +01:00
Alberto Fanjul
8cd144271a log messages with time in human readable way 2022-10-30 01:28:43 +02:00
Alberto Fanjul
7135a99e71 Call script helpers from any path 2022-10-29 22:43:26 +02:00
Alberto Fanjul
42c5bda976 fix cmake CI 2022-10-29 13:32:27 +02:00
Alberto Fanjul
f740af2655 Clean cmake files 2022-10-29 13:32:27 +02:00
Alberto Fanjul
175495c729 fix cmake dependencies for 2022-10-29 13:32:27 +02:00
Alberto Fanjul
72f61549d9 log messages with time in human readable way 2022-10-29 13:32:27 +02:00
Alberto Fanjul
380504b0f8 helpers to start/stop wifi on several distros 2022-10-16 23:02:57 +02:00
Alberto Fanjul
b46679d5a4 Use posix functions 2022-10-16 22:44:20 +02:00
Alberto Fanjul
6f84dc8b7d Add ENABLE_SYSTEMD config to meson and autotools 2022-10-16 13:17:41 +02:00
Andreas K. Hüttel
85723432f9 Option to use elogind instead of whole systemd
Signed-off-by: Andreas K. Hüttel <dilfridge@gentoo.org>
2022-10-16 13:17:41 +02:00
Semara Incorporated
7f263b06b4 Change kdus to kdbus
Change kdus to kdbus in Arch linux aur installation instruction
2022-10-16 11:38:04 +02:00
Fernando van Loenhout
a1590ecc17 Support more than 2 devices when testing the capabilities
At the moment, this typo is preventing the test script from working when you have more than 2 devices plugged in. Fixes #449
2022-09-19 10:59:02 +02:00
Alberto Fanjul
43505ab3dd Allow to override/extend the request parameters 2022-05-28 10:46:44 +02:00
Alberto Fanjul
e816de93d3 Allow auto commands with any number of parameters 2022-05-28 10:43:12 +02:00
Alberto Fanjul
c42624885b simplify pass request options 2022-05-26 20:55:51 +02:00
Edward Betts
3ab7d5913f Correct spelling mistakes 2022-04-12 09:22:13 +02:00
Alberto Fanjul
0b72e0b435 cmake: fix build for Ninja generator 2022-03-09 17:45:30 +01:00
Коренберг ☢️ Марк
125f4a847f Fix .spec file 2022-01-11 23:49:22 +01:00
Michael Partridge
264a222d24 Add wpa_supplicant to requirements 2021-11-20 11:24:37 +01:00
Michael Partridge
4b229dc282 Move optional packages towards the end 2021-11-20 11:24:37 +01:00
mcp292
5ac3e0593f Add missing period 2021-11-18 22:18:39 +01:00
mcp292
e25d8d5a0b Fix broken relative link to #4 in README 2021-11-18 11:32:26 +01:00
Alberto Fanjul
df12df656c Configurable ip binary path
Different OS have ip binary on different locations

It can be configured at:
  - compile time `IP_BINARY`
  - execution time `--ip-binary`
2021-10-27 12:10:54 +02:00
Alberto Fanjul
65a7a0aad1 Assig dupped string 2021-10-27 12:10:54 +02:00
Alberto Fanjul
3f5270ee7a Avoid automatic make commands on autotools 2021-10-27 12:09:33 +02:00
Alberto Fanjul
506f1e7b28 whitespace 2021-10-27 12:09:33 +02:00
Alberto Fanjul
7739a9cee0 Fix completion path on meson buildsystem 2021-10-27 10:08:12 +02:00
dzink
a66b998883 Update README to indicate that source is not yet implemented.
Prevent issue duplication and manage user expectations by indicating up front that source is not yet implemented (see issue #4).
2021-09-06 11:38:48 +02:00
Alberto Fanjul
0481252438 Specify path for miracle utils script 2021-07-09 15:57:40 +02:00
Alberto Fanjul
a13c1b7c33 Do not fail stopping existing links 2021-01-03 19:45:49 +01:00
Alberto Fanjul
62c5b8daa8 Do not fail if peers are found before links
After discover peers, a miracle-sinkctl restart can lead to peers
without links. Just ignore that
2021-01-03 19:45:05 +01:00
Alberto Fanjul
8d6530ebc9 protect from NULL binary path 2021-01-03 14:07:44 +01:00
Alberto Fanjul
3b3531de5c Allow to set friendly name even if unmanaged 2021-01-03 14:04:38 +01:00
Alberto Fanjul
92a8b0b2c6 Detect miracle-wifid already running 2021-01-01 19:43:03 +01:00
Alberto Fanjul
058c9cc309 Update travis build status 2020-11-13 16:50:57 +01:00
Alberto Fanjul
c864ff6246 Use new semaphore 2.0 CI 2020-11-13 16:47:16 +01:00
Alberto Fanjul
4f37045eea Use travis as CI 2020-11-13 16:47:16 +01:00
Andrey Alekseenko
be54804768 Remove access to freed memory
If rtsp_unlink_waiting(m) is called and actually destroys m, it's not
safe to pass it to rtsp_unlink_outgoing later.

We make rtsp_unlink_waiting and rtsp_unlink_outgoing return true if they
destroy the message, and in this case we make sure not to try freeing it
again.
2020-09-20 06:38:05 +02:00
Andrey Alekseenko
57d05a1808 Remove double free
The function parser_submit_data is only called from parser_feed_char_data_body, which frees the buffer pointer anyway.
2020-09-20 06:38:05 +02:00
Andrey Alekseenko
9edb225905 Add option to use cppcheck in CMake 2020-09-20 06:38:05 +02:00
Andrey Alekseenko
2ef4c1f40f Random static analysis fixes 2020-09-20 06:38:05 +02:00
Alberto Fanjul
4111333521 Set readline as required package
autotools already set it, but cmake and meson no.
2019-08-27 11:01:22 +02:00
Warren Crossing
58dd6c411e Add --syslog flag to see wpa logs in syslog 2019-08-27 10:05:10 +02:00
Martin Kennedy
960a785e10 Use a guard to prevent dependency on pkg-config when it is not available
I picked the methodology off of https://github.com/curl/curl/issues/972.
The benefit here is that if someone does not have pkg-config (we know this
is unlikely anyways), their configuration script just won't end up with the
checks.

This provides a patch similar to the one requested in issue #120.
2019-04-04 01:11:50 +02:00
Alberto Fanjul
59df9a4a3f Add CI badge status 2018-12-25 11:42:35 +01:00
Alberto Fanjul
3a459e5316 Add docker for CI 2018-12-25 11:29:58 +01:00
Graham White
46089b18f0 Add spec file for building rpm files 2018-12-25 08:32:31 +01:00
Alberto Fanjul
c3c868e523 refactor to fix clang AST parsing 2018-10-06 15:25:39 +02:00
albfan
8151bb8cbd whitespace 2018-09-30 12:41:40 +02:00
Alberto Fanjul
4e67272f2f Avoid log errors
Do not output error settings null values or configuring unmanaged links
2018-09-22 08:51:54 +02:00
Xu Fasheng
8b76e3c212 Make miracle-wifid conexists with other network tools
The new option --lazy-managed will let miracle-wifid don't managed the
links automatically. Instead, the link will be managed only when the new
DBus property Managed was set to true. So this will be possible that
miracle-wifid could be coexists with other network tools like
networkmanager.

For example, unmanage the device in networkmanager with setting the DBus
property org.freedesktop.NetworkManager.Device.Managed to false and
manage it in miracle-wifid with setting
org.freedesktop.miracle.wifi.Link.Managed to true, then both them could
works and don't need to kill each other.

Besides, there is new command named make-managed in miracle-wifictl and
miracle-sinkctl.

closes #135, #75
2018-09-21 13:41:16 +02:00
Alberto Fanjul
ec7e11c8bf bash completion with lazy load 2018-09-21 13:41:16 +02:00
Alberto Fanjul
5bfc97a72c Customizable config_methods 2018-09-21 13:41:16 +02:00
Julian Kornberger
34595f035e Update README.md
Use ### for sub-sub-titles
2018-03-19 18:29:29 +01:00
albfan
1d01ae2117 sink: Enable all resolutions by default 2018-02-22 22:30:09 +01:00
Arsh Singh
76f0189829 Fix Arch Linux AUR package link 2018-02-14 08:12:49 +01:00
albfan
0c3f02e97b fix info to stop normal wifi 2018-01-31 22:14:26 +01:00
Derek Dai
a395c3c7af remove valgrind checking since now it is optional
Change-Id: I39dc6b3e23307fd039775983b6d68878547523ba
2017-04-22 11:24:36 +02:00
Alberto Fanjul
3dec3e9b83 Fixing CI for Code Coverage 2017-04-18 10:42:36 +02:00
albfan
eceb252ec7 Adding code coverage 2017-04-18 05:11:33 +02:00
albfan
20036e77dc Accept configure params on autogen.sh 2017-04-18 04:50:13 +02:00
albfan
92ab7eb2d3 Move info from README to wiki 2017-04-15 23:09:22 +02:00
albfan
033690b7fb Fix omxplayer execution 2017-04-15 23:00:51 +02:00
albfan
68802bb592 Customizable sysconfdir on cmake 2017-04-15 22:35:43 +02:00
Derek 呆
a6f672311e Logging Formation failure
miracle-sinkctl: Fix signal emotion of FormationFailure
miracle-wifid: Log formation failure
2017-04-15 15:59:46 +02:00
Derek Dai
6b5907ad45 Add meson build system
Use it as:

    $ meson build
    $ cd build
    $ ninja build
    $ ninja test
    $ sudo ninja install
2017-04-14 11:32:27 +02:00
Eric Nelson
fe9a39bee8 miracle-sinkctl: don't use readline if no stdin
Signed-off-by: Eric Nelson <eric@nelint.com>
2017-04-08 09:53:36 +02:00
albfan
c3f6b7f683 Fix cmake compilation for ini files 2017-04-07 21:49:43 +02:00
albfan
b8de12cab3 Avoid config stdin if not a tty
Allow to run miracle-sinkctl as a service

relates to #98
2017-04-05 07:51:11 +02:00
albfan
04a1ec8aa3 Ini files for miracle-wifid
Honor ~/.miraclecast or ~/.config/miraclecastrc

Properties avaliable:
- wifid: log-level
- sinkctl: external-player, rstp-port, log-level, log-journal-level, autocmd
- wifictl: log-level, journal-log-level

Command line has always higher priority over ini files

fixes #113
2017-04-04 22:00:39 +02:00
albfan
1bc0648f4b Workaround for Wayland GDK backend 2017-04-02 22:25:48 +02:00
albfan
3dfa75d90b Find interface index for wireless interfaces
helper function to find number to pass to run command of miracle-sinkctl
2017-03-27 00:10:30 +02:00
albfan
a2735d9b4d find pci bus slot for wireless adapter
helper function to find number to pass to run command of miracle-sinkctl
2017-03-26 23:32:03 +02:00
Alberto Fanjul
bc9ebcc849 Merge pull request #184 from Lin-Buo-Ren/patch-1
Fix minor typo in README
2017-03-26 13:39:35 +02:00
林博仁(Buo-Ren Lin)
dec8ee0c07 Fix minor typo in README
Signed-Off-By: 林博仁 <Buo.Ren.Lin@gmail.com>
2017-03-26 19:38:26 +08:00
albfan
52cd3f0f28 cleanup for gstplayer 2017-03-26 13:12:33 +02:00
albfan
a9da0067e3 Improve autocompletion
Standardize help commands
Add autocompletion for short arguments
Install autocompletion
2017-03-26 09:31:32 +02:00
albfan
2aeec41290 Provide wrapper for vlc
external player wrapper for vlc
2017-03-25 12:53:13 +01:00
albfan
3628f789b4 Add external player config
Allow to run custom player. It will be run within a wrapper that
respect -p/--port, option at least

fixes #87
2017-03-25 12:48:04 +01:00
albfan
016159bb1e Check running as root
Detect running with elevated privileges to access dbus

fixes #89
2017-03-25 11:55:28 +01:00
Alberto Fanjul
c0f49c3b9c Merge pull request #169 from nixjdm/master
Fixed small grammar error in README: this -> these.
2017-02-10 07:06:50 +01:00
Joseph Nix
2757c4f5e6 Fixed small grammar error in README: this -> these. 2017-02-09 21:40:49 -06:00
Alberto Fanjul
e5795edd04 Read UIBC capability in raw mode
Some devices send its UIBC capabilities with spaces. Read in raw mode to parse port correctly
2016-10-28 11:37:16 +02:00
albfan
cedfeeebe1 Debug GStreamer execution
Allow to config gstreamer log level
2016-10-23 10:20:12 +02:00
albfan
472fbf4a24 Check source UIBC capability
If source has no UIBC capability don't try to connect UIBC controller,
and listen UIBC setting to disable UIBC

relates to #115
2016-10-23 09:46:02 +02:00
albfan
5e93ad0638 Fix compilation warnings 2016-10-23 04:27:17 +02:00
albfan
cd23da3c74 Fix finding subnet 2016-10-01 12:29:48 +02:00
Alberto Fanjul
142ba08987 Merge pull request #125 from derekdai/master
add option --use-dev to miracle-wifid to workaround the 'no ifname' issue
2016-09-25 21:00:46 +02:00
Derek Dai
3886dcb7c7 add option --use-dev to miracle-wifid to workaround the 'no ifname' issue 2016-09-14 16:01:54 +08:00
Derek Dai
a9266e5055 re-dispatch wpa_message with ifname!=NULL from supplicant_global_fn() to supplicant_dev_fn() 2016-09-14 15:01:26 +08:00
Derek Dai
1cc667a00c don't modify raw message 2016-09-14 15:01:26 +08:00
albfan
36be37ed8b Fix wpa_supplicant search when path did not exists 2016-08-26 06:29:34 +02:00
albfan
28252707a2 Install dbus policy
closes #107
2016-08-22 03:08:40 +02:00
Alberto Fanjul
011d7e7fba Merge pull request #110 from foamboarder/master
CMake: use PROGRAMS during install
2016-08-18 18:31:47 +02:00
Tony Y
59ea55c28c CMake: use PROGRAMS during install
During install, PROGRAMS (instead of FILES) will set
execute permissions for all targets.
2016-08-18 09:19:47 -07:00
Alberto Fanjul
c412cdb846 Merge pull request #108 from foamboarder/master
CMake: correctly set file permissions
2016-08-18 12:13:53 +02:00
Alberto Fanjul
866f90f92a Merge pull request #105 from derekdai/master
Add debian support to kill-wpa.sh and find wpa_supplicant throught PATH
2016-08-18 12:09:31 +02:00
Tony Y
b6400aa14a CMake: correctly set file permissions
When building using CMake, the display would not appear
because the files did not have execute permissions.
2016-08-16 17:09:39 -07:00
Alberto Fanjul
3f800251a9 Merge pull request #106 from foamboarder/master
Add uibc-viewer to CMake install list
2016-08-16 23:48:12 +02:00
Tony Y
c7b49c9fbd Add uibc-viewer to CMake install list 2016-08-16 14:05:24 -07:00
Derek Dai
4601514799 find wpa_supplicant through PATH instead of option --wpa-bindir 2016-08-15 17:35:38 +08:00
Derek Dai
7fc8d0884c add debian support to miracle-utils.sh and normal-wifi.sh 2016-08-15 11:15:12 +08:00
Alberto Fanjul
70ffd2d532 Merge pull request #104 from derekdai/master
Add global define for debug
2016-08-12 14:51:59 +02:00
Derek Dai
6dd107a2fd since shl_log.h does not include config.h, define BUILD_ENABLE_DEBUG through add_definitions() instead of config.h.cmake 2016-08-12 16:55:29 +08:00
Derek Dai
99ed3efd88 fix typo in README.md 2016-08-12 16:55:29 +08:00
Alberto Fanjul
091d7092dc Merge pull request #102 from derekdai/master
fix some minor build issues
2016-08-10 10:59:24 +02:00
Derek Dai
fc6f19d132 pkg_check_modules() before if(CHECK_FOUND) 2016-08-10 16:44:38 +08:00
Derek Dai
e863a4498d add dependency to libsystemd to the root CMakeLists.txt 2016-08-10 16:43:33 +08:00
Derek Dai
44625ff4fc specify GdkX11 version before import it 2016-08-10 16:42:35 +08:00
Alberto Fanjul
7228c3e63d Change to support CI environment 2016-07-23 20:20:58 +02:00
albfan
412ce067b6 Use resolution provided to resize viewer 2016-07-23 19:15:38 +02:00
albfan
3f2266e254 Kill uibc viewer subprocess
closes #93
2016-07-16 15:29:13 +02:00
albfan
42820c00ba Add check cflags on cmake
closes #82 closes #85
2016-07-05 23:13:54 +02:00
albfan
349db0f04a help functions for arch linux 2016-07-02 16:15:09 +02:00
albfan
0ac755a765 Add technical specs
relates #38
2016-07-02 16:06:02 +02:00
albfan
1e69dd6dae UIBC support
Basic support for UIBC (single mouse events and key events)

 - Option --uibc on miracle-sinkctl to enable it
 - Option --log-journal-level to see player execution on journalctl
 - Controller for uibc miracle-uibcctl
 - Player based on PyGtk to receive and communicate mouse and keyevents

closes #57
2016-07-02 15:47:15 +02:00
albfan
4637409d0b relay on PATH to find executables 2016-07-02 15:22:13 +02:00
albfan
c6d645d720 Fix test suite on cmake 2016-06-30 21:16:32 +02:00
Alberto Fanjul
442103a333 Details for ubuntu distributions
General details for systemd prerequisite.

relates to #83
2016-06-30 11:59:45 +02:00
albfan
a95de6395e kdbus is avaliable by default
relates to #41
2016-06-26 12:30:40 +02:00
albfan
c94be167c8 open UIBC connection
relates to #57
2016-04-02 13:15:38 +02:00
albfan
1cbfa5b42a Fix hardware tester when no devices available 2016-02-20 11:06:45 +01:00
albfan
ce1cbba7d5 Add continuous integration 2016-01-24 23:33:31 +01:00
albfan
72fee8ea0e Debug hardware tester 2016-01-24 20:56:06 +01:00
85 changed files with 4085 additions and 848 deletions

7
.gitignore vendored
View file

@ -26,6 +26,7 @@ miracle-dhcp
miracle-sinkctl
miracle-wifictl
miracle-wifid
miracle-uibcctl
miraclectl
miracled
stamp-h1
@ -35,3 +36,9 @@ test_valgrind
test_wpas
wpa_cli
wpa_supplicant
CMakeFiles/
cmake_install.cmake
CMakeCache.txt
libmiracle-shared.a
install_manifest.txt
/build/

35
.semaphore/semaphore.yml Normal file
View file

@ -0,0 +1,35 @@
version: v1.0
name: Docker
agent:
machine:
type: e1-standard-2
os_image: ubuntu1804
global_job_config:
secrets:
- name: dockerhub
blocks:
- name: Checkout
task:
jobs:
- name: Checkout
commands:
- checkout
- name: Build
task:
jobs:
- name: Autotools
commands:
- checkout
- docker build -t autotools -f autotools.Dockerfile .
- name: Cmake
commands:
- checkout
- docker build -t cmake -f cmake.Dockerfile .
- name: meson
commands:
- checkout
- docker build -t meson -f meson.Dockerfile .

16
.travis.yml Normal file
View file

@ -0,0 +1,16 @@
language: c
services:
- docker
jobs:
include:
- stage: autotools
script:
docker build -t autotools -f autotools.Dockerfile .
- stage: cmake
script:
docker build -t cmake -f cmake.Dockerfile .
- stage: meson
script:
docker build -t meson -f meson.Dockerfile .

View file

@ -1,28 +1,63 @@
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake
${CMAKE_MODULE_PATH})
set(CMAKE_C_FLAGS "-std=gnu11")
add_definitions(-D_GNU_SOURCE)
cmake_minimum_required(VERSION 2.8)
project(Miraclecast)
cmake_minimum_required(VERSION 3.0)
add_subdirectory(src)
add_subdirectory(res)
add_subdirectory(test)
SET(BUILD_BINDIR "${CMAKE_INSTALL_PREFIX}/bin")
OPTION(BUILD_ENABLE_DEBUG "Enable Debug" ON )
OPTION(RELY_UDEV "Rely in udev tag to select device" OFF )
OPTION(BUILD_TESTS "Enable TEST" ON )
SET(PACKAGE_NAME miraclecast)
SET(PACKAGE_VERSION 1)
SET(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
set(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)
find_package(PkgConfig)
if(ENABLE_SYSTEMD)
pkg_check_modules (SYSTEMD REQUIRED libsystemd>=213)
SET(SESSION_LIBRARIES "${SYSTEMD_LIBRARIES}")
else(ENABLE_SYSTEMD)
pkg_check_modules (ELOGIND REQUIRED libelogind>=213)
include_directories ("${ELOGIND_INCLUDEDIR}")
SET(SESSION_LIBRARIES "${ELOGIND_LIBRARIES}")
endif(ENABLE_SYSTEMD)
SET(BUILD_BINDIR "${CMAKE_INSTALL_PREFIX}/bin")
OPTION(BUILD_ENABLE_DEBUG "Enable Debug" ON )
OPTION(RELY_UDEV "Rely in udev tag to select device" OFF )
OPTION(BUILD_TESTS "Enable TEST" ON )
OPTION(BUILD_ENABLE_CPPCHECK "Enable CPPCheck static analysis" OFF )
SET(IP_BINARY "/bin/ip" CACHE STRING "Path to ip binary")
if(BUILD_ENABLE_DEBUG)
add_definitions(-DBUILD_ENABLE_DEBUG)
endif()
set(SYSCONFDIR "/etc" CACHE STRING "system config dir")
set(DATADIR "${CMAKE_INSTALL_PREFIX}/share" CACHE STRING "shared data dir")
pkg_check_modules (GLIB2 REQUIRED glib-2.0)
pkg_check_modules (UDEV REQUIRED libudev)
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h)
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/config.h.cmake ${CMAKE_BINARY_DIR}/config.h)
if(BUILD_ENABLE_CPPCHECK)
find_program(CMAKE_C_CPPCHECK NAMES cppcheck)
if (CMAKE_C_CPPCHECK)
list(
APPEND CMAKE_C_CPPCHECK
"--enable=warning"
"--inline-suppr"
"--std=c11"
"-D__SIZEOF_POINTER__=8"
)
endif()
endif()
add_subdirectory(src)
add_subdirectory(res)
add_subdirectory(test)

View file

@ -8,7 +8,7 @@ This software was written by:
This software is licensed under the terms of the LGPL. Please see each source
file for the related copyright notice and license.
If a file does not contain a copright notice, the following notice shall apply:
If a file does not contain a copyright notice, the following notice shall apply:
MiracleCast - Wifi-Display/Miracast Implementation

View file

@ -4,3 +4,21 @@ EXTRA_DIST = README.md \
NEWS
ACLOCAL_AMFLAGS = -I m4
.PHONY: lcov genlcov lcov-clean
lcov:
-$(MAKE) $(AM_MAKEFLAGS) -k check
$(MAKE) $(AM_MAKEFLAGS) genlcov
# we have to massage the lcov.info file slightly to hide the effect of libtool
# placing the objects files in the .libs/ directory separate from the *.c
genlcov:
$(LTP) --directory $(top_builddir) --capture --output-file miraclecast-lcov.info --test-name GLIB_PERF --no-checksum
LANG=C $(LTP_GENHTML) --prefix $(top_builddir) --output-directory miraclecast-lcov --title "Miraclecast Code Coverage" --legend --show-details miraclecast-lcov.info
lcov-clean:
-$(LTP) --directory $(top_builddir) -z
-rm -rf miraclecast-lcov.info miraclecast-lcov
-find -name '*.gcda' -print | xargs -Ix rm x

View file

@ -1,61 +1,46 @@
# MiracleCast - Wifi-Display/Miracast Implementation
[![Join the chat at https://gitter.im/albfan/miraclecast](https://badges.gitter.im/albfan/miraclecast.svg)](https://gitter.im/albfan/miraclecast?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Semaphore CI Build Status](https://albfan.semaphoreci.com/badges/miraclecast/branches/master.svg?style=shields)](https://albfan.semaphoreci.com/projects/miraclecast)
[![Travis CI Build Status](https://travis-ci.org/albfan/miraclecast.svg?branch=master)](https://travis-ci.org/albfan/miraclecast)
[![Coverage Status](https://coveralls.io/repos/github/albfan/miraclecast/badge.svg?branch=master)](https://coveralls.io/github/albfan/miraclecast?branch=master)
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.
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).*
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.
## Requirements
The MiracleCast projects requires the following software to be installed:
- **systemd**: A system management daemon. It is used for device-management (udev), dbus management (sd-bus) and service management.
Systemd must be compiled with --enable-kdbus, even though kdbus isn't used, but only the independent, experimental sd-libraries.
Systemd >= 221 will work out of the box. For earlier versions systemd must be compiled with --enable-kdbus, even though kdbus isn't used, but only the independent, experimental sd-libraries.
*required*: >=systemd-213
- **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..)
- **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)
- **wpa_supplicant**: MiracleCast spawns wpa_supplicant with a custom config.
- **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)
- **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 relay 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)
- **P2P Wi-Fi device** Although widespread this 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)
- copy the dbus policy **res/org.freedesktop.miracle.conf** to `/etc/dbus-1/system.d/`
## Install
## Build and install
To compile MiracleCast, you can choose from [autotools](http://en.wikipedia.org/wiki/GNU_build_system) or [cmake](http://en.wikipedia.org/wiki/CMake):
To compile MiracleCast, you can choose from:
Autotools:
- [autotools](http://en.wikipedia.org/wiki/GNU_build_system)
- [cmake](http://en.wikipedia.org/wiki/CMake)
- [meson](http://mesonbuild.com/)
$ ./autogen.sh
$ mkdir build
$ cd build
$ ../configure --prefix=/usr/local #avoid --prefix for a standard install
Cmake:
$ mkdir build
$ cd build
$ cmake ..
Compile
$ make
Test
$ make check #only with autotools by now
Install
$ sudo make install
See more info on wiki [Building](https://github.com/albfan/miraclecast/wiki/Building)
## Automatic interface selection with udev
@ -69,17 +54,23 @@ You can also choose the interface with `--interface` option for miracle-wifid.
### Ubuntu
This specific linux flavour is so hard to get miraclecast dependencies that an alternative repo was created to install systemd with dbus
Check your systemd version with:
$ systemctl --version
If you are on 221 or above your systemd has kdbus enabled.
If you are below 221, an alternative repo was created to install systemd with dbus
https://github.com/albfan/systemd-ubuntu-with-dbus
At this time, ubuntu is on version 15.04 and systemd is stick on 219 version, use branch [systemd-219](https://github.com/albfan/miraclecast/tree/systemd-219) to compile miraclecast
See there was interface changes on systemd 219, if you are below that version, use branch [systemd-219](https://github.com/albfan/miraclecast/tree/systemd-219) to compile miraclecast
> See specific instructions on that repo
### Arch linux
Use existing [AUR package](https://aur.archlinux.org/packages/miraclecast/). Remember to enable kdus to systemd-git dependency
Use existing [AUR package](https://aur.archlinux.org/packages/miraclecast-git/). Remember to enable kdbus to systemd-git dependency if you are below 221 systemd.
$ export _systemd_git_kdbus=--enable-kdbus
@ -94,15 +85,12 @@ If you feel confidence enough (since systemd is the entrypoint for an OS) extrac
## Documentation
Steps to use it as sink:
### Steps to use it as sink
1. shutdown wpa_supplicant
1. shutdown wpa_supplicant and NetworkManager
$ sudo kill -9 $(ps -ef | grep wpa_supplican[t] | awk '{print $2}')
# now you can use `res/kill-wpa.sh`
>Remember to save your config to use with `res/normal-wifi.sh`
>it will be easily located with `ps -ef | grep wpa_supplicant` on `-c` option.
$ systemctl stop NetworkManager.service
$ systemctl stop wpa_supplicant.service
2. launch wifi daemon
@ -121,7 +109,7 @@ Steps to use it as sink:
6. See your screen device on this machine
Steps to use it as peer:
### Steps to use it as peer
1. Repeat steps 1 and 2 from "use as sink"
@ -133,9 +121,15 @@ Steps to use it as peer:
4. Locate them using scanning
> psp-scan
> p2p-scan
5. Apart from list, or show info with peer &lt;mac&gt; there's nothing useful here by now.
5. Apart from list, or show info with peer &lt;mac&gt; there's nothing useful here by now. For a Q&D see [Using as peer](https://github.com/albfan/miraclecast/issues/4)
## UIBC
> The User Input Back Channel (UIBC) is an optional WFD feature that when implemented facilitates communication of user inputs to a User Interface, present at the WFD Sink, to the WFD Source.
To use it just add `--uibc` on `miracle-sinkctl` startup. Single mouse events and key events are implemented.
## Autocompletion
@ -158,4 +152,4 @@ If you have any questions, do not hesitate to contact one of the maintainers.
- Website: http://www.freedesktop.org/wiki/Software/miracle
- Original repo: git://people.freedesktop.org/~dvdhrm/miracle
- Fork repo: https://github.com/albfan/miraclecast
- Technical spec: https://www.wi-fi.org/file/wi-fi-display-technical-specification-v11 (free registration required)

View file

@ -1,2 +1,68 @@
#!/bin/sh
autoreconf -f --install
set -e
oldpwd=$(pwd)
topdir=$(dirname $0)
cd $topdir
#intltoolize --force --automake
autoreconf --force --install --symlink
libdir() {
echo $(cd "$1/$(gcc -print-multi-os-directory)"; pwd)
}
args="\
--sysconfdir=/etc \
--localstatedir=/var \
--libdir=$(libdir /usr/lib) \
"
if [ -f "$topdir/.config.args" ]; then
args="$args $(cat $topdir/.config.args)"
fi
cd $oldpwd
if [ "x$1" = "xc" ]; then
shift
args="$args $@"
$topdir/configure CFLAGS='-g -O0 -ftrapv' $args
elif [ "x$1" = "xg" ]; then
shift
args="$args $@"
$topdir/configure CFLAGS='-g -O0 -ftrapv' $args
elif [ "x$1" = "xa" ]; then
shift
args="$args $@"
$topdir/configure CFLAGS='-g -O0 -Wsuggest-attribute=pure -Wsuggest-attribute=const -ftrapv' $args
elif [ "x$1" = "xl" ]; then
shift
args="$args $@"
$topdir/configure CC=clang CFLAGS='-g -O0 -ftrapv' $args
elif [ "x$1" = "xs" ]; then
shift
args="$args $@"
scan-build $topdir/configure CFLAGS='-std=gnu99 -g -O0 -ftrapv' $args
scan-build make
else
cat <<EOF
----------------------------------------------------------------
Initialized build system. For a common configuration please run:
----------------------------------------------------------------
$topdir/configure CFLAGS='-g -O0 -ftrapv' $args
or run $0 with param:
- c: compilation
- g: debugging
- a: pure/const warning
- l: clang build
- s: scan-build reporting
EOF
fi

11
autotools.Dockerfile Normal file
View file

@ -0,0 +1,11 @@
FROM docker.io/albfan/miraclecast-ci
COPY . ./
RUN rm -rf build-autotools ; \
mkdir build-autotools; \
cd build-autotools; \
../autogen.sh; \
../configure; \
make; \
make check

9
cmake.Dockerfile Normal file
View file

@ -0,0 +1,9 @@
FROM docker.io/albfan/miraclecast-ci
RUN mkdir src
COPY . ./src
WORKDIR src
RUN cmake -Bbuild . && make -C build

View file

@ -10,8 +10,7 @@ AM_CFLAGS = -Wall \
AM_CPPFLAGS = -include $(top_builddir)/config.h \
-I $(top_srcdir)/src \
-I $(top_srcdir)/src/shared \
'-DBUILD_BINDIR="$(bindir)"'
-I $(top_srcdir)/src/shared
AM_LDFLAGS = -Wl,--as-needed \
-Wl,--gc-sections \

View file

@ -1,9 +1,11 @@
#ifndef CONFIG_H
#define CONFIG_H
#cmakedefine BUILD_ENABLE_DEBUG
#cmakedefine ENABLE_SYSTEMD
#cmakedefine BUILD_BINDIR "@BUILD_BINDIR@"
#cmakedefine RELY_UDEV @RELY_UDEV@
#cmakedefine IP_BINARY @IP_BINARY@
#cmakedefine PACKAGE_STRING "@PACKAGE_STRING@"

View file

@ -35,17 +35,45 @@ AC_PROG_AWK
LT_PREREQ(2.2)
LT_INIT
LT_LIB_M
AC_ARG_ENABLE([rely-udev],
AS_HELP_STRING([--enable-rely-udev], [Use tagged device with miraclecast]), AC_DEFINE([RELY_UDEV], [], [Rely on udev to find miraclecast device]))
AC_ARG_ENABLE([log-debug],
AS_HELP_STRING([--disable-log-debug], [Disable log debug]), , AC_DEFINE([BUILD_ENABLE_DEBUG], [], [Enable debug log level]))
AC_ARG_VAR(IP_BINARY, [Path for ip binary])
if test -z "$IP_BINARY"; then
IP_BINARY=/bin/ip
fi
AC_ARG_ENABLE([systemd],
AS_HELP_STRING([--disable-systemd], [Disable systemd]),
[case "${enableval}" in
yes) use_libsystemd=yes ;;
no) use_libsystemd=no ;;
*) AC_MSG_ERROR([bad value ${enableval} for --enable-systemd]) ;;
esac],
[use_libsystemd=yes])
AC_DEFINE_UNQUOTED([IP_BINARY], [$IP_BINARY], [Path for ip binary])
#
# Mandatory dependencies
#
PKG_CHECK_MODULES([DEPS], [libudev libsystemd > 219])
PKG_CHECK_MODULES([GDHCP], [glib-2.0])
AS_IF([test "$use_libsystemd" = "yes"],
[
AC_DEFINE([ENABLE_SYSTEMD], [], [Use systemd])
m4_ifdef([PKG_CHECK_MODULES], [
PKG_CHECK_MODULES([DEPS], [libudev libsystemd > 219])
PKG_CHECK_MODULES([GLIB], [glib-2.0])
])
],
[
m4_ifdef([PKG_CHECK_MODULES], [
PKG_CHECK_MODULES([DEPS], [libudev libelogind])
PKG_CHECK_MODULES([GLIB], [glib-2.0])
])
])
AC_CHECK_HEADERS(readline/readline.h,, AC_MSG_ERROR(GNU readline not found))
@ -54,10 +82,84 @@ AC_CHECK_HEADERS(readline/readline.h,, AC_MSG_ERROR(GNU readline not found))
# all tests.
#
PKG_CHECK_MODULES([CHECK], [check],
[have_check=yes], [have_check=no])
m4_ifdef([PKG_CHECK_MODULES], [
PKG_CHECK_MODULES([CHECK], [check],
[have_check=yes], [have_check=no])
])
AM_CONDITIONAL([BUILD_HAVE_CHECK], [test "x$have_check" = "xyes"])
if test "x$have_check" = "xyes"
then
dnl ************************************
dnl *** Enable lcov coverage reports ***
dnl ************************************
AC_ARG_ENABLE(gcov,
AS_HELP_STRING([--enable-gcov],
[Enable gcov]),
[use_gcov=$enableval], [use_gcov=no])
if test "x$use_gcov" = "xyes"; then
dnl we need gcc:
if test "$GCC" != "yes"; then
AC_MSG_ERROR([GCC is required for --enable-gcov])
fi
dnl Check if ccache is being used
AC_CHECK_PROG(SHTOOL, shtool, shtool)
case `$SHTOOL path $CC` in
*ccache*[)] gcc_ccache=yes;;
*[)] gcc_ccache=no;;
esac
if test "$gcc_ccache" = "yes" && (test -z "$CCACHE_DISABLE" || test "$CCACHE_DISABLE" != "1"); then
AC_MSG_ERROR([ccache must be disabled when --enable-gcov option is used. You can disable ccache by setting environment variable CCACHE_DISABLE=1.])
fi
ltp_version_list="1.6 1.7 1.8 1.10 1.11 1.12"
AC_CHECK_PROG(LTP, lcov, lcov)
AC_CHECK_PROG(LTP_GENHTML, genhtml, genhtml)
if test "$LTP"; then
AC_CACHE_CHECK([for ltp version], glib_cv_ltp_version, [
glib_cv_ltp_version=invalid
ltp_version=`$LTP -v 2>/dev/null | $SED -e 's/^.* //'`
for ltp_check_version in $ltp_version_list; do
if test "$ltp_version" = "$ltp_check_version"; then
glib_cv_ltp_version="$ltp_check_version (ok)"
fi
done
])
else
ltp_msg="To enable code coverage reporting you must have one of the following LTP versions installed: $ltp_version_list"
AC_MSG_ERROR([$ltp_msg])
fi
case $glib_cv_ltp_version in
""|invalid[)]
ltp_msg="You must have one of the following versions of LTP: $ltp_version_list (found: $ltp_version)."
AC_MSG_ERROR([$ltp_msg])
LTP="exit 0;"
;;
esac
if test -z "$LTP_GENHTML"; then
AC_MSG_ERROR([Could not find genhtml from the LTP package])
fi
AC_DEFINE(HAVE_GCOV, 1, [Whether you have gcov])
dnl Remove all optimization flags from CFLAGS
changequote({,})
CFLAGS=`echo "$CFLAGS" | $SED -e 's/-O[0-9]*//g'`
changequote([,])
dnl Add the special gcc flags
CFLAGS="$CFLAGS -O0 -fprofile-arcs -ftest-coverage"
LDFLAGS="$LDFLAGS -lgcov"
fi
fi
#
# Makefile vars
# After everything is configured, we create all makefiles.
@ -70,6 +172,7 @@ AC_CONFIG_FILES([Makefile
src/dhcp/Makefile
src/shared/Makefile
src/wifi/Makefile
src/uibc/Makefile
test/Makefile])
AC_OUTPUT
@ -87,12 +190,15 @@ AC_MSG_NOTICE([Build configuration:
bindir: $bindir
libdir: $libdir
includedir: $includedir
sysconfdir: $sysconfdir
ip-binary: $IP_BINARY
Miscellaneous Options:
building tests: $have_check
code coverage: $use_gcov
rely udev: ${enable_rely_udev:-no}
Compilation
mkdir build && cd build
"${MAKE-make}" to start compilation process
"${MAKE-make}" check to pass test])
else
@ -102,11 +208,13 @@ AC_MSG_NOTICE([Build configuration:
bindir: $bindir
libdir: $libdir
includedir: $includedir
sysconfdir: $sysconfdir
ip-binary: $IP_BINARY
Miscellaneous Options:
building tests: $have_check
rely udev: ${enable_rely_udev:-no}
Compilation
mkdir build && cd build
"${MAKE-make}" to start compilation process])
fi

7
meson.Dockerfile Normal file
View file

@ -0,0 +1,7 @@
FROM docker.io/albfan/miraclecast-ci
COPY . ./
RUN rm -rf build-meson; \
meson build-meson; \
ninja -C build-meson

55
meson.build Normal file
View file

@ -0,0 +1,55 @@
project('Miraclecast',
'c',
version: '1',
meson_version: '>=0.39',
default_options: ['buildtype=debugoptimized', 'c_std=gnu11']
)
add_project_arguments('-D_GNU_SOURCE', language: 'c')
conf_data = configuration_data()
conf_data.set_quoted('BUILD_BINDIR',
join_paths(get_option('prefix'), get_option('bindir'))
)
conf_data.set_quoted('PACKAGE_STRING',
'@0@ @1@'.format(meson.project_name(), meson.project_version())
)
configure_file(output: 'config.h',
configuration: conf_data
)
c_compiler = meson.get_compiler('c')
readline = c_compiler.find_library('readline', required: true)
if readline.found()
add_project_arguments('-DHAVE_READLINE', language: 'c')
endif
if get_option('build-enable-debug')
add_project_arguments('-DBUILD_ENABLE_DEBUG', language: 'c')
endif
if get_option('rely-udev')
add_project_arguments('-DRELY_UDEV', language: 'c')
endif
if get_option('enable-systemd')
add_project_arguments('-DENABLE_SYSTEMD', language: 'c')
libsystemd = dependency('libsystemd')
else
libsystemd = dependency('libelogind')
endif
add_project_arguments('-DIP_BINARY='+get_option('ip-binary'), language: 'c')
glib2 = dependency('glib-2.0')
udev = dependency('libudev')
m = c_compiler.find_library('m', required: false)
subdir('src')
subdir('res')
if get_option('build-tests')
subdir('test')
endif

20
meson_options.txt Normal file
View file

@ -0,0 +1,20 @@
option('build-enable-debug',
type: 'boolean',
value: true,
description: 'Enable Debug')
option('rely-udev',
type: 'boolean',
value: false,
description: 'Rely in udev tag to select device')
option('build-tests',
type: 'boolean',
value: true,
description: 'Enable TEST')
option('ip-binary',
type: 'string',
value: '/bin/ip',
description: 'Path for ip binary')
option('enable-systemd',
type: 'boolean',
value: 'true',
description: 'Enable systemd')

View file

@ -1,11 +1,14 @@
INSTALL(
PROGRAMS miracle-gst gstplayer uibc-viewer
DESTINATION bin
)
########### install files ###############
INSTALL(
FILES org.freedesktop.miracle.conf
DESTINATION ${SYSCONFDIR}/dbus-1/system.d
)
install(FILES miracle-gst.sh DESTINATION bin)
#original Makefile.am contents follow:
#bin_SCRIPTS = miracle-gst.sh
#EXTRA_DIST = wpa.conf
INSTALL(
FILES miracle-wifid miracle-sinkctl miracle-wifictl
DESTINATION ${DATADIR}/bash-completion/completions
)

View file

@ -1,2 +1,9 @@
bin_SCRIPTS = miracle-gst.sh
bin_SCRIPTS = miracle-gst gstplayer uibc-viewer miracle-omxplayer
EXTRA_DIST = wpa.conf
dbuspolicydir=$(sysconfdir)/dbus-1/system.d
dbuspolicy_DATA = org.freedesktop.miracle.conf
bashcompletiondir=${datadir}/bash-completion/completions
bashcompletion_DATA=miracle-wifid miracle-sinkctl miracle-wifictl

223
res/gstplayer Executable file
View file

@ -0,0 +1,223 @@
#!/usr/bin/python3 -u
import gi
import argparse
gi.require_version('Gst', '1.0')
gi.require_version('Gtk', '3.0')
gi.require_version('GstVideo', '1.0')
gi.require_version('GdkX11', '3.0')
from gi.repository import GObject, Gst, Gtk, Gdk, GLib
# Needed for window.get_xid(), xvimagesink.set_window_handle(), respectively:
from gi.repository import GdkX11, GstVideo
GObject.threads_init()
Gst.init(None)
class Player(object):
def __init__(self, **kwargs):
resolution = kwargs.get("resolution")
if resolution:
split = resolution.split("x")
self.width = int(split[0])
self.height = int(split[1])
scale = kwargs.get("scale")
if scale:
split = scale.split("x")
self.width = int(split[0])
self.height = int(split[1])
debug = kwargs.get("debug")
if debug:
Gst.debug_set_active(True)
Gst.debug_set_threshold_from_string(debug, True)
port = kwargs.get("port")
uri = kwargs.get("uri")
self.window = Gtk.Window()
self.window.set_name('gstplayer')
self.window.connect('destroy', self.quit)
title = kwargs.get("title")
if title:
self.window.set_title(title)
if hasattr(self,'width') and hasattr(self,'height'):
self.window.set_default_size(self.width, self.height)
self.drawingarea = Gtk.DrawingArea()
self.window.add(self.drawingarea)
if hasattr(self,'width') and hasattr(self,'height'):
self.drawingarea.set_size_request(self.width,self.height)
self.drawingarea.add_events(Gdk.EventMask.BUTTON_PRESS_MASK|Gdk.EventMask.BUTTON_RELEASE_MASK)
self.drawingarea.connect('button-press-event', self.on_mouse_pressed)
self.drawingarea.connect('button-release-event', self.on_mouse_pressed)
self.drawingarea.add_events(Gdk.EventMask.KEY_PRESS_MASK)
self.window.connect('key-press-event', self.on_key_pressed)
audio = kwargs.get("audio")
self.playbin = None
#Create GStreamer pipeline
if uri is not None:
self.pipeline = Gst.Pipeline()
# Create GStreamer elements
self.playbin = Gst.ElementFactory.make('playbin', "source")
if not uri.startswith("http://") or not uri.startswith("http://") or not uri.startswith("file://"):
if not uri.startswith("/"):
import os
uri = os.path.abspath(uri)
uri = "file://"+uri
# Set properties
self.playbin.set_property('uri', uri)
# Add playbin to the pipeline
self.pipeline.add(self.playbin)
else:
gstcommand = "udpsrc port="+str(port)+" caps=\"application/x-rtp, media=video\" ! rtpjitterbuffer latency=100 ! rtpmp2tdepay ! tsdemux "
if audio:
gstcommand += "name=demuxer demuxer. "
gstcommand += "! queue max-size-buffers=0 max-size-time=0 ! h264parse ! avdec_h264 ! videoconvert ! "
if scale:
gstcommand += "videoscale method=1 ! video/x-raw,width="+str(self.width)+",height="+str(self.height)+" ! "
gstcommand += "autovideosink "
if audio:
gstcommand += "demuxer. ! queue max-size-buffers=0 max-size-time=0 ! aacparse ! avdec_aac ! audioconvert ! audioresample ! autoaudiosink "
self.pipeline = Gst.parse_launch(gstcommand)
# Create bus to get events from GStreamer pipeline
self.bus = self.pipeline.get_bus()
self.bus.add_signal_watch()
self.bus.connect('message::eos', self.on_eos)
self.bus.connect('message::error', self.on_error)
# This is needed to make the video output in our DrawingArea:
self.bus.enable_sync_message_emission()
self.bus.connect('sync-message::element', self.on_sync_message)
self.bus.connect('message', self.on_message)
self.success = False
def on_message(self, bus, message):
if self.playbin:
videoPad = self.playbin.emit("get-video-pad", 0)
if videoPad and not self.success:
videoPadCapabilities = videoPad.get_current_caps()
if videoPadCapabilities:
(self.success, self.videoWidth) = \
videoPadCapabilities.get_structure(0).get_int("width")
(self.success, self.videoHeight) = \
videoPadCapabilities.get_structure(0).get_int("height")
if self.success:
print("{0} {1}".format(self.videoWidth, self.videoHeight))
self.drawingarea.set_size_request(self.videoWidth, self.videoHeight)
def on_mouse_pressed(self, widget, event):
#<type>,<count>,<id>,<x>,<y>
if event.type == Gdk.EventType.BUTTON_PRESS:
type = 0
else:
type = 1
width = self.drawingarea.get_allocation().width
height = self.drawingarea.get_allocation().height
half_area_width = width / 2
half_area_height = height / 2
half_def_width = self.width / 2
half_def_height = self.height / 2
min_hor_pos = half_area_width - half_def_width
max_hor_pos = half_area_width + half_def_width
min_ver_pos = half_area_height - half_def_height
max_ver_pos = half_area_height + half_def_height
pos_event_x = event.x
pos_event_y = event.y
if min_hor_pos <= pos_event_x <= max_hor_pos and min_ver_pos <= pos_event_y <= max_ver_pos:
uibc_x = int(pos_event_x - (half_area_width - half_def_width))
uibc_y = int(pos_event_y - (half_area_height - half_def_height))
print('{0},1,0,{1},{2}'.format(type, uibc_x , uibc_y))
def on_key_pressed(self, widget, event):
print("3,0x%04X,0x0000" % event.keyval)
def run(self):
self.window.show_all()
# You need to get the XID after window.show_all(). You shouldn't get it
# in the on_sync_message() handler because threading issues will cause
# segfaults there.
window = self.drawingarea.get_property('window')
if hasattr(window,'get_xid'):
self.xid = self.drawingarea.get_property('window').get_xid()
self.pipeline.set_state(Gst.State.PLAYING)
Gtk.main()
def quit(self, window):
self.pipeline.set_state(Gst.State.NULL)
Gtk.main_quit()
def on_sync_message(self, bus, msg):
if msg.get_structure().get_name() == 'prepare-window-handle':
print(self.drawingarea.get_allocation())
if hasattr(self,'xid'):
msg.src.set_window_handle(self.xid)
def on_eos(self, bus, msg):
print('on_eos(): seeking to start of video')
self.pipeline.seek_simple(
Gst.Format.TIME,
Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT,
0
)
def on_error(self, bus, msg):
print('on_error():', msg.parse_error())
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("uri", nargs="?", help="Uri to play")
parser.add_argument("-v", "--version", help="Show package version")
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("-a", "--audio", dest="audio", action="store_true", help="Enable audio support")
parser.add_argument("-s", "--scale", metavar="WxH", help="Scale to resolution")
parser.add_argument("-d", "--debug", help="Debug")
parser.add_argument("--uibc", help="Enable UIBC")
parser.add_argument("--title", help="set player title")
parser.add_argument("--res", metavar="n,n,n", help="Supported resolutions masks (CEA, VESA, HH)")
# res
# " default CEA %08X\n"
# " default VESA %08X\n"
# " default HH %08X\n"
parser.add_argument("-r", "--resolution", help="Resolution")
parser.set_defaults(audio=True)
args = parser.parse_args()
p = Player(**vars(args))
p.run()

View file

@ -1,8 +1,10 @@
#!/bin/bash
. miracle-utils.sh
DIRNAME=$(dirname $0)
kill_ubuntu_network_manager
. $DIRNAME/miracle-utils.sh
kill_network_manager
WPA_PID=$(find_wpa_supplicant_pid)
if [ -n "$WPA_PID" ]

13
res/meson.build Normal file
View file

@ -0,0 +1,13 @@
install_data(
'org.freedesktop.miracle.conf',
install_dir: join_paths(get_option('sysconfdir'), 'dbus-1', 'system.d')
)
install_data('miracle-gst', 'gstplayer', 'uibc-viewer',
install_dir: get_option('bindir'),
install_mode: 'rwxr-xr-x')
install_data(
'miracle-wifid', 'miracle-sinkctl', 'miracle-wifictl',
install_dir: join_paths(get_option('datadir'), 'bash-completion', 'completions')
)

View file

@ -1,12 +1,46 @@
#!/bin/bash
function help {
local scriptname="$(basename $0)"
cat >&2 <<EOF
$scriptname [options]
play rtp stream
Options:
-r Resolution
-s <Width>x<height> Scale
-d <level> Log level for gst
-p <port> Port for stream
-a Enables audio
-h Show this help
Examples:
# play stream on port 7236
$ $scriptname -p 7236
# play stream with resolution 800x600
$ $scriptname -s 800x600
# play stream with audio
$ $scriptname -a
# play stream with debug level 3
$ $scriptname -d 3
EOF
}
DEBUG='0'
AUDIO='0'
SCALE='0'
while getopts "r:d:as:" optname
while getopts "r:d:as:p:h" optname
do
case "$optname" in
"h")
help
exit 0
;;
"d")
DEBUG=`echo ${OPTARG} | tr -d ' '`
;;
@ -16,6 +50,9 @@ while getopts "r:d:as:" optname
"a")
AUDIO='1'
;;
"p")
PORT=`echo ${OPTARG} | tr -d ' '`
;;
"s")
SCALE='1'
WIDTH=`echo ${OPTARG} | tr -d ' ' | cut -dx -f 1`
@ -25,8 +62,9 @@ while getopts "r:d:as:" optname
echo "Unknown option $OPTARG"
;;
*)
# Should not occur
echo "Unknown error while processing options"
echo "Unknown parameter $OPTARG"
help
exit 1
;;
esac
done
@ -37,7 +75,7 @@ then
RUN+="--gst-debug=${DEBUG} "
fi
RUN+="udpsrc port=1991 caps=\"application/x-rtp, media=video\" ! rtpjitterbuffer latency=100 ! rtpmp2tdepay ! tsdemux "
RUN+="udpsrc port=$PORT caps=\"application/x-rtp, media=video\" ! rtpjitterbuffer latency=100 ! rtpmp2tdepay ! tsdemux "
if [ $AUDIO == '1' ]
then

69
res/miracle-omxplayer Executable file
View file

@ -0,0 +1,69 @@
#!/bin/bash
function help {
local scriptname="$(basename $0)"
cat >&2 <<EOF
$scriptname [options]
play rtp stream with omxplayer
Options:
-r Resolution
-s <Width>x<height> Scale
-d <level> Log level for gst
-p <port> Port for stream
-a Enables audio
-h Show this help
Examples:
# play stream on port 7236
$ $scriptname -p 7236
EOF
}
DEBUG='0'
AUDIO='0'
SCALE='0'
while getopts "r:d:as:p:h" optname
do
case "$optname" in
"h")
help
exit 0
;;
"d")
DEBUG=`echo ${OPTARG} | tr -d ' '`
;;
"r")
RESOLUTION=`echo ${OPTARG} | tr -d ' '`
;;
"a")
AUDIO='1'
;;
"p")
PORT=`echo ${OPTARG} | tr -d ' '`
;;
"s")
SCALE='1'
WIDTH=`echo ${OPTARG} | tr -d ' ' | cut -dx -f 1`
HEIGHT=`echo ${OPTARG} | tr -d ' ' | cut -dx -f 2`
;;
"?")
echo "Unknown option $OPTARG"
;;
*)
echo "Unknown parameter $OPTARG"
help
exit 1
;;
esac
done
RUN="omxplayer -live -b -o hdmi rtp://@:$PORT"
echo "running: $RUN"
exec ${RUN}

85
res/miracle-sinkctl Executable file
View file

@ -0,0 +1,85 @@
#
# Autocompletion for miraclecast commands
#
# Maintainer: Alberto Fanjul <albertofanjul@gmail.com>
#
function _miracle-sinkctl() {
local cur prev
_get_comp_words_by_ref cur
prev=${COMP_WORDS[COMP_CWORD-1]}
case "$prev" in
--log-level)
COMPREPLY=($(compgen -W 'fatal alert critical error warning notice info debug trace 1 2 3 4 5 6 7 8' -- "$cur"))
return 0
;;
esac
COMPREPLY=($(compgen -W "$(_parse_help miracle-sinkctl) $(_parse_short_help miracle-sinkctl)" -- "$cur"))
}
_parse_short_help ()
{
eval local cmd=$( quote "$1" );
local line;
{
case $cmd in
-)
cat
;;
*)
LC_ALL=C "$( dequote "$cmd" )" ${2:---help} 2>&1
;;
esac
} | while read -r line; do
[[ $line == *([[:blank:]])-* ]] || continue;
while [[ $line =~ ((^|[^-])-[A-Za-z0-9?][[:space:]]+)\[?[A-Z0-9]+\]? ]]; do
line=${line/"${BASH_REMATCH[0]}"/"${BASH_REMATCH[1]}"};
done;
__parse_short_options "${line// or /, }";
done
}
__parse_short_options ()
{
local option option2 i IFS='
,/|';
option=;
local -a array;
read -a array <<< "$1";
for i in "${array[@]}";
do
case "$i" in
---*)
break
;;
--?*)
break
;;
-?*)
option=$i;
break
;;
*)
break
;;
esac;
done;
[[ -n $option ]] || return;
IFS='
';
if [[ $option =~ (\[((no|dont)-?)\]). ]]; then
option2=${option/"${BASH_REMATCH[1]}"/};
option2=${option2%%[<{().[]*};
printf '%s\n' "${option2/=*/=}";
option=${option/"${BASH_REMATCH[1]}"/"${BASH_REMATCH[2]}"};
fi;
option=${option%%[<{().[]*};
printf '%s\n' "${option/=*/=}"
}
complete -F _miracle-sinkctl miracle-sinkctl
# ex: filetype=sh

View file

@ -27,6 +27,13 @@ function find_wireless_network_interfaces {
done
}
#
# show pci slot
#
function show_pci_slot {
basename $(readlink /sys/class/net/$1/device) | cut -d: -f2 | sed 's/^0*//'
}
#
# test if interface is connected
#
@ -34,6 +41,26 @@ function is_interface_connected {
test x$( cat /sys/class/net/$1/carrier 2>/dev/null) = x1
}
#
# find wireless pci slot
#
function find_wireless_pci_slot {
for i in $( find_wireless_network_interfaces )
do
show_pci_slot $i
done
}
#
# find wireless pci slot
#
function find_wireless_ifindex {
for i in $( find_wireless_network_interfaces )
do
show_ifindex $i
done
}
#
# find wireless connected interfaces
#
@ -52,12 +79,23 @@ function find_wireless_connected_network_interfaces {
#
function find_physical_for_network_interface {
PHY_INDEX=$(iw dev $1 info | grep wiphy | awk '{print $2}')
if [ -n $PHY_INDEX ]
if [ -n "$PHY_INDEX" ]
then
echo phy$PHY_INDEX
fi
}
#
# find interface index for interface
#
function show_ifindex {
IF_INDEX=$(iw dev $1 info | grep ifindex | awk '{print $2}')
if [ -n "$IF_INDEX" ]
then
echo $IF_INDEX
fi
}
#
# Check interface for P2P capabilities
#
@ -65,6 +103,12 @@ function search_p2p_capabilities {
WI_DEVICE=$1
PHY_DEVICE=$(find_physical_for_network_interface $WI_DEVICE)
if [ -z "$PHY_DEVICE" ]
then
echo "cannot find physical device for $WI_DEVICE"
return
fi
if iw phy $PHY_DEVICE info | grep -Pzo "(?s)Supported interface modes.*Supported commands" | grep "P2P" &> /dev/null
then
echo $WI_DEVICE supports P2P
@ -95,31 +139,55 @@ function find_wpa_supplicant_pid {
show_wpa_supplicant_process | awk '{print $2}'
}
#
# checking if distro is archlinux
#
function check_archlinux_distro {
test -f "/etc/arch-release"
}
#
# checking if distro is ubuntu
#
function check_ubuntu_distro {
cat /proc/version | grep -i ubuntu
}
#
# checking if distro is debian
#
function check_debian_distro {
cat /proc/version | grep -i debian
}
#
# ubuntu manager restarts automatically wpa_supplicant
# kills network manager
#
function kill_ubuntu_network_manager {
if check_ubuntu_distro
function kill_network_manager {
echo stopping NetworkManager
if check_ubuntu_distro || check_debian_distro
then
echo stopping NetworkManager
# ubuntu manager restarts automatically wpa_supplicant
sudo service NetworkManager stop
elif check_archlinux_distro
then
sudo systemctl stop Network.service
else
sudo systemctl stop NetworkManager
sudo systemctl stop wpa_supplicant
fi
}
#
# start ubuntu manager
# start network manager
#
function start_ubuntu_network_manager {
if check_ubuntu_distro
function start_network_manager {
echo starting NetworkManager
if check_ubuntu_distro || check_debian_distro
then
echo starting NetworkManager
sudo service NetworkManager start
elif check_archlinux_distro
then
sudo systemctl start Network.service
else
sudo service NetworkManager start
fi
}

69
res/miracle-vlc Executable file
View file

@ -0,0 +1,69 @@
#!/bin/bash
function help {
local scriptname="$(basename $0)"
cat >&2 <<EOF
$scriptname [options]
play rtp stream with vlc
Options:
-r Resolution
-s <Width>x<height> Scale
-d <level> Log level for gst
-p <port> Port for stream
-a Enables audio
-h Show this help
Examples:
# play stream on port 7236
$ $scriptname -p 7236
EOF
}
DEBUG='0'
AUDIO='0'
SCALE='0'
while getopts "r:d:as:p:h" optname
do
case "$optname" in
"h")
help
exit 0
;;
"d")
DEBUG=`echo ${OPTARG} | tr -d ' '`
;;
"r")
RESOLUTION=`echo ${OPTARG} | tr -d ' '`
;;
"a")
AUDIO='1'
;;
"p")
PORT=`echo ${OPTARG} | tr -d ' '`
;;
"s")
SCALE='1'
WIDTH=`echo ${OPTARG} | tr -d ' ' | cut -dx -f 1`
HEIGHT=`echo ${OPTARG} | tr -d ' ' | cut -dx -f 2`
;;
"?")
echo "Unknown option $OPTARG"
;;
*)
echo "Unknown parameter $OPTARG"
help
exit 1
;;
esac
done
RUN="vlc rtp://@:$PORT"
echo "running: $RUN"
exec ${RUN}

85
res/miracle-wifictl Executable file
View file

@ -0,0 +1,85 @@
#
# Autocompletion for miracle-wifictl
#
# Maintainer: Alberto Fanjul <albertofanjul@gmail.com>
#
function _miracle-wifictl() {
local cur prev
_get_comp_words_by_ref cur
prev=${COMP_WORDS[COMP_CWORD-1]}
case "$prev" in
--log-level)
COMPREPLY=($(compgen -W 'fatal alert critical error warning notice info debug trace 1 2 3 4 5 6 7 8' -- "$cur"))
return 0
;;
esac
COMPREPLY=($(compgen -W "$(_parse_help miracle-wifictl) $(_parse_short_help miracle-wifictl)" -- "$cur"))
}
_parse_short_help ()
{
eval local cmd=$( quote "$1" );
local line;
{
case $cmd in
-)
cat
;;
*)
LC_ALL=C "$( dequote "$cmd" )" ${2:---help} 2>&1
;;
esac
} | while read -r line; do
[[ $line == *([[:blank:]])-* ]] || continue;
while [[ $line =~ ((^|[^-])-[A-Za-z0-9?][[:space:]]+)\[?[A-Z0-9]+\]? ]]; do
line=${line/"${BASH_REMATCH[0]}"/"${BASH_REMATCH[1]}"};
done;
__parse_short_options "${line// or /, }";
done
}
__parse_short_options ()
{
local option option2 i IFS='
,/|';
option=;
local -a array;
read -a array <<< "$1";
for i in "${array[@]}";
do
case "$i" in
---*)
break
;;
--?*)
break
;;
-?*)
option=$i;
break
;;
*)
break
;;
esac;
done;
[[ -n $option ]] || return;
IFS='
';
if [[ $option =~ (\[((no|dont)-?)\]). ]]; then
option2=${option/"${BASH_REMATCH[1]}"/};
option2=${option2%%[<{().[]*};
printf '%s\n' "${option2/=*/=}";
option=${option/"${BASH_REMATCH[1]}"/"${BASH_REMATCH[2]}"};
fi;
option=${option%%[<{().[]*};
printf '%s\n' "${option/=*/=}"
}
complete -F _miracle-wifictl miracle-wifictl
# ex: filetype=sh

95
res/miracle-wifid Executable file
View file

@ -0,0 +1,95 @@
#
# Autocompletion for miracle-wifid
#
# Maintainer: Alberto Fanjul <albertofanjul@gmail.com>
#
function _miracle-wifid() {
local cur prev
_get_comp_words_by_ref cur
prev=${COMP_WORDS[COMP_CWORD-1]}
case "$prev" in
--config-methods)
COMPREPLY=($(compgen -W 'pbc pin usba ethernet label display ext_nfc_token int_nfc_token nfc_interface push_button keypad virtual_display physical_display virtual_push_button physical_push_button' -- "$cur"))
return 0
;;
--log-level)
COMPREPLY=($(compgen -W 'fatal alert critical error warning notice info debug trace 1 2 3 4 5 6 7 8' -- "$cur"))
return 0
;;
--wpa-loglevel)
COMPREPLY=($(compgen -W 'fatal alert critical error warning notice info debug trace 1 2 3 4 5 6 7 8' -- "$cur"))
return 0
;;
esac
COMPREPLY=($(compgen -W "$(_parse_help miracle-wifid) $(_parse_short_help miracle-wifid)" -- "$cur"))
}
_parse_short_help ()
{
eval local cmd=$( quote "$1" );
local line;
{
case $cmd in
-)
cat
;;
*)
LC_ALL=C "$( dequote "$cmd" )" ${2:---help} 2>&1
;;
esac
} | while read -r line; do
[[ $line == *([[:blank:]])-* ]] || continue;
while [[ $line =~ ((^|[^-])-[A-Za-z0-9?][[:space:]]+)\[?[A-Z0-9]+\]? ]]; do
line=${line/"${BASH_REMATCH[0]}"/"${BASH_REMATCH[1]}"};
done;
__parse_short_options "${line// or /, }";
done
}
__parse_short_options ()
{
local option option2 i IFS='
,/|';
option=;
local -a array;
read -a array <<< "$1";
for i in "${array[@]}";
do
case "$i" in
---*)
break
;;
--?*)
break
;;
-?*)
option=$i;
break
;;
*)
break
;;
esac;
done;
[[ -n $option ]] || return;
IFS='
';
if [[ $option =~ (\[((no|dont)-?)\]). ]]; then
option2=${option/"${BASH_REMATCH[1]}"/};
option2=${option2%%[<{().[]*};
printf '%s\n' "${option2/=*/=}";
option=${option/"${BASH_REMATCH[1]}"/"${BASH_REMATCH[2]}"};
fi;
option=${option%%[<{().[]*};
printf '%s\n' "${option/=*/=}"
}
complete -F _miracle-wifid miracle-wifid
# ex: filetype=sh

View file

@ -0,0 +1,18 @@
FROM debian:buster-slim
RUN dpkg --add-architecture i386
RUN apt-get update && apt-get install -y \
build-essential \
systemd \
libglib2.0-dev \
libreadline-dev \
libudev-dev \
libsystemd-dev \
libusb-dev \
automake \
autoconf \
libtool \
cmake \
meson

View file

@ -1,57 +0,0 @@
function _miracle-wifid() {
local cur prev
_get_comp_words_by_ref cur
prev=${COMP_WORDS[COMP_CWORD-1]}
case "$prev" in
--log-level)
COMPREPLY=($(compgen -W 'fatal alert critical error warning notice info debug trace 1 2 3 4 5 6 7 8' -- "$cur"))
return 0
;;
--wpa-loglevel)
COMPREPLY=($(compgen -W 'fatal alert critical error warning notice info debug trace 1 2 3 4 5 6 7 8' -- "$cur"))
return 0
;;
esac
COMPREPLY=($(compgen -W "$(_parse_help miracle-wifid)" -- "$cur"))
}
complete -F _miracle-wifid miracle-wifid
function _miracle-sinkctl() {
local cur prev
_get_comp_words_by_ref cur
prev=${COMP_WORDS[COMP_CWORD-1]}
case "$prev" in
--log-level)
COMPREPLY=($(compgen -W 'fatal alert critical error warning notice info debug trace 1 2 3 4 5 6 7 8' -- "$cur"))
return 0
;;
esac
COMPREPLY=($(compgen -W "$(_parse_help miracle-sinkctl)" -- "$cur"))
}
complete -F _miracle-sinkctl miracle-sinkctl
function _miracle-wifictl() {
local cur prev
_get_comp_words_by_ref cur
prev=${COMP_WORDS[COMP_CWORD-1]}
case "$prev" in
--log-level)
COMPREPLY=($(compgen -W 'fatal alert critical error warning notice info debug trace 1 2 3 4 5 6 7 8' -- "$cur"))
return 0
;;
esac
COMPREPLY=($(compgen -W "$(_parse_help miracle-wifictl)" -- "$cur"))
}
complete -F _miracle-wifictl miracle-wifictl

68
res/miraclecast.spec Normal file
View file

@ -0,0 +1,68 @@
%global commit 264a222d242734da369ca287aa6cfc6ca4f1f7bf
%global shortcommit %(c=%{commit}; echo ${c:0:7})
Name: miraclecast
Version: 1.0
Release: 2.git%{shortcommit}%{?dist}
Summary: Connect external monitors to your system via Wi-Fi Display (miracast)
License: LGPLv2
URL: https://github.com/albfan/miraclecast
Source0: https://github.com/albfan/miraclecast/archive/%{commit}/%{name}-%{shortcommit}.tar.gz
BuildRequires: autoconf
BuildRequires: automake
BuildRequires: libtool
BuildRequires: glib2-devel
BuildRequires: readline-devel
BuildRequires: systemd-devel
# "Recommends" is stronger than "Suggests", and gets installed by default by DNF.
# for gstplayer
Recommends: python3-gobject-base
# for miracle-gst (/usr/bin/gst-launch-1.0)
Suggests: gstreamer
# for miracle-omxplayer
Suggests: omxplayer
%description
The MiracleCast project provides software to connect external monitors to your
system via Wi-Fi. It is compatible to the Wi-Fi 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.
On the other hand, the Display-Sink side allows you to create Wi-Fi 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.
%prep
%autosetup -n %{name}-%{commit}
%build
autoreconf -fiv
%configure
%make_build
%install
%make_install
%files
%license COPYING LICENSE_gdhcp LICENSE_htable LICENSE_lgpl
%doc README.md
%config(noreplace) %{_sysconfdir}/dbus-1/system.d/org.freedesktop.miracle.conf
%{_bindir}/*
%{_datadir}/bash-completion/completions/*
%changelog
* Wed Jan 05 2022 Korenberg Mark
- Fix .spec-file, bump version
* Wed Nov 21 2018 Graham White
- first build

View file

@ -0,0 +1,24 @@
This file can be used with the rpmbuild or mock commands to create a binary RPM. Tested and working on Fedora 28 x86\_64.
Example usage (for Fedora 28 systems):
1. Create a tar.gz file of the source code and add the short name of the commit to the tar file, for the most recent commit at the time of writing this would be:
* `git clone https://github.com/albfan/miraclecast.git miraclecast-c3c868e`
* `tar -zcf miraclecast-c3c868e.tar.gz miraclecast-c3c868e`
2. Create a Source RPM
* `rpmbuild -bs miraclecast.spec --define "_sourcedir $PWD"`
Then build the RPM with one of the following:
* Using mock (assuming you're in the dir where the .src.rpm file is):
* `mock --arch=x86_64 -r fedora-28-x86_64 --resultdir=results miraclecast-1.0-1.gitc3c868e.fc28.src.rpm`
OR
* Using rpmbuild (assuming you're in the dir where the .src.rpm file is):
* `rpmbuild -ra miraclecast-1.0-1.gitc3c868e.fc28.src.rpm`
OR
* Using rpmbuild (assuming you're in the dir where the .spec and .tar.gz files are):
* `rpmbuild -bs miraclecast.spec --define "_sourcedir $PWD"``

View file

@ -1,57 +1,9 @@
#!/bin/bash
./kill-wpa.sh
DIRNAME=$(dirname $0)
. miracle-utils.sh
. $DIRNAME/miracle-utils.sh
ETHER_NAMES=$(find_choosable_networknames)
ETHER_COUNT=$(echo "$ETHER_NAMES" | wc -l)
if [ 0 = $ETHER_COUNT ]
then
echo There is no net devices avaliable
exit 1
elif [ 1 = $ETHER_COUNT ]
then
ETHERNAME="$ETHER_NAMES"
elif [ 2 -le $ETHER_COUNT ]
then
echo choose device for normal connection:
QUIT="exit"
select et_name in $ETHER_NAMES $QUIT
do
case $et_name
in
"$QUIT")
exit
;;
"")
if [ "$REPLY" = $QUIT ]
then
exit
else
echo unknow $REPLY
fi
;;
*)
ETHERNAME=$et_name
break
;;
esac
done
fi
# default path for config file
CONFIG_FILE=${1:-/run/network/wpa_supplicant_${ETHERNAME}.conf}
echo starting wpa_supplicant for normal connection
if check_ubuntu_distro
then
start_ubuntu_network_manager
sudo wpa_supplicant -B -u -s -O /var/run/wpa_supplicant
else
sudo wpa_supplicant -B -u -P /run/wpa_supplicant_${ETHERNAME}pid -i ${ETHERNAME} -D nl80211 -c$CONFIG_FILE
fi
./$DIRNAME/kill-wpa.sh
start_network_manager

View file

@ -0,0 +1,18 @@
#Example of extra parameters to extend sink request
[sinkctl]
extends.wfd_video_formats=40 00 01 10 0001bdeb 051557ff 00000fff 10 0000 001f 11 0780 0438, 02 10 0001bdeb 155557ff 00000fff 10 0000 001f 11 0780 0438
extends.wfd_audio_codecs=LPCM 00000003 00, AAC 0000000f 00, AC3 00000007 00
extends.wfd_display_edid=0001 00ffffffffffff0051f38f50010000000e100104a51d10ff2f0000a057499b2610484f000000010101010101010101010101010101011a36809c70381f403020350025a510000018000000fc00496e7465726e616c204c43440a000000fd003c3c9a9a0e00000000000000000000000000000000000000000000000000000030
extends.wfd_connector_type=05
extends.microsoft_cursor=none
extends.microsoft_rtcp_capability=none
extends.wfd_idr_request_capability=1
extends.microsoft_latency_management_capability=none
extends.microsoft_format_change_capability=none
extends.microsoft_diagnostics_capability=none
extends.intel_friendly_name=miraclecast
extends.intel_sink_manufacturer_name=GNU Linux
extends.intel_sink_model_name=Arch linux
extends.intel_sink_device_URL=http://github.com/albfan/miraclecast
extends.wfd_uibc_capability=input_category_list=GENERIC, HIDC;generic_cap_list=Keyboard;hidc_cap_list=Keyboard/USB, Mouse/USB, MultiTouch/USB, Gesture/USB, RemoteControl/USB;port=none

View file

@ -1,18 +1,34 @@
#!/bin/bash
. miracle-utils.sh
eval SCRIPT_DEBUG="\$$(basename $0 .sh | tr - _)_DEBUG"
SCRIPT_DEBUG=${SCRIPT_DEBUG:--1}
WIFI_NAMES=$(find_wireless_network_interfaces)
WIFI_COUNT=$(echo "$WIFI_NAMES" | wc -l)
if [ "$SCRIPT_DEBUG" -ge 1 ]
then
set -x
fi
if [ "$SCRIPT_DEBUG" -ge 10 ]
then
set -v
fi
. ./miracle-utils.sh
WIFI_COUNT=0
WIFI_NAMES="$(find_wireless_network_interfaces)"
if [ -n "$WIFI_NAMES" ]
then
WIFI_COUNT=$(echo "$WIFI_NAMES" | wc -l)
fi
if [ 0 = $WIFI_COUNT ]
then
echo There is no wireless devices avaliable
echo There is no wireless devices available
exit 1
elif [ 1 = $WIFI_COUNT ]
then
WIFI_NAME="$WIFI_NAMES"
elif [ 2 -ge $WIFI_COUNT ]
elif [ 2 -le $WIFI_COUNT ]
then
echo Choose wireless device:
PS3="device: "

View file

@ -35,7 +35,7 @@ Try installing packages "gst-plugins-bad, gst-plugins-base, gst-plugins-base-lib
If that fails too, try:
$ vlc rtp://@1991
$ vlc rtp://@:7236
EOF
else

19
res/uibc-viewer Executable file
View file

@ -0,0 +1,19 @@
#!/bin/bash
function kill_child() {
CHILDREN="$(ps -o pid= --ppid $$)"
echo killing $CHILDREN
kill $CHILDREN
}
IP=$1
shift
UIBC_PORT=$1
shift
echo $$
trap 'kill_child' SIGTERM
gstplayer $@ | miracle-uibcctl $IP $UIBC_PORT --daemon &
wait

View file

@ -8,7 +8,7 @@ ETHER_COUNT=$(echo "$ETHER_NAMES" | wc -l)
if [ 0 = $ETHER_COUNT ]
then
echo There is no net devices avaliable
echo There is no net devices available
exit 1
elif [ 1 = $ETHER_COUNT ]
then

View file

@ -1,37 +1,15 @@
set(CMAKE_C_FLAGS "-std=gnu11")
set(CMAKE_C_FLAGS "-std=gnu11 ${CMAKE_C_FLAGS}")
add_subdirectory(shared)
add_subdirectory(wifi)
add_subdirectory(dhcp)
add_subdirectory(ctl)
add_subdirectory(uibc)
set(miracled_SRCS miracled.h miracled.c)
add_executable(miracled ${miracled_SRCS})
target_link_libraries(miracled miracle-shared)
target_link_libraries(miracled m)
install(TARGETS miracled DESTINATION bin)
INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/shared)
########### install files ###############
#original Makefile.am contents follow:
#include $(top_srcdir)/common.am
#SUBDIRS = shared wifi dhcp ctl
#
#bin_PROGRAMS = miracled
#
#miracled_SOURCES = \
# miracled.h \
# miracled.c
#miracled_CPPFLAGS = \
# $(AM_CPPFLAGS) \
# $(DEPS_CFLAGS)
#miracled_LDADD = \
# shared/libmiracle-shared.la \
# $(DEPS_LIBS)
#

View file

@ -1,5 +1,5 @@
include $(top_srcdir)/common.am
SUBDIRS = shared wifi dhcp ctl
SUBDIRS = shared wifi dhcp ctl uibc
bin_PROGRAMS = miracled

BIN
src/ctl/.ctl-sink.c.swo Normal file

Binary file not shown.

View file

@ -1,6 +1,9 @@
find_package(Readline)
########### next target ###############
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-wifictl_SRCS ctl.h
ctl-cli.c
@ -20,17 +23,20 @@ if(READLINE_FOUND)
endif(READLINE_FOUND)
target_link_libraries(miracle-wifictl miracle-shared)
########### next target ###############
target_link_libraries(miracle-wifictl m)
target_link_libraries(miracle-wifictl ${GLIB2_LIBRARIES})
set(miracle-sinkctl_SRCS ctl.h
ctl-cli.c
ctl-sink.h
ctl-sink.c
ctl-wifi.c
sinkctl.c
wfd.c)
add_executable(miracle-sinkctl ${miracle-sinkctl_SRCS})
target_link_libraries(miracle-sinkctl ${GLIB2_LIBRARIES})
target_link_libraries(miracle-sinkctl m)
install(TARGETS miracle-sinkctl DESTINATION bin)
@ -45,42 +51,3 @@ endif(READLINE_FOUND)
target_link_libraries(miracle-sinkctl miracle-shared)
INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/shared)
########### install files ###############
#original Makefile.am contents follow:
#include $(top_srcdir)/common.am
#bin_PROGRAMS = miracle-wifictl miracle-sinkctl
#
#miracle_wifictl_SOURCES = \
# ctl.h \
# ctl-cli.c \
# ctl-wifi.c \
# wifictl.c
#miracle_wifictl_CPPFLAGS = \
# $(AM_CPPFLAGS) \
# $(DEPS_CFLAGS)
#miracle_wifictl_LDADD = \
# ../shared/libmiracle-shared.la \
# -lreadline \
# $(DEPS_LIBS)
#
#miracle_sinkctl_SOURCES = \
# ctl.h \
# ctl-cli.c \
# ctl-sink.c \
# ctl-wifi.c \
# sinkctl.c
#miracle_sinkctl_CPPFLAGS = \
# $(AM_CPPFLAGS) \
# $(DEPS_CFLAGS)
#miracle_sinkctl_LDADD = \
# ../shared/libmiracle-shared.la \
# -lreadline \
# $(DEPS_LIBS)
#
#

View file

@ -8,7 +8,8 @@ miracle_wifictl_SOURCES = \
wifictl.c
miracle_wifictl_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(DEPS_CFLAGS)
$(DEPS_CFLAGS) \
$(GLIB_CFLAGS)
miracle_wifictl_LDADD = \
../shared/libmiracle-shared.la \
-lreadline \
@ -17,16 +18,19 @@ miracle_wifictl_LDADD = \
miracle_sinkctl_SOURCES = \
ctl.h \
ctl-cli.c \
ctl-sink.h \
ctl-sink.c \
ctl-wifi.c \
wfd.c \
sinkctl.c
miracle_sinkctl_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(DEPS_CFLAGS)
$(DEPS_CFLAGS) \
$(GLIB_CFLAGS)
miracle_sinkctl_LDADD = \
../shared/libmiracle-shared.la \
-lreadline \
$(DEPS_LIBS)
$(DEPS_LIBS) \
$(GLIB_LIBS)

View file

@ -27,15 +27,15 @@
#include <string.h>
#include <strings.h>
#include <sys/signalfd.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <systemd/sd-bus.h>
#include "ctl.h"
#include "shl_macro.h"
#include "shl_util.h"
#include "shl_log.h"
#include <math.h>
/* *sigh* readline doesn't include all their deps, so put them last */
#include <readline/history.h>
#include <readline/readline.h>
/*
* Helpers for interactive commands
@ -47,14 +47,14 @@ static sd_event_source *cli_sigs[_NSIG];
static sd_event_source *cli_stdin;
static bool cli_rl;
static const struct cli_cmd *cli_cmds;
int cli_max_sev = LOG_NOTICE;
unsigned int cli_max_sev = LOG_NOTICE;
static bool is_cli(void)
{
return cli_rl;
}
void cli_printv(const char *fmt, va_list args)
void cli_printv(const char *fmt, bool prefix_time, va_list args)
{
SHL_PROTECT_ERRNO;
_shl_free_ char *line = NULL;
@ -73,6 +73,10 @@ void cli_printv(const char *fmt, va_list args)
rl_redisplay();
}
if (prefix_time) {
cli_printf_time_prefix();
}
vprintf(fmt, args);
if (async) {
@ -83,24 +87,65 @@ void cli_printv(const char *fmt, va_list args)
}
}
void cli_printf_time_prefix(const char *fmt, va_list args)
{
long long sec, usec;
time_t now;
struct tm *timeinfo;
struct timeval tv;
char buffertmp[80];
char buffer[120];
int millisec;
log__time(&sec, &usec);
if (log_date_time) {
gettimeofday(&tv, NULL);
millisec = lrint(tv.tv_usec/1000.0);
if (millisec>=1000) {
millisec -=1000;
tv.tv_sec++;
}
time(&now);
timeinfo = localtime(&now);
strftime(buffertmp, 80, "%x - %X.%03d", timeinfo);
sprintf(buffer, "%s.%03d", buffertmp, millisec);
}
if (log_date_time)
printf("[%s] ", buffer);
else if (log__have_time())
printf("[%.4lld.%.6lld] ", sec, usec);
}
void cli_command_printf(const char *fmt, ...)
{
SHL_PROTECT_ERRNO;
va_list args;
va_start(args, fmt);
cli_printv(fmt, false, args);
va_end(args);
}
void cli_printf(const char *fmt, ...)
{
SHL_PROTECT_ERRNO;
va_list args;
va_start(args, fmt);
cli_printv(fmt, args);
cli_printv(fmt, true, args);
va_end(args);
}
int cli_help(const struct cli_cmd *cmds)
int cli_help(const struct cli_cmd *cmds, int whitespace)
{
unsigned int i;
if (!is_cli()) {
cli_fn_help();
}
cli_printf("Available commands:\n");
cli_command_printf("Available commands:\n");
for (i = 0; cmds[i].cmd; ++i) {
if (!cmds[i].desc)
@ -110,11 +155,11 @@ int cli_help(const struct cli_cmd *cmds)
if (!is_cli() && cmds[i].cli_cmp == CLI_Y)
continue;
cli_printf(" %s %-*s %s\n",
cmds[i].cmd,
(int)(40 - strlen(cmds[i].cmd)),
cmds[i].args ? : "",
cmds[i].desc ? : "");
cli_command_printf(" %s %-*s %s\n",
cmds[i].cmd,
(int)(whitespace - strlen(cmds[i].cmd)),
cmds[i].args ? : "",
cmds[i].desc ? : "");
}
return 0;
@ -143,21 +188,21 @@ int cli_do(const struct cli_cmd *cmds, char **args, unsigned int n)
switch (cmds[i].argc_cmp) {
case CLI_EQUAL:
if (n != cmds[i].argc) {
cli_printf("Invalid number of arguments\n");
cli_command_printf("Invalid number of arguments\n");
return -EINVAL;
}
break;
case CLI_MORE:
if (n < cmds[i].argc) {
cli_printf("too few arguments\n");
cli_command_printf("too few arguments\n");
return -EINVAL;
}
break;
case CLI_LESS:
if (n > cmds[i].argc) {
cli_printf("too many arguments\n");
cli_command_printf("too many arguments\n");
return -EINVAL;
}
@ -173,7 +218,7 @@ int cli_do(const struct cli_cmd *cmds, char **args, unsigned int n)
}
if (!strcmp(cmd, "help"))
return cli_help(cmds);
return cli_help(cmds, 40);
return -EAGAIN;
}
@ -198,12 +243,15 @@ static void cli_handler_fn(char *input)
else if (!r)
return;
add_history(original);
if (!(strcmp(original, "quit") == 0 || strcmp(original, "exit") == 0)) {
add_history(original);
write_history(get_history_filename());
}
r = cli_do(cli_cmds, args, r);
if (r != -EAGAIN)
return;
cli_printf("Command not found\n");
cli_command_printf("Command not found\n");
}
static int cli_stdin_fn(sd_event_source *source,
@ -277,6 +325,312 @@ void cli_destroy(void)
cli_event = NULL;
}
char *yes_no_options[] = {"yes", "no", NULL};
char *
yes_no_generator (const char *text, int state)
{
static int list_index, len;
const char *name;
/* If this is a new word to complete, initialize now. This includes
saving the length of TEXT for efficiency, and initializing the index
variable to 0. */
if (!state)
{
list_index = 0;
len = strlen (text);
}
/* Return the next name which partially matches from the command list. */
while ((name = yes_no_options[list_index]) != NULL)
{
list_index++;
if (strncmp (name, text, len) == 0)
return (strdup(name));
}
/* If no names matched, then return NULL. */
return ((char *)NULL);
}
char *
links_peers_generator (const char *text, int state)
{
static int list_index, len;
size_t peer_cnt = 0;
size_t link_cnt = 0;
struct shl_dlist *i, *j;
struct ctl_link *l;
struct ctl_peer *p;
/* If this is a new word to complete, initialize now. This includes
saving the length of TEXT for efficiency, and initializing the index
variable to 0. */
if (!state)
{
list_index = 0;
len = strlen (text);
}
shl_dlist_for_each(i, &get_wifi()->links) {
l = link_from_dlist(i);
char *name = l->label;
if (strncmp (name, text, len) == 0)
{
if (link_cnt == list_index)
{
list_index++;
return strdup(name);
}
link_cnt++;
}
name = l->friendly_name;
if (!shl_isempty(name))
{
if (strncmp (name, text, len) == 0)
{
if (link_cnt == list_index)
{
list_index++;
return strdup(name);
}
link_cnt++;
}
}
}
peer_cnt = link_cnt;
shl_dlist_for_each(i, &get_wifi()->links) {
l = link_from_dlist(i);
shl_dlist_for_each(j, &l->peers) {
p = peer_from_dlist(j);
char *name = p->label;
if (strncmp (name, text, len) == 0)
{
if (peer_cnt == list_index)
{
list_index++;
return strdup(name);
}
peer_cnt++;
}
name = p->friendly_name;
if (!shl_isempty(name))
{
if (strncmp (name, text, len) == 0)
{
if (peer_cnt == list_index)
{
list_index++;
return strdup(name);
}
peer_cnt++;
}
}
}
}
/* If no names matched, then return NULL. */
return ((char *)NULL);
}
char *
peers_generator (const char *text, int state)
{
static int list_index, len;
size_t peer_cnt = 0;
struct shl_dlist *i, *j;
struct ctl_link *l;
struct ctl_peer *p;
/* If this is a new word to complete, initialize now. This includes
saving the length of TEXT for efficiency, and initializing the index
variable to 0. */
if (!state)
{
list_index = 0;
len = strlen (text);
}
shl_dlist_for_each(i, &get_wifi()->links) {
l = link_from_dlist(i);
shl_dlist_for_each(j, &l->peers) {
p = peer_from_dlist(j);
char *name = p->label;
if (strncmp (name, text, len) == 0)
{
if (peer_cnt == list_index)
{
list_index++;
return strdup(name);
}
peer_cnt++;
}
name = p->friendly_name;
if (!shl_isempty(name))
{
if (strncmp (name, text, len) == 0)
{
if (peer_cnt == list_index)
{
list_index++;
return strdup(name);
}
peer_cnt++;
}
}
}
}
/* If no names matched, then return NULL. */
return ((char *)NULL);
}
char *
links_generator (const char *text, int state)
{
static int list_index, len;
size_t link_cnt = 0;
struct shl_dlist *i;
struct ctl_link *l;
/* If this is a new word to complete, initialize now. This includes
saving the length of TEXT for efficiency, and initializing the index
variable to 0. */
if (!state)
{
list_index = 0;
len = strlen (text);
}
shl_dlist_for_each(i, &get_wifi()->links) {
l = link_from_dlist(i);
char *name = l->label;
if (strncmp (name, text, len) == 0)
{
if (link_cnt == list_index)
{
list_index++;
return strdup(name);
}
link_cnt++;
}
name = l->friendly_name;
if (!shl_isempty(name))
{
if (strncmp (name, text, len) == 0)
{
if (link_cnt == list_index)
{
list_index++;
return strdup(name);
}
link_cnt++;
}
}
}
/* If no names matched, then return NULL. */
return ((char *)NULL);
}
/* Generator function for command completion. STATE lets us know whether
* to start from scratch; without any state (i.e. STATE == 0), then we
* start at the top of the list.
*/
char *
command_generator (const char *text, int state)
{
static int list_index, len;
const char *name;
/* If this is a new word to complete, initialize now. This includes
saving the length of TEXT for efficiency, and initializing the index
variable to 0. */
if (!state)
{
list_index = 0;
len = strlen (text);
}
/* Return the next name which partially matches from the command list. */
while ((name = cli_cmds[list_index].cmd) != NULL)
{
list_index++;
if (strncmp (name, text, len) == 0)
return (strdup(name));
}
/* If no names matched, then return NULL. */
return ((char *)NULL);
}
int get_args(char* line)
{
char* tmp = line;
char* last_delim = tmp;
int count = 0;
/* Count how many elements will be extracted. */
while (*tmp)
{
if (' ' == *tmp)
{
if (last_delim+1 < tmp)
count++;
last_delim = tmp;
}
tmp++;
}
if (' ' != *last_delim)
count++;
return count;
}
/*
* Attempt to complete on the contents of TEXT. START and END bound the
* region of rl_line_buffer that contains the word to complete. TEXT is
* the word to complete. We can use the entire contents of rl_line_buffer
* in case we want to do some simple parsing. Return the array of matches,
* or NULL if there aren't any.
*/
char **
completion_fn (const char *text, int start, int end)
{
char **matches;
rl_attempted_completion_over = 1;
if (start == 0)
matches = rl_completion_matches (text, command_generator);
else
{
matches = (char **)NULL;
struct cli_cmd cmd;
int cmd_pos = 0;
while ((cmd = cli_cmds[cmd_pos++]).cmd)
{
if (strncmp(cmd.cmd, rl_line_buffer, strlen(cmd.cmd)) == 0)
{
int nargs = get_args(rl_line_buffer);
rl_compentry_func_t* completion_fn = cmd.completion_fns[nargs-2];
if (completion_fn)
matches = rl_completion_matches (text, completion_fn);
}
}
}
return (matches);
}
int cli_init(sd_bus *bus, const struct cli_cmd *cmds)
{
static const int sigs[] = {
@ -320,27 +674,31 @@ int cli_init(sd_bus *bus, const struct cli_cmd *cmds)
}
}
r = sd_event_add_io(cli_event,
if (isatty(fileno(stdin))) {
r = sd_event_add_io(cli_event,
&cli_stdin,
fileno(stdin),
EPOLLHUP | EPOLLERR | EPOLLIN,
cli_stdin_fn,
NULL);
if (r < 0) {
cli_vERR(r);
goto error;
if (r < 0) {
cli_vERR(r);
goto error;
}
cli_rl = true;
rl_erase_empty_line = 1;
rl_attempted_completion_function = completion_fn;
rl_callback_handler_install(get_cli_prompt(), cli_handler_fn);
using_history();
read_history(get_history_filename());
rl_end_of_history(0, 0);
printf("\r");
rl_on_new_line();
rl_redisplay();
}
cli_rl = true;
rl_erase_empty_line = 1;
rl_callback_handler_install(NULL, cli_handler_fn);
rl_set_prompt(CLI_PROMPT);
printf("\r");
rl_on_new_line();
rl_redisplay();
return 0;
error:

View file

@ -17,49 +17,7 @@
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
*/
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <systemd/sd-event.h>
#include <time.h>
#include <unistd.h>
#include "ctl.h"
#include "rtsp.h"
#include "shl_macro.h"
#include "shl_util.h"
#include "wfd.h"
struct ctl_sink {
sd_event *event;
char *target;
char *session;
char *url;
struct sockaddr_storage addr;
size_t addr_size;
int fd;
sd_event_source *fd_source;
struct rtsp *rtsp;
bool connected : 1;
bool hup : 1;
uint32_t resolutions_cea;
uint32_t resolutions_vesa;
uint32_t resolutions_hh;
int hres;
int vres;
};
#include "ctl-sink.h"
/*
* RTSP Session
@ -119,53 +77,92 @@ static void sink_handle_options(struct ctl_sink *s,
}
static void sink_handle_get_parameter(struct ctl_sink *s,
struct rtsp_message *m)
struct rtsp_message *m)
{
_rtsp_message_unref_ struct rtsp_message *rep = NULL;
int r;
_rtsp_message_unref_ struct rtsp_message *rep = NULL;
int r;
r = rtsp_message_new_reply_for(m, &rep, RTSP_CODE_OK, NULL);
if (r < 0)
return cli_vERR(r);
r = rtsp_message_new_reply_for(m, &rep, RTSP_CODE_OK, NULL);
if (r < 0)
return cli_vERR(r);
/* wfd_content_protection */
if (rtsp_message_read(m, "{<>}", "wfd_content_protection") >= 0) {
r = rtsp_message_append(rep, "{&}",
"wfd_content_protection: none");
if (r < 0)
return cli_vERR(r);
}
/* wfd_video_formats */
if (rtsp_message_read(m, "{<>}", "wfd_video_formats") >= 0) {
char wfd_video_formats[128];
sprintf(wfd_video_formats,
"wfd_video_formats: 00 00 03 10 %08x %08x %08x 00 0000 0000 10 none none",
s->resolutions_cea, s->resolutions_vesa, s->resolutions_hh);
r = rtsp_message_append(rep, "{&}", wfd_video_formats);
if (r < 0)
return cli_vERR(r);
}
/* wfd_audio_codecs */
if (rtsp_message_read(m, "{<>}", "wfd_audio_codecs") >= 0) {
r = rtsp_message_append(rep, "{&}",
"wfd_audio_codecs: AAC 00000007 00");
if (r < 0)
return cli_vERR(r);
}
/* wfd_client_rtp_ports */
if (rtsp_message_read(m, "{<>}", "wfd_client_rtp_ports") >= 0) {
r = rtsp_message_append(rep, "{&}",
"wfd_client_rtp_ports: RTP/AVP/UDP;unicast 1991 0 mode=play");
if (r < 0)
return cli_vERR(r);
}
/* wfd_content_protection */
check_and_response_option("wfd_content_protection", "none");
GHashTable* protocol_extensions = s->protocol_extensions;
/* wfd_video_formats */
gchar* wfd_video_formats = NULL;
if (protocol_extensions != NULL) {
gchar* wfd_video_formats_extension = g_hash_table_lookup(protocol_extensions, WFD_VIDEO_FORMATS);
if (wfd_video_formats_extension != NULL) {
wfd_video_formats = wfd_video_formats_extension;
}
}
bool create_wfd_video_formats = wfd_video_formats == NULL;
if (create_wfd_video_formats) {
gchar video_formats[128];
sprintf(video_formats, "00 00 03 10 %08x %08x %08x 00 0000 0000 10 none none",
s->resolutions_cea, s->resolutions_vesa, s->resolutions_hh);
wfd_video_formats = strdup(video_formats);
}
check_and_response_option(WFD_VIDEO_FORMATS, wfd_video_formats);
if (create_wfd_video_formats) {
g_free(wfd_video_formats);
}
rtsp_message_seal(rep);
cli_debug("OUTGOING: %s\n", rtsp_message_get_raw(rep));
/* wfd_audio_codecs */
gchar* wfd_audio_codecs = "AAC 00000007 00";
if (protocol_extensions != NULL) {
gchar* wfd_audio_codecs_extension = g_hash_table_lookup(protocol_extensions, WFD_AUDIO_CODECS);
if (wfd_audio_codecs_extension != NULL) {
wfd_audio_codecs = wfd_audio_codecs_extension;
}
}
check_and_response_option(WFD_AUDIO_CODECS, wfd_audio_codecs);
r = rtsp_send(s->rtsp, rep);
if (r < 0)
return cli_vERR(r);
/* wfd_client_rtp_ports */
char wfd_client_rtp_ports[128];
sprintf(wfd_client_rtp_ports, "RTP/AVP/UDP;unicast %d 0 mode=play", rstp_port);
check_and_response_option("wfd_client_rtp_ports", wfd_client_rtp_ports);
if (protocol_extensions != NULL) {
GList* extension_keys = g_hash_table_get_keys(protocol_extensions);
for (int i = 0; i<g_list_length(extension_keys); i++) {
gchar* key = g_list_nth_data(extension_keys, i);
if (g_strcmp0(key, WFD_VIDEO_FORMATS) == 0
|| g_strcmp0(key, WFD_AUDIO_CODECS) == 0
|| g_strcmp0(key, WFD_UIBC_CAPABILITY) == 0) {
continue;
}
gchar* value = g_hash_table_lookup(protocol_extensions, key);
check_and_response_option(key, value);
}
}
/* wfd_uibc_capability */
if (uibc_option) {
gchar* wfd_uibc_capability = "input_category_list=GENERIC;"
"generic_cap_list=Mouse,SingleTouch;"
"hidc_cap_list=none;"
"port=none";
if (protocol_extensions != NULL) {
gchar* wfd_uibc_capability_extension = g_hash_table_lookup(protocol_extensions, WFD_UIBC_CAPABILITY);
if (wfd_uibc_capability_extension != NULL) {
wfd_uibc_capability = wfd_uibc_capability_extension;
}
}
check_and_response_option(WFD_UIBC_CAPABILITY, wfd_uibc_capability);
}
rtsp_message_seal(rep);
cli_debug("OUTGOING: %s\n", rtsp_message_get_raw(rep));
r = rtsp_send(s->rtsp, rep);
if (r < 0)
return cli_vERR(r);
}
bool check_rtsp_option(struct rtsp_message *m, char *option) {
return rtsp_message_read(m, "{<>}", option) >= 0;
}
static int sink_setup_fn(struct rtsp *bus, struct rtsp_message *m, void *data)
@ -227,7 +224,7 @@ static int sink_set_format(struct ctl_sink *s,
if (hres && vres) {
s->hres = hres;
s->vres = vres;
ctl_fn_sink_resolution_set(s, hres, vres);
ctl_fn_sink_resolution_set(s);
return 0;
}
}
@ -241,6 +238,8 @@ static void sink_handle_set_parameter(struct ctl_sink *s,
_rtsp_message_unref_ struct rtsp_message *rep = NULL;
const char *trigger;
const char *url;
char *uibc_config;
const char *uibc_setting;
char *nu;
unsigned int cea_res, vesa_res, hh_res;
int r;
@ -273,6 +272,49 @@ static void sink_handle_set_parameter(struct ctl_sink *s,
}
}
/* M4 (or any other) can pass presentation URLs */
r = rtsp_message_read(m, "{<&>}", "wfd_uibc_capability", &uibc_config);
if (r >= 0) {
if (!s->uibc_config || strcmp(s->uibc_config, uibc_config)) {
nu = strdup(uibc_config);
if (!nu)
return cli_vENOMEM();
free(s->uibc_config);
s->uibc_config = nu;
if (!strcasecmp(uibc_config, "none")) {
uibc_enabled = false;
} else {
char* token = strtok(uibc_config, ";");
while (token) {
if (sscanf(token, "port=%d", &uibc_port)) {
log_debug("UIBC port: %d\n", uibc_port);
if (uibc_option) {
uibc_enabled = true;
}
break;
}
token = strtok(0, ";");
}
}
}
}
/* M4 (or any other) can pass presentation URLs */
r = rtsp_message_read(m, "{<s>}", "wfd_uibc_setting", &uibc_setting);
if (r >= 0) {
if (!s->uibc_setting || strcmp(s->uibc_setting, uibc_setting)) {
nu = strdup(uibc_setting);
if (!nu)
return cli_vENOMEM();
free(s->uibc_setting);
s->uibc_setting = nu;
cli_debug("uibc setting: %s\n", s->uibc_setting);
}
}
/* M4 again */
r = rtsp_message_read(m, "{<****hhh>}", "wfd_video_formats",
&cea_res, &vesa_res, &hh_res);
@ -300,9 +342,9 @@ static void sink_handle_set_parameter(struct ctl_sink *s,
if (r < 0)
return cli_vERR(r);
r = rtsp_message_append(rep, "<s>",
"Transport",
"RTP/AVP/UDP;unicast;client_port=1991");
char rtsp_setup[128];
sprintf(rtsp_setup, "RTP/AVP/UDP;unicast;client_port=%d", rstp_port);
r = rtsp_message_append(rep, "<s>", "Transport", rtsp_setup);
if (r < 0)
return cli_vERR(r);
@ -528,6 +570,8 @@ void ctl_sink_free(struct ctl_sink *s)
free(s->session);
free(s->url);
sd_event_unref(s->event);
if (s->protocol_extensions)
g_hash_table_destroy(s->protocol_extensions);
free(s);
}

92
src/ctl/ctl-sink.h Normal file
View file

@ -0,0 +1,92 @@
/*
* MiracleCast - Wifi-Display/Miracast Implementation
*
* 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/>.
*/
#ifndef CTL_SINK_H
#define CTL_SINK_H
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <systemd/sd-event.h>
#include <time.h>
#include <unistd.h>
#include <glib.h>
#include "ctl.h"
#include "rtsp.h"
#include "shl_macro.h"
#include "shl_util.h"
#include "wfd.h"
#define WFD_VIDEO_FORMATS "wfd_video_formats"
#define WFD_AUDIO_CODECS "wfd_audio_codecs"
#define WFD_UIBC_CAPABILITY "wfd_uibc_capability"
extern int rstp_port;
extern bool uibc_option;
extern bool uibc_enabled;
extern int uibc_port;
struct ctl_sink {
sd_event *event;
char *target;
char *session;
char *url;
char *uibc_config;
char *uibc_setting;
struct sockaddr_storage addr;
size_t addr_size;
int fd;
sd_event_source *fd_source;
struct rtsp *rtsp;
bool connected : 1;
bool hup : 1;
uint32_t resolutions_cea;
uint32_t resolutions_vesa;
uint32_t resolutions_hh;
int hres;
int vres;
GHashTable* protocol_extensions;
};
bool check_rtsp_option(struct rtsp_message *m, char *option);
#define check_and_response_option(option, response) \
if (check_rtsp_option(m, option)) { \
char option_response[512]; \
sprintf(option_response, "%s: %s", option, response); \
r = rtsp_message_append(rep, "{&}", option_response); \
if (r < 0) {\
return cli_vERR(r); \
} \
}
#endif /* CTL_SINK_H */

View file

@ -414,6 +414,8 @@ static int ctl_link_parse_properties(struct ctl_link *l,
bool p2p_scanning_set = false;
char *tmp;
int p2p_scanning, r;
bool managed_set = false;
int managed;
if (!l || !m)
return cli_EINVAL();
@ -444,6 +446,13 @@ static int ctl_link_parse_properties(struct ctl_link *l,
&friendly_name);
if (r < 0)
return cli_log_parser(r);
} else if (!strcmp(t, "Managed")) {
r = bus_message_read_basic_variant(m, "b",
&managed);
if (r < 0)
return cli_log_parser(r);
managed_set = true;
} else if (!strcmp(t, "P2PScanning")) {
r = bus_message_read_basic_variant(m, "b",
&p2p_scanning);
@ -494,6 +503,9 @@ static int ctl_link_parse_properties(struct ctl_link *l,
}
}
if (managed_set)
l->managed = managed;
if (p2p_scanning_set)
l->p2p_scanning = p2p_scanning;
@ -620,6 +632,63 @@ int ctl_link_set_wfd_subelements(struct ctl_link *l, const char *val)
return 0;
}
int ctl_link_set_managed(struct ctl_link *l, bool val)
{
_sd_bus_message_unref_ sd_bus_message *m = NULL;
_sd_bus_error_free_ sd_bus_error err = SD_BUS_ERROR_NULL;
_shl_free_ char *node = NULL;
int r;
if (!l)
return cli_EINVAL();
if (l->managed == val)
return 0;
r = sd_bus_path_encode("/org/freedesktop/miracle/wifi/link",
l->label,
&node);
if (r < 0)
return cli_ERR(r);
r = sd_bus_message_new_method_call(l->w->bus,
&m,
"org.freedesktop.miracle.wifi",
node,
"org.freedesktop.DBus.Properties",
"Set");
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)
return cli_log_create(r);
r = sd_bus_call(l->w->bus, m, 0, &err, NULL);
if (r < 0) {
cli_error("cannot change managed state on link %s to %d: %s",
l->label, val, bus_error_message(&err, r));
return r;
}
l->managed = val;
return 0;
}
int ctl_link_set_p2p_scanning(struct ctl_link *l, bool val)
{
_sd_bus_message_unref_ sd_bus_message *m = NULL;
@ -754,7 +823,7 @@ static int ctl_wifi_parse_peer(struct ctl_wifi *w,
l = ctl_wifi_find_link_by_peer(w, label);
if (!l)
return cli_EINVAL();
return 0;
r = ctl_peer_new(&p, l, label);
if (r < 0)

View file

@ -17,6 +17,9 @@
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CTL_CTL_H
#define CTL_CTL_H
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
@ -29,13 +32,22 @@
#include "shl_dlist.h"
#include "shl_log.h"
#ifndef CTL_CTL_H
#define CTL_CTL_H
/* *sigh* readline doesn't include all their deps, so put them last */
#include <readline/history.h>
#include <readline/readline.h>
#include <readline/rltypedefs.h>
struct ctl_wifi;
struct ctl_link;
struct ctl_peer;
char* get_history_filename ();
struct ctl_wifi * get_wifi ();
char * links_peers_generator (const char *text, int state);
char * links_generator (const char *text, int state);
char * peers_generator (const char *text, int state);
char * yes_no_generator (const char *text, int state);
/* wifi handling */
struct ctl_peer {
@ -71,6 +83,7 @@ struct ctl_link {
unsigned int ifindex;
char *ifname;
char *friendly_name;
bool managed;
char *wfd_subelements;
bool p2p_scanning;
};
@ -78,6 +91,7 @@ struct ctl_link {
#define link_from_dlist(_l) shl_dlist_entry((_l), struct ctl_link, list);
int ctl_link_set_friendly_name(struct ctl_link *l, const char *name);
int ctl_link_set_managed(struct ctl_link *l, bool val);
int ctl_link_set_wfd_subelements(struct ctl_link *l, const char *val);
int ctl_link_set_p2p_scanning(struct ctl_link *l, bool val);
@ -120,9 +134,11 @@ bool ctl_sink_is_closed(struct ctl_sink *s);
/* CLI handling */
extern int cli_max_sev;
void cli_printv(const char *fmt, va_list args);
extern unsigned int cli_max_sev;
void cli_printv(const char *fmt, bool prefix_time, va_list args);
void cli_printf_time_prefix();
void cli_printf(const char *fmt, ...);
void cli_command_printf(const char *fmt, ...);
#define cli_log(_fmt, ...) \
cli_printf(_fmt "\n", ##__VA_ARGS__)
@ -186,7 +202,6 @@ void cli_printf(const char *fmt, ...);
#define CLI_BLUE "\x1B[0;94m"
#define CLI_BOLDGRAY "\x1B[1;30m"
#define CLI_BOLDWHITE "\x1B[1;37m"
#define CLI_PROMPT CLI_BLUE "[miraclectl] # " CLI_DEFAULT
struct cli_cmd {
const char *cmd;
@ -204,6 +219,7 @@ struct cli_cmd {
int argc;
int (*fn) (char **args, unsigned int n);
const char *desc;
rl_compentry_func_t *completion_fns[2];
};
extern sd_event *cli_event;
@ -213,13 +229,14 @@ extern unsigned int wfd_supported_res_cea;
extern unsigned int wfd_supported_res_vesa;
extern unsigned int wfd_supported_res_hh;
char* get_cli_prompt();
int cli_init(sd_bus *bus, const struct cli_cmd *cmds);
void cli_destroy(void);
int cli_run(void);
void cli_exit(void);
bool cli_running(void);
int cli_help(const struct cli_cmd *cmds);
int cli_help(const struct cli_cmd *cmds, int whitespace);
int cli_do(const struct cli_cmd *cmds, char **args, unsigned int n);
/* callback functions */
@ -240,7 +257,7 @@ void ctl_fn_link_free(struct ctl_link *l);
void ctl_fn_sink_connected(struct ctl_sink *s);
void ctl_fn_sink_disconnected(struct ctl_sink *s);
void ctl_fn_sink_resolution_set(struct ctl_sink *s, int hres, int vres);
void ctl_fn_sink_resolution_set(struct ctl_sink *s);
void cli_fn_help(void);

24
src/ctl/meson.build Normal file
View file

@ -0,0 +1,24 @@
inc = include_directories('../..')
deps = [libsystemd, libmiracle_shared_dep, glib2, m]
if readline.found()
deps += readline
endif
miracle_wifictl_srcs = ['ctl-cli.c', 'ctl-wifi.c', 'wifictl.c']
executable('miracle-wifictl', miracle_wifictl_srcs,
install: true,
include_directories: inc,
dependencies: deps
)
miracle_sinkctl_srcs = ['ctl-cli.c',
'ctl-sink.c',
'ctl-wifi.c',
'sinkctl.c',
'wfd.c'
]
executable('miracle-sinkctl', miracle_sinkctl_srcs,
install: true,
include_directories: inc,
dependencies: deps
)

View file

@ -17,6 +17,8 @@
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <errno.h>
#include <getopt.h>
#include <locale.h>
@ -30,15 +32,27 @@
#include <sys/time.h>
#include <systemd/sd-bus.h>
#include <systemd/sd-event.h>
#ifdef ENABLE_SYSTEMD
#include <systemd/sd-journal.h>
#endif
#include <time.h>
#include <unistd.h>
#include "ctl.h"
#include "ctl-sink.h"
#include "wfd.h"
#include "shl_macro.h"
#include "shl_util.h"
#include "util.h"
#include "config.h"
#include <readline/readline.h>
#define HISTORY_FILENAME ".miracle-sink.history"
#define CLI_PROMPT "\001" CLI_BLUE "\002" "[sinkctl] # " "\001" CLI_DEFAULT "\002"
static sd_bus *bus;
static struct ctl_wifi *wifi;
static struct ctl_sink *sink;
@ -53,11 +67,41 @@ static struct ctl_link *running_link;
static struct ctl_peer *running_peer;
static struct ctl_peer *pending_peer;
void launch_player(struct ctl_sink *s);
char *gst_scale_res;
int gst_audio_en = 1;
unsigned int wfd_supported_res_cea = 0x0000001f; /* up to 720x576 */
unsigned int wfd_supported_res_vesa = 0x00000003; /* up to 800x600 */
unsigned int wfd_supported_res_hh = 0x00000000; /* not supported */
static const int DEFAULT_RSTP_PORT = 7236;
bool uibc_option;
bool uibc_enabled;
bool external_player;
int rstp_port;
int uibc_port;
char* player;
GHashTable* protocol_extensions;
unsigned int wfd_supported_res_cea = 0x0001ffff;
unsigned int wfd_supported_res_vesa = 0x1fffffff;
unsigned int wfd_supported_res_hh = 0x00001fff;
struct ctl_wifi *get_wifi()
{
return wifi;
}
char* get_cli_prompt()
{
return CLI_PROMPT;
}
/*
* get history filename
*/
char* get_history_filename()
{
return HISTORY_FILENAME;
}
/*
* cmd list
@ -72,27 +116,28 @@ static int cmd_list(char **args, unsigned int n)
/* list links */
cli_printf("%6s %-24s %-30s\n",
"LINK", "INTERFACE", "FRIENDLY-NAME");
cli_command_printf("%6s %-24s %-30s %-10s\n",
"LINK", "INTERFACE", "FRIENDLY-NAME", "MANAGED");
shl_dlist_for_each(i, &wifi->links) {
l = link_from_dlist(i);
++link_cnt;
cli_printf("%6s %-24s %-30s\n",
l->label,
shl_isempty(l->ifname) ?
"<unknown>" : l->ifname,
cli_command_printf("%6s %-24s %-30s %-10s\n",
l->label,
shl_isempty(l->ifname) ?
"<unknown>" : l->ifname,
shl_isempty(l->friendly_name) ?
"<unknown>" : l->friendly_name);
"<unknown>" : l->friendly_name,
l->managed ? "yes": "no");
}
cli_printf("\n");
cli_command_printf("\n");
/* list peers */
cli_printf("%6s %-24s %-30s %-10s\n",
"LINK", "PEER-ID", "FRIENDLY-NAME", "CONNECTED");
cli_command_printf("%6s %-24s %-30s %-10s\n",
"LINK", "PEER-ID", "FRIENDLY-NAME", "CONNECTED");
shl_dlist_for_each(i, &wifi->links) {
l = link_from_dlist(i);
@ -101,16 +146,16 @@ static int cmd_list(char **args, unsigned int n)
p = peer_from_dlist(j);
++peer_cnt;
cli_printf("%6s %-24s %-30s %-10s\n",
p->l->label,
p->label,
shl_isempty(p->friendly_name) ?
cli_command_printf("%6s %-24s %-30s %-10s\n",
p->l->label,
p->label,
shl_isempty(p->friendly_name) ?
"<unknown>" : p->friendly_name,
p->connected ? "yes" : "no");
}
}
cli_printf("\n %u peers and %u links listed.\n", peer_cnt, link_cnt);
cli_command_printf("\n %u peers and %u links listed.\n", peer_cnt, link_cnt);
return 0;
}
@ -129,45 +174,81 @@ static int cmd_show(char **args, unsigned int n)
!(p = ctl_wifi_find_peer(wifi, args[0])) &&
!(l = ctl_wifi_search_link(wifi, args[0])) &&
!(p = ctl_wifi_search_peer(wifi, args[0]))) {
cli_error("unknown link or peer %s", args[0]);
cli_error("unknown %s", args[0]);
return 0;
}
}
if (l) {
cli_printf("Link=%s\n", l->label);
cli_command_printf("Link=%s\n", l->label);
if (l->ifindex > 0)
cli_printf("InterfaceIndex=%u\n", l->ifindex);
cli_command_printf("InterfaceIndex=%u\n", l->ifindex);
if (l->ifname && *l->ifname)
cli_printf("InterfaceName=%s\n", l->ifname);
cli_command_printf("InterfaceName=%s\n", l->ifname);
if (l->friendly_name && *l->friendly_name)
cli_printf("FriendlyName=%s\n", l->friendly_name);
cli_printf("P2PScanning=%d\n", l->p2p_scanning);
cli_command_printf("FriendlyName=%s\n", l->friendly_name);
cli_command_printf("P2PScanning=%d\n", l->p2p_scanning);
if (l->wfd_subelements && *l->wfd_subelements)
cli_printf("WfdSubelements=%s\n", l->wfd_subelements);
cli_command_printf("WfdSubelements=%s\n", l->wfd_subelements);
cli_command_printf("Managed=%d\n", l->managed);
} else if (p) {
cli_printf("Peer=%s\n", p->label);
cli_command_printf("Peer=%s\n", p->label);
if (p->p2p_mac && *p->p2p_mac)
cli_printf("P2PMac=%s\n", p->p2p_mac);
cli_command_printf("P2PMac=%s\n", p->p2p_mac);
if (p->friendly_name && *p->friendly_name)
cli_printf("FriendlyName=%s\n", p->friendly_name);
cli_printf("Connected=%d\n", p->connected);
cli_command_printf("FriendlyName=%s\n", p->friendly_name);
cli_command_printf("Connected=%d\n", p->connected);
if (p->interface && *p->interface)
cli_printf("Interface=%s\n", p->interface);
cli_command_printf("Interface=%s\n", p->interface);
if (p->local_address && *p->local_address)
cli_printf("LocalAddress=%s\n", p->local_address);
cli_command_printf("LocalAddress=%s\n", p->local_address);
if (p->remote_address && *p->remote_address)
cli_printf("RemoteAddress=%s\n", p->remote_address);
cli_command_printf("RemoteAddress=%s\n", p->remote_address);
if (p->wfd_subelements && *p->wfd_subelements)
cli_printf("WfdSubelements=%s\n", p->wfd_subelements);
cli_command_printf("WfdSubelements=%s\n", p->wfd_subelements);
} else {
cli_printf("Show what?\n");
cli_command_printf("Show what?\n");
return 0;
}
return 0;
}
/*
* cmd: set-friendly-name
*/
static int cmd_set_friendly_name(char **args, unsigned int n)
{
struct ctl_link *l = NULL;
const char *name;
if (n < 1) {
cli_command_printf("To what?\n");
return 0;
}
if (n > 1) {
l = ctl_wifi_search_link(wifi, args[0]);
if (!l) {
cli_error("unknown link %s", args[0]);
return 0;
}
name = args[1];
} else {
name = args[0];
}
l = l ? : running_link;
if (!l) {
cli_error("no running link");
return 0;
}
return ctl_link_set_friendly_name(l, name);
}
/*
* cmd: run
*/
@ -198,6 +279,11 @@ static int cmd_run(char **args, unsigned int n)
return 0;
}
if (!l->managed) {
cli_printf("link %s not managed\n", l->label);
return 0;
}
run_on(l);
return 0;
@ -228,11 +314,37 @@ static int cmd_bind(char **args, unsigned int n)
if (!l)
return 0;
if (!l->managed) {
cli_printf("link %s not managed\n", l->label);
return 0;
}
run_on(l);
return 0;
}
/*
* cmd: set-managed
*/
static int cmd_set_managed(char **args, unsigned int n)
{
struct ctl_link *l = NULL;
bool managed = true;
l = ctl_wifi_search_link(wifi, args[0]);
if (!l) {
cli_error("unknown link %s", args[0]);
return 0;
}
if (!strcmp(args[1], "no")) {
managed = false;
}
return ctl_link_set_managed(l, managed);
}
/*
* cmd: quit/exit
*/
@ -325,22 +437,22 @@ static int sink_timeout_fn(sd_event_source *s, uint64_t usec, void *data)
}
static const struct cli_cmd cli_cmds[] = {
{ "list", NULL, CLI_M, CLI_LESS, 0, cmd_list, "List all objects" },
{ "show", "<link|peer>", CLI_M, CLI_LESS, 1, cmd_show, "Show detailed object information" },
{ "run", "<link>", CLI_M, CLI_EQUAL, 1, cmd_run, "Run sink on given link" },
{ "bind", "<link>", CLI_M, CLI_EQUAL, 1, cmd_bind, "Like 'run' but bind the link name to run when it is hotplugged" },
{ "quit", NULL, CLI_Y, CLI_MORE, 0, cmd_quit, "Quit program" },
{ "exit", NULL, CLI_Y, CLI_MORE, 0, cmd_quit, NULL },
{ "help", NULL, CLI_M, CLI_MORE, 0, NULL, "Print help" },
{ "list", NULL, CLI_M, CLI_LESS, 0, cmd_list, "List all objects", {NULL} },
{ "show", "<link|peer>", CLI_M, CLI_LESS, 1, cmd_show, "Show detailed object information", {links_peers_generator, NULL} },
{ "run", "<link>", CLI_M, CLI_EQUAL, 1, cmd_run, "Run sink on given link", {links_generator, NULL} },
{ "bind", "<link>", CLI_M, CLI_EQUAL, 1, cmd_bind, "Like 'run' but bind the link name to run when it is hotplugged", {links_generator, NULL} },
{ "set-friendly-name", "[link] <name>", CLI_M, CLI_LESS, 2, cmd_set_friendly_name, "Set friendly name of an object", {links_generator, NULL} },
{ "set-managed", "<link> <yes|no>", CLI_M, CLI_EQUAL, 2, cmd_set_managed, "Manage or unmnage a link", {links_generator, yes_no_generator} },
{ "quit", NULL, CLI_Y, CLI_MORE, 0, cmd_quit, "Quit program", {NULL} },
{ "exit", NULL, CLI_Y, CLI_MORE, 0, cmd_quit, NULL, {NULL} },
{ "help", NULL, CLI_M, CLI_MORE, 0, NULL, "Print help", {NULL} },
{ },
};
static void spawn_gst(int hres, int vres)
static void spawn_gst(struct ctl_sink *s)
{
char *argv[64];
char resolution[64];
pid_t pid;
int fd_journal, i;
int fd_journal;
sigset_t mask;
if (sink_pid > 0)
@ -355,43 +467,113 @@ static void spawn_gst(int hres, int vres)
sigemptyset(&mask);
sigprocmask(SIG_SETMASK, &mask, NULL);
#ifdef ENABLE_SYSTEMD
/* redirect stdout/stderr to journal */
fd_journal = sd_journal_stream_fd("miracle-sinkctl-gst",
LOG_INFO,
LOG_DEBUG,
false);
if (fd_journal >= 0) {
/* dup journal-fd to stdout and stderr */
dup2(fd_journal, 1);
dup2(fd_journal, 2);
} else {
#endif
/* no journal? redirect stdout to parent's stderr */
dup2(2, 1);
#ifdef ENABLE_SYSTEMD
}
#endif
i = 0;
argv[i++] = (char*) BUILD_BINDIR "/miracle-gst.sh";
if (cli_max_sev >= 7)
argv[i++] = "-d 3";
if (gst_audio_en)
argv[i++] = "-a";
if (gst_scale_res) {
argv[i++] = "-s";
argv[i++] = gst_scale_res;
}
if (hres && vres) {
sprintf(resolution, "%dx%d", hres, vres);
argv[i++] = "-r";
argv[i++] = resolution;
}
argv[i] = NULL;
execve(argv[0], argv, environ);
launch_player(s);
_exit(1);
} else {
sink_pid = pid;
}
}
void launch_player(struct ctl_sink *s) {
char *argv[64];
char resolution[64];
char port[64];
char uibc_portStr[64];
int i = 0;
if (!external_player) {
if (uibc_enabled) {
player = "uibc-viewer";
} else {
player = "miracle-gst";
}
}
argv[i++] = player;
if (uibc_enabled) {
argv[i++] = s->target;
sprintf(uibc_portStr, "%d", uibc_port);
argv[i++] = uibc_portStr;
}
if (gst_debug) {
argv[i++] = "-d";
argv[i++] = gst_debug;
} else if (cli_max_sev >= LOG_DEBUG) {
argv[i++] = "-d";
argv[i++] = "3";
}
if (gst_audio_en)
argv[i++] = "-a";
if (gst_scale_res) {
argv[i++] = "-s";
argv[i++] = gst_scale_res;
}
argv[i++] = "-p";
sprintf(port, "%d", rstp_port);
argv[i++] = port;
if (s->hres && s->vres) {
sprintf(resolution, "%dx%d", s->hres, s->vres);
argv[i++] = "-r";
argv[i++] = resolution;
}
argv[i] = NULL;
i = 0;
size_t size = 0;
while (argv[i]) {
size += strlen(argv[i++]) + 1;
}
char* player_command = malloc(size);
i = 0;
strcpy(player_command, argv[i++]);
while (argv[i]) {
strcat(player_command, " ");
strcat(player_command, argv[i++]);
}
log_debug("player command: %s", player_command);
if (execvpe(argv[0], argv, environ) < 0) {
cli_debug("stream player failed (%d): %m", errno);
int i = 0;
cli_debug("printing environment: ");
while (environ[i]) {
cli_debug("%s", environ[i++]);
}
}
}
void launch_uibc_daemon(int port) {
char *argv[64];
char portStr[64];
int i = 0;
argv[i++] = "miracle-uibcctl";
argv[i++] = "localhost";
sprintf(portStr, "%d", port);
argv[i++] = portStr;
argv[i] = NULL;
cli_debug("uibc daemon: %s", argv[0]);
execvpe(argv[0], argv, environ);
}
static void kill_gst(void)
{
if (sink_pid <= 0)
@ -418,11 +600,11 @@ void ctl_fn_sink_disconnected(struct ctl_sink *s)
}
}
void ctl_fn_sink_resolution_set(struct ctl_sink *s, int hres, int vres)
void ctl_fn_sink_resolution_set(struct ctl_sink *s)
{
cli_printf("SINK set resolution %dx%d\n", hres, vres);
cli_printf("SINK set resolution %dx%d\n", s->hres, s->vres);
if (sink_connected)
spawn_gst(hres, vres);
spawn_gst(s);
}
void ctl_fn_peer_new(struct ctl_peer *p)
@ -601,20 +783,29 @@ void cli_fn_help()
*/
printf("%s [OPTIONS...] ...\n\n"
"Control a dedicated local sink via MiracleCast.\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --log-level <lvl> Maximum level for log messages\n"
" --audio <0/1> Enable audio support (default %d)\n"
" --scale WxH Scale to resolution\n"
" --res <n,n,n> Supported resolutions masks (CEA, VESA, HH)\n"
" default CEA %08X\n"
" default VESA %08X\n"
" default HH %08X\n"
" -h --help Show this help\n"
" --help-commands Show available commands\n"
" --version Show package version\n"
" --log-level <lvl> Maximum level for log messages\n"
" --log-time Prefix log-messages with timestamp\n"
" --log-date-time Prefix log-messages with date time\n"
"\n"
, program_invocation_short_name, gst_audio_en,
" --log-journal-level <lvl> Maximum level for journal log messages\n"
" --gst-debug [cat:]lvl[,...] List of categories an level of debug\n"
" --audio <0/1> Enable audio support (default %d)\n"
" --scale WxH Scale to resolution\n"
" -p --port <port> Port for rtsp (default %d)\n"
" --uibc Enables UIBC\n"
" -e --external-player Configure player to use\n"
" --res <n,n,n> Supported resolutions masks (CEA, VESA, HH)\n"
" default CEA %08X\n"
" default VESA %08X\n"
" default HH %08X\n"
" --help-res Shows available values for res\n"
"\n"
, program_invocation_short_name, gst_audio_en, DEFAULT_RSTP_PORT,
wfd_supported_res_cea, wfd_supported_res_vesa, wfd_supported_res_hh
);
wfd_print_resolutions();
/*
* 80-char barrier:
* 01234567890123456789012345678901234567890123456789012345678901234567890123456789
@ -632,6 +823,7 @@ static int ctl_interactive(char **argv, int argc)
r = ctl_sink_new(&sink, cli_event);
if (r < 0)
goto error;
sink->protocol_extensions = protocol_extensions;
r = ctl_wifi_fetch(wifi);
if (r < 0)
@ -657,12 +849,19 @@ static int ctl_main(int argc, char *argv[])
struct ctl_link *l;
int r, left;
if (getuid() != 0) {
r = EACCES;
log_notice("Must run as root");
return r;
}
r = ctl_wifi_new(&wifi, bus);
if (r < 0)
return r;
left = argc - optind;
r = ctl_interactive(argv + optind, left <= 0 ? 0 : left);
left = argc - optind;
left = left <= 0 ? 0 : left;
r = ctl_interactive(argv + optind, left);
/* stop all scans */
shl_dlist_for_each(i, &wifi->links) {
@ -680,31 +879,70 @@ static int parse_argv(int argc, char *argv[])
enum {
ARG_VERSION = 0x100,
ARG_LOG_LEVEL,
ARG_LOG_TIME,
ARG_LOG_DATE_TIME,
ARG_JOURNAL_LEVEL,
ARG_GST_DEBUG,
ARG_AUDIO,
ARG_SCALE,
ARG_RES,
ARG_HELP_RES,
ARG_UIBC,
ARG_HELP_COMMANDS,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "log-level", required_argument, NULL, ARG_LOG_LEVEL },
{ "help", no_argument, NULL, 'h' },
{ "help-commands", no_argument, NULL, ARG_HELP_COMMANDS },
{ "version" , no_argument, NULL, ARG_VERSION },
{ "log-level", required_argument, NULL, ARG_LOG_LEVEL },
{ "log-time", no_argument, NULL, ARG_LOG_TIME },
{ "log-date-time", no_argument, NULL, ARG_LOG_DATE_TIME },
{ "log-journal-level", required_argument, NULL, ARG_JOURNAL_LEVEL },
{ "gst-debug", required_argument, NULL, ARG_GST_DEBUG },
{ "audio", required_argument, NULL, ARG_AUDIO },
{ "scale", required_argument, NULL, ARG_SCALE },
{ "res", required_argument, NULL, ARG_RES },
{ "help-res", no_argument, NULL, ARG_HELP_RES },
{ "port", required_argument, NULL, 'p' },
{ "uibc", no_argument, NULL, ARG_UIBC },
{ "external-player", required_argument, NULL, 'e' },
{}
};
int c;
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
uibc_option = false;
uibc_enabled = false;
external_player = false;
rstp_port = DEFAULT_RSTP_PORT;
while ((c = getopt_long(argc, argv, "he:p:", options, NULL)) >= 0) {
switch (c) {
case 'h':
return cli_help(cli_cmds);
cli_fn_help();
return 0;
case ARG_HELP_COMMANDS:
return cli_help(cli_cmds, 20);
case ARG_HELP_RES:
wfd_print_resolutions("");
return 0;
case ARG_VERSION:
puts(PACKAGE_STRING);
return 0;
case ARG_LOG_LEVEL:
cli_max_sev = log_parse_arg(optarg);
break;
case ARG_LOG_TIME:
log_init_time();
break;
case ARG_LOG_DATE_TIME:
log_date_time = true;
break;
case ARG_GST_DEBUG:
gst_debug = optarg;
break;
case ARG_JOURNAL_LEVEL:
log_max_sev = log_parse_arg(optarg);
break;
case ARG_AUDIO:
gst_audio_en = atoi(optarg);
break;
@ -717,6 +955,16 @@ static int parse_argv(int argc, char *argv[])
&wfd_supported_res_vesa,
&wfd_supported_res_hh);
break;
case 'p':
rstp_port = atoi(optarg);
break;
case 'e':
external_player = true;
player = optarg;
break;
case ARG_UIBC:
uibc_option = true;
break;
case '?':
return -EINVAL;
}
@ -728,9 +976,75 @@ static int parse_argv(int argc, char *argv[])
int main(int argc, char **argv)
{
int r;
bool free_argv = false;
setlocale(LC_ALL, "");
GKeyFile* gkf = load_ini_file();
gchar** autocmds_free = NULL;
if (gkf) {
player = g_key_file_get_string (gkf, "sinkctl", "external-player", NULL);
if (player) {
external_player = true;
}
gchar* log_level;
log_level = g_key_file_get_string (gkf, "sinkctl", "log-journal-level", NULL);
if (log_level) {
log_max_sev = log_parse_arg(log_level);
g_free(log_level);
}
log_level = g_key_file_get_string (gkf, "sinkctl", "log-level", NULL);
if (log_level) {
cli_max_sev = log_parse_arg(log_level);
g_free(log_level);
}
gchar* rstp_port_str = g_key_file_get_string (gkf, "sinkctl", "rstp-port", NULL);
if (rstp_port_str) {
rstp_port = atoi(rstp_port_str);
g_free(rstp_port_str);
}
gchar* autocmd;
autocmd = g_key_file_get_string (gkf, "sinkctl", "autocmd", NULL);
if (autocmd) {
gchar** autocmds = g_strsplit(autocmd, " ", -1);
autocmds_free = autocmds;
while (*autocmds) {
if (strcmp(*autocmds, "") != 0) {
gchar **newv = malloc((argc + 2) * sizeof(gchar*));
memmove(newv, argv, sizeof(gchar*) * argc);
newv[argc] = *autocmds;
newv[argc+1] = NULL;
argc++;
if (free_argv) {
free(argv);
}
argv = newv;
free_argv = true;
}
autocmds++;
}
g_free(autocmd);
}
gchar** sinkctl_keys;
gsize len = 0;
protocol_extensions = g_hash_table_new(g_str_hash, g_str_equal);
sinkctl_keys = g_key_file_get_keys (gkf,
"sinkctl",
&len,
NULL);
for (int i = 0; i < (int)len; i++) {
if (g_str_has_prefix(sinkctl_keys[i], "extends.")) {
gchar* orig_key = sinkctl_keys[i];
gchar* key = orig_key+8;
gchar* value = g_key_file_get_string (gkf, "sinkctl", orig_key, NULL);
g_hash_table_insert(protocol_extensions, key, value);
}
}
g_key_file_free(gkf);
}
r = parse_argv(argc, argv);
if (r < 0)
return EXIT_FAILURE;
@ -744,6 +1058,10 @@ int main(int argc, char **argv)
}
r = ctl_main(argc, argv);
g_strfreev(autocmds_free);
if (free_argv) {
free(argv);
}
sd_bus_unref(bus);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;

View file

@ -103,27 +103,27 @@ struct resolution_bitmap resolutions_hh[] = {
{0, 0, 0, 0},
};
void wfd_print_resolutions(void)
void wfd_print_resolutions(char * prefix)
{
int i;
printf("CEA resolutions:\n");
printf("%sCEA resolutions:\n", prefix);
for (i = 0; resolutions_cea[i].hres != 0; i++) {
printf("\t%2d %08x %4dx%4d@%d\n",
printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
resolutions_cea[i].index, 1 << resolutions_cea[i].index,
resolutions_cea[i].hres, resolutions_cea[i].vres,
resolutions_cea[i].fps);
}
printf("VESA resolutions:\n");
printf("%sVESA resolutions:\n", prefix);
for (i = 0; resolutions_vesa[i].hres != 0; i++) {
printf("\t%2d %08x %4dx%4d@%d\n",
printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
resolutions_vesa[i].index, 1 << resolutions_vesa[i].index,
resolutions_vesa[i].hres, resolutions_vesa[i].vres,
resolutions_vesa[i].fps);
}
printf("HH resolutions:\n");
printf("%sHH resolutions:\n", prefix);
for (i = 0; resolutions_hh[i].hres != 0; i++) {
printf("\t%2d %08x %4dx%4d@%d\n",
printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
resolutions_hh[i].index, 1 << resolutions_hh[i].index,
resolutions_hh[i].hres, resolutions_hh[i].vres,
resolutions_hh[i].fps);

View file

@ -21,7 +21,7 @@
#ifndef WFD_H
#define WFD_H
void wfd_print_resolutions(void);
void wfd_print_resolutions(char * prefix);
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_hh_resolution(uint32_t mask, int *hres, int *vres);

View file

@ -31,13 +31,39 @@
#include "ctl.h"
#include "shl_macro.h"
#include "shl_util.h"
#include "util.h"
#include "config.h"
#include <readline/readline.h>
#define HISTORY_FILENAME ".miracle-wifi.history"
#define CLI_PROMPT "\001" CLI_BLUE "\002" "[wifictl] # " "\001" CLI_DEFAULT "\002"
static sd_bus *bus;
static struct ctl_wifi *wifi;
static struct ctl_link *selected_link;
char* get_cli_prompt()
{
return CLI_PROMPT;
}
/*
* get history filename
*/
char* get_history_filename()
{
return HISTORY_FILENAME;
}
struct ctl_wifi *get_wifi()
{
return wifi;
}
/*
* cmd list
*/
@ -51,26 +77,27 @@ static int cmd_list(char **args, unsigned int n)
/* list links */
cli_printf("%6s %-24s %-30s\n",
"LINK", "INTERFACE", "FRIENDLY-NAME");
cli_command_printf("%6s %-24s %-30s %-10s\n",
"LINK", "INTERFACE", "FRIENDLY-NAME", "MANAGED");
shl_dlist_for_each(i, &wifi->links) {
l = link_from_dlist(i);
++link_cnt;
cli_printf("%6s %-24s %-30s\n",
l->label,
shl_isempty(l->ifname) ?
"<unknown>" : l->ifname,
shl_isempty(l->friendly_name) ?
"<unknown>" : l->friendly_name);
cli_command_printf("%6s %-24s %-30s %-10s\n",
l->label,
shl_isempty(l->ifname) ?
"<unknown>" : l->ifname,
shl_isempty(l->friendly_name) ?
"<unknown>" : l->friendly_name,
l->managed ? "yes": "no");
}
cli_printf("\n");
cli_command_printf("\n");
/* list peers */
cli_printf("%6s %-24s %-30s %-10s\n",
cli_command_printf("%6s %-24s %-30s %-10s\n",
"LINK", "PEER-ID", "FRIENDLY-NAME", "CONNECTED");
shl_dlist_for_each(i, &wifi->links) {
@ -80,16 +107,16 @@ static int cmd_list(char **args, unsigned int n)
p = peer_from_dlist(j);
++peer_cnt;
cli_printf("%6s %-24s %-30s %-10s\n",
p->l->label,
p->label,
shl_isempty(p->friendly_name) ?
"<unknown>" : p->friendly_name,
p->connected ? "yes" : "no");
cli_command_printf("%6s %-24s %-30s %-10s\n",
p->l->label,
p->label,
shl_isempty(p->friendly_name) ?
"<unknown>" : p->friendly_name,
p->connected ? "yes" : "no");
}
}
cli_printf("\n %u peers and %u links listed.\n", peer_cnt, link_cnt);
cli_command_printf("\n %u peers and %u links listed.\n", peer_cnt, link_cnt);
return 0;
}
@ -119,6 +146,7 @@ static int cmd_select(char **args, unsigned int n)
}
selected_link = l;
ctl_link_set_wfd_subelements(l, "000600111c4400c8");
cli_printf("link %s selected\n", selected_link->label);
return 0;
@ -146,33 +174,34 @@ static int cmd_show(char **args, unsigned int n)
}
if (l) {
cli_printf("Link=%s\n", l->label);
cli_command_printf("Link=%s\n", l->label);
if (l->ifindex > 0)
cli_printf("InterfaceIndex=%u\n", l->ifindex);
cli_command_printf("InterfaceIndex=%u\n", l->ifindex);
if (l->ifname && *l->ifname)
cli_printf("InterfaceName=%s\n", l->ifname);
cli_command_printf("InterfaceName=%s\n", l->ifname);
if (l->friendly_name && *l->friendly_name)
cli_printf("FriendlyName=%s\n", l->friendly_name);
cli_printf("P2PScanning=%d\n", l->p2p_scanning);
cli_command_printf("FriendlyName=%s\n", l->friendly_name);
cli_command_printf("P2PScanning=%d\n", l->p2p_scanning);
if (l->wfd_subelements && *l->wfd_subelements)
cli_printf("WfdSubelements=%s\n", l->wfd_subelements);
cli_command_printf("WfdSubelements=%s\n", l->wfd_subelements);
cli_command_printf("Managed=%d\n", l->managed);
} else if (p) {
cli_printf("Peer=%s\n", p->label);
cli_command_printf("Peer=%s\n", p->label);
if (p->p2p_mac && *p->p2p_mac)
cli_printf("P2PMac=%s\n", p->p2p_mac);
cli_command_printf("P2PMac=%s\n", p->p2p_mac);
if (p->friendly_name && *p->friendly_name)
cli_printf("FriendlyName=%s\n", p->friendly_name);
cli_printf("Connected=%d\n", p->connected);
cli_command_printf("FriendlyName=%s\n", p->friendly_name);
cli_command_printf("Connected=%d\n", p->connected);
if (p->interface && *p->interface)
cli_printf("Interface=%s\n", p->interface);
cli_command_printf("Interface=%s\n", p->interface);
if (p->local_address && *p->local_address)
cli_printf("LocalAddress=%s\n", p->local_address);
cli_command_printf("LocalAddress=%s\n", p->local_address);
if (p->remote_address && *p->remote_address)
cli_printf("RemoteAddress=%s\n", p->remote_address);
cli_command_printf("RemoteAddress=%s\n", p->remote_address);
if (p->wfd_subelements && *p->wfd_subelements)
cli_printf("WfdSubelements=%s\n", p->wfd_subelements);
cli_command_printf("WfdSubelements=%s\n", p->wfd_subelements);
} else {
cli_printf("Show what?\n");
cli_command_printf("Show what?\n");
return 0;
}
@ -189,7 +218,7 @@ static int cmd_set_friendly_name(char **args, unsigned int n)
const char *name;
if (n < 1) {
cli_printf("To what?\n");
cli_command_printf("To what?\n");
return 0;
}
@ -214,6 +243,45 @@ static int cmd_set_friendly_name(char **args, unsigned int n)
return ctl_link_set_friendly_name(l, name);
}
/*
* cmd: set-managed
*/
static int cmd_set_managed(char **args, unsigned int n)
{
struct ctl_link *l = NULL;
const char *value;
bool managed = true;
if (n < 1) {
cli_command_printf("To what?\n");
return 0;
}
if (n > 1) {
l = ctl_wifi_search_link(wifi, args[0]);
if (!l) {
cli_error("unknown link %s", args[0]);
return 0;
}
value = args[1];
} else {
value = args[0];
}
l = l ? : selected_link;
if (!l) {
cli_error("no link selected");
return 0;
}
if (!strcmp(value, "no")) {
managed = false;
}
return ctl_link_set_managed(l, managed);
}
/*
* cmd: p2p-scan
*/
@ -242,6 +310,11 @@ static int cmd_p2p_scan(char **args, unsigned int n)
return 0;
}
if (!l->managed) {
cli_printf("link %s not managed\n", l->label);
return 0;
}
return ctl_link_set_p2p_scanning(l, !stop);
}
@ -263,7 +336,7 @@ static int cmd_connect(char **args, unsigned int n)
const char *prov, *pin;
if (n < 1) {
cli_printf("To whom?\n");
cli_command_printf("To whom?\n");
return 0;
}
@ -289,6 +362,11 @@ static int cmd_connect(char **args, unsigned int n)
pin = "";
}
if (!p->l->managed) {
cli_printf("link %s not managed\n", p->l->label);
return 0;
}
return ctl_peer_connect(p, prov, pin);
}
@ -301,7 +379,7 @@ static int cmd_disconnect(char **args, unsigned int n)
struct ctl_peer *p;
if (n < 1) {
cli_printf("From whom?\n");
cli_command_printf("From whom?\n");
return 0;
}
@ -311,6 +389,11 @@ static int cmd_disconnect(char **args, unsigned int n)
return 0;
}
if (!p->l->managed) {
cli_printf("link %s not managed\n", p->l->label);
return 0;
}
return ctl_peer_disconnect(p);
}
@ -329,16 +412,17 @@ static int cmd_quit(char **args, unsigned int n)
*/
static const struct cli_cmd cli_cmds[] = {
{ "list", NULL, CLI_M, CLI_LESS, 0, cmd_list, "List all objects" },
{ "select", "[link]", CLI_Y, CLI_LESS, 1, cmd_select, "Select default link" },
{ "show", "[link|peer]", CLI_M, CLI_LESS, 1, cmd_show, "Show detailed object information" },
{ "set-friendly-name", "[link] <name>", CLI_M, CLI_LESS, 2, cmd_set_friendly_name, "Set friendly name of an object" },
{ "p2p-scan", "[link] [stop]", CLI_Y, CLI_LESS, 2, cmd_p2p_scan, "Control neighborhood P2P scanning" },
{ "connect", "<peer> [provision] [pin]", CLI_M, CLI_LESS, 3, cmd_connect, "Connect to peer" },
{ "disconnect", "<peer>", CLI_M, CLI_EQUAL, 1, cmd_disconnect, "Disconnect from peer" },
{ "quit", NULL, CLI_Y, CLI_MORE, 0, cmd_quit, "Quit program" },
{ "exit", NULL, CLI_Y, CLI_MORE, 0, cmd_quit, NULL },
{ "help", NULL, CLI_M, CLI_MORE, 0, NULL, "Print help" },
{ "list", NULL, CLI_M, CLI_LESS, 0, cmd_list, "List all objects", {NULL}},
{ "select", "[link]", CLI_Y, CLI_LESS, 1, cmd_select, "Select default link", {links_generator, NULL} },
{ "show", "[link|peer]", CLI_M, CLI_LESS, 1, cmd_show, "Show detailed object information", {links_peers_generator, NULL} },
{ "set-friendly-name", "[link] <name>", CLI_M, CLI_LESS, 2, cmd_set_friendly_name, "Set friendly name of an object", {links_generator, yes_no_generator} },
{ "set-managed", "[link] <yes|no>", CLI_M, CLI_LESS, 2, cmd_set_managed, "Manage or unmnage a link" },
{ "p2p-scan", "[link] [stop]", CLI_Y, CLI_LESS, 2, cmd_p2p_scan, "Control neighborhood P2P scanning", {links_generator, NULL} },
{ "connect", "<peer> [provision] [pin]", CLI_M, CLI_LESS, 3, cmd_connect, "Connect to peer", {peers_generator, NULL} },
{ "disconnect", "<peer>", CLI_M, CLI_EQUAL, 1, cmd_disconnect, "Disconnect from peer", {peers_generator, NULL} },
{ "quit", NULL, CLI_Y, CLI_MORE, 0, cmd_quit, "Quit program", {NULL} },
{ "exit", NULL, CLI_Y, CLI_MORE, 0, cmd_quit, NULL , {NULL}},
{ "help", NULL, CLI_M, CLI_MORE, 0, NULL, "Print help" , {NULL} },
{ },
};
@ -419,9 +503,13 @@ void cli_fn_help()
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Send control command to or query the MiracleCast Wifi-Manager. If no arguments\n"
"are given, an interactive command-line tool is provided.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --log-level <lvl> Maximum level for log messages\n"
" -h --help Show this help\n"
" --help-commands Show available commands\n"
" --version Show package version\n"
" --log-level <lvl> Maximum level for log messages\n"
" --log-time Prefix log-messages with timestamp\n"
" --log-date-time Prefix log-messages with date time\n"
" --log-journal-level <lvl> Maximum level for journal log messages\n"
"\n"
"Commands:\n"
, program_invocation_short_name);
@ -497,11 +585,19 @@ static int parse_argv(int argc, char *argv[])
enum {
ARG_VERSION = 0x100,
ARG_LOG_LEVEL,
ARG_LOG_TIME,
ARG_LOG_DATE_TIME,
ARG_JOURNAL_LEVEL,
ARG_HELP_COMMANDS,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "log-level", required_argument, NULL, ARG_LOG_LEVEL },
{ "help", no_argument, NULL, 'h' },
{ "help-commands", no_argument, NULL, ARG_HELP_COMMANDS },
{ "version", no_argument, NULL, ARG_VERSION },
{ "log-level", required_argument, NULL, ARG_LOG_LEVEL },
{ "log-time", no_argument, NULL, ARG_LOG_TIME },
{ "log-date-time", no_argument, NULL, ARG_LOG_DATE_TIME },
{ "log-journal-level", required_argument, NULL, ARG_JOURNAL_LEVEL },
{}
};
int c;
@ -509,13 +605,25 @@ static int parse_argv(int argc, char *argv[])
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
switch (c) {
case 'h':
return cli_help(cli_cmds);
cli_fn_help();
return 0;
case ARG_HELP_COMMANDS:
return cli_help(cli_cmds, 20);
case ARG_VERSION:
puts(PACKAGE_STRING);
return 0;
case ARG_LOG_LEVEL:
cli_max_sev = log_parse_arg(optarg);
break;
case ARG_LOG_TIME:
log_init_time();
break;
case ARG_LOG_DATE_TIME:
log_date_time = true;
break;
case ARG_JOURNAL_LEVEL:
log_max_sev = log_parse_arg(optarg);
break;
case '?':
return -EINVAL;
}
@ -530,6 +638,23 @@ int main(int argc, char **argv)
setlocale(LC_ALL, "");
GKeyFile* gkf = load_ini_file();
if (gkf) {
gchar* log_level;
log_level = g_key_file_get_string (gkf, "wifictl", "log-journal-level", NULL);
if (log_level) {
log_max_sev = log_parse_arg(log_level);
g_free(log_level);
}
log_level = g_key_file_get_string (gkf, "wifictl", "log-level", NULL);
if (log_level) {
cli_max_sev = log_parse_arg(log_level);
g_free(log_level);
}
g_key_file_free(gkf);
}
r = parse_argv(argc, argv);
if (r < 0)
return EXIT_FAILURE;

View file

@ -1,6 +1,3 @@
########### next target ###############
set(miracle-dhcp_SRCS dhcp.c
gdhcp.h
unaligned.h
@ -16,47 +13,15 @@ add_executable(miracle-dhcp ${miracle-dhcp_SRCS})
find_package(PkgConfig)
pkg_check_modules (GLIB2 REQUIRED glib-2.0)
pkg_check_modules (UDEV REQUIRED libudev)
target_link_libraries(miracle-dhcp miracle-shared)
link_directories( ${UDEV_LIBRARY_DIRS})
include_directories( ${UDEV_INCLUDE_DIRS})
target_link_libraries(miracle-dhcp ${UDEV_LIBRARIES})
target_link_libraries(miracle-dhcp m)
link_directories( ${GLIB2_LIBRARY_DIRS})
include_directories( ${GLIB2_INCLUDE_DIRS})
target_link_libraries(miracle-dhcp ${GLIB2_LIBRARIES})
target_link_libraries(miracle-dhcp miracle-shared)
install(TARGETS miracle-dhcp DESTINATION bin)
INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/shared)
########### install files ###############
#original Makefile.am contents follow:
#include $(top_srcdir)/common.am
#bin_PROGRAMS = miracle-dhcp
#
#miracle_dhcp_SOURCES = \
# dhcp.c \
# gdhcp.h \
# unaligned.h \
# common.h \
# common.c \
# ipv4ll.h \
# ipv4ll.c \
# client.c \
# server.c
#miracle_dhcp_CPPFLAGS = \
# $(AM_CPPFLAGS) \
# $(DEPS_CFLAGS) \
# $(GDHCP_CFLAGS)
#miracle_dhcp_LDADD = \
# ../shared/libmiracle-shared.la \
# $(DEPS_LIBS) \
# $(GDHCP_LIBS)
#
#
#

View file

@ -14,11 +14,11 @@ miracle_dhcp_SOURCES = \
miracle_dhcp_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(DEPS_CFLAGS) \
$(GDHCP_CFLAGS)
$(GLIB_CFLAGS)
miracle_dhcp_LDADD = \
../shared/libmiracle-shared.la \
$(DEPS_LIBS) \
$(GDHCP_LIBS)
$(GLIB_LIBS)

View file

@ -2785,7 +2785,7 @@ int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address)
addr = ntohl(inet_addr(last_address));
if (addr == 0xFFFFFFFF) {
addr = 0;
} else {
} else if (dhcp_client->last_address != last_address) { // Avoiding use-after-free
g_free(dhcp_client->last_address);
dhcp_client->last_address = g_strdup(last_address);
}

View file

@ -35,7 +35,7 @@
* of Wifi-P2P support in common network managers. Once they gain proper
* support, we will drop this helper!
*
* The "ip" invokation is quite fragile and ugly. However, performing these
* The "ip" invocation is quite fragile and ugly. However, performing these
* steps directly involves netlink operations and more. As no-one came up with
* patches, yet, we keep the hack. To anyone trying to fix it: Please, spend
* this time hacking on NetworkManager, connman and friends instead! If they
@ -67,8 +67,11 @@
#include "shl_log.h"
#include "config.h"
#define XSTR(x) STR(x)
#define STR(x) #x
static const char *arg_netdev;
static const char *arg_ip_binary = "/bin/ip";
static const char *arg_ip_binary = XSTR(IP_BINARY);
static bool arg_server;
static char arg_local[INET_ADDRSTRLEN];
static char arg_gateway[INET_ADDRSTRLEN];
@ -747,9 +750,10 @@ static int help(void)
" --version Show package version\n"
" --log-level <lvl> Maximum level for log messages\n"
" --log-time Prefix log-messages with timestamp\n"
" --log-date-time Prefix log-messages with date time\n"
"\n"
" --netdev <dev> Network device to run on\n"
" --ip-binary <path> Path to 'ip' binary [default: /bin/ip]\n"
" --ip-binary <path> Path to 'ip' binary [default: "XSTR(IP_BINARY)"]\n"
" --comm-fd <int> Comm-socket FD passed through execve()\n"
"\n"
"Server Options:\n"
@ -772,6 +776,7 @@ static int parse_argv(int argc, char *argv[])
ARG_VERSION = 0x100,
ARG_LOG_LEVEL,
ARG_LOG_TIME,
ARG_LOG_DATE_TIME,
ARG_NETDEV,
ARG_IP_BINARY,
@ -787,10 +792,11 @@ static int parse_argv(int argc, char *argv[])
ARG_TO,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "log-level", required_argument, NULL, ARG_LOG_LEVEL },
{ "log-time", no_argument, NULL, ARG_LOG_TIME },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "log-level", required_argument, NULL, ARG_LOG_LEVEL },
{ "log-time", no_argument, NULL, ARG_LOG_TIME },
{ "log-date-time", no_argument, NULL, ARG_LOG_DATE_TIME },
{ "netdev", required_argument, NULL, ARG_NETDEV },
{ "ip-binary", required_argument, NULL, ARG_IP_BINARY },
@ -823,6 +829,9 @@ static int parse_argv(int argc, char *argv[])
case ARG_LOG_TIME:
log_init_time();
break;
case ARG_LOG_DATE_TIME:
log_date_time = true;
break;
case ARG_NETDEV:
arg_netdev = optarg;
break;

11
src/dhcp/meson.build Normal file
View file

@ -0,0 +1,11 @@
miracle_dhcp_srcs = ['dhcp.c',
'common.c',
'ipv4ll.c',
'client.c',
'server.c'
]
executable('miracle-dhcp', miracle_dhcp_srcs,
install: true,
include_directories: include_directories('../..'),
dependencies: [glib2, udev, libmiracle_shared_dep, m]
)

11
src/meson.build Normal file
View file

@ -0,0 +1,11 @@
subdir('shared')
subdir('wifi')
subdir('dhcp')
subdir('ctl')
subdir('uibc')
executable('miracled', 'miracled.c',
dependencies: [libmiracle_shared_dep, m],
include_directories: include_directories('..'),
install: true
)

View file

@ -66,6 +66,7 @@ static int help(void)
" --version Show package version\n"
" --log-level <lvl> Maximum level for log messages\n"
" --log-time Prefix log-messages with timestamp\n"
" --log-date-time Prefix log-messages with date time\n"
, program_invocation_short_name);
/*
* 80-char barrier:
@ -81,12 +82,14 @@ static int parse_argv(int argc, char *argv[])
ARG_VERSION = 0x100,
ARG_LOG_LEVEL,
ARG_LOG_TIME,
ARG_LOG_DATE_TIME,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "log-level", required_argument, NULL, ARG_LOG_LEVEL },
{ "log-time", no_argument, NULL, ARG_LOG_TIME },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "log-level", required_argument, NULL, ARG_LOG_LEVEL },
{ "log-time", no_argument, NULL, ARG_LOG_TIME },
{ "log-date-time", no_argument, NULL, ARG_LOG_DATE_TIME },
{}
};
int c;
@ -104,6 +107,9 @@ static int parse_argv(int argc, char *argv[])
case ARG_LOG_TIME:
log_init_time();
break;
case ARG_LOG_DATE_TIME:
log_date_time = true;
break;
case '?':
return -EINVAL;
}

View file

@ -1,7 +1,5 @@
set(CMAKE_C_FLAGS "-std=gnu11")
find_package(PkgConfig)
pkg_check_modules (SYSTEMD REQUIRED systemd>=213)
set(miracle-shared_SOURCES rtsp.h
rtsp.c
shl_dlist.h
@ -18,36 +16,4 @@ set(miracle-shared_SOURCES rtsp.h
wpas.h
wpas.c)
add_library(miracle-shared STATIC ${miracle-shared_SOURCES})
target_link_libraries (miracle-shared systemd)
########### install files ###############
#original Makefile.am contents follow:
#include $(top_srcdir)/common.am
#noinst_LTLIBRARIES = libmiracle-shared.la
#
#libmiracle_shared_la_SOURCES = \
# rtsp.h \
# rtsp.c \
# shl_dlist.h \
# shl_htable.h \
# shl_htable.c \
# shl_log.h \
# shl_log.c \
# shl_macro.h \
# shl_ring.h \
# shl_ring.c \
# shl_util.h \
# shl_util.c \
# util.h \
# wpas.h \
# wpas.c
#libmiracle_shared_la_LIBADD = -lsystemd
#
#
#
target_link_libraries (miracle-shared ${SESSION_LIBRARIES})

View file

@ -17,7 +17,8 @@ libmiracle_shared_la_SOURCES = \
util.h \
wpas.h \
wpas.c
libmiracle_shared_la_LIBADD = -lsystemd
libmiracle_shared_la_LIBADD = \
$(DEPS_LIBS) \
$(GLIB_LIBS) \
$(LIBM)

22
src/shared/meson.build Normal file
View file

@ -0,0 +1,22 @@
libmiracle_shared = static_library('miracle-shared',
'rtsp.h',
'rtsp.c',
'shl_dlist.h',
'shl_htable.h',
'shl_htable.c',
'shl_log.h',
'shl_log.c',
'shl_macro.h',
'shl_ring.h',
'shl_ring.c',
'shl_util.h',
'shl_util.c',
'util.h',
'wpas.h',
'wpas.c',
dependencies: [libsystemd]
)
libmiracle_shared_dep = declare_dependency(
include_directories: include_directories('.'),
link_with: libmiracle_shared
)

View file

@ -2169,7 +2169,6 @@ static int parser_submit_data(struct rtsp *bus, uint8_t *p)
p,
dec->data_size);
if (r < 0) {
free(p);
return r;
}
@ -2717,7 +2716,7 @@ error:
return r;
}
static void rtsp_unlink_waiting(struct rtsp_message *m)
static bool rtsp_unlink_waiting(struct rtsp_message *m)
{
if (m->is_waiting) {
sd_event_source_unref(m->timer_source);
@ -2726,7 +2725,9 @@ static void rtsp_unlink_waiting(struct rtsp_message *m)
m->is_waiting = false;
--m->bus->waiting_cnt;
rtsp_message_unref(m);
return true;
}
return false;
}
static void rtsp_link_outgoing(struct rtsp_message *m)
@ -2737,7 +2738,7 @@ static void rtsp_link_outgoing(struct rtsp_message *m)
rtsp_message_ref(m);
}
static void rtsp_unlink_outgoing(struct rtsp_message *m)
static bool rtsp_unlink_outgoing(struct rtsp_message *m)
{
if (m->is_outgoing) {
shl_dlist_unlink(&m->list);
@ -2745,7 +2746,9 @@ static void rtsp_unlink_outgoing(struct rtsp_message *m)
m->is_sending = false;
--m->bus->outgoing_cnt;
rtsp_message_unref(m);
return true;
}
return false;
}
static int rtsp_incoming_message(struct rtsp_message *m)
@ -2823,10 +2826,11 @@ static int rtsp_write_message(struct rtsp_message *m)
if (m->sent >= m->raw_size) {
/* no need to wait for answer if no-body listens */
if (!m->cb_fn)
rtsp_unlink_waiting(m);
if (rtsp_unlink_waiting(m))
m = NULL;
/* might destroy the message */
rtsp_unlink_outgoing(m);
if (m)
rtsp_unlink_outgoing(m);
}
return 0;
@ -3246,10 +3250,12 @@ static void rtsp_drop_message(struct rtsp_message *m)
/* never interrupt messages while being partly sent */
if (!m->is_sending)
rtsp_unlink_outgoing(m);
if (rtsp_unlink_outgoing(m))
m = NULL;
/* remove from waiting list so neither timeouts nor completions fire */
rtsp_unlink_waiting(m);
if (m)
rtsp_unlink_waiting(m);
}
void rtsp_call_async_cancel(struct rtsp *bus, uint64_t cookie)

View file

@ -12,6 +12,9 @@
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <math.h>
#include <limits.h>
#include "shl_log.h"
/*
@ -36,7 +39,7 @@ static inline void log_unlock()
static struct timeval log__ftime;
static bool log__have_time(void)
bool log__have_time(void)
{
return !(log__ftime.tv_sec == 0 && log__ftime.tv_usec == 0);
}
@ -47,7 +50,7 @@ void log_init_time(void)
gettimeofday(&log__ftime, NULL);
}
static void log__time(long long *sec, long long *usec)
void log__time(long long *sec, long long *usec)
{
struct timeval t;
@ -83,6 +86,9 @@ const char *LOG_SUBSYSTEM = NULL;
*/
unsigned int log_max_sev = LOG_NOTICE;
bool log_date_time = false;
char *gst_debug = NULL;
/*
* Forward declaration so we can use the locked-versions in other functions
@ -135,25 +141,51 @@ static void log__submit(const char *file,
const char *prefix = NULL;
FILE *out;
long long sec, usec;
time_t now;
struct tm *timeinfo;
struct timeval tv;
char buffertmp[80];
char buffer[120];
int millisec;
out = stderr;
log__time(&sec, &usec);
if (sev < LOG_SEV_NUM && sev > log_max_sev)
return;
log__time(&sec, &usec);
if (log_date_time) {
gettimeofday(&tv, NULL);
millisec = lrint(tv.tv_usec/1000.0);
if (millisec>=1000) {
millisec -=1000;
tv.tv_sec++;
}
time(&now);
timeinfo = localtime(&now);
strftime(buffertmp, 80, "%x - %X.%03d", timeinfo);
sprintf(buffer, "%s.%03d", buffertmp, millisec);
}
if (sev < LOG_SEV_NUM)
prefix = log__sev2str[sev];
if (prefix) {
if (subs) {
if (log__have_time())
if (log_date_time)
fprintf(out, "[%s] %s: %s: ", buffer, prefix, subs);
else if (log__have_time())
fprintf(out, "[%.4lld.%.6lld] %s: %s: ",
sec, usec, prefix, subs);
else
fprintf(out, "%s: %s: ", prefix, subs);
} else {
if (log__have_time())
if (log_date_time)
fprintf(out, "[%s] %s: ", buffer, prefix);
else if (log__have_time())
fprintf(out, "[%.4lld.%.6lld] %s: ",
sec, usec, prefix);
else
@ -161,13 +193,18 @@ static void log__submit(const char *file,
}
} else {
if (subs) {
if (log__have_time())
if (log_date_time)
fprintf(out, "[%s] %s: ",
buffer, subs);
else if (log__have_time())
fprintf(out, "[%.4lld.%.6lld] %s: ",
sec, usec, subs);
else
fprintf(out, "%s: ", subs);
} else {
if (log__have_time())
if (log_date_time)
fprintf(out, "[%s] ", buffer);
else if (log__have_time())
fprintf(out, "[%.4lld.%.6lld] ", sec, usec);
}
}
@ -239,9 +276,9 @@ void log_llog(void *data,
log_submit(file, line, func, subs, sev, format, args);
}
int log_parse_arg(char *optarg)
unsigned int log_parse_arg(char *optarg)
{
int log_max_sev;
unsigned int log_max_sev;
if(!strcasecmp(optarg, "fatal")) {
log_max_sev = LOG_FATAL;
} else if(!strcasecmp(optarg, "alert")) {
@ -261,7 +298,23 @@ int log_parse_arg(char *optarg)
} else if(!strcasecmp(optarg, "trace")) {
log_max_sev = LOG_TRACE;
} else {
log_max_sev = atoi(optarg);
errno = 0;
char *temp;
long val = strtoul(optarg, &temp, 0);
if (temp == optarg || *temp != '\0'
|| ((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE)) {
log_error("Could not convert '%s' to long and leftover string is: '%s'\n", optarg, temp);
}
if (val > INT_MAX) {
errno = ERANGE;
return INT_MAX;
}
if (val < INT_MIN) {
errno = ERANGE;
return INT_MIN;
}
log_max_sev = (unsigned int) val;
}
return log_max_sev;
}

View file

@ -61,6 +61,17 @@ enum log_severity {
extern unsigned int log_max_sev;
/*
* Defines if log time should use local time
* Default: false
*/
extern bool log_date_time;
/*
* Defines the debug configuration for gstreamer
*/
extern char* gst_debug;
/*
* Timestamping
* Call this to initialize timestamps and cause all log-messages to be prefixed
@ -69,6 +80,10 @@ extern unsigned int log_max_sev;
void log_init_time(void);
void log__time(long long *sec, long long *usec);
bool log__have_time(void);
/*
* Log-Functions
* These functions pass a log-message to the log-subsystem. Handy helpers are
@ -116,7 +131,7 @@ void log_llog(void *data,
const char *format,
va_list args);
int log_parse_arg(char *optarg);
unsigned int log_parse_arg(char *optarg);
static inline __attribute__((format(printf, 2, 3)))
void log_dummyf(unsigned int sev, const char *format, ...)
@ -226,4 +241,9 @@ extern const char *LOG_SUBSYSTEM;
#define log_vERR(_r) \
((void)log_ERR(_r))
#define log_EUNMANAGED() \
(log_error("interface unmanaged"), -EFAULT)
#define log_vEUNMANAGED() \
((void)log_EUNMANAGED())
#endif /* SHL_LOG_H */

View file

@ -31,7 +31,7 @@
* variants to allow these.
* Base-prefix parsing is only done if base=0 is requested. Otherwise,
* base-prefixes are forbidden.
* The input string must be ASCII compatbile (which includes UTF8).
* The input string must be ASCII compatible (which includes UTF8).
*
* We also always check for overflows and return errors (but continue parsing!)
* so callers can catch it correctly.
@ -826,8 +826,9 @@ int shl__mkdir_parents(const char *prefix, const char *path, mode_t mode)
if (!e || e == path)
return 0;
p = strndupa(path, e - path);
p = strndup(path, e - path);
r = shl__is_dir(p);
free((char*)p);
if (r > 0)
return 0;
if (r == 0)

View file

@ -31,6 +31,26 @@
#include <systemd/sd-bus.h>
#include <time.h>
#include "shl_macro.h"
#include <glib.h>
static inline GKeyFile* load_ini_file() {
GKeyFile* gkf = NULL;
gchar* config_file;
gkf = g_key_file_new();
config_file = g_build_filename(g_get_home_dir(), ".config", "miraclecastrc", NULL);
if (!g_key_file_load_from_file(gkf, config_file, G_KEY_FILE_NONE, NULL)) {
g_free(config_file);
config_file = g_build_filename(g_get_home_dir(), ".miraclecast", NULL);
if (!g_key_file_load_from_file(gkf, config_file, G_KEY_FILE_NONE, NULL)) {
g_key_file_free(gkf);
gkf = NULL;
}
}
g_free(config_file);
return gkf;
}
static inline void cleanup_sd_bus_message(sd_bus_message **ptr)
{

View file

@ -742,6 +742,7 @@ static int wpas__parse_message(struct wpas *w,
const char *ifname = NULL;
unsigned int level;
char *pos;
char *orig_raw = raw;
int r, num;
bool is_event = false;
@ -751,7 +752,7 @@ static int wpas__parse_message(struct wpas *w,
ifname = pos;
pos = strchrnul(pos, ' ');
if (*pos)
*pos++ = 0;
pos++;
len -= pos - raw;
raw = pos;
@ -811,15 +812,12 @@ static int wpas__parse_message(struct wpas *w,
m->sealed = true;
m->rawlen = len;
m->raw = malloc(len + 1);
m->raw = strdup(orig_raw);
if (!m->raw)
return -ENOMEM;
/* copy with 0-terminator */
memcpy(m->raw, raw, len + 1);
if (ifname) {
m->ifname = strdup(ifname);
m->ifname = strndup(ifname, strchrnul(ifname, ' ') - ifname);
if (!m->ifname)
return -ENOMEM;
}
@ -1056,7 +1054,7 @@ static int wpas__bind_server_socket(int fd, const char *ctrl_path, char *name)
}
}
strncpy(name, src.sun_path, UNIX_PATH_MAX - 1);
strncpy(name, src.sun_path, UNIX_PATH_MAX);
name[UNIX_PATH_MAX - 1] = 0;
return 0;

10
src/uibc/CMakeLists.txt Normal file
View file

@ -0,0 +1,10 @@
set(miracle-uibcctl_SRCS miracle-uibcctl.h
miracle-uibcctl.c)
add_executable(miracle-uibcctl ${miracle-uibcctl_SRCS})
target_link_libraries(miracle-uibcctl miracle-shared)
target_link_libraries(miracle-uibcctl m)
install(TARGETS miracle-uibcctl DESTINATION bin)
INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/shared)

16
src/uibc/Makefile.am Normal file
View file

@ -0,0 +1,16 @@
include $(top_srcdir)/common.am
bin_PROGRAMS = miracle-uibcctl
miracle_uibcctl_SOURCES = \
miracle-uibcctl.h \
miracle-uibcctl.c
miracle_uibctcl_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(DEPS_CFLAGS)
miracle_uibcctl_LDADD = \
../shared/libmiracle-shared.la \
$(LIBM)

4
src/uibc/meson.build Normal file
View file

@ -0,0 +1,4 @@
executable('miracle-uibcctl', 'miracle-uibcctl.h', 'miracle-uibcctl.c',
install: true,
dependencies: [m, libmiracle_shared_dep]
)

624
src/uibc/miracle-uibcctl.c Normal file
View file

@ -0,0 +1,624 @@
#include "miracle-uibcctl.h"
int main(int argc, char *argv[]) {
//TODO: Add miracle TUI interface
//TODO: Add parsearg
//--daemon (read stdin)
int portno;
struct hostent *server;
int sockfd;
struct sockaddr_in serv_addr;
char buffer[256];
int r;
log_max_sev = LOG_INFO;
if (argc < 3) {
fprintf(stderr, "Usage:\n");
fprintf(stderr, " %s <hostname> <port>\n", argv[0]);
return EXIT_FAILURE;
}
server = gethostbyname(argv[1]);
portno = atoi(argv[2]);
log_info("server %s port %d", argv[1], portno);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
return EXIT_FAILURE;
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy(server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR connecting");
return EXIT_FAILURE;
}
bool daemon = true;
while(1) {
if (!daemon) {
printf("enter event <type>,<count>,<id>,<x>,<y>: ");
}
bzero(buffer, 256);
fgets(buffer, 255, stdin);
if (strlen(buffer) == 0) {
break;
}
if (!daemon) {
printf("input: %s", buffer);
}
char type = buffer[0];
UibcMessage uibcmessage;
if (type == '0' || type == '1') {
uibcmessage = buildUibcMessage(GENERIC_TOUCH_DOWN, buffer, 1, 1);
} else if (type == '3' || type == '4') {
uibcmessage = buildUibcMessage(GENERIC_KEY_DOWN, buffer, 1, 1);
} else {
if (!daemon) {
printf("unknow event type: %s", buffer);
}
continue;
}
r = sendUibcMessage(&uibcmessage, sockfd);
if (r) {
return r;
}
}
close(sockfd);
return EXIT_SUCCESS;
}
const char *int2binary(int x, int padding)
{
char *b;
int min_padding = x > 0 ? floor(log2(x)) : 0;
if (padding < min_padding) {
padding = min_padding;
}
b = (char *)malloc (sizeof (char *) * padding + 1);
strcpy(b, "");
int z;
for (z = pow(2,padding); z > 0; z >>= 1) {
strcat(b, ((x & z) == z) ? "1" : "0");
}
return b;
}
int sendUibcMessage(UibcMessage* uibcmessage, int sockfd) {
ssize_t n;
printf("sending %zu bytes\n", uibcmessage->m_PacketDataLen);
n = write(sockfd, uibcmessage->m_PacketData , uibcmessage->m_PacketDataLen);
if (n < 0) {
perror("ERROR writing to socket");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
UibcMessage buildUibcMessage(MessageType type,
const char* inEventDesc,
double widthRatio,
double heightRatio) {
UibcMessage uibcmessage;
uibcmessage.m_PacketData = NULL;
uibcmessage.m_PacketDataLen = 0;
uibcmessage.m_DataValid = false;
switch (type) {
case GENERIC_TOUCH_DOWN:
case GENERIC_TOUCH_UP:
case GENERIC_TOUCH_MOVE:
getUIBCGenericTouchPacket(inEventDesc,
&uibcmessage,
widthRatio,
heightRatio);
break;
case GENERIC_KEY_DOWN:
case GENERIC_KEY_UP:
getUIBCGenericKeyPacket(inEventDesc, &uibcmessage);
break;
case GENERIC_ZOOM:
getUIBCGenericZoomPacket(inEventDesc, &uibcmessage);
break;
case GENERIC_VERTICAL_SCROLL:
case GENERIC_HORIZONTAL_SCROLL:
getUIBCGenericScalePacket(inEventDesc, &uibcmessage);
break;
case GENERIC_ROTATE:
getUIBCGenericRotatePacket(inEventDesc, &uibcmessage);
break;
};
return uibcmessage;
}
// format: "typeId, number of pointers, pointer Id1, X coordnate, Y coordinate, pointer Id2, X coordnate, Y coordnate,..."
void getUIBCGenericTouchPacket(const char *inEventDesc,
UibcMessage* uibcmessage,
double widthRatio,
double heightRatio) {
log_info("getUIBCGenericTouchPacket (%s)", inEventDesc);
char* outData;
int32_t typeId = 0;
int32_t numberOfPointers = 0;
size_t uibcBodyLen = 0;
int32_t genericPacketLen = 0;
int32_t temp;
size_t size;
char** splitedStr = str_split((char*)inEventDesc, ",", &size);
if ((int)size - 5 < 0 || ((int)size - 5) % 3 != 0) {
log_error("getUIBCGenericTouchPacket (%s)", "bad input event");
return;
}
int offset_split = 0;
typeId = atoi(*(splitedStr + offset_split++));
numberOfPointers = atoi(*(splitedStr + offset_split++));
genericPacketLen = numberOfPointers * 5 + 1;
uibcBodyLen = genericPacketLen + 7; // Generic header length = 7
//Padding to even number
uibcBodyLen = (uibcBodyLen % 2 == 0) ? uibcBodyLen : (uibcBodyLen + 1);
outData = (char*)malloc(uibcBodyLen);
// UIBC header Octets
int offset_header = 0;
//Version (3 bits),T (1 bit),Reserved(8 bits),Input Category (4 bits)
outData[offset_header++] = 0x00; // 000 0 0000
outData[offset_header++] = 0x00; // 0000 0000
//Length(16 bits)
outData[offset_header++] = (uibcBodyLen >> 8) & 0xFF; // first 8 bytes
outData[offset_header++] = uibcBodyLen & 0xFF; // last 8 bytes
//Generic Input Body Format
outData[offset_header++] = typeId & 0xFF; // Tyoe ID, 1 octet
// Length, 2 octets
outData[offset_header++] = (genericPacketLen >> 8) & 0xFF; // first 8 bytes
outData[offset_header++] = genericPacketLen & 0xFF; //last 8 bytes
// Number of pointers, 1 octet
outData[offset_header++] = numberOfPointers & 0xFF;
int offset = 0;
log_info("getUIBCGenericTouchPacket numberOfPointers=[%d]\n", numberOfPointers);
for (int i = 0; i < numberOfPointers; i++) {
int splitoffset = offset_split + (i * 3);
temp = atoi(*(splitedStr + splitoffset++));
offset = offset_header + ( i * 5);
outData[offset++] = temp & 0xFF;
log_info("getUIBCGenericTouchPacket PointerId=[%d]\n", temp);
temp = atoi(*(splitedStr + splitoffset++));
temp = (int32_t)((double)temp / widthRatio);
log_info("getUIBCGenericTouchPacket X-coordinate=[%d]\n", temp);
outData[offset++] = (temp >> 8) & 0xFF;
outData[offset++] = temp & 0xFF;
temp = atoi(*(splitedStr + splitoffset++));
temp = (int32_t)((double)temp / heightRatio);
log_info("getUIBCGenericTouchPacket Y-coordinate=[%d]\n", temp);
outData[offset++] = (temp >> 8) & 0xFF;
outData[offset++] = temp & 0xFF;
}
while (offset <= uibcBodyLen) {
outData[offset++] = 0x00;
}
for (int i = 0; i < size; i++) {
free(*(splitedStr + i));
}
free(splitedStr);
binarydump(outData, uibcBodyLen);
uibcmessage->m_DataValid = true;
uibcmessage->m_PacketData = outData;
uibcmessage->m_PacketDataLen = uibcBodyLen;
}
void hexdump(void *_data, size_t len)
{
unsigned char *data = _data;
size_t count;
int line = 15;
for (count = 0; count < len; count++) {
if ((count & line) == 0) {
fprintf(stderr,"%04zu: ", count);
}
fprintf(stderr,"%02x ", *data);
data++;
if ((count & line) == line) {
fprintf(stderr,"\n");
}
}
if ((count & line) != 0) {
fprintf(stderr,"\n");
}
}
void binarydump(void *_data, size_t len)
{
unsigned char *data = _data;
size_t count;
int line = 7;
for (count = 0; count < len; count++) {
if ((count & line) == 0) {
fprintf(stderr,"%04zu: ", count);
}
fprintf(stderr,"%s ", int2binary(*data, 8));
data++;
if ((count & line) == line) {
fprintf(stderr,"\n");
}
}
if ((count & line) != 0) {
fprintf(stderr,"\n");
}
}
// format: "typeId, Key code 1(0x00), Key code 2(0x00)"
void getUIBCGenericKeyPacket(const char *inEventDesc,
UibcMessage* uibcmessage) {
log_info("getUIBCGenericKeyPacket (%s)", inEventDesc);
char* outData = uibcmessage->m_PacketData;
int32_t typeId = 0;
int32_t uibcBodyLen = 0;
int32_t genericPacketLen = 0;
int32_t temp = 0;
size_t size;
char** splitedStr = str_split((char*)inEventDesc, ",", &size);
if (size > 0) {
if (((int)size) % 3 != 0) {
log_error("getUIBCGenericKeyPacket (%s)", "bad input event");
return;
}
int i;
for (i = 0; i < size; i++) {
log_info("getUIBCGenericKeyPacket splitedStr tokens=[%s]\n", *(splitedStr + i));
switch (i) {
case 0: {
typeId = atoi(*(splitedStr + i));
log_info("getUIBCGenericKeyPacket typeId=[%d]\n", typeId);
genericPacketLen = 5;
uibcBodyLen = genericPacketLen + 7; // Generic header length = 7
outData = (char*)malloc(uibcBodyLen + 1);
// UIBC header
outData[0] = 0x00; //Version (3 bits),T (1 bit),Reserved(8 bits),Input Category (4 bits)
outData[1] = 0x00; //Version (3 bits),T (1 bit),Reserved(8 bits),Input Category (4 bits)
outData[2] = (uibcBodyLen >> 8) & 0xFF; //Length(16 bits)
outData[3] = uibcBodyLen & 0xFF; //Length(16 bits)
//Generic Input Body Format
outData[4] = typeId & 0xFF; // Tyoe ID, 1 octet
outData[5] = (genericPacketLen >> 8) & 0xFF; // Length, 2 octets
outData[6] = genericPacketLen & 0xFF; // Length, 2 octets
outData[7] = 0x00; // reserved
break;
}
case 1: {
sscanf(*(splitedStr + i), " 0x%04X", &temp);
if (temp == 0) {
outData[8] = 0x00;
outData[9] = 0x00;
}
log_info("getUIBCGenericKeyPacket key code 1=[%d]\n", temp);
outData[8] = (temp >> 8) & 0xFF;
outData[9] = temp & 0xFF;
break;
}
case 2: {
sscanf(*(splitedStr + i), " 0x%04X", &temp);
if (temp == 0) {
outData[10] = 0x00;
outData[11] = 0x00;
}
log_info("getUIBCGenericKeyPacket key code 2=[%d]\n", temp);
outData[10] = (temp >> 8) & 0xFF;
outData[11] = temp & 0xFF;
break;
}
default: {
}
break;
}
free(*(splitedStr + i));
}
}
free(splitedStr);
binarydump(outData, uibcBodyLen);
uibcmessage->m_DataValid = true;
uibcmessage->m_PacketData = outData;
uibcmessage->m_PacketDataLen = uibcBodyLen;
}
// format: "typeId, X coordnate, Y coordnate, integer part, fraction part"
void getUIBCGenericZoomPacket(const char *inEventDesc, UibcMessage* uibcmessage) {
log_info("getUIBCGenericZoomPacket (%s)", inEventDesc);
char* outData = uibcmessage->m_PacketData;
int32_t typeId;
int32_t uibcBodyLen, genericPacketLen;
int32_t xCoord, yCoord, integerPart, FractionPart;
size_t size;
char** splitedStr = str_split((char*)inEventDesc, ",", &size);
if (splitedStr) {
int i;
for (i = 0; * (splitedStr + i); i++) {
//log_info("getUIBCGenericZoomPacket splitedStr tokens=[%s]\n", *(splitedStr + i));
switch (i) {
case 0: {
typeId = atoi(*(splitedStr + i));
//log_info("getUIBCGenericZoomPacket typeId=[%d]\n", typeId);
genericPacketLen = 6;
uibcBodyLen = genericPacketLen + 7; // Generic herder leh = 7
outData = (char*)malloc(uibcBodyLen + 1);
// UIBC header
outData[0] = 0x00; //Version (3 bits),T (1 bit),Reserved(8 bits),Input Category (4 bits)
outData[1] = 0x00; //Version (3 bits),T (1 bit),Reserved(8 bits),Input Category (4 bits)
outData[2] = (uibcBodyLen >> 8) & 0xFF; //Length(16 bits)
outData[3] = uibcBodyLen & 0xFF; //Length(16 bits)
//Generic Input Body Format
outData[4] = typeId & 0xFF; // Tyoe ID, 1 octet
outData[5] = (genericPacketLen >> 8) & 0xFF; // Length, 2 octets
outData[6] = genericPacketLen & 0xFF; // Length, 2 octets
break;
}
case 1: {
xCoord = atoi(*(splitedStr + i));
outData[7] = (xCoord >> 8) & 0xFF;
outData[8] = xCoord & 0xFF;
log_info("getUIBCGenericZoomPacket xCoord=[%d]\n", xCoord);
break;
}
case 2: {
yCoord = atoi(*(splitedStr + i));
log_info("getUIBCGenericZoomPacket yCoord=[%d]\n", yCoord);
break;
}
case 3: {
integerPart = atoi(*(splitedStr + i));
outData[11] = integerPart & 0xFF;
//log_info("getUIBCGenericZoomPacket integerPart=[%d]\n", integerPart);
break;
}
case 4: {
FractionPart = atoi(*(splitedStr + i));
outData[12] = FractionPart & 0xFF;
//log_info("getUIBCGenericZoomPacket FractionPart=[%d]\n", FractionPart);
break;
}
default: {
break;
}
}
free(*(splitedStr + i));
}
free(splitedStr);
}
//hexdump(outData, uibcBodyLen);
uibcmessage->m_DataValid = true;
uibcmessage->m_PacketDataLen = uibcBodyLen;
}
// format: "typeId, unit, direction, amount to scroll"
void getUIBCGenericScalePacket(const char *inEventDesc, UibcMessage* uibcmessage) {
log_info("getUIBCGenericScalePacket (%s)", inEventDesc);
char* outData = uibcmessage->m_PacketData;
int32_t typeId;
int32_t uibcBodyLen, genericPacketLen;
int32_t temp;
size_t size;
char** splitedStr = str_split((char*)inEventDesc, ",", &size);
if (splitedStr) {
int i;
for (i = 0; * (splitedStr + i); i++) {
//log_info("getUIBCGenericScalePacket splitedStr tokens=[%s]\n", *(splitedStr + i));
switch (i) {
case 0: {
typeId = atoi(*(splitedStr + i));
//log_info("getUIBCGenericScalePacket typeId=[%d]\n", typeId);
genericPacketLen = 2;
uibcBodyLen = genericPacketLen + 7; // Generic herder leh = 7
outData = (char*)malloc(uibcBodyLen + 1);
// UIBC header
outData[0] = 0x00; //Version (3 bits),T (1 bit),Reserved(8 bits),Input Category (4 bits)
outData[1] = 0x00; //Version (3 bits),T (1 bit),Reserved(8 bits),Input Category (4 bits)
outData[2] = (uibcBodyLen >> 8) & 0xFF; //Length(16 bits)
outData[3] = uibcBodyLen & 0xFF; //Length(16 bits)
//Generic Input Body Format
outData[4] = typeId & 0xFF; // Tyoe ID, 1 octet
outData[5] = (genericPacketLen >> 8) & 0xFF; // Length, 2 octets
outData[6] = genericPacketLen & 0xFF; // Length, 2 octets
outData[7] = 0x00; // Clear the byte
outData[8] = 0x00; // Clear the byte
/*
B15B14; Scroll Unit Indication bits.
0b00; the unit is a pixel (normalized with respect to the WFD Source display resolution that is conveyed in an RTSP M4 request message).
0b01; the unit is a mouse notch (where the application is responsible for representing the number of pixels per notch).
0b10-0b11; Reserved.
B13; Scroll Direction Indication bit.
0b0; Scrolling to the right. Scrolling to the right means the displayed content being shifted to the left from a user perspective.
0b1; Scrolling to the left. Scrolling to the left means the displayed content being shifted to the right from a user perspective.
B12:B0; Number of Scroll bits.
Number of units for a Horizontal scroll.
*/
break;
}
case 1: {
temp = atoi(*(splitedStr + i));
//log_info("getUIBCGenericScalePacket unit=[%d]\n", temp);
outData[7] = (temp >> 8) & 0xFF;
break;
}
case 2: {
temp = atoi(*(splitedStr + i));
//log_info("getUIBCGenericScalePacket direction=[%d]\n", temp);
outData[7] |= ((temp >> 10) & 0xFF);
break;
}
case 3: {
temp = atoi(*(splitedStr + i));
//log_info("getUIBCGenericScalePacket amount to scroll=[%d]\n", temp);
outData[7] |= ((temp >> 12) & 0xFF);
outData[8] = temp & 0xFF;
break;
}
default: {
break;
}
}
free(*(splitedStr + i));
}
free(splitedStr);
}
//hexdump(outData, uibcBodyLen);
uibcmessage->m_DataValid = true;
uibcmessage->m_PacketDataLen = uibcBodyLen;
}
// format: "typeId, integer part, fraction part"
void getUIBCGenericRotatePacket(const char * inEventDesc, UibcMessage* uibcmessage) {
log_info("getUIBCGenericRotatePacket (%s)", inEventDesc);
char* outData = uibcmessage->m_PacketData;
int32_t typeId;
int32_t uibcBodyLen, genericPacketLen;
int32_t integerPart, FractionPart;
size_t size;
char** splitedStr = str_split((char*)inEventDesc, ",", &size);
if (splitedStr) {
int i;
for (i = 0; * (splitedStr + i); i++) {
//log_info("getUIBCGenericRotatePacket splitedStr tokens=[%s]\n", *(splitedStr + i));
switch (i) {
case 0: {
typeId = atoi(*(splitedStr + i));
//log_info("getUIBCGenericRotatePacket typeId=[%d]\n", typeId);
genericPacketLen = 2;
uibcBodyLen = genericPacketLen + 7; // Generic herder leh = 7
outData = (char*)malloc(uibcBodyLen + 1);
// UIBC header
outData[0] = 0x00; //Version (3 bits),T (1 bit),Reserved(8 bits),Input Category (4 bits)
outData[1] = 0x00; //Version (3 bits),T (1 bit),Reserved(8 bits),Input Category (4 bits)
outData[2] = (uibcBodyLen >> 8) & 0xFF; //Length(16 bits)
outData[3] = uibcBodyLen & 0xFF; //Length(16 bits)
//Generic Input Body Format
outData[4] = typeId & 0xFF; // Tyoe ID, 1 octet
outData[5] = (genericPacketLen >> 8) & 0xFF; // Length, 2 octets
outData[6] = genericPacketLen & 0xFF; // Length, 2 octets
break;
}
case 1: {
integerPart = atoi(*(splitedStr + i));
outData[7] = integerPart & 0xFF;
//log_info("getUIBCGenericRotatePacket integerPart=[%d]\n", integerPart);
break;
}
case 2: {
FractionPart = atoi(*(splitedStr + i));
outData[8] = FractionPart & 0xFF;
//log_info("getUIBCGenericRotatePacket FractionPart=[%d]\n", FractionPart);
break;
}
default: {
break;
}
}
free(*(splitedStr + i));
}
free(splitedStr);
}
//hexdump(outData, uibcBodyLen);
uibcmessage->m_DataValid = true;
uibcmessage->m_PacketDataLen = uibcBodyLen;
}
char** str_split(char* pStr, const char* pDelim, size_t* size) {
char** result = 0;
size_t count = 0;
char* tmp = pStr;
char* tmpStr = NULL;
char* last_comma = 0;
asprintf(&tmpStr, "%s", pStr);
/* Count how many elements will be extracted. */
while (*tmp) {
if (*pDelim == *tmp) {
count++;
last_comma = tmp;
}
tmp++;
}
/* Add space for trailing token. */
count += last_comma < (pStr + strlen(pStr) - 1) ? 1 : 0;
result = (char**)malloc(sizeof(char*) * count);
*size = count;
tmp = tmpStr = strdup(pStr);
size_t idx = 0;
char* token;
while ((token = strsep(&tmp, pDelim)) != NULL) {
* (result + idx++) = strdup(token);
}
free(tmpStr);
return result;
}

View file

@ -0,0 +1,48 @@
#ifndef MIRACLE_UIBCCTL_H
#define MIRACLE_UIBCCTL_H
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdlib.h>
#include <netdb.h>
#include<arpa/inet.h>
#include <math.h>
#include "shl_log.h"
typedef enum {
GENERIC_TOUCH_DOWN = 0,
GENERIC_TOUCH_UP,
GENERIC_TOUCH_MOVE,
GENERIC_KEY_DOWN,
GENERIC_KEY_UP,
GENERIC_ZOOM,
GENERIC_VERTICAL_SCROLL,
GENERIC_HORIZONTAL_SCROLL,
GENERIC_ROTATE
} MessageType;
typedef struct {
char* m_PacketData;
size_t m_PacketDataLen;
bool m_DataValid;
} UibcMessage;
UibcMessage buildUibcMessage(MessageType type, const char* inEventDesc, double widthRatio, double heightRatio);
static char** str_split(char* pStr, const char* pDelim, size_t* size);
void getUIBCGenericTouchPacket(const char *inEventDesc, UibcMessage* uibcmessage, double widthRatio, double heightRatio);
void getUIBCGenericKeyPacket(const char *inEventDesc, UibcMessage* uibcmessage);
void getUIBCGenericZoomPacket(const char *inEventDesc,UibcMessage* uibcmessage);
void getUIBCGenericScalePacket(const char *inEventDesc, UibcMessage* uibcmessage);
void getUIBCGenericRotatePacket(const char *inEventDesc, UibcMessage* uibcmessage);
void hexdump(void *_data, size_t len);
void binarydump(void *_data, size_t len);
int sendUibcMessage(UibcMessage* uibcmessage, int sockfd);
#endif

View file

@ -1,6 +1,3 @@
########### next target ###############
set(miracle-wifid_SRCS wifid.h
wifid.c
wifid-dbus.c
@ -12,6 +9,7 @@ add_executable(miracle-wifid ${miracle-wifid_SRCS})
target_link_libraries(miracle-wifid ${KDE4_KDECORE_LIBS})
cmake_policy(SET CMP0015 NEW)
include_directories(shared)
link_directories(shared)
target_link_libraries(miracle-wifid miracle-shared)
@ -19,40 +17,11 @@ target_link_libraries(miracle-wifid miracle-shared)
find_package(PkgConfig)
pkg_check_modules (GLIB2 REQUIRED glib-2.0)
pkg_check_modules (UDEV REQUIRED libudev)
#link_directories( ${UDEV_LIBRARY_DIRS})
#include_directories( ${UDEV_INCLUDE_DIRS})
target_link_libraries(miracle-wifid ${UDEV_LIBRARIES})
#link_directories( ${GLIB2_LIBRARY_DIRS})
#include_directories( ${GLIB2_INCLUDE_DIRS})
target_link_libraries(miracle-wifid m)
link_directories( ${GLIB2_LIBRARY_DIRS})
include_directories( ${GLIB2_INCLUDE_DIRS})
target_link_libraries(miracle-wifid ${GLIB2_LIBRARIES})
install(TARGETS miracle-wifid DESTINATION bin)
INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/shared)
########### install files ###############
#set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake-extensions/ )
#original Makefile.am contents follow:
#include $(top_srcdir)/common.am
#bin_PROGRAMS = miracle-wifid
#
#miracle_wifid_SOURCES = \
# wifid.h \
# wifid.c \
# wifid-dbus.c \
# wifid-link.c \
# wifid-peer.c \
# wifid-supplicant.c
#miracle_wifid_CPPFLAGS = \
# $(AM_CPPFLAGS) \
# $(DEPS_CFLAGS)
#miracle_wifid_LDADD = \
# ../shared/libmiracle-shared.la \
# $(DEPS_LIBS)
#

View file

@ -10,8 +10,10 @@ miracle_wifid_SOURCES = \
wifid-supplicant.c
miracle_wifid_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(DEPS_CFLAGS)
$(DEPS_CFLAGS) \
$(GLIB_CFLAGS)
miracle_wifid_LDADD = \
../shared/libmiracle-shared.la \
$(DEPS_LIBS)
$(DEPS_LIBS) \
$(GLIB_LIBS)

14
src/wifi/meson.build Normal file
View file

@ -0,0 +1,14 @@
inc = include_directories('../..')
miracle_wifid_src = ['wifid.h',
'wifid.c',
'wifid-dbus.c',
'wifid-link.c',
'wifid-peer.c',
'wifid-supplicant.c'
]
executable('miracle-wifid', miracle_wifid_src,
include_directories: inc,
install: true,
dependencies: [udev, glib2, libsystemd, libmiracle_shared_dep, m]
)

View file

@ -309,6 +309,7 @@ static const sd_bus_vtable peer_dbus_vtable[] = {
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_SIGNAL("ProvisionDiscovery", "ss", 0),
SD_BUS_SIGNAL("GoNegRequest", "ss", 0),
SD_BUS_SIGNAL("FormationFailure", "s", 0),
SD_BUS_VTABLE_END
};
@ -570,6 +571,42 @@ static int link_dbus_set_friendly_name(sd_bus *bus,
return link_set_friendly_name(l, name);
}
static int link_dbus_get_managed(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(reply, "b", link_get_managed(l));
if (r < 0)
return r;
return 1;
}
static int link_dbus_set_managed(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *value,
void *data,
sd_bus_error *err)
{
struct link *l = data;
int val, r;
r = sd_bus_message_read(value, "b", &val);
if (r < 0)
return r;
return link_set_managed(l, val);
}
static int link_dbus_get_p2p_scanning(sd_bus *bus,
const char *path,
const char *interface,
@ -661,6 +698,12 @@ static const sd_bus_vtable link_dbus_vtable[] = {
link_dbus_set_friendly_name,
0,
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_WRITABLE_PROPERTY("Managed",
"b",
link_dbus_get_managed,
link_dbus_set_managed,
0,
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_WRITABLE_PROPERTY("P2PScanning",
"b",
link_dbus_get_p2p_scanning,
@ -894,8 +937,12 @@ int manager_dbus_connect(struct manager *m)
r = sd_bus_request_name(m->bus, "org.freedesktop.miracle.wifi", 0);
if (r < 0) {
log_error("cannot claim org.freedesktop.miracle.wifi bus-name: %d",
if (r == -EEXIST) {
log_info("cannot claim org.freedesktop.miracle.wifi bus-name: it is already being acquired");
} else {
log_error("cannot claim org.freedesktop.miracle.wifi bus-name: %d",
r);
}
goto error_silent;
}

View file

@ -100,6 +100,8 @@ int link_new(struct manager *m,
if (out)
*out = l;
l->public = true;
link_dbus_added(l);
return 0;
error:
@ -116,6 +118,9 @@ void link_free(struct link *l)
link_set_managed(l, false);
link_dbus_removed(l);
l->public = false;
if (shl_htable_remove_uint(&l->m->links, l->ifindex, NULL)) {
log_info("remove link: %s", l->ifname);
--l->m->link_cnt;
@ -129,17 +134,66 @@ void link_free(struct link *l)
free(l->wfd_subelements);
free(l->friendly_name);
free(l->ifname);
free(l->config_methods);
free(l->ip_binary);
free(l);
}
void link_set_managed(struct link *l, bool set)
void link_use_dev(struct link *l)
{
l->use_dev = true;
}
bool link_is_using_dev(struct link *l)
{
return l->use_dev;
}
int link_set_config_methods(struct link *l, char *config_methods)
{
char *cm;
if (!config_methods)
return log_EINVAL();
cm = strdup(config_methods);
if (!cm)
return log_ENOMEM();
free(l->config_methods);
l->config_methods = cm;
return 0;
}
int link_set_ip_binary(struct link *l, const char *ip_binary)
{
char *ipb;
if (!ip_binary)
return log_EINVAL();
ipb = strdup(ip_binary);
if (!ipb)
return log_ENOMEM();
free(l->ip_binary);
l->ip_binary = ipb;
return 0;
}
bool link_get_managed(struct link *l)
{
return l->managed;
}
int link_set_managed(struct link *l, bool set)
{
int r;
if (!l)
return log_vEINVAL();
return log_EINVAL();
if (l->managed == set)
return;
return 0;
if (set) {
log_info("manage link %s", l->ifname);
@ -147,7 +201,7 @@ void link_set_managed(struct link *l, bool set)
r = supplicant_start(l->s);
if (r < 0) {
log_error("cannot start supplicant on %s", l->ifname);
return;
return -EFAULT;
}
} else {
log_info("link %s no longer managed", l->ifname);
@ -155,6 +209,7 @@ void link_set_managed(struct link *l, bool set)
}
l->managed = set;
return 0;
}
int link_renamed(struct link *l, const char *ifname)
@ -224,6 +279,9 @@ int link_set_wfd_subelements(struct link *l, const char *val)
if (!l || !val)
return log_EINVAL();
if (!l->managed)
return log_EUNMANAGED();
t = strdup(val);
if (!t)
return log_ENOMEM();
@ -256,6 +314,9 @@ int link_set_p2p_scanning(struct link *l, bool set)
if (!l)
return log_EINVAL();
if (!l->managed)
return log_EUNMANAGED();
if (set) {
return supplicant_p2p_start_scan(l->s);
} else {
@ -266,7 +327,16 @@ int link_set_p2p_scanning(struct link *l, bool set)
bool link_get_p2p_scanning(struct link *l)
{
return l && supplicant_p2p_scanning(l->s);
if (!l) {
log_vEINVAL();
return false;
}
if (!l->managed) {
return false;
}
return supplicant_p2p_scanning(l->s);
}
void link_supplicant_started(struct link *l)
@ -274,9 +344,9 @@ void link_supplicant_started(struct link *l)
if (!l || l->public)
return;
log_debug("link %s started", l->ifname);
l->public = true;
link_dbus_added(l);
if (l->m->friendly_name && l->managed)
link_set_friendly_name(l, l->m->friendly_name);
log_info("link %s managed", l->ifname);
}
void link_supplicant_stopped(struct link *l)
@ -284,9 +354,7 @@ void link_supplicant_stopped(struct link *l)
if (!l || !l->public)
return;
log_debug("link %s stopped", l->ifname);
link_dbus_removed(l);
l->public = false;
log_info("link %s unmanaged", l->ifname);
}
void link_supplicant_p2p_scan_changed(struct link *l, bool new_value)

View file

@ -61,7 +61,8 @@ int peer_new(struct link *l,
r = log_ENOMEM();
goto error;
}
strncpy(p->p2p_mac, mac, MAC_STRLEN - 1);
strncpy(p->p2p_mac, mac, MAC_STRLEN);
p->p2p_mac[MAC_STRLEN - 1] = 0;
r = shl_htable_insert_str(&l->peers, &p->p2p_mac, NULL);
if (r < 0) {

View file

@ -19,14 +19,21 @@
#define LOG_SUBSYSTEM "supplicant"
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <systemd/sd-event.h>
#ifdef ENABLE_SYSTEMD
#include <systemd/sd-journal.h>
#endif
#include <unistd.h>
#include "shl_dlist.h"
#include "shl_log.h"
@ -34,7 +41,6 @@
#include "util.h"
#include "wifid.h"
#include "wpas.h"
#include "config.h"
struct supplicant_group {
unsigned long users;
@ -373,6 +379,7 @@ static int supplicant_group_spawn_dhcp_server(struct supplicant_group *g,
sigemptyset(&mask);
sigprocmask(SIG_SETMASK, &mask, NULL);
#ifdef ENABLE_SYSTEMD
/* redirect stdout/stderr to journal */
sprintf(journal_id, "miracle-dhcp-%s", g->ifname);
fd_journal = sd_journal_stream_fd(journal_id, LOG_INFO, false);
@ -381,12 +388,15 @@ static int supplicant_group_spawn_dhcp_server(struct supplicant_group *g,
dup2(fd_journal, 1);
dup2(fd_journal, 2);
} else {
#endif
/* no journal? redirect stdout to parent's stderr */
dup2(2, 1);
#ifdef ENABLE_SYSTEMD
}
#endif
i = 0;
argv[i++] = (char*) BUILD_BINDIR "/miracle-dhcp";
argv[i++] = (char*) "miracle-dhcp";
argv[i++] = "--server";
argv[i++] = "--prefix";
argv[i++] = prefix;
@ -396,9 +406,15 @@ static int supplicant_group_spawn_dhcp_server(struct supplicant_group *g,
argv[i++] = g->ifname;
argv[i++] = "--comm-fd";
argv[i++] = commfd;
if (g->s->l->ip_binary) {
argv[i++] = "--ip-binary";
argv[i++] = g->s->l->ip_binary;
}
argv[i] = NULL;
execve(argv[0], argv, environ);
if (execvpe(argv[0], argv, environ)< 0) {
log_error("dhcp failed (%d): %m", errno);
}
_exit(1);
}
@ -435,6 +451,7 @@ static int supplicant_group_spawn_dhcp_client(struct supplicant_group *g)
sigemptyset(&mask);
sigprocmask(SIG_SETMASK, &mask, NULL);
#ifdef ENABLE_SYSTEMD
/* redirect stdout/stderr to journal */
sprintf(journal_id, "miracle-dhcp-%s", g->ifname);
fd_journal = sd_journal_stream_fd(journal_id, LOG_INFO, false);
@ -443,21 +460,30 @@ static int supplicant_group_spawn_dhcp_client(struct supplicant_group *g)
dup2(fd_journal, 1);
dup2(fd_journal, 2);
} else {
#endif
/* no journal? redirect stdout to parent's stderr */
dup2(2, 1);
#ifdef ENABLE_SYSTEMD
}
#endif
i = 0;
argv[i++] = (char*) BUILD_BINDIR "/miracle-dhcp";
argv[i++] = (char*) "miracle-dhcp";
argv[i++] = "--log-level";
argv[i++] = loglevel;
argv[i++] = "--netdev";
argv[i++] = g->ifname;
argv[i++] = "--comm-fd";
argv[i++] = commfd;
if (g->s->l->ip_binary) {
argv[i++] = "--ip-binary";
argv[i++] = g->s->l->ip_binary;
}
argv[i] = NULL;
execve(argv[0], argv, environ);
if (execvpe(argv[0], argv, environ) < 0) {
log_error("dhcp failed (%d): %m", errno);
}
_exit(1);
}
@ -504,7 +530,7 @@ static int supplicant_group_new(struct supplicant *s,
j = shl_dlist_entry(i,
struct supplicant_group,
list);
if (j->subnet == j->subnet)
if (j->subnet == subnet)
break;
}
@ -871,7 +897,7 @@ static void supplicant_parse_peer(struct supplicant *s,
/* TODO: wfd_dev_info only contains the dev-info sub-elem,
* while wfd_sublemens contains all. Fix that! The user has no
* chance to distinguish both.
* We currently use it only as boolen (set/unset) but once we
* We currently use it only as boolean (set/unset) but once we
* parse it we _definitely_ have to provide proper data. */
r = wpas_message_dict_read(m, "wfd_dev_info", 's', &val);
if (r >= 0) {
@ -1287,6 +1313,20 @@ static void supplicant_event_p2p_group_removed(struct supplicant *s,
supplicant_group_free(g);
}
static void supplicant_event_p2p_go_neg_failure(struct supplicant *s,
struct wpas_message *ev)
{
struct peer *p;
if (s->pending) {
log_debug("peer %s group owner negotiation failed",
s->pending->friendly_name);
p = s->pending->p;
s->pending = NULL;
peer_supplicant_formation_failure(p, "group owner negotiation failed");
}
}
static void supplicant_event_p2p_group_formation_failure(struct supplicant *s,
struct wpas_message *ev)
{
@ -1432,7 +1472,6 @@ static void supplicant_event(struct supplicant *s, struct wpas_message *m)
!strcmp(name, "WPS-AP-AVAILABLE-PIN") ||
!strcmp(name, "CTRL-EVENT-EAP-STATUS") ||
!strcmp(name, "CTRL-EVENT-EAP-METHOD") ||
!strcmp(name, "CTRL-EVENT-EAP-STATUS") ||
!strcmp(name, "WPS-CRED-RECEIVED") ||
!strcmp(name, "WPS-AP-AVAILABLE") ||
!strcmp(name, "WPS-REG-SUCCESS") ||
@ -1467,6 +1506,8 @@ static void supplicant_event(struct supplicant *s, struct wpas_message *m)
supplicant_event_p2p_group_started(s, m);
else if (!strcmp(name, "P2P-GROUP-REMOVED"))
supplicant_event_p2p_group_removed(s, m);
else if (!strcmp(name, "P2P-GO-NEG-FAILURE"))
supplicant_event_p2p_go_neg_failure(s, m);
else if (!strcmp(name, "P2P-GROUP-FORMATION-FAILURE"))
supplicant_event_p2p_group_formation_failure(s, m);
else if (!strcmp(name, "AP-STA-CONNECTED"))
@ -2142,6 +2183,9 @@ static int supplicant_global_fn(struct wpas *w,
}
/* ignore events on the global-iface, we only listen on dev-iface */
if(link_is_using_dev(s->l) && wpas_message_get_ifname(m)) {
supplicant_event(s, m);
}
return 0;
@ -2188,7 +2232,7 @@ static int supplicant_global_attach_fn(struct wpas *w,
* Devices with P2P_DEVICE support (instead of direct P2P_GO/CLIENT
* support) are broken with a *lot* of wpa_supplicant versions on the
* global interface. Therefore, try to open the p2p-dev-* interface
* after the global-ATTACH succeded (which means the iface is properly
* after the global-ATTACH succeeded (which means the iface is properly
* set up). If this works, use the p2p-dev-* interface, otherwise, just
* copy the global interface over to bus_dev.
* Event-forwarding is broken on the global-interface in such cases,
@ -2356,6 +2400,7 @@ static void supplicant_run(struct supplicant *s, const char *binary)
sigemptyset(&mask);
sigprocmask(SIG_SETMASK, &mask, NULL);
#ifdef ENABLE_SYSTEMD
/* redirect stdout/stderr to journal */
sprintf(journal_id, "miracle-wifid-%s-%u",
s->l->ifname, s->l->ifindex);
@ -2365,9 +2410,12 @@ static void supplicant_run(struct supplicant *s, const char *binary)
dup2(fd_journal, 1);
dup2(fd_journal, 2);
} else {
#endif
/* no journal? redirect stdout to parent's stderr */
dup2(2, 1);
#ifdef ENABLE_SYSTEMD
}
#endif
/* initialize wpa_supplicant args */
i = 0;
@ -2391,12 +2439,60 @@ static void supplicant_run(struct supplicant *s, const char *binary)
argv[i++] = s->l->ifname;
argv[i++] = "-g";
argv[i++] = s->global_ctrl;
if (arg_wpa_syslog) {
argv[i++] = "-s";
}
argv[i] = NULL;
/* execute wpa_supplicant; if it fails, the caller issues exit(1) */
execve(argv[0], argv, environ);
}
static int supplicant_find(char **binary)
{
_shl_free_ char *path = getenv("PATH");
if(!path) {
return -EINVAL;
}
path = strdup(path);
if(!path) {
return log_ENOMEM();
}
struct stat bin_stat;
char *curr = path, *next;
while(1) {
curr = strtok_r(curr, ":", &next);
if(!curr) {
break;
}
_shl_free_ char *bin = shl_strcat(curr, "/wpa_supplicant");
if (!bin)
return log_ENOMEM();
if(stat(bin, &bin_stat) < 0) {
if(ENOENT == errno || ENOTDIR == errno) {
goto end;
}
return log_ERRNO();
}
if (!access(bin, X_OK)) {
*binary = strdup(bin);
return 0;
}
end:
curr = NULL;
}
return -EINVAL;
}
static int supplicant_spawn(struct supplicant *s)
{
_shl_free_ char *binary = NULL;
@ -2410,14 +2506,16 @@ static int supplicant_spawn(struct supplicant *s)
log_debug("spawn supplicant of %s", s->l->ifname);
binary = shl_strcat(arg_wpa_bindir, "/wpa_supplicant");
if (!binary)
return log_ENOMEM();
if (access(binary, X_OK) < 0) {
log_error("execution of wpas (%s) not possible: %m", binary);
return -EINVAL;
if (supplicant_find(&binary) < 0) {
if (binary != NULL) {
log_error("execution of wpas (%s) not possible: %m", binary);
} else {
log_error("execution of wpas not possible: %m");
}
return -EINVAL;
}
log_info("wpa_supplicant found: %s", binary);
pid = fork();
if (pid < 0) {
@ -2532,10 +2630,9 @@ static int supplicant_write_config(struct supplicant *s)
"driver_param=%s\n"
"ap_scan=%s\n"
"# End of configuration\n",
s->l->friendly_name ? : "unknown",
s->l->friendly_name ?: "unknown",
"1-0050F204-1",
"pbc",
//"pbc keypad pin display",
s->l->config_methods ?: "pbc",
"p2p_device=1",
"1");
if (r < 0) {

View file

@ -40,9 +40,16 @@
#include "wifid.h"
#include "config.h"
const char *arg_wpa_bindir = "/usr/bin";
#define XSTR(x) STR(x)
#define STR(x) #x
const char *interface_name = NULL;
const char *config_methods = NULL;
unsigned int arg_wpa_loglevel = LOG_NOTICE;
bool arg_wpa_syslog = false;
bool use_dev = false;
bool lazy_managed = false;
const char *arg_ip_binary = NULL;
/*
* Manager Handling
@ -101,13 +108,22 @@ static void manager_add_udev_link(struct manager *m,
if (r < 0)
return;
link_set_friendly_name(l, m->friendly_name);
if (m->friendly_name && l->managed)
link_set_friendly_name(l, m->friendly_name);
if (m->config_methods)
link_set_config_methods(l, m->config_methods);
if(use_dev)
link_use_dev(l);
if(arg_ip_binary)
link_set_ip_binary(l, arg_ip_binary);
#ifdef RELY_UDEV
if (udev_device_has_tag(d, "miracle")) {
bool managed = udev_device_has_tag(d, "miracle") && !lazy_managed;
#else
if (!interface_name || !strcmp(interface_name, ifname)) {
bool managed = (!interface_name || !strcmp(interface_name, ifname)) && !lazy_managed;
#endif
if (managed) {
link_set_managed(l, true);
} else {
log_debug("ignored device: %s", ifname);
@ -147,12 +163,12 @@ static int manager_udev_fn(sd_event_source *source,
}
#ifdef RELY_UDEV
if (udev_device_has_tag(d, "miracle"))
if (udev_device_has_tag(d, "miracle") && !lazy_managed)
link_set_managed(l, true);
else
link_set_managed(l, false);
#else
if (!interface_name || !strcmp(interface_name, ifname)) {
if ((!interface_name || !strcmp(interface_name, ifname)) && !lazy_managed) {
link_set_managed(l, true);
} else {
log_debug("ignored device: %s", ifname);
@ -212,6 +228,7 @@ static void manager_free(struct manager *m)
sd_event_unref(m->event);
free(m->friendly_name);
free(m->config_methods);
free(m);
}
@ -224,6 +241,7 @@ static int manager_new(struct manager **out)
unsigned int i;
sigset_t mask;
int r;
char *cm;
m = calloc(1, sizeof(*m));
if (!m)
@ -231,6 +249,16 @@ static int manager_new(struct manager **out)
shl_htable_init_uint(&m->links);
if (config_methods) {
cm = strdup(config_methods);
if (!cm)
return log_ENOMEM();
free(m->config_methods);
m->config_methods = cm;
}
r = sd_event_default(&m->event);
if (r < 0) {
log_vERR(r);
@ -454,11 +482,16 @@ static int help(void)
" --version Show package version\n"
" --log-level <lvl> Maximum level for log messages\n"
" --log-time Prefix log-messages with timestamp\n"
" --log-date-time Prefix log-messages with date time\n"
"\n"
" -i --interface Choose the interface to use\n"
" --config-methods Define config methods for pairing, default 'pbc'\n"
"\n"
" --wpa-bindir <dir> wpa_supplicant binary dir [/usr/bin]\n"
" --wpa-loglevel <lvl wpa_supplicant log-level\n"
" --wpa-loglevel <lvl> wpa_supplicant log-level\n"
" --wpa-syslog wpa_supplicant use syslog\n"
" --use-dev enable workaround for 'no ifname' issue\n"
" --lazy-managed manage interface only when user decide to do\n"
" --ip-binary <path> path to 'ip' binary [default: "XSTR(IP_BINARY)"]\n"
, program_invocation_short_name);
/*
* 80-char barrier:
@ -474,19 +507,28 @@ static int parse_argv(int argc, char *argv[])
ARG_VERSION = 0x100,
ARG_LOG_LEVEL,
ARG_LOG_TIME,
ARG_WPA_BINDIR,
ARG_LOG_DATE_TIME,
ARG_WPA_LOGLEVEL,
ARG_WPA_SYSLOG,
ARG_USE_DEV,
ARG_CONFIG_METHODS,
ARG_LAZY_MANAGED,
ARG_IP_BINARY,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "log-level", required_argument, NULL, ARG_LOG_LEVEL },
{ "log-time", no_argument, NULL, ARG_LOG_TIME },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "log-level", required_argument, NULL, ARG_LOG_LEVEL },
{ "log-time", no_argument, NULL, ARG_LOG_TIME },
{ "log-date-time", no_argument, NULL, ARG_LOG_DATE_TIME },
{ "wpa-bindir", required_argument, NULL, ARG_WPA_BINDIR },
{ "wpa-loglevel", required_argument, NULL, ARG_WPA_LOGLEVEL },
{ "wpa-syslog", no_argument, NULL, ARG_WPA_SYSLOG },
{ "interface", required_argument, NULL, 'i' },
{ "use-dev", no_argument, NULL, ARG_USE_DEV },
{ "config-methods", required_argument, NULL, ARG_CONFIG_METHODS },
{ "lazy-managed", no_argument, NULL, ARG_LAZY_MANAGED },
{ "ip-binary", required_argument, NULL, ARG_IP_BINARY },
{}
};
int c;
@ -507,13 +549,27 @@ static int parse_argv(int argc, char *argv[])
case ARG_LOG_TIME:
log_init_time();
break;
case ARG_WPA_BINDIR:
arg_wpa_bindir = optarg;
case ARG_LOG_DATE_TIME:
log_date_time = true;
break;
case ARG_USE_DEV:
use_dev = true;
break;
case ARG_CONFIG_METHODS:
config_methods = optarg;
break;
case ARG_LAZY_MANAGED:
lazy_managed = true;
break;
case ARG_WPA_LOGLEVEL:
arg_wpa_loglevel = log_parse_arg(optarg);
break;
case ARG_WPA_SYSLOG:
arg_wpa_syslog = true;
break;
case ARG_IP_BINARY:
arg_ip_binary = optarg;
break;
case '?':
return -EINVAL;
}
@ -539,12 +595,30 @@ int main(int argc, char **argv)
srand(time(NULL));
GKeyFile* gkf = load_ini_file();
if (gkf) {
gchar* log_level;
log_level = g_key_file_get_string (gkf, "wifid", "log-level", NULL);
if (log_level) {
log_max_sev = log_parse_arg(log_level);
g_free(log_level);
}
g_key_file_free(gkf);
}
r = parse_argv(argc, argv);
if (r < 0)
return EXIT_FAILURE;
if (!r)
return EXIT_SUCCESS;
if (getuid() != 0) {
r = EACCES;
log_notice("Must run as root");
goto finish;
}
r = manager_new(&m);
if (r < 0)
goto finish;

View file

@ -129,12 +129,15 @@ struct link {
char *ifname;
char *friendly_name;
char *wfd_subelements;
char *config_methods;
char *ip_binary;
size_t peer_cnt;
struct shl_htable peers;
bool managed : 1;
bool public : 1;
bool use_dev : 1;
};
#define link_from_htable(_l) \
@ -153,9 +156,17 @@ int link_new(struct manager *m,
struct link **out);
void link_free(struct link *l);
void link_set_managed(struct link *l, bool set);
/* workaround for the 'no ifname' issue */
void link_use_dev(struct link *l);
bool link_is_using_dev(struct link *l);
int link_set_ip_binary(struct link *l, const char *ip_binary);
int link_set_managed(struct link *l, bool set);
bool link_get_managed(struct link *l);
int link_renamed(struct link *l, const char *ifname);
int link_set_config_methods(struct link *l, char *config_methods);
int link_set_friendly_name(struct link *l, const char *name);
const char *link_get_friendly_name(struct link *l);
int link_set_wfd_subelements(struct link *l, const char *val);
@ -183,6 +194,7 @@ struct manager {
sd_event_source *udev_mon_source;
char *friendly_name;
char *config_methods;
size_t link_cnt;
struct shl_htable links;
@ -203,7 +215,7 @@ void manager_dbus_disconnect(struct manager *m);
/* cli arguments */
extern const char *arg_wpa_bindir;
extern unsigned int arg_wpa_loglevel;
extern bool arg_wpa_syslog;
#endif /* WIFID_H */

View file

@ -1,31 +1,31 @@
########### next target ###############
find_package(PkgConfig)
pkg_check_modules (CHECK check)
if(CHECK_FOUND)
set(test_rtsp_SOURCES test_common.h test_rtsp.c)
add_executable(test_rtsp ${test_rtsp_SOURCES})
target_link_libraries(test_rtsp miracle-shared)
target_link_libraries(test_rtsp ${CHECK_LIBRARIES})
target_link_libraries(test_rtsp ${UDEV_LIBRARIES})
target_link_libraries(test_rtsp ${GLIB2_LIBRARIES})
target_link_libraries(test_rtsp ${CHECK_LIBRARIES})
target_link_libraries(test_rtsp ${CHECK_CFLAGS})
set(test_wpas_SOURCES test_common.h test_wpas.c)
add_executable(test_wpas ${test_wpas_SOURCES})
target_link_libraries(test_wpas miracle-shared)
target_link_libraries(test_wpas ${CHECK_LIBRARIES})
target_link_libraries(test_wpas ${UDEV_LIBRARIES})
target_link_libraries(test_wpas ${GLIB2_LIBRARIES})
target_link_libraries(test_wpas ${CHECK_LIBRARIES})
target_link_libraries(test_wpas ${CHECK_CFLAGS})
target_link_libraries(test_wpas m)
set(test_valgrind_SOURCES test_common.h test_valgrind.c)
add_executable(test_valgrind ${test_valgrind_SOURCES})
target_link_libraries(test_valgrind miracle-shared)
target_link_libraries(test_valgrind ${CHECK_LIBRARIES})
target_link_libraries(test_valgrind ${UDEV_LIBRARIES})
target_link_libraries(test_valgrind ${GLIB2_LIBRARIES})
target_link_libraries(test_valgrind ${CHECK_LIBRARIES})
target_link_libraries(test_valgrind ${CHECK_CFLAGS})
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/shared)
@ -39,7 +39,7 @@ if(CHECK_FOUND)
add_custom_target(memcheck
DEPENDS memcheck-verify
COMMAND for i in $(MEMTESTS) |
COMMAND for i in $$(MEMTESTS) |
do |
${VALGRIND} --log-file=${CMAKE_SOURCE_DIR}/$$i.memlog |
${CMAKE_SOURCE_DIR}/$$i >/dev/null || (echo "memcheck failed on: $$i" ; exit 1) ; |
@ -48,69 +48,3 @@ if(CHECK_FOUND)
COMMENT "verify memcheck")
endif(CHECK_FOUND)
########### install files ###############
#original Makefile.am contents follow:
#include $(top_srcdir)/common.am
#tests = \
# test_rtsp \
# test_wpas
#
#if BUILD_HAVE_CHECK
#check_PROGRAMS = $(tests) test_valgrind
#TESTS = $(tests) test_valgrind
#MEMTESTS = $(tests)
#endif
#
#test_sources = \
# test_common.h
#test_libs = \
# ../src/shared/libmiracle-shared.la \
# $(DEPS_LIBS) \
# $(CHECK_LIBS)
#test_cflags = \
# $(AM_CPPFLAGS) \
# $(DEPS_CFLAGS) \
# $(CHECK_CFLAGS)
#
#test_rtsp_SOURCES = test_rtsp.c $(test_sources)
#test_rtsp_CPPFLAGS = $(test_cflags)
#test_rtsp_LDADD = $(test_libs)
#
#test_valgrind_SOURCES = test_valgrind.c $(test_sources)
#test_valgrind_CPPFLAGS = $(test_cflags)
#test_valgrind_LDADD = $(test_libs)
#
#test_wpas_SOURCES = test_wpas.c $(test_sources)
#test_wpas_CPPFLAGS = $(test_cflags)
#test_wpas_LDADD = $(test_libs)
#
### custom recipes
#
#VALGRIND = CK_FORK=no valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --leak-resolution=high --error-exitcode=1 --suppressions=$(top_builddir)/test.supp
#
## verify that test_valgrind actually leaks data
#memcheck-verify: check
# $(AM_V_GEN)$(VALGRIND) --log-file=/dev/null ./test_valgrind >/dev/null ; test 1 = $$?
#
## run memcheck tests via valgrind
#memcheck: memcheck-verify
# $(AM_V_GEN)for i in $(MEMTESTS) ; do \
# $(VALGRIND) --log-file=$(top_builddir)/$$i.memlog \
# $(top_builddir)/$$i >/dev/null || (echo "memcheck failed on: $$i" ; exit 1) ; \
# done
#
#distcheck-hook: memcheck
#AM_MAKEFLAGS = --no-print-directory
#AUTOMAKE_OPTIONS = color-tests
#
##
## Phony targets
##
#
#.PHONY: memcheck-verify

35
test/meson.build Normal file
View file

@ -0,0 +1,35 @@
check = dependency('check', required: false)
deps = [udev, glib2, check, libsystemd, libmiracle_shared_dep, m]
if check.found()
test_rtsp = executable('test_rtsp', 'test_rtsp.c', dependencies: deps)
test_wpas = executable('test_wpas', 'test_wpas.c', dependencies: deps)
test_valgrind = executable('test_valgrind',
'test_valgrind.c',
dependencies: deps
)
test('rtsp test', test_rtsp)
test('wpas test', test_wpas)
test('valgrind test', test_valgrind)
# set(VALGRIND CK_FORK=no valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --leak-resolution=high --error-exitcode=1 --suppressions=${CMAKE_SOURCE_DIR}/test.supp)
#
# add_custom_target(memcheck-verify
# DEPENDS test_rtsp test_wpas test_valgrind
# COMMAND ${VALGRIND} --log-file=/dev/null ./test_valgrind >/dev/null |
# test 1 = $$?
# COMMENT "verify memcheck")
#
# add_custom_target(memcheck
# DEPENDS memcheck-verify
# COMMAND for i in $(MEMTESTS) |
# do |
# ${VALGRIND} --log-file=${CMAKE_SOURCE_DIR}/$$i.memlog |
# ${CMAKE_SOURCE_DIR}/$$i >/dev/null || (echo "memcheck failed on: $$i" ; exit 1) ; |
# done
# SOURCES test_rtsp test_valgrind test_wpas
# COMMENT "verify memcheck")
endif