Skip to content

feat: enable the configuration for device blacklist.#503

Merged
deepin-bot[bot] merged 1 commit into
linuxdeepin:masterfrom
add-uos:master
Jul 21, 2026
Merged

feat: enable the configuration for device blacklist.#503
deepin-bot[bot] merged 1 commit into
linuxdeepin:masterfrom
add-uos:master

Conversation

@add-uos

@add-uos add-uos commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Use the DConfig mechanism to enable the configuration for device blacklist. 使用DConfig机制,开发设备黑名单功能,处在黑名单中的设备不会被应用显示。如果现有设备全部在黑名单中,则会有一个默认设备用于预览。 代码来自xiwo分支。

pick from:183e017c67513974b76786469eb03df8f2e2b6e8

Bug: https://pms.uniontech.com/bug-view-349401.html

Summary by Sourcery

Introduce a configurable camera device blacklist and update camera selection logic to respect it while maintaining sensible fallbacks.

New Features:

  • Add support for configuring a camera device blacklist via DConfig and applying it at application startup.

Enhancements:

  • Track and expose a list of valid (non-blacklisted) camera devices for use in preview initialization and device switching UI.
  • Adjust camera switching behavior to operate only on valid devices and improve handling when no cameras or no valid devices are available.
  • Hide the camera switch button when there is one or fewer valid devices to switch between.

Chores:

  • Extend DevNumMonitor to emit a device list change signal and hook it up so the valid device list is refreshed when hardware changes.

Use the DConfig mechanism to enable the configuration for device blacklist.
使用DConfig机制,开发设备黑名单功能,处在黑名单中的设备不会被应用显示。如果现有设备全部在黑名单中,则会有一个默认设备用于预览。
代码来自xiwo分支。

pick from:183e017c67513974b76786469eb03df8f2e2b6e8

Bug: https://pms.uniontech.com/bug-view-349401.html
@sourcery-ai

sourcery-ai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implements a DConfig-driven camera device blacklist and integrates it into device selection, grouping, and UI behavior, including maintaining a thread-safe list of valid devices and reacting to device list changes.

Sequence diagram for DConfig-driven device blacklist and valid device list updates

sequenceDiagram
    actor User
    participant main
    participant DConfig
    participant DataManager
    participant DevNumMonitor
    participant CMainWindow
    participant videowidget

    User->>main: start application
    main->>DConfig: value(deviceBlacklist)
    DConfig-->>main: QStringList deviceBlacklist
    main->>DataManager: setDeviceBlacklist(deviceBlacklist)
    DataManager-->>main: (blacklist stored)

    DevNumMonitor->>DevNumMonitor: timeOutSlot()
    DevNumMonitor->>DevNumMonitor: check_device_list_events(get_v4l2_device_handler())
    DevNumMonitor-->>DevNumMonitor: [device list changed] true
    DevNumMonitor-->>CMainWindow: deviceListChanged
    CMainWindow->>videowidget: updateValidDevices()

    videowidget->>videowidget: updateValidDevices()
    videowidget->>videowidget: get_device_list()
    videowidget->>DataManager: isDeviceValid(vid,pid,name)
    DataManager-->>videowidget: bool
    videowidget->>videowidget: m_validDevices.push_back(ValidDevice)

    videowidget->>videowidget: delayInit()
    videowidget->>videowidget: updateValidDevices()
    videowidget->>videowidget: getFirstValidDevice()
    videowidget->>videowidget: isDeviceValidByDevice(device)
    alt [device invalid]
        videowidget->>videowidget: switchCamera(validDevice,"")
    else [device valid]
        videowidget->>videowidget: switchCamera(device,"")
    end
Loading

File-Level Changes

Change Details Files
Introduce a device blacklist configuration and validation logic via DataManager and DConfig.
  • Add setDeviceBlacklist to parse and validate blacklist entries as vid,pid,name triples and store them in a QSet
  • Add isDeviceValid to check whether a device (VID, PID, name) is allowed, treating empty parameters as valid
  • Wire DConfig key deviceBlacklist in main.cpp to initialize the blacklist at startup
src/src/basepub/datamanager.cpp
src/src/basepub/datamanager.h
src/main.cpp
Maintain and use a thread-safe list of valid camera devices in videowidget for preview, switching, and grouping.
  • Add ValidDevice class and videowidget members m_validDevices and m_mutexValidDevices guarded by QReadWriteLock
  • Implement updateValidDevices to rebuild m_validDevices from v4l2_device_list_t using DataManager::isDeviceValid and formatted VID/PID
  • Add helper methods formatDeviceId, getFirstValidDevice, isDeviceValidByDevice, getValidDeviceIndexByDevice, and getValidDeviceNum for querying valid devices
