pax_global_header00006660000000000000000000000064145545023440014520gustar00rootroot0000000000000052 comment=ea116aa4430f39fa784eba7d5fb2dd7dec316ae6 hfd-service-0.2.2/000077500000000000000000000000001455450234400137205ustar00rootroot00000000000000hfd-service-0.2.2/.gitignore000066400000000000000000000006101455450234400157050ustar00rootroot00000000000000 # Created by https://www.gitignore.io/api/cmake # Edit at https://www.gitignore.io/?templates=cmake ### CMake ### CMakeLists.txt.user CMakeCache.txt CMakeFiles CMakeScripts Testing Makefile cmake_install.cmake install_manifest.txt compile_commands.json CTestTestfile.cmake _deps ### CMake Patch ### # External projects *-prefix/ # End of https://www.gitignore.io/api/cmake build build-* hfd-service-0.2.2/AUTHORS000066400000000000000000000002661455450234400147740ustar00rootroot00000000000000Alfred Neumayer Andreas Bart Ribbers Caleb Connolly Erfan Abdi Florian Leeber Guido Berhoerster Jami Kettunen Marius Gripsgard Matthias Gerstner Mike Gabriel Ratchanan Srirattanamet hfd-service-0.2.2/CMakeLists.txt000066400000000000000000000050461455450234400164650ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5) project(hfd-service VERSION 0.2.2) set(CMAKE_CXX_STANDARD 14) set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) include(FindPkgConfig) include(GNUInstallDirs) pkg_check_modules(UDEV REQUIRED libudev) pkg_check_modules(DEVICEINFO REQUIRED deviceinfo) # For variables only. pkg_check_modules(DBUS_1 REQUIRED dbus-1) pkg_get_variable(DBUS_INTERFACES_DIR dbus-1 interfaces_dir) pkg_check_modules(ACCOUNTSSERVICE REQUIRED accountsservice) pkg_get_variable(AS_INTERFACES_DIR accountsservice interfacesdir) option(ENABLE_LIBHYBRIS "Enable libhybris support" ON) if (ENABLE_LIBHYBRIS) pkg_check_modules(ANDROID_HEADERS android-headers) pkg_check_modules(ANDROID_HARDWARE libhardware) if(ANDROID_HEADERS_FOUND AND ANDROID_HARDWARE_FOUND) message(STATUS "Bulding with libhybris support") set(HAVE_LIBHYBRIS true) else() message(WARNING "Bulding without libhybris support, missing required dependencies!") endif() else() message(STATUS "Bulding without libhybris support") endif() option(ENABLE_LIBGBINDER "Enable libgbinder support" ON) if (ENABLE_LIBGBINDER) pkg_check_modules(GLIB_UTIL libglibutil) pkg_check_modules(GBINDER libgbinder) if(GLIB_UTIL_FOUND AND GBINDER_FOUND) message(STATUS "Bulding with libgbinder support") set(HAVE_LIBGBINDER true) else() message(WARNING "Bulding without libgbinder support, missing required dependencies!") endif() else() message(STATUS "Bulding without libgbinder support") endif() find_package(Qt5Core REQUIRED) find_package(Qt5DBus REQUIRED) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") add_subdirectory(src) add_subdirectory(tools) add_subdirectory(qt) add_subdirectory(init) # Dbus policy install(FILES data/com.lomiri.hfd.conf DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d ) # Dbus service file configure_file(data/com.lomiri.hfd.service.in com.lomiri.hfd.service) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/com.lomiri.hfd.service DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/dbus-1/system-services ) # AccountsService interface install(FILES data/com.lomiri.hfd.AccountsService.Settings.xml DESTINATION ${DBUS_INTERFACES_DIR}) install(DIRECTORY DESTINATION ${AS_INTERFACES_DIR}) install(CODE "message(STATUS \"Symlinking: \$ENV{DESTDIR}${AS_INTERFACES_DIR}/com.lomiri.hfd.AccountsService.Settings.xml\")") install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ../../dbus-1/interfaces/com.lomiri.hfd.AccountsService.Settings.xml \$ENV{DESTDIR}${AS_INTERFACES_DIR}/com.lomiri.hfd.AccountsService.Settings.xml)") hfd-service-0.2.2/ChangeLog000066400000000000000000000175401455450234400155010ustar00rootroot000000000000002024-01-25 Mike Gabriel * Release 0.2.2 (HEAD -> main, tag: 0.2.2) * Merge branch 'personal/gberh/improve-metadata' into 'main' (b5451ce) 2024-01-25 Guido Berhoerster * Improve README file and Debian package metadata (28e9b3e) 2023-11-19 Marius Gripsgard * Merge branch 'personal/peat-psuwit/11-vibratePattern' into 'main' (dd7626a) 2023-11-10 Ratchanan Srirattanamet * service: add vibratePattern DBus API (d9b103c) 2023-11-09 Ratchanan Srirattanamet * Moves vibrateDurationExtraMs handling to VibratorController (ef940dd) * vibrator: move pattern repetition to non-core and use QTimer (d1054fb) 2023-11-10 Marius Gripsgard * Merge branch 'fix/musl-build' into 'main' (06ea1d2) 2023-10-17 Bart Ribbers * Add missing include to fix Musl compatibility (44fa015) 2023-10-15 Mike Gabriel * Release 0.2.1 (23ade31) (tag: 0.2.1) 2023-08-18 Mike Gabriel * Merge branch 'personal/peat-psuwit/othervibrate' into 'main' (b268aaa) 2023-08-11 Ratchanan Srirattanamet * usersettings: handle user deletion (b2e97a7) 2023-08-10 Ratchanan Srirattanamet * qt/feedback-plugin: use privileged path if env var is set (e991b80) 2023-08-04 Ratchanan Srirattanamet * Add settings to allow disabling general vibration (f9ecd13) 2023-02-05 Mike Gabriel * Release 0.2.0 (04ff43e) (tag: 0.2.0) 2023-01-30 Mike Gabriel * Merge branch 'personal/gberh/dh-12' into 'main' (8f278d0) 2023-01-30 Guido Berhoerster * Update to dh version 12 (dcb9bc7) 2022-12-02 Mike Gabriel * Merge branch 'abuGit-main-patch-97528' into 'main' (7c7b264) 2022-12-02 Andreas * Spelling mistake: sutch --> such (cc391dd) 2022-10-06 Mike Gabriel * Merge branch 'focal-vibrate-scale' into 'main' (de06edd) 2022-08-31 Jami Kettunen * service: Add support for vibrate duration length extension (1c36299) 2022-08-01 Ratchanan Srirattanamet * Merge branch 'fix-dbus-systemd-apparmor' into 'main' (dc3d89b) 2022-08-01 Guido Berhoerster * Allow DBus activation by AppArmor-confined apps (d128f51) * Make hfd-service DBus-activatable (eee84ad) * Correct path of the executable in the systemd unit file (85ae8d1) * Correct path of the executable in the DBus service file (1c03fe3) 2022-07-03 Mike Gabriel * Merge branch 'mariogrip-main-patch-06981' into 'main' (f52d1b7) 2022-07-03 Marius Gripsgard * debian/control: Depend on systemd (e25fcd0) 2022-01-23 Marius Gripsgard * Merge branch 'mr/switch-to-systemd' into 'main' (23e9c69) 2021-12-17 Florian Leeber * Merge branch 'Flohack74-main-patch-59104' into 'main' (d230558) * Allow 0 value for LEDs off milliseconds (620a2ee) 2021-11-27 Marius Gripsgard * Merge branch 'main' into 'main' (5c747fb) 2021-11-22 Marius Gripsgard * Merge branch 'xenial' into 'main' (5f920c6) 2021-06-28 Matthias Gerstner * vibrator-ff: check for valid file descriptor before writing (d3eafe5) 2021-06-25 Matthias Gerstner * global: fix various compiler warnings (0186b78) * vibrator-ff: zero-initialize features array, check for ioctl() error (07246e9) * DBusAdaptorService: check for bad integer values passed via D-Bus (92954aa) * vibrator-ff: open files with O_CLOEXEC (e2a8d08) * vibrator-legacy: remove unused m_thread member (8b0566c) 2021-10-06 Ratchanan Srirattanamet * Move Jenkinsfile to debian/ per the new guideline (05ef6e0) * Update Jenkinsfile to use shared library (873551d) 2021-10-02 Erfan Abdi * Proper android 8+ support (#21) (8d6e2e1) 2021-09-08 Florian Leeber * Add missing .service file (#20) (a05537e) 2021-04-13 Marius Gripsgard * Merge branch 'mr/debian-folder-copy-paste-flaw-fixes' into 'main' (7bc710d) 2021-04-09 Mike Gabriel * Revert "data/com.lomiri.hfd.conf: Fix D-Bus policy without send destination." (bfc78ee) * init/hfd-service.service.in: Add Documentation= key (pointing to upstream Git repo, i.e. its Readme.md). (4a4e1e6) * data/com.lomiri.hfd.conf: Fix D-Bus policy without send destination. (945e7b8) * data/com.lomiri.hfd.conf: Fix D-Bus policy without send destination. (ffc4e30) * debian/hfd-service.install: Update for systemd switch-over and moved service binary. (8048bc7) * init/: Add systemd system service file. (b427598) * src/CMakeLists.txt: Install hfd-service executable to LIBEXECDIR. (ecc76ca) * data/hfd-service.conf: Drop file. Drop upstart support. (e0aec42) * debian/control: Upstream repo has been moved to gitlab.com. (6c018d6) * debian/copyright: Upstream repo has been moved to gitlab.com. (ac4b7c9) * debian/: Fix copy-paste flaws (wrong pkg name). (88afa6c) 2021-03-16 Marius Gripsgard * Merge pull request #16 from PureTryOut/sysconfdir (d7a36de) (tag: 0.1.0) * Merge pull request #17 from PureTryOut/spelling-mistakes (34415cd) * Merge pull request #15 from ubports/xenial_-_waitforlxc (00c850e) * Merge pull request #14 from calebccff/vibrator-forcefeedback (a564c9b) 2021-03-03 Bart Ribbers * Fix spelling mistakes when DBus connection fails (bc91acd) * Fix install location of conf files (b37e724) 2021-02-27 Alfred Neumayer * upstart: Speed up waiting for lxc-android-config to be up (effca39) 2021-01-28 Caleb Connolly * vibrator: ff: add new Force Feedback vibrator (014aec3) 2021-01-24 Marius Gripsgard * Merge pull request #13 from ubports-oneplus6/oneplus6-fixes (c7d0237) 2021-01-21 Caleb Connolly * vibrator-sysfs: more robust transient trigger support detection (6bc0ace) 2021-01-18 Caleb Connolly * udevDevice: fix indent (96e56e2) * vibrator-sysfs: Don't crash when failing to add transient trigger (9c7c6dd) 2020-08-30 Marius Gripsgard * Merge remote-tracking branch 'origin/xenial_-_android9' into xenial (896ef75) * Merge branch 'xenial_-_hybrisopt' into xenial (1e15fb9) 2020-04-23 Alfred Neumayer * leds-hybris: Fix segfault when light device cannot be loaded (4e85878) 2020-04-16 Alfred Neumayer * service: Add env vars to switch between backend implementations (04681b9) 2020-07-24 Marius Gripsgard * Make libhybris optional (7378be4) * Add missing include for newer gcc (89871d8) * [cmake] Remove uneeded qt5_use_modules (f56ae91) 2020-07-07 Ratchanan Srirattanamet * leds: prioritise hybris backend (8bc7c0b) * Wait for Android container to be up (01d7964) 2020-06-27 Marius Gripsgard * Merge pull request #3 from peat-psuwit/xenial_-_hfd-fix-fp2 (5ccbb0e) 2020-06-17 Ratchanan Srirattanamet * leds-hybris: don't check for light device at construction (335e294) * leds-sysfs: don't use where it lacks timer trigger (2225a79) 2020-05-28 Marius Gripsgard * Merge pull request #2 from peat-psuwit/xenial_-_repeat-thread-cv (bd52bb0) * Merge pull request #1 from peat-psuwit/xenial_-_fix-rumble (b8b4501) 2020-05-27 Ratchanan Srirattanamet * Make repeatThread uses mutex/cv (7c15716) * [vibrator] capture duration by value (3ccc8d0) 2020-04-23 Marius Gripsgard * [leds-sysfs] Improve search for color leds (9469fb1) 2020-04-13 Marius Gripsgard * Move components to hfd namespace (c6b0a48) 2020-03-21 Marius Gripsgard * [qmlplugin] Make sure all values are up-to-date before setting state on (bbaded1) * [debian] Add missing deps (28d8900) * Inital commit (4c70084) hfd-service-0.2.2/README.md000066400000000000000000000010661455450234400152020ustar00rootroot00000000000000# HFD Service **H**uman **F**eedback **D**evice **Service** is a DBus activated service that manages human feedback devices such as leds and vibrators on mobile devices. It replaces usensord and Unity8's own LED/light handler. ## Why? We have a need for a more modular system running service now that we have different devices handling implementations differently. Secondly, we could not use Unity8's own LED handler which only runs in userspace from where it cannot access sysfs devices in a secure manner. We thus use a DBus service protected with AppArmor. hfd-service-0.2.2/data/000077500000000000000000000000001455450234400146315ustar00rootroot00000000000000hfd-service-0.2.2/data/com.lomiri.hfd.AccountsService.Settings.xml000066400000000000000000000010141455450234400250550ustar00rootroot00000000000000 hfd-service-0.2.2/data/com.lomiri.hfd.conf000066400000000000000000000006461455450234400203160ustar00rootroot00000000000000 hfd-service-0.2.2/data/com.lomiri.hfd.service.in000066400000000000000000000002421455450234400214260ustar00rootroot00000000000000[D-BUS Service] Name=com.lomiri.hfd Exec=@CMAKE_INSTALL_FULL_LIBEXECDIR@/hfd-service User=root SystemdService=hfd-service.service AssumedAppArmorLabel=unconfined hfd-service-0.2.2/data/com.lomiri.hfd.xml000066400000000000000000000021551455450234400201660ustar00rootroot00000000000000 hfd-service-0.2.2/debian/000077500000000000000000000000001455450234400151425ustar00rootroot00000000000000hfd-service-0.2.2/debian/Jenkinsfile000066400000000000000000000006511455450234400173300ustar00rootroot00000000000000@Library('ubports-build-tools') _ buildAndProvideDebianPackage() // Or if the package consists entirely of arch-independent packages: // (optional optimization, will confuse BlueOcean's live view at build stage) // buildAndProvideDebianPackage(/* isArchIndependent */ true) // Optionally, to skip building on some architectures (amd64 is always built): // buildAndProvideDebianPackage(false, /* ignoredArchs */ ['arm64']) hfd-service-0.2.2/debian/changelog000066400000000000000000000014761455450234400170240ustar00rootroot00000000000000hfd-service (0.2.2) unstable; urgency=medium * Upstream-provided Debian package for hfd-service. See upstream ChangeLog for recent changes. -- UBports developers Thu, 25 Jan 2024 16:57:24 +0100 hfd-service (0.2.1) unstable; urgency=medium * Upstream-provided Debian package for hfd-service. See upstream ChangeLog for recent changes. -- UBports developers Sun, 15 Oct 2023 01:10:40 +0200 hfd-service (0.2.0) unstable; urgency=medium * Upstream-provided Debian package for hfd-service. See upstream ChangeLog for recent changes. -- UBports developers Sun, 05 Feb 2023 01:02:11 +0100 hfd-service (0.1.0) xenial; urgency=medium * initial release -- Marius Gripsgard Sat, 21 Mar 2020 02:06:32 +0100 hfd-service-0.2.2/debian/control000066400000000000000000000045101455450234400165450ustar00rootroot00000000000000Source: hfd-service Section: admin Priority: optional Build-Depends: cmake (>= 3.5), debhelper-compat (= 12), pkg-config, libandroid-properties-dev, libdeviceinfo-dev, libhardware-dev, android-headers, libglib2.0-dev, libgbinder-dev, libglibutil-dev, libudev-dev, qtbase5-dev, qtfeedback5-dev, cmake-extras, qtdeclarative5-dev, systemd [linux-any], accountsservice:native, libaccountsservice-dev, libdbus-1-dev, Maintainer: Marius Gripsgard Standards-Version: 3.9.5 Homepage: https://gitlab.com/ubports/development/core/hfd-service Vcs-Git: https://gitlab.com/ubports/development/core/hfd-service.git Vcs-Browser: https://gitlab.com/ubports/development/core/hfd-service Package: hfd-service Section: libs Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, Description: HFD service for Lomiri HFD service is a DBus activated service that manages human feedback devices such as leds and vibrators on mobile devices. Package: libqt5feedback5-hfd Section: libs Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, hfd-service, Description: QtFeedback Qt plugin for HFD service HFD service is a DBus activated service that manages human feedback devices such as leds and vibrators on mobile devices. . This package provides a Qt plugin which integrates HFD service with the QtFeedback API. Package: qml-module-hfd Section: libs Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, hfd-service, Description: QML plugin for HFD service HFD service is a DBus activated service that manages human feedback devices such as leds and vibrators on mobile devices. . This package provides a QML plugin which provides access to HFD service. Package: hfd-service-tools Section: admin Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, hfd-service, Description: Tools for HFD service HFD service is a DBus activated service that manages human feedback devices such as leds and vibrators on mobile devices. . This package provides tools for testing HFD service. hfd-service-0.2.2/debian/copyright000066400000000000000000000020661455450234400171010ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: hfd-service Upstream-Contact: Marius Gripsgard Source: https://gitlab.com/ubports/core/hfd-service/ Files: * Copyright: 2019 UBports foundation, Marius Gripsgard License: GPL-3 License: GPL-3 This program is free software: you can redistribute it and/or modify it under the terms of the the GNU General Public License version 3, as published by the Free Software Foundation. . This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR PURPOSE. See the applicable version of the GNU Lesser General Public License for more details. . You should have received a copy of the GNU General Public License along with this program. If not, see . . On Debian systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL-3' hfd-service-0.2.2/debian/hfd-service-tools.install000066400000000000000000000000341455450234400220640ustar00rootroot00000000000000usr/bin/hfd-service-tools-* hfd-service-0.2.2/debian/hfd-service.install000066400000000000000000000004701455450234400207320ustar00rootroot00000000000000usr/libexec/hfd-service lib/systemd/system/hfd-service.service etc/dbus-1/system.d/com.lomiri.hfd.conf usr/share/dbus-1/system-services/com.lomiri.hfd.service usr/share/dbus-1/interfaces/com.lomiri.hfd.AccountsService.Settings.xml usr/share/accountsservice/interfaces/com.lomiri.hfd.AccountsService.Settings.xml hfd-service-0.2.2/debian/libqt5feedback5-hfd.install000066400000000000000000000000651455450234400222240ustar00rootroot00000000000000/usr/lib/*/qt5/plugins/feedback/libqtfeedback_hfd.so hfd-service-0.2.2/debian/qml-module-hfd.install000066400000000000000000000000311455450234400213370ustar00rootroot00000000000000/usr/lib/*/qt5/qml/Hfd/* hfd-service-0.2.2/debian/rules000077500000000000000000000001431455450234400162200ustar00rootroot00000000000000#!/usr/bin/make -f %: dh $@ --buildsystem cmake override_dh_missing: dh_missing --fail-missing hfd-service-0.2.2/init/000077500000000000000000000000001455450234400146635ustar00rootroot00000000000000hfd-service-0.2.2/init/CMakeLists.txt000066400000000000000000000012651455450234400174270ustar00rootroot00000000000000## ## Systemd Unit File ## pkg_check_modules(SYSTEMD systemd) if (${SYSTEMD_FOUND}) pkg_get_variable(SYSTEMD_SYSTEM_DIR systemd systemdsystemunitdir) message (STATUS "${SYSTEMD_SYSTEM_DIR} is the systemd system unit file install dir") set (SYSTEMD_SYSTEM_NAME "${CMAKE_PROJECT_NAME}.service") set (SYSTEMD_SYSTEM_FILE "${CMAKE_CURRENT_BINARY_DIR}/${SYSTEMD_SYSTEM_NAME}") set (SYSTEMD_SYSTEM_FILE_IN "${CMAKE_CURRENT_SOURCE_DIR}/${SYSTEMD_SYSTEM_NAME}.in") # build it configure_file ("${SYSTEMD_SYSTEM_FILE_IN}" "${SYSTEMD_SYSTEM_FILE}") # install it install (FILES "${SYSTEMD_SYSTEM_FILE}" DESTINATION "${SYSTEMD_SYSTEM_DIR}") endif() hfd-service-0.2.2/init/hfd-service.service.in000066400000000000000000000003711455450234400210520ustar00rootroot00000000000000[Unit] Description=HFD system service Documentation=https://gitlab.com/ubports/core/hfd-service/ [Service] Type=dbus BusName=com.lomiri.hfd ExecStart=@CMAKE_INSTALL_FULL_LIBEXECDIR@/hfd-service Restart=on-failure [Install] WantedBy=default.target hfd-service-0.2.2/qt/000077500000000000000000000000001455450234400143445ustar00rootroot00000000000000hfd-service-0.2.2/qt/CMakeLists.txt000066400000000000000000000000701455450234400171010ustar00rootroot00000000000000add_subdirectory(feedback-plugin) add_subdirectory(qml) hfd-service-0.2.2/qt/demo/000077500000000000000000000000001455450234400152705ustar00rootroot00000000000000hfd-service-0.2.2/qt/demo/feedback.qml000066400000000000000000000016371455450234400175360ustar00rootroot00000000000000import QtQuick 2.7 import Ubuntu.Components 1.3 import QtFeedback 5.0 MainView { id: root objectName: 'mainView' applicationName: "app" automaticOrientation: true width: units.gu(45) height: units.gu(75) Page { anchors.fill: parent header: PageHeader { id: header title: i18n.tr("led test") } Column { anchors { top: header.bottom left: parent.left right: parent.right bottom: parent.bottom } Button { text: "feedback" onClicked: haptic.start(); } } HapticsEffect { id: haptic attackIntensity: 0.0 attackTime: 50 intensity: 1.0 duration: 100 fadeTime: 50 fadeIntensity: 0.0 } } } hfd-service-0.2.2/qt/demo/leds.qml000066400000000000000000000012041455450234400167270ustar00rootroot00000000000000import QtQuick 2.7 import Ubuntu.Components 1.3 import Hfd 0.1 MainView { id: root objectName: 'mainView' applicationName: "app" automaticOrientation: true width: units.gu(45) height: units.gu(75) Page { anchors.fill: parent header: PageHeader { id: header title: i18n.tr("led test") } Column { anchors.fill: parent Button { text: "on" onClicked: Leds.state = Leds.state == 0 ? 1 : 0 } Label { text: "state: " + Leds.state } } } } hfd-service-0.2.2/qt/feedback-plugin/000077500000000000000000000000001455450234400173645ustar00rootroot00000000000000hfd-service-0.2.2/qt/feedback-plugin/CMakeLists.txt000066400000000000000000000017001455450234400221220ustar00rootroot00000000000000find_package(PkgConfig) find_package(Qt5Core REQUIRED) find_package(Qt5DBus REQUIRED) find_package(Qt5Feedback REQUIRED) qt5_add_resources(FEEDBACK_RESOURCES feedback.qrc) set(QTFEEDBACK_HFD_SRC hfd_feedback.h hfd_feedback.cpp ) qt5_add_dbus_interface(QTFEEDBACK_HFD_SRC ${CMAKE_SOURCE_DIR}/data/com.lomiri.hfd.xml hfdInterface ) add_library( qtfeedback_hfd SHARED ${QTFEEDBACK_HFD_SRC} ${FEEDBACK_RESOURCES} ) # Ideally, we would read the plugin installation location from cmake # but this does not work currently. set(PLUGIN_INSTALL_LOCATION "${CMAKE_INSTALL_LIBDIR}/qt5/plugins/feedback") # get_target_property(PLUGIN_LOCATION Qt5::Feedback PLUGIN_LOCATION) message(STATUS "Installing Qt5 feedback plugin to: ${PLUGIN_INSTALL_LOCATION}") target_link_libraries( qtfeedback_hfd ${CMAKE_THREAD_LIBS_INIT} Qt5::Core Qt5::DBus Qt5::Feedback ) install( TARGETS qtfeedback_hfd LIBRARY DESTINATION ${PLUGIN_INSTALL_LOCATION}) hfd-service-0.2.2/qt/feedback-plugin/feedback.json000066400000000000000000000000601455450234400217770ustar00rootroot00000000000000{ "Interfaces": ["QFeedbackHapticsInterface"] } hfd-service-0.2.2/qt/feedback-plugin/feedback.qrc000066400000000000000000000001411455450234400216130ustar00rootroot00000000000000 feedback.json hfd-service-0.2.2/qt/feedback-plugin/hfd_feedback.cpp000066400000000000000000000132731455450234400224430ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #include "hfd_feedback.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hfdInterface.h" hfd::Feedback::Feedback() : QObject(), enabled(false), state(QFeedbackEffect::Stopped) { const auto objectPath = qEnvironmentVariableIsSet("HFD_USE_PRIVILEGED_INTERFACE") ? QStringLiteral("/com/lomiri/hfd/privileged") : QStringLiteral("/com/lomiri/hfd"); m_interface = std::make_shared("com.lomiri.hfd", objectPath, QDBusConnection::systemBus(), this); actuatorList << createFeedbackActuator(this, 42); } hfd::Feedback::~Feedback() { } QFeedbackInterface::PluginPriority hfd::Feedback::pluginPriority() { return PluginHighPriority; } QList hfd::Feedback::actuators() { return actuatorList; } void hfd::Feedback::setActuatorProperty(const QFeedbackActuator&, ActuatorProperty prop, const QVariant &value) { switch (prop) { case Enabled: enabled = value.toBool(); break; default: break; } } QVariant hfd::Feedback::actuatorProperty(const QFeedbackActuator &actuator, ActuatorProperty prop) { QVariant result; switch (prop) { case Name: result = QString::fromLocal8Bit("Hfd Vibrator"); break; case State: result = actuator.isValid() ? QFeedbackActuator::Ready : QFeedbackActuator::Unknown; break; case Enabled: result = enabled; break; } return result; } bool hfd::Feedback::isActuatorCapabilitySupported(const QFeedbackActuator &, QFeedbackActuator::Capability cap) { bool result = false; switch(cap) { case QFeedbackActuator::Envelope: result = true; break; case QFeedbackActuator::Period: result = false; break; } return result; } void hfd::Feedback::updateEffectProperty(const QFeedbackHapticsEffect *, EffectProperty) { } void hfd::Feedback::hapticsVibrateReply(QDBusPendingCallWatcher *watcher, int period, int repeat) { QDBusPendingReply<> reply = *watcher; if (reply.isError()) { qWarning() << "Failed to vibrate with pattern:" << reply.error().message(); state = QFeedbackEffect::Stopped; } else { if ((repeat == QFeedbackEffect::Infinite) || (--repeat > 0)) QTimer::singleShot(period*2, [=]() { vibrate(period, repeat); }); else state = QFeedbackEffect::Stopped; } watcher->deleteLater(); } void hfd::Feedback::vibrate(int period, int repeat) { if (!(period && repeat)) state = QFeedbackEffect::Stopped; if (state != QFeedbackEffect::Running) { // Maybe stopped/paused before this async call. return; } QDBusInterface iface("com.lomiri.hfd", "/com/lomiri/hfd", "com.lomiri.hfd.Vibrator"); QDBusPendingCall call = m_interface->vibrate(period); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this); connect(watcher, &QDBusPendingCallWatcher::finished, [=](){ hapticsVibrateReply(watcher, period, repeat); }); } void hfd::Feedback::startVibration(const QFeedbackHapticsEffect *effect) { int duration = effect->duration(); if (duration == 0) duration = 150; int period = effect->period(); int repeat; if ((duration == QFeedbackEffect::Infinite) || (duration < 0)) { // If duration is set to QFeedbackEffect::Infinite or a negative // value, we repeat this effect forever until stopped. The // effective period should have been set to a positive value or // 150ms by default. duration = QFeedbackEffect::Infinite; repeat = QFeedbackEffect::Infinite; if (period <= 0) period = 150; } else if (period <= 0) { // If duration is set to a positive value and period is invalid, // then use duration as period. repeat = 1; period = duration; } else { // Otherwise, repeat this effect as many times as the duration // may cover the effect period. repeat = (duration + period - 1) / period; } vibrate(period, repeat); } void hfd::Feedback::setEffectState(const QFeedbackHapticsEffect *effect, QFeedbackEffect::State state) { this->state = state; switch (state) { case QFeedbackEffect::Stopped: break; case QFeedbackEffect::Paused: break; case QFeedbackEffect::Running: QTimer::singleShot(0, [=]() { startVibration(effect); }); break; case QFeedbackEffect::Loading: break; } } QFeedbackEffect::State hfd::Feedback::effectState(const QFeedbackHapticsEffect *) { return state; } hfd-service-0.2.2/qt/feedback-plugin/hfd_feedback.h000066400000000000000000000042301455450234400221010ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #pragma once #include #include #include #include class QDBusPendingCallWatcher; class ComLomiriHfdVibratorInterface; namespace hfd { class Feedback : public QObject, public QFeedbackHapticsInterface { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtFeedbackPlugin" FILE "feedback.json") Q_INTERFACES(QFeedbackHapticsInterface) public: Feedback(); virtual ~Feedback(); // From QFeedbackHapticsInterface virtual PluginPriority pluginPriority(); QList actuators(); QFeedbackEffect::State effectState(const QFeedbackHapticsEffect *); void setEffectState (const QFeedbackHapticsEffect* effect, QFeedbackEffect::State state); bool isActuatorCapabilitySupported(const QFeedbackActuator &, QFeedbackActuator::Capability); void setActuatorProperty(const QFeedbackActuator &, ActuatorProperty, const QVariant &); QVariant actuatorProperty(const QFeedbackActuator &, ActuatorProperty); void updateEffectProperty(const QFeedbackHapticsEffect* effect, EffectProperty property); private: std::shared_ptr m_interface; QList actuatorList; void hapticsVibrateReply(QDBusPendingCallWatcher *watcher, int period, int repeat); void vibrate(int period, int repeat); void startVibration(const QFeedbackHapticsEffect *effect); bool enabled; QFeedbackEffect::State state; }; } hfd-service-0.2.2/qt/qml/000077500000000000000000000000001455450234400151355ustar00rootroot00000000000000hfd-service-0.2.2/qt/qml/CMakeLists.txt000066400000000000000000000006341455450234400177000ustar00rootroot00000000000000find_package(QmlPlugins) find_package(Qt5Gui REQUIRED) set(HFD_QML_SRC plugin.cpp leds.cpp ) qt5_add_dbus_interface(HFD_QML_SRC ${CMAKE_SOURCE_DIR}/data/com.lomiri.hfd.xml hfdInterface ) add_library(hfd-qml MODULE ${HFD_QML_SRC} ) target_link_libraries(hfd-qml hfd-core Qt5::Core Qt5::Gui Qt5::DBus ) add_qmlplugin( Hfd 1.0 Hfd TARGET_PREFIX Hfd TARGETS hfd-qml ) hfd-service-0.2.2/qt/qml/leds.cpp000066400000000000000000000044051455450234400165730ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #include "leds.h" #include #include "hfdInterface.h" Leds::Leds(QObject* parent) : QObject(parent), m_color("blue"), m_state(Leds::Off), m_onMs(1000), m_offMs(3000) { m_interface = std::make_shared("com.lomiri.hfd", "/com/lomiri/hfd", QDBusConnection::systemBus(), this); } void Leds::setState(Leds::State newState) { if (m_state != newState) { // Make sure all values are up-to-date before setting state on if (newState == State::On) { m_interface->setColor(m_color.rgba()); m_interface->setOnMs(m_onMs); m_interface->setOffMs(m_offMs); } m_interface->setState(newState); m_state = newState; Q_EMIT stateChanged(m_state); } } Leds::State Leds::state() const { return m_state; } void Leds::setColor(const QColor &color) { if (m_color != color) { m_interface->setColor(color.rgba()); m_color = color; Q_EMIT colorChanged(m_color); } } QColor Leds::color() const { return m_color; } int Leds::onMillisec() const { return m_onMs; } void Leds::setOnMillisec(int onMs) { if (m_onMs != onMs) { m_interface->setOnMs(onMs); m_onMs = onMs; Q_EMIT onMillisecChanged(m_onMs); } } int Leds::offMillisec() const { return m_offMs; } void Leds::setOffMillisec(int offMs) { if (m_offMs != offMs) { m_interface->setOffMs(offMs); m_offMs = offMs; Q_EMIT offMillisecChanged(m_offMs); } } hfd-service-0.2.2/qt/qml/leds.h000066400000000000000000000036261455450234400162440ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #pragma once #include #include #include class ComLomiriHfdLedsInterface; class Leds: public QObject { Q_OBJECT Q_PROPERTY(State state READ state WRITE setState NOTIFY stateChanged) Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) Q_PROPERTY(int onMillisec READ onMillisec WRITE setOnMillisec NOTIFY onMillisecChanged) Q_PROPERTY(int offMillisec READ offMillisec WRITE setOffMillisec NOTIFY offMillisecChanged) public: enum State { Off = 0, On = 1, }; Q_ENUM(State) explicit Leds(QObject *parent = 0); void setState(State newState); State state() const; void setColor(const QColor &color); QColor color() const; int onMillisec() const; void setOnMillisec(int onMs); int offMillisec() const; void setOffMillisec(int offMs); Q_SIGNALS: void stateChanged(State newState); void colorChanged(const QColor &color); void onMillisecChanged(int onMs); void offMillisecChanged(int offMs); private: std::shared_ptr m_interface; QColor m_color; State m_state; int m_onMs; int m_offMs; void turnOff(); void turnOn(); }; hfd-service-0.2.2/qt/qml/plugin.cpp000066400000000000000000000020731455450234400171410ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #include "plugin.h" #include "leds.h" #include static QObject *leds_provider(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) Q_UNUSED(scriptEngine) return new Leds(); } void HfdPlugin::registerTypes(const char *uri) { Q_ASSERT(uri == QLatin1String("Hfd")); qmlRegisterSingletonType(uri, 0, 1, "Leds", leds_provider); } hfd-service-0.2.2/qt/qml/plugin.h000066400000000000000000000017171455450234400166120ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #pragma once #include #include class HfdPlugin : public QQmlExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") public: void registerTypes(const char *uri) override; }; hfd-service-0.2.2/qt/qml/qmldir000066400000000000000000000000601455450234400163440ustar00rootroot00000000000000module Hfd plugin hfd-qml typeinfo hfd.qmltypes hfd-service-0.2.2/src/000077500000000000000000000000001455450234400145075ustar00rootroot00000000000000hfd-service-0.2.2/src/CMakeLists.txt000066400000000000000000000042561455450234400172560ustar00rootroot00000000000000add_subdirectory(udev) if (HAVE_LIBHYBRIS) include_directories( ${ANDROID_HEADERS_INCLUDE_DIRS} ) set(LIBHYBRIS_SRC leds-hybris.cpp ) set(LIBHYBRIS_LIBRARIES ${ANDROID_HARDWARE_LIBRARIES} ) add_definitions(-DHAVE_LIBHYBRIS) endif() if (HAVE_LIBGBINDER) include_directories( ${GLIB_UTIL_INCLUDE_DIRS} ${GBINDER_INCLUDE_DIRS} ) set(LIBGBINDER_SRC leds-binder.cpp vibrator-binder.cpp ) set(LIBGBINDER_LIBRARIES ${GLIB_UTIL_LDFLAGS} ${GLIB_UTIL_LIBRARIES} ${GBINDER_LDFLAGS} ${GBINDER_LIBRARIES} ) add_definitions(-DHAVE_LIBGBINDER) endif() add_library(hfd-core STATIC leds.cpp leds-sysfs.cpp vibrator.cpp vibrator-ff.cpp vibrator-sysfs.cpp vibrator-legacy.cpp ${LIBHYBRIS_SRC} ${LIBGBINDER_SRC} ) target_link_libraries(hfd-core udev-cpp ${LIBHYBRIS_LIBRARIES} ${LIBGBINDER_LIBRARIES} ) include_directories( ${DEVICEINFO_INCLUDE_DIRS} ) set(SERVICE_SRC credentialscache.cpp credentialscache.h service.cpp dbusAdaptor.h usersettings.cpp usersettings.h vibrator-controller.cpp vibrator-controller.h ) set_source_files_properties(bus.xml PROPERTIES CLASSNAME BusInterface NO_NAMESPACE TRUE) qt5_add_dbus_interface(interface_files bus.xml businterface) set_source_files_properties(properties.xml PROPERTIES CLASSNAME PropertiesInterface NO_NAMESPACE TRUE) qt5_add_dbus_interface(interface_files properties.xml propertiesinterface) set(ACCOUNTS_INTERFACE_FILE "${DBUS_INTERFACES_DIR}/org.freedesktop.Accounts.xml") set_source_files_properties(${ACCOUNTS_INTERFACE_FILE} PROPERTIES CLASSNAME ASInterface NO_NAMESPACE TRUE) qt5_add_dbus_interface(interface_files ${ACCOUNTS_INTERFACE_FILE} asinterface) qt5_add_dbus_adaptor(SERVICE_SRC ${CMAKE_SOURCE_DIR}/data/com.lomiri.hfd.xml ${CMAKE_CURRENT_SOURCE_DIR}/dbusAdaptor.h DbusAdaptor ) add_executable(hfd-service ${SERVICE_SRC} ${interface_files} ) target_link_libraries(hfd-service hfd-core Qt5::Core Qt5::DBus ${DEVICEINFO_LDFLAGS} ) install(TARGETS hfd-service RUNTIME DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}) hfd-service-0.2.2/src/bus.xml000066400000000000000000000010631455450234400160220ustar00rootroot00000000000000 hfd-service-0.2.2/src/common.h000066400000000000000000000014171455450234400161530ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #pragma once namespace hfd { enum State { Off, On }; } hfd-service-0.2.2/src/credentialscache.cpp000066400000000000000000000106111455450234400204730ustar00rootroot00000000000000/* * Copyright (C) 2015 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: James Henstridge */ #include "credentialscache.h" #include #include #include /* * Note: this file originates from lomiri-thumbnailer, with a few modifications * to remove its namespace and AppArmor support (not needed by us). */ using namespace std; namespace { char const DBUS_BUS_NAME[] = "org.freedesktop.DBus"; char const DBUS_BUS_PATH[] = "/org/freedesktop/DBus"; char const UNIX_USER_ID[] = "UnixUserID"; int const MAX_CACHE_SIZE = 50; } struct CredentialsCache::Request { QDBusPendingCallWatcher watcher; std::vector callbacks; Request(QDBusPendingReply const& call) : watcher(call) {} }; CredentialsCache::CredentialsCache(QDBusConnection const& bus) : bus_daemon_(DBUS_BUS_NAME, DBUS_BUS_PATH, bus) { } CredentialsCache::~CredentialsCache() = default; void CredentialsCache::get(QString const& peer, Callback const& callback) { // Return the credentials directly if they are cached try { Credentials const& credentials = cache_.at(peer); callback(credentials); return; } catch (std::out_of_range const &) { // ignore } // If the credentials exist in the previous generation of the // cache, move them to the current generation. try { Credentials& credentials = old_cache_.at(peer); // No real way to get coverage here because we'd // need more than 50 peers with different credentials. // LCOV_EXCL_START cache_.emplace(peer, std::move(credentials)); old_cache_.erase(peer); callback(cache_.at(peer)); return; // LCOV_EXCL_STOP } catch (std::out_of_range const &) { // ignore } // If the credentials are already being requested, add ourselves // to the callback list. try { unique_ptr& request = pending_.at(peer); request->callbacks.push_back(callback); return; } catch (std::out_of_range const &) { // ignore } // Ask the bus daemon for the peer's credentials unique_ptr request( new Request(bus_daemon_.GetConnectionCredentials(peer))); QObject::connect(&request->watcher, &QDBusPendingCallWatcher::finished, [this, peer](QDBusPendingCallWatcher *watcher) { this->received_credentials(peer, *watcher); }); request->callbacks.push_back(callback); pending_.emplace(peer, std::move(request)); } void CredentialsCache::received_credentials(QString const& peer, QDBusPendingReply const& reply) { Credentials credentials; if (reply.isError()) { // LCOV_EXCL_START qWarning() << "CredentialsCache::received_credentials(): " "error retrieving credentials for" << peer << ":" << reply.error().message(); // LCOV_EXCL_STOP } else { credentials.valid = true; // The contents of this map are described in the specification here: // http://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-get-connection-credentials credentials.user = reply.value().value(UNIX_USER_ID).value(); } // If we've hit our maximum cache size, start a new generation. if (cache_.size() >= MAX_CACHE_SIZE) { // LCOV_EXCL_START old_cache_ = std::move(cache_); cache_.clear(); // LCOV_EXCL_STOP } cache_.emplace(peer, credentials); // Notify anyone waiting on the request and remove it from the map: for (auto& callback : pending_.at(peer)->callbacks) { callback(credentials); } pending_.erase(peer); } hfd-service-0.2.2/src/credentialscache.h000066400000000000000000000033311455450234400201410ustar00rootroot00000000000000/* * Copyright (C) 2015 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: James Henstridge */ #pragma once #include "businterface.h" #include #include #include #include #include #include #include #include class CredentialsCache final { public: struct Credentials { bool valid = false; uid_t user = 0; }; typedef std::function Callback; CredentialsCache(QDBusConnection const& bus); ~CredentialsCache(); CredentialsCache(CredentialsCache const&) = delete; CredentialsCache& operator=(CredentialsCache const&) = delete; // Retrieve the security credentials for the given D-Bus peer. void get(QString const& peer, Callback const& callback); private: struct Request; BusInterface bus_daemon_; std::map cache_; std::map old_cache_; std::map> pending_; void received_credentials(QString const& peer, QDBusPendingReply const& reply); }; hfd-service-0.2.2/src/dbusAdaptor.h000066400000000000000000000023571455450234400171370ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #pragma once #include class DbusAdaptor : public QObject { Q_OBJECT public: DbusAdaptor(QObject *parent = 0) : QObject(parent) {}; public Q_SLOTS: virtual void vibrate() = 0; virtual void vibrate(int durationMs) = 0; virtual void rumble(int durationMs, int repeat) = 0; virtual void vibratePattern(const QVector &patternMs, uint repeat) = 0; virtual void setState(int state) = 0; virtual void setColor(unsigned int color) = 0; virtual void setOnMs(int ms) = 0; virtual void setOffMs(int ms) = 0; }; hfd-service-0.2.2/src/leds-binder.cpp000066400000000000000000000141751455450234400174130ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard * Author: Erfan Abdi */ #include "leds-binder.h" #include #include #define BINDER_LIGHT_SERVICE_DEVICE "/dev/hwbinder" #define BINDER_LIGHT_SERVICE_IFACE "android.hardware.light@2.0::ILight" #define BINDER_LIGHT_SERVICE_FQNAME BINDER_LIGHT_SERVICE_IFACE "/default" namespace hfd { bool LedsBinder::usable() { GBinderServiceManager *sm; GBinderRemoteObject *remote; GBinderClient *client; GBinderRemoteReply *reply; int status; sm = gbinder_servicemanager_new(BINDER_LIGHT_SERVICE_DEVICE); if (!sm) return false; remote = gbinder_servicemanager_get_service_sync(sm, BINDER_LIGHT_SERVICE_FQNAME, NULL); if (!remote) { gbinder_servicemanager_unref(sm); return false; } client = gbinder_client_new(remote, BINDER_LIGHT_SERVICE_IFACE); if (!client) { gbinder_remote_object_unref(remote); gbinder_servicemanager_unref(sm); return false; } GBinderLocalRequest *req = gbinder_client_new_request(client); reply = gbinder_client_transact_sync_reply(client, 2 /* getSupportedTypes */, req, &status); gbinder_local_request_unref(req); if (status == GBINDER_STATUS_OK) { GBinderReader reader; guint value; gbinder_remote_reply_init_reader(reply, &reader); status = gbinder_reader_read_uint32(&reader, &value); if (value != GBINDER_STATUS_OK) { gbinder_remote_object_unref(remote); gbinder_servicemanager_unref(sm); return false; } gsize count = 0; gsize vecSize = 0; const int32_t *types; types = (const int32_t*) gbinder_reader_read_hidl_vec(&reader, &count, &vecSize); for (int i = 0; i < count; i++) { if (types[i] == LIGHT_TYPE_NOTIFICATIONS) { gbinder_remote_object_unref(remote); gbinder_servicemanager_unref(sm); return true; } } } gbinder_remote_object_unref(remote); gbinder_servicemanager_unref(sm); return false; } LedsBinder::LedsBinder() : Leds() { mSm = gbinder_servicemanager_new(BINDER_LIGHT_SERVICE_DEVICE); if (!mSm) return; mRemote = gbinder_servicemanager_get_service_sync(mSm, BINDER_LIGHT_SERVICE_FQNAME, NULL); if (!mRemote) { gbinder_servicemanager_unref(mSm); return; } mClient = gbinder_client_new(mRemote, BINDER_LIGHT_SERVICE_IFACE); if (!mClient) { gbinder_remote_object_unref(mRemote); gbinder_servicemanager_unref(mSm); return; } // Get up to date configure(); } LedsBinder::~LedsBinder() { gbinder_remote_object_unref(mRemote); gbinder_servicemanager_unref(mSm); } void LedsBinder::configure() { if (m_state == State::On) turnOn(); else turnOff(); } void LedsBinder::turnOn() { GBinderRemoteReply *reply; GBinderWriter writer; LightState *notification_state; int status; GBinderLocalRequest *req = gbinder_client_new_request(mClient); gbinder_local_request_init_writer(req, &writer); notification_state = gbinder_writer_new0(&writer, LightState); int alpha = (m_color >> 24) & 0xFF; int red = (m_color >> 16) & 0xFF; int green = (m_color >> 8) & 0xFF; int blue = m_color & 0xFF; alpha = 0xff; //TODO: Support alpha int newcolor = ((alpha & 0xff) << 24) + ((red & 0xff) << 16) + ((green & 0xff) << 8) + (blue & 0xff); notification_state->color = newcolor; notification_state->flashMode = FLASH_TYPE_TIMED; notification_state->flashOnMs = m_onMs; notification_state->flashOffMs = m_offMs; notification_state->brightnessMode = BRIGHTNESS_MODE_USER; gbinder_writer_append_int32(&writer, LIGHT_TYPE_NOTIFICATIONS); gbinder_writer_append_buffer_object(&writer, notification_state, sizeof(*notification_state)); reply = gbinder_client_transact_sync_reply(mClient, 1 /* setLight */, req, &status); gbinder_local_request_unref(req); if (status == GBINDER_STATUS_OK) { GBinderReader reader; guint value; gbinder_remote_reply_init_reader(reply, &reader); status = gbinder_reader_read_uint32(&reader, &value); } } void LedsBinder::turnOff() { GBinderRemoteReply *reply; GBinderWriter writer; LightState *notification_state; int status; GBinderLocalRequest *req = gbinder_client_new_request(mClient); gbinder_local_request_init_writer(req, &writer); notification_state = gbinder_writer_new0(&writer, LightState); notification_state->color = 0; notification_state->flashMode = FLASH_TYPE_NONE; notification_state->flashOnMs = 0; notification_state->flashOffMs = 0; notification_state->brightnessMode = BRIGHTNESS_MODE_USER; gbinder_writer_append_int32(&writer, LIGHT_TYPE_NOTIFICATIONS); gbinder_writer_append_buffer_object(&writer, notification_state, sizeof(*notification_state)); reply = gbinder_client_transact_sync_reply(mClient, 1 /* setLight */, req, &status); gbinder_local_request_unref(req); if (status == GBINDER_STATUS_OK) { GBinderReader reader; guint value; gbinder_remote_reply_init_reader(reply, &reader); status = gbinder_reader_read_uint32(&reader, &value); } } } hfd-service-0.2.2/src/leds-binder.h000066400000000000000000000036141455450234400170540ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard * Author: Erfan Abdi */ #include "leds.h" #include #include #define ALIGNED(x) __attribute__((aligned(x))) enum { LIGHT_TYPE_BACKLIGHT = 0, LIGHT_TYPE_KEYBOARD = 1, LIGHT_TYPE_BUTTONS = 2, LIGHT_TYPE_BATTERY = 3, LIGHT_TYPE_NOTIFICATIONS = 4, LIGHT_TYPE_ATTENTION = 5, LIGHT_TYPE_BLUETOOTH = 6, LIGHT_TYPE_WIFI = 7, LIGHT_TYPE_COUNT = 8, }; enum { FLASH_TYPE_NONE = 0, FLASH_TYPE_TIMED = 1, FLASH_TYPE_HARDWARE = 2, }; enum { BRIGHTNESS_MODE_USER = 0, BRIGHTNESS_MODE_SENSOR = 1, BRIGHTNESS_MODE_LOW_PERSISTENCE = 2, }; struct LightState { uint32_t color ALIGNED(4); int32_t flashMode ALIGNED(4); int32_t flashOnMs ALIGNED(4); int32_t flashOffMs ALIGNED(4); int32_t brightnessMode ALIGNED(4); } ALIGNED(4); static_assert(sizeof(LightState) == 20, "wrong size"); namespace hfd { class LedsBinder : public Leds { public: LedsBinder(); ~LedsBinder(); static bool usable(); protected: void configure() override; private: void turnOn(); void turnOff(); bool initClient(); GBinderServiceManager* mSm; GBinderRemoteObject* mRemote; GBinderClient* mClient; }; } hfd-service-0.2.2/src/leds-hybris.cpp000066400000000000000000000066311455450234400174460ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #include "leds-hybris.h" #include #include extern "C" { #include #include #include } // legacy hybris support namespace hfd { light_device_t* getLightDevice() { int err; hw_module_t* module; light_device_t* lightDevice; err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module); if (err == 0) { hw_device_t* device; err = module->methods->open(module, LIGHT_ID_NOTIFICATIONS, &device); if (err == 0 && device) { lightDevice = (light_device_t*)device; return lightDevice; } else { std::cout << "Failed to access notification lights" << std::endl; } } else { std::cout << "Failed to initialize lights hardware." << std::endl; } return nullptr; } bool LedsHybris::usable() { light_device_t* lightDevice = getLightDevice(); const bool loaded = (lightDevice != nullptr); if (lightDevice) { hw_device_t* device = (hw_device_t*) lightDevice; device->close(device); } return loaded; } LedsHybris::LedsHybris() : Leds() { if (m_lightDevice) { return; } m_lightDevice = getLightDevice(); if (m_lightDevice) turnOff(); // Get up to date configure(); } LedsHybris::~LedsHybris() { if (m_lightDevice) { hw_device_t* device = (hw_device_t*) m_lightDevice; device->close(device); } } void LedsHybris::configure() { if (m_state == State::On) turnOn(); else turnOff(); } void LedsHybris::turnOn() { // pulse light_state_t state; memset(&state, 0, sizeof(light_state_t)); int alpha = (m_color >> 24) & 0xFF; int red = (m_color >> 16) & 0xFF; int green = (m_color >> 8) & 0xFF; int blue = m_color & 0xFF; alpha = 0xff; //TODO: Support alpha int newcolor = ((alpha & 0xff) << 24) + ((red & 0xff) << 16) + ((green & 0xff) << 8) + (blue & 0xff); state.color = newcolor; state.flashMode = LIGHT_FLASH_TIMED; state.flashOnMS = m_onMs; state.flashOffMS = m_offMs; state.brightnessMode = BRIGHTNESS_MODE_USER; if (!m_lightDevice) return; if (m_lightDevice->set_light(m_lightDevice, &state) != 0) { std::cout << "Failed to turn the light off"; } } void LedsHybris::turnOff() { light_state_t state; memset(&state, 0, sizeof(light_state_t)); state.color = 0x00000000; state.flashMode = LIGHT_FLASH_NONE; state.flashOnMS = 0; state.flashOffMS = 0; state.brightnessMode = 0; if (!m_lightDevice) return; if (m_lightDevice->set_light(m_lightDevice, &state) != 0) { std::cout << "Failed to turn the light off"; } } } hfd-service-0.2.2/src/leds-hybris.h000066400000000000000000000017611455450234400171120ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #include "leds.h" struct light_device_t; namespace hfd { class LedsHybris : public Leds { public: LedsHybris(); ~LedsHybris(); static bool usable(); protected: void configure() override; private: void turnOn(); void turnOff(); light_device_t* m_lightDevice = nullptr; }; } hfd-service-0.2.2/src/leds-sysfs.cpp000066400000000000000000000102001455450234400173000ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #include "leds-sysfs.h" #include "utils.h" #include #include #include static bool has_timer_trigger(Udev::UdevDevice const& dev) { std::string trigger_value; try { // get_sysattr() always gives the full list of available values. trigger_value = dev.get_sysattr("trigger"); } catch (std::runtime_error const&) { return false; } /* * Word "timer" maybe at the start or the end, in the middle surrounded by * spaces, or surrounded by '[' and ']'. */ static std::regex timer_re("(^|[[ ])timer([] ]|$)", std::regex_constants::extended); return std::regex_search(trigger_value, timer_re); } namespace hfd { bool LedsSysfs::usable() { if (access("/sys/class/leds", F_OK ) == -1) return false; Udev::Udev udev; Udev::UdevEnumerate enumerator = udev.enumerate_new(); enumerator.add_match_sysattr("subsystem","leds"); enumerator.scan_devices(); bool red, green, blue = false; for (auto dev : enumerator.enumerate_devices()) { auto splitted = utils::split(dev.get_sysname(), ':'); for (auto color : splitted) { std::cout << "got: " << color << std::endl; if (color == "red") red = has_timer_trigger(dev); if (color == "green") green = has_timer_trigger(dev); if (color == "blue") blue = has_timer_trigger(dev); } } return (red && green && blue); } LedsSysfs::LedsSysfs(): Leds() { Udev::Udev udev; Udev::UdevEnumerate enumerator = udev.enumerate_new(); enumerator.add_match_sysattr("subsystem","leds"); enumerator.scan_devices(); for (auto dev : enumerator.enumerate_devices()) { auto splitted = utils::split(dev.get_sysname(), ':'); for (auto color : splitted) { std::cout << "got: " << color << std::endl; if (color == "red") m_rgbDevices[Colors::RED] = dev; if (color == "green") m_rgbDevices[Colors::GREEN] = dev; if (color == "blue") m_rgbDevices[Colors::BLUE] = dev; } } for (auto dev : m_rgbDevices) { dev.second.set_sysattr("trigger", "timer"); } m_timer = true; // Get up to date configure(); } // Over simplyfied led controlls! void LedsSysfs::configure() { if (m_state == State::On) { std::cout << "red: " << std::to_string(rgba::Red(m_color)) << std::endl; std::cout << "green: " << std::to_string(rgba::Green(m_color)) << std::endl; std::cout << "blue: " << std::to_string(rgba::Blue(m_color)) << std::endl; setDelay(); setLed(Colors::RED, rgba::Red(m_color)); setLed(Colors::GREEN, rgba::Green(m_color)); setLed(Colors::BLUE, rgba::Blue(m_color)); } else { m_rgbDevices[Colors::RED].set_sysattr("brightness", "0"); m_rgbDevices[Colors::GREEN].set_sysattr("brightness", "0"); m_rgbDevices[Colors::BLUE].set_sysattr("brightness", "0"); } } void LedsSysfs::setDelay() { if (m_timer) { for (auto dev : m_rgbDevices) { dev.second.set_sysattr("trigger", "timer"); dev.second.set_sysattr("delay_on", std::to_string(m_onMs)); dev.second.set_sysattr("delay_off", std::to_string(m_offMs)); } } } void LedsSysfs::setLed(Colors color, int value) { m_rgbDevices[color].set_sysattr("brightness", std::to_string(value)); } } hfd-service-0.2.2/src/leds-sysfs.h000066400000000000000000000021561455450234400167600ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #pragma once #include "leds.h" #include #include "udev/udev-cpp.h" namespace hfd { class LedsSysfs : public Leds { public: LedsSysfs(); static bool usable(); protected: void configure() override; private: enum Colors { RED = 0, GREEN = 1, BLUE = 2 }; void setDelay(); void setLed(Colors color, int value); std::map m_rgbDevices; }; } hfd-service-0.2.2/src/leds.cpp000066400000000000000000000063541455450234400161520ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #include "leds.h" #include "leds-sysfs.h" #ifdef HAVE_LIBHYBRIS #include "leds-hybris.h" #endif #ifdef HAVE_LIBGBINDER #include "leds-binder.h" #endif #include namespace hfd { class LedsDummy : public Leds { public: LedsDummy() = default; protected: void configure() override { if (m_state == State::On) { std::cout << "LedsDummy on, color: " << m_color << std::endl; } else { std::cout << "LedsDummy off" << std::endl; } }; }; std::shared_ptr Leds::create() { #ifdef HAVE_LIBGBINDER if (LedsBinder::usable()) { std::cout << "Using binder leds" << std::endl; return std::make_shared(); } else #endif #ifdef HAVE_LIBHYBRIS if (LedsHybris::usable()) { std::cout << "Using hybris leds" << std::endl; return std::make_shared(); } else #endif if (LedsSysfs::usable()) { std::cout << "Using sysfs leds" << std::endl; return std::make_shared(); } std::cout << "Using dummy leds" << std::endl; return std::make_shared(); } std::shared_ptr Leds::create(std::string type) { if (type == "sysfs") { std::cout << "Using sysfs leds" << std::endl; return std::make_shared(); } #ifdef HAVE_LIBHYBRIS else if (type == "hybris") { std::cout << "Using hybris leds" << std::endl; return std::make_shared(); } #endif #ifdef HAVE_LIBGBINDER else if (type == "binder") { std::cout << "Using binder leds" << std::endl; return std::make_shared(); } #endif std::cout << "Using dummy leds" << std::endl; return std::make_shared(); } Leds::Leds() : m_color(0x00ff0080), m_state(State::Off), m_onMs(1000), m_offMs(3000) { } void Leds::setState(State newState) { m_state = newState; configure(); } State Leds::state() const { return m_state; } void Leds::setColor(const Rgba &color) { if (m_color != color) { m_color = color; if (m_state == State::On) configure(); } } Rgba Leds::color() const { return m_color; } int Leds::onMs() const { return m_onMs; } void Leds::setOnMs(int onMs) { if (m_onMs != onMs) { m_onMs = onMs; if (m_state == State::On) configure(); } } int Leds::offMs() const { return m_offMs; } void Leds::setOffMs(int offMs) { if (m_offMs != offMs) { m_offMs = offMs; if (m_state == State::On) configure(); } } } hfd-service-0.2.2/src/leds.h000066400000000000000000000025171455450234400156140ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #pragma once #include "common.h" #include "rgba.h" #include namespace hfd { class Leds { public: Leds(); void turnOn(const Rgba &color, int onMs, int offMs); void setState(State state); State state() const; void setColor(const Rgba &color); Rgba color() const; int onMs() const; void setOnMs(int onMs); int offMs() const; void setOffMs(int offMs); static std::shared_ptr create(); static std::shared_ptr create(std::string type); protected: virtual void configure() = 0; Rgba m_color; State m_state; int m_onMs; int m_offMs; bool m_timer = false; }; } hfd-service-0.2.2/src/properties.xml000066400000000000000000000014461455450234400174320ustar00rootroot00000000000000 hfd-service-0.2.2/src/rgba.h000066400000000000000000000017711455450234400156010ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #pragma once typedef unsigned int Rgba; namespace hfd { namespace rgba { inline int Red(Rgba rgb) { return ((rgb >> 16) & 0xff); } inline int Green(Rgba rgb) { return ((rgb >> 8) & 0xff); } inline int Blue(Rgba rgb) { return (rgb & 0xff); } inline int Alpha(Rgba rgb) { return rgb >> 24; } } } hfd-service-0.2.2/src/service.cpp000066400000000000000000000214551455450234400166620ustar00rootroot00000000000000/* * Copyright 2020-2022 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #include "credentialscache.h" #include "dbusAdaptor.h" #include "vibrator.h" #include "leds.h" #include "usersettings.h" #include "utils.h" #include "vibrator-controller.h" #include "hfdadaptor.h" #include #include #include #include #include #include class DbusAdaptorPrivilegedService : public DbusAdaptor, protected QDBusContext { Q_OBJECT public: DbusAdaptorPrivilegedService(std::shared_ptr vibratorCtl, std::shared_ptr leds) : DbusAdaptor() , m_vibratorCtl(vibratorCtl) , m_leds(leds) {} public Q_SLOTS: void vibrate() override { constexpr uint defaultVibrationMs = 500; m_vibratorCtl->vibrate(defaultVibrationMs); }; void vibrate(int durationMs) override { if (!verifyDBusInt("durationMs", durationMs, 1, 1000 * 60)) return; m_vibratorCtl->vibrate(durationMs); }; void rumble(int durationMs, int repeat) override { if (!verifyDBusInt("durationMs", durationMs, 1, 1000)) return; if (!verifyDBusInt("repeat", repeat, 1, 60)) return; m_vibratorCtl->rumble(durationMs, repeat); }; void vibratePattern(const QVector &patternMs, uint repeat) override { for (uint patOnOffMs : patternMs) { if (!verifyDBusInt("patternMs", patOnOffMs, 1, 1000)) return; } if (!verifyDBusInt("repeat", repeat, 1, 60)) return; m_vibratorCtl->vibratePattern(patternMs, repeat); } void setState(int state) override { m_leds->setState(hfd::utils::toState(state)); }; void setColor(unsigned int color) override { m_leds->setColor(color); } void setOnMs(int ms) override { if (!verifyDBusInt("onMs", ms)) return; m_leds->setOnMs(ms); }; void setOffMs(int ms) override { if (!verifyDBusInt("offMs", ms, 0)) return; m_leds->setOffMs(ms); }; protected: bool verifyDBusInt(const std::string &name, int val, int min = 1, int max = INT_MAX) { if (val >= min && val <= max) return true; this->sendErrorReply(QDBusError::InvalidArgs, QString::fromStdString(std::string(name) + ": parameter out of range")); return false; } private: std::shared_ptr m_vibratorCtl; std::shared_ptr m_leds; }; class DbusAdaptorService : public DbusAdaptor, protected QDBusContext { Q_OBJECT public: DbusAdaptorService( std::shared_ptr vibratorCtl, std::shared_ptr leds, QDBusConnection const& connection) : DbusAdaptor() , m_vibratorCtl(vibratorCtl) , m_leds(leds) , m_connection(connection) , m_credcache(connection) , m_usersettings(connection) {} public Q_SLOTS: void vibrate() override { callIfVibrateAllowed([this] () { constexpr uint defaultVibrationMs = 500; m_vibratorCtl->vibrate(defaultVibrationMs); }); }; void vibrate(int durationMs) override { if (!verifyDBusInt("durationMs", durationMs, 1, 1000 * 60)) return; callIfVibrateAllowed([=] () { m_vibratorCtl->vibrate(durationMs); }); }; void rumble(int durationMs, int repeat) override { if (!verifyDBusInt("durationMs", durationMs, 1, 1000)) return; if (!verifyDBusInt("repeat", repeat, 1, 60)) return; callIfVibrateAllowed([=] () { m_vibratorCtl->rumble(durationMs, repeat); }); }; void vibratePattern(const QVector &patternMs, uint repeat) override { for (uint patOnOffMs : patternMs) { if (!verifyDBusInt("patternMs", patOnOffMs, 1, 1000)) return; } if (!verifyDBusInt("repeat", repeat, 1, 60)) return; callIfVibrateAllowed([=] () { m_vibratorCtl->vibratePattern(patternMs, repeat); }); } // TODO: maybe disallow LEDs on the unprivileged object path. void setState(int state) override { m_leds->setState(hfd::utils::toState(state)); }; void setColor(unsigned int color) override { m_leds->setColor(color); } void setOnMs(int ms) override { if (!verifyDBusInt("onMs", ms)) return; m_leds->setOnMs(ms); }; void setOffMs(int ms) override { if (!verifyDBusInt("offMs", ms, 0)) return; m_leds->setOffMs(ms); }; protected: bool verifyDBusInt(const std::string &name, int val, int min = 1, int max = INT_MAX) { if (val >= min && val <= max) return true; this->sendErrorReply(QDBusError::InvalidArgs, QString::fromStdString(std::string(name) + ": parameter out of range")); return false; } private: void callIfVibrateAllowed(std::function callback) { using namespace std::placeholders; auto msg = message(); setDelayedReply(true); m_credcache.get(msg.service(), std::bind(&DbusAdaptorService::onCredentialReceived, this, callback, msg, _1)); } void onCredentialReceived( std::function callback, QDBusMessage msg, CredentialsCache::Credentials const& cred) { using namespace std::placeholders; if (!cred.valid) { // Fallback to previous behavior and allows vibration. // TODO: is this a sane thing to do? callback(); m_connection.send(msg.createReply()); return; } m_usersettings.getSettingsForUid(cred.user, std::bind(&DbusAdaptorService::onUserSettingsReceived, this, callback, msg, _1)); } void onUserSettingsReceived( std::function callback, QDBusMessage msg, bool allowed) { if (allowed) { callback(); m_connection.send(msg.createReply()); } else { m_connection.send(msg.createErrorReply( QStringLiteral("org.freedesktop.DBus.Error.AccessDenied"), QStringLiteral("User disallow general vibration") )); } } std::shared_ptr m_vibratorCtl; std::shared_ptr m_leds; QDBusConnection m_connection; CredentialsCache m_credcache; UserSettings m_usersettings; }; #include "service.moc" int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); // Environment variables to switch between implementations const char* vibrator_impl_env = getenv("HFD_VIBRATOR_IMPL"); const char* leds_impl_env = getenv("HFD_LEDS_IMPL"); std::cout << "Starting vibrator impl" << std::endl; std::shared_ptr vibrator; if (vibrator_impl_env) vibrator = hfd::Vibrator::create(vibrator_impl_env); else vibrator = hfd::Vibrator::create(); auto vibratorCtl = std::make_shared(vibrator); std::cout << "Starting leds impl" << std::endl; std::shared_ptr leds; if (leds_impl_env) leds = hfd::Leds::create(leds_impl_env); else leds = hfd::Leds::create(); std::cout << "done" << std::endl; QDBusConnection connection = QDBusConnection::systemBus(); auto dbusAdaptor = new DbusAdaptorService(vibratorCtl, leds, connection); new VibratorAdaptor(dbusAdaptor); new LedsAdaptor(dbusAdaptor); connection.registerObject("/com/lomiri/hfd", dbusAdaptor); auto dbusAdaptorPrivileged = new DbusAdaptorPrivilegedService(vibratorCtl, leds); new VibratorAdaptor(dbusAdaptorPrivileged); new LedsAdaptor(dbusAdaptorPrivileged); connection.registerObject("/com/lomiri/hfd/privileged", dbusAdaptorPrivileged); connection.registerService("com.lomiri.hfd"); if (connection.lastError().isValid()) { std::cout << "Not connected to DBus!!!" << std::endl; qWarning() << connection.lastError(); return 1; } std::cout << "Started dbus conn" << std::endl; return app.exec(); } hfd-service-0.2.2/src/udev/000077500000000000000000000000001455450234400154525ustar00rootroot00000000000000hfd-service-0.2.2/src/udev/CMakeLists.txt000066400000000000000000000002431455450234400202110ustar00rootroot00000000000000 add_library(udev-cpp STATIC udev.cpp udevDevice.cpp udevEnumerate.cpp udevMonitor.cpp ) target_link_libraries(udev-cpp ${UDEV_LIBRARIES} ) hfd-service-0.2.2/src/udev/udev-cpp.h000066400000000000000000000114761455450234400173570ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #pragma once #include #include #include #include #include namespace Udev { using UdevHandle = struct udev; using UdevMonitorHandle = struct udev_monitor; using UdevDeviceHandle = struct udev_device; using UdevEnumerateHandle = struct udev_enumerate; class UdevMonitor; class UdevDevice; class UdevEnumerate; /** * Class representing a udev context */ class Udev { public: /** * Acquire new udev context */ explicit Udev(); Udev(const Udev& other); Udev(Udev&& other); Udev& operator=(const Udev& other); Udev& operator=(Udev&& other); ~Udev(); /** * Create new udev monitor for netlink described by @name. * @param name Name can be "udev" or "kernel" (default is "udev") * @return A {@link UdevMonitor} instance */ UdevMonitor monitor_new_from_netlink(const char *name = "udev"); UdevDevice device_from_syspath(std::string) const; /** * Create new udev enumerator * @return A {@link UdevEnumerator} instance which can be used to enumerate devices known to udev */ UdevEnumerate enumerate_new(); private: UdevHandle *handle; }; /** * Class that encapsulates monitoring functionality provided by Udev */ class UdevMonitor { public: UdevMonitor(UdevMonitorHandle *monitor); UdevMonitor(const UdevMonitor &other); UdevMonitor(UdevMonitor &&other); UdevMonitor& operator=(const UdevMonitor &monitor); UdevMonitor& operator=(UdevMonitor &&monitor); ~UdevMonitor(); void enable_receiving() const; int get_fd() const; UdevDevice receive_device() const; private: UdevMonitorHandle *handle; }; /** * Class that encapsulated enumeration functionality provided by Udev */ class UdevEnumerate { public: UdevEnumerate(UdevEnumerateHandle *enumerate); UdevEnumerate(const UdevEnumerate &other); UdevEnumerate(UdevEnumerate &&other); UdevEnumerate &operator=(const UdevEnumerate &other); UdevEnumerate &operator=(UdevEnumerate &&other); ~UdevEnumerate(); void add_match_subsystem(const std::string subsystem) const; void add_nomatch_subsystem(const std::string subsystem) const; void add_match_sysattr(const std::string sysattr, const std::string value = "") const; void add_nomatch_sysattr(const std::string sysattr, const std::string value = "") const; void add_match_property(const std::string property, const std::string value) const; void add_match_tag(const std::string tag) const; void add_match_is_initialized() const; void scan_devices() const; void scan_subsystems() const; std::vector enumerate_devices() const; private: UdevEnumerateHandle *handle; }; /** * Class that encapsulates the concept of a device as described by Udev */ class UdevDevice { public: UdevDevice(UdevDeviceHandle *device); UdevDevice(const UdevDevice &); UdevDevice(UdevDevice &&); UdevDevice() = default; UdevDevice& operator=(const UdevDevice &); UdevDevice& operator=(UdevDevice &&); ~UdevDevice(); bool is_initialized() const; bool has_action() const; std::string get_action() const; bool has_devnode() const; std::string get_devnode() const; bool has_devtype() const; std::string get_devtype() const; bool has_subsystem() const; std::string get_subsystem() const; std::string get_devpath() const; std::string get_syspath() const; std::string get_sysname() const; bool has_sysnum() const; std::string get_sysnum() const; bool has_driver() const; std::string get_driver() const; bool has_sysattr(const std::string named) const; std::string get_sysattr(const std::string named) const; void set_sysattr(const std::string named, const std::string value) const; std::vector get_sysattr_keys() const; std::map get_sysattr_map() const; std::vector get_devlinks() const; bool has_property(const std::string named) const; std::string get_property(const std::string named) const; std::map get_properties() const; bool has_tag(const std::string named) const; std::vector get_tags() const; private: UdevDeviceHandle *handle; }; } hfd-service-0.2.2/src/udev/udev.cpp000066400000000000000000000023561455450234400171270ustar00rootroot00000000000000#include #include #include #include "udev-cpp.h" namespace Udev { Udev::Udev() : handle(udev_new()) { } Udev::Udev(const Udev &other) : handle(udev_ref(other.handle)) { } Udev::Udev(Udev &&other) : handle(other.handle) { other.handle = nullptr; } Udev& Udev::operator=(const Udev &other) { this->handle = udev_ref(other.handle); return *this; } Udev& Udev::operator=(Udev &&other) { this->handle = other.handle; other.handle = nullptr; return *this; } Udev::~Udev() { if (handle) { udev_unref(handle); } } UdevMonitor Udev::monitor_new_from_netlink(const char *name) { assert(name != nullptr && (std::string(name) == "udev" || std::string(name) == "kernel")); return UdevMonitor(udev_monitor_new_from_netlink(handle, name)); } UdevDevice Udev::device_from_syspath(std::string syspath) const { UdevDeviceHandle *devicep = udev_device_new_from_syspath(handle, syspath.c_str()); if (devicep == NULL) { throw std::runtime_error((std::string("Error while creating UdevDevice from syspath, error is: ") + std::strerror(errno)).c_str()); } return UdevDevice(devicep); } UdevEnumerate Udev::enumerate_new() { return UdevEnumerate(udev_enumerate_new(handle)); } } hfd-service-0.2.2/src/udev/udevDevice.cpp000066400000000000000000000125541455450234400202500ustar00rootroot00000000000000#include #include #include #include "udev-cpp.h" namespace Udev { UdevDevice::UdevDevice(UdevDeviceHandle *device) : handle(device) { } UdevDevice::UdevDevice(const UdevDevice &other) : handle(udev_device_ref(other.handle)) { } UdevDevice::UdevDevice(UdevDevice &&other) : handle(other.handle) { other.handle = nullptr; } UdevDevice& UdevDevice::operator=(const UdevDevice &other) { handle = udev_device_ref(other.handle); return *this; } UdevDevice& UdevDevice::operator=(UdevDevice &&other) { handle = udev_device_ref(other.handle); other.handle = nullptr; return *this; } UdevDevice::~UdevDevice() { if (handle) { udev_device_unref(handle); } } bool UdevDevice::is_initialized() const { return udev_device_get_is_initialized(handle) == 1; } bool UdevDevice::has_action() const { return udev_device_get_action(handle) != nullptr; } std::string UdevDevice::get_action() const { return std::string(udev_device_get_action(handle)); } bool UdevDevice::has_devnode() const { return udev_device_get_devnode(handle) != nullptr; } std::string UdevDevice::get_devnode() const { return udev_device_get_devnode(handle); } bool UdevDevice::has_devtype() const { return udev_device_get_devtype(handle) != nullptr; } std::string UdevDevice::get_devtype() const { return udev_device_get_devtype(handle); } bool UdevDevice::has_subsystem() const { return udev_device_get_subsystem(handle) != nullptr; } std::string UdevDevice::get_subsystem() const { return udev_device_get_subsystem(handle); } std::string UdevDevice::get_devpath() const { return udev_device_get_devpath(handle); } std::string UdevDevice::get_syspath() const { return udev_device_get_syspath(handle); } std::string UdevDevice::get_sysname() const { return udev_device_get_sysname(handle); } bool UdevDevice::has_sysnum() const { return udev_device_get_sysnum(handle) != nullptr; } std::string UdevDevice::get_sysnum() const { return udev_device_get_sysnum(handle); } bool UdevDevice::has_driver() const { return udev_device_get_driver(handle) != nullptr; } std::string UdevDevice::get_driver() const { return udev_device_get_driver(handle); } bool UdevDevice::has_sysattr(const std::string named) const { auto keys = get_sysattr_keys(); return std::find(keys.begin(), keys.end(), named) != keys.end(); } std::string UdevDevice::get_sysattr(const std::string named) const { const char *value = udev_device_get_sysattr_value(handle, named.c_str()); if (value == nullptr) { throw std::runtime_error("systemattr does not exist or could not be retrieved"); } return std::string(value); } void UdevDevice::set_sysattr(const std::string named, const std::string value) const { int ret = udev_device_set_sysattr_value(handle, named.c_str(), (char*)value.c_str()); if (ret < 0) { throw std::runtime_error("systemattr does not exist or could not be set to requested value (check permissions)"); } return; } std::vector UdevDevice::get_sysattr_keys() const { std::vector keys; auto sysattr_list = udev_device_get_sysattr_list_entry(handle); struct udev_list_entry *entry = nullptr; udev_list_entry_foreach(entry, sysattr_list) { keys.emplace_back(udev_list_entry_get_name(entry)); } return keys; } std::map UdevDevice::get_sysattr_map() const { std::map attr; auto sysattr_list = udev_device_get_sysattr_list_entry(handle); struct udev_list_entry *entry = nullptr; udev_list_entry_foreach(entry, sysattr_list) { const char *key = udev_list_entry_get_name(entry); const char *value = udev_device_get_sysattr_value(handle, key); if (entry != nullptr) { if (key != nullptr && value != nullptr) { attr[std::string(udev_list_entry_get_name(entry))] = std::string(value); } } } return attr; } std::vector UdevDevice::get_devlinks() const { std::vector links; struct udev_list_entry *entry = nullptr; udev_list_entry_foreach(entry, udev_device_get_devlinks_list_entry(handle)) { links.emplace_back(udev_list_entry_get_name(entry)); } return links; } bool UdevDevice::has_property(const std::string named) const { return udev_device_get_property_value(handle, named.c_str()) != nullptr; } std::string UdevDevice::get_property(const std::string named) const { return udev_device_get_property_value(handle, named.c_str()); } std::map UdevDevice::get_properties() const { std::map property_map; struct udev_list_entry *entry = nullptr; struct udev_list_entry *properties = udev_device_get_properties_list_entry(handle); udev_list_entry_foreach(entry, properties) { property_map[std::string(udev_list_entry_get_name(entry))] = std::string(udev_list_entry_get_value(entry)); } return property_map; } bool UdevDevice::has_tag(const std::string named) const { return udev_device_has_tag(handle, named.c_str()); } std::vector UdevDevice::get_tags() const { std::vector tags; struct udev_list_entry *entry; struct udev_list_entry *tags_list = udev_device_get_tags_list_entry(handle); udev_list_entry_foreach(entry, tags_list) { tags.emplace_back(udev_list_entry_get_name(entry)); } return tags; } } hfd-service-0.2.2/src/udev/udevEnumerate.cpp000066400000000000000000000047721455450234400210010ustar00rootroot00000000000000#include #include "udev-cpp.h" #include namespace Udev { UdevEnumerate::UdevEnumerate(UdevEnumerateHandle *enumerate) : handle(enumerate) { } UdevEnumerate::UdevEnumerate(const UdevEnumerate &other) : handle(udev_enumerate_ref(other.handle)) { } UdevEnumerate::UdevEnumerate(UdevEnumerate &&other) : handle(other.handle) { other.handle = nullptr; } UdevEnumerate &UdevEnumerate::operator=(const UdevEnumerate &other) { udev_enumerate_ref(other.handle); return *this; } UdevEnumerate &UdevEnumerate::operator=(UdevEnumerate &&other) { handle = other.handle; other.handle = nullptr; return *this; } UdevEnumerate::~UdevEnumerate() { if (handle) { udev_enumerate_unref(handle); } } void UdevEnumerate::add_match_subsystem(const std::string subsystem) const { udev_enumerate_add_match_subsystem(handle, subsystem.c_str()); } void UdevEnumerate::add_nomatch_subsystem(const std::string subsystem) const { udev_enumerate_add_nomatch_subsystem(handle, subsystem.c_str()); } void UdevEnumerate::add_match_sysattr(const std::string sysattr, const std::string value) const { udev_enumerate_add_match_sysattr(handle, sysattr.c_str(), value.length() > 0 ? value.c_str() : nullptr); } void UdevEnumerate::add_nomatch_sysattr(const std::string sysattr, const std::string value) const { udev_enumerate_add_nomatch_sysattr(handle, sysattr.c_str(), value.length() > 0 ? value.c_str() : nullptr); } void UdevEnumerate::add_match_property(const std::string property, const std::string value) const { udev_enumerate_add_match_property(handle, property.c_str(), value.c_str()); } void UdevEnumerate::add_match_tag(const std::string tag) const { udev_enumerate_add_match_tag(handle, tag.c_str()); } void UdevEnumerate::add_match_is_initialized() const { udev_enumerate_add_match_is_initialized(handle); } void UdevEnumerate::scan_devices() const { udev_enumerate_scan_devices(handle); } void UdevEnumerate::scan_subsystems() const { udev_enumerate_scan_subsystems(handle); } std::vector UdevEnumerate::enumerate_devices() const { std::vector devices; struct udev_list_entry *entry = nullptr; struct udev_list_entry *enumeration_list = udev_enumerate_get_list_entry(handle); udev_list_entry_foreach(entry, enumeration_list) { const char *device_path = udev_list_entry_get_name(entry); devices.emplace_back(udev_device_new_from_syspath(udev_enumerate_get_udev(handle), device_path)); } return devices; } } hfd-service-0.2.2/src/udev/udevMonitor.cpp000066400000000000000000000022641455450234400204750ustar00rootroot00000000000000#include #include #include "udev-cpp.h" namespace Udev { UdevMonitor::UdevMonitor(UdevMonitorHandle *monitor) : handle(monitor) { } UdevMonitor::UdevMonitor(const UdevMonitor& other) : handle(udev_monitor_ref(other.handle)) { } UdevMonitor::UdevMonitor(UdevMonitor&& other) : handle(other.handle) { other.handle = nullptr; } UdevMonitor& UdevMonitor::operator=(const UdevMonitor& other) { handle = udev_monitor_ref(other.handle); return *this; } UdevMonitor& UdevMonitor::operator=(UdevMonitor&& other) { handle = other.handle; other.handle = nullptr; return *this; } UdevMonitor::~UdevMonitor() { if (handle) { udev_monitor_unref(handle); } } void UdevMonitor::enable_receiving() const { if (udev_monitor_enable_receiving(handle) != 0) { throw std::runtime_error("Unable to enable receiving of events"); } } int UdevMonitor::get_fd() const { return udev_monitor_get_fd(handle); } UdevDevice UdevMonitor::receive_device() const { auto device = udev_monitor_receive_device(handle); if (device == nullptr) { throw std::runtime_error("Device could not be received"); } return UdevDevice(device); } } hfd-service-0.2.2/src/usersettings.cpp000066400000000000000000000204371455450234400177600ustar00rootroot00000000000000/* * Copyright 2023 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Ratchanan Srirattanamet */ #include "usersettings.h" #include #include const auto AS_SERVICE = QStringLiteral("org.freedesktop.Accounts"); const auto AS_PATH = QStringLiteral("/org/freedesktop/Accounts"); const auto ASSETTINGS_INTERFACE = QStringLiteral("com.lomiri.hfd.AccountsService.Settings"); const auto ASSETTINGS_ALLOWGENERALVIBRATION = QStringLiteral("AllowGeneralVibration"); UserSettings::UserSettings(QDBusConnection bus, QObject *parent) : QObject(parent) , m_bus(bus) , m_accountsService(new ASInterface( AS_SERVICE, AS_PATH, bus, /* parent */ this)) { // Allow testing asychronous initial value case. if (qEnvironmentVariableIsSet("HFD_USERSETTINGS_SKIP_CACHED_USERS")) return; auto listCachedUsersAsync = m_accountsService->ListCachedUsers(); auto watcher = new QDBusPendingCallWatcher(listCachedUsersAsync, this); QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, &UserSettings::onListCachedUsersFinished); QObject::connect(m_accountsService, &ASInterface::UserDeleted, this, &UserSettings::onUserDeleted); } void UserSettings::onListCachedUsersFinished(QDBusPendingCallWatcher * call) { QDBusPendingReply> reply = *call; if (reply.isError()) { qWarning() << "AccountsService fails to list cached users." << reply.error(); return; } for (auto const & userObjPath : reply.argumentAt<0>()) { auto userPath = userObjPath.path(); // The path looks like: `/org/freedesktop/Accounts/User1000` bool ok; uid_t uid = userPath.midRef(userPath.lastIndexOf("User") + 4).toInt(&ok, /* base */ 10); if (!ok) { // Normally we would have to retrieve property org.freedesktop.Accounts.User.Uid, // but I think it would be rare... qWarning() << "Unable to parse the object path" << userPath; continue; } // In case a request arrives before we're called. if (m_infoForUid.contains(uid)) { return; } initUser(uid, userPath); } call->deleteLater(); } UserSettings::UserInfo & UserSettings::initUser(uid_t uid, QString const & userObjPath) { using namespace std::placeholders; auto & user = m_infoForUid[uid] = UserInfo { .properties = nullptr, .allowed = false, .valid = false, .pendingRequests = {}, }; if (!userObjPath.isNull()) { userSetObjPath(user, userObjPath); } else { // The purpose of FindUserById isn't to just get the path (since the // format is static), but to ensure that path exists and is exported // on the bus. auto findUserByIdAsync = m_accountsService->FindUserById(uid); auto watcher = new QDBusPendingCallWatcher(findUserByIdAsync, this); QObject::connect( watcher, &QDBusPendingCallWatcher::finished, this, [this, uid, &user] (QDBusPendingCallWatcher * call) { QDBusPendingReply reply = *call; if (reply.isError()) { qWarning() << "AccountsService fails to create object for " "user " << uid << ", defaults to deny."; userAllowedChanged(user, false); } else { userSetObjPath(user, reply.argumentAt<0>().path()); } call->deleteLater(); }); } return user; } void UserSettings::userSetObjPath(UserSettings::UserInfo & user, QString const & userPath) { using namespace std::placeholders; user.properties = new PropertiesInterface(AS_SERVICE, userPath, m_bus, this); // Connect to change signal first, to avoid race condition. QObject::connect( user.properties, &PropertiesInterface::PropertiesChanged, this, std::bind(&UserSettings::onPropertiesChanged, this, std::ref(user), _1, _2, _3)); userGetAllowed(user); } void UserSettings::userGetAllowed(UserSettings::UserInfo & user) { using namespace std::placeholders; auto getAsync = user.properties->Get( ASSETTINGS_INTERFACE, ASSETTINGS_ALLOWGENERALVIBRATION); auto watcher = new QDBusPendingCallWatcher(getAsync, this); QObject::connect( watcher, &QDBusPendingCallWatcher::finished, this, std::bind(&UserSettings::onGetFinished, this, std::ref(user), _1) ); } void UserSettings::onGetFinished(UserInfo & user, QDBusPendingCallWatcher * call) { QDBusPendingReply reply = *call; bool allowed; if (reply.isError()) { qWarning() << "AccountsService fails to provide settings for " << user.properties->path() << ", defaults to not allowed." << reply.error(); allowed = false; } else { auto variant = reply.argumentAt<0>().variant(); if (variant.type() != QVariant::Bool) { qWarning() << "AccountsService provides invalid value for " << user.properties->path() << ", defaults to not allowed." << reply.error(); allowed = false; } else { allowed = variant.value(); } } userAllowedChanged(user, allowed); call->deleteLater(); } void UserSettings::onPropertiesChanged( UserInfo & user, const QString &interface_name, const QVariantMap &changed_properties, const QStringList &invalidated_properties) { if (interface_name != ASSETTINGS_INTERFACE) { // We don't care about this. return; } if (invalidated_properties.contains(ASSETTINGS_ALLOWGENERALVIBRATION)) { // Unfortunately we have to retrieve value asynchronously from AS again. userGetAllowed(user); } else if (changed_properties.contains(ASSETTINGS_ALLOWGENERALVIBRATION)) { auto variant = changed_properties[ASSETTINGS_ALLOWGENERALVIBRATION]; if (variant.type() != QVariant::Bool) { qWarning() << "AccountsService provides invalid value for " << user.properties->path() << " during update, ignore."; return; } userAllowedChanged(user, variant.value()); } } void UserSettings::userAllowedChanged(UserSettings::UserInfo & user, bool allowed) { user.allowed = allowed; if (!user.valid) { user.valid = true; for (auto const & callback : qAsConst(user.pendingRequests)) callback(user.allowed); user.pendingRequests = {}; } } void UserSettings::onUserDeleted(const QDBusObjectPath & userObjPath) { auto userPath = userObjPath.path(); // The path looks like: `/org/freedesktop/Accounts/User1000` bool ok; uid_t uid = userPath.midRef(userPath.lastIndexOf("User") + 4).toInt(&ok, /* base */ 10); if (!ok) { qWarning() << "Unable to parse the object path" << userPath; return; } if (m_infoForUid.contains(uid)) { auto & user = m_infoForUid[uid]; // In case any pending requests exists at all. for (auto const & callback : qAsConst(user.pendingRequests)) callback(false); if (user.properties) delete user.properties; m_infoForUid.remove(uid); } } void UserSettings::getSettingsForUid(uid_t uid, callback_t callback) { UserInfo & user = m_infoForUid.contains(uid) ? m_infoForUid[uid] : initUser(uid, /* path */ QString()); if (user.valid) { callback(user.allowed); } else { user.pendingRequests.append(callback); } } hfd-service-0.2.2/src/usersettings.h000066400000000000000000000037071455450234400174260ustar00rootroot00000000000000/* * Copyright 2023 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Ratchanan Srirattanamet */ #include #include #include #include #include #include "asinterface.h" #include "propertiesinterface.h" class UserSettings : public QObject { Q_OBJECT public: typedef std::function callback_t; UserSettings(QDBusConnection bus, QObject * parent = nullptr); void getSettingsForUid(uid_t uid, callback_t cb); private: struct UserInfo { PropertiesInterface * properties; bool allowed; bool valid; QList pendingRequests; }; void onListCachedUsersFinished(QDBusPendingCallWatcher * call); UserInfo & initUser(uid_t uid, QString const & userPath); void userSetObjPath(UserInfo & user, QString const & userPath); void userGetAllowed(UserInfo & user); void onGetFinished(UserInfo & user, QDBusPendingCallWatcher * call); void onPropertiesChanged(UserInfo & user, const QString &interface_name, const QVariantMap &changed_properties, const QStringList &invalidated_properties); void userAllowedChanged(UserInfo & user, bool allowed); void onUserDeleted(const QDBusObjectPath & userObjPath); QMap m_infoForUid; QDBusConnection m_bus; ASInterface * m_accountsService; }; hfd-service-0.2.2/src/utils.h000066400000000000000000000032331455450234400160210ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #pragma once #include "leds.h" #include #include #include #include #include namespace hfd { namespace utils { inline std::vector split(std::string strToSplit, char delimeter) { std::stringstream ss(strToSplit); std::string item; std::vector splittedStrings; while (std::getline(ss, item, delimeter)) { splittedStrings.push_back(item); } return splittedStrings; }; inline State toState(int state) { if (state == 1) return State::On; return State::Off; } class FileDescGuard { public: FileDescGuard(int fd) : m_fd(fd) {} ~FileDescGuard() { if (close(m_fd) != 0) { std::cerr << "Failed to close file descriptor " << m_fd << ": errno = " << errno << std::endl; } } private: int m_fd = -1; }; } } hfd-service-0.2.2/src/vibrator-binder.cpp000066400000000000000000000064601455450234400203120ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard * Author: Erfan Abdi */ #include "vibrator-binder.h" #include #include #define BINDER_VIBRATOR_SERVICE_DEVICE "/dev/hwbinder" #define BINDER_VIBRATOR_SERVICE_IFACE "android.hardware.vibrator@1.0::IVibrator" #define BINDER_VIBRATOR_SERVICE_FQNAME BINDER_VIBRATOR_SERVICE_IFACE "/default" namespace hfd { bool VibratorBinder::usable() { GBinderServiceManager *sm; GBinderRemoteObject *remote; GBinderClient *client; sm = gbinder_servicemanager_new(BINDER_VIBRATOR_SERVICE_DEVICE); if (!sm) return false; remote = gbinder_servicemanager_get_service_sync(sm, BINDER_VIBRATOR_SERVICE_FQNAME, NULL); if (!remote) { gbinder_servicemanager_unref(sm); return false; } client = gbinder_client_new(remote, BINDER_VIBRATOR_SERVICE_IFACE); if (!client) { gbinder_remote_object_unref(remote); gbinder_servicemanager_unref(sm); return false; } gbinder_remote_object_unref(remote); gbinder_servicemanager_unref(sm); return true; } VibratorBinder::VibratorBinder(): Vibrator() { mSm = gbinder_servicemanager_new(BINDER_VIBRATOR_SERVICE_DEVICE); if (!mSm) return; mRemote = gbinder_servicemanager_get_service_sync(mSm, BINDER_VIBRATOR_SERVICE_FQNAME, NULL); if (!mRemote) { gbinder_servicemanager_unref(mSm); return; } mClient = gbinder_client_new(mRemote, BINDER_VIBRATOR_SERVICE_IFACE); if (!mClient) { gbinder_remote_object_unref(mRemote); gbinder_servicemanager_unref(mSm); return; } // Make sure we are off on init configure(State::Off, 0); } VibratorBinder::~VibratorBinder() { gbinder_remote_object_unref(mRemote); gbinder_servicemanager_unref(mSm); if (m_thread) { m_thread->join(); } } void VibratorBinder::configure(State state, int durationMs) { int status; GBinderRemoteReply *reply; GBinderLocalRequest *req = gbinder_client_new_request(mClient); if (state == State::On) { gbinder_local_request_append_int32(req, durationMs); reply = gbinder_client_transact_sync_reply(mClient, 1 /* on */, req, &status); } else { reply = gbinder_client_transact_sync_reply(mClient, 2 /* off */, req, &status); } gbinder_local_request_unref(req); if (status == GBINDER_STATUS_OK) { GBinderReader reader; guint value; gbinder_remote_reply_init_reader(reply, &reader); status = gbinder_reader_read_uint32(&reader, &value); } } } hfd-service-0.2.2/src/vibrator-binder.h000066400000000000000000000022671455450234400177600ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard * Author: Erfan Abdi */ #pragma once #include "vibrator.h" #include #include #include namespace hfd { class VibratorBinder : public Vibrator { public: VibratorBinder(); ~VibratorBinder(); static bool usable(); protected: void configure(State state, int durationMs) override; private: GBinderServiceManager *mSm; GBinderRemoteObject *mRemote; GBinderClient *mClient; std::shared_ptr m_thread; }; } hfd-service-0.2.2/src/vibrator-controller.cpp000066400000000000000000000074751455450234400212410ustar00rootroot00000000000000/* * Copyright 2023 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Ratchanan Srirattanamet */ #include #include #include namespace hfd { VibratorController::VibratorController(const std::shared_ptr &vibrator, QObject * parent) : m_vibrator(vibrator) { DeviceInfo devInfo; m_vibrateDurationExtraMs = std::stoul(devInfo.get("VibrateDurationExtraMs", "0")); m_timer.setSingleShot(true); // Not the best for power consumption, but to make sure the pattern doesn't skew. m_timer.setTimerType(Qt::PreciseTimer); QObject::connect(&m_timer, &QTimer::timeout, this, &VibratorController::continuePattern); } void VibratorController::vibratePattern(const QVector &pattern, quint32 repeat) { m_currentPattern = pattern; m_currentPatternOffset = 0; m_remainingRepeat = repeat; // Use timer, instead of calling continuePattern() directly, to avoid // blocking DBus call in case vibrator implementation blocks. // This will also cancel an ongoing pattern, if any, in favor of this one. m_firstRun = true; m_timer.start(0 /* ms */); } /* * Here, we're assuming that VibrateDurationExtraMs is the time it takes for the * vibrator to spin up and spin down. So, the idea is we split the 'off' part of * the pattern into the spin down, rest and the spin up (rest can be 0 if not * enough 'off' time is left in the pattern). * * We sleep through the first and second part, then wake up and command * vibration for spin up + (next) pattern 'on' time. This results in perceptible * vibration for a short pattern without making it actually longer. */ void VibratorController::continuePattern() { /* Get previous off ms before advancing repeat. */ uint prevPatOffMs; if (m_firstRun) { prevPatOffMs = UINT_MAX; m_firstRun = false; } else if (m_currentPatternOffset % 2 != 0) { // Can happens for odd-numbered pattern length. prevPatOffMs = 0; } else { prevPatOffMs = m_currentPattern[m_currentPatternOffset - 1]; } if (m_currentPatternOffset == m_currentPattern.size() && m_remainingRepeat != 0) { m_remainingRepeat -= 1; m_currentPatternOffset = 0; } if (m_remainingRepeat == 0) { m_currentPattern = QVector(); m_currentPatternOffset = 0; return; } uint patOnMs = m_currentPattern[m_currentPatternOffset]; m_currentPatternOffset++; uint patOffMs = 0; if (m_currentPatternOffset != m_currentPattern.size()) { patOffMs = m_currentPattern[m_currentPatternOffset]; m_currentPatternOffset++; } uint spinUpMs = prevPatOffMs / 2 > m_vibrateDurationExtraMs ? m_vibrateDurationExtraMs : prevPatOffMs / 2; uint spinDownAndRestMs = patOffMs / 2 > m_vibrateDurationExtraMs ? patOffMs - m_vibrateDurationExtraMs : (patOffMs + 1) / 2; // +1 makes the equation rounds the number up instead of down. m_timer.start(spinUpMs + patOnMs + spinDownAndRestMs); // Call vibrator after starting timer. Some vibrator implementation blocks // for the vibration duration. m_vibrator->vibrate(spinUpMs + patOnMs); } } // namespace hfd hfd-service-0.2.2/src/vibrator-controller.h000066400000000000000000000032301455450234400206670ustar00rootroot00000000000000/* * Copyright 2023 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Ratchanan Srirattanamet */ #include #include #include #include #include namespace hfd { class Vibrator; class VibratorController : public QObject { Q_OBJECT public: VibratorController(const std::shared_ptr &vibrator, QObject * parent = nullptr); void vibratePattern(const QVector &pattern, quint32 repeat); inline void vibrate(uint durationMs) { vibratePattern({ durationMs }, /* repeat */ 1); } inline void rumble(uint durationMs, uint repeat) { vibratePattern({ durationMs, durationMs }, repeat); } private: void continuePattern(); std::shared_ptr m_vibrator; uint m_vibrateDurationExtraMs; QTimer m_timer; /* In the format of [onMs, offMs, ...] */ QVector m_currentPattern; uint m_currentPatternOffset = 0; /* Includes the current iteration. */ uint m_remainingRepeat = 0; bool m_firstRun = false; }; } // namespace hfd hfd-service-0.2.2/src/vibrator-ff.cpp000066400000000000000000000113501455450234400174340ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Caleb Connolly */ #include "vibrator-ff.h" #include "utils.h" #include #include #include #include #include #include #include #include #include /* * This vibrator supports devices using the Kernel Force Feedback API. * Mostly Mainline devices. */ namespace hfd { // For this implementation call the static getFirstFFDevice // And check that it didn't return the empty string. bool VibratorFF::usable() { return VibratorFF::getFirstFFDevice().size() > 0; } // This takes a path to a device like '/dev/input/eventX' // And queries the device to see if it supports FF_RUMBLE bool inputDeviceSupportsFF(std::string devname) { unsigned char features[1 + FF_MAX/8/sizeof(unsigned char)] = {0}; int tempFd = open(devname.c_str(), O_RDWR|O_CLOEXEC); hfd::utils::FileDescGuard tempFdGuard(tempFd); int request = EVIOCGBIT(EV_FF, sizeof(features)*sizeof(unsigned char)); if (ioctl(tempFd, request, &features) < 0) { std::cerr << __FUNCTION__ << ": ioctl() failed with errno = " << errno << std::endl; return false; } if (testBit(FF_RUMBLE, features)) { return true; } return false; } // Create play and/or stop input events to control // the rumble effect we uploaded in the constructor. void VibratorFF::configure(State state, int durationMs) { struct input_event play; struct input_event stop; if (!isOpen()) return; if (state == State::On) { std::cout << "rumbling with magnitude: " << effect.u.rumble.strong_magnitude << " for " << durationMs << "ms" << std::endl; play.type = EV_FF; play.code = effect.id; play.value = 1; if (write(fd, (const void*) &play, sizeof(play)) != sizeof(play)) { std::cerr << "failed to fully write play command to input device fd: errno = " << errno << std::endl; return; } usleep(durationMs * 1000); } stop.type = EV_FF; stop.code = effect.id; stop.value = 0; if (write(fd, (const void*) &stop, sizeof(stop)) != sizeof(stop)) { std::cerr << "failed to fully write stop command to input device fd: errno = " << errno << std::endl; } } // This finds the first device that supports force feedback // and assumes that it supports rumble, which it may not. // We should also query the device feature flags and be SURE std::string VibratorFF::getFirstFFDevice() { Udev::Udev udev; Udev::UdevEnumerate enumerate = udev.enumerate_new(); enumerate.add_match_subsystem("input"); enumerate.scan_devices(); std::vector devices = enumerate.enumerate_devices(); std::cout << "FF: Found " << devices.size() << " input devices" << std::endl; for(size_t i = 0; i < devices.size(); i++) { const auto properties = devices.at(i).get_properties(); if (properties.find("DEVNAME") != properties.end()) { std::string temp = devices.at(i).get_properties().at("DEVNAME"); if (inputDeviceSupportsFF(temp)) { std::cout << "Using " << temp << std::endl; return temp; } } } return ""; } // Create a force feedback vibrator VibratorFF::VibratorFF(): Vibrator() { int ret; // Find the first available input device that supports // force feedback, we expect this to succeed as the caller // should have called the static 'usable' method first. devname = VibratorFF::getFirstFFDevice(); std::memset(&effect, 0, sizeof(effect)); // Create a rumble effect effect.type = FF_RUMBLE; effect.id = -1; effect.u.rumble.strong_magnitude = 0x6000; // This should be adjustable effect.u.rumble.weak_magnitude = 0; fd = open(devname.c_str(), O_RDWR|O_CLOEXEC); if (fd < 0) { std::cerr << "Can't open force feedback device path: " << devname << std::endl; return; } // Upload the effect to the device, this doesn't cause // it to vibrate, just to store the effect in kernel memory. ret = ioctl(fd, EVIOCSFF, &effect); if (ret < 0) { std::cerr << "Failed to upload rumble effect" << std::endl; return; } } VibratorFF::~VibratorFF() { int ret; if (isOpen()) { // Unload the effect ret = ioctl(fd, EVIOCRMFF, effect.id); if (ret < 0) std::cerr << "Failed to unload effect " << effect.id << std::endl; close(fd); } } } hfd-service-0.2.2/src/vibrator-ff.h000066400000000000000000000032711455450234400171040ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Caleb Connolly */ #pragma once #include #include "vibrator.h" #include "udev/udev-cpp.h" // Borrowed from linuxconsole/utils/bitmaskros.h /* Number of bits for 1 unsigned char */ #define nBitsPerUchar (sizeof(unsigned char) * 8) /* Index=Offset of given bit in 1 unsigned char */ #define bitOffsetInUchar(bit) ((bit)%nBitsPerUchar) /* Index=Offset of the unsigned char associated to the bit * at the given index=offset */ #define ucharIndexForBit(bit) ((bit)/nBitsPerUchar) /* Test the bit with given index=offset in an unsigned char array */ #define testBit(bit, array) ((array[ucharIndexForBit(bit)] >> bitOffsetInUchar(bit)) & 1) namespace hfd { class VibratorFF : public Vibrator { public: VibratorFF(); ~VibratorFF(); bool isOpen() const { return fd >= 0; } static bool usable(); static std::string getFirstFFDevice(); protected: void configure(State state, int durationMs) override; private: struct ff_effect effect; std::string devname; int fd; }; } hfd-service-0.2.2/src/vibrator-legacy.cpp000066400000000000000000000026631455450234400203140ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #include "vibrator-legacy.h" #include #include // if the kernel support led triggers // we set namespace hfd { bool VibratorLegacy::usable() { return access("/sys/class/timed_output/vibrator", F_OK ) != -1; } VibratorLegacy::VibratorLegacy(): Vibrator() { Udev::Udev udev; m_device = udev.device_from_syspath("/sys/class/timed_output/vibrator"); // Make sure we are off on init configure(State::Off, 0); } VibratorLegacy::~VibratorLegacy() { } // Over simplyfied led controlls! void VibratorLegacy::configure(State state, int durationMs) { if (state == State::On) { m_device.set_sysattr("enable", std::to_string(durationMs)); } else { m_device.set_sysattr("enable", "0"); } } } hfd-service-0.2.2/src/vibrator-legacy.h000066400000000000000000000020471455450234400177550ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #pragma once #include "vibrator.h" #include "udev/udev-cpp.h" #include #include namespace hfd { class VibratorLegacy : public Vibrator { public: VibratorLegacy(); ~VibratorLegacy(); static bool usable(); protected: void configure(State state, int durationMs) override; private: Udev::UdevDevice m_device; }; } hfd-service-0.2.2/src/vibrator-sysfs.cpp000066400000000000000000000064131455450234400202140ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #include "vibrator-sysfs.h" #include #include #include #include // if the kernel support led triggers // we set namespace hfd { // True if and only if the standard transient trigger is supported. // Some devices implement the transient trigger but don't bother // Actually support it, this function will return a false negative // in that case bool VibratorSysfs::supportTransient() { std::ifstream source_stream("/sys/class/leds/vibrator/trigger"); if (!source_stream) { std::cerr << "Can't read '/sys/class/leds/vibrator/trigger', does the file exist?" << std::endl; return false; } std::string supported_triggers = std::string(std::istreambuf_iterator(source_stream), std::istreambuf_iterator()); bool transientSupported = supported_triggers.find("transient") != std::string::npos; std::cout << "Vibrator supports transient trigger: " << transientSupported << std::endl; return transientSupported; } // Checks that the transient trigger is supported, // or implemented manually by the driver bool VibratorSysfs::usable() { // Need to be able to access the vibrator bool usable = (access("/sys/class/leds/vibrator", F_OK ) != -1); if (!usable) { return false; } // Check if transient is in the list of supported triggers usable = (supportTransient()); // Check if the device supports the transient trigger implicitly. usable |= (access("/sys/class/leds/vibrator/duration", F_OK ) != -1 && access("/sys/class/leds/vibrator/state", F_OK ) != -1 && access("/sys/class/leds/vibrator/activate", F_OK ) != -1); std::cout << "Usable: " << usable << std::endl; return usable; } VibratorSysfs::VibratorSysfs(): Vibrator() { Udev::Udev udev; m_device = udev.device_from_syspath("/sys/class/leds/vibrator"); // If we reached this point but supportTransient() returns false // then the device implements the transient trigger implicitly // so we don't need to enable the transient trigger if (supportTransient()) { m_device.set_sysattr("trigger", "transient"); } // Make sure we are off on init configure(State::Off, 0); } // Over simplyfied led controlls! void VibratorSysfs::configure(State state, int durationMs) { if (state == State::On) { m_device.set_sysattr("state", "1"); m_device.set_sysattr("duration", std::to_string(durationMs)); m_device.set_sysattr("activate", "1"); } else { m_device.set_sysattr("activate", "0"); } } } hfd-service-0.2.2/src/vibrator-sysfs.h000066400000000000000000000020141455450234400176520ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #pragma once #include "vibrator.h" #include "udev/udev-cpp.h" namespace hfd { class VibratorSysfs : public Vibrator { public: VibratorSysfs(); static bool usable(); protected: void configure(State state, int durationMs) override; private: Udev::UdevDevice m_device; static bool supportTransient(); }; } hfd-service-0.2.2/src/vibrator.cpp000066400000000000000000000056271455450234400170550ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #include "vibrator.h" #include "vibrator-ff.h" #include "vibrator-sysfs.h" #include "vibrator-legacy.h" #ifdef HAVE_LIBGBINDER #include "vibrator-binder.h" #endif #include namespace hfd { class VibratorDummy : public Vibrator { public: VibratorDummy() = default; protected: void configure(State state, int durationMs) override { if (state == State::On) { std::cout << "VibratorDummy on, duration: " << durationMs << std::endl; } else { std::cout << "VibratorDummy off" << std::endl; } }; }; std::shared_ptr Vibrator::create() { #ifdef HAVE_LIBGBINDER if (VibratorBinder::usable()) { std::cout << "Using binder vibrator" << std::endl; return std::make_shared(); } else #endif if (VibratorSysfs::usable()) { std::cout << "Using sysfs vibrator" << std::endl; return std::make_shared(); } else if (VibratorLegacy::usable()) { std::cout << "Using legacy vibrator" << std::endl; return std::make_shared(); } else if (VibratorFF::usable()) { std::cout << "Using force feedback vibrator" << std::endl; return std::make_shared(); } std::cout << "Using dummy vibrator" << std::endl; return std::make_shared(); } std::shared_ptr Vibrator::create(std::string type) { if (type == "ff") { std::cout << "Using force feedback vibrator" << std::endl; return std::make_shared(); } #ifdef HAVE_LIBGBINDER else if (type == "binder") { std::cout << "Using binder vibrator" << std::endl; return std::make_shared(); } #endif else if (type == "sysfs") { std::cout << "Using sysfs vibrator" << std::endl; return std::make_shared(); } else if (type == "legacy") { std::cout << "Using legacy vibrator" << std::endl; return std::make_shared(); } std::cout << "Using dummy vibrator" << std::endl; return std::make_shared(); } Vibrator::Vibrator() { } void Vibrator::vibrate(int durationMs) { configure(State::On, durationMs); } } hfd-service-0.2.2/src/vibrator.h000066400000000000000000000020401455450234400165040ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #pragma once #include #include "common.h" namespace hfd { class RepeatThread; class Vibrator { public: Vibrator(); void vibrate(int durationMs); static std::shared_ptr create(); static std::shared_ptr create(std::string type); protected: virtual void configure(State state, int durationMs) = 0; }; } hfd-service-0.2.2/tools/000077500000000000000000000000001455450234400150605ustar00rootroot00000000000000hfd-service-0.2.2/tools/CMakeLists.txt000066400000000000000000000007041455450234400176210ustar00rootroot00000000000000add_executable(hfd-service-tools-leds hfd_leds.cpp ) add_executable(hfd-service-tools-vibrator hfd_vibrator.cpp ) include_directories("../src") target_link_libraries(hfd-service-tools-leds hfd-core ) target_link_libraries(hfd-service-tools-vibrator hfd-core ) install(TARGETS hfd-service-tools-leds RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(TARGETS hfd-service-tools-vibrator RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) hfd-service-0.2.2/tools/hfd_cli.cpp000066400000000000000000000004551455450234400171600ustar00rootroot00000000000000void help() { std::cout << "HFD cli tool" << std::endl << " - hdf-service-cli leds [color]" << std::endl << " - hdf-service-cli vibrator [duration] [repeat]" << std::endl; } int main(int argc, char** argv) { if(argc > 1) { } else help(); } hfd-service-0.2.2/tools/hfd_leds.cpp000066400000000000000000000021621455450234400173350ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #include #include int main() { auto leds = hfd::Leds::create(); leds->setState(hfd::State::On); sleep(1); leds->setState(hfd::State::Off); leds->setState(hfd::State::On); leds->setColor(0xff0000); sleep(10); leds->setColor(0x00ff00); sleep(10); leds->setColor(0x0000ff); sleep(10); leds->setColor(0xbd5751); sleep(10); leds->setState(hfd::State::Off); } hfd-service-0.2.2/tools/hfd_vibrator.cpp000066400000000000000000000015121455450234400202340ustar00rootroot00000000000000/* * Copyright 2020 UBports foundation * * This program 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; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Author: Marius Gripsgard */ #include #include int main() { auto leds = hfd::Vibrator::create(); leds->vibrate(500); sleep(1); }