Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ Depends:
deepin-service-manager (>> 1.0.21),
dbus,
deepin-power-control (>= 2.0.1),
deepin-security-loader
deepin-security-loader,
dde-daemon (>> 6.1.104)
Recommends:
iio-sensor-proxy
Breaks: dde-daemon (<< 6.1.104.1)
Expand Down
1 change: 0 additions & 1 deletion src/plugin-qt/shortcut/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,6 @@ install(FILES configs/org.deepin.dde.keybinding.ini
DESTINATION share/deepin/org.deepin.dde.keybinding)

install(PROGRAMS
scripts/camera-switch
scripts/toggle-grand-search
DESTINATION libexec/dde-services/keybinding)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,11 @@
},
"triggerValue": {
"value": [
"/usr/libexec/dde-services/keybinding/camera-switch"
"/usr/bin/dde-shortcut-tool",
"camera",
"toggle"
],
"serial": 0,
"serial": 1,
"flags": [],
"name": "triggerValue",
"name[zh_CN]": "快捷键触发动作",
Expand Down
11 changes: 0 additions & 11 deletions src/plugin-qt/shortcut/scripts/camera-switch

This file was deleted.

2 changes: 2 additions & 0 deletions src/plugin-qt/shortcut/tools/dde-shortcut-tool/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ add_executable(dde-shortcut-tool
basecontroller.h
audiocontroller.cpp
audiocontroller.h
cameracontroller.cpp
cameracontroller.h
displaycontroller.cpp
displaycontroller.h
touchpadcontroller.cpp
Expand Down
192 changes: 192 additions & 0 deletions src/plugin-qt/shortcut/tools/dde-shortcut-tool/cameracontroller.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later

#include "cameracontroller.h"

#include <QDBusConnection>

Check warning on line 7 in src/plugin-qt/shortcut/tools/dde-shortcut-tool/cameracontroller.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QDBusConnection> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QDBusInterface>

Check warning on line 8 in src/plugin-qt/shortcut/tools/dde-shortcut-tool/cameracontroller.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QDBusInterface> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QDBusReply>

Check warning on line 9 in src/plugin-qt/shortcut/tools/dde-shortcut-tool/cameracontroller.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QDBusReply> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QDebug>

Check warning on line 10 in src/plugin-qt/shortcut/tools/dde-shortcut-tool/cameracontroller.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QDebug> not found. Please note: Cppcheck does not need standard library headers to get proper results.


namespace {

// dde-system-daemon owns the camera privacy switch: it applies the V4L2
// privacy control when the hardware exposes one and otherwise unbinds the
// uvcvideo driver on the video interface (which keeps a built-in mic on the
// same device working).
constexpr const char *kDaemonService = "org.deepin.dde.Daemon1";
constexpr const char *kDaemonPath = "/org/deepin/dde/Daemon1";
constexpr const char *kDaemonInterface = "org.deepin.dde.Daemon1";


} // namespace

CameraController::CameraController(QObject *parent)
: BaseController(parent)
, m_daemonInterface(nullptr)
{
m_daemonInterface = new QDBusInterface(
kDaemonService,
kDaemonPath,
kDaemonInterface,
QDBusConnection::systemBus(),
this
);

if (!m_daemonInterface->isValid()) {
qWarning() << "Failed to connect to Daemon1 service:"
<< m_daemonInterface->lastError().message();
}
}

CameraController::~CameraController()
{
}

QStringList CameraController::commandActions()
{
return QStringList{
"toggle",
"on",
"off"
};
}

QMap<QString, QString> CameraController::commandActionHelp()
{
return {
{"toggle", "Toggle camera privacy (camera off/on)"},
{"on", "Enable the camera (privacy off)"},
{"off", "Disable the camera (privacy on)"}
};
}

QStringList CameraController::supportedActions() const
{
return commandActions();
}

bool CameraController::execute(const QString &action, const QStringList &args)
{
Q_UNUSED(args);

if (action == "toggle") {
return toggle();
} else if (action == "on") {
return setPrivacy(false);
} else if (action == "off") {
return setPrivacy(true);
}

qWarning() << "Unknown camera action:" << action;
return false;
}

QString CameraController::actionHelp(const QString &action) const
{
return commandActionHelp().value(action);
}

bool CameraController::toggle()
{
bool known = false;
const bool privacy = currentPrivacy(&known);
if (!known) {
// No camera device to switch: the hotkey only drives hardware, so do
// nothing rather than launching a camera application.
return false;
}
return setPrivacy(!privacy);
}

bool CameraController::setPrivacy(bool privacy)
{
bool applied = false;
if (!requestPrivacy(privacy, &applied)) {
return false;
}

if (!applied) {
// No camera the daemon can switch at the hardware level: stay silent
// and do not fall back to the camera application.
return false;
}

showOSD(privacy ? "CameraOff" : "CameraOn");
return true;
}


bool CameraController::currentPrivacy(bool *known) const
{
if (known) {
*known = false;
}

if (!m_daemonInterface || !m_daemonInterface->isValid()) {
qWarning() << "Daemon1 interface not available";
return false;
}

QDBusMessage reply = m_daemonInterface->call("GetCameraPrivacy");
if (reply.type() != QDBusMessage::ReplyMessage) {
qWarning() << "GetCameraPrivacy failed:" << reply.errorMessage();
return false;
}

const QList<QVariant> values = reply.arguments();
if (values.size() < 2) {
qWarning() << "GetCameraPrivacy returned unexpected arguments:" << values.size();
return false;
}

const bool privacy = values.at(0).toBool();
const bool deviceKnown = values.at(1).toBool();
if (known) {
*known = deviceKnown;
}
return privacy;
}

bool CameraController::requestPrivacy(bool privacy, bool *applied)
{
if (applied) {
*applied = false;
}

if (!m_daemonInterface || !m_daemonInterface->isValid()) {
qWarning() << "Daemon1 interface not available";
return false;
}

QDBusReply<bool> reply = m_daemonInterface->call("SetCameraPrivacy", privacy);
if (!reply.isValid()) {
qWarning() << "SetCameraPrivacy failed:" << reply.error().message();
return false;
}

if (applied) {
*applied = reply.value();
}
qDebug() << "SetCameraPrivacy privacy:" << privacy << "applied:" << reply.value();
return true;
}


void CameraController::showOSD(const QString &signal)
{
QDBusInterface osdInterface(
"org.deepin.dde.Osd1",
"/org/deepin/dde/shell/osd",
"org.deepin.dde.shell.osd",
QDBusConnection::sessionBus()
);

if (osdInterface.isValid()) {
osdInterface.call("ShowOSD", signal);
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
} else {
qWarning() << "Failed to connect to OSD interface";
}
}
54 changes: 54 additions & 0 deletions src/plugin-qt/shortcut/tools/dde-shortcut-tool/cameracontroller.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later

#ifndef CAMERACONTROLLER_H
#define CAMERACONTROLLER_H

#include "basecontroller.h"

#include <QMap>

Check warning on line 10 in src/plugin-qt/shortcut/tools/dde-shortcut-tool/cameracontroller.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QMap> not found. Please note: Cppcheck does not need standard library headers to get proper results.

class QDBusInterface;

/**
* @brief Camera hotkey controller
*
* Toggles camera privacy through the system daemon
* (org.deepin.dde.Daemon1.SetCameraPrivacy). The hotkey only drives the
* hardware switch; it shows the matching OSD and never launches or closes a
* camera application.
*/
class CameraController : public BaseController
{
Q_OBJECT

public:
explicit CameraController(QObject *parent = nullptr);
~CameraController() override;

static QString commandName() { return "camera"; }
static QStringList commandActions();
static QMap<QString, QString> commandActionHelp();

// BaseController interface
QString name() const override { return commandName(); }
QStringList supportedActions() const override;
bool execute(const QString &action, const QStringList &args = QStringList()) override;
QString actionHelp(const QString &action) const override;

private:
bool toggle();
bool setPrivacy(bool privacy);
// Returns the daemon's privacy state; known is false when no camera is
// available to report on.
bool currentPrivacy(bool *known) const;
// Asks the daemon to switch the camera; applied reports whether hardware
// switching happened.
bool requestPrivacy(bool privacy, bool *applied);
void showOSD(const QString &signal);

QDBusInterface *m_daemonInterface;
};

#endif // CAMERACONTROLLER_H
2 changes: 2 additions & 0 deletions src/plugin-qt/shortcut/tools/dde-shortcut-tool/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "commandparser.h"
#include "audiocontroller.h"
#include "cameracontroller.h"
#include "displaycontroller.h"
#include "touchpadcontroller.h"
#include "powercontroller.h"
Expand Down Expand Up @@ -50,6 +51,7 @@ int main(int argc, char *argv[])
registerControllerFactory<LaunchController>(parser);
registerControllerFactory<NetworkController>(parser);
registerControllerFactory<WmController>(parser);
registerControllerFactory<CameraController>(parser);

// Execute command and return result
return parser.run(argc, argv);
Expand Down
Loading