src/src/videowidget.cpp
src/src/videowidget.h
Adapt camera startup and device-switching logic to respect the blacklist and valid device list, including fallback behaviors.
  • Modify delayInit to update valid devices, validate the configured device from Settings, and fall back to the first valid device or default logic
  • Change onChangeDev to handle zero devices, lack of valid devices, and to switch only among m_validDevices when groupNum==1
  • Update getUSBCameraGroup to skip invalid devices and to guard against null/empty device lists
src/src/videowidget.cpp
React to device list changes and hide the camera switch button when there are not enough valid devices.
  • Extend DevNumMonitor with a deviceListChanged signal and emit it when check_device_list_events reports changes
  • Connect deviceListChanged to videowidget::updateValidDevices in CMainWindow::loadAfterShow
  • Update setSelBtnShow to show the switch button only when more than one valid device exists
src/src/devnummonitor.cpp
src/src/devnummonitor.h
src/src/mainwindow.cpp
Update copyright headers to reflect extended years.
  • Adjust copyright year ranges in several source files to 2020-2026 and SPDX-FileCopyrightText ranges accordingly
src/src/basepub/datamanager.cpp
src/src/mainwindow.cpp
src/src/devnummonitor.h

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

★ 总体评分:95分

■ 【总体评价】

代码实现了相机设备黑名单过滤功能,逻辑清晰且线程安全措施基本到位
逻辑正确但因存在轻微竞态条件风险扣5分

■ 【详细分析】

  • 1.语法逻辑基本正确✓

DataManager::setDeviceBlacklist 和 videowidget::updateValidDevices 实现了黑名单的解析与设备过滤。在 videowidget::onChangeDev 中,调用 getValidDeviceIndexByDevice 获取索引后释放了锁,随后直接通过索引访问 m_validDevices,若此时另一线程触发 updateValidDevices 清空并重写列表,可能引发越界或数据不一致。
潜在问题:getValidDeviceIndexByDevice 与后续 m_validDevices 访问之间存在竞态条件
建议:在 onChangeDev 中获取设备信息时,应将锁的范围扩大到整个设备访问周期,或在持锁期间拷贝所需设备信息到局部变量

  • 2.代码质量良好✓

代码结构清晰,ValidDevice 类封装合理,注释详尽。onChangeDev 的逻辑重构后消除了重复代码,可读性显著提升。
潜在问题:无
建议:无

  • 3.代码性能良好✓

使用 QSet 存储黑名单提供 O(1) 查找效率,设备列表遍历复杂度合理。读写锁的使用优化了多线程读取场景下的性能。
潜在问题:无
建议:无

  • 4.代码安全存在0个安全漏洞✓

漏洞对比统计:新增漏洞 0 个,减少漏洞 0 个,持平 0 个
代码对输入进行了严格的正则校验和长度限制,未发现注入或溢出风险。

  • 建议:无

■ 【改进建议代码示例】

void videowidget::onChangeDev()
{
    // ... 前置代码 ...
    } else if (groupNum == 1) {
        // 加锁获取当前设备信息和有效设备列表快照
        QString currentDevice = str;
        QVector<ValidDevice> validDevicesSnapshot;
        {
            QReadLocker locker(&m_mutexValidDevices);
            validDevicesSnapshot = m_validDevices;
        }
        
        if (validDevicesSnapshot.empty()) {
            switchCamera("", ""); // 无有效设备,但是有设备,走默认逻辑
        } else {
            int idx = -1;
            for (int i = 0; i < validDevicesSnapshot.size(); ++i) {
                if (validDevicesSnapshot[i].getDevice() == currentDevice) {
                    idx = i;
                    break;
                }
            }
            
            if (idx == -1 || idx >= validDevicesSnapshot.size() - 1) {
                // 获取第一个有效设备,直接切换到该设备
                const ValidDevice &dev = validDevicesSnapshot[0];
                switchCamera(dev.getDevice().toStdString().c_str(), dev.getName().toStdString().c_str());
            } else {
                // 切换到当前设备的下一个有效设备
                const ValidDevice &dev = validDevicesSnapshot[idx + 1];
                switchCamera(dev.getDevice().toStdString().c_str(), dev.getName().toStdString().c_str());
            }
        }
    } else {
    // ... 后续代码 ...
}

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: add-uos, lzwind

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • In DevNumMonitor::timeOutSlot, get_device_list()->num_devices is used without checking that get_device_list() and its list_devices are non-null, which can lead to crashes if the handler or device list is unavailable; consider adding null checks before accessing num_devices.
  • updateValidDevices() returns early when devList or its list_devices is null but does not appear to release any resources associated with the device list; if get_device_list() requires explicit cleanup, ensure the list is properly freed in all exit paths.
  • The blacklist matching in DataManager::setDeviceBlacklist and isDeviceValid relies on exact vid,pid,name strings; if device names can vary (e.g., localization or minor formatting changes), consider normalizing or relaxing name matching to avoid unintentionally treating valid devices as unlisted.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In DevNumMonitor::timeOutSlot, get_device_list()->num_devices is used without checking that get_device_list() and its list_devices are non-null, which can lead to crashes if the handler or device list is unavailable; consider adding null checks before accessing num_devices.
