diff --git a/CMakeLists.txt b/CMakeLists.txt index f6350b3..2eb0017 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,32 +108,33 @@ else() endif() add_executable(projecteur - src/main.cc src/enum-helper.h - src/aboutdlg.cc src/aboutdlg.h - src/actiondelegate.cc src/actiondelegate.h - src/colorselector.cc src/colorselector.h - src/device.cc src/device.h - src/device-hidpp.cc src/device-hidpp.h - src/device-key-lookup.cc src/device-key-lookup.h - src/device-vibration.cc src/device-vibration.h - src/deviceinput.cc src/deviceinput.h - src/devicescan.cc src/devicescan.h - src/deviceswidget.cc src/deviceswidget.h - src/hidpp.cc src/hidpp.h - src/linuxdesktop.cc src/linuxdesktop.h - src/iconwidgets.cc src/iconwidgets.h - src/imageitem.cc src/imageitem.h - src/inputmapconfig.cc src/inputmapconfig.h - src/inputseqedit.cc src/inputseqedit.h - src/logging.cc src/logging.h - src/nativekeyseqedit.cc src/nativekeyseqedit.h - src/preferencesdlg.cc src/preferencesdlg.h - src/projecteurapp.cc src/projecteurapp.h - src/runguard.cc src/runguard.h - src/settings.cc src/settings.h - src/spotlight.cc src/spotlight.h - src/spotshapes.cc src/spotshapes.h - src/virtualdevice.h src/virtualdevice.cc + src/main.cc src/enum-helper.h + src/aboutdlg.cc src/aboutdlg.h + src/actiondelegate.cc src/actiondelegate.h + src/colorselector.cc src/colorselector.h + src/device.cc src/device.h + src/device-command-helper.cc src/device-command-helper.h + src/device-hidpp.cc src/device-hidpp.h + src/device-key-lookup.cc src/device-key-lookup.h + src/device-vibration.cc src/device-vibration.h + src/deviceinput.cc src/deviceinput.h + src/devicescan.cc src/devicescan.h + src/deviceswidget.cc src/deviceswidget.h + src/hidpp.cc src/hidpp.h + src/linuxdesktop.cc src/linuxdesktop.h + src/iconwidgets.cc src/iconwidgets.h + src/imageitem.cc src/imageitem.h + src/inputmapconfig.cc src/inputmapconfig.h + src/inputseqedit.cc src/inputseqedit.h + src/logging.cc src/logging.h + src/nativekeyseqedit.cc src/nativekeyseqedit.h + src/preferencesdlg.cc src/preferencesdlg.h + src/projecteurapp.cc src/projecteurapp.h + src/runguard.cc src/runguard.h + src/settings.cc src/settings.h + src/spotlight.cc src/spotlight.h + src/spotshapes.cc src/spotshapes.h + src/virtualdevice.cc src/virtualdevice.h ${RESOURCES}) target_include_directories(projecteur PRIVATE src) diff --git a/README.md b/README.md index 45bbf41..6464f74 100644 --- a/README.md +++ b/README.md @@ -22,22 +22,39 @@ So here it is: a Linux application for the Logitech Spotlight. ## Table of Contents -* [Motivation](#motivation) -* [Features](#features) -* [Supported Environments](#supported-environments) -* [How it works](#how-it-works) -* [Download](#download) -* [Building](#building) -* [Installation/Running](#installationrunning) - * [Pre-requisites](#pre-requisites) - * [Application Menu](#application-menu) - * [Command Line Interface](#command-line-interface) - * [Scriptability / Keyboard shortcuts](#scriptability) - * [Using Projecteur without a device](#using-projecteur-without-a-device) - * [Device Support](#device-support) - * [Troubleshooting](#troubleshooting) -* [Changelog](#changelog) -* [License](#license) +- [Projecteur](#projecteur) + - [Motivation](#motivation) + - [Table of Contents](#table-of-contents) + - [Features](#features) + - [Screenshots](#screenshots) + - [Planned features](#planned-features) + - [Supported Environments](#supported-environments) + - [How it works](#how-it-works) + - [Button mapping](#button-mapping) + - [Hold Button Mapping for Logitech Spotlight](#hold-button-mapping-for-logitech-spotlight) + - [Download](#download) + - [Building](#building) + - [Requirements](#requirements) + - [Build Example](#build-example) + - [Installation/Running](#installationrunning) + - [Pre-requisites](#pre-requisites) + - [When building Projecteur yourself](#when-building-projecteur-yourself) + - [Application Menu](#application-menu) + - [Command Line Interface](#command-line-interface) + - [Scriptability](#scriptability) + - [Using Projecteur without a device](#using-projecteur-without-a-device) + - [Device Support](#device-support) + - [Compile Time](#compile-time) + - [Runtime](#runtime) + - [Troubleshooting](#troubleshooting) + - [Opaque Spotlight / No Transparency](#opaque-spotlight--no-transparency) + - [Missing System Tray](#missing-system-tray) + - [Zoom is not updated while spotlight is shown](#zoom-is-not-updated-while-spotlight-is-shown) + - [Wayland](#wayland) + - [Wayland Zoom](#wayland-zoom) + - [Device shows as not connected](#device-shows-as-not-connected) + - [Changelog](#changelog) + - [License](#license) ## Features @@ -250,6 +267,9 @@ Example: projecteur -c border=true # Set the border color to red projecteur -c border.color=#ff0000 +# Send a vibrate command to the device with +# intensity=128 and length=0 (only Logitech Spotlight) +projecteur -c vibrate=128,0 ``` While _Projecteur_ does not provide global keyboard shortcuts, command line options diff --git a/cmake/templates/projecteur.bash-completion b/cmake/templates/projecteur.bash-completion index c7ad86c..718ced8 100644 --- a/cmake/templates/projecteur.bash-completion +++ b/cmake/templates/projecteur.bash-completion @@ -20,7 +20,7 @@ _projecteur() case "$prev" in "-c") # Auto completion for commands and properties - local commands="quit spot= spot.size.adjust= settings= preset=" + local commands="quit spot= spot.size.adjust= settings= preset= vibrate=" commands="${commands} spot.size= spot.rotation= spot.shape= spot.shape.square.radius=" commands="${commands} spot.multi-screen= spot.overlay=" commands="${commands} spot.shape.star.points= spot.shape.star.innerradius= spot.shape.ngon.sides=" diff --git a/src/device-command-helper.cc b/src/device-command-helper.cc new file mode 100644 index 0000000..85ad73d --- /dev/null +++ b/src/device-command-helper.cc @@ -0,0 +1,52 @@ +// This file is part of Projecteur - https://github.com/jahnf/projecteur +// - See LICENSE.md and README.md + +#include "device-command-helper.h" + +#include "device-hidpp.h" +#include "spotlight.h" + +// ------------------------------------------------------------------------------------------------- +DeviceCommandHelper::DeviceCommandHelper(QObject* parent, Spotlight* spotlight) + : QObject(parent), m_spotlight(spotlight) +{ + +} + +// ------------------------------------------------------------------------------------------------- +DeviceCommandHelper::~DeviceCommandHelper() = default; + + +// ------------------------------------------------------------------------------------------------- +bool DeviceCommandHelper::sendVibrateCommand(uint8_t intensity, uint8_t length) +{ + if (m_spotlight.isNull()) { + return false; + } + + for ( auto const& dev : m_spotlight->connectedDevices()) { + if (auto connection = m_spotlight->deviceConnection(dev.id)) { + if (!connection->hasHidppSupport()) { + continue; + } + + for (auto const& subInfo : connection->subDevices()) { + auto const& subConn = subInfo.second; + if (!subConn || !subConn->hasFlags(DeviceFlag::Vibrate)) { + continue; + } + + if (auto hidppConn = std::dynamic_pointer_cast(subConn)) + { + hidppConn->sendVibrateCommand(intensity, length, + [](HidppConnectionInterface::MsgResult, HIDPP::Message&&) { + // logDebug(hid) << tr("Vibrate command returned: %1 (%2)") + // .arg(toString(result)).arg(msg.hex()); + }); + } + } + } + } + + return true; +} diff --git a/src/device-command-helper.h b/src/device-command-helper.h new file mode 100644 index 0000000..00fb34e --- /dev/null +++ b/src/device-command-helper.h @@ -0,0 +1,24 @@ +// This file is part of Projecteur - https://github.com/jahnf/projecteur +// - See LICENSE.md and README.md +#pragma once + +#include +#include + +class Spotlight; + +/// Class that offers easy access to device commands with a given Spotlight +/// instance. +class DeviceCommandHelper : public QObject +{ + Q_OBJECT + +public: + explicit DeviceCommandHelper(QObject* parent, Spotlight* spotlight); + virtual ~DeviceCommandHelper(); + + bool sendVibrateCommand(uint8_t intensity, uint8_t length); + +private: + QPointer m_spotlight; +}; diff --git a/src/main.cc b/src/main.cc index fe2985e..045130b 100644 --- a/src/main.cc +++ b/src/main.cc @@ -290,6 +290,7 @@ namespace { print() << " settings=[show|hide] " << Main::tr("Show/hide preferences dialog."); if (fullHelp) { print() << " preset=NAME " << Main::tr("Set a preset."); + print() << " vibrate[=I[,L]] " << Main::tr("Send vibrate command to device with intensity,length."); } print() << " quit " << Main::tr("Quit the running instance."); diff --git a/src/projecteurapp.cc b/src/projecteurapp.cc index cc73981..378b3c6 100644 --- a/src/projecteurapp.cc +++ b/src/projecteurapp.cc @@ -4,6 +4,7 @@ #include "projecteurapp.h" #include "aboutdlg.h" +#include "device-command-helper.h" #include "imageitem.h" #include "linuxdesktop.h" #include "logging.h" @@ -68,6 +69,8 @@ ProjecteurApplication::ProjecteurApplication(int &argc, char **argv, const Optio m_spotlight = new Spotlight(this, Spotlight::Options{options.enableUInput, options.additionalDevices}, m_settings); + m_deviceCommandHelper = new DeviceCommandHelper(this, m_spotlight); + m_settings->setOverlayDisabled(options.disableOverlay); m_dialog = std::make_unique(m_settings, m_spotlight, options.dialogMinimizeOnly @@ -586,6 +589,42 @@ void ProjecteurApplication::readCommand(QLocalSocket* clientConnection) logDebug(cmdserver) << tr("Received quit command."); this->quit(); } + else if (cmdKey == "vibrate") // with args intensity (0-255), length (0-10) + { + #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + auto const args = cmdValue.split(QLatin1Char(','), Qt::SkipEmptyParts); + #else + auto const args = cmdValue.split(QLatin1Char(','), QString::SkipEmptyParts); + #endif + + std::uint8_t const intensity = [&args]{ + if (args.size() >= 1) { + bool ok = false; + auto intensity = args[0].toInt(&ok); + if (ok) { + return static_cast(qMin(255, qMax(0, intensity))); + } + } + return std::uint8_t{128}; + }(); + + std::uint8_t const length = [&args]{ + if (args.size() >= 2) { + bool ok = false; + auto intensity = args[1].toInt(&ok); + if (ok) { + return static_cast(qMin(10, qMax(0, intensity))); + } + } + return std::uint8_t{0}; + }(); + + logDebug(cmdserver) << tr("Received command vibrate = intensity:%1, length:%2") + .arg(intensity) + .arg(length); + + m_deviceCommandHelper->sendVibrateCommand(intensity, length); + } else if (cmdKey == "spot.size.adjust") { bool ok = false; diff --git a/src/projecteurapp.h b/src/projecteurapp.h index 6a394c8..e3ab04a 100644 --- a/src/projecteurapp.h +++ b/src/projecteurapp.h @@ -2,7 +2,7 @@ // - See LICENSE.md and README.md #pragma once -#include "spotlight.h" +#include "devicescan.h" #include #include @@ -11,6 +11,7 @@ #include class AboutDialog; +class DeviceCommandHelper; class LinuxDesktop; class PreferencesDialog; class QLocalServer; @@ -20,6 +21,7 @@ class QQmlApplicationEngine; class QQmlComponent; class QSystemTrayIcon; class Settings; +class Spotlight; class ProjecteurApplication : public QApplication { @@ -78,8 +80,9 @@ private slots: std::unique_ptr m_dialog; QPointer m_aboutDialog; QLocalServer* const m_localServer = nullptr; - Spotlight* m_spotlight = nullptr; Settings* m_settings = nullptr; + Spotlight* m_spotlight = nullptr; + DeviceCommandHelper* m_deviceCommandHelper = nullptr; LinuxDesktop* m_linuxDesktop = nullptr; QQmlApplicationEngine* m_qmlEngine = nullptr; QQmlComponent* m_windowQmlComponent = nullptr;