- updateValidDevices() returns early when devList or its list_devices is null but does not appear to release any resources associated with the device list; if get_device_list() requires explicit cleanup, ensure the list is properly freed in all exit paths.
- The blacklist matching in DataManager::setDeviceBlacklist and isDeviceValid relies on exact vid,pid,name strings; if device names can vary (e.g., localization or minor formatting changes), consider normalizing or relaxing name matching to avoid unintentionally treating valid devices as unlisted.

## Individual Comments

### Comment 1
<location path="src/src/videowidget.cpp" line_range="1388-1391" />
<code_context>
     // 收到建议使用 QMap<QString, QVector<v4l2_dev_sys_data_t *>> 来存储分组数据,但我们担心影响现有代码逻辑,
     // 所以暂时保留 QVector<QPair<QString, QVector<v4l2_dev_sys_data_t *>>> 来存储分组数据。
-    if (devlist == nullptr) {
+    if (devlist == nullptr || devlist->list_devices == nullptr || devlist->num_devices == 0) {
         qWarning() << __func__ << "devlist is NULL!";
         return 0;
     }
</code_context>
<issue_to_address>
**suggestion:** Warning message no longer matches the broader null/empty checks.

The condition now includes `devlist == nullptr`, `devlist->list_devices == nullptr`, and `devlist->num_devices == 0`, but the warning still only reports "devlist is NULL". Please update the message to reflect all these cases (e.g. "devlist is NULL or empty") so logs accurately describe the failure condition.

```suggestion
    if (devlist == nullptr || devlist->list_devices == nullptr || devlist->num_devices == 0) {
        qWarning() << __func__ << "devlist is NULL or empty (list_devices is NULL or num_devices == 0)!";
        return 0;
    }
```
</issue_to_address>

### Comment 2
<location path="src/src/videowidget.h" line_range="83" />
<code_context>
+/**
+* @brief ValidDevice 有效相机设备,与黑名单机制配套使用
+*/
+class ValidDevice
+{
+public:
</code_context>
<issue_to_address>
**issue (complexity):** Consider simplifying the ValidDevice type, its lookup helpers, and lock usage to match current needs and reduce boilerplate and cognitive overhead.

The `ValidDevice` + locking + helper set looks more complex than it needs to be for the current usage.

You can keep all functionality while reducing boilerplate and cognitive load:

### 1. Simplify `ValidDevice` to a POD-like struct

If you don’t need mutation semantics or encapsulation, you can drop the setters/getters and use public members. Equality can be a free function or operator on the struct:

```cpp
struct ValidDevice
{
    QString vid;    // 相机VID
    QString pid;    // 相机PID
    QString name;   // 相机名称
    QString device; // 相机设备节点路径
};

inline bool operator==(const ValidDevice &a, const ValidDevice &b)
{
    return a.vid == b.vid
        && a.pid == b.pid
        && a.name == b.name
        && a.device == b.device;
}
```

Usage in `videowidget` stays the same but without getter/setter overhead:

```cpp
ValidDevice dev{vid, pid, name, device};
// access: dev.device, dev.vid, etc.
```

### 2. Collapse multiple lookup helpers into one

`isDeviceValidByDevice` and `getValidDeviceIndexByDevice` can be represented by a single `findValidDeviceByDevice` helper returning a pointer, which callers can use for “exists?” and “index” logic as needed:

```cpp
// in videowidget:
const ValidDevice *findValidDeviceByDevice(const QString &device) const
{
    for (const auto &d : m_validDevices) {
        if (d.device == device)
            return &d;
    }
    return nullptr;
}

// example usage:
bool isDeviceValidByDevice(const QString &device)
{
    return findValidDeviceByDevice(device) != nullptr;
}

int getValidDeviceIndexByDevice(const QString &device)
{
    for (int i = 0; i < m_validDevices.size(); ++i) {
        if (m_validDevices[i].device == device)
            return i;
    }
    return -1;
}
```

Or you can even keep only `findValidDeviceByDevice` public and make the others private/inline wrappers if external code doesn’t need them.

### 3. Reconsider `QReadWriteLock` usage

If `m_validDevices` is only accessed on the UI thread (common for widgets), you can remove `QReadWriteLock` entirely and keep the data structure unguarded:

```cpp
QVector<ValidDevice> m_validDevices; // 有效相机设备列表
// QReadWriteLock m_mutexValidDevices; // remove if single-threaded
```

If you *do* need synchronization, a simpler pattern is to hide the lock inside the helpers so callers don’t have to manage it:

```cpp
QVector<ValidDevice> m_validDevices;
QReadWriteLock m_mutexValidDevices;

// helper with internal locking
const ValidDevice *videowidget::findValidDeviceByDevice(const QString &device) const
{
    QReadLocker locker(&m_mutexValidDevices);
    for (const auto &d : m_validDevices) {
        if (d.device == device)
            return &d;
    }
    return nullptr;
}
```

This keeps the same behavior but reduces the number of places that have to reason about lock lifetime and type.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/src/videowidget.cpp
Comment on lines +1388 to 1391
if (devlist == nullptr || devlist->list_devices == nullptr || devlist->num_devices == 0) {
qWarning() << __func__ << "devlist is NULL!";
return 0;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Warning message no longer matches the broader null/empty checks.

The condition now includes devlist == nullptr, devlist->list_devices == nullptr, and devlist->num_devices == 0, but the warning still only reports "devlist is NULL". Please update the message to reflect all these cases (e.g. "devlist is NULL or empty") so logs accurately describe the failure condition.

Suggested change
if (devlist == nullptr || devlist->list_devices == nullptr || devlist->num_devices == 0) {
qWarning() << __func__ << "devlist is NULL!";
return 0;
}
if (devlist == nullptr || devlist->list_devices == nullptr || devlist->num_devices == 0) {
qWarning() << __func__ << "devlist is NULL or empty (list_devices is NULL or num_devices == 0)!";
return 0;
}

Comment thread src/src/videowidget.h
/**
* @brief ValidDevice 有效相机设备,与黑名单机制配套使用
*/
class ValidDevice

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider simplifying the ValidDevice type, its lookup helpers, and lock usage to match current needs and reduce boilerplate and cognitive overhead.

The ValidDevice + locking + helper set looks more complex than it needs to be for the current usage.

You can keep all functionality while reducing boilerplate and cognitive load:

1. Simplify ValidDevice to a POD-like struct

If you don’t need mutation semantics or encapsulation, you can drop the setters/getters and use public members. Equality can be a free function or operator on the struct:

struct ValidDevice
{
    QString vid;    // 相机VID
    QString pid;    // 相机PID
    QString name;   // 相机名称
    QString device; // 相机设备节点路径
};

inline bool operator==(const ValidDevice &a, const ValidDevice &b)
{
    return a.vid == b.vid
        && a.pid == b.pid
        && a.name == b.name
        && a.device == b.device;
}

Usage in videowidget stays the same but without getter/setter overhead:

ValidDevice dev{vid, pid, name, device};
// access: dev.device, dev.vid, etc.

2. Collapse multiple lookup helpers into one

isDeviceValidByDevice and getValidDeviceIndexByDevice can be represented by a single findValidDeviceByDevice helper returning a pointer, which callers can use for “exists?” and “index” logic as needed:

// in videowidget:
const ValidDevice *findValidDeviceByDevice(const QString &device) const
{
    for (const auto &d : m_validDevices) {
        if (d.device == device)
            return &d;
    }
    return nullptr;
}

// example usage:
bool isDeviceValidByDevice(const QString &device)
{
    return findValidDeviceByDevice(device) != nullptr;
}

int getValidDeviceIndexByDevice(const QString &device)
{
    for (int i = 0; i < m_validDevices.size(); ++i) {
        if (m_validDevices[i].device == device)
            return i;
    }
    return -1;
}

Or you can even keep only findValidDeviceByDevice public and make the others private/inline wrappers if external code doesn’t need them.

3. Reconsider QReadWriteLock usage

If m_validDevices is only accessed on the UI thread (common for widgets), you can remove QReadWriteLock entirely and keep the data structure unguarded:

QVector<ValidDevice> m_validDevices; // 有效相机设备列表
// QReadWriteLock m_mutexValidDevices; // remove if single-threaded

If you do need synchronization, a simpler pattern is to hide the lock inside the helpers so callers don’t have to manage it:

QVector<ValidDevice> m_validDevices;
QReadWriteLock m_mutexValidDevices;

// helper with internal locking
const ValidDevice *videowidget::findValidDeviceByDevice(const QString &device) const
{
    QReadLocker locker(&m_mutexValidDevices);
    for (const auto &d : m_validDevices) {
        if (d.device == device)
            return &d;
    }
    return nullptr;
}

This keeps the same behavior but reduces the number of places that have to reason about lock lifetime and type.

@add-uos

add-uos commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

/merge

@deepin-bot
deepin-bot Bot merged commit 3807ba4 into linuxdeepin:master Jul 21, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants